leds: Fix led trigger locking bugs

Convert part of the led trigger core from rw spinlocks to rw
semaphores. We're calling functions which can sleep from invalid
contexts otherwise. Fixes bug #9264.

Signed-off-by: Richard Purdie <rpurdie@rpsys.net>
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 575368c..0bdb786 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -19,13 +19,14 @@
 #include <linux/device.h>
 #include <linux/sysdev.h>
 #include <linux/timer.h>
+#include <linux/rwsem.h>
 #include <linux/leds.h>
 #include "leds.h"
 
 /*
  * Nests outside led_cdev->trigger_lock
  */
-static DEFINE_RWLOCK(triggers_list_lock);
+static DECLARE_RWSEM(triggers_list_lock);
 static LIST_HEAD(trigger_list);
 
 ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
@@ -44,24 +45,24 @@
 		trigger_name[len - 1] = '\0';
 
 	if (!strcmp(trigger_name, "none")) {
-		write_lock(&led_cdev->trigger_lock);
+		down_write(&led_cdev->trigger_lock);
 		led_trigger_set(led_cdev, NULL);
-		write_unlock(&led_cdev->trigger_lock);
+		up_write(&led_cdev->trigger_lock);
 		return count;
 	}
 
-	read_lock(&triggers_list_lock);
+	down_read(&triggers_list_lock);
 	list_for_each_entry(trig, &trigger_list, next_trig) {
 		if (!strcmp(trigger_name, trig->name)) {
-			write_lock(&led_cdev->trigger_lock);
+			down_write(&led_cdev->trigger_lock);
 			led_trigger_set(led_cdev, trig);
-			write_unlock(&led_cdev->trigger_lock);
+			up_write(&led_cdev->trigger_lock);
 
-			read_unlock(&triggers_list_lock);
+			up_read(&triggers_list_lock);
 			return count;
 		}
 	}
-	read_unlock(&triggers_list_lock);
+	up_read(&triggers_list_lock);
 
 	return -EINVAL;
 }
@@ -74,8 +75,8 @@
 	struct led_trigger *trig;
 	int len = 0;
 
-	read_lock(&triggers_list_lock);
-	read_lock(&led_cdev->trigger_lock);
+	down_read(&triggers_list_lock);
+	down_read(&led_cdev->trigger_lock);
 
 	if (!led_cdev->trigger)
 		len += sprintf(buf+len, "[none] ");
@@ -89,8 +90,8 @@
 		else
 			len += sprintf(buf+len, "%s ", trig->name);
 	}
-	read_unlock(&led_cdev->trigger_lock);
-	read_unlock(&triggers_list_lock);
+	up_read(&led_cdev->trigger_lock);
+	up_read(&triggers_list_lock);
 
 	len += sprintf(len+buf, "\n");
 	return len;
@@ -145,14 +146,14 @@
 	if (!led_cdev->default_trigger)
 		return;
 
-	read_lock(&triggers_list_lock);
-	write_lock(&led_cdev->trigger_lock);
+	down_read(&triggers_list_lock);
+	down_write(&led_cdev->trigger_lock);
 	list_for_each_entry(trig, &trigger_list, next_trig) {
 		if (!strcmp(led_cdev->default_trigger, trig->name))
 			led_trigger_set(led_cdev, trig);
 	}
-	write_unlock(&led_cdev->trigger_lock);
-	read_unlock(&triggers_list_lock);
+	up_write(&led_cdev->trigger_lock);
+	up_read(&triggers_list_lock);
 }
 
 int led_trigger_register(struct led_trigger *trigger)
@@ -163,18 +164,18 @@
 	INIT_LIST_HEAD(&trigger->led_cdevs);
 
 	/* Add to the list of led triggers */
-	write_lock(&triggers_list_lock);
+	down_write(&triggers_list_lock);
 	list_add_tail(&trigger->next_trig, &trigger_list);
-	write_unlock(&triggers_list_lock);
+	up_write(&triggers_list_lock);
 
 	/* Register with any LEDs that have this as a default trigger */
 	read_lock(&leds_list_lock);
 	list_for_each_entry(led_cdev, &leds_list, node) {
-		write_lock(&led_cdev->trigger_lock);
+		down_write(&led_cdev->trigger_lock);
 		if (!led_cdev->trigger && led_cdev->default_trigger &&
 			    !strcmp(led_cdev->default_trigger, trigger->name))
 			led_trigger_set(led_cdev, trigger);
-		write_unlock(&led_cdev->trigger_lock);
+		up_write(&led_cdev->trigger_lock);
 	}
 	read_unlock(&leds_list_lock);
 
@@ -206,17 +207,17 @@
 	struct led_classdev *led_cdev;
 
 	/* Remove from the list of led triggers */
-	write_lock(&triggers_list_lock);
+	down_write(&triggers_list_lock);
 	list_del(&trigger->next_trig);
-	write_unlock(&triggers_list_lock);
+	up_write(&triggers_list_lock);
 
 	/* Remove anyone actively using this trigger */
 	read_lock(&leds_list_lock);
 	list_for_each_entry(led_cdev, &leds_list, node) {
-		write_lock(&led_cdev->trigger_lock);
+		down_write(&led_cdev->trigger_lock);
 		if (led_cdev->trigger == trigger)
 			led_trigger_set(led_cdev, NULL);
-		write_unlock(&led_cdev->trigger_lock);
+		up_write(&led_cdev->trigger_lock);
 	}
 	read_unlock(&leds_list_lock);
 }