Extend ranges of the critical sections to ensure that context switch

code never sees FPU pcb flags not consistent with the hardware state.

This is uncovered by the eager FPU switch mode.

Analyzed, reviewed and tested by:	gleb
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2018-07-24 19:22:52 +00:00
parent 6bdda3346c
commit 41bed185c1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=336683
4 changed files with 43 additions and 37 deletions

View File

@ -783,22 +783,22 @@ fpugetregs(struct thread *td)
int max_ext_n, i, owned;
pcb = td->td_pcb;
critical_enter();
if ((pcb->pcb_flags & PCB_USERFPUINITDONE) == 0) {
bcopy(fpu_initialstate, get_pcb_user_save_pcb(pcb),
cpu_max_ext_state_size);
get_pcb_user_save_pcb(pcb)->sv_env.en_cw =
pcb->pcb_initial_fpucw;
fpuuserinited(td);
critical_exit();
return (_MC_FPOWNED_PCB);
}
critical_enter();
if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) {
fpusave(get_pcb_user_save_pcb(pcb));
owned = _MC_FPOWNED_FPU;
} else {
owned = _MC_FPOWNED_PCB;
}
critical_exit();
if (use_xsave) {
/*
* Handle partially saved state.
@ -818,6 +818,7 @@ fpugetregs(struct thread *td)
*xstate_bv |= bit;
}
}
critical_exit();
return (owned);
}
@ -826,6 +827,7 @@ fpuuserinited(struct thread *td)
{
struct pcb *pcb;
CRITICAL_ASSERT(td);
pcb = td->td_pcb;
if (PCB_USER_FPU(pcb))
set_pcb_flags(pcb,
@ -884,26 +886,25 @@ fpusetregs(struct thread *td, struct savefpu *addr, char *xfpustate,
addr->sv_env.en_mxcsr &= cpu_mxcsr_mask;
pcb = td->td_pcb;
error = 0;
critical_enter();
if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) {
error = fpusetxstate(td, xfpustate, xfpustate_size);
if (error != 0) {
critical_exit();
return (error);
if (error == 0) {
bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr));
fpurestore(get_pcb_user_save_td(td));
set_pcb_flags(pcb, PCB_FPUINITDONE |
PCB_USERFPUINITDONE);
}
bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr));
fpurestore(get_pcb_user_save_td(td));
critical_exit();
set_pcb_flags(pcb, PCB_FPUINITDONE | PCB_USERFPUINITDONE);
} else {
critical_exit();
error = fpusetxstate(td, xfpustate, xfpustate_size);
if (error != 0)
return (error);
bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr));
fpuuserinited(td);
if (error == 0) {
bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr));
fpuuserinited(td);
}
}
return (0);
critical_exit();
return (error);
}
/*
@ -1077,6 +1078,7 @@ fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags)
ctx->flags = FPU_KERN_CTX_DUMMY | FPU_KERN_CTX_INUSE;
return;
}
critical_enter();
KASSERT(!PCB_USER_FPU(pcb) || pcb->pcb_save ==
get_pcb_user_save_pcb(pcb), ("mangled pcb_save"));
ctx->flags = FPU_KERN_CTX_INUSE;
@ -1087,7 +1089,7 @@ fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags)
pcb->pcb_save = fpu_kern_ctx_savefpu(ctx);
set_pcb_flags(pcb, PCB_KERNFPU);
clear_pcb_flags(pcb, PCB_FPUINITDONE);
return;
critical_exit();
}
int
@ -1105,7 +1107,6 @@ fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx)
clear_pcb_flags(pcb, PCB_FPUNOSAVE | PCB_FPUINITDONE);
start_emulating();
critical_exit();
} else {
KASSERT((ctx->flags & FPU_KERN_CTX_INUSE) != 0,
("leaving not inuse ctx"));
@ -1119,7 +1120,6 @@ fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx)
critical_enter();
if (curthread == PCPU_GET(fpcurthread))
fpudrop();
critical_exit();
pcb->pcb_save = ctx->prev;
}
@ -1136,6 +1136,7 @@ fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx)
clear_pcb_flags(pcb, PCB_FPUINITDONE);
KASSERT(!PCB_USER_FPU(pcb), ("unpaired fpu_kern_leave"));
}
critical_exit();
return (0);
}

View File

@ -2171,8 +2171,10 @@ int
set_fpregs(struct thread *td, struct fpreg *fpregs)
{
critical_enter();
set_fpregs_xmm(fpregs, get_pcb_user_save_td(td));
fpuuserinited(td);
critical_exit();
return (0);
}

View File

@ -2874,6 +2874,7 @@ int
set_fpregs(struct thread *td, struct fpreg *fpregs)
{
critical_enter();
if (cpu_fxsr)
npx_set_fpregs_xmm((struct save87 *)fpregs,
&get_pcb_user_save_td(td)->sv_xmm);
@ -2881,6 +2882,7 @@ set_fpregs(struct thread *td, struct fpreg *fpregs)
bcopy(fpregs, &get_pcb_user_save_td(td)->sv_87,
sizeof(*fpregs));
npxuserinited(td);
critical_exit();
return (0);
}

View File

@ -966,14 +966,15 @@ npxgetregs(struct thread *td)
return (_MC_FPOWNED_NONE);
pcb = td->td_pcb;
critical_enter();
if ((pcb->pcb_flags & PCB_NPXINITDONE) == 0) {
bcopy(npx_initialstate, get_pcb_user_save_pcb(pcb),
cpu_max_ext_state_size);
SET_FPU_CW(get_pcb_user_save_pcb(pcb), pcb->pcb_initial_npxcw);
npxuserinited(td);
critical_exit();
return (_MC_FPOWNED_PCB);
}
critical_enter();
if (td == PCPU_GET(fpcurthread)) {
fpusave(get_pcb_user_save_pcb(pcb));
if (!cpu_fxsr)
@ -987,7 +988,6 @@ npxgetregs(struct thread *td)
} else {
owned = _MC_FPOWNED_PCB;
}
critical_exit();
if (use_xsave) {
/*
* Handle partially saved state.
@ -1010,6 +1010,7 @@ npxgetregs(struct thread *td)
*xstate_bv |= bit;
}
}
critical_exit();
return (owned);
}
@ -1018,6 +1019,7 @@ npxuserinited(struct thread *td)
{
struct pcb *pcb;
CRITICAL_ASSERT(td);
pcb = td->td_pcb;
if (PCB_USER_FPU(pcb))
pcb->pcb_flags |= PCB_NPXINITDONE;
@ -1075,28 +1077,26 @@ npxsetregs(struct thread *td, union savefpu *addr, char *xfpustate,
if (cpu_fxsr)
addr->sv_xmm.sv_env.en_mxcsr &= cpu_mxcsr_mask;
pcb = td->td_pcb;
error = 0;
critical_enter();
if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) {
error = npxsetxstate(td, xfpustate, xfpustate_size);
if (error != 0) {
critical_exit();
return (error);
if (error == 0) {
if (!cpu_fxsr)
fnclex(); /* As in npxdrop(). */
bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr));
fpurstor(get_pcb_user_save_td(td));
pcb->pcb_flags |= PCB_NPXUSERINITDONE | PCB_NPXINITDONE;
}
if (!cpu_fxsr)
fnclex(); /* As in npxdrop(). */
bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr));
fpurstor(get_pcb_user_save_td(td));
critical_exit();
pcb->pcb_flags |= PCB_NPXUSERINITDONE | PCB_NPXINITDONE;
} else {
critical_exit();
error = npxsetxstate(td, xfpustate, xfpustate_size);
if (error != 0)
return (error);
bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr));
npxuserinited(td);
if (error == 0) {
bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr));
npxuserinited(td);
}
}
return (0);
critical_exit();
return (error);
}
static void
@ -1364,6 +1364,7 @@ fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags)
return;
}
pcb = td->td_pcb;
critical_enter();
KASSERT(!PCB_USER_FPU(pcb) || pcb->pcb_save ==
get_pcb_user_save_pcb(pcb), ("mangled pcb_save"));
ctx->flags = FPU_KERN_CTX_INUSE;
@ -1374,7 +1375,7 @@ fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags)
pcb->pcb_save = fpu_kern_ctx_savefpu(ctx);
pcb->pcb_flags |= PCB_KERNNPX;
pcb->pcb_flags &= ~PCB_NPXINITDONE;
return;
critical_exit();
}
int
@ -1392,7 +1393,6 @@ fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx)
critical_enter();
if (curthread == PCPU_GET(fpcurthread))
npxdrop();
critical_exit();
pcb->pcb_save = ctx->prev;
if (pcb->pcb_save == get_pcb_user_save_pcb(pcb)) {
if ((pcb->pcb_flags & PCB_NPXUSERINITDONE) != 0)
@ -1407,6 +1407,7 @@ fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx)
pcb->pcb_flags &= ~PCB_NPXINITDONE;
KASSERT(!PCB_USER_FPU(pcb), ("unpaired fpu_kern_leave"));
}
critical_exit();
return (0);
}