LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Benjamin Herrenschmidt @ 2013-06-16  4:39 UTC (permalink / raw)
  To: Alexey Kardashevskiy
  Cc: kvm, linux-kernel, kvm-ppc, Alexander Graf, Paul Mackerras,
	linuxppc-dev, David Gibson
In-Reply-To: <1370412673-1345-4-git-send-email-aik@ozlabs.ru>

>  static pte_t kvmppc_lookup_pte(pgd_t *pgdir, unsigned long hva, bool writing,
> -			unsigned long *pte_sizep)
> +			unsigned long *pte_sizep, bool do_get_page)
>  {
>  	pte_t *ptep;
>  	unsigned int shift = 0;
> @@ -135,6 +136,14 @@ static pte_t kvmppc_lookup_pte(pgd_t *pgdir, unsigned long hva, bool writing,
>  	if (!pte_present(*ptep))
>  		return __pte(0);
>  
> +	/*
> +	 * Put huge pages handling to the virtual mode.
> +	 * The only exception is for TCE list pages which we
> +	 * do need to call get_page() for.
> +	 */
> +	if ((*pte_sizep > PAGE_SIZE) && do_get_page)
> +		return __pte(0);
> +
>  	/* wait until _PAGE_BUSY is clear then set it atomically */
>  	__asm__ __volatile__ (
>  		"1:	ldarx	%0,0,%3\n"
> @@ -148,6 +157,18 @@ static pte_t kvmppc_lookup_pte(pgd_t *pgdir, unsigned long hva, bool writing,
>  		: "cc");
>  
>  	ret = pte;
> +	if (do_get_page && pte_present(pte) && (!writing || pte_write(pte))) {
> +		struct page *pg = NULL;
> +		pg = realmode_pfn_to_page(pte_pfn(pte));
> +		if (realmode_get_page(pg)) {
> +			ret = __pte(0);
> +		} else {
> +			pte = pte_mkyoung(pte);
> +			if (writing)
> +				pte = pte_mkdirty(pte);
> +		}
> +	}
> +	*ptep = pte;	/* clears _PAGE_BUSY */
>  
>  	return ret;
>  }

So now you are adding the clearing of _PAGE_BUSY that was missing for
your first patch, except that this is not enough since that means that
in the "emulated" case (ie, !do_get_page) you will in essence return
and then use a PTE that is not locked without any synchronization to
ensure that the underlying page doesn't go away... then you'll
dereference that page.

So either make everything use speculative get_page, or make the emulated
case use the MMU notifier to drop the operation in case of collision.

The former looks easier.

Also, any specific reason why you do:

  - Lock the PTE
  - get_page()
  - Unlock the PTE

Instead of

  - Read the PTE
  - get_page_unless_zero
  - re-check PTE

Like get_user_pages_fast() does ?

The former will be two atomic ops, the latter only one (faster), but
maybe you have a good reason why that can't work...

Cheers,
Ben.

^ permalink raw reply

* [PATCH 2/9] PTR_RET is now PTR_ERR_OR_ZERO(): Replace most.
From: Rusty Russell @ 2013-06-16  4:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: netdev, Rusty Russell, linuxppc-dev, linux-arm-kernel,
	Julia Lawall
In-Reply-To: <1371357768-4968-1-git-send-email-rusty@rustcorp.com.au>

Sweep of the simple cases.

Cc: netdev@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: Julia Lawall <julia.lawall@lip6.fr>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 arch/arm/mach-omap2/i2c.c                         |  2 +-
 arch/m68k/amiga/platform.c                        |  2 +-
 arch/m68k/kernel/time.c                           |  2 +-
 arch/m68k/q40/config.c                            |  2 +-
 arch/powerpc/kernel/iommu.c                       |  2 +-
 arch/powerpc/kernel/time.c                        |  2 +-
 arch/powerpc/platforms/ps3/time.c                 |  2 +-
 arch/powerpc/sysdev/rtc_cmos_setup.c              |  2 +-
 arch/s390/hypfs/hypfs_dbfs.c                      |  2 +-
 drivers/char/tile-srom.c                          |  2 +-
 drivers/infiniband/core/cma.c                     |  2 +-
 drivers/net/appletalk/cops.c                      |  2 +-
 drivers/net/appletalk/ltpc.c                      |  2 +-
 drivers/net/ethernet/amd/atarilance.c             |  2 +-
 drivers/net/ethernet/amd/mvme147.c                |  2 +-
 drivers/net/ethernet/amd/ni65.c                   |  2 +-
 drivers/net/ethernet/amd/sun3lance.c              |  2 +-
 drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c |  2 +-
 drivers/net/wireless/brcm80211/brcmsmac/debug.c   |  2 +-
 drivers/platform/x86/samsung-q10.c                |  2 +-
 drivers/regulator/fan53555.c                      |  2 +-
 drivers/spi/spi-fsl-spi.c                         |  2 +-
 drivers/spi/spidev.c                              |  2 +-
 drivers/video/omap2/dss/core.c                    |  2 +-
 fs/btrfs/dev-replace.c                            |  2 +-
 fs/btrfs/inode.c                                  |  2 +-
 net/bluetooth/hci_sysfs.c                         |  2 +-
 net/bridge/netfilter/ebtable_broute.c             |  2 +-
 net/bridge/netfilter/ebtable_filter.c             |  2 +-
 net/bridge/netfilter/ebtable_nat.c                |  2 +-
 net/ipv4/netfilter/arptable_filter.c              |  2 +-
 net/ipv4/netfilter/iptable_filter.c               |  2 +-
 net/ipv4/netfilter/iptable_mangle.c               |  2 +-
 net/ipv4/netfilter/iptable_nat.c                  |  2 +-
 net/ipv4/netfilter/iptable_raw.c                  |  2 +-
 net/ipv4/netfilter/iptable_security.c             |  2 +-
 net/ipv6/netfilter/ip6table_filter.c              |  2 +-
 net/ipv6/netfilter/ip6table_mangle.c              |  2 +-
 net/ipv6/netfilter/ip6table_nat.c                 |  2 +-
 net/ipv6/netfilter/ip6table_raw.c                 |  2 +-
 net/ipv6/netfilter/ip6table_security.c            |  2 +-
 scripts/coccinelle/api/ptr_ret.cocci              | 10 +++++-----
 sound/soc/soc-io.c                                |  2 +-
 43 files changed, 47 insertions(+), 47 deletions(-)

diff --git a/arch/arm/mach-omap2/i2c.c b/arch/arm/mach-omap2/i2c.c
index d940e53..b456b44 100644
--- a/arch/arm/mach-omap2/i2c.c
+++ b/arch/arm/mach-omap2/i2c.c
@@ -181,7 +181,7 @@ int __init omap_i2c_add_bus(struct omap_i2c_bus_platform_data *i2c_pdata,
 				 sizeof(struct omap_i2c_bus_platform_data));
 	WARN(IS_ERR(pdev), "Could not build omap_device for %s\n", name);
 
-	return PTR_RET(pdev);
+	return PTR_ERR_OR_ZERO(pdev);
 }
 
 static  int __init omap_i2c_cmdline(void)
diff --git a/arch/m68k/amiga/platform.c b/arch/m68k/amiga/platform.c
index 6083088..dacd9f9 100644
--- a/arch/m68k/amiga/platform.c
+++ b/arch/m68k/amiga/platform.c
@@ -56,7 +56,7 @@ static int __init amiga_init_bus(void)
 	n = AMIGAHW_PRESENT(ZORRO3) ? 4 : 2;
 	pdev = platform_device_register_simple("amiga-zorro", -1,
 					       zorro_resources, n);
-	return PTR_RET(pdev);
+	return PTR_ERR_OR_ZERO(pdev);
 }
 
 subsys_initcall(amiga_init_bus);
diff --git a/arch/m68k/kernel/time.c b/arch/m68k/kernel/time.c
index bea6bcf..7eb9792 100644
--- a/arch/m68k/kernel/time.c
+++ b/arch/m68k/kernel/time.c
@@ -90,7 +90,7 @@ static int __init rtc_init(void)
 		return -ENODEV;
 
 	pdev = platform_device_register_simple("rtc-generic", -1, NULL, 0);
-	return PTR_RET(pdev);
+	return PTR_ERR_OR_ZERO(pdev);
 }
 
 module_init(rtc_init);
diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c
index 658542b..078bb74 100644
--- a/arch/m68k/q40/config.c
+++ b/arch/m68k/q40/config.c
@@ -338,6 +338,6 @@ static __init int q40_add_kbd_device(void)
 		return -ENODEV;
 
 	pdev = platform_device_register_simple("q40kbd", -1, NULL, 0);
-	return PTR_RET(pdev);
+	return PTR_ERR_OR_ZERO(pdev);
 }
 arch_initcall(q40_add_kbd_device);
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index c0d0dbd..3a149eb 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -102,7 +102,7 @@ static int __init fail_iommu_debugfs(void)
 	struct dentry *dir = fault_create_debugfs_attr("fail_iommu",
 						       NULL, &fail_iommu);
 
-	return PTR_RET(dir);
+	return PTR_ERR_OR_ZERO(dir);
 }
 late_initcall(fail_iommu_debugfs);
 
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 5fc29ad..77e78ef 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -1050,7 +1050,7 @@ static int __init rtc_init(void)
 
 	pdev = platform_device_register_simple("rtc-generic", -1, NULL, 0);
 
-	return PTR_RET(pdev);
+	return PTR_ERR_OR_ZERO(pdev);
 }
 
 module_init(rtc_init);
diff --git a/arch/powerpc/platforms/ps3/time.c b/arch/powerpc/platforms/ps3/time.c
index cba1e6b..ce73ce8 100644
--- a/arch/powerpc/platforms/ps3/time.c
+++ b/arch/powerpc/platforms/ps3/time.c
@@ -90,7 +90,7 @@ static int __init ps3_rtc_init(void)
 
 	pdev = platform_device_register_simple("rtc-ps3", -1, NULL, 0);
 
-	return PTR_RET(pdev);
+	return PTR_ERR_OR_ZERO(pdev);
 }
 
 module_init(ps3_rtc_init);
diff --git a/arch/powerpc/sysdev/rtc_cmos_setup.c b/arch/powerpc/sysdev/rtc_cmos_setup.c
index af79e1e..af0f9be 100644
--- a/arch/powerpc/sysdev/rtc_cmos_setup.c
+++ b/arch/powerpc/sysdev/rtc_cmos_setup.c
@@ -62,7 +62,7 @@ static int  __init add_rtc(void)
 	pd = platform_device_register_simple("rtc_cmos", -1,
 					     &res[0], num_res);
 
-	return PTR_RET(pd);
+	return PTR_ERR_OR_ZERO(pd);
 }
 fs_initcall(add_rtc);
 
diff --git a/arch/s390/hypfs/hypfs_dbfs.c b/arch/s390/hypfs/hypfs_dbfs.c
index bb5dd49..17ab8b7 100644
--- a/arch/s390/hypfs/hypfs_dbfs.c
+++ b/arch/s390/hypfs/hypfs_dbfs.c
@@ -105,7 +105,7 @@ void hypfs_dbfs_remove_file(struct hypfs_dbfs_file *df)
 int hypfs_dbfs_init(void)
 {
 	dbfs_dir = debugfs_create_dir("s390_hypfs", NULL);
-	return PTR_RET(dbfs_dir);
+	return PTR_ERR_OR_ZERO(dbfs_dir);
 }
 
 void hypfs_dbfs_exit(void)
diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c
index 2e2036e..a3a792f 100644
--- a/drivers/char/tile-srom.c
+++ b/drivers/char/tile-srom.c
@@ -371,7 +371,7 @@ static int srom_setup_minor(struct srom_dev *srom, int index)
 
 	dev = device_create(srom_class, &platform_bus,
 			    MKDEV(srom_major, index), srom, "%d", index);
-	return PTR_RET(dev);
+	return PTR_ERR_OR_ZERO(dev);
 }
 
 /** srom_init() - Initialize the driver's module. */
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 71c2c71..17a6ec5 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -3055,7 +3055,7 @@ static int cma_join_ib_multicast(struct rdma_id_private *id_priv,
 						id_priv->id.port_num, &rec,
 						comp_mask, GFP_KERNEL,
 						cma_ib_mc_handler, mc);
-	return PTR_RET(mc->multicast.ib);
+	return PTR_ERR_OR_ZERO(mc->multicast.ib);
 }
 
 static void iboe_mcast_work_handler(struct work_struct *work)
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index cff6f02..7f2a032 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -996,7 +996,7 @@ static int __init cops_module_init(void)
 		printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n",
 			cardname);
 	cops_dev = cops_probe(-1);
-	return PTR_RET(cops_dev);
+	return PTR_ERR_OR_ZERO(cops_dev);
 }
 
 static void __exit cops_module_exit(void)
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index b5782cd..01e2ac5 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -1243,7 +1243,7 @@ static int __init ltpc_module_init(void)
 		       "ltpc: Autoprobing is not recommended for modules\n");
 
 	dev_ltpc = ltpc_probe();
-	return PTR_RET(dev_ltpc);
+	return PTR_ERR_OR_ZERO(dev_ltpc);
 }
 module_init(ltpc_module_init);
 #endif
diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c
index e8d0ef5..10ceca5 100644
--- a/drivers/net/ethernet/amd/atarilance.c
+++ b/drivers/net/ethernet/amd/atarilance.c
@@ -1147,7 +1147,7 @@ static struct net_device *atarilance_dev;
 static int __init atarilance_module_init(void)
 {
 	atarilance_dev = atarilance_probe(-1);
-	return PTR_RET(atarilance_dev);
+	return PTR_ERR_OR_ZERO(atarilance_dev);
 }
 
 static void __exit atarilance_module_exit(void)
diff --git a/drivers/net/ethernet/amd/mvme147.c b/drivers/net/ethernet/amd/mvme147.c
index a51497c..e108e91 100644
--- a/drivers/net/ethernet/amd/mvme147.c
+++ b/drivers/net/ethernet/amd/mvme147.c
@@ -188,7 +188,7 @@ static struct net_device *dev_mvme147_lance;
 int __init init_module(void)
 {
 	dev_mvme147_lance = mvme147lance_probe(-1);
-	return PTR_RET(dev_mvme147_lance);
+	return PTR_ERR_OR_ZERO(dev_mvme147_lance);
 }
 
 void __exit cleanup_module(void)
diff --git a/drivers/net/ethernet/amd/ni65.c b/drivers/net/ethernet/amd/ni65.c
index 26fc0ce..1cf33ad 100644
--- a/drivers/net/ethernet/amd/ni65.c
+++ b/drivers/net/ethernet/amd/ni65.c
@@ -1238,7 +1238,7 @@ MODULE_PARM_DESC(dma, "ni6510 ISA DMA channel (ignored for some cards)");
 int __init init_module(void)
 {
  	dev_ni65 = ni65_probe(-1);
-	return PTR_RET(dev_ni65);
+	return PTR_ERR_OR_ZERO(dev_ni65);
 }
 
 void __exit cleanup_module(void)
diff --git a/drivers/net/ethernet/amd/sun3lance.c b/drivers/net/ethernet/amd/sun3lance.c
index 4375abe..d6b2029 100644
--- a/drivers/net/ethernet/amd/sun3lance.c
+++ b/drivers/net/ethernet/amd/sun3lance.c
@@ -940,7 +940,7 @@ static struct net_device *sun3lance_dev;
 int __init init_module(void)
 {
 	sun3lance_dev = sun3lance_probe(-1);
-	return PTR_RET(sun3lance_dev);
+	return PTR_ERR_OR_ZERO(sun3lance_dev);
 }
 
 void __exit cleanup_module(void)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
index 202869c..3962a7a 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
@@ -50,7 +50,7 @@ int brcmf_debugfs_attach(struct brcmf_pub *drvr)
 		return -ENODEV;
 
 	drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
-	return PTR_RET(drvr->dbgfs_dir);
+	return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
 }
 
 void brcmf_debugfs_detach(struct brcmf_pub *drvr)
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/brcm80211/brcmsmac/debug.c
index 9761deb..a5d4add 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.c
@@ -56,7 +56,7 @@ int brcms_debugfs_attach(struct brcms_pub *drvr)
 
 	drvr->dbgfs_dir = debugfs_create_dir(
 		 dev_name(&drvr->wlc->hw->d11core->dev), root_folder);
-	return PTR_RET(drvr->dbgfs_dir);
+	return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
 }
 
 void brcms_debugfs_detach(struct brcms_pub *drvr)
diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c
index 1a90b62..4430b8c 100644
--- a/drivers/platform/x86/samsung-q10.c
+++ b/drivers/platform/x86/samsung-q10.c
@@ -176,7 +176,7 @@ static int __init samsungq10_init(void)
 						   samsungq10_probe,
 						   NULL, 0, NULL, 0);
 
-	return PTR_RET(samsungq10_device);
+	return PTR_ERR_OR_ZERO(samsungq10_device);
 }
 
 static void __exit samsungq10_exit(void)
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
index f0e1ae5..192444a 100644
--- a/drivers/regulator/fan53555.c
+++ b/drivers/regulator/fan53555.c
@@ -219,7 +219,7 @@ static int fan53555_regulator_register(struct fan53555_device_info *di,
 	rdesc->owner = THIS_MODULE;
 
 	di->rdev = regulator_register(&di->desc, config);
-	return PTR_RET(di->rdev);
+	return PTR_ERR_OR_ZERO(di->rdev);
 
 }
 
diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c
index 14e202e..6e80639 100644
--- a/drivers/spi/spi-fsl-spi.c
+++ b/drivers/spi/spi-fsl-spi.c
@@ -901,7 +901,7 @@ static int plat_mpc8xxx_spi_probe(struct platform_device *pdev)
 		return -EINVAL;
 
 	master = fsl_spi_probe(&pdev->dev, mem, irq);
-	return PTR_RET(master);
+	return PTR_ERR_OR_ZERO(master);
 }
 
 static int plat_mpc8xxx_spi_remove(struct platform_device *pdev)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 911e9e0..ca5bcfe 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -603,7 +603,7 @@ static int spidev_probe(struct spi_device *spi)
 		dev = device_create(spidev_class, &spi->dev, spidev->devt,
 				    spidev, "spidev%d.%d",
 				    spi->master->bus_num, spi->chip_select);
-		status = PTR_RET(dev);
+		status = PTR_ERR_OR_ZERO(dev);
 	} else {
 		dev_dbg(&spi->dev, "no minor number available!\n");
 		status = -ENODEV;
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
index c9c2252..a65f1ac 100644
--- a/drivers/video/omap2/dss/core.c
+++ b/drivers/video/omap2/dss/core.c
@@ -189,7 +189,7 @@ int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
 	d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,
 			write, &dss_debug_fops);
 
-	return PTR_RET(d);
+	return PTR_ERR_OR_ZERO(d);
 }
 #else /* CONFIG_OMAP2_DSS_DEBUGFS */
 static inline int dss_initialize_debugfs(void)
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
index 65241f3..b63903f 100644
--- a/fs/btrfs/dev-replace.c
+++ b/fs/btrfs/dev-replace.c
@@ -747,7 +747,7 @@ int btrfs_resume_dev_replace_async(struct btrfs_fs_info *fs_info)
 	WARN_ON(atomic_xchg(
 		&fs_info->mutually_exclusive_operation_running, 1));
 	task = kthread_run(btrfs_dev_replace_kthread, fs_info, "btrfs-devrepl");
-	return PTR_RET(task);
+	return PTR_ERR_OR_ZERO(task);
 }
 
 static int btrfs_dev_replace_kthread(void *data)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index af978f7..1850a96 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3140,7 +3140,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
 		found_key.type = BTRFS_INODE_ITEM_KEY;
 		found_key.offset = 0;
 		inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL);
-		ret = PTR_RET(inode);
+		ret = PTR_ERR_OR_ZERO(inode);
 		if (ret && ret != -ESTALE)
 			goto out;
 
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 7ad6ecf..edf623a 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -590,7 +590,7 @@ int __init bt_sysfs_init(void)
 
 	bt_class = class_create(THIS_MODULE, "bluetooth");
 
-	return PTR_RET(bt_class);
+	return PTR_ERR_OR_ZERO(bt_class);
 }
 
 void bt_sysfs_cleanup(void)
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
index 70f656c..dbd1c78 100644
--- a/net/bridge/netfilter/ebtable_broute.c
+++ b/net/bridge/netfilter/ebtable_broute.c
@@ -64,7 +64,7 @@ static int ebt_broute(struct sk_buff *skb)
 static int __net_init broute_net_init(struct net *net)
 {
 	net->xt.broute_table = ebt_register_table(net, &broute_table);
-	return PTR_RET(net->xt.broute_table);
+	return PTR_ERR_OR_ZERO(net->xt.broute_table);
 }
 
 static void __net_exit broute_net_exit(struct net *net)
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index 3c2e9dc..94b2b70 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -100,7 +100,7 @@ static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
 static int __net_init frame_filter_net_init(struct net *net)
 {
 	net->xt.frame_filter = ebt_register_table(net, &frame_filter);
-	return PTR_RET(net->xt.frame_filter);
+	return PTR_ERR_OR_ZERO(net->xt.frame_filter);
 }
 
 static void __net_exit frame_filter_net_exit(struct net *net)
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index 10871bc..322555a 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -100,7 +100,7 @@ static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
 static int __net_init frame_nat_net_init(struct net *net)
 {
 	net->xt.frame_nat = ebt_register_table(net, &frame_nat);
-	return PTR_RET(net->xt.frame_nat);
+	return PTR_ERR_OR_ZERO(net->xt.frame_nat);
 }
 
 static void __net_exit frame_nat_net_exit(struct net *net)
diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c
index eadab1e..a865f6f 100644
--- a/net/ipv4/netfilter/arptable_filter.c
+++ b/net/ipv4/netfilter/arptable_filter.c
@@ -48,7 +48,7 @@ static int __net_init arptable_filter_net_init(struct net *net)
 	net->ipv4.arptable_filter =
 		arpt_register_table(net, &packet_filter, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv4.arptable_filter);
+	return PTR_ERR_OR_ZERO(net->ipv4.arptable_filter);
 }
 
 static void __net_exit arptable_filter_net_exit(struct net *net)
diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c
index 6b3da5c..50af5b4 100644
--- a/net/ipv4/netfilter/iptable_filter.c
+++ b/net/ipv4/netfilter/iptable_filter.c
@@ -69,7 +69,7 @@ static int __net_init iptable_filter_net_init(struct net *net)
 	net->ipv4.iptable_filter =
 		ipt_register_table(net, &packet_filter, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv4.iptable_filter);
+	return PTR_ERR_OR_ZERO(net->ipv4.iptable_filter);
 }
 
 static void __net_exit iptable_filter_net_exit(struct net *net)
diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c
index cba5658..0d8cd82 100644
--- a/net/ipv4/netfilter/iptable_mangle.c
+++ b/net/ipv4/netfilter/iptable_mangle.c
@@ -107,7 +107,7 @@ static int __net_init iptable_mangle_net_init(struct net *net)
 	net->ipv4.iptable_mangle =
 		ipt_register_table(net, &packet_mangler, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv4.iptable_mangle);
+	return PTR_ERR_OR_ZERO(net->ipv4.iptable_mangle);
 }
 
 static void __net_exit iptable_mangle_net_exit(struct net *net)
diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c
index 6383273..683bfaf 100644
--- a/net/ipv4/netfilter/iptable_nat.c
+++ b/net/ipv4/netfilter/iptable_nat.c
@@ -292,7 +292,7 @@ static int __net_init iptable_nat_net_init(struct net *net)
 		return -ENOMEM;
 	net->ipv4.nat_table = ipt_register_table(net, &nf_nat_ipv4_table, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv4.nat_table);
+	return PTR_ERR_OR_ZERO(net->ipv4.nat_table);
 }
 
 static void __net_exit iptable_nat_net_exit(struct net *net)
diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c
index 03d9696..1f82aea 100644
--- a/net/ipv4/netfilter/iptable_raw.c
+++ b/net/ipv4/netfilter/iptable_raw.c
@@ -48,7 +48,7 @@ static int __net_init iptable_raw_net_init(struct net *net)
 	net->ipv4.iptable_raw =
 		ipt_register_table(net, &packet_raw, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv4.iptable_raw);
+	return PTR_ERR_OR_ZERO(net->ipv4.iptable_raw);
 }
 
 static void __net_exit iptable_raw_net_exit(struct net *net)
diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c
index b283d8e..f867a8d 100644
--- a/net/ipv4/netfilter/iptable_security.c
+++ b/net/ipv4/netfilter/iptable_security.c
@@ -66,7 +66,7 @@ static int __net_init iptable_security_net_init(struct net *net)
 	net->ipv4.iptable_security =
 		ipt_register_table(net, &security_table, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv4.iptable_security);
+	return PTR_ERR_OR_ZERO(net->ipv4.iptable_security);
 }
 
 static void __net_exit iptable_security_net_exit(struct net *net)
diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c
index beb5777..29b44b1 100644
--- a/net/ipv6/netfilter/ip6table_filter.c
+++ b/net/ipv6/netfilter/ip6table_filter.c
@@ -61,7 +61,7 @@ static int __net_init ip6table_filter_net_init(struct net *net)
 	net->ipv6.ip6table_filter =
 		ip6t_register_table(net, &packet_filter, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv6.ip6table_filter);
+	return PTR_ERR_OR_ZERO(net->ipv6.ip6table_filter);
 }
 
 static void __net_exit ip6table_filter_net_exit(struct net *net)
diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c
index e075399..c705907 100644
--- a/net/ipv6/netfilter/ip6table_mangle.c
+++ b/net/ipv6/netfilter/ip6table_mangle.c
@@ -101,7 +101,7 @@ static int __net_init ip6table_mangle_net_init(struct net *net)
 	net->ipv6.ip6table_mangle =
 		ip6t_register_table(net, &packet_mangler, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv6.ip6table_mangle);
+	return PTR_ERR_OR_ZERO(net->ipv6.ip6table_mangle);
 }
 
 static void __net_exit ip6table_mangle_net_exit(struct net *net)
diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c
index 6383f90..9b076d2 100644
--- a/net/ipv6/netfilter/ip6table_nat.c
+++ b/net/ipv6/netfilter/ip6table_nat.c
@@ -293,7 +293,7 @@ static int __net_init ip6table_nat_net_init(struct net *net)
 		return -ENOMEM;
 	net->ipv6.ip6table_nat = ip6t_register_table(net, &nf_nat_ipv6_table, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv6.ip6table_nat);
+	return PTR_ERR_OR_ZERO(net->ipv6.ip6table_nat);
 }
 
 static void __net_exit ip6table_nat_net_exit(struct net *net)
diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c
index 60d1bdd..9a626d8 100644
--- a/net/ipv6/netfilter/ip6table_raw.c
+++ b/net/ipv6/netfilter/ip6table_raw.c
@@ -40,7 +40,7 @@ static int __net_init ip6table_raw_net_init(struct net *net)
 	net->ipv6.ip6table_raw =
 		ip6t_register_table(net, &packet_raw, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv6.ip6table_raw);
+	return PTR_ERR_OR_ZERO(net->ipv6.ip6table_raw);
 }
 
 static void __net_exit ip6table_raw_net_exit(struct net *net)
diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c
index db15535..ce88d1d 100644
--- a/net/ipv6/netfilter/ip6table_security.c
+++ b/net/ipv6/netfilter/ip6table_security.c
@@ -58,7 +58,7 @@ static int __net_init ip6table_security_net_init(struct net *net)
 	net->ipv6.ip6table_security =
 		ip6t_register_table(net, &security_table, repl);
 	kfree(repl);
-	return PTR_RET(net->ipv6.ip6table_security);
+	return PTR_ERR_OR_ZERO(net->ipv6.ip6table_security);
 }
 
 static void __net_exit ip6table_security_net_exit(struct net *net)
diff --git a/scripts/coccinelle/api/ptr_ret.cocci b/scripts/coccinelle/api/ptr_ret.cocci
index 15f076f..e0981bb 100644
--- a/scripts/coccinelle/api/ptr_ret.cocci
+++ b/scripts/coccinelle/api/ptr_ret.cocci
@@ -1,5 +1,5 @@
 ///
-/// Use PTR_RET rather than if(IS_ERR(...)) + PTR_ERR
+/// Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR
 ///
 // Confidence: High
 // Copyright: (C) 2012 Julia Lawall, INRIA/LIP6.  GPLv2.
@@ -7,7 +7,7 @@
 // URL: http://coccinelle.lip6.fr/
 // Options: -no_includes -include_headers
 //
-// Keywords: ERR_PTR, PTR_ERR, PTR_RET
+// Keywords: ERR_PTR, PTR_ERR, PTR_RET, PTR_ERR_OR_ZERO
 // Version min: 2.6.39
 //
 
@@ -21,21 +21,21 @@ expression ptr;
 @@
 
 - if (IS_ERR(ptr)) return PTR_ERR(ptr); else return 0;
-+ return PTR_RET(ptr);
++ return PTR_ERR_OR_ZERO(ptr);
 
 @depends on patch@
 expression ptr;
 @@
 
 - if (IS_ERR(ptr)) return PTR_ERR(ptr); return 0;
-+ return PTR_RET(ptr);
++ return PTR_ERR_OR_ZERO(ptr);
 
 @depends on patch@
 expression ptr;
 @@
 
 - (IS_ERR(ptr) ? PTR_ERR(ptr) : 0)
-+ PTR_RET(ptr)
++ PTR_ERR_OR_ZERO(ptr)
 
 @r1 depends on !patch@
 expression ptr;
diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c
index 8ca9ecc..122c0c1 100644
--- a/sound/soc/soc-io.c
+++ b/sound/soc/soc-io.c
@@ -158,7 +158,7 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
 		return -EINVAL;
 	}
 
-	return PTR_RET(codec->control_data);
+	return PTR_ERR_OR_ZERO(codec->control_data);
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
 #else
-- 
1.8.1.2

^ permalink raw reply related

* Re: [PATCH 4/4] KVM: PPC: Add hugepage support for IOMMU in-kernel handling
From: Benjamin Herrenschmidt @ 2013-06-16  4:46 UTC (permalink / raw)
  To: Alexey Kardashevskiy
  Cc: kvm, linux-kernel, kvm-ppc, Alexander Graf, Paul Mackerras,
	linuxppc-dev, David Gibson
In-Reply-To: <1370412673-1345-5-git-send-email-aik@ozlabs.ru>

On Wed, 2013-06-05 at 16:11 +1000, Alexey Kardashevskiy wrote:

