diff options
author | David Howells <dhowells@redhat.com> | 2009-11-19 13:10:23 -0500 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2009-11-19 13:10:23 -0500 |
commit | 3d7a641e544e428191667e8b1f83f96fa46dbd65 (patch) | |
tree | 172aa672eca96b94f5531885b82abb82b43c7d8a /kernel | |
parent | 66b00a7c93ec782d118d2c03bd599cfd041e80a1 (diff) |
SLOW_WORK: Wait for outstanding work items belonging to a module to clear
Wait for outstanding slow work items belonging to a module to clear when
unregistering that module as a user of the facility. This prevents the put_ref
code of a work item from being taken away before it returns.
Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/slow-work.c | 132 |
1 files changed, 126 insertions, 6 deletions
diff --git a/kernel/slow-work.c b/kernel/slow-work.c index 0d31135efbf4..dd08f376e406 100644 --- a/kernel/slow-work.c +++ b/kernel/slow-work.c | |||
@@ -22,6 +22,8 @@ | |||
22 | #define SLOW_WORK_OOM_TIMEOUT (5 * HZ) /* can't start new threads for 5s after | 22 | #define SLOW_WORK_OOM_TIMEOUT (5 * HZ) /* can't start new threads for 5s after |
23 | * OOM */ | 23 | * OOM */ |
24 | 24 | ||
25 | #define SLOW_WORK_THREAD_LIMIT 255 /* abs maximum number of slow-work threads */ | ||
26 | |||
25 | static void slow_work_cull_timeout(unsigned long); | 27 | static void slow_work_cull_timeout(unsigned long); |
26 | static void slow_work_oom_timeout(unsigned long); | 28 | static void slow_work_oom_timeout(unsigned long); |
27 | 29 | ||
@@ -46,7 +48,7 @@ static unsigned vslow_work_proportion = 50; /* % of threads that may process | |||
46 | 48 | ||
47 | #ifdef CONFIG_SYSCTL | 49 | #ifdef CONFIG_SYSCTL |
48 | static const int slow_work_min_min_threads = 2; | 50 | static const int slow_work_min_min_threads = 2; |
49 | static int slow_work_max_max_threads = 255; | 51 | static int slow_work_max_max_threads = SLOW_WORK_THREAD_LIMIT; |
50 | static const int slow_work_min_vslow = 1; | 52 | static const int slow_work_min_vslow = 1; |
51 | static const int slow_work_max_vslow = 99; | 53 | static const int slow_work_max_vslow = 99; |
52 | 54 | ||
@@ -98,6 +100,23 @@ static DEFINE_TIMER(slow_work_oom_timer, slow_work_oom_timeout, 0, 0); | |||
98 | static struct slow_work slow_work_new_thread; /* new thread starter */ | 100 | static struct slow_work slow_work_new_thread; /* new thread starter */ |
99 | 101 | ||
100 | /* | 102 | /* |
103 | * slow work ID allocation (use slow_work_queue_lock) | ||
104 | */ | ||
105 | static DECLARE_BITMAP(slow_work_ids, SLOW_WORK_THREAD_LIMIT); | ||
106 | |||
107 | /* | ||
108 | * Unregistration tracking to prevent put_ref() from disappearing during module | ||
109 | * unload | ||
110 | */ | ||
111 | #ifdef CONFIG_MODULES | ||
112 | static struct module *slow_work_thread_processing[SLOW_WORK_THREAD_LIMIT]; | ||
113 | static struct module *slow_work_unreg_module; | ||
114 | static struct slow_work *slow_work_unreg_work_item; | ||
115 | static DECLARE_WAIT_QUEUE_HEAD(slow_work_unreg_wq); | ||
116 | static DEFINE_MUTEX(slow_work_unreg_sync_lock); | ||
117 | #endif | ||
118 | |||
119 | /* | ||
101 | * The queues of work items and the lock governing access to them. These are | 120 | * The queues of work items and the lock governing access to them. These are |
102 | * shared between all the CPUs. It doesn't make sense to have per-CPU queues | 121 | * shared between all the CPUs. It doesn't make sense to have per-CPU queues |
103 | * as the number of threads bears no relation to the number of CPUs. | 122 | * as the number of threads bears no relation to the number of CPUs. |
@@ -149,8 +168,11 @@ static unsigned slow_work_calc_vsmax(void) | |||
149 | * Attempt to execute stuff queued on a slow thread. Return true if we managed | 168 | * Attempt to execute stuff queued on a slow thread. Return true if we managed |
150 | * it, false if there was nothing to do. | 169 | * it, false if there was nothing to do. |
151 | */ | 170 | */ |
152 | static bool slow_work_execute(void) | 171 | static bool slow_work_execute(int id) |
153 | { | 172 | { |
173 | #ifdef CONFIG_MODULES | ||
174 | struct module *module; | ||
175 | #endif | ||
154 | struct slow_work *work = NULL; | 176 | struct slow_work *work = NULL; |
155 | unsigned vsmax; | 177 | unsigned vsmax; |
156 | bool very_slow; | 178 | bool very_slow; |
@@ -186,6 +208,12 @@ static bool slow_work_execute(void) | |||
186 | } else { | 208 | } else { |
187 | very_slow = false; /* avoid the compiler warning */ | 209 | very_slow = false; /* avoid the compiler warning */ |
188 | } | 210 | } |
211 | |||
212 | #ifdef CONFIG_MODULES | ||
213 | if (work) | ||
214 | slow_work_thread_processing[id] = work->owner; | ||
215 | #endif | ||
216 | |||
189 | spin_unlock_irq(&slow_work_queue_lock); | 217 | spin_unlock_irq(&slow_work_queue_lock); |
190 | 218 | ||
191 | if (!work) | 219 | if (!work) |
@@ -219,7 +247,18 @@ static bool slow_work_execute(void) | |||
219 | spin_unlock_irq(&slow_work_queue_lock); | 247 | spin_unlock_irq(&slow_work_queue_lock); |
220 | } | 248 | } |
221 | 249 | ||
250 | /* sort out the race between module unloading and put_ref() */ | ||
222 | work->ops->put_ref(work); | 251 | work->ops->put_ref(work); |
252 | |||
253 | #ifdef CONFIG_MODULES | ||
254 | module = slow_work_thread_processing[id]; | ||
255 | slow_work_thread_processing[id] = NULL; | ||
256 | smp_mb(); | ||
257 | if (slow_work_unreg_work_item == work || | ||
258 | slow_work_unreg_module == module) | ||
259 | wake_up_all(&slow_work_unreg_wq); | ||
260 | #endif | ||
261 | |||
223 | return true; | 262 | return true; |
224 | 263 | ||
225 | auto_requeue: | 264 | auto_requeue: |
@@ -232,6 +271,7 @@ auto_requeue: | |||
232 | else | 271 | else |
233 | list_add_tail(&work->link, &slow_work_queue); | 272 | list_add_tail(&work->link, &slow_work_queue); |
234 | spin_unlock_irq(&slow_work_queue_lock); | 273 | spin_unlock_irq(&slow_work_queue_lock); |
274 | slow_work_thread_processing[id] = NULL; | ||
235 | return true; | 275 | return true; |
236 | } | 276 | } |
237 | 277 | ||
@@ -368,13 +408,22 @@ static inline bool slow_work_available(int vsmax) | |||
368 | */ | 408 | */ |
369 | static int slow_work_thread(void *_data) | 409 | static int slow_work_thread(void *_data) |
370 | { | 410 | { |
371 | int vsmax; | 411 | int vsmax, id; |
372 | 412 | ||
373 | DEFINE_WAIT(wait); | 413 | DEFINE_WAIT(wait); |
374 | 414 | ||
375 | set_freezable(); | 415 | set_freezable(); |
376 | set_user_nice(current, -5); | 416 | set_user_nice(current, -5); |
377 | 417 | ||
418 | /* allocate ourselves an ID */ | ||
419 | spin_lock_irq(&slow_work_queue_lock); | ||
420 | id = find_first_zero_bit(slow_work_ids, SLOW_WORK_THREAD_LIMIT); | ||
421 | BUG_ON(id < 0 || id >= SLOW_WORK_THREAD_LIMIT); | ||
422 | __set_bit(id, slow_work_ids); | ||
423 | spin_unlock_irq(&slow_work_queue_lock); | ||
424 | |||
425 | sprintf(current->comm, "kslowd%03u", id); | ||
426 | |||
378 | for (;;) { | 427 | for (;;) { |
379 | vsmax = vslow_work_proportion; | 428 | vsmax = vslow_work_proportion; |
380 | vsmax *= atomic_read(&slow_work_thread_count); | 429 | vsmax *= atomic_read(&slow_work_thread_count); |
@@ -395,7 +444,7 @@ static int slow_work_thread(void *_data) | |||
395 | vsmax *= atomic_read(&slow_work_thread_count); | 444 | vsmax *= atomic_read(&slow_work_thread_count); |
396 | vsmax /= 100; | 445 | vsmax /= 100; |
397 | 446 | ||
398 | if (slow_work_available(vsmax) && slow_work_execute()) { | 447 | if (slow_work_available(vsmax) && slow_work_execute(id)) { |
399 | cond_resched(); | 448 | cond_resched(); |
400 | if (list_empty(&slow_work_queue) && | 449 | if (list_empty(&slow_work_queue) && |
401 | list_empty(&vslow_work_queue) && | 450 | list_empty(&vslow_work_queue) && |
@@ -412,6 +461,10 @@ static int slow_work_thread(void *_data) | |||
412 | break; | 461 | break; |
413 | } | 462 | } |
414 | 463 | ||
464 | spin_lock_irq(&slow_work_queue_lock); | ||
465 | __clear_bit(id, slow_work_ids); | ||
466 | spin_unlock_irq(&slow_work_queue_lock); | ||
467 | |||
415 | if (atomic_dec_and_test(&slow_work_thread_count)) | 468 | if (atomic_dec_and_test(&slow_work_thread_count)) |
416 | complete_and_exit(&slow_work_last_thread_exited, 0); | 469 | complete_and_exit(&slow_work_last_thread_exited, 0); |
417 | return 0; | 470 | return 0; |
@@ -475,6 +528,7 @@ static void slow_work_new_thread_execute(struct slow_work *work) | |||
475 | } | 528 | } |
476 | 529 | ||
477 | static const struct slow_work_ops slow_work_new_thread_ops = { | 530 | static const struct slow_work_ops slow_work_new_thread_ops = { |
531 | .owner = THIS_MODULE, | ||
478 | .get_ref = slow_work_new_thread_get_ref, | 532 | .get_ref = slow_work_new_thread_get_ref, |
479 | .put_ref = slow_work_new_thread_put_ref, | 533 | .put_ref = slow_work_new_thread_put_ref, |
480 | .execute = slow_work_new_thread_execute, | 534 | .execute = slow_work_new_thread_execute, |
@@ -546,12 +600,13 @@ static int slow_work_max_threads_sysctl(struct ctl_table *table, int write, | |||
546 | 600 | ||
547 | /** | 601 | /** |
548 | * slow_work_register_user - Register a user of the facility | 602 | * slow_work_register_user - Register a user of the facility |
603 | * @module: The module about to make use of the facility | ||
549 | * | 604 | * |
550 | * Register a user of the facility, starting up the initial threads if there | 605 | * Register a user of the facility, starting up the initial threads if there |
551 | * aren't any other users at this point. This will return 0 if successful, or | 606 | * aren't any other users at this point. This will return 0 if successful, or |
552 | * an error if not. | 607 | * an error if not. |
553 | */ | 608 | */ |
554 | int slow_work_register_user(void) | 609 | int slow_work_register_user(struct module *module) |
555 | { | 610 | { |
556 | struct task_struct *p; | 611 | struct task_struct *p; |
557 | int loop; | 612 | int loop; |
@@ -598,14 +653,79 @@ error: | |||
598 | } | 653 | } |
599 | EXPORT_SYMBOL(slow_work_register_user); | 654 | EXPORT_SYMBOL(slow_work_register_user); |
600 | 655 | ||
656 | /* | ||
657 | * wait for all outstanding items from the calling module to complete | ||
658 | * - note that more items may be queued whilst we're waiting | ||
659 | */ | ||
660 | static void slow_work_wait_for_items(struct module *module) | ||
661 | { | ||
662 | DECLARE_WAITQUEUE(myself, current); | ||
663 | struct slow_work *work; | ||
664 | int loop; | ||
665 | |||
666 | mutex_lock(&slow_work_unreg_sync_lock); | ||
667 | add_wait_queue(&slow_work_unreg_wq, &myself); | ||
668 | |||
669 | for (;;) { | ||
670 | spin_lock_irq(&slow_work_queue_lock); | ||
671 | |||
672 | /* first of all, we wait for the last queued item in each list | ||
673 | * to be processed */ | ||
674 | list_for_each_entry_reverse(work, &vslow_work_queue, link) { | ||
675 | if (work->owner == module) { | ||
676 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
677 | slow_work_unreg_work_item = work; | ||
678 | goto do_wait; | ||
679 | } | ||
680 | } | ||
681 | list_for_each_entry_reverse(work, &slow_work_queue, link) { | ||
682 | if (work->owner == module) { | ||
683 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
684 | slow_work_unreg_work_item = work; | ||
685 | goto do_wait; | ||
686 | } | ||
687 | } | ||
688 | |||
689 | /* then we wait for the items being processed to finish */ | ||
690 | slow_work_unreg_module = module; | ||
691 | smp_mb(); | ||
692 | for (loop = 0; loop < SLOW_WORK_THREAD_LIMIT; loop++) { | ||
693 | if (slow_work_thread_processing[loop] == module) | ||
694 | goto do_wait; | ||
695 | } | ||
696 | spin_unlock_irq(&slow_work_queue_lock); | ||
697 | break; /* okay, we're done */ | ||
698 | |||
699 | do_wait: | ||
700 | spin_unlock_irq(&slow_work_queue_lock); | ||
701 | schedule(); | ||
702 | slow_work_unreg_work_item = NULL; | ||
703 | slow_work_unreg_module = NULL; | ||
704 | } | ||
705 | |||
706 | remove_wait_queue(&slow_work_unreg_wq, &myself); | ||
707 | mutex_unlock(&slow_work_unreg_sync_lock); | ||
708 | } | ||
709 | |||
601 | /** | 710 | /** |
602 | * slow_work_unregister_user - Unregister a user of the facility | 711 | * slow_work_unregister_user - Unregister a user of the facility |
712 | * @module: The module whose items should be cleared | ||
603 | * | 713 | * |
604 | * Unregister a user of the facility, killing all the threads if this was the | 714 | * Unregister a user of the facility, killing all the threads if this was the |
605 | * last one. | 715 | * last one. |
716 | * | ||
717 | * This waits for all the work items belonging to the nominated module to go | ||
718 | * away before proceeding. | ||
606 | */ | 719 | */ |
607 | void slow_work_unregister_user(void) | 720 | void slow_work_unregister_user(struct module *module) |
608 | { | 721 | { |
722 | /* first of all, wait for all outstanding items from the calling module | ||
723 | * to complete */ | ||
724 | if (module) | ||
725 | slow_work_wait_for_items(module); | ||
726 | |||
727 | /* then we can actually go about shutting down the facility if need | ||
728 | * be */ | ||
609 | mutex_lock(&slow_work_user_lock); | 729 | mutex_lock(&slow_work_user_lock); |
610 | 730 | ||
611 | BUG_ON(slow_work_user_count <= 0); | 731 | BUG_ON(slow_work_user_count <= 0); |