* Re: [PATCH V4 1/8] elf: Add new PowerPC specifc core note sections
From: Anshuman Khandual @ 2014-11-25 5:26 UTC (permalink / raw)
To: Andrew Morton
Cc: mikey, james.hogan, avagin, Paul.Clothier, peterz, palves,
linux-kernel, oleg, dhowells, linuxppc-dev, davej, tglx, sukadev,
davem, sam.bobroff
In-Reply-To: <20141121154310.489097fb4403d664f2156043@linux-foundation.org>
On 11/22/2014 05:13 AM, Andrew Morton wrote:
> On Tue, 11 Nov 2014 10:56:30 +0530 Anshuman Khandual <khandual@linux.vnet.ibm.com> wrote:
>
>> This patch adds four new core note sections for PowerPC transactional
>> memory and one core note section for general miscellaneous debug registers.
>> These addition of new elf core note sections extends the existing elf ABI
>> without affecting it in any manner.
>>
>> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
>> ---
>> include/uapi/linux/elf.h | 5 +++++
>> 1 file changed, 5 insertions(+)
>>
>> diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
>> index ea9bf25..2260fc0 100644
>> --- a/include/uapi/linux/elf.h
>> +++ b/include/uapi/linux/elf.h
>> @@ -379,6 +379,11 @@ typedef struct elf64_shdr {
>> #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
>> #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
>> #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
>> +#define NT_PPC_TM_SPR 0x103 /* PowerPC TM special registers */
>> +#define NT_PPC_TM_CGPR 0x104 /* PowerpC TM checkpointed GPR */
>> +#define NT_PPC_TM_CFPR 0x105 /* PowerPC TM checkpointed FPR */
>> +#define NT_PPC_TM_CVMX 0x106 /* PowerPC TM checkpointed VMX */
>> +#define NT_PPC_MISC 0x107 /* PowerPC miscellaneous registers */
>> #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
>> #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
>> #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
>
> ack from me, if that was at all expected.
Thanks Andrew.
>
> Please cc Shuah Khan <shuah.kh@samsung.com> on the tools/testing/selftests
> changes.
Sure, will do.
^ permalink raw reply
* [V5 0/7] Add new powerpc specific ELF core notes
From: Anshuman Khandual @ 2014-11-25 8:05 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev
Cc: shuahkh, mikey, james.hogan, avagin, Paul.Clothier, peterz,
palves, oleg, davem, dhowells, kirjanov, davej, akpm, sukadev,
tglx, sam.bobroff
This patch series adds five new ELF core note sections which can be
used with existing ptrace request PTRACE_GETREGSET-SETREGSET for accessing
various transactional memory and miscellaneous debug register sets on powerpc
platform.
Previous versions:
==================
RFC: https://lkml.org/lkml/2014/4/1/292
V1: https://lkml.org/lkml/2014/4/2/43
V2: https://lkml.org/lkml/2014/5/5/88
V3: https://lkml.org/lkml/2014/5/23/486
V4: https://lkml.org/lkml/2014/11/11/6
Changes in V5:
--------------
- Changed flush_tmregs_to_thread, so not to take into account self tracing
- Dropped the 3rd patch in the series which had merged two functions
- Fixed one build problem for the misc debug register patch
- Accommodated almost all the review comments from Suka on the 6th patch
- Minor changes to the self test program
- Changed commit messages for some of the patches
Changes in V4:
--------------
- Added one test program into the powerpc selftest bucket in this regard
- Split the 2nd patch in the previous series into four different patches
- Accommodated most of the review comments on the previous patch series
- Added a patch to merge functions __switch_to_tm and tm_reclaim_task
Changes in V3:
--------------
- Added two new error paths in every TM related get/set functions when regset
support is not present on the system (ENODEV) or when the process does not
have any transaction active (ENODATA) in the context
- Installed the active hooks for all the newly added regset core note types
Changes in V2:
--------------
- Removed all the power specific ptrace requests corresponding to new NT_PPC_*
elf core note types. Now all the register sets can be accessed from ptrace
through PTRACE_GETREGSET/PTRACE_SETREGSET using the individual NT_PPC* core
note type instead
- Fixed couple of attribute values for REGSET_TM_CGPR register set
- Renamed flush_tmreg_to_thread as flush_tmregs_to_thread
- Fixed 32 bit checkpointed GPR support
- Changed commit messages accordingly
Test Result
-----------
The patch series has been verified both in 32 bit and 64 bit compiled test
program. Test result for the selftest test (64 bit compiled) can be found here.
test: tm_ptrace
tags: git_version:v3.18-rc6-8-ge2aa4ce
===Testing TM based PTRACE Interface===
Testing TM specific SPR:
TFHAR: 10001098
TEXASR: de0000018c000001
TFIAR: c000000000041858
TM ORIG_MSR: 800000050000f032
TM CH DSCR: a (PASSED)
TM CH TAR: 14 (PASSED)
TM CH PPR: 8000000000000 (PASSED)
Testing TM checkpointed GPR:
TM CH NIP: 10001098
TM CH LINK: 10000ea0
TM CH CCR: 24000422
TM CH GPR[0]: 0 (PASSED)
TM CH GPR[1]: 1 (PASSED)
TM CH GPR[2]: 2 (PASSED)
TM CH GPR[3]: 3 (PASSED)
TM CH GPR[4]: 4 (PASSED)
TM CH GPR[5]: 5 (PASSED)
TM CH GPR[6]: 6 (PASSED)
TM CH GPR[7]: 7 (PASSED)
TM CH GPR[8]: 8 (PASSED)
TM CH GPR[9]: 9 (PASSED)
TM CH GPR[10]: a (PASSED)
TM CH GPR[11]: b (PASSED)
TM CH GPR[12]: c (PASSED)
TM CH GPR[13]: d (PASSED)
TM CH GPR[14]: e (PASSED)
TM CH GPR[15]: f (PASSED)
TM CH GPR[16]: 0 (PASSED)
TM CH GPR[17]: 1 (PASSED)
TM CH GPR[18]: 2 (PASSED)
TM CH GPR[19]: 3 (PASSED)
TM CH GPR[20]: 4 (PASSED)
TM CH GPR[21]: 5 (PASSED)
TM CH GPR[22]: 6 (PASSED)
TM CH GPR[23]: 7 (PASSED)
TM CH GPR[24]: 8 (PASSED)
TM CH GPR[25]: 9 (PASSED)
TM CH GPR[26]: a (PASSED)
TM CH GPR[27]: b (PASSED)
TM CH GPR[28]: c (PASSED)
TM CH GPR[29]: d (PASSED)
TM CH GPR[30]: e (PASSED)
TM CH GPR[31]: f (PASSED)
Testing TM checkpointed FPR:
TM CH FPSCR: 0
TM CH FPR[0]: 0 (PASSED)
TM CH FPR[1]: 1 (PASSED)
TM CH FPR[2]: 2 (PASSED)
TM CH FPR[3]: 3 (PASSED)
TM CH FPR[4]: 4 (PASSED)
TM CH FPR[5]: 5 (PASSED)
TM CH FPR[6]: 6 (PASSED)
TM CH FPR[7]: 7 (PASSED)
TM CH FPR[8]: 8 (PASSED)
TM CH FPR[9]: 9 (PASSED)
TM CH FPR[10]: a (PASSED)
TM CH FPR[11]: b (PASSED)
TM CH FPR[12]: c (PASSED)
TM CH FPR[13]: d (PASSED)
TM CH FPR[14]: e (PASSED)
TM CH FPR[15]: f (PASSED)
TM CH FPR[16]: 0 (PASSED)
TM CH FPR[17]: 1 (PASSED)
TM CH FPR[18]: 2 (PASSED)
TM CH FPR[19]: 3 (PASSED)
TM CH FPR[20]: 4 (PASSED)
TM CH FPR[21]: 5 (PASSED)
TM CH FPR[22]: 6 (PASSED)
TM CH FPR[23]: 7 (PASSED)
TM CH FPR[24]: 8 (PASSED)
TM CH FPR[25]: 9 (PASSED)
TM CH FPR[26]: a (PASSED)
TM CH FPR[27]: b (PASSED)
TM CH FPR[28]: c (PASSED)
TM CH FPR[29]: d (PASSED)
TM CH FPR[30]: e (PASSED)
TM CH FPR[31]: f (PASSED)
Testing TM running GPR:
TM RN NIP: 100011b0
TM RN LINK: 10000ea0
TM RN CCR: 4000422
TM RN GPR[0]: f (PASSED)
TM RN GPR[1]: e (PASSED)
TM RN GPR[2]: d (PASSED)
TM RN GPR[3]: c (PASSED)
TM RN GPR[4]: b (PASSED)
TM RN GPR[5]: a (PASSED)
TM RN GPR[6]: 9 (PASSED)
TM RN GPR[7]: 8 (PASSED)
TM RN GPR[8]: 7 (PASSED)
TM RN GPR[9]: 6 (PASSED)
TM RN GPR[10]: 5 (PASSED)
TM RN GPR[11]: 4 (PASSED)
TM RN GPR[12]: 3 (PASSED)
TM RN GPR[13]: 2 (PASSED)
TM RN GPR[14]: 1 (PASSED)
TM RN GPR[15]: 0 (PASSED)
TM RN GPR[16]: f (PASSED)
TM RN GPR[17]: e (PASSED)
TM RN GPR[18]: d (PASSED)
TM RN GPR[19]: c (PASSED)
TM RN GPR[20]: b (PASSED)
TM RN GPR[21]: a (PASSED)
TM RN GPR[22]: 9 (PASSED)
TM RN GPR[23]: 8 (PASSED)
TM RN GPR[24]: 7 (PASSED)
TM RN GPR[25]: 6 (PASSED)
TM RN GPR[26]: 5 (PASSED)
TM RN GPR[27]: 4 (PASSED)
TM RN GPR[28]: 3 (PASSED)
TM RN GPR[29]: 2 (PASSED)
TM RN GPR[30]: 1 (PASSED)
TM RN GPR[31]: 0 (PASSED)
Testing TM running FPR:
TM RN FPSCR: 0
TM RN FPR[0]: f (PASSED)
TM RN FPR[1]: e (PASSED)
TM RN FPR[2]: d (PASSED)
TM RN FPR[3]: c (PASSED)
TM RN FPR[4]: b (PASSED)
TM RN FPR[5]: a (PASSED)
TM RN FPR[6]: 9 (PASSED)
TM RN FPR[7]: 8 (PASSED)
TM RN FPR[8]: 7 (PASSED)
TM RN FPR[9]: 6 (PASSED)
TM RN FPR[10]: 5 (PASSED)
TM RN FPR[11]: 4 (PASSED)
TM RN FPR[12]: 3 (PASSED)
TM RN FPR[13]: 2 (PASSED)
TM RN FPR[14]: 1 (PASSED)
TM RN FPR[15]: 0 (PASSED)
TM RN FPR[16]: f (PASSED)
TM RN FPR[17]: e (PASSED)
TM RN FPR[18]: d (PASSED)
TM RN FPR[19]: c (PASSED)
TM RN FPR[20]: b (PASSED)
TM RN FPR[21]: a (PASSED)
TM RN FPR[22]: 9 (PASSED)
TM RN FPR[23]: 8 (PASSED)
TM RN FPR[24]: 7 (PASSED)
TM RN FPR[25]: 6 (PASSED)
TM RN FPR[26]: 5 (PASSED)
TM RN FPR[27]: 4 (PASSED)
TM RN FPR[28]: 3 (PASSED)
TM RN FPR[29]: 2 (PASSED)
TM RN FPR[30]: 1 (PASSED)
TM RN FPR[31]: 0 (PASSED)
Testing TM running MISC debug registers:
TM RN DSCR: 32 (PASSED)
TM RN TAR: 3c (PASSED)
TM RN PPR: 4000000000000 (PASSED)
success: tm_ptrace
Anshuman Khandual (7):
elf: Add new powerpc specifc core note sections
powerpc, process: Add the function flush_tmregs_to_thread
powerpc, ptrace: Enable fpr_(get/set) for transactional memory
powerpc, ptrace: Enable vr_(get/set) for transactional memory
powerpc, ptrace: Enable support for transactional memory register sets
powerpc, ptrace: Enable support for miscellaneous debug registers
selftests, powerpc: Add test case for TM related ptrace interface
arch/powerpc/include/asm/switch_to.h | 8 +
arch/powerpc/include/uapi/asm/elf.h | 3 +
arch/powerpc/kernel/process.c | 20 +
arch/powerpc/kernel/ptrace.c | 987 ++++++++++++++++++++++++-
include/uapi/linux/elf.h | 5 +
tools/testing/selftests/powerpc/tm/Makefile | 2 +-
tools/testing/selftests/powerpc/tm/tm-ptrace.c | 542 ++++++++++++++
7 files changed, 1542 insertions(+), 25 deletions(-)
create mode 100644 tools/testing/selftests/powerpc/tm/tm-ptrace.c
--
1.9.3
^ permalink raw reply
* [V5 1/7] elf: Add new powerpc specifc core note sections
From: Anshuman Khandual @ 2014-11-25 8:05 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev
Cc: shuahkh, mikey, james.hogan, avagin, Paul.Clothier, peterz,
palves, oleg, davem, dhowells, kirjanov, davej, akpm, sukadev,
tglx, sam.bobroff
In-Reply-To: <1416902731-22446-1-git-send-email-khandual@linux.vnet.ibm.com>
This patch adds four new ELF core note sections for powerpc
transactional memory and one new ELF core note section for
powerpc general miscellaneous debug registers. These addition
of new ELF core note sections extends the existing ELF ABI
without affecting it in any manner.
Acked-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
include/uapi/linux/elf.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
index ea9bf25..2260fc0 100644
--- a/include/uapi/linux/elf.h
+++ b/include/uapi/linux/elf.h
@@ -379,6 +379,11 @@ typedef struct elf64_shdr {
#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
+#define NT_PPC_TM_SPR 0x103 /* PowerPC TM special registers */
+#define NT_PPC_TM_CGPR 0x104 /* PowerpC TM checkpointed GPR */
+#define NT_PPC_TM_CFPR 0x105 /* PowerPC TM checkpointed FPR */
+#define NT_PPC_TM_CVMX 0x106 /* PowerPC TM checkpointed VMX */
+#define NT_PPC_MISC 0x107 /* PowerPC miscellaneous registers */
#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
--
1.9.3
^ permalink raw reply related
* [V5 2/7] powerpc, process: Add the function flush_tmregs_to_thread
From: Anshuman Khandual @ 2014-11-25 8:05 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev
Cc: shuahkh, mikey, james.hogan, avagin, Paul.Clothier, peterz,
palves, oleg, davem, dhowells, kirjanov, davej, akpm, sukadev,
tglx, sam.bobroff
In-Reply-To: <1416902731-22446-1-git-send-email-khandual@linux.vnet.ibm.com>
This patch creates a function flush_tmregs_to_thread which
will then be used by subsequent patches in this series. The
function checks for self tracing ptrace interface attempts
while in the TM context and logs appropriate warning message.
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/switch_to.h | 8 ++++++++
arch/powerpc/kernel/process.c | 20 ++++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h
index 58abeda..23752a9 100644
--- a/arch/powerpc/include/asm/switch_to.h
+++ b/arch/powerpc/include/asm/switch_to.h
@@ -82,6 +82,14 @@ static inline void flush_spe_to_thread(struct task_struct *t)
}
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+extern void flush_tmregs_to_thread(struct task_struct *);
+#else
+static inline void flush_tmregs_to_thread(struct task_struct *t)
+{
+}
+#endif
+
static inline void clear_task_ebb(struct task_struct *t)
{
#ifdef CONFIG_PPC_BOOK3S_64
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 923cd2d..0013f24 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -745,6 +745,26 @@ void restore_tm_state(struct pt_regs *regs)
#define __switch_to_tm(prev)
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+void flush_tmregs_to_thread(struct task_struct *tsk)
+{
+ /*
+ * Process self tracing is not yet supported through
+ * ptrace interface. Ptrace generic code should have
+ * prevented this from happening in the first place.
+ * Warn once here with the message, if some how it
+ * is attempted.
+ */
+ WARN_ONCE(tsk == current,
+ "Not expecting ptrace on self: TM regs may be incorrect\n");
+
+ /*
+ * If task is not current, it should have been flushed
+ * already to it's thread_struct during __switch_to().
+ */
+}
+#endif
+
struct task_struct *__switch_to(struct task_struct *prev,
struct task_struct *new)
{
--
1.9.3
^ permalink raw reply related
* [V5 4/7] powerpc, ptrace: Enable vr_(get/set) for transactional memory
From: Anshuman Khandual @ 2014-11-25 8:05 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev
Cc: shuahkh, mikey, james.hogan, avagin, Paul.Clothier, peterz,
palves, oleg, davem, dhowells, kirjanov, davej, akpm, sukadev,
tglx, sam.bobroff
In-Reply-To: <1416902731-22446-1-git-send-email-khandual@linux.vnet.ibm.com>
This patch enables the vr_get which gets the running value of all
the VMX registers and the vr_set which sets the running value of
of all the VMX registers to accommodate in transaction ptrace
interface based requests.
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
arch/powerpc/kernel/ptrace.c | 94 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 91 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index fd36b32..2bbbd10 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -523,10 +523,30 @@ static int vr_active(struct task_struct *target,
return target->thread.used_vr ? regset->n : 0;
}
+/*
+ * vr_get
+ *
+ * When the transaction is active, 'transact_vr' holds the current running
+ * value of all the VMX registers and 'vr_state' holds the last checkpointed
+ * value of all the VMX registers for the current transaction to fall back
+ * on in case it aborts. When transaction is not active 'vr_state' holds
+ * the current running state of all the VMX registers. So this function which
+ * gets the current running values of all the VMX registers, needs to know
+ * whether any transaction is active or not.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * vector128 vr[32];
+ * vector128 vscr;
+ * vector128 vrsave;
+ * };
+ */
static int vr_get(struct task_struct *target, const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
{
+ struct thread_vr_state *addr;
int ret;
flush_altivec_to_thread(target);
@@ -534,8 +554,19 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset,
BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) !=
offsetof(struct thread_vr_state, vr[32]));
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_fp_to_thread(target);
+ flush_tmregs_to_thread(target);
+ addr = &target->thread.transact_vr;
+ } else {
+ addr = &target->thread.vr_state;
+ }
+#else
+ addr = &target->thread.vr_state;
+#endif
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
- &target->thread.vr_state, 0,
+ addr, 0,
33 * sizeof(vector128));
if (!ret) {
/*
@@ -546,7 +577,16 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset,
u32 word;
} vrsave;
memset(&vrsave, 0, sizeof(vrsave));
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ vrsave.word = target->thread.transact_vrsave;
+ else
+ vrsave.word = target->thread.vrsave;
+#else
vrsave.word = target->thread.vrsave;
+#endif
+
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave,
33 * sizeof(vector128), -1);
}
@@ -554,10 +594,30 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset,
return ret;
}
+/*
+ * vr_set
+ *
+ * When the transaction is active, 'transact_vr' holds the current running
+ * value of all the VMX registers and 'vr_state' holds the last checkpointed
+ * value of all the VMX registers for the current transaction to fall back
+ * on in case it aborts. When transaction is not active 'vr_state' holds
+ * the current running state of all the VMX registers. So this function which
+ * sets the current running values of all the VMX registers, needs to know
+ * whether any transaction is active or not.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * vector128 vr[32];
+ * vector128 vscr;
+ * vector128 vrsave;
+ * };
+ */
static int vr_set(struct task_struct *target, const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
+ struct thread_vr_state *addr;
int ret;
flush_altivec_to_thread(target);
@@ -565,8 +625,19 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset,
BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) !=
offsetof(struct thread_vr_state, vr[32]));
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_fp_to_thread(target);
+ flush_tmregs_to_thread(target);
+ addr = &target->thread.transact_vr;
+ } else {
+ addr = &target->thread.vr_state;
+ }
+#else
+ addr = &target->thread.vr_state;
+#endif
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &target->thread.vr_state, 0,
+ addr, 0,
33 * sizeof(vector128));
if (!ret && count > 0) {
/*
@@ -577,11 +648,28 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset,
u32 word;
} vrsave;
memset(&vrsave, 0, sizeof(vrsave));
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ vrsave.word = target->thread.transact_vrsave;
+ else
+ vrsave.word = target->thread.vrsave;
+#else
vrsave.word = target->thread.vrsave;
+#endif
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave,
33 * sizeof(vector128), -1);
- if (!ret)
+ if (!ret) {
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ target->thread.transact_vrsave = vrsave.word;
+ else
+ target->thread.vrsave = vrsave.word;
+#else
target->thread.vrsave = vrsave.word;
+#endif
+ }
}
return ret;
--
1.9.3
^ permalink raw reply related
* [V5 5/7] powerpc, ptrace: Enable support for transactional memory register sets
From: Anshuman Khandual @ 2014-11-25 8:05 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev
Cc: shuahkh, mikey, james.hogan, avagin, Paul.Clothier, peterz,
palves, oleg, davem, dhowells, kirjanov, davej, akpm, sukadev,
tglx, sam.bobroff
In-Reply-To: <1416902731-22446-1-git-send-email-khandual@linux.vnet.ibm.com>
This patch enables get and set of transactional memory related register
sets through PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing
four new powerpc specific register sets i.e REGSET_TM_SPR, REGSET_TM_CGPR,
REGSET_TM_CFPR, REGSET_CVMX support corresponding to these following new
ELF core note types added previously in this regard.
(1) NT_PPC_TM_SPR
(2) NT_PPC_TM_CGPR
(3) NT_PPC_TM_CFPR
(4) NT_PPC_TM_CVMX
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
arch/powerpc/include/uapi/asm/elf.h | 2 +
arch/powerpc/kernel/ptrace.c | 667 +++++++++++++++++++++++++++++++++++-
2 files changed, 654 insertions(+), 15 deletions(-)
diff --git a/arch/powerpc/include/uapi/asm/elf.h b/arch/powerpc/include/uapi/asm/elf.h
index 59dad11..fdc8e2f 100644
--- a/arch/powerpc/include/uapi/asm/elf.h
+++ b/arch/powerpc/include/uapi/asm/elf.h
@@ -91,6 +91,8 @@
#define ELF_NGREG 48 /* includes nip, msr, lr, etc. */
#define ELF_NFPREG 33 /* includes fpscr */
+#define ELF_NVMX 34 /* includes all vector registers */
+#define ELF_NTMSPRREG 7 /* includes TM sprs, org_msr, dscr, tar, ppr */
typedef unsigned long elf_greg_t64;
typedef elf_greg_t64 elf_gregset_t64[ELF_NGREG];
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 2bbbd10..faa9058 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -63,6 +63,11 @@ struct pt_regs_offset {
{.name = STR(gpr##num), .offset = offsetof(struct pt_regs, gpr[num])}
#define REG_OFFSET_END {.name = NULL, .offset = 0}
+/* Some common structure offsets */
+#define TSO(f) (offsetof(struct thread_struct, f))
+#define TVSO(f) (offsetof(struct thread_vr_state, f))
+#define TFSO(f) (offsetof(struct thread_fp_state, f))
+
static const struct pt_regs_offset regoffset_table[] = {
GPR_OFFSET_NAME(0),
GPR_OFFSET_NAME(1),
@@ -792,6 +797,532 @@ static int evr_set(struct task_struct *target, const struct user_regset *regset,
}
#endif /* CONFIG_SPE */
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+/*
+ * tm_spr_active
+ *
+ * This function checks number of available regisers in
+ * the transactional memory SPR category.
+ */
+static int tm_spr_active(struct task_struct *target,
+ const struct user_regset *regset)
+{
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return 0;
+
+ return regset->n;
+}
+
+/*
+ * tm_spr_get
+ *
+ * This function gets transactional memory related SPR registers
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct {
+ * u64 tm_tfhar;
+ * u64 tm_texasr;
+ * u64 tm_tfiar;
+ * unsigned long tm_orig_msr;
+ * unsigned long tm_tar;
+ * unsigned long tm_ppr;
+ * unsigned long tm_dscr;
+ * };
+ */
+static int tm_spr_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ /* Build tests */
+ BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
+ BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
+ BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
+ BUILD_BUG_ON(TSO(ckpt_regs) + sizeof(struct pt_regs) != TSO(tm_tar));
+ BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
+ BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ /* Flush the states */
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+
+ /* TFHAR register */
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfhar, 0, sizeof(u64));
+
+ /* TEXASR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_texasr, sizeof(u64),
+ 2 * sizeof(u64));
+
+ /* TFIAR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfiar,
+ 2 * sizeof(u64), 3 * sizeof(u64));
+
+ /* TM checkpointed original MSR */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_orig_msr, 3 * sizeof(u64),
+ 3 * sizeof(u64) + sizeof(unsigned long));
+
+ /* TM checkpointed TAR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tar, 3 * sizeof(u64) +
+ sizeof(unsigned long) ,
+ 3 * sizeof(u64) + 2 * sizeof(unsigned long));
+
+ /* TM checkpointed PPR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_ppr, 3 * sizeof(u64) +
+ 2 * sizeof(unsigned long),
+ 3 * sizeof(u64) + 3 * sizeof(unsigned long));
+
+ /* TM checkpointed DSCR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_dscr, 3 * sizeof(u64) +
+ 3 * sizeof(unsigned long),
+ 3 * sizeof(u64) + 4 * sizeof(unsigned long));
+ return ret;
+}
+
+/*
+ * tm_spr_set
+ *
+ * This function sets transactional memory related SPR registers
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct {
+ * u64 tm_tfhar;
+ * u64 tm_texasr;
+ * u64 tm_tfiar;
+ * unsigned long tm_orig_msr;
+ * unsigned long tm_tar;
+ * unsigned long tm_ppr;
+ * unsigned long tm_dscr;
+ * };
+ */
+static int tm_spr_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+
+ /* Build tests */
+ BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
+ BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
+ BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
+ BUILD_BUG_ON(TSO(ckpt_regs) + sizeof(struct pt_regs) != TSO(tm_tar));
+ BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
+ BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ /* Flush the states */
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+
+ /* TFHAR register */
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfhar, 0, sizeof(u64));
+
+ /* TEXASR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_texasr, sizeof(u64),
+ 2 * sizeof(u64));
+
+ /* TFIAR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfiar,
+ 2 * sizeof(u64), 3 * sizeof(u64));
+
+
+ /* TM checkpointed orig MSR */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_orig_msr, 3 * sizeof(u64),
+ 3 * sizeof(u64) + sizeof(unsigned long));
+
+
+ /* TM checkpointed TAR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tar, 3 * sizeof(u64) +
+ sizeof(unsigned long), 3 * sizeof(u64) +
+ 2 * sizeof(unsigned long));
+
+ /* TM checkpointed PPR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_ppr, 3 * sizeof(u64) +
+ 2 * sizeof(unsigned long), 3 * sizeof(u64) +
+ 3 * sizeof(unsigned long));
+
+ /* TM checkpointed DSCR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_dscr, 3 * sizeof(u64) +
+ 3 * sizeof(unsigned long), 3 * sizeof(u64) +
+ 4 * sizeof(unsigned long));
+ return ret;
+}
+
+/*
+ * tm_cgpr_active
+ *
+ * This function checks the number of available regisers in
+ * transaction checkpointed GPR category.
+ */
+static int tm_cgpr_active(struct task_struct *target,
+ const struct user_regset *regset)
+{
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return 0;
+
+ return regset->n;
+}
+
+/*
+ * tm_cgpr_get
+ *
+ * This function gets transaction checkpointed GPR registers
+ *
+ * When the transaction is active, 'ckpt_regs' holds all the checkpointed
+ * GPR register values for the current transaction to fall back on if it
+ * aborts in between. This function gets those checkpointed GPR registers.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * struct pt_regs ckpt_regs;
+ * };
+ */
+static int tm_cgpr_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ckpt_regs, 0,
+ sizeof(struct pt_regs));
+ return ret;
+}
+
+/*
+ * tm_cgpr_set
+ *
+ * This function sets in transaction checkpointed GPR registers
+ *
+ * When the transaction is active, 'ckpt_regs' holds the checkpointed
+ * GPR register values for the current transaction to fall back on if it
+ * aborts in between. This function sets those checkpointed GPR registers.
+ *
+ * Userspace intaerface buffer:
+ *
+ * struct data {
+ * struct pt_regs ckpt_regs;
+ * };
+ */
+static int tm_cgpr_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ckpt_regs, 0,
+ sizeof(struct pt_regs));
+ return ret;
+}
+
+/*
+ * tm_cfpr_active
+ *
+ * This function checks number of available regisers in
+ * transaction checkpointed FPR category.
+ */
+static int tm_cfpr_active(struct task_struct *target,
+ const struct user_regset *regset)
+{
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return 0;
+
+ return regset->n;
+}
+
+/*
+ * tm_cfpr_get
+ *
+ * This function gets in transaction checkpointed FPR registers
+ *
+ * When the transaction is active 'fp_state' holds the checkpointed
+ * values for the current transaction to fall back on if it aborts
+ * in between. This function gets those checkpointed FPR registers.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ *};
+ */
+static int tm_cfpr_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ u64 buf[33];
+ int i;
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+
+ /* copy to local buffer then write that out */
+ for (i = 0; i < 32 ; i++)
+ buf[i] = target->thread.TS_FPR(i);
+ buf[32] = target->thread.fp_state.fpscr;
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+}
+
+/*
+ * tm_cfpr_set
+ *
+ * This function sets in transaction checkpointed FPR registers
+ *
+ * When the transaction is active 'fp_state' holds the checkpointed
+ * FPR register values for the current transaction to fall back on
+ * if it aborts in between. This function sets these checkpointed
+ * FPR registers.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ *};
+ */
+static int tm_cfpr_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ u64 buf[33];
+ int i;
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+
+ /* copy to local buffer then write that out */
+ i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+ if (i)
+ return i;
+ for (i = 0; i < 32 ; i++)
+ target->thread.TS_FPR(i) = buf[i];
+ target->thread.fp_state.fpscr = buf[32];
+ return 0;
+}
+
+/*
+ * tm_cvmx_active
+ *
+ * This function checks the number of available regisers in
+ * checkpointed VMX category.
+ */
+static int tm_cvmx_active(struct task_struct *target,
+ const struct user_regset *regset)
+{
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return 0;
+
+ return regset->n;
+}
+
+/*
+ * tm_cvmx_get
+ *
+ * This function gets in transaction checkpointed VMX registers
+ *
+ * When the transaction is active 'vr_state' and 'vr_save' hold
+ * the checkpointed values for the current transaction to fall
+ * back on if it aborts in between.
+ *
+ * User interface buffer:
+ *
+ * struct data {
+ * vector128 vr[32];
+ * vector128 vscr;
+ * vector128 vrsave;
+ *};
+ */
+static int tm_cvmx_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ BUILD_BUG_ON(TVSO(vscr) != TVSO(vr[32]));
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ /* Flush the state */
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.vr_state, 0,
+ 33 * sizeof(vector128));
+ if (!ret) {
+ /*
+ * Copy out only the low-order word of vrsave.
+ */
+ union {
+ elf_vrreg_t reg;
+ u32 word;
+ } vrsave;
+ memset(&vrsave, 0, sizeof(vrsave));
+ vrsave.word = target->thread.vrsave;
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave,
+ 33 * sizeof(vector128), -1);
+ }
+
+ return ret;
+}
+
+/*
+ * tm_cvmx_set
+ *
+ * This function sets in transaction checkpointed VMX registers
+ *
+ * When the transaction is active 'vr_state' and 'vr_save' hold
+ * the checkpointed values for the current transaction to fall
+ * back on if it aborts in between.
+ *
+ * Userspace interface buffer:
+ *
+ * struct data {
+ * vector128 vr[32];
+ * vector128 vscr;
+ * vector128 vrsave;
+ *};
+ */
+static int tm_cvmx_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+
+ BUILD_BUG_ON(TVSO(vscr) != TVSO(vr[32]));
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.vr_state, 0,
+ 33 * sizeof(vector128));
+ if (!ret && count > 0) {
+ /*
+ * We use only the low-order word of vrsave.
+ */
+ union {
+ elf_vrreg_t reg;
+ u32 word;
+ } vrsave;
+ memset(&vrsave, 0, sizeof(vrsave));
+ vrsave.word = target->thread.vrsave;
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave,
+ 33 * sizeof(vector128), -1);
+ if (!ret)
+ target->thread.vrsave = vrsave.word;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
/*
* These are our native regset flavors.
@@ -808,6 +1339,12 @@ enum powerpc_regset {
#ifdef CONFIG_SPE
REGSET_SPE,
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ REGSET_TM_SPR, /* TM specific SPR registers */
+ REGSET_TM_CGPR, /* TM checkpointed GPR registers */
+ REGSET_TM_CFPR, /* TM checkpointed FPR registers */
+ REGSET_TM_CVMX, /* TM checkpointed VMX registers */
+#endif
};
static const struct user_regset native_regsets[] = {
@@ -842,6 +1379,28 @@ static const struct user_regset native_regsets[] = {
.active = evr_active, .get = evr_get, .set = evr_set
},
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ [REGSET_TM_SPR] = {
+ .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
+ },
+ [REGSET_TM_CGPR] = {
+ .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG,
+ .size = sizeof(long), .align = sizeof(long),
+ .active = tm_cgpr_active, .get = tm_cgpr_get, .set = tm_cgpr_set
+ },
+ [REGSET_TM_CFPR] = {
+ .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
+ .size = sizeof(double), .align = sizeof(double),
+ .active = tm_cfpr_active, .get = tm_cfpr_get, .set = tm_cfpr_set
+ },
+ [REGSET_TM_CVMX] = {
+ .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX,
+ .size = sizeof(vector128), .align = sizeof(vector128),
+ .active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
+ },
+#endif
};
static const struct user_regset_view user_ppc_native_view = {
@@ -852,26 +1411,38 @@ static const struct user_regset_view user_ppc_native_view = {
#ifdef CONFIG_PPC64
#include <linux/compat.h>
-static int gpr32_get(struct task_struct *target,
+static int common_gpr32_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
- void *kbuf, void __user *ubuf)
+ void *kbuf, void __user *ubuf, bool in_tm)
{
- const unsigned long *regs = &target->thread.regs->gpr[0];
+ const unsigned long *regs = NULL;
compat_ulong_t *k = kbuf;
compat_ulong_t __user *u = ubuf;
compat_ulong_t reg;
int i;
- if (target->thread.regs == NULL)
- return -EIO;
+ if (in_tm) {
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ regs = &target->thread.ckpt_regs.gpr[0];
+#endif
+ } else {
+ regs = &target->thread.regs->gpr[0];
- if (!FULL_REGS(target->thread.regs)) {
- /* We have a partial register set. Fill 14-31 with bogus values */
- for (i = 14; i < 32; i++)
- target->thread.regs->gpr[i] = NV_REG_POISON;
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+ if (!FULL_REGS(target->thread.regs)) {
+ /*
+ * We have a partial register set.
+ * Fill 14-31 with bogus values.
+ */
+ for (i = 14; i < 32; i++)
+ target->thread.regs->gpr[i] = NV_REG_POISON;
+ }
}
+ BUG_ON(!regs);
pos /= sizeof(reg);
count /= sizeof(reg);
@@ -909,21 +1480,30 @@ static int gpr32_get(struct task_struct *target,
PT_REGS_COUNT * sizeof(reg), -1);
}
-static int gpr32_set(struct task_struct *target,
+static int common_gpr32_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
+ const void *kbuf, const void __user *ubuf, bool in_tm)
{
- unsigned long *regs = &target->thread.regs->gpr[0];
+ unsigned long *regs = NULL;
const compat_ulong_t *k = kbuf;
const compat_ulong_t __user *u = ubuf;
compat_ulong_t reg;
- if (target->thread.regs == NULL)
- return -EIO;
+ if (in_tm) {
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ regs = &target->thread.ckpt_regs.gpr[0];
+#endif
+ } else {
+ regs = &target->thread.regs->gpr[0];
- CHECK_FULL_REGS(target->thread.regs);
+ if (target->thread.regs == NULL)
+ return -EIO;
+ CHECK_FULL_REGS(target->thread.regs);
+ }
+
+ BUG_ON(!regs);
pos /= sizeof(reg);
count /= sizeof(reg);
@@ -982,6 +1562,40 @@ static int gpr32_set(struct task_struct *target,
(PT_TRAP + 1) * sizeof(reg), -1);
}
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+static int tm_cgpr32_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return common_gpr32_get(target, regset, pos, count, kbuf, ubuf, 1);
+}
+
+static int tm_cgpr32_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 1);
+}
+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+
+static int gpr32_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return common_gpr32_get(target, regset, pos, count, kbuf, ubuf, 0);
+}
+
+static int gpr32_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 0);
+}
+
/*
* These are the regset flavors matching the CONFIG_PPC32 native set.
*/
@@ -1010,6 +1624,29 @@ static const struct user_regset compat_regsets[] = {
.active = evr_active, .get = evr_get, .set = evr_set
},
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ [REGSET_TM_SPR] = {
+ .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
+ },
+ [REGSET_TM_CGPR] = {
+ .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG,
+ .size = sizeof(long), .align = sizeof(long),
+ .active = tm_cgpr_active,
+ .get = tm_cgpr32_get, .set = tm_cgpr32_set
+ },
+ [REGSET_TM_CFPR] = {
+ .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
+ .size = sizeof(double), .align = sizeof(double),
+ .active = tm_cfpr_active, .get = tm_cfpr_get, .set = tm_cfpr_set
+ },
+ [REGSET_TM_CVMX] = {
+ .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX,
+ .size = sizeof(vector128), .align = sizeof(vector128),
+ .active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
+ },
+#endif
};
static const struct user_regset_view user_ppc_compat_view = {
--
1.9.3
^ permalink raw reply related
* [V5 6/7] powerpc, ptrace: Enable support for miscellaneous debug registers
From: Anshuman Khandual @ 2014-11-25 8:05 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev
Cc: shuahkh, mikey, james.hogan, avagin, Paul.Clothier, peterz,
palves, oleg, davem, dhowells, kirjanov, davej, akpm, sukadev,
tglx, sam.bobroff
In-Reply-To: <1416902731-22446-1-git-send-email-khandual@linux.vnet.ibm.com>
This patch enables get and set of miscellaneous debug registers through
ptrace PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing new
powerpc specific register set REGSET_MISC support corresponding to the
new ELF core note NT_PPC_MISC added previously in this regard.
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
arch/powerpc/include/uapi/asm/elf.h | 1 +
arch/powerpc/kernel/ptrace.c | 123 ++++++++++++++++++++++++++++++++++++
2 files changed, 124 insertions(+)
diff --git a/arch/powerpc/include/uapi/asm/elf.h b/arch/powerpc/include/uapi/asm/elf.h
index fdc8e2f..a41bd98 100644
--- a/arch/powerpc/include/uapi/asm/elf.h
+++ b/arch/powerpc/include/uapi/asm/elf.h
@@ -93,6 +93,7 @@
#define ELF_NFPREG 33 /* includes fpscr */
#define ELF_NVMX 34 /* includes all vector registers */
#define ELF_NTMSPRREG 7 /* includes TM sprs, org_msr, dscr, tar, ppr */
+#define ELF_NMISCREG 3 /* includes dscr, tar, ppr */
typedef unsigned long elf_greg_t64;
typedef elf_greg_t64 elf_gregset_t64[ELF_NGREG];
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index faa9058..61cc7ae 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -1324,6 +1324,114 @@ static int tm_cvmx_set(struct task_struct *target,
}
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+#ifdef CONFIG_PPC64
+/*
+ * get_misc_dbg
+ *
+ * This function gets miscellaneous debug registers
+ * which includes DSCR, PPR and TAR.
+ *
+ * Userspace intarface buffer layout:
+ *
+ * struct {
+ * unsigned long dscr;
+ * unsigned long ppr;
+ * unsigned long tar;
+ * };
+ *
+ * The data element 'tar' will be valid only if the
+ * kernel has CONFIG_PPC_BOOK3S_64 config option enabled.
+ */
+static int get_misc_dbg(struct task_struct *target,
+ const struct user_regset *regset, unsigned int pos,
+ unsigned int count, void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ /* Build test */
+ BUILD_BUG_ON(TSO(dscr) + 2 * sizeof(unsigned long) != TSO(ppr));
+
+#ifdef CONFIG_PPC_BOOK3S_64
+ BUILD_BUG_ON(TSO(ppr) + sizeof(unsigned long) != TSO(tar));
+#endif
+
+ /* DSCR register */
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dscr, 0,
+ sizeof(unsigned long));
+
+ /* PPR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ppr,
+ sizeof(unsigned long),
+ 2 * sizeof(unsigned long));
+
+#ifdef CONFIG_PPC_BOOK3S_64
+ /* TAR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tar,
+ 2 * sizeof(unsigned long),
+ 3 * sizeof(unsigned long));
+#endif
+ return ret;
+}
+
+/*
+ * set_misc_dbg
+ *
+ * This function sets miscellaneous debug registers
+ * which includes DSCR, PPR and TAR.
+ *
+ * Userspace intarface buffer layout:
+ *
+ * struct {
+ * unsigned long dscr;
+ * unsigned long ppr;
+ * unsigned long tar;
+ * };
+ *
+ * The data element 'tar' will be valid only if the
+ * kernel has CONFIG_PPC_BOOK3S_64 config option enabled.
+ */
+static int set_misc_dbg(struct task_struct *target,
+ const struct user_regset *regset, unsigned int pos,
+ unsigned int count, const void *kbuf,
+ const void __user *ubuf)
+{
+ int ret;
+
+ /* Build test */
+ BUILD_BUG_ON(TSO(dscr) + 2 * sizeof(unsigned long) != TSO(ppr));
+
+#ifdef CONFIG_PPC_BOOK3S_64
+ BUILD_BUG_ON(TSO(ppr) + sizeof(unsigned long) != TSO(tar));
+#endif
+
+ /* DSCR register */
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dscr, 0,
+ sizeof(unsigned long));
+
+ /* PPR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ppr,
+ sizeof(unsigned long),
+ 2 * sizeof(unsigned long));
+#ifdef CONFIG_PPC_BOOK3S_64
+ /* TAR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tar,
+ 2 * sizeof(unsigned long),
+ 3 * sizeof(unsigned long));
+#endif
+ return ret;
+}
+#endif /* CONFIG_PPC64 */
+
/*
* These are our native regset flavors.
*/
@@ -1345,6 +1453,9 @@ enum powerpc_regset {
REGSET_TM_CFPR, /* TM checkpointed FPR registers */
REGSET_TM_CVMX, /* TM checkpointed VMX registers */
#endif
+#ifdef CONFIG_PPC64
+ REGSET_MISC /* Miscellaneous debug registers */
+#endif
};
static const struct user_regset native_regsets[] = {
@@ -1401,6 +1512,13 @@ static const struct user_regset native_regsets[] = {
.active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
},
#endif
+#ifdef CONFIG_PPC64
+ [REGSET_MISC] = {
+ .core_note_type = NT_PPC_MISC, .n = ELF_NMISCREG,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .get = get_misc_dbg, .set = set_misc_dbg
+ },
+#endif
};
static const struct user_regset_view user_ppc_native_view = {
@@ -1647,6 +1765,11 @@ static const struct user_regset compat_regsets[] = {
.active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
},
#endif
+ [REGSET_MISC] = {
+ .core_note_type = NT_PPC_MISC, .n = ELF_NMISCREG,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .get = get_misc_dbg, .set = set_misc_dbg
+ },
};
static const struct user_regset_view user_ppc_compat_view = {
--
1.9.3
^ permalink raw reply related
* [V5 7/7] selftests, powerpc: Add test case for TM related ptrace interface
From: Anshuman Khandual @ 2014-11-25 8:05 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev
Cc: shuahkh, mikey, james.hogan, avagin, Paul.Clothier, peterz,
palves, oleg, davem, dhowells, kirjanov, davej, akpm, sukadev,
tglx, sam.bobroff
In-Reply-To: <1416902731-22446-1-git-send-email-khandual@linux.vnet.ibm.com>
This patch adds one more test case called 'tm-ptrace' targeting TM
related ptrace interface. This test creates one child process to
run some basic TM transactions and the parent process attaches the
child to do some ptrace probing using the recently added regset
interfaces. The parent process then compares the received values
against the expected values to verify whether it has passed the
given test or not.
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
tools/testing/selftests/powerpc/tm/Makefile | 2 +-
tools/testing/selftests/powerpc/tm/tm-ptrace.c | 542 +++++++++++++++++++++++++
2 files changed, 543 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/powerpc/tm/tm-ptrace.c
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile
index 2cede23..71d400a 100644
--- a/tools/testing/selftests/powerpc/tm/Makefile
+++ b/tools/testing/selftests/powerpc/tm/Makefile
@@ -1,4 +1,4 @@
-PROGS := tm-resched-dscr
+PROGS := tm-resched-dscr tm-ptrace
all: $(PROGS)
diff --git a/tools/testing/selftests/powerpc/tm/tm-ptrace.c b/tools/testing/selftests/powerpc/tm/tm-ptrace.c
new file mode 100644
index 0000000..7a6c7d3
--- /dev/null
+++ b/tools/testing/selftests/powerpc/tm/tm-ptrace.c
@@ -0,0 +1,542 @@
+/*
+ * Test program for TM ptrace interface
+ *
+ * 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.
+ *
+ * Copyright 2014 IBM Corporation
+ *
+ * Author: Anshuman Khandual <khandual@linux.vnet.ibm.com>
+ */
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <linux/elf.h>
+#include <linux/types.h>
+
+#include "utils.h"
+
+#define TEST_PASS 0
+#define TEST_FAIL 1
+
+#define MAX_STR_LENGTH 100
+
+/* ELF core notes */
+#define NT_PPC_TM_SPR 0x103 /* PowerPC TM special registers */
+#define NT_PPC_TM_CGPR 0x104 /* PowerpC TM checkpointed GPR */
+#define NT_PPC_TM_CFPR 0x105 /* PowerPC TM checkpointed FPR */
+#define NT_PPC_TM_CVMX 0x106 /* PowerPC TM checkpointed VMX */
+#define NT_PPC_MISC 0x107 /* PowerPC miscellaneous registers */
+
+/* TM instructions */
+#define TBEGIN ".long 0x7C00051D ;"
+#define TEND ".long 0x7C00055D ;"
+
+/* SPR number */
+#define SPRN_DSCR 3
+#define SPRN_TAR 815
+#define SPRN_PPR 896
+
+#define C_DSCR 10 /* TM checkpointed DSCR */
+#define C_TAR 20 /* TM checkpointed TAR */
+#define C_PPR 0x8000000000000 /* TM checkpointed PPR */
+
+#define DSCR 50 /* TM running DSCR */
+#define TAR 60 /* TM running TAR */
+#define PPR 0x4000000000000 /* TM running PPR */
+
+/* Values for GPR-FPR[0..31] */
+#define VAL0 0
+#define VAL1 1
+#define VAL2 2
+#define VAL3 3
+#define VAL4 4
+#define VAL5 5
+#define VAL6 6
+#define VAL7 7
+#define VAL8 8
+#define VAL9 9
+#define VAL10 10
+#define VAL11 11
+#define VAL12 12
+#define VAL13 13
+#define VAL14 14
+#define VAL15 15
+#define VAL_MAX 16
+
+/* Standard data types */
+typedef unsigned int u32;
+typedef __vector128 vector128;
+
+/* NT_PPC_TM_SPR buffer layout */
+struct tm_spr_regs {
+ u64 tm_tfhar;
+ u64 tm_texasr;
+ u64 tm_tfiar;
+ u64 tm_orig_msr;
+ u64 tm_tar;
+ u64 tm_ppr;
+ u64 tm_dscr;
+};
+
+/*
+ * NT_PPC_TM_CGPR buffer layout
+ *
+ * Same as that of struct pt_regs
+ */
+
+/* NT_PPC_TM_CFPR buffer layout */
+struct tm_cfpr {
+ u64 fpr[32];
+ u64 fpscr;
+};
+
+/* NT_PPC_TM_CVMX buffer layout */
+struct tm_cvmx {
+ vector128 vr[32] __attribute__((aligned(16)));
+ vector128 vscr __attribute__((aligned(16)));
+ u32 vrsave;
+};
+
+/* NT_PPC_MISC buffer layout */
+struct misc_regs {
+ u64 dscr;
+ u64 ppr;
+ u64 tar;
+};
+
+/*
+ * do_tm_transaction
+ *
+ * This functions sets the values for TAR, DSCR, PPR, GPR[0..31],
+ * FPR[0..31] registers before starting the trasanction which will
+ * enable the kernel to save them as checkpointed values. Then it
+ * starts the transaction where it loads a different set of values
+ * into the same registers again thus enabling the kernel to save
+ * them off as running values for this transaction. Then the function
+ * gets stuck forcing the process to loop at one single instruction.
+ * The transaction never finishes, thus giving the parent process
+ * the opportunity to trace the running and checkpointed values of
+ * various registers.
+ */
+void do_tm_transaction(void)
+{
+ asm __volatile__(
+ /* TM checkpointed values */
+
+ /* SPR */
+ "li 0, %[c_tar];" /* TAR */
+ "mtspr %[sprn_tar], 0;"
+ "li 0, %[c_dscr];" /* DSCR */
+ "mtspr %[sprn_dscr], 0;"
+ "or 1,1,1;" /* PPR (0x8000000000000) */
+
+ /* GPR[0..31] */
+ "li 0, %[val0];" /* GPR[0] */
+ "li 1, %[val1];" /* GPR[1] */
+ "li 2, %[val2];" /* GPR[2] */
+ "li 3, %[val3];" /* GPR[3] */
+ "li 4, %[val4];" /* GPR[4] */
+ "li 5, %[val5];" /* GPR[5] */
+ "li 6, %[val6];" /* GPR[6] */
+ "li 7, %[val7];" /* GPR[7] */
+ "li 8, %[val8];" /* GPR[8] */
+ "li 9, %[val9];" /* GPR[9] */
+ "li 10, %[val10];" /* GPR[10] */
+ "li 11, %[val11];" /* GPR[11] */
+ "li 12, %[val12];" /* GPR[12] */
+ "li 13, %[val13];" /* GPR[13] */
+ "li 14, %[val14];" /* GPR[14] */
+ "li 15, %[val15];" /* GPR[15] */
+ "li 16, %[val0];" /* GPR[16] */
+ "li 17, %[val1];" /* GPR[17] */
+ "li 18, %[val2];" /* GPR[18] */
+ "li 19, %[val3];" /* GPR[19] */
+ "li 20, %[val4];" /* GPR[20] */
+ "li 21, %[val5];" /* GPR[21] */
+ "li 22, %[val6];" /* GPR[22] */
+ "li 23, %[val7];" /* GPR[23] */
+ "li 24, %[val8];" /* GPR[24] */
+ "li 25, %[val9];" /* GPR[25] */
+ "li 26, %[val10];" /* GPR[26] */
+ "li 27, %[val11];" /* GPR[27] */
+ "li 28, %[val12];" /* GPR[28] */
+ "li 29, %[val13];" /* GPR[29] */
+ "li 30, %[val14];" /* GPR[30] */
+ "li 31, %[val15];" /* GPR[31] */
+
+ /* FPR[0..31] */
+ ".long 0x7C000166;" /* GPR[0] --> FPR[0] */
+ ".long 0x7C210166;" /* GPR[1] --> FPR[1] */
+ ".long 0x7C420166;" /* GPR[0] --> FPR[2] */
+ ".long 0x7C630166;" /* GPR[3] --> FPR[3] */
+ ".long 0x7C840166;" /* GPR[4] --> FPR[4] */
+ ".long 0x7CA50166;" /* GPR[5] --> FPR[5] */
+ ".long 0x7CC60166;" /* GPR[6] --> FPR[6] */
+ ".long 0x7CE70166;" /* GPR[7] --> FPR[7] */
+ ".long 0x7D080166;" /* GPR[8] --> FPR[8] */
+ ".long 0x7D290166;" /* GPR[9] --> FPR[9] */
+ ".long 0x7d4a0166;" /* GPR[10] --> FPR[10] */
+ ".long 0x7d6b0166;" /* GPR[11] --> FPR[11] */
+ ".long 0x7d8c0166;" /* GPR[12] --> FPR[12] */
+ ".long 0x7dad0166;" /* GPR[13] --> FPR[13] */
+ ".long 0x7dce0166;" /* GPR[14] --> FPR[14] */
+ ".long 0x7def0166;" /* GPR[15] --> FPR[15] */
+ ".long 0x7e100166;" /* GPR[16] --> FPR[16] */
+ ".long 0x7e310166;" /* GPR[17] --> FPR[17] */
+ ".long 0x7e520166;" /* GPR[18] --> FPR[18] */
+ ".long 0x7e730166;" /* GPR[19] --> FPR[19] */
+ ".long 0x7e940166;" /* GPR[20] --> FPR[20] */
+ ".long 0x7eb50166;" /* GPR[21] --> FPR[21] */
+ ".long 0x7ed60166;" /* GPR[22] --> FPR[22] */
+ ".long 0x7ef70166;" /* GPR[23] --> FPR[23] */
+ ".long 0x7f180166;" /* GPR[24] --> FPR[24] */
+ ".long 0x7f390166;" /* GPR[25] --> FPR[25] */
+ ".long 0x7f5a0166;" /* GPR[26] --> FPR[26] */
+ ".long 0x7f7b0166;" /* GPR[27] --> FPR[27] */
+ ".long 0x7f9c0166;" /* GPR[28] --> FPR[28] */
+ ".long 0x7fbd0166;" /* GPR[29] --> FPR[29] */
+ ".long 0x7fde0166;" /* GPR[30] --> FPR[30] */
+ ".long 0x7fff0166;" /* GPR[31] --> FPR[31] */
+
+ /* TM running values */
+
+ "1: ;"
+ TBEGIN
+ "beq 2f;"
+
+ /* SPR */
+ "li 0, %[tar];" /* TAR */
+ "mtspr %[sprn_tar], 0;"
+ "li 0, %[dscr];" /* DSCR */
+ "mtspr %[sprn_dscr], 0;"
+ "or 31,31,31;" /* PPR (0x4000000000000) */
+
+ /* GPR[0..31] */
+ "li 0, %[val15];" /* GPR[0] */
+ "li 1, %[val14];" /* GPR[1] */
+ "li 2, %[val13];" /* GPR[2] */
+ "li 3, %[val12];" /* GPR[3] */
+ "li 4, %[val11];" /* GPR[4] */
+ "li 5, %[val10];" /* GPR[5] */
+ "li 6, %[val9];" /* GPR[6] */
+ "li 7, %[val8];" /* GPR[7] */
+ "li 8, %[val7];" /* GPR[8] */
+ "li 9, %[val6];" /* GPR[9] */
+ "li 10, %[val5];" /* GPR[10] */
+ "li 11, %[val4];" /* GPR[11] */
+ "li 12, %[val3];" /* GPR[12] */
+ "li 13, %[val2];" /* GPR[13] */
+ "li 14, %[val1];" /* GPR[14] */
+ "li 15, %[val0];" /* GPR[15] */
+ "li 16, %[val15];" /* GPR[16] */
+ "li 17, %[val14];" /* GPR[17] */
+ "li 18, %[val13];" /* GPR[18] */
+ "li 19, %[val12];" /* GPR[19] */
+ "li 20, %[val11];" /* GPR[20] */
+ "li 21, %[val10];" /* GPR[21] */
+ "li 22, %[val9];" /* GPR[22] */
+ "li 23, %[val8];" /* GPR[23] */
+ "li 24, %[val7];" /* GPR[24] */
+ "li 25, %[val6];" /* GPR[25] */
+ "li 26, %[val5];" /* GPR[26] */
+ "li 27, %[val4];" /* GPR[27] */
+ "li 28, %[val3];" /* GPR[28] */
+ "li 29, %[val2];" /* GPR[29] */
+ "li 30, %[val1];" /* GPR[30] */
+ "li 31, %[val0];" /* GPR[31] */
+
+ /* FPR[0..31] */
+ ".long 0x7C000166;" /* GPR[0] --> FPR[0] */
+ ".long 0x7C210166;" /* GPR[1] --> FPR[1] */
+ ".long 0x7C420166;" /* GPR[2] --> FPR[2] */
+ ".long 0x7C630166;" /* GPR[3] --> FPR[3] */
+ ".long 0x7C840166;" /* GPR[4] --> FPR[4] */
+ ".long 0x7CA50166;" /* GPR[5] --> FPR[5] */
+ ".long 0x7CC60166;" /* GPR[6] --> FPR[6] */
+ ".long 0x7CE70166;" /* GPR[7] --> FPR[7] */
+ ".long 0x7D080166;" /* GPR[8] --> FPR[8] */
+ ".long 0x7D290166;" /* GPR[9] --> FPR[9] */
+ ".long 0x7d4a0166;" /* GPR[10] --> FPR[10] */
+ ".long 0x7d6b0166;" /* GPR[11] --> FPR[11] */
+ ".long 0x7d8c0166;" /* GPR[12] --> FPR[12] */
+ ".long 0x7dad0166;" /* GPR[13] --> FPR[13] */
+ ".long 0x7dce0166;" /* GPR[14] --> FPR[14] */
+ ".long 0x7def0166;" /* GPR[15] --> FPR[15] */
+ ".long 0x7e100166;" /* GPR[16] --> FPR[16] */
+ ".long 0x7e310166;" /* GPR[17] --> FPR[17] */
+ ".long 0x7e520166;" /* GPR[18] --> FPR[18] */
+ ".long 0x7e730166;" /* GPR[19] --> FPR[19] */
+ ".long 0x7e940166;" /* GPR[20] --> FPR[20] */
+ ".long 0x7eb50166;" /* GPR[21] --> FPR[21] */
+ ".long 0x7ed60166;" /* GPR[22] --> FPR[22] */
+ ".long 0x7ef70166;" /* GPR[23] --> FPR[23] */
+ ".long 0x7f180166;" /* GPR[24] --> FPR[24] */
+ ".long 0x7f390166;" /* GPR[25] --> FPR[25] */
+ ".long 0x7f5a0166;" /* GPR[26] --> FPR[26] */
+ ".long 0x7f7b0166;" /* GPR[27] --> FPR[27] */
+ ".long 0x7f9c0166;" /* GPR[28] --> FPR[28] */
+ ".long 0x7fbd0166;" /* GPR[29] --> FPR[29] */
+ ".long 0x7fde0166;" /* GPR[30] --> FPR[30] */
+ ".long 0x7fff0166;" /* GPR[31] --> FPR[31] */
+
+ "b .;" /* Get stuck here */
+ TEND
+
+ /* Transaction abort handler */
+ "2: ;"
+ "b 1b;" /* Start from TBEGIN */
+
+ :: [sprn_dscr]"i"(SPRN_DSCR), [sprn_tar]"i"(SPRN_TAR),
+ [sprn_ppr]"i"(SPRN_PPR), [val0]"i"(VAL0),
+ [val1]"i"(VAL1), [val2]"i"(VAL2), [val3]"i"(VAL3),
+ [val4]"i"(VAL4), [val5]"i"(VAL5), [val6]"i"(VAL6),
+ [val7]"i"(VAL7), [val8]"i"(VAL8), [val9]"i"(VAL9),
+ [val10]"i"(VAL10), [val11]"i"(VAL11), [val12]"i"(VAL12),
+ [val13]"i"(VAL13), [val14]"i"(VAL14), [val15]"i"(VAL15),
+ [c_tar]"i"(C_TAR), [c_dscr]"i"(C_DSCR), [tar]"i"(TAR),
+ [dscr]"i"(DSCR), [ppr]"i"(PPR), [c_ppr]"i"(C_PPR)
+ : "memory", "r7");
+}
+
+void test_result(const u64 variable, const u64 value, const char *str)
+{
+ if (variable == value)
+ printf("%s: %llx (PASSED)\n", str, variable);
+ else
+ printf("%s: %llx (FAILED)\n", str, variable);
+}
+
+int trace_tm_transaction(pid_t child)
+{
+ struct tm_spr_regs *tmspr;
+ struct pt_regs *cregs, *regs;
+ struct tm_cfpr *cfpr, *fpr;
+ struct misc_regs *mregs;
+ struct iovec iov;
+ char str[MAX_STR_LENGTH];
+ int ret, i, j;
+
+ regs = (struct pt_regs *) malloc(sizeof(struct pt_regs));
+ fpr = (struct tm_cfpr *) malloc(sizeof(struct tm_cfpr));
+
+ /* Wait till the tracee hits "b ." instruction */
+ sleep(3);
+
+ ret = ptrace(PTRACE_ATTACH, child, NULL, NULL);
+ if (ret) {
+ printf("ptrace(PTRACE_ATTACH) Failed: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+
+ ret = waitpid(child, NULL, 0);
+ if (ret != child) {
+ printf("PID mismatch: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+
+ /* TM specific SPR */
+ printf("Testing TM specific SPR:\n");
+ iov.iov_base = (struct tm_spr_regs *)
+ malloc(sizeof(struct tm_spr_regs));
+ iov.iov_len = sizeof(struct tm_spr_regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_SPR, &iov);
+ if (ret) {
+ printf("ptrace(NT_PPC_TM_SPR) Failed: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+
+ if (iov.iov_len != sizeof(struct tm_spr_regs)) {
+ printf("ptrace(NT_PPC_TM_SPR): Returned wrong length\n");
+ return TEST_FAIL;
+ }
+
+ tmspr = iov.iov_base;
+
+ printf("TFHAR: %llx\n", tmspr->tm_tfhar);
+ printf("TEXASR: %llx\n", tmspr->tm_texasr);
+ printf("TFIAR: %llx\n", tmspr->tm_tfiar);
+ printf("TM ORIG_MSR: %llx\n", tmspr->tm_orig_msr);
+
+ test_result(tmspr->tm_dscr, C_DSCR, "TM CH DSCR");
+ test_result(tmspr->tm_tar, C_TAR, "TM CH TAR");
+ test_result(tmspr->tm_ppr, C_PPR, "TM CH PPR");
+
+ /* TM checkpointed GPR */
+ printf("Testing TM checkpointed GPR:\n");
+ iov.iov_base = (struct pt_regs *) malloc(sizeof(struct pt_regs));
+ iov.iov_len = sizeof(struct pt_regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov);
+ if (ret) {
+ printf("ptrace(NT_PPC_TM_CGPR) Failed: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+
+ if (iov.iov_len != sizeof(struct pt_regs)) {
+ printf("ptrace(NT_PPC_TM_CGPR): Returned wrong length\n");
+ return TEST_FAIL;
+ }
+
+ cregs = iov.iov_base;
+
+ printf("TM CH NIP: %lx\n", cregs->nip);
+ printf("TM CH LINK: %lx\n", cregs->link);
+ printf("TM CH CCR: %lx\n", cregs->ccr);
+
+ for (i = 0; i < VAL_MAX; i++) {
+ sprintf(str, "TM CH GPR[%d]", i);
+ test_result(cregs->gpr[i], i, str);
+ }
+
+ for (j = 0; i < VAL_MAX * 2; j++, i++) {
+ sprintf(str, "TM CH GPR[%d]", i);
+ test_result(cregs->gpr[i], j, str);
+ }
+
+ /* TM checkpointed FPR */
+ printf("Testing TM checkpointed FPR:\n");
+ iov.iov_base = (struct tm_cfpr *) malloc(sizeof(struct tm_cfpr));
+ iov.iov_len = sizeof(struct tm_cfpr);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov);
+ if (ret) {
+ printf("ptrace(NT_PPC_TM_CFPR) Failed: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+
+ if (iov.iov_len != sizeof(struct tm_cfpr)) {
+ printf("ptrace(NT_PPC_TM_CFPR): Returned wrong length\n");
+ return TEST_FAIL;
+ }
+
+ cfpr = iov.iov_base;
+ printf("TM CH FPSCR: %llx\n", cfpr->fpscr);
+
+ for (i = 0; i < VAL_MAX; i++) {
+ sprintf(str, "TM CH FPR[%d]", i);
+ test_result(cfpr->fpr[i], i, str);
+ }
+
+ for (j = 0; i < VAL_MAX * 2; j++, i++) {
+ sprintf(str, "TM CH FPR[%d]", i);
+ test_result(cfpr->fpr[i], j, str);
+ }
+
+ /* TM running GPR */
+ printf("Testing TM running GPR:\n");
+ ret = ptrace(PTRACE_GETREGS, child, NULL, regs);
+ if (ret) {
+ printf("ptrace(PTRACE_GETREGS) Failed: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+
+ printf("TM RN NIP: %lx\n", regs->nip);
+ printf("TM RN LINK: %lx\n", regs->link);
+ printf("TM RN CCR: %lx\n", regs->ccr);
+
+ for (i = 0, j = VAL_MAX - 1; i < VAL_MAX; i++, j--) {
+ sprintf(str, "TM RN GPR[%d]", i);
+ test_result(regs->gpr[i], j, str);
+ }
+
+ for (j = VAL_MAX - 1 ; i < VAL_MAX * 2; i++, j--) {
+ sprintf(str, "TM RN GPR[%d]", i);
+ test_result(regs->gpr[i], j, str);
+ }
+
+ /* TM running FPR */
+ printf("Testing TM running FPR:\n");
+ ret = ptrace(PTRACE_GETFPREGS, child, NULL, fpr);
+ if (ret) {
+ printf("ptrace(PTRACE_GETFPREGS) Failed: %s\n",
+ strerror(errno));
+ return TEST_FAIL;
+ }
+
+ printf("TM RN FPSCR: %llx\n", fpr->fpscr);
+
+ for (i = 0, j = VAL_MAX - 1; i < VAL_MAX; i++, j--) {
+ sprintf(str, "TM RN FPR[%d]", i);
+ test_result(fpr->fpr[i], j, str);
+ }
+
+ for (j = VAL_MAX - 1; i < VAL_MAX * 2; i++, j--) {
+ sprintf(str, "TM RN FPR[%d]", i);
+ test_result(fpr->fpr[i], j, str);
+ }
+
+ /* TM running MISC debug registers */
+ printf("Testing TM running MISC debug registers:\n");
+ iov.iov_base = (struct misc_regs *) malloc(sizeof(struct misc_regs));
+ iov.iov_len = sizeof(struct misc_regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_MISC, &iov);
+ if (ret) {
+ printf("ptrace(NT_PPC_MISC): Failed: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+
+ if (iov.iov_len != sizeof(struct misc_regs)) {
+ printf("ptrace(NT_PPC_TM_MISC): Returned wrong length\n");
+ return TEST_FAIL;
+ }
+
+ mregs = iov.iov_base;
+ test_result(mregs->dscr, DSCR, "TM RN DSCR");
+ test_result(mregs->tar, TAR, "TM RN TAR");
+ test_result(mregs->ppr, PPR, "TM RN PPR");
+
+ ret = ptrace(PTRACE_DETACH, child, NULL, NULL);
+ if (ret) {
+ printf("ptrace(PTRACE_DETACH) Failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ if (kill(child, SIGTERM)) {
+ printf("kill() Failed\n");
+ return TEST_FAIL;
+ }
+
+ ret = waitpid(child, NULL, 0);
+ if (ret != child) {
+ printf("PID mismatch: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int tm_ptrace_test(void)
+{
+ pid_t child;
+
+ printf("===Testing TM based PTRACE Interface===\n");
+ fflush(stdout);
+ child = fork();
+ if (child < 0) {
+ printf("fork() Failed: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+
+ /* Child to run the transaction */
+ if (child == 0)
+ do_tm_transaction();
+
+ /* Parent to trace the child */
+ if (child)
+ trace_tm_transaction(child);
+ return TEST_PASS;
+}
+
+int main(void)
+{
+ return test_harness(tm_ptrace_test, "tm_ptrace");
+}
--
1.9.3
^ permalink raw reply related
* [V5 3/7] powerpc, ptrace: Enable fpr_(get/set) for transactional memory
From: Anshuman Khandual @ 2014-11-25 8:05 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev
Cc: shuahkh, mikey, james.hogan, avagin, Paul.Clothier, peterz,
palves, oleg, davem, dhowells, kirjanov, davej, akpm, sukadev,
tglx, sam.bobroff
In-Reply-To: <1416902731-22446-1-git-send-email-khandual@linux.vnet.ibm.com>
This patch enables the fpr_get which gets the running value of all
the FPR registers and the fpr_set which sets the running value of
of all the FPR registers to accommodate in transaction ptrace
interface based requests.
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
arch/powerpc/kernel/ptrace.c | 103 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 97 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index f21897b..fd36b32 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -357,6 +357,33 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset,
return ret;
}
+
+/*
+ * fpr_get
+ *
+ * When the transaction is active, 'transact_fp' holds the current running
+ * value of all FPR registers and 'fp_state' holds the last checkpointed
+ * value of all FPR registers for the current transaction. When transaction
+ * is not active 'fp_state' holds the current running state of all the FPR
+ * registers. So this function which returns the current running values of
+ * all the FPR registers, needs to know whether any transaction is active
+ * or not.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ * };
+ *
+ * There are two config options CONFIG_VSX and CONFIG_PPC_TRANSACTIONAL_MEM
+ * which determines the final code in this function. All the combinations of
+ * these two config options are possible except the one below as transactional
+ * memory config pulls in CONFIG_VSX automatically.
+ *
+ * !defined(CONFIG_VSX) && defined(CONFIG_PPC_TRANSACTIONAL_MEM)
+ *
+ */
static int fpr_get(struct task_struct *target, const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
@@ -367,22 +394,64 @@ static int fpr_get(struct task_struct *target, const struct user_regset *regset,
#endif
flush_fp_to_thread(target);
-#ifdef CONFIG_VSX
+#if defined(CONFIG_VSX) && defined(CONFIG_PPC_TRANSACTIONAL_MEM)
+ /* copy to local buffer then write that out */
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+ for (i = 0; i < 32 ; i++)
+ buf[i] = target->thread.TS_TRANS_FPR(i);
+ buf[32] = target->thread.transact_fp.fpscr;
+ } else {
+ for (i = 0; i < 32 ; i++)
+ buf[i] = target->thread.TS_FPR(i);
+ buf[32] = target->thread.fp_state.fpscr;
+ }
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+#endif
+
+#if defined(CONFIG_VSX) && !defined(CONFIG_PPC_TRANSACTIONAL_MEM)
/* copy to local buffer then write that out */
for (i = 0; i < 32 ; i++)
buf[i] = target->thread.TS_FPR(i);
buf[32] = target->thread.fp_state.fpscr;
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+#endif
-#else
+
+#if !defined(CONFIG_VSX) && !defined(CONFIG_PPC_TRANSACTIONAL_MEM)
BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
offsetof(struct thread_fp_state, fpr[32][0]));
-
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_state, 0, -1);
#endif
}
+/*
+ * fpr_set
+ *
+ * When the transaction is active, 'transact_fp' holds the current running
+ * value of all FPR registers and 'fp_state' holds the last checkpointed
+ * value of all FPR registers for the current transaction. When transaction
+ * is not active 'fp_state' holds the current running state of all the FPR
+ * registers. So this function which setss the current running values of
+ * all the FPR registers, needs to know whether any transaction is active
+ * or not.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ * };
+ *
+ * There are two config options CONFIG_VSX and CONFIG_PPC_TRANSACTIONAL_MEM
+ * which determines the final code in this function. All the combinations of
+ * these two config options are possible except the one below as transactional
+ * memory config pulls in CONFIG_VSX automatically.
+ *
+ * !defined(CONFIG_VSX) && defined(CONFIG_PPC_TRANSACTIONAL_MEM)
+ */
static int fpr_set(struct task_struct *target, const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
@@ -393,7 +462,27 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
#endif
flush_fp_to_thread(target);
-#ifdef CONFIG_VSX
+#if defined(CONFIG_VSX) && defined(CONFIG_PPC_TRANSACTIONAL_MEM)
+ /* copy to local buffer then write that out */
+ i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+ if (i)
+ return i;
+
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+ for (i = 0; i < 32 ; i++)
+ target->thread.TS_TRANS_FPR(i) = buf[i];
+ target->thread.transact_fp.fpscr = buf[32];
+ } else {
+ for (i = 0; i < 32 ; i++)
+ target->thread.TS_FPR(i) = buf[i];
+ target->thread.fp_state.fpscr = buf[32];
+ }
+ return 0;
+#endif
+
+#if defined(CONFIG_VSX) && !defined(CONFIG_PPC_TRANSACTIONAL_MEM)
/* copy to local buffer then write that out */
i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
if (i)
@@ -402,12 +491,14 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
target->thread.TS_FPR(i) = buf[i];
target->thread.fp_state.fpscr = buf[32];
return 0;
-#else
+#endif
+
+#if !defined(CONFIG_VSX) && !defined(CONFIG_PPC_TRANSACTIONAL_MEM)
BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
offsetof(struct thread_fp_state, fpr[32][0]));
-
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_state, 0, -1);
+
#endif
}
--
1.9.3
^ permalink raw reply related
* Re: [PATCH V9 08/18] powrepc/pci: Refactor pci_dn
From: Wei Yang @ 2014-11-25 9:28 UTC (permalink / raw)
To: Gavin Shan; +Cc: Bjorn Helgaas, linux-pci, Wei Yang, benh, linuxppc-dev
In-Reply-To: <20141121000411.GA19858@shangw>
On Fri, Nov 21, 2014 at 11:04:11AM +1100, Gavin Shan wrote:
>>> 2.4 The potential problem for [Patch 08/18]
>>> ===============================================================================
>>> According to the SPEC, the offset/stride will change after each
>>> sriov_enable(). This means the bus/devfn will change after each
>>> sriov_enable().
>>>
>>> My current thought is to fix it up in virtfn_add(). If the total VF number
>>> will not change, we could create those pci_dn at the beginning and fix the
>>> bus/devfn at each time the VF is truely created.
>>
>>By "fix it up," I assume you mean call an arch function that does the
>>pci_pdn setup you need.
>>
>>It sounds reasonable to do this either in virtfn_add()/virtfn_remove() or
>>at the points where we write PCI_SRIOV_CTRL_VFE, i.e., in sriov_init(),
>>sriov_enable(), sriov_disable(), and sriov_restore_state(). From a
>>hardware point of view, the VFs exist whenever PCI_SRIOV_CTRL_VFE is set,
>>so it might be nice to have this setup connected to that.
>>
>
>Yes, Both ways can fix the issue. For couple reasons, I want add weak
>pcibios_virtfn_add(), which is called in virtfn_add() if you agree.
>
>- PCI_SRIOV_CTRL_VFE might be set somewhere except the functions you pointed.
> Set/clear PCI_SRIOV_CTRL_VFE will invoke background work to check pci_dn
> and add/remove accordingly. It would be overhead which we can avoid.
>- We plan to support EEH for VFs in future. virtfn_add() way matches with
> current EEH implementation better. EEH device and PE are created based
> on (struct pci_dev), and EEH devices and PE can be destroied in time in
> pcibios_release_device(), which is invoked by virtfn_remove(). So we only
> need one weak function. In contrast, we have to create EEH device and PE
> for VFs a bit early before any VFs are instantiated, and destroy them a
> bit late after all VFs are offline.
>
Bjorn,
Since my mail box got some problem in the last few days, I am not sure you
agree with Gavin's proposal or not?
And if you agree to enumerate the maximum VF bus range, I will add this logic
in the next versin.
>Thanks,
>Gavin
>
>>Bjorn
>>
>
>--
>To unsubscribe from this list: send the line "unsubscribe linux-pci" in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Richard Yang
Help you, Help me
^ permalink raw reply
* [RESEND PATCH V3] powerpc, xmon: Enable HW instruction breakpoint on POWER8
From: Anshuman Khandual @ 2014-11-25 10:08 UTC (permalink / raw)
To: linuxppc-dev; +Cc: mikey
This patch enables support for hardware instruction breakpoints
on POWER8 with the help of a new register CIABR (Completed
Instruction Address Breakpoint Register). With this patch, single
hardware instruction breakpoint can be added and cleared during
any active xmon debug session. This hardware based instruction
breakpoint mechanism works correctly along with the existing TRAP
based instruction breakpoints available on xmon.
With this new patch, we would be able to interact with xmon debugger
as demonstrated in the following sample debug session.
-----------------------------
(A) Start xmon session:
$echo x > /proc/sysrq-trigger
SysRq : Entering xmon
cpu 0x0: Vector: 0 at [c000001f6c67f960]
pc: c000000000072078: .sysrq_handle_xmon+0x58/0x60
lr: c000000000072078: .sysrq_handle_xmon+0x58/0x60
sp: c000001f6c67fac0
msr: 9000000000009032
current = 0xc000001f6e709ac0
paca = 0xc00000000fffa000 softe: 0 irq_happened: 0x00
pid = 3250, comm = bash
enter ? for help
0:mon> b
type address
(B) Set one HW instruction breakpoint:
0:mon> ls .power_pmu_add
.power_pmu_add: c000000000078f50
0:mon> bi c000000000078f50
0:mon> b
type address
1 inst c000000000078f50 .power_pmu_add+0x0/0x2e0
0:mon> ls .perf_event_interrupt
.perf_event_interrupt: c00000000007aee0
0:mon> bi c00000000007aee0
One instruction breakpoint possible with CIABR [as expected]
0:mon> x
(C) Run the sampple workload [with the breakpoint in place]:
$./perf record ls
cpu 0x2: Vector: d00 (Single Step) at [c000001f718133a0]
pc: c000000000078f54: .power_pmu_add+0x4/0x2e0
lr: c000000000155be0: .event_sched_in+0x90/0x1d0
sp: c000001f71813620
msr: 9000000040109032
current = 0xc000001f6ce30000
paca = 0xc00000000fffa600 softe: 0 irq_happened: 0x01
pid = 3270, comm = ls
std r22,-80(r1)
enter ? for help
(D) Clear the breakpoint:
2:mon> bc
All breakpoints cleared
2:mon> x
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.002 MB perf.data (~66 samples) ]
(E) Run the workload again [without any breakpoints]:
$./perf record ls
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.001 MB perf.data (~61 samples) ]
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
Changes in V3:
- Moved the 'ciabr_used' early init inside 'cmds' function
- Some minor code cleanup
- Added more in-code documentation
- Changed the commit message
Changes in V2: (http://patchwork.ozlabs.org/patch/373114/)
- Fixed the compilation problem in 32 bit archs
- Selective inclusion of plapr_set_ciabr for required platforms
- Cleaned up the white space issues
arch/powerpc/include/asm/xmon.h | 6 +++
arch/powerpc/xmon/xmon.c | 83 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 85 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/include/asm/xmon.h b/arch/powerpc/include/asm/xmon.h
index 5eb8e59..5d17aec 100644
--- a/arch/powerpc/include/asm/xmon.h
+++ b/arch/powerpc/include/asm/xmon.h
@@ -29,5 +29,11 @@ static inline void xmon_register_spus(struct list_head *list) { };
extern int cpus_are_in_xmon(void);
#endif
+#if defined(CONFIG_PPC_BOOK3S_64) && defined(CONFIG_PPC_SPLPAR)
+#include <asm/plpar_wrappers.h>
+#else
+static inline long plapr_set_ciabr(unsigned long ciabr) {return 0; };
+#endif
+
#endif /* __KERNEL __ */
#endif /* __ASM_POWERPC_XMON_H */
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index b988b5a..c2f601a 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -90,6 +90,7 @@ struct bpt {
/* Bits in bpt.enabled */
#define BP_IABR_TE 1 /* IABR translation enabled */
#define BP_IABR 2
+#define BP_CIABR 4
#define BP_TRAP 8
#define BP_DABR 0x10
@@ -98,6 +99,7 @@ static struct bpt bpts[NBPTS];
static struct bpt dabr;
static struct bpt *iabr;
static unsigned bpinstr = 0x7fe00008; /* trap */
+static bool ciabr_used; /* CIABR instruction breakpoint */
#define BP_NUM(bp) ((bp) - bpts + 1)
@@ -271,6 +273,55 @@ static inline void cinval(void *p)
}
/*
+ * write_ciabr
+ *
+ * This function writes a value to the
+ * CIARB register either directly through
+ * mtspr instruction if the kernel is in HV
+ * privilege mode or call a hypervisor function
+ * to achieve the same in case the kernel is in
+ * supervisor privilege mode.
+ */
+static void write_ciabr(unsigned long ciabr)
+{
+ if (cpu_has_feature(CPU_FTR_HVMODE)) {
+ mtspr(SPRN_CIABR, ciabr);
+ return;
+ }
+ plapr_set_ciabr(ciabr);
+}
+
+/*
+ * set_ciabr
+ *
+ * This function sets the correct privilege
+ * value into the the HW breakpoint address
+ * before writing it up in the CIABR register.
+ */
+static void set_ciabr(unsigned long addr)
+{
+ addr &= ~CIABR_PRIV;
+ if (cpu_has_feature(CPU_FTR_HVMODE))
+ addr |= CIABR_PRIV_HYPER;
+ else
+ addr |= CIABR_PRIV_SUPER;
+ write_ciabr(addr);
+}
+
+/*
+ * clear_ciabr
+ *
+ * This function clears the CIABR register
+ * which in turn removes the hardware
+ * instruction breakpoint from the CPU.
+ */
+static void clear_ciabr(void)
+{
+ if (cpu_has_feature(CPU_FTR_ARCH_207S))
+ write_ciabr(0);
+}
+
+/*
* Disable surveillance (the service processor watchdog function)
* while we are in xmon.
* XXX we should re-enable it when we leave. :)
@@ -767,6 +818,9 @@ static void insert_cpu_bpts(void)
if (iabr && cpu_has_feature(CPU_FTR_IABR))
mtspr(SPRN_IABR, iabr->address
| (iabr->enabled & (BP_IABR|BP_IABR_TE)));
+
+ if (iabr && cpu_has_feature(CPU_FTR_ARCH_207S))
+ set_ciabr(iabr->address);
}
static void remove_bpts(void)
@@ -794,6 +848,7 @@ static void remove_cpu_bpts(void)
hw_breakpoint_disable();
if (cpu_has_feature(CPU_FTR_IABR))
mtspr(SPRN_IABR, 0);
+ clear_ciabr();
}
/* Command interpreting routine */
@@ -806,6 +861,7 @@ cmds(struct pt_regs *excp)
last_cmd = NULL;
xmon_regs = excp;
+ ciabr_used = false;
if (!xmon_no_auto_backtrace) {
xmon_no_auto_backtrace = 1;
@@ -1127,7 +1183,7 @@ static char *breakpoint_help_string =
"b <addr> [cnt] set breakpoint at given instr addr\n"
"bc clear all breakpoints\n"
"bc <n/addr> clear breakpoint number n or at addr\n"
- "bi <addr> [cnt] set hardware instr breakpoint (POWER3/RS64 only)\n"
+ "bi <addr> [cnt] set hardware instr breakpoint (POWER3/RS64/POWER8 only)\n"
"bd <addr> [cnt] set hardware data breakpoint\n"
"";
@@ -1166,11 +1222,21 @@ bpt_cmds(void)
break;
case 'i': /* bi - hardware instr breakpoint */
- if (!cpu_has_feature(CPU_FTR_IABR)) {
+ if (!cpu_has_feature(CPU_FTR_IABR) &&
+ !cpu_has_feature(CPU_FTR_ARCH_207S)) {
printf("Hardware instruction breakpoint "
"not supported on this cpu\n");
break;
}
+
+ if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
+ if (ciabr_used) {
+ printf("One instruction breakpoint "
+ "possible with CIABR\n");
+ break;
+ }
+ }
+
if (iabr) {
iabr->enabled &= ~(BP_IABR | BP_IABR_TE);
iabr = NULL;
@@ -1181,7 +1247,11 @@ bpt_cmds(void)
break;
bp = new_breakpoint(a);
if (bp != NULL) {
- bp->enabled |= BP_IABR | BP_IABR_TE;
+ if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
+ bp->enabled |= BP_CIABR;
+ ciabr_used = true;
+ } else
+ bp->enabled |= BP_IABR | BP_IABR_TE;
iabr = bp;
}
break;
@@ -1194,6 +1264,7 @@ bpt_cmds(void)
bpts[i].enabled = 0;
iabr = NULL;
dabr.enabled = 0;
+ ciabr_used = false;
printf("All breakpoints cleared\n");
break;
}
@@ -1210,6 +1281,9 @@ bpt_cmds(void)
}
}
+ if (bp->enabled & BP_CIABR)
+ ciabr_used = false;
+
printf("Cleared breakpoint %lx (", BP_NUM(bp));
xmon_print_symbol(bp->address, " ", ")\n");
bp->enabled = 0;
@@ -1238,7 +1312,8 @@ bpt_cmds(void)
if (!bp->enabled)
continue;
printf("%2x %s ", BP_NUM(bp),
- (bp->enabled & BP_IABR)? "inst": "trap");
+ (bp->enabled & (BP_IABR | BP_CIABR))
+ ? "inst" : "trap");
xmon_print_symbol(bp->address, " ", "\n");
}
break;
--
1.9.3
^ permalink raw reply related
* [PATCH v2 0/4] powernv: cpuidle: Redesign idle states management
From: Shreyas B. Prabhu @ 2014-11-25 11:17 UTC (permalink / raw)
To: linux-kernel
Cc: linux-pm, Shreyas B. Prabhu, Rafael J. Wysocki, Paul Mackerras,
Preeti U Murthy, linuxppc-dev
Deep idle states like sleep and winkle are per core idle states. A core
enters these states only when all the threads enter either the particular
idle state or a deeper one. There are tasks like fastsleep hardware bug
workaround and hypervisor core state save which have to be done only by
the last thread of the core entering deep idle state and similarly tasks
like timebase resync, hypervisor core register restore that have to be
done only by the first thread waking up from these states.
The current idle state management does not have a way to distinguish the
first/last thread of the core waking/entering idle states. Tasks like
timebase resync are done for all the threads. This is not only is suboptimal,
but can cause functionality issues when subcores are involved.
Winkle is deeper idle state compared to fastsleep. In this state the power
supply to the chiplet, i.e core, private L2 and private L3 is turned off.
This results in a total hypervisor state loss. This patch set adds support
for winkle and provides a way to track the idle states of the threads of the
core and use it for idle state management of idle states sleep and winkle.
Changes in v2:
--------------
-Using PNV_THREAD_NAP/SLEEP defines while calling power7_powersave_common
-Comment changes based on review
-Rebased on top of 3.18-rc6
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: linux-pm@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
Cc: Preeti U Murthy <preeti@linux.vnet.ibm.com>
Paul Mackerras (1):
powerpc: powernv: Switch off MMU before entering nap/sleep/rvwinkle
mode
Preeti U. Murthy (1):
powerpc/powernv: Enable Offline CPUs to enter deep idle states
Shreyas B. Prabhu (2):
powernv: cpuidle: Redesign idle states management
powernv: powerpc: Add winkle support for offline cpus
arch/powerpc/include/asm/cpuidle.h | 14 ++
arch/powerpc/include/asm/opal.h | 13 +
arch/powerpc/include/asm/paca.h | 6 +
arch/powerpc/include/asm/ppc-opcode.h | 2 +
arch/powerpc/include/asm/processor.h | 1 +
arch/powerpc/include/asm/reg.h | 4 +
arch/powerpc/kernel/asm-offsets.c | 6 +
arch/powerpc/kernel/cpu_setup_power.S | 4 +
arch/powerpc/kernel/exceptions-64s.S | 30 ++-
arch/powerpc/kernel/idle_power7.S | 332 +++++++++++++++++++++----
arch/powerpc/platforms/powernv/opal-wrappers.S | 39 +++
arch/powerpc/platforms/powernv/powernv.h | 2 +
arch/powerpc/platforms/powernv/setup.c | 160 ++++++++++++
arch/powerpc/platforms/powernv/smp.c | 10 +-
arch/powerpc/platforms/powernv/subcore.c | 34 +++
arch/powerpc/platforms/powernv/subcore.h | 1 +
drivers/cpuidle/cpuidle-powernv.c | 10 +-
17 files changed, 608 insertions(+), 60 deletions(-)
create mode 100644 arch/powerpc/include/asm/cpuidle.h
--
1.9.3
^ permalink raw reply
* [PATCH v2 1/4] powerpc: powernv: Switch off MMU before entering nap/sleep/rvwinkle mode
From: Shreyas B. Prabhu @ 2014-11-25 11:17 UTC (permalink / raw)
To: linux-kernel; +Cc: Shreyas B. Prabhu, linuxppc-dev, Paul Mackerras
In-Reply-To: <1416914279-30384-1-git-send-email-shreyas@linux.vnet.ibm.com>
From: Paul Mackerras <paulus@samba.org>
Currently, when going idle, we set the flag indicating that we are in
nap mode (paca->kvm_hstate.hwthread_state) and then execute the nap
(or sleep or rvwinkle) instruction, all with the MMU on. This is bad
for two reasons: (a) the architecture specifies that those instructions
must be executed with the MMU off, and in fact with only the SF, HV, ME
and possibly RI bits set, and (b) this introduces a race, because as
soon as we set the flag, another thread can switch the MMU to a guest
context. If the race is lost, this thread will typically start looping
on relocation-on ISIs at 0xc...4400.
This fixes it by setting the MSR as required by the architecture before
setting the flag or executing the nap/sleep/rvwinkle instruction.
[ shreyas@linux.vnet.ibm.com: Edited to handle LE ]
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Shreyas B. Prabhu <shreyas@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: linuxppc-dev@lists.ozlabs.org
---
arch/powerpc/include/asm/reg.h | 2 ++
arch/powerpc/kernel/idle_power7.S | 18 +++++++++++++++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index c998279..a68ee15 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -118,8 +118,10 @@
#define __MSR (MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_ISF |MSR_HV)
#ifdef __BIG_ENDIAN__
#define MSR_ __MSR
+#define MSR_IDLE (MSR_ME | MSR_SF | MSR_HV)
#else
#define MSR_ (__MSR | MSR_LE)
+#define MSR_IDLE (MSR_ME | MSR_SF | MSR_HV | MSR_LE)
#endif
#define MSR_KERNEL (MSR_ | MSR_64BIT)
#define MSR_USER32 (MSR_ | MSR_PR | MSR_EE)
diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S
index c0754bb..283c603 100644
--- a/arch/powerpc/kernel/idle_power7.S
+++ b/arch/powerpc/kernel/idle_power7.S
@@ -101,7 +101,23 @@ _GLOBAL(power7_powersave_common)
std r9,_MSR(r1)
std r1,PACAR1(r13)
-_GLOBAL(power7_enter_nap_mode)
+ /*
+ * Go to real mode to do the nap, as required by the architecture.
+ * Also, we need to be in real mode before setting hwthread_state,
+ * because as soon as we do that, another thread can switch
+ * the MMU context to the guest.
+ */
+ LOAD_REG_IMMEDIATE(r5, MSR_IDLE)
+ li r6, MSR_RI
+ andc r6, r9, r6
+ LOAD_REG_ADDR(r7, power7_enter_nap_mode)
+ mtmsrd r6, 1 /* clear RI before setting SRR0/1 */
+ mtspr SPRN_SRR0, r7
+ mtspr SPRN_SRR1, r5
+ rfid
+
+ .globl power7_enter_nap_mode
+power7_enter_nap_mode:
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
/* Tell KVM we're napping */
li r4,KVM_HWTHREAD_IN_NAP
--
1.9.3
^ permalink raw reply related
* [PATCH v2 2/4] powerpc/powernv: Enable Offline CPUs to enter deep idle states
From: Shreyas B. Prabhu @ 2014-11-25 11:17 UTC (permalink / raw)
To: linux-kernel
Cc: linux-pm, Shreyas B. Prabhu, Rafael J. Wysocki, Paul Mackerras,
Preeti U. Murthy, linuxppc-dev
In-Reply-To: <1416914279-30384-1-git-send-email-shreyas@linux.vnet.ibm.com>
From: "Preeti U. Murthy" <preeti@linux.vnet.ibm.com>
The secondary threads should enter deep idle states so as to gain maximum
powersavings when the entire core is offline. To do so the offline path
must be made aware of the available deepest idle state. Hence probe the
device tree for the possible idle states in powernv core code and
expose the deepest idle state through flags.
Since the device tree is probed by the cpuidle driver as well, move
the parameters required to discover the idle states into an appropriate
common place to both the driver and the powernv core code.
Another point is that fastsleep idle state may require workarounds in
the kernel to function properly. This workaround is introduced in the
subsequent patches. However neither the cpuidle driver or the hotplug
path need be bothered about this workaround.
They will be taken care of by the core powernv code.
Originally-by: Srivatsa S. Bhat <srivatsa@mit.edu>
Signed-off-by: Preeti U. Murthy <preeti@linux.vnet.ibm.com>
Signed-off-by: Shreyas B. Prabhu <shreyas@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: linux-pm@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
---
arch/powerpc/include/asm/opal.h | 8 ++++++
arch/powerpc/platforms/powernv/powernv.h | 2 ++
arch/powerpc/platforms/powernv/setup.c | 49 ++++++++++++++++++++++++++++++++
arch/powerpc/platforms/powernv/smp.c | 7 ++++-
drivers/cpuidle/cpuidle-powernv.c | 9 ++----
5 files changed, 68 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 9124b0e..f8b95c0 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -155,6 +155,14 @@ struct opal_sg_list {
#define OPAL_REGISTER_DUMP_REGION 101
#define OPAL_UNREGISTER_DUMP_REGION 102
+/* Device tree flags */
+
+/* Flags set in power-mgmt nodes in device tree if
+ * respective idle states are supported in the platform.
+ */
+#define OPAL_PM_NAP_ENABLED 0x00010000
+#define OPAL_PM_SLEEP_ENABLED 0x00020000
+
#ifndef __ASSEMBLY__
#include <linux/notifier.h>
diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h
index 6c8e2d1..604c48e 100644
--- a/arch/powerpc/platforms/powernv/powernv.h
+++ b/arch/powerpc/platforms/powernv/powernv.h
@@ -29,6 +29,8 @@ static inline u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev)
}
#endif
+extern u32 pnv_get_supported_cpuidle_states(void);
+
extern void pnv_lpc_init(void);
bool cpu_core_split_required(void);
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 3f9546d..34c6665 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -290,6 +290,55 @@ static void __init pnv_setup_machdep_rtas(void)
}
#endif /* CONFIG_PPC_POWERNV_RTAS */
+static u32 supported_cpuidle_states;
+
+u32 pnv_get_supported_cpuidle_states(void)
+{
+ return supported_cpuidle_states;
+}
+
+static int __init pnv_init_idle_states(void)
+{
+ struct device_node *power_mgt;
+ int dt_idle_states;
+ const __be32 *idle_state_flags;
+ u32 len_flags, flags;
+ int i;
+
+ supported_cpuidle_states = 0;
+
+ if (cpuidle_disable != IDLE_NO_OVERRIDE)
+ return 0;
+
+ if (!firmware_has_feature(FW_FEATURE_OPALv3))
+ return 0;
+
+ power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
+ if (!power_mgt) {
+ pr_warn("opal: PowerMgmt Node not found\n");
+ return 0;
+ }
+
+ idle_state_flags = of_get_property(power_mgt,
+ "ibm,cpu-idle-state-flags", &len_flags);
+ if (!idle_state_flags) {
+ pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-flags\n");
+ return 0;
+ }
+
+ dt_idle_states = len_flags / sizeof(u32);
+
+ for (i = 0; i < dt_idle_states; i++) {
+ flags = be32_to_cpu(idle_state_flags[i]);
+ supported_cpuidle_states |= flags;
+ }
+
+ return 0;
+}
+
+subsys_initcall(pnv_init_idle_states);
+
+
static int __init pnv_probe(void)
{
unsigned long root = of_get_flat_dt_root();
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index 4753958..3dc4cec 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -149,6 +149,7 @@ static int pnv_smp_cpu_disable(void)
static void pnv_smp_cpu_kill_self(void)
{
unsigned int cpu;
+ u32 idle_states;
/* Standard hot unplug procedure */
local_irq_disable();
@@ -159,13 +160,17 @@ static void pnv_smp_cpu_kill_self(void)
generic_set_cpu_dead(cpu);
smp_wmb();
+ idle_states = pnv_get_supported_cpuidle_states();
/* We don't want to take decrementer interrupts while we are offline,
* so clear LPCR:PECE1. We keep PECE2 enabled.
*/
mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
while (!generic_check_cpu_restart(cpu)) {
ppc64_runlatch_off();
- power7_nap(1);
+ if (idle_states & OPAL_PM_SLEEP_ENABLED)
+ power7_sleep();
+ else
+ power7_nap(1);
ppc64_runlatch_on();
/* Clear the IPI that woke us up */
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
index 7d3a349..0a7d827 100644
--- a/drivers/cpuidle/cpuidle-powernv.c
+++ b/drivers/cpuidle/cpuidle-powernv.c
@@ -16,13 +16,10 @@
#include <asm/machdep.h>
#include <asm/firmware.h>
+#include <asm/opal.h>
#include <asm/runlatch.h>
-/* Flags and constants used in PowerNV platform */
-
#define MAX_POWERNV_IDLE_STATES 8
-#define IDLE_USE_INST_NAP 0x00010000 /* Use nap instruction */
-#define IDLE_USE_INST_SLEEP 0x00020000 /* Use sleep instruction */
struct cpuidle_driver powernv_idle_driver = {
.name = "powernv_idle",
@@ -198,7 +195,7 @@ static int powernv_add_idle_states(void)
* target residency to be 10x exit_latency
*/
latency_ns = be32_to_cpu(idle_state_latency[i]);
- if (flags & IDLE_USE_INST_NAP) {
+ if (flags & OPAL_PM_NAP_ENABLED) {
/* Add NAP state */
strcpy(powernv_states[nr_idle_states].name, "Nap");
strcpy(powernv_states[nr_idle_states].desc, "Nap");
@@ -211,7 +208,7 @@ static int powernv_add_idle_states(void)
nr_idle_states++;
}
- if (flags & IDLE_USE_INST_SLEEP) {
+ if (flags & OPAL_PM_SLEEP_ENABLED) {
/* Add FASTSLEEP state */
strcpy(powernv_states[nr_idle_states].name, "FastSleep");
strcpy(powernv_states[nr_idle_states].desc, "FastSleep");
--
1.9.3
^ permalink raw reply related
* [PATCH v2 4/4] powernv: powerpc: Add winkle support for offline cpus
From: Shreyas B. Prabhu @ 2014-11-25 11:17 UTC (permalink / raw)
To: linux-kernel; +Cc: Shreyas B. Prabhu, linuxppc-dev, Paul Mackerras
In-Reply-To: <1416914279-30384-1-git-send-email-shreyas@linux.vnet.ibm.com>
Winkle is a deep idle state supported in power8 chips. A core enters
winkle when all the threads of the core enter winkle. In this state
power supply to the entire chiplet i.e core, private L2 and private L3
is turned off. As a result it gives higher powersavings compared to
sleep.
But entering winkle results in a total hypervisor state loss. Hence the
hypervisor context has to be preserved before entering winkle and
restored upon wake up.
Power-on Reset Engine (PORE) is a dedicated engine which is responsible
for powering on the chiplet during wake up. It can be programmed to
restore the register contests of a few specific registers. This patch
uses PORE to restore register state wherever possible and uses stack to
save and restore rest of the necessary registers.
With hypervisor state restore things fall under three categories-
per-core state, per-subcore state and per-thread state. To manage this,
extend the infrastructure introduced for sleep. Mainly we add a paca
variable subcore_sibling_mask. Using this and the core_idle_state we can
distingush first thread in core and subcore.
Signed-off-by: Shreyas B. Prabhu <shreyas@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: linuxppc-dev@lists.ozlabs.org
---
arch/powerpc/include/asm/opal.h | 3 +
arch/powerpc/include/asm/paca.h | 2 +
arch/powerpc/include/asm/ppc-opcode.h | 2 +
arch/powerpc/include/asm/processor.h | 1 +
arch/powerpc/include/asm/reg.h | 2 +
arch/powerpc/kernel/asm-offsets.c | 2 +
arch/powerpc/kernel/cpu_setup_power.S | 4 +
arch/powerpc/kernel/exceptions-64s.S | 10 ++
arch/powerpc/kernel/idle_power7.S | 156 ++++++++++++++++++++++---
arch/powerpc/platforms/powernv/opal-wrappers.S | 2 +
arch/powerpc/platforms/powernv/setup.c | 73 ++++++++++++
arch/powerpc/platforms/powernv/smp.c | 4 +-
arch/powerpc/platforms/powernv/subcore.c | 34 ++++++
arch/powerpc/platforms/powernv/subcore.h | 1 +
14 files changed, 282 insertions(+), 14 deletions(-)
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index bef7fbc..f0ca2d9 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -153,6 +153,7 @@ struct opal_sg_list {
#define OPAL_PCI_EEH_FREEZE_SET 97
#define OPAL_HANDLE_HMI 98
#define OPAL_CONFIG_CPU_IDLE_STATE 99
+#define OPAL_SLW_SET_REG 100
#define OPAL_REGISTER_DUMP_REGION 101
#define OPAL_UNREGISTER_DUMP_REGION 102
@@ -163,6 +164,7 @@ struct opal_sg_list {
*/
#define OPAL_PM_NAP_ENABLED 0x00010000
#define OPAL_PM_SLEEP_ENABLED 0x00020000
+#define OPAL_PM_WINKLE_ENABLED 0x00040000
#define OPAL_PM_SLEEP_ENABLED_ER1 0x00080000
#ifndef __ASSEMBLY__
@@ -972,6 +974,7 @@ int64_t opal_sensor_read(uint32_t sensor_hndl, int token, __be32 *sensor_data);
int64_t opal_handle_hmi(void);
int64_t opal_register_dump_region(uint32_t id, uint64_t start, uint64_t end);
int64_t opal_unregister_dump_region(uint32_t id);
+int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);
int64_t opal_pci_set_phb_cxl_mode(uint64_t phb_id, uint64_t mode, uint64_t pe_number);
/* Internal functions */
diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index 85aeedb..c2e51b7 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -162,6 +162,8 @@ struct paca_struct {
/* Per-core mask tracking idle threads and a lock bit-[L][TTTTTTTT] */
u32 *core_idle_state_ptr;
u8 thread_idle_state; /* ~Idle[0]/Nap[1]/Sleep[2]/Winkle[3] */
+ /* Mask to denote subcore sibling threads */
+ u8 subcore_sibling_mask;
#endif
#ifdef CONFIG_PPC_BOOK3S_64
/* Exclusive emergency stack pointer for machine check exception. */
diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h
index 6f85362..5155be7 100644
--- a/arch/powerpc/include/asm/ppc-opcode.h
+++ b/arch/powerpc/include/asm/ppc-opcode.h
@@ -194,6 +194,7 @@
#define PPC_INST_NAP 0x4c000364
#define PPC_INST_SLEEP 0x4c0003a4
+#define PPC_INST_WINKLE 0x4c0003e4
/* A2 specific instructions */
#define PPC_INST_ERATWE 0x7c0001a6
@@ -374,6 +375,7 @@
#define PPC_NAP stringify_in_c(.long PPC_INST_NAP)
#define PPC_SLEEP stringify_in_c(.long PPC_INST_SLEEP)
+#define PPC_WINKLE stringify_in_c(.long PPC_INST_WINKLE)
/* BHRB instructions */
#define PPC_CLRBHRB stringify_in_c(.long PPC_INST_CLRBHRB)
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index dda7ac4..c076842 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -453,6 +453,7 @@ enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF};
extern int powersave_nap; /* set if nap mode can be used in idle loop */
extern void power7_nap(int check_irq);
extern void power7_sleep(void);
+extern void power7_winkle(void);
extern void flush_instruction_cache(void);
extern void hard_reset_now(void);
extern void poweroff_now(void);
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index a68ee15..1c874fb 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -373,6 +373,7 @@
#define SPRN_DBAT7L 0x23F /* Data BAT 7 Lower Register */
#define SPRN_DBAT7U 0x23E /* Data BAT 7 Upper Register */
#define SPRN_PPR 0x380 /* SMT Thread status Register */
+#define SPRN_TSCR 0x399 /* Thread Switch Control Register */
#define SPRN_DEC 0x016 /* Decrement Register */
#define SPRN_DER 0x095 /* Debug Enable Regsiter */
@@ -730,6 +731,7 @@
#define SPRN_BESCR 806 /* Branch event status and control register */
#define BESCR_GE 0x8000000000000000ULL /* Global Enable */
#define SPRN_WORT 895 /* Workload optimization register - thread */
+#define SPRN_WORC 863 /* Workload optimization register - core */
#define SPRN_PMC1 787
#define SPRN_PMC2 788
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 50f299e..b7fb63a 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -735,6 +735,8 @@ int main(void)
offsetof(struct paca_struct, core_idle_state_ptr));
DEFINE(PACA_THREAD_IDLE_STATE,
offsetof(struct paca_struct, thread_idle_state));
+ DEFINE(PACA_SUBCORE_SIBLING_MASK,
+ offsetof(struct paca_struct, subcore_sibling_mask));
#endif
return 0;
diff --git a/arch/powerpc/kernel/cpu_setup_power.S b/arch/powerpc/kernel/cpu_setup_power.S
index 4673353..66874aa 100644
--- a/arch/powerpc/kernel/cpu_setup_power.S
+++ b/arch/powerpc/kernel/cpu_setup_power.S
@@ -55,6 +55,8 @@ _GLOBAL(__setup_cpu_power8)
beqlr
li r0,0
mtspr SPRN_LPID,r0
+ mtspr SPRN_WORT,r0
+ mtspr SPRN_WORC,r0
mfspr r3,SPRN_LPCR
ori r3, r3, LPCR_PECEDH
bl __init_LPCR
@@ -75,6 +77,8 @@ _GLOBAL(__restore_cpu_power8)
li r0,0
mtspr SPRN_LPID,r0
mfspr r3,SPRN_LPCR
+ mtspr SPRN_WORT,r0
+ mtspr SPRN_WORC,r0
ori r3, r3, LPCR_PECEDH
bl __init_LPCR
bl __init_HFSCR
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 3311c8d..c9897cb 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -112,6 +112,16 @@ BEGIN_FTR_SECTION
cmpwi cr1,r13,2
+ /* Check if last bit of HSPGR0 is set. This indicates whether we are
+ * waking up from winkle */
+ li r3,1
+ mfspr r4,SPRN_HSPRG0
+ and r5,r4,r3
+ cmpwi cr4,r5,1 /* Store result in cr4 for later use */
+
+ andc r4,r4,r3
+ mtspr SPRN_HSPRG0,r4
+
GET_PACA(r13)
lbz r0,PACA_THREAD_IDLE_STATE(r13)
cmpwi cr2,r0,PNV_THREAD_NAP
diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S
index c1d590f..78c30b0 100644
--- a/arch/powerpc/kernel/idle_power7.S
+++ b/arch/powerpc/kernel/idle_power7.S
@@ -19,8 +19,22 @@
#include <asm/kvm_book3s_asm.h>
#include <asm/opal.h>
#include <asm/cpuidle.h>
+#include <asm/mmu-hash64.h>
#undef DEBUG
+/*
+ * Use unused space in the interrupt stack to save and restore
+ * registers for winkle support.
+ */
+#define _SDR1 GPR3
+#define _RPR GPR4
+#define _SPURR GPR5
+#define _PURR GPR6
+#define _TSCR GPR7
+#define _DSCR GPR8
+#define _AMOR GPR9
+#define _PMC5 GPR10
+#define _PMC6 GPR11
/* Idle state entry routines */
@@ -153,32 +167,60 @@ lwarx_loop1:
b common_enter
last_thread:
- LOAD_REG_ADDR(r3, pnv_need_fastsleep_workaround)
- lbz r3,0(r3)
- cmpwi r3,1
- bne common_enter
/*
* Last thread of the core entering sleep. Last thread needs to execute
* the hardware bug workaround code. Before that, set the lock bit to
* avoid the race of other threads waking up and undoing workaround
* before workaround is applied.
*/
+ LOAD_REG_ADDR(r3, pnv_need_fastsleep_workaround)
+ lbz r3,0(r3)
+ cmpwi r3,1
+ bne common_enter
+
ori r15,r15,PNV_CORE_IDLE_LOCK_BIT
stwcx. r15,0,r14
bne- lwarx_loop1
/* Fast sleep workaround */
+ mfcr r16 /* Backup CR to a non-volatile register */
li r3,1
li r4,1
li r0,OPAL_CONFIG_CPU_IDLE_STATE
bl opal_call_realmode
+ mtcr r16 /* Restore CR */
/* Clear Lock bit */
andi. r15,r15,PNV_CORE_IDLE_THREAD_BITS
stw r15,0(r14)
-common_enter: /* common code for all the threads entering sleep */
+common_enter: /* common code for all the threads entering sleep or winkle*/
+ bgt cr1,enter_winkle
IDLE_STATE_ENTER_SEQ(PPC_SLEEP)
+enter_winkle:
+ /*
+ * Note all register i.e per-core, per-subcore or per-thread is saved
+ * here since any thread in the core might wake up first
+ */
+ mfspr r3,SPRN_SDR1
+ std r3,_SDR1(r1)
+ mfspr r3,SPRN_RPR
+ std r3,_RPR(r1)
+ mfspr r3,SPRN_SPURR
+ std r3,_SPURR(r1)
+ mfspr r3,SPRN_PURR
+ std r3,_PURR(r1)
+ mfspr r3,SPRN_TSCR
+ std r3,_TSCR(r1)
+ mfspr r3,SPRN_DSCR
+ std r3,_DSCR(r1)
+ mfspr r3,SPRN_AMOR
+ std r3,_AMOR(r1)
+ mfspr r3,SPRN_PMC5
+ std r3,_PMC5(r1)
+ mfspr r3,SPRN_PMC6
+ std r3,_PMC6(r1)
+ IDLE_STATE_ENTER_SEQ(PPC_WINKLE)
_GLOBAL(power7_idle)
/* Now check if user or arch enabled NAP mode */
@@ -201,6 +243,12 @@ _GLOBAL(power7_sleep)
b power7_powersave_common
/* No return */
+_GLOBAL(power7_winkle)
+ li r3,PNV_THREAD_WINKLE
+ li r4,1
+ b power7_powersave_common
+ /* No return */
+
#define CHECK_HMI_INTERRUPT \
mfspr r0,SPRN_SRR1; \
BEGIN_FTR_SECTION_NESTED(66); \
@@ -250,22 +298,54 @@ lwarx_loop2:
*/
bne lwarx_loop2
- cmpwi cr2,r15,0
+ cmpwi cr2,r15,0 /* Check if first in core */
+ lbz r4,PACA_SUBCORE_SIBLING_MASK(r13)
+ and r4,r4,r15
+ cmpwi cr3,r4,0 /* Check if first in subcore */
+
+ /*
+ * At this stage
+ * cr1 - 01 if waking up from sleep or winkle
+ * cr2 - 10 if first thread to wakeup in core
+ * cr3 - 10 if first thread to wakeup in subcore
+ * cr4 - 10 if waking up from winkle
+ */
+
or r15,r15,r7 /* Set thread bit */
- beq cr2,first_thread
+ beq cr3,first_thread_in_subcore
- /* Not first thread in core to wake up */
+ /* Not first thread in subcore to wake up */
stwcx. r15,0,r14
bne- lwarx_loop2
b common_exit
-first_thread:
- /* First thread in core to wakeup */
+first_thread_in_subcore:
+ /* First thread in subcore to wakeup set the lock bit */
ori r15,r15,PNV_CORE_IDLE_LOCK_BIT
stwcx. r15,0,r14
bne- lwarx_loop2
+ /*
+ * If waking up from sleep, subcore state is not lost. Hence
+ * skip subcore state restore
+ */
+ bne cr4,subcore_state_restored
+
+ /* Restore per-subcore state */
+ ld r4,_SDR1(r1)
+ mtspr SPRN_SDR1,r4
+ ld r4,_RPR(r1)
+ mtspr SPRN_RPR,r4
+ ld r4,_AMOR(r1)
+ mtspr SPRN_AMOR,r4
+
+subcore_state_restored:
+ /* Check if the thread is also the first thread in the core. If not,
+ * skip to clear_lock */
+ bne cr2,clear_lock
+
+first_thread_in_core:
LOAD_REG_ADDR(r3, pnv_need_fastsleep_workaround)
lbz r3,0(r3)
cmpwi r3,1
@@ -280,21 +360,71 @@ first_thread:
bl opal_call_realmode
mtcr r16 /* Restore CR */
- /* Do timebase resync if we are waking up from sleep. Use cr1 value
- * set in exceptions-64s.S */
+timebase_resync:
+ /* Do timebase resync only if the core truly woke up from
+ * sleep/winkle */
ble cr1,clear_lock
-timebase_resync:
/* Time base re-sync */
+ mfcr r16 /* Backup CR into a non-volatile register */
li r0,OPAL_RESYNC_TIMEBASE
bl opal_call_realmode;
/* TODO: Check r3 for failure */
+ mtcr r16 /* Restore CR */
+
+ /*
+ * If waking up from sleep, per core state is not lost, skip to
+ * clear_lock.
+ */
+ bne cr4,clear_lock
+
+ /* Restore per core state */
+ ld r4,_TSCR(r1)
+ mtspr SPRN_TSCR,r4
clear_lock:
andi. r15,r15,PNV_CORE_IDLE_THREAD_BITS
stw r15,0(r14)
common_exit:
+ /* Common to all threads
+ *
+ * If waking up from sleep, hypervisor state is not lost. Hence
+ * skip hypervisor state restore.
+ */
+ bne cr4,hypervisor_state_restored
+
+ /* Waking up from winkle */
+
+ /* Restore per thread state */
+ bl __restore_cpu_power8
+
+ /* Restore SLB from PACA */
+ ld r8,PACA_SLBSHADOWPTR(r13)
+
+ .rept SLB_NUM_BOLTED
+ li r3, SLBSHADOW_SAVEAREA
+ LDX_BE r5, r8, r3
+ addi r3, r3, 8
+ LDX_BE r6, r8, r3
+ andis. r7,r5,SLB_ESID_V@h
+ beq 1f
+ slbmte r6,r5
+1: addi r8,r8,16
+ .endr
+
+ ld r4,_SPURR(r1)
+ mtspr SPRN_SPURR,r4
+ ld r4,_PURR(r1)
+ mtspr SPRN_PURR,r4
+ ld r4,_DSCR(r1)
+ mtspr SPRN_DSCR,r4
+ ld r4,_PMC5(r1)
+ mtspr SPRN_PMC5,r4
+ ld r4,_PMC6(r1)
+ mtspr SPRN_PMC6,r4
+
+hypervisor_state_restored:
li r5,PNV_THREAD_RUNNING
stb r5,PACA_THREAD_IDLE_STATE(r13)
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index b2aa93b..e1e91e0 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -191,6 +191,7 @@ return_from_opal_call:
#ifdef __LITTLE_ENDIAN__
FIXUP_ENDIAN
#endif
+ ld r2,PACATOC(r13)
ld r12,_LINK(r1)
mtlr r12
blr
@@ -284,6 +285,7 @@ OPAL_CALL(opal_sensor_read, OPAL_SENSOR_READ);
OPAL_CALL(opal_get_param, OPAL_GET_PARAM);
OPAL_CALL(opal_set_param, OPAL_SET_PARAM);
OPAL_CALL(opal_handle_hmi, OPAL_HANDLE_HMI);
+OPAL_CALL(opal_slw_set_reg, OPAL_SLW_SET_REG);
OPAL_CALL(opal_register_dump_region, OPAL_REGISTER_DUMP_REGION);
OPAL_CALL(opal_unregister_dump_region, OPAL_UNREGISTER_DUMP_REGION);
OPAL_CALL(opal_pci_set_phb_cxl_mode, OPAL_PCI_SET_PHB_CXL_MODE);
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 17fb98c..4a886a1 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -40,6 +40,7 @@
#include <asm/cpuidle.h>
#include "powernv.h"
+#include "subcore.h"
static void __init pnv_setup_arch(void)
{
@@ -293,6 +294,74 @@ static void __init pnv_setup_machdep_rtas(void)
#endif /* CONFIG_PPC_POWERNV_RTAS */
static u32 supported_cpuidle_states;
+int pnv_save_sprs_for_winkle(void)
+{
+ int cpu;
+ int rc;
+
+ /*
+ * hid0, hid1, hid4, hid5, hmeer and lpcr values are symmetric accross
+ * all cpus at boot. Get these reg values of current cpu and use the
+ * same accross all cpus.
+ */
+ uint64_t lpcr_val = mfspr(SPRN_LPCR);
+ uint64_t hid0_val = mfspr(SPRN_HID0);
+ uint64_t hid1_val = mfspr(SPRN_HID1);
+ uint64_t hid4_val = mfspr(SPRN_HID4);
+ uint64_t hid5_val = mfspr(SPRN_HID5);
+ uint64_t hmeer_val = mfspr(SPRN_HMEER);
+
+ for_each_possible_cpu(cpu) {
+ uint64_t pir = get_hard_smp_processor_id(cpu);
+ uint64_t hsprg0_val = (uint64_t)&paca[cpu];
+
+ /*
+ * HSPRG0 is used to store the cpu's pointer to paca. Hence last
+ * 3 bits are guaranteed to be 0. Program slw to restore HSPRG0
+ * with 63rd bit set, so that when a thread wakes up at 0x100 we
+ * can use this bit to distinguish between fastsleep and
+ * deep winkle.
+ */
+ hsprg0_val |= 1;
+
+ rc = opal_slw_set_reg(pir, SPRN_HSPRG0, hsprg0_val);
+ if (rc != 0)
+ return rc;
+
+ rc = opal_slw_set_reg(pir, SPRN_LPCR, lpcr_val);
+ if (rc != 0)
+ return rc;
+
+ /* HIDs are per core registers */
+ if (cpu_thread_in_core(cpu) == 0) {
+
+ rc = opal_slw_set_reg(pir, SPRN_HMEER, hmeer_val);
+ if (rc != 0)
+ return rc;
+
+ rc = opal_slw_set_reg(pir, SPRN_HID0, hid0_val);
+ if (rc != 0)
+ return rc;
+
+ rc = opal_slw_set_reg(pir, SPRN_HID1, hid1_val);
+ if (rc != 0)
+ return rc;
+
+ rc = opal_slw_set_reg(pir, SPRN_HID4, hid4_val);
+ if (rc != 0)
+ return rc;
+
+ rc = opal_slw_set_reg(pir, SPRN_HID5, hid5_val);
+ if (rc != 0)
+ return rc;
+
+ }
+
+ }
+
+ return 0;
+
+}
static void pnv_alloc_idle_core_states(void)
{
@@ -324,6 +393,10 @@ static void pnv_alloc_idle_core_states(void)
}
}
+ update_subcore_sibling_mask();
+ if (supported_cpuidle_states & OPAL_PM_WINKLE_ENABLED)
+ pnv_save_sprs_for_winkle();
+
}
u32 pnv_get_supported_cpuidle_states(void)
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index 12b761a..5e35857 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -167,7 +167,9 @@ static void pnv_smp_cpu_kill_self(void)
mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
while (!generic_check_cpu_restart(cpu)) {
ppc64_runlatch_off();
- if ((idle_states & OPAL_PM_SLEEP_ENABLED) ||
+ if (idle_states & OPAL_PM_WINKLE_ENABLED)
+ power7_winkle();
+ else if ((idle_states & OPAL_PM_SLEEP_ENABLED) ||
(idle_states & OPAL_PM_SLEEP_ENABLED_ER1))
power7_sleep();
else
diff --git a/arch/powerpc/platforms/powernv/subcore.c b/arch/powerpc/platforms/powernv/subcore.c
index c87f96b..f60f80a 100644
--- a/arch/powerpc/platforms/powernv/subcore.c
+++ b/arch/powerpc/platforms/powernv/subcore.c
@@ -160,6 +160,18 @@ static void wait_for_sync_step(int step)
mb();
}
+static void update_hid_in_slw(u64 hid0)
+{
+ u64 idle_states = pnv_get_supported_cpuidle_states();
+
+ if (idle_states & OPAL_PM_WINKLE_ENABLED) {
+ /* OPAL call to patch slw with the new HID0 value */
+ u64 cpu_pir = hard_smp_processor_id();
+
+ opal_slw_set_reg(cpu_pir, SPRN_HID0, hid0);
+ }
+}
+
static void unsplit_core(void)
{
u64 hid0, mask;
@@ -179,6 +191,7 @@ static void unsplit_core(void)
hid0 = mfspr(SPRN_HID0);
hid0 &= ~HID0_POWER8_DYNLPARDIS;
mtspr(SPRN_HID0, hid0);
+ update_hid_in_slw(hid0);
while (mfspr(SPRN_HID0) & mask)
cpu_relax();
@@ -215,6 +228,7 @@ static void split_core(int new_mode)
hid0 = mfspr(SPRN_HID0);
hid0 |= HID0_POWER8_DYNLPARDIS | split_parms[i].value;
mtspr(SPRN_HID0, hid0);
+ update_hid_in_slw(hid0);
/* Wait for it to happen */
while (!(mfspr(SPRN_HID0) & split_parms[i].mask))
@@ -251,6 +265,25 @@ bool cpu_core_split_required(void)
return true;
}
+void update_subcore_sibling_mask(void)
+{
+ int cpu;
+ /*
+ * sibling mask for the first cpu. Left shift this by required bits
+ * to get sibling mask for the rest of the cpus.
+ */
+ int sibling_mask_first_cpu = (1 << threads_per_subcore) - 1;
+
+ for_each_possible_cpu(cpu) {
+ int tid = cpu_thread_in_core(cpu);
+ int offset = (tid / threads_per_subcore) * threads_per_subcore;
+ int mask = sibling_mask_first_cpu << offset;
+
+ paca[cpu].subcore_sibling_mask = mask;
+
+ }
+}
+
static int cpu_update_split_mode(void *data)
{
int cpu, new_mode = *(int *)data;
@@ -284,6 +317,7 @@ static int cpu_update_split_mode(void *data)
/* Make the new mode public */
subcores_per_core = new_mode;
threads_per_subcore = threads_per_core / subcores_per_core;
+ update_subcore_sibling_mask();
/* Make sure the new mode is written before we exit */
mb();
diff --git a/arch/powerpc/platforms/powernv/subcore.h b/arch/powerpc/platforms/powernv/subcore.h
index 148abc9..604eb40 100644
--- a/arch/powerpc/platforms/powernv/subcore.h
+++ b/arch/powerpc/platforms/powernv/subcore.h
@@ -15,4 +15,5 @@
#ifndef __ASSEMBLY__
void split_core_secondary_loop(u8 *state);
+extern void update_subcore_sibling_mask(void);
#endif
--
1.9.3
^ permalink raw reply related
* [PATCH v2 3/4] powernv: cpuidle: Redesign idle states management
From: Shreyas B. Prabhu @ 2014-11-25 11:17 UTC (permalink / raw)
To: linux-kernel
Cc: linux-pm, Shreyas B. Prabhu, Rafael J. Wysocki, Paul Mackerras,
linuxppc-dev
In-Reply-To: <1416914279-30384-1-git-send-email-shreyas@linux.vnet.ibm.com>
Deep idle states like sleep and winkle are per core idle states. A core
enters these states only when all the threads enter either the
particular idle state or a deeper one. There are tasks like fastsleep
hardware bug workaround and hypervisor core state save which have to be
done only by the last thread of the core entering deep idle state and
similarly tasks like timebase resync, hypervisor core register restore
that have to be done only by the first thread waking up from these
state.
The current idle state management does not have a way to distinguish the
first/last thread of the core waking/entering idle states. Tasks like
timebase resync are done for all the threads. This is not only is
suboptimal, but can cause functionality issues when subcores and kvm is
involved.
This patch adds the necessary infrastructure to track idle states of
threads in a per-core structure. It uses this info to perform tasks like
fastsleep workaround and timebase resync only once per core.
Signed-off-by: Shreyas B. Prabhu <shreyas@linux.vnet.ibm.com>
Originally-by: Preeti U. Murthy <preeti@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: linux-pm@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
---
arch/powerpc/include/asm/cpuidle.h | 14 ++
arch/powerpc/include/asm/opal.h | 2 +
arch/powerpc/include/asm/paca.h | 4 +
arch/powerpc/kernel/asm-offsets.c | 4 +
arch/powerpc/kernel/exceptions-64s.S | 20 ++-
arch/powerpc/kernel/idle_power7.S | 186 +++++++++++++++++++------
arch/powerpc/platforms/powernv/opal-wrappers.S | 37 +++++
arch/powerpc/platforms/powernv/setup.c | 42 +++++-
arch/powerpc/platforms/powernv/smp.c | 3 +-
drivers/cpuidle/cpuidle-powernv.c | 3 +-
10 files changed, 258 insertions(+), 57 deletions(-)
create mode 100644 arch/powerpc/include/asm/cpuidle.h
diff --git a/arch/powerpc/include/asm/cpuidle.h b/arch/powerpc/include/asm/cpuidle.h
new file mode 100644
index 0000000..8c82850
--- /dev/null
+++ b/arch/powerpc/include/asm/cpuidle.h
@@ -0,0 +1,14 @@
+#ifndef _ASM_POWERPC_CPUIDLE_H
+#define _ASM_POWERPC_CPUIDLE_H
+
+#ifdef CONFIG_PPC_POWERNV
+/* Used in powernv idle state management */
+#define PNV_THREAD_RUNNING 0
+#define PNV_THREAD_NAP 1
+#define PNV_THREAD_SLEEP 2
+#define PNV_THREAD_WINKLE 3
+#define PNV_CORE_IDLE_LOCK_BIT 0x100
+#define PNV_CORE_IDLE_THREAD_BITS 0x0FF
+#endif
+
+#endif
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index f8b95c0..bef7fbc 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -152,6 +152,7 @@ struct opal_sg_list {
#define OPAL_PCI_ERR_INJECT 96
#define OPAL_PCI_EEH_FREEZE_SET 97
#define OPAL_HANDLE_HMI 98
+#define OPAL_CONFIG_CPU_IDLE_STATE 99
#define OPAL_REGISTER_DUMP_REGION 101
#define OPAL_UNREGISTER_DUMP_REGION 102
@@ -162,6 +163,7 @@ struct opal_sg_list {
*/
#define OPAL_PM_NAP_ENABLED 0x00010000
#define OPAL_PM_SLEEP_ENABLED 0x00020000
+#define OPAL_PM_SLEEP_ENABLED_ER1 0x00080000
#ifndef __ASSEMBLY__
diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index a5139ea..85aeedb 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -158,6 +158,10 @@ struct paca_struct {
* early exception handler for use by high level C handler
*/
struct opal_machine_check_event *opal_mc_evt;
+
+ /* Per-core mask tracking idle threads and a lock bit-[L][TTTTTTTT] */
+ u32 *core_idle_state_ptr;
+ u8 thread_idle_state; /* ~Idle[0]/Nap[1]/Sleep[2]/Winkle[3] */
#endif
#ifdef CONFIG_PPC_BOOK3S_64
/* Exclusive emergency stack pointer for machine check exception. */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 9d7dede..50f299e 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -731,6 +731,10 @@ int main(void)
DEFINE(OPAL_MC_SRR0, offsetof(struct opal_machine_check_event, srr0));
DEFINE(OPAL_MC_SRR1, offsetof(struct opal_machine_check_event, srr1));
DEFINE(PACA_OPAL_MC_EVT, offsetof(struct paca_struct, opal_mc_evt));
+ DEFINE(PACA_CORE_IDLE_STATE_PTR,
+ offsetof(struct paca_struct, core_idle_state_ptr));
+ DEFINE(PACA_THREAD_IDLE_STATE,
+ offsetof(struct paca_struct, thread_idle_state));
#endif
return 0;
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 72e783e..3311c8d 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -15,6 +15,7 @@
#include <asm/hw_irq.h>
#include <asm/exception-64s.h>
#include <asm/ptrace.h>
+#include <asm/cpuidle.h>
/*
* We layout physical memory as follows:
@@ -109,15 +110,19 @@ BEGIN_FTR_SECTION
rlwinm. r13,r13,47-31,30,31
beq 9f
- /* waking up from powersave (nap) state */
cmpwi cr1,r13,2
- /* Total loss of HV state is fatal, we could try to use the
- * PIR to locate a PACA, then use an emergency stack etc...
- * OPAL v3 based powernv platforms have new idle states
- * which fall in this catagory.
- */
- bgt cr1,8f
+
GET_PACA(r13)
+ lbz r0,PACA_THREAD_IDLE_STATE(r13)
+ cmpwi cr2,r0,PNV_THREAD_NAP
+ bgt cr2,8f /* Either sleep or Winkle */
+
+ /* Waking up from nap should not cause hypervisor state loss */
+ bgt cr1,.
+
+ /* Waking up from nap */
+ li r0,PNV_THREAD_RUNNING
+ stb r0,PACA_THREAD_IDLE_STATE(r13) /* Clear thread state */
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
li r0,KVM_HWTHREAD_IN_KERNEL
@@ -1386,6 +1391,7 @@ machine_check_handle_early:
MACHINE_CHECK_HANDLER_WINDUP
GET_PACA(r13)
ld r1,PACAR1(r13)
+ li r3,PNV_THREAD_NAP
b power7_enter_nap_mode
4:
#endif
diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S
index 283c603..c1d590f 100644
--- a/arch/powerpc/kernel/idle_power7.S
+++ b/arch/powerpc/kernel/idle_power7.S
@@ -18,6 +18,7 @@
#include <asm/hw_irq.h>
#include <asm/kvm_book3s_asm.h>
#include <asm/opal.h>
+#include <asm/cpuidle.h>
#undef DEBUG
@@ -37,8 +38,7 @@
/*
* Pass requested state in r3:
- * 0 - nap
- * 1 - sleep
+ * r3 - PNV_THREAD_NAP/SLEEP/WINKLE
*
* To check IRQ_HAPPENED in r4
* 0 - don't check
@@ -123,12 +123,62 @@ power7_enter_nap_mode:
li r4,KVM_HWTHREAD_IN_NAP
stb r4,HSTATE_HWTHREAD_STATE(r13)
#endif
- cmpwi cr0,r3,1
- beq 2f
+ stb r3,PACA_THREAD_IDLE_STATE(r13)
+ cmpwi cr1,r3,PNV_THREAD_SLEEP
+ bge cr1,2f
IDLE_STATE_ENTER_SEQ(PPC_NAP)
/* No return */
-2: IDLE_STATE_ENTER_SEQ(PPC_SLEEP)
- /* No return */
+2:
+ /* Sleep or winkle */
+ li r7,1
+ mfspr r8,SPRN_PIR
+ /*
+ * The last 3 bits of PIR represents the thread id of a cpu
+ * in power8. This will need adjusting for power7.
+ */
+ andi. r8,r8,0x07 /* Get thread id into r8 */
+ rotld r7,r7,r8
+
+ ld r14,PACA_CORE_IDLE_STATE_PTR(r13)
+lwarx_loop1:
+ lwarx r15,0,r14
+ andc r15,r15,r7 /* Clear thread bit */
+
+ andi. r15,r15,PNV_CORE_IDLE_THREAD_BITS
+ beq last_thread
+
+ /* Not the last thread to goto sleep */
+ stwcx. r15,0,r14
+ bne- lwarx_loop1
+ b common_enter
+
+last_thread:
+ LOAD_REG_ADDR(r3, pnv_need_fastsleep_workaround)
+ lbz r3,0(r3)
+ cmpwi r3,1
+ bne common_enter
+ /*
+ * Last thread of the core entering sleep. Last thread needs to execute
+ * the hardware bug workaround code. Before that, set the lock bit to
+ * avoid the race of other threads waking up and undoing workaround
+ * before workaround is applied.
+ */
+ ori r15,r15,PNV_CORE_IDLE_LOCK_BIT
+ stwcx. r15,0,r14
+ bne- lwarx_loop1
+
+ /* Fast sleep workaround */
+ li r3,1
+ li r4,1
+ li r0,OPAL_CONFIG_CPU_IDLE_STATE
+ bl opal_call_realmode
+
+ /* Clear Lock bit */
+ andi. r15,r15,PNV_CORE_IDLE_THREAD_BITS
+ stw r15,0(r14)
+
+common_enter: /* common code for all the threads entering sleep */
+ IDLE_STATE_ENTER_SEQ(PPC_SLEEP)
_GLOBAL(power7_idle)
/* Now check if user or arch enabled NAP mode */
@@ -141,49 +191,16 @@ _GLOBAL(power7_idle)
_GLOBAL(power7_nap)
mr r4,r3
- li r3,0
+ li r3,PNV_THREAD_NAP
b power7_powersave_common
/* No return */
_GLOBAL(power7_sleep)
- li r3,1
+ li r3,PNV_THREAD_SLEEP
li r4,1
b power7_powersave_common
/* No return */
-/*
- * Make opal call in realmode. This is a generic function to be called
- * from realmode from reset vector. It handles endianess.
- *
- * r13 - paca pointer
- * r1 - stack pointer
- * r3 - opal token
- */
-opal_call_realmode:
- mflr r12
- std r12,_LINK(r1)
- ld r2,PACATOC(r13)
- /* Set opal return address */
- LOAD_REG_ADDR(r0,return_from_opal_call)
- mtlr r0
- /* Handle endian-ness */
- li r0,MSR_LE
- mfmsr r12
- andc r12,r12,r0
- mtspr SPRN_HSRR1,r12
- mr r0,r3 /* Move opal token to r0 */
- LOAD_REG_ADDR(r11,opal)
- ld r12,8(r11)
- ld r2,0(r11)
- mtspr SPRN_HSRR0,r12
- hrfid
-
-return_from_opal_call:
- FIXUP_ENDIAN
- ld r0,_LINK(r1)
- mtlr r0
- blr
-
#define CHECK_HMI_INTERRUPT \
mfspr r0,SPRN_SRR1; \
BEGIN_FTR_SECTION_NESTED(66); \
@@ -196,10 +213,8 @@ ALT_FTR_SECTION_END_NESTED_IFSET(CPU_FTR_ARCH_207S, 66); \
/* Invoke opal call to handle hmi */ \
ld r2,PACATOC(r13); \
ld r1,PACAR1(r13); \
- std r3,ORIG_GPR3(r1); /* Save original r3 */ \
- li r3,OPAL_HANDLE_HMI; /* Pass opal token argument*/ \
+ li r0,OPAL_HANDLE_HMI; /* Pass opal token argument*/ \
bl opal_call_realmode; \
- ld r3,ORIG_GPR3(r1); /* Restore original r3 */ \
20: nop;
@@ -210,12 +225,91 @@ _GLOBAL(power7_wakeup_tb_loss)
BEGIN_FTR_SECTION
CHECK_HMI_INTERRUPT
END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
+
+ li r7,1
+ mfspr r8,SPRN_PIR
+ /*
+ * The last 3 bits of PIR represents the thread id of a cpu
+ * in power8. This will need adjusting for power7.
+ */
+ andi. r8,r8,0x07 /* Get thread id into r8 */
+ rotld r7,r7,r8
+ /* r7 now has 'thread_id'th bit set */
+
+ ld r14,PACA_CORE_IDLE_STATE_PTR(r13)
+lwarx_loop2:
+ lwarx r15,0,r14
+ andi. r9,r15,PNV_CORE_IDLE_LOCK_BIT
+ /*
+ * Lock bit is set in one of the 2 cases-
+ * a. In the sleep/winkle enter path, the last thread is executing
+ * fastsleep workaround code.
+ * b. In the wake up path, another thread is executing fastsleep
+ * workaround undo code or resyncing timebase or restoring context
+ * In either case loop until the lock bit is cleared.
+ */
+ bne lwarx_loop2
+
+ cmpwi cr2,r15,0
+ or r15,r15,r7 /* Set thread bit */
+
+ beq cr2,first_thread
+
+ /* Not first thread in core to wake up */
+ stwcx. r15,0,r14
+ bne- lwarx_loop2
+ b common_exit
+
+first_thread:
+ /* First thread in core to wakeup */
+ ori r15,r15,PNV_CORE_IDLE_LOCK_BIT
+ stwcx. r15,0,r14
+ bne- lwarx_loop2
+
+ LOAD_REG_ADDR(r3, pnv_need_fastsleep_workaround)
+ lbz r3,0(r3)
+ cmpwi r3,1
+ /* skip fastsleep workaround if its not needed */
+ bne timebase_resync
+
+ /* Undo fast sleep workaround */
+ mfcr r16 /* Backup CR into a non-volatile register */
+ li r3,1
+ li r4,0
+ li r0,OPAL_CONFIG_CPU_IDLE_STATE
+ bl opal_call_realmode
+ mtcr r16 /* Restore CR */
+
+ /* Do timebase resync if we are waking up from sleep. Use cr1 value
+ * set in exceptions-64s.S */
+ ble cr1,clear_lock
+
+timebase_resync:
/* Time base re-sync */
- li r3,OPAL_RESYNC_TIMEBASE
+ li r0,OPAL_RESYNC_TIMEBASE
bl opal_call_realmode;
-
/* TODO: Check r3 for failure */
+clear_lock:
+ andi. r15,r15,PNV_CORE_IDLE_THREAD_BITS
+ stw r15,0(r14)
+
+common_exit:
+ li r5,PNV_THREAD_RUNNING
+ stb r5,PACA_THREAD_IDLE_STATE(r13)
+
+#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
+ li r0,KVM_HWTHREAD_IN_KERNEL
+ stb r0,HSTATE_HWTHREAD_STATE(r13)
+ /* Order setting hwthread_state vs. testing hwthread_req */
+ sync
+ lbz r0,HSTATE_HWTHREAD_REQ(r13)
+ cmpwi r0,0
+ beq 6f
+ b kvm_start_guest
+6:
+#endif
+
REST_NVGPRS(r1)
REST_GPR(2, r1)
ld r3,_CCR(r1)
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index feb549a..b2aa93b 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -158,6 +158,43 @@ opal_tracepoint_return:
blr
#endif
+/*
+ * Make opal call in realmode. This is a generic function to be called
+ * from realmode. It handles endianness.
+ *
+ * r13 - paca pointer
+ * r1 - stack pointer
+ * r0 - opal token
+ */
+_GLOBAL(opal_call_realmode)
+ mflr r12
+ std r12,_LINK(r1)
+ ld r2,PACATOC(r13)
+ /* Set opal return address */
+ LOAD_REG_ADDR(r12,return_from_opal_call)
+ mtlr r12
+
+ mfmsr r12
+#ifdef __LITTLE_ENDIAN__
+ /* Handle endian-ness */
+ li r11,MSR_LE
+ andc r12,r12,r11
+#endif
+ mtspr SPRN_HSRR1,r12
+ LOAD_REG_ADDR(r11,opal)
+ ld r12,8(r11)
+ ld r2,0(r11)
+ mtspr SPRN_HSRR0,r12
+ hrfid
+
+return_from_opal_call:
+#ifdef __LITTLE_ENDIAN__
+ FIXUP_ENDIAN
+#endif
+ ld r12,_LINK(r1)
+ mtlr r12
+ blr
+
OPAL_CALL(opal_invalid_call, OPAL_INVALID_CALL);
OPAL_CALL(opal_console_write, OPAL_CONSOLE_WRITE);
OPAL_CALL(opal_console_read, OPAL_CONSOLE_READ);
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 34c6665..17fb98c 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -36,6 +36,8 @@
#include <asm/opal.h>
#include <asm/kexec.h>
#include <asm/smp.h>
+#include <asm/cputhreads.h>
+#include <asm/cpuidle.h>
#include "powernv.h"
@@ -292,11 +294,45 @@ static void __init pnv_setup_machdep_rtas(void)
static u32 supported_cpuidle_states;
+static void pnv_alloc_idle_core_states(void)
+{
+ int i, j;
+ int nr_cores = cpu_nr_cores();
+ u32 *core_idle_state;
+
+ /*
+ * core_idle_state - First 8 bits track the idle state of each thread
+ * of the core. The 8th bit is the lock bit. Initially all thread bits
+ * are set. They are cleared when the thread enters deep idle state
+ * like sleep and winkle. Initially the lock bit is cleared.
+ * The lock bit has 2 purposes
+ * a. While the first thread is restoring core state, it prevents
+ * from other threads in the core from switching to prcoess context.
+ * b. While the last thread in the core is saving the core state, it
+ * prevent a different thread from waking up.
+ */
+ for (i = 0; i < nr_cores; i++) {
+ int first_cpu = i * threads_per_core;
+ int node = cpu_to_node(first_cpu);
+
+ core_idle_state = kmalloc_node(sizeof(u32), GFP_KERNEL, node);
+ for (j = 0; j < threads_per_core; j++) {
+ int cpu = first_cpu + j;
+
+ paca[cpu].core_idle_state_ptr = core_idle_state;
+ paca[cpu].thread_idle_state = PNV_THREAD_RUNNING;
+
+ }
+ }
+}
+
u32 pnv_get_supported_cpuidle_states(void)
{
return supported_cpuidle_states;
}
+EXPORT_SYMBOL_GPL(pnv_get_supported_cpuidle_states);
+u8 pnv_need_fastsleep_workaround;
static int __init pnv_init_idle_states(void)
{
struct device_node *power_mgt;
@@ -306,6 +342,7 @@ static int __init pnv_init_idle_states(void)
int i;
supported_cpuidle_states = 0;
+ pnv_need_fastsleep_workaround = 0;
if (cpuidle_disable != IDLE_NO_OVERRIDE)
return 0;
@@ -332,13 +369,14 @@ static int __init pnv_init_idle_states(void)
flags = be32_to_cpu(idle_state_flags[i]);
supported_cpuidle_states |= flags;
}
-
+ if (supported_cpuidle_states & OPAL_PM_SLEEP_ENABLED_ER1)
+ pnv_need_fastsleep_workaround = 1;
+ pnv_alloc_idle_core_states();
return 0;
}
subsys_initcall(pnv_init_idle_states);
-
static int __init pnv_probe(void)
{
unsigned long root = of_get_flat_dt_root();
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index 3dc4cec..12b761a 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -167,7 +167,8 @@ static void pnv_smp_cpu_kill_self(void)
mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
while (!generic_check_cpu_restart(cpu)) {
ppc64_runlatch_off();
- if (idle_states & OPAL_PM_SLEEP_ENABLED)
+ if ((idle_states & OPAL_PM_SLEEP_ENABLED) ||
+ (idle_states & OPAL_PM_SLEEP_ENABLED_ER1))
power7_sleep();
else
power7_nap(1);
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
index 0a7d827..a489b56 100644
--- a/drivers/cpuidle/cpuidle-powernv.c
+++ b/drivers/cpuidle/cpuidle-powernv.c
@@ -208,7 +208,8 @@ static int powernv_add_idle_states(void)
nr_idle_states++;
}
- if (flags & OPAL_PM_SLEEP_ENABLED) {
+ if (flags & OPAL_PM_SLEEP_ENABLED ||
+ flags & OPAL_PM_SLEEP_ENABLED_ER1) {
/* Add FASTSLEEP state */
strcpy(powernv_states[nr_idle_states].name, "FastSleep");
strcpy(powernv_states[nr_idle_states].desc, "FastSleep");
--
1.9.3
^ permalink raw reply related
* [RFC 1/2] powerpc/fsl-pci: atomic get_user when pagefault_disabled
From: David Hildenbrand @ 2014-11-25 11:43 UTC (permalink / raw)
To: linuxppc-dev, linux-arch, linux-kernel
Cc: borntraeger, heiko.carstens, dahi, paulus, schwidefsky, akpm
In-Reply-To: <1416915806-24757-1-git-send-email-dahi@linux.vnet.ibm.com>
Whenever we have pagefaults disabled, we have to use the atomic variants of
(set|get)_user and copy_(from|to)_user.
Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
---
arch/powerpc/sysdev/fsl_pci.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 65d2ed4..c0af4ef 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -1025,7 +1025,7 @@ int fsl_pci_mcheck_exception(struct pt_regs *regs)
if (is_in_pci_mem_space(addr)) {
if (user_mode(regs)) {
pagefault_disable();
- ret = get_user(regs->nip, &inst);
+ ret = __get_user_inatomic(regs->nip, &inst);
pagefault_enable();
} else {
ret = probe_kernel_address(regs->nip, inst);
--
1.8.5.5
^ permalink raw reply related
* [RFC 0/2] Reenable might_sleep() checks for might_fault() when atomic
From: David Hildenbrand @ 2014-11-25 11:43 UTC (permalink / raw)
To: linuxppc-dev, linux-arch, linux-kernel
Cc: borntraeger, heiko.carstens, dahi, paulus, schwidefsky, akpm
I recently discovered that commit 662bbcb2747c2422cf98d3d97619509379eee466
removed/skipped all might_sleep checks for might_fault() calls when in atomic.
Reason was to allow calling copy_(to|from)_user while in pagefault_disabled(),
because otherwise, CONFIG_DEBUG_ATOMIC_SLEEP would find false positives. However
we have the inatomic variants of these function for this purpose.
The result of this change was that all guest access (that correctly uses
might_fault()) doesn't perform atomic checks when CONFIG_DEBUG_ATOMIC_SLEEP is
enabled. We lost a mighty debugging feature for user access.
As I wasn't able to come up with any other reason why this should be
necessary, I suggest turning the might_sleep() checks on again in the
might_fault() code.
pagefault_disable/pagefault_enable seems to be used mainly for futex, perf event
and kmap.
Going over all changes since that commit, it seems like most code already uses the
inatomic variants of copy_(to|from)_user. Code relying on (get|put)_user during
pagefault_disable() don't make use of any might_fault() in their (get|put)_user
implementation. Examples:
- arch/m68k/include/asm/futex.h
- arch/parisc/include/asm/futex.h
- arch/sh/include/asm/futex-irq.h
- arch/tile/include/asm/futex.h
So changing might_fault() back to trigger might_sleep() won't change a thing for
them. Hope I haven't missed anything.
I only identified one might_fault() that would get triggered by get_user() on
powerpc and fixed it by using the inatomic variant (not tested). I am not sure
if we need some non-sleeping access_ok() prior to __get_user_inatomic().
By looking at the code I was wondering where the correct place for might_fault()
calls is? Doesn't seem to be very consistent. Examples:
| asm-generic | arm | arm64 | frv | m32r | x86 and s390
---------------------------------------------------------------------------
get_user() | Yes | Yes | Yes | No | Yes | Yes
__get_user() | No | Yes | No | No | Yes | No
put_user() | Yes | Yes | Yes | No | Yes | Yes
__put_user() | No | Yes | No | No | Yes | No
copy_to_user() | Yes | No | No | Yes | Yes | Yes
__copy_to_user() | No | No | No | Yes | No | No
copy_from_user() | Yes | No | No | Yes | Yes | Yes
__copy_from_user() | No | No | No | Yes | No | No
So I would have assume that the way asm-generic, x86 and s390 (+ propably
others) do this is the right way?
So we can speed up multiple calls to e.g. copy_to_user() by doing the access
check manually (and also the might_fault() if relevant), then calling
__copy_to_user().
So in general, I conclude that the concept is:
1. __.* variants perform no checking and don't call might_fault()
2. [a-z].* variants perform access checking (access_ok() if implemented)
3. [a-z].* variants call might_fault()
4. .*_inatomic variants usually don't perform access checks
5. .*_inatomic variants don't call might_fault()
6. If common code uses the __.* variants, it has to trigger access_ok() and
call might_fault()
7. For pagefault_disable(), the inatomic variants are to be used
Comments? Opinions?
David Hildenbrand (2):
powerpc/fsl-pci: atomic get_user when pagefault_disabled
mm, sched: trigger might_sleep() in might_fault() when atomic
arch/powerpc/sysdev/fsl_pci.c | 2 +-
include/linux/kernel.h | 8 ++++++--
mm/memory.c | 11 ++++-------
3 files changed, 11 insertions(+), 10 deletions(-)
--
1.8.5.5
^ permalink raw reply
* [RFC 2/2] mm, sched: trigger might_sleep() in might_fault() when atomic
From: David Hildenbrand @ 2014-11-25 11:43 UTC (permalink / raw)
To: linuxppc-dev, linux-arch, linux-kernel
Cc: borntraeger, heiko.carstens, dahi, paulus, schwidefsky, akpm
In-Reply-To: <1416915806-24757-1-git-send-email-dahi@linux.vnet.ibm.com>
Commit 662bbcb2747c2422cf98d3d97619509379eee466 disabled in atomic checks
for all user access code (that uses might_fault()).
That change basically disabled CONFIG_DEBUG_ATOMIC_SLEEP for all user access
functions. However, this is a mighty debugging aid that we want.
If user memory is to be accessed while pagefault_disabled() is set, the atomic
variants of copy_(to|from)_user can be used.
This patch reverts commit 662bbcb2747c2422cf98d3d97619509379eee466 taking care
of the !MMU optimization.
Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
---
include/linux/kernel.h | 8 ++++++--
mm/memory.c | 11 ++++-------
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 3d770f55..1d3397c 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -225,9 +225,13 @@ static inline u32 reciprocal_scale(u32 val, u32 ep_ro)
return (u32)(((u64) val * ep_ro) >> 32);
}
-#if defined(CONFIG_MMU) && \
- (defined(CONFIG_PROVE_LOCKING) || defined(CONFIG_DEBUG_ATOMIC_SLEEP))
+#if defined(CONFIG_MMU) && defined(CONFIG_PROVE_LOCKING)
void might_fault(void);
+#elif defined(CONFIG_MMU) && defined(CONFIG_DEBUG_ATOMIC_SLEEP)
+static inline void might_fault(void)
+{
+ __might_sleep(__FILE__, __LINE__, 0);
+}
#else
static inline void might_fault(void) { }
#endif
diff --git a/mm/memory.c b/mm/memory.c
index 3e50383..fe0c815 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3699,7 +3699,7 @@ void print_vma_addr(char *prefix, unsigned long ip)
up_read(&mm->mmap_sem);
}
-#if defined(CONFIG_PROVE_LOCKING) || defined(CONFIG_DEBUG_ATOMIC_SLEEP)
+#ifdef CONFIG_PROVE_LOCKING
void might_fault(void)
{
/*
@@ -3711,17 +3711,14 @@ void might_fault(void)
if (segment_eq(get_fs(), KERNEL_DS))
return;
+ __might_sleep(__FILE__, __LINE__, 0);
+
/*
* it would be nicer only to annotate paths which are not under
* pagefault_disable, however that requires a larger audit and
* providing helpers like get_user_atomic.
*/
- if (in_atomic())
- return;
-
- __might_sleep(__FILE__, __LINE__, 0);
-
- if (current->mm)
+ if (!in_atomic() && current->mm)
might_lock_read(¤t->mm->mmap_sem);
}
EXPORT_SYMBOL(might_fault);
--
1.8.5.5
^ permalink raw reply related
* Re: [PATCH 0/2] ASoC: fsl: Use dynamic slot width for ESAI.
From: Mark Brown @ 2014-11-25 12:47 UTC (permalink / raw)
To: Nicolin Chen
Cc: alsa-devel, lgirdwood, b02247, linux-kernel, timur, Li.Xiubo,
linuxppc-dev
In-Reply-To: <cover.1414193668.git.nicoleotsuka@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 340 bytes --]
On Fri, Oct 24, 2014 at 04:48:10PM -0700, Nicolin Chen wrote:
> This series of patches add the support of dynamic slot width for
> ESAI driver. To do this, the PATCH-1 removes the fixed width. In
> order not to break those platforms that use fixed width, there
> comes the PATCH-2 to apply an override of slot_width.
Applied both, thanks.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply
* [PATCH V3] powerpc, powernv: Add OPAL platform event driver
From: Anshuman Khandual @ 2014-11-25 13:21 UTC (permalink / raw)
To: linuxppc-dev; +Cc: mikey
This patch creates a new OPAL platform event character driver
which will give userspace clients the access to these events
and process them effectively. Following platforms events are
currently supported with this platform driver.
(1) Environmental and Power Warning (EPOW)
(2) Delayed Power Off (DPO)
The user interface for this driver is /dev/opal_event character
device file where the user space clients can poll and read for
new opal platform events. The expected sequence of events driven
from user space should be like the following.
(1) Open the character device file
(2) Poll on the file for POLLIN event
(3) When unblocked, must attempt to read PLAT_EVENT_MAX_SIZE size
(4) Kernel driver will pass at most one opal_plat_event structure
(5) Poll again for more new events
The driver registers for OPAL messages notifications corresponding to
individual OPAL events. When any of those event messages arrive in the
kernel, the callbacks are called to process them which in turn unblocks
the polling thread on the character device file. The driver also registers
a timer function which will be called after a threshold amount of time to
shutdown the system. The user space client receives the timeout value for
all individual OPAL platform events and hence must prepare the system and
eventually shutdown. In case the user client does not shutdown the system,
the timer function will be called after the threshold and shutdown the
system explicitly.
Reviewed-by: Neelesh Gupta <neelegup@linux.vnet.ibm.com>
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
Changes in V3:
- Rebased the patch against the mainline
Changes in V2:
- Changed the function fetch_dpo_timeout
- Export opal_platform_events.h for user space consumption
- Posted here https://patchwork.ozlabs.org/patch/396725/
Original V1:
- Original patch
- Posted here http://patchwork.ozlabs.org/patch/394340/
arch/powerpc/include/asm/opal.h | 45 +-
arch/powerpc/include/uapi/asm/Kbuild | 1 +
.../include/uapi/asm/opal_platform_events.h | 90 +++
arch/powerpc/platforms/powernv/Makefile | 2 +-
.../platforms/powernv/opal-platform-events.c | 736 +++++++++++++++++++++
arch/powerpc/platforms/powernv/opal-wrappers.S | 1 +
arch/powerpc/platforms/powernv/opal.c | 8 +-
7 files changed, 880 insertions(+), 3 deletions(-)
create mode 100644 arch/powerpc/include/uapi/asm/opal_platform_events.h
create mode 100644 arch/powerpc/platforms/powernv/opal-platform-events.c
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 9124b0e..af51778 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -154,6 +154,7 @@ struct opal_sg_list {
#define OPAL_HANDLE_HMI 98
#define OPAL_REGISTER_DUMP_REGION 101
#define OPAL_UNREGISTER_DUMP_REGION 102
+#define OPAL_GET_DPO_STATUS 105
#ifndef __ASSEMBLY__
@@ -281,6 +282,7 @@ enum OpalMessageType {
OPAL_MSG_EPOW,
OPAL_MSG_SHUTDOWN,
OPAL_MSG_HMI_EVT,
+ OPAL_MSG_DPO,
OPAL_MSG_TYPE_MAX,
};
@@ -452,6 +454,46 @@ struct opal_msg {
__be64 params[8];
};
+/*
+ * EPOW status sharing (OPAL and the host)
+ *
+ * The host will pass on OPAL, a buffer of length OPAL_SYSEPOW_MAX
+ * with individual elements being 16 bits wide to fetch the system
+ * wide EPOW status. Each element in the buffer will contain the
+ * EPOW status in it's bit representation for a particular EPOW sub
+ * class as defiend here. So multiple detailed EPOW status bits
+ * specific for any sub class can be represented in a single buffer
+ * element as it's bit representation.
+ */
+
+/* System EPOW type */
+enum OpalSysEpow {
+ OPAL_SYSEPOW_POWER = 0, /* Power EPOW */
+ OPAL_SYSEPOW_TEMP = 1, /* Temperature EPOW */
+ OPAL_SYSEPOW_COOLING = 2, /* Cooling EPOW */
+ OPAL_SYSEPOW_MAX = 3, /* Max EPOW categories */
+};
+
+/* Power EPOW */
+enum OpalSysPower {
+ OPAL_SYSPOWER_UPS = 0x0001, /* System on UPS power */
+ OPAL_SYSPOWER_CHNG = 0x0002, /* System power config change */
+ OPAL_SYSPOWER_FAIL = 0x0004, /* System impending power failure */
+ OPAL_SYSPOWER_INCL = 0x0008, /* System incomplete power */
+};
+
+/* Temperature EPOW */
+enum OpalSysTemp {
+ OPAL_SYSTEMP_AMB = 0x0001, /* System over ambient temperature */
+ OPAL_SYSTEMP_INT = 0x0002, /* System over internal temperature */
+ OPAL_SYSTEMP_HMD = 0x0004, /* System over ambient humidity */
+};
+
+/* Cooling EPOW */
+enum OpalSysCooling {
+ OPAL_SYSCOOL_INSF = 0x0001, /* System insufficient cooling */
+};
+
struct opal_machine_check_event {
enum OpalMCE_Version version:8; /* 0x00 */
uint8_t in_use; /* 0x01 */
@@ -918,7 +960,7 @@ int64_t opal_pci_fence_phb(uint64_t phb_id);
int64_t opal_pci_reinit(uint64_t phb_id, uint64_t reinit_scope, uint64_t data);
int64_t opal_pci_mask_pe_error(uint64_t phb_id, uint16_t pe_number, uint8_t error_type, uint8_t mask_action);
int64_t opal_set_slot_led_status(uint64_t phb_id, uint64_t slot_id, uint8_t led_type, uint8_t led_action);
-int64_t opal_get_epow_status(__be64 *status);
+int64_t opal_get_epow_status(uint16_t *status, uint16_t *length);
int64_t opal_set_system_attention_led(uint8_t led_action);
int64_t opal_pci_next_error(uint64_t phb_id, __be64 *first_frozen_pe,
__be16 *pci_error_type, __be16 *severity);
@@ -963,6 +1005,7 @@ int64_t opal_handle_hmi(void);
int64_t opal_register_dump_region(uint32_t id, uint64_t start, uint64_t end);
int64_t opal_unregister_dump_region(uint32_t id);
int64_t opal_pci_set_phb_cxl_mode(uint64_t phb_id, uint64_t mode, uint64_t pe_number);
+int64_t opal_get_dpo_status(int64_t *dpo_timeout);
/* Internal functions */
extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
diff --git a/arch/powerpc/include/uapi/asm/Kbuild b/arch/powerpc/include/uapi/asm/Kbuild
index 7a3f795..d20970b 100644
--- a/arch/powerpc/include/uapi/asm/Kbuild
+++ b/arch/powerpc/include/uapi/asm/Kbuild
@@ -44,3 +44,4 @@ header-y += tm.h
header-y += types.h
header-y += ucontext.h
header-y += unistd.h
+header-y += opal_platform_events.h
diff --git a/arch/powerpc/include/uapi/asm/opal_platform_events.h b/arch/powerpc/include/uapi/asm/opal_platform_events.h
new file mode 100644
index 0000000..6d2e13e
--- /dev/null
+++ b/arch/powerpc/include/uapi/asm/opal_platform_events.h
@@ -0,0 +1,90 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2014
+ *
+ * Author: Anshuman Khandual <khandual@linux.vnet.ibm.com>
+ */
+#ifndef __LINUX_OPAL_PLATFORM_EVENTS_H
+#define __LINUX_OPAL_PLATFORM_EVENTS_H
+
+#include <linux/types.h>
+
+/* EPOW classification */
+enum epow_condition {
+ EPOW_SYSPOWER_CHNG = 0, /* Power */
+ EPOW_SYSPOWER_FAIL = 1,
+ EPOW_SYSPOWER_INCL = 2,
+ EPOW_SYSPOWER_UPS = 3,
+ EPOW_SYSTEMP_AMB = 4, /* Temperature */
+ EPOW_SYSTEMP_INT = 5,
+ EPOW_SYSTEMP_HMD = 6,
+ EPOW_SYSCOOL_INSF = 7, /* Cooling */
+ EPOW_MAX = 8,
+};
+
+/* OPAL EPOW event */
+struct epow_event {
+ __u64 epow[EPOW_MAX]; /* Detailed system EPOW status */
+ __u64 timeout; /* Timeout to shutdown in secs */
+};
+
+/* OPAL DPO event */
+struct dpo_event {
+ __u64 orig_timeout; /* Platform provided timeout in secs */
+ __u64 remain_timeout; /* Timeout to shutdown in secs */
+};
+
+/* OPAL event */
+struct opal_plat_event {
+ __u32 type; /* Type of OPAL platform event */
+#define OPAL_PLAT_EVENT_TYPE_EPOW 0
+#define OPAL_PLAT_EVENT_TYPE_DPO 1
+#define OPAL_PLAT_EVENT_TYPE_MAX 2
+ __u32 size; /* Size of OPAL platform event */
+ union {
+ struct epow_event epow; /* EPOW platform event */
+ struct dpo_event dpo; /* DPO platform event */
+ };
+};
+
+/*
+ * Suggested read size
+ *
+ * The user space client should attempt to read OPAL_PLAT_EVENT_READ_SIZE
+ * amount of data from the character device file '/dev/opal_event' at any
+ * point of time. The kernel driver will pass an entire opal_plat_event
+ * structure in every read. This ensures that minium data the user space
+ * client gets from the kernel is one opal_plat_event structure.
+ */
+#define PLAT_EVENT_MAX_SIZE 4096
+
+/*
+ * Suggested user operation
+ *
+ * The user space client must follow these steps in order to be able to
+ * exploit the features exported through the OPAL platform event driver.
+ *
+ * (1) Open the character device file
+ * (2) Poll on the file for POLLIN
+ * (3) When unblocked, must attempt to read PLAT_EVENT_MAX_SIZE size
+ * (4) Kernel driver will pass one opal_plat_event structure
+ * (5) Poll again for more new events
+ *
+ * The character device file (/dev/opal_event) must be opened and operated by
+ * only one user space client at any point of time. Other attempts to open the
+ * file will be returned by the driver as EBUSY.
+ */
+
+#endif /* __LINUX_OPAL_PLATFORM_EVENTS_H */
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index f241acc..c4b3b9b 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,7 +1,7 @@
obj-y += setup.o opal-wrappers.o opal.o opal-async.o
obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
-obj-y += opal-msglog.o opal-hmi.o
+obj-y += opal-msglog.o opal-hmi.o opal-platform-events.o
obj-$(CONFIG_SMP) += smp.o subcore.o subcore-asm.o
obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/opal-platform-events.c b/arch/powerpc/platforms/powernv/opal-platform-events.c
new file mode 100644
index 0000000..cea2a378e
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-platform-events.c
@@ -0,0 +1,736 @@
+/*
+ * IBM PowerNV OPAL platform events driver
+ *
+ * Copyright IBM Corporation 2014
+ *
+ * Author: Anshuman Khandual <khandual@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.
+ *
+ * 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
+ */
+#define PREFIX "OPAL_EVENT"
+#define pr_fmt(fmt) PREFIX ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/timer.h>
+#include <linux/reboot.h>
+#include <asm/uaccess.h>
+#include <asm/opal.h>
+#include <asm/opal_platform_events.h>
+
+/* Platform events driver */
+static dev_t opal_event_dev;
+static struct cdev opal_event_cdev;
+static struct class *opal_event_class;
+static bool opal_event_open_flag;
+
+#define OPAL_EVENT_MAX_DEVS 1
+
+/* Platform events timers */
+static struct timer_list opal_event_timer;
+
+static DECLARE_WAIT_QUEUE_HEAD(opal_plat_evt_wait);
+static DECLARE_WAIT_QUEUE_HEAD(opal_plat_open_wait);
+static DEFINE_SPINLOCK(opal_plat_evt_spinlock);
+static DEFINE_SPINLOCK(opal_plat_timer_spinlock);
+static DEFINE_MUTEX(opal_plat_evt_mutex);
+
+/*
+ * Platform timeout values
+ *
+ * XXX: The default timeout value is 5 minutes. In future this
+ * should be communicated from the platform firmware through
+ * device tree attributes.
+ */
+#define OPAL_EPOW_TIMEOUT 300
+
+struct opal_platform_evt {
+ struct opal_plat_event opal_event;
+ struct list_head link;
+};
+static LIST_HEAD(opal_event_queue);
+static unsigned long opal_dpo_target;
+static bool opal_event_probe_finished;
+
+/*
+ * OPAL event map
+ *
+ * Converts OPAL event type into it's description.
+ */
+static const char *opal_event_map[OPAL_PLAT_EVENT_TYPE_MAX] = {
+ "OPAL_PLAT_EVENT_TYPE_EPOW", "OPAL_PLAT_EVENT_TYPE_DPO"
+};
+
+/*
+ * opal_event_timeout
+ *
+ * This is the actual timer handler. If the any of the timers
+ * expire, this function will be called to shutdown the system
+ * gracefully.
+ */
+static void opal_event_timeout(unsigned long data)
+{
+ orderly_poweroff(1);
+}
+
+/*
+ * opal_event_start_timer
+ *
+ * This will start opal event timer with given timeout value as the expiry
+ * if either the timer is not active or the expiry value of the already
+ * activated timer is at a later point of time in the future compared to the
+ * timeout value for this given new event. The function mod_timer takes care
+ * all the cases whether the opal event timer is already active or not.
+ */
+static void opal_event_start_timer(unsigned long event, u64 timeout)
+{
+ unsigned long flags;
+
+ /* Timer active with earlier timeout */
+ spin_lock_irqsave(&opal_plat_timer_spinlock, flags);
+ if (timer_pending(&opal_event_timer) &&
+ (opal_event_timer.expires < (jiffies + timeout * HZ))) {
+ spin_unlock_irqrestore(&opal_plat_timer_spinlock, flags);
+ pr_info("Timer for %s event active with an earlier timeout\n",
+ opal_event_map[opal_event_timer.data]);
+ return;
+ }
+ opal_event_timer.data = event;
+ mod_timer(&opal_event_timer, jiffies + timeout * HZ);
+ spin_unlock_irqrestore(&opal_plat_timer_spinlock, flags);
+ pr_info("Timer activated for %s event\n", opal_event_map[event]);
+}
+
+/*
+ * opal_event_stop_timer
+ *
+ * This will attempt to stop opal_event_timer if it is already enabled.
+ */
+static void opal_event_stop_timer(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&opal_plat_timer_spinlock, flags);
+ del_timer_sync(&opal_event_timer);
+ spin_unlock_irqrestore(&opal_plat_timer_spinlock, flags);
+ pr_info("Timer deactivated\n");
+}
+
+/*
+ * opal_event_read
+ *
+ * User client needs to attempt to read PLAT_EVENT_MAX_SIZE amount of data
+ * from the file descriptor at a time. The driver will pass a single node
+ * from the list if available at a time and then delete the node from the list.
+ */
+static ssize_t opal_event_read(struct file *filep,
+ char __user *buf, size_t len, loff_t *off)
+{
+ struct opal_platform_evt *evt;
+ unsigned long flags;
+
+ if (len < sizeof(struct opal_plat_event))
+ return -EINVAL;
+
+ /* Fetch the first node on the list */
+ spin_lock_irqsave(&opal_plat_evt_spinlock, flags);
+ if (list_empty(&opal_event_queue)) {
+ spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+ return 0;
+ }
+
+ /* Fetch and delete from the list */
+ evt = list_first_entry(&opal_event_queue,
+ struct opal_platform_evt, link);
+ list_del(&evt->link);
+
+ /*
+ * Update the remaining timeout for DPO event.
+ * This can only be updated during the read time.
+ */
+ if (evt->opal_event.type == OPAL_PLAT_EVENT_TYPE_DPO) {
+ unsigned long timeout;
+
+ if (opal_dpo_target &&
+ evt->opal_event.dpo.orig_timeout) {
+ timeout = (opal_dpo_target - jiffies) / HZ;
+ evt->opal_event.dpo.remain_timeout = timeout;
+ }
+ }
+ spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+
+ if (copy_to_user(buf, &evt->opal_event,
+ sizeof(struct opal_plat_event))) {
+
+ /*
+ * Copy to user has failed. The event node had
+ * been deleted from the list. Lets add it back
+ * there.
+ */
+ spin_lock_irqsave(&opal_plat_evt_spinlock, flags);
+ list_add_tail(&evt->link, &opal_event_queue);
+ spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+ return -EFAULT;
+ }
+
+ kfree(evt);
+ return sizeof(struct opal_plat_event);
+}
+
+/*
+ * opal_event_poll
+ *
+ * Poll is unblocked right away with POLLIN when data is available.
+ * When data is not available, the process will have to block till
+ * it gets waked up and data is available to read.
+ */
+static unsigned int opal_event_poll(struct file *file, poll_table *wait)
+{
+ poll_wait(file, &opal_plat_evt_wait, wait);
+ if (!list_empty(&opal_event_queue))
+ return POLLIN;
+ return 0;
+}
+
+/*
+ * opal_event_open
+ *
+ * This makes sure that only one process can open the
+ * character device file at any point of time. Others
+ * attempting to open the file descriptor will either
+ * get EBUSY (with O_NONBLOCK flag) or wait for the
+ * other process to close the file descriptor.
+ */
+static int opal_event_open(struct inode *inode, struct file *file)
+{
+ int err;
+
+ mutex_lock(&opal_plat_evt_mutex);
+ while (opal_event_open_flag) {
+ mutex_unlock(&opal_plat_evt_mutex);
+ if (file->f_flags & O_NONBLOCK)
+ return -EBUSY;
+ err = wait_event_interruptible(opal_plat_open_wait,
+ !opal_event_open_flag);
+ if (err)
+ return -ERESTARTSYS;
+ mutex_lock(&opal_plat_evt_mutex);
+ }
+ opal_event_open_flag = true;
+ mutex_unlock(&opal_plat_evt_mutex);
+ return 0;
+}
+
+/*
+ * opal_event_release
+ *
+ * Releases the file descriptor for the device file.
+ */
+static int opal_event_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&opal_plat_evt_mutex);
+ if (opal_event_open_flag) {
+ opal_event_open_flag = false;
+ wake_up_interruptible(&opal_plat_open_wait);
+ }
+ mutex_unlock(&opal_plat_evt_mutex);
+ return 0;
+}
+
+/* Defined file operation */
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = opal_event_open,
+ .release = opal_event_release,
+ .read = opal_event_read,
+ .poll = opal_event_poll,
+};
+
+/* Process the received EPOW information */
+void process_epow(__u64 *epow, int16_t *epow_status, int max_epow_class)
+{
+ /*
+ * Platform might have returned less number of EPOW
+ * subclass status than asked for. This situation
+ * happens when the platform firmware is older compared
+ * to the kernel.
+ */
+
+ if (!max_epow_class) {
+ pr_warn("EPOW: OPAL_SYSEPOW_POWER subclass not present\n");
+ return;
+ }
+
+ /* Power */
+ max_epow_class--;
+ if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_CHNG) {
+ pr_info("EPOW: Power configuration changed\n");
+ epow[EPOW_SYSPOWER_CHNG] = 1;
+ }
+
+ if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_FAIL) {
+ pr_info("EPOW: Impending system power failure\n");
+ epow[EPOW_SYSPOWER_FAIL] = 1;
+ }
+
+ if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_INCL) {
+ pr_info("EPOW: Incomplete system power\n");
+ epow[EPOW_SYSPOWER_INCL] = 1;
+ }
+
+ if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_UPS) {
+ pr_info("EPOW: System on UPS power\n");
+ epow[EPOW_SYSPOWER_UPS] = 1;
+ }
+
+ if (!max_epow_class) {
+ pr_warn("EPOW: OPAL_SYSEPOW_TEMP subclass not present\n");
+ return;
+ }
+
+ /* Temperature */
+ max_epow_class--;
+ if (epow_status[OPAL_SYSEPOW_TEMP] & OPAL_SYSTEMP_AMB) {
+ pr_info("EPOW: Over ambient temperature\n");
+ epow[EPOW_SYSTEMP_AMB] = 1;
+ }
+
+ if (epow_status[OPAL_SYSEPOW_TEMP] & OPAL_SYSTEMP_INT) {
+ pr_info("EPOW: Over internal temperature\n");
+ epow[EPOW_SYSTEMP_INT] = 1;
+ }
+
+ if (epow_status[OPAL_SYSEPOW_TEMP] & OPAL_SYSTEMP_HMD) {
+ pr_info("EPOW: Over internal humidity\n");
+ epow[EPOW_SYSTEMP_HMD] = 1;
+ }
+
+ if (!max_epow_class) {
+ pr_warn("EPOW: OPAL_SYSEPOW_COOLING subclass not present\n");
+ return;
+ }
+
+ /* Cooling */
+ max_epow_class--;
+ if (epow_status[OPAL_SYSEPOW_COOLING] & OPAL_SYSCOOL_INSF) {
+ pr_info("EPOW: Insufficient cooling\n");
+ epow[EPOW_SYSCOOL_INSF] = 1;
+ }
+}
+
+/*
+ * fetch_epow_status
+ *
+ * Fetch the system EPOW status through an OPAL call and
+ * validate the number of EPOW sub class status received.
+ */
+static void fetch_epow_status(int16_t *epow_status, int16_t *n_epow)
+{
+ int rc;
+
+ memset(epow_status, 0, sizeof(int16_t) * OPAL_SYSEPOW_MAX);
+ *n_epow = OPAL_SYSEPOW_MAX;
+ rc = opal_get_epow_status(epow_status, n_epow);
+ if (rc != OPAL_SUCCESS) {
+ pr_err("EPOW: OPAL call failed\n");
+ memset(epow_status, 0, sizeof(int16_t) * OPAL_SYSEPOW_MAX);
+ *n_epow = 0;
+ return;
+ }
+ if (!(*n_epow))
+ pr_err("EPOW: No subclass status received\n");
+}
+
+/*
+ * fetch_dpo_timeout
+ *
+ * Fetch the system DPO timeout status through an OPAL call.
+ */
+static void fetch_dpo_timeout(int64_t *dpo_timeout)
+{
+ int rc;
+
+ rc = opal_get_dpo_status(dpo_timeout);
+ if (rc == OPAL_WRONG_STATE) {
+ pr_info("DPO: Not initiated by OPAL\n");
+ *dpo_timeout = 0;
+ }
+}
+
+/*
+ * valid_epow
+ *
+ * Validate the received EPOW event status. This ensures
+ * that there are valid status for various EPOW sub classes
+ * and their individual events.
+ */
+static bool valid_epow(int16_t *epow_status, int16_t n_epow)
+{
+ int i;
+
+ /* EPOW sub classes present */
+ if (!n_epow)
+ return false;
+
+ /* EPOW events present */
+ for (i = 0; i < n_epow; i++) {
+ if (epow_status[i])
+ return true;
+ }
+ return false;
+}
+
+/*
+ * epow_exclude
+ *
+ * XXX: EPOW events on the action exclude list. System shutdown
+ * would not be scheduled for all these platform events. In future
+ * this should be communicated from the platform firmware through
+ * device tree attributes.
+ */
+static bool epow_exclude(int epow_event)
+{
+ switch (epow_event) {
+ case EPOW_SYSPOWER_CHNG:
+ return true;
+ case EPOW_SYSPOWER_INCL:
+ return true;
+ case EPOW_SYSPOWER_UPS:
+ return true;
+ case EPOW_SYSTEMP_HMD:
+ return true;
+ case EPOW_SYSCOOL_INSF:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * actionable_epow
+ *
+ * There are some EPOW events for which the user client must receive
+ * their status but the driver would not schedule a timer for that
+ * event as the platform would not force shutdown the system because
+ * of this event. This filters only the actionable EPOW events for
+ * which shutdown timer need to be scheduled.
+ */
+static bool actionable_epow(__u64 *epow)
+{
+ int i;
+
+ for (i = 0; i < EPOW_MAX; i++) {
+ if (!epow_exclude(i) && epow[i])
+ return true;
+ }
+ return false;
+}
+
+/*
+ * opal_event_handle_basic
+ *
+ * Sets up the basic information for an opal platform event,
+ * activates the timer, adds to the list and wakes up waiting
+ * threads on the character device.
+ */
+static void opal_event_handle_basic(struct opal_platform_evt *evt,
+ unsigned long type, unsigned long timeout)
+{
+ unsigned long flags;
+
+ evt->opal_event.type = type;
+ switch (type) {
+ case OPAL_PLAT_EVENT_TYPE_EPOW:
+ evt->opal_event.size = sizeof(struct epow_event);
+ evt->opal_event.epow.timeout = timeout;
+ if (actionable_epow(evt->opal_event.epow.epow))
+ opal_event_start_timer(OPAL_PLAT_EVENT_TYPE_EPOW,
+ OPAL_EPOW_TIMEOUT);
+ break;
+ case OPAL_PLAT_EVENT_TYPE_DPO:
+ evt->opal_event.size = sizeof(struct dpo_event);
+ evt->opal_event.dpo.orig_timeout = timeout;
+ opal_event_start_timer(OPAL_PLAT_EVENT_TYPE_DPO, timeout);
+ break;
+ default:
+ pr_err("Unknown event type\n");
+ break;
+ }
+ spin_lock_irqsave(&opal_plat_evt_spinlock, flags);
+ list_add_tail(&evt->link, &opal_event_queue);
+ spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+ wake_up_interruptible(&opal_plat_evt_wait);
+}
+
+/*
+ * opal_event_existing_status
+ *
+ * Fetch and process existing opal platform event conditions
+ * present on the system. If events detected, add them to the
+ * list which can be consumed by the user space right away.
+ */
+static void opal_event_existing_status(void)
+{
+ struct opal_platform_evt *evt;
+ int64_t dpo_timeout;
+ int16_t epow_status[OPAL_SYSEPOW_MAX], n_epow;
+
+ fetch_epow_status(epow_status, &n_epow);
+ if (valid_epow(epow_status, n_epow)) {
+ evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+ if (!evt) {
+ pr_err("EPOW: Memory allocation for event failed\n");
+ return;
+ }
+ process_epow(evt->opal_event.epow.epow, epow_status, n_epow);
+ opal_event_handle_basic(evt, OPAL_PLAT_EVENT_TYPE_EPOW,
+ OPAL_EPOW_TIMEOUT);
+ }
+
+ fetch_dpo_timeout(&dpo_timeout);
+ if (dpo_timeout) {
+ evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+ if (!evt) {
+ pr_err("DPO: Memory allocation for event failed\n");
+ return;
+ }
+ opal_dpo_target = jiffies + dpo_timeout * HZ;
+ opal_event_handle_basic(evt, OPAL_PLAT_EVENT_TYPE_DPO,
+ dpo_timeout);
+ }
+}
+
+/* Platform EPOW message received */
+static int opal_epow_event(struct notifier_block *nb,
+ unsigned long msg_type, void *msg)
+{
+ struct opal_platform_evt *evt;
+ int16_t epow_status[OPAL_SYSEPOW_MAX], n_epow;
+
+ if (msg_type != OPAL_MSG_EPOW)
+ return 0;
+
+ fetch_epow_status(epow_status, &n_epow);
+ if (!valid_epow(epow_status, n_epow))
+ return -EINVAL;
+
+ pr_debug("EPOW event: Power(%x) Thermal(%x) Cooling(%x)\n",
+ epow_status[0], epow_status[1], epow_status[2]);
+ evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+ if (!evt) {
+ pr_err("EPOW: Memory allocation for event failed\n");
+ return -ENOMEM;
+ }
+ process_epow(evt->opal_event.epow.epow, epow_status, n_epow);
+ opal_event_handle_basic(evt,
+ OPAL_PLAT_EVENT_TYPE_EPOW, OPAL_EPOW_TIMEOUT);
+ return 0;
+}
+
+/* Platform DPO message received */
+static int opal_dpo_event(struct notifier_block *nb,
+ unsigned long msg_type, void *msg)
+{
+ struct opal_platform_evt *evt;
+ int64_t dpo_timeout;
+
+ if (msg_type != OPAL_MSG_DPO)
+ return 0;
+
+ fetch_dpo_timeout(&dpo_timeout);
+ if (!dpo_timeout)
+ return -EINVAL;
+
+ pr_debug("DPO event: Timeout:%llu\n", dpo_timeout);
+ evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+ if (!evt) {
+ pr_err("DPO: Memory allocation for event failed\n");
+ return -ENOMEM;
+ }
+ opal_dpo_target = jiffies + dpo_timeout * HZ;
+ opal_event_handle_basic(evt, OPAL_PLAT_EVENT_TYPE_DPO, dpo_timeout);
+ return 0;
+}
+
+/* OPAL EPOW event notifier block */
+static struct notifier_block opal_epow_nb = {
+ .notifier_call = opal_epow_event,
+ .next = NULL,
+ .priority = 0,
+};
+
+/* OPAL DPO event notifier block */
+static struct notifier_block opal_dpo_nb = {
+ .notifier_call = opal_dpo_event,
+ .next = NULL,
+ .priority = 0,
+};
+
+/* Platform driver probe */
+static int opal_event_probe(struct platform_device *pdev)
+{
+ struct device *dev;
+ int ret;
+
+ if (opal_event_probe_finished) {
+ pr_err("%s getting called once again\n", __func__);
+ return 0;
+ }
+ opal_event_probe_finished = true;
+
+ init_timer(&opal_event_timer);
+ opal_event_timer.function = opal_event_timeout;
+ opal_event_open_flag = false;
+ opal_dpo_target = 0;
+
+ ret = alloc_chrdev_region(&opal_event_dev, 0,
+ OPAL_EVENT_MAX_DEVS, "opal_event");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "aloc_chrdev_region failed\n");
+ return ret;
+ }
+
+ opal_event_class = class_create(THIS_MODULE, "opal_event");
+ if (IS_ERR(opal_event_class)) {
+ ret = PTR_ERR(opal_event_class);
+ dev_err(&pdev->dev, "class_create failed with %d\n", ret);
+ goto fail_chrdev;
+ }
+
+ dev = device_create(opal_event_class, &pdev->dev,
+ opal_event_dev, NULL, "opal_event");
+ if (IS_ERR(dev)) {
+ ret = PTR_ERR(dev);
+ dev_err(&pdev->dev, "device_create failed with %d\n", ret);
+ goto fail_class;
+ }
+
+ cdev_init(&opal_event_cdev, &fops);
+ ret = cdev_add(&opal_event_cdev, opal_event_dev, OPAL_EVENT_MAX_DEVS);
+ if (ret < 0) {
+ dev_err(dev, "cdev_add failed\n");
+ ret = -EINVAL;
+ goto fail_device;
+ }
+
+ ret = opal_message_notifier_register(OPAL_MSG_EPOW, &opal_epow_nb);
+ if (ret) {
+ pr_err("EPOW: Platform event message notifier failed\n");
+ goto fail_cdev;
+ }
+
+ ret = opal_message_notifier_register(OPAL_MSG_DPO, &opal_dpo_nb);
+ if (ret) {
+ pr_err("DPO: Platform event message notifier failed\n");
+ opal_notifier_unregister(&opal_epow_nb);
+ goto fail_cdev;
+ }
+
+ /*
+ * During the system boot, reboot and kexecs, the host can miss
+ * some of the EPOW or DPO messages sent from OPAL. This ensures
+ * that the current status of EPOW or DPO if any, is fetched and
+ * then updated correctly. The user space needs to first read the
+ * existing system status before entering into the poll/read loop.
+ *
+ */
+ opal_event_existing_status();
+ pr_info("OPAL platform event driver initialized\n");
+ return 0;
+fail_cdev:
+ cdev_del(&opal_event_cdev);
+fail_device:
+ device_destroy(opal_event_class, opal_event_dev);
+fail_class:
+ class_destroy(opal_event_class);
+fail_chrdev:
+ unregister_chrdev_region(opal_event_dev, OPAL_EVENT_MAX_DEVS);
+ return ret;
+}
+
+/* Platform driver remove */
+static int opal_event_remove(struct platform_device *pdev)
+{
+ struct opal_platform_evt *evt;
+
+ /* OPAL notifiers */
+ opal_notifier_unregister(&opal_dpo_nb);
+ opal_notifier_unregister(&opal_epow_nb);
+
+ /* Devices */
+ cdev_del(&opal_event_cdev);
+ device_destroy(opal_event_class, opal_event_dev);
+ class_destroy(opal_event_class);
+ unregister_chrdev_region(opal_event_dev, OPAL_EVENT_MAX_DEVS);
+
+ /* Timers */
+ opal_event_stop_timer();
+
+ /* Flush the list */
+ while (!list_empty(&opal_event_queue)) {
+ evt = list_first_entry(&opal_event_queue,
+ struct opal_platform_evt, link);
+ list_del(&evt->link);
+ kfree(evt);
+ }
+
+ pr_info("OPAL platform event driver exited\n");
+ return 0;
+}
+
+/* Platform driver property match */
+static struct of_device_id opal_event_match[] = {
+ {
+ .compatible = "ibm,opal-v3-epow",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, opal_event_match);
+
+static struct platform_driver opal_event_driver = {
+ .probe = opal_event_probe,
+ .remove = opal_event_remove,
+ .driver = {
+ .name = "opal-platform-event-driver",
+ .owner = THIS_MODULE,
+ .of_match_table = opal_event_match,
+ },
+};
+
+static int __init opal_platform_event_init(void)
+{
+ opal_event_probe_finished = false;
+ return platform_driver_register(&opal_event_driver);
+}
+
+static void __exit opal_platform_event_exit(void)
+{
+ platform_driver_unregister(&opal_event_driver);
+}
+module_init(opal_platform_event_init);
+module_exit(opal_platform_event_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anshuman Khandual <khandual@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("PowerNV OPAL platform events driver");
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index feb549a..458ab91 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -250,3 +250,4 @@ OPAL_CALL(opal_handle_hmi, OPAL_HANDLE_HMI);
OPAL_CALL(opal_register_dump_region, OPAL_REGISTER_DUMP_REGION);
OPAL_CALL(opal_unregister_dump_region, OPAL_UNREGISTER_DUMP_REGION);
OPAL_CALL(opal_pci_set_phb_cxl_mode, OPAL_PCI_SET_PHB_CXL_MODE);
+OPAL_CALL(opal_get_dpo_status, OPAL_GET_DPO_STATUS);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index d019b08..3001c2c 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -646,7 +646,7 @@ static void __init opal_dump_region_init(void)
}
static int __init opal_init(void)
{
- struct device_node *np, *consoles;
+ struct device_node *np, *consoles, *epow;
const __be32 *irqs;
int rc, i, irqlen;
@@ -670,6 +670,12 @@ static int __init opal_init(void)
of_node_put(consoles);
}
+ epow = of_find_node_by_path("/ibm,opal/epow");
+ if (epow) {
+ of_platform_device_create(epow, "opal_event", NULL);
+ of_node_put(epow);
+ }
+
/* Find all OPAL interrupts and request them */
irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
pr_debug("opal: Found %d interrupts reserved for OPAL\n",
--
1.9.3
^ permalink raw reply related
* [PATCH] powerpc: drop useless warning in eeh_init()
From: Greg Kurz @ 2014-11-25 16:10 UTC (permalink / raw)
To: Michael Ellerman; +Cc: linuxppc-dev
This is what we get in dmesg when booting a pseries guest and
the hypervisor doesn't provide EEH support.
[ 0.166655] EEH functionality not supported
[ 0.166778] eeh_init: Failed to call platform init function (-22)
Since both powernv_eeh_init() and pseries_eeh_init() already complain when
hitting an error, it is not needed to print more (especially such an
uninformative message).
Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com>
---
arch/powerpc/kernel/eeh.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 2248a19..7d62018 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -920,11 +920,8 @@ int eeh_init(void)
pr_warn("%s: Platform EEH operation not found\n",
__func__);
return -EEXIST;
- } else if ((ret = eeh_ops->init())) {
- pr_warn("%s: Failed to call platform init function (%d)\n",
- __func__, ret);
+ } else if ((ret = eeh_ops->init()))
return ret;
- }
/* Initialize EEH event */
ret = eeh_event_init();
^ permalink raw reply related
* Re: [V5 7/7] selftests, powerpc: Add test case for TM related ptrace interface
From: Shuah Khan @ 2014-11-25 17:06 UTC (permalink / raw)
To: Anshuman Khandual, linux-kernel, linuxppc-dev
Cc: mikey, james.hogan, avagin, Paul.Clothier, peterz, palves, oleg,
davem, dhowells, kirjanov, davej, akpm, sukadev, tglx,
sam.bobroff
In-Reply-To: <1416902731-22446-8-git-send-email-khandual@linux.vnet.ibm.com>
On 11/25/2014 01:05 AM, Anshuman Khandual wrote:
> This patch adds one more test case called 'tm-ptrace' targeting TM
> related ptrace interface. This test creates one child process to
> run some basic TM transactions and the parent process attaches the
> child to do some ptrace probing using the recently added regset
> interfaces. The parent process then compares the received values
> against the expected values to verify whether it has passed the
> given test or not.
>
> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> ---
> tools/testing/selftests/powerpc/tm/Makefile | 2 +-
> tools/testing/selftests/powerpc/tm/tm-ptrace.c | 542 +++++++++++++++++++++++++
> 2 files changed, 543 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/powerpc/tm/tm-ptrace.c
>
> diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile
> index 2cede23..71d400a 100644
> --- a/tools/testing/selftests/powerpc/tm/Makefile
> +++ b/tools/testing/selftests/powerpc/tm/Makefile
> @@ -1,4 +1,4 @@
> -PROGS := tm-resched-dscr
> +PROGS := tm-resched-dscr tm-ptrace
>
Could you please add .gitignore for the binaries in this directory
to avoid git status including the binaries it in its output.
thanks,
-- Shuah
--
Shuah Khan
Sr. Linux Kernel Developer
Samsung Research America (Silicon Valley)
shuahkh@osg.samsung.com | (970) 217-8978
^ permalink raw reply
* Re: [PATCH v2] i2c: Driver to expose PowerNV platform i2c busses
From: Wolfram Sang @ 2014-11-25 17:14 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Neelesh Gupta, linuxppc-dev, linux-i2c
In-Reply-To: <1416889937.4998.54.camel@kernel.crashing.org>
[-- Attachment #1: Type: text/plain, Size: 2155 bytes --]
On Tue, Nov 25, 2014 at 03:32:17PM +1100, Benjamin Herrenschmidt wrote:
> On Mon, 2014-11-24 at 13:18 +0100, Wolfram Sang wrote:
> >
> > I think there are now 3 drivers in my queue which are not fully I2C
> > compatible but more supporting the very minimum to, say, read an
> > eeprom.
> > I am not feeling well to allow them to use I2C_FUNC_I2C. So, I want to
> > think about ways how to communicate deficiencies like "only 255 byte"
> > or
> > "only WRRD messages" to users of that I2C controller. This is most
> > likely not happening before 3.19. But assistance is very welcome.
>
> There are drivers doing that already, this is afaik common practice.
Well, there are drivers doing it, but to me this is somewhere between a
hack and a workaround. Somehow like using subsys_initcall() to overcome
probe ordering issues. That has never been nice and I still don't have a
solution how to convert those drivers back to module_init() + deferred
probe without causing regressions. As a result, people still want to
copy that behaviour.
> Please don't gate the merging process to some hypothetical rework that
> will imply reworking also a number of user space tools and in-kernel i2c
> device drivers. Basically that would mean "your platform won't be
> supported upstream for the next year and btw, rewrite all userspace".
Call me naive but I don't think we need to rewrite all userspace. That
is what I am at least aiming for.
> By all means let's create new "smbus" APIs for >1 bytes offset but let's
> not make it a gate to merging drivers using the existing way of doing
> things that has been so far perfectly functional.
You say "let's", yet my experience is that once I accept a patch like
yours, people are off with their support leaving me alone with the
topic. Not because they are evil, but because they also have tons of
other stuff to do. I perfectly understand I know this myself. And then,
more and more people will come asking for the same as you and the
situation gets harder and harder to fix.
That all being said, the driver has other issues which I will mention in
a seperate thread.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v2] i2c: Driver to expose PowerNV platform i2c busses
From: Wolfram Sang @ 2014-11-25 17:53 UTC (permalink / raw)
To: Neelesh Gupta; +Cc: linuxppc-dev, linux-i2c
In-Reply-To: <20141116171605.4750.17472.stgit@localhost.localdomain>
[-- Attachment #1: Type: text/plain, Size: 3378 bytes --]
On Sun, Nov 16, 2014 at 10:47:46PM +0530, Neelesh Gupta wrote:
> The patch exposes the available i2c busses on the PowerNV platform
> to the kernel and implements the bus driver to support i2c and
> smbus commands.
> The driver uses the platform device infrastructure to probe the busses
> on the platform and registers them with the i2c driver framework.
>
> Signed-off-by: Neelesh Gupta <neelegup@linux.vnet.ibm.com>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
...
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 917c358..71ad6e1 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -1044,4 +1044,15 @@ config SCx200_ACB
> This support is also available as a module. If so, the module
> will be called scx200_acb.
>
> +config I2C_OPAL
> + tristate "IBM OPAL I2C driver"
> + depends on PPC_POWERNV
> + default y
> + help
> + This exposes the PowerNV platform i2c busses to the linux i2c layer,
> + the driver is based on the OPAL interfaces.
> +
> + This driver can also be built as a module. If so, the module will be
> + called as i2c-opal.
> +
> endmenu
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 78d56c5..350aa86 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -102,5 +102,6 @@ obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
> obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
> obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
> obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
> +obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
Please keep it proprly sorted.
> + rc = of_property_read_u32(pdev->dev.of_node, "ibm,opal-id", &opal_id);
> + if (rc) {
> + dev_err(&pdev->dev, "Missing ibm,opal-id property !\n");
> + return -EIO;
> + }
You introduce new bindings which need to be documented in
Docuemntation/devicetree/bindings/i2c.
They should be posted as a seperate patch with
devicetree@vger.kernel.org CCed, so they can comment on it. This is
required these days and especially important..
> + adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
> + if (!adapter)
> + return -ENOMEM;
> +
> + adapter->algo = &i2c_opal_algo;
> + adapter->algo_data = (void *)(unsigned long)opal_id;
> + adapter->dev.parent = &pdev->dev;
> + adapter->dev.of_node = of_node_get(pdev->dev.of_node);
> + pname = of_get_property(pdev->dev.of_node, "ibm,port-name", NULL);
> + if (pname)
> + strlcpy(adapter->name, pname, sizeof(adapter->name));
> + else
> + strlcpy(adapter->name, "opal", sizeof(adapter->name));
... because I'd like to get an ack from them because of this binding. I
don't know if we can just say "this comes from firmware, so we must
support it" (although you wrote the firmware IIUC) or if we have to
judge if this is a HW description which should go into DT? I am open
meanwhile that the adapter name does not need to be static anymore.
However, I don't know much about server world and FW, so maybe they can
assist.
An example binding in that document would also be very helpful.
> +static struct platform_driver i2c_opal_driver = {
> + .probe = i2c_opal_probe,
> + .remove = i2c_opal_remove,
> + .driver = {
> + .name = "i2c-opal",
> + .owner = THIS_MODULE,
owner not needed.
Thanks,
Wolfram
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ 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