> @@ -185,7 +186,31 @@ static unsigned long kvmppc_realmode_gpa_to_hpa(struct kvm_vcpu *vcpu,
>  	unsigned long hva, hpa, pg_size = 0, offset;
>  	unsigned long gfn = gpa >> PAGE_SHIFT;
>  	bool writing = gpa & TCE_PCI_WRITE;
> +	struct kvmppc_iommu_hugepage *hp;
>  
> +	/*
> +	 * Try to find an already used hugepage.
> +	 * If it is not there, the kvmppc_lookup_pte() will return zero
> +	 * as it won't do get_page() on a huge page in real mode
> +	 * and therefore the request will be passed to the virtual mode.
> +	 */
> +	if (tt) {
> +		spin_lock(&tt->hugepages_lock);
> +		list_for_each_entry(hp, &tt->hugepages, list) {
> +			if ((gpa < hp->gpa) || (gpa >= hp->gpa + hp->size))
> +				continue;
> +
> +			/* Calculate host phys address keeping flags and offset in the page */
> +			offset = gpa & (hp->size - 1);
> +
> +			/* pte_pfn(pte) should return an address aligned to pg_size */
> +			hpa = (pte_pfn(hp->pte) << PAGE_SHIFT) + offset;
> +			spin_unlock(&tt->hugepages_lock);
> +
> +			return hpa;
> +		}
> +		spin_unlock(&tt->hugepages_lock);
> +	}

Wow .... this is run in real mode right ?

spin_lock() and spin_unlock() are a big no-no in real mode. If lockdep
and/or spinlock debugging are enabled and something goes pear-shaped
they are going to bring your whole system down in a blink in quite
horrible ways.

If you are going to do that, you need some kind of custom low-level
lock.

Also, I see that you are basically using a non-ordered list and doing a
linear search in it every time. That's going to COST !

You should really consider a more efficient data structure. You should
also be able to do something that doesn't require locks for readers.

>  	/* Find a KVM memslot */
>  	memslot = search_memslots(kvm_memslots(vcpu->kvm), gfn);
>  	if (!memslot)
> @@ -237,6 +262,10 @@ static long kvmppc_clear_tce_real_mode(struct kvm_vcpu *vcpu,
>  		if (oldtce & TCE_PCI_WRITE)
>  			SetPageDirty(page);
>  
> +		/* Do not put a huge page and continue without error */
> +		if (PageCompound(page))
> +			continue;
> +
>  		if (realmode_put_page(page)) {
>  			ret = H_TOO_HARD;
>  			break;
> @@ -282,7 +311,7 @@ long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
>  			if (iommu_tce_put_param_check(tbl, ioba, tce))
>  				return H_PARAMETER;
>  
> -			hpa = kvmppc_realmode_gpa_to_hpa(vcpu, tce, true);
> +			hpa = kvmppc_realmode_gpa_to_hpa(vcpu, tt, tce, true);
>  			if (hpa == ERROR_ADDR) {
>  				vcpu->arch.tce_reason = H_TOO_HARD;
>  				return H_TOO_HARD;
> @@ -295,6 +324,11 @@ long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
>  			if (unlikely(ret)) {
>  				struct page *pg = realmode_pfn_to_page(hpa);
>  				BUG_ON(!pg);
> +
> +				/* Do not put a huge page and return an error */
> +				if (!PageCompound(pg))
> +					return H_HARDWARE;
> +
>  				if (realmode_put_page(pg)) {
>  					vcpu->arch.tce_reason = H_HARDWARE;
>  					return H_TOO_HARD;
> @@ -351,7 +385,7 @@ long kvmppc_h_put_tce_indirect(struct kvm_vcpu *vcpu,
>  	vcpu->arch.tce_tmp_num = 0;
>  	vcpu->arch.tce_reason = 0;
>  
> -	tces = (unsigned long *) kvmppc_realmode_gpa_to_hpa(vcpu,
> +	tces = (unsigned long *) kvmppc_realmode_gpa_to_hpa(vcpu, NULL,
>  			tce_list, false);
>  	if ((unsigned long)tces == ERROR_ADDR)
>  		return H_TOO_HARD;
> @@ -374,7 +408,7 @@ long kvmppc_h_put_tce_indirect(struct kvm_vcpu *vcpu,
>  
>  		/* Translate TCEs and go get_page */
>  		for (i = 0; i < npages; ++i) {
> -			unsigned long hpa = kvmppc_realmode_gpa_to_hpa(vcpu,
> +			unsigned long hpa = kvmppc_realmode_gpa_to_hpa(vcpu, tt,
>  					vcpu->arch.tce_tmp[i], true);
>  			if (hpa == ERROR_ADDR) {
>  				vcpu->arch.tce_tmp_num = i;

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH 21/27] powerpc/eeh: Process interrupts caused by EEH
From: Benjamin Herrenschmidt @ 2013-06-16  5:12 UTC (permalink / raw)
  To: Gavin Shan; +Cc: linuxppc-dev
In-Reply-To: <1371286998-2842-22-git-send-email-shangw@linux.vnet.ibm.com>

On Sat, 2013-06-15 at 17:03 +0800, Gavin Shan wrote:
> On PowerNV platform, the EEH event is produced either by detect
> on accessing config or I/O registers, or by interrupts dedicated
> for EEH report. The patch adds support to process the interrupts
> dedicated for EEH report.
> 
> Firstly, the kernel thread will be waken up to process incoming
> interrupt. The PHBs will be scanned one by one to process all
> existing EEH errors. Besides, There're mulple EEH errors that can
> be reported from interrupts and we have differentiated actions
> against them:
> 
> - If the IOC is dead, all PCI buses under all PHBs will be removed
>   from the system.
> - If the PHB is dead, all PCI buses under the PHB will be removed
>   from the system.
> - If the PHB is fenced, EEH event will be sent to EEH core and
>   the fenced PHB is expected to be resetted completely.
> - If specific PE has been put into frozen state, EEH event will
>   be sent to EEH core so that the PE will be resetted.
> - If the error is informational one, we just output the related
>   registers for debugging purpose and no more action will be
>   taken.

Getting better.... but:

 - I still don't like having a kthread for that. Why not use schedule_work() ?

 - We already have an EEH thread, why not just use it ? IE send it a special
type of message that makes it query the backend for error info instead ?

 - I'm not fan of exposing that EEH private lock. I don't entirely understand
why you need to do that either.

Generally speaking, I'm thinking this file should contain less stuff, most of
it should move into the ioda backend, the interrupt just turning into some
request down to the existing EEH thread.

Cheers,
Ben.

^ permalink raw reply

* Re: PowerPC assembler question
From: Erik de Castro Lopo @ 2013-06-16  6:30 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <AE90C24D6B3A694183C094C60CF0A2F6026B727F@saturn3.aculab.com>


Been busy, but I'm back looking at this.

David Laight wrote:

> With a scratch register there are some two instruction sequences.
> Reusing the destination register something like:
>
> 	addis	%r30,%r31,hi(.label - 1b)
> 	lwzx	%r30,lo(.label - 1b)(%r30)
>

I've managed to convert that into something gcc understands:

	addis	30, 31, (.label-(1b))@ha
	lwz	30, (.label-(1b))@l(30)

The "@l" is the equivalent of lo() and "@ha" is the sign extending
verions of hi(). Unfortunately, this code segfaults on executiion.
I suspect that is because I have used the LWZ instruction instead
of LWZX suggested by David.

Unfortunately gcc doesn't accept LWZX which I found documented
here:

     http://pds.twi.tudelft.nl/vakken/in101/labcourse/instruction-set/lwzx.html

and seems to have LX as a synonym, but that instruction only seems
to work on registers, not on addresses as we have here.

I'm actually beginning to have a better understanding of the code
and I've pulled out these snippets code code into a small assember
program I'm compiling with gcc and running under gdb.

Cheers,
Erik
-- 
----------------------------------------------------------------------
Erik de Castro Lopo
http://www.mega-nerd.com/

^ permalink raw reply

* Re: [PATCH 21/27] powerpc/eeh: Process interrupts caused by EEH
From: Gavin Shan @ 2013-06-16  7:27 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, Gavin Shan
In-Reply-To: <1371359531.21896.128.camel@pasglop>

On Sun, Jun 16, 2013 at 03:12:11PM +1000, Benjamin Herrenschmidt wrote:
>On Sat, 2013-06-15 at 17:03 +0800, Gavin Shan wrote:
>> On PowerNV platform, the EEH event is produced either by detect
>> on accessing config or I/O registers, or by interrupts dedicated
>> for EEH report. The patch adds support to process the interrupts
>> dedicated for EEH report.
>> 
>> Firstly, the kernel thread will be waken up to process incoming
>> interrupt. The PHBs will be scanned one by one to process all
>> existing EEH errors. Besides, There're mulple EEH errors that can
>> be reported from interrupts and we have differentiated actions
>> against them:
>> 
>> - If the IOC is dead, all PCI buses under all PHBs will be removed
>>   from the system.
>> - If the PHB is dead, all PCI buses under the PHB will be removed
>>   from the system.
>> - If the PHB is fenced, EEH event will be sent to EEH core and
>>   the fenced PHB is expected to be resetted completely.
>> - If specific PE has been put into frozen state, EEH event will
>>   be sent to EEH core so that the PE will be resetted.
>> - If the error is informational one, we just output the related
>>   registers for debugging purpose and no more action will be
>>   taken.
>

Thanks for the review, Ben.

>Getting better.... but:
>
> - I still don't like having a kthread for that. Why not use schedule_work() ?
>

Ok. Will update it with schedule_work() in next revision :-)

> - We already have an EEH thread, why not just use it ? IE send it a special
>type of message that makes it query the backend for error info instead ?
>

Ok. I'll try to do as you suggested in next revision. Something like:

	- Interrupt comes in
	- OPAL notifier callback
	- Mark all PHB and its subordinate PEs "isolated" since we don't know
	  which PHB/PE has problems (Note: we still need eeh_serialize_lock())
	- Create an EEH event without binding PE to EEH core.
	- EEH core starts new kthread and calls to next_error() backend
	  and handle the EEH errors accordingly.
	  
	  * Informational errors: clear PHB "isolated" state and output diag-data
	    in backend (in eeh-ioda.c as you suggested).
	  * Fenced PHB: PHB complete reset by EEH core and "isolated" state will
	    be cleared during the reset automatically.
	  * Dead PHB: Remove the PHB and its subordinate PCI buses/devices from
		      the system.
	  * Dead IOC: Remove PCI domain from the system.

The problem with the scheme is that the PHB's state can't reflect the real state
any more. For example, PHB#0 has been fenced, but PHB#1 is normal state. We have
to mark all PHBs as "isolated" (fenced) since we don't know which PHB is encountering
problems in the OPAL notifier callback.

I think it would work well. Let me have a try to change the code and make it
workable. The side-effect would be introducing more logic to EEH core and it's
shared by multiple platforms (powernv, pseries, powerkvm guest in future). So
my initial though is making opal_pci_next_error() invisible from EEH core and
make the EEH core totally event-driven :-)

> - I'm not fan of exposing that EEH private lock. I don't entirely understand
>why you need to do that either.
>

It's used to get consistent PE isolated state, which is protected by the lock.
Without it, we would have following case. Since we're going to change the
PE's state in platform code (pci-err.c), we need the lock to protect the PE's
state.

	
		    CPU#0				CPU#1
	PCI-CFG read returns 0xFF's		PCI-CFG read returns 0xFF's
	PE not fenced				PE not fenced
	PE marked as fenced			PE marked as fenced
	EEH event to EEH core			EEH event to EEH core

>Generally speaking, I'm thinking this file should contain less stuff, most of
>it should move into the ioda backend, the interrupt just turning into some
>request down to the existing EEH thread.
>

Yeah, I'll move most of the stuff into eeh-ioda.c with above scheme applied :-)

Thanks,
Gavin

^ permalink raw reply

* Re: [PATCH 21/27] powerpc/eeh: Process interrupts caused by EEH
From: Benjamin Herrenschmidt @ 2013-06-16  8:37 UTC (permalink / raw)
  To: Gavin Shan; +Cc: linuxppc-dev
In-Reply-To: <20130616072744.GA3845@shangw.(null)>

On Sun, 2013-06-16 at 15:27 +0800, Gavin Shan wrote:

> Thanks for the review, Ben.
> 
> >Getting better.... but:
> >
> > - I still don't like having a kthread for that. Why not use schedule_work() ?
> >
> 
> Ok. Will update it with schedule_work() in next revision :-)
> 
> > - We already have an EEH thread, why not just use it ? IE send it a special
> >type of message that makes it query the backend for error info instead ?
> >
> 
> Ok. I'll try to do as you suggested in next revision. Something like:
> 
> 	- Interrupt comes in
> 	- OPAL notifier callback
> 	- Mark all PHB and its subordinate PEs "isolated" since we don't know
> 	  which PHB/PE has problems (Note: we still need eeh_serialize_lock())

No, don't mark anything. It wouldn't be good to start marking "isolated"
things that aren't. It doesn't matter if we don't "know" they are
isolated just yet. Just "poke" the EEH thread with a different type of
message from the current one.

> 	- Create an EEH event without binding PE to EEH core.
> 	- EEH core starts new kthread and calls to next_error() backend
> 	  and handle the EEH errors accordingly.

No need for a new kthread, EEH core already runs in one, doesn't it ? or
am I missing something here ? And yes, if EEH core doesn't get a PE in
the message, then it can call a backend function along the lines of
"next_error()" which ... returns a PE, and does whatever additional
processing we want to do for PHBs.

That can also return "nothing to do" (INF).
	  
> 	  * Informational errors: clear PHB "isolated" state and output diag-data
> 	    in backend (in eeh-ioda.c as you suggested).

Don't mark isolated in the first place.

> 	  * Fenced PHB: PHB complete reset by EEH core and "isolated" state will
> 	    be cleared during the reset automatically.
> 	  * Dead PHB: Remove the PHB and its subordinate PCI buses/devices from
> 		      the system.
> 	  * Dead IOC: Remove PCI domain from the system.

Might want to have return codes from next_error for doing that from the
core, up to you, not a big deal.

> The problem with the scheme is that the PHB's state can't reflect the real state
> any more. For example, PHB#0 has been fenced, but PHB#1 is normal state. We have
> to mark all PHBs as "isolated" (fenced) since we don't know which PHB is encountering
> problems in the OPAL notifier callback.

No, we don't have to mark anything. The interrupt is an asynchronous
thing anyway, so the effect in practice is that we'll react to it a
little bit later, but it's already coming an undefined amount of time
after the error anyway so we may as well deal with it, and that helps
with the synchronization between detection via the interrupt vs.
detection (of the same error) via the MMIO reads.

> I think it would work well. Let me have a try to change the code and make it
> workable. The side-effect would be introducing more logic to EEH core and it's
> shared by multiple platforms (powernv, pseries, powerkvm guest in future). So
> my initial though is making opal_pci_next_error() invisible from EEH core and
> make the EEH core totally event-driven :-)
> 
> > - I'm not fan of exposing that EEH private lock. I don't entirely understand
> >why you need to do that either.
> >
> 
> It's used to get consistent PE isolated state, which is protected by the lock.
> Without it, we would have following case. Since we're going to change the
> PE's state in platform code (pci-err.c), we need the lock to protect the PE's
> state.

I'd rather you expose get/set_state functions and keep the lock local if
that makes sense but first look at what I've proposed above. It might be
that you are right and the lock must be exposed.

> 	
> 		    CPU#0				CPU#1
> 	PCI-CFG read returns 0xFF's		PCI-CFG read returns 0xFF's
> 	PE not fenced				PE not fenced
> 	PE marked as fenced			PE marked as fenced
> 	EEH event to EEH core			EEH event to EEH core
> 
> >Generally speaking, I'm thinking this file should contain less stuff, most of
> >it should move into the ioda backend, the interrupt just turning into some
> >request down to the existing EEH thread.
> >
> 
> Yeah, I'll move most of the stuff into eeh-ioda.c with above scheme applied :-)

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH 1/4] KVM: PPC: Add support for multiple-TCE hcalls
From: Alexander Graf @ 2013-06-16 22:06 UTC (permalink / raw)
  To: Alexey Kardashevskiy
  Cc: kvm, linux-kernel, kvm-ppc, Paul Mackerras, linuxppc-dev,
	David Gibson
In-Reply-To: <1370412673-1345-2-git-send-email-aik@ozlabs.ru>


On 05.06.2013, at 08:11, Alexey Kardashevskiy wrote:

> This adds real mode handlers for the H_PUT_TCE_INDIRECT and
> H_STUFF_TCE hypercalls for QEMU emulated devices such as IBMVIO
> devices or emulated PCI.  These calls allow adding multiple entries
> (up to 512) into the TCE table in one call which saves time on
> transition to/from real mode.
>=20
> This adds a tce_tmp cache to kvm_vcpu_arch to save valid TCEs
> (copied from user and verified) before writing the whole list into
> the TCE table. This cache will be utilized more in the upcoming
> VFIO/IOMMU support to continue TCE list processing in the virtual
> mode in the case if the real mode handler failed for some reason.
>=20
> This adds a guest physical to host real address converter
> and calls the existing H_PUT_TCE handler. The converting function
> is going to be fully utilized by upcoming VFIO supporting patches.
>=20
> This also implements the KVM_CAP_PPC_MULTITCE capability,
> so in order to support the functionality of this patch, QEMU
> needs to query for this capability and set the "hcall-multi-tce"
> hypertas property only if the capability is present, otherwise
> there will be serious performance degradation.
>=20
> Cc: David Gibson <david@gibson.dropbear.id.au>
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> Signed-off-by: Paul Mackerras <paulus@samba.org>

Only a few minor nits. Ben already commented on implementation details.

>=20
> ---
> Changelog:
> 2013/06/05:
> * fixed mistype about IBMVIO in the commit message
> * updated doc and moved it to another section
> * changed capability number
>=20
> 2013/05/21:
> * added kvm_vcpu_arch::tce_tmp
> * removed cleanup if put_indirect failed, instead we do not even start
> writing to TCE table if we cannot get TCEs from the user and they are
> invalid
> * kvmppc_emulated_h_put_tce is split to kvmppc_emulated_put_tce
> and kvmppc_emulated_validate_tce (for the previous item)
> * fixed bug with failthrough for H_IPI
> * removed all get_user() from real mode handlers
> * kvmppc_lookup_pte() added (instead of making lookup_linux_pte =
public)
> ---
> Documentation/virtual/kvm/api.txt       |   17 ++
> arch/powerpc/include/asm/kvm_host.h     |    2 +
> arch/powerpc/include/asm/kvm_ppc.h      |   16 +-
> arch/powerpc/kvm/book3s_64_vio.c        |  118 ++++++++++++++
> arch/powerpc/kvm/book3s_64_vio_hv.c     |  266 =
+++++++++++++++++++++++++++----
> arch/powerpc/kvm/book3s_hv.c            |   39 +++++
> arch/powerpc/kvm/book3s_hv_rmhandlers.S |    6 +
> arch/powerpc/kvm/book3s_pr_papr.c       |   37 ++++-
> arch/powerpc/kvm/powerpc.c              |    3 +
> include/uapi/linux/kvm.h                |    1 +
> 10 files changed, 473 insertions(+), 32 deletions(-)
>=20
> diff --git a/Documentation/virtual/kvm/api.txt =
b/Documentation/virtual/kvm/api.txt
> index 5f91eda..6c082ff 100644
> --- a/Documentation/virtual/kvm/api.txt
> +++ b/Documentation/virtual/kvm/api.txt
> @@ -2362,6 +2362,23 @@ calls by the guest for that service will be =
passed to userspace to be
> handled.
>=20
>=20
> +4.83 KVM_CAP_PPC_MULTITCE
> +
> +Capability: KVM_CAP_PPC_MULTITCE
> +Architectures: ppc
> +Type: vm
> +
> +This capability tells the guest that multiple TCE entry add/remove =
hypercalls
> +handling is supported by the kernel. This significanly accelerates =
DMA
> +operations for PPC KVM guests.
> +
> +Unlike other capabilities in this section, this one does not have an =
ioctl.
> +Instead, when the capability is present, the H_PUT_TCE_INDIRECT and
> +H_STUFF_TCE hypercalls are to be handled in the host kernel and not =
passed to
> +the guest. Othwerwise it might be better for the guest to continue =
using H_PUT_TCE
> +hypercall (if KVM_CAP_SPAPR_TCE or KVM_CAP_SPAPR_TCE_IOMMU are =
present).

While this describes perfectly well what the consequences are of the =
patches, it does not describe properly what the CAP actually expresses. =
The CAP only says "this kernel is able to handle H_PUT_TCE_INDIRECT and =
H_STUFF_TCE hypercalls directly". All other consequences are nice to =
document, but the semantics of the CAP are missing.

We also usually try to keep KVM behavior unchanged with regards to older =
versions until a CAP is enabled. In this case I don't think it matters =
all that much, so I'm fine with declaring it as enabled by default. =
Please document that this is a change in behavior versus older KVM =
versions though.

> +
> +
> 5. The kvm_run structure
> ------------------------
>=20
> diff --git a/arch/powerpc/include/asm/kvm_host.h =
b/arch/powerpc/include/asm/kvm_host.h
> index af326cd..85d8f26 100644
> --- a/arch/powerpc/include/asm/kvm_host.h
> +++ b/arch/powerpc/include/asm/kvm_host.h
> @@ -609,6 +609,8 @@ struct kvm_vcpu_arch {
> 	spinlock_t tbacct_lock;
> 	u64 busy_stolen;
> 	u64 busy_preempt;
> +
> +	unsigned long *tce_tmp;    /* TCE cache for TCE_PUT_INDIRECT =
hall */
> #endif
> };

[...]
>=20
>=20

[...]

> diff --git a/arch/powerpc/kvm/book3s_hv.c =
b/arch/powerpc/kvm/book3s_hv.c
> index 550f592..a39039a 100644
> --- a/arch/powerpc/kvm/book3s_hv.c
> +++ b/arch/powerpc/kvm/book3s_hv.c
> @@ -568,6 +568,30 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu =
*vcpu)
> 			ret =3D kvmppc_xics_hcall(vcpu, req);
> 			break;
> 		} /* fallthrough */

The fallthrough comment isn't accurate anymore.

> +		return RESUME_HOST;
> +	case H_PUT_TCE:
> +		ret =3D kvmppc_virtmode_h_put_tce(vcpu, =
kvmppc_get_gpr(vcpu, 4),
> +						kvmppc_get_gpr(vcpu, 5),
> +						kvmppc_get_gpr(vcpu, =
6));
> +		if (ret =3D=3D H_TOO_HARD)
> +			return RESUME_HOST;
> +		break;
> +	case H_PUT_TCE_INDIRECT:
> +		ret =3D kvmppc_virtmode_h_put_tce_indirect(vcpu, =
kvmppc_get_gpr(vcpu, 4),
> +						kvmppc_get_gpr(vcpu, 5),
> +						kvmppc_get_gpr(vcpu, 6),
> +						kvmppc_get_gpr(vcpu, =
7));
> +		if (ret =3D=3D H_TOO_HARD)
> +			return RESUME_HOST;
> +		break;
> +	case H_STUFF_TCE:
> +		ret =3D kvmppc_virtmode_h_stuff_tce(vcpu, =
kvmppc_get_gpr(vcpu, 4),
> +						kvmppc_get_gpr(vcpu, 5),
> +						kvmppc_get_gpr(vcpu, 6),
> +						kvmppc_get_gpr(vcpu, =
7));
> +		if (ret =3D=3D H_TOO_HARD)
> +			return RESUME_HOST;
> +		break;
> 	default:
> 		return RESUME_HOST;
> 	}
> @@ -958,6 +982,20 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct =
kvm *kvm, unsigned int id)
> 	vcpu->arch.cpu_type =3D KVM_CPU_3S_64;
> 	kvmppc_sanity_check(vcpu);
>=20
> +	/*
> +	 * As we want to minimize the chance of having =
H_PUT_TCE_INDIRECT
> +	 * half executed, we first read TCEs from the user, check them =
and
> +	 * return error if something went wrong and only then put TCEs =
into
> +	 * the TCE table.
> +	 *
> +	 * tce_tmp is a cache for TCEs to avoid stack allocation or
> +	 * kmalloc as the whole TCE list can take up to 512 items 8 =
bytes
> +	 * each (4096 bytes).
> +	 */
> +	vcpu->arch.tce_tmp =3D kmalloc(4096, GFP_KERNEL);
> +	if (!vcpu->arch.tce_tmp)
> +		goto free_vcpu;
> +
> 	return vcpu;
>=20
> free_vcpu:
> @@ -980,6 +1018,7 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
> 	unpin_vpa(vcpu->kvm, &vcpu->arch.slb_shadow);
> 	unpin_vpa(vcpu->kvm, &vcpu->arch.vpa);
> 	spin_unlock(&vcpu->arch.vpa_update_lock);
> +	kfree(vcpu->arch.tce_tmp);
> 	kvm_vcpu_uninit(vcpu);
> 	kmem_cache_free(kvm_vcpu_cache, vcpu);
> }
> diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S =
b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> index b02f91e..d35554e 100644
> --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> @@ -1490,6 +1490,12 @@ hcall_real_table:
> 	.long	0		/* 0x11c */
> 	.long	0		/* 0x120 */
> 	.long	.kvmppc_h_bulk_remove - hcall_real_table
> +	.long	0		/* 0x128 */
> +	.long	0		/* 0x12c */
> +	.long	0		/* 0x130 */
> +	.long	0		/* 0x134 */
> +	.long	.kvmppc_h_stuff_tce - hcall_real_table
> +	.long	.kvmppc_h_put_tce_indirect - hcall_real_table
> hcall_real_table_end:
>=20
> ignore_hdec:
> diff --git a/arch/powerpc/kvm/book3s_pr_papr.c =
b/arch/powerpc/kvm/book3s_pr_papr.c
> index da0e0bc..91d4b45 100644
> --- a/arch/powerpc/kvm/book3s_pr_papr.c
> +++ b/arch/powerpc/kvm/book3s_pr_papr.c
> @@ -220,7 +220,38 @@ static int kvmppc_h_pr_put_tce(struct kvm_vcpu =
*vcpu)
> 	unsigned long tce =3D kvmppc_get_gpr(vcpu, 6);
> 	long rc;
>=20
> -	rc =3D kvmppc_h_put_tce(vcpu, liobn, ioba, tce);
> +	rc =3D kvmppc_virtmode_h_put_tce(vcpu, liobn, ioba, tce);
> +	if (rc =3D=3D H_TOO_HARD)
> +		return EMULATE_FAIL;
> +	kvmppc_set_gpr(vcpu, 3, rc);
> +	return EMULATE_DONE;
> +}
> +
> +static int kvmppc_h_pr_put_tce_indirect(struct kvm_vcpu *vcpu)
> +{
> +	unsigned long liobn =3D kvmppc_get_gpr(vcpu, 4);
> +	unsigned long ioba =3D kvmppc_get_gpr(vcpu, 5);
> +	unsigned long tce =3D kvmppc_get_gpr(vcpu, 6);
> +	unsigned long npages =3D kvmppc_get_gpr(vcpu, 7);
> +	long rc;
> +
> +	rc =3D kvmppc_virtmode_h_put_tce_indirect(vcpu, liobn, ioba,
> +			tce, npages);
> +	if (rc =3D=3D H_TOO_HARD)
> +		return EMULATE_FAIL;
> +	kvmppc_set_gpr(vcpu, 3, rc);
> +	return EMULATE_DONE;
> +}
> +
> +static int kvmppc_h_pr_stuff_tce(struct kvm_vcpu *vcpu)
> +{
> +	unsigned long liobn =3D kvmppc_get_gpr(vcpu, 4);
> +	unsigned long ioba =3D kvmppc_get_gpr(vcpu, 5);
> +	unsigned long tce_value =3D kvmppc_get_gpr(vcpu, 6);
> +	unsigned long npages =3D kvmppc_get_gpr(vcpu, 7);
> +	long rc;
> +
> +	rc =3D kvmppc_virtmode_h_stuff_tce(vcpu, liobn, ioba, tce_value, =
npages);
> 	if (rc =3D=3D H_TOO_HARD)
> 		return EMULATE_FAIL;
> 	kvmppc_set_gpr(vcpu, 3, rc);
> @@ -247,6 +278,10 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned =
long cmd)
> 		return kvmppc_h_pr_bulk_remove(vcpu);
> 	case H_PUT_TCE:
> 		return kvmppc_h_pr_put_tce(vcpu);
> +	case H_PUT_TCE_INDIRECT:
> +		return kvmppc_h_pr_put_tce_indirect(vcpu);
> +	case H_STUFF_TCE:
> +		return kvmppc_h_pr_stuff_tce(vcpu);
> 	case H_CEDE:
> 		vcpu->arch.shared->msr |=3D MSR_EE;
> 		kvm_vcpu_block(vcpu);
> diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
> index 6316ee3..8465c2a 100644
> --- a/arch/powerpc/kvm/powerpc.c
> +++ b/arch/powerpc/kvm/powerpc.c
> @@ -395,6 +395,9 @@ int kvm_dev_ioctl_check_extension(long ext)
> 		r =3D 1;
> 		break;
> #endif
> +	case KVM_CAP_SPAPR_MULTITCE:
> +		r =3D 1;

This should only be true for book3s.



Alex

^ permalink raw reply

* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Alexander Graf @ 2013-06-16 22:25 UTC (permalink / raw)
  To: Alexey Kardashevskiy
  Cc: kvm, linux-kernel, kvm-ppc, Paul Mackerras, linuxppc-dev,
	David Gibson
In-Reply-To: <1370412673-1345-4-git-send-email-aik@ozlabs.ru>


On 05.06.2013, at 08:11, Alexey Kardashevskiy wrote:

> This allows the host kernel to handle H_PUT_TCE, H_PUT_TCE_INDIRECT
> and H_STUFF_TCE requests without passing them to QEMU, which should
> save time on switching to QEMU and back.
>=20
> Both real and virtual modes are supported - whenever the kernel
> fails to handle TCE request, it passes it to the virtual mode.
> If it the virtual mode handlers fail, then the request is passed
> to the user mode, for example, to QEMU.
>=20
> This adds a new KVM_CAP_SPAPR_TCE_IOMMU ioctl to asssociate
> a virtual PCI bus ID (LIOBN) with an IOMMU group, which enables
> in-kernel handling of IOMMU map/unmap.
>=20
> Tests show that this patch increases transmission speed from 220MB/s
> to 750..1020MB/s on 10Gb network (Chelsea CXGB3 10Gb ethernet card).
>=20
> Cc: David Gibson <david@gibson.dropbear.id.au>
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> Signed-off-by: Paul Mackerras <paulus@samba.org>
>=20
> ---
>=20
> Changes:
> 2013/06/05:
> * changed capability number
> * changed ioctl number
> * update the doc article number
>=20
> 2013/05/20:
> * removed get_user() from real mode handlers
> * kvm_vcpu_arch::tce_tmp usage extended. Now real mode handler puts =
there
> translated TCEs, tries realmode_get_page() on those and if it fails, =
it
> passes control over the virtual mode handler which tries to finish
> the request handling
> * kvmppc_lookup_pte() now does realmode_get_page() protected by BUSY =
bit
> on a page
> * The only reason to pass the request to user mode now is when the =
user mode
> did not register TCE table in the kernel, in all other cases the =
virtual mode
> handler is expected to do the job
> ---
> Documentation/virtual/kvm/api.txt   |   28 +++++
> arch/powerpc/include/asm/kvm_host.h |    3 +
> arch/powerpc/include/asm/kvm_ppc.h  |    2 +
> arch/powerpc/include/uapi/asm/kvm.h |    7 ++
> arch/powerpc/kvm/book3s_64_vio.c    |  198 =
++++++++++++++++++++++++++++++++++-
> arch/powerpc/kvm/book3s_64_vio_hv.c |  193 =
+++++++++++++++++++++++++++++++++-
> arch/powerpc/kvm/powerpc.c          |   12 +++
> include/uapi/linux/kvm.h            |    2 +
> 8 files changed, 439 insertions(+), 6 deletions(-)
>=20
> diff --git a/Documentation/virtual/kvm/api.txt =
b/Documentation/virtual/kvm/api.txt
> index 6c082ff..e962e3b 100644
> --- a/Documentation/virtual/kvm/api.txt
> +++ b/Documentation/virtual/kvm/api.txt
> @@ -2379,6 +2379,34 @@ the guest. Othwerwise it might be better for =
the guest to continue using H_PUT_T
> hypercall (if KVM_CAP_SPAPR_TCE or KVM_CAP_SPAPR_TCE_IOMMU are =
present).
>=20
>=20
> +4.84 KVM_CREATE_SPAPR_TCE_IOMMU
> +
> +Capability: KVM_CAP_SPAPR_TCE_IOMMU
> +Architectures: powerpc
> +Type: vm ioctl
> +Parameters: struct kvm_create_spapr_tce_iommu (in)
> +Returns: 0 on success, -1 on error
> +
> +This creates a link between IOMMU group and a hardware TCE =
(translation
> +control entry) table. This link lets the host kernel know what IOMMU
> +group (i.e. TCE table) to use for the LIOBN number passed with
> +H_PUT_TCE, H_PUT_TCE_INDIRECT, H_STUFF_TCE hypercalls.
> +
> +/* for KVM_CAP_SPAPR_TCE_IOMMU */
> +struct kvm_create_spapr_tce_iommu {
> +	__u64 liobn;
> +	__u32 iommu_id;
> +	__u32 flags;
> +};
> +
> +No flag is supported at the moment.
> +
> +When the guest issues TCE call on a liobn for which a TCE table has =
been
> +registered, the kernel will handle it in real mode, updating the =
hardware
> +TCE table. TCE table calls for other liobns will cause a vm exit and =
must
> +be handled by userspace.

Ok, please walk me through the security model you have in mind here.

Basically what this ioctl does is that it creates a guest TCE table that =
reflects its changes into a host TCE table whenever it gets modified. So =
far so good.

Now I don't see any checks that verify whether iommu_id is actually good =
to use from that user's access rights. Just because I have access to =
/dev/kvm I don't necessarily have access to an iommu control device.

So the least I can see would be a local DoS attack where one user space =
program with only access to /dev/kvm can simply kill any access to =
another process's device by overflowing a host iommu TCE table with junk =
entries.

There's even a certain chance of an information disclosure exploit here =
where a malicious user space program could get itself all network =
traffic DMA'd from another VM.

How does this work on the host level? What is the security token to take =
control of a host TCE table?


Alex

^ permalink raw reply

* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Benjamin Herrenschmidt @ 2013-06-16 22:39 UTC (permalink / raw)
  To: Alexey Kardashevskiy
  Cc: kvm, linux-kernel, kvm-ppc, Alexander Graf, Alex Williamson,
	Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1370412673-1345-4-git-send-email-aik@ozlabs.ru>

On Wed, 2013-06-05 at 16:11 +1000, Alexey Kardashevskiy wrote:
> +long kvm_vm_ioctl_create_spapr_tce_iommu(struct kvm *kvm,
> +               struct kvm_create_spapr_tce_iommu *args)
> +{
> +       struct kvmppc_spapr_tce_table *tt = NULL;
> +       struct iommu_group *grp;
> +       struct iommu_table *tbl;
> +
> +       /* Find an IOMMU table for the given ID */
> +       grp = iommu_group_get_by_id(args->iommu_id);
> +       if (!grp)
> +               return -ENXIO;
> +
> +       tbl = iommu_group_get_iommudata(grp);
> +       if (!tbl)
> +               return -ENXIO;

So Alex Graf pointed out here, there is a security issue here, or are we
missing something ?

What prevents a malicious program that has access to /dev/kvm from
taking over random iommu groups (including host used ones) that way?

What is the security model of that whole iommu stuff to begin with ?

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH -V10 00/15] THP support for PPC64
From: Benjamin Herrenschmidt @ 2013-06-16 23:02 UTC (permalink / raw)
  To: Aneesh Kumar K.V; +Cc: Alexey Kardashevskiy, linuxppc-dev, paulus
In-Reply-To: <1371355567.21896.101.camel@pasglop>

On Sun, 2013-06-16 at 14:06 +1000, Benjamin Herrenschmidt wrote:
> Look at patches http://patchwork.ozlabs.org/patch/248920/ and followup,
> he basically walks the page tables here in a slightly different way than
> Paul does in H_ENTER. It's more like gup_fast. It will need to handle
> concurrent split/collapse etc... as well which it doesn't right now.
> 
> I'm considering merging Alexei stuff first (provided I don't find major
> problems with it), then you can provide a new THP series on top of it.

Ok, for now ignore Alexey stuff, there's enough issues in it that I can't
quite merge it yet. Just handle my other comments.

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH 1/5] powerpc/dts: add MPIC v4.3 dts node
From: Lian Minghuan-b31939 @ 2013-06-17  2:23 UTC (permalink / raw)
  To: Scott Wood; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <1371246799.2996.14@snotra>

Hi Scott,

please see my comments.

On 06/15/2013 05:53 AM, Scott Wood wrote:
> On 06/14/2013 03:39:26 PM, Scott Wood wrote:
>> On 06/14/2013 02:15:55 AM, Minghuan Lian wrote:
>>> +msi0: msi@41600 {
>>> +    compatible = "fsl,mpic-msi", "fsl,mpic-msi-v4.3";
>>
>> More specific compatibles come first -- and I don't think this is 
>> 100% backwards compatible with "fsl,mpic-msi" anyway.
>
> Also please update the binding.
[Minghuan] Yes, maybe I should remove "fsl,mpic-msi". What do you think?
>
> -Scott

^ permalink raw reply

* Re: PowerPC assembler question
From: Benjamin Herrenschmidt @ 2013-06-17  2:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20130616163041.fa5d1f2d41118b39e22241d5@mega-nerd.com>

On Sun, 2013-06-16 at 16:30 +1000, Erik de Castro Lopo wrote:
> Been busy, but I'm back looking at this.
> 
> David Laight wrote:
> 
> > With a scratch register there are some two instruction sequences.
> > Reusing the destination register something like:
> >
> > 	addis	%r30,%r31,hi(.label - 1b)
> > 	lwzx	%r30,lo(.label - 1b)(%r30)
> >
> 
> I've managed to convert that into something gcc understands:
> 
> 	addis	30, 31, (.label-(1b))@ha
> 	lwz	30, (.label-(1b))@l(30)
> 
> The "@l" is the equivalent of lo() and "@ha" is the sign extending
> verions of hi(). Unfortunately, this code segfaults on executiion.
> I suspect that is because I have used the LWZ instruction instead
> of LWZX suggested by David.

No, lwz is the right instruction. The above should work, tried
stepping it in something like gdb to see waht exactly is going on ?

lwzx is a form that takes only registers (adds two registers to make
the address to load from). lwz takes a register and an immediate offset.

Cheers,
Ben.


> Unfortunately gcc doesn't accept LWZX which I found documented
> here:
> 
>      http://pds.twi.tudelft.nl/vakken/in101/labcourse/instruction-set/lwzx.html
> 
> and seems to have LX as a synonym, but that instruction only seems
> to work on registers, not on addresses as we have here.
> 
> I'm actually beginning to have a better understanding of the code
> and I've pulled out these snippets code code into a small assember
> program I'm compiling with gcc and running under gdb.
> 
> Cheers,
> Erik

^ permalink raw reply

* Re: [PATCH 2/5] powerpc/fsl_msi: add MSIIR1 support for MPIC v4.3
From: Lian Minghuan-b31939 @ 2013-06-17  3:00 UTC (permalink / raw)
  To: Scott Wood; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <1371247751.2996.16@snotra>

Hi Scott,

please see my comments inline.

On 06/15/2013 06:09 AM, Scott Wood wrote:
> On 06/14/2013 02:15:56 AM, Minghuan Lian wrote:
>> @@ -421,10 +440,29 @@ static int fsl_of_msi_probe(struct 
>> platform_device *dev)
>>          }
>>          msi->msiir_offset =
>>              features->msiir_offset + (res.start & 0xfffff);
>> +
>> +        /*
>> +         * First read the MSIIR/MSIIR1 offset from dts
>> +         * If failure use the hardcode MSIIR offset
>
> "On failure"
>
[Minghuan] OK, thanks.
>> +         */
>> +        if (of_address_to_resource(dev->dev.of_node, 1, &msiir))
>> +            msi->msiir_offset = features->msiir_offset +
>> +                        (res.start & MSIIR_OFFSET_MASK);
>> +        else
>> +            msi->msiir_offset = msiir.start & MSIIR_OFFSET_MASK;
>>      }
>>
>>      msi->feature = features->fsl_pic_ip;
>>
>> +    if (of_device_is_compatible(dev->dev.of_node, 
>> "fsl,mpic-msi-v4.3")) {
>> +        msi->srs_shift = MSIIR1_SRS_SHIFT;
>> +        msi->ibs_shift = MSIIR1_IBS_SHIFT;
>> +
>> +    } else {
>> +        msi->srs_shift = MSIIR_SRS_SHIFT;
>> +        msi->ibs_shift = MSIIR_IBS_SHIFT;
>> +    }
>
> Remove the blank line just before the "} else {".
>
[Minghuan] OK, Thanks.
>> diff --git a/arch/powerpc/sysdev/fsl_msi.h 
>> b/arch/powerpc/sysdev/fsl_msi.h
>> index 8225f86..43a9d99 100644
>> --- a/arch/powerpc/sysdev/fsl_msi.h
>> +++ b/arch/powerpc/sysdev/fsl_msi.h
>> @@ -16,7 +16,7 @@
>>  #include <linux/of.h>
>>  #include <asm/msi_bitmap.h>
>>
>> -#define NR_MSI_REG        8
>> +#define NR_MSI_REG        16
>>  #define IRQS_PER_MSI_REG    32
>>  #define NR_MSI_IRQS    (NR_MSI_REG * IRQS_PER_MSI_REG)
>
> I don't see where you update all_avail in fsl_of_msi_probe.
>
> We should also be bounds-checking the contents of msi-available-ranges.
> Currently it looks like we just silently overrun the bitmap if we get bad
> input from the device tree.
>
[Minghuan] all_avail definition: static const u32 all_avail[] = { 0, 
NR_MSI_IRQS };
When changing NR_MSI_REG to 16, NR_MSI_IRQS has been changed to 16*32, 
and all_avail also is updated.

Before calling fsl_msi_setup_hwirq(), the code has checked 
'msi-available-ranges',  only the interrupts lied in 
'msi-available-ranges' will be initialized by call fsl_msi_setup_hwirq() 
, and the corresponding bitmap will be freed. I moved 
msi_bitmap_free_hwirqs() to fsl_msi_setup_hwirq(), because the code 
would generate different bitmap when using MSIIR or MSIIR1.

> -Scott

^ permalink raw reply

* Re: [PATCH 01/27] powerpc/eeh: Move common part to kernel directory
From: Mike Qiu @ 2013-06-17  3:03 UTC (permalink / raw)
  To: Gavin Shan; +Cc: linuxppc-dev
In-Reply-To: <1371286998-2842-2-git-send-email-shangw@linux.vnet.ibm.com>

