Extract code from qemu_hostdev.c and make it reusable for multiple drivers, meanwhile maintain a global hostdev state to solve conflict between different drivers. Signed-off-by: Chunyan Liu <cyliu@xxxxxxxx> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 21 + src/lxc/lxc_hostdev.c | 11 +- src/qemu/qemu_driver.c | 4 +- src/qemu/qemu_hostdev.c | 42 +- src/util/virhostdev.c | 1691 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virhostdev.h | 134 ++++ src/util/virpci.c | 30 +- src/util/virpci.h | 9 +- src/util/virscsi.c | 28 +- src/util/virscsi.h | 8 +- src/util/virusb.c | 29 +- src/util/virusb.h | 8 +- 14 files changed, 1969 insertions(+), 48 deletions(-) create mode 100644 src/util/virhostdev.c create mode 100644 src/util/virhostdev.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 49dfc9c..ba63428 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -161,6 +161,7 @@ src/util/vireventpoll.c src/util/virfile.c src/util/virhash.c src/util/virhook.c +src/util/virhostdev.c src/util/viridentity.c src/util/virinitctl.c src/util/viriptables.c diff --git a/src/Makefile.am b/src/Makefile.am index 57e163f..98233cd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -104,6 +104,7 @@ UTIL_SOURCES = \ util/virhash.c util/virhash.h \ util/virhashcode.c util/virhashcode.h \ util/virhook.c util/virhook.h \ + util/virhostdev.c util/virhostdev.h \ util/viridentity.c util/viridentity.h \ util/virinitctl.c util/virinitctl.h \ util/viriptables.c util/viriptables.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2dbb8f8..dcb0a3e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1264,6 +1264,27 @@ virHookInitialize; virHookPresent; +#util/virhostdev.h +virHostdevHostSupportsPassthroughKVM; +virHostdevHostSupportsPassthroughVFIO; +virHostdevManagerGetDefault; +virHostdevPciNodeDeviceDetach; +virHostdevPciNodeDeviceReAttach; +virHostdevPciNodeDeviceReset; +virHostdevPrepareDomainHostdevs; +virHostdevPreparePciHostdevs; +virHostdevPrepareScsiHostdevs; +virHostdevPrepareUsbHostdevs; +virHostdevReAttachDomainHostdevs; +virHostdevReAttachPciHostdevs; +virHostdevReAttachScsiHostdevs; +virHostdevReAttachUsbHostdevs; +virHostdevUpdateActiveHostdevs; +virHostdevUpdateActivePciHostdevs; +virHostdevUpdateActiveScsiHostdevs; +virHostdevUpdateActiveUsbHostdevs; + + # util/viridentity.h virIdentityGetAttr; virIdentityGetCurrent; diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c index 3b371fc..77ce965 100644 --- a/src/lxc/lxc_hostdev.c +++ b/src/lxc/lxc_hostdev.c @@ -60,7 +60,7 @@ virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, continue; } - virUSBDeviceSetUsedBy(usb, def->name); + virUSBDeviceSetUsedBy(usb, "QEMU", def->name); virObjectLock(driver->activeUsbHostdevs); if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { @@ -90,7 +90,9 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, for (i = 0; i < count; i++) { virUSBDevicePtr usb = virUSBDeviceListGet(list, i); if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) { - const char *other_name = virUSBDeviceGetUsedBy(tmp); + const char *other_name = NULL; + const char *other_drvname = NULL; + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -103,7 +105,7 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, goto error; } - virUSBDeviceSetUsedBy(usb, name); + virUSBDeviceSetUsedBy(usb, "QEMU", name); VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name); /* @@ -352,6 +354,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virUSBDevicePtr usb, tmp; const char *used_by = NULL; + const char *used_by_drvname = NULL; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; @@ -389,7 +392,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, continue; } - used_by = virUSBDeviceGetUsedBy(tmp); + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by); if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", hostdev->source.subsys.u.usb.bus, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 45d11cd..90820ab 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10885,7 +10885,9 @@ qemuNodeDeviceReAttach(virNodeDevicePtr dev) virObjectLock(driver->inactivePciHostdevs); other = virPCIDeviceListFind(driver->activePciHostdevs, pci); if (other) { - const char *other_name = virPCIDeviceGetUsedBy(other); + const char *other_name = NULL; + const char *other_drvname = NULL; + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index dee61e7..1dc652b 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -177,7 +177,7 @@ qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, goto cleanup; } - virPCIDeviceSetUsedBy(dev, def->name); + virPCIDeviceSetUsedBy(dev, "QEMU", def->name); /* Setup the original states for the PCI device */ virPCIDeviceSetUnbindFromStub(dev, hostdev->origstates.states.pci.unbind_from_stub); @@ -230,7 +230,7 @@ qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, continue; } - virUSBDeviceSetUsedBy(usb, def->name); + virUSBDeviceSetUsedBy(usb, "QEMU", def->name); if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { virUSBDeviceFree(usb); @@ -270,7 +270,7 @@ qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver, hostdev->readonly))) goto cleanup; - virSCSIDeviceSetUsedBy(scsi, def->name); + virSCSIDeviceSetUsedBy(scsi, "QEMU", def->name); if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) { virSCSIDeviceFree(scsi); @@ -683,7 +683,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, * the dev is in list driver->activePciHostdevs. */ if ((other = virPCIDeviceListFind(driver->activePciHostdevs, dev))) { - const char *other_name = virPCIDeviceGetUsedBy(other); + const char *other_name = NULL; + const char *other_drvname = NULL; + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -756,7 +758,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev); if (activeDev) - virPCIDeviceSetUsedBy(activeDev, name); + virPCIDeviceSetUsedBy(activeDev, "QEMU", name); } /* Loop 8: Now set the original states for hostdev def */ @@ -847,7 +849,9 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, for (i = 0; i < count; i++) { virUSBDevicePtr usb = virUSBDeviceListGet(list, i); if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) { - const char *other_name = virUSBDeviceGetUsedBy(tmp); + const char *other_name = NULL; + const char *other_drvname = NULL; + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -860,7 +864,7 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, goto error; } - virUSBDeviceSetUsedBy(usb, name); + virUSBDeviceSetUsedBy(usb, "QEMU", name); VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name); /* @@ -1116,7 +1120,9 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, for (i = 0; i < count; i++) { virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i); if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi))) { - const char *other_name = virSCSIDeviceGetUsedBy(tmp); + const char *other_name = NULL; + const char *other_drvname = NULL; + virSCSIDeviceGetUsedBy(tmp, &other_drvname, &other_name); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -1129,7 +1135,7 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, goto error; } - virSCSIDeviceSetUsedBy(scsi, name); + virSCSIDeviceSetUsedBy(scsi, "QEMU", name); VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi)); if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) @@ -1258,10 +1264,14 @@ qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, * been used by this domain. */ activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev); - if (activeDev && - STRNEQ_NULLABLE(name, virPCIDeviceGetUsedBy(activeDev))) { - virPCIDeviceListDel(pcidevs, dev); - continue; + if (activeDev) { + const char *tmp_name = NULL; + const char *tmp_drvname = NULL; + virPCIDeviceGetUsedBy(activeDev, &tmp_drvname, &tmp_name); + if (STRNEQ_NULLABLE(name, tmp_name)) { + virPCIDeviceListDel(pcidevs, dev); + continue; + } } virPCIDeviceListDel(driver->activePciHostdevs, dev); @@ -1316,6 +1326,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virUSBDevicePtr usb, tmp; const char *used_by = NULL; + const char *used_by_drvname = NULL; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; @@ -1353,7 +1364,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, continue; } - used_by = virUSBDeviceGetUsedBy(tmp); + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by); if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", hostdev->source.subsys.u.usb.bus, @@ -1380,6 +1391,7 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virSCSIDevicePtr scsi, tmp; const char *used_by = NULL; + const char *used_by_drvname = NULL; virDomainDeviceDef dev; dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; @@ -1421,7 +1433,7 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, continue; } - used_by = virSCSIDeviceGetUsedBy(tmp); + virSCSIDeviceGetUsedBy(tmp, &used_by_drvname, &used_by); if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %s:%d:%d:%d dom=%s from activeScsiHostdevs", hostdev->source.subsys.u.scsi.adapter, diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c new file mode 100644 index 0000000..63690b7 --- /dev/null +++ b/src/util/virhostdev.c @@ -0,0 +1,1691 @@ +/* virhostdev.c: hostdev management + * + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. + * Copyright (C) 2006-2007, 2009-2013 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Chunyan Liu <cyliu@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 "virnetdev.h" +#include "virutil.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define HOSTDEV_STATE_DIR LOCALSTATEDIR "/run/libvirt/hostdevmgr" + +/* for upgrade, may need to find netconfig file in old qemu state dir */ +#define QEMU_STATE_DIR LOCALSTATEDIR "/run/libvirt/qemu" + +static virHostdevManagerPtr hostdevMgr; + +static void +virHostdevManagerCleanup(void) +{ + if (!hostdevMgr) + return; + + virObjectUnref(hostdevMgr->activePciHostdevs); + virObjectUnref(hostdevMgr->inactivePciHostdevs); + virObjectUnref(hostdevMgr->activeUsbHostdevs); + VIR_FREE(hostdevMgr->stateDir); + + VIR_FREE(hostdevMgr); +} + +static int +virHostdevOnceInit(void) +{ + if (VIR_ALLOC(hostdevMgr) < 0) + goto error; + + if ((hostdevMgr->activePciHostdevs = virPCIDeviceListNew()) == NULL) + goto error; + + if ((hostdevMgr->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) + goto error; + + if ((hostdevMgr->inactivePciHostdevs = virPCIDeviceListNew()) == NULL) + goto error; + + if ((hostdevMgr->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL) + goto error; + + if (VIR_STRDUP(hostdevMgr->stateDir, HOSTDEV_STATE_DIR) < 0) + goto error; + + if (virFileMakePath(hostdevMgr->stateDir) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Failed to create state dir '%s'"), + hostdevMgr->stateDir); + goto error; + } + + return 0; + +error: + virHostdevManagerCleanup(); + return -1; +} + +VIR_ONCE_GLOBAL_INIT(virHostdev) + +virHostdevManagerPtr +virHostdevManagerGetDefault(void) +{ + if (virHostdevInitialize() < 0) + return NULL; + return hostdevMgr; +} + +static int +virHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path) +{ + virPCIDeviceAddress config_address; + + config_address.domain = hostdev->source.subsys.u.pci.addr.domain; + config_address.bus = hostdev->source.subsys.u.pci.addr.bus; + config_address.slot = hostdev->source.subsys.u.pci.addr.slot; + config_address.function = hostdev->source.subsys.u.pci.addr.function; + + return virPCIDeviceAddressGetSysfsFile(&config_address, sysfs_path); +} + +static int +virHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) +{ + char *sysfs_path = NULL; + int ret = -1; + + if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0) + return ret; + + ret = virPCIIsVirtualFunction(sysfs_path); + + VIR_FREE(sysfs_path); + + return ret; +} + +static int +virHostdevNetDevice(virDomainHostdevDefPtr hostdev, + char **linkdev, + int *vf) +{ + int ret = -1; + char *sysfs_path = NULL; + + if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0) + return ret; + + if (virPCIIsVirtualFunction(sysfs_path) == 1) { + if (virPCIGetVirtualFunctionInfo(sysfs_path, linkdev, vf) < 0) + goto cleanup; + } else { + if (virPCIGetNetName(sysfs_path, linkdev) < 0) + goto cleanup; + *vf = -1; + } + + ret = 0; + +cleanup: + VIR_FREE(sysfs_path); + + return ret; +} + +static int +virHostdevNetConfigVirtPortProfile(const char *linkdev, int vf, + virNetDevVPortProfilePtr virtPort, + const virMacAddr *macaddr, + const unsigned char *uuid, + int associate) +{ + int ret = -1; + + if (!virtPort) + return ret; + + switch (virtPort->virtPortType) { + case VIR_NETDEV_VPORT_PROFILE_NONE: + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH: + case VIR_NETDEV_VPORT_PROFILE_8021QBG: + case VIR_NETDEV_VPORT_PROFILE_LAST: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("virtualport type %s is " + "currently not supported on interfaces of type " + "hostdev"), + virNetDevVPortTypeToString(virtPort->virtPortType)); + break; + + case VIR_NETDEV_VPORT_PROFILE_8021QBH: + if (associate) + ret = virNetDevVPortProfileAssociate(NULL, virtPort, macaddr, + linkdev, vf, uuid, + VIR_NETDEV_VPORT_PROFILE_OP_CREATE, false); + else + ret = virNetDevVPortProfileDisassociate(NULL, virtPort, + macaddr, linkdev, vf, + VIR_NETDEV_VPORT_PROFILE_OP_DESTROY); + break; + } + + return ret; +} + +static int +virHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev, + const unsigned char *uuid, + char *stateDir) +{ + char *linkdev = NULL; + virNetDevVlanPtr vlan; + virNetDevVPortProfilePtr virtPort; + int ret = -1; + int vf = -1; + int vlanid = -1; + int port_profile_associate = 1; + int isvf; + + isvf = virHostdevIsVirtualFunction(hostdev); + if (isvf <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Interface type hostdev is currently supported on " + "SR-IOV Virtual Functions only")); + return ret; + } + + if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0) + return ret; + + vlan = virDomainNetGetActualVlan(hostdev->parent.data.net); + virtPort = virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net); + if (virtPort) { + if (vlan) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("direct setting of the vlan tag is not allowed " + "for hostdev devices using %s mode"), + virNetDevVPortTypeToString(virtPort->virtPortType)); + goto cleanup; + } + ret = virHostdevNetConfigVirtPortProfile(linkdev, vf, + virtPort, + &hostdev->parent.data.net->mac, + uuid, + port_profile_associate); + } else { + /* Set only mac and vlan */ + if (vlan) { + if (vlan->nTags != 1 || vlan->trunk) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vlan trunking is not supported " + "by SR-IOV network devices")); + goto cleanup; + } + if (vf == -1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("vlan can only be set for SR-IOV VFs, but " + "%s is not a VF"), linkdev); + goto cleanup; + } + vlanid = vlan->tag[0]; + } else if (vf >= 0) { + vlanid = 0; /* assure any current vlan tag is reset */ + } + + ret = virNetDevReplaceNetConfig(linkdev, vf, + &hostdev->parent.data.net->mac, + vlanid, stateDir); + } +cleanup: + VIR_FREE(linkdev); + return ret; +} + +/* oldStateDir: + * For upgrade, if there is an existing VM on QEMU, the hostdev netconfig file + * is previously stored in /var/run/libvirt/qemu. With new version, it tries to + * find in hostdevManager->stateDir location but certainly it cannot find it. + * In this case, we should find in the old state dir. + */ +static int +virHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, + char *stateDir, + char *oldStateDir) +{ + char *linkdev = NULL; + virNetDevVPortProfilePtr virtPort; + int ret = -1; + int vf = -1; + int port_profile_associate = 0; + int isvf; + + /* This is only needed for PCI devices that have been defined + * using <interface type='hostdev'>. For all others, it is a NOP. + */ + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI || + hostdev->parent.type != VIR_DOMAIN_DEVICE_NET || + !hostdev->parent.data.net) + return 0; + + isvf = virHostdevIsVirtualFunction(hostdev); + if (isvf <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Interface type hostdev is currently supported on " + "SR-IOV Virtual Functions only")); + return ret; + } + + if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0) + return ret; + + virtPort = virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net); + if (virtPort) + ret = virHostdevNetConfigVirtPortProfile(linkdev, vf, virtPort, + &hostdev->parent.data.net->mac, + NULL, port_profile_associate); + else { + ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir); + if (ret < 0 && oldStateDir != NULL) + ret = virNetDevRestoreNetConfig(linkdev, vf, oldStateDir); + } + + VIR_FREE(linkdev); + + return ret; +} + +static virPCIDeviceListPtr +virHostdevGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) +{ + virPCIDeviceListPtr list; + size_t i; + + if (!(list = virPCIDeviceListNew())) + return NULL; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virPCIDevicePtr dev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + if (!dev) { + virObjectUnref(list); + return NULL; + } + + if (virPCIDeviceListAdd(list, dev) < 0) { + virPCIDeviceFree(dev); + virObjectUnref(list); + return NULL; + } + + virPCIDeviceSetManaged(dev, hostdev->managed); + + switch (hostdev->source.subsys.u.pci.backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) { + virObjectUnref(list); + return NULL; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) { + virObjectUnref(list); + return NULL; + } + break; + } + } + + return list; +} + +bool +virHostdevHostSupportsPassthroughVFIO(void) +{ + DIR *iommuDir = NULL; + struct dirent *iommuGroup = NULL; + bool ret = false; + + /* condition 1 - /sys/kernel/iommu_groups/ contains entries */ + if (!(iommuDir = opendir("/sys/kernel/iommu_groups/"))) + goto cleanup; + + while ((iommuGroup = readdir(iommuDir))) { + /* skip ./ ../ */ + if (STRPREFIX(iommuGroup->d_name, ".")) + continue; + + /* assume we found a group */ + break; + } + + if (!iommuGroup) + goto cleanup; + /* okay, iommu is on and recognizes groups */ + + /* condition 2 - /dev/vfio/vfio exists */ + if (!virFileExists("/dev/vfio/vfio")) + goto cleanup; + + ret = true; + +cleanup: + if (iommuDir) + closedir(iommuDir); + + return ret; +} + + +#if HAVE_LINUX_KVM_H +# include <linux/kvm.h> +bool +virHostdevHostSupportsPassthroughKVM(void) +{ + int kvmfd = -1; + bool ret = false; + + if ((kvmfd = open("/dev/kvm", O_RDONLY)) < 0) + goto cleanup; + +# ifdef KVM_CAP_IOMMU + if ((ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_IOMMU)) < 0) + goto cleanup; + + ret = true; +# endif + +cleanup: + VIR_FORCE_CLOSE(kvmfd); + + return ret; +} +#else +bool +virHostdevHostSupportsPassthroughKVM(void) +{ + return false; +} +#endif + +static bool +virHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, + size_t nhostdevs) +{ + bool supportsPassthroughVFIO = virHostdevHostSupportsPassthroughVFIO(); + bool supportsPassthroughKVM = virHostdevHostSupportsPassthroughKVM(); + size_t i; + + /* assign defaults for hostdev passthrough */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + int *backend = &hostdev->source.subsys.u.pci.backend; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + switch ((virDomainHostdevSubsysPciBackendType) *backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (!supportsPassthroughVFIO) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support VFIO PCI passthrough")); + return false; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: + if (!supportsPassthroughKVM) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support legacy PCI passthrough")); + return false; + } + + break; + + default: + break; + } + } + + return true; +} +int +virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + const unsigned char *uuid, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags) +{ + virPCIDeviceListPtr pcidevs = NULL; + int last_processed_hostdev_vf = -1; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + if (mgr == NULL) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + if (!virHostdevPCICheckSupport(hostdevs, nhostdevs)) + goto cleanup; + + if (!(pcidevs = virHostdevGetPciHostDeviceList(hostdevs, nhostdevs))) + goto cleanup; + + /* We have to use 9 loops here. *All* devices must + * be detached before we reset any of them, because + * in some cases you have to reset the whole PCI, + * which impacts all devices on it. Also, all devices + * must be reset before being marked as active. + */ + + /* Loop 1: validate that non-managed device isn't in use, eg + * by checking that device is either un-bound, or bound + * to stub driver + */ + + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDevicePtr other; + bool strict_acs_check = !!(flags & VIR_STRICT_ACS_CHECK); + + if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is not assignable"), + virPCIDeviceGetName(dev)); + goto cleanup; + } + /* The device is in use by other active domain if + * the dev is in list activePciHostdevs. + */ + if ((other = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) { + const char *other_drvname; + const char *other_domname; + + virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is in use by driver %s,domain %s"), + virPCIDeviceGetName(dev), other_drvname, + other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is already in use"), + virPCIDeviceGetName(dev)); + goto cleanup; + } + } + + /* Loop 2: detach managed devices */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceGetManaged(dev) && + virPCIDeviceDetach(dev, mgr->activePciHostdevs, NULL) < 0) + goto reattachdevs; + } + + /* Loop 3: Now that all the PCI hostdevs have been detached, we + * can safely reset them */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceReset(dev, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + goto reattachdevs; + } + + /* Loop 4: For SRIOV network devices, Now that we have detached the + * the network device, set the netdev config */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET && + hostdev->parent.data.net) { + if (virHostdevNetConfigReplace(hostdev, uuid, mgr->stateDir) < 0) + goto resetvfnetconfig; + } + last_processed_hostdev_vf = i; + } + + /* Loop 5: Now mark all the devices as active */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0) + goto inactivedevs; + } + + /* Loop 6: Now remove the devices from inactive list. */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDeviceListDel(mgr->inactivePciHostdevs, dev); + } + + /* Loop 7: Now set the used_by_domain of the device in + * driver->activePciHostdevs as domain name. + */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev, activeDev; + + dev = virPCIDeviceListGet(pcidevs, i); + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev); + + if (activeDev) + virPCIDeviceSetUsedBy(activeDev, drv_name, dom_name); + } + + /* Loop 8: Now set the original states for hostdev def */ + for (i = 0; i < nhostdevs; i++) { + virPCIDevicePtr dev; + virPCIDevicePtr pcidev; + virDomainHostdevDefPtr hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + + /* original states "unbind_from_stub", "remove_slot", + * "reprobe" were already set by pciDettachDevice in + * loop 2. + */ + if ((pcidev = virPCIDeviceListFind(pcidevs, dev))) { + hostdev->origstates.states.pci.unbind_from_stub = + virPCIDeviceGetUnbindFromStub(pcidev); + hostdev->origstates.states.pci.remove_slot = + virPCIDeviceGetRemoveSlot(pcidev); + hostdev->origstates.states.pci.reprobe = + virPCIDeviceGetReprobe(pcidev); + } + + virPCIDeviceFree(dev); + } + + /* Loop 9: Now steal all the devices from pcidevs */ + while (virPCIDeviceListCount(pcidevs) > 0) + virPCIDeviceListStealIndex(pcidevs, 0); + + ret = 0; + goto cleanup; + +inactivedevs: + /* Only steal all the devices from driver->activePciHostdevs. We will + * free them in virObjectUnref(). + */ + while (virPCIDeviceListCount(pcidevs) > 0) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, 0); + virPCIDeviceListSteal(mgr->activePciHostdevs, dev); + } + +resetvfnetconfig: + for (i = 0; + last_processed_hostdev_vf != -1 && i < last_processed_hostdev_vf; i++) + virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, NULL); + +reattachdevs: + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDeviceReattach(dev, mgr->activePciHostdevs, NULL); + } + +cleanup: + virObjectUnlock(mgr->activePciHostdevs); + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnref(pcidevs); + return ret; +} + +static int +virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, + bool mandatory, + virUSBDevicePtr *usb) +{ + unsigned vendor = hostdev->source.subsys.u.usb.vendor; + unsigned product = hostdev->source.subsys.u.usb.product; + unsigned bus = hostdev->source.subsys.u.usb.bus; + unsigned device = hostdev->source.subsys.u.usb.device; + bool autoAddress = hostdev->source.subsys.u.usb.autoAddress; + int rc; + + *usb = NULL; + + if (vendor && bus) { + rc = virUSBDeviceFind(vendor, product, bus, device, + NULL, + autoAddress ? false : mandatory, + usb); + if (rc < 0) { + return -1; + } else if (!autoAddress) { + goto out; + } else { + VIR_INFO("USB device %x:%x could not be found at previous" + " address (bus:%u device:%u)", + vendor, product, bus, device); + } + } + + /* When vendor is specified, its USB address is either unspecified or the + * device could not be found at the USB device where it had been + * automatically found before. + */ + if (vendor) { + virUSBDeviceListPtr devs; + + rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs); + if (rc < 0) + return -1; + + if (rc == 1) { + *usb = virUSBDeviceListGet(devs, 0); + virUSBDeviceListSteal(devs, *usb); + } + virObjectUnref(devs); + + if (rc == 0) { + goto out; + } else if (rc > 1) { + if (autoAddress) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x were found, " + "but none of them is at bus:%u device:%u"), + vendor, product, bus, device); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x, " + "use <address> to specify one"), + vendor, product); + } + return -1; + } + + hostdev->source.subsys.u.usb.bus = virUSBDeviceGetBus(*usb); + hostdev->source.subsys.u.usb.device = virUSBDeviceGetDevno(*usb); + hostdev->source.subsys.u.usb.autoAddress = true; + + if (autoAddress) { + VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved" + " from bus:%u device:%u)", + vendor, product, + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + bus, device); + } + } else if (!vendor && bus) { + if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0) + return -1; + } + +out: + if (!*usb) + hostdev->missing = true; + return 0; +} + +static int +virHostdevMarkUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virUSBDeviceListPtr list) +{ + size_t i, j; + unsigned int count; + virUSBDevicePtr tmp; + + virObjectLock(mgr->activeUsbHostdevs); + count = virUSBDeviceListCount(list); + + for (i = 0; i < count; i++) { + virUSBDevicePtr usb = virUSBDeviceListGet(list, i); + if ((tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb))) { + const char *other_drvname; + const char *other_domname; + + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is in use by " + "driver %s,domain %s"), + virUSBDeviceGetName(tmp), + other_drvname, other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is already in use"), + virUSBDeviceGetName(tmp)); + goto error; + } + + virUSBDeviceSetUsedBy(usb, drv_name, dom_name); + VIR_DEBUG("Adding %03d.%03d driver= %s dom=%s to activeUsbHostdevs", + virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), + drv_name, dom_name); + /* + * The caller is responsible to steal these usb devices + * from the virUSBDeviceList that passed in on success, + * perform rollback on failure. + */ + if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0) + goto error; + } + + virObjectUnlock(mgr->activeUsbHostdevs); + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = virUSBDeviceListGet(list, j); + virUSBDeviceListSteal(mgr->activeUsbHostdevs, tmp); + } + virObjectUnlock(mgr->activeUsbHostdevs); + return -1; +} + +int +virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + bool coldBoot) +{ + size_t i; + int ret = -1; + virUSBDeviceListPtr list; + virUSBDevicePtr tmp; + + if (!nhostdevs) + return 0; + if (mgr == NULL) + return -1; + + /* To prevent situation where USB device is assigned to two domains + * we need to keep a list of currently assigned USB devices. + * This is done in several loops which cannot be joined into one + * big loop. + */ + if (!(list = virUSBDeviceListNew())) + goto cleanup; + + /* Loop 1: build temporary list + */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + bool required = true; + virUSBDevicePtr usb; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL || + (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE && + !coldBoot)) + required = false; + + if (virHostdevFindUSBDevice(hostdev, required, &usb) < 0) + goto cleanup; + + if (usb && virUSBDeviceListAdd(list, usb) < 0) { + virUSBDeviceFree(usb); + goto cleanup; + } + } + + /* Mark devices in temporary list as used by @name + * and add them do active list. However, if something goes + * wrong, perform rollback. + */ + if (virHostdevMarkUsbHostdevs(mgr, drv_name, dom_name, list) < 0) + goto cleanup; + + /* Loop 2: Temporary list was successfully merged with + * active list, so steal all items to avoid freeing them + * in cleanup label. + */ + while (virUSBDeviceListCount(list) > 0) { + tmp = virUSBDeviceListGet(list, 0); + virUSBDeviceListSteal(list, tmp); + } + + ret = 0; + +cleanup: + virObjectUnref(list); + return ret; +} + +int +virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i, j; + int count; + virSCSIDeviceListPtr list; + virSCSIDevicePtr tmp; + + if (!nhostdevs) + return 0; + if (mgr == NULL) + return -1; + + /* To prevent situation where SCSI device is assigned to two domains + * we need to keep a list of currently assigned SCSI devices. + */ + if (!(list = virSCSIDeviceListNew())) + goto cleanup; + + /* Loop 1: build temporary list */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virSCSIDevicePtr scsi; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (hostdev->managed) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("SCSI host device doesn't support managed mode")); + goto cleanup; + } + + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly))) + goto cleanup; + + if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + } + + /* Loop 2: Mark devices in temporary list as used by + * and add them to driver list. However, if something goes + * wrong, perform rollback. + */ + virObjectLock(mgr->activeScsiHostdevs); + count = virSCSIDeviceListCount(list); + + for (i = 0; i < count; i++) { + virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i); + if ((tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) { + const char *other_drvname; + const char *other_domname; + + virSCSIDeviceGetUsedBy(tmp, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("SCSI device %s is in use by " + "driver %s domain %s"), + virSCSIDeviceGetName(tmp), + other_drvname, other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("SCSI device %s is already in use"), + virSCSIDeviceGetName(tmp)); + goto error; + } + + virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name); + VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi)); + + if (virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0) + goto error; + } + + virObjectUnlock(mgr->activeScsiHostdevs); + + /* Loop 3: Temporary list was successfully merged with + * driver list, so steal all items to avoid freeing them + * when freeing temporary list. + */ + while (virSCSIDeviceListCount(list) > 0) { + tmp = virSCSIDeviceListGet(list, 0); + virSCSIDeviceListSteal(list, tmp); + } + + virObjectUnref(list); + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = virSCSIDeviceListGet(list, j); + virSCSIDeviceListSteal(mgr->activeScsiHostdevs, tmp); + } + virObjectUnlock(mgr->activeScsiHostdevs); +cleanup: + virObjectUnref(list); + return -1; +} + +int +virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags) +{ + if (!def->nhostdevs) + return 0; + + if (mgr == NULL) + return -1; + + if (flags & VIR_SP_PCI_HOSTDEV) { + if (virHostdevPreparePciHostdevs(mgr, driver, + def->name, def->uuid, + def->hostdevs, + def->nhostdevs, + flags) < 0) + return -1; + } + + if (flags & VIR_SP_USB_HOSTDEV) { + bool coldBoot = !!(flags & VIR_COLD_BOOT); + if (virHostdevPrepareUsbHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs, + coldBoot) < 0) + return -1; + } + + if (flags & VIR_SP_SCSI_HOSTDEV) { + if (virHostdevPrepareScsiHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs) < 0) + return -1; + } + + return 0; +} + +/* + * Pre-condition: mgr->inactivePciHostdevs & mgr->activePciHostdevs + * are locked + */ +static void +virHostdevReAttachPciDevice(virHostdevManagerPtr mgr, virPCIDevicePtr dev) +{ + /* If the device is not managed and was attached to guest + * successfully, it must have been inactive. + */ + if (!virPCIDeviceGetManaged(dev)) { + if (virPCIDeviceListAdd(mgr->inactivePciHostdevs, dev) < 0) + virPCIDeviceFree(dev); + return; + } + + /* Wait for device cleanup if it is qemu/kvm */ + if (STREQ(virPCIDeviceGetStubDriver(dev), "pci-stub")) { + int retries = 100; + while (virPCIDeviceWaitForCleanup(dev, "kvm_assigned_device") + && retries) { + usleep(100*1000); + retries--; + } + } + + if (virPCIDeviceReattach(dev, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to re-attach PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + virPCIDeviceFree(dev); +} + +/* + * Pre-condition: mgr->activePciHostdevs is locked + */ +static virPCIDeviceListPtr +virHostdevGetActivePciHostDeviceList(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + virPCIDeviceListPtr list; + size_t i; + + if (!(list = virPCIDeviceListNew())) + return NULL; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virPCIDevicePtr dev; + virPCIDevicePtr activeDev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + if (!dev) { + virObjectUnref(list); + return NULL; + } + + if ((activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) { + if (virPCIDeviceListAdd(list, activeDev) < 0) { + virPCIDeviceFree(dev); + virObjectUnref(list); + return NULL; + } + } + + virPCIDeviceFree(dev); + } + + return list; +} + +int +virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name) +{ + virDomainHostdevDefPtr hostdev = NULL; + virPCIDevicePtr dev = NULL; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + + if (!mgr) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + for (i = 0; i < nhostdevs; i++) { + hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + + if (!dev) + goto cleanup; + + virPCIDeviceSetManaged(dev, hostdev->managed); + switch (hostdev->source.subsys.u.pci.backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) + goto cleanup; + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) + goto cleanup; + break; + } + virPCIDeviceSetUsedBy(dev, drv_name, dom_name); + + /* Setup the original states for the PCI device */ + virPCIDeviceSetUnbindFromStub(dev, hostdev->origstates.states.pci.unbind_from_stub); + virPCIDeviceSetRemoveSlot(dev, hostdev->origstates.states.pci.remove_slot); + virPCIDeviceSetReprobe(dev, hostdev->origstates.states.pci.reprobe); + + if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0) + goto cleanup; + dev = NULL; + } + + ret = 0; +cleanup: + virPCIDeviceFree(dev); + virObjectUnlock(mgr->activePciHostdevs); + virObjectUnlock(mgr->inactivePciHostdevs); + return ret; +} + + +int +virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name) +{ + virDomainHostdevDefPtr hostdev = NULL; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + + if (!mgr) + return -1; + + virObjectLock(mgr->activeUsbHostdevs); + for (i = 0; i < nhostdevs; i++) { + virUSBDevicePtr usb = NULL; + hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL); + if (!usb) { + VIR_WARN("Unable to reattach USB device %03d.%03d " + "on driver %s domain %s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + drv_name, dom_name); + continue; + } + + virUSBDeviceSetUsedBy(usb, drv_name, dom_name); + + if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0) { + virUSBDeviceFree(usb); + goto cleanup; + } + } + ret = 0; +cleanup: + virObjectUnlock(mgr->activeUsbHostdevs); + return ret; +} + +int +virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name) +{ + virDomainHostdevDefPtr hostdev = NULL; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + + if (!mgr) + return -1; + + virObjectLock(mgr->activeScsiHostdevs); + for (i = 0; i < nhostdevs; i++) { + virSCSIDevicePtr scsi = NULL; + hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly))) + goto cleanup; + + virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name); + + if (virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + } + ret = 0; + +cleanup: + virObjectUnlock(mgr->activeScsiHostdevs); + return ret; +} + +int +virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags) +{ + if (!def->nhostdevs) + return 0; + + if (flags & VIR_SP_PCI_HOSTDEV) { + if (virHostdevUpdateActivePciHostdevs(mgr, + def->hostdevs, + def->nhostdevs, + driver, def->name) < 0) + return -1; + } + + if (flags & VIR_SP_USB_HOSTDEV) { + if (virHostdevUpdateActiveUsbHostdevs(mgr, + def->hostdevs, + def->nhostdevs, + driver, def->name) < 0) + return -1; + } + + if (flags & VIR_SP_SCSI_HOSTDEV) { + if (virHostdevUpdateActiveScsiHostdevs(mgr, + def->hostdevs, + def->nhostdevs, + driver, def->name) < 0) + return -1; + } + + return 0; +} + + +void +virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + virPCIDeviceListPtr pcidevs; + size_t i; + char *oldStateDir = NULL; + + if (!nhostdevs || !mgr) + return; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + /* for upgrade, if netconfig file is not found in mgr->stateDir, + * find in old state dir */ + if (VIR_STRDUP(oldStateDir, QEMU_STATE_DIR) < 0) + goto cleanup; + + if (!(pcidevs = virHostdevGetActivePciHostDeviceList(mgr, + hostdevs, + nhostdevs))) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to allocate PCI device list: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + goto cleanup; + } + + /* Again 4 loops; mark all devices as inactive before reset + * them and reset all the devices before re-attach. + * Attach mac and port profile parameters to devices + */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDevicePtr activeDev = NULL; + const char *usedby_drvname; + const char *usedby_domname; + + /* Never delete the dev from list driver->activePciHostdevs + * if it's used by other domain. + */ + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev); + if (activeDev) { + virPCIDeviceGetUsedBy(activeDev, &usedby_drvname, &usedby_domname); + if (STRNEQ_NULLABLE(drv_name, usedby_drvname) || + STRNEQ_NULLABLE(dom_name, usedby_domname)) { + virPCIDeviceListSteal(pcidevs, dev); + continue; + } + } + + virPCIDeviceListSteal(mgr->activePciHostdevs, dev); + } + + /* + * For SRIOV net host devices, unset mac and port profile before + * reset and reattach device + */ + for (i = 0; i < nhostdevs; i++) + virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, oldStateDir); + + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceReset(dev, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to reset PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + } + + while (virPCIDeviceListCount(pcidevs) > 0) { + virPCIDevicePtr dev = virPCIDeviceListStealIndex(pcidevs, 0); + virHostdevReAttachPciDevice(mgr, dev); + } + + virObjectUnref(pcidevs); +cleanup: + VIR_FREE(oldStateDir); + virObjectUnlock(mgr->activePciHostdevs); + virObjectUnlock(mgr->inactivePciHostdevs); +} + +void +virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i; + + if (!nhostdevs || !mgr) + return; + + virObjectLock(mgr->activeUsbHostdevs); + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virUSBDevicePtr usb, tmp; + const char *usedby_drvname; + const char *usedby_domname; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + if (hostdev->missing) + continue; + + usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL); + + if (!usb) { + VIR_WARN("Unable to reattach USB device %03d.%03d " + "on driver %s domain %s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + drv_name, dom_name); + continue; + } + + /* Delete only those USB devices which belongs + * to domain. Therefore we want to steal only + * those devices from the list which were taken + * by domain */ + + tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb); + virUSBDeviceFree(usb); + + if (!tmp) { + VIR_WARN("Unable to find device %03d.%03d " + "in list of active USB devices", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device); + continue; + } + + virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname); + if (STREQ_NULLABLE(drv_name, usedby_drvname) && + STREQ_NULLABLE(dom_name, usedby_domname)) { + VIR_DEBUG("Removing %03d.%03d dom=%s:%s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + drv_name, dom_name); + virUSBDeviceListDel(mgr->activeUsbHostdevs, tmp); + } + + } + + virObjectUnlock(mgr->activeUsbHostdevs); +} + +void +virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i; + + if (!nhostdevs || !mgr) + return; + + virObjectLock(mgr->activeScsiHostdevs); + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virSCSIDevicePtr scsi, tmp; + const char *usedby_drvname; + const char *usedby_domname; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly))) { + VIR_WARN("Unable to reattach SCSI device %s:%d:%d:%d on driver %s domain %s", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + drv_name, dom_name); + continue; + } + + tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi); + virSCSIDeviceFree(scsi); + + if (!tmp) { + VIR_WARN("Unable to find device %s:%d:%d:%d " + "in list of active SCSI devices", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit); + continue; + } + + virSCSIDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname); + if (STREQ_NULLABLE(usedby_drvname, drv_name) && + STREQ_NULLABLE(usedby_domname, dom_name)) { + VIR_DEBUG("Removing %s:%d:%d:%d driver:%s dom:%s from activeScsiHostdevs", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + drv_name, dom_name); + virSCSIDeviceListDel(mgr->activeScsiHostdevs, tmp); + } + } + + virObjectUnlock(mgr->activeScsiHostdevs); +} + +void +virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags) +{ + if (!def->nhostdevs || !mgr) + return; + + if (flags & VIR_SP_PCI_HOSTDEV) { + virHostdevReAttachPciHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs); + } + + if (flags & VIR_SP_USB_HOSTDEV) { + virHostdevReAttachUsbHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs); + } + + if (flags & VIR_SP_SCSI_HOSTDEV) { + virHostdevReAttachScsiHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs); + } +} + +/* following functions are used by NodeDevDetach/Reattach/Reset */ +int +virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci) +{ + int ret; + + if (!mgr || !pci) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + if (virPCIDeviceDetach(pci, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + ret = -1; + else + ret = 0; + + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnlock(mgr->activePciHostdevs); + return ret; +} + +int +virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci) +{ + int ret = -1; + virPCIDevicePtr other; + + if (!mgr || !pci) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + other = virPCIDeviceListFind(mgr->activePciHostdevs, pci); + if (other) { + const char *other_drvname; + const char *other_domname; + + virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is still in use by driver %s, domain %s"), + virPCIDeviceGetName(pci), other_drvname, other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is still in use"), + virPCIDeviceGetName(pci)); + goto out; + } + + virPCIDeviceReattachInit(pci); + + if (virPCIDeviceReattach(pci, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + goto out; + + ret = 0; +out: + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnlock(mgr->activePciHostdevs); + return ret; +} + +int +virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr, + virPCIDevicePtr pci) +{ + int ret; + + if (!mgr || !pci) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + if (virPCIDeviceReset(pci, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + ret = -1; + else + ret = 0; + + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnlock(mgr->activePciHostdevs); + return ret; +} diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h new file mode 100644 index 0000000..fc69540 --- /dev/null +++ b/src/util/virhostdev.h @@ -0,0 +1,134 @@ +/* virhostdev.h: hostdev management + * + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. + * Copyright (C) 2006-2007, 2009-2013 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Chunyan Liu <cyliu@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" +# include "virusb.h" +# include "virscsi.h" + +typedef enum { + VIR_SP_PCI_HOSTDEV = (1 << 0), /* support pci passthrough */ + VIR_SP_USB_HOSTDEV = (1 << 1), /* support usb passthrough */ + VIR_SP_SCSI_HOSTDEV = (1 << 2), /* support scsi passthrough */ + + VIR_COLD_BOOT = (1 << 8), /* cold boot */ + VIR_STRICT_ACS_CHECK = (1 << 9), /* strict acs check */ +} virHostdevManagerFlag; + +typedef struct _virHostdevManager virHostdevManager; +typedef virHostdevManager *virHostdevManagerPtr; +struct _virHostdevManager{ + char *stateDir; + + virPCIDeviceListPtr activePciHostdevs; + virPCIDeviceListPtr inactivePciHostdevs; + virUSBDeviceListPtr activeUsbHostdevs; + virSCSIDeviceListPtr activeScsiHostdevs; +}; + +virHostdevManagerPtr virHostdevManagerGetDefault(void); + +bool virHostdevHostSupportsPassthroughVFIO(void); +bool virHostdevHostSupportsPassthroughKVM(void); + +/* functions used to prepare/unprepare hostdevs for domain */ +int virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags); +void virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags); +int virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + const unsigned char *uuid, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags); +int virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + bool coldBoot); +int virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +int +virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name); +int +virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name); +int +virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name); +int +virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags); + +/* functions used by NodeDevDetach/Reattach/Reset */ +int virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci); +int virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci); +int virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr, + virPCIDevicePtr pci); + +#endif /* __VIR_HOSTDEV_H__ */ diff --git a/src/util/virpci.c b/src/util/virpci.c index 8ec642f..7402898 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -58,7 +58,10 @@ struct _virPCIDevice { char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */ char id[PCI_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* The domain which uses the device */ + + /* The driver:domain which uses the device */ + char *used_by_drvname; + char *used_by_domname; unsigned int pcie_cap_pos; unsigned int pci_pm_cap_pos; @@ -1562,6 +1565,8 @@ virPCIDeviceFree(virPCIDevicePtr dev) VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); VIR_FREE(dev->stubDriver); + VIR_FREE(dev->used_by_drvname); + VIR_FREE(dev->used_by_domname); VIR_FREE(dev); } @@ -1631,16 +1636,27 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool reprobe) dev->reprobe = reprobe; } -void -virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name) +int +virPCIDeviceSetUsedBy(virPCIDevicePtr dev, + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0) + return -1; + + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0) + return -1; + + return 0; } -const char * -virPCIDeviceGetUsedBy(virPCIDevicePtr dev) +void +virPCIDeviceGetUsedBy(virPCIDevicePtr dev, + const char **drv_name, + const char **dom_name) { - return dev->used_by; + *drv_name = dev->used_by_drvname; + *dom_name = dev->used_by_domname; } void virPCIDeviceReattachInit(virPCIDevicePtr pci) diff --git a/src/util/virpci.h b/src/util/virpci.h index 0479f0b..2347b95 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -65,9 +65,12 @@ unsigned int virPCIDeviceGetManaged(virPCIDevice *dev); int virPCIDeviceSetStubDriver(virPCIDevicePtr dev, const char *driver); const char *virPCIDeviceGetStubDriver(virPCIDevicePtr dev); -void virPCIDeviceSetUsedBy(virPCIDevice *dev, - const char *used_by); -const char *virPCIDeviceGetUsedBy(virPCIDevice *dev); +int virPCIDeviceSetUsedBy(virPCIDevice *dev, + const char *drv_name, + const char *dom_name); +void virPCIDeviceGetUsedBy(virPCIDevice *dev, + const char **drv_name, + const char **dom_name); unsigned int virPCIDeviceGetUnbindFromStub(virPCIDevicePtr dev); void virPCIDeviceSetUnbindFromStub(virPCIDevice *dev, bool unbind); diff --git a/src/util/virscsi.c b/src/util/virscsi.c index 7aca9e6..062e8bd 100644 --- a/src/util/virscsi.c +++ b/src/util/virscsi.c @@ -55,7 +55,10 @@ struct _virSCSIDevice { char *name; /* adapter:bus:target:unit */ char *id; /* model:vendor */ char *sg_path; /* e.g. /dev/sg2 */ - const char *used_by; /* name of the domain using this dev */ + + /* driver:domain using this dev */ + char *used_by_drvname; + char *used_by_domname; bool readonly; }; @@ -259,20 +262,31 @@ virSCSIDeviceFree(virSCSIDevicePtr dev) VIR_FREE(dev->id); VIR_FREE(dev->name); VIR_FREE(dev->sg_path); + VIR_FREE(dev->used_by_drvname); + VIR_FREE(dev->used_by_domname); VIR_FREE(dev); } -void +int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { - dev->used_by = name; + if (VIR_STRDUP(dev->used_by_drvname, drvname) < 0) + return -1; + if (VIR_STRDUP(dev->used_by_domname, domname) < 0) + return -1; + + return 0; } -const char * -virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev) +void +virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev, + const char **drvname, + const char **domname) { - return dev->used_by; + *drvname = dev->used_by_drvname; + *domname = dev->used_by_domname; } const char * diff --git a/src/util/virscsi.h b/src/util/virscsi.h index cce5df4..263b175 100644 --- a/src/util/virscsi.h +++ b/src/util/virscsi.h @@ -49,8 +49,12 @@ virSCSIDevicePtr virSCSIDeviceNew(const char *adapter, bool readonly); void virSCSIDeviceFree(virSCSIDevicePtr dev); -void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, const char *name); -const char *virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev); +int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, + const char *drvname, + const char *domname); +void virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev, + const char **drvname, + const char **domname); const char *virSCSIDeviceGetName(virSCSIDevicePtr dev); unsigned int virSCSIDeviceGetAdapter(virSCSIDevicePtr dev); unsigned int virSCSIDeviceGetBus(virSCSIDevicePtr dev); diff --git a/src/util/virusb.c b/src/util/virusb.c index 3c82200..7b17b0a 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -55,7 +55,10 @@ struct _virUSBDevice { char name[USB_ADDR_LEN]; /* domain:bus:slot.function */ char id[USB_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* name of the domain using this dev */ + + /* driver:domain using this dev */ + char *used_by_drvname; + char *used_by_domname; }; struct _virUSBDeviceList { @@ -375,19 +378,31 @@ virUSBDeviceFree(virUSBDevicePtr dev) return; VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); + VIR_FREE(dev->used_by_drvname); + VIR_FREE(dev->used_by_domname); VIR_FREE(dev); } - -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, - const char *name) +int +virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0) + return -1; + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0) + return -1; + + return 0; } -const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev) +void +virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + const char **drv_name, + const char **dom_name) { - return dev->used_by; + *drv_name = dev->used_by_drvname; + *dom_name = dev->used_by_domname; } const char *virUSBDeviceGetName(virUSBDevicePtr dev) diff --git a/src/util/virusb.h b/src/util/virusb.h index aa59d12..41e680f 100644 --- a/src/util/virusb.h +++ b/src/util/virusb.h @@ -60,8 +60,12 @@ int virUSBDeviceFind(unsigned int vendor, virUSBDevicePtr *usb); void virUSBDeviceFree(virUSBDevicePtr dev); -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, const char *name); -const char *virUSBDeviceGetUsedBy(virUSBDevicePtr dev); +int virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name); +void virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + const char **drv_name, + const char **dom_name); const char *virUSBDeviceGetName(virUSBDevicePtr dev); unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev); -- 1.6.0.2 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list