On Thu, Oct 11, 2007 at 10:45:44AM -0500, Ryan Harper wrote: > * Daniel Veillard <veillard@xxxxxxxxxx> [2007-10-11 08:01]: > > - for the mapping at the XML level I suggest to use a simple extension > > to the <vcpu>n</vcpu> and extend it to > > <vcpu cpuset='2,3'>n</vcpu> > > with a limited syntax which is just the comma separated list of > > allowed CPU numbers (if the code actually detects such a cpuset is > > in effect i.e. in general this won't be added). > > I think we should support the same cpuset notation that Xen supports, > which means including ranges (1-4) and negation (^1). These two > features make describing large ranges much more compact. Enclosed is a rewrite of the cpuset notations, which can plug as a replacement for the current code in xend_internals, it should support the existing syntax currently used to parse xend topology strings, and also alllow ranges and negation. It's not as a patch but as a standlone replacement program which can be used to test (in spirit of the old topology.c one from Beth). I guess that's okay, check the test output (and possibly extend the test cases in tests array), It tried to think of everything including the weird \\n python xend bug and the 'no cpus' in cell cases. Just dump tst.c in libvirt/src, add $(INCLUDES) to the $(CC) $(CFLAGS) -I../include -o tst tst.c .... line and run make tst ./tst and check the output (also enclosed), The parsing is done in a slightly different way, but that should not change the output, 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/
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/time.h> #include <sys/resource.h> #include <unistd.h> #include <stdlib.h> #include <libvirt/libvirt.h> #define STANDALONE #include "buf.c" static int parseTopology(virConnectPtr conn, virBufferPtr xml, const char *str, int maxcpu); static const char *last_error = ""; static void virXendError(void *conn, virErrorNumber error, const char *info) { last_error = info; } #ifdef STANDALONE const char *tests[] = { "node0:0-3,7,9-10\n node1:11-14\n", "node0:4,7\n", "node0:8-9,11\n", "node0:0,3,7,9\n node11:11,14\n", "node0:9\n node11:11\n node13:12-14\n", "node0:0\n", "node0:0-3\n node1:4,5-9\n", "node0:0-3\n \n", "node0:0\n node1:4,6-9\n", "node0:Easeirasde1:4,6-9\n", "node0:0-3-7,9-10\n node1:11-14\n", "node0:0\n node1:1", "node0:0\\nnode1:1", /* xend serialization fun */ "node0:0 node1:^0", /* all CPUs except 0 */ "node0:0-3,^2", "node0:0-3,^2,^1", "node0:0-3,^2,^1,^1", "node0:0-3,^1-2", /* not allowed */ "node0:0-3\nnode1:no cpus\nnode2:4-7", }; int main(int argc, char **argv) { int i; virBufferPtr buf; int ret = 0; buf = virBufferNew(1000); if (buf == NULL) { virXendError(NULL, VIR_ERR_NO_MEMORY, _("allocate buffer")); exit(1); } virBufferAdd(buf, "<results>\n", -1); for (i = 0;i < (sizeof(tests) / sizeof(tests[0]));i++) { virBufferVSprintf (buf, " <test no='%d'>\n", i); virBufferVSprintf (buf, " <input>%s</input>\n", tests[i]); if (parseTopology(NULL, buf, tests[i], 16) != 0) { virBufferVSprintf (buf, " <error>%s</error>\n", last_error); } virBufferVSprintf (buf, " </test>\n"); } virBufferAdd(buf, "</results>\n", -1); printf("%s", buf->content); virBufferFree(buf); exit(ret); } #endif /* STANDALONE */ /** * 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; } /** * 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); } /** * 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; if (*cur == '^') { /* initialize cpumap to all 1s */ for (i = 0;i < maxcpu;i++) cpuset[i] = 1; ret = maxcpu; } else { /* 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 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++) { 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 = parseCpuNumber(&cur, maxcpu); 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); 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); }
<results> <test no='0'> <input>node0:0-3,7,9-10 node1:11-14 </input> <cell id='0'> <cpus num='7'> <cpu id='0'/> <cpu id='1'/> <cpu id='2'/> <cpu id='3'/> <cpu id='7'/> <cpu id='9'/> <cpu id='10'/> </cpus> </cell> <cell id='1'> <cpus num='4'> <cpu id='11'/> <cpu id='12'/> <cpu id='13'/> <cpu id='14'/> </cpus> </cell> </test> <test no='1'> <input>node0:4,7 </input> <cell id='0'> <cpus num='2'> <cpu id='4'/> <cpu id='7'/> </cpus> </cell> </test> <test no='2'> <input>node0:8-9,11 </input> <cell id='0'> <cpus num='3'> <cpu id='8'/> <cpu id='9'/> <cpu id='11'/> </cpus> </cell> </test> <test no='3'> <input>node0:0,3,7,9 node11:11,14 </input> <cell id='0'> <cpus num='4'> <cpu id='0'/> <cpu id='3'/> <cpu id='7'/> <cpu id='9'/> </cpus> </cell> <cell id='11'> <cpus num='2'> <cpu id='11'/> <cpu id='14'/> </cpus> </cell> </test> <test no='4'> <input>node0:9 node11:11 node13:12-14 </input> <cell id='0'> <cpus num='1'> <cpu id='9'/> </cpus> </cell> <cell id='11'> <cpus num='1'> <cpu id='11'/> </cpus> </cell> <cell id='13'> <cpus num='3'> <cpu id='12'/> <cpu id='13'/> <cpu id='14'/> </cpus> </cell> </test> <test no='5'> <input>node0:0 </input> <cell id='0'> <cpus num='1'> <cpu id='0'/> </cpus> </cell> </test> <test no='6'> <input>node0:0-3 node1:4,5-9 </input> <cell id='0'> <cpus num='4'> <cpu id='0'/> <cpu id='1'/> <cpu id='2'/> <cpu id='3'/> </cpus> </cell> <cell id='1'> <cpus num='6'> <cpu id='4'/> <cpu id='5'/> <cpu id='6'/> <cpu id='7'/> <cpu id='8'/> <cpu id='9'/> </cpus> </cell> </test> <test no='7'> <input>node0:0-3 </input> <cell id='0'> <cpus num='4'> <cpu id='0'/> <cpu id='1'/> <cpu id='2'/> <cpu id='3'/> </cpus> </cell> </test> <test no='8'> <input>node0:0 node1:4,6-9 </input> <cell id='0'> <cpus num='1'> <cpu id='0'/> </cpus> </cell> <cell id='1'> <cpus num='5'> <cpu id='4'/> <cpu id='6'/> <cpu id='7'/> <cpu id='8'/> <cpu id='9'/> </cpus> </cell> </test> <test no='9'> <input>node0:Easeirasde1:4,6-9 </input> <error>topology cpuset syntax error</error> </test> <test no='10'> <input>node0:0-3-7,9-10 node1:11-14 </input> <error>topology cpuset syntax error</error> </test> <test no='11'> <input>node0:0 node1:1</input> <cell id='0'> <cpus num='1'> <cpu id='0'/> </cpus> </cell> <cell id='1'> <cpus num='1'> <cpu id='1'/> </cpus> </cell> </test> <test no='12'> <input>node0:0\nnode1:1</input> <cell id='0'> <cpus num='1'> <cpu id='0'/> </cpus> </cell> <cell id='1'> <cpus num='1'> <cpu id='1'/> </cpus> </cell> </test> <test no='13'> <input>node0:0 node1:^0</input> <cell id='0'> <cpus num='1'> <cpu id='0'/> </cpus> </cell> <cell id='1'> <cpus num='15'> <cpu id='1'/> <cpu id='2'/> <cpu id='3'/> <cpu id='4'/> <cpu id='5'/> <cpu id='6'/> <cpu id='7'/> <cpu id='8'/> <cpu id='9'/> <cpu id='10'/> <cpu id='11'/> <cpu id='12'/> <cpu id='13'/> <cpu id='14'/> <cpu id='15'/> </cpus> </cell> </test> <test no='14'> <input>node0:0-3,^2</input> <cell id='0'> <cpus num='3'> <cpu id='0'/> <cpu id='1'/> <cpu id='3'/> </cpus> </cell> </test> <test no='15'> <input>node0:0-3,^2,^1</input> <cell id='0'> <cpus num='2'> <cpu id='0'/> <cpu id='3'/> </cpus> </cell> </test> <test no='16'> <input>node0:0-3,^2,^1,^1</input> <cell id='0'> <cpus num='2'> <cpu id='0'/> <cpu id='3'/> </cpus> </cell> </test> <test no='17'> <input>node0:0-3,^1-2</input> <error>topology cpuset syntax error</error> </test> <test no='18'> <input>node0:0-3 node1:no cpus node2:4-7</input> <cell id='0'> <cpus num='4'> <cpu id='0'/> <cpu id='1'/> <cpu id='2'/> <cpu id='3'/> </cpus> </cell> <cell id='1'> <cpus num='0'> </cpus> </cell> <cell id='2'> <cpus num='4'> <cpu id='4'/> <cpu id='5'/> <cpu id='6'/> <cpu id='7'/> </cpus> </cell> </test> </results>
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list