summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@rjwysocki.net>2014-01-15 14:33:20 +0100
committerBjorn Helgaas <bhelgaas@google.com>2014-01-15 10:31:29 -0700
commit1c2042c83aa7af10579b71a1fe5e22bbae69f08c (patch)
treedaba13b2d79d608c8d3cf28ca183eb0cc879c8d3 /arch/powerpc/kernel
parent64cdb4181d87900c6560f8c72d02c7ccf75c9070 (diff)
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>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/eeh_driver.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 36bed5a1275..c17f90d0f73 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -369,7 +369,9 @@ static void *eeh_rmv_device(void *data, void *userdata)
edev->mode |= EEH_DEV_DISCONNECTED;
(*removed)++;
+ pci_lock_rescan_remove();
pci_stop_and_remove_bus_device(dev);
+ pci_unlock_rescan_remove();
return NULL;
}
@@ -416,10 +418,13 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
* into pcibios_add_pci_devices().
*/
eeh_pe_state_mark(pe, EEH_PE_KEEP);
- if (bus)
+ if (bus) {
+ pci_lock_rescan_remove();
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);
+ }
/* Reset the pci controller. (Asserts RST#; resets config space).
* 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)
return rc;
+ pci_lock_rescan_remove();
+
/* Restore PE */
eeh_ops->configure_bridge(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->freeze_count = cnt;
+ pci_unlock_rescan_remove();
return 0;
}
@@ -618,8 +626,11 @@ perm_error:
eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
/* Shut down the device drivers for good. */
- if (frozen_bus)
+ if (frozen_bus) {
+ pci_lock_rescan_remove();
pcibios_remove_pci_devices(frozen_bus);
+ pci_unlock_rescan_remove();
+ }
}
static void eeh_handle_special_event(void)
@@ -692,6 +703,7 @@ static void eeh_handle_special_event(void)
if (rc == 2 || rc == 1)
eeh_handle_normal_event(pe);
else {
+ pci_lock_rescan_remove();
list_for_each_entry_safe(hose, tmp,
&hose_list, list_node) {
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);
pcibios_remove_pci_devices(bus);
}
+ pci_unlock_rescan_remove();
}
}