On Thu, Oct 18, 2007 at 04:53:15AM -0400, Daniel Veillard wrote: > On Wed, Oct 17, 2007 at 10:14:56PM -0400, beth kon wrote: > > This is certainly a nit, but I might change parseCpuNumber to > > parseNumber, since it looks a little odd that you are getting the cell > > id from the cpu number. A nit to be sure! > > well, the only problem is if you have more cells than maxcpu, > which doesn't make that much sense. Since we are testing against > maxcpu I really think we should keep the function name, I can > duplicate code and make a simpler function just for parsing the > cell no. > > > Other than our discussion on #virt about handling of ^N specifications > > in parseCpuSet, looks good! > > Which is done in my version, will propagate. New version with the changes suggested. Also the dump exercize the serialization code ifor CPUset (see the new <dump> elements which won't be used in the final code but useful to check correctness of output) Valgrind tst allowed me to found a bug in virBufferContentAndFree() 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 char * saveCpuSet(virConnectPtr conn, char *cpuset, 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", "node0:^2,1-3", }; 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; } /** * 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) { virXendError(NULL, 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); } /** * 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); }
<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