]> xenbits.xensource.com Git - libvirt.git/commitdiff
networkAllocateActualDevice: Set QoS for bridgeless networks too
authorMichal Privoznik <mprivozn@redhat.com>
Wed, 22 Jan 2014 17:58:33 +0000 (18:58 +0100)
committerMichal Privoznik <mprivozn@redhat.com>
Mon, 27 Jan 2014 11:11:27 +0000 (12:11 +0100)
https://bugzilla.redhat.com/show_bug.cgi?id=1055484

Currently, libvirt's XML schema of network allows QoS to be defined for
every network even though it has no bridge. For instance:

<network>
    <name>vdsm-no-bridge</name>
    <forward mode='passthrough'>
      <interface dev='em1.10'/>
    </forward>
    <bandwidth>
        <inbound average='1000' peak='5000' burst='1024'/>
        <outbound average='1000' burst='1024'/>
    </bandwidth>
</network>

The bandwidth limitations can be, however, applied even on such
networks. In fact, they are going to be applied on the interface that
will be connected to the network on a domain startup. This approach,
however, has one limitation. With bridged networks, there are two points
where QoS can be set: bridge and domain interface. The lower limit of
the two is enforced then. For instance, if the interface has 10Mbps
average, but the network only 1Mbps, there's no way for interface to
transmit packets faster than the 1Mbps limit. With two points this is
enforced by kernel.  With only one point, we must combine both QoS
settings into one which is set afterwards. Look at
virNetDevBandwidthMinimal() and you'll understand immediately what I
mean.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
src/libvirt_private.syms
src/network/bridge_driver.c
src/util/virnetdevbandwidth.c
src/util/virnetdevbandwidth.h

index 13caf93eb0269dabbe3904f1360f4efd6e1ccd68..0c163437de47b2d97153222b598d379468499839 100644 (file)
@@ -1469,6 +1469,7 @@ virNetDevBandwidthClear;
 virNetDevBandwidthCopy;
 virNetDevBandwidthEqual;
 virNetDevBandwidthFree;
+virNetDevBandwidthMinimal;
 virNetDevBandwidthPlug;
 virNetDevBandwidthSet;
 virNetDevBandwidthUnplug;
index 0b43a67e8cb33f24e10dd82bb4638a47d78ecd4f..6bdd1d66dd85f6c00d66eb380b4a81a0d68d69cd 100644 (file)
@@ -3284,9 +3284,30 @@ networkAllocateActualDevice(virDomainNetDefPtr iface)
     else if (portgroup && portgroup->bandwidth)
         bandwidth = portgroup->bandwidth;
 
-    if (bandwidth && virNetDevBandwidthCopy(&iface->data.network.actual->bandwidth,
-                                            bandwidth) < 0)
+    /* For bridgeless networks we must take into account any QoS set to them.
+     * This is, however, a bit tricky. With bridged network there are two
+     * points where QoS can be set: domain's interface and the bridge. The
+     * limits on those differ (in general) in which case the lower limit gets
+     * into effect. For example, if domain's interface average is 10Mbps but
+     * the network's is just 1Mbps, the domain will not be able to send data
+     * any faster than 1Mbps. However, on bridgeless networks we can't just set
+     * those two points and let kernel do its job since we have only single
+     * point. Therefore, we must combine the QoS settings from both domain's
+     * interface and network and choose the minimal value from pairs.
+     */
+    if (netdef->forward.type == VIR_NETWORK_FORWARD_PRIVATE ||
+        netdef->forward.type == VIR_NETWORK_FORWARD_VEPA ||
+        netdef->forward.type == VIR_NETWORK_FORWARD_PASSTHROUGH ||
+        netdef->forward.type == VIR_NETWORK_FORWARD_HOSTDEV) {
+        if (virNetDevBandwidthMinimal(&iface->data.network.actual->bandwidth,
+                                      bandwidth,
+                                      netdef->bandwidth) < 0)
+            goto error;
+    } else if (bandwidth &&
+               virNetDevBandwidthCopy(&iface->data.network.actual->bandwidth,
+                                      bandwidth) < 0) {
         goto error;
+    }
 
     /* copy appropriate vlan info to actualNet */
     if (iface->vlan.nTags > 0)
