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 <mac address> or <virtualport> 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: <interface type='hostdev'> 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 <hostdev> 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.)
This commit is contained in:
Laine Stump 2012-02-23 10:45:35 -05:00
parent 3b1c191fe7
commit 8639a42059
4 changed files with 87 additions and 11 deletions

View File

@ -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;
/* <interface type='hostdev'> 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)

View File

@ -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 <forward mode='hostdev', there will need to be
* code here that adds the newly minted hostdev to the
* hostdevs array).
*/
continue;
}
if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
int tapfd = qemuNetworkIfaceConnect(def, conn, driver, net,

View File

@ -0,0 +1,6 @@
LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S \
-M pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -monitor \
unix:/tmp/test-monitor,server,nowait -no-acpi -boot c \
-hda /dev/HostVG/QEMUGuest1 -usb \
-device pci-assign,host=03:07.1,id=hostdev0,bus=pci.0,addr=0x3 \
-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4

View File

@ -542,6 +542,8 @@ mymain(void)
DO_TEST("net-client", false, NONE);
DO_TEST("net-server", false, NONE);
DO_TEST("net-mcast", false, NONE);
DO_TEST("net-hostdev", false,
QEMU_CAPS_PCIDEVICE, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG);
DO_TEST("serial-vc", false, NONE);
DO_TEST("serial-pty", false, NONE);