2014-04-02 07:09:07

by Anshuman Khandual

[permalink] [raw]
Subject: [PATCH 0/3] Add new ptrace request macros on PowerPC

This patch series adds new ELF note sections which are used to
create new ptrace request macros for various transactional memory and
miscellaneous registers on PowerPC. Please find the test case exploiting
the new ptrace request macros and it's results on a POWER8 system.

RFC: https://lkml.org/lkml/2014/4/1/292

============================== Results ==============================
-------TM specific SPR------
TM TFHAR: 100009dc
TM TEXASR: de000001ac000001
TM TFIAR: c00000000003f386
TM CH ORIG_MSR: 900000050000f032
TM CH TAR: 6
TM CH PPR: c000000000000
TM CH DSCR: 1
-------TM checkpointed GPR-----
TM CH GPR[0]: 1000097c
TM CH GPR[1]: 5
TM CH GPR[2]: 6
TM CH GPR[7]: 1
TM CH NIP: 100009dc
TM CH LINK: 1000097c
TM CH CCR: 22000422
-------TM running GPR-----
TM RN GPR[0]: 1000097c
TM RN GPR[1]: 7
TM RN GPR[2]: 8
TM RN GPR[7]: 5
TM RN NIP: 100009fc
TM RN LINK: 1000097c
TM RN CCR: 2000422
-------TM running FPR-----
TM RN FPR[0]: 1002d3a3780
TM RN FPR[1]: 7
TM RN FPR[2]: 8
TM RN FPSCR: 0
-------TM checkpointed FPR-----
TM CH FPR[0]: 1002d3a3780
TM CH FPR[1]: 5
TM CH FPR[2]: 6
TM CH FPSCR: 0
-------Running miscellaneous registers-------
TM RN DSCR: 0
TM RN PPR: c000000000000
TM RN TAR: 8
============================ Test case ==============================
#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/signal.h>
#include <sys/user.h>
#include <linux/elf.h>
#include <linux/types.h>

typedef unsigned long u64;
typedef unsigned int u32;

typedef __vector128 vector128;

/* TM CFPR */
struct tm_cfpr {
u64 fpr[32];
u64 fpscr;
};

/* TM CVMX */
struct tm_cvmx {
vector128 vr[32] __attribute__((aligned(16)));
vector128 vscr __attribute__((aligned(16)));
u32 vrsave;
};

/* TM SPR */
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;
};

/* Miscellaneous registers */
struct misc_regs {
u64 dscr;
u64 ppr;
u64 tar;
};

/* TM instructions */
#define TBEGIN ".long 0x7C00051D ;"
#define TEND ".long 0x7C00055D ;"

#define SPRN_DSCR 0x3
#define SPRN_TAR 815

/* Ptrace request macros */
#define PTRACE_GETTM_SPRREGS 0x70
#define PTRACE_SETTM_SPRREGS 0x71
#define PTRACE_GETTM_CGPRREGS 0x72
#define PTRACE_SETTM_CGPRREGS 0x73
#define PTRACE_GETTM_CFPRREGS 0x74
#define PTRACE_SETTM_CFPRREGS 0x75
#define PTRACE_GETTM_CVMXREG 0x76
#define PTRACE_SETTM_CVMXREG 0x77
#define PTRACE_GETMSCREGS 0x78
#define PTRACE_SETMSCREGS 0x79

