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

Rong Zhang <[email protected]>
Newsgroups dev.linux.lists.chrome-platform,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-leds,org.kernel.vger.netdev,org.kernel.vger.platform-driver-x86
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.