diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2008-07-25 04:47:50 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-25 13:53:40 -0400 |
commit | 3da1c84c00c7e5fa8348336bd8c342f9128b0f14 (patch) | |
tree | af960c65a670319fa86846a493a6d97f9fb37cd9 /kernel | |
parent | 8616a89ab761239c963eea3a63be383f127cc7e8 (diff) |
workqueues: make get_online_cpus() useable for work->func()
workqueue_cpu_callback(CPU_DEAD) flushes cwq->thread under
cpu_maps_update_begin(). This means that the multithreaded workqueues
can't use get_online_cpus() due to the possible deadlock, very bad and
very old problem.
Introduce the new state, CPU_POST_DEAD, which is called after
cpu_hotplug_done() but before cpu_maps_update_done().
Change workqueue_cpu_callback() to use CPU_POST_DEAD instead of CPU_DEAD.
This means that create/destroy functions can't rely on get_online_cpus()
any longer and should take cpu_add_remove_lock instead.
[akpm@linux-foundation.org: fix CONFIG_SMP=n]
Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
Acked-by: Gautham R Shenoy <ego@in.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Max Krasnyansky <maxk@qualcomm.com>
Cc: Paul Jackson <pj@sgi.com>
Cc: Paul Menage <menage@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/cpu.c | 5 | ||||
-rw-r--r-- | kernel/workqueue.c | 18 |
2 files changed, 14 insertions, 9 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index 2cc409ce0a8f..10ba5f1004a5 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
@@ -285,6 +285,11 @@ out_allowed: | |||
285 | set_cpus_allowed_ptr(current, &old_allowed); | 285 | set_cpus_allowed_ptr(current, &old_allowed); |
286 | out_release: | 286 | out_release: |
287 | cpu_hotplug_done(); | 287 | cpu_hotplug_done(); |
288 | if (!err) { | ||
289 | if (raw_notifier_call_chain(&cpu_chain, CPU_POST_DEAD | mod, | ||
290 | hcpu) == NOTIFY_BAD) | ||
291 | BUG(); | ||
292 | } | ||
288 | return err; | 293 | return err; |
289 | } | 294 | } |
290 | 295 | ||
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 5fbffd302eb5..828e58230cbc 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c | |||
@@ -828,7 +828,7 @@ struct workqueue_struct *__create_workqueue_key(const char *name, | |||
828 | err = create_workqueue_thread(cwq, singlethread_cpu); | 828 | err = create_workqueue_thread(cwq, singlethread_cpu); |
829 | start_workqueue_thread(cwq, -1); | 829 | start_workqueue_thread(cwq, -1); |
830 | } else { | 830 | } else { |
831 | get_online_cpus(); | 831 | cpu_maps_update_begin(); |
832 | spin_lock(&workqueue_lock); | 832 | spin_lock(&workqueue_lock); |
833 | list_add(&wq->list, &workqueues); | 833 | list_add(&wq->list, &workqueues); |
834 | spin_unlock(&workqueue_lock); | 834 | spin_unlock(&workqueue_lock); |
@@ -840,7 +840,7 @@ struct workqueue_struct *__create_workqueue_key(const char *name, | |||
840 | err = create_workqueue_thread(cwq, cpu); | 840 | err = create_workqueue_thread(cwq, cpu); |
841 | start_workqueue_thread(cwq, cpu); | 841 | start_workqueue_thread(cwq, cpu); |
842 | } | 842 | } |
843 | put_online_cpus(); | 843 | cpu_maps_update_done(); |
844 | } | 844 | } |
845 | 845 | ||
846 | if (err) { | 846 | if (err) { |
@@ -854,8 +854,8 @@ EXPORT_SYMBOL_GPL(__create_workqueue_key); | |||
854 | static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) | 854 | static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) |
855 | { | 855 | { |
856 | /* | 856 | /* |
857 | * Our caller is either destroy_workqueue() or CPU_DEAD, | 857 | * Our caller is either destroy_workqueue() or CPU_POST_DEAD, |
858 | * get_online_cpus() protects cwq->thread. | 858 | * cpu_add_remove_lock protects cwq->thread. |
859 | */ | 859 | */ |
860 | if (cwq->thread == NULL) | 860 | if (cwq->thread == NULL) |
861 | return; | 861 | return; |
@@ -865,7 +865,7 @@ static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) | |||
865 | 865 | ||
866 | flush_cpu_workqueue(cwq); | 866 | flush_cpu_workqueue(cwq); |
867 | /* | 867 | /* |
868 | * If the caller is CPU_DEAD and cwq->worklist was not empty, | 868 | * If the caller is CPU_POST_DEAD and cwq->worklist was not empty, |
869 | * a concurrent flush_workqueue() can insert a barrier after us. | 869 | * a concurrent flush_workqueue() can insert a barrier after us. |
870 | * However, in that case run_workqueue() won't return and check | 870 | * However, in that case run_workqueue() won't return and check |
871 | * kthread_should_stop() until it flushes all work_struct's. | 871 | * kthread_should_stop() until it flushes all work_struct's. |
@@ -889,14 +889,14 @@ void destroy_workqueue(struct workqueue_struct *wq) | |||
889 | const cpumask_t *cpu_map = wq_cpu_map(wq); | 889 | const cpumask_t *cpu_map = wq_cpu_map(wq); |
890 | int cpu; | 890 | int cpu; |
891 | 891 | ||
892 | get_online_cpus(); | 892 | cpu_maps_update_begin(); |
893 | spin_lock(&workqueue_lock); | 893 | spin_lock(&workqueue_lock); |
894 | list_del(&wq->list); | 894 | list_del(&wq->list); |
895 | spin_unlock(&workqueue_lock); | 895 | spin_unlock(&workqueue_lock); |
896 | 896 | ||
897 | for_each_cpu_mask_nr(cpu, *cpu_map) | 897 | for_each_cpu_mask_nr(cpu, *cpu_map) |
898 | cleanup_workqueue_thread(per_cpu_ptr(wq->cpu_wq, cpu)); | 898 | cleanup_workqueue_thread(per_cpu_ptr(wq->cpu_wq, cpu)); |
899 | put_online_cpus(); | 899 | cpu_maps_update_done(); |
900 | 900 | ||
901 | free_percpu(wq->cpu_wq); | 901 | free_percpu(wq->cpu_wq); |
902 | kfree(wq); | 902 | kfree(wq); |
@@ -935,7 +935,7 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, | |||
935 | 935 | ||
936 | case CPU_UP_CANCELED: | 936 | case CPU_UP_CANCELED: |
937 | start_workqueue_thread(cwq, -1); | 937 | start_workqueue_thread(cwq, -1); |
938 | case CPU_DEAD: | 938 | case CPU_POST_DEAD: |
939 | cleanup_workqueue_thread(cwq); | 939 | cleanup_workqueue_thread(cwq); |
940 | break; | 940 | break; |
941 | } | 941 | } |
@@ -943,7 +943,7 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, | |||
943 | 943 | ||
944 | switch (action) { | 944 | switch (action) { |
945 | case CPU_UP_CANCELED: | 945 | case CPU_UP_CANCELED: |
946 | case CPU_DEAD: | 946 | case CPU_POST_DEAD: |
947 | cpu_clear(cpu, cpu_populated_map); | 947 | cpu_clear(cpu, cpu_populated_map); |
948 | } | 948 | } |
949 | 949 | ||