diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2013-01-11 19:21:15 (GMT) |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2013-01-14 17:23:22 (GMT) |
commit | f652e7d2916fe2fcf9e7d709aa5b7476b431e2dd (patch) | |
tree | 3de39a91ba274a307e11face7f5bbb8a3f7244b5 /drivers/pci/hotplug/shpchp.h | |
parent | d347e75847c1fb299c97736638f45e6ea39702d4 (diff) | |
download | linux-f652e7d2916fe2fcf9e7d709aa5b7476b431e2dd.tar.xz |
PCI: shpchp: Use per-slot workqueues to avoid deadlock
When we have an SHPC-capable bridge with a second SHPC-capable bridge
below it, pushing the upstream bridge's attention button causes a
deadlock.
The deadlock happens because we use the shpchp_wq workqueue to run
shpchp_pushbutton_thread(), which uses shpchp_disable_slot() to remove
devices below the upstream bridge. When we remove the downstream bridge,
we call shpc_remove(), the shpchp driver's .remove() method. That calls
flush_workqueue(shpchp_wq), which deadlocks because the
shpchp_pushbutton_thread() work item is still running.
This patch avoids the deadlock by creating a workqueue for every slot
and removing the single shared workqueue.
Here's the call path that leads to the deadlock:
shpchp_queue_pushbutton_work
queue_work(shpchp_wq) # shpchp_pushbutton_thread
...
shpchp_pushbutton_thread
shpchp_disable_slot
remove_board
shpchp_unconfigure_device
pci_stop_and_remove_bus_device
...
shpc_remove # shpchp driver .remove method
hpc_release_ctlr
cleanup_slots
flush_workqueue(shpchp_wq)
This change is based on code inspection, since we don't have hardware
with this topology.
Based-on-patch-by: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: stable@vger.kernel.org
Diffstat (limited to 'drivers/pci/hotplug/shpchp.h')
-rw-r--r-- | drivers/pci/hotplug/shpchp.h | 2 |
1 files changed, 1 insertions, 1 deletions
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 1b69d95..b849f995 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -46,7 +46,6 @@ extern bool shpchp_poll_mode; extern int shpchp_poll_time; extern bool shpchp_debug; -extern struct workqueue_struct *shpchp_wq; #define dbg(format, arg...) \ do { \ @@ -90,6 +89,7 @@ struct slot { struct list_head slot_list; struct delayed_work work; /* work for button event */ struct mutex lock; + struct workqueue_struct *wq; u8 hp_slot; }; |