powerpc/eeh: Use global PCI rescan-remove locking

Race conditions are theoretically possible between the PCI device addition
and removal in the PPC64 PCI error recovery driver and the generic PCI bus
rescan and device removal that can be triggered via sysfs.

To avoid those race conditions make PPC64 PCI error recovery driver use
global PCI rescan-remove locking around PCI device addition and removal.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
Rafael J. Wysocki 2014-01-15 14:33:20 +01:00 коммит произвёл Bjorn Helgaas
Родитель 64cdb4181d
Коммит 1c2042c83a
1 изменённых файлов: 16 добавлений и 3 удалений

Просмотреть файл

@ -369,7 +369,9 @@ static void *eeh_rmv_device(void *data, void *userdata)
edev->mode |= EEH_DEV_DISCONNECTED; edev->mode |= EEH_DEV_DISCONNECTED;
(*removed)++; (*removed)++;
pci_lock_rescan_remove();
pci_stop_and_remove_bus_device(dev); pci_stop_and_remove_bus_device(dev);
pci_unlock_rescan_remove();
return NULL; return NULL;
} }
@ -416,10 +418,13 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
* into pcibios_add_pci_devices(). * into pcibios_add_pci_devices().
*/ */
eeh_pe_state_mark(pe, EEH_PE_KEEP); eeh_pe_state_mark(pe, EEH_PE_KEEP);
if (bus) if (bus) {
pci_lock_rescan_remove();
pcibios_remove_pci_devices(bus); pcibios_remove_pci_devices(bus);
else if (frozen_bus) pci_unlock_rescan_remove();
} else if (frozen_bus) {
eeh_pe_dev_traverse(pe, eeh_rmv_device, &removed); eeh_pe_dev_traverse(pe, eeh_rmv_device, &removed);
}
/* Reset the pci controller. (Asserts RST#; resets config space). /* Reset the pci controller. (Asserts RST#; resets config space).
* Reconfigure bridges and devices. Don't try to bring the system * Reconfigure bridges and devices. Don't try to bring the system
@ -429,6 +434,8 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
if (rc) if (rc)
return rc; return rc;
pci_lock_rescan_remove();
/* Restore PE */ /* Restore PE */
eeh_ops->configure_bridge(pe); eeh_ops->configure_bridge(pe);
eeh_pe_restore_bars(pe); eeh_pe_restore_bars(pe);
@ -462,6 +469,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
pe->tstamp = tstamp; pe->tstamp = tstamp;
pe->freeze_count = cnt; pe->freeze_count = cnt;
pci_unlock_rescan_remove();
return 0; return 0;
} }
@ -618,8 +626,11 @@ perm_error:
eeh_pe_dev_traverse(pe, eeh_report_failure, NULL); eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
/* Shut down the device drivers for good. */ /* Shut down the device drivers for good. */
if (frozen_bus) if (frozen_bus) {
pci_lock_rescan_remove();
pcibios_remove_pci_devices(frozen_bus); pcibios_remove_pci_devices(frozen_bus);
pci_unlock_rescan_remove();
}
} }
static void eeh_handle_special_event(void) static void eeh_handle_special_event(void)
@ -692,6 +703,7 @@ static void eeh_handle_special_event(void)
if (rc == 2 || rc == 1) if (rc == 2 || rc == 1)
eeh_handle_normal_event(pe); eeh_handle_normal_event(pe);
else { else {
pci_lock_rescan_remove();
list_for_each_entry_safe(hose, tmp, list_for_each_entry_safe(hose, tmp,
&hose_list, list_node) { &hose_list, list_node) {
phb_pe = eeh_phb_pe_get(hose); phb_pe = eeh_phb_pe_get(hose);
@ -703,6 +715,7 @@ static void eeh_handle_special_event(void)
eeh_pe_dev_traverse(pe, eeh_report_failure, NULL); eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
pcibios_remove_pci_devices(bus); pcibios_remove_pci_devices(bus);
} }
pci_unlock_rescan_remove();
} }
} }