Commit 3ac8ff1c authored by Paul Mackerras's avatar Paul Mackerras Committed by Benjamin Herrenschmidt

powerpc: Fix transactional FP/VMX/VSX unavailable handlers

Currently, if a process starts a transaction and then takes an
exception because the FPU, VMX or VSX unit is unavailable to it,
we end up corrupting any FP/VMX/VSX state that was valid before
the interrupt.  For example, if the process starts a transaction
with the FPU available to it but VMX unavailable, and then does
a VMX instruction inside the transaction, the FP state gets
corrupted.

Loading up the desired state generally involves doing a reclaim
and a recheckpoint.  To avoid corrupting already-valid state, we have
to be careful not to reload that state from the thread_struct
between the reclaim and the recheckpoint (since the thread_struct
values are stale by now), and we have to reload that state from
the transact_fp/vr arrays after the recheckpoint to get back the
current transactional values saved there by the reclaim.
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent d31626f7
...@@ -1416,11 +1416,19 @@ void fp_unavailable_tm(struct pt_regs *regs) ...@@ -1416,11 +1416,19 @@ void fp_unavailable_tm(struct pt_regs *regs)
/* This loads and recheckpoints the FP registers from /* This loads and recheckpoints the FP registers from
* thread.fpr[]. They will remain in registers after the * thread.fpr[]. They will remain in registers after the
* checkpoint so we don't need to reload them after. * checkpoint so we don't need to reload them after.
* If VMX is in use, the VRs now hold checkpointed values,
* so we don't want to load the VRs from the thread_struct.
*/ */
tm_recheckpoint(&current->thread, regs->msr); tm_recheckpoint(&current->thread, MSR_FP);
/* If VMX is in use, get the transactional values back */
if (regs->msr & MSR_VEC) {
do_load_up_transact_altivec(&current->thread);
/* At this point all the VSX state is loaded, so enable it */
regs->msr |= MSR_VSX;
}
} }
#ifdef CONFIG_ALTIVEC
void altivec_unavailable_tm(struct pt_regs *regs) void altivec_unavailable_tm(struct pt_regs *regs)
{ {
/* See the comments in fp_unavailable_tm(). This function operates /* See the comments in fp_unavailable_tm(). This function operates
...@@ -1432,14 +1440,19 @@ void altivec_unavailable_tm(struct pt_regs *regs) ...@@ -1432,14 +1440,19 @@ void altivec_unavailable_tm(struct pt_regs *regs)
regs->nip, regs->msr); regs->nip, regs->msr);
tm_reclaim_current(TM_CAUSE_FAC_UNAV); tm_reclaim_current(TM_CAUSE_FAC_UNAV);
regs->msr |= MSR_VEC; regs->msr |= MSR_VEC;
tm_recheckpoint(&current->thread, regs->msr); tm_recheckpoint(&current->thread, MSR_VEC);
current->thread.used_vr = 1; current->thread.used_vr = 1;
if (regs->msr & MSR_FP) {
do_load_up_transact_fpu(&current->thread);
regs->msr |= MSR_VSX;
}
} }
#endif
#ifdef CONFIG_VSX
void vsx_unavailable_tm(struct pt_regs *regs) void vsx_unavailable_tm(struct pt_regs *regs)
{ {
unsigned long orig_msr = regs->msr;
/* See the comments in fp_unavailable_tm(). This works similarly, /* See the comments in fp_unavailable_tm(). This works similarly,
* though we're loading both FP and VEC registers in here. * though we're loading both FP and VEC registers in here.
* *
...@@ -1451,16 +1464,30 @@ void vsx_unavailable_tm(struct pt_regs *regs) ...@@ -1451,16 +1464,30 @@ void vsx_unavailable_tm(struct pt_regs *regs)
"MSR=%lx\n", "MSR=%lx\n",
regs->nip, regs->msr); regs->nip, regs->msr);
current->thread.used_vsr = 1;
/* If FP and VMX are already loaded, we have all the state we need */
if ((orig_msr & (MSR_FP | MSR_VEC)) == (MSR_FP | MSR_VEC)) {
regs->msr |= MSR_VSX;
return;
}
/* This reclaims FP and/or VR regs if they're already enabled */ /* This reclaims FP and/or VR regs if they're already enabled */
tm_reclaim_current(TM_CAUSE_FAC_UNAV); tm_reclaim_current(TM_CAUSE_FAC_UNAV);
regs->msr |= MSR_VEC | MSR_FP | current->thread.fpexc_mode | regs->msr |= MSR_VEC | MSR_FP | current->thread.fpexc_mode |
MSR_VSX; MSR_VSX;
/* This loads & recheckpoints FP and VRs. */
tm_recheckpoint(&current->thread, regs->msr); /* This loads & recheckpoints FP and VRs; but we have
current->thread.used_vsr = 1; * to be sure not to overwrite previously-valid state.
*/
tm_recheckpoint(&current->thread, regs->msr & ~orig_msr);
if (orig_msr & MSR_FP)
do_load_up_transact_fpu(&current->thread);
if (orig_msr & MSR_VEC)
do_load_up_transact_altivec(&current->thread);
} }
#endif
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
void performance_monitor_exception(struct pt_regs *regs) void performance_monitor_exception(struct pt_regs *regs)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment