[PATCH AUTOSEL 6.18] leds: core: Fix race condition for software blink
From: Sasha Levin
Date: Mon Aug 31 2026 - 11:04:52 EST
From: Craig McQueen <craig@xxxxxxxxxx>
[ Upstream commit f92135f100669b508dd62b424ab20bcb33494c79 ]
led_set_brightness() function: Change handling of software blink to
avoid race conditions when stopping blink and setting brightness.
Triggers may call led_set_brightness(LED_OFF),
led_set_brightness(LED_FULL) in quick succession to disable blinking and
turn the LED on. If the delayed work task has not yet disabled blinking
by the time the second call occurs, then the brightness also needs to be
changed in the delayed work task.
Signed-off-by: Craig McQueen <craig@xxxxxxxxxx>
Link: https://patch.msgid.link/20260423113638.2079302-1-craig@xxxxxxxxxx
Signed-off-by: Lee Jones <lee@xxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
LLM Generated explanations, may be completely bogus:
The background searches finished and line up with the earlier analysis:
- **Commit lookup:** `f92135f100669` (`leds: core: Fix race condition
for software blink`) is in mainline via the `leds-next-7.2` merge, but
it is **not** in the current `6.18.44` checkout — so it is a valid
backport candidate for this tree.
- **Related history:** The prerequisite fixes are already here
(`fa15d8c69238b`, `2c70953b6f535`, ordered LED workqueue from
`32360bf6a5d40`).
That supports the **YES** verdict: the bug exists in v6.18.44, the fix
is small and upstream, and the needed infrastructure is already present.
drivers/leds/led-core.c | 33 ++++++++++++++++++++-------------
1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 59473f286b31f..eeb36e50c285c 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -304,24 +304,31 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink);
void led_set_brightness(struct led_classdev *led_cdev, unsigned int brightness)
{
- /*
- * If software blink is active, delay brightness setting
- * until the next timer tick.
- */
- if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
+ if (brightness) {
/*
- * If we need to disable soft blinking delegate this to the
- * work queue task to avoid problems in case we are called
- * from hard irq context.
+ * If software blink disable is pending, also queue brightness setting.
+ * If software blink is active, delay brightness setting
+ * until the next timer tick.
*/
- if (!brightness) {
- set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
+ if (test_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags) ||
+ test_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
+ led_cdev->delayed_set_value = brightness;
+ set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
queue_work(led_cdev->wq, &led_cdev->set_brightness_work);
- } else {
- set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
- &led_cdev->work_flags);
+ return;
+ } else if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
led_cdev->new_blink_brightness = brightness;
+ set_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags);
+ return;
}
+ } else if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
+ /*
+ * If we need to disable soft blinking delegate this to the
+ * work queue task to avoid problems in case we are called
+ * from hard irq context.
+ */
+ set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
+ queue_work(led_cdev->wq, &led_cdev->set_brightness_work);
return;
}
--
2.53.0