<div dir="ltr"><br><div class="gmail_extra"><br><br><div class="gmail_quote">2013/5/27 Marek Marczykowski <span dir="ltr"><<a href="mailto:marmarek@invisiblethingslab.com" target="_blank">marmarek@invisiblethingslab.com</a>></span><br>
<blockquote class="gmail_quote" style="margin:0pt 0pt 0pt 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="im">On 16.05.2013 08:07, Chunyan Liu wrote:<br>
> Write separate module for hostdev passthrough so that it could be used by all<br>
> hypervisor drivers and maintain a global hostdev state.<br>
><br>
> Signed-off-by: Chunyan Liu <<a href="mailto:cyliu@suse.com">cyliu@suse.com</a>><br>
<br>
</div>Some comments inline. Besides that seems to be working (with your next patch<br>
for libxl) :)<br>
<div><div class="h5"><br></div></div></blockquote><div>Thanks for your comments! Will update. <br>(Didn't change qemu and lxc drivers yet, I think it's better to change existing drivers after there is a consensus on the common library.)<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0pt 0pt 0pt 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="h5">
> ---<br>
> po/POTFILES.in | 1 +<br>
> src/Makefile.am | 1 +<br>
> src/libvirt.c | 5 +<br>
> src/libvirt_private.syms | 15 +<br>
> src/util/virhostdevmanager.c | 1218 ++++++++++++++++++++++++++++++++++++++++++<br>
> src/util/virhostdevmanager.h | 91 ++++<br>
> src/util/virpci.c | 17 +-<br>
> src/util/virpci.h | 7 +-<br>
> src/util/virusb.c | 19 +-<br>
> src/util/virusb.h | 4 +-<br>
> 10 files changed, 1362 insertions(+), 16 deletions(-)<br>
> create mode 100644 src/util/virhostdevmanager.c<br>
> create mode 100644 src/util/virhostdevmanager.h<br>
><br>
> diff --git a/po/POTFILES.in b/po/POTFILES.in<br>
> index f3ea4da..a7c21bf 100644<br>
> --- a/po/POTFILES.in<br>
> +++ b/po/POTFILES.in<br>
> @@ -188,6 +188,7 @@ src/util/viruri.c<br>
> src/util/virusb.c<br>
> src/util/virutil.c<br>
> src/util/virxml.c<br>
> +src/util/virhostdevmanager.c<br>
> src/vbox/vbox_MSCOMGlue.c<br>
> src/vbox/vbox_XPCOMCGlue.c<br>
> src/vbox/vbox_driver.c<br>
> diff --git a/src/Makefile.am b/src/Makefile.am<br>
> index 4312c3c..a197c2b 100644<br>
> --- a/src/Makefile.am<br>
> +++ b/src/Makefile.am<br>
> @@ -130,6 +130,7 @@ UTIL_SOURCES = \<br>
> util/virutil.c util/virutil.h \<br>
> util/viruuid.c util/viruuid.h \<br>
> util/virxml.c util/virxml.h \<br>
> + util/virhostdevmanager.c util/virhostdevmanager.h \<br>
> $(NULL)<br>
><br>
><br>
> diff --git a/src/libvirt.c b/src/libvirt.c<br>
> index 2b3515e..d9af5a6 100644<br>
> --- a/src/libvirt.c<br>
> +++ b/src/libvirt.c<br>
> @@ -65,6 +65,7 @@<br>
> #include "virthread.h"<br>
> #include "virstring.h"<br>
> #include "virutil.h"<br>
> +#include "virhostdevmanager.h"<br>
><br>
> #ifdef WITH_TEST<br>
> # include "test/test_driver.h"<br>
> @@ -827,6 +828,7 @@ int virStateInitialize(bool privileged,<br>
> if (virInitialize() < 0)<br>
> return -1;<br>
><br>
> + virHostdevManagerInit();<br>
> for (i = 0 ; i < virStateDriverTabCount ; i++) {<br>
> if (virStateDriverTab[i]->stateInitialize) {<br>
> VIR_DEBUG("Running global init for %s state driver",<br>
> @@ -858,6 +860,9 @@ int virStateCleanup(void) {<br>
> virStateDriverTab[i]->stateCleanup() < 0)<br>
> ret = -1;<br>
> }<br>
> +<br>
> + virHostdevManagerCleanup();<br>
> +<br>
> return ret;<br>
> }<br>
><br>
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms<br>
> index cdd0b41..824de4e 100644<br>
> --- a/src/libvirt_private.syms<br>
> +++ b/src/libvirt_private.syms<br>
> @@ -1991,6 +1991,21 @@ virXPathULong;<br>
> virXPathULongHex;<br>
> virXPathULongLong;<br>
><br>
> +#util/virhostdevmanager.h<br>
> +virHostdevManagerGetDefault;<br>
> +virHostdevManagerInit;<br>
> +virHostdevManagerCleanup;<br>
> +virHostdevManagerPrepareHostdevs;<br>
> +virHostdevManagerReAttachHostdevs;<br>
> +virHostdevManagerPreparePciHostdevs;<br>
> +virHostdevManagerPrepareUsbHostdevs;<br>
> +virHostdevManagerReAttachPciHostdevs;<br>
> +virHostdevManagerReAttachUsbHostdevs;<br>
> +virGetActivePciHostdevs;<br>
> +virGetActiveUsbHostdevs;<br>
> +virGetDomainActivePciHostdevs;<br>
> +virGetDomainActiveUsbHostdevs;<br>
> +virFreeHostdevNameList;<br>
<br>
</div></div>"make check" reports wrong symbol order here.<br>
<div><div class="h5"><br>
><br>
> # Let emacs know we want case-insensitive sorting<br>
> # Local Variables:<br>
> diff --git a/src/util/virhostdevmanager.c b/src/util/virhostdevmanager.c<br>
> new file mode 100644<br>
> index 0000000..9034212<br>
> --- /dev/null<br>
> +++ b/src/util/virhostdevmanager.c<br>
> @@ -0,0 +1,1218 @@<br>
> +/*<br>
> + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.<br>
> + *<br>
> + * This library is free software; you can redistribute it and/or<br>
> + * modify it under the terms of the GNU Lesser General Public<br>
> + * License as published by the Free Software Foundation; either<br>
> + * version 2.1 of the License, or (at your option) any later version.<br>
> + *<br>
> + * This library is distributed in the hope that it will be useful,<br>
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>
> + * Lesser General Public License for more details.<br>
> + *<br>
> + * You should have received a copy of the GNU Lesser General Public<br>
> + * License along with this library. If not, see<br>
> + * <<a href="http://www.gnu.org/licenses/" target="_blank">http://www.gnu.org/licenses/</a>>.<br>
> + *<br>
> + * Author: Chunyan Liu <<a href="mailto:cyliu@suse.com">cyliu@suse.com</a>><br>
> + */<br>
> +#include <config.h><br>
> +<br>
> +#include "virhostdevmanager.h"<br>
> +<br>
> +#include <sys/types.h><br>
> +#include <sys/stat.h><br>
> +#include <unistd.h><br>
> +#include <stdlib.h><br>
> +#include <stdio.h><br>
> +<br>
> +#include "viralloc.h"<br>
> +#include "virstring.h"<br>
> +#include "virfile.h"<br>
> +#include "virerror.h"<br>
> +#include "virlog.h"<br>
> +#include "virpci.h"<br>
> +#include "virusb.h"<br>
> +#include "virnetdev.h"<br>
> +<br>
> +/* For virReportOOMError() and virReportSystemError() */<br>
> +#define VIR_FROM_THIS VIR_FROM_NONE<br>
> +<br>
> +struct _virHostdevManager{<br>
> + char *stateDir;<br>
> +<br>
> + virPCIDeviceListPtr activePciHostdevs;<br>
> + virPCIDeviceListPtr inactivePciHostdevs;<br>
> + virUSBDeviceListPtr activeUsbHostdevs;<br>
> +};<br>
> +<br>
> +static virHostdevManagerPtr hostdevMgr;<br>
> +<br>
> +int virHostdevManagerInit(void)<br>
> +{<br>
> + char ebuf[1024];<br>
> + if (VIR_ALLOC(hostdevMgr) < 0)<br>
> + return -1;<br>
> +<br>
> + if ((hostdevMgr->activePciHostdevs = virPCIDeviceListNew()) == NULL)<br>
> + return -1;<br>
<br>
</div></div>goto out_of_memory ?<br>
<div class="im"><br>
> +<br>
> + if ((hostdevMgr->activeUsbHostdevs = virUSBDeviceListNew()) == NULL)<br>
> + return -1;<br>
<br>
</div>goto out_of_memory ?<br>
<div class="im"><br>
> +<br>
> + if ((hostdevMgr->inactivePciHostdevs = virPCIDeviceListNew()) == NULL)<br>
> + return -1;<br>
<br>
</div>goto out_of_memory ?<br>
<div><div class="h5"><br>
> +<br>
> + if (virAsprintf(&hostdevMgr->stateDir,<br>
> + "%s",HOSTDEV_STATE_DIR) == -1)<br>
> + goto out_of_memory;<br>
> +<br>
> + if (virFileMakePath(hostdevMgr->stateDir) < 0) {<br>
> + VIR_ERROR(_("Failed to create state dir '%s': %s"),<br>
> + hostdevMgr->stateDir, virStrerror(errno, ebuf, sizeof(ebuf)));<br>
> + goto error;<br>
> + }<br>
> +<br>
> + return 0;<br>
> +<br>
> +out_of_memory:<br>
> + virReportOOMError();<br>
> +error:<br>
> + return -1;<br>
> +}<br>
> +<br>
> +void virHostdevManagerCleanup(void)<br>
> +{<br>
> + if(!hostdevMgr)<br>
> + return;<br>
> +<br>
> + virObjectUnref(hostdevMgr->activePciHostdevs);<br>
> + virObjectUnref(hostdevMgr->inactivePciHostdevs);<br>
> + virObjectUnref(hostdevMgr->activeUsbHostdevs);<br>
> +<br>
> + VIR_FREE(hostdevMgr->stateDir);<br>
> +}<br>
> +<br>
> +virHostdevManagerPtr virHostdevManagerGetDefault(void)<br>
> +{<br>
> + return hostdevMgr;<br>
> +}<br>
> +<br>
> +static int<br>
> +virDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path)<br>
> +{<br>
> + virPCIDeviceAddress config_address;<br>
> +<br>
> + config_address.domain = hostdev->source.subsys.u.pci.addr.domain;<br>
> + config_address.bus = hostdev->source.subsys.u.pci.addr.bus;<br>
> + config_address.slot = hostdev->source.subsys.u.pci.addr.slot;<br>
> + config_address.function = hostdev->source.subsys.u.pci.addr.function;<br>
> +<br>
> + return virPCIDeviceAddressGetSysfsFile(&config_address, sysfs_path);<br>
> +}<br>
> +<br>
> +static int<br>
> +virDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev)<br>
> +{<br>
> + char *sysfs_path = NULL;<br>
> + int ret = -1;<br>
> +<br>
> + if (virDomainHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)<br>
> + return ret;<br>
> +<br>
> + ret = virPCIIsVirtualFunction(sysfs_path);<br>
> +<br>
> + VIR_FREE(sysfs_path);<br>
> +<br>
> + return ret;<br>
> +}<br>
> +<br>
> +static int<br>
> +virDomainHostdevNetDevice(virDomainHostdevDefPtr hostdev, char **linkdev,<br>
> + int *vf)<br>
> +{<br>
> + int ret = -1;<br>
> + char *sysfs_path = NULL;<br>
> +<br>
> + if (virDomainHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)<br>
> + return ret;<br>
> +<br>
> + if (virPCIIsVirtualFunction(sysfs_path) == 1) {<br>
> + if (virPCIGetVirtualFunctionInfo(sysfs_path, linkdev,<br>
> + vf) < 0)<br>
> + goto cleanup;<br>
> + } else {<br>
> + if (virPCIGetNetName(sysfs_path, linkdev) < 0)<br>
> + goto cleanup;<br>
> + *vf = -1;<br>
> + }<br>
> +<br>
> + ret = 0;<br>
> +<br>
> +cleanup:<br>
> + VIR_FREE(sysfs_path);<br>
> +<br>
> + return ret;<br>
> +}<br>
> +<br>
> +static int<br>
> +virDomainHostdevNetConfigVirtPortProfile(const char *linkdev, int vf,<br>
> + virNetDevVPortProfilePtr virtPort,<br>
> + const virMacAddrPtr macaddr,<br>
> + const unsigned char *uuid,<br>
> + int associate)<br>
> +{<br>
> + int ret = -1;<br>
> +<br>
> + if (!virtPort)<br>
> + return ret;<br>
> +<br>
> + switch (virtPort->virtPortType) {<br>
> + case VIR_NETDEV_VPORT_PROFILE_NONE:<br>
> + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:<br>
> + case VIR_NETDEV_VPORT_PROFILE_8021QBG:<br>
> + case VIR_NETDEV_VPORT_PROFILE_LAST:<br>
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,<br>
> + _("virtualport type %s is "<br>
> + "currently not supported on interfaces of type "<br>
> + "hostdev"),<br>
> + virNetDevVPortTypeToString(virtPort->virtPortType));<br>
> + break;<br>
> +<br>
> + case VIR_NETDEV_VPORT_PROFILE_8021QBH:<br>
> + if (associate)<br>
> + ret = virNetDevVPortProfileAssociate(NULL, virtPort, macaddr,<br>
> + linkdev, vf, uuid,<br>
> + VIR_NETDEV_VPORT_PROFILE_OP_CREATE, false);<br>
> + else<br>
> + ret = virNetDevVPortProfileDisassociate(NULL, virtPort,<br>
> + macaddr, linkdev, vf,<br>
> + VIR_NETDEV_VPORT_PROFILE_OP_DESTROY);<br>
> + break;<br>
> + }<br>
> +<br>
> + return ret;<br>
> +}<br>
> +<br>
> +static int<br>
> +virDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev,<br>
> + const unsigned char *uuid,<br>
> + char *stateDir)<br>
> +{<br>
> + char *linkdev = NULL;<br>
> + virNetDevVlanPtr vlan;<br>
> + virNetDevVPortProfilePtr virtPort;<br>
> + int ret = -1;<br>
> + int vf = -1;<br>
> + int vlanid = -1;<br>
> + int port_profile_associate = 1;<br>
> + int isvf;<br>
> +<br>
> + isvf = virDomainHostdevIsVirtualFunction(hostdev);<br>
> + if (isvf <= 0) {<br>
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",<br>
> + _("Interface type hostdev is currently supported on"<br>
> + " SR-IOV Virtual Functions only"));<br>
> + return ret;<br>
> + }<br>
> +<br>
> + if (virDomainHostdevNetDevice(hostdev, &linkdev, &vf) < 0)<br>
> + return ret;<br>
> +<br>
> + vlan = virDomainNetGetActualVlan(hostdev-><a href="http://parent.data.net" target="_blank">parent.data.net</a>);<br>
> + virtPort = virDomainNetGetActualVirtPortProfile(<br>
> + hostdev-><a href="http://parent.data.net" target="_blank">parent.data.net</a>);<br>
> + if (virtPort) {<br>
> + if (vlan) {<br>
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,<br>
> + _("direct setting of the vlan tag is not allowed "<br>
> + "for hostdev devices using %s mode"),<br>
> + virNetDevVPortTypeToString(virtPort->virtPortType));<br>
> + goto cleanup;<br>
> + }<br>
> + ret = virDomainHostdevNetConfigVirtPortProfile(linkdev, vf,<br>
> + virtPort, &hostdev->parent.data.net->mac, uuid,<br>
> + port_profile_associate);<br>
> + } else {<br>
> + /* Set only mac and vlan */<br>
> + if (vlan) {<br>
> + if (vlan->nTags != 1 || vlan->trunk) {<br>
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",<br>
> + _("vlan trunking is not supported "<br>
> + "by SR-IOV network devices"));<br>
> + goto cleanup;<br>
> + }<br>
> + if (vf == -1) {<br>
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,<br>
> + _("vlan can only be set for SR-IOV VFs, but "<br>
> + "%s is not a VF"), linkdev);<br>
> + goto cleanup;<br>
> + }<br>
> + vlanid = vlan->tag[0];<br>
> + } else if (vf >= 0) {<br>
> + vlanid = 0; /* assure any current vlan tag is reset */<br>
> + }<br>
> +<br>
> + ret = virNetDevReplaceNetConfig(linkdev, vf,<br>
> + &hostdev->parent.data.net->mac,<br>
> + vlanid, stateDir);<br>
> + }<br>
> +cleanup:<br>
> + VIR_FREE(linkdev);<br>
> + return ret;<br>
> +}<br>
> +<br>
> +static int<br>
> +virDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,<br>
> + char *stateDir)<br>
> +{<br>
> + char *linkdev = NULL;<br>
> + virNetDevVPortProfilePtr virtPort;<br>
> + int ret = -1;<br>
> + int vf = -1;<br>
> + int port_profile_associate = 0;<br>
> + int isvf;<br>
> +<br>
> + isvf = virDomainHostdevIsVirtualFunction(hostdev);<br>
> + if (isvf <= 0) {<br>
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",<br>
> + _("Interface type hostdev is currently supported on"<br>
> + " SR-IOV Virtual Functions only"));<br>
> + return ret;<br>
> + }<br>
> +<br>
> + if (virDomainHostdevNetDevice(hostdev, &linkdev, &vf) < 0)<br>
> + return ret;<br>
> +<br>
> + virtPort = virDomainNetGetActualVirtPortProfile(<br>
> + hostdev-><a href="http://parent.data.net" target="_blank">parent.data.net</a>);<br>
> + if (virtPort)<br>
> + ret = virDomainHostdevNetConfigVirtPortProfile(linkdev, vf, virtPort,<br>
> + &hostdev->parent.data.net->mac, NULL,<br>
> + port_profile_associate);<br>
> + else<br>
> + ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir);<br>
> +<br>
> + VIR_FREE(linkdev);<br>
> +<br>
> + return ret;<br>
> +}<br>
> +<br>
> +static virPCIDeviceListPtr<br>
> +virHostdevManagerGetPciHostDeviceList(<br>
> + virDomainHostdevDefPtr *hostdevs,<br>
> + int nhostdevs)<br>
> +{<br>
> + virPCIDeviceListPtr list;<br>
> + int i;<br>
> +<br>
> + if (!(list = virPCIDeviceListNew()))<br>
> + return NULL;<br>
> +<br>
> + for (i = 0 ; i < nhostdevs ; i++) {<br>
> + virDomainHostdevDefPtr hostdev = hostdevs[i];<br>
> + virPCIDevicePtr dev;<br>
> +<br>
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)<br>
> + continue;<br>
> + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)<br>
> + continue;<br>
> +<br>
> + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,<br>
> + hostdev->source.subsys.u.pci.addr.bus,<br>
> + hostdev->source.subsys.u.pci.addr.slot,<br>
> + hostdev->source.subsys.u.pci.addr.function);<br>
> + if (!dev) {<br>
> + virObjectUnref(list);<br>
> + return NULL;<br>
> + }<br>
> +<br>
> + if (virPCIDeviceListAdd(list, dev) < 0) {<br>
> + virPCIDeviceFree(dev);<br>
> + virObjectUnref(list);<br>
> + return NULL;<br>
> + }<br>
> +<br>
> + virPCIDeviceSetManaged(dev, hostdev->managed);<br>
> + }<br>
> +<br>
> + return list;<br>
> +}<br>
> +<br>
> +int<br>
> +virHostdevManagerPreparePciHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name,<br>
> + const unsigned char *uuid,<br>
> + virDomainHostdevDefPtr *hostdevs,<br>
> + int nhostdevs,<br>
> + unsigned int flags)<br>
> +{<br>
> + virPCIDeviceListPtr pcidevs;<br>
> + int last_processed_hostdev_vf = -1;<br>
> + int i;<br>
> + int ret = -1;<br>
> +<br>
> + virObjectLock(mgr->activePciHostdevs);<br>
> + virObjectLock(mgr->inactivePciHostdevs);<br>
> +<br>
> + if (!(pcidevs = virHostdevManagerGetPciHostDeviceList(hostdevs, nhostdevs)))<br>
> + goto cleanup;<br>
> +<br>
> + /* We have to use 9 loops here. *All* devices must<br>
> + * be detached before we reset any of them, because<br>
> + * in some cases you have to reset the whole PCI,<br>
> + * which impacts all devices on it. Also, all devices<br>
> + * must be reset before being marked as active.<br>
> + */<br>
> +<br>
> + /* Loop 1: validate that non-managed device isn't in use, eg<br>
> + * by checking that device is either un-bound, or bound<br>
> + * to pci-stub.ko<br>
> + */<br>
> +<br>
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);<br>
> + virPCIDevicePtr other;<br>
> + bool strict_acs_check = (flags & VIR_STRICT_ACS_CHECK)?true:false;<br>
> +<br>
> + if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) {<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("PCI device %s is not assignable"),<br>
> + virPCIDeviceGetName(dev));<br>
> + goto cleanup;<br>
> + }<br>
> + /* The device is in use by other active domain if<br>
> + * the dev is in list activePciHostdevs.<br>
> + */<br>
> + if ((other = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) {<br>
> + char *other_drvname = NULL;<br>
> + char *other_domname = NULL;<br>
> + virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname);<br>
> +<br>
> + if (other_drvname && other_domname)<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("PCI device %s is in use by driver %s,domain %s"),<br>
> + virPCIDeviceGetName(dev), other_drvname,<br>
> + other_domname);<br>
> + else<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("PCI device %s is already in use"),<br>
> + virPCIDeviceGetName(dev));<br>
> + VIR_FREE(other_drvname);<br>
> + VIR_FREE(other_domname);<br>
> + goto cleanup;<br>
> + }<br>
> + }<br>
> +<br>
> + /* Loop 2: detach managed devices */<br>
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);<br>
> + const char *stub_driver;<br>
> + if (STREQ_NULLABLE(drv_name, "xenlight"))<br>
> + stub_driver = "pciback";<br>
> + else<br>
> + stub_driver = "pci_stub";<br>
<br>
</div></div>Really need hardcode driver name here? BTW "xen" driver missing here.<br>
Perhaps this can be passes as parameter (with default to "pci_stub" if NULL)?<br>
Or set by driver before calling this function (virPCIDeviceSetSubDriver?).<br>
<div><div class="h5"><br></div></div></blockquote><div>Yes, it's better to set by driver before calling this function by virPCIDeviceSetSubDriver. Will update.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0pt 0pt 0pt 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div><div class="h5">
> +<br>
> + if (virPCIDeviceGetManaged(dev) &&<br>
> + virPCIDeviceDetach(dev, mgr->activePciHostdevs, NULL, stub_driver) < 0)<br>
> + goto reattachdevs;<br>
> + }<br>
> +<br>
> + /* Loop 3: Now that all the PCI hostdevs have been detached, we<br>
> + * can safely reset them */<br>
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);<br>
> + if (virPCIDeviceReset(dev, mgr->activePciHostdevs,<br>
> + mgr->inactivePciHostdevs) < 0)<br>
> + goto reattachdevs;<br>
> + }<br>
> +<br>
> + /* Loop 4: For SRIOV network devices, Now that we have detached the<br>
> + * the network device, set the netdev config */<br>
> + for (i = 0; i < nhostdevs; i++) {<br>
> + virDomainHostdevDefPtr hostdev = hostdevs[i];<br>
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)<br>
> + continue;<br>
> + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)<br>
> + continue;<br>
> + if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET &&<br>
> + hostdev-><a href="http://parent.data.net" target="_blank">parent.data.net</a>) {<br>
> + if (virDomainHostdevNetConfigReplace(hostdev, uuid,<br>
> + mgr->stateDir) < 0) {<br>
> + goto resetvfnetconfig;<br>
> + }<br>
> + }<br>
> + last_processed_hostdev_vf = i;<br>
> + }<br>
> +<br>
> + /* Loop 5: Now mark all the devices as active */<br>
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);<br>
> + if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0)<br>
> + goto inactivedevs;<br>
> + }<br>
> +<br>
> + /* Loop 6: Now remove the devices from inactive list. */<br>
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);<br>
> + virPCIDeviceListDel(mgr->inactivePciHostdevs, dev);<br>
> + }<br>
> +<br>
> + /* Loop 7: Now set the used_by_domain of the device in<br>
> + * driver->activePciHostdevs as domain name.<br>
> + */<br>
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {<br>
> + virPCIDevicePtr dev, activeDev;<br>
> +<br>
> + dev = virPCIDeviceListGet(pcidevs, i);<br>
> + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev);<br>
> +<br>
> + if (activeDev)<br>
> + virPCIDeviceSetUsedBy(activeDev, drv_name, dom_name);<br>
> + }<br>
> +<br>
> + /* Loop 8: Now set the original states for hostdev def */<br>
> + for (i = 0; i < nhostdevs; i++) {<br>
> + virPCIDevicePtr dev;<br>
> + virPCIDevicePtr pcidev;<br>
> + virDomainHostdevDefPtr hostdev = hostdevs[i];<br>
> +<br>
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)<br>
> + continue;<br>
> + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)<br>
> + continue;<br>
> +<br>
> + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,<br>
> + hostdev->source.subsys.u.pci.addr.bus,<br>
> + hostdev->source.subsys.u.pci.addr.slot,<br>
> + hostdev->source.subsys.u.pci.addr.function);<br>
> +<br>
> + /* original states "unbind_from_stub", "remove_slot",<br>
> + * "reprobe" were already set by pciDettachDevice in<br>
> + * loop 2.<br>
> + */<br>
> + if ((pcidev = virPCIDeviceListFind(pcidevs, dev))) {<br>
> + hostdev->origstates.states.pci.unbind_from_stub =<br>
> + virPCIDeviceGetUnbindFromStub(pcidev);<br>
> + hostdev->origstates.states.pci.remove_slot =<br>
> + virPCIDeviceGetRemoveSlot(pcidev);<br>
> + hostdev->origstates.states.pci.reprobe =<br>
> + virPCIDeviceGetReprobe(pcidev);<br>
> + }<br>
> +<br>
> + virPCIDeviceFree(dev);<br>
> + }<br>
> +<br>
> + /* Loop 9: Now steal all the devices from pcidevs */<br>
> + while (virPCIDeviceListCount(pcidevs) > 0)<br>
> + virPCIDeviceListStealIndex(pcidevs, 0);<br>
> +<br>
> + ret = 0;<br>
> + goto cleanup;<br>
> +<br>
> +inactivedevs:<br>
> + /* Only steal all the devices from driver->activePciHostdevs. We will<br>
> + * free them in virObjectUnref().<br>
> + */<br>
> + while (virPCIDeviceListCount(pcidevs) > 0) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, 0);<br>
> + virPCIDeviceListSteal(mgr->activePciHostdevs, dev);<br>
> + }<br>
> +<br>
> +resetvfnetconfig:<br>
> + for (i = 0; i < last_processed_hostdev_vf; i++) {<br>
> + virDomainHostdevDefPtr hostdev = hostdevs[i];<br>
> + if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET &&<br>
> + hostdev-><a href="http://parent.data.net" target="_blank">parent.data.net</a>) {<br>
> + virDomainHostdevNetConfigRestore(hostdev, mgr->stateDir);<br>
> + }<br>
> + }<br>
> +<br>
> +reattachdevs:<br>
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);<br>
> + virPCIDeviceReattach(dev, mgr->activePciHostdevs, NULL);<br>
> + }<br>
> +<br>
> +cleanup:<br>
> + virObjectUnlock(mgr->activePciHostdevs);<br>
> + virObjectUnlock(mgr->inactivePciHostdevs);<br>
> + virObjectUnref(pcidevs);<br>
> + return ret;<br>
> +}<br>
> +<br>
> +static int<br>
> +virFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,<br>
> + bool mandatory,<br>
> + virUSBDevicePtr *usb)<br>
> +{<br>
> + unsigned vendor = hostdev->source.subsys.u.usb.vendor;<br>
> + unsigned product = hostdev->source.subsys.u.usb.product;<br>
> + unsigned bus = hostdev->source.subsys.u.usb.bus;<br>
> + unsigned device = hostdev->source.subsys.u.usb.device;<br>
> + bool autoAddress = hostdev->source.subsys.u.usb.autoAddress;<br>
> + int rc;<br>
> +<br>
> + *usb = NULL;<br>
> +<br>
> + if (vendor && bus) {<br>
> + rc = virUSBDeviceFind(vendor, product, bus, device,<br>
> + NULL,<br>
> + autoAddress ? false : mandatory,<br>
> + usb);<br>
> + if (rc < 0) {<br>
> + return -1;<br>
> + } else if (!autoAddress) {<br>
> + goto out;<br>
> + } else {<br>
> + VIR_INFO("USB device %x:%x could not be found at previous"<br>
> + " address (bus:%u device:%u)",<br>
> + vendor, product, bus, device);<br>
> + }<br>
> + }<br>
> +<br>
> + /* When vendor is specified, its USB address is either unspecified or the<br>
> + * device could not be found at the USB device where it had been<br>
> + * automatically found before.<br>
> + */<br>
> + if (vendor) {<br>
> + virUSBDeviceListPtr devs;<br>
> +<br>
> + rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs);<br>
> + if (rc < 0)<br>
> + return -1;<br>
> +<br>
> + if (rc == 1) {<br>
> + *usb = virUSBDeviceListGet(devs, 0);<br>
> + virUSBDeviceListSteal(devs, *usb);<br>
> + }<br>
> + virObjectUnref(devs);<br>
> +<br>
> + if (rc == 0) {<br>
> + goto out;<br>
> + } else if (rc > 1) {<br>
> + if (autoAddress) {<br>
> + virReportError(VIR_ERR_OPERATION_FAILED,<br>
> + _("Multiple USB devices for %x:%x were found,"<br>
> + " but none of them is at bus:%u device:%u"),<br>
> + vendor, product, bus, device);<br>
> + } else {<br>
> + virReportError(VIR_ERR_OPERATION_FAILED,<br>
> + _("Multiple USB devices for %x:%x, "<br>
> + "use <address> to specify one"),<br>
> + vendor, product);<br>
> + }<br>
> + return -1;<br>
> + }<br>
> +<br>
> + hostdev->source.subsys.u.usb.bus = virUSBDeviceGetBus(*usb);<br>
> + hostdev->source.subsys.u.usb.device = virUSBDeviceGetDevno(*usb);<br>
> + hostdev->source.subsys.u.usb.autoAddress = true;<br>
> +<br>
> + if (autoAddress) {<br>
> + VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved"<br>
> + " from bus:%u device:%u)",<br>
> + vendor, product,<br>
> + hostdev->source.subsys.u.usb.bus,<br>
> + hostdev->source.subsys.u.usb.device,<br>
> + bus, device);<br>
> + }<br>
> + } else if (!vendor && bus) {<br>
> + if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0)<br>
> + return -1;<br>
> + }<br>
> +<br>
> +out:<br>
> + if (!*usb)<br>
> + hostdev->missing = true;<br>
> + return 0;<br>
> +}<br>
> +<br>
> +static int<br>
> +virHostdevManagerMarkUsbHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name,<br>
> + virUSBDeviceListPtr list)<br>
> +{<br>
> + int i, j;<br>
> + unsigned int count;<br>
> + virUSBDevicePtr tmp;<br>
> +<br>
> + virObjectLock(mgr->activeUsbHostdevs);<br>
> + count = virUSBDeviceListCount(list);<br>
> +<br>
> + for (i = 0; i < count; i++) {<br>
> + virUSBDevicePtr usb = virUSBDeviceListGet(list, i);<br>
> + if ((tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb))) {<br>
> + char *other_drvname = NULL;<br>
> + char *other_domname = NULL;<br>
> + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_domname);<br>
> +<br>
> + if (other_drvname && other_domname)<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("USB device %s is in use by driver %s,domain %s"),<br>
> + virUSBDeviceGetName(tmp), other_drvname,<br>
> + other_domname);<br>
> + else<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("USB device %s is already in use"),<br>
> + virUSBDeviceGetName(tmp));<br>
> + VIR_FREE(other_drvname);<br>
> + VIR_FREE(other_domname);<br>
> + goto error;<br>
> + }<br>
> +<br>
> + virUSBDeviceSetUsedBy(usb, drv_name, dom_name);<br>
> + VIR_DEBUG("Adding %03d.%03d driver= %s dom=%s to activeUsbHostdevs",<br>
> + virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb),drv_name, dom_name);<br>
> + /*<br>
> + * The caller is responsible to steal these usb devices<br>
> + * from the virUSBDeviceList that passed in on success,<br>
> + * perform rollback on failure.<br>
> + */<br>
> + if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0)<br>
> + goto error;<br>
> + }<br>
> +<br>
> + virObjectUnlock(mgr->activeUsbHostdevs);<br>
> + return 0;<br>
> +<br>
> +error:<br>
> + for (j = 0; j < i; j++) {<br>
> + tmp = virUSBDeviceListGet(list, i);<br>
> + virUSBDeviceListSteal(mgr->activeUsbHostdevs, tmp);<br>
> + }<br>
> + virObjectUnlock(mgr->activeUsbHostdevs);<br>
> + return -1;<br>
> +}<br>
> +<br>
> +int virHostdevManagerPrepareUsbHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *driver,<br>
> + virDomainDefPtr def,<br>
> + bool coldBoot)<br>
> +{<br>
> + int i, ret = -1;<br>
> + virUSBDeviceListPtr list;<br>
> + virUSBDevicePtr tmp;<br>
> + virDomainHostdevDefPtr *hostdevs = def->hostdevs;<br>
> + int nhostdevs = def->nhostdevs;<br>
> +<br>
> + /* To prevent situation where USB device is assigned to two domains<br>
> + * we need to keep a list of currently assigned USB devices.<br>
> + * This is done in several loops which cannot be joined into one<br>
> + * big loop.<br>
> + */<br>
> + if (!(list = virUSBDeviceListNew()))<br>
> + goto cleanup;<br>
> +<br>
> + /* Loop 1: build temporary list<br>
> + */<br>
> + for (i = 0 ; i < nhostdevs ; i++) {<br>
> + virDomainHostdevDefPtr hostdev = hostdevs[i];<br>
> + bool required = true;<br>
> + virUSBDevicePtr usb;<br>
> +<br>
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)<br>
> + continue;<br>
> + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)<br>
> + continue;<br>
> +<br>
> + if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL ||<br>
> + (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE &&<br>
> + !coldBoot))<br>
> + required = false;<br>
> +<br>
> + if (virFindHostdevUSBDevice(hostdev, required, &usb) < 0)<br>
> + goto cleanup;<br>
> +<br>
> + if (usb && virUSBDeviceListAdd(list, usb) < 0) {<br>
> + virUSBDeviceFree(usb);<br>
> + goto cleanup;<br>
> + }<br>
> + }<br>
> +<br>
> + /* Mark devices in temporary list as used by @name<br>
> + * and add them do driver list. However, if something goes<br>
> + * wrong, perform rollback.<br>
> + */<br>
> + if (virHostdevManagerMarkUsbHostdevs(mgr, driver, def->name, list) < 0)<br>
> + goto cleanup;<br>
> +<br>
> + /* Loop 2: Temporary list was successfully merged with<br>
> + * driver list, so steal all items to avoid freeing them<br>
> + * in cleanup label.<br>
> + */<br>
> + while (virUSBDeviceListCount(list) > 0) {<br>
> + tmp = virUSBDeviceListGet(list, 0);<br>
> + virUSBDeviceListSteal(list, tmp);<br>
> + }<br>
> +<br>
> + ret = 0;<br>
> +<br>
> +cleanup:<br>
> + virObjectUnref(list);<br>
> + return ret;<br>
> +}<br>
> +<br>
> +int virHostdevManagerPrepareHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *driver,<br>
> + virDomainDefPtr def,<br>
> + unsigned int flags)<br>
> +{<br>
> + if (!def->nhostdevs)<br>
> + return 0;<br>
> +<br>
> + if (flags & VIR_SP_PCI_HOSTDEV) {<br>
> + if(virHostdevManagerPreparePciHostdevs(mgr, driver, def->name, def->uuid,<br>
> + def->hostdevs, def->nhostdevs, flags) < 0)<br>
> + return -1;<br>
> + }<br>
> +<br>
> + if (flags & VIR_SP_USB_HOSTDEV) {<br>
> + bool coldBoot = (flags & VIR_COLD_BOOT)?true:false;<br>
> + if(virHostdevManagerPrepareUsbHostdevs(mgr, driver, def, coldBoot) < 0)<br>
> + return -1;<br>
> + }<br>
> +<br>
> + return 0;<br>
> +}<br>
> +<br>
> +/*<br>
> + * Pre-condition: mgr->inactivePciHostdevs & mgr->activePciHostdevs<br>
> + * are locked<br>
> + */<br>
> +static void<br>
> +virReattachPciDevice(virHostdevManagerPtr mgr, virPCIDevicePtr dev, const char *driver)<br>
> +{<br>
> + /* If the device is not managed and was attached to guest<br>
> + * successfully, it must have been inactive.<br>
> + */<br>
> + if (!virPCIDeviceGetManaged(dev)) {<br>
> + if (virPCIDeviceListAdd(mgr->inactivePciHostdevs, dev) < 0)<br>
> + virPCIDeviceFree(dev);<br>
> + return;<br>
> + }<br>
> +<br>
> + if (STREQ_NULLABLE(driver, "QEMU")) {<br>
<br>
</div></div>Same as earlier, perhaps the better approach will be checking for current stub<br>
driver here?<br>
<div><div class="h5"><br>
> + int retries = 100;<br>
> +<br>
> + while (virPCIDeviceWaitForCleanup(dev, "kvm_assigned_device")<br>
> + && retries) {<br>
> + usleep(100*1000);<br>
> + retries--;<br>
> + }<br>
> + }<br>
> +<br>
> + if (virPCIDeviceReattach(dev, mgr->activePciHostdevs,<br>
> + mgr->inactivePciHostdevs) < 0) {<br>
> + virErrorPtr err = virGetLastError();<br>
> + VIR_ERROR(_("Failed to re-attach PCI device: %s"),<br>
> + err ? err->message : _("unknown error"));<br>
> + virResetError(err);<br>
> + }<br>
> + virPCIDeviceFree(dev);<br>
> +}<br>
> +<br>
> +/*<br>
> + * Pre-condition: driver->activePciHostdevs is locked<br>
> + */<br>
> +static virPCIDeviceListPtr<br>
> +virGetActivePciHostDeviceList(virHostdevManagerPtr mgr,<br>
> + virDomainHostdevDefPtr *hostdevs,<br>
> + int nhostdevs)<br>
> +{<br>
> + virPCIDeviceListPtr list;<br>
> + int i;<br>
> +<br>
> + if (!(list = virPCIDeviceListNew()))<br>
> + return NULL;<br>
> +<br>
> + for (i = 0 ; i < nhostdevs ; i++) {<br>
> + virDomainHostdevDefPtr hostdev = hostdevs[i];<br>
> + virPCIDevicePtr dev;<br>
> + virPCIDevicePtr activeDev;<br>
> +<br>
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)<br>
> + continue;<br>
> + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)<br>
> + continue;<br>
> +<br>
> + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,<br>
> + hostdev->source.subsys.u.pci.addr.bus,<br>
> + hostdev->source.subsys.u.pci.addr.slot,<br>
> + hostdev->source.subsys.u.pci.addr.function);<br>
> + if (!dev) {<br>
> + virObjectUnref(list);<br>
> + return NULL;<br>
> + }<br>
> +<br>
> + if ((activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) {<br>
> + if (virPCIDeviceListAdd(list, activeDev) < 0) {<br>
> + virPCIDeviceFree(dev);<br>
> + virObjectUnref(list);<br>
> + return NULL;<br>
> + }<br>
> + }<br>
> +<br>
> + virPCIDeviceFree(dev);<br>
> + }<br>
> +<br>
> + return list;<br>
> +}<br>
> +<br>
> +void<br>
> +virHostdevManagerReAttachPciHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name,<br>
> + virDomainHostdevDefPtr *hostdevs,<br>
> + int nhostdevs)<br>
> +{<br>
> + virPCIDeviceListPtr pcidevs;<br>
> + int i;<br>
> +<br>
> + virObjectLock(mgr->activePciHostdevs);<br>
> + virObjectLock(mgr->inactivePciHostdevs);<br>
> +<br>
> + if (!(pcidevs = virGetActivePciHostDeviceList(mgr,<br>
> + hostdevs,<br>
> + nhostdevs))) {<br>
> + virErrorPtr err = virGetLastError();<br>
> + VIR_ERROR(_("Failed to allocate PCI device list: %s"),<br>
> + err ? err->message : _("unknown error"));<br>
> + virResetError(err);<br>
> + goto cleanup;<br>
> + }<br>
> +<br>
> + /* Again 4 loops; mark all devices as inactive before reset<br>
> + * them and reset all the devices before re-attach.<br>
> + * Attach mac and port profile parameters to devices<br>
> + */<br>
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);<br>
> + virPCIDevicePtr activeDev = NULL;<br>
> +<br>
> + /* Never delete the dev from list driver->activePciHostdevs<br>
> + * if it's used by other domain.<br>
> + */<br>
> + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev);<br>
> + if (activeDev) {<br>
> + char *usedby_drvname = NULL;<br>
> + char *usedby_domname = NULL;<br>
> + virPCIDeviceGetUsedBy(activeDev, &usedby_drvname, &usedby_domname);<br>
> + if (STRNEQ_NULLABLE(drv_name, usedby_drvname) &&<br>
> + STRNEQ_NULLABLE(dom_name, usedby_domname)) {<br>
> + virPCIDeviceListSteal(pcidevs, dev);<br>
> + continue;<br>
> + }<br>
> + VIR_FREE(usedby_drvname);<br>
> + VIR_FREE(usedby_domname);<br>
> + }<br>
> +<br>
> + /* virObjectUnref() will take care of freeing the dev. */<br>
> + virPCIDeviceListSteal(mgr->activePciHostdevs, dev);<br>
> + }<br>
> +<br>
> + /*<br>
> + * For SRIOV net host devices, unset mac and port profile before<br>
> + * reset and reattach device<br>
> + */<br>
> + for (i = 0; i < nhostdevs; i++) {<br>
> + virDomainHostdevDefPtr hostdev = hostdevs[i];<br>
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)<br>
> + continue;<br>
> + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)<br>
> + continue;<br>
> + if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET &&<br>
> + hostdev-><a href="http://parent.data.net" target="_blank">parent.data.net</a>) {<br>
> + virDomainHostdevNetConfigRestore(hostdev, mgr->stateDir);<br>
> + }<br>
> + }<br>
> +<br>
> + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);<br>
> + if (virPCIDeviceReset(dev, mgr->activePciHostdevs,<br>
> + mgr->inactivePciHostdevs) < 0) {<br>
> + virErrorPtr err = virGetLastError();<br>
> + VIR_ERROR(_("Failed to reset PCI device: %s"),<br>
> + err ? err->message : _("unknown error"));<br>
> + virResetError(err);<br>
> + }<br>
> + }<br>
> +<br>
> + while (virPCIDeviceListCount(pcidevs) > 0) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListStealIndex(pcidevs, 0);<br>
> + virReattachPciDevice(mgr, dev, drv_name);<br>
> + }<br>
> +<br>
> + virObjectUnref(pcidevs);<br>
> +cleanup:<br>
> + virObjectUnlock(mgr->activePciHostdevs);<br>
> + virObjectUnlock(mgr->inactivePciHostdevs);<br>
> +}<br>
> +<br>
> +void<br>
> +virHostdevManagerReAttachUsbHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name,<br>
> + virDomainHostdevDefPtr *hostdevs,<br>
> + int nhostdevs)<br>
> +{<br>
> + int i;<br>
> +<br>
> + virObjectLock(mgr->activeUsbHostdevs);<br>
> + for (i = 0; i < nhostdevs; i++) {<br>
> + virDomainHostdevDefPtr hostdev = hostdevs[i];<br>
> + virUSBDevicePtr usb, tmp;<br>
> +<br>
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)<br>
> + continue;<br>
> + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)<br>
> + continue;<br>
> + if (hostdev->missing)<br>
> + continue;<br>
> +<br>
> + usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus,<br>
> + hostdev->source.subsys.u.usb.device,<br>
> + NULL);<br>
> +<br>
> + if (!usb) {<br>
> + VIR_WARN("Unable to reattach USB device %03d.%03d on driver %s domain %s",<br>
> + hostdev->source.subsys.u.usb.bus,<br>
> + hostdev->source.subsys.u.usb.device,<br>
> + drv_name, dom_name);<br>
> + continue;<br>
> + }<br>
> +<br>
> + /* Delete only those USB devices which belongs<br>
> + * to domain. Therefore we want to steal only<br>
> + * those devices from the list which were taken<br>
> + * by domain */<br>
> +<br>
> + tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb);<br>
> + virUSBDeviceFree(usb);<br>
> +<br>
> + if (!tmp) {<br>
> + VIR_WARN("Unable to find device %03d.%03d "<br>
> + "in list of active USB devices",<br>
> + hostdev->source.subsys.u.usb.bus,<br>
> + hostdev->source.subsys.u.usb.device);<br>
> + continue;<br>
> + }<br>
> +<br>
> + char *usedby_drvname = NULL;<br>
> + char *usedby_domname = NULL;<br>
> + virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname);<br>
> + if (STREQ_NULLABLE(drv_name, usedby_drvname) &&<br>
> + STREQ_NULLABLE(dom_name, usedby_domname)) {<br>
> + VIR_DEBUG("Removing %03d.%03d dom=%s:%s",<br>
> + hostdev->source.subsys.u.usb.bus,<br>
> + hostdev->source.subsys.u.usb.device,<br>
> + drv_name, dom_name);<br>
> + virUSBDeviceListDel(mgr->activeUsbHostdevs, tmp);<br>
> + }<br>
> + VIR_FREE(usedby_drvname);<br>
> + VIR_FREE(usedby_domname);<br>
> +<br>
> + }<br>
> + virObjectUnlock(mgr->activeUsbHostdevs);<br>
> +}<br>
> +<br>
> +void virHostdevManagerReAttachHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *driver,<br>
> + virDomainDefPtr def,<br>
> + unsigned int flags)<br>
> +{<br>
> + if (!def->nhostdevs)<br>
> + return;<br>
> +<br>
> + if (flags & VIR_SP_PCI_HOSTDEV) {<br>
> + virHostdevManagerReAttachPciHostdevs(mgr, driver, def->name, def->hostdevs, def->nhostdevs);<br>
> + }<br>
> +<br>
> + if (flags & VIR_SP_USB_HOSTDEV) {<br>
> + virHostdevManagerReAttachUsbHostdevs(mgr, driver, def->name, def->hostdevs, def->nhostdevs);<br>
> + }<br>
> +}<br>
> +<br>
> +<br>
> +/*After using any of the above four *Get* APIs, virFreeHostdevNameList should<br>
> + *be called to free memory.<br>
> + */<br>
> +void virFreeHostdevNameList(virHostdevNameListPtr list){<br>
> + int i;<br>
> +<br>
> + if (list && list->names) {<br>
> + for (i = 0; i < list->nnames; i++) {<br>
> + if (list->names[i])<br>
> + VIR_FREE(list->names[i]);<br>
> + }<br>
> + VIR_FREE(list->names);<br>
> + }<br>
> + VIR_FREE(list);<br>
> +}<br>
> +<br>
> +virHostdevNameListPtr<br>
> +virGetActivePciHostdevs(virHostdevManagerPtr mgr)<br>
> +{<br>
> + int i,count;<br>
> + virHostdevNameListPtr list = NULL;<br>
> + virObjectLock(mgr->activePciHostdevs);<br>
> +<br>
> + count = virPCIDeviceListCount(mgr->activePciHostdevs);<br>
> + if (count > 0) {<br>
> + if (VIR_ALLOC_N(list, 1) < 0) {<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("Fail to alloc memory"));<br>
<br>
</div></div>virReportOOMError?<br>
<div class="im"><br>
> + goto cleanup;<br>
> + }<br>
> +<br>
> + if (VIR_ALLOC_N(list->names,count) < 0) {<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("Fail to alloc memory"));<br>
<br>
</div>virReportOOMError?<br>
<div><div class="h5"><br>
> + goto cleanup;<br>
> + }<br>
> + list->nnames = 0;<br>
> +<br>
> + for (i = 0; i < count; i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(mgr->activePciHostdevs, i);<br>
> + list->names[list->nnames++] = strdup(virPCIDeviceGetName(dev));<br>
> + }<br>
> + }<br>
> +<br>
> +cleanup:<br>
> + virFreeHostdevNameList(list);<br>
> + virObjectUnlock(mgr->activePciHostdevs);<br>
> + return list;<br>
> +<br>
> +<br>
> +}<br>
> +<br>
> +virHostdevNameListPtr<br>
> +virGetActiveUsbHostdevs(virHostdevManagerPtr mgr)<br>
> +{<br>
> + int i,count;<br>
> + virHostdevNameListPtr list = NULL;<br>
> + virObjectLock(mgr->activeUsbHostdevs);<br>
> +<br>
> + count = virUSBDeviceListCount(mgr->activeUsbHostdevs);<br>
> + if (count > 0) {<br>
> + if (VIR_ALLOC_N(list, 1) < 0) {<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("Fail to alloc memory"));<br>
<br>
</div></div>virReportOOMError?<br>
<div class="im"><br>
> + goto cleanup;<br>
> + }<br>
> +<br>
> + if (VIR_ALLOC_N(list->names,count) < 0) {<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("Fail to alloc memory"));<br>
<br>
</div>virReportOOMError?<br>
<div><div class="h5"><br>
> + goto cleanup;<br>
> + }<br>
> + list->nnames = 0;<br>
> +<br>
> + for (i = 0; i < count; i++) {<br>
> + virUSBDevicePtr usb = virUSBDeviceListGet(mgr->activeUsbHostdevs, i);<br>
> + list->names[list->nnames++] = strdup(virUSBDeviceGetName(usb));<br>
> + }<br>
> + }<br>
> +<br>
> +cleanup:<br>
> + virFreeHostdevNameList(list);<br>
> + virObjectUnlock(mgr->activeUsbHostdevs);<br>
> + return list;<br>
> +}<br>
> +<br>
> +virHostdevNameListPtr<br>
> +virGetDomainActivePciHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name)<br>
> +{<br>
> + int i;<br>
> + size_t count;<br>
> + virHostdevNameListPtr list = NULL;<br>
> + virObjectLock(mgr->activePciHostdevs);<br>
> +<br>
> + count = virPCIDeviceListCount(mgr->activePciHostdevs);<br>
> + if (count > 0) {<br>
> + if (VIR_ALLOC_N(list, 1) < 0) {<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("Fail to alloc memory"));<br>
<br>
</div></div>virReportOOMError?<br>
<div class="im"><br>
> + goto cleanup;<br>
> + }<br>
> +<br>
> + if (VIR_ALLOC_N(list->names,count) < 0) {<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("Fail to alloc memory"));<br>
<br>
</div>virReportOOMError?<br>
<div><div class="h5"><br>
> + goto cleanup;<br>
> + }<br>
> + list->nnames = 0;<br>
> +<br>
> + for (i = 0; i < count; i++) {<br>
> + virPCIDevicePtr dev = virPCIDeviceListGet(mgr->activePciHostdevs, i);<br>
> + char *usedby_drvname = NULL;<br>
> + char *usedby_domname = NULL;<br>
> + virPCIDeviceGetUsedBy(dev, &usedby_drvname, &usedby_domname);<br>
> + if (STRNEQ(drv_name, usedby_drvname) &&<br>
> + STRNEQ(dom_name, usedby_domname)) {<br>
> + list->names[list->nnames++] = strdup(virPCIDeviceGetName(dev));<br>
> + }<br>
> + VIR_FREE(usedby_drvname);<br>
> + VIR_FREE(usedby_domname);<br>
> + }<br>
> +<br>
> + VIR_SHRINK_N(list->names, count, count - list->nnames);<br>
> + }<br>
> +<br>
> +cleanup:<br>
> + virFreeHostdevNameList(list);<br>
> + virObjectUnlock(mgr->activePciHostdevs);<br>
> + return list;<br>
> +}<br>
> +<br>
> +virHostdevNameListPtr<br>
> +virGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name)<br>
> +{<br>
> + int i;<br>
> + size_t count;<br>
> + virHostdevNameListPtr list = NULL;<br>
> + virObjectLock(mgr->activeUsbHostdevs);<br>
> +<br>
> + count = virUSBDeviceListCount(mgr->activeUsbHostdevs);<br>
> + if (count > 0) {<br>
> + if (VIR_ALLOC_N(list, 1) < 0) {<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("Fail to alloc memory"));<br>
<br>
</div></div>virReportOOMError?<br>
<div class="im"><br>
> + goto cleanup;<br>
> + }<br>
> +<br>
> + if (VIR_ALLOC_N(list->names,count) < 0) {<br>
> + virReportError(VIR_ERR_OPERATION_INVALID,<br>
> + _("Fail to alloc memory"));<br>
<br>
</div>virReportOOMError?<br>
<div><div class="h5"><br>
> + goto cleanup;<br>
> + }<br>
> + list->nnames = 0;<br>
> +<br>
> + for (i = 0; i < count; i++) {<br>
> + virUSBDevicePtr usb = virUSBDeviceListGet(mgr->activeUsbHostdevs, i);<br>
> + char *usedby_drvname = NULL;<br>
> + char *usedby_domname = NULL;<br>
> + virUSBDeviceGetUsedBy(usb, &usedby_drvname, &usedby_domname);<br>
> + if (STRNEQ(drv_name, usedby_drvname) &&<br>
> + STRNEQ(dom_name, usedby_domname)) {<br>
> + list->names[list->nnames++] = strdup(virUSBDeviceGetName(usb));<br>
> + }<br>
> + VIR_FREE(usedby_drvname);<br>
> + VIR_FREE(usedby_domname);<br>
> + }<br>
> +<br>
> + VIR_SHRINK_N(list->names, count, count - list->nnames);<br>
> + }<br>
> +<br>
> +cleanup:<br>
> + virFreeHostdevNameList(list);<br>
> + virObjectUnlock(mgr->activeUsbHostdevs);<br>
> + return list;<br>
> +}<br>
> diff --git a/src/util/virhostdevmanager.h b/src/util/virhostdevmanager.h<br>
> new file mode 100644<br>
> index 0000000..e493ac5<br>
> --- /dev/null<br>
> +++ b/src/util/virhostdevmanager.h<br>
> @@ -0,0 +1,91 @@<br>
> +/*<br>
> + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.<br>
> + *<br>
> + * This library is free software; you can redistribute it and/or<br>
> + * modify it under the terms of the GNU Lesser General Public<br>
> + * License as published by the Free Software Foundation; either<br>
> + * version 2.1 of the License, or (at your option) any later version.<br>
> + *<br>
> + * This library is distributed in the hope that it will be useful,<br>
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>
> + * Lesser General Public License for more details.<br>
> + *<br>
> + * You should have received a copy of the GNU Lesser General Public<br>
> + * License along with this library. If not, see<br>
> + * <<a href="http://www.gnu.org/licenses/" target="_blank">http://www.gnu.org/licenses/</a>>.<br>
> + *<br>
> + * Author: Chunyan Liu <<a href="mailto:cyliu@suse.com">cyliu@suse.com</a>><br>
> + */<br>
> +<br>
> +#ifndef __VIR_HOSTDEV_MANAGER_H__<br>
> +# define __VIR_HOSTDEV_MANAGER_H__<br>
> +<br>
> +# include "internal.h"<br>
> +# include "configmake.h"<br>
> +# include "conf/domain_conf.h"<br>
> +<br>
> +# define HOSTDEV_STATE_DIR LOCALSTATEDIR "/run/libvirt/hostdevmanager"<br>
> +<br>
> +typedef enum {<br>
> + VIR_SP_PCI_HOSTDEV = (1 << 0), /* support pci passthrough */<br>
> + VIR_SP_USB_HOSTDEV = (1 << 1), /* support usb passthrough */<br>
> + VIR_COLD_BOOT = (1 << 2), /* cold boot */<br>
> + VIR_STRICT_ACS_CHECK = (1 << 3), /* strict acs check */<br>
> +} virHostdevManagerFlag;<br>
> +<br>
> +typedef struct _virHostdevManager virHostdevManager;<br>
> +typedef virHostdevManager *virHostdevManagerPtr;<br>
> +<br>
> +struct virHostdevNameList {<br>
> + char **names;<br>
> + size_t nnames;<br>
> +};<br>
> +typedef struct virHostdevNameList *virHostdevNameListPtr;<br>
> +<br>
> +virHostdevManagerPtr virHostdevManagerGetDefault(void);<br>
> +int virHostdevManagerInit(void);<br>
> +void virHostdevManagerCleanup(void);<br>
> +int virHostdevManagerPrepareHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *driver,<br>
> + virDomainDefPtr def,<br>
> + unsigned int flags);<br>
> +void virHostdevManagerReAttachHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *driver,<br>
> + virDomainDefPtr def,<br>
> + unsigned int flags);<br>
> +int virHostdevManagerPreparePciHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name,<br>
> + const unsigned char *uuid,<br>
> + virDomainHostdevDefPtr *hostdevs,<br>
> + int nhostdevs,<br>
> + unsigned int flags);<br>
> +int virHostdevManagerPrepareUsbHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *driver,<br>
> + virDomainDefPtr def,<br>
> + bool coldBoot);<br>
> +void virHostdevManagerReAttachPciHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name,<br>
> + virDomainHostdevDefPtr *hostdevs,<br>
> + int nhostdevs);<br>
> +void virHostdevManagerReAttachUsbHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name,<br>
> + virDomainHostdevDefPtr *hostdevs,<br>
> + int nhostdevs);<br>
> +virHostdevNameListPtr virGetActivePciHostdevs(virHostdevManagerPtr mgr);<br>
> +virHostdevNameListPtr virGetActiveUsbHostdevs(virHostdevManagerPtr mgr);<br>
> +virHostdevNameListPtr virGetDomainActivePciHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name);<br>
> +virHostdevNameListPtr virGetDomainActiveUsbHostdevs(virHostdevManagerPtr mgr,<br>
> + const char *drv_name,<br>
> + const char *dom_name);<br>
> +/*After using any of the above four *Get* APIs, virFreeHostdevNameList should<br>
> + *be called to free memory.<br>
> + */<br>
> +void virFreeHostdevNameList(virHostdevNameListPtr list);<br>
> +<br>
> +#endif /* __VIR_HOSTDEV_MANAGER_H__ */<br>
> diff --git a/src/util/virpci.c b/src/util/virpci.c<br>
> index 5865613..7fe0921 100644<br>
> --- a/src/util/virpci.c<br>
> +++ b/src/util/virpci.c<br>
> @@ -62,7 +62,10 @@ struct _virPCIDevice {<br>
> char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */<br>
> char id[PCI_ID_LEN]; /* product vendor */<br>
> char *path;<br>
> - const char *used_by; /* The domain which uses the device */<br>
> +<br>
> + /* The driver:domain which uses the device */<br>
> + const char *used_by_drvname;<br>
> + const char *used_by_domname;<br>
><br>
> unsigned int pcie_cap_pos;<br>
> unsigned int pci_pm_cap_pos;<br>
> @@ -1553,15 +1556,17 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool reprobe)<br>
> }<br>
><br>
> void<br>
> -virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name)<br>
> +virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *drv_name, const char *dom_name)<br>
> {<br>
> - dev->used_by = name;<br>
> + dev->used_by_drvname = drv_name;<br>
> + dev->used_by_domname = dom_name;<br>
> }<br>
><br>
> -const char *<br>
> -virPCIDeviceGetUsedBy(virPCIDevicePtr dev)<br>
> +void<br>
> +virPCIDeviceGetUsedBy(virPCIDevicePtr dev, char **drv_name, char **dom_name)<br>
> {<br>
> - return dev->used_by;<br>
> + *drv_name = strdup(dev->used_by_drvname);<br>
> + *dom_name = strdup(dev->used_by_domname);<br>
> }<br>
><br>
> void virPCIDeviceReattachInit(virPCIDevicePtr pci)<br>
> diff --git a/src/util/virpci.h b/src/util/virpci.h<br>
> index 7bcadb4..294cb0e 100644<br>
> --- a/src/util/virpci.h<br>
> +++ b/src/util/virpci.h<br>
> @@ -66,8 +66,11 @@ void virPCIDeviceSetStubDriver(virPCIDevicePtr dev,<br>
> const char *driver);<br>
> const char *virPCIDeviceGetStubDriver(virPCIDevicePtr dev);<br>
> void virPCIDeviceSetUsedBy(virPCIDevice *dev,<br>
> - const char *used_by);<br>
> -const char *virPCIDeviceGetUsedBy(virPCIDevice *dev);<br>
> + const char *drv_name,<br>
> + const char *dom_name);<br>
> +void virPCIDeviceGetUsedBy(virPCIDevice *dev,<br>
> + char **drv_name,<br>
> + char **dom_name);<br>
> unsigned int virPCIDeviceGetUnbindFromStub(virPCIDevicePtr dev);<br>
> void virPCIDeviceSetUnbindFromStub(virPCIDevice *dev,<br>
> bool unbind);<br>
> diff --git a/src/util/virusb.c b/src/util/virusb.c<br>
> index 27ba9c7..043206e 100644<br>
> --- a/src/util/virusb.c<br>
> +++ b/src/util/virusb.c<br>
> @@ -55,7 +55,10 @@ struct _virUSBDevice {<br>
> char name[USB_ADDR_LEN]; /* domain:bus:slot.function */<br>
> char id[USB_ID_LEN]; /* product vendor */<br>
> char *path;<br>
> - const char *used_by; /* name of the domain using this dev */<br>
> +<br>
> + /* driver:domain using this dev */<br>
> + const char *used_by_drvname;<br>
> + const char *used_by_domname;<br>
> };<br>
><br>
> struct _virUSBDeviceList {<br>
> @@ -383,16 +386,20 @@ virUSBDeviceFree(virUSBDevicePtr dev)<br>
> VIR_FREE(dev);<br>
> }<br>
><br>
> -<br>
> void virUSBDeviceSetUsedBy(virUSBDevicePtr dev,<br>
> - const char *name)<br>
> + const char *drv_name,<br>
> + const char *dom_name)<br>
> {<br>
> - dev->used_by = name;<br>
> + dev->used_by_drvname = drv_name;<br>
> + dev->used_by_domname = dom_name;<br>
> }<br>
><br>
> -const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev)<br>
> +void virUSBDeviceGetUsedBy(virUSBDevicePtr dev,<br>
> + char **drv_name,<br>
> + char **dom_name)<br>
> {<br>
> - return dev->used_by;<br>
> + *drv_name = strdup(dev->used_by_drvname);<br>
> + *dom_name = strdup(dev->used_by_domname);<br>
> }<br>
><br>
> const char *virUSBDeviceGetName(virUSBDevicePtr dev)<br>
> diff --git a/src/util/virusb.h b/src/util/virusb.h<br>
> index aa59d12..16e48e5 100644<br>
> --- a/src/util/virusb.h<br>
> +++ b/src/util/virusb.h<br>
> @@ -60,8 +60,8 @@ int virUSBDeviceFind(unsigned int vendor,<br>
> virUSBDevicePtr *usb);<br>
><br>
> void virUSBDeviceFree(virUSBDevicePtr dev);<br>
> -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, const char *name);<br>
> -const char *virUSBDeviceGetUsedBy(virUSBDevicePtr dev);<br>
> +void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, const char *drv_name, const char *dom_name);<br>
> +void virUSBDeviceGetUsedBy(virUSBDevicePtr dev, char **drv_name, char **dom_name);<br>
<br>
</div></div>This change gives qemu and lxc drivers compile fail. So those drivers needs<br>
appropriate change.<br>
<div class="im"><br></div></blockquote><div>Yes, you are right. I didn't change qemu and lxc drivers yet. There might be some opinions about these APIs and need to rework many times before acceptable, so I think it's better to have a consensus on the common library, then change qemu and lxc driver.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0pt 0pt 0pt 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="im">
> const char *virUSBDeviceGetName(virUSBDevicePtr dev);<br>
><br>
> unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev);<br>
><br>
<br>
<br>
--<br>
</div>Best Regards / Pozdrawiam,<br>
Marek Marczykowski<br>
Invisible Things Lab<br>
<br>
</blockquote></div><br></div></div>