Commit 4dee0606 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull LED updates from Pavel Machek:
 "Johannes pointed out that locking is still problematic with triggers
  list, attempt to solve that by using RCU"

* tag 'leds-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds:
  leds: trigger: Disable CPU trigger on PREEMPT_RT
  leds: trigger: use RCU to protect the led_cdevs list
  led-class-flash: fix -Wrestrict warning
parents 73d21a35 97b31c1f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -207,7 +207,7 @@ static ssize_t flash_fault_show(struct device *dev,
		mask <<= 1;
	}

	return sprintf(buf, "%s\n", buf);
	return strlen(strcat(buf, "\n"));
}
static DEVICE_ATTR_RO(flash_fault);

+21 −20
Original line number Diff line number Diff line
@@ -157,7 +157,6 @@ EXPORT_SYMBOL_GPL(led_trigger_read);
/* Caller must ensure led_cdev->trigger_lock held */
int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
{
	unsigned long flags;
	char *event = NULL;
	char *envp[2];
	const char *name;
@@ -171,10 +170,13 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)

	/* Remove any existing trigger */
	if (led_cdev->trigger) {
		write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
		list_del(&led_cdev->trig_list);
		write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
			flags);
		spin_lock(&led_cdev->trigger->leddev_list_lock);
		list_del_rcu(&led_cdev->trig_list);
		spin_unlock(&led_cdev->trigger->leddev_list_lock);

		/* ensure it's no longer visible on the led_cdevs list */
		synchronize_rcu();

		cancel_work_sync(&led_cdev->set_brightness_work);
		led_stop_software_blink(led_cdev);
		if (led_cdev->trigger->deactivate)
@@ -186,9 +188,9 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
		led_set_brightness(led_cdev, LED_OFF);
	}
	if (trig) {
		write_lock_irqsave(&trig->leddev_list_lock, flags);
		list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
		write_unlock_irqrestore(&trig->leddev_list_lock, flags);
		spin_lock(&trig->leddev_list_lock);
		list_add_tail_rcu(&led_cdev->trig_list, &trig->led_cdevs);
		spin_unlock(&trig->leddev_list_lock);
		led_cdev->trigger = trig;

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

	write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
	list_del(&led_cdev->trig_list);
	write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
	spin_lock(&led_cdev->trigger->leddev_list_lock);
	list_del_rcu(&led_cdev->trig_list);
	spin_unlock(&led_cdev->trigger->leddev_list_lock);
	synchronize_rcu();
	led_cdev->trigger = NULL;
	led_cdev->trigger_data = NULL;
	led_set_brightness(led_cdev, LED_OFF);
@@ -285,7 +288,7 @@ int led_trigger_register(struct led_trigger *trig)
	struct led_classdev *led_cdev;
	struct led_trigger *_trig;

	rwlock_init(&trig->leddev_list_lock);
	spin_lock_init(&trig->leddev_list_lock);
	INIT_LIST_HEAD(&trig->led_cdevs);

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

	if (!trig)
		return;

	read_lock_irqsave(&trig->leddev_list_lock, flags);
	list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list)
	rcu_read_lock();
	list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list)
		led_set_brightness(led_cdev, brightness);
	read_unlock_irqrestore(&trig->leddev_list_lock, flags);
	rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(led_trigger_event);

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

	if (!trig)
		return;

	read_lock_irqsave(&trig->leddev_list_lock, flags);
	list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
	rcu_read_lock();
	list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) {
		if (oneshot)
			led_blink_set_oneshot(led_cdev, delay_on, delay_off,
					      invert);
		else
			led_blink_set(led_cdev, delay_on, delay_off);
	}
	read_unlock_irqrestore(&trig->leddev_list_lock, flags);
	rcu_read_unlock();
}

void led_trigger_blink(struct led_trigger *trig,
+1 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ config LEDS_TRIGGER_BACKLIGHT

config LEDS_TRIGGER_CPU
	bool "LED CPU Trigger"
	depends on !PREEMPT_RT
	help
	  This allows LEDs to be controlled by active CPUs. This shows
	  the active CPUs across an array of LEDs so you can see which
+1 −1
Original line number Diff line number Diff line
@@ -360,7 +360,7 @@ struct led_trigger {
	struct led_hw_trigger_type *trigger_type;

	/* LEDs under control by this trigger (for simple triggers) */
	rwlock_t	  leddev_list_lock;
	spinlock_t	  leddev_list_lock;
	struct list_head  led_cdevs;

	/* Link to next registered trigger */