svn commit: r241489 - in projects/bhyve/sys/amd64: include vmm vmm/intel vmm/io

Neel Natu neel at FreeBSD.org
Fri Oct 12 18:32:45 UTC 2012


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 <machine/vm.h>
 #include <machine/pcb.h>
+#include <machine/smp.h>
 #include <x86/apicreg.h>
 
 #include <machine/vmm.h>
@@ -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 <machine/apicvar.h>
 #include <machine/segments.h>
 #include <machine/md_var.h>
-#include <machine/smp.h>
 
 #include <machine/vmm.h>
 #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


More information about the svn-src-projects mailing list