Anshuman Khandual [khand...@linux.vnet.ibm.com] wrote:
| 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 <khand...@linux.vnet.ibm.com>
| ---
|  arch/powerpc/include/asm/switch_to.h |   8 +
|  arch/powerpc/kernel/process.c        |  24 ++
|  arch/powerpc/kernel/ptrace.c         | 792 
+++++++++++++++++++++++++++++++++--
|  3 files changed, 795 insertions(+), 29 deletions(-)
| 
| diff --git a/arch/powerpc/include/asm/switch_to.h 
b/arch/powerpc/include/asm/switch_to.h
| index 0e83e7d..2737f46 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_tmregs_to_thread(struct task_struct *);
| +#else
| +static inline void flush_tmregs_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/kernel/process.c b/arch/powerpc/kernel/process.c
| index 31d0215..e247898 100644
| --- a/arch/powerpc/kernel/process.c
| +++ b/arch/powerpc/kernel/process.c
| @@ -695,6 +695,30 @@ static inline void __switch_to_tm(struct task_struct 
*prev)
|       }
|  }
|  
| +void flush_tmregs_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..17642ef 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;
| + * };
| + */

Maybe a reference to 'struct thread_fp_state' in the comments will help ?


|  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_tmregs_to_thread(target);
| +     } else {
| +             flush_fp_to_thread(target);
| +     }

flush_fp_to_thread(target) is uncondtional - so could be outside
the if and else blocks ?

|  
|  #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]));

Is MSR_TM_ACTIVE() a run time check or compile time ?
ie is there a reason to move the compile time check into the if block ?

|  
| -     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_tmregs_to_thread(target);
| +     } else {
| +             flush_fp_to_thread(target);
| +     }
|  

As mentioned above, flush_fp_to_thread() could be outside the if block.
|  #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];

I think this code is easier to read than the similar code above
in fpr_get() where you have the 'for' loop in both if and else blocks.

|       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);

Is order of flush_fp_to_thread() and flush_altivec_to_thread()
significant or can we call flush_altivec_to_thread() unconditionally
outside the if block ?
| +             flush_tmregs_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_tmregs_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,442 @@ static int evr_set(struct task_struct *target, const 
struct user_regset *regset,
|  }
|  #endif /* CONFIG_SPE */
|  
| +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
| +
| +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;
| +}
| +/*
| + *  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;
| +
| +     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);
| +

Can we copy all the values ot a local buf and then do a single
copyout to user space ?

| +     /* 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;
| +
| +     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);
| +
| +     /* 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));

A single copyin into a local buffer followed by a local assignment would
be easier to read ?

It could be easier to read if all BUILD_BUG_ON() calls are in one place
maybe near the beginning of the function or in the header file.

| +
| +     /* 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;
| +}
| +
| +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 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;
| +
| +     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;
| +}
| +
| +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;
| +}
| +
| +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 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
| +     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);
| +
| +#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
| +     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);
| +
| +#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
| +}
| +
| +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;
| +}

All these tm_*_active() functions do the same checks - maybe they could all
call a common function ?

| +
| +/*
| + * 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;
| +
| +     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);
| +
| +     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;
| +
| +     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);
| +
| +     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 +1163,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 +1203,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,

How about adding a macro ELF_NSPRREG for the 7 special registers ?

| +             .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 = 34,

Similarly for the .n = 34

| +             .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 = {
| @@ -690,7 +1252,7 @@ static int gpr32_get(struct task_struct *target,
|       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; 
| +                     target->thread.regs->gpr[i] = NV_REG_POISON;
|       }
|  
|       pos /= sizeof(reg);
| @@ -803,6 +1365,157 @@ static int gpr32_set(struct task_struct *target,
|                                        (PT_TRAP + 1) * sizeof(reg), -1);
|  }
|  
| +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)
| +{
| +     const unsigned long *regs = &target->thread.ckpt_regs.gpr[0];
| +     compat_ulong_t *k = kbuf;
| +     compat_ulong_t __user *u = ubuf;
| +     compat_ulong_t reg;
| +     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);
| +
| +     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; 
| +     }
| +
| +     pos /= sizeof(reg);
| +     count /= sizeof(reg);
| +
| +     if (kbuf)
| +             for (; count > 0 && pos < PT_MSR; --count)
| +                     *k++ = regs[pos++];
| +     else
| +             for (; count > 0 && pos < PT_MSR; --count)
| +                     if (__put_user((compat_ulong_t) regs[pos++], u++))
| +                             return -EFAULT;
| +
| +     if (count > 0 && pos == PT_MSR) {
| +             reg = get_user_msr(target);
| +             if (kbuf)
| +                     *k++ = reg;
| +             else if (__put_user(reg, u++))
| +                     return -EFAULT;
| +             ++pos;
| +             --count;
| +     }
| +
| +     if (kbuf)
| +             for (; count > 0 && pos < PT_REGS_COUNT; --count)
| +                     *k++ = regs[pos++];
| +     else
| +             for (; count > 0 && pos < PT_REGS_COUNT; --count)
| +                     if (__put_user((compat_ulong_t) regs[pos++], u++))
| +                             return -EFAULT;
| +
| +     kbuf = k;
| +     ubuf = u;
| +     pos *= sizeof(reg);
| +     count *= sizeof(reg);
| +     return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
| +                                     PT_REGS_COUNT * sizeof(reg), -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)
| +{
| +     unsigned long *regs = &target->thread.ckpt_regs.gpr[0];
| +     const compat_ulong_t *k = kbuf;
| +     const compat_ulong_t __user *u = ubuf;
| +     compat_ulong_t reg;
| +
| +     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);
| +
| +     if (target->thread.regs == NULL)
| +             return -EIO;
| +
| +     CHECK_FULL_REGS(target->thread.regs);
| +
| +     pos /= sizeof(reg);
| +     count /= sizeof(reg);
| +
| +     if (kbuf)
| +             for (; count > 0 && pos < PT_MSR; --count)
| +                     regs[pos++] = *k++;
| +     else
| +             for (; count > 0 && pos < PT_MSR; --count) {
| +                     if (__get_user(reg, u++))
| +                             return -EFAULT;
| +                     regs[pos++] = reg;
| +             }
| +
| +
| +     if (count > 0 && pos == PT_MSR) {
| +             if (kbuf)
| +                     reg = *k++;
| +             else if (__get_user(reg, u++))
| +                     return -EFAULT;
| +             set_user_msr(target, reg);
| +             ++pos;
| +             --count;
| +     }
| +
| +     if (kbuf) {
| +             for (; count > 0 && pos <= PT_MAX_PUT_REG; --count)
| +                     regs[pos++] = *k++;
| +             for (; count > 0 && pos < PT_TRAP; --count, ++pos)
| +                     ++k;
| +     } else {
| +             for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) {
| +                     if (__get_user(reg, u++))
| +                             return -EFAULT;
| +                     regs[pos++] = reg;
| +             }
| +             for (; count > 0 && pos < PT_TRAP; --count, ++pos)
| +                     if (__get_user(reg, u++))
| +                             return -EFAULT;
| +     }
| +
| +     if (count > 0 && pos == PT_TRAP) {
| +             if (kbuf)
| +                     reg = *k++;
| +             else if (__get_user(reg, u++))
| +                     return -EFAULT;
| +             set_user_trap(target, reg);
| +             ++pos;
| +             --count;
| +     }
| +
| +     kbuf = k;
| +     ubuf = u;
| +     pos *= sizeof(reg);
| +     count *= sizeof(reg);
| +     return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
| +                                      (PT_TRAP + 1) * sizeof(reg), -1);
| +}
| +
| +
|  /*
|   * These are the regset flavors matching the CONFIG_PPC32 native set.
|   */
| @@ -831,6 +1544,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),
| +             .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 = 34,
| +             .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 = {
| @@ -1754,7 +2489,6 @@ long arch_ptrace(struct task_struct *child, long 
request,
|                                            REGSET_SPE, 0, 35 * sizeof(u32),
|                                            datavp);
|  #endif
| -
|       default:
|               ret = ptrace_request(child, request, addr, data);
|               break;
| -- 
| 1.7.11.7
| 
| _______________________________________________
| Linuxppc-dev mailing list
| linuxppc-...@lists.ozlabs.org
| https://lists.ozlabs.org/listinfo/linuxppc-dev

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to