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

From: Chunyan Liu <cyliu@suse.com> 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 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 po/POTFILES.in | 3 +- src/Makefile.am | 3 +- src/libvirt_private.syms | 22 + src/libxl/libxl_conf.c | 65 ++ src/libxl/libxl_conf.h | 5 +- src/libxl/libxl_driver.c | 250 +++++++- 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_driver.c | 72 +-- src/qemu/qemu_hostdev.c | 1289 ----------------------------------- src/qemu/qemu_hostdev.h | 72 -- src/qemu/qemu_hotplug.c | 127 ++-- src/qemu/qemu_process.c | 34 +- src/util/virhostdev.c | 1683 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virhostdev.h | 123 ++++ src/util/virpci.c | 21 +- src/util/virpci.h | 7 +- src/util/virscsi.c | 22 +- src/util/virscsi.h | 8 +- src/util/virusb.c | 23 +- src/util/virusb.h | 8 +- 26 files changed, 2341 insertions(+), 2032 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

From: Chunyan Liu <cyliu@suse.com> 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> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 22 + src/util/virhostdev.c | 1683 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virhostdev.h | 123 ++++ src/util/virpci.c | 21 +- src/util/virpci.h | 7 +- src/util/virscsi.c | 22 +- src/util/virscsi.h | 8 +- src/util/virusb.c | 23 +- src/util/virusb.h | 8 +- 11 files changed, 1895 insertions(+), 24 deletions(-) create mode 100644 src/util/virhostdev.c create mode 100644 src/util/virhostdev.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 1fd84af..9176b95 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -193,6 +193,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 62e427e..a89e414 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -145,6 +145,7 @@ UTIL_SOURCES = \ util/virutil.c util/virutil.h \ util/viruuid.c util/viruuid.h \ util/virxml.c util/virxml.h \ + util/virhostdev.c util/virhostdev.h \ $(NULL) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 201b2cb..5dba1b5 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1424,6 +1424,28 @@ virHookInitialize; virHookPresent; +#util/virhostdev.h +virHostdevGetActivePciHostdevs; +virHostdevGetActiveScsiHostdevs; +virHostdevGetActiveUsbHostdevs; +virHostdevGetDomainActivePciHostdevs; +virHostdevGetDomainActiveScsiHostdevs; +virHostdevGetDomainActiveUsbHostdevs; +virHostdevManagerGetDefault; +virHostdevNameListFree; +virHostdevPciNodeDeviceDetach; +virHostdevPciNodeDeviceReAttach; +virHostdevPciNodeDeviceReset; +virHostdevPrepareDomainHostdevs; +virHostdevPreparePciHostdevs; +virHostdevPrepareScsiHostdevs; +virHostdevPrepareUsbHostdevs; +virHostdevReAttachDomainHostdevs; +virHostdevReAttachPciHostdevs; +virHostdevReAttachScsiHostdevs; +virHostdevReAttachUsbHostdevs; + + # util/viridentity.h virIdentityGetAttr; virIdentityGetCurrent; diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c new file mode 100644 index 0000000..0712e89 --- /dev/null +++ b/src/util/virhostdev.c @@ -0,0 +1,1683 @@ +/* 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> + */ + +#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" + +/* For virReportOOMError() and virReportSystemError() */ +#define VIR_FROM_THIS VIR_FROM_NONE + +/* Same name as in virDriver. For special check. */ +#define LIBXL_DRIVER_NAME "xenlight" +#define QEMU_DRIVER_NAME "QEMU" + +struct _virHostdevManager{ + char *stateDir; + + virPCIDeviceListPtr activePciHostdevs; + virPCIDeviceListPtr inactivePciHostdevs; + virUSBDeviceListPtr activeUsbHostdevs; + virSCSIDeviceListPtr activeScsiHostdevs; +}; + +static virHostdevManagerPtr hostdevMgr; + +static int +virHostdevOnceInit(void) +{ + char ebuf[1024]; + if (VIR_ALLOC(hostdevMgr) < 0) + goto out_of_memory; + + 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 (virAsprintf(&hostdevMgr->stateDir, + "%s",HOSTDEV_STATE_DIR) < 0) + goto out_of_memory; + + if (virFileMakePath(hostdevMgr->stateDir) < 0) { + VIR_ERROR(_("Failed to create state dir '%s': %s"), + hostdevMgr->stateDir, virStrerror(errno, ebuf, sizeof(ebuf))); + goto error; + } + + return 0; + +out_of_memory: + virReportOOMError(); +error: + return -1; +} + +VIR_ONCE_GLOBAL_INIT(virHostdev) + +virHostdevManagerPtr +virHostdevManagerGetDefault(void) +{ + virHostdevInitialize(); + 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, + const char *driver) +{ + virPCIDeviceListPtr list; + size_t i; + + if (!(list = virPCIDeviceListNew())) + return NULL; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virPCIDevicePtr dev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + if (!dev) { + virObjectUnref(list); + return NULL; + } + + if (virPCIDeviceListAdd(list, dev) < 0) { + virPCIDeviceFree(dev); + virObjectUnref(list); + return NULL; + } + + virPCIDeviceSetManaged(dev, hostdev->managed); + + switch (hostdev->source.subsys.u.pci.backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) { + virObjectUnref(list); + return NULL; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) { + virObjectUnref(list); + return NULL; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + /* driver name not specified, if it's libxl driver, + * use pciback; otherwise, use pci-stub + */ + if (STREQ(driver, LIBXL_DRIVER_NAME)) { + if (virPCIDeviceSetStubDriver(dev, "pciback") < 0) { + virObjectUnref(list); + return NULL; + } + } else { + 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, drv_name))) + 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))) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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)); + VIR_FREE(other_drvname); + VIR_FREE(other_domname); + 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))) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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)); + VIR_FREE(other_drvname); + VIR_FREE(other_domname); + 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, i); + 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))) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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, i); + 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, const char *driver) +{ + /* 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(driver, QEMU_DRIVER_NAME) && + 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; + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + + /* Never delete the dev from list driver->activePciHostdevs + * if it's used by other domain. + */ + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev); + if (activeDev) { + if (virPCIDeviceGetUsedBy(activeDev, &usedby_drvname, &usedby_domname) < 0) { + VIR_WARN("Unable to get driver and domain name " + "using PCI device %s", + virPCIDeviceGetName(dev)); + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + + if (STRNEQ_NULLABLE(drv_name, usedby_drvname) || + STRNEQ_NULLABLE(dom_name, usedby_domname)) { + virPCIDeviceListSteal(pcidevs, dev); + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + } + + /* virObjectUnref() will take care of freeing the dev. */ + virPCIDeviceListSteal(mgr->activePciHostdevs, dev); + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + /* + * 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, drv_name); + } + + 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; + char *usedby_drvname = NULL; + char *usedby_domname = 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 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; + } + + if (virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname) < 0) { + VIR_WARN("Unable to get driver and domain name " + "using USB device %03d.%03d", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device); + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + + 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); + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + + } + + 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; + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + + 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; + } + + if (virSCSIDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname) < 0) { + VIR_WARN("Unable to get driver and domain name " + "using SCSI device %s:%d:%d:%d ", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit); + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + + 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); + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + 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) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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; +} + +/* this function is used to free memory after using following + * get_active/inactive_list functions + */ +void +virHostdevNameListFree(virHostdevNameListPtr list) +{ + size_t i; + + if (list && list->names) { + for (i = 0; i < list->nnames; i++) { + VIR_FREE(list->names[i]); + } + VIR_FREE(list->names); + } + VIR_FREE(list); +} + +virHostdevNameListPtr +virHostdevGetActivePciHostdevs(virHostdevManagerPtr mgr) +{ + size_t i; + int count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activePciHostdevs); + + count = virPCIDeviceListCount(mgr->activePciHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError(); + goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(mgr->activePciHostdevs, i); + if (VIR_STRDUP(list->names[list->nnames++], virPCIDeviceGetName(dev)) < 0) + goto cleanup; + } + } + + goto end; + +cleanup: + virHostdevNameListFree(list); + +end: + virObjectUnlock(mgr->activePciHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetActiveUsbHostdevs(virHostdevManagerPtr mgr) +{ + size_t i; + int count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeUsbHostdevs); + + count = virUSBDeviceListCount(mgr->activeUsbHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError(); + goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virUSBDevicePtr usb = virUSBDeviceListGet(mgr->activeUsbHostdevs, i); + if (VIR_STRDUP(list->names[list->nnames++], virUSBDeviceGetName(usb)) < 0) + goto cleanup; + } + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeUsbHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetActiveScsiHostdevs(virHostdevManagerPtr mgr) +{ + size_t i; + int count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeScsiHostdevs); + + count = virSCSIDeviceListCount(mgr->activeScsiHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError(); + goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virSCSIDevicePtr dev = virSCSIDeviceListGet(mgr->activeScsiHostdevs, i); + if (VIR_STRDUP(list->names[list->nnames++], virSCSIDeviceGetName(dev)) < 0) + goto cleanup; + } + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeScsiHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetDomainActivePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name) +{ + size_t i, count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activePciHostdevs); + + count = virPCIDeviceListCount(mgr->activePciHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError(); + goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(mgr->activePciHostdevs, i); + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + int ret; + + if (virPCIDeviceGetUsedBy(dev, &usedby_drvname, &usedby_domname) < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + + goto cleanup; + } + + if (STREQ(drv_name, usedby_drvname) && + STREQ(dom_name, usedby_domname)) { + ret = VIR_STRDUP(list->names[list->nnames++], virPCIDeviceGetName(dev)); + if (ret < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + VIR_SHRINK_N(list->names, count, count - list->nnames); + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activePciHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name) +{ + size_t i, count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeUsbHostdevs); + + count = virUSBDeviceListCount(mgr->activeUsbHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError(); + goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virUSBDevicePtr usb = virUSBDeviceListGet(mgr->activeUsbHostdevs, i); + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + int ret; + + if (virUSBDeviceGetUsedBy(usb, &usedby_drvname, &usedby_domname) < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + + if (STREQ(drv_name, usedby_drvname) && + STREQ(dom_name, usedby_domname)) { + ret = VIR_STRDUP(list->names[list->nnames++], virUSBDeviceGetName(usb)); + if (ret < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + VIR_SHRINK_N(list->names, count, count - list->nnames); + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeUsbHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetDomainActiveScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name) +{ + size_t i, count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeScsiHostdevs); + + count = virSCSIDeviceListCount(mgr->activeScsiHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError(); + goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virSCSIDevicePtr dev = virSCSIDeviceListGet(mgr->activeScsiHostdevs, i); + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + int ret; + + if (virSCSIDeviceGetUsedBy(dev, &usedby_drvname, &usedby_domname) < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + + if (STREQ(drv_name, usedby_drvname) && + STREQ(dom_name, usedby_domname)) { + ret = VIR_STRDUP(list->names[list->nnames++], virSCSIDeviceGetName(dev)); + if (ret < 0){ + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + VIR_SHRINK_N(list->names, count, count - list->nnames); + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeScsiHostdevs); + return list; +} diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h new file mode 100644 index 0000000..09377db --- /dev/null +++ b/src/util/virhostdev.h @@ -0,0 +1,123 @@ +/* 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_MANAGER_H__ +# define __VIR_HOSTDEV_MANAGER_H__ + +# include "internal.h" +# include "configmake.h" + +# include "domain_conf.h" +# include "virpci.h" + +# define HOSTDEV_STATE_DIR LOCALSTATEDIR "/run/libvirt/hostdevmanager" + +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); + +/* functions to get active/inactive lists */ +virHostdevNameListPtr virHostdevGetActivePciHostdevs(virHostdevManagerPtr mgr); +virHostdevNameListPtr virHostdevGetActiveUsbHostdevs(virHostdevManagerPtr mgr); +virHostdevNameListPtr virHostdevGetActiveScsiHostdevs(virHostdevManagerPtr mgr); +virHostdevNameListPtr virHostdevGetDomainActivePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name); +virHostdevNameListPtr virHostdevGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name); +virHostdevNameListPtr virHostdevGetDomainActiveScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name); +/* function to free memory after get active/inactive lists */ +void virHostdevNameListFree(virHostdevNameListPtr list); + +#endif /* __VIR_HOSTDEV_MANAGER_H__ */ diff --git a/src/util/virpci.c b/src/util/virpci.c index be50b4f..dc38efe 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 */ + const char *used_by_drvname; + const char *used_by_domname; unsigned int pcie_cap_pos; unsigned int pci_pm_cap_pos; @@ -1642,15 +1645,21 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool reprobe) } void -virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name) +virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *drv_name, const char *dom_name) { - dev->used_by = name; + dev->used_by_drvname = drv_name; + dev->used_by_domname = dom_name; } -const char * -virPCIDeviceGetUsedBy(virPCIDevicePtr dev) +int +virPCIDeviceGetUsedBy(virPCIDevicePtr dev, char **drv_name, char **dom_name) { - return dev->used_by; + if (VIR_STRDUP(*drv_name, dev->used_by_drvname) < 0) + return -1; + if (VIR_STRDUP(*dom_name, dev->used_by_domname) < 0) + return -1; + + return 0; } void virPCIDeviceReattachInit(virPCIDevicePtr pci) diff --git a/src/util/virpci.h b/src/util/virpci.h index 0aa6fee..dc1a80b 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -66,8 +66,11 @@ 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); + const char *drv_name, + const char *dom_name); +int virPCIDeviceGetUsedBy(virPCIDevice *dev, + char **drv_name, + 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..dc1eebb 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 */ + const char *used_by_drvname; + const char *used_by_domname; bool readonly; }; @@ -267,15 +270,22 @@ virSCSIDeviceFree(virSCSIDevicePtr dev) void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { - dev->used_by = name; + dev->used_by_drvname = drvname; + dev->used_by_domname = domname; } -const char * -virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev) +int +virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev, char **drvname, char **domname) { - return dev->used_by; + if (VIR_STRDUP(*drvname, dev->used_by_drvname) < 0) + return -1; + if (VIR_STRDUP(*domname, dev->used_by_domname) < 0) + return -1; + + return 0; } const char * diff --git a/src/util/virscsi.h b/src/util/virscsi.h index cce5df4..89b3e5d 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); +void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, + const char *drvname, + const char *domname); +int virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev, + char **drvname, + 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..54d836d 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 */ + const char *used_by_drvname; + const char *used_by_domname; }; struct _virUSBDeviceList { @@ -378,16 +381,24 @@ virUSBDeviceFree(virUSBDevicePtr dev) VIR_FREE(dev); } - void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, - const char *name) + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + dev->used_by_drvname = drv_name; + dev->used_by_domname = dom_name; } -const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev) +int virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + char **drv_name, + char **dom_name) { - return dev->used_by; + if (VIR_STRDUP(*drv_name, dev->used_by_drvname) < 0) + return -1; + if (VIR_STRDUP(*dom_name, dev->used_by_domname) < 0) + return -1; + + return 0; } const char *virUSBDeviceGetName(virUSBDevicePtr dev) diff --git a/src/util/virusb.h b/src/util/virusb.h index aa59d12..5f1df58 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); +void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name); +int virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + char **drv_name, + char **dom_name); const char *virUSBDeviceGetName(virUSBDevicePtr dev); unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev); -- 1.6.0.2