index ed6a19d7faa3b564e4aa6c184418ebd85305a712..273c2db8c8b21148ff076068bb8167eae5c0932c 100644 (file)
@@ -27,6 +27,7 @@
 #include "viralloc.h"
 #include "virerror.h"
 #include "virstring.h"
+#include "virutil.h"
 
 #define VIR_FROM_THIS VIR_FROM_NONE
 
@@ -588,3 +589,86 @@ cleanup:
     VIR_FREE(ceil);
     return ret;
 }
+
+static void
+virNetDevBandwidthRateMinimal(virNetDevBandwidthRatePtr result,
+                              virNetDevBandwidthRatePtr band1,
+                              virNetDevBandwidthRatePtr band2)
+{
+    if (!band1 || !band2) {
+        memcpy(result, band1 ? band1 : band2, sizeof(*result));
+        return;
+    }
+
+    /* We can't do a simple MIN() here, because zero value doesn't mean the
+     * narrowest limit, but an unset value. Hence we need symmetric F(a, b)
+     * so that:
+     * F(a, 0) = F(0, a) = a; special corner case: F(0, 0) = 0
+     * F(a, b) = MIN(a, b) for a != 0 and b != 0
+     */
+#define NON_ZERO_MIN(a, b) \
+    ((a) && (b) ? MIN(a, b) : (a) ? (a) : (b))
+
+    result->average = NON_ZERO_MIN(band1->average, band2->average);
+    result->peak    = NON_ZERO_MIN(band1->peak,    band2->peak);
+    result->floor   = NON_ZERO_MIN(band1->floor,   band2->floor);
+    result->burst   = NON_ZERO_MIN(band1->burst,   band2->burst);
+}
+
+/**
+ * virNetDevBandwidthMinimal:
+ * @result: the minimal intersect of @band1 and @band2
+ * @band1: the first bandwidth
+ * @band2: the second bandwidth
+ *
+ * Combine the two bandwidths into one with choosing the minimal value for each
+ * pair of items within bandwidth structure. The resulting bandwidth is stored
+ * into @result. In case of both @band1 and @band2 being NULL pointers, the
+ * @ret is set to NULL as well as there's no bandwidth to calculate (this is
+ * not considered an error). The caller is responsible for freeing @result when
+ * no longer needed.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int
+virNetDevBandwidthMinimal(virNetDevBandwidthPtr *result,
+                          virNetDevBandwidthPtr band1,
+                          virNetDevBandwidthPtr band2)
+{
+    int ret = -1;
+
+    if (!band1 && !band2) {
+        /* Nothing to compute */
+        *result = NULL;
+        return 0;
+    }
+
+    if (!band1 || !band2) {
+        /* Sweet, one of the args is NULL. The non-NULL one
+         * is our minimum then. */
+        return virNetDevBandwidthCopy(result, band1 ? band1 : band2);
+    }
+
+    if (VIR_ALLOC(*result) < 0)
+        goto cleanup;
+
+    if (band1->in || band2->in) {
+        if (VIR_ALLOC((*result)->in) < 0)
+            goto cleanup;
+
+        virNetDevBandwidthRateMinimal((*result)->in, band1->in, band2->in);
+    }
+
+    if (band1->out || band2->out) {
+        if (VIR_ALLOC((*result)->out) < 0)
+            goto cleanup;
+
+        virNetDevBandwidthRateMinimal((*result)->out, band1->out, band2->out);
+    }
+
+    ret = 0;
+cleanup:
+    if (ret < 0)
+        VIR_FREE(*result);
+    return ret;
+}
index 4606a07526408c4a64008d3628f27008e4025342..ea40df461f5c33e7a8531baf680eba3bcc43ef4a 100644 (file)
@@ -74,4 +74,10 @@ int virNetDevBandwidthUpdateRate(const char *ifname,
                                  unsigned long long new_rate)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
     ATTRIBUTE_RETURN_CHECK;
+
+int
+virNetDevBandwidthMinimal(virNetDevBandwidthPtr *result,
+                          virNetDevBandwidthPtr band1,
+                          virNetDevBandwidthPtr band2)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
 #endif /* __VIR_NETDEV_BANDWIDTH_H__ */