mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-29 03:45:02 +01:00
powerpc: keep FP, VMX and VSX ownership changes atomic with preemption
trap() and set_mcontext() change the FP/VMX/VSX ownership state in several steps with preemption enabled. A context switch in between leaves the thread computing with another thread's register contents: - enable_fpu()/enable_vec() set PCB_FPU/PCB_VEC before loading the registers, so a switch mid-load saves a half-loaded unit over the PCB. - set_mcontext() clears the frame's MSR bits and then the PCB flags; a switch in between re-enables the unit, and the thread returns to user space with it enabled but not owned, so it is neither saved nor restored until the next signal. Both paths run after every sigreturn(2), setcontext(2) and swapcontext(3). A POWER9 test that keeps f14-f31 live across a signal, with other threads using the FPU on the same CPU, saw 283 corrupted round trips out of 882000; none after this change. Hold a critical section around both, as amd64 and arm64 do. Reviewed by: jhibbits MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D60054
This commit is contained in:
1 parent
80af0a32e4
commit
1f1043d550
2 files changed
+13
-1
No files matched your search
@@ -522,7 +522,14 @@ set_mcontext(struct thread *td, mcontext_t *mcp)
|
||||
*
|
||||
* Additionally, ensure VSX is disabled as well, as it is illegal
|
||||
* to leave it turned on when FP or VEC are off.
|
||||
*
|
||||
* The MSR bits in the frame and the PCB flags must change together:
|
||||
* if the thread is switched out in between, cpu_switchin() still
|
||||
* sees the flags, reloads the registers and puts the MSR bits back
|
||||
* into the frame, and the thread then runs with the units enabled
|
||||
* but without owning them.
|
||||
*/
|
||||
critical_enter();
|
||||
tf->srr1 &= ~(PSL_FP | PSL_VSX | PSL_VEC);
|
||||
pcb->pcb_flags &= ~(PCB_FPU | PCB_VSX | PCB_VEC);
|
||||
|
||||
@@ -536,7 +543,6 @@ set_mcontext(struct thread *td, mcontext_t *mcp)
|
||||
* sigresume is callled will used by the resumed thread, instead of the
|
||||
* previously saved data from the mcontext.
|
||||
*/
|
||||
critical_enter();
|
||||
msr = mfmsr() & ~(PSL_FP | PSL_VSX | PSL_VEC);
|
||||
isync();
|
||||
mtmsr(msr);
|
||||
|
||||
@@ -294,24 +294,30 @@ trap(struct trapframe *frame)
|
||||
case EXC_FPU:
|
||||
KASSERT((td->td_pcb->pcb_flags & PCB_FPU) != PCB_FPU,
|
||||
("FPU already enabled for thread"));
|
||||
critical_enter();
|
||||
enable_fpu(td);
|
||||
critical_exit();
|
||||
break;
|
||||
|
||||
case EXC_VEC:
|
||||
KASSERT((td->td_pcb->pcb_flags & PCB_VEC) != PCB_VEC,
|
||||
("Altivec already enabled for thread"));
|
||||
critical_enter();
|
||||
enable_vec(td);
|
||||
critical_exit();
|
||||
break;
|
||||
|
||||
case EXC_VSX:
|
||||
KASSERT((td->td_pcb->pcb_flags & PCB_VSX) != PCB_VSX,
|
||||
("VSX already enabled for thread"));
|
||||
critical_enter();
|
||||
if (!(td->td_pcb->pcb_flags & PCB_VEC))
|
||||
enable_vec(td);
|
||||
if (td->td_pcb->pcb_flags & PCB_FPU)
|
||||
save_fpu(td);
|
||||
td->td_pcb->pcb_flags |= PCB_VSX;
|
||||
enable_fpu(td);
|
||||
critical_exit();
|
||||
break;
|
||||
|
||||
case EXC_FAC:
|
||||
|
||||
Reference in new issue
Block a user