[libvirt] [PATCH v2 5/7] bandwitdh: Implement functions to enable and disable QoS

Michal Privoznik mprivozn at redhat.com
Tue Jul 12 11:57:11 UTC 2011


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 ae747fb..bef204d 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 2131108..8c4217c 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -684,6 +684,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 554a8ac..750dee4 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 b131ce7..08867c7 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) {
         qemuAuditNetDevice(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 b24f977..0266616 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,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 (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




More information about the libvir-list mailing list