int main(int argc, char *argv[])
{
struct tm_spr_regs *tmr1;
struct pt_regs *pregs1, *pregs2;
struct tm_cfpr *fpr, *fpr1;
struct tm_cvmx *vr1;
struct misc_regs *dbr1;

pid_t child;
int ret = 0, status = 0, i = 0, flag = 1;

tmr1 = (struct tm_spr_regs *) malloc(sizeof(struct tm_spr_regs));
pregs1 = (struct pt_regs *) malloc(sizeof(struct pt_regs));
pregs2 = (struct pt_regs *) malloc(sizeof(struct pt_regs));
fpr = (struct tm_cfpr *) malloc(sizeof(struct tm_cfpr));
fpr1 = (struct tm_cfpr *) malloc(sizeof(struct tm_cfpr));
vr1 = (struct tm_cvmx *) malloc(sizeof(struct tm_cvmx));
dbr1 = (struct misc_regs *) malloc(sizeof(struct misc_regs));

child = fork();
if (child < 0) {
printf("fork() failed \n");
exit(-1);
}

/* Child code */
if (child == 0) {
asm __volatile__(
"6: ;" /* TM checkpointed values */
"li 1, 5;" /* GPR[1] */
".long 0x7C210166;" /* FPR[1] */
"li 2, 6;" /* GPR[2] */
".long 0x7C420166;" /* FPR[2] */
"mtspr %[tar], 2;" /* TAR */
"li 7, 1;" /* GPR[7] */
"mtspr %[dscr], 7;" /* DSCR */
"1: ;"
TBEGIN /* TM running values */
"beq 2f ;"
"li 1, 7;" /* GPR[1] */
".long 0x7C210166;" /* FPR[1] */
"li 2, 8;" /* GPR[2] */
".long 0x7C420166;" /* FPR[2] */
"mtspr %[tar], 2;" /* TAR */
"li 7, 5;" /* GPR[7] */
"mtspr %[dscr], 7;" /* DSCR */
"b .;"
TEND
"2: ;" /* Abort handler */
"b 1b;" /* Start from TBEGIN */

"3: ;"
"b 6b;" /* Start all over again */
:: [dscr]"i"(SPRN_DSCR), [tar]"i"(SPRN_TAR)
: "memory", "r7");
}

/* Parent */
if (child) {
do {
/* Wait till child hits "b ." instruction */
sleep(3);

memset(tmr1, 0 , sizeof(struct tm_spr_regs));
memset(pregs1, 0 , sizeof(struct pt_regs));
memset(pregs2, 0 , sizeof(struct pt_regs));
memset(fpr1, 0 , sizeof(struct tm_cfpr));
memset(fpr, 0 , sizeof(struct tm_cfpr));
memset(vr1, 0 , sizeof(struct tm_cvmx));
memset(dbr1, 0 , sizeof(struct misc_regs));

ret = ptrace(PTRACE_ATTACH, child, NULL, NULL);
if (ret == -1) {
printf("PTRACE_ATTACH failed: %s\n", strerror(errno));
exit(-1);
}

ret = waitpid(child, NULL, 0);
if (ret != child) {
printf("PID does not match\n");
exit(-1);
}

/* TM specific SPR */
ret = ptrace(PTRACE_GETTM_SPRREGS, child, NULL, tmr1);
if (ret == -1) {
printf("PTRACE_GETTM_SPRREGS failed: %s\n", strerror(errno));
exit(-1);
}

printf("-------TM specific SPR------\n");
printf("TM TFHAR: %lx\n", tmr1->tm_tfhar);
printf("TM TEXASR: %lx\n", tmr1->tm_texasr);
printf("TM TFIAR: %lx\n", tmr1->tm_tfiar);
printf("TM CH ORIG_MSR: %lx\n", tmr1->tm_orig_msr);
printf("TM CH TAR: %lx\n", tmr1->tm_tar);
printf("TM CH PPR: %lx\n", tmr1->tm_ppr);
printf("TM CH DSCR: %lx\n", tmr1->tm_dscr);

/* TM checkpointed GPR */
ret = ptrace(PTRACE_GETTM_CGPRREGS, child, NULL, pregs1);
if (ret == -1) {
printf("PTRACE_GETTM_CGPRREGS failed: %s\n", strerror(errno));
exit(-1);
}

printf("-------TM checkpointed GPR-----\n");
printf("TM CH GPR[0]: %lx\n", pregs1->gpr[0]);
printf("TM CH GPR[1]: %lx\n", pregs1->gpr[1]);
printf("TM CH GPR[2]: %lx\n", pregs1->gpr[2]);
printf("TM CH GPR[7]: %lx\n", pregs1->gpr[7]);
printf("TM CH NIP: %lx\n", pregs1->nip);
printf("TM CH LINK: %lx\n", pregs1->link);
printf("TM CH CCR: %lx\n", pregs1->ccr);

/* TM running GPR */
ret = ptrace(PTRACE_GETREGS, child, NULL, pregs2);
if (ret == -1) {
printf("PTRACE_GETREGS failed: %s\n", strerror(errno));
exit(-1);
}

printf("-------TM running GPR-----\n");
printf("TM RN GPR[0]: %lx\n", pregs2->gpr[0]);
printf("TM RN GPR[1]: %lx\n", pregs2->gpr[1]);
printf("TM RN GPR[2]: %lx\n", pregs2->gpr[2]);
printf("TM RN GPR[7]: %lx\n", pregs2->gpr[7]);
printf("TM RN NIP: %lx\n", pregs2->nip);
printf("TM RN LINK: %lx\n", pregs2->link);
printf("TM RN CCR: %lx\n", pregs2->ccr);

/* TM running FPR */
ret = ptrace(PTRACE_GETFPREGS, child, NULL, fpr);
if (ret == -1) {
printf("PTRACE_GETFPREGS failed: %s\n", strerror(errno));
exit(-1);
}

printf("-------TM running FPR-----\n");
printf("TM RN FPR[0]: %lx\n", fpr->fpr[0]);
printf("TM RN FPR[1]: %lx\n", fpr->fpr[1]);
printf("TM RN FPR[2]: %lx\n", fpr->fpr[2]);
printf("TM RN FPSCR: %lx\n", fpr->fpscr);

/* TM checkpointed FPR */
ret = ptrace(PTRACE_GETTM_CFPRREGS, child, NULL, fpr1);
if (ret == -1) {
printf("PTRACE_GETTM_CFPRREGS failed: %s\n", strerror(errno));
exit(-1);
}
printf("-------TM checkpointed FPR-----\n");
printf("TM CH FPR[0]: %lx\n", fpr1->fpr[0]);
printf("TM CH FPR[1]: %lx\n", fpr1->fpr[1]);
printf("TM CH FPR[2]: %lx\n", fpr1->fpr[2]);
printf("TM CH FPSCR: %lx\n", fpr1->fpscr);

/* Misc debug */
ret = ptrace(PTRACE_GETMSCREGS, child, NULL, dbr1);
if (ret == -1) {
printf("PTRACE_GETMSCREGS failed: %s\n", strerror(errno));
exit(-1);
}

printf("-------Running miscellaneous registers-------\n");
printf("TM RN DSCR: %lx\n", dbr1->dscr);
printf("TM RN PPR: %lx\n", dbr1->ppr);
printf("TM RN TAR: %lx\n", dbr1->tar);

ret = ptrace(PTRACE_DETACH, child, NULL, NULL);
if (ret == -1) {
printf("PTRACE_DETACH failed: %s\n", strerror(errno));
exit(-1);
}
} while (0);
}
return 0;
}
=====================================================================

Anshuman Khandual (3):
elf: Add some new PowerPC specifc note sections
powerpc, ptrace: Add new ptrace request macros for transactional memory
powerpc, ptrace: Add new ptrace request macro for miscellaneous registers

arch/powerpc/include/asm/switch_to.h | 8 +
arch/powerpc/include/uapi/asm/ptrace.h | 61 +++
arch/powerpc/kernel/process.c | 24 ++
arch/powerpc/kernel/ptrace.c | 659 +++++++++++++++++++++++++++++++--
include/uapi/linux/elf.h | 5 +
5 files changed, 729 insertions(+), 28 deletions(-)

--
1.7.11.7


2014-04-02 07:04:27

by Anshuman Khandual