cyliu@suse.com wrote:
From: Chunyan Liu <cyliu@suse.com>
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> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 22 + src/util/virhostdev.c | 1683 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virhostdev.h | 123 ++++ src/util/virpci.c | 21 +- src/util/virpci.h | 7 +- src/util/virscsi.c | 22 +- src/util/virscsi.h | 8 +- src/util/virusb.c | 23 +- src/util/virusb.h | 8 +- 11 files changed, 1895 insertions(+), 24 deletions(-) create mode 100644 src/util/virhostdev.c create mode 100644 src/util/virhostdev.h
diff --git a/po/POTFILES.in b/po/POTFILES.in index 1fd84af..9176b95 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -193,6 +193,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 62e427e..a89e414 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -145,6 +145,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 \
Tabs should be used here instead of spaces. Also, the '\' should be aligned with the others.
$(NULL)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 201b2cb..5dba1b5 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1424,6 +1424,28 @@ virHookInitialize; virHookPresent;
+#util/virhostdev.h +virHostdevGetActivePciHostdevs; +virHostdevGetActiveScsiHostdevs; +virHostdevGetActiveUsbHostdevs; +virHostdevGetDomainActivePciHostdevs; +virHostdevGetDomainActiveScsiHostdevs; +virHostdevGetDomainActiveUsbHostdevs; +virHostdevManagerGetDefault; +virHostdevNameListFree; +virHostdevPciNodeDeviceDetach; +virHostdevPciNodeDeviceReAttach; +virHostdevPciNodeDeviceReset; +virHostdevPrepareDomainHostdevs; +virHostdevPreparePciHostdevs; +virHostdevPrepareScsiHostdevs; +virHostdevPrepareUsbHostdevs; +virHostdevReAttachDomainHostdevs; +virHostdevReAttachPciHostdevs; +virHostdevReAttachScsiHostdevs; +virHostdevReAttachUsbHostdevs; + + # util/viridentity.h virIdentityGetAttr; virIdentityGetCurrent; diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c new file mode 100644 index 0000000..0712e89 --- /dev/null +++ b/src/util/virhostdev.c @@ -0,0 +1,1683 @@ +/* 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> + */ + +#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" + +/* For virReportOOMError() and virReportSystemError() */ +#define VIR_FROM_THIS VIR_FROM_NONE + +/* Same name as in virDriver. For special check. */ +#define LIBXL_DRIVER_NAME "xenlight" +#define QEMU_DRIVER_NAME "QEMU" + +struct _virHostdevManager{ + char *stateDir; + + virPCIDeviceListPtr activePciHostdevs; + virPCIDeviceListPtr inactivePciHostdevs; + virUSBDeviceListPtr activeUsbHostdevs; + virSCSIDeviceListPtr activeScsiHostdevs; +}; + +static virHostdevManagerPtr hostdevMgr; + +static int +virHostdevOnceInit(void) +{ + char ebuf[1024];
Typically a newline after local variables.
+ if (VIR_ALLOC(hostdevMgr) < 0) + goto out_of_memory; + + 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 (virAsprintf(&hostdevMgr->stateDir, + "%s",HOSTDEV_STATE_DIR) < 0)
This should all fit on one line. Also, need a space between ',' and 'HOSTDEV_STATE_DIR'.
+ goto out_of_memory; + + if (virFileMakePath(hostdevMgr->stateDir) < 0) { + VIR_ERROR(_("Failed to create state dir '%s': %s"), + hostdevMgr->stateDir, virStrerror(errno, ebuf, sizeof(ebuf))); + goto error; + } + + return 0; + +out_of_memory: + virReportOOMError();
VIR_ALLOC and virAsprintf already report the error so this can be dropped.
+error: + return -1; +} + +VIR_ONCE_GLOBAL_INIT(virHostdev) + +virHostdevManagerPtr +virHostdevManagerGetDefault(void) +{ + virHostdevInitialize(); + 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)
Fits on one line.
+ 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"));
To be consistent, space should be after 'on' instead of before '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);
Fits on one line.
+ 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"));
Same comment here about trailing vs leading space on multiple line error messages.
+ return ret; + } + + if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0) + return ret; + + virtPort = virDomainNetGetActualVirtPortProfile( + hostdev->parent.data.net);
Fits on one line.
+ if (virtPort) + ret = virHostdevNetConfigVirtPortProfile(linkdev, vf, virtPort, + &hostdev->parent.data.net->mac, NULL, + port_profile_associate);
Parameter whitespace alignment.
+ else + ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir); + + VIR_FREE(linkdev); + + return ret; +} + +static virPCIDeviceListPtr +virHostdevGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *driver) +{ + virPCIDeviceListPtr list; + size_t i; + + if (!(list = virPCIDeviceListNew())) + return NULL; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virPCIDevicePtr dev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + if (!dev) { + virObjectUnref(list); + return NULL; + } + + if (virPCIDeviceListAdd(list, dev) < 0) { + virPCIDeviceFree(dev); + virObjectUnref(list); + return NULL; + } + + virPCIDeviceSetManaged(dev, hostdev->managed); + + switch (hostdev->source.subsys.u.pci.backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) { + virObjectUnref(list); + return NULL; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) { + virObjectUnref(list); + return NULL; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + /* driver name not specified, if it's libxl driver, + * use pciback; otherwise, use pci-stub + */ + if (STREQ(driver, LIBXL_DRIVER_NAME)) { + if (virPCIDeviceSetStubDriver(dev, "pciback") < 0) { + virObjectUnref(list); + return NULL; + } + } else { + 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, drv_name))) + 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))) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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)); + VIR_FREE(other_drvname); + VIR_FREE(other_domname); + 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))) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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)); + VIR_FREE(other_drvname); + VIR_FREE(other_domname); + 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);
Space between ',' and 'drv_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, i); + 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))) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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, i);
Shouldn't this be '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)
Parameter whitespace alignment.
+{ + 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, const char *driver)
This line is > 80 columns.
+{ + /* 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(driver, QEMU_DRIVER_NAME) && + 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; + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + + /* Never delete the dev from list driver->activePciHostdevs + * if it's used by other domain. + */ + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev); + if (activeDev) { + if (virPCIDeviceGetUsedBy(activeDev, &usedby_drvname, &usedby_domname) < 0) { + VIR_WARN("Unable to get driver and domain name " + "using PCI device %s", + virPCIDeviceGetName(dev)); + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + + if (STRNEQ_NULLABLE(drv_name, usedby_drvname) || + STRNEQ_NULLABLE(dom_name, usedby_domname)) { + virPCIDeviceListSteal(pcidevs, dev); + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + } + + /* virObjectUnref() will take care of freeing the dev. */ + virPCIDeviceListSteal(mgr->activePciHostdevs, dev); + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + /* + * 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, drv_name); + } + + 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; + char *usedby_drvname = NULL; + char *usedby_domname = 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 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; + } + + if (virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname) < 0) { + VIR_WARN("Unable to get driver and domain name " + "using USB device %03d.%03d", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device); + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + + 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); + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + + } + + 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; + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + + 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; + } + + if (virSCSIDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname) < 0) { + VIR_WARN("Unable to get driver and domain name " + "using SCSI device %s:%d:%d:%d ", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit); + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + + 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); + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + virObjectUnlock(mgr->activeScsiHostdevs); +} + +void +virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags)
Parameter whitespace alignment.
+{ + 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;
Needs a line after local variable declaration.
+ 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) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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;
Needs line after local variable declaration.
+ 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; +} + +/* this function is used to free memory after using following + * get_active/inactive_list functions + */ +void +virHostdevNameListFree(virHostdevNameListPtr list) +{ + size_t i; + + if (list && list->names) { + for (i = 0; i < list->nnames; i++) { + VIR_FREE(list->names[i]); + } + VIR_FREE(list->names); + } + VIR_FREE(list); +} + +virHostdevNameListPtr +virHostdevGetActivePciHostdevs(virHostdevManagerPtr mgr) +{ + size_t i; + int count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activePciHostdevs); + + count = virPCIDeviceListCount(mgr->activePciHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError();
No need to explicitly report OOM error now.
+ goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
Same here.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(mgr->activePciHostdevs, i); + if (VIR_STRDUP(list->names[list->nnames++], virPCIDeviceGetName(dev)) < 0) + goto cleanup; + } + } + + goto end; + +cleanup: + virHostdevNameListFree(list); + +end: + virObjectUnlock(mgr->activePciHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetActiveUsbHostdevs(virHostdevManagerPtr mgr) +{ + size_t i; + int count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeUsbHostdevs); + + count = virUSBDeviceListCount(mgr->activeUsbHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError();
Same, no need for OOM error reporting.
+ goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
And again.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virUSBDevicePtr usb = virUSBDeviceListGet(mgr->activeUsbHostdevs, i); + if (VIR_STRDUP(list->names[list->nnames++], virUSBDeviceGetName(usb)) < 0) + goto cleanup; + } + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeUsbHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetActiveScsiHostdevs(virHostdevManagerPtr mgr) +{ + size_t i; + int count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeScsiHostdevs); + + count = virSCSIDeviceListCount(mgr->activeScsiHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError();
And again.
+ goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
Here too.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virSCSIDevicePtr dev = virSCSIDeviceListGet(mgr->activeScsiHostdevs, i); + if (VIR_STRDUP(list->names[list->nnames++], virSCSIDeviceGetName(dev)) < 0) + goto cleanup; + } + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeScsiHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetDomainActivePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name) +{ + size_t i, count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activePciHostdevs); + + count = virPCIDeviceListCount(mgr->activePciHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError();
Same.
+ goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
Same.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(mgr->activePciHostdevs, i); + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + int ret; + + if (virPCIDeviceGetUsedBy(dev, &usedby_drvname, &usedby_domname) < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + + goto cleanup; + } + + if (STREQ(drv_name, usedby_drvname) && + STREQ(dom_name, usedby_domname)) { + ret = VIR_STRDUP(list->names[list->nnames++], virPCIDeviceGetName(dev)); + if (ret < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + VIR_SHRINK_N(list->names, count, count - list->nnames); + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activePciHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name) +{ + size_t i, count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeUsbHostdevs); + + count = virUSBDeviceListCount(mgr->activeUsbHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
A few more cases of unneeded OOM error reporting.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virUSBDevicePtr usb = virUSBDeviceListGet(mgr->activeUsbHostdevs, i); + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + int ret; + + if (virUSBDeviceGetUsedBy(usb, &usedby_drvname, &usedby_domname) < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + + if (STREQ(drv_name, usedby_drvname) && + STREQ(dom_name, usedby_domname)) { + ret = VIR_STRDUP(list->names[list->nnames++], virUSBDeviceGetName(usb)); + if (ret < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + VIR_SHRINK_N(list->names, count, count - list->nnames); + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeUsbHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetDomainActiveScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name) +{ + size_t i, count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeScsiHostdevs); + + count = virSCSIDeviceListCount(mgr->activeScsiHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
You're probably getting tired of this comment by now :).
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virSCSIDevicePtr dev = virSCSIDeviceListGet(mgr->activeScsiHostdevs, i); + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + int ret; + + if (virSCSIDeviceGetUsedBy(dev, &usedby_drvname, &usedby_domname) < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + + if (STREQ(drv_name, usedby_drvname) && + STREQ(dom_name, usedby_domname)) { + ret = VIR_STRDUP(list->names[list->nnames++], virSCSIDeviceGetName(dev)); + if (ret < 0){ + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + VIR_SHRINK_N(list->names, count, count - list->nnames); + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeScsiHostdevs); + return list; +} diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h new file mode 100644 index 0000000..09377db --- /dev/null +++ b/src/util/virhostdev.h @@ -0,0 +1,123 @@ +/* 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_MANAGER_H__ +# define __VIR_HOSTDEV_MANAGER_H__ + +# include "internal.h" +# include "configmake.h" + +# include "domain_conf.h" +# include "virpci.h" + +# define HOSTDEV_STATE_DIR LOCALSTATEDIR "/run/libvirt/hostdevmanager"
This could be moved to virhostdev.c right?
+ +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);
Parameter whitespace alignment in these two function declarations.
+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); + +/* functions to get active/inactive lists */ +virHostdevNameListPtr virHostdevGetActivePciHostdevs(virHostdevManagerPtr mgr); +virHostdevNameListPtr virHostdevGetActiveUsbHostdevs(virHostdevManagerPtr mgr); +virHostdevNameListPtr virHostdevGetActiveScsiHostdevs(virHostdevManagerPtr mgr); +virHostdevNameListPtr virHostdevGetDomainActivePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name); +virHostdevNameListPtr virHostdevGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name); +virHostdevNameListPtr virHostdevGetDomainActiveScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name); +/* function to free memory after get active/inactive lists */ +void virHostdevNameListFree(virHostdevNameListPtr list); + +#endif /* __VIR_HOSTDEV_MANAGER_H__ */ diff --git a/src/util/virpci.c b/src/util/virpci.c index be50b4f..dc38efe 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 */ + const char *used_by_drvname; + const char *used_by_domname;
unsigned int pcie_cap_pos; unsigned int pci_pm_cap_pos; @@ -1642,15 +1645,21 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool reprobe) }
void -virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name) +virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *drv_name, const char *dom_name) { - dev->used_by = name; + dev->used_by_drvname = drv_name; + dev->used_by_domname = dom_name; }
-const char * -virPCIDeviceGetUsedBy(virPCIDevicePtr dev) +int +virPCIDeviceGetUsedBy(virPCIDevicePtr dev, char **drv_name, char **dom_name) { - return dev->used_by; + if (VIR_STRDUP(*drv_name, dev->used_by_drvname) < 0) + return -1; + if (VIR_STRDUP(*dom_name, dev->used_by_domname) < 0) + return -1; + + return 0; }
void virPCIDeviceReattachInit(virPCIDevicePtr pci) diff --git a/src/util/virpci.h b/src/util/virpci.h index 0aa6fee..dc1a80b 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -66,8 +66,11 @@ 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); + const char *drv_name, + const char *dom_name); +int virPCIDeviceGetUsedBy(virPCIDevice *dev, + char **drv_name, + char **dom_name);
Parameters not aligned properly in these two function declarations.
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..dc1eebb 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 */ + const char *used_by_drvname; + const char *used_by_domname;
bool readonly; }; @@ -267,15 +270,22 @@ virSCSIDeviceFree(virSCSIDevicePtr dev)
void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { - dev->used_by = name; + dev->used_by_drvname = drvname; + dev->used_by_domname = domname; }
-const char * -virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev) +int +virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev, char **drvname, char **domname) { - return dev->used_by; + if (VIR_STRDUP(*drvname, dev->used_by_drvname) < 0) + return -1; + if (VIR_STRDUP(*domname, dev->used_by_domname) < 0) + return -1; + + return 0; }
const char * diff --git a/src/util/virscsi.h b/src/util/virscsi.h index cce5df4..89b3e5d 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); +void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, + const char *drvname, + const char *domname); +int virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev, + char **drvname, + 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..54d836d 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 */ + const char *used_by_drvname; + const char *used_by_domname; };
struct _virUSBDeviceList { @@ -378,16 +381,24 @@ virUSBDeviceFree(virUSBDevicePtr dev) VIR_FREE(dev); }
- void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, - const char *name) + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + dev->used_by_drvname = drv_name; + dev->used_by_domname = dom_name; }
-const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev) +int virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + char **drv_name, + char **dom_name)
Parameter alignment. This patch is looking good, but since I'm not terribly familiar with this area of libvirt and historically there have been quite a few bugs related to passthrough, it would be best if one of the devs familiar with this code (Laine, Osier) could also review it. Regards, Jim
{ - return dev->used_by; + if (VIR_STRDUP(*drv_name, dev->used_by_drvname) < 0) + return -1; + if (VIR_STRDUP(*dom_name, dev->used_by_domname) < 0) + return -1; + + return 0; }
const char *virUSBDeviceGetName(virUSBDevicePtr dev) diff --git a/src/util/virusb.h b/src/util/virusb.h index aa59d12..5f1df58 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); +void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name); +int virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + char **drv_name, + char **dom_name); const char *virUSBDeviceGetName(virUSBDevicePtr dev);
unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev);

2013/8/12 Jim Fehlig <jfehlig@suse.com>
+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))) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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, i);
Shouldn't this be 'virSCSIDeviceListGet(list, j)'?
Oh, shamed. Yes, should be 'virSCSIDeviceListGet(list, j)'. I copied it
from qemu_hostdev.c, wrong in qemu_hostdev.c too.
+ virSCSIDeviceListSteal(mgr->activeScsiHostdevs, tmp);
+ } + virObjectUnlock(mgr->activeScsiHostdevs); +cleanup: + virObjectUnref(list); + return -1; +} + + +int +virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags)
Parameter whitespace alignment.
+{ + 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, const char *driver)
This line is > 80 columns.
+{ + /* 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(driver, QEMU_DRIVER_NAME) && + 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; + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + + /* Never delete the dev from list driver->activePciHostdevs + * if it's used by other domain. + */ + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev); + if (activeDev) { + if (virPCIDeviceGetUsedBy(activeDev, &usedby_drvname, &usedby_domname) < 0) { + VIR_WARN("Unable to get driver and domain name " + "using PCI device %s", + virPCIDeviceGetName(dev)); + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + + if (STRNEQ_NULLABLE(drv_name, usedby_drvname) || + STRNEQ_NULLABLE(dom_name, usedby_domname)) { + virPCIDeviceListSteal(pcidevs, dev); + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + } + + /* virObjectUnref() will take care of freeing the dev. */ + virPCIDeviceListSteal(mgr->activePciHostdevs, dev); + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + /* + * 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, drv_name); + } + + 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; + char *usedby_drvname = NULL; + char *usedby_domname = 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 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; + } + + if (virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname) < 0) { + VIR_WARN("Unable to get driver and domain name " + "using USB device %03d.%03d", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device); + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + + 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); + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + + } + + 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; + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + + 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; + } + + if (virSCSIDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname) < 0) { + VIR_WARN("Unable to get driver and domain name " + "using SCSI device %s:%d:%d:%d ", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit); + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + continue; + } + + 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); + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + virObjectUnlock(mgr->activeScsiHostdevs); +} + +void +virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags)
Parameter whitespace alignment.
+{ + 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;
Needs a line after local variable declaration.
+ 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) { + char *other_drvname = NULL; + char *other_domname = NULL; + 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;
Needs line after local variable declaration.
+ 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; +} + +/* this function is used to free memory after using following + * get_active/inactive_list functions + */ +void +virHostdevNameListFree(virHostdevNameListPtr list) +{ + size_t i; + + if (list && list->names) { + for (i = 0; i < list->nnames; i++) { + VIR_FREE(list->names[i]); + } + VIR_FREE(list->names); + } + VIR_FREE(list); +} + +virHostdevNameListPtr +virHostdevGetActivePciHostdevs(virHostdevManagerPtr mgr) +{ + size_t i; + int count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activePciHostdevs); + + count = virPCIDeviceListCount(mgr->activePciHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError();
No need to explicitly report OOM error now.
+ goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
Same here.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(mgr->activePciHostdevs, i); + if (VIR_STRDUP(list->names[list->nnames++], virPCIDeviceGetName(dev)) < 0) + goto cleanup; + } + } + + goto end; + +cleanup: + virHostdevNameListFree(list); + +end: + virObjectUnlock(mgr->activePciHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetActiveUsbHostdevs(virHostdevManagerPtr mgr) +{ + size_t i; + int count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeUsbHostdevs); + + count = virUSBDeviceListCount(mgr->activeUsbHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError();
Same, no need for OOM error reporting.
+ goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
And again.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virUSBDevicePtr usb = virUSBDeviceListGet(mgr->activeUsbHostdevs, i); + if (VIR_STRDUP(list->names[list->nnames++], virUSBDeviceGetName(usb)) < 0) + goto cleanup; + } + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeUsbHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetActiveScsiHostdevs(virHostdevManagerPtr mgr) +{ + size_t i; + int count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeScsiHostdevs); + + count = virSCSIDeviceListCount(mgr->activeScsiHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError();
And again.
+ goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
Here too.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virSCSIDevicePtr dev = virSCSIDeviceListGet(mgr->activeScsiHostdevs, i); + if (VIR_STRDUP(list->names[list->nnames++], virSCSIDeviceGetName(dev)) < 0) + goto cleanup; + } + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeScsiHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetDomainActivePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name) +{ + size_t i, count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activePciHostdevs); + + count = virPCIDeviceListCount(mgr->activePciHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError();
Same.
+ goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
Same.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(mgr->activePciHostdevs, i); + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + int ret; + + if (virPCIDeviceGetUsedBy(dev, &usedby_drvname, &usedby_domname) < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + + goto cleanup; + } + + if (STREQ(drv_name, usedby_drvname) && + STREQ(dom_name, usedby_domname)) { + ret = VIR_STRDUP(list->names[list->nnames++], virPCIDeviceGetName(dev)); + if (ret < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + VIR_SHRINK_N(list->names, count, count - list->nnames); + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activePciHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name) +{ + size_t i, count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeUsbHostdevs); + + count = virUSBDeviceListCount(mgr->activeUsbHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
A few more cases of unneeded OOM error reporting.
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virUSBDevicePtr usb = virUSBDeviceListGet(mgr->activeUsbHostdevs, i); + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + int ret; + + if (virUSBDeviceGetUsedBy(usb, &usedby_drvname, &usedby_domname) < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + + if (STREQ(drv_name, usedby_drvname) && + STREQ(dom_name, usedby_domname)) { + ret = VIR_STRDUP(list->names[list->nnames++], virUSBDeviceGetName(usb)); + if (ret < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + VIR_SHRINK_N(list->names, count, count - list->nnames); + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeUsbHostdevs); + return list; +} + +virHostdevNameListPtr +virHostdevGetDomainActiveScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name) +{ + size_t i, count; + virHostdevNameListPtr list = NULL; + virObjectLock(mgr->activeScsiHostdevs); + + count = virSCSIDeviceListCount(mgr->activeScsiHostdevs); + if (count > 0) { + if (VIR_ALLOC_N(list, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_ALLOC_N(list->names,count) < 0) { + virReportOOMError();
You're probably getting tired of this comment by now :).
+ goto cleanup; + } + list->nnames = 0; + + for (i = 0; i < count; i++) { + virSCSIDevicePtr dev = virSCSIDeviceListGet(mgr->activeScsiHostdevs, i); + char *usedby_drvname = NULL; + char *usedby_domname = NULL; + int ret; + + if (virSCSIDeviceGetUsedBy(dev, &usedby_drvname, &usedby_domname) < 0) { + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + + if (STREQ(drv_name, usedby_drvname) && + STREQ(dom_name, usedby_domname)) { + ret = VIR_STRDUP(list->names[list->nnames++], virSCSIDeviceGetName(dev)); + if (ret < 0){ + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + goto cleanup; + } + } + + VIR_FREE(usedby_drvname); + VIR_FREE(usedby_domname); + } + + VIR_SHRINK_N(list->names, count, count - list->nnames); + } + + goto end; + +cleanup: + virHostdevNameListFree(list); +end: + virObjectUnlock(mgr->activeScsiHostdevs); + return list; +} diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h new file mode 100644 index 0000000..09377db --- /dev/null +++ b/src/util/virhostdev.h @@ -0,0 +1,123 @@ +/* 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_MANAGER_H__ +# define __VIR_HOSTDEV_MANAGER_H__ + +# include "internal.h" +# include "configmake.h" + +# include "domain_conf.h" +# include "virpci.h" + +# define HOSTDEV_STATE_DIR LOCALSTATEDIR "/run/libvirt/hostdevmanager"
This could be moved to virhostdev.c right?
Yeah, could be.
+ +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);
Parameter whitespace alignment in these two function declarations.
+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); + +/* functions to get active/inactive lists */ +virHostdevNameListPtr virHostdevGetActivePciHostdevs(virHostdevManagerPtr mgr); +virHostdevNameListPtr virHostdevGetActiveUsbHostdevs(virHostdevManagerPtr mgr); +virHostdevNameListPtr virHostdevGetActiveScsiHostdevs(virHostdevManagerPtr mgr); +virHostdevNameListPtr virHostdevGetDomainActivePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name); +virHostdevNameListPtr virHostdevGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name); +virHostdevNameListPtr virHostdevGetDomainActiveScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name); +/* function to free memory after get active/inactive lists */ +void virHostdevNameListFree(virHostdevNameListPtr list); + +#endif /* __VIR_HOSTDEV_MANAGER_H__ */ diff --git a/src/util/virpci.c b/src/util/virpci.c index be50b4f..dc38efe 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 */ + const char *used_by_drvname; + const char *used_by_domname;
unsigned int pcie_cap_pos; unsigned int pci_pm_cap_pos; @@ -1642,15 +1645,21 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool reprobe) }
void -virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name) +virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *drv_name, const char *dom_name) { - dev->used_by = name; + dev->used_by_drvname = drv_name; + dev->used_by_domname = dom_name; }
-const char * -virPCIDeviceGetUsedBy(virPCIDevicePtr dev) +int +virPCIDeviceGetUsedBy(virPCIDevicePtr dev, char **drv_name, char **dom_name) { - return dev->used_by; + if (VIR_STRDUP(*drv_name, dev->used_by_drvname) < 0) + return -1; + if (VIR_STRDUP(*dom_name, dev->used_by_domname) < 0) + return -1; + + return 0; }
void virPCIDeviceReattachInit(virPCIDevicePtr pci) diff --git a/src/util/virpci.h b/src/util/virpci.h index 0aa6fee..dc1a80b 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -66,8 +66,11 @@ 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); + const char *drv_name, + const char *dom_name); +int virPCIDeviceGetUsedBy(virPCIDevice *dev, + char **drv_name, + char **dom_name);
Parameters not aligned properly in these two function declarations.
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..dc1eebb 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 */ + const char *used_by_drvname; + const char *used_by_domname;
bool readonly; }; @@ -267,15 +270,22 @@ virSCSIDeviceFree(virSCSIDevicePtr dev)
void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { - dev->used_by = name; + dev->used_by_drvname = drvname; + dev->used_by_domname = domname; }
-const char * -virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev) +int +virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev, char **drvname, char **domname) { - return dev->used_by; + if (VIR_STRDUP(*drvname, dev->used_by_drvname) < 0) + return -1; + if (VIR_STRDUP(*domname, dev->used_by_domname) < 0) + return -1; + + return 0; }
const char * diff --git a/src/util/virscsi.h b/src/util/virscsi.h index cce5df4..89b3e5d 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); +void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, + const char *drvname, + const char *domname); +int virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev, + char **drvname, + 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..54d836d 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 */ + const char *used_by_drvname; + const char *used_by_domname; };
struct _virUSBDeviceList { @@ -378,16 +381,24 @@ virUSBDeviceFree(virUSBDevicePtr dev) VIR_FREE(dev); }
- void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, - const char *name) + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + dev->used_by_drvname = drv_name; + dev->used_by_domname = dom_name; }
-const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev) +int virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + char **drv_name, + char **dom_name)
Parameter alignment.
This patch is looking good, but since I'm not terribly familiar with this area of libvirt and historically there have been quite a few bugs related to passthrough, it would be best if one of the devs familiar with this code (Laine, Osier) could also review it.
Regards, Jim
Thanks a lot for your review and comment. Since this patch affect existing qemu/lxc driver too, I also hope some one expert in that could review and point out problem if there is. Thanks, Chunyan
{ - return dev->used_by; + if (VIR_STRDUP(*drv_name, dev->used_by_drvname) < 0) + return -1; + if (VIR_STRDUP(*dom_name, dev->used_by_domname) < 0) + return -1; + + return 0; }
const char *virUSBDeviceGetName(virUSBDevicePtr dev) diff --git a/src/util/virusb.h b/src/util/virusb.h index aa59d12..5f1df58 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); +void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name); +int virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + char **drv_name, + char **dom_name); const char *virUSBDeviceGetName(virUSBDevicePtr dev);
unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev);

