aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/workqueue.c
diff options
context:
space:
mode:
authorLai Jiangshan <laijs@cn.fujitsu.com>2012-09-18 13:40:00 -0400
committerTejun Heo <tj@kernel.org>2012-09-18 13:40:00 -0400
commit3aa62497594430ea522050b75c033f71f2c60ee6 (patch)
treece26e616d6d40a7279ec37fe615d717d849c2532 /kernel/workqueue.c
parenta5b4e57d7cc07cb28ccf16de0876a4770ae84920 (diff)
workqueue: fix possible stall on try_to_grab_pending() of a delayed work item
Currently, when try_to_grab_pending() grabs a delayed work item, it leaves its linked work items alone on the delayed_works. The linked work items are always NO_COLOR and will cause future cwq_activate_first_delayed() increase cwq->nr_active incorrectly, and may cause the whole cwq to stall. For example, state: cwq->max_active = 1, cwq->nr_active = 1 one work in cwq->pool, many in cwq->delayed_works. step1: try_to_grab_pending() removes a work item from delayed_works but leaves its NO_COLOR linked work items on it. step2: Later on, cwq_activate_first_delayed() activates the linked work item increasing ->nr_active. step3: cwq->nr_active = 1, but all activated work items of the cwq are NO_COLOR. When they finish, cwq->nr_active will not be decreased due to NO_COLOR, and no further work items will be activated from cwq->delayed_works. the cwq stalls. Fix it by ensuring the target work item is activated before stealing PENDING in try_to_grab_pending(). This ensures that all the linked work items are activated without incorrectly bumping cwq->nr_active. tj: Updated comment and description. Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> Signed-off-by: Tejun Heo <tj@kernel.org> Cc: stable@kernel.org
Diffstat (limited to 'kernel/workqueue.c')
-rw-r--r--kernel/workqueue.c25
1 files changed, 22 insertions, 3 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 48becaba1c94..d2fe8e77ceb7 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -977,10 +977,9 @@ static void move_linked_works(struct work_struct *work, struct list_head *head,
977 *nextp = n; 977 *nextp = n;
978} 978}
979 979
980static void cwq_activate_first_delayed(struct cpu_workqueue_struct *cwq) 980static void cwq_activate_delayed_work(struct work_struct *work)
981{ 981{
982 struct work_struct *work = list_first_entry(&cwq->delayed_works, 982 struct cpu_workqueue_struct *cwq = get_work_cwq(work);
983 struct work_struct, entry);
984 983
985 trace_workqueue_activate_work(work); 984 trace_workqueue_activate_work(work);
986 move_linked_works(work, &cwq->pool->worklist, NULL); 985 move_linked_works(work, &cwq->pool->worklist, NULL);
@@ -988,6 +987,14 @@ static void cwq_activate_first_delayed(struct cpu_workqueue_struct *cwq)
988 cwq->nr_active++; 987 cwq->nr_active++;
989} 988}
990 989
990static void cwq_activate_first_delayed(struct cpu_workqueue_struct *cwq)
991{
992 struct work_struct *work = list_first_entry(&cwq->delayed_works,
993 struct work_struct, entry);
994
995 cwq_activate_delayed_work(work);
996}
997
991/** 998/**
992 * cwq_dec_nr_in_flight - decrement cwq's nr_in_flight 999 * cwq_dec_nr_in_flight - decrement cwq's nr_in_flight
993 * @cwq: cwq of interest 1000 * @cwq: cwq of interest
@@ -1106,6 +1113,18 @@ static int try_to_grab_pending(struct work_struct *work, bool is_dwork,
1106 smp_rmb(); 1113 smp_rmb();
1107 if (gcwq == get_work_gcwq(work)) { 1114 if (gcwq == get_work_gcwq(work)) {
1108 debug_work_deactivate(work); 1115 debug_work_deactivate(work);
1116
1117 /*
1118 * A delayed work item cannot be grabbed directly
1119 * because it might have linked NO_COLOR work items
1120 * which, if left on the delayed_list, will confuse
1121 * cwq->nr_active management later on and cause
1122 * stall. Make sure the work item is activated
1123 * before grabbing.
1124 */
1125 if (*work_data_bits(work) & WORK_STRUCT_DELAYED)
1126 cwq_activate_delayed_work(work);
1127
1109 list_del_init(&work->entry); 1128 list_del_init(&work->entry);
1110 cwq_dec_nr_in_flight(get_work_cwq(work), 1129 cwq_dec_nr_in_flight(get_work_cwq(work),
1111 get_work_color(work), 1130 get_work_color(work),