History

workqueue: fix state-dump console deadlock

Console drivers often queue work while holding locks also taken in their console write paths, something which can lead to deadlocks on SMP when dumping workqueue state (e.g. sysrq-t or on suspend failures). For serial console drivers this could look like: CPU0 CPU1 ---- ---- show_workqueue_state(); lock(&pool->lock); <IRQ> lock(&port->lock); schedule_work(); lock(&pool->lock); printk(); lock(console_owner); lock(&port->lock); where workqueues are, for example, used to push data to the line discipline, process break signals and handle modem-status changes. Line disciplines and serdev drivers can also queue work on write-wakeup notifications, etc. Reworking every console driver to avoid queuing work while holding locks also taken in their write paths would complicate drivers and is neither desirable or feasible. Instead use the deferred-printk mechanism to avoid printing while holding pool locks when dumping workqueue state. Note that there are a few WARN_ON() assertions in the workqueue code which could potentially also trigger a deadlock. Hopefully the ongoing printk rework will provide a general solution for this eventually. This was originally reported after a lockdep splat when executing sysrq-t with the imx serial driver.

Johan Hovold committed 5 years ago

Commit 57116ce1 parent 80f0a1f1 file · +16 −2Browse files

Showing the changes to kernel/workqueue.c (1 of 1 file). Show all

Unified · Split

kernel/workqueue.c+16−2
@@ -4830,8 +4830,16 @@ void show_workqueue_state(void) · 4829 unmodified lines
48304830
48314831 for_each_pwq(pwq, wq) {
48324832 raw_spin_lock_irqsave(&pwq->pool->lock, flags);
4833− if (pwq->nr_active || !list_empty(&pwq->inactive_works))
4833+ if (pwq->nr_active || !list_empty(&pwq->inactive_works)) {
4834+ /*
4835+ * Defer printing to avoid deadlocks in console
4836+ * drivers that queue work while holding locks
4837+ * also taken in their write paths.
4838+ */
4839+ printk_deferred_enter();
48344840 show_pwq(pwq);
4841+ printk_deferred_exit();
4842+ }
48354843 raw_spin_unlock_irqrestore(&pwq->pool->lock, flags);
48364844 /*
48374845 * We could be printing a lot from atomic context, e.g.
@@ -4849,7 +4857,12 @@ void show_workqueue_state(void) · 11 unmodified lines
48494857 raw_spin_lock_irqsave(&pool->lock, flags);
48504858 if (pool->nr_workers == pool->nr_idle)
48514859 goto next_pool;
4852−
4860+ /*
4861+ * Defer printing to avoid deadlocks in console drivers that
4862+ * queue work while holding locks also taken in their write
4863+ * paths.
4864+ */
4865+ printk_deferred_enter();
48534866 pr_info("pool %d:", pool->id);
48544867 pr_cont_pool_info(pool);
48554868 pr_cont(" hung=%us workers=%d",
@@ -4864,6 +4877,7 @@ void show_workqueue_state(void) · 8 unmodified lines
48644877 first = false;
48654878 }
48664879 pr_cont("\n");
4880+ printk_deferred_exit();
48674881 next_pool:
48684882 raw_spin_unlock_irqrestore(&pool->lock, flags);
48694883 /*

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t