[permalink] [raw]
Subject: [PATCH 1/3] elf: Add some new PowerPC specifc note sections

This patch adds four new note sections for transactional memory
and one note section for some miscellaneous registers. This addition
of new elf note sections extends the existing elf ABI without affecting
it in any manner.

Signed-off-by: Anshuman Khandual <[email protected]>
---
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 ef6103b..bd59452 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 transactional memory special registers */
+#define NT_PPC_TM_CGPR 0x104 /* PowerpC transactional memory checkpointed GPR */
+#define NT_PPC_TM_CFPR 0x105 /* PowerPC transactional memory checkpointed FPR */
+#define NT_PPC_TM_CVMX 0x106 /* PowerPC transactional memory 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.7.11.7

2014-04-02 07:05:02

by Anshuman Khandual

[permalink] [raw]
Subject: [PATCH 3/3] powerpc, ptrace: Add new ptrace request macro for miscellaneous registers

This patch adds following new set of ptrace request macros for miscellaneous
registers expanding the existing ptrace ABI on PowerPC.

/* Miscellaneous registers */
PTRACE_GETMSCREGS
PTRACE_SETMSCREGS

Signed-off-by: Anshuman Khandual <[email protected]>
---
arch/powerpc/include/uapi/asm/ptrace.h | 10 ++++
arch/powerpc/kernel/ptrace.c | 91 +++++++++++++++++++++++++++++++++-
2 files changed, 100 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h
index 1a12c36..bce1055 100644
--- a/arch/powerpc/include/uapi/asm/ptrace.h
+++ b/arch/powerpc/include/uapi/asm/ptrace.h
@@ -241,6 +241,16 @@ struct pt_regs {
#define PTRACE_GETTM_CVMXREGS 0x76
#define PTRACE_SETTM_CVMXREGS 0x77

+/* Miscellaneous registers */
+#define PTRACE_GETMSCREGS 0x78
+#define PTRACE_SETMSCREGS 0x79
+
+/*
+ * XXX: A note to application developers. The existing data layout
+ * of the above four ptrace requests can change when new registers
+ * are available for each category in forthcoming processors.
+ */
+
#ifndef __ASSEMBLY__

struct ppc_debug_info {
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 9fbcb6a..2893958 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -1054,6 +1054,76 @@ static int tm_cvmx_set(struct task_struct *target, const struct user_regset *reg
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */

/*
+ * Miscellaneous Registers
+ *
+ * struct {
+ * unsigned long dscr;
+ * unsigned long ppr;
+ * unsigned long tar;
+ * };
+ */
+static int misc_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ /* DSCR register */
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dscr, 0,
+ sizeof(unsigned long));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, dscr) + sizeof(unsigned long) +
+ sizeof(unsigned long) != offsetof(struct thread_struct, ppr));
+
+ /* PPR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ppr, sizeof(unsigned long),
+ 2 * sizeof(unsigned long));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, ppr) + sizeof(unsigned long)
+ != offsetof(struct thread_struct, tar));
+ /* TAR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tar, 2 * sizeof(unsigned long),
+ 3 * sizeof(unsigned long));
+ return ret;
+}
+
+static int misc_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;
+
+ /* DSCR register */
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dscr, 0,
+ sizeof(unsigned long));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, dscr) + sizeof(unsigned long) +
+ sizeof(unsigned long) != offsetof(struct thread_struct, ppr));
+
+ /* PPR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ppr, sizeof(unsigned long),
+ 2 * sizeof(unsigned long));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, ppr) + sizeof(unsigned long)
+ != offsetof(struct thread_struct, tar));
+
+ /* TAR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tar, 2 * sizeof(unsigned long),
+ 3 * sizeof(unsigned long));
+ return ret;
+}
+
+/*
* These are our native regset flavors.
*/
enum powerpc_regset {
@@ -1072,8 +1142,9 @@ enum powerpc_regset {
REGSET_TM_SPR, /* TM specific SPR */
REGSET_TM_CGPR, /* TM checkpointed GPR */
REGSET_TM_CFPR, /* TM checkpointed FPR */
- REGSET_TM_CVMX /* TM checkpointed VMX */
+ REGSET_TM_CVMX, /* TM checkpointed VMX */
#endif
+ REGSET_MISC /* Miscellaneous */
};

static const struct user_regset native_regsets[] = {
@@ -1130,6 +1201,11 @@ static const struct user_regset native_regsets[] = {
.get = tm_cvmx_get, .set = tm_cvmx_set
},
#endif
+ [REGSET_MISC] = {
+ .core_note_type = NT_PPC_MISC, .n = 3,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .get = misc_get, .set = misc_set
+ },
};

static const struct user_regset_view user_ppc_native_view = {
@@ -1320,6 +1396,11 @@ static const struct user_regset compat_regsets[] = {
.get = tm_cvmx_get, .set = tm_cvmx_set
},
#endif
+ [REGSET_MISC] = {
+ .core_note_type = NT_PPC_MISC, .n = 3,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .get = misc_get, .set = misc_set
+ },
};

static const struct user_regset_view user_ppc_compat_view = {
@@ -2269,6 +2350,14 @@ long arch_ptrace(struct task_struct *child, long request,
return copy_regset_from_user(child, &user_ppc_native_view,
REGSET_TM_CVMX, 0, (33 * sizeof(vector128) + sizeof(u32)), datavp);
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+ case PTRACE_GETMSCREGS:
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_MISC, 0, 3 * sizeof(u64),
+ datavp);
+ case PTRACE_SETMSCREGS:
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_MISC, 0, 3 * sizeof(u64),
+ datavp);
default:
ret = ptrace_request(child, request, addr, data);
break;
--
1.7.11.7

2014-04-02 07:05:42

by Anshuman Khandual

[permalink] [raw]
Subject: [PATCH 2/3] powerpc, ptrace: Add new ptrace request macros for transactional memory

This patch adds following new sets of ptrace request macros for transactional
memory expanding the existing ptrace ABI on PowerPC.

/* TM special purpose registers */
PTRACE_GETTM_SPRREGS
PTRACE_SETTM_SPRREGS

/* TM checkpointed GPR registers */
PTRACE_GETTM_CGPRREGS
PTRACE_SETTM_CGPRREGS

/* TM checkpointed FPR registers */
PTRACE_GETTM_CFPRREGS
PTRACE_SETTM_CFPRREGS

/* TM checkpointed VMX registers */
PTRACE_GETTM_CVMXREGS
PTRACE_SETTM_CVMXREGS

Signed-off-by: Anshuman Khandual <[email protected]>
---
arch/powerpc/include/asm/switch_to.h | 8 +
arch/powerpc/include/uapi/asm/ptrace.h | 51 +++
arch/powerpc/kernel/process.c | 24 ++
arch/powerpc/kernel/ptrace.c | 570 +++++++++++++++++++++++++++++++--
4 files changed, 625 insertions(+), 28 deletions(-)

diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h
index 0e83e7d..22095e2 100644
--- a/arch/powerpc/include/asm/switch_to.h
+++ b/arch/powerpc/include/asm/switch_to.h
@@ -80,6 +80,14 @@ static inline void flush_spe_to_thread(struct task_struct *t)
}
#endif

