qemuDomainAttachNetDevice - re-ordered some things at start of function because networkAllocateActualDevice should always be run and a slot in def->nets always allocated, but host_net_add isn't needed if the actual type is hostdev. - if actual type is hostdev, defer to qemuDomainAttachHostDevice (which will reach up to the NetDef for things like MAC address when necessary). After return from qemuDomainAttachHostDevice, slip directly to cleanup, since the rest of the function is specific to emulated net devices. - put assignment of new NetDef into expanded def->nets down below cleanup: (but only on success) since it is also needed for emulated and hostdev net devices. qemuDomainDetachHostDevice - after locating the exact device to detach, check if it's a network device and, if so, use toplevel qemuDomainDetachNetDevice instead so that the def->nets list is properly updated, and 'actual device' properly returned to network pool if appropriate. Otherwise, for normal hostdevs, call the lower level qemuDomainDetachThisDevice. qemuDomainDetachNetDevice - This is where it gets a bit tricky. After locating the device on the def->nets list, if the network device type == hostdev, call the *lower level* qemuDomainDetachThisDevice (which will reach back up to the parent net device for MAC address / virtualport when appropriate, then clear the device out of def->hostdevs) before skipping past all the emulated net-device-specific code to cleanup:, where the network device is removed from def->nets, and the network device object is freed. In short, any time a hostdev-type network device is detached, we must go through the toplevel virDomaineDetachNetDevice function first and last, to make sure 1) the def->nnets list is properly managed, and 2) any device allocated with networkAllocateActualDevice is properly freed. At the same time, in the middle we need to go through the lower-level virDomainDetach*This*HostDevice to be sure that 1) the def->hostdevs list is properly managed, 2) the PCI device is properly detached from the guest and reattached to the host (if appropriate), and 3) any higher level setup/teardown is called at the appropriate time, by reaching back up to the NetDef config (part (3) will be covered in a separate patch). --- src/qemu/qemu_hotplug.c | 61 +++++++++++++++++++++++++++++++++------------- 1 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 6119108..50563c5 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -661,9 +661,9 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, bool iface_connected = false; int actualType; - if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_HOST_NET_ADD)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("installed qemu version does not support host_net_add")); + /* preallocate new slot for device */ + if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0) { + virReportOOMError(); return -1; } @@ -672,9 +672,27 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, * to the one defined in the network definition. */ if (networkAllocateActualDevice(net) < 0) - goto cleanup; + return -1; actualType = virDomainNetGetActualType(net); + + if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + /* This is really a "smart hostdev", so it should be attached + * as a hostdev (the hostdev code will reach over into the + * netdev-specific code as appropriate), then also added to + * the nets list (see cleanup:) if successful. + */ + ret = qemuDomainAttachHostDevice(driver, vm, + virDomainNetGetActualHostdev(net)); + goto cleanup; + } + + if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_HOST_NET_ADD)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("installed qemu version does not support host_net_add")); + goto cleanup; + } + if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE || actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { if ((tapfd = qemuNetworkIfaceConnect(vm->def, conn, driver, net, @@ -693,9 +711,6 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, goto cleanup; } - if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0) - goto no_memory; - if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_NET_NAME) || qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0) @@ -826,10 +841,10 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, ret = 0; - vm->def->nets[vm->def->nnets++] = net; - cleanup: - if (ret < 0) { + if (!ret) { + vm->def->nets[vm->def->nnets++] = net; + } else { if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) && (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && releaseaddr && @@ -2032,7 +2047,13 @@ int qemuDomainDetachHostDevice(struct qemud_driver *driver, return -1; } - return qemuDomainDetachThisHostDevice(driver, vm, detach, idx); + /* If this is a network hostdev, we need to use the higher-level detach + * function so that mac address / virtualport are reset + */ + if (detach->parent.type == VIR_DOMAIN_DEVICE_NET) + return qemuDomainDetachNetDevice(driver, vm, &detach->parent); + else + return qemuDomainDetachThisHostDevice(driver, vm, detach, idx); } int @@ -2056,6 +2077,13 @@ qemuDomainDetachNetDevice(struct qemud_driver *driver, } } + if (virDomainNetGetActualType(detach) == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + ret = qemuDomainDetachThisHostDevice(driver, vm, + virDomainNetGetActualHostdev(detach), + -1); + goto cleanup; + } + if (!detach) { qemuReportError(VIR_ERR_OPERATION_FAILED, _("network device %02x:%02x:%02x:%02x:%02x:%02x not found"), @@ -2156,14 +2184,13 @@ qemuDomainDetachNetDevice(struct qemud_driver *driver, ignore_value(virNetDevOpenvswitchRemovePort( virDomainNetGetActualBridgeName(detach), detach->ifname)); - - networkReleaseActualDevice(detach); - virDomainNetRemove(vm->def, i); - virDomainNetDefFree(detach); - ret = 0; - cleanup: + if (!ret) { + networkReleaseActualDevice(detach); + virDomainNetRemove(vm->def, i); + virDomainNetDefFree(detach); + } VIR_FREE(hostnet_name); return ret; } -- 1.7.7.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list