* [PATCH 1/4] powerpc: enable relocatable support for 6xx
From: Kevin Hao @ 2013-06-19 9:20 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc
In-Reply-To: <1371633604-4491-1-git-send-email-haokexin@gmail.com>
This is based on the codes in head_44x.S. With this patch the kernel
can only boot from 0 with CONFIG_RELOCATABLE enabled. We will add the
support to boot from a non 0 address in the following patches.
Signed-off-by: Kevin Hao <haokexin@gmail.com>
---
arch/powerpc/Kconfig | 2 +-
arch/powerpc/include/asm/page.h | 2 +-
arch/powerpc/kernel/head_32.S | 103 +++++++++++++++++++++++++++++++++
arch/powerpc/kernel/prom_init_check.sh | 2 +-
4 files changed, 106 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index c33e3ad..8fe2792 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -866,7 +866,7 @@ config DYNAMIC_MEMSTART
config RELOCATABLE
bool "Build a relocatable kernel"
- depends on ADVANCED_OPTIONS && FLATMEM && 44x
+ depends on ADVANCED_OPTIONS && FLATMEM && (44x || 6xx)
select NONSTATIC_KERNEL
help
This builds a kernel image that is capable of running at the
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index 988c812..7145c14 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -207,7 +207,7 @@ extern long long virt_phys_offset;
* On non-Book-E PPC64 PAGE_OFFSET and MEMORY_START are constants so use
* the other definitions for __va & __pa.
*/
-#ifdef CONFIG_BOOKE
+#if defined(CONFIG_BOOKE) || defined(CONFIG_6xx)
#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + VIRT_PHYS_OFFSET))
#define __pa(x) ((unsigned long)(x) - VIRT_PHYS_OFFSET)
#else
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index dc0488b..eb47b13 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -73,6 +73,8 @@ _ENTRY(_start);
nop /* used by __secondary_hold on prep (mtx) and chrp smp */
nop
+ bl perform_relocation
+
/* PMAC
* Enter here with the kernel text, data and bss loaded starting at
* 0, running with virtual == physical mapping.
@@ -149,6 +151,8 @@ __start:
*/
bl early_init
+ bl after_relocation_init
+
/* Switch MMU off, clear BATs and flush TLB. At this point, r3 contains
* the physical address we are running at, returned by early_init()
*/
@@ -180,6 +184,7 @@ __after_mmu_off:
#endif /* CONFIG_6xx */
+#ifndef CONFIG_RELOCATABLE
/*
* We need to run with _start at physical address 0.
* On CHRP, we are loaded at 0x10000 since OF on CHRP uses
@@ -193,6 +198,8 @@ __after_mmu_off:
lis r5,PHYSICAL_START@h
cmplw 0,r4,r5 /* already running at PHYSICAL_START? */
bne relocate_kernel
+#endif
+
/*
* we now have the 1st 16M of ram mapped with the bats.
* prep needs the mmu to be turned on here, but pmac already has it on.
@@ -1263,6 +1270,102 @@ m8260_gorom:
blr
#endif
+perform_relocation:
+#ifdef CONFIG_RELOCATABLE
+/*
+ * Relocate ourselves to the current runtime address.
+ * This is called only by the Boot CPU.
+ * r21 will be loaded with the physical runtime address of _stext
+ * Save the r3~r7 since these registers may contain the values needed
+ * by the following boot code.
+ */
+ mr r22,r3
+ mr r23,r4
+ mr r24,r5
+ mr r25,r6
+ mr r26,r7
+
+ mflr r20
+ bl 0f /* Get our runtime address */
+0: mflr r21 /* Make it accessible */
+ addis r21,r21,(_stext - 0b)@ha
+ addi r21,r21,(_stext - 0b)@l /*Get our current runtime base*/
+
+ /*
+ * We have the runtime address of our base.
+ * We calculate our shift of offset from a 256M page.
+ * We could map the 256M page we belong to at PAGE_OFFSET and
+ * get going from there.
+ */
+ lis r4,KERNELBASE@h
+ ori r4,r4,KERNELBASE@l
+ rlwinm r6,r21,0,4,31 /* r6 = PHYS_START % 256M */
+ rlwinm r5,r4,0,4,31 /* r5 = KERNELBASE % 256M */
+ subf r3,r5,r6 /* r3 = r6 - r5 */
+ add r3,r4,r3 /* Required Virutal Address */
+
+ bl relocate
+ mtlr r20
+ mr r3,r22
+ mr r4,r23
+ mr r5,r24
+ mr r6,r25
+ mr r7,r26
+#endif
+ blr
+
+after_relocation_init:
+#ifdef CONFIG_RELOCATABLE
+ /*
+ * Relocatable kernel support based on processing of dynamic
+ * relocation entries.
+ *
+ * r21 will contain the current offset of _stext
+ */
+ lis r3,kernstart_addr@ha
+ la r3,kernstart_addr@l(r3)
+
+ /* Store kernstart_addr */
+ tophys(r3,r3)
+ stw r21,0(r3)
+
+ /*
+ * Compute the virt_phys_offset :
+ * virt_phys_offset = stext.run - kernstart_addr
+ *
+ * stext.run = (KERNELBASE & ~0xfffffff) + (kernstart_addr & 0xfffffff)
+ * When we relocate, we have :
+ *
+ * (kernstart_addr & 0xfffffff) = (stext.run & 0xfffffff)
+ *
+ * hence:
+ * virt_phys_offset = (KERNELBASE & ~0xfffffff)
+ * - (kernstart_addr & ~0xfffffff)
+ *
+ */
+
+ /* KERNELBASE&~0xfffffff => (r4,r5) */
+ lis r5,KERNELBASE@h
+ rlwinm r5,r5,0,0,3 /* Align to 256M, lower 32bit */
+
+ /* kernelstart_addr & ~0xfffffff => (r6,r7) */
+ rlwinm r7,r21,0,0,3 /* Align to 256M, lower 32bit */
+
+ /*
+ * 64bit subtraction.
+ */
+ subf r5,r7,r5
+
+ /* Store virt_phys_offset */
+ lis r3,virt_phys_offset@h
+ ori r3,r3,virt_phys_offset@l
+
+ tophys(r3,r3)
+ li r4,0
+ stw r4,0(r3) /* Higher 32bit */
+ stw r5,4(r3) /* Lower 32bit */
+#endif
+ blr
/*
* We put a few things here that have to be page-aligned.
diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh
index 3765da6..a24c208 100644
--- a/arch/powerpc/kernel/prom_init_check.sh
+++ b/arch/powerpc/kernel/prom_init_check.sh
@@ -22,7 +22,7 @@ __secondary_hold_acknowledge __secondary_hold_spinloop __start
strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224
reloc_got2 kernstart_addr memstart_addr linux_banner _stext
opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry
-boot_command_line __prom_init_toc_start __prom_init_toc_end"
+boot_command_line __prom_init_toc_start __prom_init_toc_end virt_phys_offset"
NM="$1"
OBJ="$2"
--
1.8.1.4
^ permalink raw reply related
* [PATCH 0/4] powerpc: enable relocatable support for 6xx
From: Kevin Hao @ 2013-06-19 9:20 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc
This patch series enables the relocatable support for 6xx boards.
With these patches:
* the kernel can boot from any address between 0x10000 ~ 0x2000000
* kdump is workable
* a single kernel image can be used as boot or kdump kernel
Boot test on a mpc8260 board. Also passed the build test for the
following configurations:
ppc40x_defconfig
ppc64e_defconfig
ppc64_defconfig
corenet32_smp_defconfig
corenet64_smp_defconfig
ppc44x_defconfig
pmac32_defconfig
pq2fads_defconfig
mpc5200_defconfig
pseries_defconfig
---
Kevin Hao (4):
powerpc: enable relocatable support for 6xx
powerpc: move the exception trampoline helper functions to a separate
file
powerpc: s/kdump/exception/ for the exception trampoline functions
powerpc: make the kernel bootable from non 0 address for 6xx
arch/powerpc/Kconfig | 4 +-
arch/powerpc/include/asm/exception_trampoline.h | 35 ++++++++
arch/powerpc/include/asm/kdump.h | 32 --------
arch/powerpc/include/asm/page.h | 2 +-
arch/powerpc/kernel/Makefile | 1 +
arch/powerpc/kernel/crash_dump.c | 41 ----------
arch/powerpc/kernel/exception_trampoline.c | 82 +++++++++++++++++++
arch/powerpc/kernel/head_32.S | 103 ++++++++++++++++++++++++
arch/powerpc/kernel/prom.c | 4 +-
arch/powerpc/kernel/prom_init_check.sh | 2 +-
arch/powerpc/kernel/setup_32.c | 3 +-
arch/powerpc/kernel/setup_64.c | 4 +-
arch/powerpc/mm/ppc_mmu_32.c | 7 +-
13 files changed, 232 insertions(+), 88 deletions(-)
create mode 100644 arch/powerpc/include/asm/exception_trampoline.h
create mode 100644 arch/powerpc/kernel/exception_trampoline.c
--
1.8.1.4
Thanks,
Kevin
^ permalink raw reply
* [PATCH 2/2] perf tools: Make Power7 events available for perf
From: Runzhen Wang @ 2013-06-19 9:15 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel
Cc: acme, icycoder, xiaoguangrong, paulus, Runzhen Wang, sukadev
In-Reply-To: <1371633326-7696-1-git-send-email-runzhen@linux.vnet.ibm.com>
Power7 supports over 530 different perf events but only a small
subset of these can be specified by name, for the remaining
events, we must specify them by their raw code:
perf stat -e r2003c <application>
This patch makes all the POWER7 events available in sysfs.
So we can instead specify these as:
perf stat -e 'cpu/PM_CMPLU_STALL_DFU/' <application>
where PM_CMPLU_STALL_DFU is the r2003c in previous example.
Before this patch is applied, the size of power7-pmu.o is:
$ size arch/powerpc/perf/power7-pmu.o
text data bss dec hex filename
3073 2720 0 5793 16a1 arch/powerpc/perf/power7-pmu.o
and after the patch is applied, it is:
$ size arch/powerpc/perf/power7-pmu.o
text data bss dec hex filename
14451 31112 0 45563 b1fb arch/powerpc/perf/power7-pmu.o
Signed-off-by: Runzhen Wang <runzhen@linux.vnet.ibm.com>
---
arch/powerpc/perf/power7-pmu.c | 1697 +++++++++++++++++++++++++++++++++++++---
1 file changed, 1608 insertions(+), 89 deletions(-)
diff --git a/arch/powerpc/perf/power7-pmu.c b/arch/powerpc/perf/power7-pmu.c
index d1821b8..55e2404 100644
--- a/arch/powerpc/perf/power7-pmu.c
+++ b/arch/powerpc/perf/power7-pmu.c
@@ -53,37 +53,544 @@
/*
* Power7 event codes.
*/
-#define PME_PM_CYC 0x1e
-#define PME_PM_GCT_NOSLOT_CYC 0x100f8
-#define PME_PM_CMPLU_STALL 0x4000a
-#define PME_PM_INST_CMPL 0x2
-#define PME_PM_LD_REF_L1 0xc880
-#define PME_PM_LD_MISS_L1 0x400f0
-#define PME_PM_BRU_FIN 0x10068
-#define PME_PM_BR_MPRED 0x400f6
-
-#define PME_PM_CMPLU_STALL_FXU 0x20014
-#define PME_PM_CMPLU_STALL_DIV 0x40014
-#define PME_PM_CMPLU_STALL_SCALAR 0x40012
-#define PME_PM_CMPLU_STALL_SCALAR_LONG 0x20018
-#define PME_PM_CMPLU_STALL_VECTOR 0x2001c
-#define PME_PM_CMPLU_STALL_VECTOR_LONG 0x4004a
-#define PME_PM_CMPLU_STALL_LSU 0x20012
-#define PME_PM_CMPLU_STALL_REJECT 0x40016
-#define PME_PM_CMPLU_STALL_ERAT_MISS 0x40018
-#define PME_PM_CMPLU_STALL_DCACHE_MISS 0x20016
-#define PME_PM_CMPLU_STALL_STORE 0x2004a
-#define PME_PM_CMPLU_STALL_THRD 0x1001c
-#define PME_PM_CMPLU_STALL_IFU 0x4004c
-#define PME_PM_CMPLU_STALL_BRU 0x4004e
-#define PME_PM_GCT_NOSLOT_IC_MISS 0x2001a
-#define PME_PM_GCT_NOSLOT_BR_MPRED 0x4001a
-#define PME_PM_GCT_NOSLOT_BR_MPRED_IC_MISS 0x4001c
-#define PME_PM_GRP_CMPL 0x30004
-#define PME_PM_1PLUS_PPC_CMPL 0x100f2
-#define PME_PM_CMPLU_STALL_DFU 0x2003c
-#define PME_PM_RUN_CYC 0x200f4
-#define PME_PM_RUN_INST_CMPL 0x400fa
+#define PME_PM_IC_DEMAND_L2_BR_ALL 0x4898
+#define PME_PM_GCT_UTIL_7_TO_10_SLOTS 0x20a0
+#define PME_PM_PMC2_SAVED 0x10022
+#define PME_PM_CMPLU_STALL_DFU 0x2003c
+#define PME_PM_VSU0_16FLOP 0xa0a4
+#define PME_PM_MRK_LSU_DERAT_MISS 0x3d05a
+#define PME_PM_MRK_ST_CMPL 0x10034
+#define PME_PM_NEST_PAIR3_ADD 0x40881
+#define PME_PM_L2_ST_DISP 0x46180
+#define PME_PM_L2_CASTOUT_MOD 0x16180
+#define PME_PM_ISEG 0x20a4
+#define PME_PM_MRK_INST_TIMEO 0x40034
+#define PME_PM_L2_RCST_DISP_FAIL_ADDR 0x36282
+#define PME_PM_LSU1_DC_PREF_STREAM_CONFIRM 0xd0b6
+#define PME_PM_IERAT_WR_64K 0x40be
+#define PME_PM_MRK_DTLB_MISS_16M 0x4d05e
+#define PME_PM_IERAT_MISS 0x100f6
+#define PME_PM_MRK_PTEG_FROM_LMEM 0x4d052
+#define PME_PM_FLOP 0x100f4
+#define PME_PM_THRD_PRIO_4_5_CYC 0x40b4
+#define PME_PM_BR_PRED_TA 0x40aa
+#define PME_PM_CMPLU_STALL_FXU 0x20014
+#define PME_PM_EXT_INT 0x200f8
+#define PME_PM_VSU_FSQRT_FDIV 0xa888
+#define PME_PM_MRK_LD_MISS_EXPOSED_CYC 0x1003e
+#define PME_PM_LSU1_LDF 0xc086
+#define PME_PM_IC_WRITE_ALL 0x488c
+#define PME_PM_LSU0_SRQ_STFWD 0xc0a0
+#define PME_PM_PTEG_FROM_RL2L3_MOD 0x1c052
+#define PME_PM_MRK_DATA_FROM_L31_SHR 0x1d04e
+#define PME_PM_DATA_FROM_L21_MOD 0x3c046
+#define PME_PM_VSU1_SCAL_DOUBLE_ISSUED 0xb08a
+#define PME_PM_VSU0_8FLOP 0xa0a0
+#define PME_PM_POWER_EVENT1 0x1006e
+#define PME_PM_DISP_CLB_HELD_BAL 0x2092
+#define PME_PM_VSU1_2FLOP 0xa09a
+#define PME_PM_LWSYNC_HELD 0x209a
+#define PME_PM_PTEG_FROM_DL2L3_SHR 0x3c054
+#define PME_PM_INST_FROM_L21_MOD 0x34046
+#define PME_PM_IERAT_XLATE_WR_16MPLUS 0x40bc
+#define PME_PM_IC_REQ_ALL 0x4888
+#define PME_PM_DSLB_MISS 0xd090
+#define PME_PM_L3_MISS 0x1f082
+#define PME_PM_LSU0_L1_PREF 0xd0b8
+#define PME_PM_VSU_SCALAR_SINGLE_ISSUED 0xb884
+#define PME_PM_LSU1_DC_PREF_STREAM_CONFIRM_STRIDE 0xd0be
+#define PME_PM_L2_INST 0x36080
+#define PME_PM_VSU0_FRSP 0xa0b4
+#define PME_PM_FLUSH_DISP 0x2082
+#define PME_PM_PTEG_FROM_L2MISS 0x4c058
+#define PME_PM_VSU1_DQ_ISSUED 0xb09a
+#define PME_PM_CMPLU_STALL_LSU 0x20012
+#define PME_PM_MRK_DATA_FROM_DMEM 0x1d04a
+#define PME_PM_LSU_FLUSH_ULD 0xc8b0
+#define PME_PM_PTEG_FROM_LMEM 0x4c052
+#define PME_PM_MRK_DERAT_MISS_16M 0x3d05c
+#define PME_PM_THRD_ALL_RUN_CYC 0x2000c
+#define PME_PM_MEM0_PREFETCH_DISP 0x20083
+#define PME_PM_MRK_STALL_CMPLU_CYC_COUNT 0x3003f
+#define PME_PM_DATA_FROM_DL2L3_MOD 0x3c04c
+#define PME_PM_VSU_FRSP 0xa8b4
+#define PME_PM_MRK_DATA_FROM_L21_MOD 0x3d046
+#define PME_PM_PMC1_OVERFLOW 0x20010
+#define PME_PM_VSU0_SINGLE 0xa0a8
+#define PME_PM_MRK_PTEG_FROM_L3MISS 0x2d058
+#define PME_PM_MRK_PTEG_FROM_L31_SHR 0x2d056
+#define PME_PM_VSU0_VECTOR_SP_ISSUED 0xb090
+#define PME_PM_VSU1_FEST 0xa0ba
+#define PME_PM_MRK_INST_DISP 0x20030
+#define PME_PM_VSU0_COMPLEX_ISSUED 0xb096
+#define PME_PM_LSU1_FLUSH_UST 0xc0b6
+#define PME_PM_INST_CMPL 0x2
+#define PME_PM_FXU_IDLE 0x1000e
+#define PME_PM_LSU0_FLUSH_ULD 0xc0b0
+#define PME_PM_MRK_DATA_FROM_DL2L3_MOD 0x3d04c
+#define PME_PM_LSU_LMQ_SRQ_EMPTY_ALL_CYC 0x3001c
+#define PME_PM_LSU1_REJECT_LMQ_FULL 0xc0a6
+#define PME_PM_INST_PTEG_FROM_L21_MOD 0x3e056
+#define PME_PM_INST_FROM_RL2L3_MOD 0x14042
+#define PME_PM_SHL_CREATED 0x5082
+#define PME_PM_L2_ST_HIT 0x46182
+#define PME_PM_DATA_FROM_DMEM 0x1c04a
+#define PME_PM_L3_LD_MISS 0x2f082
+#define PME_PM_FXU1_BUSY_FXU0_IDLE 0x4000e
+#define PME_PM_DISP_CLB_HELD_RES 0x2094
+#define PME_PM_L2_SN_SX_I_DONE 0x36382
+#define PME_PM_GRP_CMPL 0x30004
+#define PME_PM_STCX_CMPL 0xc098
+#define PME_PM_VSU0_2FLOP 0xa098
+#define PME_PM_L3_PREF_MISS 0x3f082
+#define PME_PM_LSU_SRQ_SYNC_CYC 0xd096
+#define PME_PM_LSU_REJECT_ERAT_MISS 0x20064
+#define PME_PM_L1_ICACHE_MISS 0x200fc
+#define PME_PM_LSU1_FLUSH_SRQ 0xc0be
+#define PME_PM_LD_REF_L1_LSU0 0xc080
+#define PME_PM_VSU0_FEST 0xa0b8
+#define PME_PM_VSU_VECTOR_SINGLE_ISSUED 0xb890
+#define PME_PM_FREQ_UP 0x4000c
+#define PME_PM_DATA_FROM_LMEM 0x3c04a
+#define PME_PM_LSU1_LDX 0xc08a
+#define PME_PM_PMC3_OVERFLOW 0x40010
+#define PME_PM_MRK_BR_MPRED 0x30036
+#define PME_PM_SHL_MATCH 0x5086
+#define PME_PM_MRK_BR_TAKEN 0x10036
+#define PME_PM_CMPLU_STALL_BRU 0x4004e
+#define PME_PM_ISLB_MISS 0xd092
+#define PME_PM_CYC 0x1e
+#define PME_PM_DISP_HELD_THERMAL 0x30006
+#define PME_PM_INST_PTEG_FROM_RL2L3_SHR 0x2e054
+#define PME_PM_LSU1_SRQ_STFWD 0xc0a2
+#define PME_PM_GCT_NOSLOT_BR_MPRED 0x4001a
+#define PME_PM_1PLUS_PPC_CMPL 0x100f2
+#define PME_PM_PTEG_FROM_DMEM 0x2c052
+#define PME_PM_VSU_2FLOP 0xa898
+#define PME_PM_GCT_FULL_CYC 0x4086
+#define PME_PM_MRK_DATA_FROM_L3_CYC 0x40020
+#define PME_PM_LSU_SRQ_S0_ALLOC 0xd09d
+#define PME_PM_MRK_DERAT_MISS_4K 0x1d05c
+#define PME_PM_BR_MPRED_TA 0x40ae
+#define PME_PM_INST_PTEG_FROM_L2MISS 0x4e058
+#define PME_PM_DPU_HELD_POWER 0x20006
+#define PME_PM_RUN_INST_CMPL 0x400fa
+#define PME_PM_MRK_VSU_FIN 0x30032
+#define PME_PM_LSU_SRQ_S0_VALID 0xd09c
+#define PME_PM_GCT_EMPTY_CYC 0x20008
+#define PME_PM_IOPS_DISP 0x30014
+#define PME_PM_RUN_SPURR 0x10008
+#define PME_PM_PTEG_FROM_L21_MOD 0x3c056
+#define PME_PM_VSU0_1FLOP 0xa080
+#define PME_PM_SNOOP_TLBIE 0xd0b2
+#define PME_PM_DATA_FROM_L3MISS 0x2c048
+#define PME_PM_VSU_SINGLE 0xa8a8
+#define PME_PM_DTLB_MISS_16G 0x1c05e
+#define PME_PM_CMPLU_STALL_VECTOR 0x2001c
+#define PME_PM_FLUSH 0x400f8
+#define PME_PM_L2_LD_HIT 0x36182
+#define PME_PM_NEST_PAIR2_AND 0x30883
+#define PME_PM_VSU1_1FLOP 0xa082
+#define PME_PM_IC_PREF_REQ 0x408a
+#define PME_PM_L3_LD_HIT 0x2f080
+#define PME_PM_GCT_NOSLOT_IC_MISS 0x2001a
+#define PME_PM_DISP_HELD 0x10006
+#define PME_PM_L2_LD 0x16080
+#define PME_PM_LSU_FLUSH_SRQ 0xc8bc
+#define PME_PM_BC_PLUS_8_CONV 0x40b8
+#define PME_PM_MRK_DATA_FROM_L31_MOD_CYC 0x40026
+#define PME_PM_CMPLU_STALL_VECTOR_LONG 0x4004a
+#define PME_PM_L2_RCST_BUSY_RC_FULL 0x26282
+#define PME_PM_TB_BIT_TRANS 0x300f8
+#define PME_PM_THERMAL_MAX 0x40006
+#define PME_PM_LSU1_FLUSH_ULD 0xc0b2
+#define PME_PM_LSU1_REJECT_LHS 0xc0ae
+#define PME_PM_LSU_LRQ_S0_ALLOC 0xd09f
+#define PME_PM_L3_CO_L31 0x4f080
+#define PME_PM_POWER_EVENT4 0x4006e
+#define PME_PM_DATA_FROM_L31_SHR 0x1c04e
+#define PME_PM_BR_UNCOND 0x409e
+#define PME_PM_LSU1_DC_PREF_STREAM_ALLOC 0xd0aa
+#define PME_PM_PMC4_REWIND 0x10020
+#define PME_PM_L2_RCLD_DISP 0x16280
+#define PME_PM_THRD_PRIO_2_3_CYC 0x40b2
+#define PME_PM_MRK_PTEG_FROM_L2MISS 0x4d058
+#define PME_PM_IC_DEMAND_L2_BHT_REDIRECT 0x4098
+#define PME_PM_LSU_DERAT_MISS 0x200f6
+#define PME_PM_IC_PREF_CANCEL_L2 0x4094
+#define PME_PM_MRK_FIN_STALL_CYC_COUNT 0x1003d
+#define PME_PM_BR_PRED_CCACHE 0x40a0
+#define PME_PM_GCT_UTIL_1_TO_2_SLOTS 0x209c
+#define PME_PM_MRK_ST_CMPL_INT 0x30034
+#define PME_PM_LSU_TWO_TABLEWALK_CYC 0xd0a6
+#define PME_PM_MRK_DATA_FROM_L3MISS 0x2d048
+#define PME_PM_GCT_NOSLOT_CYC 0x100f8
+#define PME_PM_LSU_SET_MPRED 0xc0a8
+#define PME_PM_FLUSH_DISP_TLBIE 0x208a
+#define PME_PM_VSU1_FCONV 0xa0b2
+#define PME_PM_DERAT_MISS_16G 0x4c05c
+#define PME_PM_INST_FROM_LMEM 0x3404a
+#define PME_PM_IC_DEMAND_L2_BR_REDIRECT 0x409a
+#define PME_PM_CMPLU_STALL_SCALAR_LONG 0x20018
+#define PME_PM_INST_PTEG_FROM_L2 0x1e050
+#define PME_PM_PTEG_FROM_L2 0x1c050
+#define PME_PM_MRK_DATA_FROM_L21_SHR_CYC 0x20024
+#define PME_PM_MRK_DTLB_MISS_4K 0x2d05a
+#define PME_PM_VSU0_FPSCR 0xb09c
+#define PME_PM_VSU1_VECT_DOUBLE_ISSUED 0xb082
+#define PME_PM_MRK_PTEG_FROM_RL2L3_MOD 0x1d052
+#define PME_PM_MEM0_RQ_DISP 0x10083
+#define PME_PM_L2_LD_MISS 0x26080
+#define PME_PM_VMX_RESULT_SAT_1 0xb0a0
+#define PME_PM_L1_PREF 0xd8b8
+#define PME_PM_MRK_DATA_FROM_LMEM_CYC 0x2002c
+#define PME_PM_GRP_IC_MISS_NONSPEC 0x1000c
+#define PME_PM_PB_NODE_PUMP 0x10081
+#define PME_PM_SHL_MERGED 0x5084
+#define PME_PM_NEST_PAIR1_ADD 0x20881
+#define PME_PM_DATA_FROM_L3 0x1c048
+#define PME_PM_LSU_FLUSH 0x208e
+#define PME_PM_LSU_SRQ_SYNC_COUNT 0xd097
+#define PME_PM_PMC2_OVERFLOW 0x30010
+#define PME_PM_LSU_LDF 0xc884
+#define PME_PM_POWER_EVENT3 0x3006e
+#define PME_PM_DISP_WT 0x30008
+#define PME_PM_CMPLU_STALL_REJECT 0x40016
+#define PME_PM_IC_BANK_CONFLICT 0x4082
+#define PME_PM_BR_MPRED_CR_TA 0x48ae
+#define PME_PM_L2_INST_MISS 0x36082
+#define PME_PM_CMPLU_STALL_ERAT_MISS 0x40018
+#define PME_PM_NEST_PAIR2_ADD 0x30881
+#define PME_PM_MRK_LSU_FLUSH 0xd08c
+#define PME_PM_L2_LDST 0x16880
+#define PME_PM_INST_FROM_L31_SHR 0x1404e
+#define PME_PM_VSU0_FIN 0xa0bc
+#define PME_PM_LARX_LSU 0xc894
+#define PME_PM_INST_FROM_RMEM 0x34042
+#define PME_PM_DISP_CLB_HELD_TLBIE 0x2096
+#define PME_PM_MRK_DATA_FROM_DMEM_CYC 0x2002e
+#define PME_PM_BR_PRED_CR 0x40a8
+#define PME_PM_LSU_REJECT 0x10064
+#define PME_PM_GCT_UTIL_3_TO_6_SLOTS 0x209e
+#define PME_PM_CMPLU_STALL_END_GCT_NOSLOT 0x10028
+#define PME_PM_LSU0_REJECT_LMQ_FULL 0xc0a4
+#define PME_PM_VSU_FEST 0xa8b8
+#define PME_PM_NEST_PAIR0_AND 0x10883
+#define PME_PM_PTEG_FROM_L3 0x2c050
+#define PME_PM_POWER_EVENT2 0x2006e
+#define PME_PM_IC_PREF_CANCEL_PAGE 0x4090
+#define PME_PM_VSU0_FSQRT_FDIV 0xa088
+#define PME_PM_MRK_GRP_CMPL 0x40030
+#define PME_PM_VSU0_SCAL_DOUBLE_ISSUED 0xb088
+#define PME_PM_GRP_DISP 0x3000a
+#define PME_PM_LSU0_LDX 0xc088
+#define PME_PM_DATA_FROM_L2 0x1c040
+#define PME_PM_MRK_DATA_FROM_RL2L3_MOD 0x1d042
+#define PME_PM_LD_REF_L1 0xc880
+#define PME_PM_VSU0_VECT_DOUBLE_ISSUED 0xb080
+#define PME_PM_VSU1_2FLOP_DOUBLE 0xa08e
+#define PME_PM_THRD_PRIO_6_7_CYC 0x40b6
+#define PME_PM_BC_PLUS_8_RSLV_TAKEN 0x40ba
+#define PME_PM_BR_MPRED_CR 0x40ac
+#define PME_PM_L3_CO_MEM 0x4f082
+#define PME_PM_LD_MISS_L1 0x400f0
+#define PME_PM_DATA_FROM_RL2L3_MOD 0x1c042
+#define PME_PM_LSU_SRQ_FULL_CYC 0x1001a
+#define PME_PM_TABLEWALK_CYC 0x10026
+#define PME_PM_MRK_PTEG_FROM_RMEM 0x3d052
+#define PME_PM_LSU_SRQ_STFWD 0xc8a0
+#define PME_PM_INST_PTEG_FROM_RMEM 0x3e052
+#define PME_PM_FXU0_FIN 0x10004
+#define PME_PM_LSU1_L1_SW_PREF 0xc09e
+#define PME_PM_PTEG_FROM_L31_MOD 0x1c054
+#define PME_PM_PMC5_OVERFLOW 0x10024
+#define PME_PM_LD_REF_L1_LSU1 0xc082
+#define PME_PM_INST_PTEG_FROM_L21_SHR 0x4e056
+#define PME_PM_CMPLU_STALL_THRD 0x1001c
+#define PME_PM_DATA_FROM_RMEM 0x3c042
+#define PME_PM_VSU0_SCAL_SINGLE_ISSUED 0xb084
+#define PME_PM_BR_MPRED_LSTACK 0x40a6
+#define PME_PM_MRK_DATA_FROM_RL2L3_MOD_CYC 0x40028
+#define PME_PM_LSU0_FLUSH_UST 0xc0b4
+#define PME_PM_LSU_NCST 0xc090
+#define PME_PM_BR_TAKEN 0x20004
+#define PME_PM_INST_PTEG_FROM_LMEM 0x4e052
+#define PME_PM_GCT_NOSLOT_BR_MPRED_IC_MISS 0x4001c
+#define PME_PM_DTLB_MISS_4K 0x2c05a
+#define PME_PM_PMC4_SAVED 0x30022
+#define PME_PM_VSU1_PERMUTE_ISSUED 0xb092
+#define PME_PM_SLB_MISS 0xd890
+#define PME_PM_LSU1_FLUSH_LRQ 0xc0ba
+#define PME_PM_DTLB_MISS 0x300fc
+#define PME_PM_VSU1_FRSP 0xa0b6
+#define PME_PM_VSU_VECTOR_DOUBLE_ISSUED 0xb880
+#define PME_PM_L2_CASTOUT_SHR 0x16182
+#define PME_PM_DATA_FROM_DL2L3_SHR 0x3c044
+#define PME_PM_VSU1_STF 0xb08e
+#define PME_PM_ST_FIN 0x200f0
+#define PME_PM_PTEG_FROM_L21_SHR 0x4c056
+#define PME_PM_L2_LOC_GUESS_WRONG 0x26480
+#define PME_PM_MRK_STCX_FAIL 0xd08e
+#define PME_PM_LSU0_REJECT_LHS 0xc0ac
+#define PME_PM_IC_PREF_CANCEL_HIT 0x4092
+#define PME_PM_L3_PREF_BUSY 0x4f080
+#define PME_PM_MRK_BRU_FIN 0x2003a
+#define PME_PM_LSU1_NCLD 0xc08e
+#define PME_PM_INST_PTEG_FROM_L31_MOD 0x1e054
+#define PME_PM_LSU_NCLD 0xc88c
+#define PME_PM_LSU_LDX 0xc888
+#define PME_PM_L2_LOC_GUESS_CORRECT 0x16480
+#define PME_PM_THRESH_TIMEO 0x10038
+#define PME_PM_L3_PREF_ST 0xd0ae
+#define PME_PM_DISP_CLB_HELD_SYNC 0x2098
+#define PME_PM_VSU_SIMPLE_ISSUED 0xb894
+#define PME_PM_VSU1_SINGLE 0xa0aa
+#define PME_PM_DATA_TABLEWALK_CYC 0x3001a
+#define PME_PM_L2_RC_ST_DONE 0x36380
+#define PME_PM_MRK_PTEG_FROM_L21_MOD 0x3d056
+#define PME_PM_LARX_LSU1 0xc096
+#define PME_PM_MRK_DATA_FROM_RMEM 0x3d042
+#define PME_PM_DISP_CLB_HELD 0x2090
+#define PME_PM_DERAT_MISS_4K 0x1c05c
+#define PME_PM_L2_RCLD_DISP_FAIL_ADDR 0x16282
+#define PME_PM_SEG_EXCEPTION 0x28a4
+#define PME_PM_FLUSH_DISP_SB 0x208c
+#define PME_PM_L2_DC_INV 0x26182
+#define PME_PM_PTEG_FROM_DL2L3_MOD 0x4c054
+#define PME_PM_DSEG 0x20a6
+#define PME_PM_BR_PRED_LSTACK 0x40a2
+#define PME_PM_VSU0_STF 0xb08c
+#define PME_PM_LSU_FX_FIN 0x10066
+#define PME_PM_DERAT_MISS_16M 0x3c05c
+#define PME_PM_MRK_PTEG_FROM_DL2L3_MOD 0x4d054
+#define PME_PM_GCT_UTIL_11_PLUS_SLOTS 0x20a2
+#define PME_PM_INST_FROM_L3 0x14048
+#define PME_PM_MRK_IFU_FIN 0x3003a
+#define PME_PM_ITLB_MISS 0x400fc
+#define PME_PM_VSU_STF 0xb88c
+#define PME_PM_LSU_FLUSH_UST 0xc8b4
+#define PME_PM_L2_LDST_MISS 0x26880
+#define PME_PM_FXU1_FIN 0x40004
+#define PME_PM_SHL_DEALLOCATED 0x5080
+#define PME_PM_L2_SN_M_WR_DONE 0x46382
+#define PME_PM_LSU_REJECT_SET_MPRED 0xc8a8
+#define PME_PM_L3_PREF_LD 0xd0ac
+#define PME_PM_L2_SN_M_RD_DONE 0x46380
+#define PME_PM_MRK_DERAT_MISS_16G 0x4d05c
+#define PME_PM_VSU_FCONV 0xa8b0
+#define PME_PM_ANY_THRD_RUN_CYC 0x100fa
+#define PME_PM_LSU_LMQ_FULL_CYC 0xd0a4
+#define PME_PM_MRK_LSU_REJECT_LHS 0xd082
+#define PME_PM_MRK_LD_MISS_L1_CYC 0x4003e
+#define PME_PM_MRK_DATA_FROM_L2_CYC 0x20020
+#define PME_PM_INST_IMC_MATCH_DISP 0x30016
+#define PME_PM_MRK_DATA_FROM_RMEM_CYC 0x4002c
+#define PME_PM_VSU0_SIMPLE_ISSUED 0xb094
+#define PME_PM_CMPLU_STALL_DIV 0x40014
+#define PME_PM_MRK_PTEG_FROM_RL2L3_SHR 0x2d054
+#define PME_PM_VSU_FMA_DOUBLE 0xa890
+#define PME_PM_VSU_4FLOP 0xa89c
+#define PME_PM_VSU1_FIN 0xa0be
+#define PME_PM_NEST_PAIR1_AND 0x20883
+#define PME_PM_INST_PTEG_FROM_RL2L3_MOD 0x1e052
+#define PME_PM_RUN_CYC 0x200f4
+#define PME_PM_PTEG_FROM_RMEM 0x3c052
+#define PME_PM_LSU_LRQ_S0_VALID 0xd09e
+#define PME_PM_LSU0_LDF 0xc084
+#define PME_PM_FLUSH_COMPLETION 0x30012
+#define PME_PM_ST_MISS_L1 0x300f0
+#define PME_PM_L2_NODE_PUMP 0x36480
+#define PME_PM_INST_FROM_DL2L3_SHR 0x34044
+#define PME_PM_MRK_STALL_CMPLU_CYC 0x3003e
+#define PME_PM_VSU1_DENORM 0xa0ae
+#define PME_PM_MRK_DATA_FROM_L31_SHR_CYC 0x20026
+#define PME_PM_NEST_PAIR0_ADD 0x10881
+#define PME_PM_INST_FROM_L3MISS 0x24048
+#define PME_PM_EE_OFF_EXT_INT 0x2080
+#define PME_PM_INST_PTEG_FROM_DMEM 0x2e052
+#define PME_PM_INST_FROM_DL2L3_MOD 0x3404c
+#define PME_PM_PMC6_OVERFLOW 0x30024
+#define PME_PM_VSU_2FLOP_DOUBLE 0xa88c
+#define PME_PM_TLB_MISS 0x20066
+#define PME_PM_FXU_BUSY 0x2000e
+#define PME_PM_L2_RCLD_DISP_FAIL_OTHER 0x26280
+#define PME_PM_LSU_REJECT_LMQ_FULL 0xc8a4
+#define PME_PM_IC_RELOAD_SHR 0x4096
+#define PME_PM_GRP_MRK 0x10031
+#define PME_PM_MRK_ST_NEST 0x20034
+#define PME_PM_VSU1_FSQRT_FDIV 0xa08a
+#define PME_PM_LSU0_FLUSH_LRQ 0xc0b8
+#define PME_PM_LARX_LSU0 0xc094
+#define PME_PM_IBUF_FULL_CYC 0x4084
+#define PME_PM_MRK_DATA_FROM_DL2L3_SHR_CYC 0x2002a
+#define PME_PM_LSU_DC_PREF_STREAM_ALLOC 0xd8a8
+#define PME_PM_GRP_MRK_CYC 0x10030
+#define PME_PM_MRK_DATA_FROM_RL2L3_SHR_CYC 0x20028
+#define PME_PM_L2_GLOB_GUESS_CORRECT 0x16482
+#define PME_PM_LSU_REJECT_LHS 0xc8ac
+#define PME_PM_MRK_DATA_FROM_LMEM 0x3d04a
+#define PME_PM_INST_PTEG_FROM_L3 0x2e050
+#define PME_PM_FREQ_DOWN 0x3000c
+#define PME_PM_PB_RETRY_NODE_PUMP 0x30081
+#define PME_PM_INST_FROM_RL2L3_SHR 0x1404c
+#define PME_PM_MRK_INST_ISSUED 0x10032
+#define PME_PM_PTEG_FROM_L3MISS 0x2c058
+#define PME_PM_RUN_PURR 0x400f4
+#define PME_PM_MRK_GRP_IC_MISS 0x40038
+#define PME_PM_MRK_DATA_FROM_L3 0x1d048
+#define PME_PM_CMPLU_STALL_DCACHE_MISS 0x20016
+#define PME_PM_PTEG_FROM_RL2L3_SHR 0x2c054
+#define PME_PM_LSU_FLUSH_LRQ 0xc8b8
+#define PME_PM_MRK_DERAT_MISS_64K 0x2d05c
+#define PME_PM_INST_PTEG_FROM_DL2L3_MOD 0x4e054
+#define PME_PM_L2_ST_MISS 0x26082
+#define PME_PM_MRK_PTEG_FROM_L21_SHR 0x4d056
+#undef LWSYNC
+#define PME_PM_LWSYNC 0xd094
+#define PME_PM_LSU0_DC_PREF_STREAM_CONFIRM_STRIDE 0xd0bc
+#define PME_PM_MRK_LSU_FLUSH_LRQ 0xd088
+#define PME_PM_INST_IMC_MATCH_CMPL 0x100f0
+#define PME_PM_NEST_PAIR3_AND 0x40883
+#define PME_PM_PB_RETRY_SYS_PUMP 0x40081
+#define PME_PM_MRK_INST_FIN 0x30030
+#define PME_PM_MRK_PTEG_FROM_DL2L3_SHR 0x3d054
+#define PME_PM_INST_FROM_L31_MOD 0x14044
+#define PME_PM_MRK_DTLB_MISS_64K 0x3d05e
+#define PME_PM_LSU_FIN 0x30066
+#define PME_PM_MRK_LSU_REJECT 0x40064
+#define PME_PM_L2_CO_FAIL_BUSY 0x16382
+#define PME_PM_MEM0_WQ_DISP 0x40083
+#define PME_PM_DATA_FROM_L31_MOD 0x1c044
+#define PME_PM_THERMAL_WARN 0x10016
+#define PME_PM_VSU0_4FLOP 0xa09c
+#define PME_PM_BR_MPRED_CCACHE 0x40a4
+#define PME_PM_CMPLU_STALL_IFU 0x4004c
+#define PME_PM_L1_DEMAND_WRITE 0x408c
+#define PME_PM_FLUSH_BR_MPRED 0x2084
+#define PME_PM_MRK_DTLB_MISS_16G 0x1d05e
+#define PME_PM_MRK_PTEG_FROM_DMEM 0x2d052
+#define PME_PM_L2_RCST_DISP 0x36280
+#define PME_PM_CMPLU_STALL 0x4000a
+#define PME_PM_LSU_PARTIAL_CDF 0xc0aa
+#define PME_PM_DISP_CLB_HELD_SB 0x20a8
+#define PME_PM_VSU0_FMA_DOUBLE 0xa090
+#define PME_PM_FXU0_BUSY_FXU1_IDLE 0x3000e
+#define PME_PM_IC_DEMAND_CYC 0x10018
+#define PME_PM_MRK_DATA_FROM_L21_SHR 0x3d04e
+#define PME_PM_MRK_LSU_FLUSH_UST 0xd086
+#define PME_PM_INST_PTEG_FROM_L3MISS 0x2e058
+#define PME_PM_VSU_DENORM 0xa8ac
+#define PME_PM_MRK_LSU_PARTIAL_CDF 0xd080
+#define PME_PM_INST_FROM_L21_SHR 0x3404e
+#define PME_PM_IC_PREF_WRITE 0x408e
+#define PME_PM_BR_PRED 0x409c
+#define PME_PM_INST_FROM_DMEM 0x1404a
+#define PME_PM_IC_PREF_CANCEL_ALL 0x4890
+#define PME_PM_LSU_DC_PREF_STREAM_CONFIRM 0xd8b4
+#define PME_PM_MRK_LSU_FLUSH_SRQ 0xd08a
+#define PME_PM_MRK_FIN_STALL_CYC 0x1003c
+#define PME_PM_L2_RCST_DISP_FAIL_OTHER 0x46280
+#define PME_PM_VSU1_DD_ISSUED 0xb098
+#define PME_PM_PTEG_FROM_L31_SHR 0x2c056
+#define PME_PM_DATA_FROM_L21_SHR 0x3c04e
+#define PME_PM_LSU0_NCLD 0xc08c
+#define PME_PM_VSU1_4FLOP 0xa09e
+#define PME_PM_VSU1_8FLOP 0xa0a2
+#define PME_PM_VSU_8FLOP 0xa8a0
+#define PME_PM_LSU_LMQ_SRQ_EMPTY_CYC 0x2003e
+#define PME_PM_DTLB_MISS_64K 0x3c05e
+#define PME_PM_THRD_CONC_RUN_INST 0x300f4
+#define PME_PM_MRK_PTEG_FROM_L2 0x1d050
+#define PME_PM_PB_SYS_PUMP 0x20081
+#define PME_PM_VSU_FIN 0xa8bc
+#define PME_PM_MRK_DATA_FROM_L31_MOD 0x1d044
+#define PME_PM_THRD_PRIO_0_1_CYC 0x40b0
+#define PME_PM_DERAT_MISS_64K 0x2c05c
+#define PME_PM_PMC2_REWIND 0x30020
+#define PME_PM_INST_FROM_L2 0x14040
+#define PME_PM_GRP_BR_MPRED_NONSPEC 0x1000a
+#define PME_PM_INST_DISP 0x200f2
+#define PME_PM_MEM0_RD_CANCEL_TOTAL 0x30083
+#define PME_PM_LSU0_DC_PREF_STREAM_CONFIRM 0xd0b4
+#define PME_PM_L1_DCACHE_RELOAD_VALID 0x300f6
+#define PME_PM_VSU_SCALAR_DOUBLE_ISSUED 0xb888
+#define PME_PM_L3_PREF_HIT 0x3f080
+#define PME_PM_MRK_PTEG_FROM_L31_MOD 0x1d054
+#define PME_PM_CMPLU_STALL_STORE 0x2004a
+#define PME_PM_MRK_FXU_FIN 0x20038
+#define PME_PM_PMC4_OVERFLOW 0x10010
+#define PME_PM_MRK_PTEG_FROM_L3 0x2d050
+#define PME_PM_LSU0_LMQ_LHR_MERGE 0xd098
+#define PME_PM_BTAC_HIT 0x508a
+#define PME_PM_L3_RD_BUSY 0x4f082
+#define PME_PM_LSU0_L1_SW_PREF 0xc09c
+#define PME_PM_INST_FROM_L2MISS 0x44048
+#define PME_PM_LSU0_DC_PREF_STREAM_ALLOC 0xd0a8
+#define PME_PM_L2_ST 0x16082
+#define PME_PM_VSU0_DENORM 0xa0ac
+#define PME_PM_MRK_DATA_FROM_DL2L3_SHR 0x3d044
+#define PME_PM_BR_PRED_CR_TA 0x48aa
+#define PME_PM_VSU0_FCONV 0xa0b0
+#define PME_PM_MRK_LSU_FLUSH_ULD 0xd084
+#define PME_PM_BTAC_MISS 0x5088
+#define PME_PM_MRK_LD_MISS_EXPOSED_CYC_COUNT 0x1003f
+#define PME_PM_MRK_DATA_FROM_L2 0x1d040
+#define PME_PM_LSU_DCACHE_RELOAD_VALID 0xd0a2
+#define PME_PM_VSU_FMA 0xa884
+#define PME_PM_LSU0_FLUSH_SRQ 0xc0bc
+#define PME_PM_LSU1_L1_PREF 0xd0ba
+#define PME_PM_IOPS_CMPL 0x10014
+#define PME_PM_L2_SYS_PUMP 0x36482
+#define PME_PM_L2_RCLD_BUSY_RC_FULL 0x46282
+#define PME_PM_LSU_LMQ_S0_ALLOC 0xd0a1
+#define PME_PM_FLUSH_DISP_SYNC 0x2088
+#define PME_PM_MRK_DATA_FROM_DL2L3_MOD_CYC 0x4002a
+#define PME_PM_L2_IC_INV 0x26180
+#define PME_PM_MRK_DATA_FROM_L21_MOD_CYC 0x40024
+#define PME_PM_L3_PREF_LDST 0xd8ac
+#define PME_PM_LSU_SRQ_EMPTY_CYC 0x40008
+#define PME_PM_LSU_LMQ_S0_VALID 0xd0a0
+#define PME_PM_FLUSH_PARTIAL 0x2086
+#define PME_PM_VSU1_FMA_DOUBLE 0xa092
+#define PME_PM_1PLUS_PPC_DISP 0x400f2
+#define PME_PM_DATA_FROM_L2MISS 0x200fe
+#define PME_PM_SUSPENDED 0x0
+#define PME_PM_VSU0_FMA 0xa084
+#define PME_PM_CMPLU_STALL_SCALAR 0x40012
+#define PME_PM_STCX_FAIL 0xc09a
+#define PME_PM_VSU0_FSQRT_FDIV_DOUBLE 0xa094
+#define PME_PM_DC_PREF_DST 0xd0b0
+#define PME_PM_VSU1_SCAL_SINGLE_ISSUED 0xb086
+#define PME_PM_L3_HIT 0x1f080
+#define PME_PM_L2_GLOB_GUESS_WRONG 0x26482
+#define PME_PM_MRK_DFU_FIN 0x20032
+#define PME_PM_INST_FROM_L1 0x4080
+#define PME_PM_BRU_FIN 0x10068
+#define PME_PM_IC_DEMAND_REQ 0x4088
+#define PME_PM_VSU1_FSQRT_FDIV_DOUBLE 0xa096
+#define PME_PM_VSU1_FMA 0xa086
+#define PME_PM_MRK_LD_MISS_L1 0x20036
+#define PME_PM_VSU0_2FLOP_DOUBLE 0xa08c
+#define PME_PM_LSU_DC_PREF_STRIDED_STREAM_CONFIRM 0xd8bc
+#define PME_PM_INST_PTEG_FROM_L31_SHR 0x2e056
+#define PME_PM_MRK_LSU_REJECT_ERAT_MISS 0x30064
+#define PME_PM_MRK_DATA_FROM_L2MISS 0x4d048
+#define PME_PM_DATA_FROM_RL2L3_SHR 0x1c04c
+#define PME_PM_INST_FROM_PREF 0x14046
+#define PME_PM_VSU1_SQ 0xb09e
+#define PME_PM_L2_LD_DISP 0x36180
+#define PME_PM_L2_DISP_ALL 0x46080
+#define PME_PM_THRD_GRP_CMPL_BOTH_CYC 0x10012
+#define PME_PM_VSU_FSQRT_FDIV_DOUBLE 0xa894
+#define PME_PM_BR_MPRED 0x400f6
+#define PME_PM_INST_PTEG_FROM_DL2L3_SHR 0x3e054
+#define PME_PM_VSU_1FLOP 0xa880
+#define PME_PM_HV_CYC 0x2000a
+#define PME_PM_MRK_LSU_FIN 0x40032
+#define PME_PM_MRK_DATA_FROM_RL2L3_SHR 0x1d04c
+#define PME_PM_DTLB_MISS_16M 0x4c05e
+#define PME_PM_LSU1_LMQ_LHR_MERGE 0xd09a
+#define PME_PM_IFU_FIN 0x40066
/*
* Layout of constraint bits:
@@ -407,39 +914,547 @@ GENERIC_EVENT_ATTR(cache-misses, LD_MISS_L1);
GENERIC_EVENT_ATTR(branch-instructions, BRU_FIN);
GENERIC_EVENT_ATTR(branch-misses, BR_MPRED);
-POWER_EVENT_ATTR(CYC, CYC);
-POWER_EVENT_ATTR(GCT_NOSLOT_CYC, GCT_NOSLOT_CYC);
-POWER_EVENT_ATTR(CMPLU_STALL, CMPLU_STALL);
-POWER_EVENT_ATTR(INST_CMPL, INST_CMPL);
-POWER_EVENT_ATTR(LD_REF_L1, LD_REF_L1);
-POWER_EVENT_ATTR(LD_MISS_L1, LD_MISS_L1);
-POWER_EVENT_ATTR(BRU_FIN, BRU_FIN)
-POWER_EVENT_ATTR(BR_MPRED, BR_MPRED);
-
-POWER_EVENT_ATTR(CMPLU_STALL_FXU, CMPLU_STALL_FXU);
-POWER_EVENT_ATTR(CMPLU_STALL_DIV, CMPLU_STALL_DIV);
-POWER_EVENT_ATTR(CMPLU_STALL_SCALAR, CMPLU_STALL_SCALAR);
-POWER_EVENT_ATTR(CMPLU_STALL_SCALAR_LONG, CMPLU_STALL_SCALAR_LONG);
-POWER_EVENT_ATTR(CMPLU_STALL_VECTOR, CMPLU_STALL_VECTOR);
-POWER_EVENT_ATTR(CMPLU_STALL_VECTOR_LONG, CMPLU_STALL_VECTOR_LONG);
-POWER_EVENT_ATTR(CMPLU_STALL_LSU, CMPLU_STALL_LSU);
-POWER_EVENT_ATTR(CMPLU_STALL_REJECT, CMPLU_STALL_REJECT);
-
-POWER_EVENT_ATTR(CMPLU_STALL_ERAT_MISS, CMPLU_STALL_ERAT_MISS);
-POWER_EVENT_ATTR(CMPLU_STALL_DCACHE_MISS, CMPLU_STALL_DCACHE_MISS);
-POWER_EVENT_ATTR(CMPLU_STALL_STORE, CMPLU_STALL_STORE);
-POWER_EVENT_ATTR(CMPLU_STALL_THRD, CMPLU_STALL_THRD);
-POWER_EVENT_ATTR(CMPLU_STALL_IFU, CMPLU_STALL_IFU);
-POWER_EVENT_ATTR(CMPLU_STALL_BRU, CMPLU_STALL_BRU);
-POWER_EVENT_ATTR(GCT_NOSLOT_IC_MISS, GCT_NOSLOT_IC_MISS);
-
-POWER_EVENT_ATTR(GCT_NOSLOT_BR_MPRED, GCT_NOSLOT_BR_MPRED);
-POWER_EVENT_ATTR(GCT_NOSLOT_BR_MPRED_IC_MISS, GCT_NOSLOT_BR_MPRED_IC_MISS);
-POWER_EVENT_ATTR(GRP_CMPL, GRP_CMPL);
-POWER_EVENT_ATTR(1PLUS_PPC_CMPL, 1PLUS_PPC_CMPL);
-POWER_EVENT_ATTR(CMPLU_STALL_DFU, CMPLU_STALL_DFU);
-POWER_EVENT_ATTR(RUN_CYC, RUN_CYC);
-POWER_EVENT_ATTR(RUN_INST_CMPL, RUN_INST_CMPL);
+POWER_EVENT_ATTR(IC_DEMAND_L2_BR_ALL, IC_DEMAND_L2_BR_ALL);
+POWER_EVENT_ATTR(GCT_UTIL_7_TO_10_SLOTS, GCT_UTIL_7_TO_10_SLOTS);
+POWER_EVENT_ATTR(PMC2_SAVED, PMC2_SAVED);
+POWER_EVENT_ATTR(CMPLU_STALL_DFU, CMPLU_STALL_DFU);
+POWER_EVENT_ATTR(VSU0_16FLOP, VSU0_16FLOP);
+POWER_EVENT_ATTR(MRK_LSU_DERAT_MISS, MRK_LSU_DERAT_MISS);
+POWER_EVENT_ATTR(MRK_ST_CMPL, MRK_ST_CMPL);
+POWER_EVENT_ATTR(NEST_PAIR3_ADD, NEST_PAIR3_ADD);
+POWER_EVENT_ATTR(L2_ST_DISP, L2_ST_DISP);
+POWER_EVENT_ATTR(L2_CASTOUT_MOD, L2_CASTOUT_MOD);
+POWER_EVENT_ATTR(ISEG, ISEG);
+POWER_EVENT_ATTR(MRK_INST_TIMEO, MRK_INST_TIMEO);
+POWER_EVENT_ATTR(L2_RCST_DISP_FAIL_ADDR, L2_RCST_DISP_FAIL_ADDR);
+POWER_EVENT_ATTR(LSU1_DC_PREF_STREAM_CONFIRM, LSU1_DC_PREF_STREAM_CONFIRM);
+POWER_EVENT_ATTR(IERAT_WR_64K, IERAT_WR_64K);
+POWER_EVENT_ATTR(MRK_DTLB_MISS_16M, MRK_DTLB_MISS_16M);
+POWER_EVENT_ATTR(IERAT_MISS, IERAT_MISS);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_LMEM, MRK_PTEG_FROM_LMEM);
+POWER_EVENT_ATTR(FLOP, FLOP);
+POWER_EVENT_ATTR(THRD_PRIO_4_5_CYC, THRD_PRIO_4_5_CYC);
+POWER_EVENT_ATTR(BR_PRED_TA, BR_PRED_TA);
+POWER_EVENT_ATTR(CMPLU_STALL_FXU, CMPLU_STALL_FXU);
+POWER_EVENT_ATTR(EXT_INT, EXT_INT);
+POWER_EVENT_ATTR(VSU_FSQRT_FDIV, VSU_FSQRT_FDIV);
+POWER_EVENT_ATTR(MRK_LD_MISS_EXPOSED_CYC, MRK_LD_MISS_EXPOSED_CYC);
+POWER_EVENT_ATTR(LSU1_LDF, LSU1_LDF);
+POWER_EVENT_ATTR(IC_WRITE_ALL, IC_WRITE_ALL);
+POWER_EVENT_ATTR(LSU0_SRQ_STFWD, LSU0_SRQ_STFWD);
+POWER_EVENT_ATTR(PTEG_FROM_RL2L3_MOD, PTEG_FROM_RL2L3_MOD);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L31_SHR, MRK_DATA_FROM_L31_SHR);
+POWER_EVENT_ATTR(DATA_FROM_L21_MOD, DATA_FROM_L21_MOD);
+POWER_EVENT_ATTR(VSU1_SCAL_DOUBLE_ISSUED, VSU1_SCAL_DOUBLE_ISSUED);
+POWER_EVENT_ATTR(VSU0_8FLOP, VSU0_8FLOP);
+POWER_EVENT_ATTR(POWER_EVENT1, POWER_EVENT1);
+POWER_EVENT_ATTR(DISP_CLB_HELD_BAL, DISP_CLB_HELD_BAL);
+POWER_EVENT_ATTR(VSU1_2FLOP, VSU1_2FLOP);
+POWER_EVENT_ATTR(LWSYNC_HELD, LWSYNC_HELD);
+POWER_EVENT_ATTR(PTEG_FROM_DL2L3_SHR, PTEG_FROM_DL2L3_SHR);
+POWER_EVENT_ATTR(INST_FROM_L21_MOD, INST_FROM_L21_MOD);
+POWER_EVENT_ATTR(IERAT_XLATE_WR_16MPLUS, IERAT_XLATE_WR_16MPLUS);
+POWER_EVENT_ATTR(IC_REQ_ALL, IC_REQ_ALL);
+POWER_EVENT_ATTR(DSLB_MISS, DSLB_MISS);
+POWER_EVENT_ATTR(L3_MISS, L3_MISS);
+POWER_EVENT_ATTR(LSU0_L1_PREF, LSU0_L1_PREF);
+POWER_EVENT_ATTR(VSU_SCALAR_SINGLE_ISSUED, VSU_SCALAR_SINGLE_ISSUED);
+POWER_EVENT_ATTR(LSU1_DC_PREF_STREAM_CONFIRM_STRIDE,
+ LSU1_DC_PREF_STREAM_CONFIRM_STRIDE);
+POWER_EVENT_ATTR(L2_INST, L2_INST);
+POWER_EVENT_ATTR(VSU0_FRSP, VSU0_FRSP);
+POWER_EVENT_ATTR(FLUSH_DISP, FLUSH_DISP);
+POWER_EVENT_ATTR(PTEG_FROM_L2MISS, PTEG_FROM_L2MISS);
+POWER_EVENT_ATTR(VSU1_DQ_ISSUED, VSU1_DQ_ISSUED);
+POWER_EVENT_ATTR(CMPLU_STALL_LSU, CMPLU_STALL_LSU);
+POWER_EVENT_ATTR(MRK_DATA_FROM_DMEM, MRK_DATA_FROM_DMEM);
+POWER_EVENT_ATTR(LSU_FLUSH_ULD, LSU_FLUSH_ULD);
+POWER_EVENT_ATTR(PTEG_FROM_LMEM, PTEG_FROM_LMEM);
+POWER_EVENT_ATTR(MRK_DERAT_MISS_16M, MRK_DERAT_MISS_16M);
+POWER_EVENT_ATTR(THRD_ALL_RUN_CYC, THRD_ALL_RUN_CYC);
+POWER_EVENT_ATTR(MEM0_PREFETCH_DISP, MEM0_PREFETCH_DISP);
+POWER_EVENT_ATTR(MRK_STALL_CMPLU_CYC_COUNT, MRK_STALL_CMPLU_CYC_COUNT);
+POWER_EVENT_ATTR(DATA_FROM_DL2L3_MOD, DATA_FROM_DL2L3_MOD);
+POWER_EVENT_ATTR(VSU_FRSP, VSU_FRSP);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L21_MOD, MRK_DATA_FROM_L21_MOD);
+POWER_EVENT_ATTR(PMC1_OVERFLOW, PMC1_OVERFLOW);
+POWER_EVENT_ATTR(VSU0_SINGLE, VSU0_SINGLE);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_L3MISS, MRK_PTEG_FROM_L3MISS);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_L31_SHR, MRK_PTEG_FROM_L31_SHR);
+POWER_EVENT_ATTR(VSU0_VECTOR_SP_ISSUED, VSU0_VECTOR_SP_ISSUED);
+POWER_EVENT_ATTR(VSU1_FEST, VSU1_FEST);
+POWER_EVENT_ATTR(MRK_INST_DISP, MRK_INST_DISP);
+POWER_EVENT_ATTR(VSU0_COMPLEX_ISSUED, VSU0_COMPLEX_ISSUED);
+POWER_EVENT_ATTR(LSU1_FLUSH_UST, LSU1_FLUSH_UST);
+POWER_EVENT_ATTR(INST_CMPL, INST_CMPL);
+POWER_EVENT_ATTR(FXU_IDLE, FXU_IDLE);
+POWER_EVENT_ATTR(LSU0_FLUSH_ULD, LSU0_FLUSH_ULD);
+POWER_EVENT_ATTR(MRK_DATA_FROM_DL2L3_MOD, MRK_DATA_FROM_DL2L3_MOD);
+POWER_EVENT_ATTR(LSU_LMQ_SRQ_EMPTY_ALL_CYC, LSU_LMQ_SRQ_EMPTY_ALL_CYC);
+POWER_EVENT_ATTR(LSU1_REJECT_LMQ_FULL, LSU1_REJECT_LMQ_FULL);
+POWER_EVENT_ATTR(INST_PTEG_FROM_L21_MOD, INST_PTEG_FROM_L21_MOD);
+POWER_EVENT_ATTR(INST_FROM_RL2L3_MOD, INST_FROM_RL2L3_MOD);
+POWER_EVENT_ATTR(SHL_CREATED, SHL_CREATED);
+POWER_EVENT_ATTR(L2_ST_HIT, L2_ST_HIT);
+POWER_EVENT_ATTR(DATA_FROM_DMEM, DATA_FROM_DMEM);
+POWER_EVENT_ATTR(L3_LD_MISS, L3_LD_MISS);
+POWER_EVENT_ATTR(FXU1_BUSY_FXU0_IDLE, FXU1_BUSY_FXU0_IDLE);
+POWER_EVENT_ATTR(DISP_CLB_HELD_RES, DISP_CLB_HELD_RES);
+POWER_EVENT_ATTR(L2_SN_SX_I_DONE, L2_SN_SX_I_DONE);
+POWER_EVENT_ATTR(GRP_CMPL, GRP_CMPL);
+POWER_EVENT_ATTR(STCX_CMPL, STCX_CMPL);
+POWER_EVENT_ATTR(VSU0_2FLOP, VSU0_2FLOP);
+POWER_EVENT_ATTR(L3_PREF_MISS, L3_PREF_MISS);
+POWER_EVENT_ATTR(LSU_SRQ_SYNC_CYC, LSU_SRQ_SYNC_CYC);
+POWER_EVENT_ATTR(LSU_REJECT_ERAT_MISS, LSU_REJECT_ERAT_MISS);
+POWER_EVENT_ATTR(L1_ICACHE_MISS, L1_ICACHE_MISS);
+POWER_EVENT_ATTR(LSU1_FLUSH_SRQ, LSU1_FLUSH_SRQ);
+POWER_EVENT_ATTR(LD_REF_L1_LSU0, LD_REF_L1_LSU0);
+POWER_EVENT_ATTR(VSU0_FEST, VSU0_FEST);
+POWER_EVENT_ATTR(VSU_VECTOR_SINGLE_ISSUED, VSU_VECTOR_SINGLE_ISSUED);
+POWER_EVENT_ATTR(FREQ_UP, FREQ_UP);
+POWER_EVENT_ATTR(DATA_FROM_LMEM, DATA_FROM_LMEM);
+POWER_EVENT_ATTR(LSU1_LDX, LSU1_LDX);
+POWER_EVENT_ATTR(PMC3_OVERFLOW, PMC3_OVERFLOW);
+POWER_EVENT_ATTR(MRK_BR_MPRED, MRK_BR_MPRED);
+POWER_EVENT_ATTR(SHL_MATCH, SHL_MATCH);
+POWER_EVENT_ATTR(MRK_BR_TAKEN, MRK_BR_TAKEN);
+POWER_EVENT_ATTR(CMPLU_STALL_BRU, CMPLU_STALL_BRU);
+POWER_EVENT_ATTR(ISLB_MISS, ISLB_MISS);
+POWER_EVENT_ATTR(CYC, CYC);
+POWER_EVENT_ATTR(DISP_HELD_THERMAL, DISP_HELD_THERMAL);
+POWER_EVENT_ATTR(INST_PTEG_FROM_RL2L3_SHR, INST_PTEG_FROM_RL2L3_SHR);
+POWER_EVENT_ATTR(LSU1_SRQ_STFWD, LSU1_SRQ_STFWD);
+POWER_EVENT_ATTR(GCT_NOSLOT_BR_MPRED, GCT_NOSLOT_BR_MPRED);
+POWER_EVENT_ATTR(1PLUS_PPC_CMPL, 1PLUS_PPC_CMPL);
+POWER_EVENT_ATTR(PTEG_FROM_DMEM, PTEG_FROM_DMEM);
+POWER_EVENT_ATTR(VSU_2FLOP, VSU_2FLOP);
+POWER_EVENT_ATTR(GCT_FULL_CYC, GCT_FULL_CYC);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L3_CYC, MRK_DATA_FROM_L3_CYC);
+POWER_EVENT_ATTR(LSU_SRQ_S0_ALLOC, LSU_SRQ_S0_ALLOC);
+POWER_EVENT_ATTR(MRK_DERAT_MISS_4K, MRK_DERAT_MISS_4K);
+POWER_EVENT_ATTR(BR_MPRED_TA, BR_MPRED_TA);
+POWER_EVENT_ATTR(INST_PTEG_FROM_L2MISS, INST_PTEG_FROM_L2MISS);
+POWER_EVENT_ATTR(DPU_HELD_POWER, DPU_HELD_POWER);
+POWER_EVENT_ATTR(RUN_INST_CMPL, RUN_INST_CMPL);
+POWER_EVENT_ATTR(MRK_VSU_FIN, MRK_VSU_FIN);
+POWER_EVENT_ATTR(LSU_SRQ_S0_VALID, LSU_SRQ_S0_VALID);
+POWER_EVENT_ATTR(GCT_EMPTY_CYC, GCT_EMPTY_CYC);
+POWER_EVENT_ATTR(IOPS_DISP, IOPS_DISP);
+POWER_EVENT_ATTR(RUN_SPURR, RUN_SPURR);
+POWER_EVENT_ATTR(PTEG_FROM_L21_MOD, PTEG_FROM_L21_MOD);
+POWER_EVENT_ATTR(VSU0_1FLOP, VSU0_1FLOP);
+POWER_EVENT_ATTR(SNOOP_TLBIE, SNOOP_TLBIE);
+POWER_EVENT_ATTR(DATA_FROM_L3MISS, DATA_FROM_L3MISS);
+POWER_EVENT_ATTR(VSU_SINGLE, VSU_SINGLE);
+POWER_EVENT_ATTR(DTLB_MISS_16G, DTLB_MISS_16G);
+POWER_EVENT_ATTR(CMPLU_STALL_VECTOR, CMPLU_STALL_VECTOR);
+POWER_EVENT_ATTR(FLUSH, FLUSH);
+POWER_EVENT_ATTR(L2_LD_HIT, L2_LD_HIT);
+POWER_EVENT_ATTR(NEST_PAIR2_AND, NEST_PAIR2_AND);
+POWER_EVENT_ATTR(VSU1_1FLOP, VSU1_1FLOP);
+POWER_EVENT_ATTR(IC_PREF_REQ, IC_PREF_REQ);
+POWER_EVENT_ATTR(L3_LD_HIT, L3_LD_HIT);
+POWER_EVENT_ATTR(GCT_NOSLOT_IC_MISS, GCT_NOSLOT_IC_MISS);
+POWER_EVENT_ATTR(DISP_HELD, DISP_HELD);
+POWER_EVENT_ATTR(L2_LD, L2_LD);
+POWER_EVENT_ATTR(LSU_FLUSH_SRQ, LSU_FLUSH_SRQ);
+POWER_EVENT_ATTR(BC_PLUS_8_CONV, BC_PLUS_8_CONV);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L31_MOD_CYC, MRK_DATA_FROM_L31_MOD_CYC);
+POWER_EVENT_ATTR(CMPLU_STALL_VECTOR_LONG, CMPLU_STALL_VECTOR_LONG);
+POWER_EVENT_ATTR(L2_RCST_BUSY_RC_FULL, L2_RCST_BUSY_RC_FULL);
+POWER_EVENT_ATTR(TB_BIT_TRANS, TB_BIT_TRANS);
+POWER_EVENT_ATTR(THERMAL_MAX, THERMAL_MAX);
+POWER_EVENT_ATTR(LSU1_FLUSH_ULD, LSU1_FLUSH_ULD);
+POWER_EVENT_ATTR(LSU1_REJECT_LHS, LSU1_REJECT_LHS);
+POWER_EVENT_ATTR(LSU_LRQ_S0_ALLOC, LSU_LRQ_S0_ALLOC);
+POWER_EVENT_ATTR(L3_CO_L31, L3_CO_L31);
+POWER_EVENT_ATTR(POWER_EVENT4, POWER_EVENT4);
+POWER_EVENT_ATTR(DATA_FROM_L31_SHR, DATA_FROM_L31_SHR);
+POWER_EVENT_ATTR(BR_UNCOND, BR_UNCOND);
+POWER_EVENT_ATTR(LSU1_DC_PREF_STREAM_ALLOC, LSU1_DC_PREF_STREAM_ALLOC);
+POWER_EVENT_ATTR(PMC4_REWIND, PMC4_REWIND);
+POWER_EVENT_ATTR(L2_RCLD_DISP, L2_RCLD_DISP);
+POWER_EVENT_ATTR(THRD_PRIO_2_3_CYC, THRD_PRIO_2_3_CYC);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_L2MISS, MRK_PTEG_FROM_L2MISS);
+POWER_EVENT_ATTR(IC_DEMAND_L2_BHT_REDIRECT, IC_DEMAND_L2_BHT_REDIRECT);
+POWER_EVENT_ATTR(LSU_DERAT_MISS, LSU_DERAT_MISS);
+POWER_EVENT_ATTR(IC_PREF_CANCEL_L2, IC_PREF_CANCEL_L2);
+POWER_EVENT_ATTR(MRK_FIN_STALL_CYC_COUNT, MRK_FIN_STALL_CYC_COUNT);
+POWER_EVENT_ATTR(BR_PRED_CCACHE, BR_PRED_CCACHE);
+POWER_EVENT_ATTR(GCT_UTIL_1_TO_2_SLOTS, GCT_UTIL_1_TO_2_SLOTS);
+POWER_EVENT_ATTR(MRK_ST_CMPL_INT, MRK_ST_CMPL_INT);
+POWER_EVENT_ATTR(LSU_TWO_TABLEWALK_CYC, LSU_TWO_TABLEWALK_CYC);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L3MISS, MRK_DATA_FROM_L3MISS);
+POWER_EVENT_ATTR(GCT_NOSLOT_CYC, GCT_NOSLOT_CYC);
+POWER_EVENT_ATTR(LSU_SET_MPRED, LSU_SET_MPRED);
+POWER_EVENT_ATTR(FLUSH_DISP_TLBIE, FLUSH_DISP_TLBIE);
+POWER_EVENT_ATTR(VSU1_FCONV, VSU1_FCONV);
+POWER_EVENT_ATTR(DERAT_MISS_16G, DERAT_MISS_16G);
+POWER_EVENT_ATTR(INST_FROM_LMEM, INST_FROM_LMEM);
+POWER_EVENT_ATTR(IC_DEMAND_L2_BR_REDIRECT, IC_DEMAND_L2_BR_REDIRECT);
+POWER_EVENT_ATTR(CMPLU_STALL_SCALAR_LONG, CMPLU_STALL_SCALAR_LONG);
+POWER_EVENT_ATTR(INST_PTEG_FROM_L2, INST_PTEG_FROM_L2);
+POWER_EVENT_ATTR(PTEG_FROM_L2, PTEG_FROM_L2);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L21_SHR_CYC, MRK_DATA_FROM_L21_SHR_CYC);
+POWER_EVENT_ATTR(MRK_DTLB_MISS_4K, MRK_DTLB_MISS_4K);
+POWER_EVENT_ATTR(VSU0_FPSCR, VSU0_FPSCR);
+POWER_EVENT_ATTR(VSU1_VECT_DOUBLE_ISSUED, VSU1_VECT_DOUBLE_ISSUED);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_RL2L3_MOD, MRK_PTEG_FROM_RL2L3_MOD);
+POWER_EVENT_ATTR(MEM0_RQ_DISP, MEM0_RQ_DISP);
+POWER_EVENT_ATTR(L2_LD_MISS, L2_LD_MISS);
+POWER_EVENT_ATTR(VMX_RESULT_SAT_1, VMX_RESULT_SAT_1);
+POWER_EVENT_ATTR(L1_PREF, L1_PREF);
+POWER_EVENT_ATTR(MRK_DATA_FROM_LMEM_CYC, MRK_DATA_FROM_LMEM_CYC);
+POWER_EVENT_ATTR(GRP_IC_MISS_NONSPEC, GRP_IC_MISS_NONSPEC);
+POWER_EVENT_ATTR(PB_NODE_PUMP, PB_NODE_PUMP);
+POWER_EVENT_ATTR(SHL_MERGED, SHL_MERGED);
+POWER_EVENT_ATTR(NEST_PAIR1_ADD, NEST_PAIR1_ADD);
+POWER_EVENT_ATTR(DATA_FROM_L3, DATA_FROM_L3);
+POWER_EVENT_ATTR(LSU_FLUSH, LSU_FLUSH);
+POWER_EVENT_ATTR(LSU_SRQ_SYNC_COUNT, LSU_SRQ_SYNC_COUNT);
+POWER_EVENT_ATTR(PMC2_OVERFLOW, PMC2_OVERFLOW);
+POWER_EVENT_ATTR(LSU_LDF, LSU_LDF);
+POWER_EVENT_ATTR(POWER_EVENT3, POWER_EVENT3);
+POWER_EVENT_ATTR(DISP_WT, DISP_WT);
+POWER_EVENT_ATTR(CMPLU_STALL_REJECT, CMPLU_STALL_REJECT);
+POWER_EVENT_ATTR(IC_BANK_CONFLICT, IC_BANK_CONFLICT);
+POWER_EVENT_ATTR(BR_MPRED_CR_TA, BR_MPRED_CR_TA);
+POWER_EVENT_ATTR(L2_INST_MISS, L2_INST_MISS);
+POWER_EVENT_ATTR(CMPLU_STALL_ERAT_MISS, CMPLU_STALL_ERAT_MISS);
+POWER_EVENT_ATTR(NEST_PAIR2_ADD, NEST_PAIR2_ADD);
+POWER_EVENT_ATTR(MRK_LSU_FLUSH, MRK_LSU_FLUSH);
+POWER_EVENT_ATTR(L2_LDST, L2_LDST);
+POWER_EVENT_ATTR(INST_FROM_L31_SHR, INST_FROM_L31_SHR);
+POWER_EVENT_ATTR(VSU0_FIN, VSU0_FIN);
+POWER_EVENT_ATTR(LARX_LSU, LARX_LSU);
+POWER_EVENT_ATTR(INST_FROM_RMEM, INST_FROM_RMEM);
+POWER_EVENT_ATTR(DISP_CLB_HELD_TLBIE, DISP_CLB_HELD_TLBIE);
+POWER_EVENT_ATTR(MRK_DATA_FROM_DMEM_CYC, MRK_DATA_FROM_DMEM_CYC);
+POWER_EVENT_ATTR(BR_PRED_CR, BR_PRED_CR);
+POWER_EVENT_ATTR(LSU_REJECT, LSU_REJECT);
+POWER_EVENT_ATTR(GCT_UTIL_3_TO_6_SLOTS, GCT_UTIL_3_TO_6_SLOTS);
+POWER_EVENT_ATTR(CMPLU_STALL_END_GCT_NOSLOT, CMPLU_STALL_END_GCT_NOSLOT);
+POWER_EVENT_ATTR(LSU0_REJECT_LMQ_FULL, LSU0_REJECT_LMQ_FULL);
+POWER_EVENT_ATTR(VSU_FEST, VSU_FEST);
+POWER_EVENT_ATTR(NEST_PAIR0_AND, NEST_PAIR0_AND);
+POWER_EVENT_ATTR(PTEG_FROM_L3, PTEG_FROM_L3);
+POWER_EVENT_ATTR(POWER_EVENT2, POWER_EVENT2);
+POWER_EVENT_ATTR(IC_PREF_CANCEL_PAGE, IC_PREF_CANCEL_PAGE);
+POWER_EVENT_ATTR(VSU0_FSQRT_FDIV, VSU0_FSQRT_FDIV);
+POWER_EVENT_ATTR(MRK_GRP_CMPL, MRK_GRP_CMPL);
+POWER_EVENT_ATTR(VSU0_SCAL_DOUBLE_ISSUED, VSU0_SCAL_DOUBLE_ISSUED);
+POWER_EVENT_ATTR(GRP_DISP, GRP_DISP);
+POWER_EVENT_ATTR(LSU0_LDX, LSU0_LDX);
+POWER_EVENT_ATTR(DATA_FROM_L2, DATA_FROM_L2);
+POWER_EVENT_ATTR(MRK_DATA_FROM_RL2L3_MOD, MRK_DATA_FROM_RL2L3_MOD);
+POWER_EVENT_ATTR(LD_REF_L1, LD_REF_L1);
+POWER_EVENT_ATTR(VSU0_VECT_DOUBLE_ISSUED, VSU0_VECT_DOUBLE_ISSUED);
+POWER_EVENT_ATTR(VSU1_2FLOP_DOUBLE, VSU1_2FLOP_DOUBLE);
+POWER_EVENT_ATTR(THRD_PRIO_6_7_CYC, THRD_PRIO_6_7_CYC);
+POWER_EVENT_ATTR(BC_PLUS_8_RSLV_TAKEN, BC_PLUS_8_RSLV_TAKEN);
+POWER_EVENT_ATTR(BR_MPRED_CR, BR_MPRED_CR);
+POWER_EVENT_ATTR(L3_CO_MEM, L3_CO_MEM);
+POWER_EVENT_ATTR(LD_MISS_L1, LD_MISS_L1);
+POWER_EVENT_ATTR(DATA_FROM_RL2L3_MOD, DATA_FROM_RL2L3_MOD);
+POWER_EVENT_ATTR(LSU_SRQ_FULL_CYC, LSU_SRQ_FULL_CYC);
+POWER_EVENT_ATTR(TABLEWALK_CYC, TABLEWALK_CYC);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_RMEM, MRK_PTEG_FROM_RMEM);
+POWER_EVENT_ATTR(LSU_SRQ_STFWD, LSU_SRQ_STFWD);
+POWER_EVENT_ATTR(INST_PTEG_FROM_RMEM, INST_PTEG_FROM_RMEM);
+POWER_EVENT_ATTR(FXU0_FIN, FXU0_FIN);
+POWER_EVENT_ATTR(LSU1_L1_SW_PREF, LSU1_L1_SW_PREF);
+POWER_EVENT_ATTR(PTEG_FROM_L31_MOD, PTEG_FROM_L31_MOD);
+POWER_EVENT_ATTR(PMC5_OVERFLOW, PMC5_OVERFLOW);
+POWER_EVENT_ATTR(LD_REF_L1_LSU1, LD_REF_L1_LSU1);
+POWER_EVENT_ATTR(INST_PTEG_FROM_L21_SHR, INST_PTEG_FROM_L21_SHR);
+POWER_EVENT_ATTR(CMPLU_STALL_THRD, CMPLU_STALL_THRD);
+POWER_EVENT_ATTR(DATA_FROM_RMEM, DATA_FROM_RMEM);
+POWER_EVENT_ATTR(VSU0_SCAL_SINGLE_ISSUED, VSU0_SCAL_SINGLE_ISSUED);
+POWER_EVENT_ATTR(BR_MPRED_LSTACK, BR_MPRED_LSTACK);
+POWER_EVENT_ATTR(MRK_DATA_FROM_RL2L3_MOD_CYC, MRK_DATA_FROM_RL2L3_MOD_CYC);
+POWER_EVENT_ATTR(LSU0_FLUSH_UST, LSU0_FLUSH_UST);
+POWER_EVENT_ATTR(LSU_NCST, LSU_NCST);
+POWER_EVENT_ATTR(BR_TAKEN, BR_TAKEN);
+POWER_EVENT_ATTR(INST_PTEG_FROM_LMEM, INST_PTEG_FROM_LMEM);
+POWER_EVENT_ATTR(GCT_NOSLOT_BR_MPRED_IC_MISS, GCT_NOSLOT_BR_MPRED_IC_MISS);
+POWER_EVENT_ATTR(DTLB_MISS_4K, DTLB_MISS_4K);
+POWER_EVENT_ATTR(PMC4_SAVED, PMC4_SAVED);
+POWER_EVENT_ATTR(VSU1_PERMUTE_ISSUED, VSU1_PERMUTE_ISSUED);
+POWER_EVENT_ATTR(SLB_MISS, SLB_MISS);
+POWER_EVENT_ATTR(LSU1_FLUSH_LRQ, LSU1_FLUSH_LRQ);
+POWER_EVENT_ATTR(DTLB_MISS, DTLB_MISS);
+POWER_EVENT_ATTR(VSU1_FRSP, VSU1_FRSP);
+POWER_EVENT_ATTR(VSU_VECTOR_DOUBLE_ISSUED, VSU_VECTOR_DOUBLE_ISSUED);
+POWER_EVENT_ATTR(L2_CASTOUT_SHR, L2_CASTOUT_SHR);
+POWER_EVENT_ATTR(DATA_FROM_DL2L3_SHR, DATA_FROM_DL2L3_SHR);
+POWER_EVENT_ATTR(VSU1_STF, VSU1_STF);
+POWER_EVENT_ATTR(ST_FIN, ST_FIN);
+POWER_EVENT_ATTR(PTEG_FROM_L21_SHR, PTEG_FROM_L21_SHR);
+POWER_EVENT_ATTR(L2_LOC_GUESS_WRONG, L2_LOC_GUESS_WRONG);
+POWER_EVENT_ATTR(MRK_STCX_FAIL, MRK_STCX_FAIL);
+POWER_EVENT_ATTR(LSU0_REJECT_LHS, LSU0_REJECT_LHS);
+POWER_EVENT_ATTR(IC_PREF_CANCEL_HIT, IC_PREF_CANCEL_HIT);
+POWER_EVENT_ATTR(L3_PREF_BUSY, L3_PREF_BUSY);
+POWER_EVENT_ATTR(MRK_BRU_FIN, MRK_BRU_FIN);
+POWER_EVENT_ATTR(LSU1_NCLD, LSU1_NCLD);
+POWER_EVENT_ATTR(INST_PTEG_FROM_L31_MOD, INST_PTEG_FROM_L31_MOD);
+POWER_EVENT_ATTR(LSU_NCLD, LSU_NCLD);
+POWER_EVENT_ATTR(LSU_LDX, LSU_LDX);
+POWER_EVENT_ATTR(L2_LOC_GUESS_CORRECT, L2_LOC_GUESS_CORRECT);
+POWER_EVENT_ATTR(THRESH_TIMEO, THRESH_TIMEO);
+POWER_EVENT_ATTR(L3_PREF_ST, L3_PREF_ST);
+POWER_EVENT_ATTR(DISP_CLB_HELD_SYNC, DISP_CLB_HELD_SYNC);
+POWER_EVENT_ATTR(VSU_SIMPLE_ISSUED, VSU_SIMPLE_ISSUED);
+POWER_EVENT_ATTR(VSU1_SINGLE, VSU1_SINGLE);
+POWER_EVENT_ATTR(DATA_TABLEWALK_CYC, DATA_TABLEWALK_CYC);
+POWER_EVENT_ATTR(L2_RC_ST_DONE, L2_RC_ST_DONE);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_L21_MOD, MRK_PTEG_FROM_L21_MOD);
+POWER_EVENT_ATTR(LARX_LSU1, LARX_LSU1);
+POWER_EVENT_ATTR(MRK_DATA_FROM_RMEM, MRK_DATA_FROM_RMEM);
+POWER_EVENT_ATTR(DISP_CLB_HELD, DISP_CLB_HELD);
+POWER_EVENT_ATTR(DERAT_MISS_4K, DERAT_MISS_4K);
+POWER_EVENT_ATTR(L2_RCLD_DISP_FAIL_ADDR, L2_RCLD_DISP_FAIL_ADDR);
+POWER_EVENT_ATTR(SEG_EXCEPTION, SEG_EXCEPTION);
+POWER_EVENT_ATTR(FLUSH_DISP_SB, FLUSH_DISP_SB);
+POWER_EVENT_ATTR(L2_DC_INV, L2_DC_INV);
+POWER_EVENT_ATTR(PTEG_FROM_DL2L3_MOD, PTEG_FROM_DL2L3_MOD);
+POWER_EVENT_ATTR(DSEG, DSEG);
+POWER_EVENT_ATTR(BR_PRED_LSTACK, BR_PRED_LSTACK);
+POWER_EVENT_ATTR(VSU0_STF, VSU0_STF);
+POWER_EVENT_ATTR(LSU_FX_FIN, LSU_FX_FIN);
+POWER_EVENT_ATTR(DERAT_MISS_16M, DERAT_MISS_16M);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_DL2L3_MOD, MRK_PTEG_FROM_DL2L3_MOD);
+POWER_EVENT_ATTR(GCT_UTIL_11_PLUS_SLOTS, GCT_UTIL_11_PLUS_SLOTS);
+POWER_EVENT_ATTR(INST_FROM_L3, INST_FROM_L3);
+POWER_EVENT_ATTR(MRK_IFU_FIN, MRK_IFU_FIN);
+POWER_EVENT_ATTR(ITLB_MISS, ITLB_MISS);
+POWER_EVENT_ATTR(VSU_STF, VSU_STF);
+POWER_EVENT_ATTR(LSU_FLUSH_UST, LSU_FLUSH_UST);
+POWER_EVENT_ATTR(L2_LDST_MISS, L2_LDST_MISS);
+POWER_EVENT_ATTR(FXU1_FIN, FXU1_FIN);
+POWER_EVENT_ATTR(SHL_DEALLOCATED, SHL_DEALLOCATED);
+POWER_EVENT_ATTR(L2_SN_M_WR_DONE, L2_SN_M_WR_DONE);
+POWER_EVENT_ATTR(LSU_REJECT_SET_MPRED, LSU_REJECT_SET_MPRED);
+POWER_EVENT_ATTR(L3_PREF_LD, L3_PREF_LD);
+POWER_EVENT_ATTR(L2_SN_M_RD_DONE, L2_SN_M_RD_DONE);
+POWER_EVENT_ATTR(MRK_DERAT_MISS_16G, MRK_DERAT_MISS_16G);
+POWER_EVENT_ATTR(VSU_FCONV, VSU_FCONV);
+POWER_EVENT_ATTR(ANY_THRD_RUN_CYC, ANY_THRD_RUN_CYC);
+POWER_EVENT_ATTR(LSU_LMQ_FULL_CYC, LSU_LMQ_FULL_CYC);
+POWER_EVENT_ATTR(MRK_LSU_REJECT_LHS, MRK_LSU_REJECT_LHS);
+POWER_EVENT_ATTR(MRK_LD_MISS_L1_CYC, MRK_LD_MISS_L1_CYC);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L2_CYC, MRK_DATA_FROM_L2_CYC);
+POWER_EVENT_ATTR(INST_IMC_MATCH_DISP, INST_IMC_MATCH_DISP);
+POWER_EVENT_ATTR(MRK_DATA_FROM_RMEM_CYC, MRK_DATA_FROM_RMEM_CYC);
+POWER_EVENT_ATTR(VSU0_SIMPLE_ISSUED, VSU0_SIMPLE_ISSUED);
+POWER_EVENT_ATTR(CMPLU_STALL_DIV, CMPLU_STALL_DIV);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_RL2L3_SHR, MRK_PTEG_FROM_RL2L3_SHR);
+POWER_EVENT_ATTR(VSU_FMA_DOUBLE, VSU_FMA_DOUBLE);
+POWER_EVENT_ATTR(VSU_4FLOP, VSU_4FLOP);
+POWER_EVENT_ATTR(VSU1_FIN, VSU1_FIN);
+POWER_EVENT_ATTR(NEST_PAIR1_AND, NEST_PAIR1_AND);
+POWER_EVENT_ATTR(INST_PTEG_FROM_RL2L3_MOD, INST_PTEG_FROM_RL2L3_MOD);
+POWER_EVENT_ATTR(RUN_CYC, RUN_CYC);
+POWER_EVENT_ATTR(PTEG_FROM_RMEM, PTEG_FROM_RMEM);
+POWER_EVENT_ATTR(LSU_LRQ_S0_VALID, LSU_LRQ_S0_VALID);
+POWER_EVENT_ATTR(LSU0_LDF, LSU0_LDF);
+POWER_EVENT_ATTR(FLUSH_COMPLETION, FLUSH_COMPLETION);
+POWER_EVENT_ATTR(ST_MISS_L1, ST_MISS_L1);
+POWER_EVENT_ATTR(L2_NODE_PUMP, L2_NODE_PUMP);
+POWER_EVENT_ATTR(INST_FROM_DL2L3_SHR, INST_FROM_DL2L3_SHR);
+POWER_EVENT_ATTR(MRK_STALL_CMPLU_CYC, MRK_STALL_CMPLU_CYC);
+POWER_EVENT_ATTR(VSU1_DENORM, VSU1_DENORM);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L31_SHR_CYC, MRK_DATA_FROM_L31_SHR_CYC);
+POWER_EVENT_ATTR(NEST_PAIR0_ADD, NEST_PAIR0_ADD);
+POWER_EVENT_ATTR(INST_FROM_L3MISS, INST_FROM_L3MISS);
+POWER_EVENT_ATTR(EE_OFF_EXT_INT, EE_OFF_EXT_INT);
+POWER_EVENT_ATTR(INST_PTEG_FROM_DMEM, INST_PTEG_FROM_DMEM);
+POWER_EVENT_ATTR(INST_FROM_DL2L3_MOD, INST_FROM_DL2L3_MOD);
+POWER_EVENT_ATTR(PMC6_OVERFLOW, PMC6_OVERFLOW);
+POWER_EVENT_ATTR(VSU_2FLOP_DOUBLE, VSU_2FLOP_DOUBLE);
+POWER_EVENT_ATTR(TLB_MISS, TLB_MISS);
+POWER_EVENT_ATTR(FXU_BUSY, FXU_BUSY);
+POWER_EVENT_ATTR(L2_RCLD_DISP_FAIL_OTHER, L2_RCLD_DISP_FAIL_OTHER);
+POWER_EVENT_ATTR(LSU_REJECT_LMQ_FULL, LSU_REJECT_LMQ_FULL);
+POWER_EVENT_ATTR(IC_RELOAD_SHR, IC_RELOAD_SHR);
+POWER_EVENT_ATTR(GRP_MRK, GRP_MRK);
+POWER_EVENT_ATTR(MRK_ST_NEST, MRK_ST_NEST);
+POWER_EVENT_ATTR(VSU1_FSQRT_FDIV, VSU1_FSQRT_FDIV);
+POWER_EVENT_ATTR(LSU0_FLUSH_LRQ, LSU0_FLUSH_LRQ);
+POWER_EVENT_ATTR(LARX_LSU0, LARX_LSU0);
+POWER_EVENT_ATTR(IBUF_FULL_CYC, IBUF_FULL_CYC);
+POWER_EVENT_ATTR(MRK_DATA_FROM_DL2L3_SHR_CYC, MRK_DATA_FROM_DL2L3_SHR_CYC);
+POWER_EVENT_ATTR(LSU_DC_PREF_STREAM_ALLOC, LSU_DC_PREF_STREAM_ALLOC);
+POWER_EVENT_ATTR(GRP_MRK_CYC, GRP_MRK_CYC);
+POWER_EVENT_ATTR(MRK_DATA_FROM_RL2L3_SHR_CYC, MRK_DATA_FROM_RL2L3_SHR_CYC);
+POWER_EVENT_ATTR(L2_GLOB_GUESS_CORRECT, L2_GLOB_GUESS_CORRECT);
+POWER_EVENT_ATTR(LSU_REJECT_LHS, LSU_REJECT_LHS);
+POWER_EVENT_ATTR(MRK_DATA_FROM_LMEM, MRK_DATA_FROM_LMEM);
+POWER_EVENT_ATTR(INST_PTEG_FROM_L3, INST_PTEG_FROM_L3);
+POWER_EVENT_ATTR(FREQ_DOWN, FREQ_DOWN);
+POWER_EVENT_ATTR(PB_RETRY_NODE_PUMP, PB_RETRY_NODE_PUMP);
+POWER_EVENT_ATTR(INST_FROM_RL2L3_SHR, INST_FROM_RL2L3_SHR);
+POWER_EVENT_ATTR(MRK_INST_ISSUED, MRK_INST_ISSUED);
+POWER_EVENT_ATTR(PTEG_FROM_L3MISS, PTEG_FROM_L3MISS);
+POWER_EVENT_ATTR(RUN_PURR, RUN_PURR);
+POWER_EVENT_ATTR(MRK_GRP_IC_MISS, MRK_GRP_IC_MISS);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L3, MRK_DATA_FROM_L3);
+POWER_EVENT_ATTR(CMPLU_STALL_DCACHE_MISS, CMPLU_STALL_DCACHE_MISS);
+POWER_EVENT_ATTR(PTEG_FROM_RL2L3_SHR, PTEG_FROM_RL2L3_SHR);
+POWER_EVENT_ATTR(LSU_FLUSH_LRQ, LSU_FLUSH_LRQ);
+POWER_EVENT_ATTR(MRK_DERAT_MISS_64K, MRK_DERAT_MISS_64K);
+POWER_EVENT_ATTR(INST_PTEG_FROM_DL2L3_MOD, INST_PTEG_FROM_DL2L3_MOD);
+POWER_EVENT_ATTR(L2_ST_MISS, L2_ST_MISS);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_L21_SHR, MRK_PTEG_FROM_L21_SHR);
+POWER_EVENT_ATTR(LWSYNC, LWSYNC);
+POWER_EVENT_ATTR(LSU0_DC_PREF_STREAM_CONFIRM_STRIDE,
+ LSU0_DC_PREF_STREAM_CONFIRM_STRIDE);
+POWER_EVENT_ATTR(MRK_LSU_FLUSH_LRQ, MRK_LSU_FLUSH_LRQ);
+POWER_EVENT_ATTR(INST_IMC_MATCH_CMPL, INST_IMC_MATCH_CMPL);
+POWER_EVENT_ATTR(NEST_PAIR3_AND, NEST_PAIR3_AND);
+POWER_EVENT_ATTR(PB_RETRY_SYS_PUMP, PB_RETRY_SYS_PUMP);
+POWER_EVENT_ATTR(MRK_INST_FIN, MRK_INST_FIN);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_DL2L3_SHR, MRK_PTEG_FROM_DL2L3_SHR);
+POWER_EVENT_ATTR(INST_FROM_L31_MOD, INST_FROM_L31_MOD);
+POWER_EVENT_ATTR(MRK_DTLB_MISS_64K, MRK_DTLB_MISS_64K);
+POWER_EVENT_ATTR(LSU_FIN, LSU_FIN);
+POWER_EVENT_ATTR(MRK_LSU_REJECT, MRK_LSU_REJECT);
+POWER_EVENT_ATTR(L2_CO_FAIL_BUSY, L2_CO_FAIL_BUSY);
+POWER_EVENT_ATTR(MEM0_WQ_DISP, MEM0_WQ_DISP);
+POWER_EVENT_ATTR(DATA_FROM_L31_MOD, DATA_FROM_L31_MOD);
+POWER_EVENT_ATTR(THERMAL_WARN, THERMAL_WARN);
+POWER_EVENT_ATTR(VSU0_4FLOP, VSU0_4FLOP);
+POWER_EVENT_ATTR(BR_MPRED_CCACHE, BR_MPRED_CCACHE);
+POWER_EVENT_ATTR(CMPLU_STALL_IFU, CMPLU_STALL_IFU);
+POWER_EVENT_ATTR(L1_DEMAND_WRITE, L1_DEMAND_WRITE);
+POWER_EVENT_ATTR(FLUSH_BR_MPRED, FLUSH_BR_MPRED);
+POWER_EVENT_ATTR(MRK_DTLB_MISS_16G, MRK_DTLB_MISS_16G);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_DMEM, MRK_PTEG_FROM_DMEM);
+POWER_EVENT_ATTR(L2_RCST_DISP, L2_RCST_DISP);
+POWER_EVENT_ATTR(CMPLU_STALL, CMPLU_STALL);
+POWER_EVENT_ATTR(LSU_PARTIAL_CDF, LSU_PARTIAL_CDF);
+POWER_EVENT_ATTR(DISP_CLB_HELD_SB, DISP_CLB_HELD_SB);
+POWER_EVENT_ATTR(VSU0_FMA_DOUBLE, VSU0_FMA_DOUBLE);
+POWER_EVENT_ATTR(FXU0_BUSY_FXU1_IDLE, FXU0_BUSY_FXU1_IDLE);
+POWER_EVENT_ATTR(IC_DEMAND_CYC, IC_DEMAND_CYC);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L21_SHR, MRK_DATA_FROM_L21_SHR);
+POWER_EVENT_ATTR(MRK_LSU_FLUSH_UST, MRK_LSU_FLUSH_UST);
+POWER_EVENT_ATTR(INST_PTEG_FROM_L3MISS, INST_PTEG_FROM_L3MISS);
+POWER_EVENT_ATTR(VSU_DENORM, VSU_DENORM);
+POWER_EVENT_ATTR(MRK_LSU_PARTIAL_CDF, MRK_LSU_PARTIAL_CDF);
+POWER_EVENT_ATTR(INST_FROM_L21_SHR, INST_FROM_L21_SHR);
+POWER_EVENT_ATTR(IC_PREF_WRITE, IC_PREF_WRITE);
+POWER_EVENT_ATTR(BR_PRED, BR_PRED);
+POWER_EVENT_ATTR(INST_FROM_DMEM, INST_FROM_DMEM);
+POWER_EVENT_ATTR(IC_PREF_CANCEL_ALL, IC_PREF_CANCEL_ALL);
+POWER_EVENT_ATTR(LSU_DC_PREF_STREAM_CONFIRM, LSU_DC_PREF_STREAM_CONFIRM);
+POWER_EVENT_ATTR(MRK_LSU_FLUSH_SRQ, MRK_LSU_FLUSH_SRQ);
+POWER_EVENT_ATTR(MRK_FIN_STALL_CYC, MRK_FIN_STALL_CYC);
+POWER_EVENT_ATTR(L2_RCST_DISP_FAIL_OTHER, L2_RCST_DISP_FAIL_OTHER);
+POWER_EVENT_ATTR(VSU1_DD_ISSUED, VSU1_DD_ISSUED);
+POWER_EVENT_ATTR(PTEG_FROM_L31_SHR, PTEG_FROM_L31_SHR);
+POWER_EVENT_ATTR(DATA_FROM_L21_SHR, DATA_FROM_L21_SHR);
+POWER_EVENT_ATTR(LSU0_NCLD, LSU0_NCLD);
+POWER_EVENT_ATTR(VSU1_4FLOP, VSU1_4FLOP);
+POWER_EVENT_ATTR(VSU1_8FLOP, VSU1_8FLOP);
+POWER_EVENT_ATTR(VSU_8FLOP, VSU_8FLOP);
+POWER_EVENT_ATTR(LSU_LMQ_SRQ_EMPTY_CYC, LSU_LMQ_SRQ_EMPTY_CYC);
+POWER_EVENT_ATTR(DTLB_MISS_64K, DTLB_MISS_64K);
+POWER_EVENT_ATTR(THRD_CONC_RUN_INST, THRD_CONC_RUN_INST);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_L2, MRK_PTEG_FROM_L2);
+POWER_EVENT_ATTR(PB_SYS_PUMP, PB_SYS_PUMP);
+POWER_EVENT_ATTR(VSU_FIN, VSU_FIN);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L31_MOD, MRK_DATA_FROM_L31_MOD);
+POWER_EVENT_ATTR(THRD_PRIO_0_1_CYC, THRD_PRIO_0_1_CYC);
+POWER_EVENT_ATTR(DERAT_MISS_64K, DERAT_MISS_64K);
+POWER_EVENT_ATTR(PMC2_REWIND, PMC2_REWIND);
+POWER_EVENT_ATTR(INST_FROM_L2, INST_FROM_L2);
+POWER_EVENT_ATTR(GRP_BR_MPRED_NONSPEC, GRP_BR_MPRED_NONSPEC);
+POWER_EVENT_ATTR(INST_DISP, INST_DISP);
+POWER_EVENT_ATTR(MEM0_RD_CANCEL_TOTAL, MEM0_RD_CANCEL_TOTAL);
+POWER_EVENT_ATTR(LSU0_DC_PREF_STREAM_CONFIRM, LSU0_DC_PREF_STREAM_CONFIRM);
+POWER_EVENT_ATTR(L1_DCACHE_RELOAD_VALID, L1_DCACHE_RELOAD_VALID);
+POWER_EVENT_ATTR(VSU_SCALAR_DOUBLE_ISSUED, VSU_SCALAR_DOUBLE_ISSUED);
+POWER_EVENT_ATTR(L3_PREF_HIT, L3_PREF_HIT);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_L31_MOD, MRK_PTEG_FROM_L31_MOD);
+POWER_EVENT_ATTR(CMPLU_STALL_STORE, CMPLU_STALL_STORE);
+POWER_EVENT_ATTR(MRK_FXU_FIN, MRK_FXU_FIN);
+POWER_EVENT_ATTR(PMC4_OVERFLOW, PMC4_OVERFLOW);
+POWER_EVENT_ATTR(MRK_PTEG_FROM_L3, MRK_PTEG_FROM_L3);
+POWER_EVENT_ATTR(LSU0_LMQ_LHR_MERGE, LSU0_LMQ_LHR_MERGE);
+POWER_EVENT_ATTR(BTAC_HIT, BTAC_HIT);
+POWER_EVENT_ATTR(L3_RD_BUSY, L3_RD_BUSY);
+POWER_EVENT_ATTR(LSU0_L1_SW_PREF, LSU0_L1_SW_PREF);
+POWER_EVENT_ATTR(INST_FROM_L2MISS, INST_FROM_L2MISS);
+POWER_EVENT_ATTR(LSU0_DC_PREF_STREAM_ALLOC, LSU0_DC_PREF_STREAM_ALLOC);
+POWER_EVENT_ATTR(L2_ST, L2_ST);
+POWER_EVENT_ATTR(VSU0_DENORM, VSU0_DENORM);
+POWER_EVENT_ATTR(MRK_DATA_FROM_DL2L3_SHR, MRK_DATA_FROM_DL2L3_SHR);
+POWER_EVENT_ATTR(BR_PRED_CR_TA, BR_PRED_CR_TA);
+POWER_EVENT_ATTR(VSU0_FCONV, VSU0_FCONV);
+POWER_EVENT_ATTR(MRK_LSU_FLUSH_ULD, MRK_LSU_FLUSH_ULD);
+POWER_EVENT_ATTR(BTAC_MISS, BTAC_MISS);
+POWER_EVENT_ATTR(MRK_LD_MISS_EXPOSED_CYC_COUNT,
+ MRK_LD_MISS_EXPOSED_CYC_COUNT);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L2, MRK_DATA_FROM_L2);
+POWER_EVENT_ATTR(LSU_DCACHE_RELOAD_VALID, LSU_DCACHE_RELOAD_VALID);
+POWER_EVENT_ATTR(VSU_FMA, VSU_FMA);
+POWER_EVENT_ATTR(LSU0_FLUSH_SRQ, LSU0_FLUSH_SRQ);
+POWER_EVENT_ATTR(LSU1_L1_PREF, LSU1_L1_PREF);
+POWER_EVENT_ATTR(IOPS_CMPL, IOPS_CMPL);
+POWER_EVENT_ATTR(L2_SYS_PUMP, L2_SYS_PUMP);
+POWER_EVENT_ATTR(L2_RCLD_BUSY_RC_FULL, L2_RCLD_BUSY_RC_FULL);
+POWER_EVENT_ATTR(LSU_LMQ_S0_ALLOC, LSU_LMQ_S0_ALLOC);
+POWER_EVENT_ATTR(FLUSH_DISP_SYNC, FLUSH_DISP_SYNC);
+POWER_EVENT_ATTR(MRK_DATA_FROM_DL2L3_MOD_CYC, MRK_DATA_FROM_DL2L3_MOD_CYC);
+POWER_EVENT_ATTR(L2_IC_INV, L2_IC_INV);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L21_MOD_CYC, MRK_DATA_FROM_L21_MOD_CYC);
+POWER_EVENT_ATTR(L3_PREF_LDST, L3_PREF_LDST);
+POWER_EVENT_ATTR(LSU_SRQ_EMPTY_CYC, LSU_SRQ_EMPTY_CYC);
+POWER_EVENT_ATTR(LSU_LMQ_S0_VALID, LSU_LMQ_S0_VALID);
+POWER_EVENT_ATTR(FLUSH_PARTIAL, FLUSH_PARTIAL);
+POWER_EVENT_ATTR(VSU1_FMA_DOUBLE, VSU1_FMA_DOUBLE);
+POWER_EVENT_ATTR(1PLUS_PPC_DISP, 1PLUS_PPC_DISP);
+POWER_EVENT_ATTR(DATA_FROM_L2MISS, DATA_FROM_L2MISS);
+POWER_EVENT_ATTR(SUSPENDED, SUSPENDED);
+POWER_EVENT_ATTR(VSU0_FMA, VSU0_FMA);
+POWER_EVENT_ATTR(CMPLU_STALL_SCALAR, CMPLU_STALL_SCALAR);
+POWER_EVENT_ATTR(STCX_FAIL, STCX_FAIL);
+POWER_EVENT_ATTR(VSU0_FSQRT_FDIV_DOUBLE, VSU0_FSQRT_FDIV_DOUBLE);
+POWER_EVENT_ATTR(DC_PREF_DST, DC_PREF_DST);
+POWER_EVENT_ATTR(VSU1_SCAL_SINGLE_ISSUED, VSU1_SCAL_SINGLE_ISSUED);
+POWER_EVENT_ATTR(L3_HIT, L3_HIT);
+POWER_EVENT_ATTR(L2_GLOB_GUESS_WRONG, L2_GLOB_GUESS_WRONG);
+POWER_EVENT_ATTR(MRK_DFU_FIN, MRK_DFU_FIN);
+POWER_EVENT_ATTR(INST_FROM_L1, INST_FROM_L1);
+POWER_EVENT_ATTR(BRU_FIN, BRU_FIN);
+POWER_EVENT_ATTR(IC_DEMAND_REQ, IC_DEMAND_REQ);
+POWER_EVENT_ATTR(VSU1_FSQRT_FDIV_DOUBLE, VSU1_FSQRT_FDIV_DOUBLE);
+POWER_EVENT_ATTR(VSU1_FMA, VSU1_FMA);
+POWER_EVENT_ATTR(MRK_LD_MISS_L1, MRK_LD_MISS_L1);
+POWER_EVENT_ATTR(VSU0_2FLOP_DOUBLE, VSU0_2FLOP_DOUBLE);
+POWER_EVENT_ATTR(LSU_DC_PREF_STRIDED_STREAM_CONFIRM,
+ LSU_DC_PREF_STRIDED_STREAM_CONFIRM);
+POWER_EVENT_ATTR(INST_PTEG_FROM_L31_SHR, INST_PTEG_FROM_L31_SHR);
+POWER_EVENT_ATTR(MRK_LSU_REJECT_ERAT_MISS, MRK_LSU_REJECT_ERAT_MISS);
+POWER_EVENT_ATTR(MRK_DATA_FROM_L2MISS, MRK_DATA_FROM_L2MISS);
+POWER_EVENT_ATTR(DATA_FROM_RL2L3_SHR, DATA_FROM_RL2L3_SHR);
+POWER_EVENT_ATTR(INST_FROM_PREF, INST_FROM_PREF);
+POWER_EVENT_ATTR(VSU1_SQ, VSU1_SQ);
+POWER_EVENT_ATTR(L2_LD_DISP, L2_LD_DISP);
+POWER_EVENT_ATTR(L2_DISP_ALL, L2_DISP_ALL);
+POWER_EVENT_ATTR(THRD_GRP_CMPL_BOTH_CYC, THRD_GRP_CMPL_BOTH_CYC);
+POWER_EVENT_ATTR(VSU_FSQRT_FDIV_DOUBLE, VSU_FSQRT_FDIV_DOUBLE);
+POWER_EVENT_ATTR(BR_MPRED, BR_MPRED);
+POWER_EVENT_ATTR(INST_PTEG_FROM_DL2L3_SHR, INST_PTEG_FROM_DL2L3_SHR);
+POWER_EVENT_ATTR(VSU_1FLOP, VSU_1FLOP);
+POWER_EVENT_ATTR(HV_CYC, HV_CYC);
+POWER_EVENT_ATTR(MRK_LSU_FIN, MRK_LSU_FIN);
+POWER_EVENT_ATTR(MRK_DATA_FROM_RL2L3_SHR, MRK_DATA_FROM_RL2L3_SHR);
+POWER_EVENT_ATTR(DTLB_MISS_16M, DTLB_MISS_16M);
+POWER_EVENT_ATTR(LSU1_LMQ_LHR_MERGE, LSU1_LMQ_LHR_MERGE);
+POWER_EVENT_ATTR(IFU_FIN, IFU_FIN);
static struct attribute *power7_events_attr[] = {
GENERIC_EVENT_PTR(CYC),
@@ -451,39 +1466,543 @@ static struct attribute *power7_events_attr[] = {
GENERIC_EVENT_PTR(BRU_FIN),
GENERIC_EVENT_PTR(BR_MPRED),
- POWER_EVENT_PTR(CYC),
- POWER_EVENT_PTR(GCT_NOSLOT_CYC),
- POWER_EVENT_PTR(CMPLU_STALL),
- POWER_EVENT_PTR(INST_CMPL),
- POWER_EVENT_PTR(LD_REF_L1),
- POWER_EVENT_PTR(LD_MISS_L1),
- POWER_EVENT_PTR(BRU_FIN),
- POWER_EVENT_PTR(BR_MPRED),
-
+ POWER_EVENT_PTR(IC_DEMAND_L2_BR_ALL),
+ POWER_EVENT_PTR(GCT_UTIL_7_TO_10_SLOTS),
+ POWER_EVENT_PTR(PMC2_SAVED),
+ POWER_EVENT_PTR(CMPLU_STALL_DFU),
+ POWER_EVENT_PTR(VSU0_16FLOP),
+ POWER_EVENT_PTR(MRK_LSU_DERAT_MISS),
+ POWER_EVENT_PTR(MRK_ST_CMPL),
+ POWER_EVENT_PTR(NEST_PAIR3_ADD),
+ POWER_EVENT_PTR(L2_ST_DISP),
+ POWER_EVENT_PTR(L2_CASTOUT_MOD),
+ POWER_EVENT_PTR(ISEG),
+ POWER_EVENT_PTR(MRK_INST_TIMEO),
+ POWER_EVENT_PTR(L2_RCST_DISP_FAIL_ADDR),
+ POWER_EVENT_PTR(LSU1_DC_PREF_STREAM_CONFIRM),
+ POWER_EVENT_PTR(IERAT_WR_64K),
+ POWER_EVENT_PTR(MRK_DTLB_MISS_16M),
+ POWER_EVENT_PTR(IERAT_MISS),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_LMEM),
+ POWER_EVENT_PTR(FLOP),
+ POWER_EVENT_PTR(THRD_PRIO_4_5_CYC),
+ POWER_EVENT_PTR(BR_PRED_TA),
POWER_EVENT_PTR(CMPLU_STALL_FXU),
- POWER_EVENT_PTR(CMPLU_STALL_DIV),
- POWER_EVENT_PTR(CMPLU_STALL_SCALAR),
- POWER_EVENT_PTR(CMPLU_STALL_SCALAR_LONG),
+ POWER_EVENT_PTR(EXT_INT),
+ POWER_EVENT_PTR(VSU_FSQRT_FDIV),
+ POWER_EVENT_PTR(MRK_LD_MISS_EXPOSED_CYC),
+ POWER_EVENT_PTR(LSU1_LDF),
+ POWER_EVENT_PTR(IC_WRITE_ALL),
+ POWER_EVENT_PTR(LSU0_SRQ_STFWD),
+ POWER_EVENT_PTR(PTEG_FROM_RL2L3_MOD),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L31_SHR),
+ POWER_EVENT_PTR(DATA_FROM_L21_MOD),
+ POWER_EVENT_PTR(VSU1_SCAL_DOUBLE_ISSUED),
+ POWER_EVENT_PTR(VSU0_8FLOP),
+ POWER_EVENT_PTR(POWER_EVENT1),
+ POWER_EVENT_PTR(DISP_CLB_HELD_BAL),
+ POWER_EVENT_PTR(VSU1_2FLOP),
+ POWER_EVENT_PTR(LWSYNC_HELD),
+ POWER_EVENT_PTR(PTEG_FROM_DL2L3_SHR),
+ POWER_EVENT_PTR(INST_FROM_L21_MOD),
+ POWER_EVENT_PTR(IERAT_XLATE_WR_16MPLUS),
+ POWER_EVENT_PTR(IC_REQ_ALL),
+ POWER_EVENT_PTR(DSLB_MISS),
+ POWER_EVENT_PTR(L3_MISS),
+ POWER_EVENT_PTR(LSU0_L1_PREF),
+ POWER_EVENT_PTR(VSU_SCALAR_SINGLE_ISSUED),
+ POWER_EVENT_PTR(LSU1_DC_PREF_STREAM_CONFIRM_STRIDE),
+ POWER_EVENT_PTR(L2_INST),
+ POWER_EVENT_PTR(VSU0_FRSP),
+ POWER_EVENT_PTR(FLUSH_DISP),
+ POWER_EVENT_PTR(PTEG_FROM_L2MISS),
+ POWER_EVENT_PTR(VSU1_DQ_ISSUED),
+ POWER_EVENT_PTR(CMPLU_STALL_LSU),
+ POWER_EVENT_PTR(MRK_DATA_FROM_DMEM),
+ POWER_EVENT_PTR(LSU_FLUSH_ULD),
+ POWER_EVENT_PTR(PTEG_FROM_LMEM),
+ POWER_EVENT_PTR(MRK_DERAT_MISS_16M),
+ POWER_EVENT_PTR(THRD_ALL_RUN_CYC),
+ POWER_EVENT_PTR(MEM0_PREFETCH_DISP),
+ POWER_EVENT_PTR(MRK_STALL_CMPLU_CYC_COUNT),
+ POWER_EVENT_PTR(DATA_FROM_DL2L3_MOD),
+ POWER_EVENT_PTR(VSU_FRSP),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L21_MOD),
+ POWER_EVENT_PTR(PMC1_OVERFLOW),
+ POWER_EVENT_PTR(VSU0_SINGLE),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_L3MISS),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_L31_SHR),
+ POWER_EVENT_PTR(VSU0_VECTOR_SP_ISSUED),
+ POWER_EVENT_PTR(VSU1_FEST),
+ POWER_EVENT_PTR(MRK_INST_DISP),
+ POWER_EVENT_PTR(VSU0_COMPLEX_ISSUED),
+ POWER_EVENT_PTR(LSU1_FLUSH_UST),
+ POWER_EVENT_PTR(INST_CMPL),
+ POWER_EVENT_PTR(FXU_IDLE),
+ POWER_EVENT_PTR(LSU0_FLUSH_ULD),
+ POWER_EVENT_PTR(MRK_DATA_FROM_DL2L3_MOD),
+ POWER_EVENT_PTR(LSU_LMQ_SRQ_EMPTY_ALL_CYC),
+ POWER_EVENT_PTR(LSU1_REJECT_LMQ_FULL),
+ POWER_EVENT_PTR(INST_PTEG_FROM_L21_MOD),
+ POWER_EVENT_PTR(INST_FROM_RL2L3_MOD),
+ POWER_EVENT_PTR(SHL_CREATED),
+ POWER_EVENT_PTR(L2_ST_HIT),
+ POWER_EVENT_PTR(DATA_FROM_DMEM),
+ POWER_EVENT_PTR(L3_LD_MISS),
+ POWER_EVENT_PTR(FXU1_BUSY_FXU0_IDLE),
+ POWER_EVENT_PTR(DISP_CLB_HELD_RES),
+ POWER_EVENT_PTR(L2_SN_SX_I_DONE),
+ POWER_EVENT_PTR(GRP_CMPL),
+ POWER_EVENT_PTR(STCX_CMPL),
+ POWER_EVENT_PTR(VSU0_2FLOP),
+ POWER_EVENT_PTR(L3_PREF_MISS),
+ POWER_EVENT_PTR(LSU_SRQ_SYNC_CYC),
+ POWER_EVENT_PTR(LSU_REJECT_ERAT_MISS),
+ POWER_EVENT_PTR(L1_ICACHE_MISS),
+ POWER_EVENT_PTR(LSU1_FLUSH_SRQ),
+ POWER_EVENT_PTR(LD_REF_L1_LSU0),
+ POWER_EVENT_PTR(VSU0_FEST),
+ POWER_EVENT_PTR(VSU_VECTOR_SINGLE_ISSUED),
+ POWER_EVENT_PTR(FREQ_UP),
+ POWER_EVENT_PTR(DATA_FROM_LMEM),
+ POWER_EVENT_PTR(LSU1_LDX),
+ POWER_EVENT_PTR(PMC3_OVERFLOW),
+ POWER_EVENT_PTR(MRK_BR_MPRED),
+ POWER_EVENT_PTR(SHL_MATCH),
+ POWER_EVENT_PTR(MRK_BR_TAKEN),
+ POWER_EVENT_PTR(CMPLU_STALL_BRU),
+ POWER_EVENT_PTR(ISLB_MISS),
+ POWER_EVENT_PTR(CYC),
+ POWER_EVENT_PTR(DISP_HELD_THERMAL),
+ POWER_EVENT_PTR(INST_PTEG_FROM_RL2L3_SHR),
+ POWER_EVENT_PTR(LSU1_SRQ_STFWD),
+ POWER_EVENT_PTR(GCT_NOSLOT_BR_MPRED),
+ POWER_EVENT_PTR(1PLUS_PPC_CMPL),
+ POWER_EVENT_PTR(PTEG_FROM_DMEM),
+ POWER_EVENT_PTR(VSU_2FLOP),
+ POWER_EVENT_PTR(GCT_FULL_CYC),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L3_CYC),
+ POWER_EVENT_PTR(LSU_SRQ_S0_ALLOC),
+ POWER_EVENT_PTR(MRK_DERAT_MISS_4K),
+ POWER_EVENT_PTR(BR_MPRED_TA),
+ POWER_EVENT_PTR(INST_PTEG_FROM_L2MISS),
+ POWER_EVENT_PTR(DPU_HELD_POWER),
+ POWER_EVENT_PTR(RUN_INST_CMPL),
+ POWER_EVENT_PTR(MRK_VSU_FIN),
+ POWER_EVENT_PTR(LSU_SRQ_S0_VALID),
+ POWER_EVENT_PTR(GCT_EMPTY_CYC),
+ POWER_EVENT_PTR(IOPS_DISP),
+ POWER_EVENT_PTR(RUN_SPURR),
+ POWER_EVENT_PTR(PTEG_FROM_L21_MOD),
+ POWER_EVENT_PTR(VSU0_1FLOP),
+ POWER_EVENT_PTR(SNOOP_TLBIE),
+ POWER_EVENT_PTR(DATA_FROM_L3MISS),
+ POWER_EVENT_PTR(VSU_SINGLE),
+ POWER_EVENT_PTR(DTLB_MISS_16G),
POWER_EVENT_PTR(CMPLU_STALL_VECTOR),
+ POWER_EVENT_PTR(FLUSH),
+ POWER_EVENT_PTR(L2_LD_HIT),
+ POWER_EVENT_PTR(NEST_PAIR2_AND),
+ POWER_EVENT_PTR(VSU1_1FLOP),
+ POWER_EVENT_PTR(IC_PREF_REQ),
+ POWER_EVENT_PTR(L3_LD_HIT),
+ POWER_EVENT_PTR(GCT_NOSLOT_IC_MISS),
+ POWER_EVENT_PTR(DISP_HELD),
+ POWER_EVENT_PTR(L2_LD),
+ POWER_EVENT_PTR(LSU_FLUSH_SRQ),
+ POWER_EVENT_PTR(BC_PLUS_8_CONV),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L31_MOD_CYC),
POWER_EVENT_PTR(CMPLU_STALL_VECTOR_LONG),
- POWER_EVENT_PTR(CMPLU_STALL_LSU),
+ POWER_EVENT_PTR(L2_RCST_BUSY_RC_FULL),
+ POWER_EVENT_PTR(TB_BIT_TRANS),
+ POWER_EVENT_PTR(THERMAL_MAX),
+ POWER_EVENT_PTR(LSU1_FLUSH_ULD),
+ POWER_EVENT_PTR(LSU1_REJECT_LHS),
+ POWER_EVENT_PTR(LSU_LRQ_S0_ALLOC),
+ POWER_EVENT_PTR(L3_CO_L31),
+ POWER_EVENT_PTR(POWER_EVENT4),
+ POWER_EVENT_PTR(DATA_FROM_L31_SHR),
+ POWER_EVENT_PTR(BR_UNCOND),
+ POWER_EVENT_PTR(LSU1_DC_PREF_STREAM_ALLOC),
+ POWER_EVENT_PTR(PMC4_REWIND),
+ POWER_EVENT_PTR(L2_RCLD_DISP),
+ POWER_EVENT_PTR(THRD_PRIO_2_3_CYC),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_L2MISS),
+ POWER_EVENT_PTR(IC_DEMAND_L2_BHT_REDIRECT),
+ POWER_EVENT_PTR(LSU_DERAT_MISS),
+ POWER_EVENT_PTR(IC_PREF_CANCEL_L2),
+ POWER_EVENT_PTR(MRK_FIN_STALL_CYC_COUNT),
+ POWER_EVENT_PTR(BR_PRED_CCACHE),
+ POWER_EVENT_PTR(GCT_UTIL_1_TO_2_SLOTS),
+ POWER_EVENT_PTR(MRK_ST_CMPL_INT),
+ POWER_EVENT_PTR(LSU_TWO_TABLEWALK_CYC),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L3MISS),
+ POWER_EVENT_PTR(GCT_NOSLOT_CYC),
+ POWER_EVENT_PTR(LSU_SET_MPRED),
+ POWER_EVENT_PTR(FLUSH_DISP_TLBIE),
+ POWER_EVENT_PTR(VSU1_FCONV),
+ POWER_EVENT_PTR(DERAT_MISS_16G),
+ POWER_EVENT_PTR(INST_FROM_LMEM),
+ POWER_EVENT_PTR(IC_DEMAND_L2_BR_REDIRECT),
+ POWER_EVENT_PTR(CMPLU_STALL_SCALAR_LONG),
+ POWER_EVENT_PTR(INST_PTEG_FROM_L2),
+ POWER_EVENT_PTR(PTEG_FROM_L2),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L21_SHR_CYC),
+ POWER_EVENT_PTR(MRK_DTLB_MISS_4K),
+ POWER_EVENT_PTR(VSU0_FPSCR),
+ POWER_EVENT_PTR(VSU1_VECT_DOUBLE_ISSUED),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_RL2L3_MOD),
+ POWER_EVENT_PTR(MEM0_RQ_DISP),
+ POWER_EVENT_PTR(L2_LD_MISS),
+ POWER_EVENT_PTR(VMX_RESULT_SAT_1),
+ POWER_EVENT_PTR(L1_PREF),
+ POWER_EVENT_PTR(MRK_DATA_FROM_LMEM_CYC),
+ POWER_EVENT_PTR(GRP_IC_MISS_NONSPEC),
+ POWER_EVENT_PTR(PB_NODE_PUMP),
+ POWER_EVENT_PTR(SHL_MERGED),
+ POWER_EVENT_PTR(NEST_PAIR1_ADD),
+ POWER_EVENT_PTR(DATA_FROM_L3),
+ POWER_EVENT_PTR(LSU_FLUSH),
+ POWER_EVENT_PTR(LSU_SRQ_SYNC_COUNT),
+ POWER_EVENT_PTR(PMC2_OVERFLOW),
+ POWER_EVENT_PTR(LSU_LDF),
+ POWER_EVENT_PTR(POWER_EVENT3),
+ POWER_EVENT_PTR(DISP_WT),
POWER_EVENT_PTR(CMPLU_STALL_REJECT),
-
+ POWER_EVENT_PTR(IC_BANK_CONFLICT),
+ POWER_EVENT_PTR(BR_MPRED_CR_TA),
+ POWER_EVENT_PTR(L2_INST_MISS),
POWER_EVENT_PTR(CMPLU_STALL_ERAT_MISS),
- POWER_EVENT_PTR(CMPLU_STALL_DCACHE_MISS),
- POWER_EVENT_PTR(CMPLU_STALL_STORE),
+ POWER_EVENT_PTR(NEST_PAIR2_ADD),
+ POWER_EVENT_PTR(MRK_LSU_FLUSH),
+ POWER_EVENT_PTR(L2_LDST),
+ POWER_EVENT_PTR(INST_FROM_L31_SHR),
+ POWER_EVENT_PTR(VSU0_FIN),
+ POWER_EVENT_PTR(LARX_LSU),
+ POWER_EVENT_PTR(INST_FROM_RMEM),
+ POWER_EVENT_PTR(DISP_CLB_HELD_TLBIE),
+ POWER_EVENT_PTR(MRK_DATA_FROM_DMEM_CYC),
+ POWER_EVENT_PTR(BR_PRED_CR),
+ POWER_EVENT_PTR(LSU_REJECT),
+ POWER_EVENT_PTR(GCT_UTIL_3_TO_6_SLOTS),
+ POWER_EVENT_PTR(CMPLU_STALL_END_GCT_NOSLOT),
+ POWER_EVENT_PTR(LSU0_REJECT_LMQ_FULL),
+ POWER_EVENT_PTR(VSU_FEST),
+ POWER_EVENT_PTR(NEST_PAIR0_AND),
+ POWER_EVENT_PTR(PTEG_FROM_L3),
+ POWER_EVENT_PTR(POWER_EVENT2),
+ POWER_EVENT_PTR(IC_PREF_CANCEL_PAGE),
+ POWER_EVENT_PTR(VSU0_FSQRT_FDIV),
+ POWER_EVENT_PTR(MRK_GRP_CMPL),
+ POWER_EVENT_PTR(VSU0_SCAL_DOUBLE_ISSUED),
+ POWER_EVENT_PTR(GRP_DISP),
+ POWER_EVENT_PTR(LSU0_LDX),
+ POWER_EVENT_PTR(DATA_FROM_L2),
+ POWER_EVENT_PTR(MRK_DATA_FROM_RL2L3_MOD),
+ POWER_EVENT_PTR(LD_REF_L1),
+ POWER_EVENT_PTR(VSU0_VECT_DOUBLE_ISSUED),
+ POWER_EVENT_PTR(VSU1_2FLOP_DOUBLE),
+ POWER_EVENT_PTR(THRD_PRIO_6_7_CYC),
+ POWER_EVENT_PTR(BC_PLUS_8_RSLV_TAKEN),
+ POWER_EVENT_PTR(BR_MPRED_CR),
+ POWER_EVENT_PTR(L3_CO_MEM),
+ POWER_EVENT_PTR(LD_MISS_L1),
+ POWER_EVENT_PTR(DATA_FROM_RL2L3_MOD),
+ POWER_EVENT_PTR(LSU_SRQ_FULL_CYC),
+ POWER_EVENT_PTR(TABLEWALK_CYC),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_RMEM),
+ POWER_EVENT_PTR(LSU_SRQ_STFWD),
+ POWER_EVENT_PTR(INST_PTEG_FROM_RMEM),
+ POWER_EVENT_PTR(FXU0_FIN),
+ POWER_EVENT_PTR(LSU1_L1_SW_PREF),
+ POWER_EVENT_PTR(PTEG_FROM_L31_MOD),
+ POWER_EVENT_PTR(PMC5_OVERFLOW),
+ POWER_EVENT_PTR(LD_REF_L1_LSU1),
+ POWER_EVENT_PTR(INST_PTEG_FROM_L21_SHR),
POWER_EVENT_PTR(CMPLU_STALL_THRD),
- POWER_EVENT_PTR(CMPLU_STALL_IFU),
- POWER_EVENT_PTR(CMPLU_STALL_BRU),
- POWER_EVENT_PTR(GCT_NOSLOT_IC_MISS),
- POWER_EVENT_PTR(GCT_NOSLOT_BR_MPRED),
-
+ POWER_EVENT_PTR(DATA_FROM_RMEM),
+ POWER_EVENT_PTR(VSU0_SCAL_SINGLE_ISSUED),
+ POWER_EVENT_PTR(BR_MPRED_LSTACK),
+ POWER_EVENT_PTR(MRK_DATA_FROM_RL2L3_MOD_CYC),
+ POWER_EVENT_PTR(LSU0_FLUSH_UST),
+ POWER_EVENT_PTR(LSU_NCST),
+ POWER_EVENT_PTR(BR_TAKEN),
+ POWER_EVENT_PTR(INST_PTEG_FROM_LMEM),
POWER_EVENT_PTR(GCT_NOSLOT_BR_MPRED_IC_MISS),
- POWER_EVENT_PTR(GRP_CMPL),
- POWER_EVENT_PTR(1PLUS_PPC_CMPL),
- POWER_EVENT_PTR(CMPLU_STALL_DFU),
+ POWER_EVENT_PTR(DTLB_MISS_4K),
+ POWER_EVENT_PTR(PMC4_SAVED),
+ POWER_EVENT_PTR(VSU1_PERMUTE_ISSUED),
+ POWER_EVENT_PTR(SLB_MISS),
+ POWER_EVENT_PTR(LSU1_FLUSH_LRQ),
+ POWER_EVENT_PTR(DTLB_MISS),
+ POWER_EVENT_PTR(VSU1_FRSP),
+ POWER_EVENT_PTR(VSU_VECTOR_DOUBLE_ISSUED),
+ POWER_EVENT_PTR(L2_CASTOUT_SHR),
+ POWER_EVENT_PTR(DATA_FROM_DL2L3_SHR),
+ POWER_EVENT_PTR(VSU1_STF),
+ POWER_EVENT_PTR(ST_FIN),
+ POWER_EVENT_PTR(PTEG_FROM_L21_SHR),
+ POWER_EVENT_PTR(L2_LOC_GUESS_WRONG),
+ POWER_EVENT_PTR(MRK_STCX_FAIL),
+ POWER_EVENT_PTR(LSU0_REJECT_LHS),
+ POWER_EVENT_PTR(IC_PREF_CANCEL_HIT),
+ POWER_EVENT_PTR(L3_PREF_BUSY),
+ POWER_EVENT_PTR(MRK_BRU_FIN),
+ POWER_EVENT_PTR(LSU1_NCLD),
+ POWER_EVENT_PTR(INST_PTEG_FROM_L31_MOD),
+ POWER_EVENT_PTR(LSU_NCLD),
+ POWER_EVENT_PTR(LSU_LDX),
+ POWER_EVENT_PTR(L2_LOC_GUESS_CORRECT),
+ POWER_EVENT_PTR(THRESH_TIMEO),
+ POWER_EVENT_PTR(L3_PREF_ST),
+ POWER_EVENT_PTR(DISP_CLB_HELD_SYNC),
+ POWER_EVENT_PTR(VSU_SIMPLE_ISSUED),
+ POWER_EVENT_PTR(VSU1_SINGLE),
+ POWER_EVENT_PTR(DATA_TABLEWALK_CYC),
+ POWER_EVENT_PTR(L2_RC_ST_DONE),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_L21_MOD),
+ POWER_EVENT_PTR(LARX_LSU1),
+ POWER_EVENT_PTR(MRK_DATA_FROM_RMEM),
+ POWER_EVENT_PTR(DISP_CLB_HELD),
+ POWER_EVENT_PTR(DERAT_MISS_4K),
+ POWER_EVENT_PTR(L2_RCLD_DISP_FAIL_ADDR),
+ POWER_EVENT_PTR(SEG_EXCEPTION),
+ POWER_EVENT_PTR(FLUSH_DISP_SB),
+ POWER_EVENT_PTR(L2_DC_INV),
+ POWER_EVENT_PTR(PTEG_FROM_DL2L3_MOD),
+ POWER_EVENT_PTR(DSEG),
+ POWER_EVENT_PTR(BR_PRED_LSTACK),
+ POWER_EVENT_PTR(VSU0_STF),
+ POWER_EVENT_PTR(LSU_FX_FIN),
+ POWER_EVENT_PTR(DERAT_MISS_16M),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_DL2L3_MOD),
+ POWER_EVENT_PTR(GCT_UTIL_11_PLUS_SLOTS),
+ POWER_EVENT_PTR(INST_FROM_L3),
+ POWER_EVENT_PTR(MRK_IFU_FIN),
+ POWER_EVENT_PTR(ITLB_MISS),
+ POWER_EVENT_PTR(VSU_STF),
+ POWER_EVENT_PTR(LSU_FLUSH_UST),
+ POWER_EVENT_PTR(L2_LDST_MISS),
+ POWER_EVENT_PTR(FXU1_FIN),
+ POWER_EVENT_PTR(SHL_DEALLOCATED),
+ POWER_EVENT_PTR(L2_SN_M_WR_DONE),
+ POWER_EVENT_PTR(LSU_REJECT_SET_MPRED),
+ POWER_EVENT_PTR(L3_PREF_LD),
+ POWER_EVENT_PTR(L2_SN_M_RD_DONE),
+ POWER_EVENT_PTR(MRK_DERAT_MISS_16G),
+ POWER_EVENT_PTR(VSU_FCONV),
+ POWER_EVENT_PTR(ANY_THRD_RUN_CYC),
+ POWER_EVENT_PTR(LSU_LMQ_FULL_CYC),
+ POWER_EVENT_PTR(MRK_LSU_REJECT_LHS),
+ POWER_EVENT_PTR(MRK_LD_MISS_L1_CYC),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L2_CYC),
+ POWER_EVENT_PTR(INST_IMC_MATCH_DISP),
+ POWER_EVENT_PTR(MRK_DATA_FROM_RMEM_CYC),
+ POWER_EVENT_PTR(VSU0_SIMPLE_ISSUED),
+ POWER_EVENT_PTR(CMPLU_STALL_DIV),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_RL2L3_SHR),
+ POWER_EVENT_PTR(VSU_FMA_DOUBLE),
+ POWER_EVENT_PTR(VSU_4FLOP),
+ POWER_EVENT_PTR(VSU1_FIN),
+ POWER_EVENT_PTR(NEST_PAIR1_AND),
+ POWER_EVENT_PTR(INST_PTEG_FROM_RL2L3_MOD),
POWER_EVENT_PTR(RUN_CYC),
- POWER_EVENT_PTR(RUN_INST_CMPL),
+ POWER_EVENT_PTR(PTEG_FROM_RMEM),
+ POWER_EVENT_PTR(LSU_LRQ_S0_VALID),
+ POWER_EVENT_PTR(LSU0_LDF),
+ POWER_EVENT_PTR(FLUSH_COMPLETION),
+ POWER_EVENT_PTR(ST_MISS_L1),
+ POWER_EVENT_PTR(L2_NODE_PUMP),
+ POWER_EVENT_PTR(INST_FROM_DL2L3_SHR),
+ POWER_EVENT_PTR(MRK_STALL_CMPLU_CYC),
+ POWER_EVENT_PTR(VSU1_DENORM),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L31_SHR_CYC),
+ POWER_EVENT_PTR(NEST_PAIR0_ADD),
+ POWER_EVENT_PTR(INST_FROM_L3MISS),
+ POWER_EVENT_PTR(EE_OFF_EXT_INT),
+ POWER_EVENT_PTR(INST_PTEG_FROM_DMEM),
+ POWER_EVENT_PTR(INST_FROM_DL2L3_MOD),
+ POWER_EVENT_PTR(PMC6_OVERFLOW),
+ POWER_EVENT_PTR(VSU_2FLOP_DOUBLE),
+ POWER_EVENT_PTR(TLB_MISS),
+ POWER_EVENT_PTR(FXU_BUSY),
+ POWER_EVENT_PTR(L2_RCLD_DISP_FAIL_OTHER),
+ POWER_EVENT_PTR(LSU_REJECT_LMQ_FULL),
+ POWER_EVENT_PTR(IC_RELOAD_SHR),
+ POWER_EVENT_PTR(GRP_MRK),
+ POWER_EVENT_PTR(MRK_ST_NEST),
+ POWER_EVENT_PTR(VSU1_FSQRT_FDIV),
+ POWER_EVENT_PTR(LSU0_FLUSH_LRQ),
+ POWER_EVENT_PTR(LARX_LSU0),
+ POWER_EVENT_PTR(IBUF_FULL_CYC),
+ POWER_EVENT_PTR(MRK_DATA_FROM_DL2L3_SHR_CYC),
+ POWER_EVENT_PTR(LSU_DC_PREF_STREAM_ALLOC),
+ POWER_EVENT_PTR(GRP_MRK_CYC),
+ POWER_EVENT_PTR(MRK_DATA_FROM_RL2L3_SHR_CYC),
+ POWER_EVENT_PTR(L2_GLOB_GUESS_CORRECT),
+ POWER_EVENT_PTR(LSU_REJECT_LHS),
+ POWER_EVENT_PTR(MRK_DATA_FROM_LMEM),
+ POWER_EVENT_PTR(INST_PTEG_FROM_L3),
+ POWER_EVENT_PTR(FREQ_DOWN),
+ POWER_EVENT_PTR(PB_RETRY_NODE_PUMP),
+ POWER_EVENT_PTR(INST_FROM_RL2L3_SHR),
+ POWER_EVENT_PTR(MRK_INST_ISSUED),
+ POWER_EVENT_PTR(PTEG_FROM_L3MISS),
+ POWER_EVENT_PTR(RUN_PURR),
+ POWER_EVENT_PTR(MRK_GRP_IC_MISS),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L3),
+ POWER_EVENT_PTR(CMPLU_STALL_DCACHE_MISS),
+ POWER_EVENT_PTR(PTEG_FROM_RL2L3_SHR),
+ POWER_EVENT_PTR(LSU_FLUSH_LRQ),
+ POWER_EVENT_PTR(MRK_DERAT_MISS_64K),
+ POWER_EVENT_PTR(INST_PTEG_FROM_DL2L3_MOD),
+ POWER_EVENT_PTR(L2_ST_MISS),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_L21_SHR),
+ POWER_EVENT_PTR(LWSYNC),
+ POWER_EVENT_PTR(LSU0_DC_PREF_STREAM_CONFIRM_STRIDE),
+ POWER_EVENT_PTR(MRK_LSU_FLUSH_LRQ),
+ POWER_EVENT_PTR(INST_IMC_MATCH_CMPL),
+ POWER_EVENT_PTR(NEST_PAIR3_AND),
+ POWER_EVENT_PTR(PB_RETRY_SYS_PUMP),
+ POWER_EVENT_PTR(MRK_INST_FIN),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_DL2L3_SHR),
+ POWER_EVENT_PTR(INST_FROM_L31_MOD),
+ POWER_EVENT_PTR(MRK_DTLB_MISS_64K),
+ POWER_EVENT_PTR(LSU_FIN),
+ POWER_EVENT_PTR(MRK_LSU_REJECT),
+ POWER_EVENT_PTR(L2_CO_FAIL_BUSY),
+ POWER_EVENT_PTR(MEM0_WQ_DISP),
+ POWER_EVENT_PTR(DATA_FROM_L31_MOD),
+ POWER_EVENT_PTR(THERMAL_WARN),
+ POWER_EVENT_PTR(VSU0_4FLOP),
+ POWER_EVENT_PTR(BR_MPRED_CCACHE),
+ POWER_EVENT_PTR(CMPLU_STALL_IFU),
+ POWER_EVENT_PTR(L1_DEMAND_WRITE),
+ POWER_EVENT_PTR(FLUSH_BR_MPRED),
+ POWER_EVENT_PTR(MRK_DTLB_MISS_16G),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_DMEM),
+ POWER_EVENT_PTR(L2_RCST_DISP),
+ POWER_EVENT_PTR(CMPLU_STALL),
+ POWER_EVENT_PTR(LSU_PARTIAL_CDF),
+ POWER_EVENT_PTR(DISP_CLB_HELD_SB),
+ POWER_EVENT_PTR(VSU0_FMA_DOUBLE),
+ POWER_EVENT_PTR(FXU0_BUSY_FXU1_IDLE),
+ POWER_EVENT_PTR(IC_DEMAND_CYC),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L21_SHR),
+ POWER_EVENT_PTR(MRK_LSU_FLUSH_UST),
+ POWER_EVENT_PTR(INST_PTEG_FROM_L3MISS),
+ POWER_EVENT_PTR(VSU_DENORM),
+ POWER_EVENT_PTR(MRK_LSU_PARTIAL_CDF),
+ POWER_EVENT_PTR(INST_FROM_L21_SHR),
+ POWER_EVENT_PTR(IC_PREF_WRITE),
+ POWER_EVENT_PTR(BR_PRED),
+ POWER_EVENT_PTR(INST_FROM_DMEM),
+ POWER_EVENT_PTR(IC_PREF_CANCEL_ALL),
+ POWER_EVENT_PTR(LSU_DC_PREF_STREAM_CONFIRM),
+ POWER_EVENT_PTR(MRK_LSU_FLUSH_SRQ),
+ POWER_EVENT_PTR(MRK_FIN_STALL_CYC),
+ POWER_EVENT_PTR(L2_RCST_DISP_FAIL_OTHER),
+ POWER_EVENT_PTR(VSU1_DD_ISSUED),
+ POWER_EVENT_PTR(PTEG_FROM_L31_SHR),
+ POWER_EVENT_PTR(DATA_FROM_L21_SHR),
+ POWER_EVENT_PTR(LSU0_NCLD),
+ POWER_EVENT_PTR(VSU1_4FLOP),
+ POWER_EVENT_PTR(VSU1_8FLOP),
+ POWER_EVENT_PTR(VSU_8FLOP),
+ POWER_EVENT_PTR(LSU_LMQ_SRQ_EMPTY_CYC),
+ POWER_EVENT_PTR(DTLB_MISS_64K),
+ POWER_EVENT_PTR(THRD_CONC_RUN_INST),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_L2),
+ POWER_EVENT_PTR(PB_SYS_PUMP),
+ POWER_EVENT_PTR(VSU_FIN),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L31_MOD),
+ POWER_EVENT_PTR(THRD_PRIO_0_1_CYC),
+ POWER_EVENT_PTR(DERAT_MISS_64K),
+ POWER_EVENT_PTR(PMC2_REWIND),
+ POWER_EVENT_PTR(INST_FROM_L2),
+ POWER_EVENT_PTR(GRP_BR_MPRED_NONSPEC),
+ POWER_EVENT_PTR(INST_DISP),
+ POWER_EVENT_PTR(MEM0_RD_CANCEL_TOTAL),
+ POWER_EVENT_PTR(LSU0_DC_PREF_STREAM_CONFIRM),
+ POWER_EVENT_PTR(L1_DCACHE_RELOAD_VALID),
+ POWER_EVENT_PTR(VSU_SCALAR_DOUBLE_ISSUED),
+ POWER_EVENT_PTR(L3_PREF_HIT),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_L31_MOD),
+ POWER_EVENT_PTR(CMPLU_STALL_STORE),
+ POWER_EVENT_PTR(MRK_FXU_FIN),
+ POWER_EVENT_PTR(PMC4_OVERFLOW),
+ POWER_EVENT_PTR(MRK_PTEG_FROM_L3),
+ POWER_EVENT_PTR(LSU0_LMQ_LHR_MERGE),
+ POWER_EVENT_PTR(BTAC_HIT),
+ POWER_EVENT_PTR(L3_RD_BUSY),
+ POWER_EVENT_PTR(LSU0_L1_SW_PREF),
+ POWER_EVENT_PTR(INST_FROM_L2MISS),
+ POWER_EVENT_PTR(LSU0_DC_PREF_STREAM_ALLOC),
+ POWER_EVENT_PTR(L2_ST),
+ POWER_EVENT_PTR(VSU0_DENORM),
+ POWER_EVENT_PTR(MRK_DATA_FROM_DL2L3_SHR),
+ POWER_EVENT_PTR(BR_PRED_CR_TA),
+ POWER_EVENT_PTR(VSU0_FCONV),
+ POWER_EVENT_PTR(MRK_LSU_FLUSH_ULD),
+ POWER_EVENT_PTR(BTAC_MISS),
+ POWER_EVENT_PTR(MRK_LD_MISS_EXPOSED_CYC_COUNT),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L2),
+ POWER_EVENT_PTR(LSU_DCACHE_RELOAD_VALID),
+ POWER_EVENT_PTR(VSU_FMA),
+ POWER_EVENT_PTR(LSU0_FLUSH_SRQ),
+ POWER_EVENT_PTR(LSU1_L1_PREF),
+ POWER_EVENT_PTR(IOPS_CMPL),
+ POWER_EVENT_PTR(L2_SYS_PUMP),
+ POWER_EVENT_PTR(L2_RCLD_BUSY_RC_FULL),
+ POWER_EVENT_PTR(LSU_LMQ_S0_ALLOC),
+ POWER_EVENT_PTR(FLUSH_DISP_SYNC),
+ POWER_EVENT_PTR(MRK_DATA_FROM_DL2L3_MOD_CYC),
+ POWER_EVENT_PTR(L2_IC_INV),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L21_MOD_CYC),
+ POWER_EVENT_PTR(L3_PREF_LDST),
+ POWER_EVENT_PTR(LSU_SRQ_EMPTY_CYC),
+ POWER_EVENT_PTR(LSU_LMQ_S0_VALID),
+ POWER_EVENT_PTR(FLUSH_PARTIAL),
+ POWER_EVENT_PTR(VSU1_FMA_DOUBLE),
+ POWER_EVENT_PTR(1PLUS_PPC_DISP),
+ POWER_EVENT_PTR(DATA_FROM_L2MISS),
+ POWER_EVENT_PTR(SUSPENDED),
+ POWER_EVENT_PTR(VSU0_FMA),
+ POWER_EVENT_PTR(CMPLU_STALL_SCALAR),
+ POWER_EVENT_PTR(STCX_FAIL),
+ POWER_EVENT_PTR(VSU0_FSQRT_FDIV_DOUBLE),
+ POWER_EVENT_PTR(DC_PREF_DST),
+ POWER_EVENT_PTR(VSU1_SCAL_SINGLE_ISSUED),
+ POWER_EVENT_PTR(L3_HIT),
+ POWER_EVENT_PTR(L2_GLOB_GUESS_WRONG),
+ POWER_EVENT_PTR(MRK_DFU_FIN),
+ POWER_EVENT_PTR(INST_FROM_L1),
+ POWER_EVENT_PTR(BRU_FIN),
+ POWER_EVENT_PTR(IC_DEMAND_REQ),
+ POWER_EVENT_PTR(VSU1_FSQRT_FDIV_DOUBLE),
+ POWER_EVENT_PTR(VSU1_FMA),
+ POWER_EVENT_PTR(MRK_LD_MISS_L1),
+ POWER_EVENT_PTR(VSU0_2FLOP_DOUBLE),
+ POWER_EVENT_PTR(LSU_DC_PREF_STRIDED_STREAM_CONFIRM),
+ POWER_EVENT_PTR(INST_PTEG_FROM_L31_SHR),
+ POWER_EVENT_PTR(MRK_LSU_REJECT_ERAT_MISS),
+ POWER_EVENT_PTR(MRK_DATA_FROM_L2MISS),
+ POWER_EVENT_PTR(DATA_FROM_RL2L3_SHR),
+ POWER_EVENT_PTR(INST_FROM_PREF),
+ POWER_EVENT_PTR(VSU1_SQ),
+ POWER_EVENT_PTR(L2_LD_DISP),
+ POWER_EVENT_PTR(L2_DISP_ALL),
+ POWER_EVENT_PTR(THRD_GRP_CMPL_BOTH_CYC),
+ POWER_EVENT_PTR(VSU_FSQRT_FDIV_DOUBLE),
+ POWER_EVENT_PTR(BR_MPRED),
+ POWER_EVENT_PTR(INST_PTEG_FROM_DL2L3_SHR),
+ POWER_EVENT_PTR(VSU_1FLOP),
+ POWER_EVENT_PTR(HV_CYC),
+ POWER_EVENT_PTR(MRK_LSU_FIN),
+ POWER_EVENT_PTR(MRK_DATA_FROM_RL2L3_SHR),
+ POWER_EVENT_PTR(DTLB_MISS_16M),
+ POWER_EVENT_PTR(LSU1_LMQ_LHR_MERGE),
+ POWER_EVENT_PTR(IFU_FIN),
NULL
};
--
1.7.9.5
^ permalink raw reply related
* [PATCH 1/2] perf tools: fix a typo of a Power7 event name
From: Runzhen Wang @ 2013-06-19 9:15 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel
Cc: acme, icycoder, xiaoguangrong, paulus, Runzhen Wang, sukadev
In the Power7 PMU guide:
https://www.power.org/documentation/commonly-used-metrics-for-performance-analysis/
PM_BRU_MPRED is referred to as PM_BR_MPRED.
This patch fix the typo by changing the name of the event in kernel and
documentation accordingly.
Signed-off-by: Runzhen Wang <runzhen@linux.vnet.ibm.com>
---
.../testing/sysfs-bus-event_source-devices-events | 2 +-
arch/powerpc/perf/power7-pmu.c | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
index 8b25ffb..3c1cc24 100644
--- a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
+++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
@@ -29,7 +29,7 @@ Description: Generic performance monitoring events
What: /sys/devices/cpu/events/PM_1PLUS_PPC_CMPL
/sys/devices/cpu/events/PM_BRU_FIN
- /sys/devices/cpu/events/PM_BRU_MPRED
+ /sys/devices/cpu/events/PM_BR_MPRED
/sys/devices/cpu/events/PM_CMPLU_STALL
/sys/devices/cpu/events/PM_CMPLU_STALL_BRU
/sys/devices/cpu/events/PM_CMPLU_STALL_DCACHE_MISS
diff --git a/arch/powerpc/perf/power7-pmu.c b/arch/powerpc/perf/power7-pmu.c
index 13c3f0e..d1821b8 100644
--- a/arch/powerpc/perf/power7-pmu.c
+++ b/arch/powerpc/perf/power7-pmu.c
@@ -60,7 +60,7 @@
#define PME_PM_LD_REF_L1 0xc880
#define PME_PM_LD_MISS_L1 0x400f0
#define PME_PM_BRU_FIN 0x10068
-#define PME_PM_BRU_MPRED 0x400f6
+#define PME_PM_BR_MPRED 0x400f6
#define PME_PM_CMPLU_STALL_FXU 0x20014
#define PME_PM_CMPLU_STALL_DIV 0x40014
@@ -349,7 +349,7 @@ static int power7_generic_events[] = {
[PERF_COUNT_HW_CACHE_REFERENCES] = PME_PM_LD_REF_L1,
[PERF_COUNT_HW_CACHE_MISSES] = PME_PM_LD_MISS_L1,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = PME_PM_BRU_FIN,
- [PERF_COUNT_HW_BRANCH_MISSES] = PME_PM_BRU_MPRED,
+ [PERF_COUNT_HW_BRANCH_MISSES] = PME_PM_BR_MPRED,
};
#define C(x) PERF_COUNT_HW_CACHE_##x
@@ -405,7 +405,7 @@ GENERIC_EVENT_ATTR(instructions, INST_CMPL);
GENERIC_EVENT_ATTR(cache-references, LD_REF_L1);
GENERIC_EVENT_ATTR(cache-misses, LD_MISS_L1);
GENERIC_EVENT_ATTR(branch-instructions, BRU_FIN);
-GENERIC_EVENT_ATTR(branch-misses, BRU_MPRED);
+GENERIC_EVENT_ATTR(branch-misses, BR_MPRED);
POWER_EVENT_ATTR(CYC, CYC);
POWER_EVENT_ATTR(GCT_NOSLOT_CYC, GCT_NOSLOT_CYC);
@@ -414,7 +414,7 @@ POWER_EVENT_ATTR(INST_CMPL, INST_CMPL);
POWER_EVENT_ATTR(LD_REF_L1, LD_REF_L1);
POWER_EVENT_ATTR(LD_MISS_L1, LD_MISS_L1);
POWER_EVENT_ATTR(BRU_FIN, BRU_FIN)
-POWER_EVENT_ATTR(BRU_MPRED, BRU_MPRED);
+POWER_EVENT_ATTR(BR_MPRED, BR_MPRED);
POWER_EVENT_ATTR(CMPLU_STALL_FXU, CMPLU_STALL_FXU);
POWER_EVENT_ATTR(CMPLU_STALL_DIV, CMPLU_STALL_DIV);
@@ -449,7 +449,7 @@ static struct attribute *power7_events_attr[] = {
GENERIC_EVENT_PTR(LD_REF_L1),
GENERIC_EVENT_PTR(LD_MISS_L1),
GENERIC_EVENT_PTR(BRU_FIN),
- GENERIC_EVENT_PTR(BRU_MPRED),
+ GENERIC_EVENT_PTR(BR_MPRED),
POWER_EVENT_PTR(CYC),
POWER_EVENT_PTR(GCT_NOSLOT_CYC),
@@ -458,7 +458,7 @@ static struct attribute *power7_events_attr[] = {
POWER_EVENT_PTR(LD_REF_L1),
POWER_EVENT_PTR(LD_MISS_L1),
POWER_EVENT_PTR(BRU_FIN),
- POWER_EVENT_PTR(BRU_MPRED),
+ POWER_EVENT_PTR(BR_MPRED),
POWER_EVENT_PTR(CMPLU_STALL_FXU),
POWER_EVENT_PTR(CMPLU_STALL_DIV),
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH 05/31] powerpc/eeh: Trace PCI bus from PE
From: Gavin Shan @ 2013-06-19 8:48 UTC (permalink / raw)
To: Mike Qiu; +Cc: linuxppc-dev, Gavin Shan
In-Reply-To: <51C15BE3.70906@linux.vnet.ibm.com>
On Wed, Jun 19, 2013 at 03:21:07PM +0800, Mike Qiu wrote:
.../...
>>+ /*
>>+ * For PCI bus sensitive PE, we can reset the parent
>>+ * bridge in order for hot-reset. However, the PCI
>>+ * devices including the associated EEH devices might
>>+ * be removed when EEH core is doing recovery. So that
>>+ * won't safe to retrieve the bridge through downstream
>>+ * EEH device. We have to trace the parent PCI bus, then
>>+ * the parent bridge explicitly.
>>+ */
>>+ if (eeh_probe_mode_dev() && !pe->bus) {
>>+ first_edev = list_first_entry(&pe->edevs,
>>+ struct eeh_dev, list);
>>+ pe->bus = eeh_dev_to_pci_dev(first_edev)->bus;
>>+ }
>Hi Gavin
>
>I have qestion, can we keep pe->bus for a device pe ? the value is
>the bus which edev belongs to.
>
>so that we can make the code more efficient for device pe.
>
>I have no idea of whether this will cause side effect
>
Thanks, Mike. Good catch actually, it won't have side-effect and
possiblly avoid problems during hot-plug: For PCI bus with only
child PCI device, we won't return the PCI bus (without fix) and
the EEH core doesn't do hot-plug on the affected PCI bus then.
I'm testing on the updated patch and will send that out soon if it
works well.
Thanks,
Gavin
^ permalink raw reply
* Re: [PATCH 01/31] powerpc/eeh: Move common part to kernel directory
From: Gavin Shan @ 2013-06-19 7:29 UTC (permalink / raw)
To: Gavin Shan; +Cc: linuxppc-dev
In-Reply-To: <1371544435-4943-2-git-send-email-shangw@linux.vnet.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 622 bytes --]
On Tue, Jun 18, 2013 at 04:33:25PM +0800, Gavin Shan wrote:
>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>
Please use the updated [1/31] in the attachment.
Thanks,
Gavin
[-- Attachment #2: 0001-powerpc-eeh-Cleanup-for-EEH-core.patch --]
[-- Type: text/x-diff, Size: 4038 bytes --]
>From 1e0a43dc5fd15daaf851e695f12cdea25c66d7e9 Mon Sep 17 00:00:00 2001
From: Gavin Shan <shangw@linux.vnet.ibm.com>
Date: Wed, 19 Jun 2013 13:53:12 +0800
Subject: [PATCH 01/31] powerpc/eeh: Cleanup for EEH core
Cleanup on EEH core to remove unnecessary whitespaces.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/eeh.c | 22 +++++++++++-----------
arch/powerpc/platforms/pseries/eeh_driver.c | 14 +++++++-------
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 6b73d6c..8a83451 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -368,7 +368,7 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
}
eeh_stats.slot_resets++;
-
+
/* Avoid repeated reports of this failure, including problems
* with other functions on this device, and functions under
* bridges.
@@ -525,7 +525,7 @@ static void eeh_reset_pe_once(struct eeh_pe *pe)
* 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)
@@ -538,8 +538,8 @@ static void eeh_reset_pe_once(struct eeh_pe *pe)
*/
#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
+
+ /* 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.
*/
@@ -604,7 +604,7 @@ void eeh_save_bars(struct eeh_dev *edev)
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]);
}
@@ -803,12 +803,12 @@ 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);
- }
+ 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);
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index a3fefb6..0acc5a2 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -154,9 +154,9 @@ static void eeh_enable_irq(struct pci_dev *dev)
* 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
+ *
+ * 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)
@@ -376,9 +376,9 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
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,
+ * 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) {
@@ -520,7 +520,7 @@ void eeh_handle_event(struct eeh_pe *pe)
eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
return;
-
+
excess_failures:
/*
* About 90% of all real-life EEH failures in the field
--
1.7.5.4
^ permalink raw reply related
* Re: [PATCH 05/31] powerpc/eeh: Trace PCI bus from PE
From: Mike Qiu @ 2013-06-19 7:21 UTC (permalink / raw)
To: Gavin Shan; +Cc: linuxppc-dev
In-Reply-To: <1371544435-4943-6-git-send-email-shangw@linux.vnet.ibm.com>
于 2013/6/18 16:33, Gavin Shan 写道:
> There're several types of PEs can be supported for now: PHB, Bus
> and Device dependent PE. For PCI bus dependent PE, tracing the
> corresponding PCI bus from PE (struct eeh_pe) would make the code
> more efficient. The patch also enables the retrieval of PCI bus based
> on the PCI bus dependent PE.
>
> Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
> ---
> arch/powerpc/include/asm/eeh.h | 1 +
> arch/powerpc/kernel/eeh_pe.c | 22 ++++++++++++++++++++++
> 2 files changed, 23 insertions(+), 0 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
> index acdfcaa..f3b49d6 100644
> --- a/arch/powerpc/include/asm/eeh.h
> +++ b/arch/powerpc/include/asm/eeh.h
> @@ -59,6 +59,7 @@ struct eeh_pe {
> int config_addr; /* Traditional PCI address */
> int addr; /* PE configuration address */
> struct pci_controller *phb; /* Associated PHB */
> + struct pci_bus *bus; /* Top PCI bus for bus PE */
> int check_count; /* Times of ignored error */
> int freeze_count; /* Times of froze up */
> int false_positives; /* Times of reported #ff's */
> diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
> index 3d2dcf5..5bd1637 100644
> --- a/arch/powerpc/kernel/eeh_pe.c
> +++ b/arch/powerpc/kernel/eeh_pe.c
> @@ -304,6 +304,7 @@ static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
> int eeh_add_to_parent_pe(struct eeh_dev *edev)
> {
> struct eeh_pe *pe, *parent;
> + struct eeh_dev *first_edev;
>
> eeh_lock();
>
> @@ -326,6 +327,21 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
> pe->type = EEH_PE_BUS;
> edev->pe = pe;
>
> + /*
> + * For PCI bus sensitive PE, we can reset the parent
> + * bridge in order for hot-reset. However, the PCI
> + * devices including the associated EEH devices might
> + * be removed when EEH core is doing recovery. So that
> + * won't safe to retrieve the bridge through downstream
> + * EEH device. We have to trace the parent PCI bus, then
> + * the parent bridge explicitly.
> + */
> + if (eeh_probe_mode_dev() && !pe->bus) {
> + first_edev = list_first_entry(&pe->edevs,
> + struct eeh_dev, list);
> + pe->bus = eeh_dev_to_pci_dev(first_edev)->bus;
> + }
Hi Gavin
I have qestion, can we keep pe->bus for a device pe ? the value is the
bus which edev belongs to.
so that we can make the code more efficient for device pe.
I have no idea of whether this will cause side effect
Thanks
Mike
> +
> /* Put the edev to PE */
> list_add_tail(&edev->list, &pe->edevs);
> eeh_unlock();
> @@ -641,12 +657,18 @@ struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
> bus = pe->phb->bus;
> } else if (pe->type & EEH_PE_BUS ||
> pe->type & EEH_PE_DEVICE) {
> + if (pe->bus) {
> + bus = pe->bus;
> + goto out;
> + }
> +
> edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
> pdev = eeh_dev_to_pci_dev(edev);
> if (pdev)
> bus = pdev->bus;
> }
>
> +out:
> eeh_unlock();
>
> return bus;
^ permalink raw reply
* Re: [PATCH] powerpc/THP: Wait for all hash_page calls to finish before invalidating HPTE entries
From: Michael Neuling @ 2013-06-19 7:14 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: paulus, linuxppc-dev
In-Reply-To: <87ehbymvif.fsf@linux.vnet.ibm.com>
Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> wrote:
> Michael Neuling <mikey@neuling.org> writes:
>
> > Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> wrote:
> >
> >> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
> >>
> >> When we collapse normal pages to hugepage, we first clear the pmd, then invalidate all
> >> the PTE entries. The assumption here is that any low level page fault will see pmd as
> >> none and take the slow path that will wait on mmap_sem. But we could very well be in
> >> a hash_page with local ptep pointer value. Such a hash page can result in adding new
> >> HPTE entries for normal subpages/small page. That means we could be modifying the
> >> page content as we copy them to a huge page. Fix this by waiting on hash_page to finish
> >> after marking the pmd none and bfore invalidating HPTE entries. We use the heavy
> >> kick_all_cpus_sync(). This should be ok as we do this in the background khugepaged
> >> thread and not in application context. But we block page fault handling for this time.
> >> Also if we find collapse slow we can ideally increase the scan rate.
> >
> > 80 columns here
> >
> >>
> >> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
> >> ---
> >> arch/powerpc/mm/pgtable_64.c | 8 ++++++++
> >> 1 file changed, 8 insertions(+)
> >>
> >> diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
> >> index bbecac4..4bb44c3 100644
> >> --- a/arch/powerpc/mm/pgtable_64.c
> >> +++ b/arch/powerpc/mm/pgtable_64.c
> >> @@ -543,6 +543,14 @@ pmd_t pmdp_clear_flush(struct vm_area_struct *vma, unsigned long address,
> >> pmd = *pmdp;
> >> pmd_clear(pmdp);
> >> /*
> >> + * Wait for all pending hash_page to finish
> >> + * We can do this by waiting for a context switch to happen on
> >> + * the cpus. Any new hash_page after this will see pmd none
> >> + * and fallback to code that takes mmap_sem and hence will block
> >> + * for collapse to finish.
> >> + */
> >> + kick_all_cpus_sync();
> >> + /*
> >
> > This doesn't apply on mainline... I assume it's needs your TPH
> > patches?
>
> yes, They are on top V10 THP series
>
> >
> > Also, dumb question. Is this a bug we're fixing or just an optimisation?
>
> This is a bug fix. The details can be found at
Can you make this more obvious in the changelog (as well as making it 80
col). I don't see 'bug' mentioned anywhere. 'Fix' is mentioned
somewhere in the middle of the changelog.
>
> http://article.gmane.org/gmane.linux.ports.ppc.embedded/60266
OK, but V10 THP is not in yet, right? So why not roll it into that
series rather than pushing broken stuff and fixing it?
Mikey
^ permalink raw reply
* Re: [PATCH] powerpc/kvm: Handle transparent hugepage in KVM
From: Michael Neuling @ 2013-06-19 7:11 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: paulus, linuxppc-dev
In-Reply-To: <1371624245-17247-1-git-send-email-aneesh.kumar@linux.vnet.ibm.com>
Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> wrote:
> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>
> We can find pte that are splitting while walking page tables. Return
> None pte in that case.
Can you expand on this more please. There are a lot of details below
like removing a ldarx/stdcx loop that should be better described here.
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
> ---
> arch/powerpc/include/asm/kvm_book3s_64.h | 51 ++++++++++++++++++--------------
> arch/powerpc/kvm/book3s_64_mmu_hv.c | 7 +++--
> arch/powerpc/kvm/book3s_hv_rm_mmu.c | 4 +--
> 3 files changed, 34 insertions(+), 28 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h
> index 9c1ff33..ce20f7e 100644
> --- a/arch/powerpc/include/asm/kvm_book3s_64.h
> +++ b/arch/powerpc/include/asm/kvm_book3s_64.h
> @@ -162,33 +162,40 @@ static inline int hpte_cache_flags_ok(unsigned long ptel, unsigned long io_type)
> * Lock and read a linux PTE. If it's present and writable, atomically
> * set dirty and referenced bits and return the PTE, otherwise return 0.
This is comment still valid now the ldarx/stdcx is gone?
> */
> -static inline pte_t kvmppc_read_update_linux_pte(pte_t *p, int writing)
> +static inline pte_t kvmppc_read_update_linux_pte(pte_t *ptep, int writing,
> + unsigned int hugepage)
> {
> - pte_t pte, tmp;
> -
> - /* wait until _PAGE_BUSY is clear then set it atomically */
> - __asm__ __volatile__ (
> - "1: ldarx %0,0,%3\n"
> - " andi. %1,%0,%4\n"
> - " bne- 1b\n"
> - " ori %1,%0,%4\n"
> - " stdcx. %1,0,%3\n"
> - " bne- 1b"
> - : "=&r" (pte), "=&r" (tmp), "=m" (*p)
> - : "r" (p), "i" (_PAGE_BUSY)
> - : "cc");
> -
> - if (pte_present(pte)) {
> - pte = pte_mkyoung(pte);
> - if (writing && pte_write(pte))
> - pte = pte_mkdirty(pte);
> - }
> + pte_t old_pte, new_pte = __pte(0);
> +repeat:
> + do {
> + old_pte = pte_val(*ptep);
> + /*
> + * wait until _PAGE_BUSY is clear then set it atomically
> + */
> + if (unlikely(old_pte & _PAGE_BUSY))
> + goto repeat;
continue here? Please don't create looping primitives.
> +
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> + /* If hugepage and is trans splitting return None */
> + if (unlikely(hugepage &&
> + pmd_trans_splitting(pte_pmd(old_pte))))
Comment looks much like the code... seems redundant.
> + return __pte(0);
> +#endif
>
> - *p = pte; /* clears _PAGE_BUSY */
> + /* If pte is not present return None */
> + if (unlikely(!(old_pte & _PAGE_PRESENT)))
> + return __pte(0);
>
> - return pte;
> + new_pte = pte_mkyoung(old_pte);
> + if (writing && pte_write(old_pte))
> + new_pte = pte_mkdirty(new_pte);
> +
> + } while (old_pte != __cmpxchg_u64((unsigned long *)ptep,
> + old_pte, new_pte));
> + return new_pte;
> }
>
> +
Whitespace
> /* Return HPTE cache control bits corresponding to Linux pte bits */
> static inline unsigned long hpte_cache_bits(unsigned long pte_val)
> {
> diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
> index 5880dfb..e1a9415 100644
> --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
> +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
> @@ -675,6 +675,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
> }
> /* if the guest wants write access, see if that is OK */
> if (!writing && hpte_is_writable(r)) {
> + unsigned int shift;
> pte_t *ptep, pte;
>
> /*
> @@ -683,9 +684,9 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
> */
> rcu_read_lock_sched();
> ptep = find_linux_pte_or_hugepte(current->mm->pgd,
> - hva, NULL);
> - if (ptep && pte_present(*ptep)) {
> - pte = kvmppc_read_update_linux_pte(ptep, 1);
> + hva, &shift);
> + if (ptep) {
> + pte = kvmppc_read_update_linux_pte(ptep, 1, shift);
> if (pte_write(pte))
> write_ok = 1;
> }
> diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
> index dcf892d..39ae723 100644
> --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
> +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
> @@ -150,9 +150,7 @@ static pte_t lookup_linux_pte(pgd_t *pgdir, unsigned long hva,
> *pte_sizep = PAGE_SIZE;
> if (ps > *pte_sizep)
> return __pte(0);
> - if (!pte_present(*ptep))
> - return __pte(0);
> - return kvmppc_read_update_linux_pte(ptep, writing);
> + return kvmppc_read_update_linux_pte(ptep, writing, shift);
'shift' goes into the new 'hugepage' parameter? Doesn't seem logical?
Can we harmonise the name to make it less confusing?
Mikey
> }
>
> static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v)
> --
> 1.8.1.2
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: [PATCH] powerpc/THP: Wait for all hash_page calls to finish before invalidating HPTE entries
From: Aneesh Kumar K.V @ 2013-06-19 7:04 UTC (permalink / raw)
To: Michael Neuling; +Cc: paulus, linuxppc-dev
In-Reply-To: <7312.1371624946@ale.ozlabs.ibm.com>
Michael Neuling <mikey@neuling.org> writes:
> Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> wrote:
>
>> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>>
>> When we collapse normal pages to hugepage, we first clear the pmd, then invalidate all
>> the PTE entries. The assumption here is that any low level page fault will see pmd as
>> none and take the slow path that will wait on mmap_sem. But we could very well be in
>> a hash_page with local ptep pointer value. Such a hash page can result in adding new
>> HPTE entries for normal subpages/small page. That means we could be modifying the
>> page content as we copy them to a huge page. Fix this by waiting on hash_page to finish
>> after marking the pmd none and bfore invalidating HPTE entries. We use the heavy
>> kick_all_cpus_sync(). This should be ok as we do this in the background khugepaged
>> thread and not in application context. But we block page fault handling for this time.
>> Also if we find collapse slow we can ideally increase the scan rate.
>
> 80 columns here
>
>>
>> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
>> ---
>> arch/powerpc/mm/pgtable_64.c | 8 ++++++++
>> 1 file changed, 8 insertions(+)
>>
>> diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
>> index bbecac4..4bb44c3 100644
>> --- a/arch/powerpc/mm/pgtable_64.c
>> +++ b/arch/powerpc/mm/pgtable_64.c
>> @@ -543,6 +543,14 @@ pmd_t pmdp_clear_flush(struct vm_area_struct *vma, unsigned long address,
>> pmd = *pmdp;
>> pmd_clear(pmdp);
>> /*
>> + * Wait for all pending hash_page to finish
>> + * We can do this by waiting for a context switch to happen on
>> + * the cpus. Any new hash_page after this will see pmd none
>> + * and fallback to code that takes mmap_sem and hence will block
>> + * for collapse to finish.
>> + */
>> + kick_all_cpus_sync();
>> + /*
>
> This doesn't apply on mainline... I assume it's needs your TPH
> patches?
yes, They are on top V10 THP series
>
> Also, dumb question. Is this a bug we're fixing or just an optimisation?
This is a bug fix. The details can be found at
http://article.gmane.org/gmane.linux.ports.ppc.embedded/60266
-aneesh
^ permalink raw reply
* Re: [PATCH] powerpc/THP: Wait for all hash_page calls to finish before invalidating HPTE entries
From: Michael Neuling @ 2013-06-19 6:55 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: paulus, linuxppc-dev
In-Reply-To: <1371624294-19451-1-git-send-email-aneesh.kumar@linux.vnet.ibm.com>
Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> wrote:
> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>
> When we collapse normal pages to hugepage, we first clear the pmd, then invalidate all
> the PTE entries. The assumption here is that any low level page fault will see pmd as
> none and take the slow path that will wait on mmap_sem. But we could very well be in
> a hash_page with local ptep pointer value. Such a hash page can result in adding new
> HPTE entries for normal subpages/small page. That means we could be modifying the
> page content as we copy them to a huge page. Fix this by waiting on hash_page to finish
> after marking the pmd none and bfore invalidating HPTE entries. We use the heavy
> kick_all_cpus_sync(). This should be ok as we do this in the background khugepaged
> thread and not in application context. But we block page fault handling for this time.
> Also if we find collapse slow we can ideally increase the scan rate.
80 columns here
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
> ---
> arch/powerpc/mm/pgtable_64.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
> index bbecac4..4bb44c3 100644
> --- a/arch/powerpc/mm/pgtable_64.c
> +++ b/arch/powerpc/mm/pgtable_64.c
> @@ -543,6 +543,14 @@ pmd_t pmdp_clear_flush(struct vm_area_struct *vma, unsigned long address,
> pmd = *pmdp;
> pmd_clear(pmdp);
> /*
> + * Wait for all pending hash_page to finish
> + * We can do this by waiting for a context switch to happen on
> + * the cpus. Any new hash_page after this will see pmd none
> + * and fallback to code that takes mmap_sem and hence will block
> + * for collapse to finish.
> + */
> + kick_all_cpus_sync();
> + /*
This doesn't apply on mainline... I assume it's needs your TPH patches?
Also, dumb question. Is this a bug we're fixing or just an optimisation?
Mikey
> * Now invalidate the hpte entries in the range
> * covered by pmd. This make sure we take a
> * fault and will find the pmd as none, which will
> --
> 1.8.1.2
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* [PATCH] powerpc/THP: Wait for all hash_page calls to finish before invalidating HPTE entries
From: Aneesh Kumar K.V @ 2013-06-19 6:44 UTC (permalink / raw)
To: benh, paulus; +Cc: linuxppc-dev, Aneesh Kumar K.V
From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
When we collapse normal pages to hugepage, we first clear the pmd, then invalidate all
the PTE entries. The assumption here is that any low level page fault will see pmd as
none and take the slow path that will wait on mmap_sem. But we could very well be in
a hash_page with local ptep pointer value. Such a hash page can result in adding new
HPTE entries for normal subpages/small page. That means we could be modifying the
page content as we copy them to a huge page. Fix this by waiting on hash_page to finish
after marking the pmd none and bfore invalidating HPTE entries. We use the heavy
kick_all_cpus_sync(). This should be ok as we do this in the background khugepaged
thread and not in application context. But we block page fault handling for this time.
Also if we find collapse slow we can ideally increase the scan rate.
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
---
arch/powerpc/mm/pgtable_64.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
index bbecac4..4bb44c3 100644
--- a/arch/powerpc/mm/pgtable_64.c
+++ b/arch/powerpc/mm/pgtable_64.c
@@ -543,6 +543,14 @@ pmd_t pmdp_clear_flush(struct vm_area_struct *vma, unsigned long address,
pmd = *pmdp;
pmd_clear(pmdp);
/*
+ * Wait for all pending hash_page to finish
+ * We can do this by waiting for a context switch to happen on
+ * the cpus. Any new hash_page after this will see pmd none
+ * and fallback to code that takes mmap_sem and hence will block
+ * for collapse to finish.
+ */
+ kick_all_cpus_sync();
+ /*
* Now invalidate the hpte entries in the range
* covered by pmd. This make sure we take a
* fault and will find the pmd as none, which will
--
1.8.1.2
^ permalink raw reply related
* [PATCH] powerpc/kvm: Handle transparent hugepage in KVM
From: Aneesh Kumar K.V @ 2013-06-19 6:44 UTC (permalink / raw)
To: benh, paulus; +Cc: linuxppc-dev, Aneesh Kumar K.V
From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
We can find pte that are splitting while walking page tables. Return
None pte in that case.
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/kvm_book3s_64.h | 51 ++++++++++++++++++--------------
arch/powerpc/kvm/book3s_64_mmu_hv.c | 7 +++--
arch/powerpc/kvm/book3s_hv_rm_mmu.c | 4 +--
3 files changed, 34 insertions(+), 28 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h
index 9c1ff33..ce20f7e 100644
--- a/arch/powerpc/include/asm/kvm_book3s_64.h
+++ b/arch/powerpc/include/asm/kvm_book3s_64.h
@@ -162,33 +162,40 @@ static inline int hpte_cache_flags_ok(unsigned long ptel, unsigned long io_type)
* Lock and read a linux PTE. If it's present and writable, atomically
* set dirty and referenced bits and return the PTE, otherwise return 0.
*/
-static inline pte_t kvmppc_read_update_linux_pte(pte_t *p, int writing)
+static inline pte_t kvmppc_read_update_linux_pte(pte_t *ptep, int writing,
+ unsigned int hugepage)
{
- pte_t pte, tmp;
-
- /* wait until _PAGE_BUSY is clear then set it atomically */
- __asm__ __volatile__ (
- "1: ldarx %0,0,%3\n"
- " andi. %1,%0,%4\n"
- " bne- 1b\n"
- " ori %1,%0,%4\n"
- " stdcx. %1,0,%3\n"
- " bne- 1b"
- : "=&r" (pte), "=&r" (tmp), "=m" (*p)
- : "r" (p), "i" (_PAGE_BUSY)
- : "cc");
-
- if (pte_present(pte)) {
- pte = pte_mkyoung(pte);
- if (writing && pte_write(pte))
- pte = pte_mkdirty(pte);
- }
+ pte_t old_pte, new_pte = __pte(0);
+repeat:
+ do {
+ old_pte = pte_val(*ptep);
+ /*
+ * wait until _PAGE_BUSY is clear then set it atomically
+ */
+ if (unlikely(old_pte & _PAGE_BUSY))
+ goto repeat;
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ /* If hugepage and is trans splitting return None */
+ if (unlikely(hugepage &&
+ pmd_trans_splitting(pte_pmd(old_pte))))
+ return __pte(0);
+#endif
- *p = pte; /* clears _PAGE_BUSY */
+ /* If pte is not present return None */
+ if (unlikely(!(old_pte & _PAGE_PRESENT)))
+ return __pte(0);
- return pte;
+ new_pte = pte_mkyoung(old_pte);
+ if (writing && pte_write(old_pte))
+ new_pte = pte_mkdirty(new_pte);
+
+ } while (old_pte != __cmpxchg_u64((unsigned long *)ptep,
+ old_pte, new_pte));
+ return new_pte;
}
+
/* Return HPTE cache control bits corresponding to Linux pte bits */
static inline unsigned long hpte_cache_bits(unsigned long pte_val)
{
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 5880dfb..e1a9415 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -675,6 +675,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
}
/* if the guest wants write access, see if that is OK */
if (!writing && hpte_is_writable(r)) {
+ unsigned int shift;
pte_t *ptep, pte;
/*
@@ -683,9 +684,9 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
*/
rcu_read_lock_sched();
ptep = find_linux_pte_or_hugepte(current->mm->pgd,
- hva, NULL);
- if (ptep && pte_present(*ptep)) {
- pte = kvmppc_read_update_linux_pte(ptep, 1);
+ hva, &shift);
+ if (ptep) {
+ pte = kvmppc_read_update_linux_pte(ptep, 1, shift);
if (pte_write(pte))
write_ok = 1;
}
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index dcf892d..39ae723 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -150,9 +150,7 @@ static pte_t lookup_linux_pte(pgd_t *pgdir, unsigned long hva,
*pte_sizep = PAGE_SIZE;
if (ps > *pte_sizep)
return __pte(0);
- if (!pte_present(*ptep))
- return __pte(0);
- return kvmppc_read_update_linux_pte(ptep, writing);
+ return kvmppc_read_update_linux_pte(ptep, writing, shift);
}
static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v)
--
1.8.1.2
^ permalink raw reply related
* Re: [PATCH v4 0/8] Nvram-to-pstore
From: Michael Neuling @ 2013-06-19 6:38 UTC (permalink / raw)
To: Aruna Balakrishnaiah
Cc: jkenisto, tony.luck, keescook, mahesh, ccross, linux-kernel,
linuxppc-dev, paulus, anton, cbouatmailru
In-Reply-To: <51C150D8.2070706@linux.vnet.ibm.com>
Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com> wrote:
> Hi Michael,
>
> On Wednesday 19 June 2013 11:45 AM, Michael Neuling wrote:
> > Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com> wrote:
> >> Currently the kernel provides the contents of p-series NVRAM only as a
> >> simple stream of bytes via /dev/nvram, which must be interpreted in user
> >> space by the nvram command in the powerpc-utils package. This patch set
> >> exploits the pstore subsystem to expose each partition in NVRAM as a
> >> separate file in /dev/pstore. For instance, Oops messages will be stored
> >> in a file named [dmesg-nvram-2].
> >>
> >> Changes from v3:
> >> - Change filename prefix for of-config and common partition
> >>
> >> Changes from v2:
> >> - Fix renaming of pstore type ids in nvram.c
> >>
> >> Changes from v1:
> >> - Reduce #ifdefs by and remove forward declarations of pstore callbacks
> >> - Handle return value of nvram_write_os_partition
> >> - Remove empty pstore callbacks and register pstore only when pstore
> >> is configured
> > When booted on pHyp, I see /dev/nvram but not /dev/pstore, even if I
> > turn on CONFIG_PSTORE. Is there something else I need to add?
>
> You need to mount pstore to access the files.
>
> # mkdir /dev/pstore
> # mount -t pstore - /dev/pstore
>
> to unmount
>
> # umount /dev/pstore
>
> References: http://lwn.net/Articles/421297/
> Documentation/ABI/testing/pstore
Thanks
>
> >
> > Should we update pseries_defconfig to include CONFIG_PSTORE (which it
> > doesn't include currently)? Maybe turn on panic/oops via
> > CONFIG_PSTORE_RAM too?
>
> Yes. We should enable CONFIG_PSTORE by default in pseries_defconfig.
> We need not enable CONFIG_PSTORE_RAM for our case. Its for systems with
> persistent RAM.
Please send addition patch to do this.
Mikey
>
> > Other than that, the series looks clean. It's passes my build and boot
> > tests.
> >
> > I've not reviewed the contents of the patches.
> >
> > Mikey
> >
> >> ---
> >>
> >> Aruna Balakrishnaiah (8):
> >> powerpc/pseries: Remove syslog prefix in uncompressed oops text
> >> powerpc/pseries: Add version and timestamp to oops header
> >> powerpc/pseries: Introduce generic read function to read nvram-partitions
> >> powerpc/pseries: Read/Write oops nvram partition via pstore
> >> powerpc/pseries: Read rtas partition via pstore
> >> powerpc/pseries: Distinguish between a os-partition and non-os partition
> >> powerpc/pseries: Read of-config partition via pstore
> >> powerpc/pseries: Read common partition via pstore
> >>
> >>
> >> arch/powerpc/platforms/pseries/nvram.c | 353 +++++++++++++++++++++++++++-----
> >> fs/pstore/inode.c | 9 +
> >> include/linux/pstore.h | 4
> >> 3 files changed, 313 insertions(+), 53 deletions(-)
> >>
> >> --
> >>
> >> _______________________________________________
> >> Linuxppc-dev mailing list
> >> Linuxppc-dev@lists.ozlabs.org
> >> https://lists.ozlabs.org/listinfo/linuxppc-dev
> >>
> > _______________________________________________
> > Linuxppc-dev mailing list
> > Linuxppc-dev@lists.ozlabs.org
> > https://lists.ozlabs.org/listinfo/linuxppc-dev
> >
>
^ permalink raw reply
* Re: [PATCH 02/31] powerpc/eeh: Cleanup for EEH core
From: Gavin Shan @ 2013-06-19 6:37 UTC (permalink / raw)
To: Gavin Shan; +Cc: linuxppc-dev
In-Reply-To: <1371544435-4943-3-git-send-email-shangw@linux.vnet.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 391 bytes --]
On Tue, Jun 18, 2013 at 04:33:26PM +0800, Gavin Shan wrote:
>While moving EEH core around from pSeries platform directory to
>arch/powerpc/kernel (in previous one patch), there has lots of
>complaints for coding style from "git show". The patch is going
>to fix them.
>
>Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Please use the updated [2/31] in the attachment.
Thanks,
Gavin
[-- Attachment #2: 0002-powerpc-eeh-Move-common-part-to-kernel-directory.patch --]
[-- Type: text/x-diff, Size: 177569 bytes --]
>From e6b677059b83c5ae96a7d8ad495c427a82ecd4b4 Mon Sep 17 00:00:00 2001
From: Gavin Shan <shangw@linux.vnet.ibm.com>
Date: Wed, 19 Jun 2013 13:53:12 +0800
Subject: [PATCH 02/31] powerpc/eeh: Move common part to kernel directory
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 | 318 +++++++++
arch/powerpc/kernel/eeh_dev.c | 112 ++++
arch/powerpc/kernel/eeh_driver.c | 551 ++++++++++++++++
arch/powerpc/kernel/eeh_event.c | 142 ++++
arch/powerpc/kernel/eeh_pe.c | 653 +++++++++++++++++++
arch/powerpc/kernel/eeh_sysfs.c | 74 +++
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, 2912 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..b92bf5a 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_dn.o pci_hotplug.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..8a83451
--- /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);
+ 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..1d5d9a6
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_cache.c
@@ -0,0 +1,318 @@
+/*
+ * 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..fb927af
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -0,0 +1,551 @@
+/*
+ * 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..e7ae348
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_sysfs.c
@@ -0,0 +1,74 @@
+/*
+ * 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 8a83451..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 0acc5a2..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;
--
1.7.5.4
^ permalink raw reply related
* [PATCH -V2] powerpc: Fix bad pmd error with book3E config
From: Aneesh Kumar K.V @ 2013-06-19 6:34 UTC (permalink / raw)
To: benh, scottwood; +Cc: linuxppc-dev, Aneesh Kumar K.V
From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
Book3E uses the hugepd at PMD level and don't encode pte directly
at the pmd level. So it will find the lower bits of pmd set
and the pmd_bad check throws error. Infact the current code
will never take the free_hugepd_range call at all because it will
clear the pmd if it find a hugepd pointer. Fix this by clearing
bad pmd only if it is not a hugepd pointer.
This is regression introduced by e2b3d202d1dba8f3546ed28224ce485bc50010be
"powerpc: Switch 16GB and 16MB explicit hugepages to a different page table format"
Reported-by: Scott Wood <scottwood@freescale.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
---
arch/powerpc/mm/hugetlbpage.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
index f2f01fd..5555778 100644
--- a/arch/powerpc/mm/hugetlbpage.c
+++ b/arch/powerpc/mm/hugetlbpage.c
@@ -536,8 +536,14 @@ static void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud,
do {
pmd = pmd_offset(pud, addr);
next = pmd_addr_end(addr, end);
- if (pmd_none_or_clear_bad(pmd))
+ if (!is_hugepd(pmd)) {
+ /*
+ * if it is not hugepd pointer, we should already find
+ * it cleared.
+ */
+ WARN_ON(!pmd_none_or_clear_bad(pmd));
continue;
+ }
#ifdef CONFIG_PPC_FSL_BOOK3E
/*
* Increment next by the size of the huge mapping since
--
1.8.1.2
^ permalink raw reply related
* Re: [PATCH v4 0/8] Nvram-to-pstore
From: Aruna Balakrishnaiah @ 2013-06-19 6:34 UTC (permalink / raw)
To: Michael Neuling
Cc: jkenisto, tony.luck, keescook, mahesh, ccross, linux-kernel,
linuxppc-dev, paulus, anton, cbouatmailru
In-Reply-To: <1822.1371622503@ale.ozlabs.ibm.com>
Hi Michael,
On Wednesday 19 June 2013 11:45 AM, Michael Neuling wrote:
> Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com> wrote:
>> Currently the kernel provides the contents of p-series NVRAM only as a
>> simple stream of bytes via /dev/nvram, which must be interpreted in user
>> space by the nvram command in the powerpc-utils package. This patch set
>> exploits the pstore subsystem to expose each partition in NVRAM as a
>> separate file in /dev/pstore. For instance, Oops messages will be stored
>> in a file named [dmesg-nvram-2].
>>
>> Changes from v3:
>> - Change filename prefix for of-config and common partition
>>
>> Changes from v2:
>> - Fix renaming of pstore type ids in nvram.c
>>
>> Changes from v1:
>> - Reduce #ifdefs by and remove forward declarations of pstore callbacks
>> - Handle return value of nvram_write_os_partition
>> - Remove empty pstore callbacks and register pstore only when pstore
>> is configured
> When booted on pHyp, I see /dev/nvram but not /dev/pstore, even if I
> turn on CONFIG_PSTORE. Is there something else I need to add?
You need to mount pstore to access the files.
# mkdir /dev/pstore
# mount -t pstore - /dev/pstore
to unmount
# umount /dev/pstore
References: http://lwn.net/Articles/421297/
Documentation/ABI/testing/pstore
>
> Should we update pseries_defconfig to include CONFIG_PSTORE (which it
> doesn't include currently)? Maybe turn on panic/oops via
> CONFIG_PSTORE_RAM too?
Yes. We should enable CONFIG_PSTORE by default in pseries_defconfig.
We need not enable CONFIG_PSTORE_RAM for our case. Its for systems with
persistent RAM.
> Other than that, the series looks clean. It's passes my build and boot
> tests.
>
> I've not reviewed the contents of the patches.
>
> Mikey
>
>> ---
>>
>> Aruna Balakrishnaiah (8):
>> powerpc/pseries: Remove syslog prefix in uncompressed oops text
>> powerpc/pseries: Add version and timestamp to oops header
>> powerpc/pseries: Introduce generic read function to read nvram-partitions
>> powerpc/pseries: Read/Write oops nvram partition via pstore
>> powerpc/pseries: Read rtas partition via pstore
>> powerpc/pseries: Distinguish between a os-partition and non-os partition
>> powerpc/pseries: Read of-config partition via pstore
>> powerpc/pseries: Read common partition via pstore
>>
>>
>> arch/powerpc/platforms/pseries/nvram.c | 353 +++++++++++++++++++++++++++-----
>> fs/pstore/inode.c | 9 +
>> include/linux/pstore.h | 4
>> 3 files changed, 313 insertions(+), 53 deletions(-)
>>
>> --
>>
>> _______________________________________________
>> Linuxppc-dev mailing list
>> Linuxppc-dev@lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/linuxppc-dev
>>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: [PATCH] powerpc: Fix bad pmd error with book3E config
From: Michael Neuling @ 2013-06-19 6:27 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: scottwood, linuxppc-dev
In-Reply-To: <87wqpqhb77.fsf@linux.vnet.ibm.com>
Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> wrote:
> Michael Neuling <mikey@neuling.org> writes:
>
> > Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> wrote:
> >
> >> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
> >>
> >> Book3E uses the hugepd at PMD level and don't encode pte directly
> >> at the pmd level. So it will find the lower bits of pmd set
> >> and the pmd_bad check throws error. Infact the current code
> >> will never take the free_hugepd_range call at all because it will
> >> clear the pmd if it find a hugepd pointer.
> >>
> >
> > Please explain what changes you are making. Currently you are only
> > describing what the issue is.
>
> will do
>
> >
> > Also include which the SHA1 which caused the regression (ie
> > e2b3d202d1dba8f3546ed28224ce485bc50010be "powerpc: Switch 16GB and 16MB
> > explicit hugepages to a different page table format")
>
> will add
>
> >
> > Mikey
> >
> >> Reported-by: Scott Wood <scottwood@freescale.com>
> >> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
> >> ---
> >> arch/powerpc/mm/hugetlbpage.c | 29 ++++++++++++++++++-----------
> >> 1 file changed, 18 insertions(+), 11 deletions(-)
> >>
> >> diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
> >> index f2f01fd..0d3d3ee 100644
> >> --- a/arch/powerpc/mm/hugetlbpage.c
> >> +++ b/arch/powerpc/mm/hugetlbpage.c
> >> @@ -536,19 +536,26 @@ static void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud,
> >> do {
> >> pmd = pmd_offset(pud, addr);
> >> next = pmd_addr_end(addr, end);
> >> - if (pmd_none_or_clear_bad(pmd))
> >> - continue;
> >> + if (!is_hugepd(pmd)) {
> >> + /*
> >> + * if it is not hugepd pointer, we should already find
> >> + * it cleared.
> >> + */
> >> + if (!pmd_none_or_clear_bad(pmd))
> >> + WARN_ON(1);
> >
> > How often are we going to hit this? Should this be a warn_on once or
> > even a bug_on?
>
> it should never happen. But i was thinking killing the system may a bit
> too much, hence WARN_ON
Maybe WARN_ON_ONCE. If you do hit it once, you are going to hit it a
lot?
Mikey
>
> >
> > Also just make it:
> > WARN_ON(!pmd_none_or_clear_bad(pmd))
> >
>
> will do
>
> -aneesh
>
^ permalink raw reply
* Re: [PATCH] powerpc: Fix bad pmd error with book3E config
From: Aneesh Kumar K.V @ 2013-06-19 6:22 UTC (permalink / raw)
To: Michael Neuling; +Cc: scottwood, linuxppc-dev
In-Reply-To: <25129.1371620895@ale.ozlabs.ibm.com>
Michael Neuling <mikey@neuling.org> writes:
> Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> wrote:
>
>> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>>
>> Book3E uses the hugepd at PMD level and don't encode pte directly
>> at the pmd level. So it will find the lower bits of pmd set
>> and the pmd_bad check throws error. Infact the current code
>> will never take the free_hugepd_range call at all because it will
>> clear the pmd if it find a hugepd pointer.
>>
>
> Please explain what changes you are making. Currently you are only
> describing what the issue is.
will do
>
> Also include which the SHA1 which caused the regression (ie
> e2b3d202d1dba8f3546ed28224ce485bc50010be "powerpc: Switch 16GB and 16MB
> explicit hugepages to a different page table format")
will add
>
> Mikey
>
>> Reported-by: Scott Wood <scottwood@freescale.com>
>> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
>> ---
>> arch/powerpc/mm/hugetlbpage.c | 29 ++++++++++++++++++-----------
>> 1 file changed, 18 insertions(+), 11 deletions(-)
>>
>> diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
>> index f2f01fd..0d3d3ee 100644
>> --- a/arch/powerpc/mm/hugetlbpage.c
>> +++ b/arch/powerpc/mm/hugetlbpage.c
>> @@ -536,19 +536,26 @@ static void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud,
>> do {
>> pmd = pmd_offset(pud, addr);
>> next = pmd_addr_end(addr, end);
>> - if (pmd_none_or_clear_bad(pmd))
>> - continue;
>> + if (!is_hugepd(pmd)) {
>> + /*
>> + * if it is not hugepd pointer, we should already find
>> + * it cleared.
>> + */
>> + if (!pmd_none_or_clear_bad(pmd))
>> + WARN_ON(1);
>
> How often are we going to hit this? Should this be a warn_on once or
> even a bug_on?
it should never happen. But i was thinking killing the system may a bit
too much, hence WARN_ON
>
> Also just make it:
> WARN_ON(!pmd_none_or_clear_bad(pmd))
>
will do
-aneesh
^ permalink raw reply
* Re: [PATCH 14/31] powerpc/eeh: EEH core to handle special event
From: Gavin Shan @ 2013-06-19 6:19 UTC (permalink / raw)
To: Gavin Shan; +Cc: linuxppc-dev
In-Reply-To: <1371544435-4943-15-git-send-email-shangw@linux.vnet.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 769 bytes --]
On Tue, Jun 18, 2013 at 04:33:38PM +0800, Gavin Shan wrote:
>On PowerNV platform, the EEH event caused by interrupt won't have
>binding PE. The patch enables EEH core to handle the special event.
>To avoid the current logic we have, The eeh_handle_event() is renamed
>to eeh_handle_normal_event(), and the eeh_handle_special_event() is
>introduced. The function eeh_handle_event() dispatches to above
>2 functions according to the input parameter. Besides, new backend
>"next_error" added to eeh_ops and it's expected to have following
>return values:
>
> 4 - Dead IOC 3 - Dead PHB
> 2 - Fenced PHB 1 - Frozen PE
> 0 - No error found
>
>Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Please use the updated [14/31] as attached.
Thanks,
Gavin
[-- Attachment #2: 0014-powerpc-eeh-EEH-core-to-handle-special-event.patch --]
[-- Type: text/x-diff, Size: 6203 bytes --]
>From a463ab4790bdf1a19920b5aefc269dc759dd6a9e Mon Sep 17 00:00:00 2001
From: Gavin Shan <shangw@linux.vnet.ibm.com>
Date: Wed, 19 Jun 2013 13:53:15 +0800
Subject: [PATCH 14/31] powerpc/eeh: EEH core to handle special event
On PowerNV platform, the EEH event caused by interrupt won't have
binding PE. The patch enables EEH core to handle the special event.
To avoid the current logic we have, The eeh_handle_event() is renamed
to eeh_handle_normal_event(), and the eeh_handle_special_event() is
introduced. The function eeh_handle_event() dispatches to above two
functions according to the input parameter. Besides, new backend
"next_error" added to eeh_ops and it's expected to have following
return values:
4 - Dead IOC 3 - Dead PHB
2 - Fenced PHB 1 - Frozen PE
0 - No error found
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 2 +
arch/powerpc/kernel/eeh_driver.c | 128 ++++++++++++++++++++++++++++++++-----
2 files changed, 112 insertions(+), 18 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 0c0ac93..a0b11fb 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -53,6 +53,7 @@ struct device_node;
#define EEH_PE_ISOLATED (1 << 0) /* Isolated PE */
#define EEH_PE_RECOVERING (1 << 1) /* Recovering PE */
+#define EEH_PE_PHB_DEAD (1 << 2) /* Dead PHB */
struct eeh_pe {
int type; /* PE type: PHB/Bus/Device */
@@ -145,6 +146,7 @@ struct eeh_ops {
int (*configure_bridge)(struct eeh_pe *pe);
int (*read_config)(struct device_node *dn, int where, int size, u32 *val);
int (*write_config)(struct device_node *dn, int where, int size, u32 val);
+ int (*next_error)(struct eeh_pe **pe);
};
extern struct eeh_ops *eeh_ops;
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 678bc6c..0974e13 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -399,24 +399,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
*/
#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)
+static void eeh_handle_normal_event(struct eeh_pe *pe)
{
struct pci_bus *frozen_bus;
int rc = 0;
@@ -554,3 +537,112 @@ perm_error:
if (frozen_bus)
pcibios_remove_pci_devices(frozen_bus);
}
+
+static void eeh_handle_special_event(void)
+{
+ struct eeh_pe *pe, *phb_pe;
+ struct pci_bus *bus;
+ struct pci_controller *hose, *tmp;
+ unsigned long flags;
+ int rc = 0;
+
+ /*
+ * The return value from next_error() has been classified as follows.
+ * It might be good to enumerate them. However, next_error() is only
+ * supported by PowerNV platform for now. So it would be fine to use
+ * integer directly:
+ *
+ * 4 - Dead IOC 3 - Dead PHB
+ * 2 - Fenced PHB 1 - Frozen PE
+ * 0 - No error found
+ *
+ */
+ rc = eeh_ops->next_error(&pe);
+ if (rc <= 0)
+ return;
+
+ switch (rc) {
+ case 4:
+ /* Mark all PHBs in dead state */
+ eeh_serialize_lock(&flags);
+ list_for_each_entry_safe(hose, tmp,
+ &hose_list, list_node) {
+ phb_pe = eeh_phb_pe_get(hose);
+ if (!phb_pe) continue;
+
+ eeh_pe_state_mark(phb_pe,
+ EEH_PE_ISOLATED | EEH_PE_PHB_DEAD);
+ }
+ eeh_serialize_unlock(flags);
+
+ /* Purge all events */
+ eeh_remove_event(NULL);
+ break;
+ case 3:
+ case 2:
+ case 1:
+ /* Mark the PE in fenced state */
+ eeh_serialize_lock(&flags);
+ if (rc == 3)
+ eeh_pe_state_mark(pe,
+ EEH_PE_ISOLATED | EEH_PE_PHB_DEAD);
+ else
+ eeh_pe_state_mark(pe,
+ EEH_PE_ISOLATED | EEH_PE_RECOVERING);
+ eeh_serialize_unlock(flags);
+
+ /* Purge all events of the PHB */
+ eeh_remove_event(pe);
+ break;
+ default:
+ pr_err("%s: Invalid value %d from next_error()\n",
+ __func__, rc);
+ return;
+ }
+
+ /*
+ * For fenced PHB and frozen PE, it's handled as normal
+ * event. We have to remove the affected PHBs for dead
+ * PHB and IOC
+ */
+ if (rc == 2 || rc == 1)
+ eeh_handle_normal_event(pe);
+ else {
+ list_for_each_entry_safe(hose, tmp,
+ &hose_list, list_node) {
+ phb_pe = eeh_phb_pe_get(hose);
+ if (!phb_pe || !(phb_pe->state & EEH_PE_PHB_DEAD))
+ continue;
+
+ bus = eeh_pe_bus_get(phb_pe);
+ /* Notify all devices that they're about to go down. */
+ eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
+ pcibios_remove_pci_devices(bus);
+ }
+ }
+}
+
+/**
+ * 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)
+{
+ if (pe)
+ eeh_handle_normal_event(pe);
+ else
+ eeh_handle_special_event();
+}
--
1.7.5.4
^ permalink raw reply related
* Re: [PATCH 01/31] powerpc/eeh: Move common part to kernel directory
From: Gavin Shan @ 2013-06-19 6:18 UTC (permalink / raw)
To: Gavin Shan; +Cc: Michael Neuling, linuxppc-dev
In-Reply-To: <20130619061153.GA31968@shangw.(null)>
On Wed, Jun 19, 2013 at 02:11:53PM +0800, Gavin Shan wrote:
>On Wed, Jun 19, 2013 at 01:58:06PM +1000, Michael Neuling wrote:
>>Bunch of whitespace issues here:
>>
>>% git am ~/Mail/linuxppc/31202
>>Applying: powerpc/eeh: Move common part to kernel directory
>>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:437: trailing whitespace.
>>
>>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:594: space before tab in indent.
>> */
>>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:607: trailing whitespace.
>>
>>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:608: trailing whitespace.
>> /* We might get hit with another EEH freeze as soon as the
>>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:673: trailing whitespace.
>>
>>error: patch failed: arch/powerpc/platforms/pseries/eeh_pe.c:1
>>error: arch/powerpc/platforms/pseries/eeh_pe.c: patch does not apply
>>Patch failed at 0001 powerpc/eeh: Move common part to kernel directory
>>When you have resolved this problem run "git am --resolved".
>>If you would prefer to skip this patch, instead run "git am --skip".
>>To restore the original branch and stop patching run "git am --abort".
>>
>
>Sorry for the inconvenience, Mikey. Please apply the updated [01/31] and [02/31] in
>the attachment. The left patches except [17/31] are not changed. The updated [17/31]
>will be contained in the attachment of the corresponding thread (original message
>about [17/31]). Something like:
>
Sorry, it's updated [14/31] instead of updated [17/31].
updated [01/31]
updated [02/31]
[03/31]
[04/31]
:
:
updated [14/31] (not updated [14/31])
[18/31]
[19/31]
:
:
[31/31]
Thanks,
Gavin
^ permalink raw reply
* Re: [PATCH v4 0/8] Nvram-to-pstore
From: Michael Neuling @ 2013-06-19 6:15 UTC (permalink / raw)
To: Aruna Balakrishnaiah
Cc: jkenisto, tony.luck, keescook, cbouatmailru, linux-kernel,
linuxppc-dev, ccross, paulus, anton, mahesh
In-Reply-To: <20130605184756.25794.43872.stgit@aruna-ThinkPad-T420>
Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com> wrote:
> Currently the kernel provides the contents of p-series NVRAM only as a
> simple stream of bytes via /dev/nvram, which must be interpreted in user
> space by the nvram command in the powerpc-utils package. This patch set
> exploits the pstore subsystem to expose each partition in NVRAM as a
> separate file in /dev/pstore. For instance, Oops messages will be stored
> in a file named [dmesg-nvram-2].
>
> Changes from v3:
> - Change filename prefix for of-config and common partition
>
> Changes from v2:
> - Fix renaming of pstore type ids in nvram.c
>
> Changes from v1:
> - Reduce #ifdefs by and remove forward declarations of pstore callbacks
> - Handle return value of nvram_write_os_partition
> - Remove empty pstore callbacks and register pstore only when pstore
> is configured
When booted on pHyp, I see /dev/nvram but not /dev/pstore, even if I
turn on CONFIG_PSTORE. Is there something else I need to add?
Should we update pseries_defconfig to include CONFIG_PSTORE (which it
doesn't include currently)? Maybe turn on panic/oops via
CONFIG_PSTORE_RAM too?
Other than that, the series looks clean. It's passes my build and boot
tests.
I've not reviewed the contents of the patches.
Mikey
> ---
>
> Aruna Balakrishnaiah (8):
> powerpc/pseries: Remove syslog prefix in uncompressed oops text
> powerpc/pseries: Add version and timestamp to oops header
> powerpc/pseries: Introduce generic read function to read nvram-partitions
> powerpc/pseries: Read/Write oops nvram partition via pstore
> powerpc/pseries: Read rtas partition via pstore
> powerpc/pseries: Distinguish between a os-partition and non-os partition
> powerpc/pseries: Read of-config partition via pstore
> powerpc/pseries: Read common partition via pstore
>
>
> arch/powerpc/platforms/pseries/nvram.c | 353 +++++++++++++++++++++++++++-----
> fs/pstore/inode.c | 9 +
> include/linux/pstore.h | 4
> 3 files changed, 313 insertions(+), 53 deletions(-)
>
> --
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: [PATCH 01/31] powerpc/eeh: Move common part to kernel directory
From: Gavin Shan @ 2013-06-19 6:11 UTC (permalink / raw)
To: Michael Neuling; +Cc: linuxppc-dev, Gavin Shan
In-Reply-To: <10320.1371614286@ale.ozlabs.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 1500 bytes --]
On Wed, Jun 19, 2013 at 01:58:06PM +1000, Michael Neuling wrote:
>Bunch of whitespace issues here:
>
>% git am ~/Mail/linuxppc/31202
>Applying: powerpc/eeh: Move common part to kernel directory
>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:437: trailing whitespace.
>
>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:594: space before tab in indent.
> */
>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:607: trailing whitespace.
>
>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:608: trailing whitespace.
> /* We might get hit with another EEH freeze as soon as the
>/home/mikey/src/powerpc-test/.git/rebase-apply/patch:673: trailing whitespace.
>
>error: patch failed: arch/powerpc/platforms/pseries/eeh_pe.c:1
>error: arch/powerpc/platforms/pseries/eeh_pe.c: patch does not apply
>Patch failed at 0001 powerpc/eeh: Move common part to kernel directory
>When you have resolved this problem run "git am --resolved".
>If you would prefer to skip this patch, instead run "git am --skip".
>To restore the original branch and stop patching run "git am --abort".
>
Sorry for the inconvenience, Mikey. Please apply the updated [01/31] and [02/31] in
the attachment. The left patches except [17/31] are not changed. The updated [17/31]
will be contained in the attachment of the corresponding thread (original message
about [17/31]). Something like:
updated [01/31]
updated [02/31]
[03/31]
[04/31]
:
:
updated [17/31]
[18/31]
[19/31]
:
:
[31/31]
Thanks,
Gavin
[-- Attachment #2: 0001-powerpc-eeh-Cleanup-for-EEH-core.patch --]
[-- Type: text/x-diff, Size: 4038 bytes --]
>From 1e0a43dc5fd15daaf851e695f12cdea25c66d7e9 Mon Sep 17 00:00:00 2001
From: Gavin Shan <shangw@linux.vnet.ibm.com>
Date: Wed, 19 Jun 2013 13:53:12 +0800
Subject: [PATCH 01/31] powerpc/eeh: Cleanup for EEH core
Cleanup on EEH core to remove unnecessary whitespaces.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/eeh.c | 22 +++++++++++-----------
arch/powerpc/platforms/pseries/eeh_driver.c | 14 +++++++-------
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 6b73d6c..8a83451 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -368,7 +368,7 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
}
eeh_stats.slot_resets++;
-
+
/* Avoid repeated reports of this failure, including problems
* with other functions on this device, and functions under
* bridges.
@@ -525,7 +525,7 @@ static void eeh_reset_pe_once(struct eeh_pe *pe)
* 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)
@@ -538,8 +538,8 @@ static void eeh_reset_pe_once(struct eeh_pe *pe)
*/
#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
+
+ /* 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.
*/
@@ -604,7 +604,7 @@ void eeh_save_bars(struct eeh_dev *edev)
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]);
}
@@ -803,12 +803,12 @@ 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);
- }
+ 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);
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index a3fefb6..0acc5a2 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -154,9 +154,9 @@ static void eeh_enable_irq(struct pci_dev *dev)
* 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
+ *
+ * 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)
@@ -376,9 +376,9 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
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,
+ * 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) {
@@ -520,7 +520,7 @@ void eeh_handle_event(struct eeh_pe *pe)
eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
return;
-
+
excess_failures:
/*
* About 90% of all real-life EEH failures in the field
--
1.7.5.4
[-- Attachment #3: 0002-powerpc-eeh-Move-common-part-to-kernel-directory.patch --]
[-- Type: text/x-diff, Size: 177569 bytes --]
>From e6b677059b83c5ae96a7d8ad495c427a82ecd4b4 Mon Sep 17 00:00:00 2001
From: Gavin Shan <shangw@linux.vnet.ibm.com>
Date: Wed, 19 Jun 2013 13:53:12 +0800
Subject: [PATCH 02/31] powerpc/eeh: Move common part to kernel directory
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 | 318 +++++++++
arch/powerpc/kernel/eeh_dev.c | 112 ++++
arch/powerpc/kernel/eeh_driver.c | 551 ++++++++++++++++
arch/powerpc/kernel/eeh_event.c | 142 ++++
arch/powerpc/kernel/eeh_pe.c | 653 +++++++++++++++++++
arch/powerpc/kernel/eeh_sysfs.c | 74 +++
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, 2912 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..b92bf5a 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_dn.o pci_hotplug.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..8a83451
--- /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);
+ 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..1d5d9a6
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_cache.c
@@ -0,0 +1,318 @@
+/*
+ * 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..fb927af
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -0,0 +1,551 @@
+/*
+ * 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..e7ae348
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_sysfs.c
@@ -0,0 +1,74 @@
+/*
+ * 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 8a83451..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 0acc5a2..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;
--
1.7.5.4
^ permalink raw reply related
* Re: [PATCH] powerpc: Fix bad pmd error with book3E config
From: Michael Neuling @ 2013-06-19 5:48 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: scottwood, linuxppc-dev
In-Reply-To: <1371033004-15864-1-git-send-email-aneesh.kumar@linux.vnet.ibm.com>
Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> wrote:
> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>
> Book3E uses the hugepd at PMD level and don't encode pte directly
> at the pmd level. So it will find the lower bits of pmd set
> and the pmd_bad check throws error. Infact the current code
> will never take the free_hugepd_range call at all because it will
> clear the pmd if it find a hugepd pointer.
>
Please explain what changes you are making. Currently you are only
describing what the issue is.
Also include which the SHA1 which caused the regression (ie
e2b3d202d1dba8f3546ed28224ce485bc50010be "powerpc: Switch 16GB and 16MB
explicit hugepages to a different page table format")
Mikey
> Reported-by: Scott Wood <scottwood@freescale.com>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
> ---
> arch/powerpc/mm/hugetlbpage.c | 29 ++++++++++++++++++-----------
> 1 file changed, 18 insertions(+), 11 deletions(-)
>
> diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
> index f2f01fd..0d3d3ee 100644
> --- a/arch/powerpc/mm/hugetlbpage.c
> +++ b/arch/powerpc/mm/hugetlbpage.c
> @@ -536,19 +536,26 @@ static void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud,
> do {
> pmd = pmd_offset(pud, addr);
> next = pmd_addr_end(addr, end);
> - if (pmd_none_or_clear_bad(pmd))
> - continue;
> + if (!is_hugepd(pmd)) {
> + /*
> + * if it is not hugepd pointer, we should already find
> + * it cleared.
> + */
> + if (!pmd_none_or_clear_bad(pmd))
> + WARN_ON(1);
How often are we going to hit this? Should this be a warn_on once or
even a bug_on?
Also just make it:
WARN_ON(!pmd_none_or_clear_bad(pmd))
Mikey
> + } else {
> #ifdef CONFIG_PPC_FSL_BOOK3E
> - /*
> - * Increment next by the size of the huge mapping since
> - * there may be more than one entry at this level for a
> - * single hugepage, but all of them point to
> - * the same kmem cache that holds the hugepte.
> - */
> - next = addr + (1 << hugepd_shift(*(hugepd_t *)pmd));
> + /*
> + * Increment next by the size of the huge mapping since
> + * there may be more than one entry at this level for a
> + * single hugepage, but all of them point to
> + * the same kmem cache that holds the hugepte.
> + */
> + next = addr + (1 << hugepd_shift(*(hugepd_t *)pmd));
> #endif
> - free_hugepd_range(tlb, (hugepd_t *)pmd, PMD_SHIFT,
> - addr, next, floor, ceiling);
> + free_hugepd_range(tlb, (hugepd_t *)pmd, PMD_SHIFT,
> + addr, next, floor, ceiling);
> + }
> } while (addr = next, addr != end);
>
> start &= PUD_MASK;
> --
> 1.8.1.2
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: [PATCH 1/2] perf/Power7: Save dcache_src fields in sample record.
From: Sukadev Bhattiprolu @ 2013-06-19 5:31 UTC (permalink / raw)
To: Michael Neuling
Cc: Anton Blanchard, linux-kernel, Stephane Eranian, linuxppc-dev,
Paul Mackerras, mingo
In-Reply-To: <23819.1371616910@ale.ozlabs.ibm.com>
Michael Neuling [mikey@neuling.org] wrote:
| Suka,
|
| One of these two patches breaks pmac32_defconfig and I suspect all other
| 32 bit configs (against mainline)
|
| arch/powerpc/perf/core-book3s.c: In function 'record_and_restart':
| arch/powerpc/perf/core-book3s.c:1632:4: error: passing argument 1 of 'ppmu->get_mem_data_src' from incompatible pointer type [-Werror]
| arch/powerpc/perf/core-book3s.c:1632:4: note: expected 'struct perf_sample_data *' but argument is of type 'struct perf_sample_data *'
|
| benh is busy enough without this junk. Please check the simple things
| like white space and compile errors!
Sorry about that.
BTW, this was an early patch more to get some feedback on mapping of
memory hierarchy levels to Power and not intended to be merged. I have
been reworking the patch based on other comments.
Sukadev
^ 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