+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+extern void flush_tmreg_to_thread(struct task_struct *);
+#else
+static inline void flush_tmreg_to_thread(struct task_struct *t)
+{
+}
+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+
static inline void clear_task_ebb(struct task_struct *t)
{
#ifdef CONFIG_PPC_BOOK3S_64
diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h
index 77d2ed3..1a12c36 100644
--- a/arch/powerpc/include/uapi/asm/ptrace.h
+++ b/arch/powerpc/include/uapi/asm/ptrace.h
@@ -190,6 +190,57 @@ struct pt_regs {
#define PPC_PTRACE_SETHWDEBUG 0x88
#define PPC_PTRACE_DELHWDEBUG 0x87

+/* Transactional memory */
+
+/*
+ * TM specific SPR
+ *
+ * struct data {
+ * u64 tm_tfhar;
+ * u64 tm_texasr;
+ * u64 tm_tfiar;
+ * unsigned long tm_orig_msr;
+ * u64 tm_tar;
+ * u64 tm_ppr;
+ * u64 tm_dscr;
+ * };
+ */
+#define PTRACE_GETTM_SPRREGS 0x70
+#define PTRACE_SETTM_SPRREGS 0x71
+
+/*
+ * TM Checkpointed GPR
+ *
+ * struct data {
+ * struct pt_regs ckpt_regs;
+ * };
+ */
+#define PTRACE_GETTM_CGPRREGS 0x72
+#define PTRACE_SETTM_CGPRREGS 0x73
+
+/*
+ * TM Checkpointed FPR
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ * };
+ */
+#define PTRACE_GETTM_CFPRREGS 0x74
+#define PTRACE_SETTM_CFPRREGS 0x75
+
+/*
+ * TM Checkpointed VMX
+ *
+ * struct data {
+ * vector128 vr[32];
+ * vector128 vscr;
+ * unsigned long vrsave;
+ *};
+ */
+#define PTRACE_GETTM_CVMXREGS 0x76
+#define PTRACE_SETTM_CVMXREGS 0x77
+
#ifndef __ASSEMBLY__

struct ppc_debug_info {
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index af064d2..230a0ee 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -673,6 +673,30 @@ static inline void __switch_to_tm(struct task_struct *prev)
}
}

+void flush_tmreg_to_thread(struct task_struct *tsk)
+{
+ /*
+ * If task is not current, it should have been flushed
+ * already to it's thread_struct during __switch_to().
+ */
+ if (tsk != current)
+ return;
+
+ preempt_disable();
+ if (tsk->thread.regs) {
+ /*
+ * If we are still current, the TM state need to
+ * be flushed to thread_struct as it will be still
+ * present in the current cpu
+ */
+ if (MSR_TM_ACTIVE(tsk->thread.regs->msr)) {
+ __switch_to_tm(tsk);
+ tm_recheckpoint_new_task(tsk);
+ }
+ }
+ preempt_enable();
+}
+
/*
* This is called if we are on the way out to userspace and the
* TIF_RESTORE_TM flag is set. It checks if we need to reload
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 2e3d2bf..9fbcb6a 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -357,6 +357,17 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset,
return ret;
}

+/*
+ * When any transaction is active, "thread_struct->transact_fp" holds
+ * the current running value of all FPR registers and "thread_struct->
+ * fp_state" holds the last checkpointed FPR registers state for the
+ * current transaction.
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ * };
+ */
static int fpr_get(struct task_struct *target, const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
@@ -365,21 +376,41 @@ static int fpr_get(struct task_struct *target, const struct user_regset *regset,
u64 buf[33];
int i;
#endif
- flush_fp_to_thread(target);
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+ } else {
+ flush_fp_to_thread(target);
+ }

#ifdef CONFIG_VSX
/* 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;
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ 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);

#else
- BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
- offsetof(struct thread_fp_state, fpr[32][0]));
+ if (MSR_TM_ACTIVE(tsk->thread.regs->msr)) {
+ BUILD_BUG_ON(offsetof(struct transact_fp, fpscr) !=
+ offsetof(struct transact_fp, fpr[32][0]));

- return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.transact_fp, 0, -1);
+ } esle {
+ 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
}

@@ -391,23 +422,44 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
u64 buf[33];
int i;
#endif
- flush_fp_to_thread(target);
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+ } else {
+ flush_fp_to_thread(target);
+ }

#ifdef CONFIG_VSX
/* 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];
+ for (i = 0; i < 32 ; i++) {
+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ target->thread.TS_TRANS_FPR(i) = buf[i];
+ else
+ target->thread.TS_FPR(i) = buf[i];
+ }
+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ target->thread.transact_fp.fpscr = buf[32];
+ else
+ target->thread.fp_state.fpscr = buf[32];
return 0;
#else
- BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
- offsetof(struct thread_fp_state, fpr[32][0]));
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ BUILD_BUG_ON(offsetof(struct transact_fp, fpscr) !=
+ offsetof(struct transact_fp, fpr[32][0]));

- return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &target->thread.fp_state, 0, -1);
+ return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.transact_fp, 0, -1);
+ } else {
+ 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
}

@@ -432,20 +484,44 @@ static int vr_active(struct task_struct *target,
return target->thread.used_vr ? regset->n : 0;
}

+/*
+ * When any transaction is active, "thread_struct->transact_vr" holds
+ * the current running value of all VMX registers and "thread_struct->
+ * vr_state" holds the last checkpointed value of VMX registers for the
+ * current transaction.
+ *
+ * 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)
{
int ret;
+ struct thread_vr_state *addr;

- flush_altivec_to_thread(target);
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+ } else {
+ flush_altivec_to_thread(target);
+ }

BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) !=
offsetof(struct thread_vr_state, vr[32]));

+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ addr = &target->thread.transact_vr;
+ else
+ addr = &target->thread.vr_state;
+
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
- &target->thread.vr_state, 0,
- 33 * sizeof(vector128));
+ addr, 0, 33 * sizeof(vector128));
+
if (!ret) {
/*
* Copy out only the low-order word of vrsave.
@@ -455,11 +531,14 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset,
u32 word;
} vrsave;
memset(&vrsave, 0, sizeof(vrsave));
- vrsave.word = target->thread.vrsave;
+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ vrsave.word = target->thread.transact_vrsave;
+ else
+ vrsave.word = target->thread.vrsave;
+
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave,
33 * sizeof(vector128), -1);
}
-
return ret;
}

@@ -467,16 +546,27 @@ 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);
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+ } else {
+ flush_altivec_to_thread(target);
+ }

BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) !=
offsetof(struct thread_vr_state, vr[32]));

+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ addr = &target->thread.transact_vr;
+ else
+ addr = &target->thread.vr_state;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &target->thread.vr_state, 0,
- 33 * sizeof(vector128));
+ addr, 0, 33 * sizeof(vector128));
+
if (!ret && count > 0) {
/*
* We use only the first word of vrsave.
@@ -486,13 +576,21 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset,
u32 word;
} vrsave;
memset(&vrsave, 0, sizeof(vrsave));
- vrsave.word = target->thread.vrsave;
+
+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ vrsave.word = target->thread.transact_vrsave;
+ else
+ 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;
+ if (!ret) {
+ if (MSR_TM_ACTIVE(target->thread.regs->msr))
+ target->thread.transact_vrsave = vrsave.word;
+ else
+ target->thread.vrsave = vrsave.word;
+ }
}
-
return ret;
}
#endif /* CONFIG_ALTIVEC */
@@ -613,6 +711,347 @@ static int evr_set(struct task_struct *target, const struct user_regset *regset,
}
#endif /* CONFIG_SPE */

+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+
+/*
+ * Transactional memory SPR
+ *
+ * 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;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+
+ /* TFHAR register */
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfhar, 0, sizeof(u64));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_tfhar) +
+ sizeof(u64) != offsetof(struct thread_struct, tm_texasr));
+
+ /* TEXASR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_texasr, sizeof(u64), 2 * sizeof(u64));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_texasr) +
+ sizeof(u64) != offsetof(struct thread_struct, tm_tfiar));
+
+ /* TFIAR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfiar, 2 * sizeof(u64), 3 * sizeof(u64));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_tfiar) +
+ sizeof(u64) != offsetof(struct thread_struct, tm_orig_msr));
+
+ /* 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));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_orig_msr) +
+ sizeof(unsigned long) + sizeof(struct pt_regs)
+ != offsetof(struct thread_struct, tm_tar));
+
+ /* 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));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_tar)
+ + sizeof(unsigned long) !=
+ offsetof(struct thread_struct, tm_ppr));
+
+ /* 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));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_ppr) +
+ sizeof(unsigned long) !=
+ offsetof(struct thread_struct, tm_dscr));
+
+ /* 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;
+}
+
+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;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+
+ /* TFHAR register */
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfhar, 0, sizeof(u64));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_tfhar)
+ + sizeof(u64) != offsetof(struct thread_struct, tm_texasr));
+
+ /* TEXASR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_texasr, sizeof(u64), 2 * sizeof(u64));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_texasr)
+ + sizeof(u64) != offsetof(struct thread_struct, tm_tfiar));
+
+ /* TFIAR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfiar, 2 * sizeof(u64), 3 * sizeof(u64));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_tfiar)
+ + sizeof(u64) != offsetof(struct thread_struct, tm_orig_msr));
+
+ /* 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));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_orig_msr)
+ + sizeof(unsigned long) + sizeof(struct pt_regs) !=
+ offsetof(struct thread_struct, tm_tar));
+
+ /* 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));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_tar)
+ + sizeof(unsigned long) != offsetof(struct thread_struct, tm_ppr));
+
+ /* 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));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, tm_ppr) +
+ sizeof(unsigned long) !=
+ offsetof(struct thread_struct, tm_dscr));
+
+ /* 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 Checkpointed GPR
+ *
+ * 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;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ckpt_regs, 0,
+ sizeof(struct pt_regs));
+ return ret;
+}
+
+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;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ckpt_regs, 0,
+ sizeof(struct pt_regs));
+ return ret;
+}
+
+/*
+ * TM Checkpointed FPR
+ *
+ * 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)
+{
+#ifdef CONFIG_VSX
+ u64 buf[33];
+ int i;
+#endif
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+
+#ifdef CONFIG_VSX
+ /* 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);
+
+#else
+ 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.thread_fp_state, 0, -1);
+#endif
+}
+
+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)
+{
+#ifdef CONFIG_VSX
+ u64 buf[33];
+ int i;
+#endif
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+
+#ifdef CONFIG_VSX
+ /* 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;
+#else
+ 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
+}
+
+/*
+ * TM Checkpointed VMX
+ *
+ * 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;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+
+ BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) !=
+ offsetof(struct thread_vr_state, vr[32]));
+
+ 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;
+}
+
+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;
+
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmreg_to_thread(target);
+
+ BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) !=
+ offsetof(struct thread_vr_state, vr[32]));
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.vr_state, 0,
+ 33 * sizeof(vector128));
+ if (!ret && count > 0) {
+ /*
+ * We use only the first 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.
@@ -629,6 +1068,12 @@ enum powerpc_regset {
#ifdef CONFIG_SPE
REGSET_SPE,
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ REGSET_TM_SPR, /* TM specific SPR */
+ REGSET_TM_CGPR, /* TM checkpointed GPR */
+ REGSET_TM_CFPR, /* TM checkpointed FPR */
+ REGSET_TM_CVMX /* TM checkpointed VMX */
+#endif
};

