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 f9dbcda..2e9a8e2 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2311,7 +2311,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> @@ -2325,15 +2325,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 3a168c3..cc1732c 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 75e51a0..ebd0473 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3370,7 +3370,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; @@ -3528,7 +3529,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 1058b07..949cea8 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -796,7 +796,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; } @@ -862,7 +862,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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list