powerpc: tm: Enable transactional memory (TM) lazily for userspace
[cascardo/linux.git] / arch / powerpc / kernel / process.c
index e220330..9e7c10f 100644 (file)
@@ -812,6 +812,12 @@ static inline bool hw_brk_match(struct arch_hw_breakpoint *a,
 }
 
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+
+static inline bool tm_enabled(struct task_struct *tsk)
+{
+       return tsk && tsk->thread.regs && (tsk->thread.regs->msr & MSR_TM);
+}
+
 static void tm_reclaim_thread(struct thread_struct *thr,
                              struct thread_info *ti, uint8_t cause)
 {
@@ -892,6 +898,9 @@ void tm_recheckpoint(struct thread_struct *thread,
 {
        unsigned long flags;
 
+       if (!(thread->regs->msr & MSR_TM))
+               return;
+
        /* We really can't be interrupted here as the TEXASR registers can't
         * change and later in the trecheckpoint code, we have a userspace R1.
         * So let's hard disable over this region.
@@ -924,7 +933,7 @@ static inline void tm_recheckpoint_new_task(struct task_struct *new)
         * unavailable later, we are unable to determine which set of FP regs
         * need to be restored.
         */
-       if (!new->thread.regs)
+       if (!tm_enabled(new))
                return;
 
        if (!MSR_TM_ACTIVE(new->thread.regs->msr)){
@@ -955,8 +964,16 @@ static inline void __switch_to_tm(struct task_struct *prev,
                struct task_struct *new)
 {
        if (cpu_has_feature(CPU_FTR_TM)) {
-               tm_enable();
-               tm_reclaim_task(prev);
+               if (tm_enabled(prev) || tm_enabled(new))
+                       tm_enable();
+
+               if (tm_enabled(prev)) {
+                       prev->thread.load_tm++;
+                       tm_reclaim_task(prev);
+                       if (!MSR_TM_ACTIVE(prev->thread.regs->msr) && prev->thread.load_tm == 0)
+                               prev->thread.regs->msr &= ~MSR_TM;
+               }
+
                tm_recheckpoint_new_task(new);
        }
 }
@@ -1393,6 +1410,9 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
         * transitions the CPU out of TM mode.  Hence we need to call
         * tm_recheckpoint_new_task() (on the same task) to restore the
         * checkpointed state back and the TM mode.
+        *
+        * Can't pass dst because it isn't ready. Doesn't matter, passing
+        * dst is only important for __switch_to()
         */
        __switch_to_tm(src, src);
 
@@ -1636,8 +1656,6 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
        current->thread.used_spe = 0;
 #endif /* CONFIG_SPE */
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-       if (cpu_has_feature(CPU_FTR_TM))
-               regs->msr |= MSR_TM;
        current->thread.tm_tfhar = 0;
        current->thread.tm_texasr = 0;
        current->thread.tm_tfiar = 0;