diff options
Diffstat (limited to 'arch/powerpc/kernel/eeh.c')
-rw-r--r-- | arch/powerpc/kernel/eeh.c | 70 |
1 files changed, 34 insertions, 36 deletions
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 39954fe941b8..ea9414c8088d 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c | |||
@@ -231,7 +231,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) | |||
231 | void eeh_slot_error_detail(struct eeh_pe *pe, int severity) | 231 | void eeh_slot_error_detail(struct eeh_pe *pe, int severity) |
232 | { | 232 | { |
233 | size_t loglen = 0; | 233 | size_t loglen = 0; |
234 | struct eeh_dev *edev; | 234 | struct eeh_dev *edev, *tmp; |
235 | bool valid_cfg_log = true; | 235 | bool valid_cfg_log = true; |
236 | 236 | ||
237 | /* | 237 | /* |
@@ -251,7 +251,7 @@ void eeh_slot_error_detail(struct eeh_pe *pe, int severity) | |||
251 | eeh_pe_restore_bars(pe); | 251 | eeh_pe_restore_bars(pe); |
252 | 252 | ||
253 | pci_regs_buf[0] = 0; | 253 | pci_regs_buf[0] = 0; |
254 | eeh_pe_for_each_dev(pe, edev) { | 254 | eeh_pe_for_each_dev(pe, edev, tmp) { |
255 | loglen += eeh_gather_pci_data(edev, pci_regs_buf + loglen, | 255 | loglen += eeh_gather_pci_data(edev, pci_regs_buf + loglen, |
256 | EEH_PCI_REGS_LOG_LEN - loglen); | 256 | EEH_PCI_REGS_LOG_LEN - loglen); |
257 | } | 257 | } |
@@ -499,8 +499,6 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon | |||
499 | } | 499 | } |
500 | 500 | ||
501 | eeh_dev_check_failure(edev); | 501 | eeh_dev_check_failure(edev); |
502 | |||
503 | pci_dev_put(eeh_dev_to_pci_dev(edev)); | ||
504 | return val; | 502 | return val; |
505 | } | 503 | } |
506 | 504 | ||
@@ -838,7 +836,7 @@ core_initcall_sync(eeh_init); | |||
838 | * on the CEC architecture, type of the device, on earlier boot | 836 | * on the CEC architecture, type of the device, on earlier boot |
839 | * command-line arguments & etc. | 837 | * command-line arguments & etc. |
840 | */ | 838 | */ |
841 | static void eeh_add_device_early(struct device_node *dn) | 839 | void eeh_add_device_early(struct device_node *dn) |
842 | { | 840 | { |
843 | struct pci_controller *phb; | 841 | struct pci_controller *phb; |
844 | 842 | ||
@@ -886,7 +884,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); | |||
886 | * This routine must be used to complete EEH initialization for PCI | 884 | * This routine must be used to complete EEH initialization for PCI |
887 | * devices that were added after system boot (e.g. hotplug, dlpar). | 885 | * devices that were added after system boot (e.g. hotplug, dlpar). |
888 | */ | 886 | */ |
889 | static void eeh_add_device_late(struct pci_dev *dev) | 887 | void eeh_add_device_late(struct pci_dev *dev) |
890 | { | 888 | { |
891 | struct device_node *dn; | 889 | struct device_node *dn; |
892 | struct eeh_dev *edev; | 890 | struct eeh_dev *edev; |
@@ -902,9 +900,23 @@ static void eeh_add_device_late(struct pci_dev *dev) | |||
902 | pr_debug("EEH: Already referenced !\n"); | 900 | pr_debug("EEH: Already referenced !\n"); |
903 | return; | 901 | return; |
904 | } | 902 | } |
905 | WARN_ON(edev->pdev); | ||
906 | 903 | ||
907 | pci_dev_get(dev); | 904 | /* |
905 | * The EEH cache might not be removed correctly because of | ||
906 | * unbalanced kref to the device during unplug time, which | ||
907 | * relies on pcibios_release_device(). So we have to remove | ||
908 | * that here explicitly. | ||
909 | */ | ||
910 | if (edev->pdev) { | ||
911 | eeh_rmv_from_parent_pe(edev); | ||
912 | eeh_addr_cache_rmv_dev(edev->pdev); | ||
913 | eeh_sysfs_remove_device(edev->pdev); | ||
914 | edev->mode &= ~EEH_DEV_SYSFS; | ||
915 | |||
916 | edev->pdev = NULL; | ||
917 | dev->dev.archdata.edev = NULL; | ||
918 | } | ||
919 | |||
908 | edev->pdev = dev; | 920 | edev->pdev = dev; |
909 | dev->dev.archdata.edev = edev; | 921 | dev->dev.archdata.edev = edev; |
910 | 922 | ||
@@ -967,7 +979,6 @@ EXPORT_SYMBOL_GPL(eeh_add_sysfs_files); | |||
967 | /** | 979 | /** |
968 | * eeh_remove_device - Undo EEH setup for the indicated pci device | 980 | * eeh_remove_device - Undo EEH setup for the indicated pci device |
969 | * @dev: pci device to be removed | 981 | * @dev: pci device to be removed |
970 | * @purge_pe: remove the PE or not | ||
971 | * | 982 | * |
972 | * This routine should be called when a device is removed from | 983 | * This routine should be called when a device is removed from |
973 | * a running system (e.g. by hotplug or dlpar). It unregisters | 984 | * a running system (e.g. by hotplug or dlpar). It unregisters |
@@ -975,7 +986,7 @@ EXPORT_SYMBOL_GPL(eeh_add_sysfs_files); | |||
975 | * this device will no longer be detected after this call; thus, | 986 | * this device will no longer be detected after this call; thus, |
976 | * i/o errors affecting this slot may leave this device unusable. | 987 | * i/o errors affecting this slot may leave this device unusable. |
977 | */ | 988 | */ |
978 | static void eeh_remove_device(struct pci_dev *dev, int purge_pe) | 989 | void eeh_remove_device(struct pci_dev *dev) |
979 | { | 990 | { |
980 | struct eeh_dev *edev; | 991 | struct eeh_dev *edev; |
981 | 992 | ||
@@ -986,42 +997,29 @@ static void eeh_remove_device(struct pci_dev *dev, int purge_pe) | |||
986 | /* Unregister the device with the EEH/PCI address search system */ | 997 | /* Unregister the device with the EEH/PCI address search system */ |
987 | pr_debug("EEH: Removing device %s\n", pci_name(dev)); | 998 | pr_debug("EEH: Removing device %s\n", pci_name(dev)); |
988 | 999 | ||
989 | if (!edev || !edev->pdev) { | 1000 | if (!edev || !edev->pdev || !edev->pe) { |
990 | pr_debug("EEH: Not referenced !\n"); | 1001 | pr_debug("EEH: Not referenced !\n"); |
991 | return; | 1002 | return; |
992 | } | 1003 | } |
1004 | |||
1005 | /* | ||
1006 | * During the hotplug for EEH error recovery, we need the EEH | ||
1007 | * device attached to the parent PE in order for BAR restore | ||
1008 | * a bit later. So we keep it for BAR restore and remove it | ||
1009 | * from the parent PE during the BAR resotre. | ||
1010 | */ | ||
993 | edev->pdev = NULL; | 1011 | edev->pdev = NULL; |
994 | dev->dev.archdata.edev = NULL; | 1012 | dev->dev.archdata.edev = NULL; |
995 | pci_dev_put(dev); | 1013 | if (!(edev->pe->state & EEH_PE_KEEP)) |
1014 | eeh_rmv_from_parent_pe(edev); | ||
1015 | else | ||
1016 | edev->mode |= EEH_DEV_DISCONNECTED; | ||
996 | 1017 | ||
997 | eeh_rmv_from_parent_pe(edev, purge_pe); | ||
998 | eeh_addr_cache_rmv_dev(dev); | 1018 | eeh_addr_cache_rmv_dev(dev); |
999 | eeh_sysfs_remove_device(dev); | 1019 | eeh_sysfs_remove_device(dev); |
1020 | edev->mode &= ~EEH_DEV_SYSFS; | ||
1000 | } | 1021 | } |
1001 | 1022 | ||
1002 | /** | ||
1003 | * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device | ||
1004 | * @dev: PCI device | ||
1005 | * @purge_pe: remove the corresponding PE or not | ||
1006 | * | ||
1007 | * This routine must be called when a device is removed from the | ||
1008 | * running system through hotplug or dlpar. The corresponding | ||
1009 | * PCI address cache will be removed. | ||
1010 | */ | ||
1011 | void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe) | ||
1012 | { | ||
1013 | struct pci_bus *bus = dev->subordinate; | ||
1014 | struct pci_dev *child, *tmp; | ||
1015 | |||
1016 | eeh_remove_device(dev, purge_pe); | ||
1017 | |||
1018 | if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
1019 | list_for_each_entry_safe(child, tmp, &bus->devices, bus_list) | ||
1020 | eeh_remove_bus_device(child, purge_pe); | ||
1021 | } | ||
1022 | } | ||
1023 | EXPORT_SYMBOL_GPL(eeh_remove_bus_device); | ||
1024 | |||
1025 | static int proc_eeh_show(struct seq_file *m, void *v) | 1023 | static int proc_eeh_show(struct seq_file *m, void *v) |
1026 | { | 1024 | { |
1027 | if (0 == eeh_subsystem_enabled) { | 1025 | if (0 == eeh_subsystem_enabled) { |