[libvirt] [PATCH v2 0/6] Allow minimal bandwidth guarantee

Since 0.9.4 release libvirt supports setting some QoS attributes on virtual bridges and/or virtual interfaces. Average and peak rate, namely. It lacks minimal guaranteed bandwidth aka 'floor' attribute, though. This patch set tries to fill that hole. However, there are some things you want to know before diving into individual patches: 1) Hierarchical shaping The whole magic hidden in here: Classes [1] can have multiple children and form a tree. A class can borrow unused bandwidth from other sibling classes up to its 'ceil'. This is done in kernel, though. Classes does not share any info among interfaces. Therefore we need to group traffic from domain interfaces so it goes over one point (a bridge). It is now obvious why this functionality is limited to VIR_DOMAIN_NET_TYPE_NETWORK only as the other bridged network type (VIR_DOMAIN_NET_TYPE_BRIDGE:) is meant to be not managed or touched by libvirt at all. This patches build that tree of classes. On network startup, a root class with one child class are created. This child is supposed to be catchment for all interfaces which will ever be plugged and don't have any 'floor'. Once we have network prepared, an interface may be plugged in. That means on a domain startup, as we create virtual devices for a domain and plug them into a bridge, we can create separate class for created interface (if it is needed = has 'floor' set). Class are distinguished by u_int16 identifier which: - needs to be unique among one bridge, obviously - we want to keep, and pass on interface unplugging 2) Network status file Because of the class ID I am mentioning above, I found myself in need of saving next free class ID among with network, so it survives daemon reboots. That's what 5th patch does actually. On interface plug event an unique class ID is taken and on successful QoS set it is stored into an interface. Having said that, domain status XML was extended to keep pair <interface alias; class id> 3) Tying interface traffic with class is done via filter [2]. Filters direct packets into a classes during which packet undergoes examination. Such filter is caller classifier. For example, filter classify packets based on their marks, src/dest ip address, port, etc. And here comes another magic trick. But not so nice as the first one. Libvirt does not know anything about guest (interface) ip address(es). The only thing that libvirt knows for sure is MAC address. But for some reason, filters refuse to use ebtables marks even if iptables marks works well. Filters, however does not support classifying by MAC address. Well, not directly. u32 filter can match any part of a packet at any offset. Offset 0 is start of IP header. And offsets can be negative :) 1: http://tldp.org/HOWTO/Traffic-Control-HOWTO/components.html#c-class 2: http://tldp.org/HOWTO/Traffic-Control-HOWTO/components.html#c-filter diff to v1: -rebased & resolved minor conflicts Michal Privoznik (6): bandwidth: add new 'floor' attribute bandwidth: Create hierarchical shaping classes bandwidth: Create (un)plug functions bandwidth: Create network (un)plug functions network: Create status files domain: Keep assigned class_id in domstatus XML daemon/libvirtd.c | 3 + docs/formatdomain.html.in | 21 +++- docs/schemas/networkcommon.rng | 5 + po/POTFILES.in | 1 + src/conf/domain_conf.c | 10 +- src/conf/domain_conf.h | 1 + src/conf/netdev_bandwidth_conf.c | 81 +++++++++++--- src/conf/netdev_bandwidth_conf.h | 3 +- src/conf/network_conf.c | 224 ++++++++++++++++++++++++++++++++------ src/conf/network_conf.h | 9 ++ src/libvirt_network.syms | 2 + src/libvirt_private.syms | 2 + src/lxc/lxc_driver.c | 3 +- src/network/bridge_driver.c | 109 +++++++++++++++++-- src/network/bridge_driver.h | 7 + src/qemu/qemu_command.c | 20 +++- src/qemu/qemu_domain.c | 66 ++++++++++- src/qemu/qemu_driver.c | 2 +- src/qemu/qemu_process.c | 12 ++ src/util/virnetdevbandwidth.c | 205 +++++++++++++++++++++++++++++++++- src/util/virnetdevbandwidth.h | 19 +++- src/util/virnetdevmacvlan.c | 2 +- 22 files changed, 714 insertions(+), 93 deletions(-) -- 1.7.3.4

