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@xxxxxxxx> > --- > 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@xxxxxxxx> > + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> > + */ > + > +#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@xxxxxxxx> > + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> > + */ > + > +#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); > -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list