diff options
-rw-r--r-- | include/linux/workqueue.h | 18 | ||||
-rw-r--r-- | kernel/workqueue.c | 39 |
2 files changed, 46 insertions, 11 deletions
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 4f4fdba722c3..eb753b7790e5 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h | |||
@@ -225,11 +225,11 @@ enum { | |||
225 | }; | 225 | }; |
226 | 226 | ||
227 | extern struct workqueue_struct * | 227 | extern struct workqueue_struct * |
228 | __create_workqueue_key(const char *name, unsigned int flags, | 228 | __create_workqueue_key(const char *name, unsigned int flags, int max_active, |
229 | struct lock_class_key *key, const char *lock_name); | 229 | struct lock_class_key *key, const char *lock_name); |
230 | 230 | ||
231 | #ifdef CONFIG_LOCKDEP | 231 | #ifdef CONFIG_LOCKDEP |
232 | #define __create_workqueue(name, flags) \ | 232 | #define __create_workqueue(name, flags, max_active) \ |
233 | ({ \ | 233 | ({ \ |
234 | static struct lock_class_key __key; \ | 234 | static struct lock_class_key __key; \ |
235 | const char *__lock_name; \ | 235 | const char *__lock_name; \ |
@@ -239,20 +239,20 @@ __create_workqueue_key(const char *name, unsigned int flags, | |||
239 | else \ | 239 | else \ |
240 | __lock_name = #name; \ | 240 | __lock_name = #name; \ |
241 | \ | 241 | \ |
242 | __create_workqueue_key((name), (flags), &__key, \ | 242 | __create_workqueue_key((name), (flags), (max_active), \ |
243 | __lock_name); \ | 243 | &__key, __lock_name); \ |
244 | }) | 244 | }) |
245 | #else | 245 | #else |
246 | #define __create_workqueue(name, flags) \ | 246 | #define __create_workqueue(name, flags, max_active) \ |
247 | __create_workqueue_key((name), (flags), NULL, NULL) | 247 | __create_workqueue_key((name), (flags), (max_active), NULL, NULL) |
248 | #endif | 248 | #endif |
249 | 249 | ||
250 | #define create_workqueue(name) \ | 250 | #define create_workqueue(name) \ |
251 | __create_workqueue((name), 0) | 251 | __create_workqueue((name), 0, 1) |
252 | #define create_freezeable_workqueue(name) \ | 252 | #define create_freezeable_workqueue(name) \ |
253 | __create_workqueue((name), WQ_FREEZEABLE | WQ_SINGLE_THREAD) | 253 | __create_workqueue((name), WQ_FREEZEABLE | WQ_SINGLE_THREAD, 1) |
254 | #define create_singlethread_workqueue(name) \ | 254 | #define create_singlethread_workqueue(name) \ |
255 | __create_workqueue((name), WQ_SINGLE_THREAD) | 255 | __create_workqueue((name), WQ_SINGLE_THREAD, 1) |
256 | 256 | ||
257 | extern void destroy_workqueue(struct workqueue_struct *wq); | 257 | extern void destroy_workqueue(struct workqueue_struct *wq); |
258 | 258 | ||
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 9953d3c7bd10..e541b5db67dd 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c | |||
@@ -77,6 +77,9 @@ struct cpu_workqueue_struct { | |||
77 | int flush_color; /* L: flushing color */ | 77 | int flush_color; /* L: flushing color */ |
78 | int nr_in_flight[WORK_NR_COLORS]; | 78 | int nr_in_flight[WORK_NR_COLORS]; |
79 | /* L: nr of in_flight works */ | 79 | /* L: nr of in_flight works */ |
80 | int nr_active; /* L: nr of active works */ | ||
81 | int max_active; /* I: max active works */ | ||
82 | struct list_head delayed_works; /* L: delayed works */ | ||
80 | }; | 83 | }; |
81 | 84 | ||
82 | /* | 85 | /* |
@@ -321,14 +324,24 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq, | |||
321 | struct work_struct *work) | 324 | struct work_struct *work) |
322 | { | 325 | { |
323 | struct cpu_workqueue_struct *cwq = target_cwq(cpu, wq); | 326 | struct cpu_workqueue_struct *cwq = target_cwq(cpu, wq); |
327 | struct list_head *worklist; | ||
324 | unsigned long flags; | 328 | unsigned long flags; |
325 | 329 | ||
326 | debug_work_activate(work); | 330 | debug_work_activate(work); |
331 | |||
327 | spin_lock_irqsave(&cwq->lock, flags); | 332 | spin_lock_irqsave(&cwq->lock, flags); |
328 | BUG_ON(!list_empty(&work->entry)); | 333 | BUG_ON(!list_empty(&work->entry)); |
334 | |||
329 | cwq->nr_in_flight[cwq->work_color]++; | 335 | cwq->nr_in_flight[cwq->work_color]++; |
330 | insert_work(cwq, work, &cwq->worklist, | 336 | |
331 | work_color_to_flags(cwq->work_color)); | 337 | if (likely(cwq->nr_active < cwq->max_active)) { |
338 | cwq->nr_active++; | ||
339 | worklist = &cwq->worklist; | ||
340 | } else | ||
341 | worklist = &cwq->delayed_works; | ||
342 | |||
343 | insert_work(cwq, work, worklist, work_color_to_flags(cwq->work_color)); | ||
344 | |||
332 | spin_unlock_irqrestore(&cwq->lock, flags); | 345 | spin_unlock_irqrestore(&cwq->lock, flags); |
333 | } | 346 | } |
334 | 347 | ||
@@ -584,6 +597,15 @@ static void move_linked_works(struct work_struct *work, struct list_head *head, | |||
584 | *nextp = n; | 597 | *nextp = n; |
585 | } | 598 | } |
586 | 599 | ||
600 | static void cwq_activate_first_delayed(struct cpu_workqueue_struct *cwq) | ||
601 | { | ||
602 | struct work_struct *work = list_first_entry(&cwq->delayed_works, | ||
603 | struct work_struct, entry); | ||
604 | |||
605 | move_linked_works(work, &cwq->worklist, NULL); | ||
606 | cwq->nr_active++; | ||
607 | } | ||
608 | |||
587 | /** | 609 | /** |
588 | * cwq_dec_nr_in_flight - decrement cwq's nr_in_flight | 610 | * cwq_dec_nr_in_flight - decrement cwq's nr_in_flight |
589 | * @cwq: cwq of interest | 611 | * @cwq: cwq of interest |
@@ -602,6 +624,12 @@ static void cwq_dec_nr_in_flight(struct cpu_workqueue_struct *cwq, int color) | |||
602 | return; | 624 | return; |
603 | 625 | ||
604 | cwq->nr_in_flight[color]--; | 626 | cwq->nr_in_flight[color]--; |
627 | cwq->nr_active--; | ||
628 | |||
629 | /* one down, submit a delayed one */ | ||
630 | if (!list_empty(&cwq->delayed_works) && | ||
631 | cwq->nr_active < cwq->max_active) | ||
632 | cwq_activate_first_delayed(cwq); | ||
605 | 633 | ||
606 | /* is flush in progress and are we at the flushing tip? */ | 634 | /* is flush in progress and are we at the flushing tip? */ |
607 | if (likely(cwq->flush_color != color)) | 635 | if (likely(cwq->flush_color != color)) |
@@ -1505,6 +1533,7 @@ static void free_cwqs(struct cpu_workqueue_struct *cwqs) | |||
1505 | 1533 | ||
1506 | struct workqueue_struct *__create_workqueue_key(const char *name, | 1534 | struct workqueue_struct *__create_workqueue_key(const char *name, |
1507 | unsigned int flags, | 1535 | unsigned int flags, |
1536 | int max_active, | ||
1508 | struct lock_class_key *key, | 1537 | struct lock_class_key *key, |
1509 | const char *lock_name) | 1538 | const char *lock_name) |
1510 | { | 1539 | { |
@@ -1513,6 +1542,8 @@ struct workqueue_struct *__create_workqueue_key(const char *name, | |||
1513 | bool failed = false; | 1542 | bool failed = false; |
1514 | unsigned int cpu; | 1543 | unsigned int cpu; |
1515 | 1544 | ||
1545 | max_active = clamp_val(max_active, 1, INT_MAX); | ||
1546 | |||
1516 | wq = kzalloc(sizeof(*wq), GFP_KERNEL); | 1547 | wq = kzalloc(sizeof(*wq), GFP_KERNEL); |
1517 | if (!wq) | 1548 | if (!wq) |
1518 | goto err; | 1549 | goto err; |
@@ -1544,8 +1575,10 @@ struct workqueue_struct *__create_workqueue_key(const char *name, | |||
1544 | cwq->cpu = cpu; | 1575 | cwq->cpu = cpu; |
1545 | cwq->wq = wq; | 1576 | cwq->wq = wq; |
1546 | cwq->flush_color = -1; | 1577 | cwq->flush_color = -1; |
1578 | cwq->max_active = max_active; | ||
1547 | spin_lock_init(&cwq->lock); | 1579 | spin_lock_init(&cwq->lock); |
1548 | INIT_LIST_HEAD(&cwq->worklist); | 1580 | INIT_LIST_HEAD(&cwq->worklist); |
1581 | INIT_LIST_HEAD(&cwq->delayed_works); | ||
1549 | init_waitqueue_head(&cwq->more_work); | 1582 | init_waitqueue_head(&cwq->more_work); |
1550 | 1583 | ||
1551 | if (failed) | 1584 | if (failed) |
@@ -1607,6 +1640,8 @@ void destroy_workqueue(struct workqueue_struct *wq) | |||
1607 | 1640 | ||
1608 | for (i = 0; i < WORK_NR_COLORS; i++) | 1641 | for (i = 0; i < WORK_NR_COLORS; i++) |
1609 | BUG_ON(cwq->nr_in_flight[i]); | 1642 | BUG_ON(cwq->nr_in_flight[i]); |
1643 | BUG_ON(cwq->nr_active); | ||
1644 | BUG_ON(!list_empty(&cwq->delayed_works)); | ||
1610 | } | 1645 | } |
1611 | 1646 | ||
1612 | free_cwqs(wq->cpu_wq); | 1647 | free_cwqs(wq->cpu_wq); |