于 2013/6/15 17:02, Gavin Shan 写道:
> The patch moves the common part of EEH core into arch/powerpc/kernel
> directory so that we needn't PPC_PSERIES while compiling POWERNV
> platform:
>
> 	* Move the EEH common part into arch/powerpc/kernel
> 	* Move the functions for PCI hotplug from pSeries platform to
> 	  arch/powerpc/kernel/pci_hotplug.c
> 	* Move CONFIG_EEH from arch/powerpc/platforms/pseries/Kconfig to
> 	  arch/powerpc/platforms/Kconfig
> 	* Adjust makefile accordingly
>
> Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
> ---
>   arch/powerpc/kernel/Makefile                |    4 +-
>   arch/powerpc/kernel/eeh.c                   |  942 +++++++++++++++++++++++++++
>   arch/powerpc/kernel/eeh_cache.c             |  319 +++++++++
>   arch/powerpc/kernel/eeh_dev.c               |  112 ++++
>   arch/powerpc/kernel/eeh_driver.c            |  552 ++++++++++++++++
>   arch/powerpc/kernel/eeh_event.c             |  142 ++++
>   arch/powerpc/kernel/eeh_pe.c                |  653 +++++++++++++++++++
>   arch/powerpc/kernel/eeh_sysfs.c             |   75 +++
>   arch/powerpc/kernel/pci_hotplug.c           |  111 ++++
>   arch/powerpc/platforms/Kconfig              |    5 +
>   arch/powerpc/platforms/pseries/Kconfig      |    5 -
>   arch/powerpc/platforms/pseries/Makefile     |    4 +-
>   arch/powerpc/platforms/pseries/eeh.c        |  942 ---------------------------
>   arch/powerpc/platforms/pseries/eeh_cache.c  |  319 ---------
>   arch/powerpc/platforms/pseries/eeh_dev.c    |  112 ----
>   arch/powerpc/platforms/pseries/eeh_driver.c |  552 ----------------
>   arch/powerpc/platforms/pseries/eeh_event.c  |  142 ----
>   arch/powerpc/platforms/pseries/eeh_pe.c     |  653 -------------------
>   arch/powerpc/platforms/pseries/eeh_sysfs.c  |   75 ---
>   arch/powerpc/platforms/pseries/pci_dlpar.c  |   85 ---
>   20 files changed, 2915 insertions(+), 2889 deletions(-)
>   create mode 100644 arch/powerpc/kernel/eeh.c
>   create mode 100644 arch/powerpc/kernel/eeh_cache.c
>   create mode 100644 arch/powerpc/kernel/eeh_dev.c
>   create mode 100644 arch/powerpc/kernel/eeh_driver.c
>   create mode 100644 arch/powerpc/kernel/eeh_event.c
>   create mode 100644 arch/powerpc/kernel/eeh_pe.c
>   create mode 100644 arch/powerpc/kernel/eeh_sysfs.c
>   create mode 100644 arch/powerpc/kernel/pci_hotplug.c
>   delete mode 100644 arch/powerpc/platforms/pseries/eeh.c
>   delete mode 100644 arch/powerpc/platforms/pseries/eeh_cache.c
>   delete mode 100644 arch/powerpc/platforms/pseries/eeh_dev.c
>   delete mode 100644 arch/powerpc/platforms/pseries/eeh_driver.c
>   delete mode 100644 arch/powerpc/platforms/pseries/eeh_event.c
>   delete mode 100644 arch/powerpc/platforms/pseries/eeh_pe.c
>   delete mode 100644 arch/powerpc/platforms/pseries/eeh_sysfs.c
>
> diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
> index f960a79..5826906 100644
> --- a/arch/powerpc/kernel/Makefile
> +++ b/arch/powerpc/kernel/Makefile
> @@ -58,6 +58,8 @@ obj-$(CONFIG_RTAS_PROC)		+= rtas-proc.o
>   obj-$(CONFIG_LPARCFG)		+= lparcfg.o
>   obj-$(CONFIG_IBMVIO)		+= vio.o
>   obj-$(CONFIG_IBMEBUS)           += ibmebus.o
> +obj-$(CONFIG_EEH)		+= eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \
> +				   eeh_driver.o eeh_event.o eeh_sysfs.o
>   obj-$(CONFIG_GENERIC_TBSYNC)	+= smp-tbsync.o
>   obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
>   obj-$(CONFIG_FA_DUMP)		+= fadump.o
> @@ -100,7 +102,7 @@ obj-$(CONFIG_PPC_UDBG_16550)	+= legacy_serial.o udbg_16550.o
>   obj-$(CONFIG_STACKTRACE)	+= stacktrace.o
>   obj-$(CONFIG_SWIOTLB)		+= dma-swiotlb.o
>
> -pci64-$(CONFIG_PPC64)		+= pci_dn.o isa-bridge.o
> +pci64-$(CONFIG_PPC64)		+= pci_hotplug.o pci_dn.o isa-bridge.o
>   obj-$(CONFIG_PCI)		+= pci_$(CONFIG_WORD_SIZE).o $(pci64-y) \
>   				   pci-common.o pci_of_scan.o
>   obj-$(CONFIG_PCI_MSI)		+= msi.o
> diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
> new file mode 100644
> index 0000000..6b73d6c
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh.c
> @@ -0,0 +1,942 @@
> +/*
> + * Copyright IBM Corporation 2001, 2005, 2006
> + * Copyright Dave Engebretsen & Todd Inglett 2001
> + * Copyright Linas Vepstas 2005, 2006
> + * Copyright 2001-2012 IBM Corporation.
> + *
> + * 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
> + *
> + * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/sched.h>
> +#include <linux/init.h>
> +#include <linux/list.h>
> +#include <linux/pci.h>
> +#include <linux/proc_fs.h>
> +#include <linux/rbtree.h>
> +#include <linux/seq_file.h>
> +#include <linux/spinlock.h>
> +#include <linux/export.h>
> +#include <linux/of.h>
> +
> +#include <linux/atomic.h>
> +#include <asm/eeh.h>
> +#include <asm/eeh_event.h>
> +#include <asm/io.h>
> +#include <asm/machdep.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/rtas.h>
> +
> +
> +/** Overview:
> + *  EEH, or "Extended Error Handling" is a PCI bridge technology for
> + *  dealing with PCI bus errors that can't be dealt with within the
> + *  usual PCI framework, except by check-stopping the CPU.  Systems
> + *  that are designed for high-availability/reliability cannot afford
> + *  to crash due to a "mere" PCI error, thus the need for EEH.
> + *  An EEH-capable bridge operates by converting a detected error
> + *  into a "slot freeze", taking the PCI adapter off-line, making
> + *  the slot behave, from the OS'es point of view, as if the slot
> + *  were "empty": all reads return 0xff's and all writes are silently
> + *  ignored.  EEH slot isolation events can be triggered by parity
> + *  errors on the address or data busses (e.g. during posted writes),
> + *  which in turn might be caused by low voltage on the bus, dust,
> + *  vibration, humidity, radioactivity or plain-old failed hardware.
> + *
> + *  Note, however, that one of the leading causes of EEH slot
> + *  freeze events are buggy device drivers, buggy device microcode,
> + *  or buggy device hardware.  This is because any attempt by the
> + *  device to bus-master data to a memory address that is not
> + *  assigned to the device will trigger a slot freeze.   (The idea
> + *  is to prevent devices-gone-wild from corrupting system memory).
> + *  Buggy hardware/drivers will have a miserable time co-existing
> + *  with EEH.
> + *
> + *  Ideally, a PCI device driver, when suspecting that an isolation
> + *  event has occurred (e.g. by reading 0xff's), will then ask EEH
> + *  whether this is the case, and then take appropriate steps to
> + *  reset the PCI slot, the PCI device, and then resume operations.
> + *  However, until that day,  the checking is done here, with the
> + *  eeh_check_failure() routine embedded in the MMIO macros.  If
> + *  the slot is found to be isolated, an "EEH Event" is synthesized
> + *  and sent out for processing.
> + */
> +
> +/* If a device driver keeps reading an MMIO register in an interrupt
> + * handler after a slot isolation event, it might be broken.
> + * This sets the threshold for how many read attempts we allow
> + * before printing an error message.
> + */
> +#define EEH_MAX_FAILS	2100000
> +
> +/* Time to wait for a PCI slot to report status, in milliseconds */
> +#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
> +
> +/* Platform dependent EEH operations */
> +struct eeh_ops *eeh_ops = NULL;
> +
> +int eeh_subsystem_enabled;
> +EXPORT_SYMBOL(eeh_subsystem_enabled);
> +
> +/*
> + * EEH probe mode support. The intention is to support multiple
> + * platforms for EEH. Some platforms like pSeries do PCI emunation
> + * based on device tree. However, other platforms like powernv probe
> + * PCI devices from hardware. The flag is used to distinguish that.
> + * In addition, struct eeh_ops::probe would be invoked for particular
> + * OF node or PCI device so that the corresponding PE would be created
> + * there.
> + */
> +int eeh_probe_mode;
> +
> +/* Global EEH mutex */
> +DEFINE_MUTEX(eeh_mutex);
> +
> +/* Lock to avoid races due to multiple reports of an error */
> +static DEFINE_RAW_SPINLOCK(confirm_error_lock);
> +
> +/* Buffer for reporting pci register dumps. Its here in BSS, and
> + * not dynamically alloced, so that it ends up in RMO where RTAS
> + * can access it.
> + */
> +#define EEH_PCI_REGS_LOG_LEN 4096
> +static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
> +
> +/*
> + * The struct is used to maintain the EEH global statistic
> + * information. Besides, the EEH global statistics will be
> + * exported to user space through procfs
> + */
> +struct eeh_stats {
> +	u64 no_device;		/* PCI device not found		*/
> +	u64 no_dn;		/* OF node not found		*/
> +	u64 no_cfg_addr;	/* Config address not found	*/
> +	u64 ignored_check;	/* EEH check skipped		*/
> +	u64 total_mmio_ffs;	/* Total EEH checks		*/
> +	u64 false_positives;	/* Unnecessary EEH checks	*/
> +	u64 slot_resets;	/* PE reset			*/
> +};
> +
> +static struct eeh_stats eeh_stats;
> +
> +#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
> +
> +/**
> + * eeh_gather_pci_data - Copy assorted PCI config space registers to buff
> + * @edev: device to report data for
> + * @buf: point to buffer in which to log
> + * @len: amount of room in buffer
> + *
> + * This routine captures assorted PCI configuration space data,
> + * and puts them into a buffer for RTAS error logging.
> + */
> +static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
> +{
> +	struct device_node *dn = eeh_dev_to_of_node(edev);
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	u32 cfg;
> +	int cap, i;
> +	int n = 0;
> +
> +	n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
> +	printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
> +
> +	eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
> +	n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
> +	printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
> +
> +	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
> +	n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
> +	printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
> +
> +	if (!dev) {
> +		printk(KERN_WARNING "EEH: no PCI device for this of node\n");
> +		return n;
> +	}
> +
> +	/* Gather bridge-specific registers */
> +	if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
> +		eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
> +		n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
> +		printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
> +
> +		eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
> +		n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
> +		printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
> +	}
> +
> +	/* Dump out the PCI-X command and status regs */
> +	cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
BTW, when move common part , here you could use dev->pcie_cap at your 
convenience, and pcie_cap has
been initialized in of_create_pci_dev--->set_pcie_port_type
> +	if (cap) {
> +		eeh_ops->read_config(dn, cap, 4, &cfg);
> +		n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
> +		printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
> +
> +		eeh_ops->read_config(dn, cap+4, 4, &cfg);
> +		n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
> +		printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
> +	}
> +
> +	/* If PCI-E capable, dump PCI-E cap 10, and the AER */
> +	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
> +	if (cap) {
> +		n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
> +		printk(KERN_WARNING
> +		       "EEH: PCI-E capabilities and status follow:\n");
> +
> +		for (i=0; i<=8; i++) {
> +			eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> +			n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> +			printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
> +		}
> +
> +		cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
> +		if (cap) {
> +			n += scnprintf(buf+n, len-n, "pci-e AER:\n");
> +			printk(KERN_WARNING
> +			       "EEH: PCI-E AER capability register set follows:\n");
> +
> +			for (i=0; i<14; i++) {
> +				eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> +				n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> +				printk(KERN_WARNING "EEH: PCI-E AER %02x: %08x\n", i, cfg);
> +			}
> +		}
> +	}
> +
> +	return n;
> +}
> +
> +/**
> + * eeh_slot_error_detail - Generate combined log including driver log and error log
> + * @pe: EEH PE
> + * @severity: temporary or permanent error log
> + *
> + * This routine should be called to generate the combined log, which
> + * is comprised of driver log and error log. The driver log is figured
> + * out from the config space of the corresponding PCI device, while
> + * the error log is fetched through platform dependent function call.
> + */
> +void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
> +{
> +	size_t loglen = 0;
> +	struct eeh_dev *edev;
> +
> +	eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> +	eeh_ops->configure_bridge(pe);
> +	eeh_pe_restore_bars(pe);
> +
> +	pci_regs_buf[0] = 0;
> +	eeh_pe_for_each_dev(pe, edev) {
> +		loglen += eeh_gather_pci_data(edev, pci_regs_buf,
> +				EEH_PCI_REGS_LOG_LEN);
> +        }
> +
> +	eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);
> +}
> +
> +/**
> + * eeh_token_to_phys - Convert EEH address token to phys address
> + * @token: I/O token, should be address in the form 0xA....
> + *
> + * This routine should be called to convert virtual I/O address
> + * to physical one.
> + */
> +static inline unsigned long eeh_token_to_phys(unsigned long token)
> +{
> +	pte_t *ptep;
> +	unsigned long pa;
> +
> +	ptep = find_linux_pte(init_mm.pgd, token);
> +	if (!ptep)
> +		return token;
> +	pa = pte_pfn(*ptep) << PAGE_SHIFT;
> +
> +	return pa | (token & (PAGE_SIZE-1));
> +}
> +
> +/**
> + * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze
> + * @edev: eeh device
> + *
> + * Check for an EEH failure for the given device node.  Call this
> + * routine if the result of a read was all 0xff's and you want to
> + * find out if this is due to an EEH slot freeze.  This routine
> + * will query firmware for the EEH status.
> + *
> + * Returns 0 if there has not been an EEH error; otherwise returns
> + * a non-zero value and queues up a slot isolation event notification.
> + *
> + * It is safe to call this routine in an interrupt context.
> + */
> +int eeh_dev_check_failure(struct eeh_dev *edev)
> +{
> +	int ret;
> +	unsigned long flags;
> +	struct device_node *dn;
> +	struct pci_dev *dev;
> +	struct eeh_pe *pe;
> +	int rc = 0;
> +	const char *location;
> +
> +	eeh_stats.total_mmio_ffs++;
> +
> +	if (!eeh_subsystem_enabled)
> +		return 0;
> +
> +	if (!edev) {
> +		eeh_stats.no_dn++;
> +		return 0;
> +	}
> +	dn = eeh_dev_to_of_node(edev);
> +	dev = eeh_dev_to_pci_dev(edev);
> +	pe = edev->pe;
> +
> +	/* Access to IO BARs might get this far and still not want checking. */
> +	if (!pe) {
> +		eeh_stats.ignored_check++;
> +		pr_debug("EEH: Ignored check for %s %s\n",
> +			eeh_pci_name(dev), dn->full_name);
> +		return 0;
> +	}
> +
> +	if (!pe->addr && !pe->config_addr) {
> +		eeh_stats.no_cfg_addr++;
> +		return 0;
> +	}
> +
> +	/* If we already have a pending isolation event for this
> +	 * slot, we know it's bad already, we don't need to check.
> +	 * Do this checking under a lock; as multiple PCI devices
> +	 * in one slot might report errors simultaneously, and we
> +	 * only want one error recovery routine running.
> +	 */
> +	raw_spin_lock_irqsave(&confirm_error_lock, flags);
> +	rc = 1;
> +	if (pe->state & EEH_PE_ISOLATED) {
> +		pe->check_count++;
> +		if (pe->check_count % EEH_MAX_FAILS == 0) {
> +			location = of_get_property(dn, "ibm,loc-code", NULL);
> +			printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
> +				"location=%s driver=%s pci addr=%s\n",
> +				pe->check_count, location,
> +				eeh_driver_name(dev), eeh_pci_name(dev));
> +			printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
> +				eeh_driver_name(dev));
> +			dump_stack();
> +		}
> +		goto dn_unlock;
> +	}
> +
> +	/*
> +	 * Now test for an EEH failure.  This is VERY expensive.
> +	 * Note that the eeh_config_addr may be a parent device
> +	 * in the case of a device behind a bridge, or it may be
> +	 * function zero of a multi-function device.
> +	 * In any case they must share a common PHB.
> +	 */
> +	ret = eeh_ops->get_state(pe, NULL);
> +
> +	/* Note that config-io to empty slots may fail;
> +	 * they are empty when they don't have children.
> +	 * We will punt with the following conditions: Failure to get
> +	 * PE's state, EEH not support and Permanently unavailable
> +	 * state, PE is in good state.
> +	 */
> +	if ((ret < 0) ||
> +	    (ret == EEH_STATE_NOT_SUPPORT) ||
> +	    (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
> +	    (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
> +		eeh_stats.false_positives++;
> +		pe->false_positives++;
> +		rc = 0;
> +		goto dn_unlock;
> +	}
> +
> +	eeh_stats.slot_resets++;
> +
> +	/* Avoid repeated reports of this failure, including problems
> +	 * with other functions on this device, and functions under
> +	 * bridges.
> +	 */
> +	eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
> +	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> +
> +	eeh_send_failure_event(pe);
> +
> +	/* Most EEH events are due to device driver bugs.  Having
> +	 * a stack trace will help the device-driver authors figure
> +	 * out what happened.  So print that out.
> +	 */
> +	WARN(1, "EEH: failure detected\n");
> +	return 1;
> +
> +dn_unlock:
> +	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> +	return rc;
> +}
> +
> +EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
> +
> +/**
> + * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
> + * @token: I/O token, should be address in the form 0xA....
> + * @val: value, should be all 1's (XXX why do we need this arg??)
> + *
> + * Check for an EEH failure at the given token address.  Call this
> + * routine if the result of a read was all 0xff's and you want to
> + * find out if this is due to an EEH slot freeze event.  This routine
> + * will query firmware for the EEH status.
> + *
> + * Note this routine is safe to call in an interrupt context.
> + */
> +unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
> +{
> +	unsigned long addr;
> +	struct eeh_dev *edev;
> +
> +	/* Finding the phys addr + pci device; this is pretty quick. */
> +	addr = eeh_token_to_phys((unsigned long __force) token);
> +	edev = eeh_addr_cache_get_dev(addr);
> +	if (!edev) {
> +		eeh_stats.no_device++;
> +		return val;
> +	}
> +
> +	eeh_dev_check_failure(edev);
> +
> +	pci_dev_put(eeh_dev_to_pci_dev(edev));
> +	return val;
> +}
> +
> +EXPORT_SYMBOL(eeh_check_failure);
> +
> +
> +/**
> + * eeh_pci_enable - Enable MMIO or DMA transfers for this slot
> + * @pe: EEH PE
> + *
> + * This routine should be called to reenable frozen MMIO or DMA
> + * so that it would work correctly again. It's useful while doing
> + * recovery or log collection on the indicated device.
> + */
> +int eeh_pci_enable(struct eeh_pe *pe, int function)
> +{
> +	int rc;
> +
> +	rc = eeh_ops->set_option(pe, function);
> +	if (rc)
> +		pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n",
> +			__func__, function, pe->phb->global_number, pe->addr, rc);
> +
> +	rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> +	if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
> +	   (function == EEH_OPT_THAW_MMIO))
> +		return 0;
> +
> +	return rc;
> +}
> +
> +/**
> + * pcibios_set_pcie_slot_reset - Set PCI-E reset state
> + * @dev: pci device struct
> + * @state: reset state to enter
> + *
> + * Return value:
> + * 	0 if success
> + */
> +int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
> +{
> +	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> +	struct eeh_pe *pe = edev->pe;
> +
> +	if (!pe) {
> +		pr_err("%s: No PE found on PCI device %s\n",
> +			__func__, pci_name(dev));
> +		return -EINVAL;
> +	}
> +
> +	switch (state) {
> +	case pcie_deassert_reset:
> +		eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> +		break;
> +	case pcie_hot_reset:
> +		eeh_ops->reset(pe, EEH_RESET_HOT);
> +		break;
> +	case pcie_warm_reset:
> +		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> +		break;
> +	default:
> +		return -EINVAL;
> +	};
> +
> +	return 0;
> +}
> +
> +/**
> + * eeh_set_pe_freset - Check the required reset for the indicated device
> + * @data: EEH device
> + * @flag: return value
> + *
> + * Each device might have its preferred reset type: fundamental or
> + * hot reset. The routine is used to collected the information for
> + * the indicated device and its children so that the bunch of the
> + * devices could be reset properly.
> + */
> +static void *eeh_set_dev_freset(void *data, void *flag)
> +{
> +	struct pci_dev *dev;
> +	unsigned int *freset = (unsigned int *)flag;
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +
> +	dev = eeh_dev_to_pci_dev(edev);
> +	if (dev)
> +		*freset |= dev->needs_freset;
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
> + * @pe: EEH PE
> + *
> + * Assert the PCI #RST line for 1/4 second.
> + */
> +static void eeh_reset_pe_once(struct eeh_pe *pe)
> +{
> +	unsigned int freset = 0;
> +
> +	/* Determine type of EEH reset required for
> +	 * Partitionable Endpoint, a hot-reset (1)
> +	 * or a fundamental reset (3).
> +	 * A fundamental reset required by any device under
> +	 * Partitionable Endpoint trumps hot-reset.
> +  	 */
> +	eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset);
> +
> +	if (freset)
> +		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> +	else
> +		eeh_ops->reset(pe, EEH_RESET_HOT);
> +
> +	/* The PCI bus requires that the reset be held high for at least
> +	 * a 100 milliseconds. We wait a bit longer 'just in case'.
> +	 */
> +#define PCI_BUS_RST_HOLD_TIME_MSEC 250
> +	msleep(PCI_BUS_RST_HOLD_TIME_MSEC);
> +	
> +	/* We might get hit with another EEH freeze as soon as the
> +	 * pci slot reset line is dropped. Make sure we don't miss
> +	 * these, and clear the flag now.
> +	 */
> +	eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
> +
> +	eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> +
> +	/* After a PCI slot has been reset, the PCI Express spec requires
> +	 * a 1.5 second idle time for the bus to stabilize, before starting
> +	 * up traffic.
> +	 */
> +#define PCI_BUS_SETTLE_TIME_MSEC 1800
> +	msleep(PCI_BUS_SETTLE_TIME_MSEC);
> +}
> +
> +/**
> + * eeh_reset_pe - Reset the indicated PE
> + * @pe: EEH PE
> + *
> + * This routine should be called to reset indicated device, including
> + * PE. A PE might include multiple PCI devices and sometimes PCI bridges
> + * might be involved as well.
> + */
> +int eeh_reset_pe(struct eeh_pe *pe)
> +{
> +	int i, rc;
> +
> +	/* Take three shots at resetting the bus */
> +	for (i=0; i<3; i++) {
> +		eeh_reset_pe_once(pe);
> +
> +		rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> +		if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
> +			return 0;
> +
> +		if (rc < 0) {
> +			pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x",
> +				__func__, pe->phb->global_number, pe->addr);
> +			return -1;
> +		}
> +		pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n",
> +			i+1, pe->phb->global_number, pe->addr, rc);
> +	}
> +
> +	return -1;
> +}
> +
> +/**
> + * eeh_save_bars - Save device bars
> + * @edev: PCI device associated EEH device
> + *
> + * Save the values of the device bars. Unlike the restore
> + * routine, this routine is *not* recursive. This is because
> + * PCI devices are added individually; but, for the restore,
> + * an entire slot is reset at a time.
> + */
> +void eeh_save_bars(struct eeh_dev *edev)
> +{
> +	int i;
> +	struct device_node *dn;
> +
> +	if (!edev)
> +		return;
> +	dn = eeh_dev_to_of_node(edev);
> +	
> +	for (i = 0; i < 16; i++)
> +		eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
> +}
> +
> +/**
> + * eeh_ops_register - Register platform dependent EEH operations
> + * @ops: platform dependent EEH operations
> + *
> + * Register the platform dependent EEH operation callback
> + * functions. The platform should call this function before
> + * any other EEH operations.
> + */
> +int __init eeh_ops_register(struct eeh_ops *ops)
> +{
> +	if (!ops->name) {
> +		pr_warning("%s: Invalid EEH ops name for %p\n",
> +			__func__, ops);
> +		return -EINVAL;
> +	}
> +
> +	if (eeh_ops && eeh_ops != ops) {
> +		pr_warning("%s: EEH ops of platform %s already existing (%s)\n",
> +			__func__, eeh_ops->name, ops->name);
> +		return -EEXIST;
> +	}
> +
> +	eeh_ops = ops;
> +
> +	return 0;
> +}
> +
> +/**
> + * eeh_ops_unregister - Unreigster platform dependent EEH operations
> + * @name: name of EEH platform operations
> + *
> + * Unregister the platform dependent EEH operation callback
> + * functions.
> + */
> +int __exit eeh_ops_unregister(const char *name)
> +{
> +	if (!name || !strlen(name)) {
> +		pr_warning("%s: Invalid EEH ops name\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> +	if (eeh_ops && !strcmp(eeh_ops->name, name)) {
> +		eeh_ops = NULL;
> +		return 0;
> +	}
> +
> +	return -EEXIST;
> +}
> +
> +/**
> + * eeh_init - EEH initialization
> + *
> + * Initialize EEH by trying to enable it for all of the adapters in the system.
> + * As a side effect we can determine here if eeh is supported at all.
> + * Note that we leave EEH on so failed config cycles won't cause a machine
> + * check.  If a user turns off EEH for a particular adapter they are really
> + * telling Linux to ignore errors.  Some hardware (e.g. POWER5) won't
> + * grant access to a slot if EEH isn't enabled, and so we always enable
> + * EEH for all slots/all devices.
> + *
> + * The eeh-force-off option disables EEH checking globally, for all slots.
> + * Even if force-off is set, the EEH hardware is still enabled, so that
> + * newer systems can boot.
> + */
> +static int __init eeh_init(void)
> +{
> +	struct pci_controller *hose, *tmp;
> +	struct device_node *phb;
> +	int ret;
> +
> +	/* call platform initialization function */
> +	if (!eeh_ops) {
> +		pr_warning("%s: Platform EEH operation not found\n",
> +			__func__);
> +		return -EEXIST;
> +	} else if ((ret = eeh_ops->init())) {
> +		pr_warning("%s: Failed to call platform init function (%d)\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	raw_spin_lock_init(&confirm_error_lock);
> +
> +	/* Enable EEH for all adapters */
> +	if (eeh_probe_mode_devtree()) {
> +		list_for_each_entry_safe(hose, tmp,
> +			&hose_list, list_node) {
> +			phb = hose->dn;
> +			traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
> +		}
> +	}
> +
> +	if (eeh_subsystem_enabled)
> +		pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n");
> +	else
> +		pr_warning("EEH: No capable adapters found\n");
> +
> +	return ret;
> +}
> +
> +core_initcall_sync(eeh_init);
> +
> +/**
> + * eeh_add_device_early - Enable EEH for the indicated device_node
> + * @dn: device node for which to set up EEH
> + *
> + * This routine must be used to perform EEH initialization for PCI
> + * devices that were added after system boot (e.g. hotplug, dlpar).
> + * This routine must be called before any i/o is performed to the
> + * adapter (inluding any config-space i/o).
> + * Whether this actually enables EEH or not for this device depends
> + * on the CEC architecture, type of the device, on earlier boot
> + * command-line arguments & etc.
> + */
> +static void eeh_add_device_early(struct device_node *dn)
> +{
> +	struct pci_controller *phb;
> +
> +	if (!of_node_to_eeh_dev(dn))
> +		return;
> +	phb = of_node_to_eeh_dev(dn)->phb;
> +
> +	/* USB Bus children of PCI devices will not have BUID's */
> +	if (NULL == phb || 0 == phb->buid)
> +		return;
> +
> +	/* FIXME: hotplug support on POWERNV */
> +	eeh_ops->of_probe(dn, NULL);
> +}
> +
> +/**
> + * eeh_add_device_tree_early - Enable EEH for the indicated device
> + * @dn: device node
> + *
> + * This routine must be used to perform EEH initialization for the
> + * indicated PCI device that was added after system boot (e.g.
> + * hotplug, dlpar).
> + */
> +void eeh_add_device_tree_early(struct device_node *dn)
> +{
> +	struct device_node *sib;
> +
> +	for_each_child_of_node(dn, sib)
> +		eeh_add_device_tree_early(sib);
> +	eeh_add_device_early(dn);
> +}
> +EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
> +
> +/**
> + * eeh_add_device_late - Perform EEH initialization for the indicated pci device
> + * @dev: pci device for which to set up EEH
> + *
> + * This routine must be used to complete EEH initialization for PCI
> + * devices that were added after system boot (e.g. hotplug, dlpar).
> + */
> +static void eeh_add_device_late(struct pci_dev *dev)
> +{
> +	struct device_node *dn;
> +	struct eeh_dev *edev;
> +
> +	if (!dev || !eeh_subsystem_enabled)
> +		return;
> +
> +	pr_debug("EEH: Adding device %s\n", pci_name(dev));
> +
> +	dn = pci_device_to_OF_node(dev);
> +	edev = of_node_to_eeh_dev(dn);
> +	if (edev->pdev == dev) {
> +		pr_debug("EEH: Already referenced !\n");
> +		return;
> +	}
> +	WARN_ON(edev->pdev);
> +
> +	pci_dev_get(dev);
> +	edev->pdev = dev;
> +	dev->dev.archdata.edev = edev;
> +
> +	eeh_addr_cache_insert_dev(dev);
> +}
> +
> +/**
> + * eeh_add_device_tree_late - Perform EEH initialization for the indicated PCI bus
> + * @bus: PCI bus
> + *
> + * This routine must be used to perform EEH initialization for PCI
> + * devices which are attached to the indicated PCI bus. The PCI bus
> + * is added after system boot through hotplug or dlpar.
> + */
> +void eeh_add_device_tree_late(struct pci_bus *bus)
> +{
> +	struct pci_dev *dev;
> +
> +	list_for_each_entry(dev, &bus->devices, bus_list) {
> + 		eeh_add_device_late(dev);
> + 		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> + 			struct pci_bus *subbus = dev->subordinate;
> + 			if (subbus)
> + 				eeh_add_device_tree_late(subbus);
> + 		}
> +	}
> +}
> +EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
> +
> +/**
> + * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
> + * @bus: PCI bus
> + *
> + * This routine must be used to add EEH sysfs files for PCI
> + * devices which are attached to the indicated PCI bus. The PCI bus
> + * is added after system boot through hotplug or dlpar.
> + */
> +void eeh_add_sysfs_files(struct pci_bus *bus)
> +{
> +	struct pci_dev *dev;
> +
> +	list_for_each_entry(dev, &bus->devices, bus_list) {
> +		eeh_sysfs_add_device(dev);
> +		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> +			struct pci_bus *subbus = dev->subordinate;
> +			if (subbus)
> +				eeh_add_sysfs_files(subbus);
> +		}
> +	}
> +}
> +EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
> +
> +/**
> + * eeh_remove_device - Undo EEH setup for the indicated pci device
> + * @dev: pci device to be removed
> + * @purge_pe: remove the PE or not
> + *
> + * This routine should be called when a device is removed from
> + * a running system (e.g. by hotplug or dlpar).  It unregisters
> + * the PCI device from the EEH subsystem.  I/O errors affecting
> + * this device will no longer be detected after this call; thus,
> + * i/o errors affecting this slot may leave this device unusable.
> + */
> +static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
> +{
> +	struct eeh_dev *edev;
> +
> +	if (!dev || !eeh_subsystem_enabled)
> +		return;
> +	edev = pci_dev_to_eeh_dev(dev);
> +
> +	/* Unregister the device with the EEH/PCI address search system */
> +	pr_debug("EEH: Removing device %s\n", pci_name(dev));
> +
> +	if (!edev || !edev->pdev) {
> +		pr_debug("EEH: Not referenced !\n");
> +		return;
> +	}
> +	edev->pdev = NULL;
> +	dev->dev.archdata.edev = NULL;
> +	pci_dev_put(dev);
> +
> +	eeh_rmv_from_parent_pe(edev, purge_pe);
> +	eeh_addr_cache_rmv_dev(dev);
> +	eeh_sysfs_remove_device(dev);
> +}
> +
> +/**
> + * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
> + * @dev: PCI device
> + * @purge_pe: remove the corresponding PE or not
> + *
> + * This routine must be called when a device is removed from the
> + * running system through hotplug or dlpar. The corresponding
> + * PCI address cache will be removed.
> + */
> +void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe)
> +{
> +	struct pci_bus *bus = dev->subordinate;
> +	struct pci_dev *child, *tmp;
> +
> +	eeh_remove_device(dev, purge_pe);
> +
> +	if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> +		list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
> +			 eeh_remove_bus_device(child, purge_pe);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
> +
> +static int proc_eeh_show(struct seq_file *m, void *v)
> +{
> +	if (0 == eeh_subsystem_enabled) {
> +		seq_printf(m, "EEH Subsystem is globally disabled\n");
> +		seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs);
> +	} else {
> +		seq_printf(m, "EEH Subsystem is enabled\n");
> +		seq_printf(m,
> +				"no device=%llu\n"
> +				"no device node=%llu\n"
> +				"no config address=%llu\n"
> +				"check not wanted=%llu\n"
> +				"eeh_total_mmio_ffs=%llu\n"
> +				"eeh_false_positives=%llu\n"
> +				"eeh_slot_resets=%llu\n",
> +				eeh_stats.no_device,
> +				eeh_stats.no_dn,
> +				eeh_stats.no_cfg_addr,
> +				eeh_stats.ignored_check,
> +				eeh_stats.total_mmio_ffs,
> +				eeh_stats.false_positives,
> +				eeh_stats.slot_resets);
> +	}
> +
> +	return 0;
> +}
> +
> +static int proc_eeh_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, proc_eeh_show, NULL);
> +}
> +
> +static const struct file_operations proc_eeh_operations = {
> +	.open      = proc_eeh_open,
> +	.read      = seq_read,
> +	.llseek    = seq_lseek,
> +	.release   = single_release,
> +};
> +
> +static int __init eeh_init_proc(void)
> +{
> +	if (machine_is(pseries))
> +		proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations);
> +	return 0;
> +}
> +__initcall(eeh_init_proc);
> diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c
> new file mode 100644
> index 0000000..5a4c879
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_cache.c
> @@ -0,0 +1,319 @@
> +/*
> + * PCI address cache; allows the lookup of PCI devices based on I/O address
> + *
> + * Copyright IBM Corporation 2004
> + * Copyright Linas Vepstas <linas@austin.ibm.com> 2004
> + *
> + * 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/list.h>
> +#include <linux/pci.h>
> +#include <linux/rbtree.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/atomic.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/ppc-pci.h>
> +
> +
> +/**
> + * The pci address cache subsystem.  This subsystem places
> + * PCI device address resources into a red-black tree, sorted
> + * according to the address range, so that given only an i/o
> + * address, the corresponding PCI device can be **quickly**
> + * found. It is safe to perform an address lookup in an interrupt
> + * context; this ability is an important feature.
> + *
> + * Currently, the only customer of this code is the EEH subsystem;
> + * thus, this code has been somewhat tailored to suit EEH better.
> + * In particular, the cache does *not* hold the addresses of devices
> + * for which EEH is not enabled.
> + *
> + * (Implementation Note: The RB tree seems to be better/faster
> + * than any hash algo I could think of for this problem, even
> + * with the penalty of slow pointer chases for d-cache misses).
> + */
> +struct pci_io_addr_range {
> +	struct rb_node rb_node;
> +	unsigned long addr_lo;
> +	unsigned long addr_hi;
> +	struct eeh_dev *edev;
> +	struct pci_dev *pcidev;
> +	unsigned int flags;
> +};
> +
> +static struct pci_io_addr_cache {
> +	struct rb_root rb_root;
> +	spinlock_t piar_lock;
> +} pci_io_addr_cache_root;
> +
> +static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
> +{
> +	struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node;
> +
> +	while (n) {
> +		struct pci_io_addr_range *piar;
> +		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> +
> +		if (addr < piar->addr_lo) {
> +			n = n->rb_left;
> +		} else {
> +			if (addr > piar->addr_hi) {
> +				n = n->rb_right;
> +			} else {
> +				pci_dev_get(piar->pcidev);
> +				return piar->edev;
> +			}
> +		}
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_addr_cache_get_dev - Get device, given only address
> + * @addr: mmio (PIO) phys address or i/o port number
> + *
> + * Given an mmio phys address, or a port number, find a pci device
> + * that implements this address.  Be sure to pci_dev_put the device
> + * when finished.  I/O port numbers are assumed to be offset
> + * from zero (that is, they do *not* have pci_io_addr added in).
> + * It is safe to call this function within an interrupt.
> + */
> +struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr)
> +{
> +	struct eeh_dev *edev;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> +	edev = __eeh_addr_cache_get_device(addr);
> +	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> +	return edev;
> +}
> +
> +#ifdef DEBUG
> +/*
> + * Handy-dandy debug print routine, does nothing more
> + * than print out the contents of our addr cache.
> + */
> +static void eeh_addr_cache_print(struct pci_io_addr_cache *cache)
> +{
> +	struct rb_node *n;
> +	int cnt = 0;
> +
> +	n = rb_first(&cache->rb_root);
> +	while (n) {
> +		struct pci_io_addr_range *piar;
> +		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> +		pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n",
> +		       (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt,
> +		       piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev));
> +		cnt++;
> +		n = rb_next(n);
> +	}
> +}
> +#endif
> +
> +/* Insert address range into the rb tree. */
> +static struct pci_io_addr_range *
> +eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
> +		      unsigned long ahi, unsigned int flags)
> +{
> +	struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node;
> +	struct rb_node *parent = NULL;
> +	struct pci_io_addr_range *piar;
> +
> +	/* Walk tree, find a place to insert into tree */
> +	while (*p) {
> +		parent = *p;
> +		piar = rb_entry(parent, struct pci_io_addr_range, rb_node);
> +		if (ahi < piar->addr_lo) {
> +			p = &parent->rb_left;
> +		} else if (alo > piar->addr_hi) {
> +			p = &parent->rb_right;
> +		} else {
> +			if (dev != piar->pcidev ||
> +			    alo != piar->addr_lo || ahi != piar->addr_hi) {
> +				pr_warning("PIAR: overlapping address range\n");
> +			}
> +			return piar;
> +		}
> +	}
> +	piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC);
> +	if (!piar)
> +		return NULL;
> +
> +	pci_dev_get(dev);
> +	piar->addr_lo = alo;
> +	piar->addr_hi = ahi;
> +	piar->edev = pci_dev_to_eeh_dev(dev);
> +	piar->pcidev = dev;
> +	piar->flags = flags;
> +
> +#ifdef DEBUG
> +	pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n",
> +	                  alo, ahi, pci_name(dev));
> +#endif
> +
> +	rb_link_node(&piar->rb_node, parent, p);
> +	rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root);
> +
> +	return piar;
> +}
> +
> +static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
> +{
> +	struct device_node *dn;
> +	struct eeh_dev *edev;
> +	int i;
> +
> +	dn = pci_device_to_OF_node(dev);
> +	if (!dn) {
> +		pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev));
> +		return;
> +	}
> +
> +	edev = of_node_to_eeh_dev(dn);
> +	if (!edev) {
> +		pr_warning("PCI: no EEH dev found for dn=%s\n",
> +			dn->full_name);
> +		return;
> +	}
> +
> +	/* Skip any devices for which EEH is not enabled. */
> +	if (!edev->pe) {
> +#ifdef DEBUG
> +		pr_info("PCI: skip building address cache for=%s - %s\n",
> +			pci_name(dev), dn->full_name);
> +#endif
> +		return;
> +	}
> +
> +	/* Walk resources on this device, poke them into the tree */
> +	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
> +		unsigned long start = pci_resource_start(dev,i);
> +		unsigned long end = pci_resource_end(dev,i);
> +		unsigned int flags = pci_resource_flags(dev,i);
> +
> +		/* We are interested only bus addresses, not dma or other stuff */
> +		if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM)))
> +			continue;
> +		if (start == 0 || ~start == 0 || end == 0 || ~end == 0)
> +			 continue;
> +		eeh_addr_cache_insert(dev, start, end, flags);
> +	}
> +}
> +
> +/**
> + * eeh_addr_cache_insert_dev - Add a device to the address cache
> + * @dev: PCI device whose I/O addresses we are interested in.
> + *
> + * In order to support the fast lookup of devices based on addresses,
> + * we maintain a cache of devices that can be quickly searched.
> + * This routine adds a device to that cache.
> + */
> +void eeh_addr_cache_insert_dev(struct pci_dev *dev)
> +{
> +	unsigned long flags;
> +
> +	/* Ignore PCI bridges */
> +	if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
> +		return;
> +
> +	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> +	__eeh_addr_cache_insert_dev(dev);
> +	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> +}
> +
> +static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> +{
> +	struct rb_node *n;
> +
> +restart:
> +	n = rb_first(&pci_io_addr_cache_root.rb_root);
> +	while (n) {
> +		struct pci_io_addr_range *piar;
> +		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> +
> +		if (piar->pcidev == dev) {
> +			rb_erase(n, &pci_io_addr_cache_root.rb_root);
> +			pci_dev_put(piar->pcidev);
> +			kfree(piar);
> +			goto restart;
> +		}
> +		n = rb_next(n);
> +	}
> +}
> +
> +/**
> + * eeh_addr_cache_rmv_dev - remove pci device from addr cache
> + * @dev: device to remove
> + *
> + * Remove a device from the addr-cache tree.
> + * This is potentially expensive, since it will walk
> + * the tree multiple times (once per resource).
> + * But so what; device removal doesn't need to be that fast.
> + */
> +void eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> +	__eeh_addr_cache_rmv_dev(dev);
> +	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> +}
> +
> +/**
> + * eeh_addr_cache_build - Build a cache of I/O addresses
> + *
> + * Build a cache of pci i/o addresses.  This cache will be used to
> + * find the pci device that corresponds to a given address.
> + * This routine scans all pci busses to build the cache.
> + * Must be run late in boot process, after the pci controllers
> + * have been scanned for devices (after all device resources are known).
> + */
> +void __init eeh_addr_cache_build(void)
> +{
> +	struct device_node *dn;
> +	struct eeh_dev *edev;
> +	struct pci_dev *dev = NULL;
> +
> +	spin_lock_init(&pci_io_addr_cache_root.piar_lock);
> +
> +	for_each_pci_dev(dev) {
> +		eeh_addr_cache_insert_dev(dev);
> +
> +		dn = pci_device_to_OF_node(dev);
> +		if (!dn)
> +			continue;
> +
> +		edev = of_node_to_eeh_dev(dn);
> +		if (!edev)
> +			continue;
> +
> +		pci_dev_get(dev);  /* matching put is in eeh_remove_device() */
> +		dev->dev.archdata.edev = edev;
> +		edev->pdev = dev;
> +
> +		eeh_sysfs_add_device(dev);
> +	}
> +
> +#ifdef DEBUG
> +	/* Verify tree built up above, echo back the list of addrs. */
> +	eeh_addr_cache_print(&pci_io_addr_cache_root);
> +#endif
> +}
> +
> diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c
> new file mode 100644
> index 0000000..1efa28f
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_dev.c
> @@ -0,0 +1,112 @@
> +/*
> + * The file intends to implement dynamic creation of EEH device, which will
> + * be bound with OF node and PCI device simutaneously. The EEH devices would
> + * be foundamental information for EEH core components to work proerly. Besides,
> + * We have to support multiple situations where dynamic creation of EEH device
> + * is required:
> + *
> + * 1) Before PCI emunation starts, we need create EEH devices according to the
> + *    PCI sensitive OF nodes.
> + * 2) When PCI emunation is done, we need do the binding between PCI device and
> + *    the associated EEH device.
> + * 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device
> + *    will be created while PCI sensitive OF node is detected from DR.
> + * 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If
> + *    PHB is newly inserted, we also need create EEH devices accordingly.
> + *
> + * 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>
> +
> +/**
> + * eeh_dev_init - Create EEH device according to OF node
> + * @dn: device node
> + * @data: PHB
> + *
> + * It will create EEH device according to the given OF node. The function
> + * might be called by PCI emunation, DR, PHB hotplug.
> + */
> +void *eeh_dev_init(struct device_node *dn, void *data)
> +{
> +	struct pci_controller *phb = data;
> +	struct eeh_dev *edev;
> +
> +	/* Allocate EEH device */
> +	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
> +	if (!edev) {
> +		pr_warning("%s: out of memory\n", __func__);
> +		return NULL;
> +	}
> +
> +	/* Associate EEH device with OF node */
> +	PCI_DN(dn)->edev = edev;
> +	edev->dn  = dn;
> +	edev->phb = phb;
> +	INIT_LIST_HEAD(&edev->list);
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB
> + * @phb: PHB
> + *
> + * Scan the PHB OF node and its child association, then create the
> + * EEH devices accordingly
> + */
> +void 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);
> +
> +	/* EEH devices for children OF nodes */
> +	traverse_pci_devices(dn, eeh_dev_init, phb);
> +}
> +
> +/**
> + * eeh_dev_phb_init - Create EEH devices for devices included in existing PHBs
> + *
> + * Scan all the existing PHBs and create EEH devices for their OF
> + * nodes and their children OF nodes
> + */
> +static int __init eeh_dev_phb_init(void)
> +{
> +	struct pci_controller *phb, *tmp;
> +
> +	list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
> +		eeh_dev_phb_init_dynamic(phb);
> +
> +	pr_info("EEH: devices created\n");
> +
> +	return 0;
> +}
> +
> +core_initcall(eeh_dev_phb_init);
> diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
> new file mode 100644
> index 0000000..a3fefb6
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_driver.c
> @@ -0,0 +1,552 @@
> +/*
> + * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
> + * Copyright IBM Corp. 2004 2005
> + * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
> + *
> + * All rights reserved.
> + *
> + * 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, GOOD TITLE or
> + * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> + */
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <asm/eeh.h>
> +#include <asm/eeh_event.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/prom.h>
> +#include <asm/rtas.h>
> +
> +/**
> + * eeh_pcid_name - Retrieve name of PCI device driver
> + * @pdev: PCI device
> + *
> + * This routine is used to retrieve the name of PCI device driver
> + * if that's valid.
> + */
> +static inline const char *eeh_pcid_name(struct pci_dev *pdev)
> +{
> +	if (pdev && pdev->dev.driver)
> +		return pdev->dev.driver->name;
> +	return "";
> +}
> +
> +/**
> + * eeh_pcid_get - Get the PCI device driver
> + * @pdev: PCI device
> + *
> + * The function is used to retrieve the PCI device driver for
> + * the indicated PCI device. Besides, we will increase the reference
> + * of the PCI device driver to prevent that being unloaded on
> + * the fly. Otherwise, kernel crash would be seen.
> + */
> +static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
> +{
> +	if (!pdev || !pdev->driver)
> +		return NULL;
> +
> +	if (!try_module_get(pdev->driver->driver.owner))
> +		return NULL;
> +
> +	return pdev->driver;
> +}
> +
> +/**
> + * eeh_pcid_put - Dereference on the PCI device driver
> + * @pdev: PCI device
> + *
> + * The function is called to do dereference on the PCI device
> + * driver of the indicated PCI device.
> + */
> +static inline void eeh_pcid_put(struct pci_dev *pdev)
> +{
> +	if (!pdev || !pdev->driver)
> +		return;
> +
> +	module_put(pdev->driver->driver.owner);
> +}
> +
> +#if 0
> +static void print_device_node_tree(struct pci_dn *pdn, int dent)
> +{
> +	int i;
> +	struct device_node *pc;
> +
> +	if (!pdn)
> +		return;
> +	for (i = 0; i < dent; i++)
> +		printk(" ");
> +	printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
> +		pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
> +		pdn->eeh_pe_config_addr, pdn->node->full_name);
> +	dent += 3;
> +	pc = pdn->node->child;
> +	while (pc) {
> +		print_device_node_tree(PCI_DN(pc), dent);
> +		pc = pc->sibling;
> +	}
> +}
> +#endif
> +
> +/**
> + * eeh_disable_irq - Disable interrupt for the recovering device
> + * @dev: PCI device
> + *
> + * This routine must be called when reporting temporary or permanent
> + * error to the particular PCI device to disable interrupt of that
> + * device. If the device has enabled MSI or MSI-X interrupt, we needn't
> + * do real work because EEH should freeze DMA transfers for those PCI
> + * devices encountering EEH errors, which includes MSI or MSI-X.
> + */
> +static void eeh_disable_irq(struct pci_dev *dev)
> +{
> +	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> +
> +	/* Don't disable MSI and MSI-X interrupts. They are
> +	 * effectively disabled by the DMA Stopped state
> +	 * when an EEH error occurs.
> +	 */
> +	if (dev->msi_enabled || dev->msix_enabled)
> +		return;
> +
> +	if (!irq_has_action(dev->irq))
> +		return;
> +
> +	edev->mode |= EEH_DEV_IRQ_DISABLED;
> +	disable_irq_nosync(dev->irq);
> +}
> +
> +/**
> + * eeh_enable_irq - Enable interrupt for the recovering device
> + * @dev: PCI device
> + *
> + * This routine must be called to enable interrupt while failed
> + * device could be resumed.
> + */
> +static void eeh_enable_irq(struct pci_dev *dev)
> +{
> +	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> +
> +	if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
> +		edev->mode &= ~EEH_DEV_IRQ_DISABLED;
> +		enable_irq(dev->irq);
> +	}
> +}
> +
> +/**
> + * eeh_report_error - Report pci error to each device driver
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * Report an EEH error to each device driver, collect up and
> + * merge the device driver responses. Cumulative response
> + * passed back in "userdata".
> + */
> +static void *eeh_report_error(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	enum pci_ers_result rc, *res = userdata;
> +	struct pci_driver *driver;
> +
> +	/* We might not have the associated PCI device,
> +	 * then we should continue for next one.
> +	 */
> +	if (!dev) return NULL;
> +	dev->error_state = pci_channel_io_frozen;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	eeh_disable_irq(dev);
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->error_detected) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
> +
> +	/* A driver that needs a reset trumps all others */
> +	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> +	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * Tells each device driver that IO ports, MMIO and config space I/O
> + * are now enabled. Collects up and merges the device driver responses.
> + * Cumulative response passed back in "userdata".
> + */
> +static void *eeh_report_mmio_enabled(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	enum pci_ers_result rc, *res = userdata;
> +	struct pci_driver *driver;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->mmio_enabled) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	rc = driver->err_handler->mmio_enabled(dev);
> +
> +	/* A driver that needs a reset trumps all others */
> +	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> +	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_report_reset - Tell device that slot has been reset
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * This routine must be called while EEH tries to reset particular
> + * PCI device so that the associated PCI device driver could take
> + * some actions, usually to save data the driver needs so that the
> + * driver can work again while the device is recovered.
> + */
> +static void *eeh_report_reset(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	enum pci_ers_result rc, *res = userdata;
> +	struct pci_driver *driver;
> +
> +	if (!dev) return NULL;
> +	dev->error_state = pci_channel_io_normal;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	eeh_enable_irq(dev);
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->slot_reset) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	rc = driver->err_handler->slot_reset(dev);
> +	if ((*res == PCI_ERS_RESULT_NONE) ||
> +	    (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
> +	if (*res == PCI_ERS_RESULT_DISCONNECT &&
> +	     rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_report_resume - Tell device to resume normal operations
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * This routine must be called to notify the device driver that it
> + * could resume so that the device driver can do some initialization
> + * to make the recovered device work again.
> + */
> +static void *eeh_report_resume(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	struct pci_driver *driver;
> +
> +	if (!dev) return NULL;
> +	dev->error_state = pci_channel_io_normal;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	eeh_enable_irq(dev);
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->resume) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	driver->err_handler->resume(dev);
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_report_failure - Tell device driver that device is dead.
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * This informs the device driver that the device is permanently
> + * dead, and that no further recovery attempts will be made on it.
> + */
> +static void *eeh_report_failure(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	struct pci_driver *driver;
> +
> +	if (!dev) return NULL;
> +	dev->error_state = pci_channel_io_perm_failure;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	eeh_disable_irq(dev);
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->error_detected) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_reset_device - Perform actual reset of a pci slot
> + * @pe: EEH PE
> + * @bus: PCI bus corresponding to the isolcated slot
> + *
> + * This routine must be called to do reset on the indicated PE.
> + * During the reset, udev might be invoked because those affected
> + * PCI devices will be removed and then added.
> + */
> +static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
> +{
> +	int cnt, rc;
> +
> +	/* pcibios will clear the counter; save the value */
> +	cnt = pe->freeze_count;
> +
> +	/*
> +	 * We don't remove the corresponding PE instances because
> +	 * we need the information afterwords. The attached EEH
> +	 * devices are expected to be attached soon when calling
> +	 * into pcibios_add_pci_devices().
> +	 */
> +	if (bus)
> +		__pcibios_remove_pci_devices(bus, 0);
> +
> +	/* Reset the pci controller. (Asserts RST#; resets config space).
> +	 * Reconfigure bridges and devices. Don't try to bring the system
> +	 * up if the reset failed for some reason.
> +	 */
> +	rc = eeh_reset_pe(pe);
> +	if (rc)
> +		return rc;
> +
> +	/* Restore PE */
> +	eeh_ops->configure_bridge(pe);
> +	eeh_pe_restore_bars(pe);
> +
> +	/* Give the system 5 seconds to finish running the user-space
> +	 * hotplug shutdown scripts, e.g. ifdown for ethernet.  Yes,
> +	 * this is a hack, but if we don't do this, and try to bring
> +	 * the device up before the scripts have taken it down,
> +	 * potentially weird things happen.
> +	 */
> +	if (bus) {
> +		ssleep(5);
> +		pcibios_add_pci_devices(bus);
> +	}
> +	pe->freeze_count = cnt;
> +
> +	return 0;
> +}
> +
> +/* The longest amount of time to wait for a pci device
> + * to come back on line, in seconds.
> + */
> +#define MAX_WAIT_FOR_RECOVERY 150
> +
> +/**
> + * eeh_handle_event - Reset a PCI device after hard lockup.
> + * @pe: EEH PE
> + *
> + * While PHB detects address or data parity errors on particular PCI
> + * slot, the associated PE will be frozen. Besides, DMA's occurring
> + * to wild addresses (which usually happen due to bugs in device
> + * drivers or in PCI adapter firmware) can cause EEH error. #SERR,
> + * #PERR or other misc PCI-related errors also can trigger EEH errors.
> + *
> + * Recovery process consists of unplugging the device driver (which
> + * generated hotplug events to userspace), then issuing a PCI #RST to
> + * the device, then reconfiguring the PCI config space for all bridges
> + * & devices under this slot, and then finally restarting the device
> + * drivers (which cause a second set of hotplug events to go out to
> + * userspace).
> + */
> +void eeh_handle_event(struct eeh_pe *pe)
> +{
> +	struct pci_bus *frozen_bus;
> +	int rc = 0;
> +	enum pci_ers_result result = PCI_ERS_RESULT_NONE;
> +
> +	frozen_bus = eeh_pe_bus_get(pe);
> +	if (!frozen_bus) {
> +		pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
> +			__func__, pe->phb->global_number, pe->addr);
> +		return;
> +	}
> +
> +	pe->freeze_count++;
> +	if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
> +		goto excess_failures;
> +	pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
> +		pe->freeze_count);
> +
> +	/* Walk the various device drivers attached to this slot through
> +	 * a reset sequence, giving each an opportunity to do what it needs
> +	 * to accomplish the reset.  Each child gets a report of the
> +	 * status ... if any child can't handle the reset, then the entire
> +	 * slot is dlpar removed and added.
> +	 */
> +	eeh_pe_dev_traverse(pe, eeh_report_error, &result);
> +
> +	/* Get the current PCI slot state. This can take a long time,
> +	 * sometimes over 3 seconds for certain systems.
> +	 */
> +	rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
> +	if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
> +		printk(KERN_WARNING "EEH: Permanent failure\n");
> +		goto hard_fail;
> +	}
> +
> +	/* Since rtas may enable MMIO when posting the error log,
> +	 * don't post the error log until after all dev drivers
> +	 * have been informed.
> +	 */
> +	eeh_slot_error_detail(pe, EEH_LOG_TEMP);
> +
> +	/* If all device drivers were EEH-unaware, then shut
> +	 * down all of the device drivers, and hope they
> +	 * go down willingly, without panicing the system.
> +	 */
> +	if (result == PCI_ERS_RESULT_NONE) {
> +		rc = eeh_reset_device(pe, frozen_bus);
> +		if (rc) {
> +			printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
> +			goto hard_fail;
> +		}
> +	}
> +
> +	/* If all devices reported they can proceed, then re-enable MMIO */
> +	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> +		rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> +
> +		if (rc < 0)
> +			goto hard_fail;
> +		if (rc) {
> +			result = PCI_ERS_RESULT_NEED_RESET;
> +		} else {
> +			result = PCI_ERS_RESULT_NONE;
> +			eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
> +		}
> +	}
> +
> +	/* If all devices reported they can proceed, then re-enable DMA */
> +	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> +		rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
> +
> +		if (rc < 0)
> +			goto hard_fail;
> +		if (rc)
> +			result = PCI_ERS_RESULT_NEED_RESET;
> +		else
> +			result = PCI_ERS_RESULT_RECOVERED;
> +	}
> +
> +	/* If any device has a hard failure, then shut off everything. */
> +	if (result == PCI_ERS_RESULT_DISCONNECT) {
> +		printk(KERN_WARNING "EEH: Device driver gave up\n");
> +		goto hard_fail;
> +	}
> +
> +	/* If any device called out for a reset, then reset the slot */
> +	if (result == PCI_ERS_RESULT_NEED_RESET) {
> +		rc = eeh_reset_device(pe, NULL);
> +		if (rc) {
> +			printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
> +			goto hard_fail;
> +		}
> +		result = PCI_ERS_RESULT_NONE;
> +		eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
> +	}
> +
> +	/* All devices should claim they have recovered by now. */
> +	if ((result != PCI_ERS_RESULT_RECOVERED) &&
> +	    (result != PCI_ERS_RESULT_NONE)) {
> +		printk(KERN_WARNING "EEH: Not recovered\n");
> +		goto hard_fail;
> +	}
> +
> +	/* Tell all device drivers that they can resume operations */
> +	eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
> +
> +	return;
> +	
> +excess_failures:
> +	/*
> +	 * About 90% of all real-life EEH failures in the field
> +	 * are due to poorly seated PCI cards. Only 10% or so are
> +	 * due to actual, failed cards.
> +	 */
> +	pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
> +	       "last hour and has been permanently disabled.\n"
> +	       "Please try reseating or replacing it.\n",
> +		pe->phb->global_number, pe->addr,
> +		pe->freeze_count);
> +	goto perm_error;
> +
> +hard_fail:
> +	pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
> +	       "Please try reseating or replacing it\n",
> +		pe->phb->global_number, pe->addr);
> +
> +perm_error:
> +	eeh_slot_error_detail(pe, EEH_LOG_PERM);
> +
> +	/* Notify all devices that they're about to go down. */
> +	eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
> +
> +	/* Shut down the device drivers for good. */
> +	if (frozen_bus)
> +		pcibios_remove_pci_devices(frozen_bus);
> +}
> +
> diff --git a/arch/powerpc/kernel/eeh_event.c b/arch/powerpc/kernel/eeh_event.c
> new file mode 100644
> index 0000000..185bedd
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_event.c
> @@ -0,0 +1,142 @@
> +/*
> + * 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
> + *
> + * Copyright (c) 2005 Linas Vepstas <linas@linas.org>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/sched.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include <linux/kthread.h>
> +#include <asm/eeh_event.h>
> +#include <asm/ppc-pci.h>
> +
> +/** Overview:
> + *  EEH error states may be detected within exception handlers;
> + *  however, the recovery processing needs to occur asynchronously
> + *  in a normal kernel context and not an interrupt context.
> + *  This pair of routines creates an event and queues it onto a
> + *  work-queue, where a worker thread can drive recovery.
> + */
> +
> +/* EEH event workqueue setup. */
> +static DEFINE_SPINLOCK(eeh_eventlist_lock);
> +LIST_HEAD(eeh_eventlist);
> +static void eeh_thread_launcher(struct work_struct *);
> +DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
> +
> +/* Serialize reset sequences for a given pci device */
> +DEFINE_MUTEX(eeh_event_mutex);
> +
> +/**
> + * eeh_event_handler - Dispatch EEH events.
> + * @dummy - unused
> + *
> + * The detection of a frozen slot can occur inside an interrupt,
> + * where it can be hard to do anything about it.  The goal of this
> + * routine is to pull these detection events out of the context
> + * of the interrupt handler, and re-dispatch them for processing
> + * at a later time in a normal context.
> + */
> +static int eeh_event_handler(void * dummy)
> +{
> +	unsigned long flags;
> +	struct eeh_event *event;
> +	struct eeh_pe *pe;
> +
> +	spin_lock_irqsave(&eeh_eventlist_lock, flags);
> +	event = NULL;
> +
> +	/* Unqueue the event, get ready to process. */
> +	if (!list_empty(&eeh_eventlist)) {
> +		event = list_entry(eeh_eventlist.next, struct eeh_event, list);
> +		list_del(&event->list);
> +	}
> +	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> +
> +	if (event == NULL)
> +		return 0;
> +
> +	/* Serialize processing of EEH events */
> +	mutex_lock(&eeh_event_mutex);
> +	pe = event->pe;
> +	eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
> +	pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n",
> +		pe->phb->global_number, pe->addr);
> +
> +	set_current_state(TASK_INTERRUPTIBLE);	/* Don't add to load average */
> +	eeh_handle_event(pe);
> +	eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
> +
> +	kfree(event);
> +	mutex_unlock(&eeh_event_mutex);
> +
> +	/* If there are no new errors after an hour, clear the counter. */
> +	if (pe && pe->freeze_count > 0) {
> +		msleep_interruptible(3600*1000);
> +		if (pe->freeze_count > 0)
> +			pe->freeze_count--;
> +
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * eeh_thread_launcher - Start kernel thread to handle EEH events
> + * @dummy - unused
> + *
> + * This routine is called to start the kernel thread for processing
> + * EEH event.
> + */
> +static void eeh_thread_launcher(struct work_struct *dummy)
> +{
> +	if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd")))
> +		printk(KERN_ERR "Failed to start EEH daemon\n");
> +}
> +
> +/**
> + * eeh_send_failure_event - Generate a PCI error event
> + * @pe: EEH PE
> + *
> + * This routine can be called within an interrupt context;
> + * the actual event will be delivered in a normal context
> + * (from a workqueue).
> + */
> +int eeh_send_failure_event(struct eeh_pe *pe)
> +{
> +	unsigned long flags;
> +	struct eeh_event *event;
> +
> +	event = kzalloc(sizeof(*event), GFP_ATOMIC);
> +	if (!event) {
> +		pr_err("EEH: out of memory, event not handled\n");
> +		return -ENOMEM;
> +	}
> +	event->pe = pe;
> +
> +	/* We may or may not be called in an interrupt context */
> +	spin_lock_irqsave(&eeh_eventlist_lock, flags);
> +	list_add(&event->list, &eeh_eventlist);
> +	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> +
> +	schedule_work(&eeh_event_wq);
> +
> +	return 0;
> +}
> diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
> new file mode 100644
> index 0000000..9d4a9e8
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_pe.c
> @@ -0,0 +1,653 @@
> +/*
> + * 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 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;
> +
> +	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)
> +			return pe;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_next - Retrieve the next PE in the tree
> + * @pe: current PE
> + * @root: root PE
> + *
> + * The function is used to retrieve the next PE in the
> + * hierarchy PE tree.
> + */
> +static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
> +				  struct eeh_pe *root)
> +{
> +	struct list_head *next = pe->child_list.next;
> +
> +	if (next == &pe->child_list) {
> +		while (1) {
> +			if (pe == root)
> +				return NULL;
> +			next = pe->child.next;
> +			if (next != &pe->parent->child_list)
> +				break;
> +			pe = pe->parent;
> +		}
> +	}
> +
> +	return list_entry(next, struct eeh_pe, child);
> +}
> +
> +/**
> + * eeh_pe_traverse - Traverse PEs in the specified PHB
> + * @root: root PE
> + * @fn: callback
> + * @flag: extra parameter to callback
> + *
> + * The function is used to traverse the specified PE and its
> + * child PEs. The traversing is to be terminated once the
> + * callback returns something other than NULL, or no more PEs
> + * to be traversed.
> + */
> +static void *eeh_pe_traverse(struct eeh_pe *root,
> +			eeh_traverse_func fn, void *flag)
> +{
> +	struct eeh_pe *pe;
> +	void *ret;
> +
> +	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> +		ret = fn(pe, flag);
> +		if (ret) return ret;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_dev_traverse - Traverse the devices from the PE
> + * @root: EEH PE
> + * @fn: function callback
> + * @flag: extra parameter to callback
> + *
> + * The function is used to traverse the devices of the specified
> + * PE and its child PEs.
> + */
> +void *eeh_pe_dev_traverse(struct eeh_pe *root,
> +		eeh_traverse_func fn, void *flag)
> +{
> +	struct eeh_pe *pe;
> +	struct eeh_dev *edev;
> +	void *ret;
> +
> +	if (!root) {
> +		pr_warning("%s: Invalid PE %p\n", __func__, root);
> +		return NULL;
> +	}
> +
> +	eeh_lock();
> +
> +	/* Traverse root PE */
> +	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> +		eeh_pe_for_each_dev(pe, edev) {
> +			ret = fn(edev, flag);
> +			if (ret) {
> +				eeh_unlock();
> +				return ret;
> +			}
> +		}
> +	}
> +
> +	eeh_unlock();
> +
> +	return NULL;
> +}
> +
> +/**
> + * __eeh_pe_get - Check the PE address
> + * @data: EEH PE
> + * @flag: EEH device
> + *
> + * For one particular PE, it can be identified by PE address
> + * or tranditional BDF address. BDF address is composed of
> + * Bus/Device/Function number. The extra data referred by flag
> + * indicates which type of address should be used.
> + */
> +static void *__eeh_pe_get(void *data, void *flag)
> +{
> +	struct eeh_pe *pe = (struct eeh_pe *)data;
> +	struct eeh_dev *edev = (struct eeh_dev *)flag;
> +
> +	/* Unexpected PHB PE */
> +	if (pe->type & EEH_PE_PHB)
> +		return NULL;
> +
> +	/* We prefer PE address */
> +	if (edev->pe_config_addr &&
> +	   (edev->pe_config_addr == pe->addr))
> +		return pe;
> +
> +	/* Try BDF address */
> +	if (edev->pe_config_addr &&
> +	   (edev->config_addr == pe->config_addr))
> +		return pe;
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_get - Search PE based on the given address
> + * @edev: EEH device
> + *
> + * Search the corresponding PE based on the specified address which
> + * is included in the eeh device. The function is used to check if
> + * the associated PE has been created against the PE address. It's
> + * notable that the PE address has 2 format: traditional PE address
> + * which is composed of PCI bus/device/function number, or unified
> + * PE address.
> + */
> +static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
> +{
> +	struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
> +	struct eeh_pe *pe;
> +
> +	pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
> +
> +	return pe;
> +}
> +
> +/**
> + * eeh_pe_get_parent - Retrieve the parent PE
> + * @edev: EEH device
> + *
> + * The whole PEs existing in the system are organized as hierarchy
> + * tree. The function is used to retrieve the parent PE according
> + * to the parent EEH device.
> + */
> +static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
> +{
> +	struct device_node *dn;
> +	struct eeh_dev *parent;
> +
> +	/*
> +	 * It might have the case for the indirect parent
> +	 * EEH device already having associated PE, but
> +	 * the direct parent EEH device doesn't have yet.
> +	 */
> +	dn = edev->dn->parent;
> +	while (dn) {
> +		/* We're poking out of PCI territory */
> +		if (!PCI_DN(dn)) return NULL;
> +
> +		parent = of_node_to_eeh_dev(dn);
> +		/* We're poking out of PCI territory */
> +		if (!parent) return NULL;
> +
> +		if (parent->pe)
> +			return parent->pe;
> +
> +		dn = dn->parent;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_add_to_parent_pe - Add EEH device to parent PE
> + * @edev: EEH device
> + *
> + * Add EEH device to the parent PE. If the parent PE already
> + * exists, the PE type will be changed to EEH_PE_BUS. Otherwise,
> + * we have to create new PE to hold the EEH device and the new
> + * PE will be linked to its parent PE as well.
> + */
> +int eeh_add_to_parent_pe(struct eeh_dev *edev)
> +{
> +	struct eeh_pe *pe, *parent;
> +
> +	eeh_lock();
> +
> +	/*
> +	 * Search the PE has been existing or not according
> +	 * to the PE address. If that has been existing, the
> +	 * PE should be composed of PCI bus and its subordinate
> +	 * components.
> +	 */
> +	pe = eeh_pe_get(edev);
> +	if (pe && !(pe->type & EEH_PE_INVALID)) {
> +		if (!edev->pe_config_addr) {
> +			eeh_unlock();
> +			pr_err("%s: PE with addr 0x%x already exists\n",
> +				__func__, edev->config_addr);
> +			return -EEXIST;
> +		}
> +
> +		/* Mark the PE as type of PCI bus */
> +		pe->type = EEH_PE_BUS;
> +		edev->pe = pe;
> +
> +		/* Put the edev to PE */
> +		list_add_tail(&edev->list, &pe->edevs);
> +		eeh_unlock();
> +		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;
> +		}
> +		eeh_unlock();
> +		pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> +			edev->dn->full_name, pe->addr, pe->parent->addr);
> +
> +		return 0;
> +	}
> +
> +	/* Create a new EEH PE */
> +	pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
> +	if (!pe) {
> +		eeh_unlock();
> +		pr_err("%s: out of memory!\n", __func__);
> +		return -ENOMEM;
> +	}
> +	pe->addr	= edev->pe_config_addr;
> +	pe->config_addr	= edev->config_addr;
> +
> +	/*
> +	 * Put the new EEH PE into hierarchy tree. If the parent
> +	 * can't be found, the newly created PE will be attached
> +	 * to PHB directly. Otherwise, we have to associate the
> +	 * PE with its parent.
> +	 */
> +	parent = eeh_pe_get_parent(edev);
> +	if (!parent) {
> +		parent = eeh_phb_pe_get(edev->phb);
> +		if (!parent) {
> +			eeh_unlock();
> +			pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
> +				__func__, edev->phb->global_number);
> +			edev->pe = NULL;
> +			kfree(pe);
> +			return -EEXIST;
> +		}
> +	}
> +	pe->parent = parent;
> +
> +	/*
> +	 * Put the newly created PE into the child list and
> +	 * link the EEH device accordingly.
> +	 */
> +	list_add_tail(&pe->child, &parent->child_list);
> +	list_add_tail(&edev->list, &pe->edevs);
> +	edev->pe = pe;
> +	eeh_unlock();
> +	pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> +		edev->dn->full_name, pe->addr, pe->parent->addr);
> +
> +	return 0;
> +}
> +
> +/**
> + * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
> + * @edev: EEH device
> + * @purge_pe: remove PE or not
> + *
> + * 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, int purge_pe)
> +{
> +	struct eeh_pe *pe, *parent, *child;
> +	int cnt;
> +
> +	if (!edev->pe) {
> +		pr_warning("%s: No PE found for EEH device %s\n",
> +			__func__, edev->dn->full_name);
> +		return -EEXIST;
> +	}
> +
> +	eeh_lock();
> +
> +	/* 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 (purge_pe) {
> +			if (list_empty(&pe->edevs) &&
> +			    list_empty(&pe->child_list)) {
> +				list_del(&pe->child);
> +				kfree(pe);
> +			} else {
> +				break;
> +			}
> +		} else {
> +			if (list_empty(&pe->edevs)) {
> +				cnt = 0;
> +				list_for_each_entry(child, &pe->child_list, child) {
> +					if (!(child->type & EEH_PE_INVALID)) {
> +						cnt++;
> +						break;
> +					}
> +				}
> +
> +				if (!cnt)
> +					pe->type |= EEH_PE_INVALID;
> +				else
> +					break;
> +			}
> +		}
> +
> +		pe = parent;
> +	}
> +
> +	eeh_unlock();
> +
> +	return 0;
> +}
> +
> +/**
> + * __eeh_pe_state_mark - Mark the state for the PE
> + * @data: EEH PE
> + * @flag: state
> + *
> + * The function is used to mark the indicated state for the given
> + * PE. Also, the associated PCI devices will be put into IO frozen
> + * state as well.
> + */
> +static void *__eeh_pe_state_mark(void *data, void *flag)
> +{
> +	struct eeh_pe *pe = (struct eeh_pe *)data;
> +	int state = *((int *)flag);
> +	struct eeh_dev *tmp;
> +	struct pci_dev *pdev;
> +
> +	/*
> +	 * Mark the PE with the indicated state. Also,
> +	 * the associated PCI device will be put into
> +	 * I/O frozen state to avoid I/O accesses from
> +	 * the PCI device driver.
> +	 */
> +	pe->state |= state;
> +	eeh_pe_for_each_dev(pe, tmp) {
> +		pdev = eeh_dev_to_pci_dev(tmp);
> +		if (pdev)
> +			pdev->error_state = pci_channel_io_frozen;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_state_mark - Mark specified state for PE and its associated device
> + * @pe: EEH PE
> + *
> + * EEH error affects the current PE and its child PEs. The function
> + * is used to mark appropriate state for the affected PEs and the
> + * associated devices.
> + */
> +void eeh_pe_state_mark(struct eeh_pe *pe, int state)
> +{
> +	eeh_lock();
> +	eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
> +	eeh_unlock();
> +}
> +
> +/**
> + * __eeh_pe_state_clear - Clear state for the PE
> + * @data: EEH PE
> + * @flag: state
> + *
> + * The function is used to clear the indicated state from the
> + * given PE. Besides, we also clear the check count of the PE
> + * as well.
> + */
> +static void *__eeh_pe_state_clear(void *data, void *flag)
> +{
> +	struct eeh_pe *pe = (struct eeh_pe *)data;
> +	int state = *((int *)flag);
> +
> +	pe->state &= ~state;
> +	pe->check_count = 0;
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_state_clear - Clear state for the PE and its children
> + * @pe: PE
> + * @state: state to be cleared
> + *
> + * When the PE and its children has been recovered from error,
> + * we need clear the error state for that. The function is used
> + * for the purpose.
> + */
> +void eeh_pe_state_clear(struct eeh_pe *pe, int state)
> +{
> +	eeh_lock();
> +	eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
> +	eeh_unlock();
> +}
> +
> +/**
> + * eeh_restore_one_device_bars - Restore the Base Address Registers for one device
> + * @data: EEH device
> + * @flag: Unused
> + *
> + * Loads the PCI configuration space base address registers,
> + * the expansion ROM base address, the latency timer, and etc.
> + * from the saved values in the device node.
> + */
> +static void *eeh_restore_one_device_bars(void *data, void *flag)
> +{
> +	int i;
> +	u32 cmd;
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct device_node *dn = eeh_dev_to_of_node(edev);
> +
> +	for (i = 4; i < 10; i++)
> +		eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
> +	/* 12 == Expansion ROM Address */
> +	eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
> +
> +#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
> +#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
> +
> +	eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
> +		SAVED_BYTE(PCI_CACHE_LINE_SIZE));
> +	eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
> +		SAVED_BYTE(PCI_LATENCY_TIMER));
> +
> +	/* max latency, min grant, interrupt pin and line */
> +	eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
> +
> +	/*
> +	 * Restore PERR & SERR bits, some devices require it,
> +	 * don't touch the other command bits
> +	 */
> +	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
> +	if (edev->config_space[1] & PCI_COMMAND_PARITY)
> +		cmd |= PCI_COMMAND_PARITY;
> +	else
> +		cmd &= ~PCI_COMMAND_PARITY;
> +	if (edev->config_space[1] & PCI_COMMAND_SERR)
> +		cmd |= PCI_COMMAND_SERR;
> +	else
> +		cmd &= ~PCI_COMMAND_SERR;
> +	eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_restore_bars - Restore the PCI config space info
> + * @pe: EEH PE
> + *
> + * This routine performs a recursive walk to the children
> + * of this device as well.
> + */
> +void eeh_pe_restore_bars(struct eeh_pe *pe)
> +{
> +	/*
> +	 * We needn't take the EEH lock since eeh_pe_dev_traverse()
> +	 * will take that.
> +	 */
> +	eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
> +}
> +
> +/**
> + * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
> + * @pe: EEH PE
> + *
> + * Retrieve the PCI bus according to the given PE. Basically,
> + * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the
> + * primary PCI bus will be retrieved. The parent bus will be
> + * returned for BUS PE. However, we don't have associated PCI
> + * bus for DEVICE PE.
> + */
> +struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
> +{
> +	struct pci_bus *bus = NULL;
> +	struct eeh_dev *edev;
> +	struct pci_dev *pdev;
> +
> +	eeh_lock();
> +
> +	if (pe->type & EEH_PE_PHB) {
> +		bus = pe->phb->bus;
> +	} else if (pe->type & EEH_PE_BUS ||
> +		   pe->type & EEH_PE_DEVICE) {
> +		edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
> +		pdev = eeh_dev_to_pci_dev(edev);
> +		if (pdev)
> +			bus = pdev->bus;
> +	}
> +
> +	eeh_unlock();
> +
> +	return bus;
> +}
> diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c
> new file mode 100644
> index 0000000..d377083
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_sysfs.c
> @@ -0,0 +1,75 @@
> +/*
> + * Sysfs entries for PCI Error Recovery for PAPR-compliant platform.
> + * Copyright IBM Corporation 2007
> + * Copyright Linas Vepstas <linas@austin.ibm.com> 2007
> + *
> + * All rights reserved.
> + *
> + * 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, GOOD TITLE or
> + * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> + */
> +#include <linux/pci.h>
> +#include <linux/stat.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/pci-bridge.h>
> +
> +/**
> + * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic
> + * @_name: name of file in sysfs directory
> + * @_memb: name of member in struct pci_dn to access
> + * @_format: printf format for display
> + *
> + * All of the attributes look very similar, so just
> + * auto-gen a cut-n-paste routine to display them.
> + */
> +#define EEH_SHOW_ATTR(_name,_memb,_format)               \
> +static ssize_t eeh_show_##_name(struct device *dev,      \
> +		struct device_attribute *attr, char *buf)          \
> +{                                                        \
> +	struct pci_dev *pdev = to_pci_dev(dev);               \
> +	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);      \
> +	                                                      \
> +	if (!edev)                                            \
> +		return 0;                                     \
> +	                                                      \
> +	return sprintf(buf, _format "\n", edev->_memb);       \
> +}                                                        \
> +static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
> +
> +EEH_SHOW_ATTR(eeh_mode,            mode,            "0x%x");
> +EEH_SHOW_ATTR(eeh_config_addr,     config_addr,     "0x%x");
> +EEH_SHOW_ATTR(eeh_pe_config_addr,  pe_config_addr,  "0x%x");
> +
> +void eeh_sysfs_add_device(struct pci_dev *pdev)
> +{
> +	int rc=0;
> +
> +	rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
> +	rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
> +	rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> +
> +	if (rc)
> +		printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
> +}
> +
> +void eeh_sysfs_remove_device(struct pci_dev *pdev)
> +{
> +	device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
> +	device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
> +	device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> +}
> +
> diff --git a/arch/powerpc/kernel/pci_hotplug.c b/arch/powerpc/kernel/pci_hotplug.c
> new file mode 100644
> index 0000000..3f60880
> --- /dev/null
> +++ b/arch/powerpc/kernel/pci_hotplug.c
> @@ -0,0 +1,111 @@
> +/*
> + * Derived from "arch/powerpc/platforms/pseries/pci_dlpar.c"
> + *
> + * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
> + * Copyright (C) 2005 International Business Machines
> + *
> + * Updates, 2005, John Rose <johnrose@austin.ibm.com>
> + * Updates, 2005, Linas Vepstas <linas@austin.ibm.com>
> + * Updates, 2013, Gavin Shan <shangw@linux.vnet.ibm.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/export.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/firmware.h>
> +#include <asm/eeh.h>
> +
> +/**
> + * __pcibios_remove_pci_devices - remove all devices under this bus
> + * @bus: the indicated PCI bus
> + * @purge_pe: destroy the PE on removal of PCI devices
> + *
> + * Remove all of the PCI devices under this bus both from the
> + * linux pci device tree, and from the powerpc EEH address cache.
> + * By default, the corresponding PE will be destroied during the
> + * normal PCI hotplug path. For PCI hotplug during EEH recovery,
> + * the corresponding PE won't be destroied and deallocated.
> + */
> +void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe)
> +{
> +	struct pci_dev *dev, *tmp;
> +	struct pci_bus *child_bus;
> +
> +	/* First go down child busses */
> +	list_for_each_entry(child_bus, &bus->children, node)
> +		__pcibios_remove_pci_devices(child_bus, purge_pe);
> +
> +	pr_debug("PCI: Removing devices on bus %04x:%02x\n",
> +		 pci_domain_nr(bus),  bus->number);
> +	list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
> +		pr_debug("     * Removing %s...\n", pci_name(dev));
> +		eeh_remove_bus_device(dev, purge_pe);
> +		pci_stop_and_remove_bus_device(dev);
> +	}
> +}
> +
> +/**
> + * pcibios_remove_pci_devices - remove all devices under this bus
> + * @bus: the indicated PCI bus
> + *
> + * Remove all of the PCI devices under this bus both from the
> + * linux pci device tree, and from the powerpc EEH address cache.
> + */
> +void pcibios_remove_pci_devices(struct pci_bus *bus)
> +{
> +	__pcibios_remove_pci_devices(bus, 1);
> +}
> +EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
> +
> +/**
> + * pcibios_add_pci_devices - adds new pci devices to bus
> + * @bus: the indicated PCI bus
> + *
> + * This routine will find and fixup new pci devices under
> + * the indicated bus. This routine presumes that there
> + * might already be some devices under this bridge, so
> + * it carefully tries to add only new devices.  (And that
> + * is how this routine differs from other, similar pcibios
> + * routines.)
> + */
> +void pcibios_add_pci_devices(struct pci_bus * bus)
> +{
> +	int slotno, num, mode, pass, max;
> +	struct pci_dev *dev;
> +	struct device_node *dn = pci_bus_to_OF_node(bus);
> +
> +	eeh_add_device_tree_early(dn);
> +
> +	mode = PCI_PROBE_NORMAL;
> +	if (ppc_md.pci_probe_mode)
> +		mode = ppc_md.pci_probe_mode(bus);
> +
> +	if (mode == PCI_PROBE_DEVTREE) {
> +		/* use ofdt-based probe */
> +		of_rescan_bus(dn, bus);
> +	} else if (mode == PCI_PROBE_NORMAL) {
> +		/* use legacy probe */
> +		slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
> +		num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
> +		if (!num)
> +			return;
> +		pcibios_setup_bus_devices(bus);
> +		max = bus->busn_res.start;
> +		for (pass = 0; pass < 2; pass++) {
> +			list_for_each_entry(dev, &bus->devices, bus_list) {
> +				if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
> +				    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
> +					max = pci_scan_bridge(bus, dev,
> +							      max, pass);
> +			}
> +		}
> +	}
> +	pcibios_finish_adding_to_bus(bus);
> +}
> +EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
> diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
> index b62aab3..bed8c60 100644
> --- a/arch/powerpc/platforms/Kconfig
> +++ b/arch/powerpc/platforms/Kconfig
> @@ -164,6 +164,11 @@ config IBMEBUS
>   	help
>   	  Bus device driver for GX bus based adapters.
>
> +config EEH
> +	bool
> +	depends on (PPC_POWERNV || PPC_PSERIES) && PCI
> +	default y
> +
>   config PPC_MPC106
>   	bool
>   	default n
> diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
> index 4459eff..1bd3399 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -33,11 +33,6 @@ config PPC_SPLPAR
>   	  processors, that is, which share physical processors between
>   	  two or more partitions.
>
> -config EEH
> -	bool
> -	depends on PPC_PSERIES && PCI
> -	default y
> -
>   config PSERIES_MSI
>          bool
>          depends on PCI_MSI && EEH
> diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
> index 53866e5..8ae0103 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -6,9 +6,7 @@ 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_pe.o eeh_dev.o eeh_cache.o \
> -			   eeh_driver.o eeh_event.o eeh_sysfs.o \
> -			   eeh_pseries.o
> +obj-$(CONFIG_EEH)	+= eeh_pseries.o
>   obj-$(CONFIG_KEXEC)	+= kexec.o
>   obj-$(CONFIG_PCI)	+= pci.o pci_dlpar.o
>   obj-$(CONFIG_PSERIES_MSI)	+= msi.o
> diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
> deleted file mode 100644
> index 6b73d6c..0000000
> --- a/arch/powerpc/platforms/pseries/eeh.c
> +++ /dev/null
> @@ -1,942 +0,0 @@
> -/*
> - * Copyright IBM Corporation 2001, 2005, 2006
> - * Copyright Dave Engebretsen & Todd Inglett 2001
> - * Copyright Linas Vepstas 2005, 2006
> - * Copyright 2001-2012 IBM Corporation.
> - *
> - * 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
> - *
> - * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> - */
> -
> -#include <linux/delay.h>
> -#include <linux/sched.h>
> -#include <linux/init.h>
> -#include <linux/list.h>
> -#include <linux/pci.h>
> -#include <linux/proc_fs.h>
> -#include <linux/rbtree.h>
> -#include <linux/seq_file.h>
> -#include <linux/spinlock.h>
> -#include <linux/export.h>
> -#include <linux/of.h>
> -
> -#include <linux/atomic.h>
> -#include <asm/eeh.h>
> -#include <asm/eeh_event.h>
> -#include <asm/io.h>
> -#include <asm/machdep.h>
> -#include <asm/ppc-pci.h>
> -#include <asm/rtas.h>
> -
> -
> -/** Overview:
> - *  EEH, or "Extended Error Handling" is a PCI bridge technology for
> - *  dealing with PCI bus errors that can't be dealt with within the
> - *  usual PCI framework, except by check-stopping the CPU.  Systems
> - *  that are designed for high-availability/reliability cannot afford
> - *  to crash due to a "mere" PCI error, thus the need for EEH.
> - *  An EEH-capable bridge operates by converting a detected error
> - *  into a "slot freeze", taking the PCI adapter off-line, making
> - *  the slot behave, from the OS'es point of view, as if the slot
> - *  were "empty": all reads return 0xff's and all writes are silently
> - *  ignored.  EEH slot isolation events can be triggered by parity
> - *  errors on the address or data busses (e.g. during posted writes),
> - *  which in turn might be caused by low voltage on the bus, dust,
> - *  vibration, humidity, radioactivity or plain-old failed hardware.
> - *
> - *  Note, however, that one of the leading causes of EEH slot
> - *  freeze events are buggy device drivers, buggy device microcode,
> - *  or buggy device hardware.  This is because any attempt by the
> - *  device to bus-master data to a memory address that is not
> - *  assigned to the device will trigger a slot freeze.   (The idea
> - *  is to prevent devices-gone-wild from corrupting system memory).
> - *  Buggy hardware/drivers will have a miserable time co-existing
> - *  with EEH.
> - *
> - *  Ideally, a PCI device driver, when suspecting that an isolation
> - *  event has occurred (e.g. by reading 0xff's), will then ask EEH
> - *  whether this is the case, and then take appropriate steps to
> - *  reset the PCI slot, the PCI device, and then resume operations.
> - *  However, until that day,  the checking is done here, with the
> - *  eeh_check_failure() routine embedded in the MMIO macros.  If
> - *  the slot is found to be isolated, an "EEH Event" is synthesized
> - *  and sent out for processing.
> - */
> -
> -/* If a device driver keeps reading an MMIO register in an interrupt
> - * handler after a slot isolation event, it might be broken.
> - * This sets the threshold for how many read attempts we allow
> - * before printing an error message.
> - */
> -#define EEH_MAX_FAILS	2100000
> -
> -/* Time to wait for a PCI slot to report status, in milliseconds */
> -#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
> -
> -/* Platform dependent EEH operations */
> -struct eeh_ops *eeh_ops = NULL;
> -
> -int eeh_subsystem_enabled;
> -EXPORT_SYMBOL(eeh_subsystem_enabled);
> -
> -/*
> - * EEH probe mode support. The intention is to support multiple
> - * platforms for EEH. Some platforms like pSeries do PCI emunation
> - * based on device tree. However, other platforms like powernv probe
> - * PCI devices from hardware. The flag is used to distinguish that.
> - * In addition, struct eeh_ops::probe would be invoked for particular
> - * OF node or PCI device so that the corresponding PE would be created
> - * there.
> - */
> -int eeh_probe_mode;
> -
> -/* Global EEH mutex */
> -DEFINE_MUTEX(eeh_mutex);
> -
> -/* Lock to avoid races due to multiple reports of an error */
> -static DEFINE_RAW_SPINLOCK(confirm_error_lock);
> -
> -/* Buffer for reporting pci register dumps. Its here in BSS, and
> - * not dynamically alloced, so that it ends up in RMO where RTAS
> - * can access it.
> - */
> -#define EEH_PCI_REGS_LOG_LEN 4096
> -static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
> -
> -/*
> - * The struct is used to maintain the EEH global statistic
> - * information. Besides, the EEH global statistics will be
> - * exported to user space through procfs
> - */
> -struct eeh_stats {
> -	u64 no_device;		/* PCI device not found		*/
> -	u64 no_dn;		/* OF node not found		*/
> -	u64 no_cfg_addr;	/* Config address not found	*/
> -	u64 ignored_check;	/* EEH check skipped		*/
> -	u64 total_mmio_ffs;	/* Total EEH checks		*/
> -	u64 false_positives;	/* Unnecessary EEH checks	*/
> -	u64 slot_resets;	/* PE reset			*/
> -};
> -
> -static struct eeh_stats eeh_stats;
> -
> -#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
> -
> -/**
> - * eeh_gather_pci_data - Copy assorted PCI config space registers to buff
> - * @edev: device to report data for
> - * @buf: point to buffer in which to log
> - * @len: amount of room in buffer
> - *
> - * This routine captures assorted PCI configuration space data,
> - * and puts them into a buffer for RTAS error logging.
> - */
> -static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
> -{
> -	struct device_node *dn = eeh_dev_to_of_node(edev);
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	u32 cfg;
> -	int cap, i;
> -	int n = 0;
> -
> -	n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
> -	printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
> -
> -	eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
> -	n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
> -	printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
> -
> -	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
> -	n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
> -	printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
> -
> -	if (!dev) {
> -		printk(KERN_WARNING "EEH: no PCI device for this of node\n");
> -		return n;
> -	}
> -
> -	/* Gather bridge-specific registers */
> -	if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
> -		eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
> -		n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
> -		printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
> -
> -		eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
> -		n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
> -		printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
> -	}
> -
> -	/* Dump out the PCI-X command and status regs */
> -	cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
> -	if (cap) {
> -		eeh_ops->read_config(dn, cap, 4, &cfg);
> -		n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
> -		printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
> -
> -		eeh_ops->read_config(dn, cap+4, 4, &cfg);
> -		n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
> -		printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
> -	}
> -
> -	/* If PCI-E capable, dump PCI-E cap 10, and the AER */
> -	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
> -	if (cap) {
> -		n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
> -		printk(KERN_WARNING
> -		       "EEH: PCI-E capabilities and status follow:\n");
> -
> -		for (i=0; i<=8; i++) {
> -			eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> -			n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> -			printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
> -		}
> -
> -		cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
> -		if (cap) {
> -			n += scnprintf(buf+n, len-n, "pci-e AER:\n");
> -			printk(KERN_WARNING
> -			       "EEH: PCI-E AER capability register set follows:\n");
> -
> -			for (i=0; i<14; i++) {
> -				eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> -				n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> -				printk(KERN_WARNING "EEH: PCI-E AER %02x: %08x\n", i, cfg);
> -			}
> -		}
> -	}
> -
> -	return n;
> -}
> -
> -/**
> - * eeh_slot_error_detail - Generate combined log including driver log and error log
> - * @pe: EEH PE
> - * @severity: temporary or permanent error log
> - *
> - * This routine should be called to generate the combined log, which
> - * is comprised of driver log and error log. The driver log is figured
> - * out from the config space of the corresponding PCI device, while
> - * the error log is fetched through platform dependent function call.
> - */
> -void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
> -{
> -	size_t loglen = 0;
> -	struct eeh_dev *edev;
> -
> -	eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> -	eeh_ops->configure_bridge(pe);
> -	eeh_pe_restore_bars(pe);
> -
> -	pci_regs_buf[0] = 0;
> -	eeh_pe_for_each_dev(pe, edev) {
> -		loglen += eeh_gather_pci_data(edev, pci_regs_buf,
> -				EEH_PCI_REGS_LOG_LEN);
> -        }
> -
> -	eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);
> -}
> -
> -/**
> - * eeh_token_to_phys - Convert EEH address token to phys address
> - * @token: I/O token, should be address in the form 0xA....
> - *
> - * This routine should be called to convert virtual I/O address
> - * to physical one.
> - */
> -static inline unsigned long eeh_token_to_phys(unsigned long token)
> -{
> -	pte_t *ptep;
> -	unsigned long pa;
> -
> -	ptep = find_linux_pte(init_mm.pgd, token);
> -	if (!ptep)
> -		return token;
> -	pa = pte_pfn(*ptep) << PAGE_SHIFT;
> -
> -	return pa | (token & (PAGE_SIZE-1));
> -}
> -
> -/**
> - * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze
> - * @edev: eeh device
> - *
> - * Check for an EEH failure for the given device node.  Call this
> - * routine if the result of a read was all 0xff's and you want to
> - * find out if this is due to an EEH slot freeze.  This routine
> - * will query firmware for the EEH status.
> - *
> - * Returns 0 if there has not been an EEH error; otherwise returns
> - * a non-zero value and queues up a slot isolation event notification.
> - *
> - * It is safe to call this routine in an interrupt context.
> - */
> -int eeh_dev_check_failure(struct eeh_dev *edev)
> -{
> -	int ret;
> -	unsigned long flags;
> -	struct device_node *dn;
> -	struct pci_dev *dev;
> -	struct eeh_pe *pe;
> -	int rc = 0;
> -	const char *location;
> -
> -	eeh_stats.total_mmio_ffs++;
> -
> -	if (!eeh_subsystem_enabled)
> -		return 0;
> -
> -	if (!edev) {
> -		eeh_stats.no_dn++;
> -		return 0;
> -	}
> -	dn = eeh_dev_to_of_node(edev);
> -	dev = eeh_dev_to_pci_dev(edev);
> -	pe = edev->pe;
> -
> -	/* Access to IO BARs might get this far and still not want checking. */
> -	if (!pe) {
> -		eeh_stats.ignored_check++;
> -		pr_debug("EEH: Ignored check for %s %s\n",
> -			eeh_pci_name(dev), dn->full_name);
> -		return 0;
> -	}
> -
> -	if (!pe->addr && !pe->config_addr) {
> -		eeh_stats.no_cfg_addr++;
> -		return 0;
> -	}
> -
> -	/* If we already have a pending isolation event for this
> -	 * slot, we know it's bad already, we don't need to check.
> -	 * Do this checking under a lock; as multiple PCI devices
> -	 * in one slot might report errors simultaneously, and we
> -	 * only want one error recovery routine running.
> -	 */
> -	raw_spin_lock_irqsave(&confirm_error_lock, flags);
> -	rc = 1;
> -	if (pe->state & EEH_PE_ISOLATED) {
> -		pe->check_count++;
> -		if (pe->check_count % EEH_MAX_FAILS == 0) {
> -			location = of_get_property(dn, "ibm,loc-code", NULL);
> -			printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
> -				"location=%s driver=%s pci addr=%s\n",
> -				pe->check_count, location,
> -				eeh_driver_name(dev), eeh_pci_name(dev));
> -			printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
> -				eeh_driver_name(dev));
> -			dump_stack();
> -		}
> -		goto dn_unlock;
> -	}
> -
> -	/*
> -	 * Now test for an EEH failure.  This is VERY expensive.
> -	 * Note that the eeh_config_addr may be a parent device
> -	 * in the case of a device behind a bridge, or it may be
> -	 * function zero of a multi-function device.
> -	 * In any case they must share a common PHB.
> -	 */
> -	ret = eeh_ops->get_state(pe, NULL);
> -
> -	/* Note that config-io to empty slots may fail;
> -	 * they are empty when they don't have children.
> -	 * We will punt with the following conditions: Failure to get
> -	 * PE's state, EEH not support and Permanently unavailable
> -	 * state, PE is in good state.
> -	 */
> -	if ((ret < 0) ||
> -	    (ret == EEH_STATE_NOT_SUPPORT) ||
> -	    (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
> -	    (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
> -		eeh_stats.false_positives++;
> -		pe->false_positives++;
> -		rc = 0;
> -		goto dn_unlock;
> -	}
> -
> -	eeh_stats.slot_resets++;
> -
> -	/* Avoid repeated reports of this failure, including problems
> -	 * with other functions on this device, and functions under
> -	 * bridges.
> -	 */
> -	eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
> -	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> -
> -	eeh_send_failure_event(pe);
> -
> -	/* Most EEH events are due to device driver bugs.  Having
> -	 * a stack trace will help the device-driver authors figure
> -	 * out what happened.  So print that out.
> -	 */
> -	WARN(1, "EEH: failure detected\n");
> -	return 1;
> -
> -dn_unlock:
> -	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> -	return rc;
> -}
> -
> -EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
> -
> -/**
> - * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
> - * @token: I/O token, should be address in the form 0xA....
> - * @val: value, should be all 1's (XXX why do we need this arg??)
> - *
> - * Check for an EEH failure at the given token address.  Call this
> - * routine if the result of a read was all 0xff's and you want to
> - * find out if this is due to an EEH slot freeze event.  This routine
> - * will query firmware for the EEH status.
> - *
> - * Note this routine is safe to call in an interrupt context.
> - */
> -unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
> -{
> -	unsigned long addr;
> -	struct eeh_dev *edev;
> -
> -	/* Finding the phys addr + pci device; this is pretty quick. */
> -	addr = eeh_token_to_phys((unsigned long __force) token);
> -	edev = eeh_addr_cache_get_dev(addr);
> -	if (!edev) {
> -		eeh_stats.no_device++;
> -		return val;
> -	}
> -
> -	eeh_dev_check_failure(edev);
> -
> -	pci_dev_put(eeh_dev_to_pci_dev(edev));
> -	return val;
> -}
> -
> -EXPORT_SYMBOL(eeh_check_failure);
> -
> -
> -/**
> - * eeh_pci_enable - Enable MMIO or DMA transfers for this slot
> - * @pe: EEH PE
> - *
> - * This routine should be called to reenable frozen MMIO or DMA
> - * so that it would work correctly again. It's useful while doing
> - * recovery or log collection on the indicated device.
> - */
> -int eeh_pci_enable(struct eeh_pe *pe, int function)
> -{
> -	int rc;
> -
> -	rc = eeh_ops->set_option(pe, function);
> -	if (rc)
> -		pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n",
> -			__func__, function, pe->phb->global_number, pe->addr, rc);
> -
> -	rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> -	if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
> -	   (function == EEH_OPT_THAW_MMIO))
> -		return 0;
> -
> -	return rc;
> -}
> -
> -/**
> - * pcibios_set_pcie_slot_reset - Set PCI-E reset state
> - * @dev: pci device struct
> - * @state: reset state to enter
> - *
> - * Return value:
> - * 	0 if success
> - */
> -int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
> -{
> -	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> -	struct eeh_pe *pe = edev->pe;
> -
> -	if (!pe) {
> -		pr_err("%s: No PE found on PCI device %s\n",
> -			__func__, pci_name(dev));
> -		return -EINVAL;
> -	}
> -
> -	switch (state) {
> -	case pcie_deassert_reset:
> -		eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> -		break;
> -	case pcie_hot_reset:
> -		eeh_ops->reset(pe, EEH_RESET_HOT);
> -		break;
> -	case pcie_warm_reset:
> -		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> -		break;
> -	default:
> -		return -EINVAL;
> -	};
> -
> -	return 0;
> -}
> -
> -/**
> - * eeh_set_pe_freset - Check the required reset for the indicated device
> - * @data: EEH device
> - * @flag: return value
> - *
> - * Each device might have its preferred reset type: fundamental or
> - * hot reset. The routine is used to collected the information for
> - * the indicated device and its children so that the bunch of the
> - * devices could be reset properly.
> - */
> -static void *eeh_set_dev_freset(void *data, void *flag)
> -{
> -	struct pci_dev *dev;
> -	unsigned int *freset = (unsigned int *)flag;
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -
> -	dev = eeh_dev_to_pci_dev(edev);
> -	if (dev)
> -		*freset |= dev->needs_freset;
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
> - * @pe: EEH PE
> - *
> - * Assert the PCI #RST line for 1/4 second.
> - */
> -static void eeh_reset_pe_once(struct eeh_pe *pe)
> -{
> -	unsigned int freset = 0;
> -
> -	/* Determine type of EEH reset required for
> -	 * Partitionable Endpoint, a hot-reset (1)
> -	 * or a fundamental reset (3).
> -	 * A fundamental reset required by any device under
> -	 * Partitionable Endpoint trumps hot-reset.
> -  	 */
> -	eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset);
> -
> -	if (freset)
> -		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> -	else
> -		eeh_ops->reset(pe, EEH_RESET_HOT);
> -
> -	/* The PCI bus requires that the reset be held high for at least
> -	 * a 100 milliseconds. We wait a bit longer 'just in case'.
> -	 */
> -#define PCI_BUS_RST_HOLD_TIME_MSEC 250
> -	msleep(PCI_BUS_RST_HOLD_TIME_MSEC);
> -	
> -	/* We might get hit with another EEH freeze as soon as the
> -	 * pci slot reset line is dropped. Make sure we don't miss
> -	 * these, and clear the flag now.
> -	 */
> -	eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
> -
> -	eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> -
> -	/* After a PCI slot has been reset, the PCI Express spec requires
> -	 * a 1.5 second idle time for the bus to stabilize, before starting
> -	 * up traffic.
> -	 */
> -#define PCI_BUS_SETTLE_TIME_MSEC 1800
> -	msleep(PCI_BUS_SETTLE_TIME_MSEC);
> -}
> -
> -/**
> - * eeh_reset_pe - Reset the indicated PE
> - * @pe: EEH PE
> - *
> - * This routine should be called to reset indicated device, including
> - * PE. A PE might include multiple PCI devices and sometimes PCI bridges
> - * might be involved as well.
> - */
> -int eeh_reset_pe(struct eeh_pe *pe)
> -{
> -	int i, rc;
> -
> -	/* Take three shots at resetting the bus */
> -	for (i=0; i<3; i++) {
> -		eeh_reset_pe_once(pe);
> -
> -		rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> -		if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
> -			return 0;
> -
> -		if (rc < 0) {
> -			pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x",
> -				__func__, pe->phb->global_number, pe->addr);
> -			return -1;
> -		}
> -		pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n",
> -			i+1, pe->phb->global_number, pe->addr, rc);
> -	}
> -
> -	return -1;
> -}
> -
> -/**
> - * eeh_save_bars - Save device bars
> - * @edev: PCI device associated EEH device
> - *
> - * Save the values of the device bars. Unlike the restore
> - * routine, this routine is *not* recursive. This is because
> - * PCI devices are added individually; but, for the restore,
> - * an entire slot is reset at a time.
> - */
> -void eeh_save_bars(struct eeh_dev *edev)
> -{
> -	int i;
> -	struct device_node *dn;
> -
> -	if (!edev)
> -		return;
> -	dn = eeh_dev_to_of_node(edev);
> -	
> -	for (i = 0; i < 16; i++)
> -		eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
> -}
> -
> -/**
> - * eeh_ops_register - Register platform dependent EEH operations
> - * @ops: platform dependent EEH operations
> - *
> - * Register the platform dependent EEH operation callback
> - * functions. The platform should call this function before
> - * any other EEH operations.
> - */
> -int __init eeh_ops_register(struct eeh_ops *ops)
> -{
> -	if (!ops->name) {
> -		pr_warning("%s: Invalid EEH ops name for %p\n",
> -			__func__, ops);
> -		return -EINVAL;
> -	}
> -
> -	if (eeh_ops && eeh_ops != ops) {
> -		pr_warning("%s: EEH ops of platform %s already existing (%s)\n",
> -			__func__, eeh_ops->name, ops->name);
> -		return -EEXIST;
> -	}
> -
> -	eeh_ops = ops;
> -
> -	return 0;
> -}
> -
> -/**
> - * eeh_ops_unregister - Unreigster platform dependent EEH operations
> - * @name: name of EEH platform operations
> - *
> - * Unregister the platform dependent EEH operation callback
> - * functions.
> - */
> -int __exit eeh_ops_unregister(const char *name)
> -{
> -	if (!name || !strlen(name)) {
> -		pr_warning("%s: Invalid EEH ops name\n",
> -			__func__);
> -		return -EINVAL;
> -	}
> -
> -	if (eeh_ops && !strcmp(eeh_ops->name, name)) {
> -		eeh_ops = NULL;
> -		return 0;
> -	}
> -
> -	return -EEXIST;
> -}
> -
> -/**
> - * eeh_init - EEH initialization
> - *
> - * Initialize EEH by trying to enable it for all of the adapters in the system.
> - * As a side effect we can determine here if eeh is supported at all.
> - * Note that we leave EEH on so failed config cycles won't cause a machine
> - * check.  If a user turns off EEH for a particular adapter they are really
> - * telling Linux to ignore errors.  Some hardware (e.g. POWER5) won't
> - * grant access to a slot if EEH isn't enabled, and so we always enable
> - * EEH for all slots/all devices.
> - *
> - * The eeh-force-off option disables EEH checking globally, for all slots.
> - * Even if force-off is set, the EEH hardware is still enabled, so that
> - * newer systems can boot.
> - */
> -static int __init eeh_init(void)
> -{
> -	struct pci_controller *hose, *tmp;
> -	struct device_node *phb;
> -	int ret;
> -
> -	/* call platform initialization function */
> -	if (!eeh_ops) {
> -		pr_warning("%s: Platform EEH operation not found\n",
> -			__func__);
> -		return -EEXIST;
> -	} else if ((ret = eeh_ops->init())) {
> -		pr_warning("%s: Failed to call platform init function (%d)\n",
> -			__func__, ret);
> -		return ret;
> -	}
> -
> -	raw_spin_lock_init(&confirm_error_lock);
> -
> -	/* Enable EEH for all adapters */
> -	if (eeh_probe_mode_devtree()) {
> -		list_for_each_entry_safe(hose, tmp,
> -			&hose_list, list_node) {
> -			phb = hose->dn;
> -			traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
> -		}
> -	}
> -
> -	if (eeh_subsystem_enabled)
> -		pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n");
> -	else
> -		pr_warning("EEH: No capable adapters found\n");
> -
> -	return ret;
> -}
> -
> -core_initcall_sync(eeh_init);
> -
> -/**
> - * eeh_add_device_early - Enable EEH for the indicated device_node
> - * @dn: device node for which to set up EEH
> - *
> - * This routine must be used to perform EEH initialization for PCI
> - * devices that were added after system boot (e.g. hotplug, dlpar).
> - * This routine must be called before any i/o is performed to the
> - * adapter (inluding any config-space i/o).
> - * Whether this actually enables EEH or not for this device depends
> - * on the CEC architecture, type of the device, on earlier boot
> - * command-line arguments & etc.
> - */
> -static void eeh_add_device_early(struct device_node *dn)
> -{
> -	struct pci_controller *phb;
> -
> -	if (!of_node_to_eeh_dev(dn))
> -		return;
> -	phb = of_node_to_eeh_dev(dn)->phb;
> -
> -	/* USB Bus children of PCI devices will not have BUID's */
> -	if (NULL == phb || 0 == phb->buid)
> -		return;
> -
> -	/* FIXME: hotplug support on POWERNV */
> -	eeh_ops->of_probe(dn, NULL);
> -}
> -
> -/**
> - * eeh_add_device_tree_early - Enable EEH for the indicated device
> - * @dn: device node
> - *
> - * This routine must be used to perform EEH initialization for the
> - * indicated PCI device that was added after system boot (e.g.
> - * hotplug, dlpar).
> - */
> -void eeh_add_device_tree_early(struct device_node *dn)
> -{
> -	struct device_node *sib;
> -
> -	for_each_child_of_node(dn, sib)
> -		eeh_add_device_tree_early(sib);
> -	eeh_add_device_early(dn);
> -}
> -EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
> -
> -/**
> - * eeh_add_device_late - Perform EEH initialization for the indicated pci device
> - * @dev: pci device for which to set up EEH
> - *
> - * This routine must be used to complete EEH initialization for PCI
> - * devices that were added after system boot (e.g. hotplug, dlpar).
> - */
> -static void eeh_add_device_late(struct pci_dev *dev)
> -{
> -	struct device_node *dn;
> -	struct eeh_dev *edev;
> -
> -	if (!dev || !eeh_subsystem_enabled)
> -		return;
> -
> -	pr_debug("EEH: Adding device %s\n", pci_name(dev));
> -
> -	dn = pci_device_to_OF_node(dev);
> -	edev = of_node_to_eeh_dev(dn);
> -	if (edev->pdev == dev) {
> -		pr_debug("EEH: Already referenced !\n");
> -		return;
> -	}
> -	WARN_ON(edev->pdev);
> -
> -	pci_dev_get(dev);
> -	edev->pdev = dev;
> -	dev->dev.archdata.edev = edev;
> -
> -	eeh_addr_cache_insert_dev(dev);
> -}
> -
> -/**
> - * eeh_add_device_tree_late - Perform EEH initialization for the indicated PCI bus
> - * @bus: PCI bus
> - *
> - * This routine must be used to perform EEH initialization for PCI
> - * devices which are attached to the indicated PCI bus. The PCI bus
> - * is added after system boot through hotplug or dlpar.
> - */
> -void eeh_add_device_tree_late(struct pci_bus *bus)
> -{
> -	struct pci_dev *dev;
> -
> -	list_for_each_entry(dev, &bus->devices, bus_list) {
> - 		eeh_add_device_late(dev);
> - 		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> - 			struct pci_bus *subbus = dev->subordinate;
> - 			if (subbus)
> - 				eeh_add_device_tree_late(subbus);
> - 		}
> -	}
> -}
> -EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
> -
> -/**
> - * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
> - * @bus: PCI bus
> - *
> - * This routine must be used to add EEH sysfs files for PCI
> - * devices which are attached to the indicated PCI bus. The PCI bus
> - * is added after system boot through hotplug or dlpar.
> - */
> -void eeh_add_sysfs_files(struct pci_bus *bus)
> -{
> -	struct pci_dev *dev;
> -
> -	list_for_each_entry(dev, &bus->devices, bus_list) {
> -		eeh_sysfs_add_device(dev);
> -		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> -			struct pci_bus *subbus = dev->subordinate;
> -			if (subbus)
> -				eeh_add_sysfs_files(subbus);
> -		}
> -	}
> -}
> -EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
> -
> -/**
> - * eeh_remove_device - Undo EEH setup for the indicated pci device
> - * @dev: pci device to be removed
> - * @purge_pe: remove the PE or not
> - *
> - * This routine should be called when a device is removed from
> - * a running system (e.g. by hotplug or dlpar).  It unregisters
> - * the PCI device from the EEH subsystem.  I/O errors affecting
> - * this device will no longer be detected after this call; thus,
> - * i/o errors affecting this slot may leave this device unusable.
> - */
> -static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
> -{
> -	struct eeh_dev *edev;
> -
> -	if (!dev || !eeh_subsystem_enabled)
> -		return;
> -	edev = pci_dev_to_eeh_dev(dev);
> -
> -	/* Unregister the device with the EEH/PCI address search system */
> -	pr_debug("EEH: Removing device %s\n", pci_name(dev));
> -
> -	if (!edev || !edev->pdev) {
> -		pr_debug("EEH: Not referenced !\n");
> -		return;
> -	}
> -	edev->pdev = NULL;
> -	dev->dev.archdata.edev = NULL;
> -	pci_dev_put(dev);
> -
> -	eeh_rmv_from_parent_pe(edev, purge_pe);
> -	eeh_addr_cache_rmv_dev(dev);
> -	eeh_sysfs_remove_device(dev);
> -}
> -
> -/**
> - * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
> - * @dev: PCI device
> - * @purge_pe: remove the corresponding PE or not
> - *
> - * This routine must be called when a device is removed from the
> - * running system through hotplug or dlpar. The corresponding
> - * PCI address cache will be removed.
> - */
> -void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe)
> -{
> -	struct pci_bus *bus = dev->subordinate;
> -	struct pci_dev *child, *tmp;
> -
> -	eeh_remove_device(dev, purge_pe);
> -
> -	if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> -		list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
> -			 eeh_remove_bus_device(child, purge_pe);
> -	}
> -}
> -EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
> -
> -static int proc_eeh_show(struct seq_file *m, void *v)
> -{
> -	if (0 == eeh_subsystem_enabled) {
> -		seq_printf(m, "EEH Subsystem is globally disabled\n");
> -		seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs);
> -	} else {
> -		seq_printf(m, "EEH Subsystem is enabled\n");
> -		seq_printf(m,
> -				"no device=%llu\n"
> -				"no device node=%llu\n"
> -				"no config address=%llu\n"
> -				"check not wanted=%llu\n"
> -				"eeh_total_mmio_ffs=%llu\n"
> -				"eeh_false_positives=%llu\n"
> -				"eeh_slot_resets=%llu\n",
> -				eeh_stats.no_device,
> -				eeh_stats.no_dn,
> -				eeh_stats.no_cfg_addr,
> -				eeh_stats.ignored_check,
> -				eeh_stats.total_mmio_ffs,
> -				eeh_stats.false_positives,
> -				eeh_stats.slot_resets);
> -	}
> -
> -	return 0;
> -}
> -
> -static int proc_eeh_open(struct inode *inode, struct file *file)
> -{
> -	return single_open(file, proc_eeh_show, NULL);
> -}
> -
> -static const struct file_operations proc_eeh_operations = {
> -	.open      = proc_eeh_open,
> -	.read      = seq_read,
> -	.llseek    = seq_lseek,
> -	.release   = single_release,
> -};
> -
> -static int __init eeh_init_proc(void)
> -{
> -	if (machine_is(pseries))
> -		proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations);
> -	return 0;
> -}
> -__initcall(eeh_init_proc);
> diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c
> deleted file mode 100644
> index 5a4c879..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_cache.c
> +++ /dev/null
> @@ -1,319 +0,0 @@
> -/*
> - * PCI address cache; allows the lookup of PCI devices based on I/O address
> - *
> - * Copyright IBM Corporation 2004
> - * Copyright Linas Vepstas <linas@austin.ibm.com> 2004
> - *
> - * 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/list.h>
> -#include <linux/pci.h>
> -#include <linux/rbtree.h>
> -#include <linux/slab.h>
> -#include <linux/spinlock.h>
> -#include <linux/atomic.h>
> -#include <asm/pci-bridge.h>
> -#include <asm/ppc-pci.h>
> -
> -
> -/**
> - * The pci address cache subsystem.  This subsystem places
> - * PCI device address resources into a red-black tree, sorted
> - * according to the address range, so that given only an i/o
> - * address, the corresponding PCI device can be **quickly**
> - * found. It is safe to perform an address lookup in an interrupt
> - * context; this ability is an important feature.
> - *
> - * Currently, the only customer of this code is the EEH subsystem;
> - * thus, this code has been somewhat tailored to suit EEH better.
> - * In particular, the cache does *not* hold the addresses of devices
> - * for which EEH is not enabled.
> - *
> - * (Implementation Note: The RB tree seems to be better/faster
> - * than any hash algo I could think of for this problem, even
> - * with the penalty of slow pointer chases for d-cache misses).
> - */
> -struct pci_io_addr_range {
> -	struct rb_node rb_node;
> -	unsigned long addr_lo;
> -	unsigned long addr_hi;
> -	struct eeh_dev *edev;
> -	struct pci_dev *pcidev;
> -	unsigned int flags;
> -};
> -
> -static struct pci_io_addr_cache {
> -	struct rb_root rb_root;
> -	spinlock_t piar_lock;
> -} pci_io_addr_cache_root;
> -
> -static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
> -{
> -	struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node;
> -
> -	while (n) {
> -		struct pci_io_addr_range *piar;
> -		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> -
> -		if (addr < piar->addr_lo) {
> -			n = n->rb_left;
> -		} else {
> -			if (addr > piar->addr_hi) {
> -				n = n->rb_right;
> -			} else {
> -				pci_dev_get(piar->pcidev);
> -				return piar->edev;
> -			}
> -		}
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_addr_cache_get_dev - Get device, given only address
> - * @addr: mmio (PIO) phys address or i/o port number
> - *
> - * Given an mmio phys address, or a port number, find a pci device
> - * that implements this address.  Be sure to pci_dev_put the device
> - * when finished.  I/O port numbers are assumed to be offset
> - * from zero (that is, they do *not* have pci_io_addr added in).
> - * It is safe to call this function within an interrupt.
> - */
> -struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr)
> -{
> -	struct eeh_dev *edev;
> -	unsigned long flags;
> -
> -	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> -	edev = __eeh_addr_cache_get_device(addr);
> -	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> -	return edev;
> -}
> -
> -#ifdef DEBUG
> -/*
> - * Handy-dandy debug print routine, does nothing more
> - * than print out the contents of our addr cache.
> - */
> -static void eeh_addr_cache_print(struct pci_io_addr_cache *cache)
> -{
> -	struct rb_node *n;
> -	int cnt = 0;
> -
> -	n = rb_first(&cache->rb_root);
> -	while (n) {
> -		struct pci_io_addr_range *piar;
> -		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> -		pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n",
> -		       (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt,
> -		       piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev));
> -		cnt++;
> -		n = rb_next(n);
> -	}
> -}
> -#endif
> -
> -/* Insert address range into the rb tree. */
> -static struct pci_io_addr_range *
> -eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
> -		      unsigned long ahi, unsigned int flags)
> -{
> -	struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node;
> -	struct rb_node *parent = NULL;
> -	struct pci_io_addr_range *piar;
> -
> -	/* Walk tree, find a place to insert into tree */
> -	while (*p) {
> -		parent = *p;
> -		piar = rb_entry(parent, struct pci_io_addr_range, rb_node);
> -		if (ahi < piar->addr_lo) {
> -			p = &parent->rb_left;
> -		} else if (alo > piar->addr_hi) {
> -			p = &parent->rb_right;
> -		} else {
> -			if (dev != piar->pcidev ||
> -			    alo != piar->addr_lo || ahi != piar->addr_hi) {
> -				pr_warning("PIAR: overlapping address range\n");
> -			}
> -			return piar;
> -		}
> -	}
> -	piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC);
> -	if (!piar)
> -		return NULL;
> -
> -	pci_dev_get(dev);
> -	piar->addr_lo = alo;
> -	piar->addr_hi = ahi;
> -	piar->edev = pci_dev_to_eeh_dev(dev);
> -	piar->pcidev = dev;
> -	piar->flags = flags;
> -
> -#ifdef DEBUG
> -	pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n",
> -	                  alo, ahi, pci_name(dev));
> -#endif
> -
> -	rb_link_node(&piar->rb_node, parent, p);
> -	rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root);
> -
> -	return piar;
> -}
> -
> -static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
> -{
> -	struct device_node *dn;
> -	struct eeh_dev *edev;
> -	int i;
> -
> -	dn = pci_device_to_OF_node(dev);
> -	if (!dn) {
> -		pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev));
> -		return;
> -	}
> -
> -	edev = of_node_to_eeh_dev(dn);
> -	if (!edev) {
> -		pr_warning("PCI: no EEH dev found for dn=%s\n",
> -			dn->full_name);
> -		return;
> -	}
> -
> -	/* Skip any devices for which EEH is not enabled. */
> -	if (!edev->pe) {
> -#ifdef DEBUG
> -		pr_info("PCI: skip building address cache for=%s - %s\n",
> -			pci_name(dev), dn->full_name);
> -#endif
> -		return;
> -	}
> -
> -	/* Walk resources on this device, poke them into the tree */
> -	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
> -		unsigned long start = pci_resource_start(dev,i);
> -		unsigned long end = pci_resource_end(dev,i);
> -		unsigned int flags = pci_resource_flags(dev,i);
> -
> -		/* We are interested only bus addresses, not dma or other stuff */
> -		if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM)))
> -			continue;
> -		if (start == 0 || ~start == 0 || end == 0 || ~end == 0)
> -			 continue;
> -		eeh_addr_cache_insert(dev, start, end, flags);
> -	}
> -}
> -
> -/**
> - * eeh_addr_cache_insert_dev - Add a device to the address cache
> - * @dev: PCI device whose I/O addresses we are interested in.
> - *
> - * In order to support the fast lookup of devices based on addresses,
> - * we maintain a cache of devices that can be quickly searched.
> - * This routine adds a device to that cache.
> - */
> -void eeh_addr_cache_insert_dev(struct pci_dev *dev)
> -{
> -	unsigned long flags;
> -
> -	/* Ignore PCI bridges */
> -	if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
> -		return;
> -
> -	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> -	__eeh_addr_cache_insert_dev(dev);
> -	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> -}
> -
> -static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> -{
> -	struct rb_node *n;
> -
> -restart:
> -	n = rb_first(&pci_io_addr_cache_root.rb_root);
> -	while (n) {
> -		struct pci_io_addr_range *piar;
> -		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> -
> -		if (piar->pcidev == dev) {
> -			rb_erase(n, &pci_io_addr_cache_root.rb_root);
> -			pci_dev_put(piar->pcidev);
> -			kfree(piar);
> -			goto restart;
> -		}
> -		n = rb_next(n);
> -	}
> -}
> -
> -/**
> - * eeh_addr_cache_rmv_dev - remove pci device from addr cache
> - * @dev: device to remove
> - *
> - * Remove a device from the addr-cache tree.
> - * This is potentially expensive, since it will walk
> - * the tree multiple times (once per resource).
> - * But so what; device removal doesn't need to be that fast.
> - */
> -void eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> -{
> -	unsigned long flags;
> -
> -	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> -	__eeh_addr_cache_rmv_dev(dev);
> -	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> -}
> -
> -/**
> - * eeh_addr_cache_build - Build a cache of I/O addresses
> - *
> - * Build a cache of pci i/o addresses.  This cache will be used to
> - * find the pci device that corresponds to a given address.
> - * This routine scans all pci busses to build the cache.
> - * Must be run late in boot process, after the pci controllers
> - * have been scanned for devices (after all device resources are known).
> - */
> -void __init eeh_addr_cache_build(void)
> -{
> -	struct device_node *dn;
> -	struct eeh_dev *edev;
> -	struct pci_dev *dev = NULL;
> -
> -	spin_lock_init(&pci_io_addr_cache_root.piar_lock);
> -
> -	for_each_pci_dev(dev) {
> -		eeh_addr_cache_insert_dev(dev);
> -
> -		dn = pci_device_to_OF_node(dev);
> -		if (!dn)
> -			continue;
> -
> -		edev = of_node_to_eeh_dev(dn);
> -		if (!edev)
> -			continue;
> -
> -		pci_dev_get(dev);  /* matching put is in eeh_remove_device() */
> -		dev->dev.archdata.edev = edev;
> -		edev->pdev = dev;
> -
> -		eeh_sysfs_add_device(dev);
> -	}
> -
> -#ifdef DEBUG
> -	/* Verify tree built up above, echo back the list of addrs. */
> -	eeh_addr_cache_print(&pci_io_addr_cache_root);
> -#endif
> -}
> -
> diff --git a/arch/powerpc/platforms/pseries/eeh_dev.c b/arch/powerpc/platforms/pseries/eeh_dev.c
> deleted file mode 100644
> index 1efa28f..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_dev.c
> +++ /dev/null
> @@ -1,112 +0,0 @@
> -/*
> - * The file intends to implement dynamic creation of EEH device, which will
> - * be bound with OF node and PCI device simutaneously. The EEH devices would
> - * be foundamental information for EEH core components to work proerly. Besides,
> - * We have to support multiple situations where dynamic creation of EEH device
> - * is required:
> - *
> - * 1) Before PCI emunation starts, we need create EEH devices according to the
> - *    PCI sensitive OF nodes.
> - * 2) When PCI emunation is done, we need do the binding between PCI device and
> - *    the associated EEH device.
> - * 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device
> - *    will be created while PCI sensitive OF node is detected from DR.
> - * 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If
> - *    PHB is newly inserted, we also need create EEH devices accordingly.
> - *
> - * 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>
> -
> -/**
> - * eeh_dev_init - Create EEH device according to OF node
> - * @dn: device node
> - * @data: PHB
> - *
> - * It will create EEH device according to the given OF node. The function
> - * might be called by PCI emunation, DR, PHB hotplug.
> - */
> -void *eeh_dev_init(struct device_node *dn, void *data)
> -{
> -	struct pci_controller *phb = data;
> -	struct eeh_dev *edev;
> -
> -	/* Allocate EEH device */
> -	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
> -	if (!edev) {
> -		pr_warning("%s: out of memory\n", __func__);
> -		return NULL;
> -	}
> -
> -	/* Associate EEH device with OF node */
> -	PCI_DN(dn)->edev = edev;
> -	edev->dn  = dn;
> -	edev->phb = phb;
> -	INIT_LIST_HEAD(&edev->list);
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB
> - * @phb: PHB
> - *
> - * Scan the PHB OF node and its child association, then create the
> - * EEH devices accordingly
> - */
> -void 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);
> -
> -	/* EEH devices for children OF nodes */
> -	traverse_pci_devices(dn, eeh_dev_init, phb);
> -}
> -
> -/**
> - * eeh_dev_phb_init - Create EEH devices for devices included in existing PHBs
> - *
> - * Scan all the existing PHBs and create EEH devices for their OF
> - * nodes and their children OF nodes
> - */
> -static int __init eeh_dev_phb_init(void)
> -{
> -	struct pci_controller *phb, *tmp;
> -
> -	list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
> -		eeh_dev_phb_init_dynamic(phb);
> -
> -	pr_info("EEH: devices created\n");
> -
> -	return 0;
> -}
> -
> -core_initcall(eeh_dev_phb_init);
> diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
> deleted file mode 100644
> index a3fefb6..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_driver.c
> +++ /dev/null
> @@ -1,552 +0,0 @@
> -/*
> - * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
> - * Copyright IBM Corp. 2004 2005
> - * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
> - *
> - * All rights reserved.
> - *
> - * 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, GOOD TITLE or
> - * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
> - *
> - * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> - */
> -#include <linux/delay.h>
> -#include <linux/interrupt.h>
> -#include <linux/irq.h>
> -#include <linux/module.h>
> -#include <linux/pci.h>
> -#include <asm/eeh.h>
> -#include <asm/eeh_event.h>
> -#include <asm/ppc-pci.h>
> -#include <asm/pci-bridge.h>
> -#include <asm/prom.h>
> -#include <asm/rtas.h>
> -
> -/**
> - * eeh_pcid_name - Retrieve name of PCI device driver
> - * @pdev: PCI device
> - *
> - * This routine is used to retrieve the name of PCI device driver
> - * if that's valid.
> - */
> -static inline const char *eeh_pcid_name(struct pci_dev *pdev)
> -{
> -	if (pdev && pdev->dev.driver)
> -		return pdev->dev.driver->name;
> -	return "";
> -}
> -
> -/**
> - * eeh_pcid_get - Get the PCI device driver
> - * @pdev: PCI device
> - *
> - * The function is used to retrieve the PCI device driver for
> - * the indicated PCI device. Besides, we will increase the reference
> - * of the PCI device driver to prevent that being unloaded on
> - * the fly. Otherwise, kernel crash would be seen.
> - */
> -static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
> -{
> -	if (!pdev || !pdev->driver)
> -		return NULL;
> -
> -	if (!try_module_get(pdev->driver->driver.owner))
> -		return NULL;
> -
> -	return pdev->driver;
> -}
> -
> -/**
> - * eeh_pcid_put - Dereference on the PCI device driver
> - * @pdev: PCI device
> - *
> - * The function is called to do dereference on the PCI device
> - * driver of the indicated PCI device.
> - */
> -static inline void eeh_pcid_put(struct pci_dev *pdev)
> -{
> -	if (!pdev || !pdev->driver)
> -		return;
> -
> -	module_put(pdev->driver->driver.owner);
> -}
> -
> -#if 0
> -static void print_device_node_tree(struct pci_dn *pdn, int dent)
> -{
> -	int i;
> -	struct device_node *pc;
> -
> -	if (!pdn)
> -		return;
> -	for (i = 0; i < dent; i++)
> -		printk(" ");
> -	printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
> -		pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
> -		pdn->eeh_pe_config_addr, pdn->node->full_name);
> -	dent += 3;
> -	pc = pdn->node->child;
> -	while (pc) {
> -		print_device_node_tree(PCI_DN(pc), dent);
> -		pc = pc->sibling;
> -	}
> -}
> -#endif
> -
> -/**
> - * eeh_disable_irq - Disable interrupt for the recovering device
> - * @dev: PCI device
> - *
> - * This routine must be called when reporting temporary or permanent
> - * error to the particular PCI device to disable interrupt of that
> - * device. If the device has enabled MSI or MSI-X interrupt, we needn't
> - * do real work because EEH should freeze DMA transfers for those PCI
> - * devices encountering EEH errors, which includes MSI or MSI-X.
> - */
> -static void eeh_disable_irq(struct pci_dev *dev)
> -{
> -	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> -
> -	/* Don't disable MSI and MSI-X interrupts. They are
> -	 * effectively disabled by the DMA Stopped state
> -	 * when an EEH error occurs.
> -	 */
> -	if (dev->msi_enabled || dev->msix_enabled)
> -		return;
> -
> -	if (!irq_has_action(dev->irq))
> -		return;
> -
> -	edev->mode |= EEH_DEV_IRQ_DISABLED;
> -	disable_irq_nosync(dev->irq);
> -}
> -
> -/**
> - * eeh_enable_irq - Enable interrupt for the recovering device
> - * @dev: PCI device
> - *
> - * This routine must be called to enable interrupt while failed
> - * device could be resumed.
> - */
> -static void eeh_enable_irq(struct pci_dev *dev)
> -{
> -	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> -
> -	if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
> -		edev->mode &= ~EEH_DEV_IRQ_DISABLED;
> -		enable_irq(dev->irq);
> -	}
> -}
> -
> -/**
> - * eeh_report_error - Report pci error to each device driver
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * Report an EEH error to each device driver, collect up and
> - * merge the device driver responses. Cumulative response
> - * passed back in "userdata".
> - */
> -static void *eeh_report_error(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	enum pci_ers_result rc, *res = userdata;
> -	struct pci_driver *driver;
> -
> -	/* We might not have the associated PCI device,
> -	 * then we should continue for next one.
> -	 */
> -	if (!dev) return NULL;
> -	dev->error_state = pci_channel_io_frozen;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	eeh_disable_irq(dev);
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->error_detected) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
> -
> -	/* A driver that needs a reset trumps all others */
> -	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> -	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * Tells each device driver that IO ports, MMIO and config space I/O
> - * are now enabled. Collects up and merges the device driver responses.
> - * Cumulative response passed back in "userdata".
> - */
> -static void *eeh_report_mmio_enabled(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	enum pci_ers_result rc, *res = userdata;
> -	struct pci_driver *driver;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->mmio_enabled) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	rc = driver->err_handler->mmio_enabled(dev);
> -
> -	/* A driver that needs a reset trumps all others */
> -	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> -	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_report_reset - Tell device that slot has been reset
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * This routine must be called while EEH tries to reset particular
> - * PCI device so that the associated PCI device driver could take
> - * some actions, usually to save data the driver needs so that the
> - * driver can work again while the device is recovered.
> - */
> -static void *eeh_report_reset(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	enum pci_ers_result rc, *res = userdata;
> -	struct pci_driver *driver;
> -
> -	if (!dev) return NULL;
> -	dev->error_state = pci_channel_io_normal;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	eeh_enable_irq(dev);
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->slot_reset) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	rc = driver->err_handler->slot_reset(dev);
> -	if ((*res == PCI_ERS_RESULT_NONE) ||
> -	    (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
> -	if (*res == PCI_ERS_RESULT_DISCONNECT &&
> -	     rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_report_resume - Tell device to resume normal operations
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * This routine must be called to notify the device driver that it
> - * could resume so that the device driver can do some initialization
> - * to make the recovered device work again.
> - */
> -static void *eeh_report_resume(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	struct pci_driver *driver;
> -
> -	if (!dev) return NULL;
> -	dev->error_state = pci_channel_io_normal;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	eeh_enable_irq(dev);
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->resume) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	driver->err_handler->resume(dev);
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_report_failure - Tell device driver that device is dead.
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * This informs the device driver that the device is permanently
> - * dead, and that no further recovery attempts will be made on it.
> - */
> -static void *eeh_report_failure(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	struct pci_driver *driver;
> -
> -	if (!dev) return NULL;
> -	dev->error_state = pci_channel_io_perm_failure;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	eeh_disable_irq(dev);
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->error_detected) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_reset_device - Perform actual reset of a pci slot
> - * @pe: EEH PE
> - * @bus: PCI bus corresponding to the isolcated slot
> - *
> - * This routine must be called to do reset on the indicated PE.
> - * During the reset, udev might be invoked because those affected
> - * PCI devices will be removed and then added.
> - */
> -static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
> -{
> -	int cnt, rc;
> -
> -	/* pcibios will clear the counter; save the value */
> -	cnt = pe->freeze_count;
> -
> -	/*
> -	 * We don't remove the corresponding PE instances because
> -	 * we need the information afterwords. The attached EEH
> -	 * devices are expected to be attached soon when calling
> -	 * into pcibios_add_pci_devices().
> -	 */
> -	if (bus)
> -		__pcibios_remove_pci_devices(bus, 0);
> -
> -	/* Reset the pci controller. (Asserts RST#; resets config space).
> -	 * Reconfigure bridges and devices. Don't try to bring the system
> -	 * up if the reset failed for some reason.
> -	 */
> -	rc = eeh_reset_pe(pe);
> -	if (rc)
> -		return rc;
> -
> -	/* Restore PE */
> -	eeh_ops->configure_bridge(pe);
> -	eeh_pe_restore_bars(pe);
> -
> -	/* Give the system 5 seconds to finish running the user-space
> -	 * hotplug shutdown scripts, e.g. ifdown for ethernet.  Yes,
> -	 * this is a hack, but if we don't do this, and try to bring
> -	 * the device up before the scripts have taken it down,
> -	 * potentially weird things happen.
> -	 */
> -	if (bus) {
> -		ssleep(5);
> -		pcibios_add_pci_devices(bus);
> -	}
> -	pe->freeze_count = cnt;
> -
> -	return 0;
> -}
> -
> -/* The longest amount of time to wait for a pci device
> - * to come back on line, in seconds.
> - */
> -#define MAX_WAIT_FOR_RECOVERY 150
> -
> -/**
> - * eeh_handle_event - Reset a PCI device after hard lockup.
> - * @pe: EEH PE
> - *
> - * While PHB detects address or data parity errors on particular PCI
> - * slot, the associated PE will be frozen. Besides, DMA's occurring
> - * to wild addresses (which usually happen due to bugs in device
> - * drivers or in PCI adapter firmware) can cause EEH error. #SERR,
> - * #PERR or other misc PCI-related errors also can trigger EEH errors.
> - *
> - * Recovery process consists of unplugging the device driver (which
> - * generated hotplug events to userspace), then issuing a PCI #RST to
> - * the device, then reconfiguring the PCI config space for all bridges
> - * & devices under this slot, and then finally restarting the device
> - * drivers (which cause a second set of hotplug events to go out to
> - * userspace).
> - */
> -void eeh_handle_event(struct eeh_pe *pe)
> -{
> -	struct pci_bus *frozen_bus;
> -	int rc = 0;
> -	enum pci_ers_result result = PCI_ERS_RESULT_NONE;
> -
> -	frozen_bus = eeh_pe_bus_get(pe);
> -	if (!frozen_bus) {
> -		pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
> -			__func__, pe->phb->global_number, pe->addr);
> -		return;
> -	}
> -
> -	pe->freeze_count++;
> -	if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
> -		goto excess_failures;
> -	pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
> -		pe->freeze_count);
> -
> -	/* Walk the various device drivers attached to this slot through
> -	 * a reset sequence, giving each an opportunity to do what it needs
> -	 * to accomplish the reset.  Each child gets a report of the
> -	 * status ... if any child can't handle the reset, then the entire
> -	 * slot is dlpar removed and added.
> -	 */
> -	eeh_pe_dev_traverse(pe, eeh_report_error, &result);
> -
> -	/* Get the current PCI slot state. This can take a long time,
> -	 * sometimes over 3 seconds for certain systems.
> -	 */
> -	rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
> -	if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
> -		printk(KERN_WARNING "EEH: Permanent failure\n");
> -		goto hard_fail;
> -	}
> -
> -	/* Since rtas may enable MMIO when posting the error log,
> -	 * don't post the error log until after all dev drivers
> -	 * have been informed.
> -	 */
> -	eeh_slot_error_detail(pe, EEH_LOG_TEMP);
> -
> -	/* If all device drivers were EEH-unaware, then shut
> -	 * down all of the device drivers, and hope they
> -	 * go down willingly, without panicing the system.
> -	 */
> -	if (result == PCI_ERS_RESULT_NONE) {
> -		rc = eeh_reset_device(pe, frozen_bus);
> -		if (rc) {
> -			printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
> -			goto hard_fail;
> -		}
> -	}
> -
> -	/* If all devices reported they can proceed, then re-enable MMIO */
> -	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> -		rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> -
> -		if (rc < 0)
> -			goto hard_fail;
> -		if (rc) {
> -			result = PCI_ERS_RESULT_NEED_RESET;
> -		} else {
> -			result = PCI_ERS_RESULT_NONE;
> -			eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
> -		}
> -	}
> -
> -	/* If all devices reported they can proceed, then re-enable DMA */
> -	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> -		rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
> -
> -		if (rc < 0)
> -			goto hard_fail;
> -		if (rc)
> -			result = PCI_ERS_RESULT_NEED_RESET;
> -		else
> -			result = PCI_ERS_RESULT_RECOVERED;
> -	}
> -
> -	/* If any device has a hard failure, then shut off everything. */
> -	if (result == PCI_ERS_RESULT_DISCONNECT) {
> -		printk(KERN_WARNING "EEH: Device driver gave up\n");
> -		goto hard_fail;
> -	}
> -
> -	/* If any device called out for a reset, then reset the slot */
> -	if (result == PCI_ERS_RESULT_NEED_RESET) {
> -		rc = eeh_reset_device(pe, NULL);
> -		if (rc) {
> -			printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
> -			goto hard_fail;
> -		}
> -		result = PCI_ERS_RESULT_NONE;
> -		eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
> -	}
> -
> -	/* All devices should claim they have recovered by now. */
> -	if ((result != PCI_ERS_RESULT_RECOVERED) &&
> -	    (result != PCI_ERS_RESULT_NONE)) {
> -		printk(KERN_WARNING "EEH: Not recovered\n");
> -		goto hard_fail;
> -	}
> -
> -	/* Tell all device drivers that they can resume operations */
> -	eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
> -
> -	return;
> -	
> -excess_failures:
> -	/*
> -	 * About 90% of all real-life EEH failures in the field
> -	 * are due to poorly seated PCI cards. Only 10% or so are
> -	 * due to actual, failed cards.
> -	 */
> -	pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
> -	       "last hour and has been permanently disabled.\n"
> -	       "Please try reseating or replacing it.\n",
> -		pe->phb->global_number, pe->addr,
> -		pe->freeze_count);
> -	goto perm_error;
> -
> -hard_fail:
> -	pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
> -	       "Please try reseating or replacing it\n",
> -		pe->phb->global_number, pe->addr);
> -
> -perm_error:
> -	eeh_slot_error_detail(pe, EEH_LOG_PERM);
> -
> -	/* Notify all devices that they're about to go down. */
> -	eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
> -
> -	/* Shut down the device drivers for good. */
> -	if (frozen_bus)
> -		pcibios_remove_pci_devices(frozen_bus);
> -}
> -
> diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c
> deleted file mode 100644
> index 185bedd..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_event.c
> +++ /dev/null
> @@ -1,142 +0,0 @@
> -/*
> - * 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
> - *
> - * Copyright (c) 2005 Linas Vepstas <linas@linas.org>
> - */
> -
> -#include <linux/delay.h>
> -#include <linux/list.h>
> -#include <linux/mutex.h>
> -#include <linux/sched.h>
> -#include <linux/pci.h>
> -#include <linux/slab.h>
> -#include <linux/workqueue.h>
> -#include <linux/kthread.h>
> -#include <asm/eeh_event.h>
> -#include <asm/ppc-pci.h>
> -
> -/** Overview:
> - *  EEH error states may be detected within exception handlers;
> - *  however, the recovery processing needs to occur asynchronously
> - *  in a normal kernel context and not an interrupt context.
> - *  This pair of routines creates an event and queues it onto a
> - *  work-queue, where a worker thread can drive recovery.
> - */
> -
> -/* EEH event workqueue setup. */
> -static DEFINE_SPINLOCK(eeh_eventlist_lock);
> -LIST_HEAD(eeh_eventlist);
> -static void eeh_thread_launcher(struct work_struct *);
> -DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
> -
> -/* Serialize reset sequences for a given pci device */
> -DEFINE_MUTEX(eeh_event_mutex);
> -
> -/**
> - * eeh_event_handler - Dispatch EEH events.
> - * @dummy - unused
> - *
> - * The detection of a frozen slot can occur inside an interrupt,
> - * where it can be hard to do anything about it.  The goal of this
> - * routine is to pull these detection events out of the context
> - * of the interrupt handler, and re-dispatch them for processing
> - * at a later time in a normal context.
> - */
> -static int eeh_event_handler(void * dummy)
> -{
> -	unsigned long flags;
> -	struct eeh_event *event;
> -	struct eeh_pe *pe;
> -
> -	spin_lock_irqsave(&eeh_eventlist_lock, flags);
> -	event = NULL;
> -
> -	/* Unqueue the event, get ready to process. */
> -	if (!list_empty(&eeh_eventlist)) {
> -		event = list_entry(eeh_eventlist.next, struct eeh_event, list);
> -		list_del(&event->list);
> -	}
> -	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> -
> -	if (event == NULL)
> -		return 0;
> -
> -	/* Serialize processing of EEH events */
> -	mutex_lock(&eeh_event_mutex);
> -	pe = event->pe;
> -	eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
> -	pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n",
> -		pe->phb->global_number, pe->addr);
> -
> -	set_current_state(TASK_INTERRUPTIBLE);	/* Don't add to load average */
> -	eeh_handle_event(pe);
> -	eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
> -
> -	kfree(event);
> -	mutex_unlock(&eeh_event_mutex);
> -
> -	/* If there are no new errors after an hour, clear the counter. */
> -	if (pe && pe->freeze_count > 0) {
> -		msleep_interruptible(3600*1000);
> -		if (pe->freeze_count > 0)
> -			pe->freeze_count--;
> -
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * eeh_thread_launcher - Start kernel thread to handle EEH events
> - * @dummy - unused
> - *
> - * This routine is called to start the kernel thread for processing
> - * EEH event.
> - */
> -static void eeh_thread_launcher(struct work_struct *dummy)
> -{
> -	if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd")))
> -		printk(KERN_ERR "Failed to start EEH daemon\n");
> -}
> -
> -/**
> - * eeh_send_failure_event - Generate a PCI error event
> - * @pe: EEH PE
> - *
> - * This routine can be called within an interrupt context;
> - * the actual event will be delivered in a normal context
> - * (from a workqueue).
> - */
> -int eeh_send_failure_event(struct eeh_pe *pe)
> -{
> -	unsigned long flags;
> -	struct eeh_event *event;
> -
> -	event = kzalloc(sizeof(*event), GFP_ATOMIC);
> -	if (!event) {
> -		pr_err("EEH: out of memory, event not handled\n");
> -		return -ENOMEM;
> -	}
> -	event->pe = pe;
> -
> -	/* We may or may not be called in an interrupt context */
> -	spin_lock_irqsave(&eeh_eventlist_lock, flags);
> -	list_add(&event->list, &eeh_eventlist);
> -	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> -
> -	schedule_work(&eeh_event_wq);
> -
> -	return 0;
> -}
> diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c
> deleted file mode 100644
> index 9d4a9e8..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_pe.c
> +++ /dev/null
> @@ -1,653 +0,0 @@
> -/*
> - * 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 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;
> -
> -	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)
> -			return pe;
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_next - Retrieve the next PE in the tree
> - * @pe: current PE
> - * @root: root PE
> - *
> - * The function is used to retrieve the next PE in the
> - * hierarchy PE tree.
> - */
> -static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
> -				  struct eeh_pe *root)
> -{
> -	struct list_head *next = pe->child_list.next;
> -
> -	if (next == &pe->child_list) {
> -		while (1) {
> -			if (pe == root)
> -				return NULL;
> -			next = pe->child.next;
> -			if (next != &pe->parent->child_list)
> -				break;
> -			pe = pe->parent;
> -		}
> -	}
> -
> -	return list_entry(next, struct eeh_pe, child);
> -}
> -
> -/**
> - * eeh_pe_traverse - Traverse PEs in the specified PHB
> - * @root: root PE
> - * @fn: callback
> - * @flag: extra parameter to callback
> - *
> - * The function is used to traverse the specified PE and its
> - * child PEs. The traversing is to be terminated once the
> - * callback returns something other than NULL, or no more PEs
> - * to be traversed.
> - */
> -static void *eeh_pe_traverse(struct eeh_pe *root,
> -			eeh_traverse_func fn, void *flag)
> -{
> -	struct eeh_pe *pe;
> -	void *ret;
> -
> -	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> -		ret = fn(pe, flag);
> -		if (ret) return ret;
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_dev_traverse - Traverse the devices from the PE
> - * @root: EEH PE
> - * @fn: function callback
> - * @flag: extra parameter to callback
> - *
> - * The function is used to traverse the devices of the specified
> - * PE and its child PEs.
> - */
> -void *eeh_pe_dev_traverse(struct eeh_pe *root,
> -		eeh_traverse_func fn, void *flag)
> -{
> -	struct eeh_pe *pe;
> -	struct eeh_dev *edev;
> -	void *ret;
> -
> -	if (!root) {
> -		pr_warning("%s: Invalid PE %p\n", __func__, root);
> -		return NULL;
> -	}
> -
> -	eeh_lock();
> -
> -	/* Traverse root PE */
> -	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> -		eeh_pe_for_each_dev(pe, edev) {
> -			ret = fn(edev, flag);
> -			if (ret) {
> -				eeh_unlock();
> -				return ret;
> -			}
> -		}
> -	}
> -
> -	eeh_unlock();
> -
> -	return NULL;
> -}
> -
> -/**
> - * __eeh_pe_get - Check the PE address
> - * @data: EEH PE
> - * @flag: EEH device
> - *
> - * For one particular PE, it can be identified by PE address
> - * or tranditional BDF address. BDF address is composed of
> - * Bus/Device/Function number. The extra data referred by flag
> - * indicates which type of address should be used.
> - */
> -static void *__eeh_pe_get(void *data, void *flag)
> -{
> -	struct eeh_pe *pe = (struct eeh_pe *)data;
> -	struct eeh_dev *edev = (struct eeh_dev *)flag;
> -
> -	/* Unexpected PHB PE */
> -	if (pe->type & EEH_PE_PHB)
> -		return NULL;
> -
> -	/* We prefer PE address */
> -	if (edev->pe_config_addr &&
> -	   (edev->pe_config_addr == pe->addr))
> -		return pe;
> -
> -	/* Try BDF address */
> -	if (edev->pe_config_addr &&
> -	   (edev->config_addr == pe->config_addr))
> -		return pe;
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_get - Search PE based on the given address
> - * @edev: EEH device
> - *
> - * Search the corresponding PE based on the specified address which
> - * is included in the eeh device. The function is used to check if
> - * the associated PE has been created against the PE address. It's
> - * notable that the PE address has 2 format: traditional PE address
> - * which is composed of PCI bus/device/function number, or unified
> - * PE address.
> - */
> -static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
> -{
> -	struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
> -	struct eeh_pe *pe;
> -
> -	pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
> -
> -	return pe;
> -}
> -
> -/**
> - * eeh_pe_get_parent - Retrieve the parent PE
> - * @edev: EEH device
> - *
> - * The whole PEs existing in the system are organized as hierarchy
> - * tree. The function is used to retrieve the parent PE according
> - * to the parent EEH device.
> - */
> -static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
> -{
> -	struct device_node *dn;
> -	struct eeh_dev *parent;
> -
> -	/*
> -	 * It might have the case for the indirect parent
> -	 * EEH device already having associated PE, but
> -	 * the direct parent EEH device doesn't have yet.
> -	 */
> -	dn = edev->dn->parent;
> -	while (dn) {
> -		/* We're poking out of PCI territory */
> -		if (!PCI_DN(dn)) return NULL;
> -
> -		parent = of_node_to_eeh_dev(dn);
> -		/* We're poking out of PCI territory */
> -		if (!parent) return NULL;
> -
> -		if (parent->pe)
> -			return parent->pe;
> -
> -		dn = dn->parent;
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_add_to_parent_pe - Add EEH device to parent PE
> - * @edev: EEH device
> - *
> - * Add EEH device to the parent PE. If the parent PE already
> - * exists, the PE type will be changed to EEH_PE_BUS. Otherwise,
> - * we have to create new PE to hold the EEH device and the new
> - * PE will be linked to its parent PE as well.
> - */
> -int eeh_add_to_parent_pe(struct eeh_dev *edev)
> -{
> -	struct eeh_pe *pe, *parent;
> -
> -	eeh_lock();
> -
> -	/*
> -	 * Search the PE has been existing or not according
> -	 * to the PE address. If that has been existing, the
> -	 * PE should be composed of PCI bus and its subordinate
> -	 * components.
> -	 */
> -	pe = eeh_pe_get(edev);
> -	if (pe && !(pe->type & EEH_PE_INVALID)) {
> -		if (!edev->pe_config_addr) {
> -			eeh_unlock();
> -			pr_err("%s: PE with addr 0x%x already exists\n",
> -				__func__, edev->config_addr);
> -			return -EEXIST;
> -		}
> -
> -		/* Mark the PE as type of PCI bus */
> -		pe->type = EEH_PE_BUS;
> -		edev->pe = pe;
> -
> -		/* Put the edev to PE */
> -		list_add_tail(&edev->list, &pe->edevs);
> -		eeh_unlock();
> -		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;
> -		}
> -		eeh_unlock();
> -		pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> -			edev->dn->full_name, pe->addr, pe->parent->addr);
> -
> -		return 0;
> -	}
> -
> -	/* Create a new EEH PE */
> -	pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
> -	if (!pe) {
> -		eeh_unlock();
> -		pr_err("%s: out of memory!\n", __func__);
> -		return -ENOMEM;
> -	}
> -	pe->addr	= edev->pe_config_addr;
> -	pe->config_addr	= edev->config_addr;
> -
> -	/*
> -	 * Put the new EEH PE into hierarchy tree. If the parent
> -	 * can't be found, the newly created PE will be attached
> -	 * to PHB directly. Otherwise, we have to associate the
> -	 * PE with its parent.
> -	 */
> -	parent = eeh_pe_get_parent(edev);
> -	if (!parent) {
> -		parent = eeh_phb_pe_get(edev->phb);
> -		if (!parent) {
> -			eeh_unlock();
> -			pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
> -				__func__, edev->phb->global_number);
> -			edev->pe = NULL;
> -			kfree(pe);
> -			return -EEXIST;
> -		}
> -	}
> -	pe->parent = parent;
> -
> -	/*
> -	 * Put the newly created PE into the child list and
> -	 * link the EEH device accordingly.
> -	 */
> -	list_add_tail(&pe->child, &parent->child_list);
> -	list_add_tail(&edev->list, &pe->edevs);
> -	edev->pe = pe;
> -	eeh_unlock();
> -	pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> -		edev->dn->full_name, pe->addr, pe->parent->addr);
> -
> -	return 0;
> -}
> -
> -/**
> - * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
> - * @edev: EEH device
> - * @purge_pe: remove PE or not
> - *
> - * 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, int purge_pe)
> -{
> -	struct eeh_pe *pe, *parent, *child;
> -	int cnt;
> -
> -	if (!edev->pe) {
> -		pr_warning("%s: No PE found for EEH device %s\n",
> -			__func__, edev->dn->full_name);
> -		return -EEXIST;
> -	}
> -
> -	eeh_lock();
> -
> -	/* 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 (purge_pe) {
> -			if (list_empty(&pe->edevs) &&
> -			    list_empty(&pe->child_list)) {
> -				list_del(&pe->child);
> -				kfree(pe);
> -			} else {
> -				break;
> -			}
> -		} else {
> -			if (list_empty(&pe->edevs)) {
> -				cnt = 0;
> -				list_for_each_entry(child, &pe->child_list, child) {
> -					if (!(child->type & EEH_PE_INVALID)) {
> -						cnt++;
> -						break;
> -					}
> -				}
> -
> -				if (!cnt)
> -					pe->type |= EEH_PE_INVALID;
> -				else
> -					break;
> -			}
> -		}
> -
> -		pe = parent;
> -	}
> -
> -	eeh_unlock();
> -
> -	return 0;
> -}
> -
> -/**
> - * __eeh_pe_state_mark - Mark the state for the PE
> - * @data: EEH PE
> - * @flag: state
> - *
> - * The function is used to mark the indicated state for the given
> - * PE. Also, the associated PCI devices will be put into IO frozen
> - * state as well.
> - */
> -static void *__eeh_pe_state_mark(void *data, void *flag)
> -{
> -	struct eeh_pe *pe = (struct eeh_pe *)data;
> -	int state = *((int *)flag);
> -	struct eeh_dev *tmp;
> -	struct pci_dev *pdev;
> -
> -	/*
> -	 * Mark the PE with the indicated state. Also,
> -	 * the associated PCI device will be put into
> -	 * I/O frozen state to avoid I/O accesses from
> -	 * the PCI device driver.
> -	 */
> -	pe->state |= state;
> -	eeh_pe_for_each_dev(pe, tmp) {
> -		pdev = eeh_dev_to_pci_dev(tmp);
> -		if (pdev)
> -			pdev->error_state = pci_channel_io_frozen;
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_state_mark - Mark specified state for PE and its associated device
> - * @pe: EEH PE
> - *
> - * EEH error affects the current PE and its child PEs. The function
> - * is used to mark appropriate state for the affected PEs and the
> - * associated devices.
> - */
> -void eeh_pe_state_mark(struct eeh_pe *pe, int state)
> -{
> -	eeh_lock();
> -	eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
> -	eeh_unlock();
> -}
> -
> -/**
> - * __eeh_pe_state_clear - Clear state for the PE
> - * @data: EEH PE
> - * @flag: state
> - *
> - * The function is used to clear the indicated state from the
> - * given PE. Besides, we also clear the check count of the PE
> - * as well.
> - */
> -static void *__eeh_pe_state_clear(void *data, void *flag)
> -{
> -	struct eeh_pe *pe = (struct eeh_pe *)data;
> -	int state = *((int *)flag);
> -
> -	pe->state &= ~state;
> -	pe->check_count = 0;
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_state_clear - Clear state for the PE and its children
> - * @pe: PE
> - * @state: state to be cleared
> - *
> - * When the PE and its children has been recovered from error,
> - * we need clear the error state for that. The function is used
> - * for the purpose.
> - */
> -void eeh_pe_state_clear(struct eeh_pe *pe, int state)
> -{
> -	eeh_lock();
> -	eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
> -	eeh_unlock();
> -}
> -
> -/**
> - * eeh_restore_one_device_bars - Restore the Base Address Registers for one device
> - * @data: EEH device
> - * @flag: Unused
> - *
> - * Loads the PCI configuration space base address registers,
> - * the expansion ROM base address, the latency timer, and etc.
> - * from the saved values in the device node.
> - */
> -static void *eeh_restore_one_device_bars(void *data, void *flag)
> -{
> -	int i;
> -	u32 cmd;
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct device_node *dn = eeh_dev_to_of_node(edev);
> -
> -	for (i = 4; i < 10; i++)
> -		eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
> -	/* 12 == Expansion ROM Address */
> -	eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
> -
> -#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
> -#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
> -
> -	eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
> -		SAVED_BYTE(PCI_CACHE_LINE_SIZE));
> -	eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
> -		SAVED_BYTE(PCI_LATENCY_TIMER));
> -
> -	/* max latency, min grant, interrupt pin and line */
> -	eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
> -
> -	/*
> -	 * Restore PERR & SERR bits, some devices require it,
> -	 * don't touch the other command bits
> -	 */
> -	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
> -	if (edev->config_space[1] & PCI_COMMAND_PARITY)
> -		cmd |= PCI_COMMAND_PARITY;
> -	else
> -		cmd &= ~PCI_COMMAND_PARITY;
> -	if (edev->config_space[1] & PCI_COMMAND_SERR)
> -		cmd |= PCI_COMMAND_SERR;
> -	else
> -		cmd &= ~PCI_COMMAND_SERR;
> -	eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_restore_bars - Restore the PCI config space info
> - * @pe: EEH PE
> - *
> - * This routine performs a recursive walk to the children
> - * of this device as well.
> - */
> -void eeh_pe_restore_bars(struct eeh_pe *pe)
> -{
> -	/*
> -	 * We needn't take the EEH lock since eeh_pe_dev_traverse()
> -	 * will take that.
> -	 */
> -	eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
> -}
> -
> -/**
> - * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
> - * @pe: EEH PE
> - *
> - * Retrieve the PCI bus according to the given PE. Basically,
> - * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the
> - * primary PCI bus will be retrieved. The parent bus will be
> - * returned for BUS PE. However, we don't have associated PCI
> - * bus for DEVICE PE.
> - */
> -struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
> -{
> -	struct pci_bus *bus = NULL;
> -	struct eeh_dev *edev;
> -	struct pci_dev *pdev;
> -
> -	eeh_lock();
> -
> -	if (pe->type & EEH_PE_PHB) {
> -		bus = pe->phb->bus;
> -	} else if (pe->type & EEH_PE_BUS ||
> -		   pe->type & EEH_PE_DEVICE) {
> -		edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
> -		pdev = eeh_dev_to_pci_dev(edev);
> -		if (pdev)
> -			bus = pdev->bus;
> -	}
> -
> -	eeh_unlock();
> -
> -	return bus;
> -}
> diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c
> deleted file mode 100644
> index d377083..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_sysfs.c
> +++ /dev/null
> @@ -1,75 +0,0 @@
> -/*
> - * Sysfs entries for PCI Error Recovery for PAPR-compliant platform.
> - * Copyright IBM Corporation 2007
> - * Copyright Linas Vepstas <linas@austin.ibm.com> 2007
> - *
> - * All rights reserved.
> - *
> - * 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, GOOD TITLE or
> - * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
> - *
> - * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> - */
> -#include <linux/pci.h>
> -#include <linux/stat.h>
> -#include <asm/ppc-pci.h>
> -#include <asm/pci-bridge.h>
> -
> -/**
> - * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic
> - * @_name: name of file in sysfs directory
> - * @_memb: name of member in struct pci_dn to access
> - * @_format: printf format for display
> - *
> - * All of the attributes look very similar, so just
> - * auto-gen a cut-n-paste routine to display them.
> - */
> -#define EEH_SHOW_ATTR(_name,_memb,_format)               \
> -static ssize_t eeh_show_##_name(struct device *dev,      \
> -		struct device_attribute *attr, char *buf)          \
> -{                                                        \
> -	struct pci_dev *pdev = to_pci_dev(dev);               \
> -	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);      \
> -	                                                      \
> -	if (!edev)                                            \
> -		return 0;                                     \
> -	                                                      \
> -	return sprintf(buf, _format "\n", edev->_memb);       \
> -}                                                        \
> -static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
> -
> -EEH_SHOW_ATTR(eeh_mode,            mode,            "0x%x");
> -EEH_SHOW_ATTR(eeh_config_addr,     config_addr,     "0x%x");
> -EEH_SHOW_ATTR(eeh_pe_config_addr,  pe_config_addr,  "0x%x");
> -
> -void eeh_sysfs_add_device(struct pci_dev *pdev)
> -{
> -	int rc=0;
> -
> -	rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
> -	rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
> -	rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> -
> -	if (rc)
> -		printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
> -}
> -
> -void eeh_sysfs_remove_device(struct pci_dev *pdev)
> -{
> -	device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
> -	device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
> -	device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> -}
> -
> diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c
> index c91b22b..efe6137 100644
> --- a/arch/powerpc/platforms/pseries/pci_dlpar.c
> +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
> @@ -64,91 +64,6 @@ pcibios_find_pci_bus(struct device_node *dn)
>   }
>   EXPORT_SYMBOL_GPL(pcibios_find_pci_bus);
>
> -/**
> - * __pcibios_remove_pci_devices - remove all devices under this bus
> - * @bus: the indicated PCI bus
> - * @purge_pe: destroy the PE on removal of PCI devices
> - *
> - * Remove all of the PCI devices under this bus both from the
> - * linux pci device tree, and from the powerpc EEH address cache.
> - * By default, the corresponding PE will be destroied during the
> - * normal PCI hotplug path. For PCI hotplug during EEH recovery,
> - * the corresponding PE won't be destroied and deallocated.
> - */
> -void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe)
> -{
> -	struct pci_dev *dev, *tmp;
> -	struct pci_bus *child_bus;
> -
> -	/* First go down child busses */
> -	list_for_each_entry(child_bus, &bus->children, node)
> -		__pcibios_remove_pci_devices(child_bus, purge_pe);
> -
> -	pr_debug("PCI: Removing devices on bus %04x:%02x\n",
> -		pci_domain_nr(bus),  bus->number);
> -	list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
> -		pr_debug("     * Removing %s...\n", pci_name(dev));
> -		eeh_remove_bus_device(dev, purge_pe);
> -		pci_stop_and_remove_bus_device(dev);
> -	}
> -}
> -
> -/**
> - * pcibios_remove_pci_devices - remove all devices under this bus
> - *
> - * Remove all of the PCI devices under this bus both from the
> - * linux pci device tree, and from the powerpc EEH address cache.
> - */
> -void pcibios_remove_pci_devices(struct pci_bus *bus)
> -{
> -	__pcibios_remove_pci_devices(bus, 1);
> -}
> -EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
> -
> -/**
> - * pcibios_add_pci_devices - adds new pci devices to bus
> - *
> - * This routine will find and fixup new pci devices under
> - * the indicated bus. This routine presumes that there
> - * might already be some devices under this bridge, so
> - * it carefully tries to add only new devices.  (And that
> - * is how this routine differs from other, similar pcibios
> - * routines.)
> - */
> -void pcibios_add_pci_devices(struct pci_bus * bus)
> -{
> -	int slotno, num, mode, pass, max;
> -	struct pci_dev *dev;
> -	struct device_node *dn = pci_bus_to_OF_node(bus);
> -
> -	eeh_add_device_tree_early(dn);
> -
> -	mode = PCI_PROBE_NORMAL;
> -	if (ppc_md.pci_probe_mode)
> -		mode = ppc_md.pci_probe_mode(bus);
> -
> -	if (mode == PCI_PROBE_DEVTREE) {
> -		/* use ofdt-based probe */
> -		of_rescan_bus(dn, bus);
> -	} else if (mode == PCI_PROBE_NORMAL) {
> -		/* use legacy probe */
> -		slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
> -		num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
> -		if (!num)
> -			return;
> -		pcibios_setup_bus_devices(bus);
> -		max = bus->busn_res.start;
> -		for (pass=0; pass < 2; pass++)
> -			list_for_each_entry(dev, &bus->devices, bus_list) {
> -			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
> -			    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
> -				max = pci_scan_bridge(bus, dev, max, pass);
> -		}
> -	}
> -	pcibios_finish_adding_to_bus(bus);
> -}
> -EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
> -
>   struct pci_controller *init_phb_dynamic(struct device_node *dn)
>   {
>   	struct pci_controller *phb;

^ permalink raw reply

* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Alex Williamson @ 2013-06-17  3:13 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc, linux-kernel,
	Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1371422343.21896.143.camel@pasglop>

On Mon, 2013-06-17 at 08:39 +1000, Benjamin Herrenschmidt wrote:
> On Wed, 2013-06-05 at 16:11 +1000, Alexey Kardashevskiy wrote:
> > +long kvm_vm_ioctl_create_spapr_tce_iommu(struct kvm *kvm,
> > +               struct kvm_create_spapr_tce_iommu *args)
> > +{
> > +       struct kvmppc_spapr_tce_table *tt = NULL;
> > +       struct iommu_group *grp;
> > +       struct iommu_table *tbl;
> > +
> > +       /* Find an IOMMU table for the given ID */
> > +       grp = iommu_group_get_by_id(args->iommu_id);
> > +       if (!grp)
> > +               return -ENXIO;
> > +
> > +       tbl = iommu_group_get_iommudata(grp);
> > +       if (!tbl)
> > +               return -ENXIO;
> 
> So Alex Graf pointed out here, there is a security issue here, or are we
> missing something ?
> 
> What prevents a malicious program that has access to /dev/kvm from
> taking over random iommu groups (including host used ones) that way?
> 
> What is the security model of that whole iommu stuff to begin with ?

IOMMU groups themselves don't provide security, they're accessed by
interfaces like VFIO, which provide the security.  Given a brief look, I
agree, this looks like a possible backdoor.  The typical VFIO way to
handle this would be to pass a VFIO file descriptor here to prove that
the process has access to the IOMMU group.  This is how /dev/vfio/vfio
gains the ability to setup an IOMMU domain an do mappings with the
SET_CONTAINER ioctl using a group fd.  Thanks,

Alex

^ permalink raw reply

* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Benjamin Herrenschmidt @ 2013-06-17  3:56 UTC (permalink / raw)
  To: Alex Williamson
  Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc, linux-kernel,
	Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1371438800.22681.38.camel@ul30vt.home>

On Sun, 2013-06-16 at 21:13 -0600, Alex Williamson wrote:

> IOMMU groups themselves don't provide security, they're accessed by
> interfaces like VFIO, which provide the security.  Given a brief look, I
> agree, this looks like a possible backdoor.  The typical VFIO way to
> handle this would be to pass a VFIO file descriptor here to prove that
> the process has access to the IOMMU group.  This is how /dev/vfio/vfio
> gains the ability to setup an IOMMU domain an do mappings with the
> SET_CONTAINER ioctl using a group fd.  Thanks,

How do you envision that in the kernel ? IE. I'm in KVM code, gets that
vfio fd, what do I do with it ?

Basically, KVM needs to know that the user is allowed to use that iommu
group. I don't think we want KVM however to call into VFIO directly
right ?

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH 3/5] powerpc/dts: update MSI bindings doc for MPIC v4.3
From: Lian Minghuan-b31939 @ 2013-06-17  5:07 UTC (permalink / raw)
  To: Scott Wood; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <1371247589.2996.15@snotra>

Hi Soctt,

please see my comments.

On 06/15/2013 06:06 AM, Scott Wood wrote:
> On 06/14/2013 02:15:57 AM, Minghuan Lian wrote:
>> Add compatible "fsl,mpic-msi-v4.3" for MPIC v4.3. MPIC v4.3 contains
>> MSIIR and MSIIR1. MSIIR supports 8 MSI registers and MSIIR1 supports
>> 16 MSI registers, but uses different IBS and SRS shift. When using
>> MSIR1, the interrupt number is not consecutive. It is hard to use
>> 'msi-available-ranges' to describe the ranges of the available
>> interrupt and the ranges are related to the application, rather than
>> the description of the hardware. this patch also removes
>> 'msi-available-ranges' property.
>>
>> Signed-off-by: Minghuan Lian <Minghuan.Lian@freescale.com>
>> ---
>>  .../devicetree/bindings/powerpc/fsl/msi-pic.txt    | 49 
>> ++++++++++------------
>>  1 file changed, 22 insertions(+), 27 deletions(-)
>>
>> diff --git 
>> a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt 
>> b/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt
>> index 5693877..e851e93 100644
>> --- a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt
>> +++ b/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt
>> @@ -1,26 +1,23 @@
>>  * Freescale MSI interrupt controller
>>
>>  Required properties:
>> -- compatible : compatible list, contains 2 entries,
>> +- compatible : compatible list, may contains one or two entries,
>>    first is "fsl,CHIP-msi", where CHIP is the processor(mpc8610, 
>> mpc8572,
>> -  etc.) and the second is "fsl,mpic-msi" or "fsl,ipic-msi" depending on
>> -  the parent type.
>> +  etc.) and the second is "fsl,mpic-msi" or "fsl,ipic-msi" or
>> +  "fsl,mpic-msi-v4.3" depending on the parent type and version. If mpic
>> +  version is 4.3, the number of MSI registers is increased to 16, 
>> MSIIR1 is
>> +  provided to access these 16 registers, compatible "fsl,mpic-msi-v4.3"
>> +  should be used.
>
> Why "one or two"?  What does it look like in the case where there's 
> just one?
>
[Minghuan] The original doc said 'contains 2 entries', but I notcie 
pq3-mpic.dtsi and qoriq-mpic.dtsi have only one entry "fsl,mpic-msi", do 
not have "fsl,CHIP-msi".
for example:
mpc8610_hpcd.dts: compatible = "fsl,mpc8610-msi", "fsl,mpic-msi";
fsl/qoriq-mpic.dtsi:  compatible = "fsl,mpic-msi"

