--- docs/schemas/capability.rng | 15 +++ src/conf/capabilities.c | 65 ++++++++++--- src/conf/capabilities.h | 7 +- src/nodeinfo.c | 155 +++++++++++++++++++++++++++++- src/test/test_driver.c | 2 +- src/xen/xend_internal.c | 4 +- tests/capabilityschemadata/caps-test3.xml | 88 +++++++++++++++++ 7 files changed, 319 insertions(+), 17 deletions(-) create mode 100644 tests/capabilityschemadata/caps-test3.xml diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index 8c928bc..1ab36ed 100644 --- a/docs/schemas/capability.rng +++ b/docs/schemas/capability.rng @@ -176,6 +176,10 @@ </attribute> <optional> + <ref name='memory'/> + </optional> + + <optional> <element name='cpus'> <attribute name='num'> <ref name='unsignedInt'/> @@ -188,11 +192,22 @@ </element> </define> + <define name='memory'> + <element name='memory'> + <ref name='scaledInteger'/> + </element> + </define> + <define name='cpu'> <element name='cpu'> <attribute name='id'> <ref name='unsignedInt'/> </attribute> + <optional> + <attribute name='thread_siblings'> + <text/> + </attribute> + </optional> </element> </define> diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index a8ee2cf..1b514ef 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -73,10 +73,16 @@ virCapabilitiesNew(const char *arch, static void virCapabilitiesFreeHostNUMACell(virCapsHostNUMACellPtr cell) { + int i; if (cell == NULL) return; + if (cell->threadSiblings) + for (i=0; i < cell->ncpus; i++) + VIR_FREE(cell->threadSiblings[i]); + VIR_FREE(cell->cpus); + VIR_FREE(cell->threadSiblings); VIR_FREE(cell); } @@ -253,7 +259,10 @@ int virCapabilitiesAddHostNUMACell(virCapsPtr caps, int num, int ncpus, - const int *cpus) + unsigned long long mem, + const int *cpus, + const virBitmapPtr *threadSiblings +) { virCapsHostNUMACellPtr cell; @@ -272,8 +281,20 @@ virCapabilitiesAddHostNUMACell(virCapsPtr caps, cpus, ncpus * sizeof(*cpus)); + if (threadSiblings) { + if (VIR_ALLOC_N(cell->threadSiblings, ncpus) < 0) { + VIR_FREE(cell->cpus); + VIR_FREE(cell); + return -1; + } + memcpy(cell->threadSiblings, + threadSiblings, + ncpus * sizeof(*threadSiblings)); + } + cell->ncpus = ncpus; cell->num = num; + cell->mem = mem; caps->host.numaCell[caps->host.nnumaCell++] = cell; @@ -695,6 +716,7 @@ virCapabilitiesFormatXML(virCapsPtr caps) virBuffer xml = VIR_BUFFER_INITIALIZER; int i, j, k; char host_uuid[VIR_UUID_STRING_BUFLEN]; + char *str; virBufferAddLit(&xml, "<capabilities>\n\n"); virBufferAddLit(&xml, " <host>\n"); @@ -754,22 +776,43 @@ virCapabilitiesFormatXML(virCapsPtr caps) } if (caps->host.nnumaCell) { - virBufferAddLit(&xml, " <topology>\n"); - virBufferAsprintf(&xml, " <cells num='%zu'>\n", + virBufferAdjustIndent(&xml, 4); + virBufferAddLit(&xml, "<topology>\n"); + virBufferAsprintf(&xml, " <cells num='%zu'>\n", caps->host.nnumaCell); for (i = 0 ; i < caps->host.nnumaCell ; i++) { - virBufferAsprintf(&xml, " <cell id='%d'>\n", + virBufferAsprintf(&xml, " <cell id='%d'>\n", caps->host.numaCell[i]->num); - virBufferAsprintf(&xml, " <cpus num='%d'>\n", + + /* Print out the numacell memory total if it is available */ + if (caps->host.numaCell[i]->mem) + virBufferAsprintf(&xml, " <memory unit='KiB'>%llu</memory>\n", + caps->host.numaCell[i]->mem); + + virBufferAsprintf(&xml, " <cpus num='%d'>\n", caps->host.numaCell[i]->ncpus); - for (j = 0 ; j < caps->host.numaCell[i]->ncpus ; j++) - virBufferAsprintf(&xml, " <cpu id='%d'/>\n", + for (j = 0 ; j < caps->host.numaCell[i]->ncpus ; j++) { + virBufferAsprintf(&xml, " <cpu id='%d'", caps->host.numaCell[i]->cpus[j]); - virBufferAddLit(&xml, " </cpus>\n"); - virBufferAddLit(&xml, " </cell>\n"); + + /* Print out thread siblings if they were populated */ + if (caps->host.numaCell[i]->threadSiblings) { + str = virBitmapFormat(caps->host.numaCell[i]->threadSiblings[j]); + if (str) { + virBufferAsprintf(&xml, " thread_siblings='%s'", str); + VIR_FREE(str); + } + } + + virBufferAsprintf(&xml, "/>\n"); + } + + virBufferAddLit(&xml, " </cpus>\n"); + virBufferAddLit(&xml, " </cell>\n"); } - virBufferAddLit(&xml, " </cells>\n"); - virBufferAddLit(&xml, " </topology>\n"); + virBufferAddLit(&xml, " </cells>\n"); + virBufferAddLit(&xml, "</topology>\n"); + virBufferAdjustIndent(&xml, -4); } for (i = 0; i < caps->host.nsecModels; i++) { diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index 99056f8..ded0d57 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -88,7 +88,9 @@ typedef virCapsHostNUMACell *virCapsHostNUMACellPtr; struct _virCapsHostNUMACell { int num; int ncpus; + unsigned long long mem; /* in kibibytes */ int *cpus; + virBitmapPtr *threadSiblings; }; typedef struct _virCapsHostSecModel virCapsHostSecModel; @@ -200,7 +202,10 @@ extern int virCapabilitiesAddHostNUMACell(virCapsPtr caps, int num, int ncpus, - const int *cpus); + unsigned long long mem, + const int *cpus, + const virBitmapPtr *threadSiblings +); extern int diff --git a/src/nodeinfo.c b/src/nodeinfo.c index 36fbd66..cc42c43 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -56,6 +56,7 @@ #ifdef __linux__ # define CPUINFO_PATH "/proc/cpuinfo" # define SYSFS_SYSTEM_PATH "/sys/devices/system" +# define SYSFS_CPU_PATH "/sys/devices/system/cpu" # define PROCSTAT_PATH "/proc/stat" # define MEMINFO_PATH "/proc/meminfo" # define SYSFS_MEMORY_SHARED_PATH "/sys/kernel/mm/ksm" @@ -77,6 +78,7 @@ static int linuxNodeGetMemoryStats(FILE *meminfo, int cellNum, virNodeMemoryStatsPtr params, int *nparams); +static unsigned long long nodeGetCellMemory(int cell); /* Return the positive decimal contents of the given * DIR/cpu%u/FILE, or -1 on error. If MISSING_OK and the @@ -125,6 +127,66 @@ cleanup: return value; } +/** + * virNodeGetThreadSiblingsList + * @dir: directory where cpu0-N files are located. + * @cpu: the specific cpu to get the siblings for. + * + * Will open the "thread_siblings_list" file for the cpu and + * return a string representing the contents. The contents of the + * file is a string represnting the cpus that are siblings; like + * 1,9 or 1-4. + * + * Returns NULL on failure, char * on success + * + * Note: Responsibility of caller to free string + */ +static char *virNodeGetThreadSiblingsList(const char *dir, unsigned int cpu) +{ + char *path; + FILE *pathfp; + char *str = NULL; + int strsize; + + if (virAsprintf(&path, "%s/cpu%u/topology/thread_siblings_list", + dir, cpu) < 0) { + virReportOOMError(); + return str; + } + + pathfp = fopen(path, "r"); + if (pathfp == NULL) { + virReportSystemError(errno, _("cannot open %s"), path); + VIR_FREE(path); + return str; + } + + /* Detect how large of a string we need to make */ + fseek(pathfp, 0L, SEEK_END); + strsize = ftell(pathfp); + fseek(pathfp, 0L, SEEK_SET); + + + if (VIR_ALLOC_N(str, strsize) < 0) { + virReportOOMError(); + str = NULL; + goto cleanup; + } + + if (fgets(str, strsize, pathfp) == NULL) { + virReportSystemError(errno, _("cannot read from %s"), path); + VIR_FREE(str); + str = NULL; + goto cleanup; + } + +cleanup: + VIR_FORCE_FCLOSE(pathfp); + VIR_FREE(path); + + return str; +} + static unsigned long virNodeCountThreadSiblings(const char *dir, unsigned int cpu) { @@ -1313,9 +1375,14 @@ nodeCapsInitNUMA(virCapsPtr caps) int n; unsigned long *mask = NULL; unsigned long *allonesmask = NULL; + unsigned long long memory; int *cpus = NULL; int ret = -1; int max_n_cpus = NUMA_MAX_N_CPUS; + char *str = NULL; + virBitmapPtr *threadSiblings = NULL; + + if (numa_available() < 0) return 0; @@ -1343,29 +1410,56 @@ nodeCapsInitNUMA(virCapsPtr caps) continue; } + /* Detect the amount of memory in the numa cell */ + memory = nodeGetCellMemory(n); + if (memory == 0) + goto cleanup; + for (ncpus = 0, i = 0 ; i < max_n_cpus ; i++) if (MASK_CPU_ISSET(mask, i)) ncpus++; + /* Create some memory for the array of cpus. */ if (VIR_ALLOC_N(cpus, ncpus) < 0) goto cleanup; - for (ncpus = 0, i = 0 ; i < max_n_cpus ; i++) - if (MASK_CPU_ISSET(mask, i)) + /* Create some memory for the array of siblings. */ + if (VIR_ALLOC_N(threadSiblings, ncpus) < 0) + goto cleanup; + + for (ncpus = 0, i = 0 ; i < max_n_cpus ; i++) { + if (MASK_CPU_ISSET(mask, i)) { + + /* Get the string of thread siblings for this cpu */ + if (!(str = virNodeGetThreadSiblingsList(SYSFS_CPU_PATH, i))) + goto cleanup; + + /* Convert the string to a bitmap to be stored */ + if (virBitmapParse(str, 0, &threadSiblings[ncpus], max_n_cpus) < 0) + goto cleanup; + cpus[ncpus++] = i; + VIR_FREE(str); + } + } if (virCapabilitiesAddHostNUMACell(caps, n, ncpus, - cpus) < 0) + memory, + cpus, + threadSiblings) < 0) goto cleanup; + VIR_FREE(threadSiblings); VIR_FREE(cpus); } ret = 0; cleanup: + VIR_FREE(str); + VIR_FREE(threadSiblings); VIR_FREE(cpus); VIR_FREE(mask); VIR_FREE(allonesmask); @@ -1441,6 +1535,54 @@ cleanup: return freeMem; } +/** + * nodeGetCellMemory + * @cell: The number of the numa cell to get memory info for. + * + * Will call the numa_node_size64() function from libnuma to get + * the amount of total memory in bytes. It is then converted to + * KiB and returned. + * + * Returns 0 on failure, amount of memory in KiB on success. + */ +static unsigned long long +nodeGetCellMemory(int cell) +{ + long long mem; + unsigned long long memKiB = 0; + int maxCell; + + if (numa_available() < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("NUMA not supported on this host")); + goto cleanup; + } + + /* Make sure the provided cell number is valid. */ + maxCell = numa_max_node(); + if (cell > maxCell) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cell %d out of range (0-%d)"), + cell, maxCell); + goto cleanup; + } + + /* Get the amount of memory(bytes) in the node */ + mem = numa_node_size64(cell, NULL); + if (mem < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to query NUMA total memory for node: %d"), + cell); + goto cleanup; + } + + /* Convert the memory from bytes to KiB */ + memKiB = mem >> 10; + +cleanup: + return memKiB; +} + #else int nodeCapsInitNUMA(virCapsPtr caps ATTRIBUTE_UNUSED) { return 0; @@ -1462,4 +1604,11 @@ unsigned long long nodeGetFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED) _("NUMA memory information not available on this platform")); return 0; } + +static unsigned long long nodeGetCellMemory(int cell) +{ + virReportError(VIR_ERR_NO_SUPPORT, "%s", + _("NUMA memory information not available on this platform")); + return 0; +} #endif diff --git a/src/test/test_driver.c b/src/test/test_driver.c index c974e1a..9d715a0 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -174,7 +174,7 @@ testBuildCapabilities(virConnectPtr conn) { for (i = 0; i < privconn->numCells; i++) { if (virCapabilitiesAddHostNUMACell(caps, i, privconn->cells[i].numCpus, - privconn->cells[i].cpus) < 0) + 0, privconn->cells[i].cpus, NULL) < 0) goto no_memory; } diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c index 922c571..d18eecb 100644 --- a/src/xen/xend_internal.c +++ b/src/xen/xend_internal.c @@ -1167,7 +1167,9 @@ sexpr_to_xend_topology(const struct sexpr *root, if (virCapabilitiesAddHostNUMACell(caps, cell, nb_cpus, - cpuNums) < 0) + 0, + cpuNums, + NULL) < 0) goto memory_error; } VIR_FREE(cpuNums); diff --git a/tests/capabilityschemadata/caps-test3.xml b/tests/capabilityschemadata/caps-test3.xml new file mode 100644 index 0000000..724ced4 --- /dev/null +++ b/tests/capabilityschemadata/caps-test3.xml @@ -0,0 +1,88 @@ +<capabilities> + + <host> + <uuid>35383339-3134-5553-4531-30314e394a50</uuid> + <cpu> + <arch>x86_64</arch> + <model>Westmere</model> + <vendor>Intel</vendor> + <topology sockets='1' cores='6' threads='2'/> + <feature name='rdtscp'/> + <feature name='pdpe1gb'/> + <feature name='dca'/> + <feature name='pdcm'/> + <feature name='xtpr'/> + <feature name='tm2'/> + <feature name='est'/> + <feature name='smx'/> + <feature name='vmx'/> + <feature name='ds_cpl'/> + <feature name='monitor'/> + <feature name='dtes64'/> + <feature name='pclmuldq'/> + <feature name='pbe'/> + <feature name='tm'/> + <feature name='ht'/> + <feature name='ss'/> + <feature name='acpi'/> + <feature name='ds'/> + <feature name='vme'/> + </cpu> + <power_management> + <suspend_disk/> + </power_management> + <migration_features> + <live/> + <uri_transports> + <uri_transport>tcp</uri_transport> + </uri_transports> + </migration_features> + <topology> + <cells num='2'> + <cell id='0'> + <memory unit='KiB'>12572412</memory> + <cpus num='12'> + <cpu id='0' thread_siblings='0,12'/> + <cpu id='2' thread_siblings='2,14'/> + <cpu id='4' thread_siblings='4,16'/> + <cpu id='6' thread_siblings='6,18'/> + <cpu id='8' thread_siblings='8,20'/> + <cpu id='10' thread_siblings='10,22'/> + <cpu id='12' thread_siblings='0,12'/> + <cpu id='14' thread_siblings='2,14'/> + <cpu id='16' thread_siblings='4,16'/> + <cpu id='18' thread_siblings='6,18'/> + <cpu id='20' thread_siblings='8,20'/> + <cpu id='22' thread_siblings='10,22'/> + </cpus> + </cell> + <cell id='1'> + <memory unit='KiB'>12582908</memory> + <cpus num='12'> + <cpu id='1' thread_siblings='1,13'/> + <cpu id='3' thread_siblings='3,15'/> + <cpu id='5' thread_siblings='5,17'/> + <cpu id='7' thread_siblings='7,19'/> + <cpu id='9' thread_siblings='9,21'/> + <cpu id='11' thread_siblings='11,23'/> + <cpu id='13' thread_siblings='1,13'/> + <cpu id='15' thread_siblings='3,15'/> + <cpu id='17' thread_siblings='5,17'/> + <cpu id='19' thread_siblings='7,19'/> + <cpu id='21' thread_siblings='9,21'/> + <cpu id='23' thread_siblings='11,23'/> + </cpus> + </cell> + </cells> + </topology> + <secmodel> + <model>none</model> + <doi>0</doi> + </secmodel> + <secmodel> + <model>dac</model> + <doi>0</doi> + </secmodel> + </host> + +</capabilities> -- 1.7.11.7 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list