[PATCH v5 08/12] leds: trigger: Do not attach trigger to a removing LED

From: Rong Zhang

Date: Thu Aug 20 2026 - 13:29:07 EST


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 <ikepanhc@xxxxxxxxx>
Signed-off-by: Rong Zhang <i@xxxxxxxx>
---
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/20260802-leds-trigger-hw-changed-v4-0-f97e2ca976fe@xxxxxxxx?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/20260719-leds-trigger-hw-changed-v3-0-5fb55722e36e@xxxxxxxx?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