diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 13caf93eb0..0c163437de 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1469,6 +1469,7 @@ virNetDevBandwidthClear; virNetDevBandwidthCopy; virNetDevBandwidthEqual; virNetDevBandwidthFree; +virNetDevBandwidthMinimal; virNetDevBandwidthPlug; virNetDevBandwidthSet; virNetDevBandwidthUnplug; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 0b43a67e8c..6bdd1d66dd 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -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) diff --git a/src/util/virnetdevbandwidth.c b/src/util/virnetdevbandwidth.c index ed6a19d7fa..273c2db8c8 100644 --- a/src/util/virnetdevbandwidth.c +++ b/src/util/virnetdevbandwidth.c @@ -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; +} diff --git a/src/util/virnetdevbandwidth.h b/src/util/virnetdevbandwidth.h index 4606a07526..ea40df461f 100644 --- a/src/util/virnetdevbandwidth.h +++ b/src/util/virnetdevbandwidth.h @@ -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__ */