static const struct user_regset native_regsets[] = {
@@ -663,6 +1108,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 = 7,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .get = tm_spr_get, .set = tm_spr_set
+ },
+ [REGSET_TM_CGPR] = {
+ .core_note_type = NT_PPC_TM_CGPR, .n = 14,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .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),
+ .get = tm_cfpr_get, .set = tm_cfpr_set
+ },
+ [REGSET_TM_CVMX] = {
+ .core_note_type = NT_PPC_TM_CVMX, .n = 34,
+ .size = sizeof(vector128), .align = sizeof(vector128),
+ .get = tm_cvmx_get, .set = tm_cvmx_set
+ },
+#endif
};

static const struct user_regset_view user_ppc_native_view = {
@@ -831,6 +1298,28 @@ 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 = 7,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .get = tm_spr_get, .set = tm_spr_set
+ },
+ [REGSET_TM_CGPR] = {
+ .core_note_type = NT_PPC_TM_CGPR, .n = 14,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .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),
+ .get = tm_cfpr_get, .set = tm_cfpr_set
+ },
+ [REGSET_TM_CVMX] = {
+ .core_note_type = NT_PPC_TM_CVMX, .n = 34,
+ .size = sizeof(vector128), .align = sizeof(vector128),
+ .get = tm_cvmx_get, .set = tm_cvmx_set
+ },
+#endif
};

