* [RFC PATCH v3 01/18] docs: Add block device (blkdev) LED trigger documentation
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 02/18] block: Add get_disk_by_name() for use by blkdev LED trigger Ian Pilcher
` (16 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Add Documentation/ABI/testing/sysfs-class-led-trigger-blkdev to
document:
* /sys/class/leds/<led>/blink_time
* /sys/class/leds/<led>/interval
* /sys/class/leds/<led>/mode
* /sys/class/leds/<led>/add_blkdev
* /sys/class/leds/<led>/delete_blkdev
* /sys/class/leds/<led>/block_devices
Add /sys/block/<disk>/blkdev_leds to Documentation/ABI/testing/sysfs-block
Add overview in Documentation/leds/ledtrig-blkdev.rst
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
Documentation/ABI/testing/sysfs-block | 9 ++
.../testing/sysfs-class-led-trigger-blkdev | 48 +++++++
Documentation/leds/index.rst | 1 +
Documentation/leds/ledtrig-blkdev.rst | 132 ++++++++++++++++++
4 files changed, 190 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-led-trigger-blkdev
create mode 100644 Documentation/leds/ledtrig-blkdev.rst
diff --git a/Documentation/ABI/testing/sysfs-block b/Documentation/ABI/testing/sysfs-block
index e34cdeeeb9d4..702601d6a276 100644
--- a/Documentation/ABI/testing/sysfs-block
+++ b/Documentation/ABI/testing/sysfs-block
@@ -316,3 +316,12 @@ Description:
does not complete in this time then the block driver timeout
handler is invoked. That timeout handler can decide to retry
the request, to fail it or to start a device recovery strategy.
+
+What: /sys/block/<disk>/blkdev_leds
+Date: August 2021
+Contact: Ian Pilcher <arequipeno@gmail.com>
+Description:
+ Directory containing links to all LEDs that are associated
+ with this block device through the blkdev LED trigger. Only
+ present when at least one LED is associated. (See
+ Documentation/leds/ledtrig-blkdev.rst.)
diff --git a/Documentation/ABI/testing/sysfs-class-led-trigger-blkdev b/Documentation/ABI/testing/sysfs-class-led-trigger-blkdev
new file mode 100644
index 000000000000..1fd164983f13
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-led-trigger-blkdev
@@ -0,0 +1,48 @@
+What: /sys/class/leds/<led>/blink_time
+Date: August 2021
+Contact: Ian Pilcher <arequipeno@gmail.com>
+Description:
+ Time (in milliseconds) that the LED will be on during a single
+ "blink".
+
+What: /sys/class/leds/<led>/interval
+Date: August 2021
+Contact: Ian Pilcher <arequipeno@gmail.com>
+Description:
+ Frequency (in milliseconds) with which block devices associated
+ with the blkdev LED trigger will be checked for activity.
+
+ NOTE that this attribute is a global setting. All changes
+ apply to all LEDs associated with the blkdev LED trigger.
+
+What: /sys/class/leds/<led>/mode
+Date: August 2021
+Contact: Ian Pilcher <arequipeno@gmail.com>
+Description:
+ Type of events for which LED will blink - read, write,
+ or rw (both). Note that any activity that changes the state of
+ the device's non-volatile storage (including discards and cache
+ flushes) is considered to be a write.
+
+What: /sys/class/leds/<led>/add_blkdev
+Date: August 2021
+Contact: Ian Pilcher <arequipeno@gmail.com>
+Description:
+ Associate a block device with this LED by writing its kernel
+ name (as shown in /sys/block) to this attribute. Multiple
+ device names may be written at once, separated by whitespace.
+
+What: /sys/class/leds/<led>/delete_blkdev
+Date: August 2021
+Contact: Ian Pilcher <arequipeno@gmail.com>
+Description:
+ Remove the association between this LED and a block device by
+ writing the device's kernel name to this attribute. Multiple
+ device names may be written at once, separated by whitespace.
+
+What: /sys/class/leds/<led>/block_devices
+Date: August 2021
+Contact: Ian Pilcher <arequipeno@gmail.com>
+Description:
+ Directory containing links to all block devices that are
+ associated with this LED.
diff --git a/Documentation/leds/index.rst b/Documentation/leds/index.rst
index e5d63b940045..e3c24e468cbc 100644
--- a/Documentation/leds/index.rst
+++ b/Documentation/leds/index.rst
@@ -10,6 +10,7 @@ LEDs
leds-class
leds-class-flash
leds-class-multicolor
+ ledtrig-blkdev
ledtrig-oneshot
ledtrig-transient
ledtrig-usbport
diff --git a/Documentation/leds/ledtrig-blkdev.rst b/Documentation/leds/ledtrig-blkdev.rst
new file mode 100644
index 000000000000..0b1a9359ec39
--- /dev/null
+++ b/Documentation/leds/ledtrig-blkdev.rst
@@ -0,0 +1,132 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================================
+Block Device (blkdev) LED Trigger
+=================================
+
+Available when ``CONFIG_LEDS_TRIGGER_BLKDEV=y``.
+
+See also:
+
+* ``Documentation/ABI/testing/sysfs-class-led-trigger-blkdev``
+* ``Documentation/ABI/testing/sysfs-block`` (``/sys/block/<disk>/leds``)
+
+Overview
+========
+
+.. note::
+ The examples below use ``<LED>`` to refer to the name of a
+ system-specific LED. If no suitable LED is available on a test
+ system (in a virtual machine, for example), it is possible to
+ use a userspace LED (``Documentation/leds/uleds.rst``).
+
+Associate the LED with the ``blkdev`` LED trigger::
+
+ # echo blkdev > /sys/class/leds/<LED>/trigger
+
+ # cat /sys/class/leds/<LED>/trigger
+ ... kbd-ctrlrlock [blkdev] disk-activity ...
+
+Note that several new device attributes are available.
+
+* ``add_blkdev`` and ``delete_blkdev`` are used to associate block devices with
+ this LED, and to remove associations.
+
+* ``mode`` is used to control the type of device activity that will cause this
+ LED to blink - read activity, write activity, or both. (Note that any
+ activity that changes the state of a device's non-volatile storage is
+ considered to be a write. This includes discard and cache flush requests.)
+
+* ``blink_time`` is the duration (in milliseconds) of each blink of this LED.
+
+* ``interval`` is the frequency (in milliseconds) with which devices are checked
+ for activity.
+
+* The ``block_devices`` directory will contain a symbolic link to every device
+ that is associated with this LED.
+
+Associate the LED with the block device::
+
+ # echo sda > /sys/class/leds/<LED>/add_blkdev
+
+ # ls /sys/class/leds/<LED>/block_devices
+ sda
+
+Reads and write activity on the device should cause the LED to blink. The
+duration of each blink (in milliseconds) can be adjusted by setting
+``/sys/class/leds/<LED>/blink_on``, and the minimum delay between blinks can
+be set via ``/sys/class/leds/<LED>/blink_off``.
+
+Associate a second device with the LED::
+
+ # echo sdb > /sys/class/leds/<LED>/add_blkdev
+
+ # ls /sys/class/leds/<LED>/block_devices
+ sda sdb
+
+When a block device is associated with one or more LEDs, the LEDs are linked
+from the device's ``blkdev_leds`` directory::
+
+ # ls /sys/block/sd{a,b}/blkdev_leds
+ /sys/block/sda/blkdev_leds:
+ <LED>
+
+ /sys/block/sdb/blkdev_leds:
+ <LED>
+
+(The ``blkdev_leds`` directory only exists when the block device is associated
+with at least one LED.)
+
+The ``add_blkdev`` and ``delete_blkdev`` attributes both accept multiple,
+whitespace separated, devices. For example::
+
+ # echo sda sdb > /sys/class/leds/<LED>/delete_blkdev
+
+ # ls /sys/class/leds/<LED>/block_devices
+
+``interval`` and ``blink_time``
+===============================
+
+* The ``interval`` attribute is a global setting. Changing the value via
+ ``/sys/class/leds/<LED>/interval`` will affect all LEDs associated with
+ the ``blkdev`` LED trigger.
+
+* All associated devices are checked for activity every ``interval``
+ milliseconds, and a blink is triggered on appropriate LEDs. The duration
+ of an LED's blink is determined by its ``blink_time`` attribute (also in
+ milliseconds). Thus (assuming that activity of the relevant type has occurred
+ on one of an LED's associated devices), the LED will be on for ``blink_time``
+ milliseconds and off for ``interval - blink_time`` milliseconds.
+
+* The LED subsystem ignores new blink requests for an LED that is currently in
+ in the process of blinking, so setting a ``blink_time`` greater than or equal
+ to ``interval`` will cause some blinks to be dropped.
+
+* Because of processing times, scheduling latencies, etc., avoiding missed
+ blinks actually requires a difference of at least a few milliseconds between
+ the ``blink_time`` and ``interval``. The required difference is likely to
+ vary from system to system. As a reference, a Thecus N5550 NAS requires a
+ difference of 7 milliseconds (``interval == 100``, ``blink_time == 93``).
+
+* The default values (``interval == 100``, ``blink_time == 75``) cause the LED
+ associated with a continuously active device to blink rapidly. For a more
+ "constantly on" effect, increase the ``blink_time`` (but not too much; see
+ the previous bullet).
+
+Other Notes
+===========
+
+* Many (possibly all) types of block devices work with this trigger, including:
+
+ * SCSI (including SATA and USB) hard disk drives and SSDs
+ * SCSI (including SATA and USB) optical drives
+ * NVMe SSDs
+ * SD cards
+ * loopback block devices (``/dev/loop*``)
+ * device mapper devices, such as LVM logical volumes
+ * MD RAID devices
+ * zRAM compressed RAM-disks
+
+* The ``blkdev`` LED trigger supports many-to-many device/LED associations.
+ A device can be associated with multiple LEDs, and an LED can be associated
+ with multiple devices.
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 02/18] block: Add get_disk_by_name() for use by blkdev LED trigger
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 01/18] docs: Add block device (blkdev) LED trigger documentation Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 03/18] ledtrig-blkdev: Add file (ledtrig-blkdev.c) for block device " Ian Pilcher
` (15 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Add API that gets a "handle" (pointer & incremented reference count) to a
block device (struct gendisk) by name. Used by the block device LED
trigger when configuring which device(s) an LED should monitor.
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
block/genhd.c | 25 +++++++++++++++++++++++++
include/linux/genhd.h | 10 ++++++++++
2 files changed, 35 insertions(+)
diff --git a/block/genhd.c b/block/genhd.c
index 298ee78c1bda..e6d7bb709d62 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1362,3 +1362,28 @@ int bdev_read_only(struct block_device *bdev)
return bdev->bd_read_only || get_disk_ro(bdev->bd_disk);
}
EXPORT_SYMBOL(bdev_read_only);
+
+static int match_disk_name(struct device *const dev, const void *const name)
+{
+ return dev->type == &disk_type
+ && strcmp(name, dev_to_disk(dev)->disk_name) == 0;
+}
+
+/**
+ * get_disk_by_name - get a gendisk by name
+ * @name: the name of the disk
+ *
+ * Returns a pointer to the gendisk named @name (if it exists), @NULL if not.
+ * Increments the disk's reference count, so caller must call put_device().
+ */
+struct gendisk *get_disk_by_name(const char *const name)
+{
+ struct device *dev;
+
+ dev = class_find_device(&block_class, NULL, name, match_disk_name);
+ if (dev == NULL)
+ return NULL;
+
+ return dev_to_disk(dev);
+}
+EXPORT_SYMBOL_GPL(get_disk_by_name);
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 13b34177cc85..b26bbf2d9048 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -342,4 +342,14 @@ static inline void printk_all_partitions(void)
}
#endif /* CONFIG_BLOCK */
+/* for blkdev LED trigger (drivers/leds/trigger/ledtrig-blkdev.c) */
+#ifdef CONFIG_BLOCK
+struct gendisk *get_disk_by_name(const char *name);
+#else
+static inline struct gendisk *get_disk_by_name(const char *name)
+{
+ return NULL;
+}
+#endif
+
#endif /* _LINUX_GENHD_H */
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 03/18] ledtrig-blkdev: Add file (ledtrig-blkdev.c) for block device LED trigger
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 01/18] docs: Add block device (blkdev) LED trigger documentation Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 02/18] block: Add get_disk_by_name() for use by blkdev LED trigger Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 04/18] ledtrig-blkdev: Add misc. helper functions to blkdev " Ian Pilcher
` (14 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Add data types and global variables
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 66 +++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
create mode 100644 drivers/leds/trigger/ledtrig-blkdev.c
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
new file mode 100644
index 000000000000..28ccbd7946ba
--- /dev/null
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Block device LED triggers
+ *
+ * Copyright 2021 Ian Pilcher <arequipeno@gmail.com>
+ */
+
+#include <linux/leds.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+/* Default blink time & polling interval (milliseconds) */
+#define LEDTRIG_BLKDEV_BLINK_MSEC 75
+#define LEDTRIG_BLKDEV_INTERVAL 100
+
+/* Minimum VALUE for interval or blink_time */
+#define LEDTRIG_BLKDEV_MIN_TIME 25
+
+enum ledtrig_blkdev_mode {
+ LEDTRIG_BLKDEV_MODE_RO = 0, /* blink for reads */
+ LEDTRIG_BLKDEV_MODE_WO = 1, /* blink for writes */
+ LEDTRIG_BLKDEV_MODE_RW = 2 /* blink for reads and writes */
+};
+
+/* Trigger-specific info about a block device */
+struct ledtrig_blkdev_disk {
+ struct gendisk *gd;
+ struct kobject *dir;
+ struct hlist_head leds;
+ unsigned long read_ios;
+ unsigned long write_ios;
+ unsigned int generation;
+ bool read_act;
+ bool write_act;
+};
+
+/* For many-to-many relationships between "disks" (block devices) and LEDs */
+struct ledtrig_blkdev_link {
+ struct hlist_node disk_leds_node;
+ struct hlist_node led_disks_node;
+ struct ledtrig_blkdev_disk *disk;
+ struct ledtrig_blkdev_led *led;
+};
+
+/* Every LED associated with the blkdev trigger gets one of these */
+struct ledtrig_blkdev_led {
+ struct kobject *dir; /* block_devices dir */
+ struct led_classdev *led_dev;
+ unsigned int blink_msec;
+ struct hlist_head disks; /* linked block devs */
+ struct hlist_node leds_node;
+ enum ledtrig_blkdev_mode mode;
+};
+
+/* All LEDs associated with the trigger */
+static HLIST_HEAD(ledtrig_blkdev_leds);
+
+/* Must hold when changing trigger/LED/device associations */
+static DEFINE_MUTEX(ledtrig_blkdev_mutex);
+
+/* Total number of device-to-LED associations */
+static unsigned int ledtrig_blkdev_count;
+
+/* How often to check for drive activity - in jiffies */
+static unsigned int ledtrig_blkdev_interval;
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 04/18] ledtrig-blkdev: Add misc. helper functions to blkdev LED trigger
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (2 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 03/18] ledtrig-blkdev: Add file (ledtrig-blkdev.c) for block device " Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 05/18] ledtrig-blkdev: Periodically check devices for activity & blink LEDs Ian Pilcher
` (13 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Add various helper functions to the block device LED trigger:
* blkdev_mkdir() - create a sysfs directory (and don't swallow error
codes)
* blkdev_streq(), blkdev_skip_space() & blkdev_find_space() - for
parsing writes to sysfs attributes
* blkdev_read_mode() & blkdev_write_mode() - LED mode activity type
helpers
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 74 +++++++++++++++++++++++++++
1 file changed, 74 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index 28ccbd7946ba..fcae7ce63b92 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -6,6 +6,7 @@
* Copyright 2021 Ian Pilcher <arequipeno@gmail.com>
*/
+#include <linux/ctype.h>
#include <linux/leds.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -64,3 +65,76 @@ static unsigned int ledtrig_blkdev_count;
/* How often to check for drive activity - in jiffies */
static unsigned int ledtrig_blkdev_interval;
+
+
+/*
+ *
+ * Miscellaneous helper functions
+ *
+ */
+
+/* Like kobject_create_and_add(), but doesn't swallow error codes */
+static struct kobject *blkdev_mkdir(const char *const name,
+ struct kobject *const parent)
+{
+ struct kobject *dir;
+ int ret;
+
+ dir = kobject_create();
+ if (dir == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ret = kobject_add(dir, parent, "%s", name);
+ if (ret != 0) {
+ kobject_put(dir);
+ return ERR_PTR(ret);
+ }
+
+ return dir;
+}
+
+/*
+ * Compare a null-terminated C string with a non-null-terminated character
+ * sequence of a known length. Returns true if equal, false if not.
+ */
+static bool blkdev_streq(const char *const cstr,
+ const char *const cbuf, const size_t buf_len)
+{
+ return (strlen(cstr) == buf_len) && (memcmp(cstr, cbuf, buf_len) == 0);
+}
+
+/*
+ * Returns a pointer to the first non-whitespace character in s
+ * (or a pointer to the terminating null).
+ */
+static const char *blkdev_skip_space(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 blkdev_skip_space()).
+ */
+static const char *blkdev_find_space(const char *s)
+{
+ while (*s != 0 && !isspace(*s))
+ ++s;
+
+ return s;
+}
+
+static bool blkdev_read_mode(const enum ledtrig_blkdev_mode mode)
+{
+ return mode != LEDTRIG_BLKDEV_MODE_WO;
+}
+
+static bool blkdev_write_mode(const enum ledtrig_blkdev_mode mode)
+{
+ return mode != LEDTRIG_BLKDEV_MODE_RO;
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 05/18] ledtrig-blkdev: Periodically check devices for activity & blink LEDs
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (3 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 04/18] ledtrig-blkdev: Add misc. helper functions to blkdev " Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 06/18] block: Add LED trigger pointer to struct gendisk Ian Pilcher
` (12 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Use a delayed workqueue to periodically check configured block devices for
activity since the last check. Blink LEDs associated with devices on which
the configured type of activity (read/write) has occurred.
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 87 +++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index fcae7ce63b92..e9c23824c33c 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -7,9 +7,11 @@
*/
#include <linux/ctype.h>
+#include <linux/genhd.h>
#include <linux/leds.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/part_stat.h>
/* Default blink time & polling interval (milliseconds) */
#define LEDTRIG_BLKDEV_BLINK_MSEC 75
@@ -66,6 +68,9 @@ static unsigned int ledtrig_blkdev_count;
/* How often to check for drive activity - in jiffies */
static unsigned int ledtrig_blkdev_interval;
+static void blkdev_process(struct work_struct *const work);
+static DECLARE_DELAYED_WORK(ledtrig_blkdev_work, blkdev_process);
+
/*
*
@@ -138,3 +143,85 @@ static bool blkdev_write_mode(const enum ledtrig_blkdev_mode mode)
{
return mode != LEDTRIG_BLKDEV_MODE_RO;
}
+
+
+/*
+ *
+ * Periodically check for device acitivity and blink LEDs
+ *
+ */
+
+static void blkdev_blink(const struct ledtrig_blkdev_led *const led)
+{
+ unsigned long delay_on = READ_ONCE(led->blink_msec);
+ unsigned long delay_off = 1; /* 0 leaves LED turned on */
+
+ led_blink_set_oneshot(led->led_dev, &delay_on, &delay_off, 0);
+}
+
+static void blkdev_update_disk(struct ledtrig_blkdev_disk *const disk,
+ const unsigned int generation)
+{
+ const struct block_device *const part0 = disk->gd->part0;
+ const unsigned long read_ios = part_stat_read(part0, ios[STAT_READ]);
+ const unsigned long write_ios = part_stat_read(part0, ios[STAT_WRITE])
+ + part_stat_read(part0, ios[STAT_DISCARD])
+ + part_stat_read(part0, ios[STAT_FLUSH]);
+
+ if (disk->read_ios != read_ios) {
+ disk->read_act = true;
+ disk->read_ios = read_ios;
+ } else {
+ disk->read_act = false;
+ }
+
+ if (disk->write_ios != write_ios) {
+ disk->write_act = true;
+ disk->write_ios = write_ios;
+ } else {
+ disk->write_act = false;
+ }
+
+ disk->generation = generation;
+}
+
+static void blkdev_process(struct work_struct *const work)
+{
+ static unsigned int generation;
+
+ struct ledtrig_blkdev_led *led;
+ struct ledtrig_blkdev_link *link;
+ unsigned long delay;
+
+ if (!mutex_trylock(&ledtrig_blkdev_mutex))
+ goto exit_reschedule;
+
+ hlist_for_each_entry(led, &ledtrig_blkdev_leds, leds_node) {
+
+ hlist_for_each_entry(link, &led->disks, led_disks_node) {
+
+ struct ledtrig_blkdev_disk *const disk = link->disk;
+
+ if (disk->generation != generation)
+ blkdev_update_disk(disk, generation);
+
+ if (disk->read_act && blkdev_read_mode(led->mode)) {
+ blkdev_blink(led);
+ break;
+ }
+
+ if (disk->write_act && blkdev_write_mode(led->mode)) {
+ blkdev_blink(led);
+ break;
+ }
+ }
+ }
+
+ ++generation;
+
+ mutex_unlock(&ledtrig_blkdev_mutex);
+
+exit_reschedule:
+ delay = READ_ONCE(ledtrig_blkdev_interval);
+ WARN_ON_ONCE(!schedule_delayed_work(&ledtrig_blkdev_work, delay));
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 06/18] block: Add LED trigger pointer to struct gendisk
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (4 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 05/18] ledtrig-blkdev: Periodically check devices for activity & blink LEDs Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 07/18] ledtrig-blkdev: Add function to initialize gendisk ledtrig member Ian Pilcher
` (11 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Needed by ledtrig_blkdev_disk_cleanup(), which removes all monitoring of a
block device by the blkdev LED trigger when the device is removed
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
include/linux/genhd.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index b26bbf2d9048..66e2760702cb 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -168,6 +168,9 @@ struct gendisk {
#endif /* CONFIG_BLK_DEV_INTEGRITY */
#if IS_ENABLED(CONFIG_CDROM)
struct cdrom_device_info *cdi;
+#endif
+#ifdef CONFIG_LEDS_TRIGGER_BLKDEV
+ struct ledtrig_blkdev_disk *ledtrig;
#endif
int node_id;
struct badblocks *bb;
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 07/18] ledtrig-blkdev: Add function to initialize gendisk ledtrig member
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (5 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 06/18] block: Add LED trigger pointer to struct gendisk Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 08/18] ledtrig-blkdev: Add function to remove LED/device association Ian Pilcher
` (10 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Ensures that gendisk ledtrig member is initialized to NULL, in case the
structure was not allocated with kzalloc() or equivalent
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
include/linux/leds.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 329fd914cf24..6b67650d8797 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -10,6 +10,7 @@
#include <dt-bindings/leds/common.h>
#include <linux/device.h>
+#include <linux/genhd.h>
#include <linux/kernfs.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -599,4 +600,19 @@ static inline void ledtrig_audio_set(enum led_audio type,
}
#endif
+#ifdef CONFIG_LEDS_TRIGGER_BLKDEV
+/**
+ * ledtrig_blkdev_disk_init - initialize the ledtrig field of a new gendisk
+ * @gd: the gendisk to be initialized
+ */
+static inline void ledtrig_blkdev_disk_init(struct gendisk *const gd)
+{
+ gd->ledtrig = NULL;
+}
+#else /* CONFIG_LEDS_TRIGGER_BLKDEV */
+static inline void ledtrig_blkdev_disk_init(const struct gendisk *gd)
+{
+}
+#endif /* CONFIG_LEDS_TRIGGER_BLKDEV */
+
#endif /* __LINUX_LEDS_H_INCLUDED */
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 08/18] ledtrig-blkdev: Add function to remove LED/device association
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (6 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 07/18] ledtrig-blkdev: Add function to initialize gendisk ledtrig member Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 09/18] ledtrig-blkdev: Add function to disassociate a device from all LEDs Ian Pilcher
` (9 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Remove symlinks in /sys/class/leds/<led>/block_devices and
/sys/block/<disk>/blkdev_leds
Decrement reference count on /sys/block/<disk>/blkdev_leds
directory (removes directory when empty)
Cancel delayed work when disassociating last device
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 56 +++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index e9c23824c33c..447fc81ae0c5 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -225,3 +225,59 @@ static void blkdev_process(struct work_struct *const work)
delay = READ_ONCE(ledtrig_blkdev_interval);
WARN_ON_ONCE(!schedule_delayed_work(&ledtrig_blkdev_work, delay));
}
+
+
+/*
+ *
+ * Disassociate a block device from an LED
+ *
+ */
+
+static void blkdev_disk_del_locked(struct ledtrig_blkdev_led *const led,
+ struct ledtrig_blkdev_link *const link,
+ struct ledtrig_blkdev_disk *const disk)
+{
+ --ledtrig_blkdev_count;
+
+ if (ledtrig_blkdev_count == 0)
+ WARN_ON(!cancel_delayed_work_sync(&ledtrig_blkdev_work));
+
+ sysfs_remove_link(led->dir, disk->gd->disk_name);
+ sysfs_remove_link(disk->dir, led->led_dev->name);
+ kobject_put(disk->dir);
+
+ hlist_del(&link->led_disks_node);
+ hlist_del(&link->disk_leds_node);
+ kfree(link);
+
+ if (hlist_empty(&disk->leds)) {
+ disk->gd->ledtrig = NULL;
+ kfree(disk);
+ }
+
+ put_device(disk_to_dev(disk->gd));
+}
+
+static void blkdev_disk_delete(struct ledtrig_blkdev_led *const led,
+ const char *const disk_name,
+ const size_t name_len)
+{
+ struct ledtrig_blkdev_link *link;
+
+ mutex_lock(&ledtrig_blkdev_mutex);
+
+ hlist_for_each_entry(link, &led->disks, led_disks_node) {
+
+ if (blkdev_streq(link->disk->gd->disk_name,
+ disk_name, name_len)) {
+ blkdev_disk_del_locked(led, link, link->disk);
+ goto exit_unlock;
+ }
+ }
+
+ pr_info("blkdev LED: %.*s not associated with LED %s\n",
+ (int)name_len, disk_name, led->led_dev->name);
+
+exit_unlock:
+ mutex_unlock(&ledtrig_blkdev_mutex);
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 09/18] ledtrig-blkdev: Add function to disassociate a device from all LEDs
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (7 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 08/18] ledtrig-blkdev: Add function to remove LED/device association Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 10/18] block: Call LED trigger init/cleanup functions Ian Pilcher
` (8 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Called when block device is being removed
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 31 +++++++++++++++++++++++++++
include/linux/leds.h | 4 ++++
2 files changed, 35 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index 447fc81ae0c5..2072cc904616 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -281,3 +281,34 @@ static void blkdev_disk_delete(struct ledtrig_blkdev_led *const led,
exit_unlock:
mutex_unlock(&ledtrig_blkdev_mutex);
}
+
+
+/*
+ *
+ * Disassociate all LEDs from a block device (because it's going away)
+ *
+ */
+
+/**
+ * ledtrig_blkdev_disk_cleanup - remove a block device from the blkdev LED
+ * trigger
+ * @disk: the disk to be removed
+ */
+void ledtrig_blkdev_disk_cleanup(struct gendisk *const gd)
+{
+ struct ledtrig_blkdev_link *link;
+ struct hlist_node *next;
+
+ mutex_lock(&ledtrig_blkdev_mutex);
+
+ if (gd->ledtrig != NULL) {
+
+ hlist_for_each_entry_safe(link, next,
+ &gd->ledtrig->leds, disk_leds_node) {
+ blkdev_disk_del_locked(link->led, link, gd->ledtrig);
+ }
+ }
+
+ mutex_unlock(&ledtrig_blkdev_mutex);
+}
+EXPORT_SYMBOL_GPL(ledtrig_blkdev_disk_cleanup);
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 6b67650d8797..98c479814988 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -609,10 +609,14 @@ static inline void ledtrig_blkdev_disk_init(struct gendisk *const gd)
{
gd->ledtrig = NULL;
}
+void ledtrig_blkdev_disk_cleanup(struct gendisk *const gd);
#else /* CONFIG_LEDS_TRIGGER_BLKDEV */
static inline void ledtrig_blkdev_disk_init(const struct gendisk *gd)
{
}
+static inline void ledtrig_blkdev_disk_cleanup(const struct gendisk *gd)
+{
+}
#endif /* CONFIG_LEDS_TRIGGER_BLKDEV */
#endif /* __LINUX_LEDS_H_INCLUDED */
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 10/18] block: Call LED trigger init/cleanup functions
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (8 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 09/18] ledtrig-blkdev: Add function to disassociate a device from all LEDs Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 11/18] ledtrig-blkdev: Add function to associate a device with an LED Ian Pilcher
` (7 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Call ledtrig_blkdev_disk_init() from __device_add_disk() to ensure that
gendisk's ledtrig field is initialized
Call ledtrig_blkdev_disk_cleanup() from del_gendisk() to clean up any
references to the device from the block device LED trigger
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
block/genhd.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/block/genhd.c b/block/genhd.c
index e6d7bb709d62..091b954ddab3 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -24,6 +24,7 @@
#include <linux/log2.h>
#include <linux/pm_runtime.h>
#include <linux/badblocks.h>
+#include <linux/leds.h>
#include "blk.h"
@@ -539,6 +540,7 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk,
disk_add_events(disk);
blk_integrity_add(disk);
+ ledtrig_blkdev_disk_init(disk);
}
void device_add_disk(struct device *parent, struct gendisk *disk,
@@ -581,6 +583,7 @@ void del_gendisk(struct gendisk *disk)
if (WARN_ON_ONCE(!disk->queue))
return;
+ ledtrig_blkdev_disk_cleanup(disk);
blk_integrity_del(disk);
disk_del_events(disk);
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 11/18] ledtrig-blkdev: Add function to associate a device with an LED
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (9 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 10/18] block: Call LED trigger init/cleanup functions Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 12/18] ledtrig-blkdev: Add sysfs attributes to [dis]associate LEDs & devices Ian Pilcher
` (6 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
If this is the first LED associated with the device, create the
/sys/block/<disk>/blkdev_leds directory. Otherwise, increment its
reference count.
Create symlinks in /sys/class/leds/<led>/block_devices and
/sys/block/<disk>/blkdev_leds
If this the first device associated with any LED, schedule delayed work
to periodically check associated devices and blink LEDs
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 168 ++++++++++++++++++++++++++
1 file changed, 168 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index 2072cc904616..a1646752b9a0 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -312,3 +312,171 @@ void ledtrig_blkdev_disk_cleanup(struct gendisk *const gd)
mutex_unlock(&ledtrig_blkdev_mutex);
}
EXPORT_SYMBOL_GPL(ledtrig_blkdev_disk_cleanup);
+
+
+/*
+ *
+ * Associate a block device with an LED
+ *
+ */
+
+/* Gets or allocs & initializes the blkdev disk for a gendisk */
+static int blkdev_get_disk(struct gendisk *const gd)
+{
+ struct ledtrig_blkdev_disk *disk;
+ struct kobject *dir;
+
+ if (gd->ledtrig != NULL) {
+ kobject_get(gd->ledtrig->dir);
+ return 0;
+ }
+
+ disk = kmalloc(sizeof(*disk), GFP_KERNEL);
+ if (disk == NULL)
+ return -ENOMEM;
+
+ dir = blkdev_mkdir("blkdev_leds", &disk_to_dev(gd)->kobj);
+ if (IS_ERR(dir)) {
+ kfree(disk);
+ return PTR_ERR(dir);
+ }
+
+ INIT_HLIST_HEAD(&disk->leds);
+ disk->gd = gd;
+ disk->dir = dir;
+ disk->read_ios = 0;
+ disk->write_ios = 0;
+
+ gd->ledtrig = disk;
+
+ return 0;
+}
+
+static void blkdev_put_disk(struct ledtrig_blkdev_disk *const disk)
+{
+ kobject_put(disk->dir);
+
+ if (hlist_empty(&disk->leds)) {
+ disk->gd->ledtrig = NULL;
+ kfree(disk);
+ }
+}
+
+static int blkdev_disk_add_locked(struct ledtrig_blkdev_led *const led,
+ struct gendisk *const gd)
+{
+ struct ledtrig_blkdev_link *link;
+ struct ledtrig_blkdev_disk *disk;
+ unsigned long delay;
+ int ret;
+
+ link = kmalloc(sizeof(*link), GFP_KERNEL);
+ if (link == NULL) {
+ ret = -ENOMEM;
+ goto error_return;
+ }
+
+ ret = blkdev_get_disk(gd);
+ if (ret != 0)
+ goto error_free_link;
+
+ disk = gd->ledtrig;
+
+ ret = sysfs_create_link(disk->dir, &led->led_dev->dev->kobj,
+ led->led_dev->name);
+ if (ret != 0)
+ goto error_put_disk;
+
+ ret = sysfs_create_link(led->dir, &disk_to_dev(gd)->kobj,
+ gd->disk_name);
+ if (ret != 0)
+ goto error_remove_link;
+
+ link->disk = disk;
+ link->led = led;
+ hlist_add_head(&link->led_disks_node, &led->disks);
+ hlist_add_head(&link->disk_leds_node, &disk->leds);
+
+ if (ledtrig_blkdev_count == 0) {
+ delay = READ_ONCE(ledtrig_blkdev_interval);
+ WARN_ON(!schedule_delayed_work(&ledtrig_blkdev_work, delay));
+ }
+
+ ++ledtrig_blkdev_count;
+
+ return 0;
+
+error_remove_link:
+ sysfs_remove_link(disk->dir, led->led_dev->name);
+error_put_disk:
+ blkdev_put_disk(disk);
+error_free_link:
+ kfree(link);
+error_return:
+ return ret;
+}
+
+static bool blkdev_already_linked(const struct ledtrig_blkdev_led *const led,
+ const struct gendisk *const gd)
+{
+ const struct ledtrig_blkdev_link *link;
+
+ if (gd->ledtrig == NULL)
+ return false;
+
+ hlist_for_each_entry(link, &gd->ledtrig->leds, disk_leds_node) {
+
+ if (link->led == led) {
+ pr_info("blkdev LED: %s already associated with %s\n",
+ gd->disk_name, led->led_dev->name);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int blkdev_disk_add(struct ledtrig_blkdev_led *const led,
+ const char *const disk_name, const size_t name_len)
+{
+ static char name[DISK_NAME_LEN]; /* only used w/ mutex locked */
+ struct gendisk *gd;
+ int ret;
+
+ if (name_len >= DISK_NAME_LEN) {
+ pr_info("blkdev LED: invalid device name %.*s\n",
+ (int)name_len, disk_name);
+ ret = -EINVAL;
+ goto exit_return;
+ }
+
+ ret = mutex_lock_interruptible(&ledtrig_blkdev_mutex);
+ if (ret != 0)
+ goto exit_return;
+
+ memcpy(name, disk_name, name_len);
+ name[name_len] = 0;
+ gd = get_disk_by_name(name); /* increments disk's refcount */
+
+ if (gd == NULL) {
+ pr_info("blkdev LED: no such block device %.*s\n",
+ (int)name_len, disk_name);
+ ret = -ENODEV;
+ goto exit_unlock;
+ }
+
+ if (blkdev_already_linked(led, gd)) {
+ ret = -EEXIST;
+ goto exit_put_dev;
+ }
+
+ ret = blkdev_disk_add_locked(led, gd);
+
+exit_put_dev:
+ if (ret != 0)
+ put_device(disk_to_dev(gd));
+exit_unlock:
+ mutex_unlock(&ledtrig_blkdev_mutex);
+exit_return:
+ return ret;
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 12/18] ledtrig-blkdev: Add sysfs attributes to [dis]associate LEDs & devices
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (10 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 11/18] ledtrig-blkdev: Add function to associate a device with an LED Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 13/18] ledtrig-blkdev: Add blink_time & interval sysfs attributes Ian Pilcher
` (5 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
/sys/class/leds/<led>/add_blkdev - to create device/LED associations
/sys/class/leds/<led>/delete_blkdev to remove device/LED associations
For both attributes, accept multiple device names separated by whitespace
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 48 +++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index a1646752b9a0..15b15aefdfd8 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -480,3 +480,51 @@ static int blkdev_disk_add(struct ledtrig_blkdev_led *const led,
exit_return:
return ret;
}
+
+
+/*
+ *
+ * sysfs attributes to add & delete devices from LEDs
+ *
+ */
+
+static ssize_t blkdev_add_or_del(struct device *const dev,
+ struct device_attribute *const attr,
+ const char *const buf, const size_t count);
+
+static struct device_attribute ledtrig_blkdev_attr_add =
+ __ATTR(add_blkdev, 0200, NULL, blkdev_add_or_del);
+
+static struct device_attribute ledtrig_blkdev_attr_del =
+ __ATTR(delete_blkdev, 0200, NULL, blkdev_add_or_del);
+
+static ssize_t blkdev_add_or_del(struct device *const dev,
+ struct device_attribute *const attr,
+ const char *const buf, const size_t count)
+{
+ struct ledtrig_blkdev_led *const led = led_trigger_get_drvdata(dev);
+ const char *const disk_name = blkdev_skip_space(buf);
+ const char *const endp = blkdev_find_space(disk_name);
+ const ptrdiff_t name_len = endp - disk_name; /* always >= 0 */
+ int ret;
+
+ if (name_len == 0) {
+ pr_info("blkdev LED: empty block device name\n");
+ return -EINVAL;
+ }
+
+ if (attr == &ledtrig_blkdev_attr_del) {
+ blkdev_disk_delete(led, disk_name, name_len);
+ } else { /* attr == &ledtrig_blkdev_attr_add */
+ ret = blkdev_disk_add(led, disk_name, name_len);
+ if (ret != 0)
+ return ret;
+ }
+
+ /*
+ * Consume everything up to the next non-whitespace token (or the end
+ * of the input). Avoids "empty block device name" error if there is
+ * whitespace after the last token (e.g. a newline).
+ */
+ return blkdev_skip_space(endp) - buf;
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 13/18] ledtrig-blkdev: Add blink_time & interval sysfs attributes
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (11 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 12/18] ledtrig-blkdev: Add sysfs attributes to [dis]associate LEDs & devices Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 14/18] ledtrig-blkdev: Add mode (read/write/rw) sysfs attributue Ian Pilcher
` (4 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
/sys/class/leds/<led>/blink_time controls - per-LED blink duration
/sys/class/leds/<led>/interval - global frequency with which devices
are checked for activity and LEDs are blinked
Enforce 25 millisecond minimum for both attributes
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 63 +++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index 15b15aefdfd8..481b2d142db2 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -528,3 +528,66 @@ static ssize_t blkdev_add_or_del(struct device *const dev,
*/
return blkdev_skip_space(endp) - buf;
}
+
+
+/*
+ *
+ * blink_time & interval device attributes
+ *
+ */
+
+static ssize_t blkdev_time_show(struct device *const dev,
+ struct device_attribute *const attr,
+ char *const buf);
+
+static ssize_t blkdev_time_store(struct device *const dev,
+ struct device_attribute *const attr,
+ const char *const buf, const size_t count);
+
+static struct device_attribute ledtrig_blkdev_attr_blink_time =
+ __ATTR(blink_time, 0644, blkdev_time_show, blkdev_time_store);
+
+static struct device_attribute ledtrig_blkdev_attr_interval =
+ __ATTR(interval, 0644, blkdev_time_show, blkdev_time_store);
+
+static ssize_t blkdev_time_show(struct device *const dev,
+ struct device_attribute *const attr,
+ char *const buf)
+{
+ const struct ledtrig_blkdev_led *const led =
+ led_trigger_get_drvdata(dev);
+ unsigned int value;
+
+ if (attr == &ledtrig_blkdev_attr_blink_time)
+ value = READ_ONCE(led->blink_msec);
+ else // attr == &ledtrig_blkdev_attr_interval
+ value = jiffies_to_msecs(READ_ONCE(ledtrig_blkdev_interval));
+
+ return sprintf(buf, "%u\n", value);
+}
+
+static ssize_t blkdev_time_store(struct device *const dev,
+ struct device_attribute *const attr,
+ const char *const buf, const size_t count)
+{
+ struct ledtrig_blkdev_led *const led = led_trigger_get_drvdata(dev);
+ unsigned int value;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &value);
+ if (ret != 0)
+ return ret;
+
+ if (value < LEDTRIG_BLKDEV_MIN_TIME) {
+ pr_info("blkdev LED: attempt to set time < %s milliseconds\n",
+ __stringify(LEDTRIG_BLKDEV_MIN_TIME));
+ return -ERANGE;
+ }
+
+ if (attr == &ledtrig_blkdev_attr_blink_time)
+ WRITE_ONCE(led->blink_msec, value);
+ else // attr == &ledtrig_blkdev_attr_interval
+ WRITE_ONCE(ledtrig_blkdev_interval, msecs_to_jiffies(value));
+
+ return count;
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 14/18] ledtrig-blkdev: Add mode (read/write/rw) sysfs attributue
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (12 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 13/18] ledtrig-blkdev: Add blink_time & interval sysfs attributes Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 15/18] ledtrig-blkdev: Add function to associate blkdev trigger with LED Ian Pilcher
` (3 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Show all modes, with current mode in square brackets, in show function
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 67 +++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index 481b2d142db2..88e2a11af1a9 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -591,3 +591,70 @@ static ssize_t blkdev_time_store(struct device *const dev,
return count;
}
+
+
+/*
+ *
+ * LED mode device attribute
+ *
+ */
+
+static const struct {
+ const char *name;
+ const char *show;
+} blkdev_modes[] = {
+ [LEDTRIG_BLKDEV_MODE_RO] = {
+ .name = "read",
+ .show = "[read] write rw\n",
+ },
+ [LEDTRIG_BLKDEV_MODE_WO] = {
+ .name = "write",
+ .show = "read [write] rw\n",
+ },
+ [LEDTRIG_BLKDEV_MODE_RW] = {
+ .name = "rw",
+ .show = "read write [rw]\n",
+ },
+};
+
+static ssize_t blkdev_mode_show(struct device *const dev,
+ struct device_attribute *const attr,
+ char *const buf)
+{
+ const struct ledtrig_blkdev_led *const led =
+ led_trigger_get_drvdata(dev);
+
+ return sprintf(buf, blkdev_modes[READ_ONCE(led->mode)].show);
+}
+
+static ssize_t blkdev_mode_store(struct device *const dev,
+ struct device_attribute *const attr,
+ const char *const buf, const size_t count)
+{
+ struct ledtrig_blkdev_led *const led = led_trigger_get_drvdata(dev);
+ const char *const mode_name = blkdev_skip_space(buf);
+ const char *const endp = blkdev_find_space(mode_name);
+ const ptrdiff_t name_len = endp - mode_name; /* always >= 0 */
+ enum ledtrig_blkdev_mode mode;
+
+ if (name_len == 0) {
+ pr_info("blkdev LED: empty mode\n");
+ return -EINVAL;
+ }
+
+ for (mode = LEDTRIG_BLKDEV_MODE_RO;
+ mode <= LEDTRIG_BLKDEV_MODE_RW; ++mode) {
+
+ if (blkdev_streq(blkdev_modes[mode].name,
+ mode_name, name_len)) {
+ WRITE_ONCE(led->mode, mode);
+ return count;
+ }
+ }
+
+ pr_info("blkdev LED: invalid mode (%.*s)\n", (int)name_len, mode_name);
+ return -EINVAL;
+}
+
+static struct device_attribute ledtrig_blkdev_attr_mode =
+ __ATTR(mode, 0644, blkdev_mode_show, blkdev_mode_store);
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 15/18] ledtrig-blkdev: Add function to associate blkdev trigger with LED
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (13 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 14/18] ledtrig-blkdev: Add mode (read/write/rw) sysfs attributue Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 16/18] ledtrig-blkdev: Add function to disassociate an LED from the trigger Ian Pilcher
` (2 subsequent siblings)
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Allocate per-LED data structure and initialize with default values
Create /sys/class/leds/<led>/block_devices directory
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 46 +++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index 88e2a11af1a9..b331e3f24b04 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -658,3 +658,49 @@ static ssize_t blkdev_mode_store(struct device *const dev,
static struct device_attribute ledtrig_blkdev_attr_mode =
__ATTR(mode, 0644, blkdev_mode_show, blkdev_mode_store);
+
+
+/*
+ *
+ * Associate an LED with the blkdev trigger
+ *
+ */
+
+static int blkdev_activate(struct led_classdev *const led_dev)
+{
+ struct ledtrig_blkdev_led *led;
+ int ret;
+
+ led = kmalloc(sizeof(*led), GFP_KERNEL);
+ if (led == NULL) {
+ ret = -ENOMEM;
+ goto exit_return;
+ }
+
+ led->led_dev = led_dev;
+ led->blink_msec = LEDTRIG_BLKDEV_BLINK_MSEC;
+ led->mode = LEDTRIG_BLKDEV_MODE_RW;
+ INIT_HLIST_HEAD(&led->disks);
+
+ ret = mutex_lock_interruptible(&ledtrig_blkdev_mutex);
+ if (ret != 0)
+ goto exit_free;
+
+ led->dir = blkdev_mkdir("block_devices", &led_dev->dev->kobj);
+ if (IS_ERR(led->dir)) {
+ ret = PTR_ERR(led->dir);
+ goto exit_unlock;
+ }
+
+ hlist_add_head(&led->leds_node, &ledtrig_blkdev_leds);
+ led_set_trigger_data(led_dev, led);
+ ret = 0;
+
+exit_unlock:
+ mutex_unlock(&ledtrig_blkdev_mutex);
+exit_free:
+ if (ret != 0)
+ kfree(led);
+exit_return:
+ return ret;
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 16/18] ledtrig-blkdev: Add function to disassociate an LED from the trigger
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (14 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 15/18] ledtrig-blkdev: Add function to associate blkdev trigger with LED Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 17/18] ledtrig-blkdev: Add initialization function Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 18/18] ledtrig-blkdev: Add config option to enable the trigger Ian Pilcher
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Remove all device associations with this LED
Remove /sys/class/leds/<led>/block_devices directory
Free per-LED data structure
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index b331e3f24b04..01573c1ad855 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -704,3 +704,28 @@ static int blkdev_activate(struct led_classdev *const led_dev)
exit_return:
return ret;
}
+
+
+/*
+ *
+ * Disassociate an LED from the trigger
+ *
+ */
+
+static void blkdev_deactivate(struct led_classdev *const led_dev)
+{
+ struct ledtrig_blkdev_led *const led = led_get_trigger_data(led_dev);
+ struct ledtrig_blkdev_link *link;
+ struct hlist_node *next;
+
+ mutex_lock(&ledtrig_blkdev_mutex);
+
+ hlist_for_each_entry_safe(link, next, &led->disks, led_disks_node)
+ blkdev_disk_del_locked(led, link, link->disk);
+
+ hlist_del(&led->leds_node);
+ kobject_put(led->dir);
+ kfree(led);
+
+ mutex_unlock(&ledtrig_blkdev_mutex);
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 17/18] ledtrig-blkdev: Add initialization function
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (15 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 16/18] ledtrig-blkdev: Add function to disassociate an LED from the trigger Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
2021-08-19 2:50 ` [RFC PATCH v3 18/18] ledtrig-blkdev: Add config option to enable the trigger Ian Pilcher
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Register the block device LED trigger
Initialize interval (convert default value to jiffies)
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/ledtrig-blkdev.c | 39 +++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c
index 01573c1ad855..f152b00a4f1a 100644
--- a/drivers/leds/trigger/ledtrig-blkdev.c
+++ b/drivers/leds/trigger/ledtrig-blkdev.c
@@ -729,3 +729,42 @@ static void blkdev_deactivate(struct led_classdev *const led_dev)
mutex_unlock(&ledtrig_blkdev_mutex);
}
+
+
+/*
+ *
+ * Initialization - register the trigger
+ *
+ */
+
+static struct attribute *ledtrig_blkdev_attrs[] = {
+ &ledtrig_blkdev_attr_add.attr,
+ &ledtrig_blkdev_attr_del.attr,
+ &ledtrig_blkdev_attr_blink_time.attr,
+ &ledtrig_blkdev_attr_interval.attr,
+ &ledtrig_blkdev_attr_mode.attr,
+ NULL
+};
+
+static const struct attribute_group ledtrig_blkdev_attr_group = {
+ .attrs = ledtrig_blkdev_attrs,
+};
+
+static const struct attribute_group *ledtrig_blkdev_attr_groups[] = {
+ &ledtrig_blkdev_attr_group,
+ NULL
+};
+
+static struct led_trigger ledtrig_blkdev_trigger = {
+ .name = "blkdev",
+ .activate = blkdev_activate,
+ .deactivate = blkdev_deactivate,
+ .groups = ledtrig_blkdev_attr_groups,
+};
+
+static int __init blkdev_init(void)
+{
+ ledtrig_blkdev_interval = msecs_to_jiffies(LEDTRIG_BLKDEV_INTERVAL);
+ return led_trigger_register(&ledtrig_blkdev_trigger);
+}
+device_initcall(blkdev_init);
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH v3 18/18] ledtrig-blkdev: Add config option to enable the trigger
2021-08-19 2:50 [RFC PATCH v3 00/18] Add block device LED trigger Ian Pilcher
` (16 preceding siblings ...)
2021-08-19 2:50 ` [RFC PATCH v3 17/18] ledtrig-blkdev: Add initialization function Ian Pilcher
@ 2021-08-19 2:50 ` Ian Pilcher
17 siblings, 0 replies; 19+ messages in thread
From: Ian Pilcher @ 2021-08-19 2:50 UTC (permalink / raw)
To: linux-block, linux-leds; +Cc: axboe, pavel, kabel, linux-kernel, kernelnewbies
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
---
drivers/leds/trigger/Kconfig | 9 +++++++++
drivers/leds/trigger/Makefile | 1 +
2 files changed, 10 insertions(+)
diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
index b77a01bd27f4..f15d38b3a632 100644
--- a/drivers/leds/trigger/Kconfig
+++ b/drivers/leds/trigger/Kconfig
@@ -153,4 +153,13 @@ config LEDS_TRIGGER_TTY
When build as a module this driver will be called ledtrig-tty.
+config LEDS_TRIGGER_BLKDEV
+ bool "LED Trigger for block devices"
+ depends on BLOCK
+ help
+ The blkdev LED trigger allows LEDs to be controlled by block device
+ activity (reads and writes).
+
+ See Documentation/leds/ledtrig-blkdev.rst.
+
endif # LEDS_TRIGGERS
diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
index 25c4db97cdd4..d53bab5d93f1 100644
--- a/drivers/leds/trigger/Makefile
+++ b/drivers/leds/trigger/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o
obj-$(CONFIG_LEDS_TRIGGER_PATTERN) += ledtrig-pattern.o
obj-$(CONFIG_LEDS_TRIGGER_AUDIO) += ledtrig-audio.o
obj-$(CONFIG_LEDS_TRIGGER_TTY) += ledtrig-tty.o
+obj-$(CONFIG_LEDS_TRIGGER_BLKDEV) += ledtrig-blkdev.o
--
2.31.1
^ permalink raw reply related [flat|nested] 19+ messages in thread