PR port-arm/52603:
There is a race here, as seen on arm with FPU: LWP L is running but not on CPU, has its FPU state on CPU2 which has not been released yet, so fpexc still has VFP_FPEXC_EN set in the PCB copy. LWP L is scheduled on CPU1, CPU1 calls cpu_switchto() for L in mi_switch(). cpu_switchto() will set VFP_FPEXC_EN in the FPU's fpexc register per the PCB fpexc copy. Before CPU1 calls pcu_switchpoint() for L, CPU2 calls pcu_do_op(PCU_CMD_SAVE | PCU_CMD_RELEASE) for L because it still holds its FPU state and wants to load another lwp. This cause VFP_FPEXC_EN to be cleared in the PCB copy, but not in CPU1's register. L's l_pcu_cpu is set to NULL. When CPU1 calls pcu_switchpoint() for L it see l_pcu_cpu is NULL, and doesn't call the release callback. Now CPU1 has its FPU enabled but with the wrong FPU state. Fix by releasing the PCU even if l_pcu_cpu is NULL.
This commit is contained in:
parent
15a89934c2
commit
d4ce271380
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: subr_pcu.c,v 1.20 2017/03/16 16:13:21 chs Exp $ */
|
||||
/* $NetBSD: subr_pcu.c,v 1.21 2017/10/16 15:03:57 bouyer Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011, 2014 The NetBSD Foundation, Inc.
|
||||
@ -52,7 +52,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: subr_pcu.c,v 1.20 2017/03/16 16:13:21 chs Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: subr_pcu.c,v 1.21 2017/10/16 15:03:57 bouyer Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/cpu.h>
|
||||
@ -110,7 +110,8 @@ pcu_switchpoint(lwp_t *l)
|
||||
continue;
|
||||
}
|
||||
struct cpu_info * const pcu_ci = l->l_pcu_cpu[id];
|
||||
if (pcu_ci == NULL || pcu_ci == l->l_cpu) {
|
||||
if (pcu_ci == l->l_cpu) {
|
||||
KASSERT(pcu_ci->ci_pcu_curlwp[id] == l);
|
||||
continue;
|
||||
}
|
||||
const pcu_ops_t * const pcu = pcu_ops_md_defs[id];
|
||||
|
Loading…
Reference in New Issue
Block a user