Commit c4ec84c7 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Bjorn Helgaas

PCI: hotplug: Use global PCI rescan-remove locking

Multiple race conditions are possible between PCI hotplug and the generic
PCI bus rescan and device removal that can be triggered via sysfs.

To avoid those race conditions make PCI hotplug use global PCI
rescan-remove locking.
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent 5ef68e88
...@@ -254,9 +254,12 @@ int __ref cpci_configure_slot(struct slot *slot) ...@@ -254,9 +254,12 @@ int __ref cpci_configure_slot(struct slot *slot)
{ {
struct pci_dev *dev; struct pci_dev *dev;
struct pci_bus *parent; struct pci_bus *parent;
int ret = 0;
dbg("%s - enter", __func__); dbg("%s - enter", __func__);
pci_lock_rescan_remove();
if (slot->dev == NULL) { if (slot->dev == NULL) {
dbg("pci_dev null, finding %02x:%02x:%x", dbg("pci_dev null, finding %02x:%02x:%x",
slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn));
...@@ -277,7 +280,8 @@ int __ref cpci_configure_slot(struct slot *slot) ...@@ -277,7 +280,8 @@ int __ref cpci_configure_slot(struct slot *slot)
slot->dev = pci_get_slot(slot->bus, slot->devfn); slot->dev = pci_get_slot(slot->bus, slot->devfn);
if (slot->dev == NULL) { if (slot->dev == NULL) {
err("Could not find PCI device for slot %02x", slot->number); err("Could not find PCI device for slot %02x", slot->number);
return -ENODEV; ret = -ENODEV;
goto out;
} }
} }
parent = slot->dev->bus; parent = slot->dev->bus;
...@@ -294,8 +298,10 @@ int __ref cpci_configure_slot(struct slot *slot) ...@@ -294,8 +298,10 @@ int __ref cpci_configure_slot(struct slot *slot)
pci_bus_add_devices(parent); pci_bus_add_devices(parent);
out:
pci_unlock_rescan_remove();
dbg("%s - exit", __func__); dbg("%s - exit", __func__);
return 0; return ret;
} }
int cpci_unconfigure_slot(struct slot* slot) int cpci_unconfigure_slot(struct slot* slot)
...@@ -308,6 +314,8 @@ int cpci_unconfigure_slot(struct slot* slot) ...@@ -308,6 +314,8 @@ int cpci_unconfigure_slot(struct slot* slot)
return -ENODEV; return -ENODEV;
} }
pci_lock_rescan_remove();
list_for_each_entry_safe(dev, temp, &slot->bus->devices, bus_list) { list_for_each_entry_safe(dev, temp, &slot->bus->devices, bus_list) {
if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn)) if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn))
continue; continue;
...@@ -318,6 +326,8 @@ int cpci_unconfigure_slot(struct slot* slot) ...@@ -318,6 +326,8 @@ int cpci_unconfigure_slot(struct slot* slot)
pci_dev_put(slot->dev); pci_dev_put(slot->dev);
slot->dev = NULL; slot->dev = NULL;
pci_unlock_rescan_remove();
dbg("%s - exit", __func__); dbg("%s - exit", __func__);
return 0; return 0;
} }
...@@ -86,6 +86,8 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) ...@@ -86,6 +86,8 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
struct pci_bus *child; struct pci_bus *child;
int num; int num;
pci_lock_rescan_remove();
if (func->pci_dev == NULL) if (func->pci_dev == NULL)
func->pci_dev = pci_get_bus_and_slot(func->bus,PCI_DEVFN(func->device, func->function)); func->pci_dev = pci_get_bus_and_slot(func->bus,PCI_DEVFN(func->device, func->function));
...@@ -100,7 +102,7 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) ...@@ -100,7 +102,7 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
func->pci_dev = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, func->function)); func->pci_dev = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, func->function));
if (func->pci_dev == NULL) { if (func->pci_dev == NULL) {
dbg("ERROR: pci_dev still null\n"); dbg("ERROR: pci_dev still null\n");
return 0; goto out;
} }
} }
...@@ -113,6 +115,8 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) ...@@ -113,6 +115,8 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
pci_dev_put(func->pci_dev); pci_dev_put(func->pci_dev);
out:
pci_unlock_rescan_remove();
return 0; return 0;
} }
...@@ -123,6 +127,7 @@ int cpqhp_unconfigure_device(struct pci_func* func) ...@@ -123,6 +127,7 @@ int cpqhp_unconfigure_device(struct pci_func* func)
dbg("%s: bus/dev/func = %x/%x/%x\n", __func__, func->bus, func->device, func->function); dbg("%s: bus/dev/func = %x/%x/%x\n", __func__, func->bus, func->device, func->function);
pci_lock_rescan_remove();
for (j=0; j<8 ; j++) { for (j=0; j<8 ; j++) {
struct pci_dev* temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j)); struct pci_dev* temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j));
if (temp) { if (temp) {
...@@ -130,6 +135,7 @@ int cpqhp_unconfigure_device(struct pci_func* func) ...@@ -130,6 +135,7 @@ int cpqhp_unconfigure_device(struct pci_func* func)
pci_stop_and_remove_bus_device(temp); pci_stop_and_remove_bus_device(temp);
} }
} }
pci_unlock_rescan_remove();
return 0; return 0;
} }
......
...@@ -718,6 +718,8 @@ static void ibm_unconfigure_device(struct pci_func *func) ...@@ -718,6 +718,8 @@ static void ibm_unconfigure_device(struct pci_func *func)
func->device, func->function); func->device, func->function);
debug("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0); debug("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0);
pci_lock_rescan_remove();
for (j = 0; j < 0x08; j++) { for (j = 0; j < 0x08; j++) {
temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j); temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j);
if (temp) { if (temp) {
...@@ -725,7 +727,10 @@ static void ibm_unconfigure_device(struct pci_func *func) ...@@ -725,7 +727,10 @@ static void ibm_unconfigure_device(struct pci_func *func)
pci_dev_put(temp); pci_dev_put(temp);
} }
} }
pci_dev_put(func->dev); pci_dev_put(func->dev);
pci_unlock_rescan_remove();
} }
/* /*
...@@ -780,6 +785,8 @@ static int ibm_configure_device(struct pci_func *func) ...@@ -780,6 +785,8 @@ static int ibm_configure_device(struct pci_func *func)
int flag = 0; /* this is to make sure we don't double scan the bus, int flag = 0; /* this is to make sure we don't double scan the bus,
for bridged devices primarily */ for bridged devices primarily */
pci_lock_rescan_remove();
if (!(bus_structure_fixup(func->busno))) if (!(bus_structure_fixup(func->busno)))
flag = 1; flag = 1;
if (func->dev == NULL) if (func->dev == NULL)
...@@ -789,7 +796,7 @@ static int ibm_configure_device(struct pci_func *func) ...@@ -789,7 +796,7 @@ static int ibm_configure_device(struct pci_func *func)
if (func->dev == NULL) { if (func->dev == NULL) {
struct pci_bus *bus = pci_find_bus(0, func->busno); struct pci_bus *bus = pci_find_bus(0, func->busno);
if (!bus) if (!bus)
return 0; goto out;
num = pci_scan_slot(bus, num = pci_scan_slot(bus,
PCI_DEVFN(func->device, func->function)); PCI_DEVFN(func->device, func->function));
...@@ -800,7 +807,7 @@ static int ibm_configure_device(struct pci_func *func) ...@@ -800,7 +807,7 @@ static int ibm_configure_device(struct pci_func *func)
PCI_DEVFN(func->device, func->function)); PCI_DEVFN(func->device, func->function));
if (func->dev == NULL) { if (func->dev == NULL) {
err("ERROR... : pci_dev still NULL\n"); err("ERROR... : pci_dev still NULL\n");
return 0; goto out;
} }
} }
if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
...@@ -810,6 +817,8 @@ static int ibm_configure_device(struct pci_func *func) ...@@ -810,6 +817,8 @@ static int ibm_configure_device(struct pci_func *func)
pci_bus_add_devices(child); pci_bus_add_devices(child);
} }
out:
pci_unlock_rescan_remove();
return 0; return 0;
} }
......
...@@ -39,22 +39,26 @@ int pciehp_configure_device(struct slot *p_slot) ...@@ -39,22 +39,26 @@ int pciehp_configure_device(struct slot *p_slot)
struct pci_dev *dev; struct pci_dev *dev;
struct pci_dev *bridge = p_slot->ctrl->pcie->port; struct pci_dev *bridge = p_slot->ctrl->pcie->port;
struct pci_bus *parent = bridge->subordinate; struct pci_bus *parent = bridge->subordinate;
int num; int num, ret = 0;
struct controller *ctrl = p_slot->ctrl; struct controller *ctrl = p_slot->ctrl;
pci_lock_rescan_remove();
dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
if (dev) { if (dev) {
ctrl_err(ctrl, "Device %s already exists " ctrl_err(ctrl, "Device %s already exists "
"at %04x:%02x:00, cannot hot-add\n", pci_name(dev), "at %04x:%02x:00, cannot hot-add\n", pci_name(dev),
pci_domain_nr(parent), parent->number); pci_domain_nr(parent), parent->number);
pci_dev_put(dev); pci_dev_put(dev);
return -EINVAL; ret = -EINVAL;
goto out;
} }
num = pci_scan_slot(parent, PCI_DEVFN(0, 0)); num = pci_scan_slot(parent, PCI_DEVFN(0, 0));
if (num == 0) { if (num == 0) {
ctrl_err(ctrl, "No new device found\n"); ctrl_err(ctrl, "No new device found\n");
return -ENODEV; ret = -ENODEV;
goto out;
} }
list_for_each_entry(dev, &parent->devices, bus_list) list_for_each_entry(dev, &parent->devices, bus_list)
...@@ -73,7 +77,9 @@ int pciehp_configure_device(struct slot *p_slot) ...@@ -73,7 +77,9 @@ int pciehp_configure_device(struct slot *p_slot)
pci_bus_add_devices(parent); pci_bus_add_devices(parent);
return 0; out:
pci_unlock_rescan_remove();
return ret;
} }
int pciehp_unconfigure_device(struct slot *p_slot) int pciehp_unconfigure_device(struct slot *p_slot)
...@@ -90,6 +96,8 @@ int pciehp_unconfigure_device(struct slot *p_slot) ...@@ -90,6 +96,8 @@ int pciehp_unconfigure_device(struct slot *p_slot)
__func__, pci_domain_nr(parent), parent->number); __func__, pci_domain_nr(parent), parent->number);
pciehp_get_adapter_status(p_slot, &presence); pciehp_get_adapter_status(p_slot, &presence);
pci_lock_rescan_remove();
/* /*
* Stopping an SR-IOV PF device removes all the associated VFs, * Stopping an SR-IOV PF device removes all the associated VFs,
* which will update the bus->devices list and confuse the * which will update the bus->devices list and confuse the
...@@ -124,5 +132,6 @@ int pciehp_unconfigure_device(struct slot *p_slot) ...@@ -124,5 +132,6 @@ int pciehp_unconfigure_device(struct slot *p_slot)
pci_dev_put(dev); pci_dev_put(dev);
} }
pci_unlock_rescan_remove();
return rc; return rc;
} }
...@@ -354,10 +354,15 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) ...@@ -354,10 +354,15 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
{ {
struct pci_bus *bus; struct pci_bus *bus;
struct slot *slot; struct slot *slot;
int ret = 0;
pci_lock_rescan_remove();
bus = pcibios_find_pci_bus(dn); bus = pcibios_find_pci_bus(dn);
if (!bus) if (!bus) {
return -EINVAL; ret = -EINVAL;
goto out;
}
pr_debug("PCI: Removing PCI slot below EADS bridge %s\n", pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
bus->self ? pci_name(bus->self) : "<!PHB!>"); bus->self ? pci_name(bus->self) : "<!PHB!>");
...@@ -371,7 +376,8 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) ...@@ -371,7 +376,8 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
printk(KERN_ERR printk(KERN_ERR
"%s: unable to remove hotplug slot %s\n", "%s: unable to remove hotplug slot %s\n",
__func__, drc_name); __func__, drc_name);
return -EIO; ret = -EIO;
goto out;
} }
} }
...@@ -382,7 +388,8 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) ...@@ -382,7 +388,8 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
if (pcibios_unmap_io_space(bus)) { if (pcibios_unmap_io_space(bus)) {
printk(KERN_ERR "%s: failed to unmap bus range\n", printk(KERN_ERR "%s: failed to unmap bus range\n",
__func__); __func__);
return -ERANGE; ret = -ERANGE;
goto out;
} }
/* Remove the EADS bridge device itself */ /* Remove the EADS bridge device itself */
...@@ -390,7 +397,9 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) ...@@ -390,7 +397,9 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self)); pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
pci_stop_and_remove_bus_device(bus->self); pci_stop_and_remove_bus_device(bus->self);
return 0; out:
pci_unlock_rescan_remove();
return ret;
} }
/** /**
......
...@@ -398,7 +398,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) ...@@ -398,7 +398,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
return retval; return retval;
if (state == PRESENT) { if (state == PRESENT) {
pci_lock_rescan_remove();
pcibios_add_pci_devices(slot->bus); pcibios_add_pci_devices(slot->bus);
pci_unlock_rescan_remove();
slot->state = CONFIGURED; slot->state = CONFIGURED;
} else if (state == EMPTY) { } else if (state == EMPTY) {
slot->state = EMPTY; slot->state = EMPTY;
...@@ -418,7 +420,9 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) ...@@ -418,7 +420,9 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
if (slot->state == NOT_CONFIGURED) if (slot->state == NOT_CONFIGURED)
return -EINVAL; return -EINVAL;
pci_lock_rescan_remove();
pcibios_remove_pci_devices(slot->bus); pcibios_remove_pci_devices(slot->bus);
pci_unlock_rescan_remove();
vm_unmap_aliases(); vm_unmap_aliases();
slot->state = NOT_CONFIGURED; slot->state = NOT_CONFIGURED;
......
...@@ -80,7 +80,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) ...@@ -80,7 +80,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
goto out_deconfigure; goto out_deconfigure;
pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN); pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN);
pci_lock_rescan_remove();
pci_bus_add_devices(slot->zdev->bus); pci_bus_add_devices(slot->zdev->bus);
pci_unlock_rescan_remove();
return rc; return rc;
...@@ -98,7 +100,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) ...@@ -98,7 +100,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
return -EIO; return -EIO;
if (slot->zdev->pdev) if (slot->zdev->pdev)
pci_stop_and_remove_bus_device(slot->zdev->pdev); pci_stop_and_remove_bus_device_locked(slot->zdev->pdev);
rc = zpci_disable_device(slot->zdev); rc = zpci_disable_device(slot->zdev);
if (rc) if (rc)
......
...@@ -459,12 +459,15 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) ...@@ -459,12 +459,15 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
acpi_scan_lock_release(); acpi_scan_lock_release();
} }
pci_lock_rescan_remove();
/* Call the driver for the new device */ /* Call the driver for the new device */
pci_bus_add_devices(slot->pci_bus); pci_bus_add_devices(slot->pci_bus);
/* Call the drivers for the new devices subordinate to PPB */ /* Call the drivers for the new devices subordinate to PPB */
if (new_ppb) if (new_ppb)
pci_bus_add_devices(new_bus); pci_bus_add_devices(new_bus);
pci_unlock_rescan_remove();
mutex_unlock(&sn_hotplug_mutex); mutex_unlock(&sn_hotplug_mutex);
if (rc == 0) if (rc == 0)
...@@ -540,6 +543,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) ...@@ -540,6 +543,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
acpi_scan_lock_release(); acpi_scan_lock_release();
} }
pci_lock_rescan_remove();
/* Free the SN resources assigned to the Linux device.*/ /* Free the SN resources assigned to the Linux device.*/
list_for_each_entry_safe(dev, temp, &slot->pci_bus->devices, bus_list) { list_for_each_entry_safe(dev, temp, &slot->pci_bus->devices, bus_list) {
if (PCI_SLOT(dev->devfn) != slot->device_num + 1) if (PCI_SLOT(dev->devfn) != slot->device_num + 1)
...@@ -550,6 +554,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) ...@@ -550,6 +554,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
pci_stop_and_remove_bus_device(dev); pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev); pci_dev_put(dev);
} }
pci_unlock_rescan_remove();
/* Remove the SSDT for the slot from the ACPI namespace */ /* Remove the SSDT for the slot from the ACPI namespace */
if (SN_ACPI_BASE_SUPPORT() && ssdt_id) { if (SN_ACPI_BASE_SUPPORT() && ssdt_id) {
......
...@@ -40,7 +40,9 @@ int __ref shpchp_configure_device(struct slot *p_slot) ...@@ -40,7 +40,9 @@ int __ref shpchp_configure_device(struct slot *p_slot)
struct controller *ctrl = p_slot->ctrl; struct controller *ctrl = p_slot->ctrl;
struct pci_dev *bridge = ctrl->pci_dev; struct pci_dev *bridge = ctrl->pci_dev;
struct pci_bus *parent = bridge->subordinate; struct pci_bus *parent = bridge->subordinate;
int num; int num, ret = 0;
pci_lock_rescan_remove();
dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0)); dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0));
if (dev) { if (dev) {
...@@ -48,13 +50,15 @@ int __ref shpchp_configure_device(struct slot *p_slot) ...@@ -48,13 +50,15 @@ int __ref shpchp_configure_device(struct slot *p_slot)
"at %04x:%02x:%02x, cannot hot-add\n", pci_name(dev), "at %04x:%02x:%02x, cannot hot-add\n", pci_name(dev),
pci_domain_nr(parent), p_slot->bus, p_slot->device); pci_domain_nr(parent), p_slot->bus, p_slot->device);
pci_dev_put(dev); pci_dev_put(dev);
return -EINVAL; ret = -EINVAL;
goto out;
} }
num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0)); num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0));
if (num == 0) { if (num == 0) {
ctrl_err(ctrl, "No new device found\n"); ctrl_err(ctrl, "No new device found\n");
return -ENODEV; ret = -ENODEV;
goto out;
} }
list_for_each_entry(dev, &parent->devices, bus_list) { list_for_each_entry(dev, &parent->devices, bus_list) {
...@@ -75,7 +79,9 @@ int __ref shpchp_configure_device(struct slot *p_slot) ...@@ -75,7 +79,9 @@ int __ref shpchp_configure_device(struct slot *p_slot)
pci_bus_add_devices(parent); pci_bus_add_devices(parent);
return 0; out:
pci_unlock_rescan_remove();
return ret;
} }
int shpchp_unconfigure_device(struct slot *p_slot) int shpchp_unconfigure_device(struct slot *p_slot)
...@@ -89,6 +95,8 @@ int shpchp_unconfigure_device(struct slot *p_slot) ...@@ -89,6 +95,8 @@ int shpchp_unconfigure_device(struct slot *p_slot)
ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:%02x\n", ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:%02x\n",
__func__, pci_domain_nr(parent), p_slot->bus, p_slot->device); __func__, pci_domain_nr(parent), p_slot->bus, p_slot->device);
pci_lock_rescan_remove();
list_for_each_entry_safe(dev, temp, &parent->devices, bus_list) { list_for_each_entry_safe(dev, temp, &parent->devices, bus_list) {
if (PCI_SLOT(dev->devfn) != p_slot->device) if (PCI_SLOT(dev->devfn) != p_slot->device)
continue; continue;
...@@ -108,6 +116,8 @@ int shpchp_unconfigure_device(struct slot *p_slot) ...@@ -108,6 +116,8 @@ int shpchp_unconfigure_device(struct slot *p_slot)
pci_stop_and_remove_bus_device(dev); pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev); pci_dev_put(dev);
} }
pci_unlock_rescan_remove();
return rc; return rc;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment