powerpc/eeh: Remove PE at appropriate time
authorGavin Shan <shangw@linux.vnet.ibm.com>
Fri, 7 Sep 2012 22:44:10 +0000 (22:44 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Sun, 9 Sep 2012 23:35:32 +0000 (09:35 +1000)
During PCI hotplug and EEH recovery, the PE hierarchy tree might be
changed due to the PCI topology changes. At later point when the
PCI device is added, the PE will be created dynamically again.

The patch introduces new function to remove EEH devices from the
associated PE. That also can cause that the parent PE is removed
from the PE tree if the parent PE doesn't include valid EEH devices
and child PEs.

Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/include/asm/eeh.h
arch/powerpc/platforms/pseries/eeh.c
arch/powerpc/platforms/pseries/eeh_pe.c

index 6b13790fdadb59bd8cc42446b7f69ebf8db1ba1f..250ae2718bb8ef2ba12fce3b72aabe3251aedadc 100644 (file)
@@ -167,6 +167,7 @@ static inline void eeh_unlock(void)
 typedef void *(*eeh_traverse_func)(void *data, void *flag);
 int __devinit eeh_phb_pe_create(struct pci_controller *phb);
 int eeh_add_to_parent_pe(struct eeh_dev *edev);
+int eeh_rmv_from_parent_pe(struct eeh_dev *edev);
 
 void * __devinit eeh_dev_init(struct device_node *dn, void *data);
 void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb);
index 8f21490618465ac71575ef427c11e4c698e8f530..b60863b184c363ce815d73001eece96f73c20f0c 100644 (file)
@@ -1156,6 +1156,7 @@ static void eeh_remove_device(struct pci_dev *dev)
        dev->dev.archdata.edev = NULL;
        pci_dev_put(dev);
 
+       eeh_rmv_from_parent_pe(edev);
        pci_addr_cache_remove_device(dev);
        eeh_sysfs_remove_device(dev);
 }
index 1d632739d28e4f4ea0094f629536a621b0519c58..e941e6315fa6b83e86e455893b3b962441ab1a3b 100644 (file)
@@ -341,3 +341,50 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
 
        return 0;
 }
+
+/**
+ * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
+ * @edev: EEH device
+ *
+ * The PE hierarchy tree might be changed when doing PCI hotplug.
+ * Also, the PCI devices or buses could be removed from the system
+ * during EEH recovery. So we have to call the function remove the
+ * corresponding PE accordingly if necessary.
+ */
+int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
+{
+       struct eeh_pe *pe, *parent;
+
+       if (!edev->pe) {
+               pr_warning("%s: No PE found for EEH device %s\n",
+                       __func__, edev->dn->full_name);
+               return -EEXIST;
+       }
+
+       /* Remove the EEH device */
+       pe = edev->pe;
+       edev->pe = NULL;
+       list_del(&edev->list);
+
+       /*
+        * Check if the parent PE includes any EEH devices.
+        * If not, we should delete that. Also, we should
+        * delete the parent PE if it doesn't have associated
+        * child PEs and EEH devices.
+        */
+       while (1) {
+               parent = pe->parent;
+               if (pe->type == EEH_PE_PHB)
+                       break;
+
+               if (list_empty(&pe->edevs) &&
+                   list_empty(&pe->child_list)) {
+                       list_del(&pe->child);
+                       kfree(pe);
+               }
+
+               pe = parent;
+       }
+
+       return 0;
+}