diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h --- a/lib/libvmmapi/vmmapi.h +++ b/lib/libvmmapi/vmmapi.h @@ -116,7 +116,7 @@ int vm_create(const char *name); int vm_get_device_fd(struct vmctx *ctx); struct vmctx *vm_open(const char *name); -void vm_destroy(struct vmctx *ctx); +int vm_destroy(struct vmctx *ctx); int vm_parse_memsize(const char *optarg, size_t *memsize); int vm_setup_memory(struct vmctx *ctx, size_t len, enum vm_mmap_style s); void *vm_map_gpa(struct vmctx *ctx, vm_paddr_t gaddr, size_t len); diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c --- a/lib/libvmmapi/vmmapi.c +++ b/lib/libvmmapi/vmmapi.c @@ -140,16 +140,21 @@ return (NULL); } -void +int vm_destroy(struct vmctx *vm) { + int dummy, error = 0; + assert(vm != NULL); if (vm->fd >= 0) - close(vm->fd); - DESTROY(vm->name); + error = ioctl(vm->fd, VM_DESTROY, &dummy); - free(vm); + if (error == 0) { + close(vm->fd); + free(vm); + } + return (error); } int @@ -1694,7 +1699,8 @@ VM_ACTIVATE_CPU, VM_GET_CPUS, VM_SUSPEND_CPU, VM_RESUME_CPU, VM_SET_INTINFO, VM_GET_INTINFO, VM_RTC_WRITE, VM_RTC_READ, VM_RTC_SETTIME, VM_RTC_GETTIME, - VM_RESTART_INSTRUCTION, VM_SET_TOPOLOGY, VM_GET_TOPOLOGY }; + VM_RESTART_INSTRUCTION, VM_SET_TOPOLOGY, VM_GET_TOPOLOGY, + VM_DESTROY }; if (len == NULL) { cmds = malloc(sizeof(vm_ioctl_cmds)); diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h --- a/sys/amd64/include/vmm_dev.h +++ b/sys/amd64/include/vmm_dev.h @@ -344,7 +344,8 @@ /* checkpoint */ IOCNUM_SNAPSHOT_REQ = 113, - IOCNUM_RESTORE_TIME = 115 + IOCNUM_RESTORE_TIME = 115, + IOCNUM_DESTROY = 116, }; #define VM_RUN \ @@ -475,4 +476,6 @@ _IOWR('v', IOCNUM_SNAPSHOT_REQ, struct vm_snapshot_meta) #define VM_RESTORE_TIME \ _IOWR('v', IOCNUM_RESTORE_TIME, int) +#define VM_DESTROY \ + _IOWR('v', IOCNUM_DESTROY, int) #endif diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -99,6 +99,7 @@ static int vmm_priv_check(struct ucred *ucred); static int devmem_create_cdev(const char *vmname, int id, char *devmem); static void devmem_destroy(void *arg); +static void vmmdev_destroy(void *arg); static int vmm_priv_check(struct ucred *ucred) @@ -353,6 +354,52 @@ return (error); } +static int +vmm_destroy(struct vmmdev_softc *sc) +{ + struct devmem_softc *dsc; + struct cdev *cdev; + int error; + + mtx_lock(&vmmdev_mtx); + if (sc == NULL || sc->cdev == NULL) { + mtx_unlock(&vmmdev_mtx); + error = EINVAL; + goto out; + } + + /* + * The 'cdev' will be destroyed asynchronously when 'si_threadcount' + * goes down to 0 so we should not do it again in the callback. + * + * Setting 'sc->cdev' to NULL is also used to indicate that the VM + * is scheduled for destruction. + */ + cdev = sc->cdev; + sc->cdev = NULL; + mtx_unlock(&vmmdev_mtx); + + /* + * Schedule all cdevs to be destroyed: + * + * - any new operations on the 'cdev' will return an error (ENXIO). + * + * - when the 'si_threadcount' dwindles down to zero the 'cdev' will + * be destroyed and the callback will be invoked in a taskqueue + * context. + * + * - the 'devmem' cdevs are destroyed before the virtual machine 'cdev' + */ + SLIST_FOREACH(dsc, &sc->devmem, link) { + KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed")); + destroy_dev_sched_cb(dsc->cdev, devmem_destroy, dsc); + } + destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); + error = 0; +out: + return (error); +} + static int vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, struct thread *td) @@ -879,6 +926,9 @@ error = vm_restore_time(sc->vm); break; #endif + case VM_DESTROY: + error = vmm_destroy(sc); + break; default: error = ENOTTY; break; @@ -1001,9 +1051,7 @@ static int sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) { - struct devmem_softc *dsc; struct vmmdev_softc *sc; - struct cdev *cdev; char *buf; int error, buflen; @@ -1020,41 +1068,9 @@ mtx_lock(&vmmdev_mtx); sc = vmmdev_lookup(buf); - if (sc == NULL || sc->cdev == NULL) { - mtx_unlock(&vmmdev_mtx); - error = EINVAL; - goto out; - } - - /* - * The 'cdev' will be destroyed asynchronously when 'si_threadcount' - * goes down to 0 so we should not do it again in the callback. - * - * Setting 'sc->cdev' to NULL is also used to indicate that the VM - * is scheduled for destruction. - */ - cdev = sc->cdev; - sc->cdev = NULL; mtx_unlock(&vmmdev_mtx); - /* - * Schedule all cdevs to be destroyed: - * - * - any new operations on the 'cdev' will return an error (ENXIO). - * - * - when the 'si_threadcount' dwindles down to zero the 'cdev' will - * be destroyed and the callback will be invoked in a taskqueue - * context. - * - * - the 'devmem' cdevs are destroyed before the virtual machine 'cdev' - */ - SLIST_FOREACH(dsc, &sc->devmem, link) { - KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed")); - destroy_dev_sched_cb(dsc->cdev, devmem_destroy, dsc); - } - destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); - error = 0; - + error = vmm_destroy(sc); out: free(buf, M_VMMDEV); return (error);