[Libvir] CPU pinning of domains at creation time

Daniel Veillard veillard at redhat.com
Thu Oct 18 16:33:54 UTC 2007


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 at redhat.com  | libxml GNOME XML XSLT toolkit  http://xmlsoft.org/
http://veillard.com/ | Rpmfind RPM search engine  http://rpmfind.net/
-------------- next part --------------
#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);
}

-------------- next part --------------
<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>


More information about the libvir-list mailing list