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);
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);
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;
}
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,
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);
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;
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)
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;
+ }
}
}
static int qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
- virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
+ virDomainDeviceDefPtr device,
virDomainDeviceInfoPtr info,
void *opaque)
{
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)
/* 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;
}
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,
--- /dev/null
+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
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);