aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Purdie <rpurdie@rpsys.net>2007-11-10 08:29:04 -0500
committerRichard Purdie <rpurdie@rpsys.net>2007-12-07 04:06:53 -0500
commitdc47206e552c0850ad11f7e9a1fca0a3c92f5d65 (patch)
tree01188f5ca89d295009a618e1be6c5cea2926a522
parentf194d132e4971111f85c18c96067acffb13cee6d (diff)
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>
-rw-r--r--drivers/leds/led-class.c6
-rw-r--r--drivers/leds/led-triggers.c49
-rw-r--r--include/linux/leds.h3
3 files changed, 30 insertions, 28 deletions
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 4211293ce862..ba8b04b03b9f 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -111,7 +111,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
111 write_unlock(&leds_list_lock); 111 write_unlock(&leds_list_lock);
112 112
113#ifdef CONFIG_LEDS_TRIGGERS 113#ifdef CONFIG_LEDS_TRIGGERS
114 rwlock_init(&led_cdev->trigger_lock); 114 init_rwsem(&led_cdev->trigger_lock);
115 115
116 rc = device_create_file(led_cdev->dev, &dev_attr_trigger); 116 rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
117 if (rc) 117 if (rc)
@@ -147,10 +147,10 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
147 device_remove_file(led_cdev->dev, &dev_attr_brightness); 147 device_remove_file(led_cdev->dev, &dev_attr_brightness);
148#ifdef CONFIG_LEDS_TRIGGERS 148#ifdef CONFIG_LEDS_TRIGGERS
149 device_remove_file(led_cdev->dev, &dev_attr_trigger); 149 device_remove_file(led_cdev->dev, &dev_attr_trigger);
150 write_lock(&led_cdev->trigger_lock); 150 down_write(&led_cdev->trigger_lock);
151 if (led_cdev->trigger) 151 if (led_cdev->trigger)
152 led_trigger_set(led_cdev, NULL); 152 led_trigger_set(led_cdev, NULL);
153 write_unlock(&led_cdev->trigger_lock); 153 up_write(&led_cdev->trigger_lock);
154#endif 154#endif
155 155
156 device_unregister(led_cdev->dev); 156 device_unregister(led_cdev->dev);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 575368c2b100..0bdb786210b1 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -19,13 +19,14 @@
19#include <linux/device.h> 19#include <linux/device.h>
20#include <linux/sysdev.h> 20#include <linux/sysdev.h>
21#include <linux/timer.h> 21#include <linux/timer.h>
22#include <linux/rwsem.h>
22#include <linux/leds.h> 23#include <linux/leds.h>
23#include "leds.h" 24#include "leds.h"
24 25
25/* 26/*
26 * Nests outside led_cdev->trigger_lock 27 * Nests outside led_cdev->trigger_lock
27 */ 28 */
28static DEFINE_RWLOCK(triggers_list_lock); 29static DECLARE_RWSEM(triggers_list_lock);
29static LIST_HEAD(trigger_list); 30static LIST_HEAD(trigger_list);
30 31
31ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, 32ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
@@ -44,24 +45,24 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
44 trigger_name[len - 1] = '\0'; 45 trigger_name[len - 1] = '\0';
45 46
46 if (!strcmp(trigger_name, "none")) { 47 if (!strcmp(trigger_name, "none")) {
47 write_lock(&led_cdev->trigger_lock); 48 down_write(&led_cdev->trigger_lock);
48 led_trigger_set(led_cdev, NULL); 49 led_trigger_set(led_cdev, NULL);
49 write_unlock(&led_cdev->trigger_lock); 50 up_write(&led_cdev->trigger_lock);
50 return count; 51 return count;
51 } 52 }
52 53
53 read_lock(&triggers_list_lock); 54 down_read(&triggers_list_lock);
54 list_for_each_entry(trig, &trigger_list, next_trig) { 55 list_for_each_entry(trig, &trigger_list, next_trig) {
55 if (!strcmp(trigger_name, trig->name)) { 56 if (!strcmp(trigger_name, trig->name)) {
56 write_lock(&led_cdev->trigger_lock); 57 down_write(&led_cdev->trigger_lock);
57 led_trigger_set(led_cdev, trig); 58 led_trigger_set(led_cdev, trig);
58 write_unlock(&led_cdev->trigger_lock); 59 up_write(&led_cdev->trigger_lock);
59 60
60 read_unlock(&triggers_list_lock); 61 up_read(&triggers_list_lock);
61 return count; 62 return count;
62 } 63 }
63 } 64 }
64 read_unlock(&triggers_list_lock); 65 up_read(&triggers_list_lock);
65 66
66 return -EINVAL; 67 return -EINVAL;
67} 68}
@@ -74,8 +75,8 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
74 struct led_trigger *trig; 75 struct led_trigger *trig;
75 int len = 0; 76 int len = 0;
76 77
77 read_lock(&triggers_list_lock); 78 down_read(&triggers_list_lock);
78 read_lock(&led_cdev->trigger_lock); 79 down_read(&led_cdev->trigger_lock);
79 80
80 if (!led_cdev->trigger) 81 if (!led_cdev->trigger)
81 len += sprintf(buf+len, "[none] "); 82 len += sprintf(buf+len, "[none] ");
@@ -89,8 +90,8 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
89 else 90 else
90 len += sprintf(buf+len, "%s ", trig->name); 91 len += sprintf(buf+len, "%s ", trig->name);
91 } 92 }
92 read_unlock(&led_cdev->trigger_lock); 93 up_read(&led_cdev->trigger_lock);
93 read_unlock(&triggers_list_lock); 94 up_read(&triggers_list_lock);
94 95
95 len += sprintf(len+buf, "\n"); 96 len += sprintf(len+buf, "\n");
96 return len; 97 return len;
@@ -145,14 +146,14 @@ void led_trigger_set_default(struct led_classdev *led_cdev)
145 if (!led_cdev->default_trigger) 146 if (!led_cdev->default_trigger)
146 return; 147 return;
147 148
148 read_lock(&triggers_list_lock); 149 down_read(&triggers_list_lock);
149 write_lock(&led_cdev->trigger_lock); 150 down_write(&led_cdev->trigger_lock);
150 list_for_each_entry(trig, &trigger_list, next_trig) { 151 list_for_each_entry(trig, &trigger_list, next_trig) {
151 if (!strcmp(led_cdev->default_trigger, trig->name)) 152 if (!strcmp(led_cdev->default_trigger, trig->name))
152 led_trigger_set(led_cdev, trig); 153 led_trigger_set(led_cdev, trig);
153 } 154 }
154 write_unlock(&led_cdev->trigger_lock); 155 up_write(&led_cdev->trigger_lock);
155 read_unlock(&triggers_list_lock); 156 up_read(&triggers_list_lock);
156} 157}
157 158
158int led_trigger_register(struct led_trigger *trigger) 159int led_trigger_register(struct led_trigger *trigger)
@@ -163,18 +164,18 @@ int led_trigger_register(struct led_trigger *trigger)
163 INIT_LIST_HEAD(&trigger->led_cdevs); 164 INIT_LIST_HEAD(&trigger->led_cdevs);
164 165
165 /* Add to the list of led triggers */ 166 /* Add to the list of led triggers */
166 write_lock(&triggers_list_lock); 167 down_write(&triggers_list_lock);
167 list_add_tail(&trigger->next_trig, &trigger_list); 168 list_add_tail(&trigger->next_trig, &trigger_list);
168 write_unlock(&triggers_list_lock); 169 up_write(&triggers_list_lock);
169 170
170 /* Register with any LEDs that have this as a default trigger */ 171 /* Register with any LEDs that have this as a default trigger */
171 read_lock(&leds_list_lock); 172 read_lock(&leds_list_lock);
172 list_for_each_entry(led_cdev, &leds_list, node) { 173 list_for_each_entry(led_cdev, &leds_list, node) {
173 write_lock(&led_cdev->trigger_lock); 174 down_write(&led_cdev->trigger_lock);
174 if (!led_cdev->trigger && led_cdev->default_trigger && 175 if (!led_cdev->trigger && led_cdev->default_trigger &&
175 !strcmp(led_cdev->default_trigger, trigger->name)) 176 !strcmp(led_cdev->default_trigger, trigger->name))
176 led_trigger_set(led_cdev, trigger); 177 led_trigger_set(led_cdev, trigger);
177 write_unlock(&led_cdev->trigger_lock); 178 up_write(&led_cdev->trigger_lock);
178 } 179 }
179 read_unlock(&leds_list_lock); 180 read_unlock(&leds_list_lock);
180 181
@@ -206,17 +207,17 @@ void led_trigger_unregister(struct led_trigger *trigger)
206 struct led_classdev *led_cdev; 207 struct led_classdev *led_cdev;
207 208
208 /* Remove from the list of led triggers */ 209 /* Remove from the list of led triggers */
209 write_lock(&triggers_list_lock); 210 down_write(&triggers_list_lock);
210 list_del(&trigger->next_trig); 211 list_del(&trigger->next_trig);
211 write_unlock(&triggers_list_lock); 212 up_write(&triggers_list_lock);
212 213
213 /* Remove anyone actively using this trigger */ 214 /* Remove anyone actively using this trigger */
214 read_lock(&leds_list_lock); 215 read_lock(&leds_list_lock);
215 list_for_each_entry(led_cdev, &leds_list, node) { 216 list_for_each_entry(led_cdev, &leds_list, node) {
216 write_lock(&led_cdev->trigger_lock); 217 down_write(&led_cdev->trigger_lock);
217 if (led_cdev->trigger == trigger) 218 if (led_cdev->trigger == trigger)
218 led_trigger_set(led_cdev, NULL); 219 led_trigger_set(led_cdev, NULL);
219 write_unlock(&led_cdev->trigger_lock); 220 up_write(&led_cdev->trigger_lock);
220 } 221 }
221 read_unlock(&leds_list_lock); 222 read_unlock(&leds_list_lock);
222} 223}
diff --git a/include/linux/leds.h b/include/linux/leds.h
index dc1178f6184b..b4130ff58d0c 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -14,6 +14,7 @@
14 14
15#include <linux/list.h> 15#include <linux/list.h>
16#include <linux/spinlock.h> 16#include <linux/spinlock.h>
17#include <linux/rwsem.h>
17 18
18struct device; 19struct device;
19/* 20/*
@@ -43,7 +44,7 @@ struct led_classdev {
43 44
44#ifdef CONFIG_LEDS_TRIGGERS 45#ifdef CONFIG_LEDS_TRIGGERS
45 /* Protects the trigger data below */ 46 /* Protects the trigger data below */
46 rwlock_t trigger_lock; 47 struct rw_semaphore trigger_lock;
47 48
48 struct led_trigger *trigger; 49 struct led_trigger *trigger;
49 struct list_head trig_list; 50 struct list_head trig_list;