Add show & store functions in blk-ledtrig.c (attributes defined in genhd.c) Show function shows all available LEDs (LEDs associated with blkdev trigger); currently associated LED is shown in square brackets ([]) Store function accepts either all whitespace or "none" to clear LED Signed-off-by: Ian Pilcher <arequipeno@xxxxxxxxx> --- block/blk-ledtrig.c | 109 ++++++++++++++++++++++++++++++++++++++++++++ block/blk-ledtrig.h | 8 ++++ block/genhd.c | 8 ++++ 3 files changed, 125 insertions(+) diff --git a/block/blk-ledtrig.c b/block/blk-ledtrig.c index 280fa9edc2dd..1af94dc7ea51 100644 --- a/block/blk-ledtrig.c +++ b/block/blk-ledtrig.c @@ -6,6 +6,7 @@ * Copyright 2021 Ian Pilcher <arequipeno@xxxxxxxxx> */ +#include <linux/ctype.h> #include <linux/genhd.h> #include <linux/leds.h> #include <linux/mutex.h> @@ -139,3 +140,111 @@ static int blk_ledtrig_dev_set(struct gendisk *const disk, led_set_exit_return: return ret; } + + +/* + * + * sysfs attribute store function to set or clear device LED + * + */ + +// Returns a pointer to the first non-whitespace character in s (or a pointer +// to the terminating null). +static const char *blk_ledtrig_skip_whitespace(const char *s) +{ + while (*s != 0 && isspace(*s)) + ++s; + + return s; +} + +// Returns a pointer to the first whitespace character in s (or a pointer to +// the terminating null), which is effectively a pointer to the position *after* +// the last character in the non-whitespace token at the beginning of s. (s is +// expected to be the result of a previous call to blk_ledtrig_skip_whitespace.) +static const char *blk_ledtrig_find_whitespace(const char *s) +{ + while (*s != 0 && !isspace(*s)) + ++s; + + return s; +} + +static bool blk_ledtrig_name_is_none(const char *const name, const size_t len) +{ + static const char none[4] = "none"; // no terminating null + + return len == sizeof(none) && memcmp(name, none, sizeof(none)) == 0; +} + +ssize_t blk_ledtrig_dev_led_store(struct device *const dev, + struct device_attribute *const attr, + const char *const buf, const size_t count) +{ + struct gendisk *const disk = dev_to_disk(dev); + const char *const led_name = blk_ledtrig_skip_whitespace(buf); + const char *const endp = blk_ledtrig_find_whitespace(led_name); + const ptrdiff_t name_len = endp - led_name; // always >= 0 + int ret; + + if (name_len == 0 || blk_ledtrig_name_is_none(led_name, name_len)) { + blk_ledtrig_dev_clear(disk); + ret = 0; + } else { + ret = blk_ledtrig_dev_set(disk, led_name, name_len); + } + + if (ret < 0) + return ret; + + return count; +} + + +/* + * + * sysfs attribute show function for device LED + * + */ + +ssize_t blk_ledtrig_dev_led_show(struct device *const dev, + struct device_attribute *const attr, + char *const buf) +{ + struct gendisk *const disk = dev_to_disk(dev); + struct blk_ledtrig_led *bd_led, *disk_led; + int ret, c = 0; + + ret = mutex_lock_interruptible(&blk_ledtrig_mutex); + if (ret != 0) + goto led_show_exit_return; + + disk_led = rcu_dereference_protected(disk->led, + lockdep_is_held(&blk_ledtrig_mutex)); + + if (disk_led == NULL) + c += sprintf(buf, "[none]"); + else + c += sprintf(buf, "none"); + + list_for_each_entry(bd_led, &blk_ledtrig_leds, leds_list_node) { + + ret = snprintf(buf + c, PAGE_SIZE - c - 1, + bd_led == disk_led ? " [%s]" : " %s", + bd_led->led->name); + if (ret >= PAGE_SIZE - c - 1) { + ret = -EOVERFLOW; + goto led_show_exit_unlock; + } + + c += ret; + } + + buf[c] = '\n'; + ret = c + 1; + +led_show_exit_unlock: + mutex_unlock(&blk_ledtrig_mutex); +led_show_exit_return: + return ret; +} diff --git a/block/blk-ledtrig.h b/block/blk-ledtrig.h index 66a1302a4174..771000d43647 100644 --- a/block/blk-ledtrig.h +++ b/block/blk-ledtrig.h @@ -18,6 +18,14 @@ static inline void blk_ledtrig_disk_init(struct gendisk *const disk) void blk_ledtrig_dev_clear(struct gendisk *const disk); +ssize_t blk_ledtrig_dev_led_store(struct device *const dev, + struct device_attribute *const attr, + const char *const buf, const size_t count); + +ssize_t blk_ledtrig_dev_led_show(struct device *const dev, + struct device_attribute *const attr, + char *const buf); + #else // CONFIG_BLK_LED_TRIGGERS static inline void blk_ledtrig_disk_init(const struct gendisk *disk) {} diff --git a/block/genhd.c b/block/genhd.c index 9fa734aeab0f..d5413a633410 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1012,6 +1012,11 @@ static struct device_attribute dev_attr_fail_timeout = __ATTR(io-timeout-fail, 0644, part_timeout_show, part_timeout_store); #endif +#ifdef CONFIG_BLK_LED_TRIGGERS +static struct device_attribute dev_attr_led = + __ATTR(led, 0644, blk_ledtrig_dev_led_show, blk_ledtrig_dev_led_store); +#endif + static struct attribute *disk_attrs[] = { &dev_attr_range.attr, &dev_attr_ext_range.attr, @@ -1033,6 +1038,9 @@ static struct attribute *disk_attrs[] = { #endif #ifdef CONFIG_FAIL_IO_TIMEOUT &dev_attr_fail_timeout.attr, +#endif +#ifdef CONFIG_BLK_LED_TRIGGERS + &dev_attr_led.attr, #endif NULL }; -- 2.31.1