Maybe I should say " For some platforms, "fsl,CHIP-msi' is optional."

>>  - reg : It may contain one or two regions. The first region should 
>> contain
>>    the address and the length of the shared message interrupt 
>> register set.
>> -  The second region should contain the address of aliased MSIIR 
>> register for
>> -  platforms that have such an alias.
>> -
>> -- msi-available-ranges: use <start count> style section to define which
>> -  msi interrupt can be used in the 256 msi interrupts. This property is
>> -  optional, without this, all the 256 MSI interrupts can be used.
>> -  Each available range must begin and end on a multiple of 32 (i.e.
>> -  no splitting an individual MSI register or the associated PIC 
>> interrupt).
>> +  The second region should contain the address of aliased MSIIR or 
>> MSIIR1
>> +  register for platforms that have such an alias, if using MSIIR1, 
>> the second
>> +  region must be added because different MSI group has different 
>> MSIRR1 offset.
>
> Why are you removing msi-available-ranges?  It's not valid for MPIC 
> v4.3, but it's still valid for older MPICs.  It should move to the 
> optional section, though.
[Minghuan] Because I would like to add kernel parameter 'msiregs' 
instead of "msi-available-ranges", for all the MPICs, we will have a 
uniform way to configure
For older MPICs, we can use "msi-available-ranges" and "msiregs", but 
for MPCI4.3 we can only use "msiregs", this may easily lead to confusion.
>>  - interrupts : each one of the interrupts here is one entry per 32 
>> MSIs,
>>    and routed to the host interrupt controller. the interrupts should
>> -  be set as edge sensitive.  If msi-available-ranges is present, only
>> -  the interrupts that correspond to available ranges shall be present.
>> +  be set as edge sensitive.
>>
>>  - interrupt-parent: the phandle for the interrupt controller
>>    that services interrupts for this device. for 83xx cpu, the 
>> interrupts
>> @@ -39,20 +36,18 @@ Optional properties:
>>
>>  Example:
>>      msi@41600 {
>> -        compatible = "fsl,mpc8610-msi", "fsl,mpic-msi";
>> -        reg = <0x41600 0x80>;
>> -        msi-available-ranges = <0 0x100>;
>> -        interrupts = <
>> -            0xe0 0
>> -            0xe1 0
>> -            0xe2 0
>> -            0xe3 0
>> -            0xe4 0
>> -            0xe5 0
>> -            0xe6 0
>> -            0xe7 0>;
>> -        interrupt-parent = <&mpic>;
>> -    };
>> +    compatible = "fsl,mpic-msi";
>> +    reg = <0x41600 0x200 0x44140 4>;
>
> Why 0x200?
>
[Minghuan] The offsets of the MSIA registers are from 0x41600 to 
0x417ff, and the size is 0x200.
offset 0x41600-0x4170 are MSIIRA1-7.
0x41720 is MSISRA,
0x41750 is MSIIR.
The others are reserved.