static const struct user_regset_view user_ppc_compat_view = {
@@ -1754,7 +2243,32 @@ long arch_ptrace(struct task_struct *child, long request,
REGSET_SPE, 0, 35 * sizeof(u32),
datavp);
#endif
-
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ case PTRACE_GETTM_SPRREGS:
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_TM_SPR, 0, 6 * sizeof(u64) + sizeof(unsigned long), datavp);
+ case PTRACE_SETTM_SPRREGS:
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_TM_SPR, 0, 6 * sizeof(u64) + sizeof(unsigned long), datavp);
+ case PTRACE_GETTM_CGPRREGS:
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_TM_CGPR, 0, sizeof(struct pt_regs), datavp);
+ case PTRACE_SETTM_CGPRREGS:
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_TM_CGPR, 0, sizeof(struct pt_regs), datavp);
+ case PTRACE_GETTM_CFPRREGS:
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_TM_CFPR, 0, sizeof(elf_fpregset_t), datavp);
+ case PTRACE_SETTM_CFPRREGS:
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_TM_CFPR, 0, sizeof(elf_fpregset_t), datavp);
+ case PTRACE_GETTM_CVMXREGS:
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_TM_CVMX, 0, (33 * sizeof(vector128) + sizeof(u32)), datavp);
+ case PTRACE_SETTM_CVMXREGS:
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_TM_CVMX, 0, (33 * sizeof(vector128) + sizeof(u32)), datavp);
+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
default:
ret = ptrace_request(child, request, addr, data);
break;
--
1.7.11.7

2014-04-02 09:34:10

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH 0/3] Add new ptrace request macros on PowerPC

On 04/02/2014 12:32 PM, Anshuman Khandual wrote:
> This patch series adds new ELF note sections which are used to
> create new ptrace request macros for various transactional memory and
> miscellaneous registers on PowerPC. Please find the test case exploiting
> the new ptrace request macros and it's results on a POWER8 system.
>
> RFC: https://lkml.org/lkml/2014/4/1/292
>
> ============================== Results ==============================
> -------TM specific SPR------
> TM TFHAR: 100009dc
> TM TEXASR: de000001ac000001
> TM TFIAR: c00000000003f386
> TM CH ORIG_MSR: 900000050000f032
> TM CH TAR: 6
> TM CH PPR: c000000000000
> TM CH DSCR: 1
> -------TM checkpointed GPR-----
> TM CH GPR[0]: 1000097c
> TM CH GPR[1]: 5
> TM CH GPR[2]: 6
> TM CH GPR[7]: 1
> TM CH NIP: 100009dc
> TM CH LINK: 1000097c
> TM CH CCR: 22000422
> -------TM running GPR-----
> TM RN GPR[0]: 1000097c
> TM RN GPR[1]: 7
> TM RN GPR[2]: 8
> TM RN GPR[7]: 5
> TM RN NIP: 100009fc
> TM RN LINK: 1000097c
> TM RN CCR: 2000422
> -------TM running FPR-----
> TM RN FPR[0]: 1002d3a3780
> TM RN FPR[1]: 7
> TM RN FPR[2]: 8
> TM RN FPSCR: 0
> -------TM checkpointed FPR-----
> TM CH FPR[0]: 1002d3a3780
> TM CH FPR[1]: 5
> TM CH FPR[2]: 6
> TM CH FPSCR: 0
> -------Running miscellaneous registers-------
TM RN DSCR: 0

