* RE: [4/4] powerpc/85xx: Update mpc85xx_defconfig for C293PCIE
From: Liu Po-B43644 @ 2013-07-23 7:13 UTC (permalink / raw)
To: Wood Scott-B07421; +Cc: linuxppc-dev@ozlabs.org, Hu Mingkai-B21284
In-Reply-To: <20130722230026.GA26621@home.buserror.net>
-----Original Message-----
From: Wood Scott-B07421=20
Sent: Tuesday, July 23, 2013 7:00 AM
To: Liu Po-B43644
Cc: linuxppc-dev@ozlabs.org; Hu Mingkai-B21284
Subject: Re: [4/4] powerpc/85xx: Update mpc85xx_defconfig for C293PCIE
On Thu, Apr 25, 2013 at 09:54:17AM +0800, Po Liu wrote:
> From: Mingkai Hu <Mingkai.Hu@freescale.com>
>=20
> Signed-off-by: Mingkai Hu <Mingkai.Hu@freescale.com>
>=20
> ---
> Base on git://git.am.freescale.net/gitolite/mirrors/linux-2.6.git
> arch/powerpc/configs/mpc85xx_defconfig | 1 +
> 1 file changed, 1 insertion(+)
>=20
> diff --git a/arch/powerpc/configs/mpc85xx_defconfig=20
> b/arch/powerpc/configs/mpc85xx_defconfig
> index cf815e8..ddc33a2 100644
> --- a/arch/powerpc/configs/mpc85xx_defconfig
> +++ b/arch/powerpc/configs/mpc85xx_defconfig
> @@ -28,6 +28,7 @@ CONFIG_MPC85xx_MDS=3Dy CONFIG_MPC8536_DS=3Dy =20
> CONFIG_MPC85xx_DS=3Dy CONFIG_MPC85xx_RDB=3Dy
> +CONFIG_C293_PCIE=3Dy
> CONFIG_P1010_RDB=3Dy
> CONFIG_P1022_DS=3Dy
> CONFIG_P1022_RDK=3Dy
Also, why only mpc85xx_defconfig and mpc85xx_smp_defconfig? Just because t=
his board isn't SMP doesn't mean it can't be supported by an SMP kernel.
Action: I will add configure to mpc85xx_smp_defconfig. And merge it to Add=
-C293PCIE-board-support.patch.
-Scott
^ permalink raw reply
* RE: [3/4] powerpc/85xx: Add C293PCIE board support
From: Liu Po-B43644 @ 2013-07-23 7:47 UTC (permalink / raw)
To: Wood Scott-B07421; +Cc: linuxppc-dev@ozlabs.org, Hu Mingkai-B21284
In-Reply-To: <20130722225835.GA26484@home.buserror.net>
> -----Original Message-----
> From: Wood Scott-B07421
> Sent: Tuesday, July 23, 2013 6:59 AM
> To: Liu Po-B43644
> Cc: linuxppc-dev@ozlabs.org; Hu Mingkai-B21284
> Subject: Re: [3/4] powerpc/85xx: Add C293PCIE board support
> =20
> On Thu, Apr 25, 2013 at 09:54:16AM +0800, Po Liu wrote:
> > From: Mingkai Hu <Mingkai.Hu@freescale.com>
> >
> > C293PCIE board is a series of Freescale PCIe add-in cards to perform
> > as public key crypto accelerator or secure key management module.
> >
> > - 512KB platform SRAM in addition to 512K L2 Cache/SRAM
> > - 512MB soldered DDR3 32bit memory
> > - CPLD System Logic
> > - 64MB x16 NOR flash and 4GB x8 NAND flash
> > - 16MB SPI flash
> >
> > Signed-off-by: Mingkai Hu <Mingkai.Hu@freescale.com>
> > Singed-off-by: Po Liu <Po.Liu@freescale.com>
> =20
> Signed
> =20
> > + partition@900000 {
> > + /* 33MB for rootfs */
> > + reg =3D <0x00900000 0x02100000>;
> > + label =3D "NOR Rootfs Image";
> > + };
> > +
> > + partition@2a00000 {
> > + /* 20MB for JFFS2 based Root file System */
> > + reg =3D <0x02a00000 0x01400000>;
> > + label =3D "NOR JFFS2 Root File System";
> > + };
> =20
> Don't specify JFFS2. Combine these two partitions into one.
Ok, I'll merge up two partition.
> =20
> > + partition@600000 {
> > + /* 4MB for Compressed Root file System Image */
> > + reg =3D <0x00600000 0x00400000>;
> > + label =3D "NAND Compressed RFS Image";
> > + };
> > +
> > + partition@a00000 {
> > + /* 15MB for JFFS2 based Root file System */
> > + reg =3D <0x00a00000 0x00f00000>;
> > + label =3D "NAND JFFS2 Root File System";
> > + };
> =20
> Likewise.
> =20
> > + partition@1900000 {
> > + /* 7MB for User Area */
> > + reg =3D <0x01900000 0x00700000>;
> > + label =3D "NAND User area";
> > + };
> =20
> Above you say there's 4 GiB of NAND, but here you define partitions that
> only cover 32 MiB.
Can I set one partion include all other space(4GB- 32MB) with label name "O=
thers"?
> =20
> > + };
> > +
> > + cpld@2,0 {
> > + #address-cells =3D <1>;
> > + #size-cells =3D <1>;
> > + compatible =3D "fsl,c293pcie-cpld";
> > + reg =3D <0x2 0x0 0x0000020>;
> > + bank-width =3D <1>;
> > + device-width =3D <1>;
> > + };
> =20
> What do bank-width and device-width mean here?
I will remove these two lines? I thought I copy from other platform.
> =20
> Why all the leading zeroes in 0x0000020?
I'll change to 0x20 from 0x0000020.
> =20
> > + partition@580000 {
> > + /* 4MB for Compressed RFS Image */
> > + reg =3D <0x00580000 0x00400000>;
> > + label =3D "SPI Flash Compressed RFSImage";
> > + };
> > +
> > + partition@980000 {
> > + /* 6.5MB for JFFS2 based RFS */
> > + reg =3D <0x00980000 0x00680000>;
> > + label =3D "SPI Flash JFFS2 RFS";
> > + };
> =20
> Again, merge these two and don't specify JFFS2.
Ok, thanks
> =20
> > diff --git a/arch/powerpc/platforms/85xx/Kconfig
> > b/arch/powerpc/platforms/85xx/Kconfig
> > index a0dcd57..df26b21 100644
> > --- a/arch/powerpc/platforms/85xx/Kconfig
> > +++ b/arch/powerpc/platforms/85xx/Kconfig
> > @@ -32,6 +32,13 @@ config BSC9131_RDB
> > StarCore SC3850 DSP
> > Manufacturer : Freescale Semiconductor, Inc
> >
> > +config C293_PCIE
> > + bool "Freescale C293PCIE"
> > + select DEFAULT_UIMAGE
> > + select SWIOTLB
> > + help
> > + This option enables support for the C293PCIE board
> =20
> Why do you need SWIOTLB if the board has 512 MiB soldered RAM?
I'll remove it.
> =20
> > diff --git a/arch/powerpc/platforms/85xx/c293pcie.c
> > b/arch/powerpc/platforms/85xx/c293pcie.c
> > new file mode 100644
> > index 0000000..75dda12
> > --- /dev/null
> > +++ b/arch/powerpc/platforms/85xx/c293pcie.c
> > @@ -0,0 +1,82 @@
> > +/*
> > + * C293PCIE Board Setup
> > + *
> > + * Copyright 2013 Freescale Semiconductor Inc.
> > + *
> > + * 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/stddef.h>
> > +#include <linux/kernel.h>
> > +#include <linux/pci.h>
> > +#include <linux/delay.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/of_platform.h>
> > +
> > +#include <asm/time.h>
> > +#include <asm/machdep.h>
> > +#include <asm/pci-bridge.h>
> > +#include <mm/mmu_decl.h>
> > +#include <asm/prom.h>
> > +#include <asm/udbg.h>
> > +#include <asm/mpic.h>
> > +
> > +#include <sysdev/fsl_soc.h>
> > +#include <sysdev/fsl_pci.h>
> > +
> > +#include "mpc85xx.h"
> =20
> Are you sure you need all of these? I don't see any delays, for example=
.
Thanks, I'll test and remove redundant includes.
> =20
> -Scott
^ permalink raw reply
* Re: [PATCH] hashtable: add hash_for_each_possible_rcu_notrace()
From: Benjamin Herrenschmidt @ 2013-07-23 7:52 UTC (permalink / raw)
To: Joe Perches
Cc: Alexey Kardashevskiy, linux-kernel, Sasha Levin, Andrew Morton,
Paul E . McKenney, linuxppc-dev
In-Reply-To: <1374548005.8308.15.camel@joe-AO722>
On Mon, 2013-07-22 at 19:53 -0700, Joe Perches wrote:
> Anyway, you cc'd all the right people already.
>
> If no one responds after a couple weeks, either
> send it to Jiri or directly to Linus.
Or I'll just merge it with the rest of the series.
Cheers,
Ben.
^ permalink raw reply
* RE: [1/4] powerpc/85xx: Add SEC6.0 device tree
From: Liu Po-B43644 @ 2013-07-23 8:01 UTC (permalink / raw)
To: Wood Scott-B07421; +Cc: linuxppc-dev@ozlabs.org, Hu Mingkai-B21284
In-Reply-To: <20130722224110.GA26221@home.buserror.net>
> -----Original Message-----
> From: Wood Scott-B07421
> Sent: Tuesday, July 23, 2013 6:41 AM
> To: Liu Po-B43644
> Cc: linuxppc-dev@ozlabs.org; Hu Mingkai-B21284
> Subject: Re: [1/4] powerpc/85xx: Add SEC6.0 device tree
> =20
> On Thu, Apr 25, 2013 at 09:54:14AM +0800, Po Liu wrote:
> > From: Mingkai Hu <Mingkai.Hu@freescale.com>
> >
> > Add device tree for SEC 6.0 used on C29x silicon.
> >
> > Signed-off-by: Mingkai Hu <Mingkai.Hu@freescale.com>
> > Singed-off-by: Po Liu <Po.Liu@freescale.com>
> =20
> I've heard of patches being flamed, but here we want signing, not
> singeing. :-)
> =20
> Don't forget that you can use the -s option to have git add the signoff
> for you.
> =20
> > ---
> > Base on git://git.am.freescale.net/gitolite/mirrors/linux-2.6.git
> =20
> This URL is not accessible outside Freescale, so don't reference it when
> posting patches publicly.
> =20
> If your patch is against the latest upstream code, you don't need to say
> anything special about that. You only need to make a note when it's
> against some other yet-to-be-merged tree or patch.
> =20
> > + compatible =3D "fsl,sec-v6.0", "fsl,sec-v5.2",
> > + "fsl,sec-v5.0", "fsl,sec-v4.4",
> > + "fsl,sec-v4.0";
> > + fsl,sec-era =3D <6>;
> > + #address-cells =3D <1>;
> > + #size-cells =3D <1>;
> > +
> > + jr@1000 {
> > + compatible =3D "fsl,sec-v6.0-job-ring",
> > + "fsl,sec-v5.2-job-ring",
> > + "fsl,sec-v5.0-job-ring",
> > + "fsl,sec-v4.4-job-ring",
> > + "fsl,sec-v4.0-job-ring";
> > + reg =3D <0x1000 0x1000>;
> > + };
> > +
> > + jr@2000 {
> > + compatible =3D "fsl,sec-v6.0-job-ring",
> > + "fsl,sec-v5.2-job-ring",
> > + "fsl,sec-v5.0-job-ring",
> > + "fsl,sec-v4.4-job-ring",
> > + "fsl,sec-v4.0-job-ring";
> > + reg =3D <0x2000 0x1000>;
> > + };
> =20
> You claim compatibility with a bunch of prior SECs, but sec-v5.2 has fou=
r
> job rings and an rtic node. Likewise for the previous compatibles liste=
d.
> This has two job rings and no rtic.
So, shall I remove "fsl,sec-v5.2","fsl,sec-v5.0", "fsl,sec-v4.4", "fsl,sec-=
v4.0" since all other SEC with 4 job rings? and only leave "fsl,sec-v6.0"?
> =20
> Can you point to where in the SEC v4.0 binding (I don't see a binding fo=
r
> the subsequent versions), it says that these are optional?
I found SEC V4.0 in file qoriq-sec4.0-0.dtsi. If "fsl,sec-v4.0" not in the =
compatible list, it is no use in this compatible list. But seems keep the "=
fsl,sec-v4.0-job-ring" job ring compatible is ok. Is that what you were ask=
?
> =20
> -Scott
^ permalink raw reply
* [PATCH] powerpc/perf: Export PERF_EVENT_CONFIG_EBB_SHIFT to userspace
From: Michael Ellerman @ 2013-07-23 8:07 UTC (permalink / raw)
To: linuxppc-dev; +Cc: azanella
We use bit 63 of the event code for userspace to request that the event
be counted using EBB (Event Based Branches). Export this value, making
it part of the API - though only on processors that support EBB.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/include/asm/perf_event_server.h | 6 +-----
arch/powerpc/include/uapi/asm/Kbuild | 1 +
arch/powerpc/include/uapi/asm/perf_event.h | 18 ++++++++++++++++++
arch/powerpc/perf/core-book3s.c | 2 +-
arch/powerpc/perf/power8-pmu.c | 6 +++---
5 files changed, 24 insertions(+), 9 deletions(-)
create mode 100644 arch/powerpc/include/uapi/asm/perf_event.h
diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h
index 2dd7bfc..8b24926 100644
--- a/arch/powerpc/include/asm/perf_event_server.h
+++ b/arch/powerpc/include/asm/perf_event_server.h
@@ -12,6 +12,7 @@
#include <linux/types.h>
#include <asm/hw_irq.h>
#include <linux/device.h>
+#include <uapi/asm/perf_event.h>
#define MAX_HWEVENTS 8
#define MAX_EVENT_ALTERNATIVES 8
@@ -69,11 +70,6 @@ struct power_pmu {
#define PPMU_LIMITED_PMC_REQD 2 /* have to put this on a limited PMC */
#define PPMU_ONLY_COUNT_RUN 4 /* only counting in run state */
-/*
- * We use the event config bit 63 as a flag to request EBB.
- */
-#define EVENT_CONFIG_EBB_SHIFT 63
-
extern int register_power_pmu(struct power_pmu *);
struct pt_regs;
diff --git a/arch/powerpc/include/uapi/asm/Kbuild b/arch/powerpc/include/uapi/asm/Kbuild
index 5182c86..48be855 100644
--- a/arch/powerpc/include/uapi/asm/Kbuild
+++ b/arch/powerpc/include/uapi/asm/Kbuild
@@ -20,6 +20,7 @@ header-y += mman.h
header-y += msgbuf.h
header-y += nvram.h
header-y += param.h
+header-y += perf_event.h
header-y += poll.h
header-y += posix_types.h
header-y += ps3fb.h
diff --git a/arch/powerpc/include/uapi/asm/perf_event.h b/arch/powerpc/include/uapi/asm/perf_event.h
new file mode 100644
index 0000000..80a4d40
--- /dev/null
+++ b/arch/powerpc/include/uapi/asm/perf_event.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2013 Michael Ellerman, IBM Corp.
+ *
+ * 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; version 2 of the
+ * License.
+ */
+
+#ifndef _UAPI_ASM_POWERPC_PERF_EVENT_H
+#define _UAPI_ASM_POWERPC_PERF_EVENT_H
+
+/*
+ * We use bit 63 of perf_event_attr.config as a flag to request EBB.
+ */
+#define PERF_EVENT_CONFIG_EBB_SHIFT 63
+
+#endif /* _UAPI_ASM_POWERPC_PERF_EVENT_H */
diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index a3985ae..2981f73 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -484,7 +484,7 @@ static bool is_ebb_event(struct perf_event *event)
* use bit 63 of the event code for something else if they wish.
*/
return (ppmu->flags & PPMU_EBB) &&
- ((event->attr.config >> EVENT_CONFIG_EBB_SHIFT) & 1);
+ ((event->attr.config >> PERF_EVENT_CONFIG_EBB_SHIFT) & 1);
}
static int ebb_event_check(struct perf_event *event)
diff --git a/arch/powerpc/perf/power8-pmu.c b/arch/powerpc/perf/power8-pmu.c
index 96a64d6..5b49e78 100644
--- a/arch/powerpc/perf/power8-pmu.c
+++ b/arch/powerpc/perf/power8-pmu.c
@@ -118,7 +118,7 @@
(EVENT_UNIT_MASK << EVENT_UNIT_SHIFT) | \
(EVENT_COMBINE_MASK << EVENT_COMBINE_SHIFT) | \
(EVENT_MARKED_MASK << EVENT_MARKED_SHIFT) | \
- (EVENT_EBB_MASK << EVENT_CONFIG_EBB_SHIFT) | \
+ (EVENT_EBB_MASK << PERF_EVENT_CONFIG_EBB_SHIFT) | \
EVENT_PSEL_MASK)
/* MMCRA IFM bits - POWER8 */
@@ -233,10 +233,10 @@ static int power8_get_constraint(u64 event, unsigned long *maskp, unsigned long
pmc = (event >> EVENT_PMC_SHIFT) & EVENT_PMC_MASK;
unit = (event >> EVENT_UNIT_SHIFT) & EVENT_UNIT_MASK;
cache = (event >> EVENT_CACHE_SEL_SHIFT) & EVENT_CACHE_SEL_MASK;
- ebb = (event >> EVENT_CONFIG_EBB_SHIFT) & EVENT_EBB_MASK;
+ ebb = (event >> PERF_EVENT_CONFIG_EBB_SHIFT) & EVENT_EBB_MASK;
/* Clear the EBB bit in the event, so event checks work below */
- event &= ~(EVENT_EBB_MASK << EVENT_CONFIG_EBB_SHIFT);
+ event &= ~(EVENT_EBB_MASK << PERF_EVENT_CONFIG_EBB_SHIFT);
if (pmc) {
if (pmc > 6)
--
1.8.1.2
^ permalink raw reply related
* [PATCH 0/3] cpuidle: (powernv) cpuidle driver.
From: Deepthi Dharwar @ 2013-07-23 9:01 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-pm, linux-kernel
Following patch series ports the cpuidle framework for powernv
platform and also implements a cpuidle back-end powernv
idle driver calling on to power7_nap and snooze idle states.
Moving the idle states over to cpuidle framework can take advantage
of advanced heuristics, tunables and features provided by cpuidle
framework. Additional idle states can be exploited using the cpuidle
framework. The statistics and tracing infrastructure provided by
the cpuidle framework also helps in enabling power management
related tools and help tune the system and applications.
This series aims to maintain compatibility and functionality to
existing powernv idle cpu management code. There are no new functions
or idle states added as part of this series. This can be extended by
adding more states to this existing framework.
With this patch series the powernv cpuidle functionalities
are on-par with pSeries idle management.
For POWERNV platform to hook into CPUIDLE framework, one
needs to enable CONFIG_POWERNV_IDLE and disable
CONFIG_PSERIES_IDLE
Deepthi Dharwar (3):
cpuidle/powernv: cpuidle backend driver for powernv
cpuidle/powernv: Enable idle powernv cpu to call into the cpuidle framework.
cpuidle/powernv: Support smt-snooze-delay parameter in powernv idle.
arch/powerpc/include/asm/processor.h | 2
arch/powerpc/platforms/powernv/Kconfig | 9 +
arch/powerpc/platforms/powernv/Makefile | 1
arch/powerpc/platforms/powernv/powernv.h | 3
arch/powerpc/platforms/powernv/processor_idle.c | 275 +++++++++++++++++++++++
arch/powerpc/platforms/powernv/setup.c | 12 +
6 files changed, 300 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/platforms/powernv/processor_idle.c
-- Deepthi
^ permalink raw reply
* [PATCH 1/3] cpuidle/powernv: cpuidle backend driver for powernv
From: Deepthi Dharwar @ 2013-07-23 9:01 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-pm, linux-kernel
In-Reply-To: <20130723090111.7291.99479.stgit@deepthi.in.ibm.com>
This patch implements a back-end cpuidle driver for
powernv calling power7_nap and snooze idle states.
This can be extended by adding more idle states
in the future to the existing framework.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/Kconfig | 9 +
arch/powerpc/platforms/powernv/Makefile | 1
arch/powerpc/platforms/powernv/processor_idle.c | 239 +++++++++++++++++++++++
3 files changed, 249 insertions(+)
create mode 100644 arch/powerpc/platforms/powernv/processor_idle.c
diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
index c24684c..ace2d22 100644
--- a/arch/powerpc/platforms/powernv/Kconfig
+++ b/arch/powerpc/platforms/powernv/Kconfig
@@ -20,3 +20,12 @@ config PPC_POWERNV_RTAS
default y
select PPC_ICS_RTAS
select PPC_RTAS
+
+config POWERNV_IDLE
+ bool "CPUIdle driver for powernv platform"
+ depends on CPU_IDLE
+ depends on PPC_POWERNV
+ default y
+ help
+ Select this option to enable processor idle state management
+ through cpuidle subsystem.
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 7fe5951..c0e44eb 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -4,3 +4,4 @@ obj-y += opal-rtc.o opal-nvram.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o
obj-$(CONFIG_EEH) += eeh-ioda.o eeh-powernv.o
+obj-$(CONFIG_POWERNV_IDLE) += processor_idle.o
diff --git a/arch/powerpc/platforms/powernv/processor_idle.c b/arch/powerpc/platforms/powernv/processor_idle.c
new file mode 100644
index 0000000..f43ad91a
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/processor_idle.c
@@ -0,0 +1,239 @@
+/*
+ * processor_idle - idle state cpuidle driver.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu.h>
+#include <linux/notifier.h>
+
+#include <asm/machdep.h>
+#include <asm/runlatch.h>
+
+struct cpuidle_driver powernv_idle_driver = {
+ .name = "powernv_idle",
+ .owner = THIS_MODULE,
+};
+
+#define MAX_IDLE_STATE_COUNT 2
+
+static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
+static struct cpuidle_device __percpu *powernv_cpuidle_devices;
+static struct cpuidle_state *cpuidle_state_table;
+
+static int snooze_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ int cpu = dev->cpu;
+
+ local_irq_enable();
+ set_thread_flag(TIF_POLLING_NRFLAG);
+
+ while ((!need_resched()) && cpu_online(cpu)) {
+ ppc64_runlatch_off();
+ HMT_very_low();
+ }
+
+ HMT_medium();
+ clear_thread_flag(TIF_POLLING_NRFLAG);
+ smp_mb();
+ return index;
+}
+
+
+static int nap_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ ppc64_runlatch_off();
+ power7_idle();
+ return index;
+}
+
+/*
+ * States for dedicated partition case.
+ */
+static struct cpuidle_state powernv_states[MAX_IDLE_STATE_COUNT] = {
+ { /* Snooze */
+ .name = "snooze",
+ .desc = "snooze",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 0,
+ .target_residency = 0,
+ .enter = &snooze_loop },
+ { /* Nap */
+ .name = "Nap",
+ .desc = "Nap",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 10,
+ .target_residency = 100,
+ .enter = &nap_loop },
+};
+
+static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,
+ unsigned long action, void *hcpu)
+{
+ int hotcpu = (unsigned long)hcpu;
+ struct cpuidle_device *dev =
+ per_cpu_ptr(powernv_cpuidle_devices, hotcpu);
+
+ if (dev && cpuidle_get_driver()) {
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ cpuidle_pause_and_lock();
+ cpuidle_enable_device(dev);
+ cpuidle_resume_and_unlock();
+ break;
+
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ cpuidle_pause_and_lock();
+ cpuidle_disable_device(dev);
+ cpuidle_resume_and_unlock();
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block setup_hotplug_notifier = {
+ .notifier_call = powernv_cpuidle_add_cpu_notifier,
+};
+
+/*
+ * powernv_cpuidle_driver_init()
+ */
+static int powernv_cpuidle_driver_init(void)
+{
+ int idle_state;
+ struct cpuidle_driver *drv = &powernv_idle_driver;
+
+ drv->state_count = 0;
+
+ for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT; ++idle_state) {
+
+ if (idle_state > max_idle_state)
+ break;
+
+ /* is the state not enabled? */
+ if (cpuidle_state_table[idle_state].enter == NULL)
+ continue;
+
+ drv->states[drv->state_count] = /* structure copy */
+ cpuidle_state_table[idle_state];
+
+ drv->state_count += 1;
+ }
+
+ return 0;
+}
+
+/* powernv_idle_devices_uninit(void)
+ * unregister cpuidle devices and de-allocate memory
+ */
+static void powernv_idle_devices_uninit(void)
+{
+ int i;
+ struct cpuidle_device *dev;
+
+ for_each_possible_cpu(i) {
+ dev = per_cpu_ptr(powernv_cpuidle_devices, i);
+ cpuidle_unregister_device(dev);
+ }
+
+ free_percpu(powernv_cpuidle_devices);
+ return;
+}
+
+/* powernv_idle_devices_init()
+ * allocate, initialize and register cpuidle device
+ */
+static int powernv_idle_devices_init(void)
+{
+ int i;
+ struct cpuidle_driver *drv = &powernv_idle_driver;
+ struct cpuidle_device *dev;
+
+ powernv_cpuidle_devices = alloc_percpu(struct cpuidle_device);
+ if (powernv_cpuidle_devices == NULL)
+ return -ENOMEM;
+
+ for_each_possible_cpu(i) {
+ dev = per_cpu_ptr(powernv_cpuidle_devices, i);
+ dev->state_count = drv->state_count;
+ dev->cpu = i;
+ if (cpuidle_register_device(dev)) {
+ printk(KERN_DEBUG \
+ "cpuidle_register_device %d failed!\n", i);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/*
+ * powernv_idle_probe()
+ * Choose state table for shared versus dedicated partition
+ */
+static int powernv_idle_probe(void)
+{
+
+ if (cpuidle_disable != IDLE_NO_OVERRIDE)
+ return -ENODEV;
+
+ cpuidle_state_table = powernv_states;
+ return 0;
+}
+
+static int __init powernv_processor_idle_init(void)
+{
+ int retval;
+
+ retval = powernv_idle_probe();
+ if (retval)
+ return retval;
+
+ powernv_cpuidle_driver_init();
+ retval = cpuidle_register_driver(&powernv_idle_driver);
+ if (retval) {
+ printk(KERN_DEBUG "Registration of powernv driver failed.\n");
+ return retval;
+ }
+
+ retval = powernv_idle_devices_init();
+ if (retval) {
+ powernv_idle_devices_uninit();
+ cpuidle_unregister_driver(&powernv_idle_driver);
+ return retval;
+ }
+
+ register_cpu_notifier(&setup_hotplug_notifier);
+ printk(KERN_DEBUG "powernv_idle_driver registered\n");
+
+ return 0;
+}
+
+static void __exit powernv_processor_idle_exit(void)
+{
+
+ unregister_cpu_notifier(&setup_hotplug_notifier);
+ powernv_idle_devices_uninit();
+ cpuidle_unregister_driver(&powernv_idle_driver);
+
+ return;
+}
+
+module_init(powernv_processor_idle_init);
+module_exit(powernv_processor_idle_exit);
+
+MODULE_AUTHOR("Deepthi Dharwar <deepthi@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("Cpuidle driver for POWERNV");
+MODULE_LICENSE("GPL");
^ permalink raw reply related
* [PATCH 2/3] cpuidle/powernv: Enable idle powernv cpu to call into the cpuidle framework.
From: Deepthi Dharwar @ 2013-07-23 9:01 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-pm, linux-kernel
In-Reply-To: <20130723090111.7291.99479.stgit@deepthi.in.ibm.com>
This patch enables idle powernv cpu to hook on to the cpuidle
framework, if available, else call on to default idle platform
code.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/setup.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 84438af..97d0951 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -25,6 +25,7 @@
#include <linux/of.h>
#include <linux/interrupt.h>
#include <linux/bug.h>
+#include <linux/cpuidle.h>
#include <asm/machdep.h>
#include <asm/firmware.h>
@@ -196,6 +197,15 @@ static int __init pnv_probe(void)
return 1;
}
+void powernv_idle(void)
+{
+ /* Hook to cpuidle framework if available, else
+ * call on default platform idle code
+ */
+ if (cpuidle_idle_call())
+ power7_idle();
+}
+
define_machine(powernv) {
.name = "PowerNV",
.probe = pnv_probe,
@@ -205,7 +215,7 @@ define_machine(powernv) {
.show_cpuinfo = pnv_show_cpuinfo,
.progress = pnv_progress,
.machine_shutdown = pnv_shutdown,
- .power_save = power7_idle,
+ .power_save = powernv_idle,
.calibrate_decr = generic_calibrate_decr,
#ifdef CONFIG_KEXEC
.kexec_cpu_down = pnv_kexec_cpu_down,
^ permalink raw reply related
* [PATCH 3/3] cpuidle/powernv: Support smt-snooze-delay parameter in powernv idle.
From: Deepthi Dharwar @ 2013-07-23 9:02 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-pm, linux-kernel
In-Reply-To: <20130723090111.7291.99479.stgit@deepthi.in.ibm.com>
smt-snooze-delay is a tunable that is supported on
powerpc platform to delay the entry to nap state.
This can be set either via sysfs, kernel commandline
or pp64_cpu util.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/processor.h | 2 +
arch/powerpc/platforms/powernv/powernv.h | 3 ++
arch/powerpc/platforms/powernv/processor_idle.c | 36 +++++++++++++++++++++++
3 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 47a35b0..5700c3c 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -426,7 +426,7 @@ enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF};
extern int powersave_nap; /* set if nap mode can be used in idle loop */
extern void power7_nap(void);
-#ifdef CONFIG_PSERIES_IDLE
+#if defined(CONFIG_PSERIES_IDLE) || defined(CONFIG_POWERNV_IDLE)
extern void update_smt_snooze_delay(int cpu, int residency);
#else
static inline void update_smt_snooze_delay(int cpu, int residency) {}
diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h
index a1c6f83..558ee69 100644
--- a/arch/powerpc/platforms/powernv/powernv.h
+++ b/arch/powerpc/platforms/powernv/powernv.h
@@ -15,4 +15,7 @@ static inline void pnv_pci_init(void) { }
static inline void pnv_pci_shutdown(void) { }
#endif
+/* Idle variable */
+DECLARE_PER_CPU(long, smt_snooze_delay);
+
#endif /* _POWERNV_H */
diff --git a/arch/powerpc/platforms/powernv/processor_idle.c b/arch/powerpc/platforms/powernv/processor_idle.c
index f43ad91a..505fea4 100644
--- a/arch/powerpc/platforms/powernv/processor_idle.c
+++ b/arch/powerpc/platforms/powernv/processor_idle.c
@@ -13,6 +13,8 @@
#include <asm/machdep.h>
#include <asm/runlatch.h>
+#include "powernv.h"
+
struct cpuidle_driver powernv_idle_driver = {
.name = "powernv_idle",
.owner = THIS_MODULE,
@@ -193,6 +195,38 @@ static int powernv_idle_probe(void)
return 0;
}
+void update_smt_snooze_delay(int cpu, int residency)
+{
+ struct cpuidle_driver *drv = cpuidle_get_driver();
+ struct cpuidle_device *dev;
+
+ if (!drv)
+ return;
+
+ if (cpu == -1) {
+ if (residency < 0) {
+ /* Disable NAP on all cpus */
+ drv->states[1].disabled = true;
+ } else {
+ drv->states[1].target_residency = residency;
+ drv->states[1].disabled = false;
+ }
+ return;
+ }
+
+ dev = per_cpu(cpuidle_devices, cpu);
+ if (!dev)
+ return;
+
+ if (residency < 0)
+ dev->states_usage[1].disable = 1;
+ else {
+ drv->states[1].target_residency = residency;
+ drv->states[1].disabled = false;
+ dev->states_usage[1].disable = 0;
+ }
+}
+
static int __init powernv_processor_idle_init(void)
{
int retval;
@@ -208,6 +242,8 @@ static int __init powernv_processor_idle_init(void)
return retval;
}
+ update_smt_snooze_delay(-1, per_cpu(smt_snooze_delay, 0));
+
retval = powernv_idle_devices_init();
if (retval) {
powernv_idle_devices_uninit();
^ permalink raw reply related
* Re: [PATCH 03/10] vfio: add external user support
From: Alexey Kardashevskiy @ 2013-07-23 9:07 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: kvm, Alexander Graf, kvm-ppc, linux-kernel, Alex Williamson,
Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1374546224.15429.8.camel@ul30vt.home>
On 07/23/2013 12:23 PM, Alex Williamson wrote:
> On Tue, 2013-07-16 at 10:53 +1000, Alexey Kardashevskiy wrote:
>> VFIO is designed to be used via ioctls on file descriptors
>> returned by VFIO.
>>
>> However in some situations support for an external user is required.
>> The first user is KVM on PPC64 (SPAPR TCE protocol) which is going to
>> use the existing VFIO groups for exclusive access in real/virtual mode
>> on a host to avoid passing map/unmap requests to the user space which
>> would made things pretty slow.
>>
>> The protocol includes:
>>
>> 1. do normal VFIO init operation:
>> - opening a new container;
>> - attaching group(s) to it;
>> - setting an IOMMU driver for a container.
>> When IOMMU is set for a container, all groups in it are
>> considered ready to use by an external user.
>>
>> 2. User space passes a group fd to an external user.
>> The external user calls vfio_group_get_external_user()
>> to verify that:
>> - the group is initialized;
>> - IOMMU is set for it.
>> If both checks passed, vfio_group_get_external_user()
>> increments the container user counter to prevent
>> the VFIO group from disposal before KVM exits.
>>
>> 3. The external user calls vfio_external_user_iommu_id()
>> to know an IOMMU ID. PPC64 KVM uses it to link logical bus
>> number (LIOBN) with IOMMU ID.
>>
>> 4. When the external KVM finishes, it calls
>> vfio_group_put_external_user() to release the VFIO group.
>> This call decrements the container user counter.
>> Everything gets released.
>>
>> The "vfio: Limit group opens" patch is also required for the consistency.
>>
>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>
> This looks fine to me. Is the plan to add this through the ppc tree
> again? Thanks,
Nope, better to add this through your tree. And faster for sure :) Thanks!
--
Alexey
^ permalink raw reply
* [PATCH 1/2] cpuidle: fix cpu idle driver as a module can not remove
From: Dongsheng Wang @ 2013-07-23 9:28 UTC (permalink / raw)
To: daniel.lezcano; +Cc: rjw, linuxppc-dev, Wang Dongsheng, linux-pm
From: Wang Dongsheng <dongsheng.wang@freescale.com>
The module can not be removed when execute "rmmod". rmmod not use
"--force".
Log:
root:~# rmmod cpuidle-e500
incs[9], decs[1]
rmmod: can't unload 'cpuidle_e500': Resource temporarily unavailable
Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
---
Branch: pm-cpuidle
drivers/cpuidle/cpuidle.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index fdc432f..534320a 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -386,6 +386,9 @@ static int __cpuidle_register_device(struct cpuidle_device *dev)
goto err_coupled;
dev->registered = 1;
+
+ module_put(drv->owner);
+
return 0;
err_coupled:
@@ -432,8 +435,6 @@ EXPORT_SYMBOL_GPL(cpuidle_register_device);
*/
void cpuidle_unregister_device(struct cpuidle_device *dev)
{
- struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
-
if (dev->registered == 0)
return;
@@ -448,8 +449,6 @@ void cpuidle_unregister_device(struct cpuidle_device *dev)
cpuidle_coupled_unregister_device(dev);
cpuidle_resume_and_unlock();
-
- module_put(drv->owner);
}
EXPORT_SYMBOL_GPL(cpuidle_unregister_device);
--
1.8.0
^ permalink raw reply related
* [PATCH 2/2] cpuidle: export cpuidle_idle_call symbol
From: Dongsheng Wang @ 2013-07-23 9:28 UTC (permalink / raw)
To: daniel.lezcano; +Cc: rjw, linuxppc-dev, Wang Dongsheng, linux-pm
In-Reply-To: <1374571681-31911-1-git-send-email-dongsheng.wang@freescale.com>
From: Wang Dongsheng <dongsheng.wang@freescale.com>
Export cpuidle_idle_call symbol, make this function can be invoked
in the module.
Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
---
Branch: pm-cpuidle
drivers/cpuidle/cpuidle.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 534320a..d0a61d6 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -168,6 +168,7 @@ int cpuidle_idle_call(void)
return 0;
}
+EXPORT_SYMBOL_GPL(cpuidle_idle_call);
/**
* cpuidle_install_idle_handler - installs the cpuidle idle loop handler
--
1.8.0
^ permalink raw reply related
* [PATCH 02/11] powerpc/eeh: Export functions for hotplug
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
Make some functions public in order to support hotplug on either specific
PCI bus or PCI device in future.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 9 +++++++++
arch/powerpc/kernel/eeh.c | 6 +++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 09a8743..d9d35c2 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -209,9 +209,12 @@ unsigned long eeh_check_failure(const volatile void __iomem *token,
unsigned long val);
int eeh_dev_check_failure(struct eeh_dev *edev);
void eeh_addr_cache_build(void);
+void eeh_add_device_early(struct device_node *);
void eeh_add_device_tree_early(struct device_node *);
+void eeh_add_device_late(struct pci_dev *);
void eeh_add_device_tree_late(struct pci_bus *);
void eeh_add_sysfs_files(struct pci_bus *);
+void eeh_remove_device(struct pci_dev *, int);
void eeh_remove_bus_device(struct pci_dev *, int);
/**
@@ -252,12 +255,18 @@ static inline unsigned long eeh_check_failure(const volatile void __iomem *token
static inline void eeh_addr_cache_build(void) { }
+static inline void eeh_add_device_early(struct device_node *dn) { }
+
static inline void eeh_add_device_tree_early(struct device_node *dn) { }
+static inline void eeh_add_device_late(struct pci_dev *dev) { }
+
static inline void eeh_add_device_tree_late(struct pci_bus *bus) { }
static inline void eeh_add_sysfs_files(struct pci_bus *bus) { }
+static inline void eeh_remove_device(struct pci_dev *dev, int purge_pe) { }
+
static inline void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe) { }
#define EEH_POSSIBLE_ERROR(val, type) (0)
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index b5c425e..582ad1e 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -836,7 +836,7 @@ core_initcall_sync(eeh_init);
* 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)
+void eeh_add_device_early(struct device_node *dn)
{
struct pci_controller *phb;
@@ -884,7 +884,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
* 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)
+void eeh_add_device_late(struct pci_dev *dev)
{
struct device_node *dn;
struct eeh_dev *edev;
@@ -972,7 +972,7 @@ EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
* 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)
+void eeh_remove_device(struct pci_dev *dev, int purge_pe)
{
struct eeh_dev *edev;
--
1.7.5.4
^ permalink raw reply related
* [PATCH 05/11] powerpc/eeh: Keep PE during hotplug
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
When we do normal hotplug, the PE shouldn't be kept. However, we
need the PE if the hotplug caused by EEH errors. Since we remove
EEH device through the PCI hook pcibios_stop_dev(), the flag
"purge_pe" passed to various functions is meaningless. So the patch
removes the meaningless flag and introduce new flag "EEH_PE_KEEP"
to save the PE while doing hotplug during EEH error recovery.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 11 +++++------
arch/powerpc/include/asm/pci-bridge.h | 1 -
arch/powerpc/kernel/eeh.c | 28 ++--------------------------
arch/powerpc/kernel/eeh_driver.c | 7 +++++--
arch/powerpc/kernel/eeh_pe.c | 7 +++----
arch/powerpc/kernel/pci-hotplug.c | 26 +++++---------------------
6 files changed, 20 insertions(+), 60 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index d9d35c2..2ce22d7 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -55,6 +55,8 @@ struct device_node;
#define EEH_PE_RECOVERING (1 << 1) /* Recovering PE */
#define EEH_PE_PHB_DEAD (1 << 2) /* Dead PHB */
+#define EEH_PE_KEEP (1 << 8) /* Keep PE on hotplug */
+
struct eeh_pe {
int type; /* PE type: PHB/Bus/Device */
int state; /* PE EEH dependent mode */
@@ -193,7 +195,7 @@ int eeh_phb_pe_create(struct pci_controller *phb);
struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb);
struct eeh_pe *eeh_pe_get(struct eeh_dev *edev);
int eeh_add_to_parent_pe(struct eeh_dev *edev);
-int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe);
+int eeh_rmv_from_parent_pe(struct eeh_dev *edev);
void eeh_pe_update_time_stamp(struct eeh_pe *pe);
void *eeh_pe_dev_traverse(struct eeh_pe *root,
eeh_traverse_func fn, void *flag);
@@ -214,8 +216,7 @@ void eeh_add_device_tree_early(struct device_node *);
void eeh_add_device_late(struct pci_dev *);
void eeh_add_device_tree_late(struct pci_bus *);
void eeh_add_sysfs_files(struct pci_bus *);
-void eeh_remove_device(struct pci_dev *, int);
-void eeh_remove_bus_device(struct pci_dev *, int);
+void eeh_remove_device(struct pci_dev *);
/**
* EEH_POSSIBLE_ERROR() -- test for possible MMIO failure.
@@ -265,9 +266,7 @@ static inline void eeh_add_device_tree_late(struct pci_bus *bus) { }
static inline void eeh_add_sysfs_files(struct pci_bus *bus) { }
-static inline void eeh_remove_device(struct pci_dev *dev, int purge_pe) { }
-
-static inline void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe) { }
+static inline void eeh_remove_device(struct pci_dev *dev) { }
#define EEH_POSSIBLE_ERROR(val, type) (0)
#define EEH_IO_ERROR_VALUE(size) (-1UL)
diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h
index 2c1d8cb..32d0d20 100644
--- a/arch/powerpc/include/asm/pci-bridge.h
+++ b/arch/powerpc/include/asm/pci-bridge.h
@@ -209,7 +209,6 @@ static inline struct eeh_dev *of_node_to_eeh_dev(struct device_node *dn)
extern struct pci_bus *pcibios_find_pci_bus(struct device_node *dn);
/** Remove all of the PCI devices under this bus */
-extern void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe);
extern void pcibios_remove_pci_devices(struct pci_bus *bus);
/** Discover new pci devices under this bus, and add them */
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 582ad1e..ce81477 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -964,7 +964,6 @@ 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
@@ -972,7 +971,7 @@ EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
* this device will no longer be detected after this call; thus,
* i/o errors affecting this slot may leave this device unusable.
*/
-void eeh_remove_device(struct pci_dev *dev, int purge_pe)
+void eeh_remove_device(struct pci_dev *dev)
{
struct eeh_dev *edev;
@@ -990,34 +989,11 @@ void eeh_remove_device(struct pci_dev *dev, int purge_pe)
edev->pdev = NULL;
dev->dev.archdata.edev = NULL;
- eeh_rmv_from_parent_pe(edev, purge_pe);
+ eeh_rmv_from_parent_pe(edev);
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) {
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 2b1ce17..9ef3bbb 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -362,8 +362,10 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
* devices are expected to be attached soon when calling
* into pcibios_add_pci_devices().
*/
- if (bus)
- __pcibios_remove_pci_devices(bus, 0);
+ if (bus) {
+ eeh_pe_state_mark(pe, EEH_PE_KEEP);
+ pcibios_remove_pci_devices(bus);
+ }
/* Reset the pci controller. (Asserts RST#; resets config space).
* Reconfigure bridges and devices. Don't try to bring the system
@@ -386,6 +388,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
if (bus) {
ssleep(5);
pcibios_add_pci_devices(bus);
+ eeh_pe_state_clear(pe, EEH_PE_KEEP);
}
pe->tstamp = tstamp;
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index 016588a..32ef409 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -333,7 +333,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
while (parent) {
if (!(parent->type & EEH_PE_INVALID))
break;
- parent->type &= ~EEH_PE_INVALID;
+ parent->type &= ~(EEH_PE_INVALID | EEH_PE_KEEP);
parent = parent->parent;
}
pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
@@ -397,14 +397,13 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
/**
* 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)
+int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
{
struct eeh_pe *pe, *parent, *child;
int cnt;
@@ -431,7 +430,7 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
if (pe->type & EEH_PE_PHB)
break;
- if (purge_pe) {
+ if (!(pe->state & EEH_PE_KEEP)) {
if (list_empty(&pe->edevs) &&
list_empty(&pe->child_list)) {
list_del(&pe->child);
diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c
index 3dab2f2..fc0831d 100644
--- a/arch/powerpc/kernel/pci-hotplug.c
+++ b/arch/powerpc/kernel/pci-hotplug.c
@@ -29,49 +29,33 @@
*/
void pcibios_release_device(struct pci_dev *dev)
{
- eeh_remove_device(dev, 1);
+ eeh_remove_device(dev);
}
/**
- * __pcibios_remove_pci_devices - remove all devices under this 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)
+void pcibios_remove_pci_devices(struct pci_bus *bus)
{
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);
+ pcibios_remove_pci_devices(child_bus);
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);
+ pr_debug(" Removing %s...\n", pci_name(dev));
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);
/**
--
1.7.5.4
^ permalink raw reply related
* [PATCH 03/11] powerpc/pci: Override pcibios_release_device()
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
The patch overrides pcibios_release_device() to release EEH
resources (EEH cache, unbinding EEH device) for the indicated PCI
device.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/kernel/pci-hotplug.c | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c
index 3f60880..3dab2f2 100644
--- a/arch/powerpc/kernel/pci-hotplug.c
+++ b/arch/powerpc/kernel/pci-hotplug.c
@@ -22,6 +22,17 @@
#include <asm/eeh.h>
/**
+ * pcibios_release_device - release PCI device
+ * @dev: PCI device
+ *
+ * The function is called before releasing the indicated PCI device.
+ */
+void pcibios_release_device(struct pci_dev *dev)
+{
+ eeh_remove_device(dev, 1);
+}
+
+/**
* __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
--
1.7.5.4
^ permalink raw reply related
* [PATCH 01/11] powerpc/eeh: Remove reference to PCI device
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
We will rely on pcibios_release_device() to remove the EEH cache
and unbind EEH device for the specific PCI device. So we shouldn't
hold the reference to the PCI device from EEH cache and EEH device.
Otherwise, pcibios_release_device() won't be called as we expected.
The patch removes the reference to the PCI device in EEH core.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/kernel/eeh.c | 4 ----
arch/powerpc/kernel/eeh_cache.c | 18 +++++-------------
2 files changed, 5 insertions(+), 17 deletions(-)
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 39954fe..b5c425e 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -499,8 +499,6 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
}
eeh_dev_check_failure(edev);
-
- pci_dev_put(eeh_dev_to_pci_dev(edev));
return val;
}
@@ -904,7 +902,6 @@ static void eeh_add_device_late(struct pci_dev *dev)
}
WARN_ON(edev->pdev);
- pci_dev_get(dev);
edev->pdev = dev;
dev->dev.archdata.edev = edev;
@@ -992,7 +989,6 @@ static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
}
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);
diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c
index f9ac123..e8c9fd5 100644
--- a/arch/powerpc/kernel/eeh_cache.c
+++ b/arch/powerpc/kernel/eeh_cache.c
@@ -68,16 +68,12 @@ static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
struct pci_io_addr_range *piar;
piar = rb_entry(n, struct pci_io_addr_range, rb_node);
- if (addr < piar->addr_lo) {
+ 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;
- }
- }
+ else if (addr > piar->addr_hi)
+ n = n->rb_right;
+ else
+ return piar->edev;
}
return NULL;
@@ -156,7 +152,6 @@ eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
if (!piar)
return NULL;
- pci_dev_get(dev);
piar->addr_lo = alo;
piar->addr_hi = ahi;
piar->edev = pci_dev_to_eeh_dev(dev);
@@ -250,7 +245,6 @@ restart:
if (piar->pcidev == dev) {
rb_erase(n, &pci_io_addr_cache_root.rb_root);
- pci_dev_put(piar->pcidev);
kfree(piar);
goto restart;
}
@@ -302,12 +296,10 @@ void eeh_addr_cache_build(void)
if (!edev)
continue;
- pci_dev_get(dev); /* matching put is in eeh_remove_device() */
dev->dev.archdata.edev = edev;
edev->pdev = dev;
eeh_addr_cache_insert_dev(dev);
-
eeh_sysfs_add_device(dev);
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 04/11] PCI/hotplug: Needn't remove EEH cache again
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Bjorn Helgaas, Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
Since pcibios_release_device() called by pci_stop_and_remove_bus_device()
has removed the EEH cache, we needn't do that again.
Cc: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
drivers/pci/hotplug/rpadlpar_core.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index b29e20b..bb7af78 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -388,7 +388,6 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
/* Remove the EADS bridge device itself */
BUG_ON(!bus->self);
pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
- eeh_remove_bus_device(bus->self, true);
pci_stop_and_remove_bus_device(bus->self);
return 0;
--
1.7.5.4
^ permalink raw reply related
* [PATCH 09/11] powerpc/eeh: Don't use pci_dev during BAR restore
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
While restoring BARs for one specific PCI device, the pci_dev
instance should have been released. So it's not reliable to use
the pci_dev instance on restoring BARs. However, we still need
some information (e.g. PCIe capability position, header type) from
the pci_dev instance. So we have to store those information to
EEH device in advance.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 8 +++-
arch/powerpc/kernel/eeh_pe.c | 25 +++++-----
arch/powerpc/platforms/powernv/eeh-powernv.c | 11 +++++
arch/powerpc/platforms/pseries/eeh_pseries.c | 63 +++++++++++++++++++++++++-
4 files changed, 91 insertions(+), 16 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index f54a601..4199d99 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -84,8 +84,11 @@ struct eeh_pe {
* another tree except the currently existing tree of PCI
* buses and PCI devices
*/
-#define EEH_DEV_IRQ_DISABLED (1 << 0) /* Interrupt disabled */
-#define EEH_DEV_DISCONNECTED (1 << 1) /* Removing from PE */
+#define EEH_DEV_BRIDGE (1 << 0) /* PCI bridge */
+#define EEH_DEV_ROOT_PORT (1 << 1) /* PCIe root port */
+#define EEH_DEV_DS_PORT (1 << 2) /* Downstream port */
+#define EEH_DEV_IRQ_DISABLED (1 << 3) /* Interrupt disabled */
+#define EEH_DEV_DISCONNECTED (1 << 4) /* Removing from PE */
struct eeh_dev {
int mode; /* EEH mode */
@@ -93,6 +96,7 @@ struct eeh_dev {
int config_addr; /* Config address */
int pe_config_addr; /* PE config address */
u32 config_space[16]; /* Saved PCI config space */
+ u8 pcie_cap; /* Saved PCIe capability */
struct eeh_pe *pe; /* Associated PE */
struct list_head list; /* Form link list in the PE */
struct pci_controller *phb; /* Associated PHB */
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index 2aa955a..f945053 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -578,7 +578,7 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state)
* blocked on normal path during the stage. So we need utilize
* eeh operations, which is always permitted.
*/
-static void eeh_bridge_check_link(struct pci_dev *pdev,
+static void eeh_bridge_check_link(struct eeh_dev *edev,
struct device_node *dn)
{
int cap;
@@ -589,16 +589,17 @@ static void eeh_bridge_check_link(struct pci_dev *pdev,
* We only check root port and downstream ports of
* PCIe switches
*/
- if (!pci_is_pcie(pdev) ||
- (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
- pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
+ if (!(edev->mode & (EEH_DEV_ROOT_PORT | EEH_DEV_DS_PORT)))
return;
- pr_debug("%s: Check PCIe link for %s ...\n",
- __func__, pci_name(pdev));
+ pr_debug("%s: Check PCIe link for %04x:%02x:%02x.%01x ...\n",
+ __func__, edev->phb->global_number,
+ edev->config_addr >> 8,
+ PCI_SLOT(edev->config_addr & 0xFF),
+ PCI_FUNC(edev->config_addr & 0xFF));
/* Check slot status */
- cap = pdev->pcie_cap;
+ cap = edev->pcie_cap;
eeh_ops->read_config(dn, cap + PCI_EXP_SLTSTA, 2, &val);
if (!(val & PCI_EXP_SLTSTA_PDS)) {
pr_debug(" No card in the slot (0x%04x) !\n", val);
@@ -652,8 +653,7 @@ static void eeh_bridge_check_link(struct pci_dev *pdev,
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
-static void eeh_restore_bridge_bars(struct pci_dev *pdev,
- struct eeh_dev *edev,
+static void eeh_restore_bridge_bars(struct eeh_dev *edev,
struct device_node *dn)
{
int i;
@@ -679,7 +679,7 @@ static void eeh_restore_bridge_bars(struct pci_dev *pdev,
eeh_ops->write_config(dn, PCI_COMMAND, 4, edev->config_space[1]);
/* Check the PCIe link is ready */
- eeh_bridge_check_link(pdev, dn);
+ eeh_bridge_check_link(edev, dn);
}
static void eeh_restore_device_bars(struct eeh_dev *edev,
@@ -729,12 +729,11 @@ static void eeh_restore_device_bars(struct eeh_dev *edev,
static void *eeh_restore_one_device_bars(void *data, void *flag)
{
struct eeh_dev *edev = (struct eeh_dev *)data;
- struct pci_dev *pdev = eeh_dev_to_pci_dev(edev);
struct device_node *dn = eeh_dev_to_of_node(edev);
/* Do special restore for bridges */
- if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
- eeh_restore_bridge_bars(pdev, edev, dn);
+ if (edev->mode & EEH_DEV_BRIDGE)
+ eeh_restore_bridge_bars(edev, dn);
else
eeh_restore_device_bars(edev, dn);
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index a380428..4361a5c 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -124,6 +124,17 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
/* Initialize eeh device */
edev->class_code = dev->class;
edev->mode = 0;
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
+ edev->mode |= EEH_DEV_BRIDGE;
+ if (pci_is_pcie(dev)) {
+ edev->pcie_cap = pci_pcie_cap(dev);
+
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+ edev->mode |= EEH_DEV_ROOT_PORT;
+ else if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)
+ edev->mode |= EEH_DEV_DS_PORT;
+ }
+
edev->config_addr = ((dev->bus->number << 8) | dev->devfn);
edev->pe_config_addr = phb->bdfn_to_pe(phb, dev->bus, dev->devfn & 0xff);
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 0f44f9f..9e80f0a 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -133,6 +133,48 @@ static int pseries_eeh_init(void)
return 0;
}
+static int pseries_eeh_cap_start(struct device_node *dn)
+{
+ struct pci_dn *pdn = PCI_DN(dn);
+ u32 status;
+
+ if (!pdn)
+ return 0;
+
+ rtas_read_config(pdn, PCI_STATUS, 2, &status);
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+
+ return PCI_CAPABILITY_LIST;
+}
+
+
+static int pseries_eeh_find_cap(struct device_node *dn, int cap)
+{
+ struct pci_dn *pdn = PCI_DN(dn);
+ int pos = pseries_eeh_cap_start(dn);
+ int cnt = 48; /* Maximal number of capabilities */
+ u32 id;
+
+ if (!pos)
+ return 0;
+
+ while (cnt--) {
+ rtas_read_config(pdn, pos, 1, &pos);
+ if (pos < 0x40)
+ break;
+ pos &= ~3;
+ rtas_read_config(pdn, pos + PCI_CAP_LIST_ID, 1, &id);
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+ pos += PCI_CAP_LIST_NEXT;
+ }
+
+ return 0;
+}
+
/**
* pseries_eeh_of_probe - EEH probe on the given device
* @dn: OF node
@@ -146,8 +188,10 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
{
struct eeh_dev *edev;
struct eeh_pe pe;
+ struct pci_dn *pdn = PCI_DN(dn);
const u32 *class_code, *vendor_id, *device_id;
const u32 *regs;
+ u32 pcie_flags;
int enable = 0;
int ret;
@@ -167,9 +211,26 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
if (dn->type && !strcmp(dn->type, "isa"))
return NULL;
- /* Update class code and mode of eeh device */
+ /*
+ * Update class code and mode of eeh device. We need
+ * correctly reflects that current device is root port
+ * or PCIe switch downstream port.
+ */
edev->class_code = *class_code;
+ edev->pcie_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_EXP);
edev->mode = 0;
+ if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {
+ edev->mode |= EEH_DEV_BRIDGE;
+ if (edev->pcie_cap) {
+ rtas_read_config(pdn, edev->pcie_cap + PCI_EXP_FLAGS,
+ 2, &pcie_flags);
+ pcie_flags = (pcie_flags & PCI_EXP_FLAGS_TYPE) >> 4;
+ if (pcie_flags == PCI_EXP_TYPE_ROOT_PORT)
+ edev->mode |= EEH_DEV_ROOT_PORT;
+ else if (pcie_flags == PCI_EXP_TYPE_DOWNSTREAM)
+ edev->mode |= EEH_DEV_DS_PORT;
+ }
+ }
/* Retrieve the device address */
regs = of_get_property(dn, "reg", NULL);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 07/11] powerpc/pci: Partial hotplug support
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
When EEH error happens to one specific PE, the device drivers
of its attached EEH devices (PCI devices) are checked to see
the further action: reset with complete hotplug, or reset without
hotplug. However, that's not enough for those PCI devices whose
drivers can't support EEH, or those PCI devices without driver.
So we need do so-called "partial hotplug" on basis of PCI devices.
In the situation, part of PCI devices of the specific PE are
unplugged and plugged again after PE reset.
The patch changes pcibios_add_pci_devices() so that it can support
full hotplug and so-called "partial" hotplug based on device-tree
or real hardware. It's notable that pci_of_scan.c has been changed
for a bit in order to support the "partial" hotplug based on dev-tree.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/kernel/pci-common.c | 1 +
arch/powerpc/kernel/pci-hotplug.c | 6 +--
arch/powerpc/kernel/pci_of_scan.c | 56 ++++++++++++++++++++++++++----------
3 files changed, 43 insertions(+), 20 deletions(-)
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index f46914a..f80a4f4 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -1462,6 +1462,7 @@ void pcibios_finish_adding_to_bus(struct pci_bus *bus)
/* Allocate bus and devices resources */
pcibios_allocate_bus_resources(bus);
pcibios_claim_one_bus(bus);
+ pci_assign_unassigned_bus_resources(bus);
/* Fixup EEH */
eeh_add_device_tree_late(bus);
diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c
index fc0831d..8e9b1c2 100644
--- a/arch/powerpc/kernel/pci-hotplug.c
+++ b/arch/powerpc/kernel/pci-hotplug.c
@@ -71,7 +71,7 @@ EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
*/
void pcibios_add_pci_devices(struct pci_bus * bus)
{
- int slotno, num, mode, pass, max;
+ int slotno, mode, pass, max;
struct pci_dev *dev;
struct device_node *dn = pci_bus_to_OF_node(bus);
@@ -87,9 +87,7 @@ void pcibios_add_pci_devices(struct pci_bus * 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;
+ pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
pcibios_setup_bus_devices(bus);
max = bus->busn_res.start;
for (pass = 0; pass < 2; pass++) {
diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c
index 6b0ba58..15d9105 100644
--- a/arch/powerpc/kernel/pci_of_scan.c
+++ b/arch/powerpc/kernel/pci_of_scan.c
@@ -230,11 +230,14 @@ void of_scan_pci_bridge(struct pci_dev *dev)
return;
}
- bus = pci_add_new_bus(dev->bus, dev, busrange[0]);
+ bus = pci_find_bus(pci_domain_nr(dev->bus), busrange[0]);
if (!bus) {
- printk(KERN_ERR "Failed to create pci bus for %s\n",
- node->full_name);
- return;
+ bus = pci_add_new_bus(dev->bus, dev, busrange[0]);
+ if (!bus) {
+ printk(KERN_ERR "Failed to create pci bus for %s\n",
+ node->full_name);
+ return;
+ }
}
bus->primary = dev->bus->number;
@@ -292,6 +295,38 @@ void of_scan_pci_bridge(struct pci_dev *dev)
}
EXPORT_SYMBOL(of_scan_pci_bridge);
+static struct pci_dev *of_scan_pci_dev(struct pci_bus *bus,
+ struct device_node *dn)
+{
+ struct pci_dev *dev = NULL;
+ const u32 *reg;
+ int reglen, devfn;
+
+ pr_debug(" * %s\n", dn->full_name);
+ if (!of_device_is_available(dn))
+ return NULL;
+
+ reg = of_get_property(dn, "reg", ®len);
+ if (reg == NULL || reglen < 20)
+ return NULL;
+ devfn = (reg[0] >> 8) & 0xff;
+
+ /* Check if the PCI device is already there */
+ dev = pci_get_slot(bus, devfn);
+ if (dev) {
+ pci_dev_put(dev);
+ return dev;
+ }
+
+ /* create a new pci_dev for this device */
+ dev = of_create_pci_dev(dn, bus, devfn);
+ if (!dev)
+ return NULL;
+
+ pr_debug(" dev header type: %x\n", dev->hdr_type);
+ return dev;
+}
+
/**
* __of_scan_bus - given a PCI bus node, setup bus and scan for child devices
* @node: device tree node for the PCI bus
@@ -302,8 +337,6 @@ static void __of_scan_bus(struct device_node *node, struct pci_bus *bus,
int rescan_existing)
{
struct device_node *child;
- const u32 *reg;
- int reglen, devfn;
struct pci_dev *dev;
pr_debug("of_scan_bus(%s) bus no %d...\n",
@@ -311,16 +344,7 @@ static void __of_scan_bus(struct device_node *node, struct pci_bus *bus,
/* Scan direct children */
for_each_child_of_node(node, child) {
- pr_debug(" * %s\n", child->full_name);
- if (!of_device_is_available(child))
- continue;
- reg = of_get_property(child, "reg", ®len);
- if (reg == NULL || reglen < 20)
- continue;
- devfn = (reg[0] >> 8) & 0xff;
-
- /* create a new pci_dev for this device */
- dev = of_create_pci_dev(child, bus, devfn);
+ dev = of_scan_pci_dev(bus, child);
if (!dev)
continue;
pr_debug(" dev header type: %x\n", dev->hdr_type);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 08/11] powerpc/eeh: Support partial hotplug
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
When EEH error happens to one specific PE, some devices with drivers
supporting EEH won't except hotplug on the deivce. However, there
might have other deivces without driver, or with driver without EEH
support. For the case, we need do partial hotplug in order to make
sure that the PE becomes absolutely quite during reset. Otherise,
the PE reset might fail and leads to failure of error recovery.
The patch intends to support so-called "partial" hotplug for EEH:
Before we do reset, we stop and remove those PCI devices without
EEH sensitive driver. The corresponding EEH devices are not detached
from its PE, but with special flag. After the reset is done, those
EEH devices with the special flag will be scanned one by one.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 6 ++-
arch/powerpc/kernel/eeh.c | 30 +++++++++-
arch/powerpc/kernel/eeh_driver.c | 74 ++++++++++++++++++++++++--
arch/powerpc/kernel/eeh_pe.c | 20 +++-----
arch/powerpc/kernel/eeh_sysfs.c | 7 +++
arch/powerpc/platforms/powernv/eeh-powernv.c | 2 +-
arch/powerpc/platforms/pseries/eeh_pseries.c | 2 +-
7 files changed, 117 insertions(+), 24 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index e8c411b..f54a601 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -84,7 +84,8 @@ struct eeh_pe {
* another tree except the currently existing tree of PCI
* buses and PCI devices
*/
-#define EEH_DEV_IRQ_DISABLED (1<<0) /* Interrupt disabled */
+#define EEH_DEV_IRQ_DISABLED (1 << 0) /* Interrupt disabled */
+#define EEH_DEV_DISCONNECTED (1 << 1) /* Removing from PE */
struct eeh_dev {
int mode; /* EEH mode */
@@ -97,6 +98,7 @@ struct eeh_dev {
struct pci_controller *phb; /* Associated PHB */
struct device_node *dn; /* Associated device node */
struct pci_dev *pdev; /* Associated PCI device */
+ struct pci_bus *bus; /* PCI bus for partial hotplug */
};
static inline struct device_node *eeh_dev_to_of_node(struct eeh_dev *edev)
@@ -197,6 +199,8 @@ struct eeh_pe *eeh_pe_get(struct eeh_dev *edev);
int eeh_add_to_parent_pe(struct eeh_dev *edev);
int eeh_rmv_from_parent_pe(struct eeh_dev *edev);
void eeh_pe_update_time_stamp(struct eeh_pe *pe);
+void *eeh_pe_traverse(struct eeh_pe *root,
+ eeh_traverse_func fn, void *flag);
void *eeh_pe_dev_traverse(struct eeh_pe *root,
eeh_traverse_func fn, void *flag);
void eeh_pe_restore_bars(struct eeh_pe *pe);
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 56bd458..a5783f1 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -900,7 +900,21 @@ void eeh_add_device_late(struct pci_dev *dev)
pr_debug("EEH: Already referenced !\n");
return;
}
- WARN_ON(edev->pdev);
+
+ /*
+ * The EEH cache might not be removed correctly because of
+ * unbalanced kref to the device during unplug time, which
+ * relies on pcibios_release_device(). So we have to remove
+ * that here explicitly.
+ */
+ if (edev->pdev) {
+ eeh_rmv_from_parent_pe(edev);
+ eeh_addr_cache_rmv_dev(edev->pdev);
+ eeh_sysfs_remove_device(edev->pdev);
+
+ edev->pdev = NULL;
+ dev->dev.archdata.edev = NULL;
+ }
edev->pdev = dev;
dev->dev.archdata.edev = edev;
@@ -982,14 +996,24 @@ void eeh_remove_device(struct pci_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) {
+ if (!edev || !edev->pdev || !edev->pe) {
pr_debug("EEH: Not referenced !\n");
return;
}
+
+ /*
+ * During the hotplug for EEH error recovery, we need the EEH
+ * device attached to the parent PE in order for BAR restore
+ * a bit later. So we keep it for BAR restore and remove it
+ * from the parent PE during the BAR resotre.
+ */
edev->pdev = NULL;
dev->dev.archdata.edev = NULL;
+ if (!(edev->pe->state & EEH_PE_KEEP))
+ eeh_rmv_from_parent_pe(edev);
+ else
+ edev->mode |= EEH_DEV_DISCONNECTED;
- eeh_rmv_from_parent_pe(edev);
eeh_addr_cache_rmv_dev(dev);
eeh_sysfs_remove_device(dev);
}
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 9ef3bbb..9fda75d 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -338,6 +338,54 @@ static void *eeh_report_failure(void *data, void *userdata)
return NULL;
}
+static void *eeh_rmv_device(void *data, void *userdata)
+{
+ struct pci_driver *driver;
+ struct eeh_dev *edev = (struct eeh_dev *)data;
+ struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
+ int *removed = (int *)userdata;
+
+ /*
+ * Actually, we should remove the PCI bridges as well.
+ * However, that's lots of complexity to do that,
+ * particularly some of devices under the bridge might
+ * support EEH. So we just care about PCI devices for
+ * simplicity here.
+ */
+ if (!dev || (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE))
+ return NULL;
+ driver = eeh_pcid_get(dev);
+ if (driver && driver->err_handler)
+ return NULL;
+
+ /* Remove it from PCI subsystem */
+ pr_debug("EEH: Removing %s without EEH sensitive driver\n",
+ pci_name(dev));
+ edev->bus = dev->bus;
+ edev->mode |= EEH_DEV_DISCONNECTED;
+ (*removed)++;
+
+ pci_stop_and_remove_bus_device(dev);
+
+ return NULL;
+}
+
+static void *eeh_pe_detach_dev(void *data, void *userdata)
+{
+ struct eeh_pe *pe = (struct eeh_pe *)data;
+ struct eeh_dev *edev, *tmp;
+
+ eeh_pe_for_each_dev(pe, edev, tmp) {
+ if (!(edev->mode & EEH_DEV_DISCONNECTED))
+ continue;
+
+ edev->mode &= ~(EEH_DEV_DISCONNECTED | EEH_DEV_IRQ_DISABLED);
+ eeh_rmv_from_parent_pe(edev);
+ }
+
+ return NULL;
+}
+
/**
* eeh_reset_device - Perform actual reset of a pci slot
* @pe: EEH PE
@@ -349,8 +397,9 @@ static void *eeh_report_failure(void *data, void *userdata)
*/
static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
{
+ struct pci_bus *frozen_bus = eeh_pe_bus_get(pe);
struct timeval tstamp;
- int cnt, rc;
+ int cnt, rc, removed = 0;
/* pcibios will clear the counter; save the value */
cnt = pe->freeze_count;
@@ -362,10 +411,11 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
* devices are expected to be attached soon when calling
* into pcibios_add_pci_devices().
*/
- if (bus) {
- eeh_pe_state_mark(pe, EEH_PE_KEEP);
+ eeh_pe_state_mark(pe, EEH_PE_KEEP);
+ if (bus)
pcibios_remove_pci_devices(bus);
- }
+ else if (frozen_bus)
+ eeh_pe_dev_traverse(pe, eeh_rmv_device, &removed);
/* Reset the pci controller. (Asserts RST#; resets config space).
* Reconfigure bridges and devices. Don't try to bring the system
@@ -386,10 +436,24 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
* potentially weird things happen.
*/
if (bus) {
+ pr_info("EEH: Sleep 5s ahead of complete hotplug\n");
ssleep(5);
+
+ /*
+ * The EEH device is still connected with its parent
+ * PE. We should disconnect it so the binding can be
+ * rebuilt when adding PCI devices.
+ */
+ eeh_pe_traverse(pe, eeh_pe_detach_dev, NULL);
pcibios_add_pci_devices(bus);
- eeh_pe_state_clear(pe, EEH_PE_KEEP);
+ } else if (frozen_bus && removed) {
+ pr_info("EEH: Sleep 5s ahead of partial hotplug\n");
+ ssleep(5);
+
+ eeh_pe_traverse(pe, eeh_pe_detach_dev, NULL);
+ pcibios_add_pci_devices(frozen_bus);
}
+ eeh_pe_state_clear(pe, EEH_PE_KEEP);
pe->tstamp = tstamp;
pe->freeze_count = cnt;
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index c8b815e..2aa955a 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -149,8 +149,8 @@ static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
* 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)
+void *eeh_pe_traverse(struct eeh_pe *root,
+ eeh_traverse_func fn, void *flag)
{
struct eeh_pe *pe;
void *ret;
@@ -409,8 +409,8 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
int cnt;
if (!edev->pe) {
- pr_warning("%s: No PE found for EEH device %s\n",
- __func__, edev->dn->full_name);
+ pr_debug("%s: No PE found for EEH device %s\n",
+ __func__, edev->dn->full_name);
return -EEXIST;
}
@@ -728,18 +728,12 @@ static void eeh_restore_device_bars(struct eeh_dev *edev,
*/
static void *eeh_restore_one_device_bars(void *data, void *flag)
{
- struct pci_dev *pdev = NULL;
struct eeh_dev *edev = (struct eeh_dev *)data;
+ struct pci_dev *pdev = eeh_dev_to_pci_dev(edev);
struct device_node *dn = eeh_dev_to_of_node(edev);
- /* Trace the PCI bridge */
- if (eeh_probe_mode_dev()) {
- pdev = eeh_dev_to_pci_dev(edev);
- if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
- pdev = NULL;
- }
-
- if (pdev)
+ /* Do special restore for bridges */
+ if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
eeh_restore_bridge_bars(pdev, edev, dn);
else
eeh_restore_device_bars(edev, dn);
diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c
index e7ae348..61e2a14 100644
--- a/arch/powerpc/kernel/eeh_sysfs.c
+++ b/arch/powerpc/kernel/eeh_sysfs.c
@@ -68,6 +68,13 @@ void eeh_sysfs_add_device(struct pci_dev *pdev)
void eeh_sysfs_remove_device(struct pci_dev *pdev)
{
+ /*
+ * The parent directory might have been removed. We needn't
+ * continue for that case.
+ */
+ if (!pdev->dev.kobj.sd)
+ return;
+
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/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index 969cce7..a380428 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -114,7 +114,7 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
* the root bridge. So it's not reasonable to continue
* the probing.
*/
- if (!dn || !edev)
+ if (!dn || !edev || edev->pe)
return 0;
/* Skip for PCI-ISA bridge */
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index b456b15..0f44f9f 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -153,7 +153,7 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
/* Retrieve OF node and eeh device */
edev = of_node_to_eeh_dev(dn);
- if (!of_device_is_available(dn))
+ if (edev->pe || !of_device_is_available(dn))
return NULL;
/* Retrieve class/vendor/device IDs */
--
1.7.5.4
^ permalink raw reply related
* [PATCH 11/11] powerpc/eeh: Introdce flag to protect sysfs
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
The patch introduces flag EEH_DEV_SYSFS to trace that the sysfs for
the corresponding EEH device (then PCI device) has been added or
removed, in order to avoid race condition.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 2 ++
arch/powerpc/kernel/eeh.c | 2 ++
arch/powerpc/kernel/eeh_sysfs.c | 16 +++++++++++++++-
arch/powerpc/platforms/powernv/eeh-powernv.c | 4 ++--
arch/powerpc/platforms/pseries/eeh_pseries.c | 2 +-
5 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 4199d99..d3e5e9b 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -90,6 +90,8 @@ struct eeh_pe {
#define EEH_DEV_IRQ_DISABLED (1 << 3) /* Interrupt disabled */
#define EEH_DEV_DISCONNECTED (1 << 4) /* Removing from PE */
+#define EEH_DEV_SYSFS (1 << 8) /* Sysfs created */
+
struct eeh_dev {
int mode; /* EEH mode */
int class_code; /* Class code of the device */
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index a5783f1..ea9414c8 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -911,6 +911,7 @@ void eeh_add_device_late(struct pci_dev *dev)
eeh_rmv_from_parent_pe(edev);
eeh_addr_cache_rmv_dev(edev->pdev);
eeh_sysfs_remove_device(edev->pdev);
+ edev->mode &= ~EEH_DEV_SYSFS;
edev->pdev = NULL;
dev->dev.archdata.edev = NULL;
@@ -1016,6 +1017,7 @@ void eeh_remove_device(struct pci_dev *dev)
eeh_addr_cache_rmv_dev(dev);
eeh_sysfs_remove_device(dev);
+ edev->mode &= ~EEH_DEV_SYSFS;
}
static int proc_eeh_show(struct seq_file *m, void *v)
diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c
index 61e2a14..5d753d4 100644
--- a/arch/powerpc/kernel/eeh_sysfs.c
+++ b/arch/powerpc/kernel/eeh_sysfs.c
@@ -56,26 +56,40 @@ EEH_SHOW_ATTR(eeh_pe_config_addr, pe_config_addr, "0x%x");
void eeh_sysfs_add_device(struct pci_dev *pdev)
{
+ struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
int rc=0;
+ if (edev && (edev->mode & EEH_DEV_SYSFS))
+ return;
+
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");
+ else if (edev)
+ edev->mode |= EEH_DEV_SYSFS;
}
void eeh_sysfs_remove_device(struct pci_dev *pdev)
{
+ struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
+
/*
* The parent directory might have been removed. We needn't
* continue for that case.
*/
- if (!pdev->dev.kobj.sd)
+ if (!pdev->dev.kobj.sd) {
+ if (edev)
+ edev->mode &= ~EEH_DEV_SYSFS;
return;
+ }
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);
+
+ if (edev)
+ edev->mode &= ~EEH_DEV_SYSFS;
}
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index 4361a5c..79663d2 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -122,8 +122,8 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
return 0;
/* Initialize eeh device */
- edev->class_code = dev->class;
- edev->mode = 0;
+ edev->class_code = dev->class;
+ edev->mode &= 0xFFFFFF00;
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
edev->mode |= EEH_DEV_BRIDGE;
if (pci_is_pcie(dev)) {
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 9e80f0a..7fbc25b 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -218,7 +218,7 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
*/
edev->class_code = *class_code;
edev->pcie_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_EXP);
- edev->mode = 0;
+ edev->mode &= 0xFFFFFF00;
if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {
edev->mode |= EEH_DEV_BRIDGE;
if (edev->pcie_cap) {
--
1.7.5.4
^ permalink raw reply related
* [PATCH v3 0/11] EEH Followup Fixes (II)
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
The series of patches bases on linux-poerpc-next initially and intends to resolve
the following problems:
- On pSeries platform, the EEH doesn't work after PHB hotplug
with "drmgr". The root cause is that the EEH resources (
EEH devices, EEH caches) aren't released correctly. For the
problem, we add one hook (pcibios_stop_dev), which is called
on pci_stop_and_remove_device(). In pcibios_stop_dev(), we
release the EEH resources.
- Another issue is that we need put the domain (PE or PHB) into
quite state while doing reset on that domain. However, some
deivces in the domain might not have EEH sensitive drivers, or
even don't have driver. Those deivces can't be put into quite
state and possibly keep issuing PCI-CFG or MMIO request during
resetting the domain. That possibly causes the failure of reset
and eventually failure of EEH recovery. For the issue, we introduces
so-called "partial hotplug". That means, those devices without driver or
without EEH sensitive driver are removed before doing reset, and
plugged (probed) into the system after reset.
- We need traverse EEH devices of one specific PE with safe variant
of list tranverse function. The EEH device might be removed while
doing iteration.
- When doing plug for PCI bus, we need check if we need reassign the
resources for subordinate devices (PCI_REASSIGN_ALL_RSRC) and do that
accordingly.
The patchset is verified on pSeires and PowerNV platforms:
pSeries Platform:
drmgr -c phb -r -s "PHB 513"
drmgr -c phb -a -s "PHB 513"
errinjct eeh -f 1 -s net/eth2
PowerNV Platform:
cd /sys/devices/pci0005:00/0005:00:00.0/0005:01:00.0/0005:02:08.0/0005:80:00.0/0005:90:01.0
while true; do od -x config > /dev/null; sleep 1; done
echo 1 > /sys/kernel/debug/powerpc/PCI0005/err_injct
---
v2 -> v3:
* Make pcibios_add_pci_devices() to support "partial" hotplug
according to Ben's comments. arch/powerpc/kernel/pci_of_scan.c
has been adjusted for that.
* Use pcibios_add_pci_devices() to do "partial" hotplug inside
eeh_reset_device().
* Introduce flag EEH_DEV_SYSFS to trace the state of sysfs entries
of the EEH device (then PCI device) to avoid race condition during
"partial" hotplug.
v1 -> v2:
* Rebase to 3.11.rc1 in order to use pcibios_release_device().
* Use pcibios_release_device() to release EEH cache and detach
EEH device from PCI device.
* Remove reference to PCI device in EEH cache since we're relying
on pcibios_release_device().
* PCI device instance (struct pci_dev) isn't available during BAR
restore and avoid use the instance that time.
* Fix unbalanced enable for IRQ in eeh_driver.c
* Retest the series of patches on Firebird-L/VPL3/VPL4
---
arch/powerpc/include/asm/eeh.h | 30 ++++++++--
arch/powerpc/include/asm/pci-bridge.h | 1 -
arch/powerpc/kernel/eeh.c | 70 +++++++++++------------
arch/powerpc/kernel/eeh_cache.c | 18 ++----
arch/powerpc/kernel/eeh_driver.c | 77 +++++++++++++++++++++++++-
arch/powerpc/kernel/eeh_pe.c | 58 ++++++++-----------
arch/powerpc/kernel/eeh_sysfs.c | 21 +++++++
arch/powerpc/kernel/pci-common.c | 1 +
arch/powerpc/kernel/pci-hotplug.c | 41 ++++++--------
arch/powerpc/kernel/pci_of_scan.c | 56 +++++++++++++-----
arch/powerpc/platforms/powernv/eeh-powernv.c | 17 +++++-
arch/powerpc/platforms/pseries/eeh_pseries.c | 67 +++++++++++++++++++++-
drivers/pci/hotplug/rpadlpar_core.c | 1 -
13 files changed, 319 insertions(+), 139 deletions(-)
Thanks,
Gavin
^ permalink raw reply
* [PATCH 06/11] powerpc/eeh: Tranverse EEH devices with safe mode
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
Currently, we're transversing EEH devices by list_for_each_entry().
That's not safe enough because the EEH devices might be removed from
its parent PE while doing iteration. The patch replaces that with
list_for_each_entry_safe().
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 4 ++--
arch/powerpc/kernel/eeh.c | 4 ++--
arch/powerpc/kernel/eeh_pe.c | 10 +++++-----
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 2ce22d7..e8c411b 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -74,8 +74,8 @@ struct eeh_pe {
struct list_head child; /* Child PEs */
};
-#define eeh_pe_for_each_dev(pe, edev) \
- list_for_each_entry(edev, &pe->edevs, list)
+#define eeh_pe_for_each_dev(pe, edev, tmp) \
+ list_for_each_entry_safe(edev, tmp, &pe->edevs, list)
/*
* The struct is used to trace EEH state for the associated
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index ce81477..56bd458 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -231,7 +231,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
{
size_t loglen = 0;
- struct eeh_dev *edev;
+ struct eeh_dev *edev, *tmp;
bool valid_cfg_log = true;
/*
@@ -251,7 +251,7 @@ void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
eeh_pe_restore_bars(pe);
pci_regs_buf[0] = 0;
- eeh_pe_for_each_dev(pe, edev) {
+ eeh_pe_for_each_dev(pe, edev, tmp) {
loglen += eeh_gather_pci_data(edev, pci_regs_buf + loglen,
EEH_PCI_REGS_LOG_LEN - loglen);
}
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index 32ef409..c8b815e 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -176,7 +176,7 @@ void *eeh_pe_dev_traverse(struct eeh_pe *root,
eeh_traverse_func fn, void *flag)
{
struct eeh_pe *pe;
- struct eeh_dev *edev;
+ struct eeh_dev *edev, *tmp;
void *ret;
if (!root) {
@@ -186,7 +186,7 @@ void *eeh_pe_dev_traverse(struct eeh_pe *root,
/* Traverse root PE */
for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
- eeh_pe_for_each_dev(pe, edev) {
+ eeh_pe_for_each_dev(pe, edev, tmp) {
ret = fn(edev, flag);
if (ret)
return ret;
@@ -501,7 +501,7 @@ 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 eeh_dev *edev, *tmp;
struct pci_dev *pdev;
/*
@@ -511,8 +511,8 @@ static void *__eeh_pe_state_mark(void *data, void *flag)
* the PCI device driver.
*/
pe->state |= state;
- eeh_pe_for_each_dev(pe, tmp) {
- pdev = eeh_dev_to_pci_dev(tmp);
+ eeh_pe_for_each_dev(pe, edev, tmp) {
+ pdev = eeh_dev_to_pci_dev(edev);
if (pdev)
pdev->error_state = pci_channel_io_frozen;
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 10/11] powerpc/eeh: Fix unbalanced enable for IRQ
From: Gavin Shan @ 2013-07-23 11:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1374577856-1712-1-git-send-email-shangw@linux.vnet.ibm.com>
The patch fixes following issue:
Unbalanced enable for IRQ 23
------------[ cut here ]------------
WARNING: at kernel/irq/manage.c:437
:
NIP [c00000000016de8c] .__enable_irq+0x11c/0x140
LR [c00000000016de88] .__enable_irq+0x118/0x140
Call Trace:
[c000003ea1f23880] [c00000000016de88] .__enable_irq+0x118/0x140 (unreliable)
[c000003ea1f23910] [c00000000016df08] .enable_irq+0x58/0xa0
[c000003ea1f239a0] [c0000000000388b4] .eeh_enable_irq+0xc4/0xe0
[c000003ea1f23a30] [c000000000038a28] .eeh_report_reset+0x78/0x130
[c000003ea1f23ac0] [c000000000037508] .eeh_pe_dev_traverse+0x98/0x170
[c000003ea1f23b60] [c0000000000391ac] .eeh_handle_normal_event+0x2fc/0x3d0
[c000003ea1f23bf0] [c000000000039538] .eeh_handle_event+0x2b8/0x2c0
[c000003ea1f23c90] [c000000000039600] .eeh_event_handler+0xc0/0x170
[c000003ea1f23d30] [c0000000000da9a0] .kthread+0xf0/0x100
[c000003ea1f23e30] [c00000000000a1dc] .ret_from_kernel_thread+0x5c/0x80
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/kernel/eeh_driver.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 9fda75d..36bed5a 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -143,10 +143,14 @@ static void eeh_disable_irq(struct pci_dev *dev)
static void eeh_enable_irq(struct pci_dev *dev)
{
struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
+ struct irq_desc *desc;
if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
edev->mode &= ~EEH_DEV_IRQ_DISABLED;
- enable_irq(dev->irq);
+
+ desc = irq_to_desc(dev->irq);
+ if (desc && desc->depth > 0)
+ enable_irq(dev->irq);
}
}
--
1.7.5.4
^ permalink raw reply related
* Re: [PATCH v3 11/31] net: can: mscan: improve clock API use
From: Gerhard Sittig @ 2013-07-23 11:53 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: Mike Turquette, Detlev Zundel, Wolfram Sang, David Woodhouse,
devicetree-discuss, Greg Kroah-Hartman, Rob Herring, Mark Brown,
Wolfgang Grandegger, Anatolij Gustschin, linuxppc-dev,
linux-arm-kernel, Mauro Carvalho Chehab
In-Reply-To: <51ED2619.5040107@pengutronix.de>
On Mon, Jul 22, 2013 at 14:31 +0200, Marc Kleine-Budde wrote:
>
> On 07/22/2013 02:14 PM, Gerhard Sittig wrote:
> > the .get_clock() callback is run from probe() and might allocate
> > resources, introduce a .put_clock() callback that is run from remove()
> > to undo any allocation activities
>
> looks good
>
> > use devm_get_clk() upon lookup (for SYS and REF) to have the clocks put
> > upon driver unload
>
> fine
>
> > assume that resources get prepared but not necessarily enabled in the
> > setup phase, make the open() and close() callbacks of the CAN network
> > device enable and disable a previously acquired and prepared clock
>
> I think you should call prepare_enable and disable_unprepare in the
> open/close functions.
After more local research, which totally eliminated the need to
pre-enable the CAN related clocks, but might need more discussion
as it touches the common gate support, I've learned something
more:
The CAN clock needs to get enabled during probe() already, since
registers get accessed between probe() for the driver and open()
for the network device -- while access to peripheral registers
crashes the kernel when clocks still are disabled (other hardware
may just hang or provide fake data, neither of this is OK).
But I see the point in your suggestion to prepare _and_ enable
the clock during open() as well -- to have open() cope with
whatever probe() did, after all the driver is shared among
platforms, which may differ in what they do during probe().
So I will:
- make open() of the network device prepare _and_ enable the
clock for the peripheral (if acquired during probe())
- adjust open() because ATM it leaves the clock enabled when the
network device operation fails (the error path is incomplete in
v3)
- make the MPC512x specific probe() time .get_clock() routine not
just prepare but enable the clock as well
- and of course address all the shutdown counter parts of the
above setup paths
This results in:
- specific chip drivers only need to balance their private get
and put clock routines which are called from probe and remove,
common paths DTRT for all of them
- correct operation for MPC512x, where common clock is used
- still everything is neutral for MPC5200 where common clock
isn't used, behaviour is identical to before the change
- no assumptions are made about what occurs or doesn't occur
during probe(), when the network device is used then the clock
is fully setup and operational
- when the CAN network device isn't setup (because device tree
doesn't describe it, or disables that node), then its clock
remains idle (neither gets setup nor enabled)
- complete preparation for future improvement wrt power
consumption, where potential changes remain isolated to the
specific chip (probe() time setup, get_clock() routine) while
the ndo part need not get touched any more
So this is the most appropriate approach I can come up with.
Removing unnecessary devm_put_clk() calls is orthogonal to that.
Putting these in isn't totally wrong (they won't harm, and they
do signal "visual balance" more clearly such that the next person
won't stop and wonder), but it's true that they are redundant.
"Trained persons" will wonder as much about their presence as
untrained persons wonder about their absence. :) Apparently I'm
not well trained yet.
I thought that being explicit and cautious would be good, but the
feedback I got suggests that encoding unnecessary instructions
isn't desirable. So I will remove those devm_put_clk() in v4.
To save us one more iteration, shall I remove those calls only
from error paths during setup? Or shall I remove them from
regular shutdown paths as well? How much pain does the community
feel with harmless yet unnecessary instructions? :)
virtually yours
Gerhard Sittig
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox