[libvirt] [RESEND][PATCHv5 0/4] write separate module for hostdev passthrough

[rebased to latest libvirt code for applying and reviewing the patches] These patches implements a separate module for hostdev passthrough so that it could be shared by different drivers and can maintain a global state of a host device. Plus, add passthrough to libxl driver, and change qemu driver and lxc driver to use hostdev common library instead of their own hostdev APIs. --- Changes to v4: * change the way checking hypervisor driver name to decide whether use pciback or pci-stub as stub driver, instead, using driver callback to handle that. * remove get active/inactive list APIs from hostdev common library since currently no code uses them. * add nodedev-detach/reattach/reset to libxl driver * other fixes to Daniel and Jim's comments v4 is here: https://www.redhat.com/archives/libvir-list/2013-August/msg00806.html Changes to v3: * fix Jim's comments v3 is here: https://www.redhat.com/archives/libvir-list/2013-August/msg00019.html Changes to v2: * add patches for qemu driver and lxc driver, use common library APIs instead of their own hostdev APIs. * add APIs for nodedev-detach and nodedev-reattach calling. * rename functions to use unified prefix 'virHostdev' * use VIR_ONCE_GLOBAL_INIT() as others instead of previous Init and Cleanup. * use VIR_STRDUP instead of strdup * rebase to latest code v2 is here: https://www.redhat.com/archives/libvir-list/2013-June/msg00263.html Chunyan Liu (4): add hostdev passthrough common library add pci passthrough to libxl driver change qemu driver to use hostdev common library change lxc driver to use hostdev common library docs/schemas/domaincommon.rng | 1 + po/POTFILES.in | 3 +- src/Makefile.am | 3 +- src/conf/domain_conf.c | 3 +- src/conf/domain_conf.h | 1 + src/libvirt_private.syms | 15 + src/libxl/libxl_conf.c | 63 ++ src/libxl/libxl_conf.h | 4 + src/libxl/libxl_domain.c | 9 + src/libxl/libxl_driver.c | 443 ++++++++++++++- src/lxc/lxc_conf.h | 4 - src/lxc/lxc_driver.c | 45 +- src/lxc/lxc_hostdev.c | 413 ------------- src/lxc/lxc_hostdev.h | 43 -- src/lxc/lxc_process.c | 21 +- src/qemu/qemu_command.c | 1 - src/qemu/qemu_conf.h | 9 +- src/qemu/qemu_domain.c | 9 + src/qemu/qemu_driver.c | 72 +-- src/qemu/qemu_hostdev.c | 1289 --------------------------------------- src/qemu/qemu_hostdev.h | 72 --- src/qemu/qemu_hotplug.c | 126 ++--- src/qemu/qemu_process.c | 34 +- src/util/virhostdev.c | 1335 +++++++++++++++++++++++++++++++++++++++++ src/util/virhostdev.h | 104 ++++ src/util/virpci.c | 28 +- src/util/virpci.h | 9 +- src/util/virscsi.c | 26 +- src/util/virscsi.h | 8 +- src/util/virusb.c | 27 +- src/util/virusb.h | 8 +- 31 files changed, 2195 insertions(+), 2033 deletions(-) delete mode 100644 src/lxc/lxc_hostdev.c delete mode 100644 src/lxc/lxc_hostdev.h delete mode 100644 src/qemu/qemu_hostdev.c delete mode 100644 src/qemu/qemu_hostdev.h create mode 100644 src/util/virhostdev.c create mode 100644 src/util/virhostdev.h

