From a87420ae598d7bba4b475a5bdf229a046fa3c07d Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Mon, 22 Oct 2007 13:06:15 +0000 Subject: [PATCH] * src/xend_internal.c: update to cpuset parsing code for NUMA Daniel --- ChangeLog | 4 + src/xend_internal.c | 488 +++++++++++++++++++++++++++++++------------- 2 files changed, 347 insertions(+), 145 deletions(-) diff --git a/ChangeLog b/ChangeLog index 35128a36ad..af02d845da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Mon Oct 22 15:05:30 CEST 2007 Daniel Veillard + + * src/xend_internal.c: update to cpuset parsing code for NUMA + Fri Oct 19 17:08:08 CEST 2007 Daniel Veillard * src/Makefile.am: fix tst build rule diff --git a/src/xend_internal.c b/src/xend_internal.c index 55dc9986a6..142835fc4d 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -1896,24 +1896,349 @@ sexpr_to_xend_node_info(struct sexpr *root, virNodeInfoPtr info) } /** - * getNumber: - * @pointer: pointer to string beginning with numerical characters - * @result: pointer to integer for storing the numerical result + * skipSpaces: + * @str: pointer to the char pointer used * - * Internal routine extracting a number from the beginning of a string + * Skip potential blanks, this includes space tabs, line feed, + * carriage returns and also '\\' which can be erronously emitted + * by xend + */ +static void +skipSpaces(const char **str) { + const char *cur = *str; + + while ((*cur == ' ') || (*cur == '\t') || (*cur == '\n') || + (*cur == '\r') || (*cur == '\\')) cur++; + *str = cur; +} + +/** + * parseNumber: + * @str: pointer to the char pointer used * - * Returns the number of characters that were extracted as digits - * or -1 if no digits were found. + * Parse a number + * + * Returns the CPU number or -1 in case of error. @str will be + * updated to skip the number. */ static int -getNumber (const char * pointer, int * result) { - int len = 0; - while (isdigit(*(pointer + len))) - len++; - if (len == 0) - return -1; - *(result) = atoi(pointer); - return (len); +parseNumber(const char **str) { + int ret = 0; + const char *cur = *str; + + if ((*cur < '0') || (*cur > '9')) + return(-1); + + while ((*cur >= '0') && (*cur <= '9')) { + ret = ret * 10 + (*cur - '0'); + cur++; + } + *str = cur; + return(ret); +} + +/** + * parseCpuNumber: + * @str: pointer to the char pointer used + * @maxcpu: maximum CPU number allowed + * + * Parse a CPU number + * + * Returns the CPU number or -1 in case of error. @str will be + * updated to skip the number. + */ +static int +parseCpuNumber(const char **str, int maxcpu) { + int ret = 0; + const char *cur = *str; + + if ((*cur < '0') || (*cur > '9')) + return(-1); + + while ((*cur >= '0') && (*cur <= '9')) { + ret = ret * 10 + (*cur - '0'); + if (ret > maxcpu) + return(-1); + cur++; + } + *str = cur; + return(ret); +} + +#if 0 /* Not used yet */ +/** + * saveCpuSet: + * @conn: connection + * @cpuset: pointer to a char array for the CPU set + * @maxcpu: number of elements available in @cpuset + * + * Serialize the cpuset to a string + * + * Returns the new string NULL in case of error. The string need to be + * freed by the caller. + */ +static char * +saveCpuSet(virConnectPtr conn, char *cpuset, int maxcpu) +{ + virBufferPtr buf; + char *ret; + int start, cur; + int first = 1; + + if ((cpuset == NULL) || (maxcpu <= 0) || (maxcpu >100000)) + return(NULL); + + buf = virBufferNew(1000); + if (buf == NULL) { + virXendError(conn, VIR_ERR_NO_MEMORY, _("allocate buffer")); + return(NULL); + } + cur = 0; + start = -1; + while (cur < maxcpu) { + if (cpuset[cur]) { + if (start == -1) + start = cur; + } else if (start != -1) { + if (!first) + virBufferAdd(buf, ",", -1); + else + first = 0; + if (cur == start + 1) + virBufferVSprintf(buf, "%d", start); + else if (cur == start + 2) + virBufferVSprintf(buf, "%d,%d", start, cur - 1); + else + virBufferVSprintf(buf, "%d-%d", start, cur - 1); + start = -1; + } + cur++; + } + if (start != -1) { + if (!first) + virBufferAdd(buf, ",", -1); + if (maxcpu == start + 1) + virBufferVSprintf(buf, "%d", start); + else if (maxcpu == start + 2) + virBufferVSprintf(buf, "%d,%d", start, maxcpu - 1); + else + virBufferVSprintf(buf, "%d-%d", start, maxcpu - 1); + } + ret = virBufferContentAndFree(buf); + return(ret); +} +#endif + +/** + * parseCpuSet: + * @str: pointer to a CPU set string pointer + * @sep: potential character used to mark the end of string if not 0 + * @cpuset: pointer to a char array for the CPU set + * @maxcpu: number of elements available in @cpuset + * + * Parse the cpu set, it will set the value for enabled CPUs in the @cpuset + * to 1, and 0 otherwise. The syntax allows coma separated entries each + * can be either a CPU number, ^N to unset that CPU or N-M for ranges. + * + * Returns the number of CPU found in that set, or -1 in case of error. + * @cpuset is modified accordingly to the value parsed. + * @str is updated to the end of the part parsed + */ +static int +parseCpuSet(virConnectPtr conn, const char **str, char sep, char *cpuset, + int maxcpu) +{ + const char *cur; + int ret = 0; + int i, start, last; + int neg = 0; + + if ((str == NULL) || (cpuset == NULL) || (maxcpu <= 0) || (maxcpu >100000)) + return(-1); + + cur = *str; + skipSpaces(&cur); + if (*cur == 0) + goto parse_error; + + /* initialize cpumap to all 0s */ + for (i = 0;i < maxcpu;i++) + cpuset[i] = 0; + ret = 0; + + while ((*cur != 0) && (*cur != sep)) { + /* + * 3 constructs are allowed: + * - N : a single CPU number + * - N-M : a range of CPU numbers with N < M + * - ^N : remove a single CPU number from the current set + */ + if (*cur == '^') { + cur++; + neg = 1; + } + + if ((*cur < '0') || (*cur > '9')) + goto parse_error; + start = parseCpuNumber(&cur, maxcpu); + if (start < 0) + goto parse_error; + skipSpaces(&cur); + if ((*cur == ',') || (*cur == 0) || (*cur == sep)) { + if (neg) { + if (cpuset[start] == 1) { + cpuset[start] = 0; + ret--; + } + } else { + if (cpuset[start] == 0) { + cpuset[start] = 1; + ret++; + } + } + } else if (*cur == '-') { + if (neg) + goto parse_error; + cur++; + skipSpaces(&cur); + last = parseCpuNumber(&cur, maxcpu); + if (last < start) + goto parse_error; + for (i = start;i <= last;i++) { + if (cpuset[i] == 0) { + cpuset[i] = 1; + ret++; + } + } + skipSpaces(&cur); + } + if (*cur == ',') { + cur++; + skipSpaces(&cur); + neg = 0; + } else if ((*cur == 0) || (*cur == sep)) { + break; + } else + goto parse_error; + } + *str = cur; + return(ret); + +parse_error: + virXendError(conn, VIR_ERR_XEN_CALL, + _("topology cpuset syntax error")); + return(-1); +} + + +/** + * parseXenCpuTopology: + * @xml: XML output buffer + * @str: the topology string + * @maxcpu: number of elements available in @cpuset + * + * Parse a Xend CPU topology string and build the associated XML + * format. + * + * Returns 0 in case of success, -1 in case of error + */ +static int +parseTopology(virConnectPtr conn, virBufferPtr xml, const char *str, + int maxcpu) +{ + const char *cur; + char *cpuset = NULL; + int cell, cpu, nb_cpus; + int ret; + + if ((str == NULL) || (xml == NULL) || (maxcpu <= 0) || (maxcpu >100000)) + return(-1); + + cpuset = malloc(maxcpu * sizeof(char)); + if (cpuset == NULL) + goto memory_error; + + cur = str; + while (*cur != 0) { + /* + * Find the next NUMA cell described in the xend output + */ + cur = strstr(cur, "node"); + if (cur == NULL) + break; + cur += 4; + cell = parseNumber(&cur); + if (cell < 0) + goto parse_error; + skipSpaces(&cur); + if (*cur != ':') + goto parse_error; + cur++; + skipSpaces(&cur); + if (!strncmp (cur, "no cpus", 7)) { + nb_cpus = 0; + for (cpu = 0;cpu < maxcpu;cpu++) + cpuset[cpu] = 0; + } else { + nb_cpus = parseCpuSet(conn, &cur, 'n', cpuset, maxcpu); + if (nb_cpus < 0) + goto error; + } + + /* + * add xml for all cpus associated with that cell + */ + ret = virBufferVSprintf (xml, "\ + \n\ + \n", cell, nb_cpus); +#ifdef STANDALONE + { + char *dump; + + dump = saveCpuSet(conn, cpuset, maxcpu); + if (dump != NULL) { + virBufferVSprintf (xml, " %s\n", dump); + free(dump); + } else { + virBufferVSprintf (xml, " %s\n", + "Failed to dump CPU set"); + } + } +#endif + if (ret < 0) + goto memory_error; + for (cpu = 0;cpu < maxcpu;cpu++) { + if (cpuset[cpu] == 1) { + ret = virBufferVSprintf (xml, "\ + \n", cpu); + if (ret < 0) + goto memory_error; + } + } + ret = virBufferAdd (xml, "\ + \n\ + \n", -1); + if (ret < 0) + goto memory_error; + + } + free(cpuset); + return(0); + +parse_error: + virXendError(conn, VIR_ERR_XEN_CALL, + _("topology syntax error")); +error: + if (cpuset != NULL) + free(cpuset); + + return(-1); + +memory_error: + if (cpuset != NULL) + free(cpuset); + virXendError(conn, VIR_ERR_NO_MEMORY, _("allocate buffer")); + return(-1); } /** @@ -1929,21 +2254,9 @@ static int sexpr_to_xend_topology_xml(virConnectPtr conn, struct sexpr *root, virBufferPtr xml) { const char *nodeToCpu; - const char *offset; - int cellNum; int numCells = 0; int numCpus; - int cellCpuCount = 0; - int nodeCpuCount = 0; - int start; - int finish; int r; - int i; - int len; - int cpuNum; - int *cpuIdsPtr = NULL; - int *iCpuIdsPtr = NULL; - char next; nodeToCpu = sexpr_node(root, "node/node_to_cpu"); if (nodeToCpu == NULL) { @@ -1955,134 +2268,21 @@ sexpr_to_xend_topology_xml(virConnectPtr conn, struct sexpr *root, virBufferPtr numCells = sexpr_int(root, "node/nr_nodes"); numCpus = sexpr_int(root, "node/nr_cpus"); - /* array for holding all cpu numbers associated with a single cell. - * Should never need more than numCpus (which is total number of - * cpus for the node) - */ - cpuIdsPtr = iCpuIdsPtr = malloc(numCpus * sizeof(int)); - if (cpuIdsPtr == NULL) { - goto vir_buffer_failed; - } - /* start filling in xml */ r = virBufferVSprintf (xml, "\ \n\ \n", numCells); - if (r == -1) goto vir_buffer_failed; + if (r < 0) goto vir_buffer_failed; - offset = nodeToCpu; - /* now iterate through all cells and find associated cpu ids */ - /* example of string being parsed: "node0:0-3,7,9-10\n node1:11-14\n" */ - while ((offset = strstr(offset, "node")) != NULL) { - cpuIdsPtr = iCpuIdsPtr; - cellCpuCount = 0; - offset +=4; - if ((len = getNumber(offset, &cellNum)) < 0) { - virXendError(conn, VIR_ERR_XEN_CALL, " topology string syntax error"); - goto error; - } - offset += len; - if (*(offset) != ':') { - virXendError(conn, VIR_ERR_XEN_CALL, " topology string syntax error"); - goto error; - } - offset++; - /* get list of cpus associated w/ single cell */ - while (1) { - len = getNumber(offset, &cpuNum); - if (len < 0) { - if (!strncmp (offset, "no cpus", 7)){ - *(cpuIdsPtr++) = -1; - break; - } else { - virXendError(conn, VIR_ERR_XEN_CALL, "topology string syntax error"); - goto error; - } - } - offset += len; - next = *(offset); - if (next == '-') { - offset++; - start = cpuNum; - if ((len = getNumber(offset, &finish)) < 0) { - virXendError(conn, VIR_ERR_XEN_CALL, " topology string syntax error"); - goto error; - } - if (start > finish) { - virXendError(conn, VIR_ERR_XEN_CALL, " topology string syntax error"); - goto error; + r = parseTopology(conn, xml, nodeToCpu, numCpus); + if (r < 0) goto error; - } - for (i=start; i<=finish; i++) { - nodeCpuCount++; - if (nodeCpuCount > numCpus) { - virXendError(conn, VIR_ERR_XEN_CALL, - "conflicting cpu counts"); - goto error; - } - *(cpuIdsPtr++) = i; - cellCpuCount++; - } - offset += len; - next = *(offset); - offset++; - if (next == ',') { - continue; - } else if ((next == '\\') || (next =='\0')) { - break; - } else { - virXendError(conn, VIR_ERR_XEN_CALL, - " topology string syntax error"); - goto error; - } - } else { - /* add the single number */ - if (nodeCpuCount >= numCpus) { - virXendError(conn, VIR_ERR_XEN_CALL, - "conflicting cpu counts"); - goto error; - } - *(cpuIdsPtr++) = cpuNum; - cellCpuCount++; - nodeCpuCount++; - if (next == ',') { - offset++; - continue; - } else if ((next == '\\') || (next =='\0')) { - break; - } else { - virXendError(conn, VIR_ERR_XEN_CALL, - " topology string syntax error"); - goto error; - } - } - } - - /* add xml for all cpus associated with one cell */ - r = virBufferVSprintf (xml, "\ - \n\ - \n", cellNum, cellCpuCount); - if (r == -1) goto vir_buffer_failed; - - for (i = 0; i < cellCpuCount; i++) { - if (*(iCpuIdsPtr + i) == -1) - break; - r = virBufferVSprintf (xml, "\ - \n", *(iCpuIdsPtr + i)); - if (r == -1) goto vir_buffer_failed; - } - r = virBufferAdd (xml, "\ - \n\ - \n", -1); - if (r == -1) goto vir_buffer_failed; - } r = virBufferAdd (xml, "\ \n\ \n", -1); - if (r == -1) goto vir_buffer_failed; - free(iCpuIdsPtr); + if (r < 0) goto vir_buffer_failed; return (0); @@ -2090,8 +2290,6 @@ vir_buffer_failed: virXendError(conn, VIR_ERR_NO_MEMORY, _("allocate new buffer")); error: - if (iCpuIdsPtr) - free(iCpuIdsPtr); return (-1); }