-Scott

^ permalink raw reply

* Re: [PATCH 4/5] powerpc/dts: remove msi-available-ranges property
From: Lian Minghuan-b31939 @ 2013-06-17  5:15 UTC (permalink / raw)
  To: Scott Wood; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <1371247853.2996.17@snotra>

On 06/15/2013 06:10 AM, Scott Wood wrote:
> On 06/14/2013 02:15:58 AM, Minghuan Lian wrote:
>> For MPIC v4.3 MSIIR supports 8 MSI registers and MSIIR1 supports
>> 16 MSI registers, but uses different IBS and SRS shift. For the
>> first register, when using MSIIR we will get the irqs 0x0 0x1 0x2
>> ...0x1f, but when using MSIIR1, the irqs are 0x0 0x10 0x20 ... 0x1f0
>> It is hard to describe the available irqs using property
>> 'msi-available-ranges'. The patch removes this property.
>
> Only remove it from mpic 4.3.  And since you introduced 
> qoriq-mpic4.3.dtsi earlier in the patchset, why didn't you just avoid 
> adding it then?
>
[Minghuan] If adding it in qoriq-mpic4.3, and the 3-5 patches are not 
accepted, mpic4.3 can also work.
> -Scott

^ permalink raw reply

* Re: [PATCH 5/5] powerpc/fsl_msi: add 'msiregs' kernel parameter
From: Lian Minghuan-b31939 @ 2013-06-17  5:36 UTC (permalink / raw)
  To: Scott Wood; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <1371248034.2996.18@snotra>

