From 90074ecfa7550524ada668b82641ee5a23b0cb36 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Fri, 22 Jul 2011 16:07:27 +0200 Subject: [PATCH] bandwidth: Implement functions to enable and disable QoS 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 4e04950f4a..ac34efc4f1 100644 --- a/configure.ac +++ b/configure.ac @@ -170,6 +170,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], [], @@ -183,6 +185,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 7c8b2ec635..002239c043 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 2efe9412c5..853ee628f1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -713,6 +713,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 4a274086e4..f242db8c05 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -1822,10 +1822,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(); @@ -1883,6 +1896,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 e785f00172..f8fd4eee9a 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 86e52fa26c..0b00776695 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 8e8613d619..2b2d835479 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 5639219816..5561012ada 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 d0181fddfa..139f6ccb8b 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__ */ -- GitLab