This is however supported only on domain interfaces with type='network'. Moreover, target network needs to have at least inbound QoS set. This is required by hierarchical traffic shaping. --- docs/formatdomain.html.in | 21 ++++++++-- docs/schemas/networkcommon.rng | 5 ++ src/conf/domain_conf.c | 6 ++- src/conf/netdev_bandwidth_conf.c | 81 ++++++++++++++++++++++++++++++-------- src/conf/netdev_bandwidth_conf.h | 3 +- src/conf/network_conf.c | 4 +- src/util/virnetdevbandwidth.c | 2 +- src/util/virnetdevbandwidth.h | 1 + 8 files changed, 96 insertions(+), 27 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 42e23a1..0c5c998 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2390,7 +2390,7 @@ qemu-kvm -net nic,model=? /dev/null <source network='default'/> <target dev='vnet0'/> <b><bandwidth> - <inbound average='1000' peak='5000' burst='1024'/> + <inbound average='1000' peak='5000' floor='200' burst='1024'/> <outbound average='128' peak='256' burst='256'/> </bandwidth></b> </interface> @@ -2404,15 +2404,28 @@ qemu-kvm -net nic,model=? /dev/null and at most one <code>outbound</code> child elements. Leaving any of these children element out result in no QoS applied on that traffic direction. So, when you want to shape only domain's incoming traffic, use - <code>inbound</code> only, and vice versa. Each of these elements have one - mandatory attribute <code>average</code>. It specifies average bit rate on - interface being shaped. Then there are two optional attributes: + <code>inbound</code> only, and vice versa. For <code>outbound</code> + element <code>average</code> is mandatory. However, <code>inbound</code> + can satisfied with <code>floor</code> (iff interface's type='network' and + source network has inbound shaping set) or <code>average</code> (if you + want to set at least one of <code>peak</code>, <code>burst</code>). + <code>average</code> specifies average bit rate on interface being shaped. + Then there are two optional attributes: <code>peak</code>, which specifies maximum rate at which interface can send data, and <code>burst</code>, amount of bytes that can be burst at <code>peak</code> speed. Accepted values for attributes are integer numbers. The units for <code>average</code> and <code>peak</code> attributes are kilobytes per second, and for the <code>burst</code> just kilobytes. <span class="since">Since 0.9.4</span> + <code>floor</code> attribute tells libvirt to set hierarchical classes, + so interfaces can share unused bandwidth and have guaranteed rate at + the same time. But to be able to do this, we need traffic to go through + one point which can then borrow unused bandwidth to all currently + transmitting interfaces. Therefore, it required that interface is 'network' + type and source network have inbound QoS set. Be aware, libvirt does + not check if sum of <code>floor</code> on interfaces connected to a network + does not exceed network <code>peak</code>. + <span class="since">Since 0.9.9</span> </p> <h5><a name="elementLink">Modyfing virtual link state</a></h5> diff --git a/docs/schemas/networkcommon.rng b/docs/schemas/networkcommon.rng index 2328892..a6bef6e 100644 --- a/docs/schemas/networkcommon.rng +++ b/docs/schemas/networkcommon.rng @@ -77,6 +77,11 @@ </attribute> </optional> <optional> + <attribute name="floor"> + <ref name="speed"/> + </attribute> + </optional> + <optional> <attribute name='burst'> <ref name="BurstSize"/> </attribute> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 180dd2b..250290e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3634,7 +3634,8 @@ virDomainActualNetDefParseXML(xmlNodePtr node, bandwidth_node = virXPathNode("./bandwidth", ctxt); if (bandwidth_node && - !(actual->bandwidth = virNetDevBandwidthParse(bandwidth_node))) + !(actual->bandwidth = virNetDevBandwidthParse(bandwidth_node, + actual->type))) goto error; *def = actual; @@ -3793,7 +3794,8 @@ virDomainNetDefParseXML(virCapsPtr caps, if (virDomainActualNetDefParseXML(cur, ctxt, &actual) < 0) goto error; } else if (xmlStrEqual(cur->name, BAD_CAST "bandwidth")) { - if (!(def->bandwidth = virNetDevBandwidthParse(cur))) + if (!(def->bandwidth = virNetDevBandwidthParse(cur, + def->type))) goto error; } } diff --git a/src/conf/netdev_bandwidth_conf.c b/src/conf/netdev_bandwidth_conf.c index 24cd13d..21e5ac6 100644 --- a/src/conf/netdev_bandwidth_conf.c +++ b/src/conf/netdev_bandwidth_conf.c @@ -26,19 +26,28 @@ #include "virterror_internal.h" #include "util.h" #include "memory.h" +#include "domain_conf.h" #define VIR_FROM_THIS VIR_FROM_NONE #define virNetDevError(code, ...) \ virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) +enum virNetDevBandwidthElem { + VIR_NET_DEV_BANDWIDTH_ELEM_INBOUND, + VIR_NET_DEV_BANDWIDTH_ELEM_OUTBOUND +}; static int -virNetDevBandwidthParseRate(xmlNodePtr node, virNetDevBandwidthRatePtr rate) +virNetDevBandwidthParseRate(xmlNodePtr node, + virNetDevBandwidthRatePtr rate, + int elem_type, + int net_type) { int ret = -1; char *average = NULL; char *peak = NULL; + char *floor = NULL; char *burst = NULL; if (!node || !rate) { @@ -49,18 +58,32 @@ virNetDevBandwidthParseRate(xmlNodePtr node, virNetDevBandwidthRatePtr rate) average = virXMLPropString(node, "average"); peak = virXMLPropString(node, "peak"); + floor = virXMLPropString(node, "floor"); burst = virXMLPropString(node, "burst"); - if (average) { - if (virStrToLong_ull(average, NULL, 10, &rate->average) < 0) { - virNetDevError(VIR_ERR_CONFIG_UNSUPPORTED, - _("could not convert %s"), - average); - goto cleanup; - } - } else { + if (!average && !floor) { + virNetDevError(VIR_ERR_XML_DETAIL, "%s", + _("Missing 'average' or 'floor' attribute")); + goto cleanup; + } + + if (!average && (peak || burst)) { virNetDevError(VIR_ERR_XML_DETAIL, "%s", - _("Missing mandatory average attribute")); + _("'peak' and 'burst' require 'average' attribute")); + goto cleanup; + } + + if (floor && elem_type != VIR_NET_DEV_BANDWIDTH_ELEM_INBOUND && + net_type != VIR_DOMAIN_NET_TYPE_NETWORK) { + virNetDevError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'floor' is not supported in this context")); + goto cleanup; + } + + if (average && virStrToLong_ull(average, NULL, 10, &rate->average) < 0) { + virNetDevError(VIR_ERR_CONFIG_UNSUPPORTED, + _("could not convert %s"), + average); goto cleanup; } @@ -71,6 +94,13 @@ virNetDevBandwidthParseRate(xmlNodePtr node, virNetDevBandwidthRatePtr rate) goto cleanup; } + if (floor && virStrToLong_ull(floor, NULL, 10, &rate->floor) < 0) { + virNetDevError(VIR_ERR_CONFIG_UNSUPPORTED, + _("could not convert %s"), + floor); + goto cleanup; + } + if (burst && virStrToLong_ull(burst, NULL, 10, &rate->burst) < 0) { virNetDevError(VIR_ERR_CONFIG_UNSUPPORTED, _("could not convert %s"), @@ -83,6 +113,7 @@ virNetDevBandwidthParseRate(xmlNodePtr node, virNetDevBandwidthRatePtr rate) cleanup: VIR_FREE(average); VIR_FREE(peak); + VIR_FREE(floor); VIR_FREE(burst); return ret; @@ -91,13 +122,21 @@ cleanup: /** * virNetDevBandwidthParse: * @node: XML node + * @net_type: type of network interface is connected to * - * Parse bandwidth XML and return pointer to structure + * Parse bandwidth XML and return pointer to structure. + * If interface is not connected to any network (e.g. + * it is a bridge), -1 must be passed. This is needed as + * some attributes are available only in some contexts, + * e.g. 'floor' only in + * "/domain/devices/interface[@type='network']/ + * bandwidth/inbound" * * Returns !NULL on success, NULL on error. */ virNetDevBandwidthPtr -virNetDevBandwidthParse(xmlNodePtr node) +virNetDevBandwidthParse(xmlNodePtr node, + int net_type) { virNetDevBandwidthPtr def = NULL; xmlNodePtr cur = node->children; @@ -144,7 +183,9 @@ virNetDevBandwidthParse(xmlNodePtr node) goto error; } - if (virNetDevBandwidthParseRate(in, def->in) < 0) { + if (virNetDevBandwidthParseRate(in, def->in, + VIR_NET_DEV_BANDWIDTH_ELEM_INBOUND, + net_type) < 0) { /* helper reported error for us */ goto error; } @@ -156,7 +197,9 @@ virNetDevBandwidthParse(xmlNodePtr node) goto error; } - if (virNetDevBandwidthParseRate(out, def->out) < 0) { + if (virNetDevBandwidthParseRate(out, def->out, + VIR_NET_DEV_BANDWIDTH_ELEM_OUTBOUND, + net_type) < 0) { /* helper reported error for us */ goto error; } @@ -179,13 +222,17 @@ virNetDevBandwidthRateFormat(virNetDevBandwidthRatePtr def, if (!def) return 0; - if (def->average) { - virBufferAsprintf(buf, " <%s average='%llu'", elem_name, - def->average); + if (def->average || def->floor) { + virBufferAsprintf(buf, " <%s", elem_name); + if (def->average) + virBufferAsprintf(buf, " average='%llu'", def->average); if (def->peak) virBufferAsprintf(buf, " peak='%llu'", def->peak); + if (def->floor) + virBufferAsprintf(buf, " floor='%llu'", def->floor); + if (def->burst) virBufferAsprintf(buf, " burst='%llu'", def->burst); virBufferAddLit(buf, "/>\n"); diff --git a/src/conf/netdev_bandwidth_conf.h b/src/conf/netdev_bandwidth_conf.h index 4bb7def..8ad6176 100644 --- a/src/conf/netdev_bandwidth_conf.h +++ b/src/conf/netdev_bandwidth_conf.h @@ -28,7 +28,8 @@ # include "buf.h" # include "xml.h" -virNetDevBandwidthPtr virNetDevBandwidthParse(xmlNodePtr node) +virNetDevBandwidthPtr virNetDevBandwidthParse(xmlNodePtr node, + int net_type) ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; int virNetDevBandwidthFormat(virNetDevBandwidthPtr def, virBufferPtr buf) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index c03ceaf..5d900eb 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -910,7 +910,7 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, bandwidth_node = virXPathNode("./bandwidth", ctxt); if (bandwidth_node && - !(def->bandwidth = virNetDevBandwidthParse(bandwidth_node))) { + !(def->bandwidth = virNetDevBandwidthParse(bandwidth_node, -1))) { goto error; } @@ -977,7 +977,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) def->domain = virXPathString("string(./domain[1]/@name)", ctxt); if ((bandwidthNode = virXPathNode("./bandwidth", ctxt)) != NULL && - (def->bandwidth = virNetDevBandwidthParse(bandwidthNode)) == NULL) + (def->bandwidth = virNetDevBandwidthParse(bandwidthNode, -1)) == NULL) goto error; /* Parse bridge information */ diff --git a/src/util/virnetdevbandwidth.c b/src/util/virnetdevbandwidth.c index b9bd2e3..d23d600 100644 --- a/src/util/virnetdevbandwidth.c +++ b/src/util/virnetdevbandwidth.c @@ -72,7 +72,7 @@ virNetDevBandwidthSet(const char *ifname, ignore_value(virNetDevBandwidthClear(ifname)); - if (bandwidth->in) { + if (bandwidth->in && bandwidth->in->average) { if (virAsprintf(&average, "%llukbps", bandwidth->in->average) < 0) goto cleanup; if (bandwidth->in->peak && diff --git a/src/util/virnetdevbandwidth.h b/src/util/virnetdevbandwidth.h index 58decff..8551caa 100644 --- a/src/util/virnetdevbandwidth.h +++ b/src/util/virnetdevbandwidth.h @@ -30,6 +30,7 @@ typedef virNetDevBandwidthRate *virNetDevBandwidthRatePtr; struct _virNetDevBandwidthRate { unsigned long long average; /* kbytes/s */ unsigned long long peak; /* kbytes/s */ + unsigned long long floor; /* kbytes/s */ unsigned long long burst; /* kbytes */ }; -- 1.7.3.4

