PCI: shpchp: Use per-slot workqueues to avoid deadlock
authorBjorn Helgaas <bhelgaas@google.com>
Fri, 11 Jan 2013 19:21:15 +0000 (12:21 -0700)
committerBen Hutchings <ben@decadent.org.uk>
Wed, 6 Feb 2013 04:33:33 +0000 (04:33 +0000)
commit0d36c06afe8ac3510ebdd21eb023077cb28efde6
treef60bc731e8331d6f32e2c7e460ac016bba823583
parent0809b51bdf57a2e813ccb06ac9ff01c01b341885
PCI: shpchp: Use per-slot workqueues to avoid deadlock

commit f652e7d2916fe2fcf9e7d709aa5b7476b431e2dd upstream.

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>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
drivers/pci/hotplug/shpchp.h
drivers/pci/hotplug/shpchp_core.c
drivers/pci/hotplug/shpchp_ctrl.c