Re: [v10 1/6] add hostdev passthrough common library

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Chunyan Liu wrote:
> Extract code from qemu_hostdev.c and make it reusable for multiple drivers,
> meanwhile maintain a global hostdev state to solve conflict between different
> drivers.
>
> Signed-off-by: Chunyan Liu <cyliu@xxxxxxxx>
> ---
>  .gnulib                  |    2 +-
>  po/POTFILES.in           |    1 +
>  src/Makefile.am          |    1 +
>  src/libvirt_private.syms |   21 +
>  src/lxc/lxc_hostdev.c    |   11 +-
>  src/qemu/qemu_driver.c   |    4 +-
>  src/qemu/qemu_hostdev.c  |   42 +-
>  src/util/virhostdev.c    | 1691 ++++++++++++++++++++++++++++++++++++++++++++++
>  src/util/virhostdev.h    |  134 ++++
>  src/util/virpci.c        |   30 +-
>  src/util/virpci.h        |    9 +-
>  src/util/virscsi.c       |   28 +-
>  src/util/virscsi.h       |    8 +-
>  src/util/virusb.c        |   29 +-
>  src/util/virusb.h        |    8 +-
>  15 files changed, 1970 insertions(+), 49 deletions(-)
>  create mode 100644 src/util/virhostdev.c
>  create mode 100644 src/util/virhostdev.h
>
> diff --git a/.gnulib b/.gnulib
> index d18d1b8..831b84c 160000
> --- a/.gnulib
> +++ b/.gnulib
> @@ -1 +1 @@
> -Subproject commit d18d1b8023822220bb8f0a079c7312a1adffdce0
> +Subproject commit 831b84c59ef413c57a36b67344467d66a8a2ba70
>   

Oops, looks like you needed to update gnulib.

Regards,
Jim

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

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]