}
/**
- * 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, "\
+ <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(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) {
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,
"\
<topology>\n\
<cells num='%d'>\n",
numCells);
- if (r == -1) 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;
-
- }
- 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;
- }
- }
- }
+ if (r < 0) goto vir_buffer_failed;
- /* add xml for all cpus associated with one cell */
- r = virBufferVSprintf (xml, "\
- <cell id='%d'>\n\
- <cpus num='%d'>\n", cellNum, cellCpuCount);
- if (r == -1) goto vir_buffer_failed;
+ r = parseTopology(conn, xml, nodeToCpu, numCpus);
+ if (r < 0) goto error;
- for (i = 0; i < cellCpuCount; i++) {
- if (*(iCpuIdsPtr + i) == -1)
- break;
- r = virBufferVSprintf (xml, "\
- <cpu id='%d'/>\n", *(iCpuIdsPtr + i));
- if (r == -1) goto vir_buffer_failed;
- }
- r = virBufferAdd (xml, "\
- </cpus>\n\
- </cell>\n", -1);
- if (r == -1) goto vir_buffer_failed;
- }
r = virBufferAdd (xml, "\
</cells>\n\
</topology>\n", -1);
- if (r == -1) goto vir_buffer_failed;
- free(iCpuIdsPtr);
+ if (r < 0) goto vir_buffer_failed;
return (0);
virXendError(conn, VIR_ERR_NO_MEMORY, _("allocate new buffer"));
error:
- if (iCpuIdsPtr)
- free(iCpuIdsPtr);
return (-1);
}