aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/workqueue.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2012-07-17 15:39:26 -0400
committerLuis Henriques <luis.henriques@canonical.com>2012-08-13 09:10:35 -0400
commit7d96871a273ffbb6e12f5df3a00fb598cc3afd9c (patch)
tree73f7171b709cae5afd5fdf74a0eed2bf827aba10 /kernel/workqueue.c
parent9f6d51ec3a369d93b89ae663d59fb47e144aaa5e (diff)
workqueue: perform cpu down operations from low priority cpu_notifier()
BugLink: http://bugs.launchpad.net/bugs/1034988 commit 6575820221f7a4dd6eadecf7bf83cdd154335eda upstream. 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> Acked-by: "Rafael J. Wysocki" <rjw@sisk.pl> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Herton Ronaldo Krzesinski <herton.krzesinski@canonical.com>
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 ee1845b8d69..e88c924fc6b 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -3561,6 +3561,41 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb,
3561 return notifier_from_errno(0); 3561 return notifier_from_errno(0);
3562} 3562}
3563 3563
3564/*
3565 * Workqueues should be brought up before normal priority CPU notifiers.
3566 * This will be registered high priority CPU notifier.
3567 */
3568static int __devinit workqueue_cpu_up_callback(struct notifier_block *nfb,
3569 unsigned long action,
3570 void *hcpu)
3571{
3572 switch (action & ~CPU_TASKS_FROZEN) {
3573 case CPU_UP_PREPARE:
3574 case CPU_UP_CANCELED:
3575 case CPU_DOWN_FAILED:
3576 case CPU_ONLINE:
3577 return workqueue_cpu_callback(nfb, action, hcpu);
3578 }
3579 return NOTIFY_OK;
3580}
3581
3582/*
3583 * Workqueues should be brought down after normal priority CPU notifiers.
3584 * This will be registered as low priority CPU notifier.
3585 */
3586static int __devinit workqueue_cpu_down_callback(struct notifier_block *nfb,
3587 unsigned long action,
3588 void *hcpu)
3589{
3590 switch (action & ~CPU_TASKS_FROZEN) {
3591 case CPU_DOWN_PREPARE:
3592 case CPU_DYING:
3593 case CPU_POST_DEAD:
3594 return workqueue_cpu_callback(nfb, action, hcpu);
3595 }
3596 return NOTIFY_OK;
3597}
3598
3564#ifdef CONFIG_SMP 3599#ifdef CONFIG_SMP
3565 3600
3566struct work_for_cpu { 3601struct work_for_cpu {
@@ -3754,7 +3789,8 @@ static int __init init_workqueues(void)
3754 unsigned int cpu; 3789 unsigned int cpu;
3755 int i; 3790 int i;
3756 3791
3757 cpu_notifier(workqueue_cpu_callback, CPU_PRI_WORKQUEUE); 3792 cpu_notifier(workqueue_cpu_up_callback, CPU_PRI_WORKQUEUE_UP);
3793 cpu_notifier(workqueue_cpu_down_callback, CPU_PRI_WORKQUEUE_DOWN);
3758 3794
3759 /* initialize gcwqs */ 3795 /* initialize gcwqs */
3760 for_each_gcwq_cpu(cpu) { 3796 for_each_gcwq_cpu(cpu) {