These classes can borrow unused bandwidth. Basically, only egress qdsics can have classes, therefore we can do this kind of traffic shaping only on host's outgoing, that is domain's incoming traffic. --- src/lxc/lxc_driver.c | 3 ++- src/network/bridge_driver.c | 4 +++- src/qemu/qemu_command.c | 3 ++- src/qemu/qemu_driver.c | 2 +- src/util/virnetdevbandwidth.c | 26 ++++++++++++++++++++------ src/util/virnetdevbandwidth.h | 4 +++- src/util/virnetdevmacvlan.c | 2 +- 7 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 3baff19..1c91680 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -1221,7 +1221,8 @@ static int lxcSetupInterfaceBridged(virConnectPtr conn, goto cleanup; if (virNetDevBandwidthSet(net->ifname, - virDomainNetGetActualBandwidth(net)) < 0) { + virDomainNetGetActualBandwidth(net), + false) < 0) { lxcError(VIR_ERR_INTERNAL_ERROR, _("cannot set bandwidth limits on %s"), net->ifname); diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 5d0d528..057c955 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -1826,7 +1826,9 @@ networkStartNetworkVirtual(struct network_driver *driver, if (v6present && networkStartRadvd(network) < 0) goto err4; - if (virNetDevBandwidthSet(network->def->bridge, network->def->bandwidth) < 0) { + if (virNetDevBandwidthSet(network->def->bridge, + network->def->bandwidth, + true) < 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, _("cannot set bandwidth limits on %s"), network->def->bridge); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d051305..22cebe6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -265,7 +265,8 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, if (tapfd >= 0 && virNetDevBandwidthSet(net->ifname, - virDomainNetGetActualBandwidth(net)) < 0) { + virDomainNetGetActualBandwidth(net), + false) < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("cannot set bandwidth limits on %s"), net->ifname); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 712f1fc..faf4258 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8005,7 +8005,7 @@ qemuDomainSetInterfaceParameters(virDomainPtr dom, sizeof(*newBandwidth->out)); } - if (virNetDevBandwidthSet(net->ifname, newBandwidth) < 0) { + if (virNetDevBandwidthSet(net->ifname, newBandwidth, false) < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("cannot set bandwidth limits on %s"), device); diff --git a/src/util/virnetdevbandwidth.c b/src/util/virnetdevbandwidth.c index d23d600..ac3a93d 100644 --- a/src/util/virnetdevbandwidth.c +++ b/src/util/virnetdevbandwidth.c @@ -46,17 +46,19 @@ virNetDevBandwidthFree(virNetDevBandwidthPtr def) * virNetDevBandwidthSet: * @ifname: on which interface * @bandwidth: rates to set (may be NULL) + * @create_child_class: TRUE iff @ifname is bridge * * This function enables QoS on specified interface * and set given traffic limits for both, incoming - * and outgoing traffic. Any previous setting get + * and outgoing traffic. Any previous setting gets * overwritten. * * Return 0 on success, -1 otherwise. */ int virNetDevBandwidthSet(const char *ifname, - virNetDevBandwidthPtr bandwidth) + virNetDevBandwidthPtr bandwidth, + bool create_child_class) { int ret = -1; virCommandPtr cmd = NULL; @@ -84,16 +86,28 @@ virNetDevBandwidthSet(const char *ifname, cmd = virCommandNew(TC); virCommandAddArgList(cmd, "qdisc", "add", "dev", ifname, "root", - "handle", "1:", "htb", "default", "1", NULL); + "handle", "1:", "htb", "default", + create_child_class ? "2" : "1", NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; + if (create_child_class) { + virCommandFree(cmd); + cmd = virCommandNew(TC); + virCommandAddArgList(cmd, "class", "add", "dev", ifname, "parent", + "1:", "classid", "1:1", "htb", "rate", + average, "ceil", peak ? peak : average, NULL); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + } + virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd,"class", "add", "dev", ifname, "parent", - "1:", "classid", "1:1", "htb", NULL); - virCommandAddArgList(cmd, "rate", average, NULL); - + create_child_class ? "1:1" : "1:", "classid", + create_child_class ? "1:2" : "1:1", "htb", + "rate", average, NULL); if (peak) virCommandAddArgList(cmd, "ceil", peak, NULL); if (burst) diff --git a/src/util/virnetdevbandwidth.h b/src/util/virnetdevbandwidth.h index 8551caa..1e7d46d 100644 --- a/src/util/virnetdevbandwidth.h +++ b/src/util/virnetdevbandwidth.h @@ -42,7 +42,9 @@ struct _virNetDevBandwidth { void virNetDevBandwidthFree(virNetDevBandwidthPtr def); -int virNetDevBandwidthSet(const char *ifname, virNetDevBandwidthPtr bandwidth) +int virNetDevBandwidthSet(const char *ifname, + virNetDevBandwidthPtr bandwidth, + bool create_child_class) ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; int virNetDevBandwidthClear(const char *ifname) ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c index 5e55b72..f496290 100644 --- a/src/util/virnetdevmacvlan.c +++ b/src/util/virnetdevmacvlan.c @@ -578,7 +578,7 @@ create_name: rc = 0; } - if (virNetDevBandwidthSet(cr_ifname, bandwidth) < 0) { + if (virNetDevBandwidthSet(cr_ifname, bandwidth, false) < 0) { virNetDevError(VIR_ERR_INTERNAL_ERROR, _("cannot set bandwidth limits on %s"), cr_ifname); -- 1.7.3.4

These set bridge part of QoS when bringing domain's interface up. --- po/POTFILES.in | 1 + src/util/virnetdevbandwidth.c | 177 +++++++++++++++++++++++++++++++++++++++++ src/util/virnetdevbandwidth.h | 14 +++ 3 files changed, 192 insertions(+), 0 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 3e8359a..edac362 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -126,6 +126,7 @@ src/util/util.c src/util/viraudit.c src/util/virfile.c src/util/virnetdev.c +src/util/virnetdevbandwidth.c src/util/virnetdevbridge.c src/util/virnetdevmacvlan.c src/util/virnetdevtap.c diff --git a/src/util/virnetdevbandwidth.c b/src/util/virnetdevbandwidth.c index ac3a93d..2d7e156 100644 --- a/src/util/virnetdevbandwidth.c +++ b/src/util/virnetdevbandwidth.c @@ -27,6 +27,7 @@ #include "memory.h" #include "virterror_internal.h" #include "ignore-value.h" +#include "logging.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -167,6 +168,182 @@ cleanup: return ret; } +/* + * virNetDevBandwidthPlug: + * @brname: name of the bridge + * @net_bandwidth: QoS settings on @brname + * @ifname: interface being plugged into @brname + * @ifmac: MAC of @ifname + * @bandwidth: QoS settings for @ifname + * @id: unique ID (MUST be greater than 2) + * + * Set bridge part of interface QoS settings, + * e.g. guaranteed bandwidth. + * @id is an unique ID (among @brname) from which + * other identifiers for class, qdisc and filter + * are derived. However, two classes were already + * set up (by virNetDevBandwidthSet). That's why + * this @id MUST be greater than 2. + * You may want to keep passed @id, as it is used + * later by virNetDevBandwidthUnplug. + * + * Returns: + * 1 if there is nothing to set + * 0 if QoS set successfully + * -1 otherwise. + */ +int +virNetDevBandwidthPlug(const char *brname, + virNetDevBandwidthPtr net_bandwidth, + const char *ifname, + unsigned const char *ifmac, + virNetDevBandwidthPtr bandwidth, + unsigned int id) +{ + int ret = -1; + virCommandPtr cmd = NULL; + char *class_id = NULL; + char *qdisc_id = NULL; + char *filter_id = NULL; + char *floor = NULL; + char *ceil = NULL; + char *mac[2]; + + if (!bandwidth || !bandwidth->in || !bandwidth->in->floor) { + /* nothing to set */ + return 1; + } + + if (!net_bandwidth || !net_bandwidth->in) { + VIR_ERROR(_("Bridge '%s' has no QoS set, " + "therefore unable to set 'floor' on '%s'"), + brname, ifname); + return -1; + } + + if (virAsprintf(&class_id, "1:%x", id) < 0 || + virAsprintf(&qdisc_id, "%x:", id) < 0 || + virAsprintf(&filter_id, "%u", id) < 0 || + virAsprintf(&mac[0], "0x%02x%02x%02x%02x", ifmac[2], + ifmac[3], ifmac[4], ifmac[5]) < 0 || + virAsprintf(&mac[1], "0x%02x%02x", ifmac[0], ifmac[1]) < 0 || + virAsprintf(&floor, "%llukbps", bandwidth->in->floor) < 0 || + virAsprintf(&ceil, "%llukbps", net_bandwidth->in->peak ? : + net_bandwidth->in->average) < 0) { + virReportOOMError(); + goto cleanup; + } + + cmd = virCommandNew(TC); + virCommandAddArgList(cmd, "class", "add", "dev", brname, "parent", "1:1", + "classid", class_id, "htb", "rate", floor, + "ceil", ceil, NULL); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + virCommandFree(cmd); + cmd = virCommandNew(TC); + virCommandAddArgList(cmd, "qdisc", "add", "dev", brname, "parent", + class_id, "handle", qdisc_id, "sfq", "perturb", + "10", NULL); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + virCommandFree(cmd); + cmd = virCommandNew(TC); + /* Okay, this not nice. But since libvirt does not know anything + * about interface IP address(es), and tc fw filter simply + * refuse to use ebtables marks, we need to use u32 selector + * to match MAC address. + * If libvirt will ever know something, remove this FIXME + */ + virCommandAddArgList(cmd, "filter", "add", "dev", brname, "protocol", "ip", + "prio", filter_id, "u32", + "match", "u16", "0x0800", "0xffff", "at", "-2", + "match", "u32", mac[0], "0xffffffff", "at", "-12", + "match", "u16", mac[1], "0xffff", "at", "-14", + "flowid", class_id, NULL); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + ret = 0; + +cleanup: + VIR_FREE(ceil); + VIR_FREE(floor); + VIR_FREE(mac[1]); + VIR_FREE(mac[0]); + VIR_FREE(filter_id); + VIR_FREE(qdisc_id); + VIR_FREE(class_id); + virCommandFree(cmd); + return ret; +} + +/* + * virNetDevBandwidthUnplug: + * @brname: from which bridge are we unplugging + * @id: unique identifier (MUST be greater than 2) + * + * Remove QoS settings from bridge. + * + * Returns 0 on success, -1 otherwise. + */ +int +virNetDevBandwidthUnplug(const char *brname, + unsigned int id) +{ + int ret = -1; + int cmd_ret = 0; + virCommandPtr cmd = NULL; + char *class_id = NULL; + char *qdisc_id = NULL; + char *filter_id = NULL; + + if (virAsprintf(&class_id, "1:%x", id) < 0 || + virAsprintf(&qdisc_id, "%x:", id) < 0 || + virAsprintf(&filter_id, "%u", id) < 0) { + virReportOOMError(); + goto cleanup; + } + + cmd = virCommandNew(TC); + virCommandAddArgList(cmd, "qdisc", "del", "dev", brname, + "handle", qdisc_id, NULL); + + /* Don't threat tc errors as fatal, but + * try to remove as much as possible */ + if (virCommandRun(cmd, &cmd_ret) < 0) + goto cleanup; + + virCommandFree(cmd); + cmd = virCommandNew(TC); + virCommandAddArgList(cmd, "filter", "del", "dev", brname, + "prio", filter_id, NULL); + + if (virCommandRun(cmd, &cmd_ret) < 0) + goto cleanup; + + cmd = virCommandNew(TC); + virCommandAddArgList(cmd, "class", "del", "dev", brname, + "classid", class_id, NULL); + + if (virCommandRun(cmd, &cmd_ret) < 0) + goto cleanup; + + ret = 0; + +cleanup: + VIR_FREE(filter_id); + VIR_FREE(qdisc_id); + VIR_FREE(class_id); + virCommandFree(cmd); + return ret; +} + /** * virNetDevBandwidthClear: * @ifname: on which interface diff --git a/src/util/virnetdevbandwidth.h b/src/util/virnetdevbandwidth.h index 1e7d46d..c1bec45 100644 --- a/src/util/virnetdevbandwidth.h +++ b/src/util/virnetdevbandwidth.h @@ -53,4 +53,18 @@ int virNetDevBandwidthCopy(virNetDevBandwidthPtr *dest, const virNetDevBandwidth bool virNetDevBandwidthEqual(virNetDevBandwidthPtr a, virNetDevBandwidthPtr b); + +int virNetDevBandwidthPlug(const char *brname, + virNetDevBandwidthPtr net_bandwidth, + const char *ifname, + unsigned const char *ifmac, + virNetDevBandwidthPtr bandwidth, + unsigned int id) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) + ATTRIBUTE_RETURN_CHECK; + +int virNetDevBandwidthUnplug(const char *brname, + unsigned int id) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + #endif /* __VIR_NETDEV_BANDWIDTH_H__ */ -- 1.7.3.4

Network should be notified if we plug in or unplug an interface, so it can perform some action, e.g. set/unset network part of QoS. --- src/conf/domain_conf.h | 1 + src/conf/network_conf.c | 1 + src/conf/network_conf.h | 2 + src/libvirt_network.syms | 2 + src/network/bridge_driver.c | 72 +++++++++++++++++++++++++++++++++++++++++++ src/network/bridge_driver.h | 7 ++++ src/qemu/qemu_command.c | 17 +++++++--- src/qemu/qemu_process.c | 12 +++++++ 8 files changed, 109 insertions(+), 5 deletions(-) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3d5d4f8..09f7cea 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -620,6 +620,7 @@ struct _virDomainNetDef { virNWFilterHashTablePtr filterparams; virNetDevBandwidthPtr bandwidth; int linkstate; + unsigned int class_id; /* ID for bandwidth internal classes */ }; /* Used for prefix of ifname of any network name generated dynamically diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 5d900eb..019cdc7 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -244,6 +244,7 @@ virNetworkObjPtr virNetworkAssignDef(virNetworkObjListPtr nets, } virNetworkObjLock(network); network->def = def; + network->class_id = 3; if (VIR_REALLOC_N(nets->objs, nets->count + 1) < 0) { virReportOOMError(); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 5cb396c..dcbf84d 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -189,6 +189,8 @@ struct _virNetworkObj { unsigned int active : 1; unsigned int autostart : 1; unsigned int persistent : 1; + unsigned int class_id; /* IDs for QoS, not to be confused with + IDs domains have */ virNetworkDefPtr def; /* The current definition */ virNetworkDefPtr newDef; /* New definition to activate at shutdown */ diff --git a/src/libvirt_network.syms b/src/libvirt_network.syms index 1fe8902..4c1cb8b 100644 --- a/src/libvirt_network.syms +++ b/src/libvirt_network.syms @@ -7,4 +7,6 @@ networkAllocateActualDevice; networkBuildDhcpDaemonCommandLine; networkGetNetworkAddress; networkNotifyActualDevice; +networkNotifyPlug; +networkNotifyUnplug; networkReleaseActualDevice; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 057c955..4cea7c2 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -3263,3 +3263,75 @@ cleanup: virNetworkObjUnlock(network); return ret; } + +int +networkNotifyPlug(virNetworkPtr net, + virDomainNetDefPtr iface) +{ + struct network_driver *driver = net->conn->networkPrivateData; + virNetworkObjPtr network; + int ret = -1; + int plug_ret; + + networkDriverLock(driver); + network = virNetworkFindByUUID(&driver->networks, net->uuid); + networkDriverUnlock(driver); + + if (!network) { + networkReportError(VIR_ERR_NO_NETWORK, + "%s", _("no network with matching uuid")); + goto cleanup; + } + + plug_ret = virNetDevBandwidthPlug(network->def->bridge, + network->def->bandwidth, + iface->ifname, + iface->mac, + iface->bandwidth, + network->class_id); + if (plug_ret < 0) { + ignore_value(virNetDevBandwidthUnplug(network->def->bridge, + network->class_id)); + goto cleanup; + } else if (plug_ret == 0) { + /* QoS was set, generate next ID */ + iface->class_id = network->class_id; + network->class_id++; + } + + ret = 0; + +cleanup: + if (network) + virNetworkObjUnlock(network); + return ret; +} + +int networkNotifyUnplug(virDomainNetDefPtr iface) +{ + struct network_driver *driver = driverState; + virNetworkObjPtr network; + int ret = -1; + + if (!iface->class_id) { + /* nothing to unplug */ + return 0; + } + + networkDriverLock(driver); + network = virNetworkFindByName(&driver->networks, iface->data.network.name); + networkDriverUnlock(driver); + + if (!network) { + networkReportError(VIR_ERR_NO_NETWORK, + "%s", _("no network with matching uuid")); + goto cleanup; + } + + ret = virNetDevBandwidthUnplug(network->def->bridge, iface->class_id); + +cleanup: + if (network) + virNetworkObjUnlock(network); + return ret; +} diff --git a/src/network/bridge_driver.h b/src/network/bridge_driver.h index 4913126..f1e5abe 100644 --- a/src/network/bridge_driver.h +++ b/src/network/bridge_driver.h @@ -64,4 +64,11 @@ typedef char *(*networkDnsmasqLeaseFileNameFunc)(const char *netname); /* this allows the testsuite to replace the lease filename resolver function */ extern networkDnsmasqLeaseFileNameFunc networkDnsmasqLeaseFileName; +int +networkNotifyPlug(virNetworkPtr net, + virDomainNetDefPtr iface) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +int networkNotifyUnplug(virDomainNetDefPtr iface) + ATTRIBUTE_NONNULL(1); #endif /* __VIR_NETWORK__DRIVER_H */ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 22cebe6..542a246 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -182,12 +182,12 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, bool template_ifname = false; unsigned char tapmac[VIR_MAC_BUFLEN]; int actualType = virDomainNetGetActualType(net); + virNetworkPtr network = NULL; if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { int active, fail = 0; virErrorPtr errobj; - virNetworkPtr network = virNetworkLookupByName(conn, - net->data.network.name); + network = virNetworkLookupByName(conn, net->data.network.name); if (!network) return -1; @@ -209,12 +209,13 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, /* Make sure any above failure is preserved */ errobj = virSaveLastError(); - virNetworkFree(network); virSetError(errobj); virFreeError(errobj); - if (fail) + if (fail) { + virNetworkFree(network); return -1; + } } else if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) { if (!(brname = strdup(virDomainNetGetActualBridgeName(net)))) { @@ -274,6 +275,12 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, goto cleanup; } + if (tapfd >= 0 && network && + networkNotifyPlug(network, net) < 0) { + VIR_FORCE_CLOSE(tapfd); + goto cleanup; + } + if (tapfd >= 0) { if ((net->filter) && (net->ifname)) { if (virDomainConfNWFilterInstantiate(conn, def->uuid, net) < 0) @@ -283,7 +290,7 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, cleanup: VIR_FREE(brname); - + virNetworkFree(network); return tapfd; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index e16ca07..a04b4de 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3460,6 +3460,18 @@ void qemuProcessStop(struct qemud_driver *driver, } } + for ( i = 0; i < vm->def->nnets; i++) { + virDomainNetDefPtr net = vm->def->nets[i]; + + if (virDomainNetGetActualType(net) != VIR_DOMAIN_NET_TYPE_NETWORK) + continue; + + if (networkNotifyUnplug(net) < 0) { + VIR_WARN("Unable to remove QoS settings for interface '%s'", + net->ifname); + } + } + if (priv->mon) qemuMonitorClose(priv->mon); -- 1.7.3.4

Like we keep domain status file to keep some internal data during daemon restart, now it is needed for network as well. Because network has now class_id attribute which is hidden from users and thus not part of network XML. --- daemon/libvirtd.c | 3 + src/conf/network_conf.c | 219 ++++++++++++++++++++++++++++++++++++------- src/conf/network_conf.h | 7 ++ src/libvirt_private.syms | 2 + src/network/bridge_driver.c | 33 +++++-- 5 files changed, 222 insertions(+), 42 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index b1b542b..aa0dccb 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -1607,6 +1607,9 @@ int main(int argc, char **argv) { 0, "shutdown", NULL); cleanup: + /* Notify drivers we are about to quit, + * so they can save their stuff */ + virStateCleanup(); virNetServerProgramFree(remoteProgram); virNetServerProgramFree(qemuProgram); virNetServerClose(srv); diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 019cdc7..9073b08 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1282,6 +1282,99 @@ cleanup: } static int +virNetworkObjParseXML(virNetworkObjPtr obj, + xmlDocPtr xml ATTRIBUTE_UNUSED, + xmlXPathContextPtr ctxt) +{ + int ret = -1; + xmlNodePtr config; + char *class_id = NULL; + + if ((config = virXPathNode("./network", ctxt))) { + xmlNodePtr oldnode; + virNetworkDefPtr tmp_def; + + oldnode = ctxt->node; + ctxt->node = config; + tmp_def = virNetworkDefParseXML(ctxt); + ctxt->node = oldnode; + + if (tmp_def) { + obj->newDef = obj->def; + obj->def = tmp_def; + } else { + goto cleanup; + } + } + + if ((class_id=virXPathString("string(./class_id[1]/@next)", ctxt))) { + if (virStrToLong_ui(class_id, NULL, 10, &obj->class_id) < 0) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("Malformed class_id attribute: %s"), + class_id); + goto cleanup; + } + } + + ret = 0; + +cleanup: + VIR_FREE(class_id); + return ret; +} + +static int +virNetworkObjParseNode(virNetworkObjPtr obj, + xmlDocPtr xml, + xmlNodePtr root) +{ + int ret = -1; + xmlXPathContextPtr ctxt = NULL; + + if (!xmlStrEqual(root->name, BAD_CAST "networkstatus")) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("unexpected root element <%s>, " + "expecting <networkstatus>"), + root->name); + return -1; + } + + if (!(ctxt = xmlXPathNewContext(xml))) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + ret = virNetworkObjParseXML(obj, xml, ctxt); + +cleanup: + xmlXPathFreeContext(ctxt); + return ret; +} + +static int +virNetworkObjParseFile(virNetworkObjPtr obj, + const char *filename) +{ + int ret = -1; + xmlDocPtr xml; + + if ((xml = virXMLParseFile(filename))) { + ret = virNetworkObjParseNode(obj, xml, xmlDocGetRootElement(xml)); + xmlFreeDoc(xml); + } + + return ret; +} + +int +virNetworkObjUpdateStatus(virNetworkObjPtr net, + const char *filename) +{ + return virNetworkObjParseFile(net, filename); +} + +static int virNetworkDNSDefFormat(virBufferPtr buf, virNetworkDNSDefPtr def) { @@ -1449,19 +1542,21 @@ virPortGroupDefFormat(virBufferPtr buf, return 0; } -char *virNetworkDefFormat(const virNetworkDefPtr def, unsigned int flags) +static int +virNetworkDefFormatInternal(virBufferPtr buf, + const virNetworkDefPtr def, + unsigned int flags) { - virBuffer buf = VIR_BUFFER_INITIALIZER; unsigned char *uuid; char uuidstr[VIR_UUID_STRING_BUFLEN]; int ii; - virBufferAddLit(&buf, "<network>\n"); - virBufferEscapeString(&buf, " <name>%s</name>\n", def->name); + virBufferAddLit(buf, "<network>\n"); + virBufferEscapeString(buf, " <name>%s</name>\n", def->name); uuid = def->uuid; virUUIDFormat(uuid, uuidstr); - virBufferAsprintf(&buf, " <uuid>%s</uuid>\n", uuidstr); + virBufferAsprintf(buf, " <uuid>%s</uuid>\n", uuidstr); if (def->forwardType != VIR_NETWORK_FORWARD_NONE) { const char *dev = NULL; @@ -1475,86 +1570,97 @@ char *virNetworkDefFormat(const virNetworkDefPtr def, unsigned int flags) def->forwardType, def->name); goto error; } - virBufferAddLit(&buf, " <forward"); - virBufferEscapeString(&buf, " dev='%s'", dev); - virBufferAsprintf(&buf, " mode='%s'%s>\n", mode, + virBufferAddLit(buf, " <forward"); + virBufferEscapeString(buf, " dev='%s'", dev); + virBufferAsprintf(buf, " mode='%s'%s>\n", mode, (def->nForwardIfs || def->nForwardPfs) ? "" : "/"); /* For now, hard-coded to at most 1 forwardPfs */ if (def->nForwardPfs) - virBufferEscapeString(&buf, " <pf dev='%s'/>\n", + virBufferEscapeString(buf, " <pf dev='%s'/>\n", def->forwardPfs[0].dev); if (def->nForwardIfs && (!def->nForwardPfs || !(flags & VIR_NETWORK_XML_INACTIVE))) { for (ii = 0; ii < def->nForwardIfs; ii++) { - virBufferEscapeString(&buf, " <interface dev='%s'/>\n", + virBufferEscapeString(buf, " <interface dev='%s'/>\n", def->forwardIfs[ii].dev); } } if (def->nForwardPfs || def->nForwardIfs) - virBufferAddLit(&buf, " </forward>\n"); + virBufferAddLit(buf, " </forward>\n"); } if (def->forwardType == VIR_NETWORK_FORWARD_NONE || def->forwardType == VIR_NETWORK_FORWARD_NAT || def->forwardType == VIR_NETWORK_FORWARD_ROUTE) { - virBufferAddLit(&buf, " <bridge"); + virBufferAddLit(buf, " <bridge"); if (def->bridge) - virBufferEscapeString(&buf, " name='%s'", def->bridge); - virBufferAsprintf(&buf, " stp='%s' delay='%ld' />\n", + virBufferEscapeString(buf, " name='%s'", def->bridge); + virBufferAsprintf(buf, " stp='%s' delay='%ld' />\n", def->stp ? "on" : "off", def->delay); } else if (def->forwardType == VIR_NETWORK_FORWARD_BRIDGE && def->bridge) { - virBufferEscapeString(&buf, " <bridge name='%s' />\n", def->bridge); + virBufferEscapeString(buf, " <bridge name='%s' />\n", def->bridge); } if (def->mac_specified) { char macaddr[VIR_MAC_STRING_BUFLEN]; virFormatMacAddr(def->mac, macaddr); - virBufferAsprintf(&buf, " <mac address='%s'/>\n", macaddr); + virBufferAsprintf(buf, " <mac address='%s'/>\n", macaddr); } if (def->domain) - virBufferAsprintf(&buf, " <domain name='%s'/>\n", def->domain); + virBufferAsprintf(buf, " <domain name='%s'/>\n", def->domain); - if (virNetworkDNSDefFormat(&buf, def->dns) < 0) + if (virNetworkDNSDefFormat(buf, def->dns) < 0) goto error; - virBufferAdjustIndent(&buf, 2); - if (virNetDevBandwidthFormat(def->bandwidth, &buf) < 0) + virBufferAdjustIndent(buf, 2); + if (virNetDevBandwidthFormat(def->bandwidth, buf) < 0) goto error; - virBufferAdjustIndent(&buf, -2); + virBufferAdjustIndent(buf, -2); for (ii = 0; ii < def->nips; ii++) { - if (virNetworkIpDefFormat(&buf, &def->ips[ii]) < 0) + if (virNetworkIpDefFormat(buf, &def->ips[ii]) < 0) goto error; } - virBufferAdjustIndent(&buf, 2); - if (virNetDevVPortProfileFormat(def->virtPortProfile, &buf) < 0) + virBufferAdjustIndent(buf, 2); + if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0) goto error; - virBufferAdjustIndent(&buf, -2); + virBufferAdjustIndent(buf, -2); for (ii = 0; ii < def->nPortGroups; ii++) - if (virPortGroupDefFormat(&buf, &def->portGroups[ii]) < 0) + if (virPortGroupDefFormat(buf, &def->portGroups[ii]) < 0) goto error; - virBufferAddLit(&buf, "</network>\n"); + virBufferAddLit(buf, "</network>\n"); - if (virBufferError(&buf)) + if (virBufferError(buf)) goto no_memory; - return virBufferContentAndReset(&buf); + return 0; - no_memory: +no_memory: virReportOOMError(); - error: - virBufferFreeAndReset(&buf); - return NULL; +error: + return -1; +} + +char *virNetworkDefFormat(const virNetworkDefPtr def, unsigned int flags) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (virNetworkDefFormatInternal(&buf, def, flags) < 0) { + virBufferFreeAndReset(&buf); + return NULL; + } + + return virBufferContentAndReset(&buf); } virPortGroupDefPtr virPortGroupFindByName(virNetworkDefPtr net, @@ -1573,6 +1679,34 @@ virPortGroupDefPtr virPortGroupFindByName(virNetworkDefPtr net, return NULL; } +static char * +virNetworkObjFormat(virNetworkObjPtr obj) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAddLit(&buf, "<networkstatus>\n"); + if (obj->class_id) + virBufferAsprintf(&buf, " <class_id next='%u'/>\n", obj->class_id); + + virBufferAdjustIndent(&buf, 2); + if (virNetworkDefFormatInternal(&buf, obj->def, 0) < 0) + goto error; + virBufferAdjustIndent(&buf, -2); + + virBufferAddLit(&buf, "</networkstatus>\n"); + + if (virBufferError(&buf)) + goto no_memory; + + return virBufferContentAndReset(&buf); + +no_memory: + virReportOOMError(); +error: + virBufferFreeAndReset(&buf); + return NULL; +} + int virNetworkSaveXML(const char *configDir, virNetworkDefPtr def, const char *xml) @@ -1597,6 +1731,24 @@ int virNetworkSaveXML(const char *configDir, return ret; } +int virNetworkSaveObj(const char *configDir, + virNetworkObjPtr net) +{ + int ret = -1; + char *xml; + + if (!(xml = virNetworkObjFormat(net))) + goto cleanup; + + if (virNetworkSaveXML(configDir, net->def, xml)) + goto cleanup; + + ret = 0; +cleanup: + VIR_FREE(xml); + return ret; +} + int virNetworkSaveConfig(const char *configDir, virNetworkDefPtr def) { @@ -1615,7 +1767,6 @@ cleanup: return ret; } - virNetworkObjPtr virNetworkLoadConfig(virNetworkObjListPtr nets, const char *configDir, const char *autostartDir, diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index dcbf84d..1161acf 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -255,11 +255,18 @@ int virNetworkSaveXML(const char *configDir, int virNetworkSaveConfig(const char *configDir, virNetworkDefPtr def); +int virNetworkSaveObj(const char *configDir, + virNetworkObjPtr net); + virNetworkObjPtr virNetworkLoadConfig(virNetworkObjListPtr nets, const char *configDir, const char *autostartDir, const char *file); +int virNetworkObjUpdateStatus(virNetworkObjPtr net, + const char *filename) + ATTRIBUTE_NONNULL(1); + int virNetworkLoadAllConfigs(virNetworkObjListPtr nets, const char *configDir, const char *autostartDir); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ca4beb1..bd74050 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -759,8 +759,10 @@ virNetworkObjIsDuplicate; virNetworkObjListFree; virNetworkObjLock; virNetworkObjUnlock; +virNetworkObjUpdateStatus; virNetworkRemoveInactive; virNetworkSaveConfig; +virNetworkSaveObj; virNetworkSetBridgeMacAddr; virNetworkSetBridgeName; virPortGroupFindByName; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 4cea7c2..2f3da60 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -184,7 +184,6 @@ networkFindActiveConfigs(struct network_driver *driver) { for (i = 0 ; i < driver->networks.count ; i++) { virNetworkObjPtr obj = driver->networks.objs[i]; - virNetworkDefPtr tmp; char *config; virNetworkObjLock(obj); @@ -201,13 +200,12 @@ networkFindActiveConfigs(struct network_driver *driver) { continue; } - /* Try and load the live config */ - tmp = virNetworkDefParseFile(config); - VIR_FREE(config); - if (tmp) { - obj->newDef = obj->def; - obj->def = tmp; + /* Try to update the live config */ + if (virNetworkObjUpdateStatus(obj, config) < 0) { + VIR_WARN("Unable to update config on '%s' network", + obj->def->name); } + VIR_FREE(config); /* If bridge exists, then mark it active */ if (obj->def->bridge && @@ -396,11 +394,25 @@ networkActive(void) { */ static int networkShutdown(void) { + unsigned int i; + if (!driverState) return -1; networkDriverLock(driverState); + for (i = 0; i < driverState->networks.count; i++) { + virNetworkObjPtr obj = driverState->networks.objs[i]; + + if (!virNetworkObjIsActive(obj)) + continue; + + if (virNetworkSaveObj(NETWORK_STATE_DIR, obj) < 0) { + VIR_WARN("Unable to save network '%s'", + obj->def->name); + } + } + /* free inactive networks */ virNetworkObjListFree(&driverState->networks); @@ -1990,7 +2002,7 @@ networkStartNetwork(struct network_driver *driver, /* Persist the live configuration now that anything autogenerated * is setup. */ - if ((ret = virNetworkSaveConfig(NETWORK_STATE_DIR, network->def)) < 0) { + if ((ret = virNetworkSaveObj(NETWORK_STATE_DIR, network)) < 0) { goto error; } @@ -3299,6 +3311,11 @@ networkNotifyPlug(virNetworkPtr net, network->class_id++; } + if (virNetworkSaveObj(NETWORK_STATE_DIR, network) < 0) { + VIR_WARN("Unable to save network '%s'", + network->def->name); + } + ret = 0; cleanup: -- 1.7.3.4

Interfaces keeps a class_id, which is an ID from which bridge part of QoS settings is derived. We need to store class_id in domain status file, so we can later pass it to virNetDevBandwidthUnplug. --- src/conf/domain_conf.c | 4 +- src/qemu/qemu_domain.c | 66 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 250290e..f80cad7 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -8177,7 +8177,7 @@ static virDomainObjPtr virDomainObjParseXML(virCapsPtr caps, VIR_FREE(nodes); if (caps->privateDataXMLParse && - ((caps->privateDataXMLParse)(ctxt, obj->privateData)) < 0) + ((caps->privateDataXMLParse)(ctxt, obj)) < 0) goto error; return obj; @@ -11859,7 +11859,7 @@ static char *virDomainObjFormat(virCapsPtr caps, } if (caps->privateDataXMLFormat && - ((caps->privateDataXMLFormat)(&buf, obj->privateData)) < 0) + ((caps->privateDataXMLFormat)(&buf, obj)) < 0) goto error; virBufferAdjustIndent(&buf, 2); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 6b03c62..2f41a49 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -225,8 +225,11 @@ static void qemuDomainObjPrivateFree(void *data) static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) { - qemuDomainObjPrivatePtr priv = data; + virDomainObjPtr vm = data; + qemuDomainObjPrivatePtr priv = vm->privateData; const char *monitorpath; + bool do_class_id = false; + int i; /* priv->monitor_chr is set only for qemu */ if (priv->monConfig) { @@ -249,7 +252,6 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) if (priv->nvcpupids) { - int i; virBufferAddLit(buf, " <vcpus>\n"); for (i = 0 ; i < priv->nvcpupids ; i++) { virBufferAsprintf(buf, " <vcpu pid='%d'/>\n", priv->vcpupids[i]); @@ -258,7 +260,6 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) } if (priv->qemuCaps) { - int i; virBufferAddLit(buf, " <qemuCaps>\n"); for (i = 0 ; i < QEMU_CAPS_LAST ; i++) { if (qemuCapsGet(priv->qemuCaps, i)) { @@ -287,15 +288,36 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) if (priv->fakeReboot) virBufferAsprintf(buf, " <fakereboot/>\n"); + for (i = 0; i < vm->def->nnets; i++) { + if (vm->def->nets[i]->class_id) { + do_class_id = true; + break; + } + } + + if (do_class_id) { + virBufferAddLit(buf, " <class_id>\n"); + for ( ; i < vm->def->nnets; i++) { + virDomainNetDefPtr iface = vm->def->nets[i]; + if (iface->class_id) { + virBufferAsprintf(buf, " <interface alias='%s' " + "class_id='%u'/>\n", + iface->info.alias, iface->class_id); + } + } + virBufferAddLit(buf, " </class_id>\n"); + } + return 0; } static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) { - qemuDomainObjPrivatePtr priv = data; + virDomainObjPtr vm = data; + qemuDomainObjPrivatePtr priv = vm->privateData; char *monitorpath; char *tmp; - int n, i; + int n, i, ii; xmlNodePtr *nodes = NULL; virBitmapPtr qemuCaps = NULL; @@ -432,6 +454,40 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) priv->fakeReboot = virXPathBoolean("boolean(./fakereboot)", ctxt) == 1; + if ((n = virXPathNodeSet("./class_id/interface", ctxt, &nodes)) < 0) + goto error; + + for (i = 0; i < n; i++) { + char *alias = virXMLPropString(nodes[i], "alias"); + char *class_id = virXMLPropString(nodes[i], "class_id"); + virDomainNetDefPtr iface = NULL; + if (alias && class_id) { + for (ii = 0; ii < vm->def->nnets; ii++) { + if (STREQ(vm->def->nets[ii]->info.alias, alias)) { + iface = vm->def->nets[ii]; + break; + } + } + + if (!iface) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("No such interface '%s'"), + alias); + VIR_FREE(alias); + VIR_FREE(class_id); + goto error; + } + + if (virStrToLong_ui(class_id, NULL, 10, &iface->class_id) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Malformed class_id attribute: %s"), + class_id); + } + } + VIR_FREE(alias); + VIR_FREE(class_id); + } + return 0; error: -- 1.7.3.4
participants (1)
-
Michal Privoznik