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. --- src/Makefile.am | 7 ++- src/conf/domain_conf.h | 1 + src/conf/network_conf.c | 1 + src/conf/network_conf.h | 3 + src/libvirt_network.syms | 8 ++ src/network/bridge_driver.c | 165 +++++++++++++++++++++++++++++++++++++++++++ src/network/bridge_driver.h | 10 ++- src/qemu/qemu_command.c | 32 ++++++--- src/qemu/qemu_process.c | 12 +++ 9 files changed, 225 insertions(+), 14 deletions(-) create mode 100644 src/libvirt_network.syms diff --git a/src/Makefile.am b/src/Makefile.am index 1f32263..9b14ef8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1366,6 +1366,10 @@ if WITH_ATOMIC_OPS_PTHREAD USED_SYM_FILES += libvirt_atomic.syms endif +if WITH_NETWORK +USED_SYM_FILES += libvirt_network.syms +endif + EXTRA_DIST += \ libvirt_public.syms \ libvirt_private.syms \ @@ -1379,7 +1383,8 @@ EXTRA_DIST += \ libvirt_sasl.syms \ libvirt_vmx.syms \ libvirt_xenxs.syms \ - libvirt_libssh2.syms + libvirt_libssh2.syms \ + libvirt_network.syms GENERATED_SYM_FILES = libvirt.syms libvirt.def libvirt_qemu.def diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 091879e..09c705a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -862,6 +862,7 @@ struct _virDomainNetDef { virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; int linkstate; + unsigned int class_id; /* Class ID for 'floor' */ }; /* Used for prefix of ifname of any network name generated dynamically diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 41831e0..80189e6 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -318,6 +318,7 @@ virNetworkAssignDef(virNetworkObjListPtr nets, } virNetworkObjLock(network); network->def = def; + network->class_id = 3; /* next free class id */ nets->objs[nets->count] = network; nets->count++; diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 3e46304..8a8d1e4 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -221,6 +221,9 @@ struct _virNetworkObj { virNetworkDefPtr def; /* The current definition */ virNetworkDefPtr newDef; /* New definition to activate at shutdown */ + + unsigned int class_id; /* next class ID for QoS */ + unsigned long long floor_sum; /* sum of all 'floor'-s of attached NICs */ }; typedef struct _virNetworkObjList virNetworkObjList; diff --git a/src/libvirt_network.syms b/src/libvirt_network.syms new file mode 100644 index 0000000..14f76ec --- /dev/null +++ b/src/libvirt_network.syms @@ -0,0 +1,8 @@ +# +# Network-specific symbols +# + +# bridge_driver.h +virNetDevBandwidthPlug; +virNetDevBandwidthUnplug; +virNetDevBandwidthUpdateRate; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 256b601..44fa56e 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4192,3 +4192,168 @@ 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; + + if (!ifaceBand || !ifaceBand->in || !ifaceBand->in->floor || + !netBand || !netBand->in) + return 1; + + 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'"), + iface->ifname, + 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'"), + iface->ifname, + net->def->bridge, + net->def->name); + goto cleanup; + } + + *new_rate = tmp_new_rate; + ret = 0; + +cleanup: + return ret; +} + +int +networkNotifyPlug(virNetworkPtr network, + virDomainNetDefPtr iface) +{ + struct network_driver *driver = network->conn->networkPrivateData; + virNetworkObjPtr net = NULL; + int ret = -1; + int plug_ret; + unsigned long long new_rate = 0; + + networkDriverLock(driver); + net = virNetworkFindByUUID(&driver->networks, network->uuid); + networkDriverUnlock(driver); + + 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; + } + + plug_ret = virNetDevBandwidthPlug(net->def->bridge, + net->def->bandwidth, + iface->ifname, + &iface->mac, + iface->bandwidth, + net->class_id); + if (plug_ret < 0) { + ignore_value(virNetDevBandwidthUnplug(net->def->bridge, + net->class_id)); + goto cleanup; + } + + if (plug_ret > 0) { + /* Well, this is embarrassing. The networkCheckBandwidth helper + * says we need to set a QoS, but virNetDevBandwidthPlug says + * we don't need to. It's almost certainly a bug then. */ + VIR_WARN("Something strange happened. You may want to upgrade libvirt"); + ret = 0; + goto cleanup; + } + + /* QoS was set, generate new class ID */ + iface->class_id = net->class_id; + net->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: + if (net) + virNetworkObjUnlock(net); + return ret; +} + +int +networkNotifyUnplug(virDomainNetDefPtr iface) +{ + struct network_driver *driver = driverState; + virNetworkObjPtr net = NULL; + int ret = 0; + unsigned long long new_rate; + + networkDriverLock(driver); + net = virNetworkFindByName(&driver->networks, iface->data.network.name); + networkDriverUnlock(driver); + + if (iface->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->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->class_id = 0; + } + +cleanup: + if (net) + virNetworkObjUnlock(net); + return ret; +} diff --git a/src/network/bridge_driver.h b/src/network/bridge_driver.h index 0fae275..2f4b2e4 100644 --- a/src/network/bridge_driver.h +++ b/src/network/bridge_driver.h @@ -48,8 +48,12 @@ int networkGetNetworkAddress(const char *netname, char **netaddr) int networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdout, char *pidfile, - dnsmasqContext *dctx) - ; + dnsmasqContext *dctx); +int networkNotifyPlug(virNetworkPtr network, + virDomainNetDefPtr iface) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int networkNotifyUnplug(virDomainNetDefPtr iface) + ATTRIBUTE_NONNULL(1); # else /* Define no-op replacements that don't drag in any link dependencies. */ # define networkAllocateActualDevice(iface) 0 @@ -57,6 +61,8 @@ int networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, # define networkReleaseActualDevice(iface) 0 # define networkGetNetworkAddress(netname, netaddr) (-2) # define networkBuildDhcpDaemonCommandLine(network, cmdout, pidfile, dctx) 0 +# define networkNotifyPlug(network, iface) 0 +# define networkNotifyUnplug(network, iface) 0 # endif typedef char *(*networkDnsmasqLeaseFileNameFunc)(const char *netname); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c1f8680..efe98b3 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -207,12 +207,13 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP; bool template_ifname = false; int actualType = virDomainNetGetActualType(net); + virNetworkPtr network = NULL; + virErrorPtr errobj = NULL; if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { int active, fail = 0; - virErrorPtr errobj; - virNetworkPtr network = virNetworkLookupByName(conn, - net->data.network.name); + network = virNetworkLookupByName(conn, + net->data.network.name); if (!network) return -1; @@ -232,14 +233,14 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, fail = 1; } - /* Make sure any above failure is preserved */ - errobj = virSaveLastError(); - virNetworkFree(network); - virSetError(errobj); - virFreeError(errobj); - - if (fail) + if (fail) { + /* Make sure any above failure is preserved */ + errobj = virSaveLastError(); + virNetworkFree(network); + virSetError(errobj); + virFreeError(errobj); return -1; + } } else if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) { if (!(brname = strdup(virDomainNetGetActualBridgeName(net)))) { @@ -301,6 +302,12 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, goto cleanup; } + if (tapfd >= 0 && network && + networkNotifyPlug(network, net) < 0) { + VIR_FORCE_CLOSE(tapfd); + goto cleanup; + } + if (tapfd >= 0) { if ((net->filter) && (net->ifname)) { if (virDomainConfNWFilterInstantiate(conn, def->uuid, net) < 0) @@ -310,7 +317,10 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, cleanup: VIR_FREE(brname); - + errobj = virSaveLastError(); + virNetworkFree(network); + virSetError(errobj); + virFreeError(errobj); return tapfd; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 29b7ae1..6ff065c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4047,6 +4047,18 @@ void qemuProcessStop(struct qemud_driver *driver, } } + for (i = 0; i < vm->def->nnets; i++) { + virDomainNetDefPtr net = vm->def->nets[i]; + + if (virDomainNetGetActualType(net) != VIR_DOMAIN_NET_TYPE_NETWORK) + continue; + + if (networkNotifyUnplug(net) < 0) { + VIR_WARN("Unable to remove QoS settings for interface '%s'", + net->ifname); + } + } + if (priv->agent) { qemuAgentClose(priv->agent); priv->agent = NULL; -- 1.7.8.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list