powerpc/eeh: Introduce EEH_PE_INVALID type PE
authorGavin Shan <shangw@linux.vnet.ibm.com>
Tue, 11 Sep 2012 19:16:16 +0000 (19:16 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Tue, 18 Sep 2012 05:02:49 +0000 (15:02 +1000)
When EEH error happens on the PE whose PCI devices don't have
attached drivers. In function eeh_handle_event(), the default
value PCI_ERS_RESULT_NONE will be returned after iterating all
drivers of those PCI devices belonging to the PE. Actually, we
don't have installed drivers for the PCI devices. Under the
circumstance, we will remove the corresponding PCI bus of the PE,
including the associated EEH devices and PE instance. However,
we still need the information stored in the PE instance to do PE
reset after that. So it's unsafe to free the PE instance.

The patch introduces EEH_PE_INVALID type PE to address the issue.
When the PCI bus and the corresponding attached EEH devices are
removed, we will mark the PE as EEH_PE_INVALID. At later point,
the PE will be changed to EEH_PE_DEVICE or EEH_PE_BUS when the
corresponding EEH devices are attached again.

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_pe.c

index 58c5ee61e70013dd05cb640362fb5aae08e4a0fa..afeb40086fb2a3e03c39ceff93e3ab7286200438 100644 (file)
@@ -45,9 +45,10 @@ struct device_node;
  * in the corresponding PHB. Therefore, the root PEs should be created
  * against existing PHBs in on-to-one fashion.
  */
-#define EEH_PE_PHB     1       /* PHB PE    */
-#define EEH_PE_DEVICE  2       /* Device PE */
-#define EEH_PE_BUS     3       /* Bus PE    */
+#define EEH_PE_INVALID (1 << 0)        /* Invalid   */
+#define EEH_PE_PHB     (1 << 1)        /* PHB PE    */
+#define EEH_PE_DEVICE  (1 << 2)        /* Device PE */
+#define EEH_PE_BUS     (1 << 3)        /* Bus PE    */
 
 #define EEH_PE_ISOLATED                (1 << 0)        /* Isolated PE          */
 #define EEH_PE_RECOVERING      (1 << 1)        /* Recovering PE        */
index 904123c7657b796113c54d2436db82970bbae569..51fc56abb7a2e04c25c11af4e79be43cba3801ed 100644 (file)
@@ -107,7 +107,7 @@ static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
                 * the PE for PHB has been determined when that
                 * was created.
                 */
-               if (pe->type == EEH_PE_PHB &&
+               if ((pe->type & EEH_PE_PHB) &&
                    pe->phb == phb) {
                        eeh_unlock();
                        return pe;
@@ -219,7 +219,7 @@ static void *__eeh_pe_get(void *data, void *flag)
        struct eeh_dev *edev = (struct eeh_dev *)flag;
 
        /* Unexpected PHB PE */
-       if (pe->type == EEH_PE_PHB)
+       if (pe->type & EEH_PE_PHB)
                return NULL;
 
        /* We prefer PE address */
@@ -314,7 +314,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
         * components.
         */
        pe = eeh_pe_get(edev);
-       if (pe) {
+       if (pe && !(pe->type & EEH_PE_INVALID)) {
                if (!edev->pe_config_addr) {
                        pr_err("%s: PE with addr 0x%x already exists\n",
                                __func__, edev->config_addr);
@@ -330,6 +330,24 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
                pr_debug("EEH: Add %s to Bus PE#%x\n",
                        edev->dn->full_name, pe->addr);
 
+               return 0;
+       } else if (pe && (pe->type & EEH_PE_INVALID)) {
+               list_add_tail(&edev->list, &pe->edevs);
+               edev->pe = pe;
+               /*
+                * We're running to here because of PCI hotplug caused by
+                * EEH recovery. We need clear EEH_PE_INVALID until the top.
+                */
+               parent = pe;
+               while (parent) {
+                       if (!(parent->type & EEH_PE_INVALID))
+                               break;
+                       parent->type &= ~EEH_PE_INVALID;
+                       parent = parent->parent;
+               }
+               pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
+                       edev->dn->full_name, pe->addr, pe->parent->addr);
+
                return 0;
        }
 
@@ -385,7 +403,8 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
  */
 int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
 {
-       struct eeh_pe *pe, *parent;
+       struct eeh_pe *pe, *parent, *child;
+       int cnt;
 
        if (!edev->pe) {
                pr_warning("%s: No PE found for EEH device %s\n",
@@ -406,13 +425,22 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
         */
        while (1) {
                parent = pe->parent;
-               if (pe->type == EEH_PE_PHB)
+               if (pe->type & EEH_PE_PHB)
                        break;
 
-               if (list_empty(&pe->edevs) &&
-                   list_empty(&pe->child_list)) {
-                       list_del(&pe->child);
-                       kfree(pe);
+               if (list_empty(&pe->edevs)) {
+                       cnt = 0;
+                       list_for_each_entry(child, &pe->child_list, child) {
+                               if (!(pe->type & EEH_PE_INVALID)) {
+                                       cnt++;
+                                       break;
+                               }
+                       }
+
+                       if (!cnt)
+                               pe->type |= EEH_PE_INVALID;
+                       else
+                               break;
                }
 
                pe = parent;
@@ -578,9 +606,9 @@ struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
        struct eeh_dev *edev;
        struct pci_dev *pdev;
 
-       if (pe->type == EEH_PE_PHB) {
+       if (pe->type & EEH_PE_PHB) {
                bus = pe->phb->bus;
-       } else if (pe->type == EEH_PE_BUS) {
+       } else if (pe->type & EEH_PE_BUS) {
                edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
                pdev = eeh_dev_to_pci_dev(edev);
                if (pdev)