[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