These function executes 'tc' with appropriate arguments to set
desired QoS setting on interface or bridge during its creation.
---
configure.ac | 4 +
libvirt.spec.in | 2 +-
src/libvirt_private.syms | 2 +
src/network/bridge_driver.c | 18 +++++
src/qemu/qemu_command.c | 11 +++-
src/util/macvtap.c | 12 +++-
src/util/macvtap.h | 3 +-
src/util/network.c | 161 +++++++++++++++++++++++++++++++++++++++++++
src/util/network.h | 3 +
9 files changed, 212 insertions(+), 4 deletions(-)
diff --git a/configure.ac b/configure.ac
index 9e39f44..72fbc41 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/libvirt.spec.in b/libvirt.spec.in
index 6cbd9ac..4f0691d 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -250,7 +250,7 @@ Requires: %{name}-client = %{version}-%{release}
Requires: bridge-utils
# for modprobe of pci devices
Requires: module-init-tools
-# for /sbin/ip
+# for /sbin/ip & /sbin/tc
Requires: iproute
%endif
%if %{with_network}
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 188d647..85100dd 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -710,6 +710,8 @@ nlComm;
virBandwidthDefFormat;
virBandwidthDefFree;
virBandwidthDefParseNode;
+virBandwidthDisable;
+virBandwidthEnable;
virSocketAddrBroadcast;
virSocketAddrBroadcastByPrefix;
virSocketAddrIsNetmask;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 99033a2..37fb6cf 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -1821,10 +1821,23 @@ networkStartNetworkVirtual(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;
+ }
+
VIR_FREE(macTapIfName);
return 0;
+ err5:
+ if (virBandwidthDisable(network->def->bridge, true) < 0) {
+ VIR_WARN("Failed to disable QoS on %s",
+ network->def->bridge);
+ }
+
err4:
if (!save_err)
save_err = virSaveLastError();
@@ -1882,6 +1895,11 @@ static int networkShutdownNetworkVirtual(struct network_driver
*driver,
int err;
char ebuf[1024];
+ 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 e785f00..f8fd4ee 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -132,7 +132,7 @@ qemuPhysIfaceConnect(virDomainDefPtr def,
vnet_hdr, def->uuid,
virDomainNetGetActualDirectVirtPortProfile(net),
&res_ifname,
- vmop, driver->stateDir);
+ vmop, driver->stateDir, net->bandwidth);
if (rc >= 0) {
virDomainAuditNetDevice(def, net, res_ifname, true);
VIR_FREE(net->ifname);
@@ -298,6 +298,15 @@ qemuNetworkIfaceConnect(virDomainDefPtr def,
}
}
+ if (tapfd >= 0 &&
+ 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 7b97c6a..9f7d320 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -267,7 +267,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;
@@ -361,6 +362,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 8e8613d..2b2d835 100644
--- a/src/util/macvtap.h
+++ b/src/util/macvtap.h
@@ -62,7 +62,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 5639219..5561012 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, ...) \
@@ -1102,3 +1103,163 @@ virBandwidthDefFormat(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 (!iface)
+ return -1;
+
+ if (!bandwidth) {
+ /* nothing to be enabled */
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (virBandwidthDisable(iface, true) < 0)
+ goto cleanup;
+
+ if (bandwidth->in) {
+ if (virAsprintf(&average, "%llukbps", bandwidth->in->average)
< 0)
+ goto cleanup;
+ if (bandwidth->in->peak &&
+ (virAsprintf(&peak, "%llukbps", bandwidth->in->peak) <
0))
+ goto cleanup;
+ if (bandwidth->in->burst &&
+ (virAsprintf(&burst, "%llukb", 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;
+
+ VIR_FREE(average);
+ VIR_FREE(peak);
+ VIR_FREE(burst);
+ }
+
+ if (bandwidth->out) {
+ if (virAsprintf(&average, "%llukbps",
bandwidth->out->average) < 0)
+ goto cleanup;
+ if (virAsprintf(&burst, "%llukb", bandwidth->out->burst ?
+ bandwidth->out->burst : bandwidth->out->average) <
0)
+ goto cleanup;
+
+ virCommandFree(cmd);
+ 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, "mtu", 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 d0181fd..139f6cc 100644
--- a/src/util/network.h
+++ b/src/util/network.h
@@ -155,4 +155,7 @@ void virBandwidthDefFree(virBandwidthPtr def);
int virBandwidthDefFormat(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