diff mbox series

[RFC,3/8] block: Add kernel APIs to create & delete block device LED triggers

Message ID 20210729015344.3366750-4-arequipeno@gmail.com
State New
Headers show
Series Add configurable block device LED triggers | expand

Commit Message

Ian Pilcher July 29, 2021, 1:53 a.m. UTC
* New file - include/linux/blk-ledtrig.h

Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
 block/blk-ledtrig.c         | 152 ++++++++++++++++++++++++++++++++++++
 include/linux/blk-ledtrig.h |  19 +++++
 2 files changed, 171 insertions(+)
 create mode 100644 include/linux/blk-ledtrig.h

Comments

Greg KH July 29, 2021, 5:52 a.m. UTC | #1
On Wed, Jul 28, 2021 at 08:53:39PM -0500, Ian Pilcher wrote:
> * New file - include/linux/blk-ledtrig.h
> 
> Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
> ---
>  block/blk-ledtrig.c         | 152 ++++++++++++++++++++++++++++++++++++
>  include/linux/blk-ledtrig.h |  19 +++++
>  2 files changed, 171 insertions(+)
>  create mode 100644 include/linux/blk-ledtrig.h
> 
> diff --git a/block/blk-ledtrig.c b/block/blk-ledtrig.c
> index 345a3b6bdbc6..c69ea1539336 100644
> --- a/block/blk-ledtrig.c
> +++ b/block/blk-ledtrig.c
> @@ -6,9 +6,11 @@
>   *	Copyright 2021 Ian Pilcher <arequipeno@gmail.com>
>   */
>  
> +#include <linux/blk-ledtrig.h>
>  #include <linux/leds.h>
>  #include <linux/list.h>
>  #include <linux/mutex.h>
> +#include <linux/slab.h>
>  
>  
>  /*
> @@ -49,3 +51,153 @@ static struct blk_ledtrig *blk_ledtrig_find(const char *const name,
>  
>  	return NULL;
>  }
> +
> +
> +/*
> + *
> + *	Create a new trigger
> + *
> + */
> +
> +static int __blk_ledtrig_create(const char *const name, const size_t len)
> +{
> +	struct blk_ledtrig *t;
> +	int ret;
> +
> +	if (len == 0) {
> +		pr_warn("empty name specified for blockdev LED trigger\n");
> +		ret = -EINVAL;
> +		goto create_exit_return;
> +	}
> +
> +	ret = mutex_lock_interruptible(&blk_ledtrig_list_mutex);
> +	if (unlikely(ret != 0))

Only ever use likely/unlikely if you can measure the difference without
it.  Otherwise the CPU and compiler will almost always get it right and
you should not clutter up the code with them at all.

For something like this function, where there is no speed difference at
all, there is no need for these types of markings, so I would recommend
just removing them all from your patchset.

thanks,

greg k-h
Ian Pilcher July 29, 2021, 4:16 p.m. UTC | #2
On 7/28/21 10:45 PM, Valdis Klētnieks wrote:
> Is pr_warn() the right level for this stuff? I'd think this sort of pilot error should
> be pr_info() or even pr_debug(), if mentioned at all.  pr_warn() would be for
> something like an unexpected situation like trying to blink an LED but failing.
> Simple syntax errors should probably just toss a -EINVAL and return.

Fair point.  I'll change it to pr_info().  I'm reluctant to completely
"swallow" the error message, as I've been on the other side as a system
administrator trying to guess at the reason for an error code.

> (Among other things, this allows a userspace script to spam the
> log by simply repeatedly trying to create the same entry)

Only root, and they've got plenty of ways to do that.

Thanks!
diff mbox series

Patch

diff --git a/block/blk-ledtrig.c b/block/blk-ledtrig.c
index 345a3b6bdbc6..c69ea1539336 100644
--- a/block/blk-ledtrig.c
+++ b/block/blk-ledtrig.c
@@ -6,9 +6,11 @@ 
  *	Copyright 2021 Ian Pilcher <arequipeno@gmail.com>
  */
 
+#include <linux/blk-ledtrig.h>
 #include <linux/leds.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
+#include <linux/slab.h>
 
 
 /*
@@ -49,3 +51,153 @@  static struct blk_ledtrig *blk_ledtrig_find(const char *const name,
 
 	return NULL;
 }
+
+
+/*
+ *
+ *	Create a new trigger
+ *
+ */
+
+static int __blk_ledtrig_create(const char *const name, const size_t len)
+{
+	struct blk_ledtrig *t;
+	int ret;
+
+	if (len == 0) {
+		pr_warn("empty name specified for blockdev LED trigger\n");
+		ret = -EINVAL;
+		goto create_exit_return;
+	}
+
+	ret = mutex_lock_interruptible(&blk_ledtrig_list_mutex);
+	if (unlikely(ret != 0))
+		goto create_exit_return;
+
+	if (blk_ledtrig_find(name, len) != NULL) {
+		pr_warn("blockdev LED trigger named %.*s already exists\n",
+			(int)len, name);
+		ret = -EEXIST;
+		goto create_exit_unlock_list;
+	}
+
+	t = kzalloc(sizeof(*t) + len + 1, GFP_KERNEL);
+	if (unlikely(t == NULL)) {
+		ret = -ENOMEM;
+		goto create_exit_unlock_list;
+	}
+
+	memcpy(t->name, name, len);
+	t->trigger.name = t->name;
+	mutex_init(&t->refcount_mutex);
+
+	ret = led_trigger_register(&t->trigger);
+	if (ret != 0) {
+		if (likely(ret == -EEXIST)) {
+			pr_warn("LED trigger named %.*s already exists\n",
+				(int)len, name);
+		}
+		goto create_exit_free;
+	}
+
+	list_add(&t->list_node, &blk_ledtrig_list);
+	ret = 0;
+
+create_exit_free:
+	if (ret != 0)
+		kfree(t);
+create_exit_unlock_list:
+	mutex_unlock(&blk_ledtrig_list_mutex);
+create_exit_return:
+	return ret;
+}
+
+/**
+ * blk_ledtrig_create() - creates a new block device LED trigger
+ * @name: the name of the new trigger
+ *
+ * Context: Process context (can sleep).  Takes and releases
+ *	    @blk_ledtrig_list_mutex.
+ *
+ * Return: 0 on success; -@errno on error
+ */
+int blk_ledtrig_create(const char *const name)
+{
+	return __blk_ledtrig_create(name, strlen(name));
+}
+EXPORT_SYMBOL_GPL(blk_ledtrig_create);
+
+
+/*
+ *
+ *	Delete a trigger
+ *
+ */
+
+static int __blk_ledtrig_delete(const char *const name, const size_t len)
+{
+	struct blk_ledtrig *t;
+	int ret;
+
+	if (len == 0) {
+		pr_warn("empty name specified for blockdev LED trigger\n");
+		ret = -EINVAL;
+		goto delete_exit_return;
+	}
+
+	ret = mutex_lock_interruptible(&blk_ledtrig_list_mutex);
+	if (unlikely(ret != 0))
+		goto delete_exit_return;
+
+	t = blk_ledtrig_find(name, len);
+	if (t == NULL) {
+		pr_warn("blockdev LED trigger named %.*s doesn't exist\n",
+			(int)len, name);
+		ret = -ENODEV;
+		goto delete_exit_unlock_list;
+	}
+
+	ret = mutex_lock_interruptible(&t->refcount_mutex);
+	if (unlikely(ret != 0))
+		goto delete_exit_unlock_list;
+
+	if (WARN_ON(t->refcount < 0)) {
+		ret = -EBADFD;
+		goto delete_exit_unlock_refcount;
+	}
+
+	if (t->refcount > 0) {
+		pr_warn("blockdev LED trigger %s still in use\n", t->name);
+		ret = -EBUSY;
+		goto delete_exit_unlock_refcount;
+	}
+
+	led_trigger_unregister(&t->trigger);
+	list_del(&t->list_node);
+
+	ret = 0;
+
+delete_exit_unlock_refcount:
+	mutex_unlock(&t->refcount_mutex);
+	if (ret == 0)
+		kfree(t);
+delete_exit_unlock_list:
+	mutex_unlock(&blk_ledtrig_list_mutex);
+delete_exit_return:
+	return ret;
+}
+
+/**
+ * blk_ledtrig_delete() - deletes a block device LED trigger
+ * @name: the name of the trigger to be deleted
+ *
+ * Context: Process context (can sleep).  Takes and releases
+ *	    @blk_ledtrig_list_mutex and trigger's @refcount_mutex.
+ *
+ * Return: 0 on success; -@errno on error
+ */
+int blk_ledtrig_delete(const char *const name)
+{
+	return __blk_ledtrig_delete(name, strlen(name));
+}
+EXPORT_SYMBOL_GPL(blk_ledtrig_delete);
diff --git a/include/linux/blk-ledtrig.h b/include/linux/blk-ledtrig.h
new file mode 100644
index 000000000000..6f73635f65ec
--- /dev/null
+++ b/include/linux/blk-ledtrig.h
@@ -0,0 +1,19 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ *	Block device LED triggers
+ *
+ *	Copyright 2021 Ian Pilcher <arequipeno@gmail.com>
+ */
+
+#ifndef _LINUX_BLK_LEDTRIG_H
+#define _LINUX_BLK_LEDTRIG_H
+
+#ifdef CONFIG_BLK_LED_TRIGGERS
+
+int blk_ledtrig_create(const char *name);
+int blk_ledtrig_delete(const char *name);
+
+#endif	// CONFIG_BLK_LED_TRIGGERS
+
+#endif	// _LINUX_BLK_LEDTRIG_H