[PATCH v5 08/12] leds: trigger: Do not attach trigger to a removing LED
Rong Zhang <[email protected]>
| Newsgroups | org.kernel.vger.platform-driver-x86,dev.linux.lists.chrome-platform,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-leds,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
Currently the LED trigger core knows little about an LED's removal as the LED_UNREGISTERING flag is set too late. This could lead to a race condition where a trigger may be attached to a removing LED right after led_classdev_unregister() has removed the previous trigger. Set the flag at the very beginning of led_classdev_unregister(), and check the flag before attaching a trigger. Acked-by: Ike Panhc <[email protected]> Signed-off-by: Rong Zhang <[email protected]> --- Changes in v5: - Address a concern from Sashiko: - Non-atomic update of led_cdev->flags causes a data race that can drop the LED_UNREGISTERING flag - Fix it by synchronize the update with trigger_lock - https://sashiko.dev/#/patchset/[email protected]?part=8 - Update "Changes in v4" to include more details Changes in v4: - New patch in the series to address a concern from Sashiko - Without it, the following patch may expose a race condition if a future LED driver synchronizes led_classdev_unregister() and led_trigger_notify_hw_control_changed() poorly. The ideapad-laptop patches in the series doesn't expose the race condition though - In theory, the race condition can also be triggered by a userspace program writing to the "trigger" attribute right before device_unregister() is called - https://sashiko.dev/#/patchset/[email protected]?part=8 --- drivers/leds/led-class.c | 5 +++-- drivers/leds/led-triggers.c | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 2460fcf0c469..771a6e6c659b 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -633,12 +633,13 @@ void led_classdev_unregister(struct led_classdev *led_cdev) #ifdef CONFIG_LEDS_TRIGGERS down_write(&led_cdev->trigger_lock); + led_cdev->flags |= LED_UNREGISTERING; if (led_cdev->trigger) led_trigger_set(led_cdev, NULL); up_write(&led_cdev->trigger_lock); -#endif - +#else led_cdev->flags |= LED_UNREGISTERING; +#endif /* Stop blinking */ led_stop_software_blink(led_cdev); diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index cb49a02a8b3c..64bc95d0bfeb 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -200,6 +200,9 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) const char *name; int ret; + if ((led_cdev->flags & LED_UNREGISTERING) && trig) + return -ENODEV; + if (!led_cdev->trigger && !trig) return 0; -- 2.55.0