There is a problem in here which I forgot to mention. The running DSCR value
comes from thread->dscr component of the target process. While we are inside the
transaction (which is the case here as we are stuck at "b ." instruction and
have not reached TEND) thread->dscr should have the running value of the DSCR
register at that point of time. Here we expect the DSCR value to be 5 instead
of 0 as shown in the output above. During the tests when I moved the "b ." after
TEND, the thread->dscr gets the value of 5 while all check pointed reg values are
thrown away. I believe there is some problem in the way thread->dscr context
is saved away inside the TM section. Will look into this problem further and
keep informed.

2014-04-25 23:43:00

by Pedro Alves

[permalink] [raw]
Subject: Re: [PATCH 2/3] powerpc, ptrace: Add new ptrace request macros for transactional memory

On 04/02/2014 08:02 AM, Anshuman Khandual wrote:
> This patch adds following new sets of ptrace request macros for transactional
> memory expanding the existing ptrace ABI on PowerPC.
>
> /* TM special purpose registers */
> PTRACE_GETTM_SPRREGS
> PTRACE_SETTM_SPRREGS
>
> /* TM checkpointed GPR registers */
> PTRACE_GETTM_CGPRREGS
> PTRACE_SETTM_CGPRREGS
>
> /* TM checkpointed FPR registers */
> PTRACE_GETTM_CFPRREGS
> PTRACE_SETTM_CFPRREGS
>
> /* TM checkpointed VMX registers */
> PTRACE_GETTM_CVMXREGS
> PTRACE_SETTM_CVMXREGS

Urgh, we're _still_ adding specialized register specific calls?
Why aren't these exported as new register sets, accessible through
PTRACE_GETREGSET / PTRACE_SETREGSET? That's supposed to be the
Modern Way to do things.

--
Pedro Alves

2014-04-28 10:32:24

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH 2/3] powerpc, ptrace: Add new ptrace request macros for transactional memory

On 04/26/2014 05:12 AM, Pedro Alves wrote:
> On 04/02/2014 08:02 AM, Anshuman Khandual wrote:
>> This patch adds following new sets of ptrace request macros for transactional
>> memory expanding the existing ptrace ABI on PowerPC.
>>
>> /* TM special purpose registers */
>> PTRACE_GETTM_SPRREGS
>> PTRACE_SETTM_SPRREGS
>>
>> /* TM checkpointed GPR registers */
>> PTRACE_GETTM_CGPRREGS
>> PTRACE_SETTM_CGPRREGS
>>
>> /* TM checkpointed FPR registers */
>> PTRACE_GETTM_CFPRREGS
>> PTRACE_SETTM_CFPRREGS
>>
>> /* TM checkpointed VMX registers */
>> PTRACE_GETTM_CVMXREGS
>> PTRACE_SETTM_CVMXREGS
>
> Urgh, we're _still_ adding specialized register specific calls?
> Why aren't these exported as new register sets, accessible through
> PTRACE_GETREGSET / PTRACE_SETREGSET? That's supposed to be the
> Modern Way to do things.

All these new register sets can be accessed through PTRACE_GETREGSET
/SETREGSET requests with the new NT_PPC_* core note types added in the
previous patch. PowerPC already has some register specific ptrace
requests, so thought of adding some new requests for transactional
memory purpose. But yes these are redundant and can be dropped.

2014-04-29 07:02:23

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH 0/3] Add new ptrace request macros on PowerPC

On 04/02/2014 03:02 PM, Anshuman Khandual wrote:
> On 04/02/2014 12:32 PM, Anshuman Khandual wrote:
>> This patch series adds new ELF note sections which are used to
>> create new ptrace request macros for various transactional memory and
>> miscellaneous registers on PowerPC. Please find the test case exploiting
>> the new ptrace request macros and it's results on a POWER8 system.
>>
>> RFC: https://lkml.org/lkml/2014/4/1/292
>>
>> ============================== Results ==============================
>> -------TM specific SPR------
>> TM TFHAR: 100009dc
>> TM TEXASR: de000001ac000001
>> TM TFIAR: c00000000003f386
>> TM CH ORIG_MSR: 900000050000f032
>> TM CH TAR: 6
>> TM CH PPR: c000000000000
>> TM CH DSCR: 1
>> -------TM checkpointed GPR-----
>> TM CH GPR[0]: 1000097c
>> TM CH GPR[1]: 5
>> TM CH GPR[2]: 6
>> TM CH GPR[7]: 1
>> TM CH NIP: 100009dc
>> TM CH LINK: 1000097c
>> TM CH CCR: 22000422
>> -------TM running GPR-----
>> TM RN GPR[0]: 1000097c
>> TM RN GPR[1]: 7
>> TM RN GPR[2]: 8
>> TM RN GPR[7]: 5
>> TM RN NIP: 100009fc
>> TM RN LINK: 1000097c
>> TM RN CCR: 2000422
>> -------TM running FPR-----
>> TM RN FPR[0]: 1002d3a3780
>> TM RN FPR[1]: 7
>> TM RN FPR[2]: 8
>> TM RN FPSCR: 0
>> -------TM checkpointed FPR-----
>> TM CH FPR[0]: 1002d3a3780
>> TM CH FPR[1]: 5
>> TM CH FPR[2]: 6
>> TM CH FPSCR: 0
>> -------Running miscellaneous registers-------
> TM RN DSCR: 0
>
> There is a problem in here which I forgot to mention. The running DSCR value
> comes from thread->dscr component of the target process. While we are inside the
> transaction (which is the case here as we are stuck at "b ." instruction and
> have not reached TEND) thread->dscr should have the running value of the DSCR
> register at that point of time. Here we expect the DSCR value to be 5 instead
> of 0 as shown in the output above. During the tests when I moved the "b ." after
> TEND, the thread->dscr gets the value of 5 while all check pointed reg values are
> thrown away. I believe there is some problem in the way thread->dscr context
> is saved away inside the TM section. Will look into this problem further and
> keep informed.

