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 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 - 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.
---
New patch in V2.
src/conf/domain_conf.c | 54 +++++++++++++++++---
src/qemu/qemu_command.c | 36 ++++++++++++--
.../qemuxml2argvdata/qemuxml2argv-net-hostdev.args | 7 +++
.../qemuxml2argvdata/qemuxml2argv-net-hostdev.xml | 7 ---
tests/qemuxml2argvtest.c | 2 +
5 files changed, 88 insertions(+), 18 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 70e9224..7135024 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;
+
+ /* <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)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index dfac389..d23d56f 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -643,8 +643,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;
+ }
}
}
@@ -836,7 +841,7 @@ static char *qemuPCIAddressAsString(virDomainDeviceInfoPtr dev)
static int qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
- virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
+ virDomainDeviceDefPtr device,
virDomainDeviceInfoPtr info,
void *opaque)
{
@@ -844,8 +849,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)
@@ -1360,8 +1372,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;
}
@@ -4776,6 +4794,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,
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.args
b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.args
new file mode 100644
index 0000000..7cdf5d1
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.args
@@ -0,0 +1,7 @@
+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 usb-host,hostbus=0,hostaddr=2,id=hostdev1 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml
b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml
index 504e4f6..3dc2077 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml
+++ b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml
@@ -36,13 +36,6 @@
<address type='usb' bus='0' device='2'/>
</source>
</interface>
- <interface type='hostdev'>
- <mac address='22:11:22:33:44:55'/>
- <source>
- <vendor id='0x0012'/>
- <product id='0x24dd'/>
- </source>
- </interface>
<memballoon model='virtio'/>
</devices>
</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index fcffc27..0abc9cf 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -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);
--
1.7.7.6