These function executes 'tc' with appropriate arguments to set
desired QoS setting on interface or bridge during its creation.
---
configure.ac | 4 +
src/libvirt_private.syms | 2 +
src/network/bridge_driver.c | 8 ++
src/qemu/qemu_command.c | 5 ++
src/util/network.c | 152 +++++++++++++++++++++++++++++++++++++++++++
src/util/network.h | 3 +
6 files changed, 174 insertions(+), 0 deletions(-)
diff --git a/configure.ac b/configure.ac
index f816696..d563e94 100644
--- a/configure.ac
+++ b/configure.ac
@@ -165,6 +165,8 @@ AC_PATH_PROG([RADVD], [radvd], [radvd],
[/sbin:/usr/sbin:/usr/local/sbin:$PATH])
AC_PATH_PROG([BRCTL], [brctl], [brctl],
[/sbin:/usr/sbin:/usr/local/sbin:$PATH])
+AC_PATH_PROG([TC], [tc], [tc],
+ [/sbin:/usr/sbin:/usr/local/sbin:$PATH])
AC_PATH_PROG([UDEVADM], [udevadm], [],
[/sbin:/usr/sbin:/usr/local/sbin:$PATH])
AC_PATH_PROG([UDEVSETTLE], [udevsettle], [],
@@ -178,6 +180,8 @@ AC_DEFINE_UNQUOTED([RADVD],["$RADVD"],
[Location or name of the radvd program])
AC_DEFINE_UNQUOTED([BRCTL],["$BRCTL"],
[Location or name of the brctl program (see bridge-utils)])
+AC_DEFINE_UNQUOTED([TC],["$TC"],
+ [Location or name of the tc profram (see iproute2)])
if test -n "$UDEVADM"; then
AC_DEFINE_UNQUOTED([UDEVADM],["$UDEVADM"],
[Location or name of the udevadm program])
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 6640cfb..adf9cb0 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -683,6 +683,8 @@ nlComm;
# network.h
virBandwidrhDefFormat;
virBandwidthDefParseNode;
+virBandwidthDisable;
+virBandwidthEnable;
virBpsToRate;
virBtoSize;
virSocketAddrBroadcast;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 4b94959..80f852f 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -1745,6 +1745,9 @@ networkStartNetworkDaemon(struct network_driver *driver,
if (v6present && networkStartRadvd(network) < 0)
goto err4;
+ if (virBandwidthEnable(&network->def->bandwidth,
network->def->bridge) < 0)
+ goto err5;
+
/* Persist the live configuration now we have bridge info */
if (virNetworkSaveConfig(NETWORK_STATE_DIR, network->def) < 0) {
goto err5;
@@ -1836,6 +1839,11 @@ static int networkShutdownNetworkDaemon(struct network_driver
*driver,
unlink(stateFile);
VIR_FREE(stateFile);
+ if (virBandwidthDisable(network->def->bridge, true) < 0) {
+ VIR_WARN("Failed to disable QoS on %s",
+ network->def->name);
+ }
+
if (network->radvdPid > 0) {
char *radvdpidbase;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index b517e1a..5ef73ed 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -291,6 +291,11 @@ qemuNetworkIfaceConnect(virDomainDefPtr def,
}
}
+ if (virBandwidthEnable(&net->bandwidth, net->ifname) < 0) {
+ VIR_FORCE_CLOSE(tapfd);
+ goto cleanup;
+ }
+
if (tapfd >= 0) {
if ((net->filter) && (net->ifname)) {
err = virDomainConfNWFilterInstantiate(conn, net);
diff --git a/src/util/network.c b/src/util/network.c
index b24f977..7bba60c 100644
--- a/src/util/network.c
+++ b/src/util/network.c
@@ -16,6 +16,7 @@
#include "network.h"
#include "util.h"
#include "virterror_internal.h"
+#include "command.h"
#define VIR_FROM_THIS VIR_FROM_NONE
#define virSocketError(code, ...) \
@@ -1030,3 +1031,154 @@ virBandwidrhDefFormat(virBufferPtr buf,
cleanup:
return ret;
}
+
+/**
+ * virBandwidthEnable:
+ * @bandwidth: rates to set
+ * @iface: on which interface
+ *
+ * This function enables QoS on specified interface
+ * and set given traffic limits for both, incoming
+ * and outgoing traffic. Any previous setting get
+ * overwritten.
+ *
+ * Return 0 on success, -1 otherwise.
+ */
+int
+virBandwidthEnable(virBandwidthPtr bandwidth,
+ const char *iface)
+{
+ int ret = -1;
+ virCommandPtr cmd = NULL;
+ char *average = NULL;
+ char *peak = NULL;
+ char *burst = NULL;
+
+ if (!bandwidth || !iface)
+ return -1;
+
+ if (!bandwidth->in.average &&
+ !bandwidth->out.average) {
+ /* nothing to be enabled */
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (virBandwidthDisable(iface, true) < 0)
+ goto cleanup;
+
+ if (bandwidth->in.average) {
+ if (virBpsToRate(&average, bandwidth->in.average) < 0)
+ goto cleanup;
+ if (bandwidth->in.peak &&
+ (virBpsToRate(&peak, bandwidth->in.peak) < 0))
+ goto cleanup;
+ if (bandwidth->in.burst &&
+ (virBtoSize(&burst, bandwidth->in.burst) < 0))
+ goto cleanup;
+
+ cmd = virCommandNew(TC);
+ virCommandAddArgList(cmd, "qdisc", "add", "dev",
iface, "root", "handle", "1:", "htb",
"default", "1", NULL);
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+
+ virCommandFree(cmd);
+ cmd = virCommandNew(TC);
+ virCommandAddArgList(cmd,"class", "add", "dev",
iface, "parent", "1:", "classid", "1:1",
"htb", NULL);
+ virCommandAddArgList(cmd, "rate", average, NULL);
+
+ if (peak)
+ virCommandAddArgList(cmd, "ceil", peak, NULL);
+ if (burst)
+ virCommandAddArgList(cmd, "burst", burst, NULL);
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+
+ virCommandFree(cmd);
+ cmd = virCommandNew(TC);
+ virCommandAddArgList(cmd,"filter", "add",
"dev", iface, "parent", "1:0", "protocol",
"ip", "handle", "1", "fw", "flowid",
"1", NULL);
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+
+ virCommandFree(cmd);
+ VIR_FREE(average);
+ VIR_FREE(peak);
+ VIR_FREE(burst);
+ }
+
+ if (bandwidth->out.average) {
+ if (virBpsToRate(&average, bandwidth->out.average) < 0)
+ goto cleanup;
+ if (virBtoSize(&burst, bandwidth->out.burst ? bandwidth->out.burst :
bandwidth->out.average) < 0)
+ goto cleanup;
+
+ cmd = virCommandNew(TC);
+ virCommandAddArgList(cmd, "qdisc", "add",
"dev", iface, "ingress", NULL);
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+
+ virCommandFree(cmd);
+ cmd = virCommandNew(TC);
+ virCommandAddArgList(cmd, "filter", "add", "dev",
iface, "parent", "ffff:", "protocol", "ip",
"u32", "match", "ip", "src",
"0.0.0.0/0", "police", "rate", average, "burst",
burst, "drop", "flowid", ":1", NULL);
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ virCommandFree(cmd);
+ VIR_FREE(average);
+ VIR_FREE(peak);
+ VIR_FREE(burst);
+ return ret;
+}
+
+/**
+ * virBandwidthDisable:
+ * @iface: on which interface
+ * @may_fail: should be unsuccessful disable considered fatal?
+ *
+ * This function tries to disable QoS on specified interface
+ * by deleting root and ingress qdisc. However, this may fail
+ * if we try to remove the default one.
+ *
+ * Return 0 on success, -1 otherwise.
+ */
+int
+virBandwidthDisable(const char *iface,
+ bool may_fail)
+{
+ int ret = -1;
+ int status;
+ virCommandPtr cmd = NULL;
+
+ if (!iface)
+ return -1;
+
+ cmd = virCommandNew(TC);
+ virCommandAddArgList(cmd, "qdisc", "del", "dev", iface,
"root", NULL);
+
+ if ((virCommandRun(cmd, &status) < 0) ||
+ (!may_fail && status))
+ goto cleanup;
+
+ virCommandFree(cmd);
+
+ cmd = virCommandNew(TC);
+ virCommandAddArgList(cmd, "qdisc", "del", "dev",
iface, "ingress", NULL);
+
+ if ((virCommandRun(cmd, &status) < 0) ||
+ (!may_fail && status))
+ goto cleanup;
+
+ ret = 0;
+
+cleanup:
+ virCommandFree(cmd);
+ return ret;
+}
diff --git a/src/util/network.h b/src/util/network.h
index f8cdc55..5a88e51 100644
--- a/src/util/network.h
+++ b/src/util/network.h
@@ -115,4 +115,7 @@ int virBandwidrhDefFormat(virBufferPtr buf,
int virBpsToRate(char **buf, unsigned long rate);
int virBtoSize(char **buf, unsigned long size);
+
+int virBandwidthEnable(virBandwidthPtr bandwidth, const char *iface);
+int virBandwidthDisable(const char *iface, bool may_fail);
#endif /* __VIR_NETWORK_H__ */
--
1.7.5.rc3