This tucks all the spinout paranoia behind `#ifdef MP_LOCKDEBUG` and uses the same approach used in amd64's pmap's TLB shootdown code.
Part of me wants to remove this altogether, but I'm not sure it's outlived its usefulness quite yet. Three areas that busy wait on ipi's are modified: 1. vmm_start - performs ipi to enable vmm on all cpus 2. vmm_stop - performs ipi to disable vmm on all cpus 3. vmx_remote_vmclear - performs ipi to vmclear a cpu (only pertinent to Intel hosts) (3) is the most likely to spin out and prior to bumping the spinout to the current value (based on __mp_lock_spinout) we had reports from users of hitting it on slower/older MP hardware. For vmm_{start, stop}, I moved the current cpu start/stop routine to before performing the ipi broadcast because if we're going to fail to (dis)enable vmm we should fail fast. If we fail, there's no need to broadcast the ipi. This simplifies the code paths and removes a local variable. All three migrate to infinite busy waits and only have a spinout if built with MP_LOCKDEBUG. On a spinout, we enter ddb. Compiled on amd64 GENERIC, GENERIC.MP, and GENERIC.MP with MP_LOCKDEBUG. (This time I won't break GENERIC :) OK? -dv Index: sys/arch/amd64/amd64/vmm.c =================================================================== RCS file: /opt/cvs/src/sys/arch/amd64/amd64/vmm.c,v retrieving revision 1.305 diff -u -p -r1.305 vmm.c --- sys/arch/amd64/amd64/vmm.c 28 Mar 2022 06:28:47 -0000 1.305 +++ sys/arch/amd64/amd64/vmm.c 16 Apr 2022 18:49:01 -0000 @@ -43,6 +43,11 @@ #include <dev/isa/isareg.h> #include <dev/pv/pvreg.h> +#ifdef MP_LOCKDEBUG +#include <ddb/db_output.h> +extern int __mp_lock_spinout; +#endif /* MP_LOCKDEBUG */ + /* #define VMM_DEBUG */ void *l1tf_flush_region; @@ -1328,17 +1333,26 @@ int vmm_start(void) { struct cpu_info *self = curcpu(); - int ret = 0; #ifdef MULTIPROCESSOR struct cpu_info *ci; CPU_INFO_ITERATOR cii; - int i; -#endif +#ifdef MP_LOCKDEBUG + int nticks; +#endif /* MP_LOCKDEBUG */ +#endif /* MULTIPROCESSOR */ /* VMM is already running */ if (self->ci_flags & CPUF_VMM) return (0); + /* Start VMM on this CPU */ + start_vmm_on_cpu(self); + if (!(self->ci_flags & CPUF_VMM)) { + printf("%s: failed to enter VMM mode\n", + self->ci_dev->dv_xname); + return (EIO); + } + #ifdef MULTIPROCESSOR /* Broadcast start VMM IPI */ x86_broadcast_ipi(X86_IPI_START_VMM); @@ -1346,25 +1360,23 @@ vmm_start(void) CPU_INFO_FOREACH(cii, ci) { if (ci == self) continue; - for (i = 100000; (!(ci->ci_flags & CPUF_VMM)) && i>0;i--) - delay(10); - if (!(ci->ci_flags & CPUF_VMM)) { - printf("%s: failed to enter VMM mode\n", - ci->ci_dev->dv_xname); - ret = EIO; +#ifdef MP_LOCKDEBUG + nticks = __mp_lock_spinout; +#endif /* MP_LOCKDEBUG */ + while (!(ci->ci_flags & CPUF_VMM)) { + CPU_BUSY_CYCLE(); +#ifdef MP_LOCKDEBUG + if (--nticks <= 0) { + db_printf("%s: spun out", __func__); + db_enter(); + nticks = __mp_lock_spinout; + } +#endif /* MP_LOCKDEBUG */ } } #endif /* MULTIPROCESSOR */ - /* Start VMM on this CPU */ - start_vmm_on_cpu(self); - if (!(self->ci_flags & CPUF_VMM)) { - printf("%s: failed to enter VMM mode\n", - self->ci_dev->dv_xname); - ret = EIO; - } - - return (ret); + return (0); } /* @@ -1376,17 +1388,26 @@ int vmm_stop(void) { struct cpu_info *self = curcpu(); - int ret = 0; #ifdef MULTIPROCESSOR struct cpu_info *ci; CPU_INFO_ITERATOR cii; - int i; -#endif +#ifdef MP_LOCKDEBUG + int nticks; +#endif /* MP_LOCKDEBUG */ +#endif /* MULTIPROCESSOR */ /* VMM is not running */ if (!(self->ci_flags & CPUF_VMM)) return (0); + /* Stop VMM on this CPU */ + stop_vmm_on_cpu(self); + if (self->ci_flags & CPUF_VMM) { + printf("%s: failed to exit VMM mode\n", + self->ci_dev->dv_xname); + return (EIO); + } + #ifdef MULTIPROCESSOR /* Stop VMM on other CPUs */ x86_broadcast_ipi(X86_IPI_STOP_VMM); @@ -1394,25 +1415,23 @@ vmm_stop(void) CPU_INFO_FOREACH(cii, ci) { if (ci == self) continue; - for (i = 100000; (ci->ci_flags & CPUF_VMM) && i>0 ;i--) - delay(10); - if (ci->ci_flags & CPUF_VMM) { - printf("%s: failed to exit VMM mode\n", - ci->ci_dev->dv_xname); - ret = EIO; +#ifdef MP_LOCKDEBUG + nticks = __mp_lock_spinout; +#endif /* MP_LOCKDEBUG */ + while ((ci->ci_flags & CPUF_VMM)) { + CPU_BUSY_CYCLE(); +#ifdef MP_LOCKDEBUG + if (--nticks <= 0) { + db_printf("%s: spunout", __func__); + db_enter(); + nticks = __mp_lock_spinout; + } +#endif /* MP_LOCKDEBUG */ } } #endif /* MULTIPROCESSOR */ - /* Stop VMM on this CPU */ - stop_vmm_on_cpu(self); - if (self->ci_flags & CPUF_VMM) { - printf("%s: failed to exit VMM mode\n", - self->ci_dev->dv_xname); - ret = EIO; - } - - return (ret); + return (0); } /* @@ -1536,7 +1555,9 @@ vmclear_on_cpu(struct cpu_info *ci) static int vmx_remote_vmclear(struct cpu_info *ci, struct vcpu *vcpu) { - int ret = 0, nticks = 200000000; +#ifdef MP_LOCKDEBUG + int nticks = __mp_lock_spinout; +#endif /* MP_LOCKDEBUG */ rw_enter_write(&ci->ci_vmcs_lock); atomic_swap_ulong(&ci->ci_vmcs_pa, vcpu->vc_control_pa); @@ -1544,16 +1565,18 @@ vmx_remote_vmclear(struct cpu_info *ci, while (ci->ci_vmcs_pa != VMX_VMCS_PA_CLEAR) { CPU_BUSY_CYCLE(); +#ifdef MP_LOCKDEBUG if (--nticks <= 0) { - printf("%s: spun out\n", __func__); - ret = 1; - break; + db_printf("%s: spun out\n", __func__); + db_enter(); + nticks = __mp_lock_spinout; } +#endif /* MP_LOCKDEBUG */ } atomic_swap_uint(&vcpu->vc_vmx_vmcs_state, VMCS_CLEARED); rw_exit_write(&ci->ci_vmcs_lock); - return (ret); + return (0); } #endif /* MULTIPROCESSOR */