Changeset View
Standalone View
lib/libvmmapi/vmmapi.c
Show All 29 Lines | ||||||||||||
#include <sys/capsicum.h> | #include <sys/capsicum.h> | |||||||||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | |||||||||||
#include <sys/ioctl.h> | #include <sys/ioctl.h> | |||||||||||
#include <sys/mman.h> | #include <sys/mman.h> | |||||||||||
#include <sys/linker.h> | #include <sys/linker.h> | |||||||||||
#include <sys/module.h> | #include <sys/module.h> | |||||||||||
#include <sys/_iovec.h> | #include <sys/_iovec.h> | |||||||||||
#include <sys/cpuset.h> | #include <sys/cpuset.h> | |||||||||||
#include <sys/domainset.h> | ||||||||||||
#include <capsicum_helpers.h> | #include <capsicum_helpers.h> | |||||||||||
#include <errno.h> | #include <errno.h> | |||||||||||
#include <stdbool.h> | #include <stdbool.h> | |||||||||||
#include <stdio.h> | #include <stdio.h> | |||||||||||
#include <stdlib.h> | #include <stdlib.h> | |||||||||||
#include <assert.h> | #include <assert.h> | |||||||||||
#include <string.h> | #include <string.h> | |||||||||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | vm_open(const char *name) | |||||||||||
vm = malloc(sizeof(struct vmctx) + strlen(name) + 1); | vm = malloc(sizeof(struct vmctx) + strlen(name) + 1); | |||||||||||
assert(vm != NULL); | assert(vm != NULL); | |||||||||||
vm->fd = -1; | vm->fd = -1; | |||||||||||
vm->memflags = 0; | vm->memflags = 0; | |||||||||||
vm->name = (char *)(vm + 1); | vm->name = (char *)(vm + 1); | |||||||||||
strcpy(vm->name, name); | strcpy(vm->name, name); | |||||||||||
memset(vm->memsegs, 0, sizeof(vm->memsegs)); | memset(vm->cpu_affinity, 0, sizeof(vm->cpu_affinity)); | |||||||||||
if ((vm->fd = vm_device_open(vm->name)) < 0) | if ((vm->fd = vm_device_open(vm->name)) < 0) | |||||||||||
goto err; | goto err; | |||||||||||
return (vm); | return (vm); | |||||||||||
err: | err: | |||||||||||
saved_errno = errno; | saved_errno = errno; | |||||||||||
free(vm); | free(vm); | |||||||||||
errno = saved_errno; | errno = saved_errno; | |||||||||||
▲ Show 20 Lines • Show All 129 Lines • ▼ Show 20 Lines | ||||||||||||
} | } | |||||||||||
int | int | |||||||||||
vm_get_guestmem_from_ctx(struct vmctx *ctx, char **guest_baseaddr, | vm_get_guestmem_from_ctx(struct vmctx *ctx, char **guest_baseaddr, | |||||||||||
size_t *lowmem_size, size_t *highmem_size) | size_t *lowmem_size, size_t *highmem_size) | |||||||||||
{ | { | |||||||||||
*guest_baseaddr = ctx->baseaddr; | *guest_baseaddr = ctx->baseaddr; | |||||||||||
*lowmem_size = ctx->memsegs[VM_MEMSEG_LOW].size; | *lowmem_size = ctx->lowmem_size; | |||||||||||
*highmem_size = ctx->memsegs[VM_MEMSEG_HIGH].size; | *highmem_size = ctx->highmem_size; | |||||||||||
return (0); | return (0); | |||||||||||
} | } | |||||||||||
int | int | |||||||||||
vm_munmap_memseg(struct vmctx *ctx, vm_paddr_t gpa, size_t len) | vm_munmap_memseg(struct vmctx *ctx, vm_paddr_t gpa, size_t len) | |||||||||||
{ | { | |||||||||||
struct vm_munmap munmap; | struct vm_munmap munmap; | |||||||||||
int error; | int error; | |||||||||||
Show All 39 Lines | cmpseg(size_t len, const char *str, size_t len2, const char *str2) | |||||||||||
if (len == len2) { | if (len == len2) { | |||||||||||
if ((!str && !str2) || (str && str2 && !strcmp(str, str2))) | if ((!str && !str2) || (str && str2 && !strcmp(str, str2))) | |||||||||||
return (0); | return (0); | |||||||||||
} | } | |||||||||||
return (-1); | return (-1); | |||||||||||
} | } | |||||||||||
static int | static int | |||||||||||
vm_alloc_memseg(struct vmctx *ctx, int segid, size_t len, const char *name) | vm_alloc_memseg(struct vmctx *ctx, int segid, size_t len, const char *name, | |||||||||||
int ds_policy, domainset_t *ds_mask) | ||||||||||||
{ | { | |||||||||||
struct vm_memseg memseg; | struct vm_memseg memseg; | |||||||||||
size_t n; | size_t n; | |||||||||||
int error; | int error; | |||||||||||
/* | /* | |||||||||||
* If the memory segment has already been created then just return. | * If the memory segment has already been created then just return. | |||||||||||
* This is the usual case for the SYSMEM segment created by userspace | * This is the usual case for the SYSMEM segment created by userspace | |||||||||||
Show All 11 Lines | if (memseg.len != 0) { | |||||||||||
} else { | } else { | |||||||||||
return (0); | return (0); | |||||||||||
} | } | |||||||||||
} | } | |||||||||||
bzero(&memseg, sizeof(struct vm_memseg)); | bzero(&memseg, sizeof(struct vm_memseg)); | |||||||||||
memseg.segid = segid; | memseg.segid = segid; | |||||||||||
memseg.len = len; | memseg.len = len; | |||||||||||
if (ds_mask == NULL) { | ||||||||||||
memseg.ds_policy = DOMAINSET_POLICY_INVALID; | ||||||||||||
} else { | ||||||||||||
memseg.ds_policy = ds_policy; | ||||||||||||
memseg.ds_mask = ds_mask; | ||||||||||||
memseg.ds_mask_size = sizeof(*ds_mask); | ||||||||||||
} | ||||||||||||
if (name != NULL) { | if (name != NULL) { | |||||||||||
n = strlcpy(memseg.name, name, sizeof(memseg.name)); | n = strlcpy(memseg.name, name, sizeof(memseg.name)); | |||||||||||
if (n >= sizeof(memseg.name)) { | if (n >= sizeof(memseg.name)) { | |||||||||||
errno = ENAMETOOLONG; | errno = ENAMETOOLONG; | |||||||||||
return (-1); | return (-1); | |||||||||||
} | } | |||||||||||
} | } | |||||||||||
Show All 19 Lines | if (n >= bufsize) { | |||||||||||
errno = ENAMETOOLONG; | errno = ENAMETOOLONG; | |||||||||||
error = -1; | error = -1; | |||||||||||
} | } | |||||||||||
} | } | |||||||||||
return (error); | return (error); | |||||||||||
} | } | |||||||||||
static int | static int | |||||||||||
setup_memory_segment(struct vmctx *ctx, vm_paddr_t gpa, size_t len, char *base) | map_memory_segment(struct vmctx *ctx, int segid, vm_paddr_t gpa, size_t len, | |||||||||||
size_t segoff, char *base) | ||||||||||||
{ | { | |||||||||||
char *ptr; | char *ptr; | |||||||||||
int error, flags; | int error, flags; | |||||||||||
/* Map 'len' bytes starting at 'gpa' in the guest address space */ | /* Map 'len' bytes starting at 'gpa' in the guest address space */ | |||||||||||
error = vm_mmap_memseg(ctx, gpa, VM_SYSMEM, gpa, len, PROT_ALL); | error = vm_mmap_memseg(ctx, gpa, segid, segoff, len, PROT_ALL); | |||||||||||
if (error) | if (error) | |||||||||||
return (error); | return (error); | |||||||||||
flags = MAP_SHARED | MAP_FIXED; | flags = MAP_SHARED | MAP_FIXED; | |||||||||||
if ((ctx->memflags & VM_MEM_F_INCORE) == 0) | if ((ctx->memflags & VM_MEM_F_INCORE) == 0) | |||||||||||
flags |= MAP_NOCORE; | flags |= MAP_NOCORE; | |||||||||||
/* mmap into the process address space on the host */ | /* mmap into the process address space on the host */ | |||||||||||
ptr = mmap(base + gpa, len, PROT_RW, flags, ctx->fd, gpa); | ptr = mmap(base + gpa, len, PROT_RW, flags, ctx->fd, gpa); | |||||||||||
if (ptr == MAP_FAILED) | if (ptr == MAP_FAILED) | |||||||||||
return (-1); | return (-1); | |||||||||||
return (0); | return (0); | |||||||||||
} | } | |||||||||||
int | int | |||||||||||
vm_setup_memory(struct vmctx *ctx, size_t memsize, enum vm_mmap_style vms) | vm_setup_memory(struct vmctx *ctx, struct vmdom *doms, int ndoms, | |||||||||||
size_t memsize, enum vm_mmap_style vms) | ||||||||||||
{ | { | |||||||||||
size_t objsize, len; | struct vmdom *dom, dom0; | |||||||||||
vm_paddr_t gpa; | struct vm_memseg memseg; | |||||||||||
size_t low_len, len, totalsize; | ||||||||||||
char *baseaddr, *ptr; | char *baseaddr, *ptr; | |||||||||||
int error; | int error, i, segid; | |||||||||||
vm_paddr_t gpa; | ||||||||||||
size_t objsize; | ||||||||||||
assert(vms == VM_MMAP_ALL); | assert(vms == VM_MMAP_ALL); | |||||||||||
/* | /* Sanity checks. */ | |||||||||||
* If 'memsize' cannot fit entirely in the 'lowmem' segment then create | if (ndoms < 0 || ndoms > VM_MAXMEMDOM) { | |||||||||||
* another 'highmem' segment above VM_HIGHMEM_BASE for the remainder. | error = -1; | |||||||||||
*/ | errno = EINVAL; | |||||||||||
return (error); | ||||||||||||
} | ||||||||||||
if (memsize > VM_LOWMEM_LIMIT) { | if (memsize > VM_LOWMEM_LIMIT) { | |||||||||||
ctx->memsegs[VM_MEMSEG_LOW].size = VM_LOWMEM_LIMIT; | totalsize = VM_HIGHMEM_BASE + (memsize - VM_LOWMEM_LIMIT); | |||||||||||
ctx->memsegs[VM_MEMSEG_HIGH].size = memsize - VM_LOWMEM_LIMIT; | ||||||||||||
objsize = VM_HIGHMEM_BASE + ctx->memsegs[VM_MEMSEG_HIGH].size; | ||||||||||||
} else { | } else { | |||||||||||
ctx->memsegs[VM_MEMSEG_LOW].size = memsize; | totalsize = memsize; | |||||||||||
ctx->memsegs[VM_MEMSEG_HIGH].size = 0; | ||||||||||||
objsize = memsize; | ||||||||||||
} | } | |||||||||||
error = vm_alloc_memseg(ctx, VM_SYSMEM, objsize, NULL); | /* | |||||||||||
if (error) | * If no domain information was passed, pretend | |||||||||||
return (error); | * that only one domain was requested. | |||||||||||
*/ | ||||||||||||
if (doms == NULL || ndoms == 0) { | ||||||||||||
memset(&dom0, 0, sizeof(struct vmdom)); | ||||||||||||
dom0.size = memsize; | ||||||||||||
dom0.ds_policy = DOMAINSET_POLICY_INVALID; | ||||||||||||
doms = &dom0; | ||||||||||||
ndoms = 1; | ||||||||||||
} | ||||||||||||
/* | /* | |||||||||||
* Stake out a contiguous region covering the guest physical memory | * Stake out a contiguous region covering the guest physical memory | |||||||||||
* and the adjoining guard regions. | * and the adjoining guard regions. | |||||||||||
*/ | */ | |||||||||||
len = VM_MMAP_GUARD_SIZE + objsize + VM_MMAP_GUARD_SIZE; | len = VM_MMAP_GUARD_SIZE + totalsize + VM_MMAP_GUARD_SIZE; | |||||||||||
ptr = mmap(NULL, len, PROT_NONE, MAP_GUARD | MAP_ALIGNED_SUPER, -1, 0); | ptr = mmap(NULL, len, PROT_NONE, MAP_GUARD | MAP_ALIGNED_SUPER, -1, 0); | |||||||||||
if (ptr == MAP_FAILED) | if (ptr == MAP_FAILED) | |||||||||||
return (-1); | return (-1); | |||||||||||
baseaddr = ptr + VM_MMAP_GUARD_SIZE; | baseaddr = ptr + VM_MMAP_GUARD_SIZE; | |||||||||||
if (ctx->memsegs[VM_MEMSEG_HIGH].size > 0) { | ||||||||||||
gpa = VM_HIGHMEM_BASE; | /* | |||||||||||
len = ctx->memsegs[VM_MEMSEG_HIGH].size; | * Allocate and map memory segments for the virutal machine. | |||||||||||
markjUnsubmitted Done Inline Actions
markj: | ||||||||||||
error = setup_memory_segment(ctx, gpa, len, baseaddr); | */ | |||||||||||
gpa = 0; | ||||||||||||
ctx->lowmem_size = 0; | ||||||||||||
ctx->highmem_size = 0; | ||||||||||||
for (i = 0; i < ndoms; i++) { | ||||||||||||
segid = VM_SYSMEM + i; | ||||||||||||
dom = &doms[i]; | ||||||||||||
/* | ||||||||||||
* Check if the memory segment already exists. | ||||||||||||
* If 'ndoms' is greater than one, refuse to proceed if the | ||||||||||||
* memseg already exists. If only one domain was requested, use | ||||||||||||
* the existing segment. | ||||||||||||
* | ||||||||||||
* Splitting existing memory segments is tedious and | ||||||||||||
* error-prone, which is why we don't support NUMA | ||||||||||||
* domains for bhyveload(8)-loaded VMs. | ||||||||||||
*/ | ||||||||||||
error = vm_get_memseg(ctx, segid, &len, memseg.name, | ||||||||||||
sizeof(memseg.name)); | ||||||||||||
if (error == 0 && len != 0) { | ||||||||||||
if (ndoms != 1) { | ||||||||||||
error = -1; | ||||||||||||
errno = EEXIST; | ||||||||||||
return (error); | ||||||||||||
} else { | ||||||||||||
doms[0].size = len; | ||||||||||||
} | ||||||||||||
} else { | ||||||||||||
objsize = dom->size; | ||||||||||||
/* Allocate new segment. */ | ||||||||||||
error = vm_alloc_memseg(ctx, segid, objsize, NULL, | ||||||||||||
dom->ds_policy, &dom->ds_mask); | ||||||||||||
if (error) | if (error) | |||||||||||
return (error); | return (error); | |||||||||||
} | } | |||||||||||
if (ctx->memsegs[VM_MEMSEG_LOW].size > 0) { | /* | |||||||||||
gpa = 0; | * If a domain is split by VM_LOWMEM_LIMIT then break | |||||||||||
len = ctx->memsegs[VM_MEMSEG_LOW].size; | * its segment mapping into two parts, one below VM_LOWMEM_LIMIT | |||||||||||
error = setup_memory_segment(ctx, gpa, len, baseaddr); | * and one above VM_HIGHMEM_BASE. | |||||||||||
*/ | ||||||||||||
if (gpa < VM_LOWMEM_LIMIT && | ||||||||||||
gpa + dom->size > VM_LOWMEM_LIMIT) { | ||||||||||||
low_len = VM_LOWMEM_LIMIT - gpa; | ||||||||||||
error = map_memory_segment(ctx, segid, gpa, low_len, 0, | ||||||||||||
baseaddr); | ||||||||||||
if (error) | if (error) | |||||||||||
return (error); | return (error); | |||||||||||
ctx->lowmem_size = VM_LOWMEM_LIMIT; | ||||||||||||
/* Map the remainder. */ | ||||||||||||
gpa = VM_HIGHMEM_BASE; | ||||||||||||
len = dom->size - low_len; | ||||||||||||
error = map_memory_segment(ctx, segid, gpa, len, | ||||||||||||
low_len, baseaddr); | ||||||||||||
if (error) | ||||||||||||
return (error); | ||||||||||||
} else { | ||||||||||||
len = dom->size; | ||||||||||||
error = map_memory_segment(ctx, segid, gpa, len, 0, | ||||||||||||
baseaddr); | ||||||||||||
if (error) | ||||||||||||
return (error); | ||||||||||||
} | } | |||||||||||
if (gpa < VM_LOWMEM_LIMIT) | ||||||||||||
ctx->lowmem_size += len; | ||||||||||||
else | ||||||||||||
ctx->highmem_size += len; | ||||||||||||
gpa += len; | ||||||||||||
} | ||||||||||||
ctx->baseaddr = baseaddr; | ctx->baseaddr = baseaddr; | |||||||||||
return (0); | return (0); | |||||||||||
} | } | |||||||||||
/* | /* | |||||||||||
* Returns a non-NULL pointer if [gaddr, gaddr+len) is entirely contained in | * Returns a non-NULL pointer if [gaddr, gaddr+len) is entirely contained in | |||||||||||
* the lowmem or highmem regions. | * the lowmem or highmem regions. | |||||||||||
* | * | |||||||||||
* In particular return NULL if [gaddr, gaddr+len) falls in guest MMIO region. | * In particular return NULL if [gaddr, gaddr+len) falls in guest MMIO region. | |||||||||||
* The instruction emulation code depends on this behavior. | * The instruction emulation code depends on this behavior. | |||||||||||
*/ | */ | |||||||||||
void * | void * | |||||||||||
vm_map_gpa(struct vmctx *ctx, vm_paddr_t gaddr, size_t len) | vm_map_gpa(struct vmctx *ctx, vm_paddr_t gaddr, size_t len) | |||||||||||
{ | { | |||||||||||
vm_size_t lowsize, highsize; | vm_size_t lowsize, highsize; | |||||||||||
lowsize = ctx->memsegs[VM_MEMSEG_LOW].size; | lowsize = ctx->lowmem_size; | |||||||||||
if (lowsize > 0) { | if (lowsize > 0) { | |||||||||||
if (gaddr < lowsize && len <= lowsize && gaddr + len <= lowsize) | if (gaddr < lowsize && len <= lowsize && gaddr + len <= lowsize) | |||||||||||
return (ctx->baseaddr + gaddr); | return (ctx->baseaddr + gaddr); | |||||||||||
} | } | |||||||||||
highsize = ctx->memsegs[VM_MEMSEG_HIGH].size; | highsize = ctx->highmem_size; | |||||||||||
if (highsize > 0 && gaddr >= VM_HIGHMEM_BASE) { | if (highsize > 0 && gaddr >= VM_HIGHMEM_BASE) { | |||||||||||
if (gaddr < VM_HIGHMEM_BASE + highsize && len <= highsize && | if (gaddr < VM_HIGHMEM_BASE + highsize && len <= highsize && | |||||||||||
gaddr + len <= VM_HIGHMEM_BASE + highsize) | gaddr + len <= VM_HIGHMEM_BASE + highsize) | |||||||||||
return (ctx->baseaddr + gaddr); | return (ctx->baseaddr + gaddr); | |||||||||||
} | } | |||||||||||
return (NULL); | return (NULL); | |||||||||||
} | } | |||||||||||
vm_paddr_t | vm_paddr_t | |||||||||||
vm_rev_map_gpa(struct vmctx *ctx, void *addr) | vm_rev_map_gpa(struct vmctx *ctx, void *addr) | |||||||||||
{ | { | |||||||||||
vm_paddr_t offaddr; | vm_paddr_t offaddr; | |||||||||||
vm_size_t lowsize, highsize; | vm_size_t lowsize, highsize; | |||||||||||
offaddr = (char *)addr - ctx->baseaddr; | offaddr = (char *)addr - ctx->baseaddr; | |||||||||||
lowsize = ctx->memsegs[VM_MEMSEG_LOW].size; | lowsize = ctx->lowmem_size; | |||||||||||
if (lowsize > 0) | if (lowsize > 0) | |||||||||||
if (offaddr <= lowsize) | if (offaddr <= lowsize) | |||||||||||
return (offaddr); | return (offaddr); | |||||||||||
highsize = ctx->memsegs[VM_MEMSEG_HIGH].size; | highsize = ctx->highmem_size; | |||||||||||
if (highsize > 0) | if (highsize > 0) | |||||||||||
if (offaddr >= VM_HIGHMEM_BASE && | if (offaddr >= VM_HIGHMEM_BASE && | |||||||||||
offaddr < VM_HIGHMEM_BASE + highsize) | offaddr < VM_HIGHMEM_BASE + highsize) | |||||||||||
return (offaddr); | return (offaddr); | |||||||||||
return ((vm_paddr_t)-1); | return ((vm_paddr_t)-1); | |||||||||||
} | } | |||||||||||
const char * | const char * | |||||||||||
vm_get_name(struct vmctx *ctx) | vm_get_name(struct vmctx *ctx) | |||||||||||
{ | { | |||||||||||
return (ctx->name); | return (ctx->name); | |||||||||||
} | } | |||||||||||
size_t | size_t | |||||||||||
vm_get_lowmem_size(struct vmctx *ctx) | vm_get_lowmem_size(struct vmctx *ctx) | |||||||||||
{ | { | |||||||||||
return (ctx->lowmem_size); | ||||||||||||
return (ctx->memsegs[VM_MEMSEG_LOW].size); | ||||||||||||
} | } | |||||||||||
vm_paddr_t | vm_paddr_t | |||||||||||
vm_get_highmem_base(struct vmctx *ctx __unused) | vm_get_highmem_base(struct vmctx *ctx __unused) | |||||||||||
{ | { | |||||||||||
return (VM_HIGHMEM_BASE); | return (VM_HIGHMEM_BASE); | |||||||||||
} | } | |||||||||||
size_t | size_t | |||||||||||
vm_get_highmem_size(struct vmctx *ctx) | vm_get_highmem_size(struct vmctx *ctx) | |||||||||||
{ | { | |||||||||||
return (ctx->highmem_size); | ||||||||||||
return (ctx->memsegs[VM_MEMSEG_HIGH].size); | ||||||||||||
} | } | |||||||||||
void * | void * | |||||||||||
vm_create_devmem(struct vmctx *ctx, int segid, const char *name, size_t len) | vm_create_devmem(struct vmctx *ctx, int segid, const char *name, size_t len) | |||||||||||
{ | { | |||||||||||
char pathname[MAXPATHLEN]; | char pathname[MAXPATHLEN]; | |||||||||||
size_t len2; | size_t len2; | |||||||||||
char *base, *ptr; | char *base, *ptr; | |||||||||||
int fd, error, flags; | int fd, error, flags; | |||||||||||
fd = -1; | fd = -1; | |||||||||||
ptr = MAP_FAILED; | ptr = MAP_FAILED; | |||||||||||
if (name == NULL || strlen(name) == 0) { | if (name == NULL || strlen(name) == 0) { | |||||||||||
errno = EINVAL; | errno = EINVAL; | |||||||||||
goto done; | goto done; | |||||||||||
} | } | |||||||||||
error = vm_alloc_memseg(ctx, segid, len, name); | error = vm_alloc_memseg(ctx, segid, len, name, 0, NULL); | |||||||||||
if (error) | if (error) | |||||||||||
goto done; | goto done; | |||||||||||
strlcpy(pathname, "/dev/vmm.io/", sizeof(pathname)); | strlcpy(pathname, "/dev/vmm.io/", sizeof(pathname)); | |||||||||||
strlcat(pathname, ctx->name, sizeof(pathname)); | strlcat(pathname, ctx->name, sizeof(pathname)); | |||||||||||
strlcat(pathname, ".", sizeof(pathname)); | strlcat(pathname, ".", sizeof(pathname)); | |||||||||||
strlcat(pathname, name, sizeof(pathname)); | strlcat(pathname, name, sizeof(pathname)); | |||||||||||
▲ Show 20 Lines • Show All 560 Lines • ▼ Show 20 Lines | vm_get_topology(struct vmctx *ctx, | |||||||||||
error = ioctl(ctx->fd, VM_GET_TOPOLOGY, &topology); | error = ioctl(ctx->fd, VM_GET_TOPOLOGY, &topology); | |||||||||||
if (error == 0) { | if (error == 0) { | |||||||||||
*sockets = topology.sockets; | *sockets = topology.sockets; | |||||||||||
*cores = topology.cores; | *cores = topology.cores; | |||||||||||
*threads = topology.threads; | *threads = topology.threads; | |||||||||||
*maxcpus = topology.maxcpus; | *maxcpus = topology.maxcpus; | |||||||||||
} | } | |||||||||||
return (error); | return (error); | |||||||||||
} | ||||||||||||
int | ||||||||||||
vm_set_domain_cpus(struct vmctx *ctx, int domain, cpuset_t *cpus) | ||||||||||||
Not Done Inline ActionsHmm, I'm kind of wary using a cpuset_t on a library boundary. The size isn't really fixed, CPU_SETSIZE might change again someday, as it has recently (256->1024). Does it make sense to have a function which sets the domain for a single vCPU, rather than a whole set? Then the caller can just do something like CPU_FOREACH_ISSET(cpu, &cpus) vm_set_domain_cpu(ctx, domain, cpu); but that doesn't work so well for vm_get_domain_cpus(), hmm. markj: Hmm, I'm kind of wary using a `cpuset_t` on a library boundary. The size isn't really fixed… | ||||||||||||
{ | ||||||||||||
struct vm_memseg memseg; | ||||||||||||
int error, segid; | ||||||||||||
cpuset_t domcpus; | ||||||||||||
int curdom; | ||||||||||||
segid = VM_SYSMEM + domain; | ||||||||||||
if (segid < VM_SYSMEM || segid >= VM_MAXSYSMEM) { | ||||||||||||
errno = EINVAL; | ||||||||||||
return (-1); | ||||||||||||
} | ||||||||||||
if (cpus == NULL) { | ||||||||||||
errno = EINVAL; | ||||||||||||
return (-1); | ||||||||||||
} | ||||||||||||
/* Check if memory segment for domain exists. */ | ||||||||||||
memset(&memseg, 0, sizeof(memseg)); | ||||||||||||
error = vm_get_memseg(ctx, VM_SYSMEM + domain, &memseg.len, memseg.name, | ||||||||||||
sizeof(memseg.name)); | ||||||||||||
if (error) | ||||||||||||
return (error); | ||||||||||||
if (memseg.len == 0) { | ||||||||||||
errno = ENOENT; | ||||||||||||
return (-1); | ||||||||||||
} | ||||||||||||
/* Check for overlapping cpusets. */ | ||||||||||||
for (curdom = 0; curdom < VM_MAXMEMDOM; curdom++) { | ||||||||||||
if (curdom == domain) | ||||||||||||
continue; | ||||||||||||
error = vm_get_domain_cpus(ctx, curdom, &domcpus); | ||||||||||||
if (error) { | ||||||||||||
if (errno == ENOENT) | ||||||||||||
break; | ||||||||||||
return (error); | ||||||||||||
} | ||||||||||||
if (CPU_OVERLAP(cpus, &domcpus)) { | ||||||||||||
errno = EEXIST; | ||||||||||||
return (-1); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
errno = 0; | ||||||||||||
ctx->cpu_affinity[domain] = *cpus; | ||||||||||||
Not Done Inline ActionsThis function does nothing but update libvmm internal state, based on the already-created memsegs. This state is only used to implement vm_get_domain_cpus(), which I think is only used when building the SRAT. What's the purposes of keeping track of these cpusets here? Isn't it just duplicating state that's already in the kernel? markj: This function does nothing but update libvmm internal state, based on the already-created… | ||||||||||||
Done Inline Actions
They're here just for building the SRAT. AFAICT the kernel doesn't keep track of affinities (only topology information), and ISTR that we discussed this already in an earlier iteration of this patch and concluded that this info should be kept out of the kernel. bnovkov: > This function does nothing but update libvmm internal state, based on the already-created… | ||||||||||||
Not Done Inline ActionsIt's a bit gross, but, can we just have the mappings exist as global variables? Similar to how we have guest_ncpus and the topology variables in bhyverun.h. (But I don't understand why you don't want to change acpi_build()? That seems less invasive than adding new library interfaces.) markj: It's a bit gross, but, can we just have the mappings exist as global variables? Similar to how… | ||||||||||||
return (0); | ||||||||||||
} | ||||||||||||
int | ||||||||||||
vm_get_domain_cpus(struct vmctx *ctx, int domain, cpuset_t *cpus) | ||||||||||||
{ | ||||||||||||
struct vm_memseg memseg; | ||||||||||||
size_t len; | ||||||||||||
int segid; | ||||||||||||
int error; | ||||||||||||
segid = VM_SYSMEM + domain; | ||||||||||||
if (segid < VM_SYSMEM || segid >= VM_MAXSYSMEM) { | ||||||||||||
errno = EINVAL; | ||||||||||||
return (-1); | ||||||||||||
} | ||||||||||||
if (cpus == NULL) { | ||||||||||||
errno = EINVAL; | ||||||||||||
return (-1); | ||||||||||||
} | ||||||||||||
memset(&memseg, 0, sizeof(memseg)); | ||||||||||||
/* Check if memory segment for domain exists. */ | ||||||||||||
error = vm_get_memseg(ctx, segid, &len, memseg.name, | ||||||||||||
sizeof(memseg.name)); | ||||||||||||
if (error) | ||||||||||||
return (error); | ||||||||||||
if (len == 0) { | ||||||||||||
errno = ENOENT; | ||||||||||||
return (-1); | ||||||||||||
} | ||||||||||||
*cpus = ctx->cpu_affinity[domain]; | ||||||||||||
return (0); | ||||||||||||
} | } | |||||||||||
int | int | |||||||||||
vm_limit_rights(struct vmctx *ctx) | vm_limit_rights(struct vmctx *ctx) | |||||||||||
{ | { | |||||||||||
cap_rights_t rights; | cap_rights_t rights; | |||||||||||
cap_rights_init(&rights, CAP_IOCTL, CAP_MMAP_RW); | cap_rights_init(&rights, CAP_IOCTL, CAP_MMAP_RW); | |||||||||||
Show All 37 Lines |