diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 47cd9af5caf3f4a5b5736a808e706a7c7f2b0bfc..bb015be3489430f23bf0b328e427c78c2070a72b 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -21,6 +21,7 @@ #include #include /* signal_pending() */ #include +#include #include #include "../pcie/portdrv.h" @@ -80,6 +81,9 @@ struct slot { * struct controller - PCIe hotplug controller * @ctrl_lock: serializes writes to the Slot Control register * @pcie: pointer to the controller's PCIe port service device + * @reset_lock: prevents access to the Data Link Layer Link Active bit in the + * Link Status register and to the Presence Detect State bit in the Slot + * Status register during a slot reset which may cause them to flap * @slot: pointer to the controller's slot structure * @queue: wait queue to wake up on reception of a Command Completed event, * used for synchronous writes to the Slot Control register @@ -109,6 +113,7 @@ struct slot { struct controller { struct mutex ctrl_lock; struct pcie_device *pcie; + struct rw_semaphore reset_lock; struct slot *slot; wait_queue_head_t queue; u32 slot_cap; diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index f4eaa99446990eee5354a2a4f13e8ece39570303..1be8871e74395f38ddfdb65f2c6466280fe9f7c7 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -237,6 +237,7 @@ static int pciehp_probe(struct pcie_device *dev) } /* Check if slot is occupied */ + down_read(&ctrl->reset_lock); mutex_lock(&slot->lock); pciehp_get_adapter_status(slot, &occupied); pciehp_get_power_status(slot, &poweron); @@ -249,6 +250,7 @@ static int pciehp_probe(struct pcie_device *dev) if (!occupied && poweron && POWER_CTRL(ctrl)) pciehp_power_off_slot(slot); mutex_unlock(&slot->lock); + up_read(&ctrl->reset_lock); return 0; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 712f3de78b09b8b84dfc1e3fba2fe42469e30731..41398b15d306e6b9bbbe34736cd9209b1abe1e06 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -603,10 +603,12 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) * Disable requests have higher priority than Presence Detect Changed * or Data Link Layer State Changed events. */ + down_read(&ctrl->reset_lock); if (events & DISABLE_SLOT) pciehp_handle_disable_request(slot); else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC)) pciehp_handle_presence_or_link_change(slot, events); + up_read(&ctrl->reset_lock); /* Check Power Fault Detected */ if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { @@ -627,9 +629,6 @@ static int pciehp_poll(void *data) schedule_timeout_idle(10 * HZ); /* start with 10 sec delay */ while (!kthread_should_stop()) { - if (kthread_should_park()) - kthread_parkme(); - /* poll for interrupt events or user requests */ while (pciehp_isr(IRQ_NOTCONNECTED, ctrl) == IRQ_WAKE_THREAD || atomic_read(&ctrl->pending_events)) @@ -723,6 +722,8 @@ int pciehp_reset_slot(struct slot *slot, int probe) if (probe) return 0; + down_write(&ctrl->reset_lock); + if (!ATTN_BUTTN(ctrl)) { ctrl_mask |= PCI_EXP_SLTCTL_PDCE; stat_mask |= PCI_EXP_SLTSTA_PDC; @@ -733,8 +734,6 @@ int pciehp_reset_slot(struct slot *slot, int probe) pcie_write_cmd(ctrl, 0, ctrl_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0); - if (pciehp_poll_mode) - kthread_park(ctrl->poll_thread); pci_reset_bridge_secondary_bus(ctrl->pcie->port); @@ -742,8 +741,8 @@ int pciehp_reset_slot(struct slot *slot, int probe) pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask); - if (pciehp_poll_mode) - kthread_unpark(ctrl->poll_thread); + + up_write(&ctrl->reset_lock); return 0; } @@ -835,6 +834,7 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl->slot_cap = slot_cap; mutex_init(&ctrl->ctrl_lock); + init_rwsem(&ctrl->reset_lock); init_waitqueue_head(&ctrl->requester); init_waitqueue_head(&ctrl->queue); dbg_ctrl(ctrl);