aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorGavin Shan <shangw@linux.vnet.ibm.com>2012-09-11 15:16:17 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-09-18 01:32:23 -0400
commit20ee6a970858a0f5711ea32cb7f855a81704cb53 (patch)
treeb09241fc593a30f494d2bad75ae0e704465b995b /arch
parent5efc3ad7325d81b5a8e09bc14d5e21ce7272d934 (diff)
powerpc/eeh: Remove EEH PE for normal PCI hotplug
Function eeh_rmv_from_parent_pe() could be called by the path of either normal PCI hotplug, or EEH recovery. For the former case, we need purge the corresponding PE on removal of the associated PE bus. The patch tries to cover that by passing more information to function pcibios_remove_pci_devices() so that we know if the corresponding PE needs to be purged or be marked as "invalid". Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/include/asm/eeh.h6
-rw-r--r--arch/powerpc/include/asm/pci-bridge.h1
-rw-r--r--arch/powerpc/platforms/pseries/eeh.c12
-rw-r--r--arch/powerpc/platforms/pseries/eeh_driver.c8
-rw-r--r--arch/powerpc/platforms/pseries/eeh_pe.c35
-rw-r--r--arch/powerpc/platforms/pseries/pci_dlpar.c32
6 files changed, 65 insertions, 29 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index afeb40086fb2..b0ef73882b38 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -185,7 +185,7 @@ static inline void eeh_unlock(void)
185typedef void *(*eeh_traverse_func)(void *data, void *flag); 185typedef void *(*eeh_traverse_func)(void *data, void *flag);
186int __devinit eeh_phb_pe_create(struct pci_controller *phb); 186int __devinit eeh_phb_pe_create(struct pci_controller *phb);
187int eeh_add_to_parent_pe(struct eeh_dev *edev); 187int eeh_add_to_parent_pe(struct eeh_dev *edev);
188int eeh_rmv_from_parent_pe(struct eeh_dev *edev); 188int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe);
189void *eeh_pe_dev_traverse(struct eeh_pe *root, 189void *eeh_pe_dev_traverse(struct eeh_pe *root,
190 eeh_traverse_func fn, void *flag); 190 eeh_traverse_func fn, void *flag);
191void eeh_pe_restore_bars(struct eeh_pe *pe); 191void eeh_pe_restore_bars(struct eeh_pe *pe);
@@ -201,7 +201,7 @@ int eeh_dev_check_failure(struct eeh_dev *edev);
201void __init eeh_addr_cache_build(void); 201void __init eeh_addr_cache_build(void);
202void eeh_add_device_tree_early(struct device_node *); 202void eeh_add_device_tree_early(struct device_node *);
203void eeh_add_device_tree_late(struct pci_bus *); 203void eeh_add_device_tree_late(struct pci_bus *);
204void eeh_remove_bus_device(struct pci_dev *); 204void eeh_remove_bus_device(struct pci_dev *, int);
205 205
206/** 206/**
207 * EEH_POSSIBLE_ERROR() -- test for possible MMIO failure. 207 * EEH_POSSIBLE_ERROR() -- test for possible MMIO failure.
@@ -240,7 +240,7 @@ static inline void eeh_add_device_tree_early(struct device_node *dn) { }
240 240
241static inline void eeh_add_device_tree_late(struct pci_bus *bus) { } 241static inline void eeh_add_device_tree_late(struct pci_bus *bus) { }
242 242
243static inline void eeh_remove_bus_device(struct pci_dev *dev) { } 243static inline void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe) { }
244 244
245static inline void eeh_lock(void) { } 245static inline void eeh_lock(void) { }
246static inline void eeh_unlock(void) { } 246static inline void eeh_unlock(void) { }
diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h
index 973df4d9d366..a059cb94952b 100644
--- a/arch/powerpc/include/asm/pci-bridge.h
+++ b/arch/powerpc/include/asm/pci-bridge.h
@@ -192,6 +192,7 @@ static inline struct eeh_dev *of_node_to_eeh_dev(struct device_node *dn)
192extern struct pci_bus *pcibios_find_pci_bus(struct device_node *dn); 192extern struct pci_bus *pcibios_find_pci_bus(struct device_node *dn);
193 193
194/** Remove all of the PCI devices under this bus */ 194/** Remove all of the PCI devices under this bus */
195extern void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe);
195extern void pcibios_remove_pci_devices(struct pci_bus *bus); 196extern void pcibios_remove_pci_devices(struct pci_bus *bus);
196 197
197/** Discover new pci devices under this bus, and add them */ 198/** Discover new pci devices under this bus, and add them */
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 18c168b752da..43f6ed415564 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -817,6 +817,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
817/** 817/**
818 * eeh_remove_device - Undo EEH setup for the indicated pci device 818 * eeh_remove_device - Undo EEH setup for the indicated pci device
819 * @dev: pci device to be removed 819 * @dev: pci device to be removed
820 * @purge_pe: remove the PE or not
820 * 821 *
821 * This routine should be called when a device is removed from 822 * This routine should be called when a device is removed from
822 * a running system (e.g. by hotplug or dlpar). It unregisters 823 * a running system (e.g. by hotplug or dlpar). It unregisters
@@ -824,7 +825,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
824 * this device will no longer be detected after this call; thus, 825 * this device will no longer be detected after this call; thus,
825 * i/o errors affecting this slot may leave this device unusable. 826 * i/o errors affecting this slot may leave this device unusable.
826 */ 827 */
827static void eeh_remove_device(struct pci_dev *dev) 828static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
828{ 829{
829 struct eeh_dev *edev; 830 struct eeh_dev *edev;
830 831
@@ -843,7 +844,7 @@ static void eeh_remove_device(struct pci_dev *dev)
843 dev->dev.archdata.edev = NULL; 844 dev->dev.archdata.edev = NULL;
844 pci_dev_put(dev); 845 pci_dev_put(dev);
845 846
846 eeh_rmv_from_parent_pe(edev); 847 eeh_rmv_from_parent_pe(edev, purge_pe);
847 eeh_addr_cache_rmv_dev(dev); 848 eeh_addr_cache_rmv_dev(dev);
848 eeh_sysfs_remove_device(dev); 849 eeh_sysfs_remove_device(dev);
849} 850}
@@ -851,21 +852,22 @@ static void eeh_remove_device(struct pci_dev *dev)
851/** 852/**
852 * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device 853 * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
853 * @dev: PCI device 854 * @dev: PCI device
855 * @purge_pe: remove the corresponding PE or not
854 * 856 *
855 * This routine must be called when a device is removed from the 857 * This routine must be called when a device is removed from the
856 * running system through hotplug or dlpar. The corresponding 858 * running system through hotplug or dlpar. The corresponding
857 * PCI address cache will be removed. 859 * PCI address cache will be removed.
858 */ 860 */
859void eeh_remove_bus_device(struct pci_dev *dev) 861void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe)
860{ 862{
861 struct pci_bus *bus = dev->subordinate; 863 struct pci_bus *bus = dev->subordinate;
862 struct pci_dev *child, *tmp; 864 struct pci_dev *child, *tmp;
863 865
864 eeh_remove_device(dev); 866 eeh_remove_device(dev, purge_pe);
865 867
866 if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { 868 if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
867 list_for_each_entry_safe(child, tmp, &bus->devices, bus_list) 869 list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
868 eeh_remove_bus_device(child); 870 eeh_remove_bus_device(child, purge_pe);
869 } 871 }
870} 872}
871EXPORT_SYMBOL_GPL(eeh_remove_bus_device); 873EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index 8370ce7d5931..37c2cf743133 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -305,8 +305,14 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
305 /* pcibios will clear the counter; save the value */ 305 /* pcibios will clear the counter; save the value */
306 cnt = pe->freeze_count; 306 cnt = pe->freeze_count;
307 307
308 /*
309 * We don't remove the corresponding PE instances because
310 * we need the information afterwords. The attached EEH
311 * devices are expected to be attached soon when calling
312 * into pcibios_add_pci_devices().
313 */
308 if (bus) 314 if (bus)
309 pcibios_remove_pci_devices(bus); 315 __pcibios_remove_pci_devices(bus, 0);
310 316
311 /* Reset the pci controller. (Asserts RST#; resets config space). 317 /* Reset the pci controller. (Asserts RST#; resets config space).
312 * Reconfigure bridges and devices. Don't try to bring the system 318 * Reconfigure bridges and devices. Don't try to bring the system
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c
index 51fc56abb7a2..30b8d96ed54d 100644
--- a/arch/powerpc/platforms/pseries/eeh_pe.c
+++ b/arch/powerpc/platforms/pseries/eeh_pe.c
@@ -395,13 +395,14 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
395/** 395/**
396 * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE 396 * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
397 * @edev: EEH device 397 * @edev: EEH device
398 * @purge_pe: remove PE or not
398 * 399 *
399 * The PE hierarchy tree might be changed when doing PCI hotplug. 400 * The PE hierarchy tree might be changed when doing PCI hotplug.
400 * Also, the PCI devices or buses could be removed from the system 401 * Also, the PCI devices or buses could be removed from the system
401 * during EEH recovery. So we have to call the function remove the 402 * during EEH recovery. So we have to call the function remove the
402 * corresponding PE accordingly if necessary. 403 * corresponding PE accordingly if necessary.
403 */ 404 */
404int eeh_rmv_from_parent_pe(struct eeh_dev *edev) 405int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
405{ 406{
406 struct eeh_pe *pe, *parent, *child; 407 struct eeh_pe *pe, *parent, *child;
407 int cnt; 408 int cnt;
@@ -428,19 +429,29 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
428 if (pe->type & EEH_PE_PHB) 429 if (pe->type & EEH_PE_PHB)
429 break; 430 break;
430 431
431 if (list_empty(&pe->edevs)) { 432 if (purge_pe) {
432 cnt = 0; 433 if (list_empty(&pe->edevs) &&
433 list_for_each_entry(child, &pe->child_list, child) { 434 list_empty(&pe->child_list)) {
434 if (!(pe->type & EEH_PE_INVALID)) { 435 list_del(&pe->child);
435 cnt++; 436 kfree(pe);
436 break; 437 } else {
437 } 438 break;
438 } 439 }
440 } else {
441 if (list_empty(&pe->edevs)) {
442 cnt = 0;
443 list_for_each_entry(child, &pe->child_list, child) {
444 if (!(pe->type & EEH_PE_INVALID)) {
445 cnt++;
446 break;
447 }
448 }
439 449
440 if (!cnt) 450 if (!cnt)
441 pe->type |= EEH_PE_INVALID; 451 pe->type |= EEH_PE_INVALID;
442 else 452 else
443 break; 453 break;
454 }
444 } 455 }
445 456
446 pe = parent; 457 pe = parent;
diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c
index 3ccebc83dc02..261a577a3dd2 100644
--- a/arch/powerpc/platforms/pseries/pci_dlpar.c
+++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
@@ -65,27 +65,43 @@ pcibios_find_pci_bus(struct device_node *dn)
65EXPORT_SYMBOL_GPL(pcibios_find_pci_bus); 65EXPORT_SYMBOL_GPL(pcibios_find_pci_bus);
66 66
67/** 67/**
68 * pcibios_remove_pci_devices - remove all devices under this bus 68 * __pcibios_remove_pci_devices - remove all devices under this bus
69 * @bus: the indicated PCI bus
70 * @purge_pe: destroy the PE on removal of PCI devices
69 * 71 *
70 * Remove all of the PCI devices under this bus both from the 72 * Remove all of the PCI devices under this bus both from the
71 * linux pci device tree, and from the powerpc EEH address cache. 73 * linux pci device tree, and from the powerpc EEH address cache.
74 * By default, the corresponding PE will be destroied during the
75 * normal PCI hotplug path. For PCI hotplug during EEH recovery,
76 * the corresponding PE won't be destroied and deallocated.
72 */ 77 */
73void pcibios_remove_pci_devices(struct pci_bus *bus) 78void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe)
74{ 79{
75 struct pci_dev *dev, *tmp; 80 struct pci_dev *dev, *tmp;
76 struct pci_bus *child_bus; 81 struct pci_bus *child_bus;
77 82
78 /* First go down child busses */ 83 /* First go down child busses */
79 list_for_each_entry(child_bus, &bus->children, node) 84 list_for_each_entry(child_bus, &bus->children, node)
80 pcibios_remove_pci_devices(child_bus); 85 __pcibios_remove_pci_devices(child_bus, purge_pe);
81 86
82 pr_debug("PCI: Removing devices on bus %04x:%02x\n", 87 pr_debug("PCI: Removing devices on bus %04x:%02x\n",
83 pci_domain_nr(bus), bus->number); 88 pci_domain_nr(bus), bus->number);
84 list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { 89 list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
85 pr_debug(" * Removing %s...\n", pci_name(dev)); 90 pr_debug(" * Removing %s...\n", pci_name(dev));
86 eeh_remove_bus_device(dev); 91 eeh_remove_bus_device(dev, purge_pe);
87 pci_stop_and_remove_bus_device(dev); 92 pci_stop_and_remove_bus_device(dev);
88 } 93 }
94}
95
96/**
97 * pcibios_remove_pci_devices - remove all devices under this bus
98 *
99 * Remove all of the PCI devices under this bus both from the
100 * linux pci device tree, and from the powerpc EEH address cache.
101 */
102void pcibios_remove_pci_devices(struct pci_bus *bus)
103{
104 __pcibios_remove_pci_devices(bus, 1);
89} 105}
90EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); 106EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
91 107