Hi Scott,

please see my comments inline.

On 06/15/2013 06:13 AM, Scott Wood wrote:
> On 06/14/2013 02:15:59 AM, Minghuan Lian wrote:
>> 1. Only MSIIR1 can index 16 MSI registers, but when using MSIIR1
>> the IRQs of a register are not continuous. for example, the first
>> register irq values are 0x0, 0x10, 0x20, 0x30 ... 0x1f0. So it
>> is hard to use 'msi-available-ranges' property to indicate the
>> available ranges and 'msi-available-ranges' property has been
>> removed from dts node, so this patch removes the related code.
>>
>> 2. Add 'msiregs' kernel parameter instead of 'msi-available-ranges'
>> functionality.
>
> The reason we used a device tree property was because this is for 
> virtualization and AMP scenarios where this instance of Linux does not 
> own all of the MSI registers.
>
> I don't see any reasonable way to partition an MPIC v4.3 MSI group -- 
> but there are more groups, so it's not that bad.  What's the use case 
> for this patch?
>
[Minghuan] I do not known any case about this patch. I add 'msiregs' 
just for achieving "msi-available-ranges" functionality. I do not want 
to remove partition functionality when updating to mpic4.3, although I 
do not see virtualization and AMP cases on T4(KVM does not need this 
functionality).
If you hope to keep 'msi-available-ranges' property for older mpic, can 
I add 'msi-available-regs' property for mpic4.3.
Using 'msi-available-regs' to implement partition is similar to 
'msi-available-ranges'. When considering the consistency, 
'msi-available-regs' is better than msiregs.
> -Scott

