Changeset View
Standalone View
sys/amd64/vmm/vmm_dev.c
Show First 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | vmm_priv_check(struct ucred *ucred) | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
vcpu_lock_one(struct vmmdev_softc *sc, int vcpu) | vcpu_lock_one(struct vmmdev_softc *sc, int vcpu) | ||||
{ | { | ||||
int error; | int error; | ||||
if (vcpu < 0 || vcpu >= VM_MAXCPU) | if (vcpu < 0 || vcpu >= vm_get_maxcpus(sc->vm)) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); | error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
vcpu_unlock_one(struct vmmdev_softc *sc, int vcpu) | vcpu_unlock_one(struct vmmdev_softc *sc, int vcpu) | ||||
{ | { | ||||
enum vcpu_state state; | enum vcpu_state state; | ||||
state = vcpu_get_state(sc->vm, vcpu, NULL); | state = vcpu_get_state(sc->vm, vcpu, NULL); | ||||
if (state != VCPU_FROZEN) { | if (state != VCPU_FROZEN) { | ||||
panic("vcpu %s(%d) has invalid state %d", vm_name(sc->vm), | panic("vcpu %s(%d) has invalid state %d", vm_name(sc->vm), | ||||
vcpu, state); | vcpu, state); | ||||
} | } | ||||
vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); | vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); | ||||
} | } | ||||
static int | static int | ||||
vcpu_lock_all(struct vmmdev_softc *sc) | vcpu_lock_all(struct vmmdev_softc *sc) | ||||
{ | { | ||||
int error, vcpu; | int error, vcpu; | ||||
uint16_t maxcpus; | |||||
for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) { | maxcpus = vm_get_maxcpus(sc->vm); | ||||
for (vcpu = 0; vcpu < maxcpus; vcpu++) { | |||||
pmooney_pfmooney.com: Cache the max here? | |||||
error = vcpu_lock_one(sc, vcpu); | error = vcpu_lock_one(sc, vcpu); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
} | } | ||||
if (error) { | if (error) { | ||||
while (--vcpu >= 0) | while (--vcpu >= 0) | ||||
vcpu_unlock_one(sc, vcpu); | vcpu_unlock_one(sc, vcpu); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
vcpu_unlock_all(struct vmmdev_softc *sc) | vcpu_unlock_all(struct vmmdev_softc *sc) | ||||
{ | { | ||||
int vcpu; | int vcpu; | ||||
uint16_t maxcpus; | |||||
for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) | maxcpus = vm_get_maxcpus(sc->vm); | ||||
for (vcpu = 0; vcpu < maxcpus; vcpu++) | |||||
Done Inline ActionsCache the max? pmooney_pfmooney.com: Cache the max? | |||||
Done Inline ActionsI would hope the compiler would do that already without me having to explicitly code code this as Also doesn't this go away when we upstream the better locking implementation you have already done? rgrimes: I would hope the compiler would do that already without me having to explicitly code code this… | |||||
Not Done Inline ActionsThere would be some caching needed in vcpu_lock_all(), which would become part of the "write lock acquisition" path. It would be safe for the unlock path, since maxcpu would be effectively fixed while you held the write lock. pmooney_pfmooney.com: There would be some caching needed in vcpu_lock_all(), which would become part of the "write… | |||||
Not Done Inline ActionsYou can tell the compiler that the return value is always the same for a given input by marking the function prototype with '__pure2' or some such in which case the compiler would cache the return value and only call it once. What you have is fine though. jhb: You can tell the compiler that the return value is always the same for a given input by marking… | |||||
vcpu_unlock_one(sc, vcpu); | vcpu_unlock_one(sc, vcpu); | ||||
} | } | ||||
static struct vmmdev_softc * | static struct vmmdev_softc * | ||||
vmmdev_lookup(const char *name) | vmmdev_lookup(const char *name) | ||||
{ | { | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
Show All 18 Lines | |||||
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; | ||||
uint16_t lastcpu; | |||||
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); | ||||
/* | /* | ||||
* Get a read lock on the guest memory map by freezing any vcpu. | * Get a read lock on the guest memory map by freezing any vcpu. | ||||
*/ | */ | ||||
error = vcpu_lock_one(sc, VM_MAXCPU - 1); | lastcpu = vm_get_maxcpus(sc->vm) - 1; | ||||
error = vcpu_lock_one(sc, lastcpu); | |||||
Done Inline ActionsCache the max - 1 once, instead of recalculating it all over the function? pmooney_pfmooney.com: Cache the `max - 1` once, instead of recalculating it all over the function? | |||||
Done Inline ActionsWill do rgrimes: Will do | |||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); | prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); | ||||
maxaddr = vmm_sysmem_maxaddr(sc->vm); | maxaddr = vmm_sysmem_maxaddr(sc->vm); | ||||
while (uio->uio_resid > 0 && error == 0) { | while (uio->uio_resid > 0 && error == 0) { | ||||
gpa = uio->uio_offset; | gpa = uio->uio_offset; | ||||
off = gpa & PAGE_MASK; | off = gpa & PAGE_MASK; | ||||
c = min(uio->uio_resid, PAGE_SIZE - off); | c = min(uio->uio_resid, PAGE_SIZE - off); | ||||
/* | /* | ||||
* The VM has a hole in its physical memory map. If we want to | * The VM has a hole in its physical memory map. If we want to | ||||
* use 'dd' to inspect memory beyond the hole we need to | * use 'dd' to inspect memory beyond the hole we need to | ||||
* provide bogus data for memory that lies in the hole. | * provide bogus data for memory that lies in the hole. | ||||
* | * | ||||
* Since this device does not support lseek(2), dd(1) will | * Since this device does not support lseek(2), dd(1) will | ||||
* read(2) blocks of data to simulate the lseek(2). | * read(2) blocks of data to simulate the lseek(2). | ||||
*/ | */ | ||||
hpa = vm_gpa_hold(sc->vm, VM_MAXCPU - 1, gpa, c, prot, &cookie); | hpa = vm_gpa_hold(sc->vm, lastcpu, gpa, c, | ||||
prot, &cookie); | |||||
if (hpa == NULL) { | if (hpa == NULL) { | ||||
if (uio->uio_rw == UIO_READ && gpa < maxaddr) | if (uio->uio_rw == UIO_READ && gpa < maxaddr) | ||||
error = uiomove(__DECONST(void *, zero_region), | error = uiomove(__DECONST(void *, zero_region), | ||||
c, uio); | c, uio); | ||||
else | else | ||||
error = EFAULT; | error = EFAULT; | ||||
} else { | } else { | ||||
error = uiomove(hpa, c, uio); | error = uiomove(hpa, c, uio); | ||||
vm_gpa_release(cookie); | vm_gpa_release(cookie); | ||||
} | } | ||||
} | } | ||||
vcpu_unlock_one(sc, VM_MAXCPU - 1); | vcpu_unlock_one(sc, lastcpu); | ||||
return (error); | return (error); | ||||
} | } | ||||
CTASSERT(sizeof(((struct vm_memseg *)0)->name) >= SPECNAMELEN + 1); | CTASSERT(sizeof(((struct vm_memseg *)0)->name) >= SPECNAMELEN + 1); | ||||
static int | static int | ||||
get_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg) | get_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 186 Lines • ▼ Show 20 Lines | case VM_REINIT: | ||||
break; | break; | ||||
case VM_GET_MEMSEG: | case VM_GET_MEMSEG: | ||||
case VM_MMAP_GETNEXT: | case VM_MMAP_GETNEXT: | ||||
/* | /* | ||||
* Lock a vcpu to make sure that the memory map cannot be | * Lock a vcpu to make sure that the memory map cannot be | ||||
* modified while it is being inspected. | * modified while it is being inspected. | ||||
*/ | */ | ||||
vcpu = VM_MAXCPU - 1; | vcpu = vm_get_maxcpus(sc->vm) - 1; | ||||
error = vcpu_lock_one(sc, vcpu); | error = vcpu_lock_one(sc, vcpu); | ||||
if (error) | if (error) | ||||
goto done; | goto done; | ||||
state_changed = 1; | state_changed = 1; | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 343 Lines • ▼ Show 20 Lines | |||||
vmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize, | vmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize, | ||||
struct vm_object **objp, int nprot) | struct vm_object **objp, int nprot) | ||||
{ | { | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
vm_paddr_t gpa; | vm_paddr_t gpa; | ||||
size_t len; | size_t len; | ||||
vm_ooffset_t segoff, first, last; | vm_ooffset_t segoff, first, last; | ||||
int error, found, segid; | int error, found, segid; | ||||
uint16_t lastcpu; | |||||
bool sysmem; | bool sysmem; | ||||
error = vmm_priv_check(curthread->td_ucred); | error = vmm_priv_check(curthread->td_ucred); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
first = *offset; | first = *offset; | ||||
last = first + mapsize; | last = first + mapsize; | ||||
if ((nprot & PROT_EXEC) || first < 0 || first >= last) | if ((nprot & PROT_EXEC) || first < 0 || first >= last) | ||||
return (EINVAL); | return (EINVAL); | ||||
sc = vmmdev_lookup2(cdev); | sc = vmmdev_lookup2(cdev); | ||||
if (sc == NULL) { | if (sc == NULL) { | ||||
/* virtual machine is in the process of being created */ | /* virtual machine is in the process of being created */ | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* | /* | ||||
* Get a read lock on the guest memory map by freezing any vcpu. | * Get a read lock on the guest memory map by freezing any vcpu. | ||||
*/ | */ | ||||
error = vcpu_lock_one(sc, VM_MAXCPU - 1); | lastcpu = vm_get_maxcpus(sc->vm) - 1; | ||||
error = vcpu_lock_one(sc, lastcpu); | |||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
gpa = 0; | gpa = 0; | ||||
found = 0; | found = 0; | ||||
while (!found) { | while (!found) { | ||||
error = vm_mmap_getnext(sc->vm, &gpa, &segid, &segoff, &len, | error = vm_mmap_getnext(sc->vm, &gpa, &segid, &segoff, &len, | ||||
NULL, NULL); | NULL, NULL); | ||||
Show All 12 Lines | KASSERT(error == 0 && *objp != NULL, | ||||
("%s: invalid memory segment %d", __func__, segid)); | ("%s: invalid memory segment %d", __func__, segid)); | ||||
if (sysmem) { | if (sysmem) { | ||||
vm_object_reference(*objp); | vm_object_reference(*objp); | ||||
*offset = segoff + (first - gpa); | *offset = segoff + (first - gpa); | ||||
} else { | } else { | ||||
error = EINVAL; | error = EINVAL; | ||||
} | } | ||||
} | } | ||||
vcpu_unlock_one(sc, VM_MAXCPU - 1); | vcpu_unlock_one(sc, lastcpu); | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
vmmdev_destroy(void *arg) | vmmdev_destroy(void *arg) | ||||
{ | { | ||||
struct vmmdev_softc *sc = arg; | struct vmmdev_softc *sc = arg; | ||||
struct devmem_softc *dsc; | struct devmem_softc *dsc; | ||||
▲ Show 20 Lines • Show All 182 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
devmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len, | devmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len, | ||||
struct vm_object **objp, int nprot) | struct vm_object **objp, int nprot) | ||||
{ | { | ||||
struct devmem_softc *dsc; | struct devmem_softc *dsc; | ||||
vm_ooffset_t first, last; | vm_ooffset_t first, last; | ||||
size_t seglen; | size_t seglen; | ||||
int error; | int error; | ||||
uint16_t lastcpu; | |||||
bool sysmem; | bool sysmem; | ||||
dsc = cdev->si_drv1; | dsc = cdev->si_drv1; | ||||
if (dsc == NULL) { | if (dsc == NULL) { | ||||
/* 'cdev' has been created but is not ready for use */ | /* 'cdev' has been created but is not ready for use */ | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
first = *offset; | first = *offset; | ||||
last = *offset + len; | last = *offset + len; | ||||
if ((nprot & PROT_EXEC) || first < 0 || first >= last) | if ((nprot & PROT_EXEC) || first < 0 || first >= last) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = vcpu_lock_one(dsc->sc, VM_MAXCPU - 1); | lastcpu = vm_get_maxcpus(dsc->sc->vm) - 1; | ||||
error = vcpu_lock_one(dsc->sc, lastcpu); | |||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
error = vm_get_memseg(dsc->sc->vm, dsc->segid, &seglen, &sysmem, objp); | error = vm_get_memseg(dsc->sc->vm, dsc->segid, &seglen, &sysmem, objp); | ||||
KASSERT(error == 0 && !sysmem && *objp != NULL, | KASSERT(error == 0 && !sysmem && *objp != NULL, | ||||
("%s: invalid devmem segment %d", __func__, dsc->segid)); | ("%s: invalid devmem segment %d", __func__, dsc->segid)); | ||||
vcpu_unlock_one(dsc->sc, VM_MAXCPU - 1); | vcpu_unlock_one(dsc->sc, lastcpu); | ||||
if (seglen >= last) { | if (seglen >= last) { | ||||
vm_object_reference(*objp); | vm_object_reference(*objp); | ||||
return (0); | return (0); | ||||
} else { | } else { | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 54 Lines • Show Last 20 Lines |
Cache the max here?