From owner-svn-src-projects@FreeBSD.ORG Fri Oct 12 18:32:45 2012 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 97BD17E9; Fri, 12 Oct 2012 18:32:45 +0000 (UTC) (envelope-from neel@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 802638FC1C; Fri, 12 Oct 2012 18:32:45 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q9CIWjE1054751; Fri, 12 Oct 2012 18:32:45 GMT (envelope-from neel@svn.freebsd.org) Received: (from neel@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q9CIWjIk054743; Fri, 12 Oct 2012 18:32:45 GMT (envelope-from neel@svn.freebsd.org) Message-Id: <201210121832.q9CIWjIk054743@svn.freebsd.org> From: Neel Natu Date: Fri, 12 Oct 2012 18:32:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r241489 - in projects/bhyve/sys/amd64: include vmm vmm/intel vmm/io X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 12 Oct 2012 18:32:45 -0000 Author: neel Date: Fri Oct 12 18:32:44 2012 New Revision: 241489 URL: http://svn.freebsd.org/changeset/base/241489 Log: Provide per-vcpu locks instead of relying on a single big lock. This also gets rid of all the witness.watch warnings related to calling malloc(M_WAITOK) while holding a mutex. Reviewed by: grehan Modified: projects/bhyve/sys/amd64/include/vmm.h projects/bhyve/sys/amd64/vmm/intel/vmx.c projects/bhyve/sys/amd64/vmm/io/ppt.c projects/bhyve/sys/amd64/vmm/vmm.c projects/bhyve/sys/amd64/vmm/vmm_dev.c projects/bhyve/sys/amd64/vmm/vmm_ipi.c projects/bhyve/sys/amd64/vmm/vmm_ipi.h Modified: projects/bhyve/sys/amd64/include/vmm.h ============================================================================== --- projects/bhyve/sys/amd64/include/vmm.h Fri Oct 12 18:21:31 2012 (r241488) +++ projects/bhyve/sys/amd64/include/vmm.h Fri Oct 12 18:32:44 2012 (r241489) @@ -130,19 +130,24 @@ int vmm_is_pptdev(int bus, int slot, int void *vm_iommu_domain(struct vm *vm); -#define VCPU_STOPPED 0 -#define VCPU_RUNNING 1 -void vm_set_run_state(struct vm *vm, int vcpu, int running); -int vm_get_run_state(struct vm *vm, int vcpu, int *hostcpu); +enum vcpu_state { + VCPU_IDLE, + VCPU_RUNNING, + VCPU_CANNOT_RUN, +}; -void *vcpu_stats(struct vm *vm, int vcpu); +int vcpu_set_state(struct vm *vm, int vcpu, enum vcpu_state state); +enum vcpu_state vcpu_get_state(struct vm *vm, int vcpu); static int __inline -vcpu_is_running(struct vm *vm, int vcpu, int *hostcpu) +vcpu_is_running(struct vm *vm, int vcpu) { - return (vm_get_run_state(vm, vcpu, hostcpu) == VCPU_RUNNING); + return (vcpu_get_state(vm, vcpu) == VCPU_RUNNING); } +void *vcpu_stats(struct vm *vm, int vcpu); +void vm_interrupt_hostcpu(struct vm *vm, int vcpu); + #endif /* KERNEL */ #define VM_MAXCPU 8 /* maximum virtual cpus */ Modified: projects/bhyve/sys/amd64/vmm/intel/vmx.c ============================================================================== --- projects/bhyve/sys/amd64/vmm/intel/vmx.c Fri Oct 12 18:21:31 2012 (r241488) +++ projects/bhyve/sys/amd64/vmm/intel/vmx.c Fri Oct 12 18:32:44 2012 (r241489) @@ -1568,7 +1568,7 @@ vmx_getreg(void *arg, int vcpu, int reg, * vmcs_getreg will VMCLEAR the vmcs when it is done which will cause * the subsequent vmlaunch/vmresume to fail. */ - if (vcpu_is_running(vmx->vm, vcpu, NULL)) + if (vcpu_is_running(vmx->vm, vcpu)) panic("vmx_getreg: %s%d is running", vm_name(vmx->vm), vcpu); return (vmcs_getreg(&vmx->vmcs[vcpu], reg, retval)); @@ -1596,7 +1596,7 @@ vmx_setreg(void *arg, int vcpu, int reg, * vmcs_setreg will VMCLEAR the vmcs when it is done which will cause * the subsequent vmlaunch/vmresume to fail. */ - if (vcpu_is_running(vmx->vm, vcpu, NULL)) + if (vcpu_is_running(vmx->vm, vcpu)) panic("vmx_setreg: %s%d is running", vm_name(vmx->vm), vcpu); error = vmcs_setreg(&vmx->vmcs[vcpu], reg, val); Modified: projects/bhyve/sys/amd64/vmm/io/ppt.c ============================================================================== --- projects/bhyve/sys/amd64/vmm/io/ppt.c Fri Oct 12 18:21:31 2012 (r241488) +++ projects/bhyve/sys/amd64/vmm/io/ppt.c Fri Oct 12 18:32:44 2012 (r241489) @@ -53,6 +53,8 @@ __FBSDID("$FreeBSD$"); #include "iommu.h" #include "ppt.h" +/* XXX locking */ + #define MAX_PPTDEVS (sizeof(pptdevs) / sizeof(pptdevs[0])) #define MAX_MMIOSEGS (PCIR_MAX_BAR_0 + 1) #define MAX_MSIMSGS 32 Modified: projects/bhyve/sys/amd64/vmm/vmm.c ============================================================================== --- projects/bhyve/sys/amd64/vmm/vmm.c Fri Oct 12 18:21:31 2012 (r241488) +++ projects/bhyve/sys/amd64/vmm/vmm.c Fri Oct 12 18:32:44 2012 (r241489) @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include @@ -65,6 +66,8 @@ struct vlapic; struct vcpu { int flags; + enum vcpu_state state; + struct mtx mtx; int pincpu; /* host cpuid this vcpu is bound to */ int hostcpu; /* host cpuid this vcpu last ran on */ uint64_t guest_msrs[VMM_MSR_NUM]; @@ -76,7 +79,6 @@ struct vcpu { enum x2apic_state x2apic_state; }; #define VCPU_F_PINNED 0x0001 -#define VCPU_F_RUNNING 0x0002 #define VCPU_PINCPU(vm, vcpuid) \ ((vm->vcpu[vcpuid].flags & VCPU_F_PINNED) ? vm->vcpu[vcpuid].pincpu : -1) @@ -89,6 +91,10 @@ do { \ vm->vcpu[vcpuid].pincpu = host_cpuid; \ } while(0) +#define vcpu_lock_init(v) mtx_init(&((v)->mtx), "vcpu lock", 0, MTX_DEF) +#define vcpu_lock(v) mtx_lock(&((v)->mtx)) +#define vcpu_unlock(v) mtx_unlock(&((v)->mtx)) + #define VM_MAX_MEMORY_SEGMENTS 2 struct vm { @@ -162,7 +168,8 @@ vcpu_init(struct vm *vm, uint32_t vcpu_i vcpu = &vm->vcpu[vcpu_id]; - vcpu->hostcpu = -1; + vcpu_lock_init(vcpu); + vcpu->hostcpu = NOCPU; vcpu->vcpuid = vcpu_id; vcpu->vlapic = vlapic_init(vm, vcpu_id); vm_set_x2apic_state(vm, vcpu_id, X2APIC_ENABLED); @@ -667,11 +674,13 @@ vm_run(struct vm *vm, struct vm_run *vmr pcb = PCPU_GET(curpcb); set_pcb_flags(pcb, PCB_FULL_IRET); - vcpu->hostcpu = curcpu; - restore_guest_msrs(vm, vcpuid); restore_guest_fpustate(vcpu); + + vcpu->hostcpu = curcpu; error = VMRUN(vm->cookie, vcpuid, vmrun->rip); + vcpu->hostcpu = NOCPU; + save_guest_fpustate(vcpu); restore_host_msrs(vm, vcpuid); @@ -787,9 +796,10 @@ vm_iommu_domain(struct vm *vm) return (vm->iommu); } -void -vm_set_run_state(struct vm *vm, int vcpuid, int state) +int +vcpu_set_state(struct vm *vm, int vcpuid, enum vcpu_state state) { + int error; struct vcpu *vcpu; if (vcpuid < 0 || vcpuid >= VM_MAXCPU) @@ -797,43 +807,42 @@ vm_set_run_state(struct vm *vm, int vcpu vcpu = &vm->vcpu[vcpuid]; - if (state == VCPU_RUNNING) { - if (vcpu->flags & VCPU_F_RUNNING) { - panic("vm_set_run_state: %s[%d] is already running", - vm_name(vm), vcpuid); - } - vcpu->flags |= VCPU_F_RUNNING; + vcpu_lock(vcpu); + + /* + * The following state transitions are allowed: + * IDLE -> RUNNING -> IDLE + * IDLE -> CANNOT_RUN -> IDLE + */ + if ((vcpu->state == VCPU_IDLE && state != VCPU_IDLE) || + (vcpu->state != VCPU_IDLE && state == VCPU_IDLE)) { + error = 0; + vcpu->state = state; } else { - if ((vcpu->flags & VCPU_F_RUNNING) == 0) { - panic("vm_set_run_state: %s[%d] is already stopped", - vm_name(vm), vcpuid); - } - vcpu->flags &= ~VCPU_F_RUNNING; + error = EBUSY; } + + vcpu_unlock(vcpu); + + return (error); } -int -vm_get_run_state(struct vm *vm, int vcpuid, int *cpuptr) +enum vcpu_state +vcpu_get_state(struct vm *vm, int vcpuid) { - int retval, hostcpu; struct vcpu *vcpu; + enum vcpu_state state; if (vcpuid < 0 || vcpuid >= VM_MAXCPU) panic("vm_get_run_state: invalid vcpuid %d", vcpuid); vcpu = &vm->vcpu[vcpuid]; - if (vcpu->flags & VCPU_F_RUNNING) { - retval = VCPU_RUNNING; - hostcpu = vcpu->hostcpu; - } else { - retval = VCPU_STOPPED; - hostcpu = -1; - } - if (cpuptr) - *cpuptr = hostcpu; + vcpu_lock(vcpu); + state = vcpu->state; + vcpu_unlock(vcpu); - return (retval); + return (state); } void @@ -884,3 +893,25 @@ vm_set_x2apic_state(struct vm *vm, int v return (0); } + +void +vm_interrupt_hostcpu(struct vm *vm, int vcpuid) +{ + int hostcpu; + struct vcpu *vcpu; + + vcpu = &vm->vcpu[vcpuid]; + + /* + * XXX racy but the worst case is that we'll send an unnecessary IPI + * to the 'hostcpu'. + * + * We cannot use vcpu_is_running() here because it acquires vcpu->mtx + * which is not allowed inside a critical section. + */ + hostcpu = vcpu->hostcpu; + if (hostcpu == NOCPU || hostcpu == curcpu) + return; + + ipi_cpu(hostcpu, vmm_ipinum); +} Modified: projects/bhyve/sys/amd64/vmm/vmm_dev.c ============================================================================== --- projects/bhyve/sys/amd64/vmm/vmm_dev.c Fri Oct 12 18:21:31 2012 (r241488) +++ projects/bhyve/sys/amd64/vmm/vmm_dev.c Fri Oct 12 18:32:44 2012 (r241489) @@ -88,9 +88,6 @@ vmmdev_lookup(const char *name) static struct vmmdev_softc * vmmdev_lookup2(struct cdev *cdev) { -#ifdef notyet /* XXX kernel is not compiled with invariants */ - mtx_assert(&vmmdev_mtx, MA_OWNED); -#endif return (cdev->si_drv1); } @@ -141,7 +138,8 @@ static int vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, struct thread *td) { - int error, vcpu; + int error, vcpu, state_changed; + enum vcpu_state new_state; struct vmmdev_softc *sc; struct vm_memory_segment *seg; struct vm_register *vmreg; @@ -160,12 +158,12 @@ vmmdev_ioctl(struct cdev *cdev, u_long c struct vm_stat_desc *statdesc; struct vm_x2apic *x2apic; - mtx_lock(&vmmdev_mtx); sc = vmmdev_lookup2(cdev); - if (sc == NULL) { - mtx_unlock(&vmmdev_mtx); + if (sc == NULL) return (ENXIO); - } + + vcpu = -1; + state_changed = 0; /* * Some VMM ioctls can operate only on vcpus that are not running. @@ -181,6 +179,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long c case VM_GET_CAPABILITY: case VM_SET_CAPABILITY: case VM_PPTDEV_MSI: + case VM_PPTDEV_MSIX: case VM_SET_X2APIC_STATE: /* * XXX fragile, handle with care @@ -192,11 +191,42 @@ vmmdev_ioctl(struct cdev *cdev, u_long c goto done; } - if (vcpu_is_running(sc->vm, vcpu, NULL)) { - error = EBUSY; + if (cmd == VM_RUN) + new_state = VCPU_RUNNING; + else + new_state = VCPU_CANNOT_RUN; + + error = vcpu_set_state(sc->vm, vcpu, new_state); + if (error) + goto done; + + state_changed = 1; + break; + + case VM_MAP_PPTDEV_MMIO: + case VM_BIND_PPTDEV: + case VM_UNBIND_PPTDEV: + case VM_MAP_MEMORY: + /* + * ioctls that operate on the entire virtual machine must + * prevent all vcpus from running. + */ + error = 0; + for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) { + error = vcpu_set_state(sc->vm, vcpu, VCPU_CANNOT_RUN); + if (error) + break; + } + + if (error) { + while (--vcpu >= 0) + vcpu_set_state(sc->vm, vcpu, VCPU_IDLE); goto done; } + + state_changed = 2; break; + default: break; } @@ -204,14 +234,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long c switch(cmd) { case VM_RUN: vmrun = (struct vm_run *)data; - - vm_set_run_state(sc->vm, vmrun->cpuid, VCPU_RUNNING); - mtx_unlock(&vmmdev_mtx); - error = vm_run(sc->vm, vmrun); - - mtx_lock(&vmmdev_mtx); - vm_set_run_state(sc->vm, vmrun->cpuid, VCPU_STOPPED); break; case VM_STAT_DESC: { const char *desc; @@ -346,9 +369,15 @@ vmmdev_ioctl(struct cdev *cdev, u_long c error = ENOTTY; break; } -done: - mtx_unlock(&vmmdev_mtx); + if (state_changed == 1) { + vcpu_set_state(sc->vm, vcpu, VCPU_IDLE); + } else if (state_changed == 2) { + for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) + vcpu_set_state(sc->vm, vcpu, VCPU_IDLE); + } + +done: return (error); } Modified: projects/bhyve/sys/amd64/vmm/vmm_ipi.c ============================================================================== --- projects/bhyve/sys/amd64/vmm/vmm_ipi.c Fri Oct 12 18:21:31 2012 (r241488) +++ projects/bhyve/sys/amd64/vmm/vmm_ipi.c Fri Oct 12 18:32:44 2012 (r241489) @@ -38,7 +38,6 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include #include "vmm_ipi.h" @@ -48,7 +47,7 @@ extern inthand_t IDTVEC(rsvd), IDTVEC(ju /* * The default is to use the IPI_AST to interrupt a vcpu. */ -static int ipinum = IPI_AST; +int vmm_ipinum = IPI_AST; CTASSERT(APIC_SPURIOUS_INT == 255); @@ -73,31 +72,22 @@ vmm_ipi_init(void) ip = &idt[idx]; func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset); if (func == (uintptr_t)&IDTVEC(rsvd)) { - ipinum = idx; - setidt(ipinum, IDTVEC(justreturn), SDT_SYSIGT, + vmm_ipinum = idx; + setidt(vmm_ipinum, IDTVEC(justreturn), SDT_SYSIGT, SEL_KPL, 0); break; } } - if (ipinum != IPI_AST && bootverbose) { + if (vmm_ipinum != IPI_AST && bootverbose) { printf("vmm_ipi_init: installing ipi handler to interrupt " - "vcpus at vector %d\n", ipinum); + "vcpus at vector %d\n", vmm_ipinum); } } void vmm_ipi_cleanup(void) { - if (ipinum != IPI_AST) - setidt(ipinum, IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0); -} - -void -vm_interrupt_hostcpu(struct vm *vm, int vcpu) -{ - int hostcpu; - - if (vcpu_is_running(vm, vcpu, &hostcpu) && hostcpu != curcpu) - ipi_cpu(hostcpu, ipinum); + if (vmm_ipinum != IPI_AST) + setidt(vmm_ipinum, IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0); } Modified: projects/bhyve/sys/amd64/vmm/vmm_ipi.h ============================================================================== --- projects/bhyve/sys/amd64/vmm/vmm_ipi.h Fri Oct 12 18:21:31 2012 (r241488) +++ projects/bhyve/sys/amd64/vmm/vmm_ipi.h Fri Oct 12 18:32:44 2012 (r241489) @@ -31,8 +31,9 @@ struct vm; +extern int vmm_ipinum; + void vmm_ipi_init(void); void vmm_ipi_cleanup(void); -void vm_interrupt_hostcpu(struct vm *vm, int vcpu); #endif