This is in response to a request in:
https://bugzilla.redhat.com/show_bug.cgi?id=665293
In short, under heavy load, it's possible for qemu's networking to
lock up due to the tap device's default 1MB sndbuf being
inadequate. adding "sndbuf=0" to the qemu commandline -netdevice
option will alleviate this problem (sndbuf=0 actually sets it to
0xffffffff).
Because we must be able to explicitly specify "0" as a value, the
standard practice of "0 means not specified" won't work here. Instead,
virDomainNetDef also has a sndbuf_specified, which defaults to 0, but
is set to 1 if some value was given.
The sndbuf value is put inside a <tune> element of each <interface> in
the domain. The intent is that further tunable settings will also be
placed inside this elemnent.
<interface type='network'>
...
<tune>
<sndbuf>0</sndbuf>
...
</tune>
</interface>
---
Note that in qemuBuildHostNetStr() I have moved
if (vhostfd && *vhostfd) {
virBufferVSprintf(&buf, ",vhost=on,vhostfd=%s", vhostfd);
into a newly created "if (is_tap) { ... }" block. This always should
have been inside such a conditional, but none existed until now. (I
can make this a separate patch if anyone wants, but it seemed so
simple and obvious that I took the slothenly way out :-)
Also, as with the vhost patch, I didn't get the html docs updated for
this addition either. I will add both in a single followup patch next
week.
docs/schemas/domain.rng | 10 ++++++++++
src/conf/domain_conf.c | 31 +++++++++++++++++++++++++++++--
src/conf/domain_conf.h | 4 ++++
src/qemu/qemu_command.c | 19 +++++++++++++++++--
4 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng
index 6d0654d..e2883aa 100644
--- a/docs/schemas/domain.rng
+++ b/docs/schemas/domain.rng
@@ -1025,6 +1025,16 @@
<ref name="filterref-node-attributes"/>
</element>
</optional>
+ <optional>
+ <element name="tune">
+ <optional>
+ <!-- size of send buffer for network tap devices -->
+ <element name="sndbuf">
+ <ref name="unsignedInt"/>
+ </element>
+ </optional>
+ </element>
+ </optional>
</interleave>
</define>
<define name="virtualPortProfile">
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 04ed502..5d1b8cf 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2280,6 +2280,7 @@ err_exit:
static virDomainNetDefPtr
virDomainNetDefParseXML(virCapsPtr caps,
xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
int flags ATTRIBUTE_UNUSED) {
virDomainNetDefPtr def;
xmlNodePtr cur;
@@ -2298,15 +2299,19 @@ virDomainNetDefParseXML(virCapsPtr caps,
char *internal = NULL;
char *devaddr = NULL;
char *mode = NULL;
+ unsigned long sndbuf;
virNWFilterHashTablePtr filterparams = NULL;
virVirtualPortProfileParams virtPort;
bool virtPortParsed = false;
+ xmlNodePtr oldnode = ctxt->node;
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
return NULL;
}
+ ctxt->node = node;
+
type = virXMLPropString(node, "type");
if (type != NULL) {
if ((int)(def->type = virDomainNetTypeFromString(type)) < 0) {
@@ -2593,7 +2598,13 @@ virDomainNetDefParseXML(virCapsPtr caps,
}
}
+ if (virXPathULong("string(./tune/sndbuf)", ctxt, &sndbuf) >= 0) {
+ def->tune.sndbuf = sndbuf;
+ def->tune.sndbuf_specified = 1;
+ }
+
cleanup:
+ ctxt->node = oldnode;
VIR_FREE(macaddr);
VIR_FREE(network);
VIR_FREE(address);
@@ -4303,6 +4314,7 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
{
xmlDocPtr xml;
xmlNodePtr node;
+ xmlXPathContextPtr ctxt = NULL;
virDomainDeviceDefPtr dev = NULL;
if (!(xml = xmlReadDoc(BAD_CAST xmlStr, "device.xml", NULL,
@@ -4319,6 +4331,13 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
goto error;
}
+ ctxt = xmlXPathNewContext(xml);
+ if (ctxt == NULL) {
+ virReportOOMError();
+ goto error;
+ }
+ ctxt->node = node;
+
if (VIR_ALLOC(dev) < 0) {
virReportOOMError();
goto error;
@@ -4334,7 +4353,7 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
goto error;
} else if (xmlStrEqual(node->name, BAD_CAST "interface")) {
dev->type = VIR_DOMAIN_DEVICE_NET;
- if (!(dev->data.net = virDomainNetDefParseXML(caps, node, flags)))
+ if (!(dev->data.net = virDomainNetDefParseXML(caps, node, ctxt, flags)))
goto error;
} else if (xmlStrEqual(node->name, BAD_CAST "input")) {
dev->type = VIR_DOMAIN_DEVICE_INPUT;
@@ -4372,11 +4391,12 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
}
xmlFreeDoc(xml);
-
+ xmlXPathFreeContext(ctxt);
return dev;
error:
xmlFreeDoc(xml);
+ xmlXPathFreeContext(ctxt);
VIR_FREE(dev);
return NULL;
}
@@ -5047,6 +5067,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
for (i = 0 ; i < n ; i++) {
virDomainNetDefPtr net = virDomainNetDefParseXML(caps,
nodes[i],
+ ctxt,
flags);
if (!net)
goto error;
@@ -6315,6 +6336,12 @@ virDomainNetDefFormat(virBufferPtr buf,
VIR_FREE(attrs);
}
+ if (def->tune.sndbuf_specified) {
+ virBufferAddLit(buf, " <tune>\n");
+ virBufferVSprintf(buf, " <sndbuf>%d</sndbuf>\n",
def->tune.sndbuf);
+ virBufferAddLit(buf, " </tune>\n");
+ }
+
if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0)
return -1;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 451ccad..2d35d68 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -346,6 +346,10 @@ struct _virDomainNetDef {
virVirtualPortProfileParams virtPortProfile;
} direct;
} data;
+ struct {
+ int sndbuf_specified : 1;
+ int sndbuf;
+ } tune;
char *ifname;
virDomainDeviceInfo info;
char *filter;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 9eb54a1..add66cb 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1587,6 +1587,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net,
const char *tapfd,
const char *vhostfd)
{
+ bool is_tap = false;
virBuffer buf = VIR_BUFFER_INITIALIZER;
switch (net->type) {
@@ -1596,6 +1597,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net,
virBufferAddLit(&buf, "tap");
virBufferVSprintf(&buf, "%cfd=%s", type_sep, tapfd);
type_sep = ',';
+ is_tap = true;
break;
case VIR_DOMAIN_NET_TYPE_ETHERNET:
@@ -1609,6 +1611,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net,
net->data.ethernet.script);
type_sep = ',';
}
+ is_tap = true;
break;
case VIR_DOMAIN_NET_TYPE_CLIENT:
@@ -1662,8 +1665,11 @@ qemuBuildHostNetStr(virDomainNetDefPtr net,
type_sep, net->info.alias);
}
- if (vhostfd && *vhostfd) {
- virBufferVSprintf(&buf, ",vhost=on,vhostfd=%s", vhostfd);
+ if (is_tap) {
+ if (vhostfd && *vhostfd)
+ virBufferVSprintf(&buf, ",vhost=on,vhostfd=%s", vhostfd);
+ if (net->tune.sndbuf_specified)
+ virBufferVSprintf(&buf, ",sndbuf=%d", net->tune.sndbuf);
}
if (virBufferError(&buf)) {
@@ -4665,6 +4671,15 @@ qemuParseCommandLineNet(virCapsPtr caps,
def->backend = VIR_DOMAIN_NET_BACKEND_TYPE_QEMU;
}
def->backend_specified = 1;
+ } else if (STREQ(keywords[i], "sndbuf") && values[i]) {
+ if (virStrToLong_i(values[i], NULL, 10, &def->tune.sndbuf) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse sndbuf size in '%s'"),
val);
+ virDomainNetDefFree(def);
+ def = NULL;
+ goto cleanup;
+ }
+ def->tune.sndbuf_specified = 1;
}
}
--
1.7.3.4