tech@, tl;dr: standardize vmd/vmm/vmctl on counting memory in bytes at all times instead of a mix of MiB and bytes.
There's some design friction between vmd(8)/vmctl(8) and vmm(4). For instance, the user-facing code deals in MiB, but internally a vm's memory ranges are defined in terms of bytes...but only after being converted at vm launch. Consequently, at different points in vmd's lifecycle, the same struct member for storing a vm's requested memory size contains a value in bytes OR in MiB meaning any code accessing the value needs to be contextually aware of if/when the value must be scaled. Given we dropped vmm(4) on i386 awhile ago, let's make use of 64-bit values! Plus this helps my other queued up changes simpler as they can avoid confusing scaling at points. There *is* some existing code duplication between vmd/vmctl related to parsing user provided memory values via scan_scaled(3), but I'm not looking to consolidate that now. If you're going to test, you'll need to build the kernel and either copy or link the patched vmmvar.h into /usr/include/machine/ before building vmd(8)/vmctl(8). (Don't forget to actually boot the kernel.) Otherwise, looking for ok's so I can continue squashing a few bugs in vmd that will be easier/cleaner to fix once this goes in. While the diff looks long-ish, it shouldn't require deep vmm/vmd knowledge to help review ;) -dv diff refs/heads/master refs/heads/vmd-bytes blob - 765fc19bca559dbfd83cd14c48dee94f86c4b3cc blob + 699798c1bbffafe7074fea43755ef7e20f073a90 --- sys/arch/amd64/amd64/vmm.c +++ sys/arch/amd64/amd64/vmm.c @@ -1575,7 +1575,7 @@ vm_create_check_mem_ranges(struct vm_create_params *vc { size_t i, memsize = 0; struct vm_mem_range *vmr, *pvmr; - const paddr_t maxgpa = (uint64_t)VMM_MAX_VM_MEM_SIZE * 1024 * 1024; + const paddr_t maxgpa = VMM_MAX_VM_MEM_SIZE; if (vcp->vcp_nmemranges == 0 || vcp->vcp_nmemranges > VMM_MAX_MEM_RANGES) blob - 94bb172832d4c2847b1e83ebb9cc05538db6ac80 blob + 012a023943b9fbc70339166889070ff0b4619046 --- sys/arch/amd64/include/vmmvar.h +++ sys/arch/amd64/include/vmmvar.h @@ -31,7 +31,7 @@ #define VMM_MAX_KERNEL_PATH 128 #define VMM_MAX_VCPUS 512 #define VMM_MAX_VCPUS_PER_VM 64 -#define VMM_MAX_VM_MEM_SIZE 32768 +#define VMM_MAX_VM_MEM_SIZE 32L * 1024 * 1024 * 1024 /* 32 GiB */ #define VMM_MAX_NICS_PER_VM 4 #define VMM_PCI_MMIO_BAR_BASE 0xF0000000ULL blob - 0f7e4329a00d54a64fe41e1fb2bd2afcbaa9d68a blob + 01d466f613621410803646cf3db49780f6ab9166 --- usr.sbin/vmctl/main.c +++ usr.sbin/vmctl/main.c @@ -404,24 +404,39 @@ parse_network(struct parse_result *res, char *word) int parse_size(struct parse_result *res, char *word) { - long long val = 0; + char result[FMT_SCALED_STRSIZE]; + long long val = 0; if (word != NULL) { if (scan_scaled(word, &val) != 0) { - warn("invalid size: %s", word); + warn("invalid memory size: %s", word); return (-1); } } if (val < (1024 * 1024)) { - warnx("size must be at least one megabyte"); + warnx("memory size must be at least 1M"); return (-1); - } else - res->size = val / 1024 / 1024; + } - if ((res->size * 1024 * 1024) != val) - warnx("size rounded to %lld megabytes", res->size); + if (val > VMM_MAX_VM_MEM_SIZE) { + if (fmt_scaled(val, result) == 0) + warnx("memory size too large (limit is %s)", result); + else + warnx("memory size too large"); + return (-1); + } + /* Round down to the megabyte. */ + res->size = (val / (1024 * 1024)) * (1024 * 1024); + + if (res->size != (size_t) val) { + if (fmt_scaled(res->size, result) == 0) + warnx("memory size rounded to %s", result); + else + warnx("memory size rounded to %zu bytes", res->size); + } + return (0); } blob - 4c0b62fc6e16adbeb5cf951dcafbaebdbc356da8 blob + 15e6dd89ec15fa2501dcf6539c9ae9d90879ba56 --- usr.sbin/vmctl/vmctl.c +++ usr.sbin/vmctl/vmctl.c @@ -73,7 +73,7 @@ struct imsgbuf *ibuf; * ENOMEM if a memory allocation failure occurred. */ int -vm_start(uint32_t start_id, const char *name, int memsize, int nnics, +vm_start(uint32_t start_id, const char *name, size_t memsize, int nnics, char **nics, int ndisks, char **disks, int *disktypes, char *kernel, char *iso, char *instance, unsigned int bootdevice) { @@ -122,7 +122,7 @@ vm_start(uint32_t start_id, const char *name, int mems /* * XXX: vmd(8) fills in the actual memory ranges. vmctl(8) - * just passes in the actual memory size in MB here. + * just passes in the actual memory size here. */ vcp->vcp_nmemranges = 1; vcp->vcp_memranges[0].vmr_size = memsize; blob - 4fd2b787debb3a0e6bea0eced9508fcce3a09991 blob + 7d44c88f8fac106508807edcad393334d806edf2 --- usr.sbin/vmctl/vmctl.h +++ usr.sbin/vmctl/vmctl.h @@ -48,7 +48,7 @@ struct parse_result { char *name; char *path; char *isopath; - long long size; + size_t size; int nifs; char **nets; int nnets; @@ -93,7 +93,7 @@ int open_imagefile(int, const char *, int, int create_imagefile(int, const char *, const char *, long, const char **); int create_raw_imagefile(const char *, long); int create_qc2_imagefile(const char *, const char *, long); -int vm_start(uint32_t, const char *, int, int, char **, int, +int vm_start(uint32_t, const char *, size_t, int, char **, int, char **, int *, char *, char *, char *, unsigned int); int vm_start_complete(struct imsg *, int *, int); void terminate_vm(uint32_t, const char *, unsigned int); blob - ebebbf24750b075414dc872d1290a0bf9c20d52b blob + ba86d1c7fe05f77247ee34c5323e8600e1fb0a6b --- usr.sbin/vmd/parse.y +++ usr.sbin/vmd/parse.y @@ -1248,26 +1248,42 @@ symget(const char *nam) ssize_t parse_size(char *word, int64_t val) { + char result[FMT_SCALED_STRSIZE]; ssize_t size; long long res; if (word != NULL) { if (scan_scaled(word, &res) != 0) { - log_warn("invalid size: %s", word); + log_warn("invalid memory size: %s", word); return (-1); } val = (int64_t)res; } if (val < (1024 * 1024)) { - log_warnx("size must be at least one megabyte"); + log_warnx("memory size must be at least 1M"); return (-1); - } else - size = val / 1024 / 1024; + } - if ((size * 1024 * 1024) != val) - log_warnx("size rounded to %zd megabytes", size); + if (val > VMM_MAX_VM_MEM_SIZE) { + if (fmt_scaled(val, result) == 0) + log_warnx("memory size too large (limit is %s)", + result); + else + log_warnx("memory size too large"); + return (-1); + } + /* Round down to the megabyte. */ + size = (val / (1024 * 1024)) * (1024 * 1024); + + if (size != val) { + if (fmt_scaled(size, result) == 0) + log_warnx("memory size rounded to %s", result); + else + log_warnx("memory size rounded to %zd bytes", size); + } + return ((ssize_t)size); } blob - 55d938ed1d118f204dc19492148fc171080c6961 blob + f59c2a834078c2e47a5b4d71de23f2b22ff2c15c --- usr.sbin/vmd/vm.c +++ usr.sbin/vmd/vm.c @@ -871,15 +871,13 @@ vcpu_reset(uint32_t vmid, uint32_t vcpu_id, struct vcp void create_memory_map(struct vm_create_params *vcp) { - size_t len, mem_bytes, mem_mb; + size_t len, mem_bytes; - mem_mb = vcp->vcp_memranges[0].vmr_size; + mem_bytes = vcp->vcp_memranges[0].vmr_size; vcp->vcp_nmemranges = 0; - if (mem_mb < 1 || mem_mb > VMM_MAX_VM_MEM_SIZE) + if (mem_bytes < 1 || mem_bytes > VMM_MAX_VM_MEM_SIZE) return; - mem_bytes = mem_mb * 1024 * 1024; - /* First memory region: 0 - LOWMEM_KB (DOS low mem) */ len = LOWMEM_KB * 1024; vcp->vcp_memranges[0].vmr_gpa = 0x0; blob - 5f33b64831792420fd9231f0b76b11f31ad6b31c blob + 4d2a5a4aecf78957b3b035c1a5b5e9449f6fe769 --- usr.sbin/vmd/vmd.h +++ usr.sbin/vmd/vmd.h @@ -56,7 +56,7 @@ #define MAX_TAP 256 #define NR_BACKLOG 5 #define VMD_SWITCH_TYPE "bridge" -#define VM_DEFAULT_MEMORY 512 +#define VM_DEFAULT_MEMORY 512 * 1024 * 1024 /* 512 MiB */ #define VMD_DEFAULT_STAGGERED_START_DELAY 30