[Libvir] CPU pinning of domains at creation time

Daniel Veillard veillard at redhat.com
Wed Oct 17 14:53:17 UTC 2007


On Thu, Oct 11, 2007 at 10:45:44AM -0500, Ryan Harper wrote:
> * Daniel Veillard <veillard at redhat.com> [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 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 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);
}

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