From 8639a420599d0323232a14cea3a346f29807bba7 Mon Sep 17 00:00:00 2001 From: Laine Stump Date: Thu, 23 Feb 2012 10:45:35 -0500 Subject: [PATCH] qemu: support type='hostdev' network devices at domain start This patch makes sure that each network device ("interface") of type='hostdev' appears on both the hostdevs list and the nets list of the virDomainDef, and it modifies the qemu driver startup code so that these devices will be presented to qemu on the commandline as hostdevs rather than as network devices. It does not add support for hotplug of these type of devices, or code to honor the or given in the config (both of those will be done in separate patches). Once each device is placed on both lists, much of what this patch does is modify places in the code that traverse all the device lists so that these hybrid devices are only acted on once - either along with the other hostdevs, or along with the other network interfaces. (In many cases, only one of the lists is traversed / a specific operation is performed on only one type of device. In those instances, the code can remain unchanged.) There is one special case - when building the commandline, interfaces are allowed to proceed all the way through networkAllocateActualDevice() before deciding to skip the rest of netdev-specific processing - this is so that (once we have support for networks with pools of hostdev devices) we can get the actual device allocated, then rely on the loop processing all hostdevs to generate the correct commandline. (NB: is only supported for PCI network devices that are SR-IOV Virtual Functions (VF). Standard PCI[e] and USB devices, and even the Physical Functions (PF) of SR-IOV devices can only be assigned to a guest using the more basic device entry. This limitation is mostly due to the fact that non-SR-IOV ethernet devices tend to lose mac address configuration whenever the card is reset, which happens when a card is assigned to a guest; SR-IOV VFs fortunately don't suffer the same problem.) --- src/conf/domain_conf.c | 54 ++++++++++++++++--- src/qemu/qemu_command.c | 36 +++++++++++-- .../qemuxml2argv-net-hostdev.args | 6 +++ tests/qemuxml2argvtest.c | 2 + 4 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.args diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 70e92242f7..7135024161 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1463,6 +1463,16 @@ void virDomainDefFree(virDomainDefPtr def) if (!def) return; + /* hostdevs must be freed before nets (or any future "intelligent + * hostdevs") because the pointer to the hostdev is really + * pointing into the middle of the higher level device's object, + * so the original object must still be available during the call + * to virDomainHostdevDefFree(). + */ + for (i = 0 ; i < def->nhostdevs ; i++) + virDomainHostdevDefFree(def->hostdevs[i]); + VIR_FREE(def->hostdevs); + for (i = 0 ; i < def->nleases ; i++) virDomainLeaseDefFree(def->leases[i]); VIR_FREE(def->leases); @@ -1519,10 +1529,6 @@ void virDomainDefFree(virDomainDefPtr def) virDomainVideoDefFree(def->videos[i]); VIR_FREE(def->videos); - for (i = 0 ; i < def->nhostdevs ; i++) - virDomainHostdevDefFree(def->hostdevs[i]); - VIR_FREE(def->hostdevs); - for (i = 0 ; i < def->nhubs ; i++) virDomainHubDefFree(def->hubs[i]); VIR_FREE(def->hubs); @@ -7061,6 +7067,10 @@ int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net) return -1; def->nets[def->nnets] = net; def->nnets++; + if (net->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + /* hostdev net devices must also exist in the hostdevs array */ + return virDomainHostdevInsert(def, &net->data.hostdev.def); + } return 0; } @@ -7076,6 +7086,23 @@ int virDomainNetIndexByMac(virDomainDefPtr def, const unsigned char *mac) static void virDomainNetRemove(virDomainDefPtr def, size_t i) { + virDomainNetDefPtr net = def->nets[i]; + + if (net->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + /* hostdev net devices are normally also be in the hostdevs + * array, but might have already been removed by the time we + * get here. + */ + virDomainHostdevDefPtr hostdev = &net->data.hostdev.def; + size_t h; + + for (h = 0; h < def->nhostdevs; h++) { + if (def->hostdevs[h] == hostdev) { + virDomainHostdevRemove(def, h); + break; + } + } + } if (def->nnets > 1) { memmove(def->nets + i, def->nets + i + 1, @@ -8086,6 +8113,12 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, goto error; def->nets[def->nnets++] = net; + + /* must also be in the hostdevs array */ + if ((net->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) && + (virDomainHostdevInsert(def, &net->data.hostdev.def) < 0)) { + goto no_memory; + } } VIR_FREE(nodes); @@ -8412,7 +8445,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, if ((n = virXPathNodeSet("./devices/hostdev", ctxt, &nodes)) < 0) { goto error; } - if (n && VIR_ALLOC_N(def->hostdevs, n) < 0) + if (n && VIR_REALLOC_N(def->hostdevs, def->nhostdevs + n) < 0) goto no_memory; for (i = 0 ; i < n ; i++) { virDomainHostdevDefPtr hostdev; @@ -12374,9 +12407,16 @@ virDomainDefFormatInternal(virDomainDefPtr def, if (virDomainVideoDefFormat(buf, def->videos[n], flags) < 0) goto cleanup; - for (n = 0 ; n < def->nhostdevs ; n++) - if (virDomainHostdevDefFormat(buf, def->hostdevs[n], flags) < 0) + for (n = 0 ; n < def->nhostdevs ; n++) { + /* If parent.type != NONE, this is just a pointer to the + * hostdev in a higher-level device (e.g. virDomainNetDef), + * and will have already been formatted there. + */ + if ((def->hostdevs[n]->parent.type == VIR_DOMAIN_DEVICE_NONE) && + (virDomainHostdevDefFormat(buf, def->hostdevs[n], flags) < 0)) { goto cleanup; + } + } for (n = 0 ; n < def->nredirdevs ; n++) if (virDomainRedirdevDefFormat(buf, def->redirdevs[n], flags) < 0) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c628aed927..867c4608dd 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -641,8 +641,13 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virBitmapPtr qemuCaps) if (qemuCapsGet(qemuCaps, QEMU_CAPS_NET_NAME) || qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { for (i = 0; i < def->nnets ; i++) { - if (qemuAssignDeviceNetAlias(def, def->nets[i], i) < 0) + /* type='hostdev' interfaces are also on the hostdevs list, + * and will have their alias assigned with other hostdevs. + */ + if ((def->nets[i]->type != VIR_DOMAIN_NET_TYPE_HOSTDEV) && + (qemuAssignDeviceNetAlias(def, def->nets[i], i) < 0)) { return -1; + } } } @@ -834,7 +839,7 @@ static char *qemuPCIAddressAsString(virDomainDeviceInfoPtr dev) static int qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED, - virDomainDeviceDefPtr device ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr device, virDomainDeviceInfoPtr info, void *opaque) { @@ -842,8 +847,15 @@ static int qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED, char *addr = NULL; qemuDomainPCIAddressSetPtr addrs = opaque; - if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + if ((info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + || ((device->type == VIR_DOMAIN_DEVICE_HOSTDEV) && + (device->data.hostdev->parent.type != VIR_DOMAIN_DEVICE_NONE))) { + /* If a hostdev has a parent, its info will be a part of the + * parent, and will have its address collected during the scan + * of the parent's device type. + */ return 0; + } addr = qemuPCIAddressAsString(info); if (!addr) @@ -1358,8 +1370,14 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) /* Network interfaces */ for (i = 0; i < def->nnets ; i++) { - if (def->nets[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + /* type='hostdev' network devices might be USB, and are also + * in hostdevs list anyway, so handle them with other hostdevs + * instead of here. + */ + if ((def->nets[i]->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) || + (def->nets[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)) { continue; + } if (qemuDomainPCIAddressSetNextAddr(addrs, &def->nets[i]->info) < 0) goto error; } @@ -4774,6 +4792,16 @@ qemuBuildCommandLine(virConnectPtr conn, goto error; actualType = virDomainNetGetActualType(net); + if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + /* type='hostdev' interfaces are handled in codepath + * for standard hostdev (NB: when there is a network + * with