Reason behind this inconsistent DSCR register value is because of the following commit
where the kernel reverts the DSCR register into a default value to avoid running with
the user set value for a long time, thus preventing any potential performance degradation.
Same reason applies to the PPR register as well. So its not a problem but an expected
behaviour.

commit e9bdc3d6143d1c4b8d8ce5231fc958268331f983
Author: Michael Neuling <[email protected]>
Date: Thu Sep 26 13:29:09 2013 +1000

powerpc/tm: Switch out userspace PPR and DSCR sooner

When we do a treclaim or trecheckpoint we end up running with userspace
PPR and DSCR values. Currently we don't do anything special to avoid
running with user values which could cause a severe performance
degradation.

This patch moves the PPR and DSCR save and restore around treclaim and
trecheckpoint so that we run with user values for a much shorter period.
More care is taken with the PPR as it's impact is greater than the DSCR.

This is similar to user exceptions, where we run HTM_MEDIUM early to
ensure that we don't run with a userspace PPR values in the kernel.

2014-04-29 08:01:16

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH 0/3] Add new ptrace request macros on PowerPC

On 04/29/2014 12:36 PM, Michael Neuling wrote:
> How is it causing the problem?

As mentioned before, what I thought to be a problem is
something expected behaviour. So it's not a problem any
more. DSCR value inside the transaction will fall back
to default as kernel wont let user specified value to
remain applied for a long time.

2014-04-29 12:24:10

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH 0/3] Add new ptrace request macros on PowerPC

On 04/29/2014 01:52 PM, Michael Neuling wrote:
> That's not what that patch does. It shouldn't make any user visible changes
> to DSCR or PPR.

It may not when it runs uninterrupted but after the tracee process has stopped,
thread.dscr reflects the default DSCR value as mentioned before. This can be
proved by changing the "dscr_default" value in arch/powerpc/sysfs.c file.

>
> Over syscall PPR and DSCR may change. Depending on your test case, that may
> be your problem.

I would guess when the tracee process stops for ptrace analysis, tm_reclaim or
tm_recheckpoint path might be crossed which is causing this dscr_default value
to go into thread_struct.

2014-04-30 00:29:14

by Michael Neuling

[permalink] [raw]
Subject: Re: [PATCH 0/3] Add new ptrace request macros on PowerPC

Anshuman Khandual <[email protected]> wrote:

> On 04/29/2014 01:52 PM, Michael Neuling wrote:
> > That's not what that patch does. It shouldn't make any user visible changes
> > to DSCR or PPR.
>
> It may not when it runs uninterrupted but after the tracee process has
> stopped, thread.dscr reflects the default DSCR value as mentioned
> before. This can be proved by changing the "dscr_default" value in
> arch/powerpc/sysfs.c file.

The intention with DSCR is that if the user changes the DSCR, the kernel
should always save/restore it. If you are seeing something else, then
that is a bug. Anton has a test case for this here:

http://ozlabs.org/~anton/junkcode/dscr_explicit_test.c

If that is failing, then there is a bug that we need to fix.

The PPR is the same, except that the kernel can change it over a
syscall.

> > Over syscall PPR and DSCR may change.

Sorry, this should be only PPR. DSCR shouldn't change over a syscall,
at least that's the intention.

> > Depending on your test case, that may
> > be your problem.
>
> I would guess when the tracee process stops for ptrace analysis, tm_reclaim or
> tm_recheckpoint path might be crossed which is causing this dscr_default value
> to go into thread_struct.

That shouldn't happen. If that's happening, it's a bug.

Mikey

2014-04-30 08:18:04

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH 0/3] Add new ptrace request macros on PowerPC

On 04/30/2014 05:59 AM, Michael Neuling wrote:
> Anshuman Khandual <[email protected]> wrote:
>
>> On 04/29/2014 01:52 PM, Michael Neuling wrote:
>>> That's not what that patch does. It shouldn't make any user visible changes
>>> to DSCR or PPR.
>>
>> It may not when it runs uninterrupted but after the tracee process has
>> stopped, thread.dscr reflects the default DSCR value as mentioned
>> before. This can be proved by changing the "dscr_default" value in
>> arch/powerpc/sysfs.c file.
>
> The intention with DSCR is that if the user changes the DSCR, the kernel
> should always save/restore it. If you are seeing something else, then
> that is a bug. Anton has a test case for this here:
>
> http://ozlabs.org/~anton/junkcode/dscr_explicit_test.c
>
> If that is failing, then there is a bug that we need to fix.
>

Anton's above DSCR test passed.

> The PPR is the same, except that the kernel can change it over a
> syscall.
>
>>> Over syscall PPR and DSCR may change.
>
> Sorry, this should be only PPR. DSCR shouldn't change over a syscall,
> at least that's the intention.
>
>>> Depending on your test case, that may
>>> be your problem.
>>
>> I would guess when the tracee process stops for ptrace analysis, tm_reclaim or
>> tm_recheckpoint path might be crossed which is causing this dscr_default value
>> to go into thread_struct.
>
> That shouldn't happen. If that's happening, it's a bug.

I would believe this is happening. Also after reverting the commit
e9bdc3d6143d1c4b8d8ce5231, thread.dscr reflects the same value as that
of thread.tm_dscr which is the check pointed DSCR register value just
before the transaction started. So even the NIP has moved passed the point
where the user changes DSCR inside the transaction, thread.dscr is unable
to capture that latest value. But thread.dscr must contain the latest user
changed value of DSCR which is definitely not happening here. So there is
a problem we need to fix.