* New file - include/linux/blk-ledtrig.h Signed-off-by: Ian Pilcher <arequipeno@xxxxxxxxx> --- 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@xxxxxxxxx> */ +#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@xxxxxxxxx> + */ + +#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