Module Name: src Committed By: snj Date: Tue Oct 24 09:14:59 UTC 2017
Modified Files: src/sys/arch/arm/vfp [netbsd-8]: vfp_init.c src/sys/kern [netbsd-8]: subr_pcu.c Log Message: Pull up following revision(s) (requested by bouyer in ticket #326): sys/arch/arm/vfp/vfp_init.c: revision 1.54-1.55 sys/kern/subr_pcu.c: revision 1.21 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. -- In the REENABLE case, make sur the fpexc copy in the pcb also has VFP_FPEXC_EN set. Otherwise we could trap on every context switch even if the CPU already has the VFP state. -- We KASSERT((fregs->vfp_fpexc & VFP_FPEXC_EN) == 0) just before, so enabled is always false. remove. To generate a diff of this commit: cvs rdiff -u -r1.53 -r1.53.2.1 src/sys/arch/arm/vfp/vfp_init.c cvs rdiff -u -r1.20 -r1.20.6.1 src/sys/kern/subr_pcu.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/vfp/vfp_init.c diff -u src/sys/arch/arm/vfp/vfp_init.c:1.53 src/sys/arch/arm/vfp/vfp_init.c:1.53.2.1 --- src/sys/arch/arm/vfp/vfp_init.c:1.53 Fri May 26 21:17:46 2017 +++ src/sys/arch/arm/vfp/vfp_init.c Tue Oct 24 09:14:59 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: vfp_init.c,v 1.53 2017/05/26 21:17:46 jmcneill Exp $ */ +/* $NetBSD: vfp_init.c,v 1.53.2.1 2017/10/24 09:14:59 snj Exp $ */ /* * Copyright (c) 2008 ARM Ltd @@ -520,29 +520,25 @@ vfp_state_load(lwp_t *l, u_int flags) curcpu()->ci_vfp_evs[1].ev_count++; } + KASSERT((armreg_fpexc_read() & VFP_FPEXC_EN) == 0); /* * If the VFP is already enabled we must be bouncing an instruction. */ if (flags & PCU_REENABLE) { uint32_t fpexc = armreg_fpexc_read(); armreg_fpexc_write(fpexc | VFP_FPEXC_EN); + fregs->vfp_fpexc |= VFP_FPEXC_EN; return; } + KASSERT((fregs->vfp_fpexc & VFP_FPEXC_EN) == 0); /* * Load and Enable the VFP (so that we can write the registers). */ - bool enabled = fregs->vfp_fpexc & VFP_FPEXC_EN; fregs->vfp_fpexc |= VFP_FPEXC_EN; armreg_fpexc_write(fregs->vfp_fpexc); - if (enabled) { - /* - * If we think the VFP is enabled, it must have be - * disabled by vfp_state_release for another LWP so - * we can now just return. - */ - return; - } + KASSERT(curcpu()->ci_pcu_curlwp[PCU_FPU] == NULL); + KASSERT(l->l_pcu_cpu[PCU_FPU] == NULL); load_vfpregs(fregs); armreg_fpscr_write(fregs->vfp_fpscr); @@ -562,6 +558,9 @@ vfp_state_save(lwp_t *l) struct vfpreg * const fregs = &pcb->pcb_vfp; uint32_t fpexc = armreg_fpexc_read(); + KASSERT(curcpu()->ci_pcu_curlwp[PCU_FPU] == l); + KASSERT(curcpu() == l->l_pcu_cpu[PCU_FPU]); + KASSERT(curlwp == l || curlwp->l_pcu_cpu[PCU_FPU] != curcpu()); /* * Enable the VFP (so we can read the registers). * Make sure the exception bit is cleared so that we can Index: src/sys/kern/subr_pcu.c diff -u src/sys/kern/subr_pcu.c:1.20 src/sys/kern/subr_pcu.c:1.20.6.1 --- src/sys/kern/subr_pcu.c:1.20 Thu Mar 16 16:13:21 2017 +++ src/sys/kern/subr_pcu.c Tue Oct 24 09:14:59 2017 @@ -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.20.6.1 2017/10/24 09:14:59 snj 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.20.6.1 2017/10/24 09:14:59 snj 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];