From: Chunyan Liu <cyliu@suse.com> 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> --- src/libxl/libxl_conf.c | 65 ++++++++++++ src/libxl/libxl_conf.h | 5 +- src/libxl/libxl_driver.c | 250 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 315 insertions(+), 5 deletions(-) diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index 827dfdd..aa6fd1e 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -871,6 +871,67 @@ error: return -1; } +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) { + virReportOOMError(); + 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) { @@ -932,6 +993,10 @@ libxlBuildDomainConfig(libxlDriverPrivatePtr driver, 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 aa57710..db3e53d 100644 --- a/src/libxl/libxl_conf.h +++ b/src/libxl/libxl_conf.h @@ -36,7 +36,7 @@ # include "virobject.h" # include "virchrdev.h" - +# define LIBXL_DRIVER_NAME "xenlight" # define LIBXL_VNC_PORT_MIN 5900 # define LIBXL_VNC_PORT_MAX 65535 @@ -126,7 +126,8 @@ libxlMakeNic(virDomainNetDefPtr l_nic, libxl_device_nic *x_nic); int 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 9e9bc89..8166baf 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -49,6 +49,7 @@ #include "virstring.h" #include "virsysinfo.h" #include "viraccessapicheck.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_LIBXL @@ -698,6 +699,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, @@ -705,6 +707,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; } @@ -928,6 +934,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, char *managed_save_path = NULL; int managed_save_fd = -1; libxlDomainObjPrivatePtr priv = vm->privateData; + virHostdevManagerPtr hostdev_mgr; /* If there is a managed saved state restore it instead of starting * from scratch. The old state is removed once the restoring succeeded. */ @@ -982,6 +989,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) @@ -1075,6 +1088,7 @@ libxlReconnectDomain(virDomainObjPtr vm, libxl_dominfo d_info; int len; uint8_t *data = NULL; + virHostdevManagerPtr hostdev_mgr; virObjectLock(vm); @@ -1097,6 +1111,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 (!driver->nactive && driver->inhibitCallback) @@ -3521,6 +3542,112 @@ 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) { + virReportOOMError(); + 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; + } +/* + if (qemuSetupHostdevCGroup(vm, hostdev) < 0) + return -1; + + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm->def, hostdev, NULL) < 0) + goto cleanup; +*/ + 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: +/* if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, + vm->def, hostdev, NULL) < 0) + VIR_WARN("Unable to restore host device labelling on hotplug fail"); + +cleanup: + if (qemuTeardownHostdevCgroup(vm, hostdev) < 0) + VIR_WARN("Unable to remove host device cgroup ACL on hotplug fail"); +*/ + return -1; +} + +static int libxlDomainDetachDeviceDiskLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -3586,7 +3713,11 @@ libxlDomainAttachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, if (!ret) 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"), @@ -3601,6 +3732,8 @@ static int libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { virDomainDiskDefPtr disk; + virDomainHostdevDefPtr hostdev; + virDomainHostdevDefPtr found; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -3615,6 +3748,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", @@ -3625,6 +3777,95 @@ libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) } 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 (qemuIsMultiFunctionDevice(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; + } +*/ + 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 (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; + } + + virDomainHostdevRemove(vm->def, idx); + + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachPciHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def->name, &hostdev, 1); + +/* + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) && + qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, + &detach->info->addr.pci) < 0) + VIR_WARN("Unable to release PCI address on host device"); +*/ + + 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) { @@ -3634,7 +3875,9 @@ libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, case VIR_DOMAIN_DEVICE_DISK: 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"), @@ -3645,6 +3888,7 @@ libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, return ret; } + static int libxlDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { @@ -4903,7 +5147,7 @@ libxlConnectSupportsFeature(virConnectPtr conn, int feature) 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 */ -- 1.6.0.2

cyliu@suse.com wrote:
From: Chunyan Liu <cyliu@suse.com>
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> --- src/libxl/libxl_conf.c | 65 ++++++++++++ src/libxl/libxl_conf.h | 5 +- src/libxl/libxl_driver.c | 250 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 315 insertions(+), 5 deletions(-)
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index 827dfdd..aa6fd1e 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -871,6 +871,67 @@ error: return -1; }
+int libxlMakePci(virDomainHostdevDefPtr hostdev, libxl_device_pci *pcidev)
Return type and function name on separate lines.
+{ + 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) { + virReportOOMError();
No need to report OOM error.
+ 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]); + }
No need for the braces. From HACKING: Omit the curly braces around an "if", "while", "for" etc. body only when that body occupies a single line.
+ VIR_FREE(x_pcidevs); + return -1; +} + virCapsPtr libxlMakeCapabilities(libxl_ctx *ctx) { @@ -932,6 +993,10 @@ libxlBuildDomainConfig(libxlDriverPrivatePtr driver, return -1; }
+ if (libxlMakePciList(def, d_config) < 0) { + return -1; + } +
Same here, no need for the braces. (I should put together a cleanup patch to fix the existing offenses in this file.)
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 aa57710..db3e53d 100644 --- a/src/libxl/libxl_conf.h +++ b/src/libxl/libxl_conf.h @@ -36,7 +36,7 @@ # include "virobject.h" # include "virchrdev.h"
-
Spurious whitespace change.
+# define LIBXL_DRIVER_NAME "xenlight" # define LIBXL_VNC_PORT_MIN 5900 # define LIBXL_VNC_PORT_MAX 65535
@@ -126,7 +126,8 @@ libxlMakeNic(virDomainNetDefPtr l_nic, libxl_device_nic *x_nic); int libxlMakeVfb(libxlDriverPrivatePtr driver, virDomainGraphicsDefPtr l_vfb, libxl_device_vfb *x_vfb); -
Same here. I'd prefer a line between all these function declarations.
+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 9e9bc89..8166baf 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -49,6 +49,7 @@ #include "virstring.h" #include "virsysinfo.h" #include "viraccessapicheck.h" +#include "virhostdev.h"
#define VIR_FROM_THIS VIR_FROM_LIBXL
@@ -698,6 +699,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, @@ -705,6 +707,10 @@ libxlVmReap(libxlDriverPrivatePtr driver, return -1; }
+ hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachDomainHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV);
Parameter indentation looks wrong.
+ libxlVmCleanup(driver, vm, reason); return 0; } @@ -928,6 +934,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, char *managed_save_path = NULL; int managed_save_fd = -1; libxlDomainObjPrivatePtr priv = vm->privateData; + virHostdevManagerPtr hostdev_mgr;
/* If there is a managed saved state restore it instead of starting * from scratch. The old state is removed once the restoring succeeded. */ @@ -982,6 +989,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) @@ -1075,6 +1088,7 @@ libxlReconnectDomain(virDomainObjPtr vm, libxl_dominfo d_info; int len; uint8_t *data = NULL; + virHostdevManagerPtr hostdev_mgr;
virObjectLock(vm);
@@ -1097,6 +1111,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 (!driver->nactive && driver->inhibitCallback) @@ -3521,6 +3542,112 @@ 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) { + virReportOOMError();
No need to report OOM error.
+ 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; + } +/* + if (qemuSetupHostdevCGroup(vm, hostdev) < 0) + return -1; + + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm->def, hostdev, NULL) < 0) + goto cleanup; +*/
Looks like this should be removed from the libxl driver :).
+ 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: +/* if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, + vm->def, hostdev, NULL) < 0) + VIR_WARN("Unable to restore host device labelling on hotplug fail"); + +cleanup: + if (qemuTeardownHostdevCgroup(vm, hostdev) < 0) + VIR_WARN("Unable to remove host device cgroup ACL on hotplug fail"); +*/ + return -1;
Again, don't think this is needed in the libxl driver. Also, there is nothing to cleanup so might as well just return -1 when errors are encountered.
+} + +static int libxlDomainDetachDeviceDiskLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -3586,7 +3713,11 @@ libxlDomainAttachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, if (!ret) dev->data.disk = NULL; break; -
Spurious whitespace change.
+ case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = libxlDomainAttachHostDevice(priv, vm, dev); + if (!ret) + dev->data.hostdev = NULL; + break;
Generally there is a line between case statements, including the default case.
default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), @@ -3601,6 +3732,8 @@ static int libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { virDomainDiskDefPtr disk; + virDomainHostdevDefPtr hostdev; + virDomainHostdevDefPtr found;
switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -3615,6 +3748,25 @@ libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) /* vmdef has the pointer. Generic codes for vmdef will do all jobs */ dev->data.disk = NULL; break;
Same here, line before the next case.
+ 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", @@ -3625,6 +3777,95 @@ libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) }
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 (qemuIsMultiFunctionDevice(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; + } +*/
Hmm, is a similar check needed in the libxl driver? It looks to me like this is checking for other devices with same domain, bus, and slot but different function. Isn't it possible to detach one function of a multi-function device, even if another function of the device is used by the domain?
+ 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 (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; + } + + virDomainHostdevRemove(vm->def, idx); + + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachPciHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def->name, &hostdev, 1); + +/* + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) && + qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, + &detach->info->addr.pci) < 0) + VIR_WARN("Unable to release PCI address on host device"); +*/
I don't think this is needed for the libxl driver, right? Also, does the libxl_device_pci need disposed?
+ + return 0; + +cleanup: + virDomainHostdevDefFree(detach); + return -1; +} + +static int libxlDomainDetachHostDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev)
Return type and function name on different lines.
+{ + 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);
Line between cases.
+ default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected hostdev type %d"), subsys->type); + break; + } + + return -1; +} + +static int libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -3634,7 +3875,9 @@ libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, case VIR_DOMAIN_DEVICE_DISK: ret = libxlDomainDetachDeviceDiskLive(priv, vm, dev); break; -
Spurious whitespace change.
+ case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = libxlDomainDetachHostDevice(priv, vm, dev); + break;
Line between cases. Regards, Jim
default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be detached"), @@ -3645,6 +3888,7 @@ libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, return ret; }
+ static int libxlDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { @@ -4903,7 +5147,7 @@ libxlConnectSupportsFeature(virConnectPtr conn, int feature)
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 */

2013/8/14 Jim Fehlig <jfehlig@suse.com>
cyliu@suse.com wrote:
From: Chunyan Liu <cyliu@suse.com>
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> --- src/libxl/libxl_conf.c | 65 ++++++++++++ src/libxl/libxl_conf.h | 5 +- src/libxl/libxl_driver.c | 250 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 315 insertions(+), 5 deletions(-)
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index 827dfdd..aa6fd1e 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -871,6 +871,67 @@ error: return -1; }
+int libxlMakePci(virDomainHostdevDefPtr hostdev, libxl_device_pci *pcidev)
Return type and function name on separate lines.
+{ + 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) { + virReportOOMError();
No need to report OOM error.
+ 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]); + }
No need for the braces. From HACKING:
Omit the curly braces around an "if", "while", "for" etc. body only when that body occupies a single line.
+ VIR_FREE(x_pcidevs); + return -1; +} + virCapsPtr libxlMakeCapabilities(libxl_ctx *ctx) { @@ -932,6 +993,10 @@ libxlBuildDomainConfig(libxlDriverPrivatePtr driver, return -1; }
+ if (libxlMakePciList(def, d_config) < 0) { + return -1; + } +
Same here, no need for the braces. (I should put together a cleanup patch to fix the existing offenses in this file.)
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 aa57710..db3e53d 100644 --- a/src/libxl/libxl_conf.h +++ b/src/libxl/libxl_conf.h @@ -36,7 +36,7 @@ # include "virobject.h" # include "virchrdev.h"
-
Spurious whitespace change.
+# define LIBXL_DRIVER_NAME "xenlight" # define LIBXL_VNC_PORT_MIN 5900 # define LIBXL_VNC_PORT_MAX 65535
@@ -126,7 +126,8 @@ libxlMakeNic(virDomainNetDefPtr l_nic, libxl_device_nic *x_nic); int libxlMakeVfb(libxlDriverPrivatePtr driver, virDomainGraphicsDefPtr l_vfb, libxl_device_vfb *x_vfb); -
Same here. I'd prefer a line between all these function declarations.
+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 9e9bc89..8166baf 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -49,6 +49,7 @@ #include "virstring.h" #include "virsysinfo.h" #include "viraccessapicheck.h" +#include "virhostdev.h"
#define VIR_FROM_THIS VIR_FROM_LIBXL
@@ -698,6 +699,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, @@ -705,6 +707,10 @@ libxlVmReap(libxlDriverPrivatePtr driver, return -1; }
+ hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachDomainHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV);
Parameter indentation looks wrong.
+ goto error; + /* use as synchronous operations => ao_how = NULL and no intermediate reports => ao_progress = NULL */
if (restore_fd < 0) @@ -1075,6 +1088,7 @@ libxlReconnectDomain(virDomainObjPtr vm, libxl_dominfo d_info; int len; uint8_t *data = NULL; + virHostdevManagerPtr hostdev_mgr;
virObjectLock(vm);
@@ -1097,6 +1111,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) <
+ libxlVmCleanup(driver, vm, reason); return 0; } @@ -928,6 +934,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, char *managed_save_path = NULL; int managed_save_fd = -1; libxlDomainObjPrivatePtr priv = vm->privateData; + virHostdevManagerPtr hostdev_mgr;
/* If there is a managed saved state restore it instead of starting * from scratch. The old state is removed once the restoring succeeded. */ @@ -982,6 +989,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) <
+ goto out; + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNKNOWN);
if (!driver->nactive && driver->inhibitCallback) @@ -3521,6 +3542,112 @@ 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) { + virReportOOMError();
No need to report OOM error.
+ 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; + } +/* + if (qemuSetupHostdevCGroup(vm, hostdev) < 0) + return -1; + + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm->def, hostdev, NULL) < 0) + goto cleanup; +*/
Looks like this should be removed from the libxl driver :).
+ 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: +/* if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, + vm->def, hostdev, NULL) <
+ VIR_WARN("Unable to restore host device labelling on hotplug fail"); + +cleanup: + if (qemuTeardownHostdevCgroup(vm, hostdev) < 0) + VIR_WARN("Unable to remove host device cgroup ACL on hotplug fail"); +*/ + return -1;
Again, don't think this is needed in the libxl driver. Also, there is nothing to cleanup so might as well just return -1 when errors are encountered.
+} + +static int libxlDomainDetachDeviceDiskLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -3586,7 +3713,11 @@ libxlDomainAttachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, if (!ret) dev->data.disk = NULL; break; -
Spurious whitespace change.
+ case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = libxlDomainAttachHostDevice(priv, vm, dev); + if (!ret) + dev->data.hostdev = NULL; + break;
Generally there is a line between case statements, including the default case.
default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), @@ -3601,6 +3732,8 @@ static int libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef,
virDomainDeviceDefPtr dev)
{ virDomainDiskDefPtr disk; + virDomainHostdevDefPtr hostdev; + virDomainHostdevDefPtr found;
switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -3615,6 +3748,25 @@ libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) /* vmdef has the pointer. Generic codes for vmdef will do all jobs */ dev->data.disk = NULL; break;
Same here, line before the next case.
+ 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", @@ -3625,6 +3777,95 @@ libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) }
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 (qemuIsMultiFunctionDevice(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; + } +*/
Hmm, is a similar check needed in the libxl driver? It looks to me like this is checking for other devices with same domain, bus, and slot but different function. Isn't it possible to detach one function of a multi-function device, even if another function of the device is used by the domain?
Not sure. Some one could explain? Quoted from Fedora documentation: "Red Hat Enterprise Linux 6.0 and newer supports hot plugging assigned PCI devices into virtual machines. However, PCI device hot plugging operates at the slot level and therefore does not support multi-function PCI devices. Multi-function PCI devices are recommended for static device configuration only." But in qemu driver, there isn't limit to ATTACH a multi-function PCI device, only limit to DETACH.
+ if (subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ r eturn -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 (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; + } + + virDomainHostdevRemove(vm->def, idx); + + hostdev_mgr = virHostdevManagerGetDefault(); + virHostdevReAttachPciHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def->name, &hostdev, 1); + +/* + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) && + qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, + &detach->info->addr.pci) < 0) + VIR_WARN("Unable to release PCI address on host device"); +*/
I don't think this is needed for the libxl driver, right?
Will remove. Also, does
the libxl_device_pci need disposed?
libxl_device_pci_dispose, not necessary (only does memset work) but better to keep code more readable.Will update.
+
+ return 0; + +cleanup: + virDomainHostdevDefFree(detach); + return -1; +} + +static int libxlDomainDetachHostDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev)
Return type and function name on different lines.
+{ + 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);
Line between cases.
+ default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected hostdev type %d"), subsys->type); + break; + } + + return -1; +} + +static int libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -3634,7 +3875,9 @@ libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, case VIR_DOMAIN_DEVICE_DISK: ret = libxlDomainDetachDeviceDiskLive(priv, vm, dev); break; -
Spurious whitespace change.
+ case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = libxlDomainDetachHostDevice(priv, vm, dev); + break;
Line between cases.
Regards, Jim
default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be detached"), @@ -3645,6 +3888,7 @@
libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm,
return ret; }
+ static int libxlDomainDetachDeviceConfig(virDomainDefPtr vmdef,
virDomainDeviceDefPtr dev)
{ @@ -4903,7 +5147,7 @@ libxlConnectSupportsFeature(virConnectPtr conn, int feature)
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 */

From: Chunyan Liu <cyliu@suse.com> 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 | 127 ++--- src/qemu/qemu_process.c | 34 +- 9 files changed, 88 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 9176b95..1124311 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -97,7 +97,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 a89e414..20fa84e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -632,7 +632,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_conf.c qemu/qemu_conf.h \ qemu/qemu_process.c qemu/qemu_process.h \ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index aa3a2fd..977317f 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 8229cfc..26050ec 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; @@ -203,14 +204,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 5634abf..776c16b 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); @@ -10906,12 +10889,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); @@ -10945,18 +10928,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); @@ -10973,13 +10950,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) @@ -10999,33 +10975,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); @@ -11036,12 +10991,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) @@ -11061,17 +11016,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 032de69..c94fd16 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -29,7 +29,8 @@ #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" @@ -49,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 @@ -1007,12 +1009,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 @@ -1093,7 +1102,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); @@ -1278,26 +1288,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) @@ -1326,9 +1331,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; } @@ -1342,6 +1347,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) || @@ -1351,8 +1357,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, @@ -1400,7 +1409,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; @@ -2362,71 +2372,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 @@ -2467,13 +2444,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 d631a6f..0f32c6c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -38,10 +38,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" @@ -64,6 +64,7 @@ #include "viratomic.h" #include "virnuma.h" #include "virstring.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -3018,6 +3019,8 @@ qemuProcessReconnect(void *opaque) int reason; virQEMUDriverConfigPtr cfg; size_t i; + virHostdevManagerPtr hostdev_mgr; + unsigned int flags; memcpy(&oldjob, &data->oldjob, sizeof(oldjob)); @@ -3049,14 +3052,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) @@ -3453,6 +3453,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 */ @@ -3527,7 +3529,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"); @@ -4075,6 +4084,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); @@ -4217,7 +4228,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

cyliu@suse.com wrote:
From: Chunyan Liu <cyliu@suse.com>
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 | 127 ++--- src/qemu/qemu_process.c | 34 +- 9 files changed, 88 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 9176b95..1124311 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -97,7 +97,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 a89e414..20fa84e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -632,7 +632,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_conf.c qemu/qemu_conf.h \ qemu/qemu_process.c qemu/qemu_process.h \ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index aa3a2fd..977317f 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 8229cfc..26050ec 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;
@@ -203,14 +204,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 5634abf..776c16b 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); @@ -10906,12 +10889,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);
@@ -10945,18 +10928,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); @@ -10973,13 +10950,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) @@ -10999,33 +10975,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); @@ -11036,12 +10991,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) @@ -11061,17 +11016,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 032de69..c94fd16 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -29,7 +29,8 @@ #include "qemu_domain.h" #include "qemu_command.h" #include "qemu_bridge_filter.h" -#include "qemu_hostdev.h" +#include "qemu_conf.h" +
Spurious whitespace addition.
#include "domain_audit.h" #include "domain_nwfilter.h" #include "virlog.h" @@ -49,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 @@ -1007,12 +1009,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 @@ -1093,7 +1102,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); @@ -1278,26 +1288,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) @@ -1326,9 +1331,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; } @@ -1342,6 +1347,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) || @@ -1351,8 +1357,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) {
Parameter alignment seems a bit off.
virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to prepare scsi hostdev: %s:%d:%d:%d"), hostdev->source.subsys.u.scsi.adapter, @@ -1400,7 +1409,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; @@ -2362,71 +2372,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);
Parameter alignment off.
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 @@ -2467,13 +2444,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 d631a6f..0f32c6c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -38,10 +38,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" @@ -64,6 +64,7 @@ #include "viratomic.h" #include "virnuma.h" #include "virstring.h" +#include "virhostdev.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -3018,6 +3019,8 @@ qemuProcessReconnect(void *opaque) int reason; virQEMUDriverConfigPtr cfg; size_t i; + virHostdevManagerPtr hostdev_mgr; + unsigned int flags;
memcpy(&oldjob, &data->oldjob, sizeof(oldjob));
@@ -3049,14 +3052,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)
Parameter alignment off.
goto error;
if (qemuConnectCgroup(driver, obj) < 0) @@ -3453,6 +3453,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 */ @@ -3527,7 +3529,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)
Parameter alignment off.
goto cleanup;
VIR_DEBUG("Preparing chr devices"); @@ -4075,6 +4084,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); @@ -4217,7 +4228,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);
Parameter alignment off. Again, this is one of those patches that I hope other libvirt devs with more expertise in this area of code can review. Regards, Jim
def = vm->def; for (i = 0; i < def->nnets; i++) {

From: Chunyan Liu <cyliu@suse.com> 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 1124311..7186457 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 20fa84e..2509db3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -578,7 +578,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 9cb95ff..4f37619 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; 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); @@ -3947,13 +3957,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, @@ -3985,8 +3994,7 @@ lxcDomainDetachDeviceHostdevCapsLive(virDomainObjPtr vm, static int -lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceHostdevLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { virLXCDomainObjPrivatePtr priv = vm->privateData; @@ -3999,7 +4007,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); @@ -4014,8 +4022,7 @@ lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, static int -lxcDomainDetachDeviceLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int ret = -1; @@ -4030,7 +4037,7 @@ lxcDomainDetachDeviceLive(virLXCDriverPtr driver, break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = lxcDomainDetachDeviceHostdevLive(driver, vm, dev); + ret = lxcDomainDetachDeviceHostdevLive(vm, dev); break; default: @@ -4363,7 +4370,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 247e516..02509d9 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. @@ -1393,6 +1403,7 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, { virLXCDriverPtr driver = opaque; virLXCDomainObjPrivatePtr priv; + virHostdevManagerPtr hostdev_mgr; int ret = -1; virObjectLock(vm); @@ -1422,7 +1433,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

cyliu@suse.com wrote:
From: Chunyan Liu <cyliu@suse.com>
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 1124311..7186457 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 20fa84e..2509db3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -578,7 +578,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 9cb95ff..4f37619 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)
Parameter alignment.
+ 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; 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); @@ -3947,13 +3957,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, @@ -3985,8 +3994,7 @@ lxcDomainDetachDeviceHostdevCapsLive(virDomainObjPtr vm,
static int -lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceHostdevLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { virLXCDomainObjPrivatePtr priv = vm->privateData; @@ -3999,7 +4007,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); @@ -4014,8 +4022,7 @@ lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver,
static int -lxcDomainDetachDeviceLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int ret = -1; @@ -4030,7 +4037,7 @@ lxcDomainDetachDeviceLive(virLXCDriverPtr driver, break;
case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = lxcDomainDetachDeviceHostdevLive(driver, vm, dev); + ret = lxcDomainDetachDeviceHostdevLive(vm, dev); break;
default: @@ -4363,7 +4370,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 247e516..02509d9 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);
Parameter alignment.
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)
Parameter alignment.
goto cleanup;
/* Here we open all the PTYs we need on the host OS side. @@ -1393,6 +1403,7 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, { virLXCDriverPtr driver = opaque; virLXCDomainObjPrivatePtr priv; + virHostdevManagerPtr hostdev_mgr; int ret = -1;
virObjectLock(vm); @@ -1422,7 +1433,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)
Parameter alignment. This patch looks rather straight-forward and similar to the changes you made in the QEMU driver, but would be good to have another set of libvirt dev eyes review it. Regards, Jim
goto error;
if (virSecurityManagerReserveLabel(driver->securityManager,
participants (3)
-
Chunyan Liu
-
cyliu@suse.com
-
Jim Fehlig