[libvirt] [PATCH v4 5/7] bandwidth: Implement functions to enable and disable QoS

Michal Privoznik mprivozn at redhat.com
Fri Jul 22 14:07:27 UTC 2011


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




More information about the libvir-list mailing list