Write separate module for hostdev passthrough common library so that it could be
used by all hypervisor drivers and maintain a global hostdev state.
Changes to v1:
* add scsi part to common library
* instead of hardcode, check for <driver name='kvm/vfio/xen'/> setting,
call
virPCIDeviceSetStubDriver to set stub driver. For libxl driver, a <driver
name='xen' /> is needed.
* rebase to current libvirt changes
Signed-off-by: Chunyan Liu <cyliu(a)suse.com>
---
po/POTFILES.in | 1 +
src/Makefile.am | 1 +
src/conf/domain_conf.c | 3 +-
src/conf/domain_conf.h | 1 +
src/libvirt.c | 5 +
src/libvirt_private.syms | 22 +-
src/util/virhostdevmanager.c | 1486 ++++++++++++++++++++++++++++++++++++++++++
src/util/virhostdevmanager.h | 109 +++
src/util/virpci.c | 17 +-
src/util/virpci.h | 7 +-
src/util/virscsi.c | 18 +-
src/util/virscsi.h | 4 +-
src/util/virusb.c | 19 +-
src/util/virusb.h | 4 +-
14 files changed, 1671 insertions(+), 26 deletions(-)
create mode 100644 src/util/virhostdevmanager.c
create mode 100644 src/util/virhostdevmanager.h
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f3ea4da..a7c21bf 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -188,6 +188,7 @@ src/util/viruri.c
src/util/virusb.c
src/util/virutil.c
src/util/virxml.c
+src/util/virhostdevmanager.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 6c626ac..0f09fa5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -130,6 +130,7 @@ UTIL_SOURCES = \
util/virutil.c util/virutil.h \
util/viruuid.c util/viruuid.h \
util/virxml.c util/virxml.h \
+ util/virhostdevmanager.c util/virhostdevmanager.h \
$(NULL)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index f0ca9d5..5065d30 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -594,7 +594,8 @@ VIR_ENUM_IMPL(virDomainHostdevSubsysPciBackend,
VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST,
"default",
"kvm",
- "vfio")
+ "vfio",
+ "xen")
VIR_ENUM_IMPL(virDomainHostdevCaps, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST,
"storage",
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index c176a4c..d24ab79 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -394,6 +394,7 @@ typedef enum {
VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, /* currently kvm, could change */
VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM, /* force legacy kvm style */
VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO, /* force vfio */
+ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN, /* force xen */
VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST
} virDomainHostdevSubsysPciBackendType;
diff --git a/src/libvirt.c b/src/libvirt.c
index 30f9646..1a3032a 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -65,6 +65,7 @@
#include "virthread.h"
#include "virstring.h"
#include "virutil.h"
+#include "virhostdevmanager.h"
#ifdef WITH_TEST
# include "test/test_driver.h"
@@ -827,6 +828,7 @@ int virStateInitialize(bool privileged,
if (virInitialize() < 0)
return -1;
+ virHostdevManagerInit();
for (i = 0 ; i < virStateDriverTabCount ; i++) {
if (virStateDriverTab[i]->stateInitialize) {
VIR_DEBUG("Running global init for %s state driver",
@@ -858,6 +860,9 @@ int virStateCleanup(void) {
virStateDriverTab[i]->stateCleanup() < 0)
ret = -1;
}
+
+ virHostdevManagerCleanup();
+
return ret;
}
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index cc734da..9967fa0 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1345,6 +1345,27 @@ virHookInitialize;
virHookPresent;
+#util/virhostdevmanager.h
+virFreeHostdevNameList;
+virGetActivePciHostdevs;
+virGetActiveScsiHostdevs;
+virGetActiveUsbHostdevs;
+virGetDomainActivePciHostdevs;
+virGetDomainActiveScsiHostdevs;
+virGetDomainActiveUsbHostdevs;
+virHostdevManagerCleanup;
+virHostdevManagerGetDefault;
+virHostdevManagerInit;
+virHostdevManagerPrepareHostdevs;
+virHostdevManagerPreparePciHostdevs;
+virHostdevManagerPrepareScsiHostdevs;
+virHostdevManagerPrepareUsbHostdevs;
+virHostdevManagerReAttachHostdevs;
+virHostdevManagerReAttachPciHostdevs;
+virHostdevManagerReAttachScsiHostdevs;
+virHostdevManagerReAttachUsbHostdevs;
+
+
# util/viridentity.h
virIdentityGetAttr;
virIdentityIsEqual;
@@ -1992,7 +2013,6 @@ virXPathULong;
virXPathULongHex;
virXPathULongLong;
-
# Let emacs know we want case-insensitive sorting
# Local Variables:
# sort-fold-case: t
diff --git a/src/util/virhostdevmanager.c b/src/util/virhostdevmanager.c
new file mode 100644
index 0000000..310c943
--- /dev/null
+++ b/src/util/virhostdevmanager.c
@@ -0,0 +1,1486 @@
+/*
+ * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ *
+ * 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(a)suse.com>
+ */
+
+#include <config.h>
+
+#include "virhostdevmanager.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"
+
+/* For virReportOOMError() and virReportSystemError() */
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+struct _virHostdevManager{
+ char *stateDir;
+
+ virPCIDeviceListPtr activePciHostdevs;
+ virPCIDeviceListPtr inactivePciHostdevs;
+ virUSBDeviceListPtr activeUsbHostdevs;
+ virSCSIDeviceListPtr activeScsiHostdevs;
+};
+
+static virHostdevManagerPtr hostdevMgr;
+
+int virHostdevManagerInit(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) == -1)
+ 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;
+}
+
+void virHostdevManagerCleanup(void)
+{
+ if(!hostdevMgr)
+ return;
+
+ virObjectUnref(hostdevMgr->activePciHostdevs);
+ virObjectUnref(hostdevMgr->inactivePciHostdevs);
+ virObjectUnref(hostdevMgr->activeUsbHostdevs);
+ virObjectUnref(hostdevMgr->activeScsiHostdevs);
+
+ VIR_FREE(hostdevMgr->stateDir);
+}
+
+virHostdevManagerPtr virHostdevManagerGetDefault(void)
+{
+ return hostdevMgr;
+}
+
+static int
+virDomainHostdevPciSysfsPath(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
+virDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev)
+{
+ char *sysfs_path = NULL;
+ int ret = -1;
+
+ if (virDomainHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)
+ return ret;
+
+ ret = virPCIIsVirtualFunction(sysfs_path);
+
+ VIR_FREE(sysfs_path);
+
+ return ret;
+}
+
+static int
+virDomainHostdevNetDevice(virDomainHostdevDefPtr hostdev, char **linkdev,
+ int *vf)
+{
+ int ret = -1;
+ char *sysfs_path = NULL;
+
+ if (virDomainHostdevPciSysfsPath(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
+virDomainHostdevNetConfigVirtPortProfile(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
+virDomainHostdevNetConfigReplace(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 = virDomainHostdevIsVirtualFunction(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 (virDomainHostdevNetDevice(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 = virDomainHostdevNetConfigVirtPortProfile(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
+virDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,
+ char *stateDir)
+{
+ char *linkdev = NULL;
+ virNetDevVPortProfilePtr virtPort;
+ int ret = -1;
+ int vf = -1;
+ int port_profile_associate = 0;
+ int isvf;
+
+ isvf = virDomainHostdevIsVirtualFunction(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 (virDomainHostdevNetDevice(hostdev, &linkdev, &vf) < 0)
+ return ret;
+
+ virtPort = virDomainNetGetActualVirtPortProfile(
+
hostdev->parent.data.net);
+ if (virtPort)
+ ret = virDomainHostdevNetConfigVirtPortProfile(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
+virHostdevManagerGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ virPCIDeviceListPtr list;
+ int 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:
+ virPCIDeviceSetStubDriver(dev, "vfio-pci");
+ break;
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN :
+ virPCIDeviceSetStubDriver(dev, "pciback");
+ break;
+ default:
+ virPCIDeviceSetStubDriver(dev, "pci-stub");
+ break;
+ }
+ }
+
+ return list;
+}
+
+int
+virHostdevManagerPreparePciHostdevs(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;
+ int i;
+ int ret = -1;
+
+ virObjectLock(mgr->activePciHostdevs);
+ virObjectLock(mgr->inactivePciHostdevs);
+
+ if (!(pcidevs = virHostdevManagerGetPciHostDeviceList(hostdevs, nhostdevs)))
+ goto cleanup;
+
+ /* We have to use 9 loops here. *All* devices must
+ * be detached before we reset any of them, because
+ * in some cases you have to reset the whole PCI,
+ * which impacts all devices on it. Also, all devices
+ * must be reset before being marked as active.
+ */
+
+ /* Loop 1: validate that non-managed device isn't in use, eg
+ * by checking that device is either un-bound, or bound
+ * to stub driver
+ */
+
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
+ virPCIDevicePtr other;
+ bool strict_acs_check = (flags & VIR_STRICT_ACS_CHECK)?true:false;
+
+ if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("PCI device %s is not assignable"),
+ virPCIDeviceGetName(dev));
+ goto cleanup;
+ }
+ /* The device is in use by other active domain if
+ * the dev is in list activePciHostdevs.
+ */
+ if ((other = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) {
+ 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, 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 (virDomainHostdevNetConfigReplace(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) {
+ virDomainHostdevNetConfigRestore(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
+virFindHostdevUSBDevice(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
+virHostdevManagerMarkUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virUSBDeviceListPtr list)
+{
+ int 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 virHostdevManagerPrepareUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ bool coldBoot)
+{
+ int i, 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.
+ */
+ 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 (virFindHostdevUSBDevice(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 (virHostdevManagerMarkUsbHostdevs(mgr, 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
+virHostdevManagerPrepareScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ int i, j, 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 virHostdevManagerPrepareHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags)
+{
+ if (!def->nhostdevs)
+ return 0;
+
+ if (flags & VIR_SP_PCI_HOSTDEV) {
+ if(virHostdevManagerPreparePciHostdevs(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(virHostdevManagerPrepareUsbHostdevs(mgr, driver, def, coldBoot) < 0)
+ return -1;
+ }
+
+ if (flags & VIR_SP_SCSI_HOSTDEV) {
+ if (virHostdevManagerPrepareScsiHostdevs(mgr, driver, def->name,
+ def->hostdevs, def->nhostdevs) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Pre-condition: mgr->inactivePciHostdevs & mgr->activePciHostdevs
+ * are locked
+ */
+static void
+virReattachPciDevice(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") &&
+ 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
+virGetActivePciHostDeviceList(virHostdevManagerPtr mgr,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ virPCIDeviceListPtr list;
+ int 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
+virHostdevManagerReAttachPciHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ virPCIDeviceListPtr pcidevs;
+ int i;
+
+ virObjectLock(mgr->activePciHostdevs);
+ virObjectLock(mgr->inactivePciHostdevs);
+
+ if (!(pcidevs = virGetActivePciHostDeviceList(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;
+
+ /* Never delete the dev from list driver->activePciHostdevs
+ * if it's used by other domain.
+ */
+ activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev);
+ if (activeDev) {
+ char *usedby_drvname = NULL;
+ char *usedby_domname = NULL;
+ virPCIDeviceGetUsedBy(activeDev, &usedby_drvname, &usedby_domname);
+ if (STRNEQ_NULLABLE(drv_name, usedby_drvname) &&
+ STRNEQ_NULLABLE(dom_name, usedby_domname)) {
+ virPCIDeviceListSteal(pcidevs, dev);
+ continue;
+ }
+ VIR_FREE(usedby_drvname);
+ VIR_FREE(usedby_domname);
+ }
+
+ /* virObjectUnref() will take care of freeing the dev. */
+ virPCIDeviceListSteal(mgr->activePciHostdevs, dev);
+ }
+
+ /*
+ * For SRIOV net host devices, unset mac and port profile before
+ * reset and reattach device
+ */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+ if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET &&
+
hostdev->parent.data.net) {
+ virDomainHostdevNetConfigRestore(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);
+ virReattachPciDevice(mgr, dev, drv_name);
+ }
+
+ virObjectUnref(pcidevs);
+cleanup:
+ virObjectUnlock(mgr->activePciHostdevs);
+ virObjectUnlock(mgr->inactivePciHostdevs);
+}
+
+void
+virHostdevManagerReAttachUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ int i;
+
+ virObjectLock(mgr->activeUsbHostdevs);
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virUSBDevicePtr usb, tmp;
+
+ 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;
+ }
+
+ char *usedby_drvname = NULL;
+ char *usedby_domname = NULL;
+ virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname);
+ if (STREQ_NULLABLE(drv_name, usedby_drvname) &&
+ STREQ_NULLABLE(dom_name, usedby_domname)) {
+ VIR_DEBUG("Removing %03d.%03d dom=%s:%s",
+ hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device,
+ drv_name, dom_name);
+ virUSBDeviceListDel(mgr->activeUsbHostdevs, tmp);
+ }
+ VIR_FREE(usedby_drvname);
+ VIR_FREE(usedby_domname);
+
+ }
+ virObjectUnlock(mgr->activeUsbHostdevs);
+}
+
+void
+virHostdevManagerReAttachScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ int i;
+
+ virObjectLock(mgr->activeScsiHostdevs);
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virSCSIDevicePtr scsi, tmp;
+ char *used_by_drvname;
+ char *used_by_domname;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
+ continue;
+
+ if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit,
+ hostdev->readonly))) {
+ VIR_WARN("Unable to reattach SCSI device %s:%d:%d:%d on driver %s domain
%s",
+ hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit,
+ drv_name, dom_name);
+ continue;
+ }
+
+ tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi);
+ virSCSIDeviceFree(scsi);
+
+ if (!tmp) {
+ VIR_WARN("Unable to find device %s:%d:%d:%d "
+ "in list of active SCSI devices",
+ hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit);
+ continue;
+ }
+
+ virSCSIDeviceGetUsedBy(tmp, &used_by_drvname, &used_by_domname);
+ if (STREQ_NULLABLE(used_by_drvname, drv_name) &&
+ STREQ_NULLABLE(used_by_domname, dom_name)) {
+ VIR_DEBUG("Removing %s:%d:%d:%d driver:%s dom:%s from
activeScsiHostdevs",
+ hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit,
+ drv_name, dom_name);
+
+ virSCSIDeviceListDel(mgr->activeScsiHostdevs, tmp);
+ }
+ }
+ virObjectUnlock(mgr->activeScsiHostdevs);
+}
+
+void virHostdevManagerReAttachHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags)
+{
+ if (!def->nhostdevs)
+ return;
+
+ if (flags & VIR_SP_PCI_HOSTDEV) {
+ virHostdevManagerReAttachPciHostdevs(mgr, driver, def->name, def->hostdevs,
def->nhostdevs);
+ }
+
+ if (flags & VIR_SP_USB_HOSTDEV) {
+ virHostdevManagerReAttachUsbHostdevs(mgr, driver, def->name, def->hostdevs,
def->nhostdevs);
+ }
+
+ if (flags & VIR_SP_SCSI_HOSTDEV) {
+ virHostdevManagerReAttachScsiHostdevs(mgr, driver, def->name,
def->hostdevs, def->nhostdevs);
+ }
+}
+
+
+/*After using any of the above four *Get* APIs, virFreeHostdevNameList should
+ *be called to free memory.
+ */
+void virFreeHostdevNameList(virHostdevNameListPtr list){
+ int i;
+
+ if (list && list->names) {
+ for (i = 0; i < list->nnames; i++) {
+ if (list->names[i])
+ VIR_FREE(list->names[i]);
+ }
+ VIR_FREE(list->names);
+ }
+ VIR_FREE(list);
+}
+
+virHostdevNameListPtr
+virGetActivePciHostdevs(virHostdevManagerPtr mgr)
+{
+ int i,count;
+ virHostdevNameListPtr list = NULL;
+ virObjectLock(mgr->activePciHostdevs);
+
+ count = virPCIDeviceListCount(mgr->activePciHostdevs);
+ if (count > 0) {
+ if (VIR_ALLOC_N(list, 1) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC_N(list->names,count) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ goto cleanup;
+ }
+ list->nnames = 0;
+
+ for (i = 0; i < count; i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(mgr->activePciHostdevs, i);
+ list->names[list->nnames++] = strdup(virPCIDeviceGetName(dev));
+ }
+ }
+
+cleanup:
+ virFreeHostdevNameList(list);
+ virObjectUnlock(mgr->activePciHostdevs);
+ return list;
+}
+
+virHostdevNameListPtr
+virGetActiveUsbHostdevs(virHostdevManagerPtr mgr)
+{
+ int i,count;
+ virHostdevNameListPtr list = NULL;
+ virObjectLock(mgr->activeUsbHostdevs);
+
+ count = virUSBDeviceListCount(mgr->activeUsbHostdevs);
+ if (count > 0) {
+ if (VIR_ALLOC_N(list, 1) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC_N(list->names,count) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ goto cleanup;
+ }
+ list->nnames = 0;
+
+ for (i = 0; i < count; i++) {
+ virUSBDevicePtr usb = virUSBDeviceListGet(mgr->activeUsbHostdevs, i);
+ list->names[list->nnames++] = strdup(virUSBDeviceGetName(usb));
+ }
+ }
+
+cleanup:
+ virFreeHostdevNameList(list);
+ virObjectUnlock(mgr->activeUsbHostdevs);
+ return list;
+}
+
+virHostdevNameListPtr
+virGetActiveScsiHostdevs(virHostdevManagerPtr mgr)
+{
+ int i,count;
+ virHostdevNameListPtr list = NULL;
+ virObjectLock(mgr->activeScsiHostdevs);
+
+ count = virSCSIDeviceListCount(mgr->activeScsiHostdevs);
+ if (count > 0) {
+ if (VIR_ALLOC_N(list, 1) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC_N(list->names,count) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ goto cleanup;
+ }
+ list->nnames = 0;
+
+ for (i = 0; i < count; i++) {
+ virSCSIDevicePtr dev = virSCSIDeviceListGet(mgr->activeScsiHostdevs, i);
+ list->names[list->nnames++] = strdup(virSCSIDeviceGetName(dev));
+ }
+ }
+
+cleanup:
+ virFreeHostdevNameList(list);
+ virObjectUnlock(mgr->activeScsiHostdevs);
+ return list;
+}
+
+virHostdevNameListPtr
+virGetDomainActivePciHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name)
+{
+ int i;
+ size_t count;
+ virHostdevNameListPtr list = NULL;
+ virObjectLock(mgr->activePciHostdevs);
+
+ count = virPCIDeviceListCount(mgr->activePciHostdevs);
+ if (count > 0) {
+ if (VIR_ALLOC_N(list, 1) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC_N(list->names,count) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ 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;
+ virPCIDeviceGetUsedBy(dev, &usedby_drvname, &usedby_domname);
+ if (STRNEQ(drv_name, usedby_drvname) &&
+ STRNEQ(dom_name, usedby_domname)) {
+ list->names[list->nnames++] = strdup(virPCIDeviceGetName(dev));
+ }
+ VIR_FREE(usedby_drvname);
+ VIR_FREE(usedby_domname);
+ }
+
+ VIR_SHRINK_N(list->names, count, count - list->nnames);
+ }
+
+cleanup:
+ virFreeHostdevNameList(list);
+ virObjectUnlock(mgr->activePciHostdevs);
+ return list;
+}
+
+virHostdevNameListPtr
+virGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name)
+{
+ int i;
+ size_t count;
+ virHostdevNameListPtr list = NULL;
+ virObjectLock(mgr->activeUsbHostdevs);
+
+ count = virUSBDeviceListCount(mgr->activeUsbHostdevs);
+ if (count > 0) {
+ if (VIR_ALLOC_N(list, 1) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC_N(list->names,count) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ 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;
+ virUSBDeviceGetUsedBy(usb, &usedby_drvname, &usedby_domname);
+ if (STRNEQ(drv_name, usedby_drvname) &&
+ STRNEQ(dom_name, usedby_domname)) {
+ list->names[list->nnames++] = strdup(virUSBDeviceGetName(usb));
+ }
+ VIR_FREE(usedby_drvname);
+ VIR_FREE(usedby_domname);
+ }
+
+ VIR_SHRINK_N(list->names, count, count - list->nnames);
+ }
+
+cleanup:
+ virFreeHostdevNameList(list);
+ virObjectUnlock(mgr->activeUsbHostdevs);
+ return list;
+}
+
+virHostdevNameListPtr
+virGetDomainActiveScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name)
+{
+ int i;
+ size_t count;
+ virHostdevNameListPtr list = NULL;
+ virObjectLock(mgr->activeScsiHostdevs);
+
+ count = virSCSIDeviceListCount(mgr->activeScsiHostdevs);
+ if (count > 0) {
+ if (VIR_ALLOC_N(list, 1) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC_N(list->names,count) < 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Fail to alloc memory"));
+ 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;
+ virSCSIDeviceGetUsedBy(dev, &usedby_drvname, &usedby_domname);
+ if (STRNEQ(drv_name, usedby_drvname) &&
+ STRNEQ(dom_name, usedby_domname)) {
+ list->names[list->nnames++] = strdup(virSCSIDeviceGetName(dev));
+ }
+ VIR_FREE(usedby_drvname);
+ VIR_FREE(usedby_domname);
+ }
+
+ VIR_SHRINK_N(list->names, count, count - list->nnames);
+ }
+
+cleanup:
+ virFreeHostdevNameList(list);
+ virObjectUnlock(mgr->activeScsiHostdevs);
+ return list;
+}
diff --git a/src/util/virhostdevmanager.h b/src/util/virhostdevmanager.h
new file mode 100644
index 0000000..9f26b9f
--- /dev/null
+++ b/src/util/virhostdevmanager.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ *
+ * 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(a)suse.com>
+ */
+
+#ifndef __VIR_HOSTDEV_MANAGER_H__
+# define __VIR_HOSTDEV_MANAGER_H__
+
+# include "internal.h"
+# include "configmake.h"
+# include "conf/domain_conf.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;
+
+int virHostdevManagerInit(void);
+void virHostdevManagerCleanup(void);
+virHostdevManagerPtr virHostdevManagerGetDefault(void);
+
+int virHostdevManagerPrepareHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags);
+void virHostdevManagerReAttachHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags);
+int virHostdevManagerPreparePciHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ const unsigned char *uuid,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ unsigned int flags);
+int virHostdevManagerPrepareUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ bool coldBoot);
+int virHostdevManagerPrepareScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
+void virHostdevManagerReAttachPciHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
+void virHostdevManagerReAttachUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
+void virHostdevManagerReAttachScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
+
+virHostdevNameListPtr virGetActivePciHostdevs(virHostdevManagerPtr mgr);
+virHostdevNameListPtr virGetActiveUsbHostdevs(virHostdevManagerPtr mgr);
+virHostdevNameListPtr virGetActiveScsiHostdevs(virHostdevManagerPtr mgr);
+virHostdevNameListPtr virGetDomainActivePciHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name);
+virHostdevNameListPtr virGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name);
+virHostdevNameListPtr virGetDomainActiveScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name);
+/*After using any of the above four *Get* APIs, virFreeHostdevNameList should
+ *be called to free memory.
+ */
+void virFreeHostdevNameList(virHostdevNameListPtr list);
+
+#endif /* __VIR_HOSTDEV_MANAGER_H__ */
diff --git a/src/util/virpci.c b/src/util/virpci.c
index 5865613..7fe0921 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;
@@ -1553,15 +1556,17 @@ 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)
+void
+virPCIDeviceGetUsedBy(virPCIDevicePtr dev, char **drv_name, char **dom_name)
{
- return dev->used_by;
+ *drv_name = strdup(dev->used_by_drvname);
+ *dom_name = strdup(dev->used_by_domname);
}
void virPCIDeviceReattachInit(virPCIDevicePtr pci)
diff --git a/src/util/virpci.h b/src/util/virpci.h
index 7bcadb4..294cb0e 100644
--- a/src/util/virpci.h
+++ b/src/util/virpci.h
@@ -66,8 +66,11 @@ void 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);
+void 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 d6685fa..3993ac4 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;
};
@@ -234,15 +237,18 @@ 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)
+void
+virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev, char **drvname, char **domname)
{
- return dev->used_by;
+ *drvname = strdup(dev->used_by_drvname);
+ *domname = strdup(dev->used_by_domname);
}
const char *
diff --git a/src/util/virscsi.h b/src/util/virscsi.h
index 8268cdf..4ae225d 100644
--- a/src/util/virscsi.h
+++ b/src/util/virscsi.h
@@ -45,8 +45,8 @@ 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);
+void 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 27ba9c7..043206e 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 {
@@ -383,16 +386,20 @@ 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)
+void virUSBDeviceGetUsedBy(virUSBDevicePtr dev,
+ char **drv_name,
+ char **dom_name)
{
- return dev->used_by;
+ *drv_name = strdup(dev->used_by_drvname);
+ *dom_name = strdup(dev->used_by_domname);
}
const char *virUSBDeviceGetName(virUSBDevicePtr dev)
diff --git a/src/util/virusb.h b/src/util/virusb.h
index aa59d12..16e48e5 100644
--- a/src/util/virusb.h
+++ b/src/util/virusb.h
@@ -60,8 +60,8 @@ 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);
+void virUSBDeviceGetUsedBy(virUSBDevicePtr dev, char **drv_name, char **dom_name);
const char *virUSBDeviceGetName(virUSBDevicePtr dev);
unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev);
--
1.6.0.2