From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:07
Ridiculous or not, here is version 3 of the block device trigger for
"freaking blinkenlights". It addresses basically all of the points
raised in response to the v2 patchset.
* The main body of the code is moved from the block directory into
the LED triggers directory (drivers/leds/trigger/ledtrig-blkdev.c)
The downside of this is that it requires adding an API to the
block subsystem - get_disk_by_name() - which allows the trigger
code to resolve a gendisk when asked to monitor it. I know of
no good way to do this today, and I don't know of a good way to
implement the sysfs API requested by Pavel and Marek without
something like this API.
Other than that, changes to the block subsystem are as minimal as
I can make them - a single pointer added to struct gendisk and
init/cleanup calls when a gendisk is added or deleted.
* This also implements Marek's suggestion of periodically checking
devices for activity, rather than directly blinking LEDs in the
I/O path. This change has the unanticipated benefit of making the
trigger work on pretty much all types of virtual block devices
(device mapper, MD RAID, zRAM, etc.), as well as NVMe SSDs.
* Relationships between devices and LEDs are now many-to-many. An
LED can monitor multiple devices, and multiple LEDs can monitor
any one device. The current "associations" are reflected in two
sysfs directories.
- /sys/class/leds/<led>/block_devices contains links to all devices
associated with an LED, and
- /sys/block/<disk>/blkdev_leds contains links to all LEDs with
which the device is associated.
(The latter directory only exists when the device is associated
with at least one LED.)
* Each LED can be set to show read activity, write activity, or both.
Discards and cache flushes are considered to be writes, as they
affect the state of the device's non-volatile storage.
Ian Pilcher (18):
docs: Add block device (blkdev) LED trigger documentation
block: Add get_disk_by_name() for use by blkdev LED trigger
ledtrig-blkdev: Add file (ledtrig-blkdev.c) for block device LED
trigger
ledtrig-blkdev: Add misc. helper functions to blkdev LED trigger
ledtrig-blkdev: Periodically check devices for activity & blink LEDs
block: Add LED trigger pointer to struct gendisk
ledtrig-blkdev: Add function to initialize gendisk ledtrig member
ledtrig-blkdev: Add function to remove LED/device association
ledtrig-blkdev: Add function to disassociate a device from all LEDs
block: Call LED trigger init/cleanup functions
ledtrig-blkdev: Add function to associate a device with an LED
ledtrig-blkdev: Add sysfs attributes to [dis]associate LEDs & devices
ledtrig-blkdev: Add blink_time & interval sysfs attributes
ledtrig-blkdev: Add mode (read/write/rw) sysfs attributue
ledtrig-blkdev: Add function to associate blkdev trigger with LED
ledtrig-blkdev: Add function to disassociate an LED from the trigger
ledtrig-blkdev: Add initialization function
ledtrig-blkdev: Add config option to enable the trigger
Documentation/ABI/testing/sysfs-block | 9 +
.../testing/sysfs-class-led-trigger-blkdev | 48 ++
Documentation/leds/index.rst | 1 +
Documentation/leds/ledtrig-blkdev.rst | 132 +++
block/genhd.c | 28 +
drivers/leds/trigger/Kconfig | 9 +
drivers/leds/trigger/Makefile | 1 +
drivers/leds/trigger/ledtrig-blkdev.c | 770 ++++++++++++++++++
include/linux/genhd.h | 13 +
include/linux/leds.h | 20 +
10 files changed, 1031 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-led-trigger-blkdev
create mode 100644 Documentation/leds/ledtrig-blkdev.rst
create mode 100644 drivers/leds/trigger/ledtrig-blkdev.c
--
2.31.1
@@ -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.)
@@ -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.
@@ -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.
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:13
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 <redacted>
---
block/genhd.c | 25 +++++++++++++++++++++++++
include/linux/genhd.h | 10 ++++++++++
2 files changed, 35 insertions(+)
@@ -0,0 +1,66 @@+// SPDX-License-Identifier: GPL-2.0-only++/*+*BlockdeviceLEDtriggers+*+*Copyright2021IanPilcher<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++enumledtrig_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 */+structledtrig_blkdev_disk{+structgendisk*gd;+structkobject*dir;+structhlist_headleds;+unsignedlongread_ios;+unsignedlongwrite_ios;+unsignedintgeneration;+boolread_act;+boolwrite_act;+};++/* For many-to-many relationships between "disks" (block devices) and LEDs */+structledtrig_blkdev_link{+structhlist_nodedisk_leds_node;+structhlist_nodeled_disks_node;+structledtrig_blkdev_disk*disk;+structledtrig_blkdev_led*led;+};++/* Every LED associated with the blkdev trigger gets one of these */+structledtrig_blkdev_led{+structkobject*dir;/* block_devices dir */+structled_classdev*led_dev;+unsignedintblink_msec;+structhlist_headdisks;/* linked block devs */+structhlist_nodeleds_node;+enumledtrig_blkdev_modemode;+};++/* All LEDs associated with the trigger */+staticHLIST_HEAD(ledtrig_blkdev_leds);++/* Must hold when changing trigger/LED/device associations */+staticDEFINE_MUTEX(ledtrig_blkdev_mutex);++/* Total number of device-to-LED associations */+staticunsignedintledtrig_blkdev_count;++/* How often to check for drive activity - in jiffies */+staticunsignedintledtrig_blkdev_interval;
@@ -64,3 +65,76 @@ static unsigned int ledtrig_blkdev_count;/* How often to check for drive activity - in jiffies */staticunsignedintledtrig_blkdev_interval;+++/*+*+*Miscellaneoushelperfunctions+*+*/++/* Like kobject_create_and_add(), but doesn't swallow error codes */+staticstructkobject*blkdev_mkdir(constchar*constname,+structkobject*constparent)+{+structkobject*dir;+intret;++dir=kobject_create();+if(dir==NULL)+returnERR_PTR(-ENOMEM);++ret=kobject_add(dir,parent,"%s",name);+if(ret!=0){+kobject_put(dir);+returnERR_PTR(ret);+}++returndir;+}++/*+*Compareanull-terminatedCstringwithanon-null-terminatedcharacter+*sequenceofaknownlength.Returnstrueifequal,falseifnot.+*/+staticboolblkdev_streq(constchar*constcstr,+constchar*constcbuf,constsize_tbuf_len)+{+return(strlen(cstr)==buf_len)&&(memcmp(cstr,cbuf,buf_len)==0);+}++/*+*Returnsapointertothefirstnon-whitespacecharacterins+*(orapointertotheterminatingnull).+*/+staticconstchar*blkdev_skip_space(constchar*s)+{+while(*s!=0&&isspace(*s))+++s;++returns;+}++/*+*Returnsapointertothefirstwhitespacecharacterins(orapointertothe+*terminatingnull),whichiseffectivelyapointertotheposition*after*the+*lastcharacterinthenon-whitespacetokenatthebeginningofs.(sis+*expectedtobetheresultofapreviouscalltoblkdev_skip_space()).+*/+staticconstchar*blkdev_find_space(constchar*s)+{+while(*s!=0&&!isspace(*s))+++s;++returns;+}++staticboolblkdev_read_mode(constenumledtrig_blkdev_modemode)+{+returnmode!=LEDTRIG_BLKDEV_MODE_WO;+}++staticboolblkdev_write_mode(constenumledtrig_blkdev_modemode)+{+returnmode!=LEDTRIG_BLKDEV_MODE_RO;+}
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:24
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 <redacted>
---
drivers/leds/trigger/ledtrig-blkdev.c | 87 +++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
@@ -66,6 +68,9 @@ static unsigned int ledtrig_blkdev_count;/* How often to check for drive activity - in jiffies */staticunsignedintledtrig_blkdev_interval;+staticvoidblkdev_process(structwork_struct*constwork);+staticDECLARE_DELAYED_WORK(ledtrig_blkdev_work,blkdev_process);+/**
@@ -138,3 +143,85 @@ static bool blkdev_write_mode(const enum ledtrig_blkdev_mode mode){returnmode!=LEDTRIG_BLKDEV_MODE_RO;}+++/*+*+*PeriodicallycheckfordeviceacitivityandblinkLEDs+*+*/++staticvoidblkdev_blink(conststructledtrig_blkdev_led*constled)+{+unsignedlongdelay_on=READ_ONCE(led->blink_msec);+unsignedlongdelay_off=1;/* 0 leaves LED turned on */++led_blink_set_oneshot(led->led_dev,&delay_on,&delay_off,0);+}++staticvoidblkdev_update_disk(structledtrig_blkdev_disk*constdisk,+constunsignedintgeneration)+{+conststructblock_device*constpart0=disk->gd->part0;+constunsignedlongread_ios=part_stat_read(part0,ios[STAT_READ]);+constunsignedlongwrite_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;+}++staticvoidblkdev_process(structwork_struct*constwork)+{+staticunsignedintgeneration;++structledtrig_blkdev_led*led;+structledtrig_blkdev_link*link;+unsignedlongdelay;++if(!mutex_trylock(&ledtrig_blkdev_mutex))+gotoexit_reschedule;++hlist_for_each_entry(led,&ledtrig_blkdev_leds,leds_node){++hlist_for_each_entry(link,&led->disks,led_disks_node){++structledtrig_blkdev_disk*constdisk=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));+}
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:25
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 <redacted>
---
include/linux/genhd.h | 3 +++
1 file changed, 3 insertions(+)
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:27
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 <redacted>
---
include/linux/leds.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:29
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 <redacted>
---
drivers/leds/trigger/ledtrig-blkdev.c | 56 +++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:32
Called when block device is being removed
Signed-off-by: Ian Pilcher <redacted>
---
drivers/leds/trigger/ledtrig-blkdev.c | 31 +++++++++++++++++++++++++++
include/linux/leds.h | 4 ++++
2 files changed, 35 insertions(+)
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:40
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 <redacted>
---
block/genhd.c | 3 +++
1 file changed, 3 insertions(+)
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:42
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 <redacted>
---
drivers/leds/trigger/ledtrig-blkdev.c | 168 ++++++++++++++++++++++++++
1 file changed, 168 insertions(+)
@@ -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);+++/*+*+*AssociateablockdevicewithanLED+*+*/++/* Gets or allocs & initializes the blkdev disk for a gendisk */+staticintblkdev_get_disk(structgendisk*constgd)+{+structledtrig_blkdev_disk*disk;+structkobject*dir;++if(gd->ledtrig!=NULL){+kobject_get(gd->ledtrig->dir);+return0;+}++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);+returnPTR_ERR(dir);+}++INIT_HLIST_HEAD(&disk->leds);+disk->gd=gd;+disk->dir=dir;+disk->read_ios=0;+disk->write_ios=0;++gd->ledtrig=disk;++return0;+}++staticvoidblkdev_put_disk(structledtrig_blkdev_disk*constdisk)+{+kobject_put(disk->dir);++if(hlist_empty(&disk->leds)){+disk->gd->ledtrig=NULL;+kfree(disk);+}+}++staticintblkdev_disk_add_locked(structledtrig_blkdev_led*constled,+structgendisk*constgd)+{+structledtrig_blkdev_link*link;+structledtrig_blkdev_disk*disk;+unsignedlongdelay;+intret;++link=kmalloc(sizeof(*link),GFP_KERNEL);+if(link==NULL){+ret=-ENOMEM;+gotoerror_return;+}++ret=blkdev_get_disk(gd);+if(ret!=0)+gotoerror_free_link;++disk=gd->ledtrig;++ret=sysfs_create_link(disk->dir,&led->led_dev->dev->kobj,+led->led_dev->name);+if(ret!=0)+gotoerror_put_disk;++ret=sysfs_create_link(led->dir,&disk_to_dev(gd)->kobj,+gd->disk_name);+if(ret!=0)+gotoerror_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;++return0;++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:+returnret;+}++staticboolblkdev_already_linked(conststructledtrig_blkdev_led*constled,+conststructgendisk*constgd)+{+conststructledtrig_blkdev_link*link;++if(gd->ledtrig==NULL)+returnfalse;++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);+returntrue;+}+}++returnfalse;+}++staticintblkdev_disk_add(structledtrig_blkdev_led*constled,+constchar*constdisk_name,constsize_tname_len)+{+staticcharname[DISK_NAME_LEN];/* only used w/ mutex locked */+structgendisk*gd;+intret;++if(name_len>=DISK_NAME_LEN){+pr_info("blkdev LED: invalid device name %.*s\n",+(int)name_len,disk_name);+ret=-EINVAL;+gotoexit_return;+}++ret=mutex_lock_interruptible(&ledtrig_blkdev_mutex);+if(ret!=0)+gotoexit_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;+gotoexit_unlock;+}++if(blkdev_already_linked(led,gd)){+ret=-EEXIST;+gotoexit_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:+returnret;+}
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:49
/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 <redacted>
---
drivers/leds/trigger/ledtrig-blkdev.c | 63 +++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:55
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 <redacted>
---
drivers/leds/trigger/ledtrig-blkdev.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
From: Ian Pilcher <hidden> Date: 2021-08-19 02:51:58
Show all modes, with current mode in square brackets, in show function
Signed-off-by: Ian Pilcher <redacted>
---
drivers/leds/trigger/ledtrig-blkdev.c | 67 +++++++++++++++++++++++++++
1 file changed, 67 insertions(+)