#include #include #include #include #include #include #include #include #include #include #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, "\n", -1); for (i = 0;i < (sizeof(tests) / sizeof(tests[0]));i++) { virBufferVSprintf (buf, " \n", i); virBufferVSprintf (buf, " %s\n", tests[i]); if (parseTopology(NULL, buf, tests[i], 16) != 0) { virBufferVSprintf (buf, " %s\n", last_error); } virBufferVSprintf (buf, " \n"); } virBufferAdd(buf, "\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, "\ \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); }