Index: lib/libvmmapi/vmmapi.h =================================================================== --- lib/libvmmapi/vmmapi.h +++ lib/libvmmapi/vmmapi.h @@ -211,6 +211,12 @@ int vm_suspended_cpus(struct vmctx *ctx, cpuset_t *cpus); int vm_activate_cpu(struct vmctx *ctx, int vcpu); +/* Cpu topology */ +int vm_set_topology(struct vmctx *ctx, uint16_t sockets, uint16_t cores, \ + uint16_t threads, uint16_t maxcpus); +int vm_get_topology(struct vmctx *ctx, uint16_t *sockets, uint16_t *cores, \ + uint16_t *threads, uint16_t *maxcpus); + /* * FreeBSD specific APIs */ Index: lib/libvmmapi/vmmapi.c =================================================================== --- lib/libvmmapi/vmmapi.c +++ lib/libvmmapi/vmmapi.c @@ -1420,6 +1420,38 @@ } int +vm_set_topology(struct vmctx *ctx, \ + uint16_t sockets, uint16_t cores, uint16_t threads, uint16_t maxcpus) +{ + struct vm_cpu_topology topology; + + bzero(&topology, sizeof (struct vm_cpu_topology)); + topology.sockets = sockets; + topology.cores = cores; + topology.threads = threads; + topology.maxcpus = maxcpus; + return (ioctl(ctx->fd, VM_SET_TOPOLOGY, &topology)); +} + +int +vm_get_topology(struct vmctx *ctx, \ + uint16_t *sockets, uint16_t *cores, uint16_t *threads, uint16_t *maxcpus) +{ + struct vm_cpu_topology topology; + int error; + + bzero(&topology, sizeof (struct vm_cpu_topology)); + error = ioctl(ctx->fd, VM_GET_TOPOLOGY, &topology); + if (error == 0) { + *sockets = topology.sockets; + *cores = topology.cores; + *threads = topology.threads; + *maxcpus = topology.maxcpus; + } + return (error); +} + +int vm_get_device_fd(struct vmctx *ctx) { @@ -1446,7 +1478,7 @@ VM_GET_HPET_CAPABILITIES, VM_GET_GPA_PMAP, VM_GLA2GPA, VM_ACTIVATE_CPU, VM_GET_CPUS, VM_SET_INTINFO, VM_GET_INTINFO, VM_RTC_WRITE, VM_RTC_READ, VM_RTC_SETTIME, VM_RTC_GETTIME, - VM_RESTART_INSTRUCTION }; + VM_RESTART_INSTRUCTION, VM_SET_TOPOLOGY, VM_GET_TOPOLOGY }; if (len == NULL) { cmds = malloc(sizeof(vm_ioctl_cmds)); Index: sys/amd64/include/vmm.h =================================================================== --- sys/amd64/include/vmm.h +++ sys/amd64/include/vmm.h @@ -181,6 +181,10 @@ void vm_destroy(struct vm *vm); int vm_reinit(struct vm *vm); const char *vm_name(struct vm *vm); +void vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores, + uint16_t *threads, uint16_t *maxcpus); +int vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores, + uint16_t threads, uint16_t maxcpus); /* * APIs that modify the guest memory map require all vcpus to be frozen. Index: sys/amd64/include/vmm_dev.h =================================================================== --- sys/amd64/include/vmm_dev.h +++ sys/amd64/include/vmm_dev.h @@ -218,6 +218,13 @@ uint8_t value; }; +struct vm_cpu_topology { + uint16_t sockets; + uint16_t cores; + uint16_t threads; + uint16_t maxcpus; +}; + enum { /* general routines */ IOCNUM_ABIVERS = 0, @@ -273,6 +280,10 @@ IOCNUM_GET_X2APIC_STATE = 61, IOCNUM_GET_HPET_CAPABILITIES = 62, + /* CPU Topology */ + IOCNUM_SET_TOPOLOGY = 63, + IOCNUM_GET_TOPOLOGY = 64, + /* legacy interrupt injection */ IOCNUM_ISA_ASSERT_IRQ = 80, IOCNUM_ISA_DEASSERT_IRQ = 81, @@ -362,6 +373,10 @@ _IOWR('v', IOCNUM_GET_X2APIC_STATE, struct vm_x2apic) #define VM_GET_HPET_CAPABILITIES \ _IOR('v', IOCNUM_GET_HPET_CAPABILITIES, struct vm_hpet_cap) +#define VM_SET_TOPOLOGY \ + _IOW('v', IOCNUM_SET_TOPOLOGY, struct vm_cpu_topology) +#define VM_GET_TOPOLOGY \ + _IOR('v', IOCNUM_GET_TOPOLOGY, struct vm_cpu_topology) #define VM_GET_GPA_PMAP \ _IOWR('v', IOCNUM_GET_GPA_PMAP, struct vm_gpa_pte) #define VM_GLA2GPA \ Index: sys/amd64/vmm/vmm.c =================================================================== --- sys/amd64/vmm/vmm.c +++ sys/amd64/vmm/vmm.c @@ -165,6 +165,11 @@ struct vmspace *vmspace; /* (o) guest's address space */ char name[VM_MAX_NAMELEN]; /* (o) virtual machine name */ struct vcpu vcpu[VM_MAXCPU]; /* (i) guest vcpus */ + /* The following describe the vm cpu topology */ + uint16_t sockets; /* (o) num of sockets */ + uint16_t cores; /* (o) num of cores/socket */ + uint16_t threads; /* (o) num of threads/core */ + uint16_t maxcpus; /* (o) max pluggable cpus */ }; static int vmm_initialized; @@ -423,6 +428,9 @@ vcpu_init(vm, i, create); } +extern u_int cores_per_package; +extern u_int threads_per_core; + int vm_create(const char *name, struct vm **retvm) { @@ -448,6 +456,11 @@ vm->vmspace = vmspace; mtx_init(&vm->rendezvous_mtx, "vm rendezvous lock", 0, MTX_DEF); + vm->sockets = 1; + vm->cores = cores_per_package; /* XXX backwards compatibility */ + vm->threads = threads_per_core; /* XXX backwards compatibility */ + vm->maxcpus = 0; /* XXX not implemented */ + vm_init(vm, true); *retvm = vm; @@ -454,6 +467,32 @@ return (0); } +void +vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores, + uint16_t *threads, uint16_t *maxcpus) +{ + *sockets = vm->sockets; + *cores = vm->cores; + *threads = vm->threads; + *maxcpus = vm->maxcpus; +} + +int +vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores, + uint16_t threads, uint16_t maxcpus) +{ + if (maxcpus != 0) + return (EINVAL); /* XXX remove when supported */ + if ((sockets * cores * threads) > VM_MAXCPU) + return (EINVAL); + /* XXX need to check sockets * cores * threads == vCPU, how? */ + vm->sockets = sockets; + vm->cores = cores; + vm->threads = threads; + vm->maxcpus = maxcpus; + return(0); +} + static void vm_cleanup(struct vm *vm, bool destroy) { Index: sys/amd64/vmm/vmm_dev.c =================================================================== --- sys/amd64/vmm/vmm_dev.c +++ sys/amd64/vmm/vmm_dev.c @@ -315,6 +315,7 @@ struct vm_rtc_time *rtctime; struct vm_rtc_data *rtcdata; struct vm_memmap *mm; + struct vm_cpu_topology *topology; sc = vmmdev_lookup2(cdev); if (sc == NULL) @@ -642,6 +643,17 @@ case VM_RESTART_INSTRUCTION: error = vm_restart_instruction(sc->vm, vcpu); break; + case VM_SET_TOPOLOGY: + topology = (struct vm_cpu_topology *)data; + error = vm_set_topology(sc->vm, topology->sockets, + topology->cores, topology->threads, topology->maxcpus); + break; + case VM_GET_TOPOLOGY: + topology = (struct vm_cpu_topology *)data; + vm_get_topology(sc->vm, &topology->sockets, &topology->cores, + &topology->threads, &topology->maxcpus); + error = 0; + break; default: error = ENOTTY; break; Index: sys/amd64/vmm/x86.c =================================================================== --- sys/amd64/vmm/x86.c +++ sys/amd64/vmm/x86.c @@ -63,12 +63,12 @@ /* * The default CPU topology is a single thread per package. */ -static u_int threads_per_core = 1; -SYSCTL_UINT(_hw_vmm_topology, OID_AUTO, threads_per_core, CTLFLAG_RDTUN, +u_int threads_per_core = 1; +SYSCTL_UINT(_hw_vmm_topology, OID_AUTO, threads_per_core, CTLFLAG_RW, &threads_per_core, 0, NULL); -static u_int cores_per_package = 1; -SYSCTL_UINT(_hw_vmm_topology, OID_AUTO, cores_per_package, CTLFLAG_RDTUN, +u_int cores_per_package = 1; +SYSCTL_UINT(_hw_vmm_topology, OID_AUTO, cores_per_package, CTLFLAG_RW, &cores_per_package, 0, NULL); static int cpuid_leaf_b = 1; @@ -95,6 +95,7 @@ int error, enable_invpcid, level, width, x2apic_id; unsigned int func, regs[4], logical_cpus; enum x2apic_state x2apic_state; + uint16_t cores, maxcpus, sockets, threads; VCPU_CTR2(vm, vcpu_id, "cpuid %#x,%#x", *eax, *ecx); @@ -142,11 +143,12 @@ * * However this matches the logical cpus as * advertised by leaf 0x1 and will work even - * if the 'threads_per_core' tunable is set - * incorrectly on an AMD host. + * if threads is set incorrectly on an AMD host. */ - logical_cpus = threads_per_core * - cores_per_package; + vm_get_topology(vm, &sockets, &cores, &threads, + &maxcpus); + logical_cpus = threads * + cores; regs[2] = logical_cpus - 1; } break; @@ -305,7 +307,9 @@ */ regs[3] |= (CPUID_MCA | CPUID_MCE | CPUID_MTRR); - logical_cpus = threads_per_core * cores_per_package; + vm_get_topology(vm, &sockets, &cores, &threads, + &maxcpus); + logical_cpus = threads * cores; regs[1] &= ~CPUID_HTT_CORES; regs[1] |= (logical_cpus & 0xff) << 16; regs[3] |= CPUID_HTT; @@ -315,8 +319,10 @@ cpuid_count(*eax, *ecx, regs); if (regs[0] || regs[1] || regs[2] || regs[3]) { + vm_get_topology(vm, &sockets, &cores, &threads, + &maxcpus); regs[0] &= 0x3ff; - regs[0] |= (cores_per_package - 1) << 26; + regs[0] |= (cores - 1) << 26; /* * Cache topology: * - L1 and L2 are shared only by the logical @@ -324,10 +330,10 @@ * - L3 and above are shared by all logical * processors in the package. */ - logical_cpus = threads_per_core; + logical_cpus = threads; level = (regs[0] >> 5) & 0x7; if (level >= 3) - logical_cpus *= cores_per_package; + logical_cpus *= cores; regs[0] |= (logical_cpus - 1) << 14; } break; @@ -389,8 +395,10 @@ /* * Processor topology enumeration */ + vm_get_topology(vm, &sockets, &cores, &threads, + &maxcpus); if (*ecx == 0) { - logical_cpus = threads_per_core; + logical_cpus = threads; width = log2(logical_cpus); level = CPUID_TYPE_SMT; x2apic_id = vcpu_id; @@ -397,8 +405,8 @@ } if (*ecx == 1) { - logical_cpus = threads_per_core * - cores_per_package; + logical_cpus = threads * + cores; width = log2(logical_cpus); level = CPUID_TYPE_CORE; x2apic_id = vcpu_id; Index: usr.sbin/bhyve/bhyve.8 =================================================================== --- usr.sbin/bhyve/bhyve.8 +++ usr.sbin/bhyve/bhyve.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 2, 2017 +.Dd March 10, 2018 .Dt BHYVE 8 .Os .Sh NAME @@ -33,7 +33,14 @@ .Sh SYNOPSIS .Nm .Op Fl abehuwxACHPSWY -.Op Fl c Ar numcpus +.Oo +.Fl c\~ Ns +.Op Ar cpus= Ns +.Ar numcpus Ns +.Op Ar ,sockets=n Ns +.Op Ar ,cores=n Ns +.Op Ar ,threads=n +.Oc .Op Fl g Ar gdbport .Op Fl l Ar lpcdev Ns Op , Ns Ar conf .Op Fl m Ar memsize Ns Op Ar K|k|M|m|G|g|T|t @@ -77,9 +84,28 @@ kernels compiled with .Cd "device bvmconsole" . This option will be deprecated in a future version. -.It Fl c Ar numcpus -Number of guest virtual CPUs. -The default is 1 and the maximum is 16. +.It Fl c Oo Ar cpus= Oc Ns Ar numcpus Ns Oo Ar ,sockets=n Oc Ns Oo Ar ,cores=n Oc Ns Op Ar ,threads=n +Number of guest virtual CPUs +and optionally the CPU topology. +The optional topology must be consistent in that the +.Ar numcpus +must equal the product of +.Ar sockets +* +.Ar cores +* +.Ar threads . +The default is 1 CPU +implemented as 1 each of +.Ar sockets , +.Ar cores , +and +.Ar threads . +If no topology is specified it is implemented as if +.Ar sockets += +.Ar numcpus . +There is a maximum of 16 virtual CPUs. .It Fl C Include guest memory in core file. .It Fl e Index: usr.sbin/bhyve/bhyverun.c =================================================================== --- usr.sbin/bhyve/bhyverun.c +++ usr.sbin/bhyve/bhyverun.c @@ -93,6 +93,8 @@ char *vmname; int guest_ncpus; +uint16_t cores, maxcpus, sockets, threads; + char *guest_uuid_str; static int guest_vmexit_on_hlt, guest_vmexit_on_pause; @@ -137,11 +139,13 @@ { fprintf(stderr, - "Usage: %s [-abehuwxACHPSWY] [-c vcpus] [-g ] [-l ]\n" + "Usage: %s [-abehuwxACHPSWY]\n" + " %*s [-c [cpus=]numcpus[,sockets=n][,cores=n][,threads=n]]\n" + " %*s [-g ] [-l ]\n" " %*s [-m mem] [-p vcpu:hostcpu] [-s ] [-U uuid] \n" " -a: local apic is in xAPIC mode (deprecated)\n" " -A: create ACPI tables\n" - " -c: # cpus (default 1)\n" + " -c: [cpus=]numcpus[,sockets=n][,cores=n][,threads=n]\n" " -C: include guest memory in core file\n" " -e: exit on unhandled I/O access\n" " -g: gdb port\n" @@ -159,12 +163,58 @@ " -W: force virtio to use single-vector MSI\n" " -x: local apic is in x2APIC mode\n" " -Y: disable MPtable generation\n", - progname, (int)strlen(progname), ""); + progname, (int)strlen(progname), "", (int)strlen(progname), "", + (int)strlen(progname), ""); exit(code); } static int +topology_parse(const char *opt) +{ + char *cp, *str; + int i; + uint16_t tmp; + + str = strdup(opt); + if ((cp = strchr(str, ',')) != NULL) { + for (i = 0; str; i++) { + if (sscanf(str, "cpus=%hu", &tmp) == 1) + guest_ncpus = tmp; + else if (sscanf(str, "sockets=%hu", &tmp) == 1) + sockets = tmp; + else if (sscanf(str, "cores=%hu", &tmp) == 1) + cores = tmp; + else if (sscanf(str, "threads=%hu", &tmp) == 1) + threads = tmp; +#ifdef notyet /* Do not expose this until vmm.ko implements it */ + else if (sscanf(str, "maxcpus=%hu", &tmp) == 1) + maxcpus = tmp; +#endif + else + return (-1); + if (cp == NULL) + str = NULL; + else { + str = ++cp; + cp = strchr(str, ','); + } + } + } else { + if (sscanf(str, "cpus=%hu", &tmp) == 1) + guest_ncpus = tmp; + else if (sscanf(str, "%hu", &tmp) == 1) + guest_ncpus = tmp; + else + return (-1); + } + if (guest_ncpus != sockets * cores * threads) + return (-1); + else + return(0); +} + +static int pincpu_parse(const char *opt) { int vcpu, pcpu; @@ -783,6 +833,11 @@ exit(1); } } + error = vm_set_topology(ctx, sockets, cores, threads, maxcpus); + if (error) { + perror("vm_set_topology"); + exit(1); + } return (ctx); } @@ -801,6 +856,8 @@ progname = basename(argv[0]); gdb_port = 0; guest_ncpus = 1; + sockets = cores = threads = 1; + maxcpus = 0; memsize = 256 * MB; mptgen = 1; rtc_localtime = 1; @@ -825,7 +882,10 @@ } break; case 'c': - guest_ncpus = atoi(optarg); + if (topology_parse(optarg) !=0) { + errx(EX_USAGE, "invalid cpu topology " + "'%s'", optarg); + } break; case 'C': memflags |= VM_MEM_F_INCORE; Index: usr.sbin/bhyvectl/bhyvectl.c =================================================================== --- usr.sbin/bhyvectl/bhyvectl.c +++ usr.sbin/bhyvectl/bhyvectl.c @@ -189,7 +189,8 @@ " [--get-msr-bitmap]\n" " [--get-msr-bitmap-address]\n" " [--get-guest-sysenter]\n" - " [--get-exit-reason]\n", + " [--get-exit-reason]\n" + " [--get-cpu-topology]\n", progname); if (cpu_intel) { @@ -282,6 +283,7 @@ enum x2apic_state x2apic_state; static int unassign_pptdev, bus, slot, func; static int run; +static int get_cpu_topology; /* * VMCB specific. @@ -1444,6 +1446,7 @@ { "get-active-cpus", NO_ARG, &get_active_cpus, 1 }, { "get-suspended-cpus", NO_ARG, &get_suspended_cpus, 1 }, { "get-intinfo", NO_ARG, &get_intinfo, 1 }, + { "get-cpu-topology", NO_ARG, &get_cpu_topology, 1 }, }; const struct option intel_opts[] = { @@ -2293,6 +2296,14 @@ } } + if (!error && (get_cpu_topology || get_all)) { + uint16_t sockets, cores, threads, maxcpus; + + vm_get_topology(ctx, &sockets, &cores, &threads, &maxcpus); + printf("cpu_topology:\tsockets=%hu, cores=%hu, threads=%hu, " + "maxcpus=%hu\n", sockets, cores, threads, maxcpus); + } + if (!error && run) { error = vm_run(ctx, vcpu, &vmexit); if (error == 0)