aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGavin Shan <shangw@linux.vnet.ibm.com>2013-07-23 22:24:55 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-07-24 00:18:47 -0400
commit807a827d4e7455a40e8f56ec2a67c57a91cab9f7 (patch)
tree35cfa0bc469d4fe436a81c7d9cc50966927625cf
parentc7b51bce636e4990662bb100bc17e1d4d6c02d34 (diff)
powerpc/eeh: Keep PE during hotplug
When we do normal hotplug, the PE (shadow EEH structure) shouldn't be kept around. However, we need to keep it if the hotplug an artifial one caused by EEH errors recovery. Since we remove EEH device through the PCI hook pcibios_release_device(), the flag "purge_pe" passed to various functions is meaningless. So the patch removes the meaningless flag and introduce new flag "EEH_PE_KEEP" to save the PE while doing hotplug during EEH error recovery. Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/include/asm/eeh.h11
-rw-r--r--arch/powerpc/include/asm/pci-bridge.h1
-rw-r--r--arch/powerpc/kernel/eeh.c28
-rw-r--r--arch/powerpc/kernel/eeh_driver.c7
-rw-r--r--arch/powerpc/kernel/eeh_pe.c7
-rw-r--r--arch/powerpc/kernel/pci-hotplug.c26
6 files changed, 20 insertions, 60 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index d9d35c27de25..2ce22d7b71a0 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -55,6 +55,8 @@ struct device_node;
55#define EEH_PE_RECOVERING (1 << 1) /* Recovering PE */ 55#define EEH_PE_RECOVERING (1 << 1) /* Recovering PE */
56#define EEH_PE_PHB_DEAD (1 << 2) /* Dead PHB */ 56#define EEH_PE_PHB_DEAD (1 << 2) /* Dead PHB */
57 57
58#define EEH_PE_KEEP (1 << 8) /* Keep PE on hotplug */
59
58struct eeh_pe { 60struct eeh_pe {
59 int type; /* PE type: PHB/Bus/Device */ 61 int type; /* PE type: PHB/Bus/Device */
60 int state; /* PE EEH dependent mode */ 62 int state; /* PE EEH dependent mode */
@@ -193,7 +195,7 @@ int eeh_phb_pe_create(struct pci_controller *phb);
193struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb); 195struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb);
194struct eeh_pe *eeh_pe_get(struct eeh_dev *edev); 196struct eeh_pe *eeh_pe_get(struct eeh_dev *edev);
195int eeh_add_to_parent_pe(struct eeh_dev *edev); 197int eeh_add_to_parent_pe(struct eeh_dev *edev);
196int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe); 198int eeh_rmv_from_parent_pe(struct eeh_dev *edev);
197void eeh_pe_update_time_stamp(struct eeh_pe *pe); 199void eeh_pe_update_time_stamp(struct eeh_pe *pe);
198void *eeh_pe_dev_traverse(struct eeh_pe *root, 200void *eeh_pe_dev_traverse(struct eeh_pe *root,
199 eeh_traverse_func fn, void *flag); 201 eeh_traverse_func fn, void *flag);
@@ -214,8 +216,7 @@ void eeh_add_device_tree_early(struct device_node *);
214void eeh_add_device_late(struct pci_dev *); 216void eeh_add_device_late(struct pci_dev *);
215void eeh_add_device_tree_late(struct pci_bus *); 217void eeh_add_device_tree_late(struct pci_bus *);
216void eeh_add_sysfs_files(struct pci_bus *); 218void eeh_add_sysfs_files(struct pci_bus *);
217void eeh_remove_device(struct pci_dev *, int); 219void eeh_remove_device(struct pci_dev *);
218void eeh_remove_bus_device(struct pci_dev *, int);
219 220
220/** 221/**
221 * EEH_POSSIBLE_ERROR() -- test for possible MMIO failure. 222 * EEH_POSSIBLE_ERROR() -- test for possible MMIO failure.
@@ -265,9 +266,7 @@ static inline void eeh_add_device_tree_late(struct pci_bus *bus) { }
265 266
266static inline void eeh_add_sysfs_files(struct pci_bus *bus) { } 267static inline void eeh_add_sysfs_files(struct pci_bus *bus) { }
267 268
268static inline void eeh_remove_device(struct pci_dev *dev, int purge_pe) { } 269static inline void eeh_remove_device(struct pci_dev *dev) { }
269
270static inline void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe) { }
271 270
272#define EEH_POSSIBLE_ERROR(val, type) (0) 271#define EEH_POSSIBLE_ERROR(val, type) (0)
273#define EEH_IO_ERROR_VALUE(size) (-1UL) 272#define EEH_IO_ERROR_VALUE(size) (-1UL)
diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h
index 2c1d8cb9b265..32d0d2018faf 100644
--- a/arch/powerpc/include/asm/pci-bridge.h
+++ b/arch/powerpc/include/asm/pci-bridge.h
@@ -209,7 +209,6 @@ static inline struct eeh_dev *of_node_to_eeh_dev(struct device_node *dn)
209extern struct pci_bus *pcibios_find_pci_bus(struct device_node *dn); 209extern struct pci_bus *pcibios_find_pci_bus(struct device_node *dn);
210 210
211/** Remove all of the PCI devices under this bus */ 211/** Remove all of the PCI devices under this bus */
212extern void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe);
213extern void pcibios_remove_pci_devices(struct pci_bus *bus); 212extern void pcibios_remove_pci_devices(struct pci_bus *bus);
214 213
215/** Discover new pci devices under this bus, and add them */ 214/** Discover new pci devices under this bus, and add them */
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 582ad1ef46a8..ce81477316be 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -964,7 +964,6 @@ EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
964/** 964/**
965 * eeh_remove_device - Undo EEH setup for the indicated pci device 965 * eeh_remove_device - Undo EEH setup for the indicated pci device
966 * @dev: pci device to be removed 966 * @dev: pci device to be removed
967 * @purge_pe: remove the PE or not
968 * 967 *
969 * This routine should be called when a device is removed from 968 * This routine should be called when a device is removed from
970 * a running system (e.g. by hotplug or dlpar). It unregisters 969 * a running system (e.g. by hotplug or dlpar). It unregisters
@@ -972,7 +971,7 @@ EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
972 * this device will no longer be detected after this call; thus, 971 * this device will no longer be detected after this call; thus,
973 * i/o errors affecting this slot may leave this device unusable. 972 * i/o errors affecting this slot may leave this device unusable.
974 */ 973 */
975void eeh_remove_device(struct pci_dev *dev, int purge_pe) 974void eeh_remove_device(struct pci_dev *dev)
976{ 975{
977 struct eeh_dev *edev; 976 struct eeh_dev *edev;
978 977
@@ -990,34 +989,11 @@ void eeh_remove_device(struct pci_dev *dev, int purge_pe)
990 edev->pdev = NULL; 989 edev->pdev = NULL;
991 dev->dev.archdata.edev = NULL; 990 dev->dev.archdata.edev = NULL;
992 991
993 eeh_rmv_from_parent_pe(edev, purge_pe); 992 eeh_rmv_from_parent_pe(edev);
994 eeh_addr_cache_rmv_dev(dev); 993 eeh_addr_cache_rmv_dev(dev);
995 eeh_sysfs_remove_device(dev); 994 eeh_sysfs_remove_device(dev);
996} 995}
997 996
998/**
999 * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
1000 * @dev: PCI device
1001 * @purge_pe: remove the corresponding PE or not
1002 *
1003 * This routine must be called when a device is removed from the
1004 * running system through hotplug or dlpar. The corresponding
1005 * PCI address cache will be removed.
1006 */
1007void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe)
1008{
1009 struct pci_bus *bus = dev->subordinate;
1010 struct pci_dev *child, *tmp;
1011
1012 eeh_remove_device(dev, purge_pe);
1013
1014 if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
1015 list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
1016 eeh_remove_bus_device(child, purge_pe);
1017 }
1018}
1019EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
1020
1021static int proc_eeh_show(struct seq_file *m, void *v) 997static int proc_eeh_show(struct seq_file *m, void *v)
1022{ 998{
1023 if (0 == eeh_subsystem_enabled) { 999 if (0 == eeh_subsystem_enabled) {
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 2b1ce17cae50..9ef3bbb8580a 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -362,8 +362,10 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
362 * devices are expected to be attached soon when calling 362 * devices are expected to be attached soon when calling
363 * into pcibios_add_pci_devices(). 363 * into pcibios_add_pci_devices().
364 */ 364 */
365 if (bus) 365 if (bus) {
366 __pcibios_remove_pci_devices(bus, 0); 366 eeh_pe_state_mark(pe, EEH_PE_KEEP);
367 pcibios_remove_pci_devices(bus);
368 }
367 369
368 /* Reset the pci controller. (Asserts RST#; resets config space). 370 /* Reset the pci controller. (Asserts RST#; resets config space).
369 * Reconfigure bridges and devices. Don't try to bring the system 371 * Reconfigure bridges and devices. Don't try to bring the system
@@ -386,6 +388,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
386 if (bus) { 388 if (bus) {
387 ssleep(5); 389 ssleep(5);
388 pcibios_add_pci_devices(bus); 390 pcibios_add_pci_devices(bus);
391 eeh_pe_state_clear(pe, EEH_PE_KEEP);
389 } 392 }
390 393
391 pe->tstamp = tstamp; 394 pe->tstamp = tstamp;
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index 016588a6f5ed..32ef40940bad 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -333,7 +333,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
333 while (parent) { 333 while (parent) {
334 if (!(parent->type & EEH_PE_INVALID)) 334 if (!(parent->type & EEH_PE_INVALID))
335 break; 335 break;
336 parent->type &= ~EEH_PE_INVALID; 336 parent->type &= ~(EEH_PE_INVALID | EEH_PE_KEEP);
337 parent = parent->parent; 337 parent = parent->parent;
338 } 338 }
339 pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", 339 pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
@@ -397,14 +397,13 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
397/** 397/**
398 * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE 398 * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
399 * @edev: EEH device 399 * @edev: EEH device
400 * @purge_pe: remove PE or not
401 * 400 *
402 * The PE hierarchy tree might be changed when doing PCI hotplug. 401 * The PE hierarchy tree might be changed when doing PCI hotplug.
403 * Also, the PCI devices or buses could be removed from the system 402 * Also, the PCI devices or buses could be removed from the system
404 * during EEH recovery. So we have to call the function remove the 403 * during EEH recovery. So we have to call the function remove the
405 * corresponding PE accordingly if necessary. 404 * corresponding PE accordingly if necessary.
406 */ 405 */
407int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) 406int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
408{ 407{
409 struct eeh_pe *pe, *parent, *child; 408 struct eeh_pe *pe, *parent, *child;
410 int cnt; 409 int cnt;
@@ -431,7 +430,7 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
431 if (pe->type & EEH_PE_PHB) 430 if (pe->type & EEH_PE_PHB)
432 break; 431 break;
433 432
434 if (purge_pe) { 433 if (!(pe->state & EEH_PE_KEEP)) {
435 if (list_empty(&pe->edevs) && 434 if (list_empty(&pe->edevs) &&
436 list_empty(&pe->child_list)) { 435 list_empty(&pe->child_list)) {
437 list_del(&pe->child); 436 list_del(&pe->child);
diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c
index 3dab2f2801b9..fc0831d4971f 100644
--- a/arch/powerpc/kernel/pci-hotplug.c
+++ b/arch/powerpc/kernel/pci-hotplug.c
@@ -29,49 +29,33 @@
29 */ 29 */
30void pcibios_release_device(struct pci_dev *dev) 30void pcibios_release_device(struct pci_dev *dev)
31{ 31{
32 eeh_remove_device(dev, 1); 32 eeh_remove_device(dev);
33} 33}
34 34
35/** 35/**
36 * __pcibios_remove_pci_devices - remove all devices under this bus 36 * pcibios_remove_pci_devices - remove all devices under this bus
37 * @bus: the indicated PCI bus 37 * @bus: the indicated PCI bus
38 * @purge_pe: destroy the PE on removal of PCI devices
39 * 38 *
40 * Remove all of the PCI devices under this bus both from the 39 * Remove all of the PCI devices under this bus both from the
41 * linux pci device tree, and from the powerpc EEH address cache. 40 * linux pci device tree, and from the powerpc EEH address cache.
42 * By default, the corresponding PE will be destroied during the
43 * normal PCI hotplug path. For PCI hotplug during EEH recovery,
44 * the corresponding PE won't be destroied and deallocated.
45 */ 41 */
46void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe) 42void pcibios_remove_pci_devices(struct pci_bus *bus)
47{ 43{
48 struct pci_dev *dev, *tmp; 44 struct pci_dev *dev, *tmp;
49 struct pci_bus *child_bus; 45 struct pci_bus *child_bus;
50 46
51 /* First go down child busses */ 47 /* First go down child busses */
52 list_for_each_entry(child_bus, &bus->children, node) 48 list_for_each_entry(child_bus, &bus->children, node)
53 __pcibios_remove_pci_devices(child_bus, purge_pe); 49 pcibios_remove_pci_devices(child_bus);
54 50
55 pr_debug("PCI: Removing devices on bus %04x:%02x\n", 51 pr_debug("PCI: Removing devices on bus %04x:%02x\n",
56 pci_domain_nr(bus), bus->number); 52 pci_domain_nr(bus), bus->number);
57 list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { 53 list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
58 pr_debug(" * Removing %s...\n", pci_name(dev)); 54 pr_debug(" Removing %s...\n", pci_name(dev));
59 eeh_remove_bus_device(dev, purge_pe);
60 pci_stop_and_remove_bus_device(dev); 55 pci_stop_and_remove_bus_device(dev);
61 } 56 }
62} 57}
63 58
64/**
65 * pcibios_remove_pci_devices - remove all devices under this bus
66 * @bus: the indicated PCI bus
67 *
68 * Remove all of the PCI devices under this bus both from the
69 * linux pci device tree, and from the powerpc EEH address cache.
70 */
71void pcibios_remove_pci_devices(struct pci_bus *bus)
72{
73 __pcibios_remove_pci_devices(bus, 1);
74}
75EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); 59EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
76 60
77/** 61/**