powerpc/eeh: Create PEs for PHBs
authorGavin Shan <shangw@linux.vnet.ibm.com>
Fri, 7 Sep 2012 22:44:07 +0000 (22:44 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Sun, 9 Sep 2012 23:35:30 +0000 (09:35 +1000)
For one particular PE, it's only meaningful in the ancestor PHB
domain. Therefore, each PHB should have its own PE hierarchy tree
to trace those PEs created against the PHB.

The patch creates PEs for the PHBs and put those PEs into the
global link list traced by "eeh_phb_pe". The link list of PEs
would be first level of overall PE hierarchy tree across the
system.

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/Makefile
arch/powerpc/platforms/pseries/eeh_dev.c
arch/powerpc/platforms/pseries/eeh_pe.c [new file with mode: 0644]

index 248b3d99112fe7d0b0a76a528de800a418a023e5..7b9c7d65ee2dd7f5c222b270535c8fe4559fc3bf 100644 (file)
@@ -164,6 +164,8 @@ static inline void eeh_unlock(void)
  */
 #define EEH_MAX_ALLOWED_FREEZES 5
 
+int __devinit eeh_phb_pe_create(struct pci_controller *phb);
+
 void * __devinit eeh_dev_init(struct device_node *dn, void *data);
 void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb);
 int __init eeh_ops_register(struct eeh_ops *ops);
index c222189f5bb230e1467103681ba0cb3923ebf275..890622b87c8f009feda9bb8bd36ac5768c6e3af9 100644 (file)
@@ -6,8 +6,9 @@ obj-y                   := lpar.o hvCall.o nvram.o reconfig.o \
                           firmware.o power.o dlpar.o mobility.o
 obj-$(CONFIG_SMP)      += smp.o
 obj-$(CONFIG_SCANLOG)  += scanlog.o
-obj-$(CONFIG_EEH)      += eeh.o eeh_dev.o eeh_cache.o eeh_driver.o \
-                          eeh_event.o eeh_sysfs.o eeh_pseries.o
+obj-$(CONFIG_EEH)      += eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \
+                          eeh_driver.o eeh_event.o eeh_sysfs.o \
+                          eeh_pseries.o
 obj-$(CONFIG_KEXEC)    += kexec.o
 obj-$(CONFIG_PCI)      += pci.o pci_dlpar.o
 obj-$(CONFIG_PSERIES_MSI)      += msi.o
index a0cee3a29c97e920b74e3764f0b44c3dd38be17a..66442341d3a6009882c0039b2049d1f7e3af1800 100644 (file)
@@ -65,6 +65,7 @@ void * __devinit eeh_dev_init(struct device_node *dn, void *data)
        PCI_DN(dn)->edev = edev;
        edev->dn  = dn;
        edev->phb = phb;
+       INIT_LIST_HEAD(&edev->list);
 
        return NULL;
 }
@@ -80,6 +81,9 @@ void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb)
 {
        struct device_node *dn = phb->dn;
 
+       /* EEH PE for PHB */
+       eeh_phb_pe_create(phb);
+
        /* EEH device for PHB */
        eeh_dev_init(dn, phb);
 
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c
new file mode 100644 (file)
index 0000000..7dee7dc
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * The file intends to implement PE based on the information from
+ * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device.
+ * All the PEs should be organized as hierarchy tree. The first level
+ * of the tree will be associated to existing PHBs since the particular
+ * PE is only meaningful in one PHB domain.
+ *
+ * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+
+#include <asm/pci-bridge.h>
+#include <asm/ppc-pci.h>
+
+static LIST_HEAD(eeh_phb_pe);
+
+/**
+ * eeh_pe_alloc - Allocate PE
+ * @phb: PCI controller
+ * @type: PE type
+ *
+ * Allocate PE instance dynamically.
+ */
+static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
+{
+       struct eeh_pe *pe;
+
+       /* Allocate PHB PE */
+       pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL);
+       if (!pe) return NULL;
+
+       /* Initialize PHB PE */
+       pe->type = type;
+       pe->phb = phb;
+       INIT_LIST_HEAD(&pe->child_list);
+       INIT_LIST_HEAD(&pe->child);
+       INIT_LIST_HEAD(&pe->edevs);
+
+       return pe;
+}
+
+/**
+ * eeh_phb_pe_create - Create PHB PE
+ * @phb: PCI controller
+ *
+ * The function should be called while the PHB is detected during
+ * system boot or PCI hotplug in order to create PHB PE.
+ */
+int __devinit eeh_phb_pe_create(struct pci_controller *phb)
+{
+       struct eeh_pe *pe;
+
+       /* Allocate PHB PE */
+       pe = eeh_pe_alloc(phb, EEH_PE_PHB);
+       if (!pe) {
+               pr_err("%s: out of memory!\n", __func__);
+               return -ENOMEM;
+       }
+
+       /* Put it into the list */
+       eeh_lock();
+       list_add_tail(&pe->child, &eeh_phb_pe);
+       eeh_unlock();
+
+       pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number);
+
+       return 0;
+}
+
+/**
+ * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB
+ * @phb: PCI controller
+ *
+ * The overall PEs form hierarchy tree. The first layer of the
+ * hierarchy tree is composed of PHB PEs. The function is used
+ * to retrieve the corresponding PHB PE according to the given PHB.
+ */
+static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
+{
+       struct eeh_pe *pe;
+
+       eeh_lock();
+
+       list_for_each_entry(pe, &eeh_phb_pe, child) {
+               /*
+                * Actually, we needn't check the type since
+                * the PE for PHB has been determined when that
+                * was created.
+                */
+               if (pe->type == EEH_PE_PHB &&
+                   pe->phb == phb) {
+                       eeh_unlock();
+                       return pe;
+               }
+       }
+
+       eeh_unlock();
+
+       return NULL;
+}