Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/vmm_dev.c
Show All 39 Lines | |||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/libkern.h> | #include <sys/libkern.h> | ||||
#include <sys/ioccom.h> | #include <sys/ioccom.h> | ||||
#include <sys/mman.h> | #include <sys/mman.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/rwlock.h> | |||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/vm_map.h> | #include <vm/vm_map.h> | ||||
#include <vm/vm_object.h> | #include <vm/vm_object.h> | ||||
#include <vm/vm_extern.h> | |||||
#include <machine/vmparam.h> | #include <machine/vmparam.h> | ||||
#include <machine/vmm.h> | #include <machine/vmm.h> | ||||
#include <machine/vmm_instruction_emul.h> | |||||
#include <machine/vmm_dev.h> | #include <machine/vmm_dev.h> | ||||
#include <machine/vmm_instruction_emul.h> | |||||
#include <machine/vmm_snapshot.h> | |||||
#include "vmm_lapic.h" | #include "vmm_lapic.h" | ||||
#include "vmm_stat.h" | #include "vmm_stat.h" | ||||
#include "vmm_mem.h" | #include "vmm_mem.h" | ||||
#include "io/ppt.h" | #include "io/ppt.h" | ||||
#include "io/vatpic.h" | #include "io/vatpic.h" | ||||
#include "io/vioapic.h" | #include "io/vioapic.h" | ||||
#include "io/vhpet.h" | #include "io/vhpet.h" | ||||
#include "io/vrtc.h" | #include "io/vrtc.h" | ||||
struct devmem_softc { | struct devmem_softc { | ||||
int segid; | int segid; | ||||
char *name; | char *name; | ||||
struct cdev *cdev; | struct cdev *cdev; | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
SLIST_ENTRY(devmem_softc) link; | SLIST_ENTRY(devmem_softc) link; | ||||
}; | }; | ||||
struct vmmdev_checkpoint_softc { | |||||
struct vmmdev_softc *vmmdev; | |||||
struct cdev *cdev; | |||||
}; | |||||
struct vmmdev_softc { | struct vmmdev_softc { | ||||
struct vm *vm; /* vm instance cookie */ | struct vm *vm; /* vm instance cookie */ | ||||
struct cdev *cdev; | struct cdev *cdev; | ||||
struct vmmdev_checkpoint_softc *vmmdev_checkpoint; | |||||
SLIST_ENTRY(vmmdev_softc) link; | SLIST_ENTRY(vmmdev_softc) link; | ||||
SLIST_HEAD(, devmem_softc) devmem; | SLIST_HEAD(, devmem_softc) devmem; | ||||
int flags; | int flags; | ||||
}; | }; | ||||
#define VSC_LINKED 0x01 | #define VSC_LINKED 0x01 | ||||
static SLIST_HEAD(, vmmdev_softc) head; | static SLIST_HEAD(, vmmdev_softc) head; | ||||
static unsigned pr_allow_flag; | static unsigned pr_allow_flag; | ||||
static struct mtx vmmdev_mtx; | static struct mtx vmmdev_mtx; | ||||
struct vm; | |||||
static MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); | static MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); | ||||
static MALLOC_DEFINE(M_VMMDEV_CHECKPOINT, "vmmdev_checkpoint", "vmmdev_checkpoint"); | |||||
SYSCTL_DECL(_hw_vmm); | SYSCTL_DECL(_hw_vmm); | ||||
static int vmm_priv_check(struct ucred *ucred); | static int vmm_priv_check(struct ucred *ucred); | ||||
static int devmem_create_cdev(const char *vmname, int id, char *devmem); | static int devmem_create_cdev(const char *vmname, int id, char *devmem); | ||||
static void devmem_destroy(void *arg); | static void devmem_destroy(void *arg); | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | |||||
static struct vmmdev_softc * | static struct vmmdev_softc * | ||||
vmmdev_lookup2(struct cdev *cdev) | vmmdev_lookup2(struct cdev *cdev) | ||||
{ | { | ||||
return (cdev->si_drv1); | return (cdev->si_drv1); | ||||
} | } | ||||
static struct vmmdev_checkpoint_softc * | |||||
vmmdev_checkpoint_lookup2(struct cdev *cdev) | |||||
{ | |||||
return (cdev->si_drv1); | |||||
} | |||||
static int | static int | ||||
vmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) | vmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) | ||||
{ | { | ||||
int error, off, c, prot; | int error, off, c, prot; | ||||
vm_paddr_t gpa, maxaddr; | vm_paddr_t gpa, maxaddr; | ||||
void *hpa, *cookie; | void *hpa, *cookie; | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
▲ Show 20 Lines • Show All 169 Lines • ▼ Show 20 Lines | vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, | ||||
struct vm_cpuset *vm_cpuset; | struct vm_cpuset *vm_cpuset; | ||||
struct vm_intinfo *vmii; | struct vm_intinfo *vmii; | ||||
struct vm_rtc_time *rtctime; | struct vm_rtc_time *rtctime; | ||||
struct vm_rtc_data *rtcdata; | struct vm_rtc_data *rtcdata; | ||||
struct vm_memmap *mm; | struct vm_memmap *mm; | ||||
struct vm_cpu_topology *topology; | struct vm_cpu_topology *topology; | ||||
uint64_t *regvals; | uint64_t *regvals; | ||||
int *regnums; | int *regnums; | ||||
struct vm_vmem_stat *vmem_stat; | |||||
struct vm_snapshot_req *snapshot_req; | |||||
error = vmm_priv_check(curthread->td_ucred); | error = vmm_priv_check(curthread->td_ucred); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
sc = vmmdev_lookup2(cdev); | sc = vmmdev_lookup2(cdev); | ||||
if (sc == NULL) | if (sc == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
Show All 36 Lines | case VM_RESTART_INSTRUCTION: | ||||
break; | break; | ||||
case VM_MAP_PPTDEV_MMIO: | case VM_MAP_PPTDEV_MMIO: | ||||
case VM_BIND_PPTDEV: | case VM_BIND_PPTDEV: | ||||
case VM_UNBIND_PPTDEV: | case VM_UNBIND_PPTDEV: | ||||
case VM_ALLOC_MEMSEG: | case VM_ALLOC_MEMSEG: | ||||
case VM_MMAP_MEMSEG: | case VM_MMAP_MEMSEG: | ||||
case VM_REINIT: | case VM_REINIT: | ||||
case VM_GET_VMEM_STAT: | |||||
/* | /* | ||||
* ioctls that operate on the entire virtual machine must | * ioctls that operate on the entire virtual machine must | ||||
* prevent all vcpus from running. | * prevent all vcpus from running. | ||||
*/ | */ | ||||
error = vcpu_lock_all(sc); | error = vcpu_lock_all(sc); | ||||
if (error) | if (error) | ||||
goto done; | goto done; | ||||
state_changed = 2; | state_changed = 2; | ||||
Show All 23 Lines | case VM_RUN: | ||||
break; | break; | ||||
case VM_SUSPEND: | case VM_SUSPEND: | ||||
vmsuspend = (struct vm_suspend *)data; | vmsuspend = (struct vm_suspend *)data; | ||||
error = vm_suspend(sc->vm, vmsuspend->how); | error = vm_suspend(sc->vm, vmsuspend->how); | ||||
break; | break; | ||||
case VM_REINIT: | case VM_REINIT: | ||||
error = vm_reinit(sc->vm); | error = vm_reinit(sc->vm); | ||||
break; | break; | ||||
case VM_VCPU_LOCK_ALL: | |||||
error = vcpu_lock_all(sc); | |||||
break; | |||||
case VM_VCPU_UNLOCK_ALL: | |||||
vcpu_unlock_all(sc); | |||||
error = 0; | |||||
break; | |||||
pmooney_pfmooney.com: I'm uncomfortable about providing ioctl interfaces to these locking operations. All of the… | |||||
Done Inline ActionsReworked the VM pause to block the threads in userspace. darius.mihaim_gmail.com: Reworked the VM pause to block the threads in userspace. | |||||
case VM_STAT_DESC: { | case VM_STAT_DESC: { | ||||
statdesc = (struct vm_stat_desc *)data; | statdesc = (struct vm_stat_desc *)data; | ||||
error = vmm_stat_desc_copy(statdesc->index, | error = vmm_stat_desc_copy(statdesc->index, | ||||
statdesc->desc, sizeof(statdesc->desc)); | statdesc->desc, sizeof(statdesc->desc)); | ||||
break; | break; | ||||
} | } | ||||
case VM_STATS: { | case VM_STATS: { | ||||
CTASSERT(MAX_VM_STATS >= MAX_VMM_STAT_ELEMS); | CTASSERT(MAX_VM_STATS >= MAX_VMM_STAT_ELEMS); | ||||
▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | case VM_MMAP_GETNEXT: | ||||
error = vm_mmap_getnext(sc->vm, &mm->gpa, &mm->segid, | error = vm_mmap_getnext(sc->vm, &mm->gpa, &mm->segid, | ||||
&mm->segoff, &mm->len, &mm->prot, &mm->flags); | &mm->segoff, &mm->len, &mm->prot, &mm->flags); | ||||
break; | break; | ||||
case VM_MMAP_MEMSEG: | case VM_MMAP_MEMSEG: | ||||
mm = (struct vm_memmap *)data; | mm = (struct vm_memmap *)data; | ||||
error = vm_mmap_memseg(sc->vm, mm->gpa, mm->segid, mm->segoff, | error = vm_mmap_memseg(sc->vm, mm->gpa, mm->segid, mm->segoff, | ||||
mm->len, mm->prot, mm->flags); | mm->len, mm->prot, mm->flags); | ||||
break; | break; | ||||
case VM_GET_VMEM_STAT: | |||||
vmem_stat = (struct vm_vmem_stat *)data; | |||||
error = vm_get_vmem_stat(sc->vm, vmem_stat); | |||||
break; | |||||
case VM_ALLOC_MEMSEG: | case VM_ALLOC_MEMSEG: | ||||
error = alloc_memseg(sc, (struct vm_memseg *)data); | error = alloc_memseg(sc, (struct vm_memseg *)data); | ||||
break; | break; | ||||
case VM_GET_MEMSEG: | case VM_GET_MEMSEG: | ||||
error = get_memseg(sc, (struct vm_memseg *)data); | error = get_memseg(sc, (struct vm_memseg *)data); | ||||
break; | break; | ||||
case VM_GET_REGISTER: | case VM_GET_REGISTER: | ||||
vmreg = (struct vm_register *)data; | vmreg = (struct vm_register *)data; | ||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | case VM_SET_TOPOLOGY: | ||||
topology = (struct vm_cpu_topology *)data; | topology = (struct vm_cpu_topology *)data; | ||||
error = vm_set_topology(sc->vm, topology->sockets, | error = vm_set_topology(sc->vm, topology->sockets, | ||||
topology->cores, topology->threads, topology->maxcpus); | topology->cores, topology->threads, topology->maxcpus); | ||||
break; | break; | ||||
case VM_GET_TOPOLOGY: | case VM_GET_TOPOLOGY: | ||||
topology = (struct vm_cpu_topology *)data; | topology = (struct vm_cpu_topology *)data; | ||||
vm_get_topology(sc->vm, &topology->sockets, &topology->cores, | vm_get_topology(sc->vm, &topology->sockets, &topology->cores, | ||||
&topology->threads, &topology->maxcpus); | &topology->threads, &topology->maxcpus); | ||||
error = 0; | error = 0; | ||||
case VM_SNAPSHOT_REQ: | |||||
snapshot_req = (struct vm_snapshot_req *)data; | |||||
error = vm_snapshot_req(sc->vm, &snapshot_req->meta); | |||||
break; | break; | ||||
case VM_RESTORE_TIME: | |||||
error = vm_restore_time(sc->vm); | |||||
Done Inline ActionsMissing a 'break' here now. jhb: Missing a 'break' here now. | |||||
break; | |||||
default: | default: | ||||
error = ENOTTY; | error = ENOTTY; | ||||
break; | break; | ||||
} | } | ||||
if (state_changed == 1) | if (state_changed == 1) | ||||
vcpu_unlock_one(sc, vcpu); | vcpu_unlock_one(sc, vcpu); | ||||
else if (state_changed == 2) | else if (state_changed == 2) | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | if (sc->vm != NULL) | ||||
vm_destroy(sc->vm); | vm_destroy(sc->vm); | ||||
if ((sc->flags & VSC_LINKED) != 0) { | if ((sc->flags & VSC_LINKED) != 0) { | ||||
mtx_lock(&vmmdev_mtx); | mtx_lock(&vmmdev_mtx); | ||||
SLIST_REMOVE(&head, sc, vmmdev_softc, link); | SLIST_REMOVE(&head, sc, vmmdev_softc, link); | ||||
mtx_unlock(&vmmdev_mtx); | mtx_unlock(&vmmdev_mtx); | ||||
} | } | ||||
if (sc->vmmdev_checkpoint != NULL) { | |||||
if (sc->vmmdev_checkpoint->cdev != NULL) | |||||
destroy_dev(sc->vmmdev_checkpoint->cdev); | |||||
free(sc->vmmdev_checkpoint, M_VMMDEV_CHECKPOINT); | |||||
} | |||||
free(sc, M_VMMDEV); | free(sc, M_VMMDEV); | ||||
} | } | ||||
static int | static int | ||||
sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) | sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error; | int error; | ||||
char buf[VM_MAX_NAMELEN]; | char buf[VM_MAX_NAMELEN]; | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | static struct cdevsw vmmdevsw = { | ||||
.d_name = "vmmdev", | .d_name = "vmmdev", | ||||
.d_version = D_VERSION, | .d_version = D_VERSION, | ||||
.d_ioctl = vmmdev_ioctl, | .d_ioctl = vmmdev_ioctl, | ||||
.d_mmap_single = vmmdev_mmap_single, | .d_mmap_single = vmmdev_mmap_single, | ||||
.d_read = vmmdev_rw, | .d_read = vmmdev_rw, | ||||
.d_write = vmmdev_rw, | .d_write = vmmdev_rw, | ||||
}; | }; | ||||
static inline int | |||||
mark_entry_cow(struct vm_map *vmmap, struct vm_map_entry *entry, struct vm_object *obj) | |||||
{ | |||||
if ((entry->eflags & MAP_ENTRY_NEEDS_COPY) == 0 && | |||||
(entry->protection & VM_PROT_WRITE) != 0) { | |||||
pmap_protect(vmmap->pmap, | |||||
entry->start, | |||||
entry->end, | |||||
entry->protection & ~VM_PROT_WRITE); | |||||
} | |||||
VM_OBJECT_WLOCK(obj); | |||||
vm_object_clear_flag(obj, OBJ_ONEMAPPING); | |||||
VM_OBJECT_WUNLOCK(obj); | |||||
/* Not sure which one is appropiate here. The commented one seems to | |||||
* break the virtio-net driver after checkpointing. Removing | |||||
* MAP_ENTRY_NEEDS_COPY seems to solve the problem, but I don't think | |||||
* it's complete like this. | |||||
*/ | |||||
//entry->eflags |= (MAP_ENTRY_COW|MAP_ENTRY_NEEDS_COPY); | |||||
entry->eflags |= (MAP_ENTRY_COW); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Search for a given vm_object in the vmspace of the current | |||||
* process and mark its entry COW | |||||
*/ | |||||
static int | static int | ||||
make_vmobject_cow(struct vm_object *obj) | |||||
{ | |||||
struct vmspace *vmspace; | |||||
struct vm_map *vmmap; | |||||
struct vm_map_entry *entry; | |||||
int found = 0; | |||||
/* get bhyverun proc vmspace */ | |||||
vmspace = vmspace_acquire_ref(curproc); | |||||
if (vmspace == NULL) { | |||||
printf("Failed acquire vmspace for bhyve-process\n"); | |||||
return (-1); | |||||
} | |||||
vmmap = &vmspace->vm_map; | |||||
vm_map_lock(vmmap); | |||||
if (vmmap->busy) | |||||
vm_map_wait_busy(vmmap); | |||||
for(entry = vmmap->header.next; entry != &vmmap->header; | |||||
entry = entry->next) { | |||||
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0 || | |||||
entry->object.vm_object == NULL) { | |||||
continue; | |||||
} | |||||
if (entry->object.vm_object == obj || /* if this is the object itself */ | |||||
entry->object.vm_object->backing_object == obj) { /* if it is backed by this object: shadow vm_object */ | |||||
if (entry->wired_count == 0 || | |||||
(entry->protection & VM_PROT_WRITE) == 0) | |||||
mark_entry_cow(vmmap, entry, obj); | |||||
found = 1; | |||||
} | |||||
} | |||||
vm_map_unlock(vmmap); | |||||
vmspace_free(vmspace); | |||||
if (found == 0) { | |||||
printf("Failed to find object in vmspace"); | |||||
return (-2); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
vmmdev_checkpoint_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize, | |||||
struct vm_object **objp, int nprot) | |||||
{ | |||||
struct vmmdev_checkpoint_softc *checkpoint_sc; | |||||
struct vmmdev_softc *sc; | |||||
vm_paddr_t gpa; | |||||
size_t len; | |||||
int flags, prot; | |||||
vm_ooffset_t segoff, first, last; | |||||
int error, found, segid; | |||||
bool sysmem; | |||||
first = *offset; | |||||
last = first + mapsize; | |||||
if ((nprot & PROT_EXEC) || first < 0 || first >= last) | |||||
return (EINVAL); | |||||
checkpoint_sc = vmmdev_checkpoint_lookup2(cdev); | |||||
if (checkpoint_sc == NULL) { | |||||
/* virtual machine is in the process of being created */ | |||||
return (EINVAL); | |||||
} | |||||
sc = checkpoint_sc->vmmdev; | |||||
error = vcpu_lock_all(sc); | |||||
if (error != 0) | |||||
return (error); | |||||
gpa = 0; | |||||
found = 0; | |||||
while (!found) { | |||||
error = vm_mmap_getnext(sc->vm, &gpa, &segid, &segoff, &len, | |||||
&flags, &prot); | |||||
if (error) | |||||
break; | |||||
if (first >= gpa && last <= gpa + len) | |||||
found = 1; | |||||
else | |||||
gpa += len; | |||||
} | |||||
if (found) { | |||||
error = vm_get_memseg(sc->vm, segid, &len, &sysmem, objp); | |||||
KASSERT(error == 0 && *objp != NULL, | |||||
("%s: invalid memory segment %d", __func__, segid)); | |||||
if (sysmem) { | |||||
vm_object_reference(*objp); | |||||
*offset = segoff + (first - gpa); | |||||
make_vmobject_cow(*objp); | |||||
} else { | |||||
error = EINVAL; | |||||
} | |||||
} | |||||
//make_vmobject_cow(*objp); | |||||
//vcpu_unlock_one(sc, VM_MAXCPU - 1); | |||||
vcpu_unlock_all(sc); | |||||
return (error); | |||||
} | |||||
static struct cdevsw vmmdev_checkpoint = { | |||||
.d_name = "vmmdev_checkpoint", | |||||
.d_version = D_VERSION, | |||||
.d_mmap_single = vmmdev_checkpoint_mmap_single, | |||||
}; | |||||
static int | |||||
sysctl_vmm_create(SYSCTL_HANDLER_ARGS) | sysctl_vmm_create(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error; | int error; | ||||
struct vm *vm; | struct vm *vm; | ||||
struct cdev *cdev; | struct cdev *cdev; | ||||
struct cdev *cdev_checkpoint; | |||||
struct vmmdev_softc *sc, *sc2; | struct vmmdev_softc *sc, *sc2; | ||||
struct vmmdev_checkpoint_softc *checkpoint_sc; | |||||
char buf[VM_MAX_NAMELEN]; | char buf[VM_MAX_NAMELEN]; | ||||
error = vmm_priv_check(req->td->td_ucred); | error = vmm_priv_check(req->td->td_ucred); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
strlcpy(buf, "beavis", sizeof(buf)); | strlcpy(buf, "beavis", sizeof(buf)); | ||||
error = sysctl_handle_string(oidp, buf, sizeof(buf), req); | error = sysctl_handle_string(oidp, buf, sizeof(buf), req); | ||||
if (error != 0 || req->newptr == NULL) | if (error != 0 || req->newptr == NULL) | ||||
return (error); | return (error); | ||||
mtx_lock(&vmmdev_mtx); | mtx_lock(&vmmdev_mtx); | ||||
sc = vmmdev_lookup(buf); | sc = vmmdev_lookup(buf); | ||||
mtx_unlock(&vmmdev_mtx); | mtx_unlock(&vmmdev_mtx); | ||||
if (sc != NULL) | if (sc != NULL) | ||||
return (EEXIST); | return (EEXIST); | ||||
error = vm_create(buf, &vm); | error = vm_create(buf, &vm); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
sc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); | sc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); | ||||
sc->vm = vm; | sc->vm = vm; | ||||
SLIST_INIT(&sc->devmem); | SLIST_INIT(&sc->devmem); | ||||
checkpoint_sc = malloc(sizeof(struct vmmdev_checkpoint_softc), M_VMMDEV_CHECKPOINT, M_WAITOK | M_ZERO); | |||||
/* | /* | ||||
* Lookup the name again just in case somebody sneaked in when we | * Lookup the name again just in case somebody sneaked in when we | ||||
* dropped the lock. | * dropped the lock. | ||||
*/ | */ | ||||
mtx_lock(&vmmdev_mtx); | mtx_lock(&vmmdev_mtx); | ||||
sc2 = vmmdev_lookup(buf); | sc2 = vmmdev_lookup(buf); | ||||
if (sc2 == NULL) { | if (sc2 == NULL) { | ||||
SLIST_INSERT_HEAD(&head, sc, link); | SLIST_INSERT_HEAD(&head, sc, link); | ||||
sc->flags |= VSC_LINKED; | sc->flags |= VSC_LINKED; | ||||
} | } | ||||
mtx_unlock(&vmmdev_mtx); | mtx_unlock(&vmmdev_mtx); | ||||
if (sc2 != NULL) { | if (sc2 != NULL) { | ||||
vmmdev_destroy(sc); | vmmdev_destroy(sc); | ||||
return (EEXIST); | return (EEXIST); | ||||
} | } | ||||
error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, NULL, | error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, NULL, | ||||
UID_ROOT, GID_WHEEL, 0600, "vmm/%s", buf); | UID_ROOT, GID_WHEEL, 0600, "vmm/%s", buf); | ||||
/* TODO: shouldn't be done inside lock? */ | |||||
Not Done Inline ActionsYou can't call make_dev* while holding locks. There is a race with setting si_drv1 that can be fixed by using make_dev_s instead of make_dev_p, but that would be a separate change from save/restore I think, jhb: You can't call make_dev* while holding locks. There is a race with setting si_drv1 that can be… | |||||
Done Inline ActionsRemoved the comment, since it was not related to save/restore. darius.mihaim_gmail.com: Removed the comment, since it was not related to save/restore. | |||||
if (error != 0) { | if (error != 0) { | ||||
vmmdev_destroy(sc); | vmmdev_destroy(sc); | ||||
return (error); | return (error); | ||||
} | } | ||||
mtx_lock(&vmmdev_mtx); | mtx_lock(&vmmdev_mtx); | ||||
sc->cdev = cdev; | sc->cdev = cdev; | ||||
sc->cdev->si_drv1 = sc; | sc->cdev->si_drv1 = sc; | ||||
mtx_unlock(&vmmdev_mtx); | |||||
/* device used for checkpointing memory */ | |||||
error = make_dev_p(MAKEDEV_CHECKNAME, &cdev_checkpoint, &vmmdev_checkpoint, NULL, | |||||
UID_ROOT, GID_WHEEL, 0600, "vmm/%s_mem", buf); | |||||
/* failed to create checkpoint device: free mem */ | |||||
if (error != 0) { | |||||
printf("Failed to create checkpoint device\n"); | |||||
free(checkpoint_sc, M_VMMDEV_CHECKPOINT); | |||||
return -1; | |||||
} | |||||
/* add device checkpoint info to vmmdev_softc */ | |||||
mtx_lock(&vmmdev_mtx); | |||||
sc->vmmdev_checkpoint = checkpoint_sc; | |||||
sc->vmmdev_checkpoint->cdev = cdev_checkpoint; | |||||
sc->vmmdev_checkpoint->cdev->si_drv1 = sc->vmmdev_checkpoint; | |||||
sc->vmmdev_checkpoint->vmmdev = sc; | |||||
mtx_unlock(&vmmdev_mtx); | mtx_unlock(&vmmdev_mtx); | ||||
return (0); | return (0); | ||||
} | } | ||||
SYSCTL_PROC(_hw_vmm, OID_AUTO, create, | SYSCTL_PROC(_hw_vmm, OID_AUTO, create, | ||||
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON, | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON, | ||||
NULL, 0, sysctl_vmm_create, "A", NULL); | NULL, 0, sysctl_vmm_create, "A", NULL); | ||||
▲ Show 20 Lines • Show All 113 Lines • Show Last 20 Lines |
I'm uncomfortable about providing ioctl interfaces to these locking operations. All of the other vCPU lock consumers will take the needed lock(s), perform their action in kernel space, and the release the lock(s) prior to returning to userspace. These ioctls break that model. Furthermore, since the save/restore operation is driven from userspace, where the vCPU run threads are operating, there's no reason why the quiescence cannot start there.