aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/workqueue.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2012-07-17 15:39:26 -0400
committerTejun Heo <tj@kernel.org>2012-07-17 15:39:26 -0400
commit6575820221f7a4dd6eadecf7bf83cdd154335eda (patch)
tree2f9061b4eb1b6cf5a4b70acc45cb46a1a287066a /kernel/workqueue.c
parent3270476a6c0ce322354df8679652f060d66526dc (diff)
workqueue: perform cpu down operations from low priority cpu_notifier()
Currently, all workqueue cpu hotplug operations run off CPU_PRI_WORKQUEUE which is higher than normal notifiers. This is to ensure that workqueue is up and running while bringing up a CPU before other notifiers try to use workqueue on the CPU. Per-cpu workqueues are supposed to remain working and bound to the CPU for normal CPU_DOWN_PREPARE notifiers. This holds mostly true even with workqueue offlining running with higher priority because workqueue CPU_DOWN_PREPARE only creates a bound trustee thread which runs the per-cpu workqueue without concurrency management without explicitly detaching the existing workers. However, if the trustee needs to create new workers, it creates unbound workers which may wander off to other CPUs while CPU_DOWN_PREPARE notifiers are in progress. Furthermore, if the CPU down is cancelled, the per-CPU workqueue may end up with workers which aren't bound to the CPU. While reliably reproducible with a convoluted artificial test-case involving scheduling and flushing CPU burning work items from CPU down notifiers, this isn't very likely to happen in the wild, and, even when it happens, the effects are likely to be hidden by the following successful CPU down. Fix it by using different priorities for up and down notifiers - high priority for up operations and low priority for down operations. Workqueue cpu hotplug operations will soon go through further cleanup. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: stable@vger.kernel.org Acked-by: "Rafael J. Wysocki" <rjw@sisk.pl>
Diffstat (limited to 'kernel/workqueue.c')
-rw-r--r--kernel/workqueue.c38
1 files changed, 37 insertions, 1 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 4fa9e3552f1..f59b7fd26e2 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -3644,6 +3644,41 @@ err_destroy:
3644 return NOTIFY_BAD; 3644 return NOTIFY_BAD;
3645} 3645}
3646 3646
3647/*
3648 * Workqueues should be brought up before normal priority CPU notifiers.
3649 * This will be registered high priority CPU notifier.
3650 */
3651static int __devinit workqueue_cpu_up_callback(struct notifier_block *nfb,
3652 unsigned long action,
3653 void *hcpu)
3654{
3655 switch (action & ~CPU_TASKS_FROZEN) {
3656 case CPU_UP_PREPARE:
3657 case CPU_UP_CANCELED:
3658 case CPU_DOWN_FAILED:
3659 case CPU_ONLINE:
3660 return workqueue_cpu_callback(nfb, action, hcpu);
3661 }
3662 return NOTIFY_OK;
3663}
3664
3665/*
3666 * Workqueues should be brought down after normal priority CPU notifiers.
3667 * This will be registered as low priority CPU notifier.
3668 */
3669static int __devinit workqueue_cpu_down_callback(struct notifier_block *nfb,
3670 unsigned long action,
3671 void *hcpu)
3672{
3673 switch (action & ~CPU_TASKS_FROZEN) {
3674 case CPU_DOWN_PREPARE:
3675 case CPU_DYING:
3676 case CPU_POST_DEAD:
3677 return workqueue_cpu_callback(nfb, action, hcpu);
3678 }
3679 return NOTIFY_OK;
3680}
3681
3647#ifdef CONFIG_SMP 3682#ifdef CONFIG_SMP
3648 3683
3649struct work_for_cpu { 3684struct work_for_cpu {
@@ -3839,7 +3874,8 @@ static int __init init_workqueues(void)
3839 unsigned int cpu; 3874 unsigned int cpu;
3840 int i; 3875 int i;
3841 3876
3842 cpu_notifier(workqueue_cpu_callback, CPU_PRI_WORKQUEUE); 3877 cpu_notifier(workqueue_cpu_up_callback, CPU_PRI_WORKQUEUE_UP);
3878 cpu_notifier(workqueue_cpu_down_callback, CPU_PRI_WORKQUEUE_DOWN);
3843 3879
3844 /* initialize gcwqs */ 3880 /* initialize gcwqs */
3845 for_each_gcwq_cpu(cpu) { 3881 for_each_gcwq_cpu(cpu) {