diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-11-15 04:39:57 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-11-18 03:25:49 -0500 |
commit | c44f242064093e640a068741b05ee6acdd49bc22 (patch) | |
tree | 58279c066e644a5da94726f1dc5eb1a7b1e2affe /drivers/input | |
parent | 8ee294cd9def0004887da7f44b80563493b0a097 (diff) |
Input: gameport - convert to use common workqueue instead of a thread
Instead of creating an exclusive thread to handle gameport events (which
happen rarely), let's switch to 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/input')
-rw-r--r-- | drivers/input/gameport/gameport.c | 152 |
1 files changed, 68 insertions, 84 deletions
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 46239e47a260..dbf741c95835 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c | |||
@@ -18,13 +18,11 @@ | |||
18 | #include <linux/ioport.h> | 18 | #include <linux/ioport.h> |
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/gameport.h> | 20 | #include <linux/gameport.h> |
21 | #include <linux/wait.h> | ||
22 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
23 | #include <linux/delay.h> | 22 | #include <linux/delay.h> |
24 | #include <linux/kthread.h> | 23 | #include <linux/workqueue.h> |
25 | #include <linux/sched.h> /* HZ */ | 24 | #include <linux/sched.h> /* HZ */ |
26 | #include <linux/mutex.h> | 25 | #include <linux/mutex.h> |
27 | #include <linux/freezer.h> | ||
28 | 26 | ||
29 | /*#include <asm/io.h>*/ | 27 | /*#include <asm/io.h>*/ |
30 | 28 | ||
@@ -234,58 +232,22 @@ struct gameport_event { | |||
234 | 232 | ||
235 | static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */ | 233 | static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */ |
236 | static LIST_HEAD(gameport_event_list); | 234 | static LIST_HEAD(gameport_event_list); |
237 | static DECLARE_WAIT_QUEUE_HEAD(gameport_wait); | ||
238 | static struct task_struct *gameport_task; | ||
239 | 235 | ||
240 | static int gameport_queue_event(void *object, struct module *owner, | 236 | static struct gameport_event *gameport_get_event(void) |
241 | enum gameport_event_type event_type) | ||
242 | { | 237 | { |
238 | struct gameport_event *event = NULL; | ||
243 | unsigned long flags; | 239 | unsigned long flags; |
244 | struct gameport_event *event; | ||
245 | int retval = 0; | ||
246 | 240 | ||
247 | spin_lock_irqsave(&gameport_event_lock, flags); | 241 | spin_lock_irqsave(&gameport_event_lock, flags); |
248 | 242 | ||
249 | /* | 243 | if (!list_empty(&gameport_event_list)) { |
250 | * Scan event list for the other events for the same gameport port, | 244 | event = list_first_entry(&gameport_event_list, |
251 | * starting with the most recent one. If event is the same we | 245 | struct gameport_event, node); |
252 | * do not need add new one. If event is of different type we | 246 | list_del_init(&event->node); |
253 | * need to add this event and should not look further because | ||
254 | * we need to preseve sequence of distinct events. | ||
255 | */ | ||
256 | list_for_each_entry_reverse(event, &gameport_event_list, node) { | ||
257 | if (event->object == object) { | ||
258 | if (event->type == event_type) | ||
259 | goto out; | ||
260 | break; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC); | ||
265 | if (!event) { | ||
266 | pr_err("Not enough memory to queue event %d\n", event_type); | ||
267 | retval = -ENOMEM; | ||
268 | goto out; | ||
269 | } | ||
270 | |||
271 | if (!try_module_get(owner)) { | ||
272 | pr_warning("Can't get module reference, dropping event %d\n", | ||
273 | event_type); | ||
274 | kfree(event); | ||
275 | retval = -EINVAL; | ||
276 | goto out; | ||
277 | } | 247 | } |
278 | 248 | ||
279 | event->type = event_type; | ||
280 | event->object = object; | ||
281 | event->owner = owner; | ||
282 | |||
283 | list_add_tail(&event->node, &gameport_event_list); | ||
284 | wake_up(&gameport_wait); | ||
285 | |||
286 | out: | ||
287 | spin_unlock_irqrestore(&gameport_event_lock, flags); | 249 | spin_unlock_irqrestore(&gameport_event_lock, flags); |
288 | return retval; | 250 | return event; |
289 | } | 251 | } |
290 | 252 | ||
291 | static void gameport_free_event(struct gameport_event *event) | 253 | static void gameport_free_event(struct gameport_event *event) |
@@ -319,24 +281,8 @@ static void gameport_remove_duplicate_events(struct gameport_event *event) | |||
319 | spin_unlock_irqrestore(&gameport_event_lock, flags); | 281 | spin_unlock_irqrestore(&gameport_event_lock, flags); |
320 | } | 282 | } |
321 | 283 | ||
322 | static struct gameport_event *gameport_get_event(void) | ||
323 | { | ||
324 | struct gameport_event *event = NULL; | ||
325 | unsigned long flags; | ||
326 | |||
327 | spin_lock_irqsave(&gameport_event_lock, flags); | ||
328 | |||
329 | if (!list_empty(&gameport_event_list)) { | ||
330 | event = list_first_entry(&gameport_event_list, | ||
331 | struct gameport_event, node); | ||
332 | list_del_init(&event->node); | ||
333 | } | ||
334 | |||
335 | spin_unlock_irqrestore(&gameport_event_lock, flags); | ||
336 | return event; | ||
337 | } | ||
338 | 284 | ||
339 | static void gameport_handle_event(void) | 285 | static void gameport_handle_events(struct work_struct *work) |
340 | { | 286 | { |
341 | struct gameport_event *event; | 287 | struct gameport_event *event; |
342 | 288 | ||
@@ -368,6 +314,59 @@ static void gameport_handle_event(void) | |||
368 | mutex_unlock(&gameport_mutex); | 314 | mutex_unlock(&gameport_mutex); |
369 | } | 315 | } |
370 | 316 | ||
317 | static DECLARE_WORK(gameport_event_work, gameport_handle_events); | ||
318 | |||
319 | static int gameport_queue_event(void *object, struct module *owner, | ||
320 | enum gameport_event_type event_type) | ||
321 | { | ||
322 | unsigned long flags; | ||
323 | struct gameport_event *event; | ||
324 | int retval = 0; | ||
325 | |||
326 | spin_lock_irqsave(&gameport_event_lock, flags); | ||
327 | |||
328 | /* | ||
329 | * Scan event list for the other events for the same gameport port, | ||
330 | * starting with the most recent one. If event is the same we | ||
331 | * do not need add new one. If event is of different type we | ||
332 | * need to add this event and should not look further because | ||
333 | * we need to preserve sequence of distinct events. | ||
334 | */ | ||
335 | list_for_each_entry_reverse(event, &gameport_event_list, node) { | ||
336 | if (event->object == object) { | ||
337 | if (event->type == event_type) | ||
338 | goto out; | ||
339 | break; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC); | ||
344 | if (!event) { | ||
345 | pr_err("Not enough memory to queue event %d\n", event_type); | ||
346 | retval = -ENOMEM; | ||
347 | goto out; | ||
348 | } | ||
349 | |||
350 | if (!try_module_get(owner)) { | ||
351 | pr_warning("Can't get module reference, dropping event %d\n", | ||
352 | event_type); | ||
353 | kfree(event); | ||
354 | retval = -EINVAL; | ||
355 | goto out; | ||
356 | } | ||
357 | |||
358 | event->type = event_type; | ||
359 | event->object = object; | ||
360 | event->owner = owner; | ||
361 | |||
362 | list_add_tail(&event->node, &gameport_event_list); | ||
363 | schedule_work(&gameport_event_work); | ||
364 | |||
365 | out: | ||
366 | spin_unlock_irqrestore(&gameport_event_lock, flags); | ||
367 | return retval; | ||
368 | } | ||
369 | |||
371 | /* | 370 | /* |
372 | * Remove all events that have been submitted for a given object, | 371 | * Remove all events that have been submitted for a given object, |
373 | * be it a gameport port or a driver. | 372 | * be it a gameport port or a driver. |
@@ -419,19 +418,6 @@ static struct gameport *gameport_get_pending_child(struct gameport *parent) | |||
419 | return child; | 418 | return child; |
420 | } | 419 | } |
421 | 420 | ||
422 | static int gameport_thread(void *nothing) | ||
423 | { | ||
424 | set_freezable(); | ||
425 | do { | ||
426 | gameport_handle_event(); | ||
427 | wait_event_freezable(gameport_wait, | ||
428 | kthread_should_stop() || !list_empty(&gameport_event_list)); | ||
429 | } while (!kthread_should_stop()); | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | |||
435 | /* | 421 | /* |
436 | * Gameport port operations | 422 | * Gameport port operations |
437 | */ | 423 | */ |
@@ -814,13 +800,6 @@ static int __init gameport_init(void) | |||
814 | return error; | 800 | return error; |
815 | } | 801 | } |
816 | 802 | ||
817 | gameport_task = kthread_run(gameport_thread, NULL, "kgameportd"); | ||
818 | if (IS_ERR(gameport_task)) { | ||
819 | bus_unregister(&gameport_bus); | ||
820 | error = PTR_ERR(gameport_task); | ||
821 | pr_err("Failed to start kgameportd, error: %d\n", error); | ||
822 | return error; | ||
823 | } | ||
824 | 803 | ||
825 | return 0; | 804 | return 0; |
826 | } | 805 | } |
@@ -828,7 +807,12 @@ static int __init gameport_init(void) | |||
828 | static void __exit gameport_exit(void) | 807 | static void __exit gameport_exit(void) |
829 | { | 808 | { |
830 | bus_unregister(&gameport_bus); | 809 | bus_unregister(&gameport_bus); |
831 | kthread_stop(gameport_task); | 810 | |
811 | /* | ||
812 | * There should not be any outstanding events but work may | ||
813 | * still be scheduled so simply cancel it. | ||
814 | */ | ||
815 | cancel_work_sync(&gameport_event_work); | ||
832 | } | 816 | } |
833 | 817 | ||
834 | subsys_initcall(gameport_init); | 818 | subsys_initcall(gameport_init); |