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

Ian Pilcher arequipeno at gmail.com
Wed Jul 28 21:53:39 EDT 2021


* New file - include/linux/blk-ledtrig.h

Signed-off-by: Ian Pilcher <arequipeno at 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 at 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; - at 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; - at 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 at 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
-- 
2.31.1




More information about the Kernelnewbies mailing list