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