Hello Ian, first, as I replied in one of the previous versions: it is not customary to use the const keyword in Linux the way you do use it, i.e. int f(const int var) Your argument was that this makes the code more safe when changes are made in the future. Maybe this is true (although in my opinion it is not), but since this isn't done anywhere in kernel AFAIK, please drop it in this proposal. If you want to introduce it, you can try sending proposals to other kernel subsystems. I am almost sure they will be rejected. Also, I won't give my Reviewed-by for this patch unless you drop it (or if Pavel or some other long-term maintainer thinks it should be used). Second, I really would like to solve the problem looking up the block device, given name. Since block API does not provide such function, I think we should try to add it. It does not make sense to not be able to set, for example "sda1" to trigger a LED, when /sys/class/block/sda1 exists, but someone deleted the /dev/sda1 device file. Anyway, see other comments below: On Mon, 4 Oct 2021 10:55:46 -0500 Ian Pilcher <arequipeno@xxxxxxxxx> wrote: > +/* Device activity type that will make LED blink */ > +enum led_bdev_led_mode { > + LED_BDEV_MODE_RO = 0, > + LED_BDEV_MODE_WO = 1, > + LED_BDEV_MODE_RW = 2 > +}; Please use a bitmap as the netdev trigger does. > +struct led_bdev_lbd { Could this be renamed to trig_bdev? > +struct led_bdev_led { And this to trig_led? Also, the led_bdev prefixes everywhere make the code a little confusing, IMO. Could this prefix be changed to blkdev_trig? This would be similar to netdev trigger. > +/* Delayed work to periodically check for activity & blink LEDs */ > +static DECLARE_DELAYED_WORK(led_bdev_work, led_bdev_process); > > +/* How often to run the delayed work - in jiffies */ > +static unsigned int led_bdev_interval; This is wrong. The interval can't be same for all triggers, this does not make sense. I want to be able to set different intervals for different LEDs, as in netdev trigger. Sure maybe not many people will use it, but the possibility should be there. The work will then also be per LED and your structures should become more simple. You won't need to have NxM mapping between LEDs and block devices. > +static int led_bdev_activate(struct led_classdev *const led_cdev) > +{ > + struct led_bdev_led *led; > + int ret; > + > + if (led_bdev_next_index == ULONG_MAX) { I don't understand why this must be done. > + ret = -EOVERFLOW; > + goto exit_return; exit_return label only returns. The whole idea behind using goto in such a way is to avoid code duplication. Instead you do the opposite. You can simply do return -EOVERFLOW; > + } > + > + led = kmalloc(sizeof(*led), GFP_KERNEL); kzalloc > + if (led == NULL) { > + ret = -ENOMEM; > + goto exit_return; return -ENOMEM. > + } > + > + led->index = led_bdev_next_index++; This variable led_bdev_next_index never gets subtracted from, it only increases. So if I enable and disable blkdev trigger ULONG_MAX times, I won't be able to enable it anymore since you return -EOVERFLOW ? This doesn't make any sense. > + led->led_cdev = led_cdev; > + led->blink_msec = LED_BDEV_BLINK_MSEC; > + led->mode = LED_BDEV_MODE_RW; > + xa_init(&led->linked_lbds); > + > + ret = mutex_lock_interruptible(&led_bdev_mutex); > + if (ret != 0) if (ret) we don't do if(ret == 0) and if(ret != 0) normally, instead we do if(!ret) and if(ret). Explicitly mentioning 0 makes sense when you have something like if (byte == 0x03 || byte == 0x00) > + goto exit_free; > +static ssize_t link_device_store(struct device *const dev, > + struct device_attribute *const attr, > + const char *const buf, const size_t count) > +{ > + struct led_bdev_led *const led = led_trigger_get_drvdata(dev); > + struct led_bdev_lbd *lbd; > + int ret; > + > + ret = mutex_lock_interruptible(&led_bdev_mutex); > + if (ret != 0) > + goto exit_return; > + > + lbd = led_bdev_get_lbd(buf, count); > + if (IS_ERR(lbd)) { > + ret = PTR_ERR(lbd); > + goto exit_unlock; > + } > + > + if (xa_load(&lbd->linked_leds, led->index) != NULL) { > + ret = -EEXIST; > + goto exit_put_lbd; > + } > + > + ret = led_bdev_link(led, lbd); > + > +exit_put_lbd: > + if (ret != 0) > + led_bdev_put_lbd(lbd); > +exit_unlock: > + mutex_unlock(&led_bdev_mutex); > +exit_return: > + return (ret == 0) ? count : ret; return ret ? : count; > +static struct block_device *led_bdev_get_bdev(const char *const buf, > + const size_t count, > + const fmode_t mode) > +{ > + struct block_device *bdev; > + char *path; > + > + path = kmemdup(buf, count + 1, GFP_KERNEL); /* include null */ > + if (path == NULL) > + return ERR_PTR(-ENOMEM); > + > + if (path[count - 1] == '\n') > + path[count - 1] = 0; > + > + bdev = blkdev_get_by_path(path, mode, THIS_MODULE); And this is what we should discuss with blk API people. I would really like to find the block device by name as seen in /sys/class/block. > +static void led_bdev_update_lbd(struct led_bdev_lbd *const lbd) > +{ > + unsigned long read_ios, write_ios; > + > + if (lbd->generation != led_bdev_generation) { > + > + read_ios = part_stat_read(lbd->bdev, ios[STAT_READ]); > + > + write_ios = part_stat_read(lbd->bdev, ios[STAT_WRITE]) > + + part_stat_read(lbd->bdev, ios[STAT_DISCARD]) > + + part_stat_read(lbd->bdev, ios[STAT_FLUSH]); Wrong indentation. Also discards and flushed should be separate (I want to be able to set "blink on dicard" and "blink on flush"). > + > + if (lbd->read_ios != read_ios) { > + lbd->read_act = true; > + lbd->read_ios = read_ios; > + } else { > + lbd->read_act = false; > + } lbd->read_act = lbd->read_ios != read_ios; lbd->read_ios = read_ios; > + lbd->generation = led_bdev_generation; I don't like this led_bdev_generation. Can't we somehow do this without a static variable?? Marek