On 12/11/2012 11:09 AM, Michal Privoznik wrote: > Network should be notified if we plug in or unplug an > interface, so it can perform some action, e.g. set/unset > network part of QoS. However, we are doing this in very > early stage, so iface->ifname isn't filled in yet. So > whenever we want to report an error, we must use a different > identifier, e.g. the MAC address. > --- > src/conf/domain_conf.h | 1 + > src/conf/network_conf.c | 18 ++++ > src/conf/network_conf.h | 4 + > src/libvirt_private.syms | 3 + > src/network/bridge_driver.c | 212 ++++++++++++++++++++++++++++++++++++++++++- > 5 files changed, 236 insertions(+), 2 deletions(-) > > diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h > index 7ad5377..e6659cd 100644 > --- a/src/conf/domain_conf.h > +++ b/src/conf/domain_conf.h > @@ -809,6 +809,7 @@ struct _virDomainActualNetDef { > virNetDevVPortProfilePtr virtPortProfile; > virNetDevBandwidthPtr bandwidth; > virNetDevVlan vlan; > + unsigned int class_id; /* class ID for bandwidth 'floor' */ > }; > > /* Stores the virtual network interface configuration */ > diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c > index 3093418..ac326e1 100644 > --- a/src/conf/network_conf.c > +++ b/src/conf/network_conf.c > @@ -47,6 +47,9 @@ > > #define MAX_BRIDGE_ID 256 > #define VIR_FROM_THIS VIR_FROM_NETWORK > +#define NEXT_FREE_CLASS_ID 3 > +/* currently, /sbin/tc implementation allows up to 16 bits for minor class size */ > +#define CLASS_ID_BITMAP_SIZE (1<<16) > > VIR_ENUM_IMPL(virNetworkForward, > VIR_NETWORK_FORWARD_LAST, > @@ -319,10 +322,25 @@ virNetworkAssignDef(virNetworkObjListPtr nets, > virNetworkObjLock(network); > network->def = def; > > + if (!(network->class_id = virBitmapNew(CLASS_ID_BITMAP_SIZE))) { > + virReportOOMError(); > + goto error; > + } > + > + /* The first three class IDs are already taken */ > + ignore_value(virBitmapSetBit(network->class_id, 0)); > + ignore_value(virBitmapSetBit(network->class_id, 1)); > + ignore_value(virBitmapSetBit(network->class_id, 2)); > + > + network->def = def; > nets->objs[nets->count] = network; > nets->count++; > > return network; > +error: > + virNetworkObjUnlock(network); > + virNetworkObjFree(network); > + return NULL; > > } > > diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h > index 949b3d2..364372d 100644 > --- a/src/conf/network_conf.h > +++ b/src/conf/network_conf.h > @@ -38,6 +38,7 @@ > # include "virnetdevvlan.h" > # include "virmacaddr.h" > # include "device_conf.h" > +# include "bitmap.h" > > enum virNetworkForwardType { > VIR_NETWORK_FORWARD_NONE = 0, > @@ -226,6 +227,9 @@ struct _virNetworkObj { > > virNetworkDefPtr def; /* The current definition */ > virNetworkDefPtr newDef; /* New definition to activate at shutdown */ > + > + virBitmapPtr class_id; /* bitmap of class IDs for QoS */ > + unsigned long long floor_sum; /* sum of all 'floor'-s of attached NICs */ > }; > > typedef struct _virNetworkObjList virNetworkObjList; > diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms > index 499ab3b..6564676 100644 > --- a/src/libvirt_private.syms > +++ b/src/libvirt_private.syms > @@ -1506,7 +1506,10 @@ virNetDevBandwidthClear; > virNetDevBandwidthCopy; > virNetDevBandwidthEqual; > virNetDevBandwidthFree; > +virNetDevBandwidthPlug; > virNetDevBandwidthSet; > +virNetDevBandwidthUnplug; > +virNetDevBandwidthUpdateRate; Yeah, those are the changes I would have put with the earlier patch... > > > # virnetdevbridge.h > diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c > index 58f1d2e..0bee453 100644 > --- a/src/network/bridge_driver.c > +++ b/src/network/bridge_driver.c > @@ -121,6 +121,11 @@ static int networkShutdownNetworkExternal(struct network_driver *driver, > static void networkReloadIptablesRules(struct network_driver *driver); > static void networkRefreshDaemons(struct network_driver *driver); > > +static int networkPlugBandwidth(virNetworkObjPtr net, > + virDomainNetDefPtr iface); > +static int networkUnplugBandwidth(virNetworkObjPtr net, > + virDomainNetDefPtr iface); > + > static struct network_driver *driverState = NULL; > > static char * > @@ -3491,6 +3496,7 @@ networkAllocateActualDevice(virDomainNetDefPtr iface) > enum virDomainNetType actualType = iface->type; > virNetworkObjPtr network = NULL; > virNetworkDefPtr netdef = NULL; > + virNetDevBandwidthPtr bandwidth = NULL; > virPortGroupDefPtr portgroup = NULL; > virNetDevVPortProfilePtr virtport = iface->virtPortProfile; > virNetDevVlanPtr vlan = NULL; > @@ -3529,7 +3535,13 @@ networkAllocateActualDevice(virDomainNetDefPtr iface) > * (already in NetDef). Otherwise, if there is bandwidth info in > * the portgroup, fill that into the ActualDef. > */ > - if (portgroup && !iface->bandwidth) { > + > + if (iface->bandwidth) > + bandwidth = iface->bandwidth; > + else if (portgroup && portgroup->bandwidth) > + bandwidth = portgroup->bandwidth; > + > + if (bandwidth) { > if (!iface->data.network.actual > && (VIR_ALLOC(iface->data.network.actual) < 0)) { > virReportOOMError(); > @@ -3537,7 +3549,7 @@ networkAllocateActualDevice(virDomainNetDefPtr iface) > } > > if (virNetDevBandwidthCopy(&iface->data.network.actual->bandwidth, > - portgroup->bandwidth) < 0) > + bandwidth) < 0) > goto error; > } > > @@ -3550,6 +3562,10 @@ networkAllocateActualDevice(virDomainNetDefPtr iface) > */ > if (iface->data.network.actual) > iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_NETWORK; > + > + if (networkPlugBandwidth(network, iface) < 0) > + goto error; > + > } else if ((netdef->forwardType == VIR_NETWORK_FORWARD_BRIDGE) && > netdef->bridge) { > > @@ -4061,6 +4077,12 @@ networkReleaseActualDevice(virDomainNetDefPtr iface) > } > netdef = network->def; > > + if ((netdef->forwardType == VIR_NETWORK_FORWARD_NONE || > + netdef->forwardType == VIR_NETWORK_FORWARD_NAT || > + netdef->forwardType == VIR_NETWORK_FORWARD_ROUTE) && > + networkUnplugBandwidth(network, iface) < 0) > + goto error; > + > if ((!iface->data.network.actual) || > ((actualType != VIR_DOMAIN_NET_TYPE_DIRECT) && > (actualType != VIR_DOMAIN_NET_TYPE_HOSTDEV))) { > @@ -4264,3 +4286,189 @@ cleanup: > error: > goto cleanup; > } > + > +/** > + * networkCheckBandwidth: > + * @net: network QoS > + * @iface: interface QoS > + * @new_rate: new rate for non guaranteed class > + * > + * Returns: -1 if plugging would overcommit network QoS > + * 0 if plugging is safe (@new_rate updated) > + * 1 if no QoS is set (@new_rate untouched) > + */ > +static int > +networkCheckBandwidth(virNetworkObjPtr net, > + virDomainNetDefPtr iface, > + unsigned long long *new_rate) > +{ > + int ret = -1; > + virNetDevBandwidthPtr netBand = net->def->bandwidth; > + virNetDevBandwidthPtr ifaceBand = iface->bandwidth; > + unsigned long long tmp_floor_sum = net->floor_sum; > + unsigned long long tmp_new_rate = 0; > + char ifmac[VIR_MAC_STRING_BUFLEN]; > + > + if (!ifaceBand || !ifaceBand->in || !ifaceBand->in->floor || > + !netBand || !netBand->in) > + return 1; > + > + virMacAddrFormat(&iface->mac, ifmac); > + > + tmp_new_rate = netBand->in->average; > + tmp_floor_sum += ifaceBand->in->floor; > + > + /* check against peak */ > + if (netBand->in->peak) { > + tmp_new_rate = netBand->in->peak; > + if (tmp_floor_sum > netBand->in->peak) { > + virReportError(VIR_ERR_OPERATION_INVALID, > + _("Cannot plug '%s' interface into '%s' because it " > + "would overcommit 'peak' on network '%s'"), > + ifmac, > + net->def->bridge, > + net->def->name); > + goto cleanup; > + } > + } else if (tmp_floor_sum > netBand->in->average) { > + /* tmp_floor_sum can be between 'average' and 'peak' iff 'peak' is set. > + * Otherwise, tmp_floor_sum must be below 'average'. */ > + virReportError(VIR_ERR_OPERATION_INVALID, > + _("Cannot plug '%s' interface into '%s' because it " > + "would overcommit 'average' on network '%s'"), > + ifmac, > + net->def->bridge, > + net->def->name); > + goto cleanup; > + } > + > + *new_rate = tmp_new_rate; > + ret = 0; > + > +cleanup: > + return ret; > +} > + > +/** > + * networkNextClassID: > + * @net: network object > + * > + * Find next free class ID. @net is supposed > + * to be locked already. If there is a free ID, > + * it is marked as used and returned. > + * > + * Returns next free class ID or -1 if none is available. > + */ > +static ssize_t > +networkNextClassID(virNetworkObjPtr net) > +{ > + size_t ret = 0; > + bool is_set = false; > + > + while (virBitmapGetBit(net->class_id, ret, &is_set) == 0 && is_set) > + ret++; > + > + if (is_set || virBitmapSetBit(net->class_id, ret) < 0) > + return -1; > + > + return ret; > +} > + > +static int > +networkPlugBandwidth(virNetworkObjPtr net, > + virDomainNetDefPtr iface) > +{ > + int ret = -1; > + int plug_ret; > + unsigned long long new_rate = 0; > + ssize_t class_id = 0; > + char ifmac[VIR_MAC_STRING_BUFLEN]; > + > + if ((plug_ret = networkCheckBandwidth(net, iface, &new_rate)) < 0) { > + /* helper reported error */ > + goto cleanup; > + } > + > + if (plug_ret > 0) { > + /* no QoS needs to be set; claim success */ > + ret = 0; > + goto cleanup; > + } > + > + virMacAddrFormat(&iface->mac, ifmac); > + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK || > + !iface->data.network.actual) { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + _("Cannot set bandwidth on interface '%s' of type %d"), > + ifmac, iface->type); > + goto cleanup; > + } > + > + /* generate new class_id */ > + if ((class_id = networkNextClassID(net)) < 0) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("Could not generate next class ID")); > + goto cleanup; > + } > + > + plug_ret = virNetDevBandwidthPlug(net->def->bridge, > + net->def->bandwidth, > + &iface->mac, > + iface->bandwidth, > + class_id); > + if (plug_ret < 0) { > + ignore_value(virNetDevBandwidthUnplug(net->def->bridge, class_id)); > + goto cleanup; > + } > + > + /* QoS was set, generate new class ID */ > + iface->data.network.actual->class_id = class_id; > + /* update sum of 'floor'-s of attached NICs */ > + net->floor_sum += iface->bandwidth->in->floor; > + /* update rate for non guaranteed NICs */ > + new_rate -= net->floor_sum; > + if (virNetDevBandwidthUpdateRate(net->def->bridge, "1:2", > + net->def->bandwidth, new_rate) < 0) > + VIR_WARN("Unable to update rate for 1:2 class on %s bridge", > + net->def->bridge); > + > + ret = 0; > + > +cleanup: > + return ret; > +} > + > +static int > +networkUnplugBandwidth(virNetworkObjPtr net, > + virDomainNetDefPtr iface) > +{ > + int ret = 0; > + unsigned long long new_rate; > + > + if (iface->data.network.actual && > + iface->data.network.actual->class_id) { > + /* we must remove class from bridge */ > + new_rate = net->def->bandwidth->in->average; > + > + if (net->def->bandwidth->in->peak > 0) > + new_rate = net->def->bandwidth->in->peak; > + > + ret = virNetDevBandwidthUnplug(net->def->bridge, > + iface->data.network.actual->class_id); > + if (ret < 0) > + goto cleanup; > + /* update sum of 'floor'-s of attached NICs */ > + net->floor_sum -= iface->bandwidth->in->floor; > + /* update rate for non guaranteed NICs */ > + new_rate -= net->floor_sum; > + if (virNetDevBandwidthUpdateRate(net->def->bridge, "1:2", > + net->def->bandwidth, new_rate) < 0) > + VIR_WARN("Unable to update rate for 1:2 class on %s bridge", > + net->def->bridge); > + /* no class is associated any longer */ > + iface->data.network.actual->class_id = 0; > + } > + > +cleanup: > + return ret; > +} ACK. -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list