[libvirt] [PATCH RESEND] qemuBuildNicDevStr: Set vectors= on Multiqueue
by Michal Privoznik
Yet another advice appeared on the Multiqueue wiki page:
http://www.linux-kvm.org/page/Multiqueue#Enable_MQ_feature
We should add vectors=N onto the qemu command line, where
N = 2 * (number of queues) + 1.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/qemu/qemu_command.c | 13 +++++++------
src/qemu/qemu_command.h | 2 +-
src/qemu/qemu_hotplug.c | 4 +---
3 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 35b7c67..81486df 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -4980,7 +4980,7 @@ qemuBuildNicDevStr(virDomainDefPtr def,
virDomainNetDefPtr net,
int vlan,
int bootindex,
- bool multiqueue,
+ int vhostfdSize,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -5035,8 +5035,11 @@ qemuBuildNicDevStr(virDomainDefPtr def,
virDomainVirtioEventIdxTypeToString(net->driver.virtio.event_idx));
}
}
- if (usingVirtio && multiqueue)
- virBufferAddLit(&buf, ",mq=on");
+ if (usingVirtio && vhostfdSize > 1) {
+ /* As advised at http://www.linux-kvm.org/page/Multiqueue
+ * we should add vectors=2*N+1 where N is the vhostfdSize */
+ virBufferAsprintf(&buf, ",mq=on,vectors=%d", 2 * vhostfdSize + 1);
+ }
if (vlan == -1)
virBufferAsprintf(&buf, ",netdev=host%s", net->info.alias);
else
@@ -7591,10 +7594,8 @@ qemuBuildInterfaceCommandLine(virCommandPtr cmd,
virCommandAddArgList(cmd, "-netdev", host, NULL);
}
if (qemuDomainSupportsNicdev(def, qemuCaps, net)) {
- bool multiqueue = tapfdSize > 1 || vhostfdSize > 1;
-
if (!(nic = qemuBuildNicDevStr(def, net, vlan, bootindex,
- multiqueue, qemuCaps)))
+ vhostfdSize, qemuCaps)))
goto cleanup;
virCommandAddArgList(cmd, "-device", nic, NULL);
} else {
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 66c23cc..de7683d 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -105,7 +105,7 @@ char * qemuBuildNicDevStr(virDomainDefPtr def,
virDomainNetDefPtr net,
int vlan,
int bootindex,
- bool multiqueue,
+ int vhostfdSize,
virQEMUCapsPtr qemuCaps);
char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk,
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 7a8caf1..4315df2 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -998,10 +998,8 @@ int qemuDomainAttachNetDevice(virConnectPtr conn,
}
if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
- bool multiqueue = tapfdSize > 1 || vhostfdSize > 1;
-
if (!(nicstr = qemuBuildNicDevStr(vm->def, net, vlan, 0,
- multiqueue, priv->qemuCaps)))
+ vhostfdSize, priv->qemuCaps)))
goto try_remove;
} else {
if (!(nicstr = qemuBuildNicStr(net, NULL, vlan)))
--
1.8.5.1
10 years, 11 months
[libvirt] [PATCH] Add Documentation fields to systemd service files
by Guido Günther
We point to the manpages where available and redirect to libvirt's
homepage as a last resort.
---
daemon/libvirtd.service.in | 2 ++
src/locking/virtlockd.service.in | 2 ++
tools/libvirt-guests.service.in | 2 ++
3 files changed, 6 insertions(+)
diff --git a/daemon/libvirtd.service.in b/daemon/libvirtd.service.in
index 25979ef..dc2433a 100644
--- a/daemon/libvirtd.service.in
+++ b/daemon/libvirtd.service.in
@@ -9,6 +9,8 @@ Before=libvirt-guests.service
After=network.target
After=dbus.service
After=iscsid.service
+Documentation=man:libvirtd(8)
+Documentation=http://libvirt.org
[Service]
EnvironmentFile=-/etc/sysconfig/libvirtd
diff --git a/src/locking/virtlockd.service.in b/src/locking/virtlockd.service.in
index a1298a3..57089b0 100644
--- a/src/locking/virtlockd.service.in
+++ b/src/locking/virtlockd.service.in
@@ -1,6 +1,8 @@
[Unit]
Description=Virtual machine lock manager
Requires=virtlockd.socket
+Documentation=man:virtlockd(8)
+Documentation=http://libvirt.org
[Service]
EnvironmentFile=-/etc/sysconfig/virtlockd
diff --git a/tools/libvirt-guests.service.in b/tools/libvirt-guests.service.in
index d48d4b8..d8d7adf 100644
--- a/tools/libvirt-guests.service.in
+++ b/tools/libvirt-guests.service.in
@@ -1,6 +1,8 @@
[Unit]
Description=Suspend Active Libvirt Guests
After=network.target libvirtd.service
+Documentation=man:libvirtd(8)
+Documentation=http://libvirt.org
[Service]
EnvironmentFile=-/etc/sysconfig/libvirt-guests
--
1.8.5.2
10 years, 11 months
[libvirt] [libvirt-java] [PATCH 0/2] Fixes for Device.listCapabilities
by Claudio Bley
Let the Device.listCapabilities method return an array filled with
non-null string objects. Furthermore, the decoding of the character
data does no longer depend upon the default JVM charset.
This also fixes a memleak because the allocated memory was not freed
in the old code.
Patch #2 just adds a unit test which simply uses this function.
Claudio Bley (2):
Make Device.listCapabilities return only valid array elements
test: ensure the Device.listCapabilities method works
src/main/java/org/libvirt/Device.java | 10 +++++++---
src/main/java/org/libvirt/jna/Libvirt.java | 2 +-
src/test/java/org/libvirt/TestJavaBindings.java | 14 ++++++++++++++
3 files changed, 22 insertions(+), 4 deletions(-)
--
1.8.5.2.msysgit.0
10 years, 11 months
Re: [libvirt] [PATCH] libxl: Fix devid init in libxlMakeNicList
by Jim Fehlig
Stefan Bader wrote:
> This basically reverts commit ba64b97134a6129a48684f22f31be92c3b6eef96
> "libxl: Allow libxl to set NIC devid". However assigning devid's
> before calling libxlMakeNic does not work as that is calling
> libxl_device_nic_init which sets it back to -1.
> Right now auto-assignment only works in the hotplug case. But even if
> that would be fixed at some point (if that is possible at all), this
> would add a weird dependency between Xen and libvirt versions.
Yeah, I had numerous inquires and bugs come my way even after fixing vfb and
vkb devid initialization in libxl with xen.git commit 5420f265. It took
some time for the fix to make its way to downstream users. Since there is
not yet a fix in Xen, it only makes sense to do the nic devid initialization
in libvirt to fix PXE booting HVM domains.
> The change here should accept any auto-assignment that makes it into
> libxl_device_nic_init. My understanding is that a caller always is
> allowed to make the devid choice itself. And assuming libxlMakeNicList
> is only used on domain creation, a sequential numbering should be ok.
>
> Signed-off-by: Stefan Bader <stefan.bader(a)canonical.com>
> ---
> src/libxl/libxl_conf.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c
> index 04d01af..4cefadf 100644
> --- a/src/libxl/libxl_conf.c
> +++ b/src/libxl/libxl_conf.c
> @@ -918,6 +918,13 @@ libxlMakeNicList(virDomainDefPtr def,
> libxl_domain_config *d_config)
> for (i = 0; i < nnics; i++) {
> if (libxlMakeNic(def, l_nics[i], &x_nics[i]))
> goto error;
> + /*
> + * The devid (at least right now) will not get initialized by
> + * libxl in the setup case but is required for starting the
> + * device-model.
> + */
> + if (x_nics[i].devid < 0)
> + x_nics[i].devid = i;
I think this is a better approach than the original code removed by ba64b971.
I've pushed this since it is clearly a bug fix and appropriate for 1.2.1.
Regards,
Jim
> }
>
> d_config->nics = x_nics;
10 years, 11 months
Re: [libvirt] [v8 1/6] add hostdev passthrough common library
by Jim Fehlig
Chunyan Liu wrote:
> Extract code from qemu_hostdev.c and make it reusable for multiple drivers,
> meanwhile maintain a global hostdev state to solve conflict between
> different
> drivers.
>
> Signed-off-by: Chunyan Liu <cyliu(a)suse.com>
> ---
> po/POTFILES.in | 1 +
> src/Makefile.am | 1 +
> src/libvirt_private.syms | 21 +
> src/lxc/lxc_hostdev.c | 11 +-
> src/qemu/qemu_driver.c | 4 +-
> src/qemu/qemu_hostdev.c | 42 +-
> src/util/virhostdev.c | 1691
> ++++++++++++++++++++++++++++++++++++++++++++++
> src/util/virhostdev.h | 134 ++++
> src/util/virpci.c | 30 +-
> src/util/virpci.h | 9 +-
> src/util/virscsi.c | 28 +-
> src/util/virscsi.h | 8 +-
> src/util/virusb.c | 29 +-
> src/util/virusb.h | 8 +-
> 14 files changed, 1969 insertions(+), 48 deletions(-)
> create mode 100644 src/util/virhostdev.c
> create mode 100644 src/util/virhostdev.h
>
> diff --git a/po/POTFILES.in b/po/POTFILES.in
> index 49dfc9c..ba63428 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -161,6 +161,7 @@ src/util/vireventpoll.c
> src/util/virfile.c
> src/util/virhash.c
> src/util/virhook.c
> +src/util/virhostdev.c
> src/util/viridentity.c
> src/util/virinitctl.c
> src/util/viriptables.c
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 57e163f..98233cd 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -104,6 +104,7 @@ UTIL_SOURCES = \
> util/virhash.c util/virhash.h \
> util/virhashcode.c util/virhashcode.h \
> util/virhook.c util/virhook.h \
> + util/virhostdev.c util/virhostdev.h \
> util/viridentity.c util/viridentity.h \
> util/virinitctl.c util/virinitctl.h \
> util/viriptables.c util/viriptables.h \
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index 2dbb8f8..dcb0a3e 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1264,6 +1264,27 @@ virHookInitialize;
> virHookPresent;
>
>
> +#util/virhostdev.h
> +virHostdevHostSupportsPassthroughKVM;
> +virHostdevHostSupportsPassthroughVFIO;
> +virHostdevManagerGetDefault;
> +virHostdevPciNodeDeviceDetach;
> +virHostdevPciNodeDeviceReAttach;
> +virHostdevPciNodeDeviceReset;
> +virHostdevPrepareDomainHostdevs;
> +virHostdevPreparePciHostdevs;
> +virHostdevPrepareScsiHostdevs;
> +virHostdevPrepareUsbHostdevs;
> +virHostdevReAttachDomainHostdevs;
> +virHostdevReAttachPciHostdevs;
> +virHostdevReAttachScsiHostdevs;
> +virHostdevReAttachUsbHostdevs;
> +virHostdevUpdateActiveHostdevs;
> +virHostdevUpdateActivePciHostdevs;
> +virHostdevUpdateActiveScsiHostdevs;
> +virHostdevUpdateActiveUsbHostdevs;
> +
> +
> # util/viridentity.h
> virIdentityGetAttr;
> virIdentityGetCurrent;
> diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c
> index 3b371fc..77ce965 100644
> --- a/src/lxc/lxc_hostdev.c
> +++ b/src/lxc/lxc_hostdev.c
> @@ -60,7 +60,7 @@ virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver,
> continue;
> }
>
> - virUSBDeviceSetUsedBy(usb, def->name);
> + virUSBDeviceSetUsedBy(usb, "QEMU", def->name);
>
> virObjectLock(driver->activeUsbHostdevs);
> if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) {
> @@ -90,7 +90,9 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver,
> for (i = 0; i < count; i++) {
> virUSBDevicePtr usb = virUSBDeviceListGet(list, i);
> if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) {
> - const char *other_name = virUSBDeviceGetUsedBy(tmp);
> + const char *other_name = NULL;
> + const char *other_drvname = NULL;
> + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name);
>
> if (other_name)
> virReportError(VIR_ERR_OPERATION_INVALID,
> @@ -103,7 +105,7 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver,
> goto error;
> }
>
> - virUSBDeviceSetUsedBy(usb, name);
> + virUSBDeviceSetUsedBy(usb, "QEMU", name);
> VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs",
> virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb),
> name);
> /*
> @@ -352,6 +354,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr
> driver,
> virDomainHostdevDefPtr hostdev = hostdevs[i];
> virUSBDevicePtr usb, tmp;
> const char *used_by = NULL;
> + const char *used_by_drvname = NULL;
>
> if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> continue;
> @@ -389,7 +392,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr
> driver,
> continue;
> }
>
> - used_by = virUSBDeviceGetUsedBy(tmp);
> + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
> if (STREQ_NULLABLE(used_by, name)) {
> VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs",
> hostdev->source.subsys.u.usb.bus,
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index 45d11cd..90820ab 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -10885,7 +10885,9 @@ qemuNodeDeviceReAttach(virNodeDevicePtr dev)
> virObjectLock(driver->inactivePciHostdevs);
> other = virPCIDeviceListFind(driver->activePciHostdevs, pci);
> if (other) {
> - const char *other_name = virPCIDeviceGetUsedBy(other);
> + const char *other_name = NULL;
> + const char *other_drvname = NULL;
> + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name);
>
> if (other_name)
> virReportError(VIR_ERR_OPERATION_INVALID,
> diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
> index dee61e7..1dc652b 100644
> --- a/src/qemu/qemu_hostdev.c
> +++ b/src/qemu/qemu_hostdev.c
> @@ -177,7 +177,7 @@ qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver,
> goto cleanup;
>
> }
> - virPCIDeviceSetUsedBy(dev, def->name);
> + virPCIDeviceSetUsedBy(dev, "QEMU", def->name);
>
> /* Setup the original states for the PCI device */
> virPCIDeviceSetUnbindFromStub(dev,
> hostdev->origstates.states.pci.unbind_from_stub);
> @@ -230,7 +230,7 @@ qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver,
> continue;
> }
>
> - virUSBDeviceSetUsedBy(usb, def->name);
> + virUSBDeviceSetUsedBy(usb, "QEMU", def->name);
>
> if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) {
> virUSBDeviceFree(usb);
> @@ -270,7 +270,7 @@ qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver,
> hostdev->readonly)))
> goto cleanup;
>
> - virSCSIDeviceSetUsedBy(scsi, def->name);
> + virSCSIDeviceSetUsedBy(scsi, "QEMU", def->name);
>
> if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) {
> virSCSIDeviceFree(scsi);
> @@ -683,7 +683,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver,
> * the dev is in list driver->activePciHostdevs.
> */
> if ((other = virPCIDeviceListFind(driver->activePciHostdevs, dev))) {
> - const char *other_name = virPCIDeviceGetUsedBy(other);
> + const char *other_name = NULL;
> + const char *other_drvname = NULL;
> + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name);
>
> if (other_name)
> virReportError(VIR_ERR_OPERATION_INVALID,
> @@ -756,7 +758,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver,
> activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev);
>
> if (activeDev)
> - virPCIDeviceSetUsedBy(activeDev, name);
> + virPCIDeviceSetUsedBy(activeDev, "QEMU", name);
> }
>
> /* Loop 8: Now set the original states for hostdev def */
> @@ -847,7 +849,9 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver,
> for (i = 0; i < count; i++) {
> virUSBDevicePtr usb = virUSBDeviceListGet(list, i);
> if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) {
> - const char *other_name = virUSBDeviceGetUsedBy(tmp);
> + const char *other_name = NULL;
> + const char *other_drvname = NULL;
> + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name);
>
> if (other_name)
> virReportError(VIR_ERR_OPERATION_INVALID,
> @@ -860,7 +864,7 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver,
> goto error;
> }
>
> - virUSBDeviceSetUsedBy(usb, name);
> + virUSBDeviceSetUsedBy(usb, "QEMU", name);
> VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs",
> virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb),
> name);
> /*
> @@ -1116,7 +1120,9 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver,
> for (i = 0; i < count; i++) {
> virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i);
> if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi)))
> {
> - const char *other_name = virSCSIDeviceGetUsedBy(tmp);
> + const char *other_name = NULL;
> + const char *other_drvname = NULL;
> + virSCSIDeviceGetUsedBy(tmp, &other_drvname, &other_name);
>
> if (other_name)
> virReportError(VIR_ERR_OPERATION_INVALID,
> @@ -1129,7 +1135,7 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver,
> goto error;
> }
>
> - virSCSIDeviceSetUsedBy(scsi, name);
> + virSCSIDeviceSetUsedBy(scsi, "QEMU", name);
> VIR_DEBUG("Adding %s to activeScsiHostdevs",
> virSCSIDeviceGetName(scsi));
>
> if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0)
> @@ -1258,10 +1264,14 @@ qemuDomainReAttachHostdevDevices(virQEMUDriverPtr
> driver,
> * been used by this domain.
> */
> activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev);
> - if (activeDev &&
> - STRNEQ_NULLABLE(name, virPCIDeviceGetUsedBy(activeDev))) {
> - virPCIDeviceListDel(pcidevs, dev);
> - continue;
> + if (activeDev) {
> + const char *tmp_name = NULL;
> + const char *tmp_drvname = NULL;
> + virPCIDeviceGetUsedBy(activeDev, &tmp_drvname, &tmp_name);
> + if (STRNEQ_NULLABLE(name, tmp_name)) {
> + virPCIDeviceListDel(pcidevs, dev);
> + continue;
> + }
> }
>
> virPCIDeviceListDel(driver->activePciHostdevs, dev);
> @@ -1316,6 +1326,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr
> driver,
> virDomainHostdevDefPtr hostdev = hostdevs[i];
> virUSBDevicePtr usb, tmp;
> const char *used_by = NULL;
> + const char *used_by_drvname = NULL;
>
> if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> continue;
> @@ -1353,7 +1364,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr
> driver,
> continue;
> }
>
> - used_by = virUSBDeviceGetUsedBy(tmp);
> + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
> if (STREQ_NULLABLE(used_by, name)) {
> VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs",
> hostdev->source.subsys.u.usb.bus,
> @@ -1380,6 +1391,7 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr
> driver,
> virDomainHostdevDefPtr hostdev = hostdevs[i];
> virSCSIDevicePtr scsi, tmp;
> const char *used_by = NULL;
> + const char *used_by_drvname = NULL;
> virDomainDeviceDef dev;
>
> dev.type = VIR_DOMAIN_DEVICE_HOSTDEV;
> @@ -1421,7 +1433,7 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr
> driver,
> continue;
> }
>
> - used_by = virSCSIDeviceGetUsedBy(tmp);
> + virSCSIDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
> if (STREQ_NULLABLE(used_by, name)) {
> VIR_DEBUG("Removing %s:%d:%d:%d dom=%s from
> activeScsiHostdevs",
> hostdev->source.subsys.u.scsi.adapter,
> diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c
> new file mode 100644
> index 0000000..63690b7
> --- /dev/null
> +++ b/src/util/virhostdev.c
> @@ -0,0 +1,1691 @@
> +/* virhostdev.c: hostdev management
> + *
> + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
> + * Copyright (C) 2006-2007, 2009-2013 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library. If not, see
> + * <http://www.gnu.org/licenses/>.
> + *
> + * Author: Chunyan Liu <cyliu(a)suse.com>
> + * Author: Daniel P. Berrange <berrange(a)redhat.com>
> + */
> +
> +#include <config.h>
> +
> +#include <dirent.h>
> +#include <fcntl.h>
> +#include <sys/ioctl.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "virhostdev.h"
> +#include "viralloc.h"
> +#include "virstring.h"
> +#include "virfile.h"
> +#include "virerror.h"
> +#include "virlog.h"
> +#include "virnetdev.h"
> +#include "virutil.h"
> +#include "configmake.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_NONE
> +
> +#define HOSTDEV_STATE_DIR LOCALSTATEDIR "/run/libvirt/hostdevmgr"
> +
> +/* for upgrade, may need to find netconfig file in old qemu state dir */
> +#define QEMU_STATE_DIR LOCALSTATEDIR "/run/libvirt/qemu"
> +
> +static virHostdevManagerPtr hostdevMgr;
> +
> +static void
> +virHostdevManagerCleanup(void)
> +{
> + if (!hostdevMgr)
> + return;
> +
> + virObjectUnref(hostdevMgr->activePciHostdevs);
> + virObjectUnref(hostdevMgr->inactivePciHostdevs);
> + virObjectUnref(hostdevMgr->activeUsbHostdevs);
> + VIR_FREE(hostdevMgr->stateDir);
> +
> + VIR_FREE(hostdevMgr);
> +}
> +
> +static int
> +virHostdevOnceInit(void)
> +{
> + if (VIR_ALLOC(hostdevMgr) < 0)
> + goto error;
> +
> + if ((hostdevMgr->activePciHostdevs = virPCIDeviceListNew()) == NULL)
> + goto error;
> +
> + if ((hostdevMgr->activeUsbHostdevs = virUSBDeviceListNew()) == NULL)
> + goto error;
> +
> + if ((hostdevMgr->inactivePciHostdevs = virPCIDeviceListNew()) == NULL)
> + goto error;
> +
> + if ((hostdevMgr->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL)
> + goto error;
> +
> + if (VIR_STRDUP(hostdevMgr->stateDir, HOSTDEV_STATE_DIR) < 0)
> + goto error;
> +
> + if (virFileMakePath(hostdevMgr->stateDir) < 0) {
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Failed to create state dir '%s'"),
> + hostdevMgr->stateDir);
> + goto error;
> + }
> +
> + return 0;
> +
> +error:
> + virHostdevManagerCleanup();
> + return -1;
> +}
> +
> +VIR_ONCE_GLOBAL_INIT(virHostdev)
> +
> +virHostdevManagerPtr
> +virHostdevManagerGetDefault(void)
> +{
> + if (virHostdevInitialize() < 0)
> + return NULL;
> + return hostdevMgr;
> +}
> +
> +static int
> +virHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path)
> +{
> + virPCIDeviceAddress config_address;
> +
> + config_address.domain = hostdev->source.subsys.u.pci.addr.domain;
> + config_address.bus = hostdev->source.subsys.u.pci.addr.bus;
> + config_address.slot = hostdev->source.subsys.u.pci.addr.slot;
> + config_address.function = hostdev->source.subsys.u.pci.addr.function;
> +
> + return virPCIDeviceAddressGetSysfsFile(&config_address, sysfs_path);
> +}
> +
> +static int
> +virHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev)
> +{
> + char *sysfs_path = NULL;
> + int ret = -1;
> +
> + if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)
> + return ret;
> +
> + ret = virPCIIsVirtualFunction(sysfs_path);
> +
> + VIR_FREE(sysfs_path);
> +
> + return ret;
> +}
> +
> +static int
> +virHostdevNetDevice(virDomainHostdevDefPtr hostdev,
> + char **linkdev,
> + int *vf)
> +{
> + int ret = -1;
> + char *sysfs_path = NULL;
> +
> + if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)
> + return ret;
> +
> + if (virPCIIsVirtualFunction(sysfs_path) == 1) {
> + if (virPCIGetVirtualFunctionInfo(sysfs_path, linkdev, vf) < 0)
> + goto cleanup;
> + } else {
> + if (virPCIGetNetName(sysfs_path, linkdev) < 0)
> + goto cleanup;
> + *vf = -1;
> + }
> +
> + ret = 0;
> +
> +cleanup:
> + VIR_FREE(sysfs_path);
> +
> + return ret;
> +}
> +
> +static int
> +virHostdevNetConfigVirtPortProfile(const char *linkdev, int vf,
> + virNetDevVPortProfilePtr virtPort,
> + const virMacAddr *macaddr,
> + const unsigned char *uuid,
> + int associate)
> +{
> + int ret = -1;
> +
> + if (!virtPort)
> + return ret;
> +
> + switch (virtPort->virtPortType) {
> + case VIR_NETDEV_VPORT_PROFILE_NONE:
> + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
> + case VIR_NETDEV_VPORT_PROFILE_8021QBG:
> + case VIR_NETDEV_VPORT_PROFILE_LAST:
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("virtualport type %s is "
> + "currently not supported on interfaces of type "
> + "hostdev"),
> + virNetDevVPortTypeToString(virtPort->virtPortType));
> + break;
> +
> + case VIR_NETDEV_VPORT_PROFILE_8021QBH:
> + if (associate)
> + ret = virNetDevVPortProfileAssociate(NULL, virtPort, macaddr,
> + linkdev, vf, uuid,
> +
> VIR_NETDEV_VPORT_PROFILE_OP_CREATE, false);
> + else
> + ret = virNetDevVPortProfileDisassociate(NULL, virtPort,
> + macaddr, linkdev, vf,
> +
> VIR_NETDEV_VPORT_PROFILE_OP_DESTROY);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int
> +virHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev,
> + const unsigned char *uuid,
> + char *stateDir)
> +{
> + char *linkdev = NULL;
> + virNetDevVlanPtr vlan;
> + virNetDevVPortProfilePtr virtPort;
> + int ret = -1;
> + int vf = -1;
> + int vlanid = -1;
> + int port_profile_associate = 1;
> + int isvf;
> +
> + isvf = virHostdevIsVirtualFunction(hostdev);
> + if (isvf <= 0) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("Interface type hostdev is currently supported on
> "
> + "SR-IOV Virtual Functions only"));
> + return ret;
> + }
> +
> + if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0)
> + return ret;
> +
> + vlan = virDomainNetGetActualVlan(hostdev->parent.data.net);
> + virtPort =
> virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net);
> + if (virtPort) {
> + if (vlan) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("direct setting of the vlan tag is not allowed
> "
> + "for hostdev devices using %s mode"),
> +
> virNetDevVPortTypeToString(virtPort->virtPortType));
> + goto cleanup;
> + }
> + ret = virHostdevNetConfigVirtPortProfile(linkdev, vf,
> + virtPort,
> + &hostdev->parent.data.net->mac,
> + uuid,
> + port_profile_associate);
> + } else {
> + /* Set only mac and vlan */
> + if (vlan) {
> + if (vlan->nTags != 1 || vlan->trunk) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("vlan trunking is not supported "
> + "by SR-IOV network devices"));
> + goto cleanup;
> + }
> + if (vf == -1) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("vlan can only be set for SR-IOV VFs, but "
> + "%s is not a VF"), linkdev);
> + goto cleanup;
> + }
> + vlanid = vlan->tag[0];
> + } else if (vf >= 0) {
> + vlanid = 0; /* assure any current vlan tag is reset */
> + }
> +
> + ret = virNetDevReplaceNetConfig(linkdev, vf,
> + &hostdev->parent.data.net->mac,
> + vlanid, stateDir);
> + }
> +cleanup:
> + VIR_FREE(linkdev);
> + return ret;
> +}
> +
> +/* oldStateDir:
> + * For upgrade, if there is an existing VM on QEMU, the hostdev netconfig
> file
> + * is previously stored in /var/run/libvirt/qemu. With new version, it
> tries to
> + * find in hostdevManager->stateDir location but certainly it cannot find it.
> + * In this case, we should find in the old state dir.
> + */
> +static int
> +virHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,
> + char *stateDir,
> + char *oldStateDir)
> +{
> + char *linkdev = NULL;
> + virNetDevVPortProfilePtr virtPort;
> + int ret = -1;
> + int vf = -1;
> + int port_profile_associate = 0;
> + int isvf;
> +
> + /* This is only needed for PCI devices that have been defined
> + * using <interface type='hostdev'>. For all others, it is a NOP.
> + */
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
> + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI ||
> + hostdev->parent.type != VIR_DOMAIN_DEVICE_NET ||
> + !hostdev->parent.data.net)
> + return 0;
> +
> + isvf = virHostdevIsVirtualFunction(hostdev);
> + if (isvf <= 0) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("Interface type hostdev is currently supported on
> "
> + "SR-IOV Virtual Functions only"));
> + return ret;
> + }
> +
> + if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0)
> + return ret;
> +
> + virtPort =
> virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net);
> + if (virtPort)
> + ret = virHostdevNetConfigVirtPortProfile(linkdev, vf, virtPort,
> + &hostdev->parent.data.net->mac,
> + NULL,
> port_profile_associate);
> + else {
> + ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir);
> + if (ret < 0 && oldStateDir != NULL)
> + ret = virNetDevRestoreNetConfig(linkdev, vf, oldStateDir);
> + }
> +
> + VIR_FREE(linkdev);
> +
> + return ret;
> +}
> +
> +static virPCIDeviceListPtr
> +virHostdevGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int
> nhostdevs)
> +{
> + virPCIDeviceListPtr list;
> + size_t i;
> +
> + if (!(list = virPCIDeviceListNew()))
> + return NULL;
> +
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + virPCIDevicePtr dev;
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> + continue;
> +
> + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,
> + hostdev->source.subsys.u.pci.addr.bus,
> + hostdev->source.subsys.u.pci.addr.slot,
> + hostdev->source.subsys.u.pci.addr.function);
> + if (!dev) {
> + virObjectUnref(list);
> + return NULL;
> + }
> +
> + if (virPCIDeviceListAdd(list, dev) < 0) {
> + virPCIDeviceFree(dev);
> + virObjectUnref(list);
> + return NULL;
> + }
> +
> + virPCIDeviceSetManaged(dev, hostdev->managed);
> +
> + switch (hostdev->source.subsys.u.pci.backend) {
> + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
> + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) {
> + virObjectUnref(list);
> + return NULL;
> + }
> + break;
> +
> + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM :
> + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
> + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) {
> + virObjectUnref(list);
> + return NULL;
> + }
> + break;
> + }
> + }
> +
> + return list;
> +}
> +
> +bool
> +virHostdevHostSupportsPassthroughVFIO(void)
> +{
> + DIR *iommuDir = NULL;
> + struct dirent *iommuGroup = NULL;
> + bool ret = false;
> +
> + /* condition 1 - /sys/kernel/iommu_groups/ contains entries */
> + if (!(iommuDir = opendir("/sys/kernel/iommu_groups/")))
> + goto cleanup;
> +
> + while ((iommuGroup = readdir(iommuDir))) {
> + /* skip ./ ../ */
> + if (STRPREFIX(iommuGroup->d_name, "."))
> + continue;
> +
> + /* assume we found a group */
> + break;
> + }
> +
> + if (!iommuGroup)
> + goto cleanup;
> + /* okay, iommu is on and recognizes groups */
> +
> + /* condition 2 - /dev/vfio/vfio exists */
> + if (!virFileExists("/dev/vfio/vfio"))
> + goto cleanup;
> +
> + ret = true;
> +
> +cleanup:
> + if (iommuDir)
> + closedir(iommuDir);
> +
> + return ret;
> +}
> +
> +
> +#if HAVE_LINUX_KVM_H
> +# include <linux/kvm.h>
> +bool
> +virHostdevHostSupportsPassthroughKVM(void)
> +{
> + int kvmfd = -1;
> + bool ret = false;
> +
> + if ((kvmfd = open("/dev/kvm", O_RDONLY)) < 0)
> + goto cleanup;
> +
> +# ifdef KVM_CAP_IOMMU
> + if ((ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_IOMMU)) < 0)
Like the original code in qemu_hostdev.c, this needs to be <= 0. Otherwise, if intel_iommu=off, the ioctl returns 0, the function returns true, and we attempt to start a domain that is destined to fail.
I've been doing a fair bit of testing of this series and with the exception of the above issue, haven't noticed any problems thus far. Unfortunately, you will need to rebase the series and post a v9, but I'm confident we can get this pushed after the 1.2.1 freeze. Thanks!
Regards,
Jim
> + goto cleanup;
> +
> + ret = true;
> +# endif
> +
> +cleanup:
> + VIR_FORCE_CLOSE(kvmfd);
> +
> + return ret;
> +}
> +#else
> +bool
> +virHostdevHostSupportsPassthroughKVM(void)
> +{
> + return false;
> +}
> +#endif
> +
> +static bool
> +virHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs,
> + size_t nhostdevs)
> +{
> + bool supportsPassthroughVFIO = virHostdevHostSupportsPassthroughVFIO();
> + bool supportsPassthroughKVM = virHostdevHostSupportsPassthroughKVM();
> + size_t i;
> +
> + /* assign defaults for hostdev passthrough */
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + int *backend = &hostdev->source.subsys.u.pci.backend;
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> + continue;
> +
> + switch ((virDomainHostdevSubsysPciBackendType) *backend) {
> + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
> + if (!supportsPassthroughVFIO) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("host doesn't support VFIO PCI
> passthrough"));
> + return false;
> + }
> + break;
> +
> + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
> + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM:
> + if (!supportsPassthroughKVM) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("host doesn't support legacy PCI
> passthrough"));
> + return false;
> + }
> +
> + break;
> +
> + default:
> + break;
> + }
> + }
> +
> + return true;
> +}
> +int
> +virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + const unsigned char *uuid,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + unsigned int flags)
> +{
> + virPCIDeviceListPtr pcidevs = NULL;
> + int last_processed_hostdev_vf = -1;
> + size_t i;
> + int ret = -1;
> +
> + if (!nhostdevs)
> + return 0;
> + if (mgr == NULL)
> + return -1;
> +
> + virObjectLock(mgr->activePciHostdevs);
> + virObjectLock(mgr->inactivePciHostdevs);
> +
> + if (!virHostdevPCICheckSupport(hostdevs, nhostdevs))
> + goto cleanup;
> +
> + if (!(pcidevs = virHostdevGetPciHostDeviceList(hostdevs, nhostdevs)))
> + goto cleanup;
> +
> + /* We have to use 9 loops here. *All* devices must
> + * be detached before we reset any of them, because
> + * in some cases you have to reset the whole PCI,
> + * which impacts all devices on it. Also, all devices
> + * must be reset before being marked as active.
> + */
> +
> + /* Loop 1: validate that non-managed device isn't in use, eg
> + * by checking that device is either un-bound, or bound
> + * to stub driver
> + */
> +
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
> + virPCIDevicePtr other;
> + bool strict_acs_check = !!(flags & VIR_STRICT_ACS_CHECK);
> +
> + if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) {
> + virReportError(VIR_ERR_OPERATION_INVALID,
> + _("PCI device %s is not assignable"),
> + virPCIDeviceGetName(dev));
> + goto cleanup;
> + }
> + /* The device is in use by other active domain if
> + * the dev is in list activePciHostdevs.
> + */
> + if ((other = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) {
> + const char *other_drvname;
> + const char *other_domname;
> +
> + virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname);
> + if (other_drvname && other_domname)
> + virReportError(VIR_ERR_OPERATION_INVALID,
> + _("PCI device %s is in use by driver
> %s,domain %s"),
> + virPCIDeviceGetName(dev), other_drvname,
> + other_domname);
> + else
> + virReportError(VIR_ERR_OPERATION_INVALID,
> + _("PCI device %s is already in use"),
> + virPCIDeviceGetName(dev));
> + goto cleanup;
> + }
> + }
> +
> + /* Loop 2: detach managed devices */
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
> + if (virPCIDeviceGetManaged(dev) &&
> + virPCIDeviceDetach(dev, mgr->activePciHostdevs, NULL) < 0)
> + goto reattachdevs;
> + }
> +
> + /* Loop 3: Now that all the PCI hostdevs have been detached, we
> + * can safely reset them */
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
> + if (virPCIDeviceReset(dev, mgr->activePciHostdevs,
> + mgr->inactivePciHostdevs) < 0)
> + goto reattachdevs;
> + }
> +
> + /* Loop 4: For SRIOV network devices, Now that we have detached the
> + * the network device, set the netdev config */
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> + continue;
> + if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET &&
> + hostdev->parent.data.net) {
> + if (virHostdevNetConfigReplace(hostdev, uuid, mgr->stateDir) < 0)
> + goto resetvfnetconfig;
> + }
> + last_processed_hostdev_vf = i;
> + }
> +
> + /* Loop 5: Now mark all the devices as active */
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
> + if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0)
> + goto inactivedevs;
> + }
> +
> + /* Loop 6: Now remove the devices from inactive list. */
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
> + virPCIDeviceListDel(mgr->inactivePciHostdevs, dev);
> + }
> +
> + /* Loop 7: Now set the used_by_domain of the device in
> + * driver->activePciHostdevs as domain name.
> + */
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
> + virPCIDevicePtr dev, activeDev;
> +
> + dev = virPCIDeviceListGet(pcidevs, i);
> + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev);
> +
> + if (activeDev)
> + virPCIDeviceSetUsedBy(activeDev, drv_name, dom_name);
> + }
> +
> + /* Loop 8: Now set the original states for hostdev def */
> + for (i = 0; i < nhostdevs; i++) {
> + virPCIDevicePtr dev;
> + virPCIDevicePtr pcidev;
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> + continue;
> +
> + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,
> + hostdev->source.subsys.u.pci.addr.bus,
> + hostdev->source.subsys.u.pci.addr.slot,
> + hostdev->source.subsys.u.pci.addr.function);
> +
> + /* original states "unbind_from_stub", "remove_slot",
> + * "reprobe" were already set by pciDettachDevice in
> + * loop 2.
> + */
> + if ((pcidev = virPCIDeviceListFind(pcidevs, dev))) {
> + hostdev->origstates.states.pci.unbind_from_stub =
> + virPCIDeviceGetUnbindFromStub(pcidev);
> + hostdev->origstates.states.pci.remove_slot =
> + virPCIDeviceGetRemoveSlot(pcidev);
> + hostdev->origstates.states.pci.reprobe =
> + virPCIDeviceGetReprobe(pcidev);
> + }
> +
> + virPCIDeviceFree(dev);
> + }
> +
> + /* Loop 9: Now steal all the devices from pcidevs */
> + while (virPCIDeviceListCount(pcidevs) > 0)
> + virPCIDeviceListStealIndex(pcidevs, 0);
> +
> + ret = 0;
> + goto cleanup;
> +
> +inactivedevs:
> + /* Only steal all the devices from driver->activePciHostdevs. We will
> + * free them in virObjectUnref().
> + */
> + while (virPCIDeviceListCount(pcidevs) > 0) {
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, 0);
> + virPCIDeviceListSteal(mgr->activePciHostdevs, dev);
> + }
> +
> +resetvfnetconfig:
> + for (i = 0;
> + last_processed_hostdev_vf != -1 && i < last_processed_hostdev_vf; i++)
> + virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, NULL);
> +
> +reattachdevs:
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
> + virPCIDeviceReattach(dev, mgr->activePciHostdevs, NULL);
> + }
> +
> +cleanup:
> + virObjectUnlock(mgr->activePciHostdevs);
> + virObjectUnlock(mgr->inactivePciHostdevs);
> + virObjectUnref(pcidevs);
> + return ret;
> +}
> +
> +static int
> +virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev,
> + bool mandatory,
> + virUSBDevicePtr *usb)
> +{
> + unsigned vendor = hostdev->source.subsys.u.usb.vendor;
> + unsigned product = hostdev->source.subsys.u.usb.product;
> + unsigned bus = hostdev->source.subsys.u.usb.bus;
> + unsigned device = hostdev->source.subsys.u.usb.device;
> + bool autoAddress = hostdev->source.subsys.u.usb.autoAddress;
> + int rc;
> +
> + *usb = NULL;
> +
> + if (vendor && bus) {
> + rc = virUSBDeviceFind(vendor, product, bus, device,
> + NULL,
> + autoAddress ? false : mandatory,
> + usb);
> + if (rc < 0) {
> + return -1;
> + } else if (!autoAddress) {
> + goto out;
> + } else {
> + VIR_INFO("USB device %x:%x could not be found at previous"
> + " address (bus:%u device:%u)",
> + vendor, product, bus, device);
> + }
> + }
> +
> + /* When vendor is specified, its USB address is either unspecified or
> the
> + * device could not be found at the USB device where it had been
> + * automatically found before.
> + */
> + if (vendor) {
> + virUSBDeviceListPtr devs;
> +
> + rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory,
> &devs);
> + if (rc < 0)
> + return -1;
> +
> + if (rc == 1) {
> + *usb = virUSBDeviceListGet(devs, 0);
> + virUSBDeviceListSteal(devs, *usb);
> + }
> + virObjectUnref(devs);
> +
> + if (rc == 0) {
> + goto out;
> + } else if (rc > 1) {
> + if (autoAddress) {
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Multiple USB devices for %x:%x were
> found, "
> + "but none of them is at bus:%u
> device:%u"),
> + vendor, product, bus, device);
> + } else {
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Multiple USB devices for %x:%x, "
> + "use <address> to specify one"),
> + vendor, product);
> + }
> + return -1;
> + }
> +
> + hostdev->source.subsys.u.usb.bus = virUSBDeviceGetBus(*usb);
> + hostdev->source.subsys.u.usb.device = virUSBDeviceGetDevno(*usb);
> + hostdev->source.subsys.u.usb.autoAddress = true;
> +
> + if (autoAddress) {
> + VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved"
> + " from bus:%u device:%u)",
> + vendor, product,
> + hostdev->source.subsys.u.usb.bus,
> + hostdev->source.subsys.u.usb.device,
> + bus, device);
> + }
> + } else if (!vendor && bus) {
> + if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0)
> + return -1;
> + }
> +
> +out:
> + if (!*usb)
> + hostdev->missing = true;
> + return 0;
> +}
> +
> +static int
> +virHostdevMarkUsbHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virUSBDeviceListPtr list)
> +{
> + size_t i, j;
> + unsigned int count;
> + virUSBDevicePtr tmp;
> +
> + virObjectLock(mgr->activeUsbHostdevs);
> + count = virUSBDeviceListCount(list);
> +
> + for (i = 0; i < count; i++) {
> + virUSBDevicePtr usb = virUSBDeviceListGet(list, i);
> + if ((tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb))) {
> + const char *other_drvname;
> + const char *other_domname;
> +
> + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_domname);
> + if (other_drvname && other_domname)
> + virReportError(VIR_ERR_OPERATION_INVALID,
> + _("USB device %s is in use by "
> + "driver %s,domain %s"),
> + virUSBDeviceGetName(tmp),
> + other_drvname, other_domname);
> + else
> + virReportError(VIR_ERR_OPERATION_INVALID,
> + _("USB device %s is already in use"),
> + virUSBDeviceGetName(tmp));
> + goto error;
> + }
> +
> + virUSBDeviceSetUsedBy(usb, drv_name, dom_name);
> + VIR_DEBUG("Adding %03d.%03d driver= %s dom=%s to
> activeUsbHostdevs",
> + virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb),
> + drv_name, dom_name);
> + /*
> + * The caller is responsible to steal these usb devices
> + * from the virUSBDeviceList that passed in on success,
> + * perform rollback on failure.
> + */
> + if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0)
> + goto error;
> + }
> +
> + virObjectUnlock(mgr->activeUsbHostdevs);
> + return 0;
> +
> +error:
> + for (j = 0; j < i; j++) {
> + tmp = virUSBDeviceListGet(list, j);
> + virUSBDeviceListSteal(mgr->activeUsbHostdevs, tmp);
> + }
> + virObjectUnlock(mgr->activeUsbHostdevs);
> + return -1;
> +}
> +
> +int
> +virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + bool coldBoot)
> +{
> + size_t i;
> + int ret = -1;
> + virUSBDeviceListPtr list;
> + virUSBDevicePtr tmp;
> +
> + if (!nhostdevs)
> + return 0;
> + if (mgr == NULL)
> + return -1;
> +
> + /* To prevent situation where USB device is assigned to two domains
> + * we need to keep a list of currently assigned USB devices.
> + * This is done in several loops which cannot be joined into one
> + * big loop.
> + */
> + if (!(list = virUSBDeviceListNew()))
> + goto cleanup;
> +
> + /* Loop 1: build temporary list
> + */
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + bool required = true;
> + virUSBDevicePtr usb;
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
> + continue;
> +
> + if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL ||
> + (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE &&
> + !coldBoot))
> + required = false;
> +
> + if (virHostdevFindUSBDevice(hostdev, required, &usb) < 0)
> + goto cleanup;
> +
> + if (usb && virUSBDeviceListAdd(list, usb) < 0) {
> + virUSBDeviceFree(usb);
> + goto cleanup;
> + }
> + }
> +
> + /* Mark devices in temporary list as used by @name
> + * and add them do active list. However, if something goes
> + * wrong, perform rollback.
> + */
> + if (virHostdevMarkUsbHostdevs(mgr, drv_name, dom_name, list) < 0)
> + goto cleanup;
> +
> + /* Loop 2: Temporary list was successfully merged with
> + * active list, so steal all items to avoid freeing them
> + * in cleanup label.
> + */
> + while (virUSBDeviceListCount(list) > 0) {
> + tmp = virUSBDeviceListGet(list, 0);
> + virUSBDeviceListSteal(list, tmp);
> + }
> +
> + ret = 0;
> +
> +cleanup:
> + virObjectUnref(list);
> + return ret;
> +}
> +
> +int
> +virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs)
> +{
> + size_t i, j;
> + int count;
> + virSCSIDeviceListPtr list;
> + virSCSIDevicePtr tmp;
> +
> + if (!nhostdevs)
> + return 0;
> + if (mgr == NULL)
> + return -1;
> +
> + /* To prevent situation where SCSI device is assigned to two domains
> + * we need to keep a list of currently assigned SCSI devices.
> + */
> + if (!(list = virSCSIDeviceListNew()))
> + goto cleanup;
> +
> + /* Loop 1: build temporary list */
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + virSCSIDevicePtr scsi;
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
> + hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
> + continue;
> +
> + if (hostdev->managed) {
> + virReportError(VIR_ERR_XML_ERROR, "%s",
> + _("SCSI host device doesn't support managed
> mode"));
> + goto cleanup;
> + }
> +
> + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter,
> + hostdev->source.subsys.u.scsi.bus,
> + hostdev->source.subsys.u.scsi.target,
> + hostdev->source.subsys.u.scsi.unit,
> + hostdev->readonly)))
> + goto cleanup;
> +
> + if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) {
> + virSCSIDeviceFree(scsi);
> + goto cleanup;
> + }
> + }
> +
> + /* Loop 2: Mark devices in temporary list as used by
> + * and add them to driver list. However, if something goes
> + * wrong, perform rollback.
> + */
> + virObjectLock(mgr->activeScsiHostdevs);
> + count = virSCSIDeviceListCount(list);
> +
> + for (i = 0; i < count; i++) {
> + virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i);
> + if ((tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) {
> + const char *other_drvname;
> + const char *other_domname;
> +
> + virSCSIDeviceGetUsedBy(tmp, &other_drvname, &other_domname);
> + if (other_drvname && other_domname)
> + virReportError(VIR_ERR_OPERATION_INVALID,
> + _("SCSI device %s is in use by "
> + "driver %s domain %s"),
> + virSCSIDeviceGetName(tmp),
> + other_drvname, other_domname);
> + else
> + virReportError(VIR_ERR_OPERATION_INVALID,
> + _("SCSI device %s is already in use"),
> + virSCSIDeviceGetName(tmp));
> + goto error;
> + }
> +
> + virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name);
> + VIR_DEBUG("Adding %s to activeScsiHostdevs",
> virSCSIDeviceGetName(scsi));
> +
> + if (virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0)
> + goto error;
> + }
> +
> + virObjectUnlock(mgr->activeScsiHostdevs);
> +
> + /* Loop 3: Temporary list was successfully merged with
> + * driver list, so steal all items to avoid freeing them
> + * when freeing temporary list.
> + */
> + while (virSCSIDeviceListCount(list) > 0) {
> + tmp = virSCSIDeviceListGet(list, 0);
> + virSCSIDeviceListSteal(list, tmp);
> + }
> +
> + virObjectUnref(list);
> + return 0;
> +
> +error:
> + for (j = 0; j < i; j++) {
> + tmp = virSCSIDeviceListGet(list, j);
> + virSCSIDeviceListSteal(mgr->activeScsiHostdevs, tmp);
> + }
> + virObjectUnlock(mgr->activeScsiHostdevs);
> +cleanup:
> + virObjectUnref(list);
> + return -1;
> +}
> +
> +int
> +virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr,
> + const char *driver,
> + virDomainDefPtr def,
> + unsigned int flags)
> +{
> + if (!def->nhostdevs)
> + return 0;
> +
> + if (mgr == NULL)
> + return -1;
> +
> + if (flags & VIR_SP_PCI_HOSTDEV) {
> + if (virHostdevPreparePciHostdevs(mgr, driver,
> + def->name, def->uuid,
> + def->hostdevs,
> + def->nhostdevs,
> + flags) < 0)
> + return -1;
> + }
> +
> + if (flags & VIR_SP_USB_HOSTDEV) {
> + bool coldBoot = !!(flags & VIR_COLD_BOOT);
> + if (virHostdevPrepareUsbHostdevs(mgr, driver, def->name,
> + def->hostdevs, def->nhostdevs,
> + coldBoot) < 0)
> + return -1;
> + }
> +
> + if (flags & VIR_SP_SCSI_HOSTDEV) {
> + if (virHostdevPrepareScsiHostdevs(mgr, driver, def->name,
> + def->hostdevs, def->nhostdevs) < 0)
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Pre-condition: mgr->inactivePciHostdevs & mgr->activePciHostdevs
> + * are locked
> + */
> +static void
> +virHostdevReAttachPciDevice(virHostdevManagerPtr mgr, virPCIDevicePtr dev)
> +{
> + /* If the device is not managed and was attached to guest
> + * successfully, it must have been inactive.
> + */
> + if (!virPCIDeviceGetManaged(dev)) {
> + if (virPCIDeviceListAdd(mgr->inactivePciHostdevs, dev) < 0)
> + virPCIDeviceFree(dev);
> + return;
> + }
> +
> + /* Wait for device cleanup if it is qemu/kvm */
> + if (STREQ(virPCIDeviceGetStubDriver(dev), "pci-stub")) {
> + int retries = 100;
> + while (virPCIDeviceWaitForCleanup(dev, "kvm_assigned_device")
> + && retries) {
> + usleep(100*1000);
> + retries--;
> + }
> + }
> +
> + if (virPCIDeviceReattach(dev, mgr->activePciHostdevs,
> + mgr->inactivePciHostdevs) < 0) {
> + virErrorPtr err = virGetLastError();
> + VIR_ERROR(_("Failed to re-attach PCI device: %s"),
> + err ? err->message : _("unknown error"));
> + virResetError(err);
> + }
> + virPCIDeviceFree(dev);
> +}
> +
> +/*
> + * Pre-condition: mgr->activePciHostdevs is locked
> + */
> +static virPCIDeviceListPtr
> +virHostdevGetActivePciHostDeviceList(virHostdevManagerPtr mgr,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs)
> +{
> + virPCIDeviceListPtr list;
> + size_t i;
> +
> + if (!(list = virPCIDeviceListNew()))
> + return NULL;
> +
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + virPCIDevicePtr dev;
> + virPCIDevicePtr activeDev;
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> + continue;
> +
> + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,
> + hostdev->source.subsys.u.pci.addr.bus,
> + hostdev->source.subsys.u.pci.addr.slot,
> + hostdev->source.subsys.u.pci.addr.function);
> + if (!dev) {
> + virObjectUnref(list);
> + return NULL;
> + }
> +
> + if ((activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev)))
> {
> + if (virPCIDeviceListAdd(list, activeDev) < 0) {
> + virPCIDeviceFree(dev);
> + virObjectUnref(list);
> + return NULL;
> + }
> + }
> +
> + virPCIDeviceFree(dev);
> + }
> +
> + return list;
> +}
> +
> +int
> +virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + const char *drv_name,
> + const char *dom_name)
> +{
> + virDomainHostdevDefPtr hostdev = NULL;
> + virPCIDevicePtr dev = NULL;
> + size_t i;
> + int ret = -1;
> +
> + if (!nhostdevs)
> + return 0;
> +
> + if (!mgr)
> + return -1;
> +
> + virObjectLock(mgr->activePciHostdevs);
> + virObjectLock(mgr->inactivePciHostdevs);
> +
> + for (i = 0; i < nhostdevs; i++) {
> + hostdev = hostdevs[i];
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> + continue;
> +
> + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,
> + hostdev->source.subsys.u.pci.addr.bus,
> + hostdev->source.subsys.u.pci.addr.slot,
> + hostdev->source.subsys.u.pci.addr.function);
> +
> + if (!dev)
> + goto cleanup;
> +
> + virPCIDeviceSetManaged(dev, hostdev->managed);
> + switch (hostdev->source.subsys.u.pci.backend) {
> + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
> + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0)
> + goto cleanup;
> + break;
> +
> + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM :
> + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
> + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0)
> + goto cleanup;
> + break;
> + }
> + virPCIDeviceSetUsedBy(dev, drv_name, dom_name);
> +
> + /* Setup the original states for the PCI device */
> + virPCIDeviceSetUnbindFromStub(dev,
> hostdev->origstates.states.pci.unbind_from_stub);
> + virPCIDeviceSetRemoveSlot(dev,
> hostdev->origstates.states.pci.remove_slot);
> + virPCIDeviceSetReprobe(dev, hostdev->origstates.states.pci.reprobe);
> +
> + if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0)
> + goto cleanup;
> + dev = NULL;
> + }
> +
> + ret = 0;
> +cleanup:
> + virPCIDeviceFree(dev);
> + virObjectUnlock(mgr->activePciHostdevs);
> + virObjectUnlock(mgr->inactivePciHostdevs);
> + return ret;
> +}
> +
> +
> +int
> +virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + const char *drv_name,
> + const char *dom_name)
> +{
> + virDomainHostdevDefPtr hostdev = NULL;
> + size_t i;
> + int ret = -1;
> +
> + if (!nhostdevs)
> + return 0;
> +
> + if (!mgr)
> + return -1;
> +
> + virObjectLock(mgr->activeUsbHostdevs);
> + for (i = 0; i < nhostdevs; i++) {
> + virUSBDevicePtr usb = NULL;
> + hostdev = hostdevs[i];
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
> + continue;
> +
> + usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus,
> + hostdev->source.subsys.u.usb.device,
> + NULL);
> + if (!usb) {
> + VIR_WARN("Unable to reattach USB device %03d.%03d "
> + "on driver %s domain %s",
> + hostdev->source.subsys.u.usb.bus,
> + hostdev->source.subsys.u.usb.device,
> + drv_name, dom_name);
> + continue;
> + }
> +
> + virUSBDeviceSetUsedBy(usb, drv_name, dom_name);
> +
> + if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0) {
> + virUSBDeviceFree(usb);
> + goto cleanup;
> + }
> + }
> + ret = 0;
> +cleanup:
> + virObjectUnlock(mgr->activeUsbHostdevs);
> + return ret;
> +}
> +
> +int
> +virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + const char *drv_name,
> + const char *dom_name)
> +{
> + virDomainHostdevDefPtr hostdev = NULL;
> + size_t i;
> + int ret = -1;
> +
> + if (!nhostdevs)
> + return 0;
> +
> + if (!mgr)
> + return -1;
> +
> + virObjectLock(mgr->activeScsiHostdevs);
> + for (i = 0; i < nhostdevs; i++) {
> + virSCSIDevicePtr scsi = NULL;
> + hostdev = hostdevs[i];
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
> + hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
> + continue;
> +
> + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter,
> + hostdev->source.subsys.u.scsi.bus,
> + hostdev->source.subsys.u.scsi.target,
> + hostdev->source.subsys.u.scsi.unit,
> + hostdev->readonly)))
> + goto cleanup;
> +
> + virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name);
> +
> + if (virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0) {
> + virSCSIDeviceFree(scsi);
> + goto cleanup;
> + }
> + }
> + ret = 0;
> +
> +cleanup:
> + virObjectUnlock(mgr->activeScsiHostdevs);
> + return ret;
> +}
> +
> +int
> +virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr,
> + const char *driver,
> + virDomainDefPtr def,
> + unsigned int flags)
> +{
> + if (!def->nhostdevs)
> + return 0;
> +
> + if (flags & VIR_SP_PCI_HOSTDEV) {
> + if (virHostdevUpdateActivePciHostdevs(mgr,
> + def->hostdevs,
> + def->nhostdevs,
> + driver, def->name) < 0)
> + return -1;
> + }
> +
> + if (flags & VIR_SP_USB_HOSTDEV) {
> + if (virHostdevUpdateActiveUsbHostdevs(mgr,
> + def->hostdevs,
> + def->nhostdevs,
> + driver, def->name) < 0)
> + return -1;
> + }
> +
> + if (flags & VIR_SP_SCSI_HOSTDEV) {
> + if (virHostdevUpdateActiveScsiHostdevs(mgr,
> + def->hostdevs,
> + def->nhostdevs,
> + driver, def->name) < 0)
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +
> +void
> +virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs)
> +{
> + virPCIDeviceListPtr pcidevs;
> + size_t i;
> + char *oldStateDir = NULL;
> +
> + if (!nhostdevs || !mgr)
> + return;
> +
> + virObjectLock(mgr->activePciHostdevs);
> + virObjectLock(mgr->inactivePciHostdevs);
> +
> + /* for upgrade, if netconfig file is not found in mgr->stateDir,
> + * find in old state dir */
> + if (VIR_STRDUP(oldStateDir, QEMU_STATE_DIR) < 0)
> + goto cleanup;
> +
> + if (!(pcidevs = virHostdevGetActivePciHostDeviceList(mgr,
> + hostdevs,
> + nhostdevs))) {
> + virErrorPtr err = virGetLastError();
> + VIR_ERROR(_("Failed to allocate PCI device list: %s"),
> + err ? err->message : _("unknown error"));
> + virResetError(err);
> + goto cleanup;
> + }
> +
> + /* Again 4 loops; mark all devices as inactive before reset
> + * them and reset all the devices before re-attach.
> + * Attach mac and port profile parameters to devices
> + */
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
> + virPCIDevicePtr activeDev = NULL;
> + const char *usedby_drvname;
> + const char *usedby_domname;
> +
> + /* Never delete the dev from list driver->activePciHostdevs
> + * if it's used by other domain.
> + */
> + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev);
> + if (activeDev) {
> + virPCIDeviceGetUsedBy(activeDev, &usedby_drvname,
> &usedby_domname);
> + if (STRNEQ_NULLABLE(drv_name, usedby_drvname) ||
> + STRNEQ_NULLABLE(dom_name, usedby_domname)) {
> + virPCIDeviceListSteal(pcidevs, dev);
> + continue;
> + }
> + }
> +
> + virPCIDeviceListSteal(mgr->activePciHostdevs, dev);
> + }
> +
> + /*
> + * For SRIOV net host devices, unset mac and port profile before
> + * reset and reattach device
> + */
> + for (i = 0; i < nhostdevs; i++)
> + virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, oldStateDir);
> +
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
> + if (virPCIDeviceReset(dev, mgr->activePciHostdevs,
> + mgr->inactivePciHostdevs) < 0) {
> + virErrorPtr err = virGetLastError();
> + VIR_ERROR(_("Failed to reset PCI device: %s"),
> + err ? err->message : _("unknown error"));
> + virResetError(err);
> + }
> + }
> +
> + while (virPCIDeviceListCount(pcidevs) > 0) {
> + virPCIDevicePtr dev = virPCIDeviceListStealIndex(pcidevs, 0);
> + virHostdevReAttachPciDevice(mgr, dev);
> + }
> +
> + virObjectUnref(pcidevs);
> +cleanup:
> + VIR_FREE(oldStateDir);
> + virObjectUnlock(mgr->activePciHostdevs);
> + virObjectUnlock(mgr->inactivePciHostdevs);
> +}
> +
> +void
> +virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs)
> +{
> + size_t i;
> +
> + if (!nhostdevs || !mgr)
> + return;
> +
> + virObjectLock(mgr->activeUsbHostdevs);
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + virUSBDevicePtr usb, tmp;
> + const char *usedby_drvname;
> + const char *usedby_domname;
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
> + continue;
> + if (hostdev->missing)
> + continue;
> +
> + usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus,
> + hostdev->source.subsys.u.usb.device,
> + NULL);
> +
> + if (!usb) {
> + VIR_WARN("Unable to reattach USB device %03d.%03d "
> + "on driver %s domain %s",
> + hostdev->source.subsys.u.usb.bus,
> + hostdev->source.subsys.u.usb.device,
> + drv_name, dom_name);
> + continue;
> + }
> +
> + /* Delete only those USB devices which belongs
> + * to domain. Therefore we want to steal only
> + * those devices from the list which were taken
> + * by domain */
> +
> + tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb);
> + virUSBDeviceFree(usb);
> +
> + if (!tmp) {
> + VIR_WARN("Unable to find device %03d.%03d "
> + "in list of active USB devices",
> + hostdev->source.subsys.u.usb.bus,
> + hostdev->source.subsys.u.usb.device);
> + continue;
> + }
> +
> + virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname);
> + if (STREQ_NULLABLE(drv_name, usedby_drvname) &&
> + STREQ_NULLABLE(dom_name, usedby_domname)) {
> + VIR_DEBUG("Removing %03d.%03d dom=%s:%s",
> + hostdev->source.subsys.u.usb.bus,
> + hostdev->source.subsys.u.usb.device,
> + drv_name, dom_name);
> + virUSBDeviceListDel(mgr->activeUsbHostdevs, tmp);
> + }
> +
> + }
> +
> + virObjectUnlock(mgr->activeUsbHostdevs);
> +}
> +
> +void
> +virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs)
> +{
> + size_t i;
> +
> + if (!nhostdevs || !mgr)
> + return;
> +
> + virObjectLock(mgr->activeScsiHostdevs);
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + virSCSIDevicePtr scsi, tmp;
> + const char *usedby_drvname;
> + const char *usedby_domname;
> +
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
> + hostdev->source.subsys.type !=
> VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
> + continue;
> +
> + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter,
> + hostdev->source.subsys.u.scsi.bus,
> + hostdev->source.subsys.u.scsi.target,
> + hostdev->source.subsys.u.scsi.unit,
> + hostdev->readonly))) {
> + VIR_WARN("Unable to reattach SCSI device %s:%d:%d:%d on driver
> %s domain %s",
> + hostdev->source.subsys.u.scsi.adapter,
> + hostdev->source.subsys.u.scsi.bus,
> + hostdev->source.subsys.u.scsi.target,
> + hostdev->source.subsys.u.scsi.unit,
> + drv_name, dom_name);
> + continue;
> + }
> +
> + tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi);
> + virSCSIDeviceFree(scsi);
> +
> + if (!tmp) {
> + VIR_WARN("Unable to find device %s:%d:%d:%d "
> + "in list of active SCSI devices",
> + hostdev->source.subsys.u.scsi.adapter,
> + hostdev->source.subsys.u.scsi.bus,
> + hostdev->source.subsys.u.scsi.target,
> + hostdev->source.subsys.u.scsi.unit);
> + continue;
> + }
> +
> + virSCSIDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname);
> + if (STREQ_NULLABLE(usedby_drvname, drv_name) &&
> + STREQ_NULLABLE(usedby_domname, dom_name)) {
> + VIR_DEBUG("Removing %s:%d:%d:%d driver:%s dom:%s from
> activeScsiHostdevs",
> + hostdev->source.subsys.u.scsi.adapter,
> + hostdev->source.subsys.u.scsi.bus,
> + hostdev->source.subsys.u.scsi.target,
> + hostdev->source.subsys.u.scsi.unit,
> + drv_name, dom_name);
> + virSCSIDeviceListDel(mgr->activeScsiHostdevs, tmp);
> + }
> + }
> +
> + virObjectUnlock(mgr->activeScsiHostdevs);
> +}
> +
> +void
> +virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr,
> + const char *driver,
> + virDomainDefPtr def,
> + unsigned int flags)
> +{
> + if (!def->nhostdevs || !mgr)
> + return;
> +
> + if (flags & VIR_SP_PCI_HOSTDEV) {
> + virHostdevReAttachPciHostdevs(mgr, driver, def->name,
> + def->hostdevs, def->nhostdevs);
> + }
> +
> + if (flags & VIR_SP_USB_HOSTDEV) {
> + virHostdevReAttachUsbHostdevs(mgr, driver, def->name,
> + def->hostdevs, def->nhostdevs);
> + }
> +
> + if (flags & VIR_SP_SCSI_HOSTDEV) {
> + virHostdevReAttachScsiHostdevs(mgr, driver, def->name,
> + def->hostdevs, def->nhostdevs);
> + }
> +}
> +
> +/* following functions are used by NodeDevDetach/Reattach/Reset */
> +int
> +virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr,
> + virPCIDevicePtr pci)
> +{
> + int ret;
> +
> + if (!mgr || !pci)
> + return -1;
> +
> + virObjectLock(mgr->activePciHostdevs);
> + virObjectLock(mgr->inactivePciHostdevs);
> + if (virPCIDeviceDetach(pci, mgr->activePciHostdevs,
> + mgr->inactivePciHostdevs) < 0)
> + ret = -1;
> + else
> + ret = 0;
> +
> + virObjectUnlock(mgr->inactivePciHostdevs);
> + virObjectUnlock(mgr->activePciHostdevs);
> + return ret;
> +}
> +
> +int
> +virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr,
> + virPCIDevicePtr pci)
> +{
> + int ret = -1;
> + virPCIDevicePtr other;
> +
> + if (!mgr || !pci)
> + return -1;
> +
> + virObjectLock(mgr->activePciHostdevs);
> + virObjectLock(mgr->inactivePciHostdevs);
> + other = virPCIDeviceListFind(mgr->activePciHostdevs, pci);
> + if (other) {
> + const char *other_drvname;
> + const char *other_domname;
> +
> + virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname);
> + if (other_drvname && other_domname)
> + virReportError(VIR_ERR_OPERATION_INVALID,
> + _("PCI device %s is still in use by driver %s,
> domain %s"),
> + virPCIDeviceGetName(pci), other_drvname,
> other_domname);
> + else
> + virReportError(VIR_ERR_OPERATION_INVALID,
> + _("PCI device %s is still in use"),
> + virPCIDeviceGetName(pci));
> + goto out;
> + }
> +
> + virPCIDeviceReattachInit(pci);
> +
> + if (virPCIDeviceReattach(pci, mgr->activePciHostdevs,
> + mgr->inactivePciHostdevs) < 0)
> + goto out;
> +
> + ret = 0;
> +out:
> + virObjectUnlock(mgr->inactivePciHostdevs);
> + virObjectUnlock(mgr->activePciHostdevs);
> + return ret;
> +}
> +
> +int
> +virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr,
> + virPCIDevicePtr pci)
> +{
> + int ret;
> +
> + if (!mgr || !pci)
> + return -1;
> +
> + virObjectLock(mgr->activePciHostdevs);
> + virObjectLock(mgr->inactivePciHostdevs);
> + if (virPCIDeviceReset(pci, mgr->activePciHostdevs,
> + mgr->inactivePciHostdevs) < 0)
> + ret = -1;
> + else
> + ret = 0;
> +
> + virObjectUnlock(mgr->inactivePciHostdevs);
> + virObjectUnlock(mgr->activePciHostdevs);
> + return ret;
> +}
> diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h
> new file mode 100644
> index 0000000..fc69540
> --- /dev/null
> +++ b/src/util/virhostdev.h
> @@ -0,0 +1,134 @@
> +/* virhostdev.h: hostdev management
> + *
> + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
> + * Copyright (C) 2006-2007, 2009-2013 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library. If not, see
> + * <http://www.gnu.org/licenses/>.
> + *
> + * Author: Chunyan Liu <cyliu(a)suse.com>
> + * Author: Daniel P. Berrange <berrange(a)redhat.com>
> + */
> +
> +#ifndef __VIR_HOSTDEV_H__
> +# define __VIR_HOSTDEV_H__
> +
> +# include "internal.h"
> +
> +# include "domain_conf.h"
> +# include "virpci.h"
> +# include "virusb.h"
> +# include "virscsi.h"
> +
> +typedef enum {
> + VIR_SP_PCI_HOSTDEV = (1 << 0), /* support pci passthrough */
> + VIR_SP_USB_HOSTDEV = (1 << 1), /* support usb passthrough */
> + VIR_SP_SCSI_HOSTDEV = (1 << 2), /* support scsi passthrough */
> +
> + VIR_COLD_BOOT = (1 << 8), /* cold boot */
> + VIR_STRICT_ACS_CHECK = (1 << 9), /* strict acs check */
> +} virHostdevManagerFlag;
> +
> +typedef struct _virHostdevManager virHostdevManager;
> +typedef virHostdevManager *virHostdevManagerPtr;
> +struct _virHostdevManager{
> + char *stateDir;
> +
> + virPCIDeviceListPtr activePciHostdevs;
> + virPCIDeviceListPtr inactivePciHostdevs;
> + virUSBDeviceListPtr activeUsbHostdevs;
> + virSCSIDeviceListPtr activeScsiHostdevs;
> +};
> +
> +virHostdevManagerPtr virHostdevManagerGetDefault(void);
> +
> +bool virHostdevHostSupportsPassthroughVFIO(void);
> +bool virHostdevHostSupportsPassthroughKVM(void);
> +
> +/* functions used to prepare/unprepare hostdevs for domain */
> +int virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr,
> + const char *driver,
> + virDomainDefPtr def,
> + unsigned int flags);
> +void virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr,
> + const char *driver,
> + virDomainDefPtr def,
> + unsigned int flags);
> +int virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + const unsigned char *uuid,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + unsigned int flags);
> +int virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + bool coldBoot);
> +int virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs);
> +void virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs);
> +void virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs);
> +void virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr,
> + const char *drv_name,
> + const char *dom_name,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs);
> +int
> +virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + const char *drv_name,
> + const char *dom_name);
> +int
> +virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + const char *drv_name,
> + const char *dom_name);
> +int
> +virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr,
> + virDomainHostdevDefPtr *hostdevs,
> + int nhostdevs,
> + const char *drv_name,
> + const char *dom_name);
> +int
> +virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr,
> + const char *driver,
> + virDomainDefPtr def,
> + unsigned int flags);
> +
> +/* functions used by NodeDevDetach/Reattach/Reset */
> +int virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr,
> + virPCIDevicePtr pci);
> +int virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr,
> + virPCIDevicePtr pci);
> +int virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr,
> + virPCIDevicePtr pci);
> +
> +#endif /* __VIR_HOSTDEV_H__ */
> diff --git a/src/util/virpci.c b/src/util/virpci.c
> index 8ec642f..7402898 100644
> --- a/src/util/virpci.c
> +++ b/src/util/virpci.c
> @@ -58,7 +58,10 @@ struct _virPCIDevice {
> char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */
> char id[PCI_ID_LEN]; /* product vendor */
> char *path;
> - const char *used_by; /* The domain which uses the device */
> +
> + /* The driver:domain which uses the device */
> + char *used_by_drvname;
> + char *used_by_domname;
>
> unsigned int pcie_cap_pos;
> unsigned int pci_pm_cap_pos;
> @@ -1562,6 +1565,8 @@ virPCIDeviceFree(virPCIDevicePtr dev)
> VIR_DEBUG("%s %s: freeing", dev->id, dev->name);
> VIR_FREE(dev->path);
> VIR_FREE(dev->stubDriver);
> + VIR_FREE(dev->used_by_drvname);
> + VIR_FREE(dev->used_by_domname);
> VIR_FREE(dev);
> }
>
> @@ -1631,16 +1636,27 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool
> reprobe)
> dev->reprobe = reprobe;
> }
>
> -void
> -virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name)
> +int
> +virPCIDeviceSetUsedBy(virPCIDevicePtr dev,
> + const char *drv_name,
> + const char *dom_name)
> {
> - dev->used_by = name;
> + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0)
> + return -1;
> +
> + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0)
> + return -1;
> +
> + return 0;
> }
>
> -const char *
> -virPCIDeviceGetUsedBy(virPCIDevicePtr dev)
> +void
> +virPCIDeviceGetUsedBy(virPCIDevicePtr dev,
> + const char **drv_name,
> + const char **dom_name)
> {
> - return dev->used_by;
> + *drv_name = dev->used_by_drvname;
> + *dom_name = dev->used_by_domname;
> }
>
> void virPCIDeviceReattachInit(virPCIDevicePtr pci)
> diff --git a/src/util/virpci.h b/src/util/virpci.h
> index 0479f0b..2347b95 100644
> --- a/src/util/virpci.h
> +++ b/src/util/virpci.h
> @@ -65,9 +65,12 @@ unsigned int virPCIDeviceGetManaged(virPCIDevice *dev);
> int virPCIDeviceSetStubDriver(virPCIDevicePtr dev,
> const char *driver);
> const char *virPCIDeviceGetStubDriver(virPCIDevicePtr dev);
> -void virPCIDeviceSetUsedBy(virPCIDevice *dev,
> - const char *used_by);
> -const char *virPCIDeviceGetUsedBy(virPCIDevice *dev);
> +int virPCIDeviceSetUsedBy(virPCIDevice *dev,
> + const char *drv_name,
> + const char *dom_name);
> +void virPCIDeviceGetUsedBy(virPCIDevice *dev,
> + const char **drv_name,
> + const char **dom_name);
> unsigned int virPCIDeviceGetUnbindFromStub(virPCIDevicePtr dev);
> void virPCIDeviceSetUnbindFromStub(virPCIDevice *dev,
> bool unbind);
> diff --git a/src/util/virscsi.c b/src/util/virscsi.c
> index 7aca9e6..062e8bd 100644
> --- a/src/util/virscsi.c
> +++ b/src/util/virscsi.c
> @@ -55,7 +55,10 @@ struct _virSCSIDevice {
> char *name; /* adapter:bus:target:unit */
> char *id; /* model:vendor */
> char *sg_path; /* e.g. /dev/sg2 */
> - const char *used_by; /* name of the domain using this dev */
> +
> + /* driver:domain using this dev */
> + char *used_by_drvname;
> + char *used_by_domname;
>
> bool readonly;
> };
> @@ -259,20 +262,31 @@ virSCSIDeviceFree(virSCSIDevicePtr dev)
> VIR_FREE(dev->id);
> VIR_FREE(dev->name);
> VIR_FREE(dev->sg_path);
> + VIR_FREE(dev->used_by_drvname);
> + VIR_FREE(dev->used_by_domname);
> VIR_FREE(dev);
> }
>
> -void
> +int
> virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev,
> - const char *name)
> + const char *drvname,
> + const char *domname)
> {
> - dev->used_by = name;
> + if (VIR_STRDUP(dev->used_by_drvname, drvname) < 0)
> + return -1;
> + if (VIR_STRDUP(dev->used_by_domname, domname) < 0)
> + return -1;
> +
> + return 0;
> }
>
> -const char *
> -virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev)
> +void
> +virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev,
> + const char **drvname,
> + const char **domname)
> {
> - return dev->used_by;
> + *drvname = dev->used_by_drvname;
> + *domname = dev->used_by_domname;
> }
>
> const char *
> diff --git a/src/util/virscsi.h b/src/util/virscsi.h
> index cce5df4..263b175 100644
> --- a/src/util/virscsi.h
> +++ b/src/util/virscsi.h
> @@ -49,8 +49,12 @@ virSCSIDevicePtr virSCSIDeviceNew(const char *adapter,
> bool readonly);
>
> void virSCSIDeviceFree(virSCSIDevicePtr dev);
> -void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, const char *name);
> -const char *virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev);
> +int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev,
> + const char *drvname,
> + const char *domname);
> +void virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev,
> + const char **drvname,
> + const char **domname);
> const char *virSCSIDeviceGetName(virSCSIDevicePtr dev);
> unsigned int virSCSIDeviceGetAdapter(virSCSIDevicePtr dev);
> unsigned int virSCSIDeviceGetBus(virSCSIDevicePtr dev);
> diff --git a/src/util/virusb.c b/src/util/virusb.c
> index 3c82200..7b17b0a 100644
> --- a/src/util/virusb.c
> +++ b/src/util/virusb.c
> @@ -55,7 +55,10 @@ struct _virUSBDevice {
> char name[USB_ADDR_LEN]; /* domain:bus:slot.function */
> char id[USB_ID_LEN]; /* product vendor */
> char *path;
> - const char *used_by; /* name of the domain using this dev
> */
> +
> + /* driver:domain using this dev */
> + char *used_by_drvname;
> + char *used_by_domname;
> };
>
> struct _virUSBDeviceList {
> @@ -375,19 +378,31 @@ virUSBDeviceFree(virUSBDevicePtr dev)
> return;
> VIR_DEBUG("%s %s: freeing", dev->id, dev->name);
> VIR_FREE(dev->path);
> + VIR_FREE(dev->used_by_drvname);
> + VIR_FREE(dev->used_by_domname);
> VIR_FREE(dev);
> }
>
> -
> -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev,
> - const char *name)
> +int
> +virUSBDeviceSetUsedBy(virUSBDevicePtr dev,
> + const char *drv_name,
> + const char *dom_name)
> {
> - dev->used_by = name;
> + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0)
> + return -1;
> + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0)
> + return -1;
> +
> + return 0;
> }
>
> -const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev)
> +void
> +virUSBDeviceGetUsedBy(virUSBDevicePtr dev,
> + const char **drv_name,
> + const char **dom_name)
> {
> - return dev->used_by;
> + *drv_name = dev->used_by_drvname;
> + *dom_name = dev->used_by_domname;
> }
>
> const char *virUSBDeviceGetName(virUSBDevicePtr dev)
> diff --git a/src/util/virusb.h b/src/util/virusb.h
> index aa59d12..41e680f 100644
> --- a/src/util/virusb.h
> +++ b/src/util/virusb.h
> @@ -60,8 +60,12 @@ int virUSBDeviceFind(unsigned int vendor,
> virUSBDevicePtr *usb);
>
> void virUSBDeviceFree(virUSBDevicePtr dev);
> -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, const char *name);
> -const char *virUSBDeviceGetUsedBy(virUSBDevicePtr dev);
> +int virUSBDeviceSetUsedBy(virUSBDevicePtr dev,
> + const char *drv_name,
> + const char *dom_name);
> +void virUSBDeviceGetUsedBy(virUSBDevicePtr dev,
> + const char **drv_name,
> + const char **dom_name);
> const char *virUSBDeviceGetName(virUSBDevicePtr dev);
>
> unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev);
10 years, 11 months
[libvirt] [PATCHv2] test driver: Add authentication to test driver.
by Richard W.M. Jones
There is no easy way to test authentication against libvirt. This
commit modifies the test driver to allow simple username/password
authentication.
You modify the test XML by adding:
<node>
...
<auth>
<user password="123456">rich</user>
<user>jane</user>
</auth>
</node>
If there are any /node/auth/user elements, then authentication is
required by the test driver (if none are present, then the test driver
will work as before and not require authentication).
In the example above, two phony users are added:
rich password: 123456
jane no password required
The test driver will demand a username. If the password attribute is
present (or if the username entered is wrong), then the password is
also asked for and checked:
$ virsh -c test://$(pwd)/testnode.xml list
Enter username for localhost: rich
Enter rich's password for localhost: ***
Id Name State
----------------------------------------------------
1 fv0 running
2 fc4 running
Signed-off-by: Richard W.M. Jones <rjones(a)redhat.com>
---
src/test/test_driver.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 110 insertions(+), 1 deletion(-)
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
index cde82a1..b724f82 100644
--- a/src/test/test_driver.c
+++ b/src/test/test_driver.c
@@ -58,6 +58,7 @@
#include "virrandom.h"
#include "virstring.h"
#include "cpu/cpu.h"
+#include "virauth.h"
#define VIR_FROM_THIS VIR_FROM_TEST
@@ -82,6 +83,13 @@ typedef struct _testCell *testCellPtr;
#define MAX_CELLS 128
+struct _testAuth {
+ char *username;
+ char *password;
+};
+typedef struct _testAuth testAuth;
+typedef struct _testAuth *testAuthPtr;
+
struct _testConn {
virMutex lock;
@@ -99,6 +107,8 @@ struct _testConn {
virNodeDeviceObjList devs;
int numCells;
testCell cells[MAX_CELLS];
+ size_t numAuths;
+ testAuthPtr auths;
virObjectEventStatePtr eventState;
};
@@ -1355,6 +1365,45 @@ error:
return ret;
}
+static int
+testParseAuthUsers(testConnPtr privconn,
+ xmlXPathContextPtr ctxt)
+{
+ int num, ret = -1;
+ size_t i;
+ xmlNodePtr *nodes = NULL;
+
+ num = virXPathNodeSet("/node/auth/user", ctxt, &nodes);
+ if (num < 0)
+ goto error;
+
+ privconn->numAuths = num;
+ if (num && VIR_ALLOC_N(privconn->auths, num) < 0)
+ goto error;
+
+ for (i = 0; i < num; i++) {
+ char *username, *password;
+
+ ctxt->node = nodes[i];
+ username = virXPathString("string(.)", ctxt);
+ if (!username || STREQ(username, "")) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing username in /node/auth/user field"));
+ VIR_FREE(username);
+ goto error;
+ }
+ /* This field is optional. */
+ password = virXMLPropString(nodes[i], "password");
+
+ privconn->auths[i].username = username;
+ privconn->auths[i].password = password;
+ }
+
+ ret = 0;
+error:
+ VIR_FREE(nodes);
+ return ret;
+}
/* No shared state between simultaneous test connections initialized
* from a file. */
@@ -1417,6 +1466,8 @@ testOpenFromFile(virConnectPtr conn, const char *file)
goto error;
if (testParseNodedevs(privconn, file, ctxt) < 0)
goto error;
+ if (testParseAuthUsers(privconn, ctxt) < 0)
+ goto error;
xmlXPathFreeContext(ctxt);
xmlFreeDoc(doc);
@@ -1439,9 +1490,63 @@ testOpenFromFile(virConnectPtr conn, const char *file)
return VIR_DRV_OPEN_ERROR;
}
+static int
+testConnectAuthenticate(virConnectPtr conn,
+ virConnectAuthPtr auth)
+{
+ testConnPtr privconn = conn->privateData;
+ int ret = -1;
+ ssize_t i;
+ char *username = NULL, *password = NULL;
+
+ if (privconn->numAuths == 0)
+ return 0;
+
+ /* Authentication is required because the test XML contains a
+ * non-empty <auth/> section. First we must ask for a username.
+ */
+ username = virAuthGetUsername(conn, auth, "test", NULL, "localhost"/*?*/);
+ if (!username) {
+ virReportError(VIR_ERR_AUTH_FAILED, "%s",
+ _("authentication failed when asking for username"));
+ goto cleanup;
+ }
+
+ /* Does the username exist? */
+ for (i = 0; i < privconn->numAuths; ++i) {
+ if (STREQ(privconn->auths[i].username, username))
+ goto found_user;
+ }
+ i = -1;
+
+found_user:
+ /* Even if we didn't find the user, we still ask for a password. */
+ if (i == -1 || privconn->auths[i].password != NULL) {
+ password = virAuthGetPassword(conn, auth, "test",
+ username, "localhost");
+ if (password == NULL) {
+ virReportError(VIR_ERR_AUTH_FAILED, "%s",
+ _("authentication failed when asking for password"));
+ goto cleanup;
+ }
+ }
+
+ if (i == -1 ||
+ (password && STRNEQ(privconn->auths[i].password, password))) {
+ virReportError(VIR_ERR_AUTH_FAILED, "%s",
+ _("authentication failed, see test XML for the correct username/password"));
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ VIR_FREE(username);
+ VIR_FREE(password);
+ return ret;
+}
static virDrvOpenStatus testConnectOpen(virConnectPtr conn,
- virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+ virConnectAuthPtr auth,
unsigned int flags)
{
int ret;
@@ -1479,6 +1584,10 @@ static virDrvOpenStatus testConnectOpen(virConnectPtr conn,
if (ret != VIR_DRV_OPEN_SUCCESS)
return ret;
+ /* Fake authentication. */
+ if (testConnectAuthenticate(conn, auth) < 0)
+ return VIR_DRV_OPEN_ERROR;
+
return VIR_DRV_OPEN_SUCCESS;
}
--
1.8.4.2
10 years, 11 months
[libvirt] [PATCH] event: clean up client side RPC code
by Eric Blake
Commit cfd62c1 was incomplete; I found more cases where error
messages were being overwritten, and where the code between
the three registration/deregistration APIs was not consistent.
Since it is fairly easy to trigger an attempt to register an
unregistered object, I also changed the error message from
VIR_ERR_INTERNAL_ERROR to VIR_ERR_INVALID_ARG.
* src/conf/object_event.c (virObjectEventCallbackListEventID):
Inline...
(virObjectEventStateEventID): ...into lone caller, and report
error on failure.
(virObjectEventCallbackListAddID, virObjectEventStateCallbackID)
(virObjectEventCallbackListRemoveID)
(virObjectEventCallbackListMarkDeleteID): Tweak error category.
* src/remote/remote_driver.c (remoteConnectDomainEventRegister):
Don't leak registration on failure.
(remoteConnectDomainEventDeregisterAny)
(remoteConnectNetworkEventDeregisterAny): Don't overwrite error.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/conf/object_event.c | 57 ++++++++++++++++++++++------------------------
src/remote/remote_driver.c | 56 ++++++++++++++++++++++-----------------------
2 files changed, 55 insertions(+), 58 deletions(-)
diff --git a/src/conf/object_event.c b/src/conf/object_event.c
index b01ffe5..b69387a 100644
--- a/src/conf/object_event.c
+++ b/src/conf/object_event.c
@@ -210,8 +210,9 @@ virObjectEventCallbackListRemoveID(virConnectPtr conn,
}
}
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("could not find event callback for removal"));
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("could not find event callback %d for deletion"),
+ callbackID);
return -1;
}
@@ -233,8 +234,9 @@ virObjectEventCallbackListMarkDeleteID(virConnectPtr conn,
}
}
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("could not find event callback for deletion"));
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("could not find event callback %d for deletion"),
+ callbackID);
return -1;
}
@@ -357,7 +359,7 @@ virObjectEventCallbackListAddID(virConnectPtr conn,
if (virObjectEventCallbackLookup(conn, cbList, uuid,
klass, eventID, callback,
!callbackID) != -1) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
_("event callback already tracked"));
return -1;
}
@@ -398,27 +400,6 @@ cleanup:
}
-static int
-virObjectEventCallbackListEventID(virConnectPtr conn,
- virObjectEventCallbackListPtr cbList,
- int callbackID)
-{
- size_t i;
-
- for (i = 0; i < cbList->count; i++) {
- virObjectEventCallbackPtr cb = cbList->callbacks[i];
-
- if (cb->deleted)
- continue;
-
- if (cb->callbackID == callbackID && cb->conn == conn)
- return cb->eventID;
- }
-
- return -1;
-}
-
-
/**
* virObjectEventQueueClear:
* @queue: pointer to the queue
@@ -897,7 +878,7 @@ virObjectEventStateCallbackID(virConnectPtr conn,
virObjectEventStateUnlock(state);
if (ret < 0)
- virReportError(VIR_ERR_INTERNAL_ERROR,
+ virReportError(VIR_ERR_INVALID_ARG,
_("event callback function %p not registered"),
callback);
return ret;
@@ -920,11 +901,27 @@ virObjectEventStateEventID(virConnectPtr conn,
virObjectEventStatePtr state,
int callbackID)
{
- int ret;
+ int ret = -1;
+ size_t i;
+ virObjectEventCallbackListPtr cbList = state->callbacks;
virObjectEventStateLock(state);
- ret = virObjectEventCallbackListEventID(conn,
- state->callbacks, callbackID);
+ for (i = 0; i < cbList->count; i++) {
+ virObjectEventCallbackPtr cb = cbList->callbacks[i];
+
+ if (cb->deleted)
+ continue;
+
+ if (cb->callbackID == callbackID && cb->conn == conn) {
+ ret = cb->eventID;
+ break;
+ }
+ }
virObjectEventStateUnlock(state);
+
+ if (ret < 0)
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("event callback id %d not registered"),
+ callbackID);
return ret;
}
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index fecb9b2..64d9d92 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -2911,6 +2911,7 @@ done:
return rv;
}
+
static int
remoteConnectNetworkEventRegisterAny(virConnectPtr conn,
virNetworkPtr net,
@@ -2968,16 +2969,12 @@ remoteConnectNetworkEventDeregisterAny(virConnectPtr conn,
remoteDriverLock(priv);
if ((eventID = virObjectEventStateEventID(conn, priv->eventState,
- callbackID)) < 0) {
- virReportError(VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID);
+ callbackID)) < 0)
goto done;
- }
if ((count = virObjectEventStateDeregisterID(conn, priv->eventState,
- callbackID)) < 0) {
- virReportError(VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID);
+ callbackID)) < 0)
goto done;
- }
/* If that was the last callback for this eventID, we need to disable
* events on the server */
@@ -2997,6 +2994,7 @@ done:
return rv;
}
+
static int
remoteConnectListAllInterfaces(virConnectPtr conn,
virInterfacePtr **ifaces,
@@ -4405,10 +4403,11 @@ out:
#endif /* WITH_POLKIT */
/*----------------------------------------------------------------------*/
-static int remoteConnectDomainEventRegister(virConnectPtr conn,
- virConnectDomainEventCallback callback,
- void *opaque,
- virFreeCallback freecb)
+static int
+remoteConnectDomainEventRegister(virConnectPtr conn,
+ virConnectDomainEventCallback callback,
+ void *opaque,
+ virFreeCallback freecb)
{
int rv = -1;
struct private_data *priv = conn->privateData;
@@ -4421,11 +4420,13 @@ static int remoteConnectDomainEventRegister(virConnectPtr conn,
goto done;
if (count == 1) {
- /* Tell the server when we are the first callback deregistering */
+ /* Tell the server when we are the first callback registering */
if (call(conn, priv, 0, REMOTE_PROC_CONNECT_DOMAIN_EVENT_REGISTER,
(xdrproc_t) xdr_void, (char *) NULL,
- (xdrproc_t) xdr_void, (char *) NULL) == -1)
+ (xdrproc_t) xdr_void, (char *) NULL) == -1) {
+ virDomainEventStateDeregister(conn, priv->eventState, callback);
goto done;
+ }
}
rv = 0;
@@ -4435,8 +4436,9 @@ done:
return rv;
}
-static int remoteConnectDomainEventDeregister(virConnectPtr conn,
- virConnectDomainEventCallback callback)
+static int
+remoteConnectDomainEventDeregister(virConnectPtr conn,
+ virConnectDomainEventCallback callback)
{
struct private_data *priv = conn->privateData;
int rv = -1;
@@ -5205,12 +5207,13 @@ static virStreamDriver remoteStreamDrv = {
};
-static int remoteConnectDomainEventRegisterAny(virConnectPtr conn,
- virDomainPtr dom,
- int eventID,
- virConnectDomainEventGenericCallback callback,
- void *opaque,
- virFreeCallback freecb)
+static int
+remoteConnectDomainEventRegisterAny(virConnectPtr conn,
+ virDomainPtr dom,
+ int eventID,
+ virConnectDomainEventGenericCallback callback,
+ void *opaque,
+ virFreeCallback freecb)
{
int rv = -1;
struct private_data *priv = conn->privateData;
@@ -5248,8 +5251,9 @@ done:
}
-static int remoteConnectDomainEventDeregisterAny(virConnectPtr conn,
- int callbackID)
+static int
+remoteConnectDomainEventDeregisterAny(virConnectPtr conn,
+ int callbackID)
{
struct private_data *priv = conn->privateData;
int rv = -1;
@@ -5260,16 +5264,12 @@ static int remoteConnectDomainEventDeregisterAny(virConnectPtr conn,
remoteDriverLock(priv);
if ((eventID = virObjectEventStateEventID(conn, priv->eventState,
- callbackID)) < 0) {
- virReportError(VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID);
+ callbackID)) < 0)
goto done;
- }
if ((count = virObjectEventStateDeregisterID(conn, priv->eventState,
- callbackID)) < 0) {
- virReportError(VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID);
+ callbackID)) < 0)
goto done;
- }
/* If that was the last callback for this eventID, we need to disable
* events on the server */
--
1.8.4.2
10 years, 11 months
[libvirt] [PATCH] Make sure AC_ARG_WITH is always executed
by Guido Günther
---
I'm not sure whether it's good autoconf style to have AC_ARG_WITH in a
conditional so this patch moves it out.
Cheers,
-- Guido
configure.ac | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/configure.ac b/configure.ac
index 97752f4..d02b9d2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1418,13 +1418,12 @@ elif test "with_secdriver_apparmor" != "no" ; then
fi
AM_CONDITIONAL([WITH_SECDRIVER_APPARMOR], [test "$with_secdriver_apparmor" != "no"])
-if test "$with_apparmor" != "no"; then
AC_ARG_WITH([apparmor-profiles],
[AS_HELP_STRING([--with-apparmor-profiles],
[install apparmor profiles @<:@default=no@:>@])],
[with_apparmor_profiles=yes],
[with_apparmor_profiles=no])
-else
+if test "$with_apparmor" = "no"; then
with_apparmor_profiles="no"
fi
AM_CONDITIONAL([WITH_APPARMOR_PROFILES], [test "$with_apparmor_profiles" != "no"])
--
1.8.5.2
10 years, 11 months
[libvirt] [libvirt-java] [PATCH 0/3] Make the Java wrapper more fun to use
by Claudio Bley
This patchset first implements the ByteChannel interface for the Stream
class which makes it readily usable with any standard Java library.
Note, that this changes the wrapping of the virStreamRecv and virStreamSend
function of the jna.Libvirt interface (which also makes this more efficient
because a ByteBuffer is used instead of an Array). However, this interface
should not be considered part of the public interface of the library, as with
all classes part of the org.libvirt.jna package.
In order to put this new functionality to use, the virDomainScreenshot
function is wrapped and a unit test added to exercise it a bit.
Claudio Bley (3):
Implement interface ByteChannel for Stream class
Domain: add screenshot method
test: add testDomainScreenshot JUnit test
src/main/java/org/libvirt/Domain.java | 11 ++
src/main/java/org/libvirt/Stream.java | 175 +++++++++++++++++++++++-
src/main/java/org/libvirt/jna/Libvirt.java | 7 +-
src/test/java/org/libvirt/TestJavaBindings.java | 38 +++++
4 files changed, 226 insertions(+), 5 deletions(-)
--
1.8.5.2.msysgit.0
10 years, 11 months