aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2010-11-15 04:39:57 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2010-11-18 03:25:45 -0500
commit8ee294cd9def0004887da7f44b80563493b0a097 (patch)
treeb1c7ff0136fa2c359fdf6898a185921dd5a5db92 /drivers
parentce16a474f6305dd631c885ba970d5746e4d5c803 (diff)
Input: serio - convert to common workqueue instead of a thread
Instead of creating an exclusive thread to handle serio events (which happen rarely), let's switch to using common workqueue. With the arrival of concurrency-managed workqueue infrastructure we are not concerned that our callers or callees also using workqueue (no deadlocks anymore) and it should reduce total number of threads in the system. Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/serio/serio.c155
1 files changed, 69 insertions, 86 deletions
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index 405bf214527c..db5b0bca1a1a 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -32,10 +32,9 @@
32#include <linux/module.h> 32#include <linux/module.h>
33#include <linux/serio.h> 33#include <linux/serio.h>
34#include <linux/errno.h> 34#include <linux/errno.h>
35#include <linux/wait.h>
36#include <linux/sched.h> 35#include <linux/sched.h>
37#include <linux/slab.h> 36#include <linux/slab.h>
38#include <linux/kthread.h> 37#include <linux/workqueue.h>
39#include <linux/mutex.h> 38#include <linux/mutex.h>
40 39
41MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 40MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
@@ -44,7 +43,7 @@ MODULE_LICENSE("GPL");
44 43
45/* 44/*
46 * serio_mutex protects entire serio subsystem and is taken every time 45 * serio_mutex protects entire serio subsystem and is taken every time
47 * serio port or driver registrered or unregistered. 46 * serio port or driver registered or unregistered.
48 */ 47 */
49static DEFINE_MUTEX(serio_mutex); 48static DEFINE_MUTEX(serio_mutex);
50 49
@@ -165,58 +164,22 @@ struct serio_event {
165 164
166static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */ 165static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */
167static LIST_HEAD(serio_event_list); 166static LIST_HEAD(serio_event_list);
168static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
169static struct task_struct *serio_task;
170 167
171static int serio_queue_event(void *object, struct module *owner, 168static struct serio_event *serio_get_event(void)
172 enum serio_event_type event_type)
173{ 169{
170 struct serio_event *event = NULL;
174 unsigned long flags; 171 unsigned long flags;
175 struct serio_event *event;
176 int retval = 0;
177 172
178 spin_lock_irqsave(&serio_event_lock, flags); 173 spin_lock_irqsave(&serio_event_lock, flags);
179 174
180 /* 175 if (!list_empty(&serio_event_list)) {
181 * Scan event list for the other events for the same serio port, 176 event = list_first_entry(&serio_event_list,
182 * starting with the most recent one. If event is the same we 177 struct serio_event, node);
183 * do not need add new one. If event is of different type we 178 list_del_init(&event->node);
184 * need to add this event and should not look further because
185 * we need to preseve sequence of distinct events.
186 */
187 list_for_each_entry_reverse(event, &serio_event_list, node) {
188 if (event->object == object) {
189 if (event->type == event_type)
190 goto out;
191 break;
192 }
193 }
194
195 event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
196 if (!event) {
197 pr_err("Not enough memory to queue event %d\n", event_type);
198 retval = -ENOMEM;
199 goto out;
200 }
201
202 if (!try_module_get(owner)) {
203 pr_warning("Can't get module reference, dropping event %d\n",
204 event_type);
205 kfree(event);
206 retval = -EINVAL;
207 goto out;
208 } 179 }
209 180
210 event->type = event_type;
211 event->object = object;
212 event->owner = owner;
213
214 list_add_tail(&event->node, &serio_event_list);
215 wake_up(&serio_wait);
216
217out:
218 spin_unlock_irqrestore(&serio_event_lock, flags); 181 spin_unlock_irqrestore(&serio_event_lock, flags);
219 return retval; 182 return event;
220} 183}
221 184
222static void serio_free_event(struct serio_event *event) 185static void serio_free_event(struct serio_event *event)
@@ -250,25 +213,7 @@ static void serio_remove_duplicate_events(struct serio_event *event)
250 spin_unlock_irqrestore(&serio_event_lock, flags); 213 spin_unlock_irqrestore(&serio_event_lock, flags);
251} 214}
252 215
253 216static void serio_handle_event(struct work_struct *work)
254static struct serio_event *serio_get_event(void)
255{
256 struct serio_event *event = NULL;
257 unsigned long flags;
258
259 spin_lock_irqsave(&serio_event_lock, flags);
260
261 if (!list_empty(&serio_event_list)) {
262 event = list_first_entry(&serio_event_list,
263 struct serio_event, node);
264 list_del_init(&event->node);
265 }
266
267 spin_unlock_irqrestore(&serio_event_lock, flags);
268 return event;
269}
270
271static void serio_handle_event(void)
272{ 217{
273 struct serio_event *event; 218 struct serio_event *event;
274 219
@@ -307,6 +252,59 @@ static void serio_handle_event(void)
307 mutex_unlock(&serio_mutex); 252 mutex_unlock(&serio_mutex);
308} 253}
309 254
255static DECLARE_WORK(serio_event_work, serio_handle_event);
256
257static int serio_queue_event(void *object, struct module *owner,
258 enum serio_event_type event_type)
259{
260 unsigned long flags;
261 struct serio_event *event;
262 int retval = 0;
263
264 spin_lock_irqsave(&serio_event_lock, flags);
265
266 /*
267 * Scan event list for the other events for the same serio port,
268 * starting with the most recent one. If event is the same we
269 * do not need add new one. If event is of different type we
270 * need to add this event and should not look further because
271 * we need to preseve sequence of distinct events.
272 */
273 list_for_each_entry_reverse(event, &serio_event_list, node) {
274 if (event->object == object) {
275 if (event->type == event_type)
276 goto out;
277 break;
278 }
279 }
280
281 event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
282 if (!event) {
283 pr_err("Not enough memory to queue event %d\n", event_type);
284 retval = -ENOMEM;
285 goto out;
286 }
287
288 if (!try_module_get(owner)) {
289 pr_warning("Can't get module reference, dropping event %d\n",
290 event_type);
291 kfree(event);
292 retval = -EINVAL;
293 goto out;
294 }
295
296 event->type = event_type;
297 event->object = object;
298 event->owner = owner;
299
300 list_add_tail(&event->node, &serio_event_list);
301 schedule_work(&serio_event_work);
302
303out:
304 spin_unlock_irqrestore(&serio_event_lock, flags);
305 return retval;
306}
307
310/* 308/*
311 * Remove all events that have been submitted for a given 309 * Remove all events that have been submitted for a given
312 * object, be it serio port or driver. 310 * object, be it serio port or driver.
@@ -356,18 +354,6 @@ static struct serio *serio_get_pending_child(struct serio *parent)
356 return child; 354 return child;
357} 355}
358 356
359static int serio_thread(void *nothing)
360{
361 do {
362 serio_handle_event();
363 wait_event_interruptible(serio_wait,
364 kthread_should_stop() || !list_empty(&serio_event_list));
365 } while (!kthread_should_stop());
366
367 return 0;
368}
369
370
371/* 357/*
372 * Serio port operations 358 * Serio port operations
373 */ 359 */
@@ -1040,21 +1026,18 @@ static int __init serio_init(void)
1040 return error; 1026 return error;
1041 } 1027 }
1042 1028
1043 serio_task = kthread_run(serio_thread, NULL, "kseriod");
1044 if (IS_ERR(serio_task)) {
1045 bus_unregister(&serio_bus);
1046 error = PTR_ERR(serio_task);
1047 pr_err("Failed to start kseriod, error: %d\n", error);
1048 return error;
1049 }
1050
1051 return 0; 1029 return 0;
1052} 1030}
1053 1031
1054static void __exit serio_exit(void) 1032static void __exit serio_exit(void)
1055{ 1033{
1056 bus_unregister(&serio_bus); 1034 bus_unregister(&serio_bus);
1057 kthread_stop(serio_task); 1035
1036 /*
1037 * There should not be any outstanding events but work may
1038 * still be scheduled so simply cancel it.
1039 */
1040 cancel_work_sync(&serio_event_work);
1058} 1041}
1059 1042
1060subsys_initcall(serio_init); 1043subsys_initcall(serio_init);