^ permalink raw reply

* RE: [PATCH 1/2] powerpc: add Book E support to 64-bit hibernation
From: Wang Dongsheng-B40534 @ 2013-06-17  5:54 UTC (permalink / raw)
  To: Wood Scott-B07421
  Cc: johannes@sipsolutions.net, anton@enomsg.org,
	linuxppc-dev@lists.ozlabs.org
In-Reply-To: <1371142282.2028.10@snotra>



> -----Original Message-----
> From: Wood Scott-B07421
> Sent: Friday, June 14, 2013 12:51 AM
> To: Wang Dongsheng-B40534
> Cc: Wood Scott-B07421; benh@kernel.crashing.org;
> johannes@sipsolutions.net; anton@enomsg.org; galak@kernel.crashing.org;
> linuxppc-dev@lists.ozlabs.org
> Subject: Re: [PATCH 1/2] powerpc: add Book E support to 64-bit
> hibernation
>=20
> On 06/13/2013 04:55:43 AM, Wang Dongsheng-B40534 wrote:
> > > > +#else
> > > > +	/* Save SPRGs */
> > > > +	RESTORE_SPRG(0)
> > > > +	RESTORE_SPRG(1)
> > > > +	RESTORE_SPRG(2)
> > > > +	RESTORE_SPRG(3)
> > > > +	RESTORE_SPRG(4)
> > > > +	RESTORE_SPRG(5)
> > > > +	RESTORE_SPRG(6)
> > > > +	RESTORE_SPRG(7)
> > >
> > > Why do we need this on book3e and not on book3s?
> > >
> > Book3e: SPRG1 used save paca, SPRG2 be defined
> > SPRN_SPRG_TLB_EXFRAME,...
> > I think those register should be save, even now some SPRG register not
> > be use.
>=20
> Are those expected/allowed to change as a result of the restore?
>=20
Those registers are used by software, some allowed to change.
Exception handling is used in some registers, see exception-64e.h
These registers can be modified and saved.

> -Scott

^ permalink raw reply

* Re: Regression in RCU subsystem in latest mainline kernel
From: Michael Ellerman @ 2013-06-17  7:42 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Rojhalat Ibrahim, paulmck, linuxppc-dev, linux-kernel,
	Steven Rostedt
In-Reply-To: <1371261741.21896.20.camel@pasglop>

On Sat, Jun 15, 2013 at 12:02:21PM +1000, Benjamin Herrenschmidt wrote:
> On Fri, 2013-06-14 at 17:06 -0400, Steven Rostedt wrote:
> > I was pretty much able to reproduce this on my PA Semi PPC box. Funny
> > thing is, when I type on the console, it makes progress. Anyway, it
> > seems that powerpc has an issue with irq_work(). I'll try to get some
> > time either tonight or next week to figure it out.
> 
> Does this help ?
> 
> diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
> index 5cbcf4d..ea185e0 100644
> --- a/arch/powerpc/kernel/irq.c
> +++ b/arch/powerpc/kernel/irq.c
> @@ -162,7 +162,7 @@ notrace unsigned int __check_irq_replay(void)
>  	 * in case we also had a rollover while hard disabled
>  	 */
>  	local_paca->irq_happened &= ~PACA_IRQ_DEC;
> -	if (decrementer_check_overflow())
> +	if ((happened & PACA_IRQ_DEC) || decrementer_check_overflow())
>  		return 0x900;
>  
>  	/* Finally check if an external interrupt happened */
> 

This seems to help, but doesn't elminate the RCU stall warnings I am
seeing. I now see them less often, but not never.

Stack trace is something like:

  INFO: rcu_sched detected stalls on CPUs/tasks: { 32} (detected by 12, t=21372 jiffies, g=18446744073709551503, c=18446744073709551502, q=1018)
  Task dump for CPU 32:
  power8-events   R  running task     4960  2009   1988 0x00000004
  Call Trace:
  [c000000fb0e3f910] [c000000fb0e3f9d0] 0xc000000fb0e3f9d0 (unreliable)
  
  [c000000fb0e3edc0] [c0000000000b2894] .__run_hrtimer+0xa4/0x2a0
  [c000000fb0e3ee70] [c0000000000b36d8] .hrtimer_interrupt+0x148/0x320
  [c000000fb0e3ef80] [c00000000001c754] .timer_interrupt+0x134/0x320
  [c000000fb0e3f040] [c00000000000a4f4] restore_check_irq_replay+0x68/0xa8
  --- Exception: 901 at .arch_local_irq_restore+0x24/0x90
      LR = .__do_softirq+0x100/0x3a0
  [c000000fb0e3f330] [c0000000000c4784] .vtime_account_irq_enter+0x34/0x70 (unreliable)
  [c000000fb0e3f3a0] [c000000000089680] .__do_softirq+0x100/0x3a0
  [c000000fb0e3f4c0] [c000000000089b38] .irq_exit+0xc8/0x110
  [c000000fb0e3f540] [c00000000001c788] .timer_interrupt+0x168/0x320
  [c000000fb0e3f600] [c0000000000025cc] decrementer_common+0x14c/0x180
  --- Exception: 901 at .arch_local_irq_restore+0x74/0x90
      LR = .arch_local_irq_restore+0x74/0x90
  [c000000fb0e3f8f0] [c000000fb0e3f970] 0xc000000fb0e3f970 (unreliable)
  [c000000fb0e3f960] [c0000000000e4ae0] .smp_call_function_single+0x1d0/0x1e0
  [c000000fb0e3fa10] [c000000000147aa4] .task_function_call+0x54/0x70
  [c000000fb0e3fab0] [c000000000147bc4] .perf_event_enable+0x104/0x1c0
  [c000000fb0e3fb60] [c000000000146800] .perf_event_for_each_child+0x60/0x110
  [c000000fb0e3fbf0] [c00000000014a528] .perf_ioctl+0x108/0x3f0
  [c000000fb0e3fca0] [c0000000001d7138] .do_vfs_ioctl+0xb8/0x730
  [c000000fb0e3fd80] [c0000000001d780c] .SyS_ioctl+0x5c/0xb0
  [c000000fb0e3fe30] [c000000000009d54] syscall_exit+0x0/0x98


cheers

^ permalink raw reply

* Re: [PATCH 1/4] KVM: PPC: Add support for multiple-TCE hcalls
From: Alexey Kardashevskiy @ 2013-06-17  7:55 UTC (permalink / raw)
  To: Alexander Graf
  Cc: kvm, linux-kernel, kvm-ppc, Paul Mackerras, linuxppc-dev,
	David Gibson
In-Reply-To: <0A6C24DE-1422-4ABA-A516-2B864F4714F8@suse.de>

On 06/17/2013 08:06 AM, Alexander Graf wrote:
> 
> On 05.06.2013, at 08:11, Alexey Kardashevskiy wrote:
> 
>> This adds real mode handlers for the H_PUT_TCE_INDIRECT and
>> H_STUFF_TCE hypercalls for QEMU emulated devices such as IBMVIO
>> devices or emulated PCI.  These calls allow adding multiple entries
>> (up to 512) into the TCE table in one call which saves time on
>> transition to/from real mode.
>>
>> This adds a tce_tmp cache to kvm_vcpu_arch to save valid TCEs
>> (copied from user and verified) before writing the whole list into
>> the TCE table. This cache will be utilized more in the upcoming
>> VFIO/IOMMU support to continue TCE list processing in the virtual
>> mode in the case if the real mode handler failed for some reason.
>>
>> This adds a guest physical to host real address converter
>> and calls the existing H_PUT_TCE handler. The converting function
>> is going to be fully utilized by upcoming VFIO supporting patches.
>>
>> This also implements the KVM_CAP_PPC_MULTITCE capability,
>> so in order to support the functionality of this patch, QEMU
>> needs to query for this capability and set the "hcall-multi-tce"
>> hypertas property only if the capability is present, otherwise
>> there will be serious performance degradation.
>>
>> Cc: David Gibson <david@gibson.dropbear.id.au>
>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>> Signed-off-by: Paul Mackerras <paulus@samba.org>
> 
> Only a few minor nits. Ben already commented on implementation details.
> 
>>
>> ---
>> Changelog:
>> 2013/06/05:
>> * fixed mistype about IBMVIO in the commit message
>> * updated doc and moved it to another section
>> * changed capability number
>>
>> 2013/05/21:
>> * added kvm_vcpu_arch::tce_tmp
>> * removed cleanup if put_indirect failed, instead we do not even start
>> writing to TCE table if we cannot get TCEs from the user and they are
>> invalid
>> * kvmppc_emulated_h_put_tce is split to kvmppc_emulated_put_tce
>> and kvmppc_emulated_validate_tce (for the previous item)
>> * fixed bug with failthrough for H_IPI
>> * removed all get_user() from real mode handlers
>> * kvmppc_lookup_pte() added (instead of making lookup_linux_pte public)
>> ---
>> Documentation/virtual/kvm/api.txt       |   17 ++
>> arch/powerpc/include/asm/kvm_host.h     |    2 +
>> arch/powerpc/include/asm/kvm_ppc.h      |   16 +-
>> arch/powerpc/kvm/book3s_64_vio.c        |  118 ++++++++++++++
>> arch/powerpc/kvm/book3s_64_vio_hv.c     |  266 +++++++++++++++++++++++++++----
>> arch/powerpc/kvm/book3s_hv.c            |   39 +++++
>> arch/powerpc/kvm/book3s_hv_rmhandlers.S |    6 +
>> arch/powerpc/kvm/book3s_pr_papr.c       |   37 ++++-
>> arch/powerpc/kvm/powerpc.c              |    3 +
>> include/uapi/linux/kvm.h                |    1 +
>> 10 files changed, 473 insertions(+), 32 deletions(-)
>>
>> diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
>> index 5f91eda..6c082ff 100644
>> --- a/Documentation/virtual/kvm/api.txt
>> +++ b/Documentation/virtual/kvm/api.txt
>> @@ -2362,6 +2362,23 @@ calls by the guest for that service will be passed to userspace to be
>> handled.
>>
>>
>> +4.83 KVM_CAP_PPC_MULTITCE
>> +
>> +Capability: KVM_CAP_PPC_MULTITCE
>> +Architectures: ppc
>> +Type: vm
>> +
>> +This capability tells the guest that multiple TCE entry add/remove hypercalls
>> +handling is supported by the kernel. This significanly accelerates DMA
>> +operations for PPC KVM guests.
>> +
>> +Unlike other capabilities in this section, this one does not have an ioctl.
>> +Instead, when the capability is present, the H_PUT_TCE_INDIRECT and
>> +H_STUFF_TCE hypercalls are to be handled in the host kernel and not passed to
>> +the guest. Othwerwise it might be better for the guest to continue using H_PUT_TCE
>> +hypercall (if KVM_CAP_SPAPR_TCE or KVM_CAP_SPAPR_TCE_IOMMU are present).
> 

> While this describes perfectly well what the consequences are of the
> patches, it does not describe properly what the CAP actually expresses.
> The CAP only says "this kernel is able to handle H_PUT_TCE_INDIRECT and
> H_STUFF_TCE hypercalls directly". All other consequences are nice to
> document, but the semantics of the CAP are missing.


? It expresses ability to handle 2 hcalls. What is missing?


> We also usually try to keep KVM behavior unchanged with regards to older
> versions until a CAP is enabled. In this case I don't think it matters
> all that much, so I'm fine with declaring it as enabled by default.
> Please document that this is a change in behavior versus older KVM
> versions though.


Ok!


>> +
>> +
>> 5. The kvm_run structure
>> ------------------------
>>
>> diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
>> index af326cd..85d8f26 100644
>> --- a/arch/powerpc/include/asm/kvm_host.h
>> +++ b/arch/powerpc/include/asm/kvm_host.h
>> @@ -609,6 +609,8 @@ struct kvm_vcpu_arch {
>> 	spinlock_t tbacct_lock;
>> 	u64 busy_stolen;
>> 	u64 busy_preempt;
>> +
>> +	unsigned long *tce_tmp;    /* TCE cache for TCE_PUT_INDIRECT hall */
>> #endif
>> };
> 
> [...]
>>
>>
> 
> [...]
> 
>> diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
>> index 550f592..a39039a 100644
>> --- a/arch/powerpc/kvm/book3s_hv.c
>> +++ b/arch/powerpc/kvm/book3s_hv.c
>> @@ -568,6 +568,30 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
>> 			ret = kvmppc_xics_hcall(vcpu, req);
>> 			break;
>> 		} /* fallthrough */
> 
> The fallthrough comment isn't accurate anymore.
> 
>> +		return RESUME_HOST;
>> +	case H_PUT_TCE:
>> +		ret = kvmppc_virtmode_h_put_tce(vcpu, kvmppc_get_gpr(vcpu, 4),
>> +						kvmppc_get_gpr(vcpu, 5),
>> +						kvmppc_get_gpr(vcpu, 6));
>> +		if (ret == H_TOO_HARD)
>> +			return RESUME_HOST;
>> +		break;
>> +	case H_PUT_TCE_INDIRECT:
>> +		ret = kvmppc_virtmode_h_put_tce_indirect(vcpu, kvmppc_get_gpr(vcpu, 4),
>> +						kvmppc_get_gpr(vcpu, 5),
>> +						kvmppc_get_gpr(vcpu, 6),
>> +						kvmppc_get_gpr(vcpu, 7));
>> +		if (ret == H_TOO_HARD)
>> +			return RESUME_HOST;
>> +		break;
>> +	case H_STUFF_TCE:
>> +		ret = kvmppc_virtmode_h_stuff_tce(vcpu, kvmppc_get_gpr(vcpu, 4),
>> +						kvmppc_get_gpr(vcpu, 5),
>> +						kvmppc_get_gpr(vcpu, 6),
>> +						kvmppc_get_gpr(vcpu, 7));
>> +		if (ret == H_TOO_HARD)
>> +			return RESUME_HOST;
>> +		break;
>> 	default:
>> 		return RESUME_HOST;
>> 	}
>> @@ -958,6 +982,20 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
>> 	vcpu->arch.cpu_type = KVM_CPU_3S_64;
>> 	kvmppc_sanity_check(vcpu);
>>
>> +	/*
>> +	 * As we want to minimize the chance of having H_PUT_TCE_INDIRECT
>> +	 * half executed, we first read TCEs from the user, check them and
>> +	 * return error if something went wrong and only then put TCEs into
>> +	 * the TCE table.
>> +	 *
>> +	 * tce_tmp is a cache for TCEs to avoid stack allocation or
>> +	 * kmalloc as the whole TCE list can take up to 512 items 8 bytes
>> +	 * each (4096 bytes).
>> +	 */
>> +	vcpu->arch.tce_tmp = kmalloc(4096, GFP_KERNEL);
>> +	if (!vcpu->arch.tce_tmp)
>> +		goto free_vcpu;
>> +
>> 	return vcpu;
>>
>> free_vcpu:
>> @@ -980,6 +1018,7 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
>> 	unpin_vpa(vcpu->kvm, &vcpu->arch.slb_shadow);
>> 	unpin_vpa(vcpu->kvm, &vcpu->arch.vpa);
>> 	spin_unlock(&vcpu->arch.vpa_update_lock);
>> +	kfree(vcpu->arch.tce_tmp);
>> 	kvm_vcpu_uninit(vcpu);
>> 	kmem_cache_free(kvm_vcpu_cache, vcpu);
>> }
>> diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
>> index b02f91e..d35554e 100644
>> --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
>> +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
>> @@ -1490,6 +1490,12 @@ hcall_real_table:
>> 	.long	0		/* 0x11c */
>> 	.long	0		/* 0x120 */
>> 	.long	.kvmppc_h_bulk_remove - hcall_real_table
>> +	.long	0		/* 0x128 */
>> +	.long	0		/* 0x12c */
>> +	.long	0		/* 0x130 */
>> +	.long	0		/* 0x134 */
>> +	.long	.kvmppc_h_stuff_tce - hcall_real_table
>> +	.long	.kvmppc_h_put_tce_indirect - hcall_real_table
>> hcall_real_table_end:
>>
>> ignore_hdec:
>> diff --git a/arch/powerpc/kvm/book3s_pr_papr.c b/arch/powerpc/kvm/book3s_pr_papr.c
>> index da0e0bc..91d4b45 100644
>> --- a/arch/powerpc/kvm/book3s_pr_papr.c
>> +++ b/arch/powerpc/kvm/book3s_pr_papr.c
>> @@ -220,7 +220,38 @@ static int kvmppc_h_pr_put_tce(struct kvm_vcpu *vcpu)
>> 	unsigned long tce = kvmppc_get_gpr(vcpu, 6);
>> 	long rc;
>>
>> -	rc = kvmppc_h_put_tce(vcpu, liobn, ioba, tce);
>> +	rc = kvmppc_virtmode_h_put_tce(vcpu, liobn, ioba, tce);
>> +	if (rc == H_TOO_HARD)
>> +		return EMULATE_FAIL;
>> +	kvmppc_set_gpr(vcpu, 3, rc);
>> +	return EMULATE_DONE;
>> +}
>> +
>> +static int kvmppc_h_pr_put_tce_indirect(struct kvm_vcpu *vcpu)
>> +{
>> +	unsigned long liobn = kvmppc_get_gpr(vcpu, 4);
>> +	unsigned long ioba = kvmppc_get_gpr(vcpu, 5);
>> +	unsigned long tce = kvmppc_get_gpr(vcpu, 6);
>> +	unsigned long npages = kvmppc_get_gpr(vcpu, 7);
>> +	long rc;
>> +
>> +	rc = kvmppc_virtmode_h_put_tce_indirect(vcpu, liobn, ioba,
>> +			tce, npages);
>> +	if (rc == H_TOO_HARD)
>> +		return EMULATE_FAIL;
>> +	kvmppc_set_gpr(vcpu, 3, rc);
>> +	return EMULATE_DONE;
>> +}
>> +
>> +static int kvmppc_h_pr_stuff_tce(struct kvm_vcpu *vcpu)
>> +{
>> +	unsigned long liobn = kvmppc_get_gpr(vcpu, 4);
>> +	unsigned long ioba = kvmppc_get_gpr(vcpu, 5);
>> +	unsigned long tce_value = kvmppc_get_gpr(vcpu, 6);
>> +	unsigned long npages = kvmppc_get_gpr(vcpu, 7);
>> +	long rc;
>> +
>> +	rc = kvmppc_virtmode_h_stuff_tce(vcpu, liobn, ioba, tce_value, npages);
>> 	if (rc == H_TOO_HARD)
>> 		return EMULATE_FAIL;
>> 	kvmppc_set_gpr(vcpu, 3, rc);
>> @@ -247,6 +278,10 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd)
>> 		return kvmppc_h_pr_bulk_remove(vcpu);
>> 	case H_PUT_TCE:
>> 		return kvmppc_h_pr_put_tce(vcpu);
>> +	case H_PUT_TCE_INDIRECT:
>> +		return kvmppc_h_pr_put_tce_indirect(vcpu);
>> +	case H_STUFF_TCE:
>> +		return kvmppc_h_pr_stuff_tce(vcpu);
>> 	case H_CEDE:
>> 		vcpu->arch.shared->msr |= MSR_EE;
>> 		kvm_vcpu_block(vcpu);
>> diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
>> index 6316ee3..8465c2a 100644
>> --- a/arch/powerpc/kvm/powerpc.c
>> +++ b/arch/powerpc/kvm/powerpc.c
>> @@ -395,6 +395,9 @@ int kvm_dev_ioctl_check_extension(long ext)
>> 		r = 1;
>> 		break;
>> #endif
>> +	case KVM_CAP_SPAPR_MULTITCE:
>> +		r = 1;
> 
> This should only be true for book3s.


We had this discussion with v2.

David:
===
So, in the case of MULTITCE, that's not quite right.  PR KVM can
emulate a PAPR system on a BookE machine, and there's no reason not to
allow TCE acceleration as well.  We can't make it dependent on PAPR
mode being selected, because that's enabled per-vcpu, whereas these
capabilities are queried on the VM before the vcpus are created.
===

Wrong?


-- 
Alexey

^ permalink raw reply

* Re: [PATCH 1/4] KVM: PPC: Add support for multiple-TCE hcalls
From: Alexander Graf @ 2013-06-17  8:02 UTC (permalink / raw)
  To: Alexey Kardashevskiy
  Cc: kvm, linux-kernel, kvm-ppc, Paul Mackerras, linuxppc-dev,
	David Gibson
In-Reply-To: <51BEC0FE.4020805@ozlabs.ru>


On 17.06.2013, at 09:55, Alexey Kardashevskiy wrote:

> On 06/17/2013 08:06 AM, Alexander Graf wrote:
>>=20
>> On 05.06.2013, at 08:11, Alexey Kardashevskiy wrote:
>>=20
>>> This adds real mode handlers for the H_PUT_TCE_INDIRECT and
>>> H_STUFF_TCE hypercalls for QEMU emulated devices such as IBMVIO
>>> devices or emulated PCI.  These calls allow adding multiple entries
>>> (up to 512) into the TCE table in one call which saves time on
>>> transition to/from real mode.
>>>=20
>>> This adds a tce_tmp cache to kvm_vcpu_arch to save valid TCEs
>>> (copied from user and verified) before writing the whole list into
>>> the TCE table. This cache will be utilized more in the upcoming
>>> VFIO/IOMMU support to continue TCE list processing in the virtual
>>> mode in the case if the real mode handler failed for some reason.
>>>=20
>>> This adds a guest physical to host real address converter
>>> and calls the existing H_PUT_TCE handler. The converting function
>>> is going to be fully utilized by upcoming VFIO supporting patches.
>>>=20
>>> This also implements the KVM_CAP_PPC_MULTITCE capability,
>>> so in order to support the functionality of this patch, QEMU
>>> needs to query for this capability and set the "hcall-multi-tce"
>>> hypertas property only if the capability is present, otherwise
>>> there will be serious performance degradation.
>>>=20
>>> Cc: David Gibson <david@gibson.dropbear.id.au>
>>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>>> Signed-off-by: Paul Mackerras <paulus@samba.org>
>>=20
>> Only a few minor nits. Ben already commented on implementation =
details.
>>=20
>>>=20
>>> ---
>>> Changelog:
>>> 2013/06/05:
>>> * fixed mistype about IBMVIO in the commit message
>>> * updated doc and moved it to another section
>>> * changed capability number
>>>=20
>>> 2013/05/21:
>>> * added kvm_vcpu_arch::tce_tmp
>>> * removed cleanup if put_indirect failed, instead we do not even =
start
>>> writing to TCE table if we cannot get TCEs from the user and they =
are
>>> invalid
>>> * kvmppc_emulated_h_put_tce is split to kvmppc_emulated_put_tce
>>> and kvmppc_emulated_validate_tce (for the previous item)
>>> * fixed bug with failthrough for H_IPI
>>> * removed all get_user() from real mode handlers
>>> * kvmppc_lookup_pte() added (instead of making lookup_linux_pte =
public)
>>> ---
>>> Documentation/virtual/kvm/api.txt       |   17 ++
>>> arch/powerpc/include/asm/kvm_host.h     |    2 +
>>> arch/powerpc/include/asm/kvm_ppc.h      |   16 +-
>>> arch/powerpc/kvm/book3s_64_vio.c        |  118 ++++++++++++++
>>> arch/powerpc/kvm/book3s_64_vio_hv.c     |  266 =
+++++++++++++++++++++++++++----
>>> arch/powerpc/kvm/book3s_hv.c            |   39 +++++
>>> arch/powerpc/kvm/book3s_hv_rmhandlers.S |    6 +
>>> arch/powerpc/kvm/book3s_pr_papr.c       |   37 ++++-
>>> arch/powerpc/kvm/powerpc.c              |    3 +
>>> include/uapi/linux/kvm.h                |    1 +
>>> 10 files changed, 473 insertions(+), 32 deletions(-)
>>>=20
>>> diff --git a/Documentation/virtual/kvm/api.txt =
b/Documentation/virtual/kvm/api.txt
>>> index 5f91eda..6c082ff 100644
>>> --- a/Documentation/virtual/kvm/api.txt
>>> +++ b/Documentation/virtual/kvm/api.txt
>>> @@ -2362,6 +2362,23 @@ calls by the guest for that service will be =
passed to userspace to be
>>> handled.
>>>=20
>>>=20
>>> +4.83 KVM_CAP_PPC_MULTITCE
>>> +
>>> +Capability: KVM_CAP_PPC_MULTITCE
>>> +Architectures: ppc
>>> +Type: vm
>>> +
>>> +This capability tells the guest that multiple TCE entry add/remove =
hypercalls
>>> +handling is supported by the kernel. This significanly accelerates =
DMA
>>> +operations for PPC KVM guests.
>>> +
>>> +Unlike other capabilities in this section, this one does not have =
an ioctl.
>>> +Instead, when the capability is present, the H_PUT_TCE_INDIRECT and
>>> +H_STUFF_TCE hypercalls are to be handled in the host kernel and not =
passed to
>>> +the guest. Othwerwise it might be better for the guest to continue =
using H_PUT_TCE
>>> +hypercall (if KVM_CAP_SPAPR_TCE or KVM_CAP_SPAPR_TCE_IOMMU are =
present).
>>=20
>=20
>> While this describes perfectly well what the consequences are of the
>> patches, it does not describe properly what the CAP actually =
expresses.
>> The CAP only says "this kernel is able to handle H_PUT_TCE_INDIRECT =
and
>> H_STUFF_TCE hypercalls directly". All other consequences are nice to
>> document, but the semantics of the CAP are missing.
>=20
>=20
> ? It expresses ability to handle 2 hcalls. What is missing?

You don't describe the kvm <-> qemu interface. You describe some =
decisions qemu can take from this cap.

>=20
>=20
>> We also usually try to keep KVM behavior unchanged with regards to =
older
>> versions until a CAP is enabled. In this case I don't think it =
matters
>> all that much, so I'm fine with declaring it as enabled by default.
>> Please document that this is a change in behavior versus older KVM
>> versions though.
>=20
>=20
> Ok!
>=20
>=20
>>> +
>>> +
>>> 5. The kvm_run structure
>>> ------------------------
>>>=20
>>> diff --git a/arch/powerpc/include/asm/kvm_host.h =
b/arch/powerpc/include/asm/kvm_host.h
>>> index af326cd..85d8f26 100644
>>> --- a/arch/powerpc/include/asm/kvm_host.h
>>> +++ b/arch/powerpc/include/asm/kvm_host.h
>>> @@ -609,6 +609,8 @@ struct kvm_vcpu_arch {
>>> 	spinlock_t tbacct_lock;
>>> 	u64 busy_stolen;
>>> 	u64 busy_preempt;
>>> +
>>> +	unsigned long *tce_tmp;    /* TCE cache for TCE_PUT_INDIRECT =
hall */
>>> #endif
>>> };
>>=20
>> [...]
>>>=20
>>>=20
>>=20
>> [...]
>>=20
>>> diff --git a/arch/powerpc/kvm/book3s_hv.c =
b/arch/powerpc/kvm/book3s_hv.c
>>> index 550f592..a39039a 100644
>>> --- a/arch/powerpc/kvm/book3s_hv.c
>>> +++ b/arch/powerpc/kvm/book3s_hv.c
>>> @@ -568,6 +568,30 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu =
*vcpu)
>>> 			ret =3D kvmppc_xics_hcall(vcpu, req);
>>> 			break;
>>> 		} /* fallthrough */
>>=20
>> The fallthrough comment isn't accurate anymore.
>>=20
>>> +		return RESUME_HOST;
>>> +	case H_PUT_TCE:
>>> +		ret =3D kvmppc_virtmode_h_put_tce(vcpu, =
kvmppc_get_gpr(vcpu, 4),
>>> +						kvmppc_get_gpr(vcpu, 5),
>>> +						kvmppc_get_gpr(vcpu, =
6));
>>> +		if (ret =3D=3D H_TOO_HARD)
>>> +			return RESUME_HOST;
>>> +		break;
>>> +	case H_PUT_TCE_INDIRECT:
>>> +		ret =3D kvmppc_virtmode_h_put_tce_indirect(vcpu, =
kvmppc_get_gpr(vcpu, 4),
>>> +						kvmppc_get_gpr(vcpu, 5),
>>> +						kvmppc_get_gpr(vcpu, 6),
>>> +						kvmppc_get_gpr(vcpu, =
7));
>>> +		if (ret =3D=3D H_TOO_HARD)
>>> +			return RESUME_HOST;
>>> +		break;
>>> +	case H_STUFF_TCE:
>>> +		ret =3D kvmppc_virtmode_h_stuff_tce(vcpu, =
kvmppc_get_gpr(vcpu, 4),
>>> +						kvmppc_get_gpr(vcpu, 5),
>>> +						kvmppc_get_gpr(vcpu, 6),
>>> +						kvmppc_get_gpr(vcpu, =
7));
>>> +		if (ret =3D=3D H_TOO_HARD)
>>> +			return RESUME_HOST;
>>> +		break;
>>> 	default:
>>> 		return RESUME_HOST;
>>> 	}
>>> @@ -958,6 +982,20 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct =
kvm *kvm, unsigned int id)
>>> 	vcpu->arch.cpu_type =3D KVM_CPU_3S_64;
>>> 	kvmppc_sanity_check(vcpu);
>>>=20
>>> +	/*
>>> +	 * As we want to minimize the chance of having =
H_PUT_TCE_INDIRECT
>>> +	 * half executed, we first read TCEs from the user, check them =
and
>>> +	 * return error if something went wrong and only then put TCEs =
into
>>> +	 * the TCE table.
>>> +	 *
>>> +	 * tce_tmp is a cache for TCEs to avoid stack allocation or
>>> +	 * kmalloc as the whole TCE list can take up to 512 items 8 =
bytes
>>> +	 * each (4096 bytes).
>>> +	 */
>>> +	vcpu->arch.tce_tmp =3D kmalloc(4096, GFP_KERNEL);
>>> +	if (!vcpu->arch.tce_tmp)
>>> +		goto free_vcpu;
>>> +
>>> 	return vcpu;
>>>=20
>>> free_vcpu:
>>> @@ -980,6 +1018,7 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu =
*vcpu)
>>> 	unpin_vpa(vcpu->kvm, &vcpu->arch.slb_shadow);
>>> 	unpin_vpa(vcpu->kvm, &vcpu->arch.vpa);
>>> 	spin_unlock(&vcpu->arch.vpa_update_lock);
>>> +	kfree(vcpu->arch.tce_tmp);
>>> 	kvm_vcpu_uninit(vcpu);
>>> 	kmem_cache_free(kvm_vcpu_cache, vcpu);
>>> }
>>> diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S =
b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
>>> index b02f91e..d35554e 100644
>>> --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
>>> +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
>>> @@ -1490,6 +1490,12 @@ hcall_real_table:
>>> 	.long	0		/* 0x11c */
>>> 	.long	0		/* 0x120 */
>>> 	.long	.kvmppc_h_bulk_remove - hcall_real_table
>>> +	.long	0		/* 0x128 */
>>> +	.long	0		/* 0x12c */
>>> +	.long	0		/* 0x130 */
>>> +	.long	0		/* 0x134 */
>>> +	.long	.kvmppc_h_stuff_tce - hcall_real_table
>>> +	.long	.kvmppc_h_put_tce_indirect - hcall_real_table
>>> hcall_real_table_end:
>>>=20
>>> ignore_hdec:
>>> diff --git a/arch/powerpc/kvm/book3s_pr_papr.c =
b/arch/powerpc/kvm/book3s_pr_papr.c
>>> index da0e0bc..91d4b45 100644
>>> --- a/arch/powerpc/kvm/book3s_pr_papr.c
>>> +++ b/arch/powerpc/kvm/book3s_pr_papr.c
>>> @@ -220,7 +220,38 @@ static int kvmppc_h_pr_put_tce(struct kvm_vcpu =
*vcpu)
>>> 	unsigned long tce =3D kvmppc_get_gpr(vcpu, 6);
>>> 	long rc;
>>>=20
>>> -	rc =3D kvmppc_h_put_tce(vcpu, liobn, ioba, tce);
>>> +	rc =3D kvmppc_virtmode_h_put_tce(vcpu, liobn, ioba, tce);
>>> +	if (rc =3D=3D H_TOO_HARD)
>>> +		return EMULATE_FAIL;
>>> +	kvmppc_set_gpr(vcpu, 3, rc);
>>> +	return EMULATE_DONE;
>>> +}
>>> +
>>> +static int kvmppc_h_pr_put_tce_indirect(struct kvm_vcpu *vcpu)
>>> +{
>>> +	unsigned long liobn =3D kvmppc_get_gpr(vcpu, 4);
>>> +	unsigned long ioba =3D kvmppc_get_gpr(vcpu, 5);
>>> +	unsigned long tce =3D kvmppc_get_gpr(vcpu, 6);
>>> +	unsigned long npages =3D kvmppc_get_gpr(vcpu, 7);
>>> +	long rc;
>>> +
>>> +	rc =3D kvmppc_virtmode_h_put_tce_indirect(vcpu, liobn, ioba,
>>> +			tce, npages);
>>> +	if (rc =3D=3D H_TOO_HARD)
>>> +		return EMULATE_FAIL;
>>> +	kvmppc_set_gpr(vcpu, 3, rc);
>>> +	return EMULATE_DONE;
>>> +}
>>> +
>>> +static int kvmppc_h_pr_stuff_tce(struct kvm_vcpu *vcpu)
>>> +{
>>> +	unsigned long liobn =3D kvmppc_get_gpr(vcpu, 4);
>>> +	unsigned long ioba =3D kvmppc_get_gpr(vcpu, 5);
>>> +	unsigned long tce_value =3D kvmppc_get_gpr(vcpu, 6);
>>> +	unsigned long npages =3D kvmppc_get_gpr(vcpu, 7);
>>> +	long rc;
>>> +
>>> +	rc =3D kvmppc_virtmode_h_stuff_tce(vcpu, liobn, ioba, tce_value, =
npages);
>>> 	if (rc =3D=3D H_TOO_HARD)
>>> 		return EMULATE_FAIL;
>>> 	kvmppc_set_gpr(vcpu, 3, rc);
>>> @@ -247,6 +278,10 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned =
long cmd)
>>> 		return kvmppc_h_pr_bulk_remove(vcpu);
>>> 	case H_PUT_TCE:
>>> 		return kvmppc_h_pr_put_tce(vcpu);
>>> +	case H_PUT_TCE_INDIRECT:
>>> +		return kvmppc_h_pr_put_tce_indirect(vcpu);
>>> +	case H_STUFF_TCE:
>>> +		return kvmppc_h_pr_stuff_tce(vcpu);
>>> 	case H_CEDE:
>>> 		vcpu->arch.shared->msr |=3D MSR_EE;
>>> 		kvm_vcpu_block(vcpu);
>>> diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
>>> index 6316ee3..8465c2a 100644
>>> --- a/arch/powerpc/kvm/powerpc.c
>>> +++ b/arch/powerpc/kvm/powerpc.c
>>> @@ -395,6 +395,9 @@ int kvm_dev_ioctl_check_extension(long ext)
>>> 		r =3D 1;
>>> 		break;
>>> #endif
>>> +	case KVM_CAP_SPAPR_MULTITCE:
>>> +		r =3D 1;
>>=20
>> This should only be true for book3s.
>=20
>=20
> We had this discussion with v2.
>=20
> David:
> =3D=3D=3D
> So, in the case of MULTITCE, that's not quite right.  PR KVM can
> emulate a PAPR system on a BookE machine, and there's no reason not to
> allow TCE acceleration as well.  We can't make it dependent on PAPR
> mode being selected, because that's enabled per-vcpu, whereas these
> capabilities are queried on the VM before the vcpus are created.
> =3D=3D=3D
>=20
> Wrong?

Partially. BookE can not emulate a PAPR system as it stands today.

The code should of course be generic and be available generically. But =
your code only patches the hypercall's availability in the book3s =
hypercall handlers, so that specific kernel version can only handle =
these hypercalls on book3s.

Whether a later version of the kernel will be able to handle them is a =
different question.


Alex

^ permalink raw reply

* Re: [PATCH 1/4] KVM: PPC: Add support for multiple-TCE hcalls
From: Alexey Kardashevskiy @ 2013-06-17  8:34 UTC (permalink / raw)
  To: Alexander Graf
  Cc: kvm, linux-kernel, kvm-ppc, Paul Mackerras, linuxppc-dev,
	David Gibson
In-Reply-To: <F3AAE42F-F325-462B-AD22-F3E568C39A8C@suse.de>

On 06/17/2013 06:02 PM, Alexander Graf wrote:
> 
> On 17.06.2013, at 09:55, Alexey Kardashevskiy wrote:
> 
>> On 06/17/2013 08:06 AM, Alexander Graf wrote:
>>>
>>> On 05.06.2013, at 08:11, Alexey Kardashevskiy wrote:
>>>
>>>> This adds real mode handlers for the H_PUT_TCE_INDIRECT and
>>>> H_STUFF_TCE hypercalls for QEMU emulated devices such as IBMVIO
>>>> devices or emulated PCI.  These calls allow adding multiple entries
>>>> (up to 512) into the TCE table in one call which saves time on
>>>> transition to/from real mode.
>>>>
>>>> This adds a tce_tmp cache to kvm_vcpu_arch to save valid TCEs
>>>> (copied from user and verified) before writing the whole list into
>>>> the TCE table. This cache will be utilized more in the upcoming
>>>> VFIO/IOMMU support to continue TCE list processing in the virtual
>>>> mode in the case if the real mode handler failed for some reason.
>>>>
>>>> This adds a guest physical to host real address converter
>>>> and calls the existing H_PUT_TCE handler. The converting function
>>>> is going to be fully utilized by upcoming VFIO supporting patches.
>>>>
>>>> This also implements the KVM_CAP_PPC_MULTITCE capability,
>>>> so in order to support the functionality of this patch, QEMU
>>>> needs to query for this capability and set the "hcall-multi-tce"
>>>> hypertas property only if the capability is present, otherwise
>>>> there will be serious performance degradation.
>>>>
>>>> Cc: David Gibson <david@gibson.dropbear.id.au>
>>>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>>>> Signed-off-by: Paul Mackerras <paulus@samba.org>
>>>
>>> Only a few minor nits. Ben already commented on implementation details.
>>>
>>>>
>>>> ---
>>>> Changelog:
>>>> 2013/06/05:
>>>> * fixed mistype about IBMVIO in the commit message
>>>> * updated doc and moved it to another section
>>>> * changed capability number
>>>>
>>>> 2013/05/21:
>>>> * added kvm_vcpu_arch::tce_tmp
>>>> * removed cleanup if put_indirect failed, instead we do not even start
>>>> writing to TCE table if we cannot get TCEs from the user and they are
>>>> invalid
>>>> * kvmppc_emulated_h_put_tce is split to kvmppc_emulated_put_tce
>>>> and kvmppc_emulated_validate_tce (for the previous item)
>>>> * fixed bug with failthrough for H_IPI
>>>> * removed all get_user() from real mode handlers
>>>> * kvmppc_lookup_pte() added (instead of making lookup_linux_pte public)
>>>> ---
>>>> Documentation/virtual/kvm/api.txt       |   17 ++
>>>> arch/powerpc/include/asm/kvm_host.h     |    2 +
>>>> arch/powerpc/include/asm/kvm_ppc.h      |   16 +-
>>>> arch/powerpc/kvm/book3s_64_vio.c        |  118 ++++++++++++++
>>>> arch/powerpc/kvm/book3s_64_vio_hv.c     |  266 +++++++++++++++++++++++++++----
>>>> arch/powerpc/kvm/book3s_hv.c            |   39 +++++
>>>> arch/powerpc/kvm/book3s_hv_rmhandlers.S |    6 +
>>>> arch/powerpc/kvm/book3s_pr_papr.c       |   37 ++++-
>>>> arch/powerpc/kvm/powerpc.c              |    3 +
>>>> include/uapi/linux/kvm.h                |    1 +
>>>> 10 files changed, 473 insertions(+), 32 deletions(-)
>>>>
>>>> diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
>>>> index 5f91eda..6c082ff 100644
>>>> --- a/Documentation/virtual/kvm/api.txt
>>>> +++ b/Documentation/virtual/kvm/api.txt
>>>> @@ -2362,6 +2362,23 @@ calls by the guest for that service will be passed to userspace to be
>>>> handled.
>>>>
>>>>
>>>> +4.83 KVM_CAP_PPC_MULTITCE
>>>> +
>>>> +Capability: KVM_CAP_PPC_MULTITCE
>>>> +Architectures: ppc
>>>> +Type: vm
>>>> +
>>>> +This capability tells the guest that multiple TCE entry add/remove hypercalls
>>>> +handling is supported by the kernel. This significanly accelerates DMA
>>>> +operations for PPC KVM guests.
>>>> +
>>>> +Unlike other capabilities in this section, this one does not have an ioctl.
>>>> +Instead, when the capability is present, the H_PUT_TCE_INDIRECT and
>>>> +H_STUFF_TCE hypercalls are to be handled in the host kernel and not passed to
>>>> +the guest. Othwerwise it might be better for the guest to continue using H_PUT_TCE
>>>> +hypercall (if KVM_CAP_SPAPR_TCE or KVM_CAP_SPAPR_TCE_IOMMU are present).
>>>
>>
>>> While this describes perfectly well what the consequences are of the
>>> patches, it does not describe properly what the CAP actually expresses.
>>> The CAP only says "this kernel is able to handle H_PUT_TCE_INDIRECT and
>>> H_STUFF_TCE hypercalls directly". All other consequences are nice to
>>> document, but the semantics of the CAP are missing.
>>
>>
>> ? It expresses ability to handle 2 hcalls. What is missing?
> 
> You don't describe the kvm <-> qemu interface. You describe some decisions qemu can take from this cap.


This file does not mention qemu at all. And the interface is - qemu (or
kvmtool could do that) just adds "hcall-multi-tce" to
"ibm,hypertas-functions" but this is for pseries linux and AIX could always
do it (no idea about it). Does it really have to be in this file?



>>> We also usually try to keep KVM behavior unchanged with regards to older
>>> versions until a CAP is enabled. In this case I don't think it matters
>>> all that much, so I'm fine with declaring it as enabled by default.
>>> Please document that this is a change in behavior versus older KVM
>>> versions though.
>>
>>
>> Ok!
>>
>>
>>>> +
>>>> +
>>>> 5. The kvm_run structure
>>>> ------------------------
>>>>
>>>> diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
>>>> index af326cd..85d8f26 100644
>>>> --- a/arch/powerpc/include/asm/kvm_host.h
>>>> +++ b/arch/powerpc/include/asm/kvm_host.h
>>>> @@ -609,6 +609,8 @@ struct kvm_vcpu_arch {
>>>> 	spinlock_t tbacct_lock;
>>>> 	u64 busy_stolen;
>>>> 	u64 busy_preempt;
>>>> +
>>>> +	unsigned long *tce_tmp;    /* TCE cache for TCE_PUT_INDIRECT hall */
>>>> #endif
>>>> };
>>>
>>> [...]
>>>>
>>>>
>>>
>>> [...]
>>>
>>>> diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
>>>> index 550f592..a39039a 100644
>>>> --- a/arch/powerpc/kvm/book3s_hv.c
>>>> +++ b/arch/powerpc/kvm/book3s_hv.c
>>>> @@ -568,6 +568,30 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
>>>> 			ret = kvmppc_xics_hcall(vcpu, req);
>>>> 			break;
>>>> 		} /* fallthrough */
>>>
>>> The fallthrough comment isn't accurate anymore.
>>>
>>>> +		return RESUME_HOST;
>>>> +	case H_PUT_TCE:
>>>> +		ret = kvmppc_virtmode_h_put_tce(vcpu, kvmppc_get_gpr(vcpu, 4),
>>>> +						kvmppc_get_gpr(vcpu, 5),
>>>> +						kvmppc_get_gpr(vcpu, 6));
>>>> +		if (ret == H_TOO_HARD)
>>>> +			return RESUME_HOST;
>>>> +		break;
>>>> +	case H_PUT_TCE_INDIRECT:
>>>> +		ret = kvmppc_virtmode_h_put_tce_indirect(vcpu, kvmppc_get_gpr(vcpu, 4),
>>>> +						kvmppc_get_gpr(vcpu, 5),
>>>> +						kvmppc_get_gpr(vcpu, 6),
>>>> +						kvmppc_get_gpr(vcpu, 7));
>>>> +		if (ret == H_TOO_HARD)
>>>> +			return RESUME_HOST;
>>>> +		break;
>>>> +	case H_STUFF_TCE:
>>>> +		ret = kvmppc_virtmode_h_stuff_tce(vcpu, kvmppc_get_gpr(vcpu, 4),
>>>> +						kvmppc_get_gpr(vcpu, 5),
>>>> +						kvmppc_get_gpr(vcpu, 6),
>>>> +						kvmppc_get_gpr(vcpu, 7));
>>>> +		if (ret == H_TOO_HARD)
>>>> +			return RESUME_HOST;
>>>> +		break;
>>>> 	default:
>>>> 		return RESUME_HOST;
>>>> 	}
>>>> @@ -958,6 +982,20 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
>>>> 	vcpu->arch.cpu_type = KVM_CPU_3S_64;
>>>> 	kvmppc_sanity_check(vcpu);
>>>>
>>>> +	/*
>>>> +	 * As we want to minimize the chance of having H_PUT_TCE_INDIRECT
>>>> +	 * half executed, we first read TCEs from the user, check them and
>>>> +	 * return error if something went wrong and only then put TCEs into
>>>> +	 * the TCE table.
>>>> +	 *
>>>> +	 * tce_tmp is a cache for TCEs to avoid stack allocation or
>>>> +	 * kmalloc as the whole TCE list can take up to 512 items 8 bytes
>>>> +	 * each (4096 bytes).
>>>> +	 */
>>>> +	vcpu->arch.tce_tmp = kmalloc(4096, GFP_KERNEL);
>>>> +	if (!vcpu->arch.tce_tmp)
>>>> +		goto free_vcpu;
>>>> +
>>>> 	return vcpu;
>>>>
>>>> free_vcpu:
>>>> @@ -980,6 +1018,7 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
>>>> 	unpin_vpa(vcpu->kvm, &vcpu->arch.slb_shadow);
>>>> 	unpin_vpa(vcpu->kvm, &vcpu->arch.vpa);
>>>> 	spin_unlock(&vcpu->arch.vpa_update_lock);
>>>> +	kfree(vcpu->arch.tce_tmp);
>>>> 	kvm_vcpu_uninit(vcpu);
>>>> 	kmem_cache_free(kvm_vcpu_cache, vcpu);
>>>> }
>>>> diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
>>>> index b02f91e..d35554e 100644
>>>> --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
>>>> +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
>>>> @@ -1490,6 +1490,12 @@ hcall_real_table:
>>>> 	.long	0		/* 0x11c */
>>>> 	.long	0		/* 0x120 */
>>>> 	.long	.kvmppc_h_bulk_remove - hcall_real_table
>>>> +	.long	0		/* 0x128 */
>>>> +	.long	0		/* 0x12c */
>>>> +	.long	0		/* 0x130 */
>>>> +	.long	0		/* 0x134 */
>>>> +	.long	.kvmppc_h_stuff_tce - hcall_real_table
>>>> +	.long	.kvmppc_h_put_tce_indirect - hcall_real_table
>>>> hcall_real_table_end:
>>>>
>>>> ignore_hdec:
>>>> diff --git a/arch/powerpc/kvm/book3s_pr_papr.c b/arch/powerpc/kvm/book3s_pr_papr.c
>>>> index da0e0bc..91d4b45 100644
>>>> --- a/arch/powerpc/kvm/book3s_pr_papr.c
>>>> +++ b/arch/powerpc/kvm/book3s_pr_papr.c
>>>> @@ -220,7 +220,38 @@ static int kvmppc_h_pr_put_tce(struct kvm_vcpu *vcpu)
>>>> 	unsigned long tce = kvmppc_get_gpr(vcpu, 6);
>>>> 	long rc;
>>>>
>>>> -	rc = kvmppc_h_put_tce(vcpu, liobn, ioba, tce);
>>>> +	rc = kvmppc_virtmode_h_put_tce(vcpu, liobn, ioba, tce);
>>>> +	if (rc == H_TOO_HARD)
>>>> +		return EMULATE_FAIL;
>>>> +	kvmppc_set_gpr(vcpu, 3, rc);
>>>> +	return EMULATE_DONE;
>>>> +}
>>>> +
>>>> +static int kvmppc_h_pr_put_tce_indirect(struct kvm_vcpu *vcpu)
>>>> +{
>>>> +	unsigned long liobn = kvmppc_get_gpr(vcpu, 4);
>>>> +	unsigned long ioba = kvmppc_get_gpr(vcpu, 5);
>>>> +	unsigned long tce = kvmppc_get_gpr(vcpu, 6);
>>>> +	unsigned long npages = kvmppc_get_gpr(vcpu, 7);
>>>> +	long rc;
>>>> +
>>>> +	rc = kvmppc_virtmode_h_put_tce_indirect(vcpu, liobn, ioba,
>>>> +			tce, npages);
>>>> +	if (rc == H_TOO_HARD)
>>>> +		return EMULATE_FAIL;
>>>> +	kvmppc_set_gpr(vcpu, 3, rc);
>>>> +	return EMULATE_DONE;
>>>> +}
>>>> +
>>>> +static int kvmppc_h_pr_stuff_tce(struct kvm_vcpu *vcpu)
>>>> +{
>>>> +	unsigned long liobn = kvmppc_get_gpr(vcpu, 4);
>>>> +	unsigned long ioba = kvmppc_get_gpr(vcpu, 5);
>>>> +	unsigned long tce_value = kvmppc_get_gpr(vcpu, 6);
>>>> +	unsigned long npages = kvmppc_get_gpr(vcpu, 7);
>>>> +	long rc;
>>>> +
>>>> +	rc = kvmppc_virtmode_h_stuff_tce(vcpu, liobn, ioba, tce_value, npages);
>>>> 	if (rc == H_TOO_HARD)
>>>> 		return EMULATE_FAIL;
>>>> 	kvmppc_set_gpr(vcpu, 3, rc);
>>>> @@ -247,6 +278,10 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd)
>>>> 		return kvmppc_h_pr_bulk_remove(vcpu);
>>>> 	case H_PUT_TCE:
>>>> 		return kvmppc_h_pr_put_tce(vcpu);
>>>> +	case H_PUT_TCE_INDIRECT:
>>>> +		return kvmppc_h_pr_put_tce_indirect(vcpu);
>>>> +	case H_STUFF_TCE:
>>>> +		return kvmppc_h_pr_stuff_tce(vcpu);
>>>> 	case H_CEDE:
>>>> 		vcpu->arch.shared->msr |= MSR_EE;
>>>> 		kvm_vcpu_block(vcpu);
>>>> diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
>>>> index 6316ee3..8465c2a 100644
>>>> --- a/arch/powerpc/kvm/powerpc.c
>>>> +++ b/arch/powerpc/kvm/powerpc.c
>>>> @@ -395,6 +395,9 @@ int kvm_dev_ioctl_check_extension(long ext)
>>>> 		r = 1;
>>>> 		break;
>>>> #endif
>>>> +	case KVM_CAP_SPAPR_MULTITCE:
>>>> +		r = 1;
>>>
>>> This should only be true for book3s.
>>
>>
>> We had this discussion with v2.
>>
>> David:
>> ===
>> So, in the case of MULTITCE, that's not quite right.  PR KVM can
>> emulate a PAPR system on a BookE machine, and there's no reason not to
>> allow TCE acceleration as well.  We can't make it dependent on PAPR
>> mode being selected, because that's enabled per-vcpu, whereas these
>> capabilities are queried on the VM before the vcpus are created.
>> ===
>>
>> Wrong?

> Partially. BookE can not emulate a PAPR system as it stands today.

Oh.
Ok.
So - #ifdef CONFIG_PPC_BOOK3S_64 ? Or run-time check for book3s (how...)?




-- 
Alexey

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox