summaryrefslogtreecommitdiffstats
path: root/drivers/pci/hotplug/shpchp_ctrl.c
diff options
context:
space:
mode:
authorBjorn Helgaas2013-01-11 20:21:15 +0100
committerBjorn Helgaas2013-01-14 18:23:22 +0100
commitf652e7d2916fe2fcf9e7d709aa5b7476b431e2dd (patch)
tree3de39a91ba274a307e11face7f5bbb8a3f7244b5 /drivers/pci/hotplug/shpchp_ctrl.c
parentPCI: shpchp: Handle push button event asynchronously (diff)
downloadkernel-qcow2-linux-f652e7d2916fe2fcf9e7d709aa5b7476b431e2dd.tar.gz
kernel-qcow2-linux-f652e7d2916fe2fcf9e7d709aa5b7476b431e2dd.tar.xz
kernel-qcow2-linux-f652e7d2916fe2fcf9e7d709aa5b7476b431e2dd.zip
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_ctrl.c')
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c6
1 files changed, 3 insertions, 3 deletions
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c
index fd2cae9eb6c2..58499277903a 100644
--- a/drivers/pci/hotplug/shpchp_ctrl.c
+++ b/drivers/pci/hotplug/shpchp_ctrl.c
@@ -51,7 +51,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
info->p_slot = p_slot;
INIT_WORK(&info->work, interrupt_event_handler);
- queue_work(shpchp_wq, &info->work);
+ queue_work(p_slot->wq, &info->work);
return 0;
}
@@ -453,7 +453,7 @@ void shpchp_queue_pushbutton_work(struct work_struct *work)
kfree(info);
goto out;
}
- queue_work(shpchp_wq, &info->work);
+ queue_work(p_slot->wq, &info->work);
out:
mutex_unlock(&p_slot->lock);
}
@@ -501,7 +501,7 @@ static void handle_button_press_event(struct slot *p_slot)
p_slot->hpc_ops->green_led_blink(p_slot);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
- queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ);
+ queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
break;
case BLINKINGOFF_STATE:
case BLINKINGON_STATE: