On Fri, Aug 04, 2006 at 06:55:17PM +0100, Daniel P. Berrange wrote: > On Fri, Aug 04, 2006 at 05:45:41AM -0400, Daniel Veillard wrote: > > On Thu, Aug 03, 2006 at 03:32:46PM +0200, Philippe Berthault wrote: > > I had a few things to change: > > - discard // based comments to stick just to /* > > - apply the header patch to libvirt.h.in not to the generated header > > - shorten some comments to fit in 80 characters wide > > - renamed the macros adding the VIR_ prefix to avoid potential name > > collision with other headers I added 3 more convenience macros: VIR_NODEINFO_MAXCPUS(nodeinfo) VIR_CPU_MAPLEN(cpu) VIR_CPU_FULL_MAPLEN(vcpu, cpu) To hide the maths from developers. I also bug-fixed the pin cpu method which mistakenly checked for vcpu < 1 instead of vcpu < 0 when validating args. See atached patch for these changes > One more: > > - expose the new APIs via virsh. Michael originally suggsted pretty much > following the style of 'xm vcpu-pin' and 'vcpu-list'. Seems like a fairly > reasonable pattern to follow. See the attached patch which adds these commands. eg: # ~berrange/usr/bin/virsh vcpuinfo Demo03 VCPU: 0 CPU: 1 State: blocked CPU time: 13.4s CPU Affinity: y- VCPU: 1 CPU: 0 State: blocked CPU time: 5.0s CPU Affinity: -y # ~berrange/usr/bin/virsh vcpupin Demo03 1 0,1 # ~berrange/usr/bin/virsh vcpuinfo Demo03 VCPU: 0 CPU: 1 State: blocked CPU time: 13.4s CPU Affinity: -y VCPU: 1 CPU: 1 State: blocked CPU time: 5.0s CPU Affinity: yy Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
diff -c -r1.200 ChangeLog *** ChangeLog 4 Aug 2006 13:36:07 -0000 1.200 --- ChangeLog 5 Aug 2006 01:17:22 -0000 *************** *** 1,3 **** --- 1,11 ---- + Fri Aug 4 20:19:23 EDT 2006 Daniel Berrange <berrange@xxxxxxxxxx> + + * src/libvirt.c: Fix off-by-one in validated VCPU number (it is + zero based, not one based). + * include/libvirt/libvirt.h: Add some convenience macros for + calculating neccessary CPU map lengths & total host CPUs + * src/virsh.c: Add 'vcpuinfo' and 'vcpumap' commands + Fri Aug 4 14:45:25 CEST 2006 Daniel Veillard <veillard@xxxxxxxxxx> * python/generator.py: fix the generator when handling long integers diff -c -r1.20 libvirt.h *** include/libvirt/libvirt.h 4 Aug 2006 10:41:05 -0000 1.20 --- include/libvirt/libvirt.h 5 Aug 2006 01:17:22 -0000 *************** *** 167,172 **** --- 167,184 ---- unsigned int threads;/* number of threads per core */ }; + + /** + * VIR_NODEINFO_MAXCPUS: + * @nodeinfo: virNodeInfo instance + * + * This macro is to calculate the total number of CPUs supported + * but not neccessarily active in the host. + */ + + + #define VIR_NODEINFO_MAXCPUS(nodeinfo) ((nodeinfo).nodes*(nodeinfo).sockets*(nodeinfo).cores*(nodeinfo).threads) + /** * virNodeInfoPtr: * *************** *** 339,344 **** --- 351,380 ---- #define VIR_UNUSE_CPU(cpumap,cpu) (cpumap[(cpu)/8] &= ~(1<<((cpu)%8))) + /** + * VIR_CPU_MAPLEN + * @cpu: number of physical CPUs + * + * This macro is to be used in conjonction with virDomainPinVcpu() API. + * It returns the length (in bytes) required to store the complete + * CPU map between a single virtual & all physical CPUs of a domain. + */ + + #define VIR_CPU_MAPLEN(cpu) (((cpu)+7)/8) + + /** + * VIR_CPU_FULL_MAPLEN + * @vcpu: number of virtual CPUs + * @cpu: number of physical CPUs + * + * This macro is to be used in conjonction with virDomainPinVcpu() API. + * It returns the length (in bytes) required to store the complete + * CPU map between all virtual & physical CPUs of a domain. + */ + + #define VIR_CPU_FULL_MAPLEN(vcpu, cpu) (((cpu)+7)/8*(vcpu)) + + int virDomainGetVcpus (virDomainPtr domain, virVcpuInfoPtr info, int maxinfo, diff -c -r1.38 libvirt.c *** src/libvirt.c 4 Aug 2006 10:41:05 -0000 1.38 --- src/libvirt.c 5 Aug 2006 01:17:22 -0000 *************** *** 1720,1726 **** } if (domain->conn->flags & VIR_CONNECT_RO) return (-1); ! if ((vcpu < 1) || (cpumap == NULL) || (maplen < 1)) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); return (-1); } --- 1720,1726 ---- } if (domain->conn->flags & VIR_CONNECT_RO) return (-1); ! if ((vcpu < 0) || (cpumap == NULL) || (maplen < 1)) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); return (-1); } diff -c -r1.32 virsh.c *** src/virsh.c 19 Jul 2006 22:24:37 -0000 1.32 --- src/virsh.c 5 Aug 2006 01:17:23 -0000 *************** *** 207,212 **** --- 207,213 ---- #define vshPrint(_ctl, ...) fprintf(stdout, __VA_ARGS__) static const char *vshDomainStateToString(int state); + static const char *vshDomainVcpuStateToString(int state); static int vshConnectionUsability(vshControl * ctl, virConnectPtr conn, int showerror); *************** *** 794,799 **** --- 795,969 ---- } /* + * "vcpuinfo" command + */ + static vshCmdInfo info_vcpuinfo[] = { + {"syntax", "vcpuinfo <domain>"}, + {"help", "domain vcpu information"}, + {"desc", "Returns basic information about the domain virtual CPUs."}, + {NULL, NULL} + }; + + static vshCmdOptDef opts_vcpuinfo[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, "domain name, id or uuid"}, + {NULL, 0, 0, NULL} + }; + + static int + cmdVcpuinfo(vshControl * ctl, vshCmd * cmd) + { + virDomainInfo info; + virDomainPtr dom; + virNodeInfo nodeinfo; + virVcpuInfoPtr cpuinfo; + unsigned char *cpumap; + int ncpus; + size_t cpumaplen; + int ret = TRUE; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) + return FALSE; + + if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) { + virDomainFree(dom); + return FALSE; + } + + if (virDomainGetInfo(dom, &info) != 0) { + virDomainFree(dom); + return FALSE; + } + + cpuinfo = malloc(sizeof(virVcpuInfo)*info.nrVirtCpu); + cpumaplen = VIR_CPU_FULL_MAPLEN(info.nrVirtCpu, VIR_NODEINFO_MAXCPUS(nodeinfo)); + cpumap = malloc(cpumaplen); + + if ((ncpus = virDomainGetVcpus(dom, + cpuinfo, info.nrVirtCpu, + cpumap, cpumaplen)) >= 0) { + int n; + for (n = 0 ; n < ncpus ; n++) { + unsigned int m; + vshPrint(ctl, "%-15s %d\n", "VCPU:", n); + vshPrint(ctl, "%-15s %d\n", "CPU:", cpuinfo[n].cpu); + vshPrint(ctl, "%-15s %s\n", "State:", + vshDomainVcpuStateToString(cpuinfo[n].state)); + if (cpuinfo[n].cpuTime != 0) { + double cpuUsed = cpuinfo[n].cpuTime; + + cpuUsed /= 1000000000.0; + + vshPrint(ctl, "%-15s %.1lfs\n", "CPU time:", cpuUsed); + } + vshPrint(ctl, "%-15s ", "CPU Affinity:"); + for (m = 0 ; m < VIR_NODEINFO_MAXCPUS(nodeinfo) ; m++) { + vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumap, cpumaplen, n, m) ? 'y' : '-'); + } + vshPrint(ctl, "\n"); + if (n < (ncpus - 1)) { + vshPrint(ctl, "\n"); + } + } + } else { + ret = FALSE; + } + + free(cpumap); + free(cpuinfo); + virDomainFree(dom); + return ret; + } + + /* + * "vcpupin" command + */ + static vshCmdInfo info_vcpupin[] = { + {"syntax", "vcpupin <domain>"}, + {"help", "control domain vcpu affinity"}, + {"desc", "Pin domain VCPUs to host physical CPUs"}, + {NULL, NULL} + }; + + static vshCmdOptDef opts_vcpupin[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, "domain name, id or uuid"}, + {"vcpu", VSH_OT_DATA, VSH_OFLAG_REQ, "vcpu number"}, + {"cpulist", VSH_OT_DATA, VSH_OFLAG_REQ, "host cpu number(s) (comma separated)"}, + {NULL, 0, 0, NULL} + }; + + static int + cmdVcpupin(vshControl * ctl, vshCmd * cmd) + { + virDomainInfo info; + virDomainPtr dom; + virNodeInfo nodeinfo; + int vcpu; + char *cpulist; + int ret = TRUE; + int vcpufound = 0; + unsigned char *cpumap; + int cpumaplen; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) + return FALSE; + + vcpu = vshCommandOptInt(cmd, "vcpu", &vcpufound); + if (!vcpufound) { + virDomainFree(dom); + return FALSE; + } + + if (!(cpulist = vshCommandOptString(cmd, "cpulist", NULL))) { + virDomainFree(dom); + return FALSE; + } + + if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) { + virDomainFree(dom); + return FALSE; + } + + if (virDomainGetInfo(dom, &info) != 0) { + virDomainFree(dom); + return FALSE; + } + + if (vcpu >= info.nrVirtCpu) { + virDomainFree(dom); + return FALSE; + } + + cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo)); + cpumap = malloc(cpumaplen); + memset(cpumap, 0, cpumaplen); + + do { + unsigned int cpu = atoi(cpulist); + + if (cpu < VIR_NODEINFO_MAXCPUS(nodeinfo)) { + VIR_USE_CPU(cpumap, cpu); + } + cpulist = index(cpulist, ','); + if (cpulist) + cpulist++; + } while (cpulist); + + if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0) { + ret = FALSE; + } + + free(cpumap); + virDomainFree(dom); + return ret; + } + + /* * "nodeinfo" command */ static vshCmdInfo info_nodeinfo[] = { *************** *** 1081,1086 **** --- 1251,1258 ---- {"save", cmdSave, opts_save, info_save}, {"shutdown", cmdShutdown, opts_shutdown, info_shutdown}, {"suspend", cmdSuspend, opts_suspend, info_suspend}, + {"vcpuinfo", cmdVcpuinfo, opts_vcpuinfo, info_vcpuinfo}, + {"vcpupin", cmdVcpupin, opts_vcpupin, info_vcpupin}, {"version", cmdVersion, NULL, info_version}, {NULL, NULL, NULL, NULL} }; *************** *** 1643,1648 **** --- 1815,1836 ---- return NULL; } + static const char * + vshDomainVcpuStateToString(int state) + { + switch (state) { + case VIR_VCPU_OFFLINE: + return "offline"; + case VIR_VCPU_BLOCKED: + return "blocked"; + case VIR_VCPU_RUNNING: + return "running"; + default: + return "no state"; + } + return NULL; + } + static int vshConnectionUsability(vshControl * ctl, virConnectPtr conn, int showerror) {