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_process.c | 3 ++-
src/network/bridge_driver.c | 3 ++-
src/qemu/qemu_command.c | 3 ++-
src/qemu/qemu_driver.c | 2 +-
src/util/virnetdevbandwidth.c | 33 +++++++++++++++++++++++++++------
src/util/virnetdevbandwidth.h | 4 +++-
src/util/virnetdevmacvlan.c | 2 +-
7 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 079bc3a..91ce2d3 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -335,7 +335,8 @@ static int virLXCProcessSetupInterfaceBridged(virConnectPtr conn,
goto cleanup;
if (virNetDevBandwidthSet(net->ifname,
- virDomainNetGetActualBandwidth(net)) < 0) {
+ virDomainNetGetActualBandwidth(net),
+ false) < 0) {
virReportError(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 c153d36..256b601 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -2240,7 +2240,8 @@ networkStartNetworkVirtual(struct network_driver *driver,
VIR_FORCE_CLOSE(tapfd);
}
- if (virNetDevBandwidthSet(network->def->bridge, network->def->bandwidth)
< 0) {
+ if (virNetDevBandwidthSet(network->def->bridge,
+ network->def->bandwidth, true) < 0) {
virReportError(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 22bb209..c1f8680 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -292,7 +292,8 @@ qemuNetworkIfaceConnect(virDomainDefPtr def,
if (tapfd >= 0 &&
virNetDevBandwidthSet(net->ifname,
- virDomainNetGetActualBandwidth(net)) < 0) {
+ virDomainNetGetActualBandwidth(net),
+ false) < 0) {
virReportError(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 595c452..9ae0c1b 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -8960,7 +8960,7 @@ qemuDomainSetInterfaceParameters(virDomainPtr dom,
sizeof(*newBandwidth->out));
}
- if (virNetDevBandwidthSet(net->ifname, newBandwidth) < 0) {
+ if (virNetDevBandwidthSet(net->ifname, newBandwidth, false) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot set bandwidth limits on %s"),
device);
diff --git a/src/util/virnetdevbandwidth.c b/src/util/virnetdevbandwidth.c
index bddb788..fcc6b56 100644
--- a/src/util/virnetdevbandwidth.c
+++ b/src/util/virnetdevbandwidth.c
@@ -45,17 +45,21 @@ virNetDevBandwidthFree(virNetDevBandwidthPtr def)
* virNetDevBandwidthSet:
* @ifname: on which interface
* @bandwidth: rates to set (may be NULL)
+ * @hierarchical_class: whether to create hierarchical class
*
* This function enables QoS on specified interface
* and set given traffic limits for both, incoming
* and outgoing traffic. Any previous setting get
- * overwritten.
+ * overwritten. If @hierarchical_class is TRUE, create
+ * hierarchical class. It is used to guarantee minimal
+ * throughput ('floor' attribute in NIC).
*
* Return 0 on success, -1 otherwise.
*/
int
virNetDevBandwidthSet(const char *ifname,
- virNetDevBandwidthPtr bandwidth)
+ virNetDevBandwidthPtr bandwidth,
+ bool hierarchical_class)
{
int ret = -1;
virCommandPtr cmd = NULL;
@@ -71,7 +75,7 @@ virNetDevBandwidthSet(const char *ifname,
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 &&
@@ -83,15 +87,32 @@ virNetDevBandwidthSet(const char *ifname,
cmd = virCommandNew(TC);
virCommandAddArgList(cmd, "qdisc", "add", "dev",
ifname, "root",
- "handle", "1:", "htb",
"default", "1", NULL);
+ "handle", "1:", "htb",
"default",
+ hierarchical_class ? "2" : "1", NULL);
if (virCommandRun(cmd, NULL) < 0)
goto cleanup;
+ if (hierarchical_class) {
+ /* If we are creating hierarchical class, all non guaranteed traffic
+ * goes to 1:2 class which will adjust 'rate' dynamically as NICs
with
+ * guaranteed throughput are plugged and unplugged. Class 1:1 is there
+ * so we don't exceed the maximum limit for network. For each NIC with
+ * guaranteed throughput a separate classid will be created.
+ * NB '1:' is just a shorter notation of '1:0' */
+ 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);
+ hierarchical_class ? "1:1" : "1:",
"classid",
+ hierarchical_class ? "1:2" : "1:1",
"htb",
+ "rate", average, NULL);
if (peak)
virCommandAddArgList(cmd, "ceil", peak, NULL);
diff --git a/src/util/virnetdevbandwidth.h b/src/util/virnetdevbandwidth.h
index 35f8b89..d308ab2 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 hierarchical_class)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virNetDevBandwidthClear(const char *ifname)
ATTRIBUTE_NONNULL(1);
diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
index bd26ba9..cfe3f87 100644
--- a/src/util/virnetdevmacvlan.c
+++ b/src/util/virnetdevmacvlan.c
@@ -925,7 +925,7 @@ create_name:
rc = 0;
}
- if (virNetDevBandwidthSet(cr_ifname, bandwidth) < 0) {
+ if (virNetDevBandwidthSet(cr_ifname, bandwidth, false) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot set bandwidth limits on %s"),
cr_ifname);
--
1.7.8.6