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 | 12 +++
src/qemu/qemu_command.c | 10 +++-
src/util/macvtap.c | 12 +++-
src/util/macvtap.h | 5 +-
src/util/network.c | 161 +++++++++++++++++++++++++++++++++++++++++++
src/util/network.h | 3 +
8 files changed, 205 insertions(+), 4 deletions(-)
diff --git a/configure.ac b/configure.ac
index e9d5be4..2b864cf 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 1cc9bca..ff398d7 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -702,6 +702,8 @@ nlComm;
# network.h
virBandwidrhDefFormat;
virBandwidthDefParseNode;
+virBandwidthDisable;
+virBandwidthEnable;
virSocketAddrBroadcast;
virSocketAddrBroadcastByPrefix;
virSocketAddrIsNetmask;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 0a12bc0..47f9799 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -1804,6 +1804,13 @@ networkStartNetworkDaemon(struct network_driver *driver,
if (v6present && networkStartRadvd(network) < 0)
goto err4;
+ if (virBandwidthEnable(&network->def->bandwidth,
network->def->bridge) < 0) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot set bandwidth limits on %s"),
+ network->def->bridge);
+ goto err5;
+ }
+
/* Persist the live configuration now we have bridge info */
if (virNetworkSaveConfig(NETWORK_STATE_DIR, network->def) < 0) {
goto err5;
@@ -1895,6 +1902,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 a3bce4a..78cb865 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -128,7 +128,7 @@ qemuPhysIfaceConnect(virDomainDefPtr def,
rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev,
net->data.direct.mode, vnet_hdr, def->uuid,
&net->data.direct.virtPortProfile, &res_ifname,
- vmop, driver->stateDir);
+ vmop, driver->stateDir, &net->bandwidth);
if (rc >= 0) {
virDomainAuditNetDevice(def, net, res_ifname, true);
VIR_FREE(net->ifname);
@@ -291,6 +291,14 @@ qemuNetworkIfaceConnect(virDomainDefPtr def,
}
}
+ if (virBandwidthEnable(&net->bandwidth, net->ifname) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot set bandwidth limits on %s"),
+ net->ifname);
+ VIR_FORCE_CLOSE(tapfd);
+ goto cleanup;
+ }
+
if (tapfd >= 0) {
if ((net->filter) && (net->ifname)) {
err = virDomainConfNWFilterInstantiate(conn, net);
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index 30343c8..bb7784c 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -266,7 +266,8 @@ openMacvtapTap(const char *tgifname,
virVirtualPortProfileParamsPtr virtPortProfile,
char **res_ifname,
enum virVMOperationType vmOp,
- char *stateDir)
+ char *stateDir,
+ virBandwidthPtr bandwidth)
{
const char *type = "macvtap";
int c, rc;
@@ -360,6 +361,15 @@ create_name:
} else
goto disassociate_exit;
+ if (virBandwidthEnable(bandwidth, cr_ifname) < 0) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot set bandwidth limits on %s"),
+ cr_ifname);
+ rc = -1;
+ goto disassociate_exit;
+ }
+
+
return rc;
disassociate_exit:
diff --git a/src/util/macvtap.h b/src/util/macvtap.h
index 1b85989..84bbc92 100644
--- a/src/util/macvtap.h
+++ b/src/util/macvtap.h
@@ -24,7 +24,7 @@
# define __UTIL_MACVTAP_H__
# include <config.h>
-
+# include "network.h"
enum virVirtualPortType {
VIR_VIRTUALPORT_NONE,
@@ -95,7 +95,8 @@ int openMacvtapTap(const char *ifname,
virVirtualPortProfileParamsPtr virtPortProfile,
char **res_ifname,
enum virVMOperationType vmop,
- char *stateDir);
+ char *stateDir,
+ virBandwidthPtr bandwidth);
void delMacvtap(const char *ifname,
const unsigned char *macaddress,
diff --git a/src/util/network.c b/src/util/network.c
index 58c0492..0a5652c 100644
--- a/src/util/network.c
+++ b/src/util/network.c
@@ -15,6 +15,7 @@
#include "network.h"
#include "util.h"
#include "virterror_internal.h"
+#include "command.h"
#define VIR_FROM_THIS VIR_FROM_NONE
#define virSocketError(code, ...) \
@@ -858,3 +859,163 @@ 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 (virAsprintf(&average, "%lukbps", bandwidth->in.average) <
0)
+ goto cleanup;
+ if (bandwidth->in.peak &&
+ (virAsprintf(&peak, "%lukbps", bandwidth->in.peak) < 0))
+ goto cleanup;
+ if (bandwidth->in.burst &&
+ (virAsprintf(&burst, "%lukb", 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 (virAsprintf(&average, "%lukbps", bandwidth->out.average)
< 0)
+ goto cleanup;
+ if (virAsprintf(&burst, "%lukb", 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 98e3082..17eab40 100644
--- a/src/util/network.h
+++ b/src/util/network.h
@@ -110,4 +110,7 @@ int virBandwidthDefParseNode(xmlNodePtr node, virBandwidthPtr def);
int virBandwidrhDefFormat(virBufferPtr buf,
virBandwidthPtr def,
const char *indent);
+
+int virBandwidthEnable(virBandwidthPtr bandwidth, const char *iface);
+int virBandwidthDisable(const char *iface, bool may_fail);
#endif /* __VIR_NETWORK_H__ */
--
1.7.5.rc3