Skip to content

Commit 2a5a8fa

Browse files
jmberg-intelpavelmachek
authored andcommitted
leds: trigger: use RCU to protect the led_cdevs list
Even with the previous commit 27af8e2 ("leds: trigger: fix potential deadlock with libata") to this file, we still get lockdep unhappy, and Boqun explained the report here: https://lore.kernel.org/r/YNA+d1X4UkoQ7g8a@boqun-archlinux Effectively, this means that the read_lock_irqsave() isn't enough here because another CPU might be trying to do a write lock, and thus block the readers. This is all pretty messy, but it doesn't seem right that the LEDs framework imposes some locking requirements on users, in particular we'd have to make the spinlock in the iwlwifi driver always disable IRQs, even if we don't need that for any other reason, just to avoid this deadlock. Since writes to the led_cdevs list are rare (and are done by userspace), just switch the list to RCU. This costs a synchronize_rcu() at removal time so we can ensure things are correct, but that seems like a small price to pay for getting lock-free iterations and no deadlocks (nor any locking requirements imposed on users.) Signed-off-by: Johannes Berg <[email protected]> Signed-off-by: Pavel Machek <[email protected]>
1 parent 811b544 commit 2a5a8fa

File tree

2 files changed

+22
-21
lines changed

2 files changed

+22
-21
lines changed

drivers/leds/led-triggers.c

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ EXPORT_SYMBOL_GPL(led_trigger_read);
157157
/* Caller must ensure led_cdev->trigger_lock held */
158158
int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
159159
{
160-
unsigned long flags;
161160
char *event = NULL;
162161
char *envp[2];
163162
const char *name;
@@ -171,10 +170,13 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
171170

172171
/* Remove any existing trigger */
173172
if (led_cdev->trigger) {
174-
write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
175-
list_del(&led_cdev->trig_list);
176-
write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
177-
flags);
173+
spin_lock(&led_cdev->trigger->leddev_list_lock);
174+
list_del_rcu(&led_cdev->trig_list);
175+
spin_unlock(&led_cdev->trigger->leddev_list_lock);
176+
177+
/* ensure it's no longer visible on the led_cdevs list */
178+
synchronize_rcu();
179+
178180
cancel_work_sync(&led_cdev->set_brightness_work);
179181
led_stop_software_blink(led_cdev);
180182
if (led_cdev->trigger->deactivate)
@@ -186,9 +188,9 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
186188
led_set_brightness(led_cdev, LED_OFF);
187189
}
188190
if (trig) {
189-
write_lock_irqsave(&trig->leddev_list_lock, flags);
190-
list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
191-
write_unlock_irqrestore(&trig->leddev_list_lock, flags);
191+
spin_lock(&trig->leddev_list_lock);
192+
list_add_tail_rcu(&led_cdev->trig_list, &trig->led_cdevs);
193+
spin_unlock(&trig->leddev_list_lock);
192194
led_cdev->trigger = trig;
193195

194196
if (trig->activate)
@@ -223,9 +225,10 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
223225
trig->deactivate(led_cdev);
224226
err_activate:
225227

226-
write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
227-
list_del(&led_cdev->trig_list);
228-
write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
228+
spin_lock(&led_cdev->trigger->leddev_list_lock);
229+
list_del_rcu(&led_cdev->trig_list);
230+
spin_unlock(&led_cdev->trigger->leddev_list_lock);
231+
synchronize_rcu();
229232
led_cdev->trigger = NULL;
230233
led_cdev->trigger_data = NULL;
231234
led_set_brightness(led_cdev, LED_OFF);
@@ -285,7 +288,7 @@ int led_trigger_register(struct led_trigger *trig)
285288
struct led_classdev *led_cdev;
286289
struct led_trigger *_trig;
287290

288-
rwlock_init(&trig->leddev_list_lock);
291+
spin_lock_init(&trig->leddev_list_lock);
289292
INIT_LIST_HEAD(&trig->led_cdevs);
290293

291294
down_write(&triggers_list_lock);
@@ -378,15 +381,14 @@ void led_trigger_event(struct led_trigger *trig,
378381
enum led_brightness brightness)
379382
{
380383
struct led_classdev *led_cdev;
381-
unsigned long flags;
382384

383385
if (!trig)
384386
return;
385387

386-
read_lock_irqsave(&trig->leddev_list_lock, flags);
387-
list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list)
388+
rcu_read_lock();
389+
list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list)
388390
led_set_brightness(led_cdev, brightness);
389-
read_unlock_irqrestore(&trig->leddev_list_lock, flags);
391+
rcu_read_unlock();
390392
}
391393
EXPORT_SYMBOL_GPL(led_trigger_event);
392394

@@ -397,20 +399,19 @@ static void led_trigger_blink_setup(struct led_trigger *trig,
397399
int invert)
398400
{
399401
struct led_classdev *led_cdev;
400-
unsigned long flags;
401402

402403
if (!trig)
403404
return;
404405

405-
read_lock_irqsave(&trig->leddev_list_lock, flags);
406-
list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
406+
rcu_read_lock();
407+
list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) {
407408
if (oneshot)
408409
led_blink_set_oneshot(led_cdev, delay_on, delay_off,
409410
invert);
410411
else
411412
led_blink_set(led_cdev, delay_on, delay_off);
412413
}
413-
read_unlock_irqrestore(&trig->leddev_list_lock, flags);
414+
rcu_read_unlock();
414415
}
415416

416417
void led_trigger_blink(struct led_trigger *trig,

include/linux/leds.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ struct led_trigger {
360360
struct led_hw_trigger_type *trigger_type;
361361

362362
/* LEDs under control by this trigger (for simple triggers) */
363-
rwlock_t leddev_list_lock;
363+
spinlock_t leddev_list_lock;
364364
struct list_head led_cdevs;
365365

366366
/* Link to next registered trigger */

0 commit comments

Comments
 (0)