The following patch finishes the cleanup for NUMA parsing code: - the cpuset parsing is moved to xml.c - some comments and cleanups of the include then add the output of a (cpus '...') line based on the /domain/vcpu/@cpuset attributes, this is parsed and reserialized as ranges to avoid any possibility of misinterpretation of say ^ or any special syntax we may want to add in the future. A few things to note: - dependant on the tiny patch I sent earlier today - if we notice that the set covers all CPU maybe we should avoid outputing (cpus '...'), trivial to add - mostly untested yet Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@xxxxxxxxxx | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/
Index: src/xend_internal.c =================================================================== RCS file: /data/cvs/libxen/src/xend_internal.c,v retrieving revision 1.150 diff -u -p -r1.150 xend_internal.c --- src/xend_internal.c 22 Oct 2007 13:06:15 -0000 1.150 +++ src/xend_internal.c 22 Oct 2007 16:31:17 -0000 @@ -1896,352 +1896,6 @@ sexpr_to_xend_node_info(struct sexpr *ro } /** - * skipSpaces: - * @str: pointer to the char pointer used - * - * 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 - * - * Parse a number - * - * Returns the CPU number or -1 in case of error. @str will be - * updated to skip the number. - */ -static int -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, "\ - <cell id='%d'>\n\ - <cpus num='%d'>\n", cell, nb_cpus); -#ifdef STANDALONE - { - char *dump; - - dump = saveCpuSet(conn, cpuset, maxcpu); - if (dump != NULL) { - virBufferVSprintf (xml, " <dump>%s</dump>\n", dump); - free(dump); - } else { - virBufferVSprintf (xml, " <error>%s</error>\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, "\ - <cpu id='%d'/>\n", cpu); - if (ret < 0) - goto memory_error; - } - } - ret = virBufferAdd (xml, "\ - </cpus>\n\ - </cell>\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); -} - -/** * sexpr_to_xend_topology_xml: * @root: an S-Expression describing a node * @@ -2276,7 +1930,7 @@ sexpr_to_xend_topology_xml(virConnectPtr numCells); if (r < 0) goto vir_buffer_failed; - r = parseTopology(conn, xml, nodeToCpu, numCpus); + r = virParseXenCpuTopology(conn, xml, nodeToCpu, numCpus); if (r < 0) goto error; r = virBufferAdd (xml, "\ Index: src/xml.c =================================================================== RCS file: /data/cvs/libxen/src/xml.c,v retrieving revision 1.92 diff -u -p -r1.92 xml.c --- src/xml.c 15 Oct 2007 21:38:56 -0000 1.92 +++ src/xml.c 22 Oct 2007 16:31:17 -0000 @@ -24,6 +24,7 @@ #include "xml.h" #include "buf.h" #include "xs_internal.h" /* for xenStoreDomainGetNetworkID */ +#include "xen_unified.h" #ifndef PROXY /** @@ -48,6 +49,362 @@ virXMLError(virConnectPtr conn, virError errmsg, info, NULL, value, 0, errmsg, info, value); } +/************************************************************************ + * * + * Parser and converter for the CPUset strings used in libvirt * + * * + ************************************************************************/ + +/** + * skipSpaces: + * @str: pointer to the char pointer used + * + * 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 + * + * Parse a number + * + * Returns the CPU number or -1 in case of error. @str will be + * updated to skip the number. + */ +static int +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); +} + +/** + * 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) { + virXMLError(conn, VIR_ERR_NO_MEMORY, _("allocate buffer"), 1000); + 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); +} + +/** + * virParseCpuSet: + * @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 + */ +int +virParseCpuSet(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: + virXMLError(conn, VIR_ERR_XEN_CALL, + _("topology cpuset syntax error"), 0); + return(-1); +} + +/** + * virParseXenCpuTopology: + * @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 + */ +int +virParseXenCpuTopology(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 = virParseCpuSet(conn, &cur, 'n', cpuset, maxcpu); + if (nb_cpus < 0) + goto error; + } + + /* + * add xml for all cpus associated with that cell + */ + ret = virBufferVSprintf (xml, "\ + <cell id='%d'>\n\ + <cpus num='%d'>\n", cell, nb_cpus); +#ifdef STANDALONE + { + char *dump; + + dump = saveCpuSet(conn, cpuset, maxcpu); + if (dump != NULL) { + virBufferVSprintf (xml, " <dump>%s</dump>\n", dump); + free(dump); + } else { + virBufferVSprintf (xml, " <error>%s</error>\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, "\ + <cpu id='%d'/>\n", cpu); + if (ret < 0) + goto memory_error; + } + } + ret = virBufferAdd (xml, "\ + </cpus>\n\ + </cell>\n", -1); + if (ret < 0) + goto memory_error; + + } + free(cpuset); + return(0); + +parse_error: + virXMLError(conn, VIR_ERR_XEN_CALL, + _("topology syntax error"), 0); +error: + if (cpuset != NULL) + free(cpuset); + + return(-1); + +memory_error: + if (cpuset != NULL) + free(cpuset); + virXMLError(conn, VIR_ERR_NO_MEMORY, _("allocate buffer"), 0); + return(-1); +} + + +/************************************************************************ + * * + * Wrappers around libxml2 XPath specific functions * + * * + ************************************************************************/ + /** * virXPathString: * @xpath: the XPath string to evaluate @@ -271,6 +628,12 @@ virXPathNodeSet(const char *xpath, xmlXP return(ret); } +/************************************************************************ + * * + * Converter functions to go from the XML tree to an S-Expr for Xen * + * * + ************************************************************************/ + /** * virtDomainParseXMLGraphicsDescImage: * @conn: pointer to the hypervisor connection @@ -1167,6 +1530,36 @@ virDomainParseXMLDesc(virConnectPtr conn } virBufferVSprintf(&buf, "(vcpus %u)", vcpus); + str = virXPathString("string(/domain/vcpu/@cpuset)", ctxt); + if (str != NULL) { + int maxcpu = xenNbCpus(conn); + char *cpuset = NULL; + char *ranges = NULL; + const char *cur = str; + + /* + * Parse the CPUset attribute given in libvirt format and reserialize + * it in a range format guaranteed to be understood by Xen. + */ + if (maxcpu > 0) { + cpuset = malloc(maxcpu * sizeof(char)); + if (cpuset != NULL) { + res = virParseCpuSet(conn, &cur, 0, cpuset, maxcpu); + if (res > 0) { + ranges = saveCpuSet(conn, cpuset, maxcpu); + if (ranges != NULL) { + virBufferVSprintf(&buf, "(cpus '%s')", ranges); + free(ranges); + } + } + free(cpuset); + } else { + virXMLError(conn, VIR_ERR_NO_MEMORY, xmldesc, 0); + } + } + free(str); + } + str = virXPathString("string(/domain/uuid[1])", ctxt); if (str != NULL) { virBufferVSprintf(&buf, "(uuid '%s')", str); Index: src/xml.h =================================================================== RCS file: /data/cvs/libxen/src/xml.h,v retrieving revision 1.18 diff -u -p -r1.18 xml.h --- src/xml.h 15 Oct 2007 21:38:56 -0000 1.18 +++ src/xml.h 22 Oct 2007 16:31:17 -0000 @@ -7,6 +7,7 @@ #include "libvirt/libvirt.h" #include "internal.h" +#include "buf.h" #include <libxml/parser.h> #include <libxml/tree.h> @@ -31,9 +32,28 @@ int virXPathNodeSet (const char *xpath, xmlXPathContextPtr ctxt, xmlNodePtr **list); -char *virDomainParseXMLDesc(virConnectPtr conn, const char *xmldesc, char **name, int xendConfigVersion); -char *virParseXMLDevice(virConnectPtr conn, const char *xmldesc, int hvm, int xendConfigVersion); - int virDomainXMLDevID(virDomainPtr domain, const char *xmldesc, char *class, char *ref, int ref_len); +int virParseXenCpuTopology(virConnectPtr conn, + virBufferPtr xml, + const char *str, + int maxcpu); +int virParseCpuSet (virConnectPtr conn, + const char **str, + char sep, + char *cpuset, + int maxcpu); +char * virDomainParseXMLDesc(virConnectPtr conn, + const char *xmldesc, + char **name, + int xendConfigVersion); +char * virParseXMLDevice(virConnectPtr conn, + const char *xmldesc, + int hvm, + int xendConfigVersion); +int virDomainXMLDevID(virDomainPtr domain, + const char *xmldesc, + char *class, + char *ref, + int ref_len); #ifdef __cplusplus }
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list