With gcwq managing all the workers and work->data pointing to the last gcwq it was on, non-reentrance can be easily implemented by checking whether the work is still running on the previous gcwq on queueing. Implement it.
Showing the changes to kernel/workqueue.c (1 of 2 files). Show all
Unified · Split
| @@ -534,11 +534,37 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq, · 533 unmodified lines | ||
| 534 | 534 | |
| 535 | 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 | 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 | 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 | 568 | } else { |
| 543 | 569 | unsigned int req_cpu = cpu; |
| 544 | 570 | |