diff options
author | Gavin Shan <shangw@linux.vnet.ibm.com> | 2013-07-23 22:24:55 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-07-24 00:18:47 -0400 |
commit | 807a827d4e7455a40e8f56ec2a67c57a91cab9f7 (patch) | |
tree | 35cfa0bc469d4fe436a81c7d9cc50966927625cf | |
parent | c7b51bce636e4990662bb100bc17e1d4d6c02d34 (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.h | 11 | ||||
-rw-r--r-- | arch/powerpc/include/asm/pci-bridge.h | 1 | ||||
-rw-r--r-- | arch/powerpc/kernel/eeh.c | 28 | ||||
-rw-r--r-- | arch/powerpc/kernel/eeh_driver.c | 7 | ||||
-rw-r--r-- | arch/powerpc/kernel/eeh_pe.c | 7 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci-hotplug.c | 26 |
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 | |||
58 | struct eeh_pe { | 60 | struct 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); | |||
193 | struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb); | 195 | struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb); |
194 | struct eeh_pe *eeh_pe_get(struct eeh_dev *edev); | 196 | struct eeh_pe *eeh_pe_get(struct eeh_dev *edev); |
195 | int eeh_add_to_parent_pe(struct eeh_dev *edev); | 197 | int eeh_add_to_parent_pe(struct eeh_dev *edev); |
196 | int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe); | 198 | int eeh_rmv_from_parent_pe(struct eeh_dev *edev); |
197 | void eeh_pe_update_time_stamp(struct eeh_pe *pe); | 199 | void eeh_pe_update_time_stamp(struct eeh_pe *pe); |
198 | void *eeh_pe_dev_traverse(struct eeh_pe *root, | 200 | void *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 *); | |||
214 | void eeh_add_device_late(struct pci_dev *); | 216 | void eeh_add_device_late(struct pci_dev *); |
215 | void eeh_add_device_tree_late(struct pci_bus *); | 217 | void eeh_add_device_tree_late(struct pci_bus *); |
216 | void eeh_add_sysfs_files(struct pci_bus *); | 218 | void eeh_add_sysfs_files(struct pci_bus *); |
217 | void eeh_remove_device(struct pci_dev *, int); | 219 | void eeh_remove_device(struct pci_dev *); |
218 | void 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 | ||
266 | static inline void eeh_add_sysfs_files(struct pci_bus *bus) { } | 267 | static inline void eeh_add_sysfs_files(struct pci_bus *bus) { } |
267 | 268 | ||
268 | static inline void eeh_remove_device(struct pci_dev *dev, int purge_pe) { } | 269 | static inline void eeh_remove_device(struct pci_dev *dev) { } |
269 | |||
270 | static 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) | |||
209 | extern struct pci_bus *pcibios_find_pci_bus(struct device_node *dn); | 209 | extern 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 */ |
212 | extern void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe); | ||
213 | extern void pcibios_remove_pci_devices(struct pci_bus *bus); | 212 | extern 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 | */ |
975 | void eeh_remove_device(struct pci_dev *dev, int purge_pe) | 974 | void 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 | */ | ||
1007 | void 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 | } | ||
1019 | EXPORT_SYMBOL_GPL(eeh_remove_bus_device); | ||
1020 | |||
1021 | static int proc_eeh_show(struct seq_file *m, void *v) | 997 | static 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 | */ |
407 | int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) | 406 | int 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 | */ |
30 | void pcibios_release_device(struct pci_dev *dev) | 30 | void 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 | */ |
46 | void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe) | 42 | void 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 | */ | ||
71 | void pcibios_remove_pci_devices(struct pci_bus *bus) | ||
72 | { | ||
73 | __pcibios_remove_pci_devices(bus, 1); | ||
74 | } | ||
75 | EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); | 59 | EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); |
76 | 60 | ||
77 | /** | 61 | /** |