aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-08-25 04:33:56 -0400
committerTejun Heo <tj@kernel.org>2010-08-25 04:33:56 -0400
commit8a2e8e5dec7e29c56a46ba176c664ab6a3d04118 (patch)
tree57da96451bead4986dfcd82aadf47ba2c05745ac
parente41e704bc4f49057fc68b643108366e6e6781aa3 (diff)
workqueue: fix cwq->nr_active underflow
cwq->nr_active is used to keep track of how many work items are active for the cpu workqueue, where 'active' is defined as either pending on global worklist or executing. This is used to implement the max_active limit and workqueue freezing. If a work item is queued after nr_active has already reached max_active, the work item doesn't increment nr_active and is put on the delayed queue and gets activated later as previous active work items retire. try_to_grab_pending() which is used in the cancellation path unconditionally decremented nr_active whether the work item being cancelled is currently active or delayed, so cancelling a delayed work item makes nr_active underflow. This breaks max_active enforcement and triggers BUG_ON() in destroy_workqueue() later on. This patch fixes this bug by adding a flag WORK_STRUCT_DELAYED, which is set while a work item in on the delayed list and making try_to_grab_pending() decrement nr_active iff the work item is currently active. The addition of the flag enlarges cwq alignment to 256 bytes which is getting a bit too large. It's scheduled to be reduced back to 128 bytes by merging WORK_STRUCT_PENDING and WORK_STRUCT_CWQ in the next devel cycle. Signed-off-by: Tejun Heo <tj@kernel.org> Reported-by: Johannes Berg <johannes@sipsolutions.net>
-rw-r--r--include/linux/workqueue.h16
-rw-r--r--kernel/workqueue.c30
2 files changed, 29 insertions, 17 deletions
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index c959666eafca..f11100f96482 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -25,18 +25,20 @@ typedef void (*work_func_t)(struct work_struct *work);
25 25
26enum { 26enum {
27 WORK_STRUCT_PENDING_BIT = 0, /* work item is pending execution */ 27 WORK_STRUCT_PENDING_BIT = 0, /* work item is pending execution */
28 WORK_STRUCT_CWQ_BIT = 1, /* data points to cwq */ 28 WORK_STRUCT_DELAYED_BIT = 1, /* work item is delayed */
29 WORK_STRUCT_LINKED_BIT = 2, /* next work is linked to this one */ 29 WORK_STRUCT_CWQ_BIT = 2, /* data points to cwq */
30 WORK_STRUCT_LINKED_BIT = 3, /* next work is linked to this one */
30#ifdef CONFIG_DEBUG_OBJECTS_WORK 31#ifdef CONFIG_DEBUG_OBJECTS_WORK
31 WORK_STRUCT_STATIC_BIT = 3, /* static initializer (debugobjects) */ 32 WORK_STRUCT_STATIC_BIT = 4, /* static initializer (debugobjects) */
32 WORK_STRUCT_COLOR_SHIFT = 4, /* color for workqueue flushing */ 33 WORK_STRUCT_COLOR_SHIFT = 5, /* color for workqueue flushing */
33#else 34#else
34 WORK_STRUCT_COLOR_SHIFT = 3, /* color for workqueue flushing */ 35 WORK_STRUCT_COLOR_SHIFT = 4, /* color for workqueue flushing */
35#endif 36#endif
36 37
37 WORK_STRUCT_COLOR_BITS = 4, 38 WORK_STRUCT_COLOR_BITS = 4,
38 39
39 WORK_STRUCT_PENDING = 1 << WORK_STRUCT_PENDING_BIT, 40 WORK_STRUCT_PENDING = 1 << WORK_STRUCT_PENDING_BIT,
41 WORK_STRUCT_DELAYED = 1 << WORK_STRUCT_DELAYED_BIT,
40 WORK_STRUCT_CWQ = 1 << WORK_STRUCT_CWQ_BIT, 42 WORK_STRUCT_CWQ = 1 << WORK_STRUCT_CWQ_BIT,
41 WORK_STRUCT_LINKED = 1 << WORK_STRUCT_LINKED_BIT, 43 WORK_STRUCT_LINKED = 1 << WORK_STRUCT_LINKED_BIT,
42#ifdef CONFIG_DEBUG_OBJECTS_WORK 44#ifdef CONFIG_DEBUG_OBJECTS_WORK
@@ -59,8 +61,8 @@ enum {
59 61
60 /* 62 /*
61 * Reserve 7 bits off of cwq pointer w/ debugobjects turned 63 * Reserve 7 bits off of cwq pointer w/ debugobjects turned
62 * off. This makes cwqs aligned to 128 bytes which isn't too 64 * off. This makes cwqs aligned to 256 bytes and allows 15
63 * excessive while allowing 15 workqueue flush colors. 65 * workqueue flush colors.
64 */ 66 */
65 WORK_STRUCT_FLAG_BITS = WORK_STRUCT_COLOR_SHIFT + 67 WORK_STRUCT_FLAG_BITS = WORK_STRUCT_COLOR_SHIFT +
66 WORK_STRUCT_COLOR_BITS, 68 WORK_STRUCT_COLOR_BITS,
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 362b50d092e2..a2dccfca03ba 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -941,6 +941,7 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq,
941 struct global_cwq *gcwq; 941 struct global_cwq *gcwq;
942 struct cpu_workqueue_struct *cwq; 942 struct cpu_workqueue_struct *cwq;
943 struct list_head *worklist; 943 struct list_head *worklist;
944 unsigned int work_flags;
944 unsigned long flags; 945 unsigned long flags;
945 946
946 debug_work_activate(work); 947 debug_work_activate(work);
@@ -990,14 +991,17 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq,
990 BUG_ON(!list_empty(&work->entry)); 991 BUG_ON(!list_empty(&work->entry));
991 992
992 cwq->nr_in_flight[cwq->work_color]++; 993 cwq->nr_in_flight[cwq->work_color]++;
994 work_flags = work_color_to_flags(cwq->work_color);
993 995
994 if (likely(cwq->nr_active < cwq->max_active)) { 996 if (likely(cwq->nr_active < cwq->max_active)) {
995 cwq->nr_active++; 997 cwq->nr_active++;
996 worklist = gcwq_determine_ins_pos(gcwq, cwq); 998 worklist = gcwq_determine_ins_pos(gcwq, cwq);
997 } else 999 } else {
1000 work_flags |= WORK_STRUCT_DELAYED;
998 worklist = &cwq->delayed_works; 1001 worklist = &cwq->delayed_works;
1002 }
999 1003
1000 insert_work(cwq, work, worklist, work_color_to_flags(cwq->work_color)); 1004 insert_work(cwq, work, worklist, work_flags);
1001 1005
1002 spin_unlock_irqrestore(&gcwq->lock, flags); 1006 spin_unlock_irqrestore(&gcwq->lock, flags);
1003} 1007}
@@ -1666,6 +1670,7 @@ static void cwq_activate_first_delayed(struct cpu_workqueue_struct *cwq)
1666 struct list_head *pos = gcwq_determine_ins_pos(cwq->gcwq, cwq); 1670 struct list_head *pos = gcwq_determine_ins_pos(cwq->gcwq, cwq);
1667 1671
1668 move_linked_works(work, pos, NULL); 1672 move_linked_works(work, pos, NULL);
1673 __clear_bit(WORK_STRUCT_DELAYED_BIT, work_data_bits(work));
1669 cwq->nr_active++; 1674 cwq->nr_active++;
1670} 1675}
1671 1676
@@ -1673,6 +1678,7 @@ static void cwq_activate_first_delayed(struct cpu_workqueue_struct *cwq)
1673 * cwq_dec_nr_in_flight - decrement cwq's nr_in_flight 1678 * cwq_dec_nr_in_flight - decrement cwq's nr_in_flight
1674 * @cwq: cwq of interest 1679 * @cwq: cwq of interest
1675 * @color: color of work which left the queue 1680 * @color: color of work which left the queue
1681 * @delayed: for a delayed work
1676 * 1682 *
1677 * A work either has completed or is removed from pending queue, 1683 * A work either has completed or is removed from pending queue,
1678 * decrement nr_in_flight of its cwq and handle workqueue flushing. 1684 * decrement nr_in_flight of its cwq and handle workqueue flushing.
@@ -1680,19 +1686,22 @@ static void cwq_activate_first_delayed(struct cpu_workqueue_struct *cwq)
1680 * CONTEXT: 1686 * CONTEXT:
1681 * spin_lock_irq(gcwq->lock). 1687 * spin_lock_irq(gcwq->lock).
1682 */ 1688 */
1683static void cwq_dec_nr_in_flight(struct cpu_workqueue_struct *cwq, int color) 1689static void cwq_dec_nr_in_flight(struct cpu_workqueue_struct *cwq, int color,
1690 bool delayed)
1684{ 1691{
1685 /* ignore uncolored works */ 1692 /* ignore uncolored works */
1686 if (color == WORK_NO_COLOR) 1693 if (color == WORK_NO_COLOR)
1687 return; 1694 return;
1688 1695
1689 cwq->nr_in_flight[color]--; 1696 cwq->nr_in_flight[color]--;
1690 cwq->nr_active--;
1691 1697
1692 if (!list_empty(&cwq->delayed_works)) { 1698 if (!delayed) {
1693 /* one down, submit a delayed one */ 1699 cwq->nr_active--;
1694 if (cwq->nr_active < cwq->max_active) 1700 if (!list_empty(&cwq->delayed_works)) {
1695 cwq_activate_first_delayed(cwq); 1701 /* one down, submit a delayed one */
1702 if (cwq->nr_active < cwq->max_active)
1703 cwq_activate_first_delayed(cwq);
1704 }
1696 } 1705 }
1697 1706
1698 /* is flush in progress and are we at the flushing tip? */ 1707 /* is flush in progress and are we at the flushing tip? */
@@ -1823,7 +1832,7 @@ __acquires(&gcwq->lock)
1823 hlist_del_init(&worker->hentry); 1832 hlist_del_init(&worker->hentry);
1824 worker->current_work = NULL; 1833 worker->current_work = NULL;
1825 worker->current_cwq = NULL; 1834 worker->current_cwq = NULL;
1826 cwq_dec_nr_in_flight(cwq, work_color); 1835 cwq_dec_nr_in_flight(cwq, work_color, false);
1827} 1836}
1828 1837
1829/** 1838/**
@@ -2388,7 +2397,8 @@ static int try_to_grab_pending(struct work_struct *work)
2388 debug_work_deactivate(work); 2397 debug_work_deactivate(work);
2389 list_del_init(&work->entry); 2398 list_del_init(&work->entry);
2390 cwq_dec_nr_in_flight(get_work_cwq(work), 2399 cwq_dec_nr_in_flight(get_work_cwq(work),
2391 get_work_color(work)); 2400 get_work_color(work),
2401 *work_data_bits(work) & WORK_STRUCT_DELAYED);
2392 ret = 1; 2402 ret = 1;
2393 } 2403 }
2394 } 2404 }