aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/workqueue.h1
-rw-r--r--kernel/workqueue.c32
2 files changed, 30 insertions, 3 deletions
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 0a7814131e66..07cf5e5f91cb 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -225,6 +225,7 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; }
225enum { 225enum {
226 WQ_FREEZEABLE = 1 << 0, /* freeze during suspend */ 226 WQ_FREEZEABLE = 1 << 0, /* freeze during suspend */
227 WQ_SINGLE_CPU = 1 << 1, /* only single cpu at a time */ 227 WQ_SINGLE_CPU = 1 << 1, /* only single cpu at a time */
228 WQ_NON_REENTRANT = 1 << 2, /* guarantee non-reentrance */
228}; 229};
229 230
230extern struct workqueue_struct * 231extern struct workqueue_struct *
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index c68277c204ab..bce1074bdec1 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -534,11 +534,37 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq,
534 534
535 debug_work_activate(work); 535 debug_work_activate(work);
536 536
537 /* determine gcwq to use */ 537 /*
538 * Determine gcwq to use. SINGLE_CPU is inherently
539 * NON_REENTRANT, so test it first.
540 */
538 if (!(wq->flags & WQ_SINGLE_CPU)) { 541 if (!(wq->flags & WQ_SINGLE_CPU)) {
539 /* just use the requested cpu for multicpu workqueues */ 542 struct global_cwq *last_gcwq;
543
544 /*
545 * It's multi cpu. If @wq is non-reentrant and @work
546 * was previously on a different cpu, it might still
547 * be running there, in which case the work needs to
548 * be queued on that cpu to guarantee non-reentrance.
549 */
540 gcwq = get_gcwq(cpu); 550 gcwq = get_gcwq(cpu);
541 spin_lock_irqsave(&gcwq->lock, flags); 551 if (wq->flags & WQ_NON_REENTRANT &&
552 (last_gcwq = get_work_gcwq(work)) && last_gcwq != gcwq) {
553 struct worker *worker;
554
555 spin_lock_irqsave(&last_gcwq->lock, flags);
556
557 worker = find_worker_executing_work(last_gcwq, work);
558
559 if (worker && worker->current_cwq->wq == wq)
560 gcwq = last_gcwq;
561 else {
562 /* meh... not running there, queue here */
563 spin_unlock_irqrestore(&last_gcwq->lock, flags);
564 spin_lock_irqsave(&gcwq->lock, flags);
565 }
566 } else
567 spin_lock_irqsave(&gcwq->lock, flags);
542 } else { 568 } else {
543 unsigned int req_cpu = cpu; 569 unsigned int req_cpu = cpu;
544 570