[libvirt] [PATCHv6 2/5] Add hostdev passthrough common library
Jim Fehlig
jfehlig at suse.com
Fri Nov 22 22:02:06 UTC 2013
Chunyan Liu wrote:
> Add hostdev passthrough common library so that it could be shared by all drivers
> and maintain a global hostdev state.
>
Hi Chunyan,
Looks like you addressed all of Daniel's comments from v5. One bug fix
and a few additional comments below...
> Signed-off-by: Chunyan Liu <cyliu at suse.com>
> ---
> po/POTFILES.in | 1 +
> src/Makefile.am | 1 +
> src/libvirt_private.syms | 20 +
> src/qemu/qemu_domain.c | 22 +
> src/qemu/qemu_hotplug.c | 2 -
> src/util/virhostdev.c | 1671 ++++++++++++++++++++++++++++++++++++++++++++++
> src/util/virhostdev.h | 129 ++++
> 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 +-
> 13 files changed, 1928 insertions(+), 30 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 15afdec..384204b 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -159,6 +159,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 21b9caf..c2dc46f 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -152,6 +152,7 @@ UTIL_SOURCES = \
> util/virutil.c util/virutil.h \
> util/viruuid.c util/viruuid.h \
> util/virxml.c util/virxml.h \
> + util/virhostdev.c util/virhostdev.h \
> $(NULL)
>
>
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index 675835f..dee4135 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1243,6 +1243,26 @@ virHookInitialize;
> virHookPresent;
>
>
> +#util/virhostdev.h
> +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/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> index 81d0ba9..52697a9 100644
> --- a/src/qemu/qemu_domain.c
> +++ b/src/qemu/qemu_domain.c
> @@ -38,6 +38,7 @@
> #include "virtime.h"
> #include "virstoragefile.h"
> #include "virstring.h"
> +#include "virhostdev.h"
>
> #include <sys/time.h>
> #include <fcntl.h>
> @@ -814,6 +815,7 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev,
> int ret = -1;
> virQEMUDriverPtr driver = opaque;
> virQEMUDriverConfigPtr cfg = NULL;
> + virQEMUCapsPtr qemuCaps = NULL;
>
> if (dev->type == VIR_DOMAIN_DEVICE_NET &&
> dev->data.net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV &&
> @@ -892,6 +894,26 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev,
> dev->data.chr->source.data.nix.listen = true;
> }
>
> + if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
> + virDomainHostdevDefPtr hostdev = dev->data.hostdev;
> + bool supportsPassthroughVFIO = virHostdevHostSupportsPassthroughVFIO();
> + if (!(qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache,
> + def->emulator)))
> + goto cleanup;
> +
> + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
> + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI &&
> + hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) {
> + if (supportsPassthroughVFIO &&
> + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI))
> + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO;
> + else
> + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM;
> + }
> +
> + virObjectUnref(qemuCaps);
> + }
> +
> ret = 0;
>
> cleanup:
> diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
> index a72e1f1..c6a3134 100644
> --- a/src/qemu/qemu_hotplug.c
> +++ b/src/qemu/qemu_hotplug.c
> @@ -2547,8 +2547,6 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver,
>
> virDomainAuditHostdev(vm, hostdev, "detach", true);
>
> - qemuDomainHostdevNetConfigRestore(hostdev, cfg->stateDir);
> -
> switch ((enum virDomainHostdevSubsysType) hostdev->source.subsys.type) {
> case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
> qemuDomainRemovePCIHostDevice(driver, vm, hostdev);
> diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c
> new file mode 100644
> index 0000000..9147894
> --- /dev/null
> +++ b/src/util/virhostdev.c
> @@ -0,0 +1,1671 @@
> +/* 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 at suse.com>
> + * Author: Daniel P. Berrange <berrange at 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 "virpci.h"
> +#include "virusb.h"
> +#include "virscsi.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"
> +
> +struct _virHostdevManager{
> + char *stateDir;
> +
> + virPCIDeviceListPtr activePciHostdevs;
> + virPCIDeviceListPtr inactivePciHostdevs;
> + virUSBDeviceListPtr activeUsbHostdevs;
> + virSCSIDeviceListPtr activeScsiHostdevs;
> +};
> +
> +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_XEN :
> + if (virPCIDeviceSetStubDriver(dev, "pciback") < 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>
> +static 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)
>
This should be '< 0'.
> + goto cleanup;
> +
> + ret = true;
> +# endif
> +
> +cleanup:
> + VIR_FORCE_CLOSE(kvmfd);
> +
> + return ret;
> +}
> +#else
> +static 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;
> +
> + 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)?true:false;
>
Needs some whitespace, e.g.
bool coldBoot = (flags & VIR_COLD_BOOT) ? true : false;
> +
> + 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;
> +
> + /* 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;
> +
> + /* 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 (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)?true:false;
> + 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;
> +
> + 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_XEN :
> + if (virPCIDeviceSetStubDriver(dev, "pciback") < 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;
> +
> + 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;
> +
> + 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;
> +
> + 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;
> +
> + 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;
> +
> + 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)
> + 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;
> +
> + 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;
> +
> + 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;
> +
> + 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..138e634
> --- /dev/null
> +++ b/src/util/virhostdev.h
> @@ -0,0 +1,129 @@
> +/* 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 at suse.com>
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#ifndef __VIR_HOSTDEV_H__
> +# define __VIR_HOSTDEV_H__
> +
> +# include "internal.h"
> +
> +# include "domain_conf.h"
> +# include "virpci.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 virHostdevNameList {
> + char **names;
> + size_t nnames;
> +};
> +typedef struct virHostdevNameList *virHostdevNameListPtr;
> +
> +virHostdevManagerPtr virHostdevManagerGetDefault(void);
> +
> +bool virHostdevHostSupportsPassthroughVFIO(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 65d7168..76ba0fe 100644
> --- a/src/util/virpci.c
> +++ b/src/util/virpci.c
> @@ -62,7 +62,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;
>
Whitespace should be consistent with the other structure members.
>
> unsigned int pcie_cap_pos;
> unsigned int pci_pm_cap_pos;
> @@ -1572,6 +1575,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);
> }
>
> @@ -1641,16 +1646,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 0aa6fee..f3f9e08 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 e901618..4adc928 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;
>
Whitespace should be consistent with the other structure members.
Regards,
Jim
> };
>
> 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);
>
More information about the libvir-list
mailing list