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 Original line Diff line number Diff line
@@ -207,7 +207,7 @@ static ssize_t flash_fault_show(struct device *dev,
		mask <<= 1;
		mask <<= 1;
	}
	}


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


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


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

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

		cancel_work_sync(&led_cdev->set_brightness_work);
		cancel_work_sync(&led_cdev->set_brightness_work);
		led_stop_software_blink(led_cdev);
		led_stop_software_blink(led_cdev);
		if (led_cdev->trigger->deactivate)
		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);
		led_set_brightness(led_cdev, LED_OFF);
	}
	}
	if (trig) {
	if (trig) {
		write_lock_irqsave(&trig->leddev_list_lock, flags);
		spin_lock(&trig->leddev_list_lock);
		list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
		list_add_tail_rcu(&led_cdev->trig_list, &trig->led_cdevs);
		write_unlock_irqrestore(&trig->leddev_list_lock, flags);
		spin_unlock(&trig->leddev_list_lock);
		led_cdev->trigger = trig;
		led_cdev->trigger = trig;


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


	write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
	spin_lock(&led_cdev->trigger->leddev_list_lock);
	list_del(&led_cdev->trig_list);
	list_del_rcu(&led_cdev->trig_list);
	write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
	spin_unlock(&led_cdev->trigger->leddev_list_lock);
	synchronize_rcu();
	led_cdev->trigger = NULL;
	led_cdev->trigger = NULL;
	led_cdev->trigger_data = NULL;
	led_cdev->trigger_data = NULL;
	led_set_brightness(led_cdev, LED_OFF);
	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_classdev *led_cdev;
	struct led_trigger *_trig;
	struct led_trigger *_trig;


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


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


	if (!trig)
	if (!trig)
		return;
		return;


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


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


	if (!trig)
	if (!trig)
		return;
		return;


	read_lock_irqsave(&trig->leddev_list_lock, flags);
	rcu_read_lock();
	list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
	list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) {
		if (oneshot)
		if (oneshot)
			led_blink_set_oneshot(led_cdev, delay_on, delay_off,
			led_blink_set_oneshot(led_cdev, delay_on, delay_off,
					      invert);
					      invert);
		else
		else
			led_blink_set(led_cdev, delay_on, delay_off);
			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,
void led_trigger_blink(struct led_trigger *trig,
+1 −0
Original line number Original line Diff line number Diff line
@@ -64,6 +64,7 @@ config LEDS_TRIGGER_BACKLIGHT


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


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


	/* Link to next registered trigger */
	/* Link to next registered trigger */