aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/workqueue.h18
-rw-r--r--kernel/workqueue.c39
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
227extern struct workqueue_struct * 227extern 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
257extern void destroy_workqueue(struct workqueue_struct *wq); 257extern 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
600static 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
1506struct workqueue_struct *__create_workqueue_key(const char *name, 1534struct 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);