diff options
Diffstat (limited to 'kernel/workqueue.c')
-rw-r--r-- | kernel/workqueue.c | 39 |
1 files changed, 37 insertions, 2 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 9953d3c7bd1..e541b5db67d 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); |