Add hostdev passthrough common library so that it could be shared by all drivers and maintain a global hostdev state. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- docs/schemas/domaincommon.rng | 1 + po/POTFILES.in | 1 + src/Makefile.am | 1 + src/conf/domain_conf.c | 3 +- src/conf/domain_conf.h | 1 + src/libvirt_private.syms | 15 + src/libxl/libxl_domain.c | 9 + src/qemu/qemu_domain.c | 9 + src/util/virhostdev.c | 1335 +++++++++++++++++++++++++++++++++++++++++ src/util/virhostdev.h | 104 ++++ src/util/virpci.c | 28 +- src/util/virpci.h | 9 +- src/util/virscsi.c | 26 +- src/util/virscsi.h | 8 +- src/util/virusb.c | 27 +- src/util/virusb.h | 8 +- 16 files changed, 1556 insertions(+), 29 deletions(-) create mode 100644 src/util/virhostdev.c create mode 100644 src/util/virhostdev.h diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index ecd3a42..c75e96c 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3312,6 +3312,7 @@ <choice> <value>kvm</value> <value>vfio</value> + <value>xen</value> </choice> </attribute> <empty/> diff --git a/po/POTFILES.in b/po/POTFILES.in index 2a0605b..ec205c9 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -196,6 +196,7 @@ src/util/viruri.c src/util/virusb.c src/util/virutil.c src/util/virxml.c +src/util/virhostdev.c src/vbox/vbox_MSCOMGlue.c src/vbox/vbox_XPCOMCGlue.c src/vbox/vbox_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index 711da32..e563960 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -148,6 +148,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/conf/domain_conf.c b/src/conf/domain_conf.c index aed2a9d..f75db9a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -611,7 +611,8 @@ VIR_ENUM_IMPL(virDomainHostdevSubsysPciBackend, VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST, "default", "kvm", - "vfio") + "vfio", + "xen") VIR_ENUM_IMPL(virDomainHostdevCaps, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST, "storage", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 539bc1c..be19b12 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -402,6 +402,7 @@ typedef enum { VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, /* currently kvm, could change */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM, /* force legacy kvm style */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO, /* force vfio */ + VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN, /* force legacy xen style, use pciback */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST } virDomainHostdevSubsysPciBackendType; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 35f0f1b..5247fed 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1428,6 +1428,21 @@ virHookInitialize; virHookPresent; +#util/virhostdev.h +virHostdevManagerGetDefault; +virHostdevPciNodeDeviceDetach; +virHostdevPciNodeDeviceReAttach; +virHostdevPciNodeDeviceReset; +virHostdevPrepareDomainHostdevs; +virHostdevPreparePciHostdevs; +virHostdevPrepareScsiHostdevs; +virHostdevPrepareUsbHostdevs; +virHostdevReAttachDomainHostdevs; +virHostdevReAttachPciHostdevs; +virHostdevReAttachScsiHostdevs; +virHostdevReAttachUsbHostdevs; + + # util/viridentity.h virIdentityGetAttr; virIdentityGetCurrent; diff --git a/src/libxl/libxl_domain.c b/src/libxl/libxl_domain.c index 0abf80b..4d6d07b 100644 --- a/src/libxl/libxl_domain.c +++ b/src/libxl/libxl_domain.c @@ -393,6 +393,15 @@ libxlDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, STRNEQ(def->os.type, "hvm")) dev->data.chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN; + if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + + 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) + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN; + } + return 0; } diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index f2cca70..91365d3 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -904,6 +904,15 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, dev->data.chr->source.data.nix.listen = true; } + if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + + 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) + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + } + ret = 0; cleanup: diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c new file mode 100644 index 0000000..d131160 --- /dev/null +++ b/src/util/virhostdev.c @@ -0,0 +1,1335 @@ +/* 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@suse.com> + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "virhostdev.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.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" + +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 virMacAddrPtr 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; +} + +static int +virHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, + char *stateDir) +{ + char *linkdev = NULL; + virNetDevVPortProfilePtr virtPort; + int ret = -1; + int vf = -1; + int port_profile_associate = 0; + 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; + + 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); + + 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; +} + +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; + int last_processed_hostdev_vf = -1; + size_t i; + int ret = -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + 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; + + 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; i < last_processed_hostdev_vf; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET && + hostdev->parent.data.net) { + virHostdevNetConfigRestore(hostdev, mgr->stateDir); + } + } + +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; +} + +void +virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + virPCIDeviceListPtr pcidevs; + size_t i; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + 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; + } + } + + /* virObjectUnref() will take care of freeing the dev. */ + 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++) { + 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) { + virHostdevNetConfigRestore(hostdev, mgr->stateDir); + } + } + + 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: + 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..16916d7 --- /dev/null +++ b/src/util/virhostdev.h @@ -0,0 +1,104 @@ +/* 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@suse.com> + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_HOSTDEV_H__ +# define __VIR_HOSTDEV_H__ + +# include "internal.h" + +# include "domain_conf.h" +# include "virpci.h" + +typedef enum { + VIR_SP_PCI_HOSTDEV = (1 << 0), /* support pci passthrough */ + VIR_SP_USB_HOSTDEV = (1 << 1), /* support usb passthrough */ + VIR_SP_SCSI_HOSTDEV = (1 << 2), /* support scsi passthrough */ + + VIR_COLD_BOOT = (1 << 8), /* cold boot */ + VIR_STRICT_ACS_CHECK = (1 << 9), /* strict acs check */ +} virHostdevManagerFlag; + +typedef struct _virHostdevManager virHostdevManager; +typedef virHostdevManager *virHostdevManagerPtr; + +struct virHostdevNameList { + char **names; + size_t nnames; +}; +typedef struct virHostdevNameList *virHostdevNameListPtr; + +virHostdevManagerPtr virHostdevManagerGetDefault(void); + +/* 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); + +/* 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 92927aa..ee45bea 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; unsigned int pcie_cap_pos; unsigned int pci_pm_cap_pos; @@ -1641,16 +1644,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 32e438b..211ed0b 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; }; @@ -265,17 +268,26 @@ virSCSIDeviceFree(virSCSIDevicePtr dev) 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..6697bd2 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 { @@ -378,16 +381,26 @@ virUSBDeviceFree(virUSBDevicePtr dev) 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

On Fri, Sep 13, 2013 at 11:34:34AM +0800, Chunyan Liu wrote:
Add hostdev passthrough common library so that it could be shared by all drivers and maintain a global hostdev state.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- docs/schemas/domaincommon.rng | 1 + src/conf/domain_conf.c | 3 +- src/conf/domain_conf.h | 1 +
I'd prefer to see these changes to the XML parser be done in a separate patch - either before or after this patch.
diff --git a/src/libxl/libxl_domain.c b/src/libxl/libxl_domain.c index 0abf80b..4d6d07b 100644 --- a/src/libxl/libxl_domain.c +++ b/src/libxl/libxl_domain.c @@ -393,6 +393,15 @@ libxlDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, STRNEQ(def->os.type, "hvm")) dev->data.chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN;
+ if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + + 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) + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN; + } + return 0; }
This should be separate too.
diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c new file mode 100644 index 0000000..d131160 --- /dev/null +++ b/src/util/virhostdev.c @@ -0,0 +1,1335 @@ +/* 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@suse.com> + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "virhostdev.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.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" + +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;
Hmm, this is going to lead to some upgrade problems when libvirt is restarted with existing VMs present. I think we'll need some code in this initialization which looks for the old QEMU-specific path and if found, tries to move the data into the new location.
+ + 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 virMacAddrPtr 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; +} + +static int +virHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, + char *stateDir) +{ + char *linkdev = NULL; + virNetDevVPortProfilePtr virtPort; + int ret = -1; + int vf = -1; + int port_profile_associate = 0; + 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; + + 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); + + 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; +} + +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; + int last_processed_hostdev_vf = -1; + size_t i; + int ret = -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + 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; + + 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; i < last_processed_hostdev_vf; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET && + hostdev->parent.data.net) { + virHostdevNetConfigRestore(hostdev, mgr->stateDir); + } + } + +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; +} + +void +virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + virPCIDeviceListPtr pcidevs; + size_t i; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + 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; + } + } + + /* virObjectUnref() will take care of freeing the dev. */ + 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++) { + 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) { + virHostdevNetConfigRestore(hostdev, mgr->stateDir); + } + } + + 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: + 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..16916d7 --- /dev/null +++ b/src/util/virhostdev.h @@ -0,0 +1,104 @@ +/* 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@suse.com> + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_HOSTDEV_H__ +# define __VIR_HOSTDEV_H__ + +# include "internal.h" + +# include "domain_conf.h" +# include "virpci.h" + +typedef enum { + VIR_SP_PCI_HOSTDEV = (1 << 0), /* support pci passthrough */ + VIR_SP_USB_HOSTDEV = (1 << 1), /* support usb passthrough */ + VIR_SP_SCSI_HOSTDEV = (1 << 2), /* support scsi passthrough */ + + VIR_COLD_BOOT = (1 << 8), /* cold boot */ + VIR_STRICT_ACS_CHECK = (1 << 9), /* strict acs check */ +} virHostdevManagerFlag; + +typedef struct _virHostdevManager virHostdevManager; +typedef virHostdevManager *virHostdevManagerPtr; + +struct virHostdevNameList { + char **names; + size_t nnames; +}; +typedef struct virHostdevNameList *virHostdevNameListPtr; + +virHostdevManagerPtr virHostdevManagerGetDefault(void); + +/* 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); + +/* 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 92927aa..ee45bea 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;
Something needs to be free'ing these names when virPCIDevice is freed.
diff --git a/src/util/virscsi.c b/src/util/virscsi.c index 32e438b..211ed0b 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;
Again, need code to free theses.
diff --git a/src/util/virusb.c b/src/util/virusb.c index e901618..6697bd2 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;
And again. I've not looked at the main bulk of code in detail, since I'm assuming that was copied without functiona change from the QEMU driver. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Add pci passthrough to libxl driver, support attach-device, detach-device and start a vm with pci hostdev specified. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 2 +- src/libxl/libxl_conf.c | 63 +++++++ src/libxl/libxl_conf.h | 4 + src/libxl/libxl_driver.c | 443 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 510 insertions(+), 2 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index ec205c9..d01cb99 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 @@ -196,7 +197,6 @@ src/util/viruri.c src/util/virusb.c src/util/virutil.c src/util/virxml.c -src/util/virhostdev.c src/vbox/vbox_MSCOMGlue.c src/vbox/vbox_XPCOMCGlue.c src/vbox/vbox_driver.c diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index d4226b8..31d8f8a 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -1128,6 +1128,66 @@ libxlDriverConfigGet(libxlDriverPrivatePtr driver) return cfg; } +int +libxlMakePci(virDomainHostdevDefPtr hostdev, libxl_device_pci *pcidev) +{ + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + return -1; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return -1; + + pcidev->domain = hostdev->source.subsys.u.pci.addr.domain; + pcidev->bus = hostdev->source.subsys.u.pci.addr.bus; + pcidev->dev = hostdev->source.subsys.u.pci.addr.slot; + pcidev->func = hostdev->source.subsys.u.pci.addr.function; + + return 0; +} + +static int +libxlMakePciList(virDomainDefPtr def, libxl_domain_config *d_config) +{ + virDomainHostdevDefPtr *l_hostdevs = def->hostdevs; + size_t nhostdevs = def->nhostdevs; + size_t npcidevs = 0; + libxl_device_pci *x_pcidevs; + size_t i, j; + + if (nhostdevs == 0) + return 0; + + if (VIR_ALLOC_N(x_pcidevs, nhostdevs) < 0) + return -1; + + for (i = 0, j = 0; i < nhostdevs; i++) { + if (l_hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (l_hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + libxl_device_pci_init(&x_pcidevs[j]); + + if (libxlMakePci(l_hostdevs[i], &x_pcidevs[j]) < 0) + goto error; + + npcidevs++; + j++; + } + + VIR_SHRINK_N(x_pcidevs, nhostdevs, nhostdevs - npcidevs); + d_config->pcidevs = x_pcidevs; + d_config->num_pcidevs = npcidevs; + + return 0; + +error: + for (i = 0; i < npcidevs; i++) + libxl_device_pci_dispose(&x_pcidevs[i]); + + VIR_FREE(x_pcidevs); + return -1; +} + virCapsPtr libxlMakeCapabilities(libxl_ctx *ctx) { @@ -1176,6 +1236,9 @@ libxlBuildDomainConfig(libxlDriverPrivatePtr driver, if (libxlMakeVfbList(driver, def, d_config) < 0) return -1; + if (libxlMakePciList(def, d_config) < 0) + return -1; + d_config->on_reboot = def->onReboot; d_config->on_poweroff = def->onPoweroff; d_config->on_crash = def->onCrash; diff --git a/src/libxl/libxl_conf.h b/src/libxl/libxl_conf.h index 8ba0ee4..31a955a 100644 --- a/src/libxl/libxl_conf.h +++ b/src/libxl/libxl_conf.h @@ -38,6 +38,7 @@ # include "virchrdev.h" +# define LIBXL_DRIVER_NAME "xenlight" # define LIBXL_VNC_PORT_MIN 5900 # define LIBXL_VNC_PORT_MAX 65535 @@ -148,6 +149,9 @@ libxlMakeVfb(libxlDriverPrivatePtr driver, virDomainGraphicsDefPtr l_vfb, libxl_device_vfb *x_vfb); int +libxlMakePci(virDomainHostdevDefPtr hostdev, libxl_device_pci *pcidev); + +int libxlBuildDomainConfig(libxlDriverPrivatePtr driver, virDomainObjPtr vm, libxl_domain_config *d_config); diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index e2a6d44..5bddf45 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -52,6 +52,8 @@ #include "virsysinfo.h" #include "viraccessapicheck.h" #include "viratomic.h" +#include "virhostdev.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_LIBXL @@ -328,6 +330,7 @@ libxlVmReap(libxlDriverPrivatePtr driver, virDomainShutoffReason reason) { libxlDomainObjPrivatePtr priv = vm->privateData; + virHostdevManagerPtr hostdev_mgr; if (libxl_domain_destroy(priv->ctx, vm->def->id, NULL) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -335,6 +338,10 @@ libxlVmReap(libxlDriverPrivatePtr driver, return -1; } + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachDomainHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV); + libxlVmCleanup(driver, vm, reason); return 0; } @@ -553,6 +560,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, int managed_save_fd = -1; libxlDomainObjPrivatePtr priv = vm->privateData; libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver); + virHostdevManagerPtr hostdev_mgr; if (libxlDomainObjPrivateInitCtx(vm) < 0) goto error; @@ -611,6 +619,12 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, goto error; } + VIR_DEBUG("Preparing host PCI devices"); + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPrepareDomainHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV) < 0) + goto error; + /* use as synchronous operations => ao_how = NULL and no intermediate reports => ao_progress = NULL */ if (restore_fd < 0) @@ -706,6 +720,7 @@ libxlReconnectDomain(virDomainObjPtr vm, libxl_dominfo d_info; int len; uint8_t *data = NULL; + virHostdevManagerPtr hostdev_mgr; virObjectLock(vm); @@ -729,6 +744,13 @@ libxlReconnectDomain(virDomainObjPtr vm, /* Update domid in case it changed (e.g. reboot) while we were gone? */ vm->def->id = d_info.domid; + + /* Update hostdev state */ + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPrepareDomainHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV) < 0) + goto out; + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNKNOWN); if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback) @@ -2896,6 +2918,95 @@ cleanup: } static int +libxlDomainAttachHostPciDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev) +{ + libxl_device_pci pcidev; + virDomainHostdevDefPtr found; + virHostdevManagerPtr hostdev_mgr; + + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return -1; + + if (virDomainHostdevFind(vm->def, hostdev, &found) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("target pci device %.4x:%.2x:%.2x.%.1x already exists"), + 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); + return -1; + } + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) + return -1; + + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPreparePciHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def->name, vm->def->uuid, + &hostdev, 1, 0) < 0) { + goto cleanup; + } + + if (libxlMakePci(hostdev, &pcidev) < 0) + goto reattach_hostdev; + + if (libxl_device_pci_add(priv->ctx, vm->def->id, &pcidev, 0) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight failed to attach pci device %.4x:%.2x:%.2x.%.1x"), + 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); + goto reattach_hostdev; + } + + vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; + return 0; + +reattach_hostdev: + virHostdevReAttachPciHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def->name, &hostdev, 1); + +cleanup: + return -1; +} + +static int +libxlDomainAttachHostDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev mode '%s' not supported"), + virDomainHostdevModeTypeToString(hostdev->mode)); + return -1; + } + + switch (hostdev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: + if (libxlDomainAttachHostPciDevice(priv, vm, hostdev) < 0) + goto error; + break; + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev subsys type '%s' not supported"), + virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type)); + goto error; + } + + return 0; + +error: + return -1; +} + +static int libxlDomainDetachDeviceDiskLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -2962,6 +3073,12 @@ libxlDomainAttachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, dev->data.disk = NULL; break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = libxlDomainAttachHostDevice(priv, vm, dev); + if (!ret) + dev->data.hostdev = NULL; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), @@ -2976,6 +3093,8 @@ static int libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { virDomainDiskDefPtr disk; + virDomainHostdevDefPtr hostdev; + virDomainHostdevDefPtr found; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -2990,6 +3109,25 @@ libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) /* vmdef has the pointer. Generic codes for vmdef will do all jobs */ dev->data.disk = NULL; break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + hostdev = dev->data.hostdev; + + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return -1; + + if (virDomainHostdevFind(vmdef, hostdev, &found) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("target pci device %.4x:%.2x:%.2x.%.1x\ + already exists"), + 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); + return -1; + } + + virDomainHostdevInsert(vmdef, hostdev); + break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -3000,6 +3138,124 @@ libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) } static int +libxlComparePCIDevice(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr device ATTRIBUTE_UNUSED, + virDomainDeviceInfoPtr info1, + void *opaque) +{ + virDomainDeviceInfoPtr info2 = opaque; + + if (info1->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI || + info2->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + return 0; + + if (info1->addr.pci.domain == info2->addr.pci.domain && + info1->addr.pci.bus == info2->addr.pci.bus && + info1->addr.pci.slot == info2->addr.pci.slot && + info1->addr.pci.function != info2->addr.pci.function) + return -1; + return 0; +} + +static bool +libxlIsMultiFunctionDevice(virDomainDefPtr def, + virDomainDeviceInfoPtr dev) +{ + if (virDomainDeviceInfoIterate(def, libxlComparePCIDevice, dev) < 0) + return true; + return false; +} + +static int +libxlDomainDetachHostPciDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev) +{ + virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; + libxl_device_pci pcidev; + virDomainHostdevDefPtr detach; + int idx; + virHostdevManagerPtr hostdev_mgr; + + if (subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return -1; + + idx = virDomainHostdevFind(vm->def, hostdev, &detach); + if (idx < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("host pci device %.4x:%.2x:%.2x.%.1x not found"), + subsys->u.pci.addr.domain, subsys->u.pci.addr.bus, + subsys->u.pci.addr.slot, subsys->u.pci.addr.function); + return -1; + } + + if (libxlIsMultiFunctionDevice(vm->def, detach->info)) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("cannot hot unplug multifunction PCI device: %.4x:%.2x:%.2x.%.1x"), + subsys->u.pci.addr.domain, subsys->u.pci.addr.bus, + subsys->u.pci.addr.slot, subsys->u.pci.addr.function); + goto cleanup; + } + + + libxl_device_pci_init(&pcidev); + + if (libxlMakePci(detach, &pcidev) < 0) + goto cleanup; + + if (libxl_device_pci_remove(priv->ctx, vm->def->id, &pcidev, 0) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight failed to detach pci device\ + %.4x:%.2x:%.2x.%.1x"), + subsys->u.pci.addr.domain, subsys->u.pci.addr.bus, + subsys->u.pci.addr.slot, subsys->u.pci.addr.function); + goto cleanup; + } + + libxl_device_pci_dispose(&pcidev); + + virDomainHostdevRemove(vm->def, idx); + + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachPciHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def->name, &hostdev, 1); + + return 0; + +cleanup: + virDomainHostdevDefFree(detach); + return -1; +} + +static int +libxlDomainDetachHostDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev mode '%s' not supported"), + virDomainHostdevModeTypeToString(hostdev->mode)); + return -1; + } + + switch (subsys->type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: + return libxlDomainDetachHostPciDevice(priv, vm, hostdev); + + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected hostdev type %d"), subsys->type); + break; + } + + return -1; +} + +static int libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -3010,6 +3266,10 @@ libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, ret = libxlDomainDetachDeviceDiskLive(priv, vm, dev); break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = libxlDomainDetachHostDevice(priv, vm, dev); + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be detached"), @@ -3020,6 +3280,7 @@ libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, return ret; } + static int libxlDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { @@ -4180,10 +4441,186 @@ libxlConnectSupportsFeature(virConnectPtr conn, int feature) } } +static int +libxlNodeDeviceGetPciInfo(virNodeDeviceDefPtr def, + unsigned *domain, + unsigned *bus, + unsigned *slot, + unsigned *function) +{ + virNodeDevCapsDefPtr cap; + int ret = -1; + + cap = def->caps; + while (cap) { + if (cap->type == VIR_NODE_DEV_CAP_PCI_DEV) { + *domain = cap->data.pci_dev.domain; + *bus = cap->data.pci_dev.bus; + *slot = cap->data.pci_dev.slot; + *function = cap->data.pci_dev.function; + break; + } + + cap = cap->next; + } + + if (!cap) { + virReportError(VIR_ERR_INVALID_ARG, + _("device %s is not a PCI device"), def->name); + goto out; + } + + ret = 0; +out: + return ret; +} + +static int +libxlNodeDeviceDetachFlags(virNodeDevicePtr dev, + const char *driverName, + unsigned int flags) +{ + virPCIDevicePtr pci = NULL; + unsigned domain = 0, bus = 0, slot = 0, function = 0; + int ret = -1; + virNodeDeviceDefPtr def = NULL; + char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; + + virCheckFlags(0, -1); + + xml = virNodeDeviceGetXMLDesc(dev, 0); + if (!xml) + goto cleanup; + + def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL); + if (!def) + goto cleanup; + + if (virNodeDeviceDetachFlagsEnsureACL(dev->conn, def) < 0) + goto cleanup; + + if (libxlNodeDeviceGetPciInfo(def, &domain, &bus, &slot, &function) < 0) + goto cleanup; + + pci = virPCIDeviceNew(domain, bus, slot, function); + if (!pci) + goto cleanup; + + if (!driverName || STREQ(driverName, "xen")) { + if (virPCIDeviceSetStubDriver(pci, "pciback") < 0) + goto cleanup; + } else { + virReportError(VIR_ERR_INVALID_ARG, + _("unsupported driver name '%s'"), driverName); + goto cleanup; + } + + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPciNodeDeviceDetach(hostdev_mgr, pci) < 0) { + goto cleanup; + } + + ret = 0; +cleanup: + virPCIDeviceFree(pci); + virNodeDeviceDefFree(def); + VIR_FREE(xml); + return ret; +} + +static int +libxlNodeDeviceDettach(virNodeDevicePtr dev) +{ + return libxlNodeDeviceDetachFlags(dev, NULL, 0); +} + +static int +libxlNodeDeviceReAttach(virNodeDevicePtr dev) +{ + virPCIDevicePtr pci = NULL; + unsigned domain = 0, bus = 0, slot = 0, function = 0; + int ret = -1; + virNodeDeviceDefPtr def = NULL; + char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; + + xml = virNodeDeviceGetXMLDesc(dev, 0); + if (!xml) + goto cleanup; + + def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL); + if (!def) + goto cleanup; + + if (virNodeDeviceReAttachEnsureACL(dev->conn, def) < 0) + goto cleanup; + + if (libxlNodeDeviceGetPciInfo(def, &domain, &bus, &slot, &function) < 0) + goto cleanup; + + pci = virPCIDeviceNew(domain, bus, slot, function); + if (!pci) + goto cleanup; + + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPciNodeDeviceReAttach(hostdev_mgr, pci) < 0) + goto out; + + ret = 0; +out: + virPCIDeviceFree(pci); +cleanup: + virNodeDeviceDefFree(def); + VIR_FREE(xml); + return ret; +} + +static int +libxlNodeDeviceReset(virNodeDevicePtr dev) +{ + virPCIDevicePtr pci; + unsigned domain = 0, bus = 0, slot = 0, function = 0; + int ret = -1; + virNodeDeviceDefPtr def = NULL; + char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; + + xml = virNodeDeviceGetXMLDesc(dev, 0); + if (!xml) + goto cleanup; + + def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL); + if (!def) + goto cleanup; + + if (virNodeDeviceResetEnsureACL(dev->conn, def) < 0) + goto cleanup; + + if (libxlNodeDeviceGetPciInfo(def, &domain, &bus, &slot, &function) < 0) + goto cleanup; + + pci = virPCIDeviceNew(domain, bus, slot, function); + if (!pci) + goto cleanup; + + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPciNodeDeviceReset(hostdev_mgr, pci) < 0) + goto out; + + ret = 0; +out: + virPCIDeviceFree(pci); +cleanup: + virNodeDeviceDefFree(def); + VIR_FREE(xml); + return ret; +} + static virDriver libxlDriver = { .no = VIR_DRV_LIBXL, - .name = "xenlight", + .name = LIBXL_DRIVER_NAME, .connectOpen = libxlConnectOpen, /* 0.9.0 */ .connectClose = libxlConnectClose, /* 0.9.0 */ .connectGetType = libxlConnectGetType, /* 0.9.0 */ @@ -4264,6 +4701,10 @@ static virDriver libxlDriver = { .connectDomainEventDeregisterAny = libxlConnectDomainEventDeregisterAny, /* 0.9.0 */ .connectIsAlive = libxlConnectIsAlive, /* 0.9.8 */ .connectSupportsFeature = libxlConnectSupportsFeature, /* 1.1.1 */ + .nodeDeviceDettach = libxlNodeDeviceDettach, /* 1.1.3 */ + .nodeDeviceDetachFlags = libxlNodeDeviceDetachFlags, /* 1.1.3 */ + .nodeDeviceReAttach = libxlNodeDeviceReAttach, /* 1.1.3 */ + .nodeDeviceReset = libxlNodeDeviceReset, /* 1.1.3 */ }; static virStateDriver libxlStateDriver = { -- 1.6.0.2

On Fri, Sep 13, 2013 at 11:34:35AM +0800, Chunyan Liu wrote:
Add pci passthrough to libxl driver, support attach-device, detach-device and start a vm with pci hostdev specified.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 2 +- src/libxl/libxl_conf.c | 63 +++++++ src/libxl/libxl_conf.h | 4 + src/libxl/libxl_driver.c | 443 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 510 insertions(+), 2 deletions(-)
diff --git a/po/POTFILES.in b/po/POTFILES.in index ec205c9..d01cb99 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 @@ -196,7 +197,6 @@ src/util/viruri.c src/util/virusb.c src/util/virutil.c src/util/virxml.c -src/util/virhostdev.c src/vbox/vbox_MSCOMGlue.c src/vbox/vbox_XPCOMCGlue.c src/vbox/vbox_driver.c
This change is bogus - the sort order should be fixed in the previous patch.
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index d4226b8..31d8f8a 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -335,6 +338,10 @@ libxlVmReap(libxlDriverPrivatePtr driver, return -1; }
+ hostdev_mgr = virHostdevManagerGetDefault();
You must check for NULL return.
+ virHostdevReAttachDomainHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV); + libxlVmCleanup(driver, vm, reason); return 0; } @@ -553,6 +560,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, int managed_save_fd = -1; libxlDomainObjPrivatePtr priv = vm->privateData; libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver); + virHostdevManagerPtr hostdev_mgr;
if (libxlDomainObjPrivateInitCtx(vm) < 0) goto error; @@ -611,6 +619,12 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, goto error; }
+ VIR_DEBUG("Preparing host PCI devices"); + hostdev_mgr = virHostdevManagerGetDefault();
Again check for NULL
+ if (virHostdevPrepareDomainHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV) < 0) + goto error; + /* use as synchronous operations => ao_how = NULL and no intermediate reports => ao_progress = NULL */
if (restore_fd < 0) @@ -706,6 +720,7 @@ libxlReconnectDomain(virDomainObjPtr vm, libxl_dominfo d_info; int len; uint8_t *data = NULL; + virHostdevManagerPtr hostdev_mgr;
virObjectLock(vm);
@@ -729,6 +744,13 @@ libxlReconnectDomain(virDomainObjPtr vm,
/* Update domid in case it changed (e.g. reboot) while we were gone? */ vm->def->id = d_info.domid; + + /* Update hostdev state */ + hostdev_mgr = virHostdevManagerGetDefault();
Check for NULL. Many more cases follow, so I won't repeat this point again.
static virDriver libxlDriver = { .no = VIR_DRV_LIBXL, - .name = "xenlight", + .name = LIBXL_DRIVER_NAME,
This change should be separate. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Change qemu driver to use hostdev common library instead of APIs in qemu_hostdev.[ch] Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 1 - src/Makefile.am | 1 - src/qemu/qemu_command.c | 1 - src/qemu/qemu_conf.h | 9 +- src/qemu/qemu_driver.c | 72 +--- src/qemu/qemu_hostdev.c | 1289 ----------------------------------------------- src/qemu/qemu_hostdev.h | 72 --- src/qemu/qemu_hotplug.c | 126 ++--- src/qemu/qemu_process.c | 34 +- 9 files changed, 87 insertions(+), 1518 deletions(-) delete mode 100644 src/qemu/qemu_hostdev.c delete mode 100644 src/qemu/qemu_hostdev.h diff --git a/po/POTFILES.in b/po/POTFILES.in index d01cb99..2fc7e67 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -99,7 +99,6 @@ src/qemu/qemu_command.c src/qemu/qemu_conf.c src/qemu/qemu_domain.c src/qemu/qemu_driver.c -src/qemu/qemu_hostdev.c src/qemu/qemu_hotplug.c src/qemu/qemu_migration.c src/qemu/qemu_monitor.c diff --git a/src/Makefile.am b/src/Makefile.am index e563960..c3a8ba0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -654,7 +654,6 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_command.c qemu/qemu_command.h \ qemu/qemu_domain.c qemu/qemu_domain.h \ qemu/qemu_cgroup.c qemu/qemu_cgroup.h \ - qemu/qemu_hostdev.c qemu/qemu_hostdev.h \ qemu/qemu_hotplug.c qemu/qemu_hotplug.h \ qemu/qemu_hotplugpriv.h \ qemu/qemu_conf.c qemu/qemu_conf.h \ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 1521431..3abb88d 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -24,7 +24,6 @@ #include <config.h> #include "qemu_command.h" -#include "qemu_hostdev.h" #include "qemu_capabilities.h" #include "qemu_bridge_filter.h" #include "cpu/cpu.h" diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 206f2c6..97c8501 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -52,6 +52,7 @@ # error "Port me" # endif +# define QEMU_DRIVER_NAME "QEMU" typedef struct _virQEMUDriver virQEMUDriver; typedef virQEMUDriver *virQEMUDriverPtr; @@ -204,14 +205,6 @@ struct _virQEMUDriver { /* Immutable pointer. self-locking APIs */ virSecurityManagerPtr securityManager; - /* Immutable pointers. Requires locks to be held before - * calling APIs. activePciHostdevs must be locked before - * inactivePciHostdevs */ - virPCIDeviceListPtr activePciHostdevs; - virPCIDeviceListPtr inactivePciHostdevs; - virUSBDeviceListPtr activeUsbHostdevs; - virSCSIDeviceListPtr activeScsiHostdevs; - /* Immutable pointer. Unsafe APIs. XXX */ virHashTablePtr sharedDevices; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index bbf2d23..b289a30 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -50,7 +50,6 @@ #include "qemu_capabilities.h" #include "qemu_command.h" #include "qemu_cgroup.h" -#include "qemu_hostdev.h" #include "qemu_hotplug.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" @@ -94,11 +93,10 @@ #include "virstring.h" #include "viraccessapicheck.h" #include "viraccessapicheckqemu.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_QEMU -#define QEMU_DRIVER_NAME "QEMU" - #define QEMU_NB_MEM_PARAM 3 #define QEMU_NB_BLOCK_IO_TUNE_PARAM 6 @@ -691,18 +689,6 @@ qemuStateInitialize(bool privileged, if (qemuSecurityInit(qemu_driver) < 0) goto error; - if ((qemu_driver->activePciHostdevs = virPCIDeviceListNew()) == NULL) - goto error; - - if ((qemu_driver->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) - goto error; - - if ((qemu_driver->inactivePciHostdevs = virPCIDeviceListNew()) == NULL) - goto error; - - if ((qemu_driver->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL) - goto error; - if (!(qemu_driver->sharedDevices = virHashCreate(30, qemuSharedDeviceEntryFree))) goto error; @@ -983,9 +969,6 @@ qemuStateCleanup(void) { virNWFilterUnRegisterCallbackDriver(&qemuCallbackDriver); virObjectUnref(qemu_driver->config); - virObjectUnref(qemu_driver->activePciHostdevs); - virObjectUnref(qemu_driver->inactivePciHostdevs); - virObjectUnref(qemu_driver->activeUsbHostdevs); virHashFree(qemu_driver->sharedDevices); virObjectUnref(qemu_driver->caps); virQEMUCapsCacheFree(qemu_driver->qemuCapsCache); @@ -10689,12 +10672,12 @@ qemuNodeDeviceDetachFlags(virNodeDevicePtr dev, const char *driverName, unsigned int flags) { - virQEMUDriverPtr driver = dev->conn->privateData; virPCIDevicePtr pci = NULL; unsigned domain = 0, bus = 0, slot = 0, function = 0; int ret = -1; virNodeDeviceDefPtr def = NULL; char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; virCheckFlags(0, -1); @@ -10728,18 +10711,12 @@ qemuNodeDeviceDetachFlags(virNodeDevicePtr dev, goto cleanup; } - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - - if (virPCIDeviceDetach(pci, driver->activePciHostdevs, - driver->inactivePciHostdevs) < 0) { - goto out; + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPciNodeDeviceDetach(hostdev_mgr, pci) < 0) { + goto cleanup; } ret = 0; -out: - virObjectUnlock(driver->inactivePciHostdevs); - virObjectUnlock(driver->activePciHostdevs); cleanup: virPCIDeviceFree(pci); virNodeDeviceDefFree(def); @@ -10756,13 +10733,12 @@ qemuNodeDeviceDettach(virNodeDevicePtr dev) static int qemuNodeDeviceReAttach(virNodeDevicePtr dev) { - virQEMUDriverPtr driver = dev->conn->privateData; virPCIDevicePtr pci = NULL; - virPCIDevicePtr other; unsigned domain = 0, bus = 0, slot = 0, function = 0; int ret = -1; virNodeDeviceDefPtr def = NULL; char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; xml = virNodeDeviceGetXMLDesc(dev, 0); if (!xml) @@ -10782,33 +10758,12 @@ qemuNodeDeviceReAttach(virNodeDevicePtr dev) if (!pci) goto cleanup; - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - other = virPCIDeviceListFind(driver->activePciHostdevs, pci); - if (other) { - const char *other_name = virPCIDeviceGetUsedBy(other); - - if (other_name) - virReportError(VIR_ERR_OPERATION_INVALID, - _("PCI device %s is still in use by domain %s"), - virPCIDeviceGetName(pci), other_name); - else - virReportError(VIR_ERR_OPERATION_INVALID, - _("PCI device %s is still in use"), - virPCIDeviceGetName(pci)); - goto out; - } - - virPCIDeviceReattachInit(pci); - - if (virPCIDeviceReattach(pci, driver->activePciHostdevs, - driver->inactivePciHostdevs) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPciNodeDeviceReAttach(hostdev_mgr, pci) < 0) goto out; ret = 0; out: - virObjectUnlock(driver->inactivePciHostdevs); - virObjectUnlock(driver->activePciHostdevs); virPCIDeviceFree(pci); cleanup: virNodeDeviceDefFree(def); @@ -10819,12 +10774,12 @@ cleanup: static int qemuNodeDeviceReset(virNodeDevicePtr dev) { - virQEMUDriverPtr driver = dev->conn->privateData; virPCIDevicePtr pci; unsigned domain = 0, bus = 0, slot = 0, function = 0; int ret = -1; virNodeDeviceDefPtr def = NULL; char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; xml = virNodeDeviceGetXMLDesc(dev, 0); if (!xml) @@ -10844,17 +10799,12 @@ qemuNodeDeviceReset(virNodeDevicePtr dev) if (!pci) goto cleanup; - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - - if (virPCIDeviceReset(pci, driver->activePciHostdevs, - driver->inactivePciHostdevs) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPciNodeDeviceReset(hostdev_mgr, pci) < 0) goto out; ret = 0; out: - virObjectUnlock(driver->inactivePciHostdevs); - virObjectUnlock(driver->activePciHostdevs); virPCIDeviceFree(pci); cleanup: virNodeDeviceDefFree(def); diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c deleted file mode 100644 index 21fe47f..0000000 --- a/src/qemu/qemu_hostdev.c +++ /dev/null @@ -1,1289 +0,0 @@ -/* - * qemu_hostdev.c: QEMU hostdev management - * - * 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: Daniel P. Berrange <berrange@redhat.com> - */ - -#include <config.h> - -#include "qemu_hostdev.h" -#include "virlog.h" -#include "virerror.h" -#include "viralloc.h" -#include "virpci.h" -#include "virusb.h" -#include "virscsi.h" -#include "virnetdev.h" - -#define VIR_FROM_THIS VIR_FROM_QEMU - -static virPCIDeviceListPtr -qemuGetPciHostDeviceList(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); - if (hostdev->source.subsys.u.pci.backend - == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) { - if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) { - virObjectUnref(list); - return NULL; - } - } else { - if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) { - virObjectUnref(list); - return NULL; - } - } - } - - return list; -} - -/* - * qemuGetActivePciHostDeviceList - make a new list with a *copy* of - * every virPCIDevice object that is found on the activePciHostdevs - * list *and* is in the hostdev list for this domain. - * - * Return the new list, or NULL if there was a failure. - * - * Pre-condition: driver->activePciHostdevs is locked - */ -static virPCIDeviceListPtr -qemuGetActivePciHostDeviceList(virQEMUDriverPtr driver, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - virPCIDeviceListPtr list; - size_t i; - - if (!(list = virPCIDeviceListNew())) - return NULL; - - for (i = 0; i < nhostdevs; i++) { - virDomainHostdevDefPtr hostdev = hostdevs[i]; - virDevicePCIAddressPtr addr; - virPCIDevicePtr activeDev; - - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - continue; - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) - continue; - - addr = &hostdev->source.subsys.u.pci.addr; - activeDev = virPCIDeviceListFindByIDs(driver->activePciHostdevs, - addr->domain, addr->bus, - addr->slot, addr->function); - if (activeDev && virPCIDeviceListAddCopy(list, activeDev) < 0) { - virObjectUnref(list); - return NULL; - } - } - - return list; -} - -int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, - virDomainDefPtr def) -{ - virDomainHostdevDefPtr hostdev = NULL; - virPCIDevicePtr dev = NULL; - size_t i; - int ret = -1; - - if (!def->nhostdevs) - return 0; - - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - - for (i = 0; i < def->nhostdevs; i++) { - hostdev = def->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); - if (hostdev->source.subsys.u.pci.backend - == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) { - if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) - goto cleanup; - } else { - if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) - goto cleanup; - - } - virPCIDeviceSetUsedBy(dev, def->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(driver->activePciHostdevs, dev) < 0) - goto cleanup; - dev = NULL; - } - - ret = 0; -cleanup: - virPCIDeviceFree(dev); - virObjectUnlock(driver->activePciHostdevs); - virObjectUnlock(driver->inactivePciHostdevs); - return ret; -} - -int -qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, - virDomainDefPtr def) -{ - virDomainHostdevDefPtr hostdev = NULL; - size_t i; - int ret = -1; - - if (!def->nhostdevs) - return 0; - - virObjectLock(driver->activeUsbHostdevs); - for (i = 0; i < def->nhostdevs; i++) { - virUSBDevicePtr usb = NULL; - hostdev = def->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 domain %s", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - def->name); - continue; - } - - virUSBDeviceSetUsedBy(usb, def->name); - - if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { - virUSBDeviceFree(usb); - goto cleanup; - } - } - ret = 0; -cleanup: - virObjectUnlock(driver->activeUsbHostdevs); - return ret; -} - -int -qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver, - virDomainDefPtr def) -{ - virDomainHostdevDefPtr hostdev = NULL; - size_t i; - int ret = -1; - - if (!def->nhostdevs) - return 0; - - virObjectLock(driver->activeScsiHostdevs); - for (i = 0; i < def->nhostdevs; i++) { - virSCSIDevicePtr scsi = NULL; - hostdev = def->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, def->name); - - if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) { - virSCSIDeviceFree(scsi); - goto cleanup; - } - } - ret = 0; - -cleanup: - virObjectUnlock(driver->activeScsiHostdevs); - return ret; -} - -static int -qemuDomainHostdevPciSysfsPath(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); -} - -int -qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) -{ - char *sysfs_path = NULL; - int ret = -1; - - if (qemuDomainHostdevPciSysfsPath(hostdev, &sysfs_path) < 0) - return ret; - - ret = virPCIIsVirtualFunction(sysfs_path); - - VIR_FREE(sysfs_path); - - return ret; -} - -static int -qemuDomainHostdevNetDevice(virDomainHostdevDefPtr hostdev, char **linkdev, - int *vf) -{ - int ret = -1; - char *sysfs_path = NULL; - - if (qemuDomainHostdevPciSysfsPath(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 -qemuDomainHostdevNetConfigVirtPortProfile(const char *linkdev, int vf, - virNetDevVPortProfilePtr virtPort, - const virMacAddrPtr macaddr, - const unsigned char *uuid, - bool 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; -} - -int -qemuDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev, - const unsigned char *uuid, - char *stateDir) -{ - char *linkdev = NULL; - virNetDevVlanPtr vlan; - virNetDevVPortProfilePtr virtPort; - int ret = -1; - int vf = -1; - int vlanid = -1; - bool port_profile_associate = true; - int isvf; - - isvf = qemuDomainHostdevIsVirtualFunction(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 (qemuDomainHostdevNetDevice(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 = qemuDomainHostdevNetConfigVirtPortProfile(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; -} - -int -qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, - char *stateDir) -{ - char *linkdev = NULL; - virNetDevVPortProfilePtr virtPort; - int ret = -1; - int vf = -1; - bool port_profile_associate = false; - int isvf; - - isvf = qemuDomainHostdevIsVirtualFunction(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 (qemuDomainHostdevNetDevice(hostdev, &linkdev, &vf) < 0) - return ret; - - virtPort = virDomainNetGetActualVirtPortProfile( - hostdev->parent.data.net); - if (virtPort) - ret = qemuDomainHostdevNetConfigVirtPortProfile(linkdev, vf, virtPort, - &hostdev->parent.data.net->mac, NULL, - port_profile_associate); - else - ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir); - - VIR_FREE(linkdev); - - return ret; -} - -int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, - const char *name, - const unsigned char *uuid, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - virPCIDeviceListPtr pcidevs; - int last_processed_hostdev_vf = -1; - size_t i; - int ret = -1; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - - if (!(pcidevs = qemuGetPciHostDeviceList(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 pci-stub.ko - */ - - for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { - virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); - virPCIDevicePtr other; - - if (!virPCIDeviceIsAssignable(dev, !cfg->relaxedACS)) { - 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 driver->activePciHostdevs. - */ - if ((other = virPCIDeviceListFind(driver->activePciHostdevs, dev))) { - const char *other_name = virPCIDeviceGetUsedBy(other); - - if (other_name) - virReportError(VIR_ERR_OPERATION_INVALID, - _("PCI device %s is in use by domain %s"), - virPCIDeviceGetName(dev), other_name); - else - virReportError(VIR_ERR_OPERATION_INVALID, - _("PCI device %s is already in use"), - virPCIDeviceGetName(dev)); - goto cleanup; - } - } - - /* Loop 2: detach managed devices (i.e. bind to appropriate stub driver) */ - for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { - virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); - if (virPCIDeviceGetManaged(dev) && - virPCIDeviceDetach(dev, driver->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, driver->activePciHostdevs, - driver->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 (qemuDomainHostdevNetConfigReplace(hostdev, uuid, - cfg->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(driver->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(driver->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(driver->activePciHostdevs, dev); - - if (activeDev) - virPCIDeviceSetUsedBy(activeDev, 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(). - */ - for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { - virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); - virPCIDeviceListSteal(driver->activePciHostdevs, dev); - } - -resetvfnetconfig: - for (i = 0; last_processed_hostdev_vf != -1 && - i < last_processed_hostdev_vf; i++) { - virDomainHostdevDefPtr hostdev = hostdevs[i]; - if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET && - hostdev->parent.data.net) { - qemuDomainHostdevNetConfigRestore(hostdev, cfg->stateDir); - } - } - -reattachdevs: - for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { - virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); - - /* NB: This doesn't actually re-bind to original driver, just - * unbinds from the stub driver - */ - virPCIDeviceReattach(dev, driver->activePciHostdevs, NULL); - } - -cleanup: - virObjectUnlock(driver->activePciHostdevs); - virObjectUnlock(driver->inactivePciHostdevs); - virObjectUnref(pcidevs); - virObjectUnref(cfg); - return ret; -} - -int -qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, - const char *name, - virUSBDeviceListPtr list) -{ - size_t i, j; - unsigned int count; - virUSBDevicePtr tmp; - - virObjectLock(driver->activeUsbHostdevs); - count = virUSBDeviceListCount(list); - - for (i = 0; i < count; i++) { - virUSBDevicePtr usb = virUSBDeviceListGet(list, i); - if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) { - const char *other_name = virUSBDeviceGetUsedBy(tmp); - - if (other_name) - virReportError(VIR_ERR_OPERATION_INVALID, - _("USB device %s is in use by domain %s"), - virUSBDeviceGetName(tmp), other_name); - else - virReportError(VIR_ERR_OPERATION_INVALID, - _("USB device %s is already in use"), - virUSBDeviceGetName(tmp)); - goto error; - } - - virUSBDeviceSetUsedBy(usb, name); - VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", - virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name); - /* - * The caller is responsible to steal these usb devices - * from the virUSBDeviceList that passed in on success, - * perform rollback on failure. - */ - if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) - goto error; - } - - virObjectUnlock(driver->activeUsbHostdevs); - return 0; - -error: - for (j = 0; j < i; j++) { - tmp = virUSBDeviceListGet(list, i); - virUSBDeviceListSteal(driver->activeUsbHostdevs, tmp); - } - virObjectUnlock(driver->activeUsbHostdevs); - return -1; -} - -int -qemuFindHostdevUSBDevice(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 -qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, - virDomainDefPtr def, - bool coldBoot) -{ - size_t i; - int ret = -1; - virUSBDeviceListPtr list; - virUSBDevicePtr tmp; - virDomainHostdevDefPtr *hostdevs = def->hostdevs; - int nhostdevs = def->nhostdevs; - - /* 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. See qemuPrepareHostdevPCIDevices() - */ - 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 (qemuFindHostdevUSBDevice(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 driver list. However, if something goes - * wrong, perform rollback. - */ - if (qemuPrepareHostdevUSBDevices(driver, def->name, list) < 0) - goto cleanup; - - /* Loop 2: Temporary list was successfully merged with - * driver 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 -qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - size_t i, j; - int count; - virSCSIDeviceListPtr list; - virSCSIDevicePtr tmp; - - /* Loop 1: Add the shared scsi host device to shared device - * table. - */ - for (i = 0; i < nhostdevs; i++) { - virDomainDeviceDef dev; - - dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; - dev.data.hostdev = hostdevs[i]; - - if (qemuAddSharedDevice(driver, &dev, name) < 0) - return -1; - - if (qemuSetUnprivSGIO(&dev) < 0) - return -1; - } - - /* To prevent situation where SCSI device is assigned to two domains - * we need to keep a list of currently assigned SCSI devices. - * This is done in several loops which cannot be joined into one big - * loop. See qemuPrepareHostdevPCIDevices() - */ - if (!(list = virSCSIDeviceListNew())) - goto cleanup; - - /* Loop 2: 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 3: Mark devices in temporary list as used by @name - * and add them to driver list. However, if something goes - * wrong, perform rollback. - */ - virObjectLock(driver->activeScsiHostdevs); - count = virSCSIDeviceListCount(list); - - for (i = 0; i < count; i++) { - virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i); - if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi))) { - const char *other_name = virSCSIDeviceGetUsedBy(tmp); - - if (other_name) - virReportError(VIR_ERR_OPERATION_INVALID, - _("SCSI device %s is in use by domain %s"), - virSCSIDeviceGetName(tmp), other_name); - else - virReportError(VIR_ERR_OPERATION_INVALID, - _("SCSI device %s is already in use"), - virSCSIDeviceGetName(tmp)); - goto error; - } - - virSCSIDeviceSetUsedBy(scsi, name); - VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi)); - - if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) - goto error; - } - - virObjectUnlock(driver->activeScsiHostdevs); - - /* Loop 4: 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, i); - virSCSIDeviceListSteal(driver->activeScsiHostdevs, tmp); - } - virObjectUnlock(driver->activeScsiHostdevs); -cleanup: - virObjectUnref(list); - return -1; -} - -int qemuPrepareHostDevices(virQEMUDriverPtr driver, - virDomainDefPtr def, - bool coldBoot) -{ - if (!def->nhostdevs) - return 0; - - if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, - def->hostdevs, def->nhostdevs) < 0) - return -1; - - if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) - return -1; - - if (qemuPrepareHostdevSCSIDevices(driver, def->name, - def->hostdevs, def->nhostdevs) < 0) - return -1; - - return 0; -} - - -/* - * Pre-condition: driver->inactivePciHostdevs & driver->activePciHostdevs - * are locked - */ -void qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver) -{ - int retries = 100; - - /* If the device is not managed and was attached to guest - * successfully, it must have been inactive. - */ - if (!virPCIDeviceGetManaged(dev)) { - if (virPCIDeviceListAdd(driver->inactivePciHostdevs, dev) < 0) - virPCIDeviceFree(dev); - return; - } - - while (virPCIDeviceWaitForCleanup(dev, "kvm_assigned_device") - && retries) { - usleep(100*1000); - retries--; - } - - if (virPCIDeviceReattach(dev, driver->activePciHostdevs, - driver->inactivePciHostdevs) < 0) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to re-attach PCI device: %s"), - err ? err->message : _("unknown error")); - virResetError(err); - } - virPCIDeviceFree(dev); -} - - -void qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - virPCIDeviceListPtr pcidevs; - size_t i; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - - if (!(pcidevs = qemuGetActivePciHostDeviceList(driver, - 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; - - /* delete the copy of the dev from pcidevs if it's used by - * other domain. Or delete it from activePciHostDevs if it had - * been used by this domain. - */ - activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev); - if (activeDev && - STRNEQ_NULLABLE(name, virPCIDeviceGetUsedBy(activeDev))) { - virPCIDeviceListDel(pcidevs, dev); - continue; - } - - virPCIDeviceListDel(driver->activePciHostdevs, dev); - } - - /* At this point, any device that had been used by the guest is in - * pcidevs, but has been removed from activePciHostdevs. - */ - - /* - * For SRIOV net host devices, unset mac and port profile before - * reset and reattach device - */ - 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) { - qemuDomainHostdevNetConfigRestore(hostdev, cfg->stateDir); - } - } - - for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { - virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); - - if (virPCIDeviceReset(dev, driver->activePciHostdevs, - driver->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); - qemuReattachPciDevice(dev, driver); - } - - virObjectUnref(pcidevs); -cleanup: - virObjectUnlock(driver->activePciHostdevs); - virObjectUnlock(driver->inactivePciHostdevs); - virObjectUnref(cfg); -} - -static void -qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - size_t i; - - virObjectLock(driver->activeUsbHostdevs); - for (i = 0; i < nhostdevs; i++) { - virDomainHostdevDefPtr hostdev = hostdevs[i]; - virUSBDevicePtr usb, tmp; - const char *used_by = NULL; - - 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 domain %s", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - name); - continue; - } - - /* Delete only those USB devices which belongs - * to domain @name because qemuProcessStart() might - * have failed because USB device is already taken. - * Therefore we want to steal only those devices from - * the list which were taken by @name */ - - tmp = virUSBDeviceListFind(driver->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; - } - - used_by = virUSBDeviceGetUsedBy(tmp); - if (STREQ_NULLABLE(used_by, name)) { - VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - name); - - virUSBDeviceListDel(driver->activeUsbHostdevs, tmp); - } - } - virObjectUnlock(driver->activeUsbHostdevs); -} - -void -qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - size_t i; - - virObjectLock(driver->activeScsiHostdevs); - for (i = 0; i < nhostdevs; i++) { - virDomainHostdevDefPtr hostdev = hostdevs[i]; - virSCSIDevicePtr scsi, tmp; - const char *used_by = NULL; - virDomainDeviceDef dev; - - dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; - dev.data.hostdev = hostdev; - - ignore_value(qemuRemoveSharedDevice(driver, &dev, name)); - - 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 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, - name); - continue; - } - - /* Only delete the devices which are marked as being used by @name, - * because qemuProcessStart could fail on the half way. */ - - tmp = virSCSIDeviceListFind(driver->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; - } - - used_by = virSCSIDeviceGetUsedBy(tmp); - if (STREQ_NULLABLE(used_by, name)) { - VIR_DEBUG("Removing %s:%d:%d:%d 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, - name); - - virSCSIDeviceListDel(driver->activeScsiHostdevs, tmp); - } - } - virObjectUnlock(driver->activeScsiHostdevs); -} - -void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver, - virDomainDefPtr def) -{ - if (!def->nhostdevs) - return; - - qemuDomainReAttachHostdevDevices(driver, def->name, def->hostdevs, - def->nhostdevs); - - qemuDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs, - def->nhostdevs); - - qemuDomainReAttachHostScsiDevices(driver, def->name, def->hostdevs, - def->nhostdevs); -} diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h deleted file mode 100644 index 327d4d5..0000000 --- a/src/qemu/qemu_hostdev.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * qemu_hostdev.h: QEMU hostdev management - * - * Copyright (C) 2006-2007, 2009-2010 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: Daniel P. Berrange <berrange@redhat.com> - */ - -#ifndef __QEMU_HOSTDEV_H__ -# define __QEMU_HOSTDEV_H__ - -# include "qemu_conf.h" -# include "domain_conf.h" - -int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, - virDomainDefPtr def); -int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, - virDomainDefPtr def); -int qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver, - virDomainDefPtr def); -int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, - const char *name, - const unsigned char *uuid, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs); -int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, - bool mandatory, - virUSBDevicePtr *usb); -int qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, - const char *name, - virUSBDeviceListPtr list); -int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs); -int qemuPrepareHostDevices(virQEMUDriverPtr driver, - virDomainDefPtr def, - bool coldBoot); -void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs); -void qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver); -void qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs); -void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver, - virDomainDefPtr def); -int qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev); -int qemuDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev, - const unsigned char *uuid, - char *stateDir); -int qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, - char *stateDir); - -#endif /* __QEMU_HOSTDEV_H__ */ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 6cdee44..e659def 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -30,7 +30,7 @@ #include "qemu_domain.h" #include "qemu_command.h" #include "qemu_bridge_filter.h" -#include "qemu_hostdev.h" +#include "qemu_conf.h" #include "domain_audit.h" #include "domain_nwfilter.h" #include "virlog.h" @@ -50,6 +50,7 @@ #include "virstoragefile.h" #include "virstring.h" #include "virtime.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_QEMU #define CHANGE_MEDIA_RETRIES 10 @@ -1134,12 +1135,19 @@ int qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, int configfd = -1; char *configfd_name = NULL; bool releaseaddr = false; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virHostdevManagerPtr hostdev_mgr; + unsigned int flags = 0; if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) return -1; - if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (!cfg->relaxedACS) + flags |= VIR_STRICT_ACS_CHECK; + if (virHostdevPreparePciHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, vm->def->uuid, + &hostdev, 1, flags) < 0) return -1; if (hostdev->source.subsys.u.pci.backend @@ -1223,7 +1231,8 @@ error: if (releaseaddr) qemuDomainReleaseDeviceAddress(vm, hostdev->info, NULL); - qemuDomainReAttachHostdevDevices(driver, vm->def->name, &hostdev, 1); + virHostdevReAttachPciHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); VIR_FREE(devstr); VIR_FREE(configfd_name); @@ -1408,26 +1417,21 @@ int qemuDomainAttachHostUsbDevice(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev) { qemuDomainObjPrivatePtr priv = vm->privateData; - virUSBDeviceList *list = NULL; virUSBDevicePtr usb = NULL; char *devstr = NULL; bool added = false; int ret = -1; + virHostdevManagerPtr hostdev_mgr; - if (qemuFindHostdevUSBDevice(hostdev, true, &usb) < 0) - return -1; - - if (!(list = virUSBDeviceListNew())) - goto cleanup; - - if (virUSBDeviceListAdd(list, usb) < 0) - goto cleanup; - - if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, list) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPrepareUsbHostdevs(hostdev_mgr, + QEMU_DRIVER_NAME, + vm->def->name, + &hostdev, + 1, 0) < 0) goto cleanup; added = true; - virUSBDeviceListSteal(list, usb); if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0) @@ -1456,9 +1460,9 @@ int qemuDomainAttachHostUsbDevice(virQEMUDriverPtr driver, ret = 0; cleanup: if (added) - virUSBDeviceListSteal(driver->activeUsbHostdevs, usb); + virHostdevReAttachUsbHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); virUSBDeviceFree(usb); - virObjectUnref(list); VIR_FREE(devstr); return ret; } @@ -1472,6 +1476,7 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, qemuDomainObjPrivatePtr priv = vm->privateData; char *devstr = NULL; char *drvstr = NULL; + virHostdevManagerPtr hostdev_mgr; if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE) || !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) || @@ -1481,8 +1486,11 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, return -1; } - if (qemuPrepareHostdevSCSIDevices(driver, vm->def->name, - &hostdev, 1)) { + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPrepareScsiHostdevs(hostdev_mgr, + QEMU_DRIVER_NAME, + vm->def->name, + &hostdev, 1) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to prepare scsi hostdev: %s:%d:%d:%d"), hostdev->source.subsys.u.scsi.adapter, @@ -1530,7 +1538,8 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, ret = 0; cleanup: if (ret < 0) - qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &hostdev, 1); + virHostdevReAttachScsiHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); VIR_FREE(drvstr); VIR_FREE(devstr); return ret; @@ -2495,71 +2504,38 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver, static void -qemuDomainRemovePCIHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainRemovePCIHostDevice(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; - virPCIDevicePtr pci; - virPCIDevicePtr activePci; + virHostdevManagerPtr hostdev_mgr; - /* - * For SRIOV net host devices, unset mac and port profile before - * reset and reattach device - */ - if (hostdev->parent.data.net) - qemuDomainHostdevNetConfigRestore(hostdev, cfg->stateDir); - - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - pci = virPCIDeviceNew(subsys->u.pci.addr.domain, subsys->u.pci.addr.bus, - subsys->u.pci.addr.slot, subsys->u.pci.addr.function); - if (pci) { - activePci = virPCIDeviceListSteal(driver->activePciHostdevs, pci); - if (activePci && - virPCIDeviceReset(activePci, driver->activePciHostdevs, - driver->inactivePciHostdevs) == 0) { - qemuReattachPciDevice(activePci, driver); - } else { - /* reset of the device failed, treat it as if it was returned */ - virPCIDeviceFree(activePci); - } - virPCIDeviceFree(pci); - } - virObjectUnlock(driver->activePciHostdevs); - virObjectUnlock(driver->inactivePciHostdevs); + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachPciHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); qemuDomainReleaseDeviceAddress(vm, hostdev->info, NULL); - virObjectUnref(cfg); } static void -qemuDomainRemoveUSBHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm ATTRIBUTE_UNUSED, +qemuDomainRemoveUSBHostDevice(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { - virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; - virUSBDevicePtr usb; - - usb = virUSBDeviceNew(subsys->u.usb.bus, subsys->u.usb.device, NULL); - if (usb) { - virObjectLock(driver->activeUsbHostdevs); - virUSBDeviceListDel(driver->activeUsbHostdevs, usb); - virObjectUnlock(driver->activeUsbHostdevs); - virUSBDeviceFree(usb); - } else { - VIR_WARN("Unable to find device %03d.%03d in list of used USB devices", - subsys->u.usb.bus, subsys->u.usb.device); - } + virHostdevManagerPtr hostdev_mgr; + + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachPciHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); } static void -qemuDomainRemoveSCSIHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainRemoveSCSIHostDevice(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { - qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &hostdev, 1); + virHostdevManagerPtr hostdev_mgr; + + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachScsiHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); } static void @@ -2600,13 +2576,13 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver, switch ((enum virDomainHostdevSubsysType) hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - qemuDomainRemovePCIHostDevice(driver, vm, hostdev); + qemuDomainRemovePCIHostDevice(vm, hostdev); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - qemuDomainRemoveUSBHostDevice(driver, vm, hostdev); + qemuDomainRemoveUSBHostDevice(vm, hostdev); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: - qemuDomainRemoveSCSIHostDevice(driver, vm, hostdev); + qemuDomainRemoveSCSIHostDevice(vm, hostdev); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST: break; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index c7cec5a..cb64f70 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -39,10 +39,10 @@ #include "qemu_capabilities.h" #include "qemu_monitor.h" #include "qemu_command.h" -#include "qemu_hostdev.h" #include "qemu_hotplug.h" #include "qemu_bridge_filter.h" #include "qemu_migration.h" +#include "qemu_conf.h" #include "datatypes.h" #include "virlog.h" @@ -65,6 +65,7 @@ #include "viratomic.h" #include "virnuma.h" #include "virstring.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -3046,6 +3047,8 @@ qemuProcessReconnect(void *opaque) int reason; virQEMUDriverConfigPtr cfg; size_t i; + virHostdevManagerPtr hostdev_mgr; + unsigned int flags; memcpy(&oldjob, &data->oldjob, sizeof(oldjob)); @@ -3077,14 +3080,11 @@ qemuProcessReconnect(void *opaque) priv->agentError = true; } - if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) { - goto error; - } - - if (qemuUpdateActiveUsbHostdevs(driver, obj->def) < 0) - goto error; - if (qemuUpdateActiveScsiHostdevs(driver, obj->def) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + flags = VIR_SP_PCI_HOSTDEV | VIR_SP_USB_HOSTDEV | VIR_SP_SCSI_HOSTDEV; + if (virHostdevPrepareDomainHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + obj->def, flags) < 0) goto error; if (qemuConnectCgroup(driver, obj) < 0) @@ -3481,6 +3481,8 @@ int qemuProcessStart(virConnectPtr conn, unsigned int stop_flags; virQEMUDriverConfigPtr cfg; virCapsPtr caps = NULL; + virHostdevManagerPtr hostdev_mgr; + unsigned int hostdev_flags; /* Okay, these are just internal flags, * but doesn't hurt to check */ @@ -3559,7 +3561,14 @@ int qemuProcessStart(virConnectPtr conn, /* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); - if (qemuPrepareHostDevices(driver, vm->def, !migrateFrom) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + hostdev_flags = VIR_SP_PCI_HOSTDEV | VIR_SP_USB_HOSTDEV | VIR_SP_SCSI_HOSTDEV; + if (!migrateFrom) + hostdev_flags |= VIR_COLD_BOOT; + if (!cfg->relaxedACS) + hostdev_flags |= VIR_STRICT_ACS_CHECK; + if (virHostdevPrepareDomainHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def, hostdev_flags) < 0) goto cleanup; VIR_DEBUG("Preparing chr devices"); @@ -4101,6 +4110,8 @@ void qemuProcessStop(virQEMUDriverPtr driver, char *timestamp; char ebuf[1024]; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virHostdevManagerPtr hostdev_mgr; + unsigned int hostdev_flags; VIR_DEBUG("Shutting down VM '%s' pid=%d flags=%x", vm->def->name, vm->pid, flags); @@ -4243,7 +4254,10 @@ void qemuProcessStop(virQEMUDriverPtr driver, priv->ccwaddrs = NULL; } - qemuDomainReAttachHostDevices(driver, vm->def); + hostdev_mgr = virHostdevManagerGetDefault(); + hostdev_flags = VIR_SP_PCI_HOSTDEV | VIR_SP_USB_HOSTDEV | VIR_SP_SCSI_HOSTDEV; + virHostdevReAttachDomainHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def, hostdev_flags); def = vm->def; for (i = 0; i < def->nnets; i++) { -- 1.6.0.2

On Fri, Sep 13, 2013 at 11:34:36AM +0800, Chunyan Liu wrote:
Change qemu driver to use hostdev common library instead of APIs in qemu_hostdev.[ch]
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 1 - src/Makefile.am | 1 - src/qemu/qemu_command.c | 1 - src/qemu/qemu_conf.h | 9 +- src/qemu/qemu_driver.c | 72 +--- src/qemu/qemu_hostdev.c | 1289 ----------------------------------------------- src/qemu/qemu_hostdev.h | 72 --- src/qemu/qemu_hotplug.c | 126 ++--- src/qemu/qemu_process.c | 34 +- 9 files changed, 87 insertions(+), 1518 deletions(-) delete mode 100644 src/qemu/qemu_hostdev.c delete mode 100644 src/qemu/qemu_hostdev.h
diff --git a/po/POTFILES.in b/po/POTFILES.in index d01cb99..2fc7e67 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -99,7 +99,6 @@ src/qemu/qemu_command.c src/qemu/qemu_conf.c src/qemu/qemu_domain.c src/qemu/qemu_driver.c -src/qemu/qemu_hostdev.c src/qemu/qemu_hotplug.c src/qemu/qemu_migration.c src/qemu/qemu_monitor.c diff --git a/src/Makefile.am b/src/Makefile.am index e563960..c3a8ba0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -654,7 +654,6 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_command.c qemu/qemu_command.h \ qemu/qemu_domain.c qemu/qemu_domain.h \ qemu/qemu_cgroup.c qemu/qemu_cgroup.h \ - qemu/qemu_hostdev.c qemu/qemu_hostdev.h \ qemu/qemu_hotplug.c qemu/qemu_hotplug.h \ qemu/qemu_hotplugpriv.h \ qemu/qemu_conf.c qemu/qemu_conf.h \ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 1521431..3abb88d 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -24,7 +24,6 @@ #include <config.h>
#include "qemu_command.h" -#include "qemu_hostdev.h" #include "qemu_capabilities.h" #include "qemu_bridge_filter.h" #include "cpu/cpu.h" diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 206f2c6..97c8501 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -52,6 +52,7 @@ # error "Port me" # endif
+# define QEMU_DRIVER_NAME "QEMU" typedef struct _virQEMUDriver virQEMUDriver; typedef virQEMUDriver *virQEMUDriverPtr;
@@ -204,14 +205,6 @@ struct _virQEMUDriver { /* Immutable pointer. self-locking APIs */ virSecurityManagerPtr securityManager;
- /* Immutable pointers. Requires locks to be held before - * calling APIs. activePciHostdevs must be locked before - * inactivePciHostdevs */ - virPCIDeviceListPtr activePciHostdevs; - virPCIDeviceListPtr inactivePciHostdevs; - virUSBDeviceListPtr activeUsbHostdevs; - virSCSIDeviceListPtr activeScsiHostdevs; - /* Immutable pointer. Unsafe APIs. XXX */ virHashTablePtr sharedDevices;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index bbf2d23..b289a30 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -50,7 +50,6 @@ #include "qemu_capabilities.h" #include "qemu_command.h" #include "qemu_cgroup.h" -#include "qemu_hostdev.h" #include "qemu_hotplug.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" @@ -94,11 +93,10 @@ #include "virstring.h" #include "viraccessapicheck.h" #include "viraccessapicheckqemu.h" +#include "virhostdev.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
-#define QEMU_DRIVER_NAME "QEMU" - #define QEMU_NB_MEM_PARAM 3
#define QEMU_NB_BLOCK_IO_TUNE_PARAM 6 @@ -691,18 +689,6 @@ qemuStateInitialize(bool privileged, if (qemuSecurityInit(qemu_driver) < 0) goto error;
- if ((qemu_driver->activePciHostdevs = virPCIDeviceListNew()) == NULL) - goto error; - - if ((qemu_driver->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) - goto error; - - if ((qemu_driver->inactivePciHostdevs = virPCIDeviceListNew()) == NULL) - goto error; - - if ((qemu_driver->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL) - goto error; - if (!(qemu_driver->sharedDevices = virHashCreate(30, qemuSharedDeviceEntryFree))) goto error;
@@ -983,9 +969,6 @@ qemuStateCleanup(void) {
virNWFilterUnRegisterCallbackDriver(&qemuCallbackDriver); virObjectUnref(qemu_driver->config); - virObjectUnref(qemu_driver->activePciHostdevs); - virObjectUnref(qemu_driver->inactivePciHostdevs); - virObjectUnref(qemu_driver->activeUsbHostdevs); virHashFree(qemu_driver->sharedDevices); virObjectUnref(qemu_driver->caps); virQEMUCapsCacheFree(qemu_driver->qemuCapsCache); @@ -10689,12 +10672,12 @@ qemuNodeDeviceDetachFlags(virNodeDevicePtr dev, const char *driverName, unsigned int flags) { - virQEMUDriverPtr driver = dev->conn->privateData; virPCIDevicePtr pci = NULL; unsigned domain = 0, bus = 0, slot = 0, function = 0; int ret = -1; virNodeDeviceDefPtr def = NULL; char *xml = NULL; + virHostdevManagerPtr hostdev_mgr;
virCheckFlags(0, -1);
@@ -10728,18 +10711,12 @@ qemuNodeDeviceDetachFlags(virNodeDevicePtr dev, goto cleanup; }
- virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - - if (virPCIDeviceDetach(pci, driver->activePciHostdevs, - driver->inactivePciHostdevs) < 0) { - goto out; + hostdev_mgr = virHostdevManagerGetDefault();
Check for NULL, and other places throughout the file
+ if (virHostdevPciNodeDeviceDetach(hostdev_mgr, pci) < 0) { + goto cleanup; }
While you're changing this, remove the {} since they are not wanted for single-line blocks. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Change lxc driver to use hostdev common library instead of its own APIs in lxc_hostdev.[ch] Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 1 - src/Makefile.am | 1 - src/lxc/lxc_conf.h | 4 - src/lxc/lxc_driver.c | 45 +++--- src/lxc/lxc_hostdev.c | 413 ------------------------------------------------- src/lxc/lxc_hostdev.h | 43 ----- src/lxc/lxc_process.c | 21 ++- 7 files changed, 43 insertions(+), 485 deletions(-) delete mode 100644 src/lxc/lxc_hostdev.c delete mode 100644 src/lxc/lxc_hostdev.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 2fc7e67..b3b7832 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -61,7 +61,6 @@ src/locking/lock_manager.c src/locking/sanlock_helper.c src/lxc/lxc_cgroup.c src/lxc/lxc_fuse.c -src/lxc/lxc_hostdev.c src/lxc/lxc_container.c src/lxc/lxc_conf.c src/lxc/lxc_controller.c diff --git a/src/Makefile.am b/src/Makefile.am index c3a8ba0..afae0a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -600,7 +600,6 @@ LXC_DRIVER_SOURCES = \ lxc/lxc_container.c lxc/lxc_container.h \ lxc/lxc_cgroup.c lxc/lxc_cgroup.h \ lxc/lxc_domain.c lxc/lxc_domain.h \ - lxc/lxc_hostdev.c lxc/lxc_hostdev.h \ lxc/lxc_monitor.c lxc/lxc_monitor.h \ lxc/lxc_process.c lxc/lxc_process.h \ lxc/lxc_fuse.c lxc/lxc_fuse.h \ diff --git a/src/lxc/lxc_conf.h b/src/lxc/lxc_conf.h index a6208a2..05f2a80 100644 --- a/src/lxc/lxc_conf.h +++ b/src/lxc/lxc_conf.h @@ -92,10 +92,6 @@ struct _virLXCDriver { /* Immutable pointer, self-locking APIs */ virDomainObjListPtr domains; - /* Immutable pointer. Requires lock to be held before - * calling APIs. */ - virUSBDeviceListPtr activeUsbHostdevs; - /* Immutable pointer, self-locking APIs */ virDomainEventStatePtr domainEventState; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index b587c22..098f2f0 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -70,6 +70,7 @@ #include "virstring.h" #include "viraccessapicheck.h" #include "viraccessapichecklxc.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_LXC @@ -1418,9 +1419,6 @@ static int lxcStateInitialize(bool privileged, if (!(lxc_driver->securityManager = lxcSecurityInit(cfg))) goto cleanup; - if ((lxc_driver->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) - goto cleanup; - if ((virLXCDriverGetCapabilities(lxc_driver, true)) == NULL) goto cleanup; @@ -1535,7 +1533,6 @@ static int lxcStateCleanup(void) virSysinfoDefFree(lxc_driver->hostsysinfo); - virObjectUnref(lxc_driver->activeUsbHostdevs); virObjectUnref(lxc_driver->caps); virObjectUnref(lxc_driver->securityManager); virObjectUnref(lxc_driver->xmlopt); @@ -3214,6 +3211,7 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, mode_t mode; bool created = false; virUSBDevicePtr usb = NULL; + virHostdevManagerPtr hostdev_mgr; if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", @@ -3221,6 +3219,13 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, return -1; } + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPrepareUsbHostdevs(hostdev_mgr, + LXC_DRIVER_NAME, + vm->def->name, + &def, 1, 0) < 0) + goto cleanup; + if (virAsprintf(&vroot, "/proc/%llu/root", (unsigned long long)priv->initpid) < 0) goto cleanup; @@ -3296,6 +3301,11 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, ret = 0; cleanup: + virHostdevReAttachUsbHostdevs(hostdev_mgr, + LXC_DRIVER_NAME, + vm->def->name, + &def, + 1); virDomainAuditHostdev(vm, def, "attach", ret == 0); if (ret < 0 && created) unlink(dstfile); @@ -3758,8 +3768,7 @@ cleanup: static int -lxcDomainDetachDeviceHostdevUSBLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceHostdevUSBLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { virLXCDomainObjPrivatePtr priv = vm->privateData; @@ -3768,6 +3777,7 @@ lxcDomainDetachDeviceHostdevUSBLive(virLXCDriverPtr driver, char *dst = NULL; char *vroot = NULL; virUSBDevicePtr usb = NULL; + virHostdevManagerPtr hostdev_mgr; if ((idx = virDomainHostdevFind(vm->def, dev->data.hostdev, @@ -3812,9 +3822,9 @@ lxcDomainDetachDeviceHostdevUSBLive(virLXCDriverPtr driver, VIR_WARN("cannot deny device %s for domain %s", dst, vm->def->name); - virObjectLock(driver->activeUsbHostdevs); - virUSBDeviceListDel(driver->activeUsbHostdevs, usb); - virObjectUnlock(driver->activeUsbHostdevs); + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachPciHostdevs(hostdev_mgr, LXC_DRIVER_NAME, + vm->def->name, &def, 1); virDomainHostdevRemove(vm->def, idx); virDomainHostdevDefFree(def); @@ -3948,13 +3958,12 @@ cleanup: static int -lxcDomainDetachDeviceHostdevSubsysLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceHostdevSubsysLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { switch (dev->data.hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - return lxcDomainDetachDeviceHostdevUSBLive(driver, vm, dev); + return lxcDomainDetachDeviceHostdevUSBLive(vm, dev); default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -3986,8 +3995,7 @@ lxcDomainDetachDeviceHostdevCapsLive(virDomainObjPtr vm, static int -lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceHostdevLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { virLXCDomainObjPrivatePtr priv = vm->privateData; @@ -4000,7 +4008,7 @@ lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, switch (dev->data.hostdev->mode) { case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: - return lxcDomainDetachDeviceHostdevSubsysLive(driver, vm, dev); + return lxcDomainDetachDeviceHostdevSubsysLive(vm, dev); case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: return lxcDomainDetachDeviceHostdevCapsLive(vm, dev); @@ -4015,8 +4023,7 @@ lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, static int -lxcDomainDetachDeviceLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int ret = -1; @@ -4031,7 +4038,7 @@ lxcDomainDetachDeviceLive(virLXCDriverPtr driver, break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = lxcDomainDetachDeviceHostdevLive(driver, vm, dev); + ret = lxcDomainDetachDeviceHostdevLive(vm, dev); break; default: @@ -4364,7 +4371,7 @@ static int lxcDomainDetachDeviceFlags(virDomainPtr dom, if (virDomainDefCompatibleDevice(vm->def, dev_copy) < 0) goto cleanup; - if ((ret = lxcDomainDetachDeviceLive(driver, vm, dev_copy)) < 0) + if ((ret = lxcDomainDetachDeviceLive(vm, dev_copy)) < 0) goto cleanup; /* * update domain status forcibly because the domain status may be diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c deleted file mode 100644 index 3b371fc..0000000 --- a/src/lxc/lxc_hostdev.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * virLXC_hostdev.c: VIRLXC hostdev management - * - * Copyright (C) 2006-2007, 2009-2012 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: Daniel P. Berrange <berrange@redhat.com> - */ - -#include <config.h> - -#include "lxc_hostdev.h" -#include "viralloc.h" -#include "virlog.h" -#include "virerror.h" - -#define VIR_FROM_THIS VIR_FROM_LXC - -int -virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, - virDomainDefPtr def) -{ - virDomainHostdevDefPtr hostdev = NULL; - size_t i; - - if (!def->nhostdevs) - return 0; - - for (i = 0; i < def->nhostdevs; i++) { - virUSBDevicePtr usb = NULL; - hostdev = def->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 domain %s", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - def->name); - continue; - } - - virUSBDeviceSetUsedBy(usb, def->name); - - virObjectLock(driver->activeUsbHostdevs); - if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { - virObjectUnlock(driver->activeUsbHostdevs); - virUSBDeviceFree(usb); - return -1; - } - virObjectUnlock(driver->activeUsbHostdevs); - } - - return 0; -} - - -int -virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, - const char *name, - virUSBDeviceList *list) -{ - size_t i, j; - unsigned int count; - virUSBDevicePtr tmp; - - count = virUSBDeviceListCount(list); - - virObjectLock(driver->activeUsbHostdevs); - for (i = 0; i < count; i++) { - virUSBDevicePtr usb = virUSBDeviceListGet(list, i); - if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) { - const char *other_name = virUSBDeviceGetUsedBy(tmp); - - if (other_name) - virReportError(VIR_ERR_OPERATION_INVALID, - _("USB device %s is in use by domain %s"), - virUSBDeviceGetName(tmp), other_name); - else - virReportError(VIR_ERR_OPERATION_INVALID, - _("USB device %s is already in use"), - virUSBDeviceGetName(tmp)); - goto error; - } - - virUSBDeviceSetUsedBy(usb, name); - VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", - virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name); - /* - * The caller is responsible to steal these usb devices - * from the virUSBDeviceList that passed in on success, - * perform rollback on failure. - */ - if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) - goto error; - } - virObjectUnlock(driver->activeUsbHostdevs); - return 0; - -error: - for (j = 0; j < i; j++) { - tmp = virUSBDeviceListGet(list, i); - virUSBDeviceListSteal(driver->activeUsbHostdevs, tmp); - } - virObjectUnlock(driver->activeUsbHostdevs); - return -1; -} - -int -virLXCFindHostdevUSBDevice(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) { - virUSBDeviceList *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 -virLXCPrepareHostUSBDevices(virLXCDriverPtr driver, - virDomainDefPtr def) -{ - size_t i; - int ret = -1; - virUSBDeviceList *list; - virUSBDevicePtr tmp; - virDomainHostdevDefPtr *hostdevs = def->hostdevs; - int nhostdevs = def->nhostdevs; - - /* 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. See virLXCPrepareHostdevPCIDevices() - */ - 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) - required = false; - - if (virLXCFindHostdevUSBDevice(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 driver list. However, if something goes - * wrong, perform rollback. - */ - if (virLXCPrepareHostdevUSBDevices(driver, def->name, list) < 0) - goto cleanup; - - /* Loop 2: Temporary list was successfully merged with - * driver 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 virLXCPrepareHostDevices(virLXCDriverPtr driver, - virDomainDefPtr def) -{ - size_t i; - - if (!def->nhostdevs) - return 0; - - /* Sanity check for supported configurations only */ - for (i = 0; i < def->nhostdevs; i++) { - virDomainHostdevDefPtr dev = def->hostdevs[i]; - - switch (dev->mode) { - case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: - switch (dev->source.subsys.type) { - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - break; - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported hostdev type %s"), - virDomainHostdevSubsysTypeToString(dev->source.subsys.type)); - return -1; - } - break; - - case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: - switch (dev->source.subsys.type) { - case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: - case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: - case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET: - break; - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported hostdev type %s"), - virDomainHostdevSubsysTypeToString(dev->source.subsys.type)); - return -1; - } - break; - - - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported hostdev mode %s"), - virDomainHostdevModeTypeToString(dev->mode)); - return -1; - } - } - - if (virLXCPrepareHostUSBDevices(driver, def) < 0) - return -1; - - return 0; -} - - -static void -virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - size_t i; - - virObjectLock(driver->activeUsbHostdevs); - for (i = 0; i < nhostdevs; i++) { - virDomainHostdevDefPtr hostdev = hostdevs[i]; - virUSBDevicePtr usb, tmp; - const char *used_by = NULL; - - 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 domain %s", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - name); - continue; - } - - /* Delete only those USB devices which belongs - * to domain @name because virLXCProcessStart() might - * have failed because USB device is already taken. - * Therefore we want to steal only those devices from - * the list which were taken by @name */ - - tmp = virUSBDeviceListFind(driver->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; - } - - used_by = virUSBDeviceGetUsedBy(tmp); - if (STREQ_NULLABLE(used_by, name)) { - VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - name); - - virUSBDeviceListDel(driver->activeUsbHostdevs, tmp); - } - } - virObjectUnlock(driver->activeUsbHostdevs); -} - -void virLXCDomainReAttachHostDevices(virLXCDriverPtr driver, - virDomainDefPtr def) -{ - if (!def->nhostdevs) - return; - - virLXCDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs, - def->nhostdevs); -} diff --git a/src/lxc/lxc_hostdev.h b/src/lxc/lxc_hostdev.h deleted file mode 100644 index 41bb178..0000000 --- a/src/lxc/lxc_hostdev.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * virLXC_hostdev.h: VIRLXC hostdev management - * - * Copyright (C) 2006-2007, 2009-2010 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: Daniel P. Berrange <berrange@redhat.com> - */ - -#ifndef __LXC_HOSTDEV_H__ -# define __LXC_HOSTDEV_H__ - -# include "lxc_conf.h" -# include "domain_conf.h" - -int virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, - virDomainDefPtr def); -int virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, - bool mandatory, - virUSBDevicePtr *usb); -int virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, - const char *name, - virUSBDeviceListPtr list); -int virLXCPrepareHostDevices(virLXCDriverPtr driver, - virDomainDefPtr def); -void virLXCDomainReAttachHostDevices(virLXCDriverPtr driver, - virDomainDefPtr def); - -#endif /* __LXC_HOSTDEV_H__ */ diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c index 4835bd5..ed35853 100644 --- a/src/lxc/lxc_process.c +++ b/src/lxc/lxc_process.c @@ -45,11 +45,11 @@ #include "virerror.h" #include "virlog.h" #include "vircommand.h" -#include "lxc_hostdev.h" #include "virhook.h" #include "virstring.h" #include "viratomic.h" #include "virprocess.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_LXC @@ -151,6 +151,7 @@ static void virLXCProcessCleanup(virLXCDriverPtr driver, virLXCDomainObjPrivatePtr priv = vm->privateData; virNetDevVPortProfilePtr vport = NULL; virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver); + virHostdevManagerPtr hostdev_mgr; VIR_DEBUG("Stopping VM name=%s pid=%d reason=%d", vm->def->name, (int)vm->pid, (int)reason); @@ -186,7 +187,11 @@ static void virLXCProcessCleanup(virLXCDriverPtr driver, if (virAtomicIntDecAndTest(&driver->nactive) && driver->inhibitCallback) driver->inhibitCallback(false, driver->inhibitOpaque); - virLXCDomainReAttachHostDevices(driver, vm->def); + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachDomainHostdevs(hostdev_mgr, + LXC_DRIVER_NAME, + vm->def, + VIR_SP_USB_HOSTDEV); for (i = 0; i < vm->def->nnets; i++) { virDomainNetDefPtr iface = vm->def->nets[i]; @@ -973,6 +978,7 @@ int virLXCProcessStart(virConnectPtr conn, virErrorPtr err = NULL; virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver); virCgroupPtr selfcgroup; + virHostdevManagerPtr hostdev_mgr; if (virCgroupNewSelf(&selfcgroup) < 0) return -1; @@ -1058,7 +1064,11 @@ int virLXCProcessStart(virConnectPtr conn, /* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); - if (virLXCPrepareHostDevices(driver, vm->def) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPrepareDomainHostdevs(hostdev_mgr, + LXC_DRIVER_NAME, + vm->def, + VIR_SP_USB_HOSTDEV) < 0) goto cleanup; /* Here we open all the PTYs we need on the host OS side. @@ -1396,6 +1406,7 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, { virLXCDriverPtr driver = opaque; virLXCDomainObjPrivatePtr priv; + virHostdevManagerPtr hostdev_mgr; int ret = -1; virObjectLock(vm); @@ -1428,7 +1439,9 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, goto error; } - if (virLXCUpdateActiveUsbHostdevs(driver, vm->def) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (virHostdevPrepareDomainHostdevs(hostdev_mgr, LXC_DRIVER_NAME, + vm->def, VIR_SP_USB_HOSTDEV) < 0) goto error; if (virSecurityManagerReserveLabel(driver->securityManager, -- 1.6.0.2

On Fri, Sep 13, 2013 at 11:34:37AM +0800, Chunyan Liu wrote:
Change lxc driver to use hostdev common library instead of its own APIs in lxc_hostdev.[ch]
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 1 - src/Makefile.am | 1 - src/lxc/lxc_conf.h | 4 - src/lxc/lxc_driver.c | 45 +++--- src/lxc/lxc_hostdev.c | 413 ------------------------------------------------- src/lxc/lxc_hostdev.h | 43 ----- src/lxc/lxc_process.c | 21 ++- 7 files changed, 43 insertions(+), 485 deletions(-) delete mode 100644 src/lxc/lxc_hostdev.c delete mode 100644 src/lxc/lxc_hostdev.h
diff --git a/po/POTFILES.in b/po/POTFILES.in index 2fc7e67..b3b7832 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -61,7 +61,6 @@ src/locking/lock_manager.c src/locking/sanlock_helper.c src/lxc/lxc_cgroup.c src/lxc/lxc_fuse.c -src/lxc/lxc_hostdev.c src/lxc/lxc_container.c src/lxc/lxc_conf.c src/lxc/lxc_controller.c diff --git a/src/Makefile.am b/src/Makefile.am index c3a8ba0..afae0a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -600,7 +600,6 @@ LXC_DRIVER_SOURCES = \ lxc/lxc_container.c lxc/lxc_container.h \ lxc/lxc_cgroup.c lxc/lxc_cgroup.h \ lxc/lxc_domain.c lxc/lxc_domain.h \ - lxc/lxc_hostdev.c lxc/lxc_hostdev.h \ lxc/lxc_monitor.c lxc/lxc_monitor.h \ lxc/lxc_process.c lxc/lxc_process.h \ lxc/lxc_fuse.c lxc/lxc_fuse.h \ diff --git a/src/lxc/lxc_conf.h b/src/lxc/lxc_conf.h index a6208a2..05f2a80 100644 --- a/src/lxc/lxc_conf.h +++ b/src/lxc/lxc_conf.h @@ -92,10 +92,6 @@ struct _virLXCDriver { /* Immutable pointer, self-locking APIs */ virDomainObjListPtr domains;
- /* Immutable pointer. Requires lock to be held before - * calling APIs. */ - virUSBDeviceListPtr activeUsbHostdevs; - /* Immutable pointer, self-locking APIs */ virDomainEventStatePtr domainEventState;
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index b587c22..098f2f0 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -70,6 +70,7 @@ #include "virstring.h" #include "viraccessapicheck.h" #include "viraccessapichecklxc.h" +#include "virhostdev.h"
#define VIR_FROM_THIS VIR_FROM_LXC
@@ -1418,9 +1419,6 @@ static int lxcStateInitialize(bool privileged, if (!(lxc_driver->securityManager = lxcSecurityInit(cfg))) goto cleanup;
- if ((lxc_driver->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) - goto cleanup; - if ((virLXCDriverGetCapabilities(lxc_driver, true)) == NULL) goto cleanup;
@@ -1535,7 +1533,6 @@ static int lxcStateCleanup(void)
virSysinfoDefFree(lxc_driver->hostsysinfo);
- virObjectUnref(lxc_driver->activeUsbHostdevs); virObjectUnref(lxc_driver->caps); virObjectUnref(lxc_driver->securityManager); virObjectUnref(lxc_driver->xmlopt); @@ -3214,6 +3211,7 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, mode_t mode; bool created = false; virUSBDevicePtr usb = NULL; + virHostdevManagerPtr hostdev_mgr;
if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", @@ -3221,6 +3219,13 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, return -1; }
+ hostdev_mgr = virHostdevManagerGetDefault();
Check for NULL, and other places. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Chunyan Liu
-
Daniel P. Berrange