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)
#include "viralloc.h"
#include "virerror.h"
#include "virstring.h"
+#include "virutil.h"
#define VIR_FROM_THIS VIR_FROM_NONE
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;
+}