2010-12-16 16:10:54 +00:00
|
|
|
/*
|
|
|
|
* qemu_hostdev.c: QEMU hostdev management
|
|
|
|
*
|
2013-03-18 19:56:12 +00:00
|
|
|
* Copyright (C) 2006-2007, 2009-2013 Red Hat, Inc.
|
2010-12-16 16:10:54 +00:00
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2010-12-16 16:10:54 +00:00
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2013-09-19 15:01:17 +00:00
|
|
|
#include <dirent.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2010-12-16 16:10:54 +00:00
|
|
|
#include "qemu_hostdev.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 14:52:25 +00:00
|
|
|
#include "virpci.h"
|
2012-12-12 17:04:51 +00:00
|
|
|
#include "virusb.h"
|
2013-05-03 18:07:29 +00:00
|
|
|
#include "virscsi.h"
|
2012-03-06 01:12:44 +00:00
|
|
|
#include "virnetdev.h"
|
2013-09-19 15:01:17 +00:00
|
|
|
#include "virfile.h"
|
2014-03-05 09:58:54 +00:00
|
|
|
#include "virhostdev.h"
|
2010-12-16 16:10:54 +00:00
|
|
|
|
2012-07-18 15:22:03 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
|
|
|
2014-03-06 04:32:42 +00:00
|
|
|
static int
|
|
|
|
virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr,
|
|
|
|
virDomainDefPtr def)
|
2010-12-16 16:10:54 +00:00
|
|
|
{
|
2011-10-20 09:50:10 +00:00
|
|
|
virDomainHostdevDefPtr hostdev = NULL;
|
2013-05-31 18:26:56 +00:00
|
|
|
virPCIDevicePtr dev = NULL;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2013-01-16 12:09:58 +00:00
|
|
|
int ret = -1;
|
2010-12-16 16:10:54 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectLock(mgr->activePciHostdevs);
|
|
|
|
virObjectLock(mgr->inactivePciHostdevs);
|
2013-01-16 12:09:58 +00:00
|
|
|
|
2011-10-20 09:50:10 +00:00
|
|
|
for (i = 0; i < def->nhostdevs; i++) {
|
|
|
|
hostdev = def->hostdevs[i];
|
|
|
|
|
|
|
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
|
|
continue;
|
|
|
|
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
|
|
|
|
continue;
|
|
|
|
|
2013-03-18 19:56:12 +00:00
|
|
|
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);
|
2011-10-20 09:50:10 +00:00
|
|
|
|
|
|
|
if (!dev)
|
2013-01-16 12:09:58 +00:00
|
|
|
goto cleanup;
|
2011-10-20 09:50:10 +00:00
|
|
|
|
2013-01-14 22:11:44 +00:00
|
|
|
virPCIDeviceSetManaged(dev, hostdev->managed);
|
2013-04-23 18:53:36 +00:00
|
|
|
if (hostdev->source.subsys.u.pci.backend
|
2013-04-26 20:44:05 +00:00
|
|
|
== VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) {
|
2013-05-31 18:26:56 +00:00
|
|
|
if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0)
|
|
|
|
goto cleanup;
|
2013-04-23 18:53:36 +00:00
|
|
|
} else {
|
2013-05-31 18:26:56 +00:00
|
|
|
if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-04-23 18:53:36 +00:00
|
|
|
}
|
2014-03-01 06:28:56 +00:00
|
|
|
virPCIDeviceSetUsedBy(dev, QEMU_DRIVER_NAME, def->name);
|
2011-10-20 09:50:10 +00:00
|
|
|
|
|
|
|
/* Setup the original states for the PCI device */
|
2013-01-14 22:11:44 +00:00
|
|
|
virPCIDeviceSetUnbindFromStub(dev, hostdev->origstates.states.pci.unbind_from_stub);
|
|
|
|
virPCIDeviceSetRemoveSlot(dev, hostdev->origstates.states.pci.remove_slot);
|
|
|
|
virPCIDeviceSetReprobe(dev, hostdev->origstates.states.pci.reprobe);
|
2010-12-16 16:10:54 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0)
|
2013-01-16 12:09:58 +00:00
|
|
|
goto cleanup;
|
2013-05-31 18:26:56 +00:00
|
|
|
dev = NULL;
|
2012-03-26 14:44:19 +00:00
|
|
|
}
|
|
|
|
|
2013-05-31 18:30:09 +00:00
|
|
|
ret = 0;
|
2013-01-16 12:09:58 +00:00
|
|
|
cleanup:
|
2013-05-31 18:26:56 +00:00
|
|
|
virPCIDeviceFree(dev);
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectUnlock(mgr->activePciHostdevs);
|
|
|
|
virObjectUnlock(mgr->inactivePciHostdevs);
|
2013-01-16 12:09:58 +00:00
|
|
|
return ret;
|
2012-03-26 14:44:19 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 04:32:42 +00:00
|
|
|
int
|
|
|
|
qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver,
|
|
|
|
virDomainDefPtr def)
|
|
|
|
{
|
|
|
|
virHostdevManagerPtr mgr = driver->hostdevMgr;
|
|
|
|
|
|
|
|
if (!def->nhostdevs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return virHostdevUpdateActivePciHostdevs(mgr, def);
|
|
|
|
}
|
|
|
|
|
2014-03-06 04:45:16 +00:00
|
|
|
static int
|
|
|
|
virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr,
|
|
|
|
virDomainDefPtr def)
|
2012-03-26 14:44:19 +00:00
|
|
|
{
|
|
|
|
virDomainHostdevDefPtr hostdev = NULL;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2013-01-16 12:09:58 +00:00
|
|
|
int ret = -1;
|
2012-03-26 14:44:19 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectLock(mgr->activeUsbHostdevs);
|
2012-03-26 14:44:19 +00:00
|
|
|
for (i = 0; i < def->nhostdevs; i++) {
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDevicePtr usb = NULL;
|
2012-03-26 14:44:19 +00:00
|
|
|
hostdev = def->hostdevs[i];
|
|
|
|
|
|
|
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
|
|
continue;
|
|
|
|
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
|
|
|
|
continue;
|
|
|
|
|
2013-01-14 22:11:44 +00:00
|
|
|
usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus,
|
|
|
|
hostdev->source.subsys.u.usb.device,
|
|
|
|
NULL);
|
2012-03-26 14:44:19 +00:00
|
|
|
if (!usb) {
|
|
|
|
VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s",
|
|
|
|
hostdev->source.subsys.u.usb.bus,
|
|
|
|
hostdev->source.subsys.u.usb.device,
|
|
|
|
def->name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-03-01 06:28:56 +00:00
|
|
|
virUSBDeviceSetUsedBy(usb, QEMU_DRIVER_NAME, def->name);
|
2012-03-26 14:44:19 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0) {
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDeviceFree(usb);
|
2013-01-16 12:09:58 +00:00
|
|
|
goto cleanup;
|
2010-12-16 16:10:54 +00:00
|
|
|
}
|
|
|
|
}
|
2013-01-16 12:09:58 +00:00
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectUnlock(mgr->activeUsbHostdevs);
|
2013-01-16 12:09:58 +00:00
|
|
|
return ret;
|
2010-12-16 16:10:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 04:45:16 +00:00
|
|
|
int
|
|
|
|
qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver,
|
|
|
|
virDomainDefPtr def)
|
|
|
|
{
|
|
|
|
virHostdevManagerPtr mgr = driver->hostdevMgr;
|
|
|
|
|
|
|
|
if (!def->nhostdevs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return virHostdevUpdateActiveUsbHostdevs(mgr, def);
|
|
|
|
}
|
|
|
|
|
2014-03-06 04:47:50 +00:00
|
|
|
static int
|
|
|
|
virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr,
|
|
|
|
virDomainDefPtr def)
|
2013-05-03 18:07:29 +00:00
|
|
|
{
|
|
|
|
virDomainHostdevDefPtr hostdev = NULL;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2013-05-03 18:07:29 +00:00
|
|
|
int ret = -1;
|
2014-01-29 17:22:42 +00:00
|
|
|
virSCSIDevicePtr scsi = NULL;
|
|
|
|
virSCSIDevicePtr tmp = NULL;
|
2013-05-03 18:07:29 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectLock(mgr->activeScsiHostdevs);
|
2013-05-03 18:07:29 +00:00
|
|
|
for (i = 0; i < def->nhostdevs; i++) {
|
|
|
|
hostdev = def->hostdevs[i];
|
|
|
|
|
|
|
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
|
|
|
|
hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
|
|
|
|
continue;
|
|
|
|
|
2014-01-30 07:05:59 +00:00
|
|
|
if (!(scsi = virSCSIDeviceNew(NULL,
|
|
|
|
hostdev->source.subsys.u.scsi.adapter,
|
2013-05-03 18:07:29 +00:00
|
|
|
hostdev->source.subsys.u.scsi.bus,
|
|
|
|
hostdev->source.subsys.u.scsi.target,
|
|
|
|
hostdev->source.subsys.u.scsi.unit,
|
2014-01-08 14:51:29 +00:00
|
|
|
hostdev->readonly,
|
|
|
|
hostdev->shareable)))
|
2013-05-03 18:07:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
if ((tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) {
|
2014-03-01 06:28:56 +00:00
|
|
|
if (virSCSIDeviceSetUsedBy(tmp, QEMU_DRIVER_NAME, def->name) < 0) {
|
2014-01-29 17:22:42 +00:00
|
|
|
virSCSIDeviceFree(scsi);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-05-03 18:07:29 +00:00
|
|
|
virSCSIDeviceFree(scsi);
|
2014-01-29 17:22:42 +00:00
|
|
|
} else {
|
2014-03-01 06:28:56 +00:00
|
|
|
if (virSCSIDeviceSetUsedBy(scsi, QEMU_DRIVER_NAME, def->name) < 0 ||
|
2014-03-05 09:58:54 +00:00
|
|
|
virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0) {
|
2014-01-29 17:22:42 +00:00
|
|
|
virSCSIDeviceFree(scsi);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-05-03 18:07:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectUnlock(mgr->activeScsiHostdevs);
|
2013-05-03 18:07:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-03-06 04:47:50 +00:00
|
|
|
int
|
|
|
|
qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver,
|
|
|
|
virDomainDefPtr def)
|
|
|
|
{
|
|
|
|
virHostdevManagerPtr mgr = driver->hostdevMgr;
|
|
|
|
|
|
|
|
if (!def->nhostdevs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return virHostdevUpdateActiveScsiHostdevs(mgr, def);
|
|
|
|
}
|
|
|
|
|
2013-10-08 13:47:36 +00:00
|
|
|
|
qemu: default to vfio for nodedev-detach
This patch resolves:
https://bugzilla.redhat.com/show_bug.cgi?id=1035188
Commit f094aaac48a6 changed the PCI device assignment in qemu domains
to default to using VFIO rather than legacy KVM device assignment
(when VFIO is available). It didn't change which driver was used by
default for virNodeDeviceDetachFlags(), though, so that API (and the
virsh nodedev-detach command) was still binding to the pci-stub
driver, used by legacy KVM assignment, by default.
This patch publicizes (only within the qemu module, though, so no
additions to the symbol exports are needed) the functions that check
for presence of KVM and VFIO device assignment, then uses those
functions to decide what to do when no driver is specified for
virNodeDeviceDetachFlags(); if the vfio driver is loaded, the device
will be bound to vfio-pci, or if legacy KVM assignment is supported on
this system, the device will be bound to pci-stub; if neither method
is available, the detach will fail.
2013-11-29 11:19:26 +00:00
|
|
|
bool
|
2013-09-19 15:01:17 +00:00
|
|
|
qemuHostdevHostSupportsPassthroughVFIO(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>
|
qemu: default to vfio for nodedev-detach
This patch resolves:
https://bugzilla.redhat.com/show_bug.cgi?id=1035188
Commit f094aaac48a6 changed the PCI device assignment in qemu domains
to default to using VFIO rather than legacy KVM device assignment
(when VFIO is available). It didn't change which driver was used by
default for virNodeDeviceDetachFlags(), though, so that API (and the
virsh nodedev-detach command) was still binding to the pci-stub
driver, used by legacy KVM assignment, by default.
This patch publicizes (only within the qemu module, though, so no
additions to the symbol exports are needed) the functions that check
for presence of KVM and VFIO device assignment, then uses those
functions to decide what to do when no driver is specified for
virNodeDeviceDetachFlags(); if the vfio driver is loaded, the device
will be bound to vfio-pci, or if legacy KVM assignment is supported on
this system, the device will be bound to pci-stub; if neither method
is available, the detach will fail.
2013-11-29 11:19:26 +00:00
|
|
|
bool
|
2013-09-19 15:01:17 +00:00
|
|
|
qemuHostdevHostSupportsPassthroughLegacy(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
|
qemu: default to vfio for nodedev-detach
This patch resolves:
https://bugzilla.redhat.com/show_bug.cgi?id=1035188
Commit f094aaac48a6 changed the PCI device assignment in qemu domains
to default to using VFIO rather than legacy KVM device assignment
(when VFIO is available). It didn't change which driver was used by
default for virNodeDeviceDetachFlags(), though, so that API (and the
virsh nodedev-detach command) was still binding to the pci-stub
driver, used by legacy KVM assignment, by default.
This patch publicizes (only within the qemu module, though, so no
additions to the symbol exports are needed) the functions that check
for presence of KVM and VFIO device assignment, then uses those
functions to decide what to do when no driver is specified for
virNodeDeviceDetachFlags(); if the vfio driver is loaded, the device
will be bound to vfio-pci, or if legacy KVM assignment is supported on
this system, the device will be bound to pci-stub; if neither method
is available, the detach will fail.
2013-11-29 11:19:26 +00:00
|
|
|
bool
|
2013-09-19 15:01:17 +00:00
|
|
|
qemuHostdevHostSupportsPassthroughLegacy(void)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs,
|
2013-09-20 08:39:51 +00:00
|
|
|
size_t nhostdevs,
|
|
|
|
virQEMUCapsPtr qemuCaps)
|
2013-09-19 15:01:17 +00:00
|
|
|
{
|
|
|
|
bool supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy();
|
|
|
|
bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO();
|
|
|
|
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) {
|
2013-09-20 08:39:51 +00:00
|
|
|
case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
|
|
|
|
if (supportsPassthroughVFIO &&
|
|
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) {
|
|
|
|
*backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO;
|
|
|
|
} else if (supportsPassthroughKVM &&
|
|
|
|
(virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE) ||
|
|
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))) {
|
|
|
|
*backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM;
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("host doesn't support passthrough of "
|
|
|
|
"host PCI devices"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2013-09-19 15:01:17 +00:00
|
|
|
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_KVM:
|
|
|
|
if (!supportsPassthroughKVM) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("host doesn't support legacy PCI passthrough"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-03-05 11:37:51 +00:00
|
|
|
int
|
|
|
|
qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver,
|
|
|
|
const char *name,
|
|
|
|
const unsigned char *uuid,
|
|
|
|
virDomainHostdevDefPtr *hostdevs,
|
|
|
|
int nhostdevs,
|
|
|
|
virQEMUCapsPtr qemuCaps,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
|
|
|
|
|
|
if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs, qemuCaps))
|
|
|
|
goto out;
|
|
|
|
|
2014-03-05 11:47:40 +00:00
|
|
|
ret = virHostdevPreparePCIDevices(hostdev_mgr, QEMU_DRIVER_NAME,
|
|
|
|
name, uuid, hostdevs,
|
2014-03-05 11:37:51 +00:00
|
|
|
nhostdevs, flags);
|
2014-03-05 09:58:54 +00:00
|
|
|
out:
|
2010-12-16 16:10:54 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-10-08 13:47:36 +00:00
|
|
|
|
2014-03-05 06:46:40 +00:00
|
|
|
static int
|
2014-03-05 09:58:54 +00:00
|
|
|
qemuPrepareHostdevUSBDevices(virHostdevManagerPtr mgr,
|
2011-12-21 17:58:29 +00:00
|
|
|
const char *name,
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDeviceListPtr list)
|
2010-12-16 16:10:54 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i, j;
|
2012-05-06 14:45:05 +00:00
|
|
|
unsigned int count;
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDevicePtr tmp;
|
2012-05-06 14:45:05 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectLock(mgr->activeUsbHostdevs);
|
2013-01-14 22:11:44 +00:00
|
|
|
count = virUSBDeviceListCount(list);
|
2012-05-06 14:45:05 +00:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDevicePtr usb = virUSBDeviceListGet(list, i);
|
2014-03-05 09:58:54 +00:00
|
|
|
if ((tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb))) {
|
2014-03-01 06:28:56 +00:00
|
|
|
const char *other_drvname;
|
|
|
|
const char *other_domname;
|
2012-05-06 14:45:05 +00:00
|
|
|
|
2014-03-01 06:28:56 +00:00
|
|
|
virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_domname);
|
|
|
|
if (other_drvname && other_domname)
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
2014-03-01 06:28:56 +00:00
|
|
|
_("USB device %s is in use by "
|
|
|
|
"driver %s, domain %s"),
|
|
|
|
virUSBDeviceGetName(tmp),
|
|
|
|
other_drvname, other_domname);
|
2012-05-06 14:45:05 +00:00
|
|
|
else
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("USB device %s is already in use"),
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDeviceGetName(tmp));
|
2012-05-16 14:42:02 +00:00
|
|
|
goto error;
|
2012-05-06 14:45:05 +00:00
|
|
|
}
|
|
|
|
|
2014-03-01 06:28:56 +00:00
|
|
|
virUSBDeviceSetUsedBy(usb, QEMU_DRIVER_NAME, name);
|
2012-05-06 14:45:05 +00:00
|
|
|
VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs",
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name);
|
2012-05-06 14:45:05 +00:00
|
|
|
/*
|
|
|
|
* The caller is responsible to steal these usb devices
|
2013-01-14 22:11:44 +00:00
|
|
|
* from the virUSBDeviceList that passed in on success,
|
2012-05-06 14:45:05 +00:00
|
|
|
* perform rollback on failure.
|
|
|
|
*/
|
2014-03-05 09:58:54 +00:00
|
|
|
if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0)
|
2012-05-16 14:42:02 +00:00
|
|
|
goto error;
|
2012-05-06 14:45:05 +00:00
|
|
|
}
|
2013-01-16 12:09:58 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectUnlock(mgr->activeUsbHostdevs);
|
2012-05-06 14:45:05 +00:00
|
|
|
return 0;
|
2012-05-16 14:42:02 +00:00
|
|
|
|
|
|
|
error:
|
|
|
|
for (j = 0; j < i; j++) {
|
2013-01-14 22:11:44 +00:00
|
|
|
tmp = virUSBDeviceListGet(list, i);
|
2014-03-05 09:58:54 +00:00
|
|
|
virUSBDeviceListSteal(mgr->activeUsbHostdevs, tmp);
|
2012-05-16 14:42:02 +00:00
|
|
|
}
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectUnlock(mgr->activeUsbHostdevs);
|
2012-05-16 14:42:02 +00:00
|
|
|
return -1;
|
2012-05-06 14:45:05 +00:00
|
|
|
}
|
|
|
|
|
2013-10-08 13:47:36 +00:00
|
|
|
|
2014-03-05 06:46:40 +00:00
|
|
|
static int
|
2012-10-03 14:57:28 +00:00
|
|
|
qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
|
|
|
|
bool mandatory,
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDevicePtr *usb)
|
2012-10-03 11:55:35 +00:00
|
|
|
{
|
|
|
|
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;
|
2012-10-09 11:15:46 +00:00
|
|
|
bool autoAddress = hostdev->source.subsys.u.usb.autoAddress;
|
2012-10-03 14:57:28 +00:00
|
|
|
int rc;
|
2012-10-03 11:55:35 +00:00
|
|
|
|
2012-10-03 14:57:28 +00:00
|
|
|
*usb = NULL;
|
2012-10-03 11:55:35 +00:00
|
|
|
|
2012-10-03 14:57:28 +00:00
|
|
|
if (vendor && bus) {
|
2013-01-14 22:11:44 +00:00
|
|
|
rc = virUSBDeviceFind(vendor, product, bus, device,
|
|
|
|
NULL,
|
|
|
|
autoAddress ? false : mandatory,
|
|
|
|
usb);
|
2012-10-09 11:15:46 +00:00
|
|
|
if (rc < 0) {
|
2012-10-03 14:57:28 +00:00
|
|
|
return -1;
|
2012-10-09 11:15:46 +00:00
|
|
|
} 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) {
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDeviceListPtr devs;
|
2012-10-03 11:55:35 +00:00
|
|
|
|
2013-01-14 22:11:44 +00:00
|
|
|
rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs);
|
2012-10-03 14:57:28 +00:00
|
|
|
if (rc < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (rc == 1) {
|
2013-01-14 22:11:44 +00:00
|
|
|
*usb = virUSBDeviceListGet(devs, 0);
|
|
|
|
virUSBDeviceListSteal(devs, *usb);
|
2012-10-03 11:55:35 +00:00
|
|
|
}
|
2013-01-16 11:49:54 +00:00
|
|
|
virObjectUnref(devs);
|
2012-10-03 11:55:35 +00:00
|
|
|
|
2012-10-03 14:57:28 +00:00
|
|
|
if (rc == 0) {
|
|
|
|
goto out;
|
|
|
|
} else if (rc > 1) {
|
2012-10-09 11:15:46 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-10-03 14:57:28 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2012-10-03 11:55:35 +00:00
|
|
|
|
2013-01-14 22:11:44 +00:00
|
|
|
hostdev->source.subsys.u.usb.bus = virUSBDeviceGetBus(*usb);
|
|
|
|
hostdev->source.subsys.u.usb.device = virUSBDeviceGetDevno(*usb);
|
2012-10-09 11:15:46 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-10-03 11:55:35 +00:00
|
|
|
} else if (!vendor && bus) {
|
2013-01-14 22:11:44 +00:00
|
|
|
if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0)
|
2012-10-03 14:57:28 +00:00
|
|
|
return -1;
|
2012-10-03 11:55:35 +00:00
|
|
|
}
|
|
|
|
|
2012-10-03 14:57:28 +00:00
|
|
|
out:
|
|
|
|
if (!*usb)
|
2013-04-10 10:46:56 +00:00
|
|
|
hostdev->missing = true;
|
2012-10-03 14:57:28 +00:00
|
|
|
return 0;
|
2012-10-03 11:55:35 +00:00
|
|
|
}
|
|
|
|
|
2013-10-08 13:47:36 +00:00
|
|
|
|
2014-03-05 06:33:30 +00:00
|
|
|
int
|
2012-11-28 16:43:10 +00:00
|
|
|
qemuPrepareHostUSBDevices(virQEMUDriverPtr driver,
|
2014-03-05 06:20:50 +00:00
|
|
|
const char *name,
|
|
|
|
virDomainHostdevDefPtr *hostdevs,
|
|
|
|
int nhostdevs,
|
2014-03-05 11:12:04 +00:00
|
|
|
unsigned int flags)
|
2012-05-06 14:45:05 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
|
|
|
int ret = -1;
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDeviceListPtr list;
|
|
|
|
virUSBDevicePtr tmp;
|
2014-03-05 09:58:54 +00:00
|
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
2014-03-05 11:12:04 +00:00
|
|
|
bool coldBoot = !!(flags & VIR_HOSTDEV_COLD_BOOT);
|
2011-12-21 17:58:29 +00:00
|
|
|
|
|
|
|
/* 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
|
2014-03-05 11:37:51 +00:00
|
|
|
* loop. See virHostdevPreparePCIDevices()
|
2011-12-21 17:58:29 +00:00
|
|
|
*/
|
2013-01-14 22:11:44 +00:00
|
|
|
if (!(list = virUSBDeviceListNew()))
|
2011-12-21 17:58:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-05-16 14:42:02 +00:00
|
|
|
/* Loop 1: build temporary list
|
2011-12-21 17:58:29 +00:00
|
|
|
*/
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < nhostdevs; i++) {
|
2011-12-21 17:58:29 +00:00
|
|
|
virDomainHostdevDefPtr hostdev = hostdevs[i];
|
2012-10-04 14:18:16 +00:00
|
|
|
bool required = true;
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDevicePtr usb;
|
2010-12-16 16:10:54 +00:00
|
|
|
|
|
|
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
|
|
continue;
|
|
|
|
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
|
|
|
|
continue;
|
|
|
|
|
2012-10-04 14:18:16 +00:00
|
|
|
if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL ||
|
|
|
|
(hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE &&
|
|
|
|
!coldBoot))
|
|
|
|
required = false;
|
|
|
|
|
|
|
|
if (qemuFindHostdevUSBDevice(hostdev, required, &usb) < 0)
|
2012-05-06 14:45:05 +00:00
|
|
|
goto cleanup;
|
2011-12-21 17:58:29 +00:00
|
|
|
|
2013-01-14 22:11:44 +00:00
|
|
|
if (usb && virUSBDeviceListAdd(list, usb) < 0) {
|
|
|
|
virUSBDeviceFree(usb);
|
2012-05-06 14:45:05 +00:00
|
|
|
goto cleanup;
|
2010-12-16 16:10:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-06 14:45:05 +00:00
|
|
|
/* Mark devices in temporary list as used by @name
|
2011-12-21 17:58:29 +00:00
|
|
|
* and add them do driver list. However, if something goes
|
|
|
|
* wrong, perform rollback.
|
|
|
|
*/
|
2014-03-05 09:58:54 +00:00
|
|
|
if (qemuPrepareHostdevUSBDevices(hostdev_mgr, name, list) < 0)
|
2012-05-16 14:42:02 +00:00
|
|
|
goto cleanup;
|
2011-12-21 17:58:29 +00:00
|
|
|
|
2012-05-06 14:45:05 +00:00
|
|
|
/* Loop 2: Temporary list was successfully merged with
|
2011-12-21 17:58:29 +00:00
|
|
|
* driver list, so steal all items to avoid freeing them
|
|
|
|
* in cleanup label.
|
|
|
|
*/
|
2013-01-14 22:11:44 +00:00
|
|
|
while (virUSBDeviceListCount(list) > 0) {
|
|
|
|
tmp = virUSBDeviceListGet(list, 0);
|
|
|
|
virUSBDeviceListSteal(list, tmp);
|
2011-12-21 17:58:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2013-01-16 11:49:54 +00:00
|
|
|
virObjectUnref(list);
|
2011-12-21 17:58:29 +00:00
|
|
|
return ret;
|
2010-12-16 16:10:54 +00:00
|
|
|
}
|
|
|
|
|
2013-10-08 13:47:36 +00:00
|
|
|
|
2013-05-03 18:07:29 +00:00
|
|
|
int
|
|
|
|
qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver,
|
|
|
|
const char *name,
|
|
|
|
virDomainHostdevDefPtr *hostdevs,
|
|
|
|
int nhostdevs)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i, j;
|
|
|
|
int count;
|
2013-05-03 18:07:29 +00:00
|
|
|
virSCSIDeviceListPtr list;
|
|
|
|
virSCSIDevicePtr tmp;
|
2014-03-05 09:58:54 +00:00
|
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
2013-05-03 18:07:29 +00:00
|
|
|
|
2013-05-03 18:07:38 +00:00
|
|
|
/* Loop 1: Add the shared scsi host device to shared device
|
|
|
|
* table.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < nhostdevs; i++) {
|
|
|
|
virDomainDeviceDef dev;
|
|
|
|
|
|
|
|
dev.type = VIR_DOMAIN_DEVICE_HOSTDEV;
|
|
|
|
dev.data.hostdev = hostdevs[i];
|
|
|
|
|
|
|
|
if (qemuAddSharedDevice(driver, &dev, name) < 0)
|
|
|
|
return -1;
|
2013-05-03 18:07:43 +00:00
|
|
|
|
|
|
|
if (qemuSetUnprivSGIO(&dev) < 0)
|
|
|
|
return -1;
|
2013-05-03 18:07:38 +00:00
|
|
|
}
|
|
|
|
|
2013-05-03 18:07:29 +00:00
|
|
|
/* To prevent situation where SCSI device is assigned to two domains
|
|
|
|
* we need to keep a list of currently assigned SCSI devices.
|
|
|
|
* This is done in several loops which cannot be joined into one big
|
2014-03-05 11:37:51 +00:00
|
|
|
* loop. See virHostdevPreparePCIDevices()
|
2013-05-03 18:07:29 +00:00
|
|
|
*/
|
|
|
|
if (!(list = virSCSIDeviceListNew()))
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-03 18:07:38 +00:00
|
|
|
/* Loop 2: build temporary list */
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < nhostdevs; i++) {
|
2013-05-03 18:07:29 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-01-30 07:05:59 +00:00
|
|
|
if (!(scsi = virSCSIDeviceNew(NULL,
|
|
|
|
hostdev->source.subsys.u.scsi.adapter,
|
2013-05-03 18:07:29 +00:00
|
|
|
hostdev->source.subsys.u.scsi.bus,
|
|
|
|
hostdev->source.subsys.u.scsi.target,
|
|
|
|
hostdev->source.subsys.u.scsi.unit,
|
2014-01-08 14:51:29 +00:00
|
|
|
hostdev->readonly,
|
|
|
|
hostdev->shareable)))
|
2013-05-03 18:07:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) {
|
|
|
|
virSCSIDeviceFree(scsi);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-03 18:07:38 +00:00
|
|
|
/* Loop 3: Mark devices in temporary list as used by @name
|
2013-05-03 18:07:29 +00:00
|
|
|
* and add them to driver list. However, if something goes
|
|
|
|
* wrong, perform rollback.
|
|
|
|
*/
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectLock(hostdev_mgr->activeScsiHostdevs);
|
2013-05-03 18:07:29 +00:00
|
|
|
count = virSCSIDeviceListCount(list);
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i);
|
2014-03-05 09:58:54 +00:00
|
|
|
if ((tmp = virSCSIDeviceListFind(hostdev_mgr->activeScsiHostdevs,
|
|
|
|
scsi))) {
|
2014-01-29 17:22:42 +00:00
|
|
|
bool scsi_shareable = virSCSIDeviceGetShareable(scsi);
|
|
|
|
bool tmp_shareable = virSCSIDeviceGetShareable(tmp);
|
2013-05-03 18:07:29 +00:00
|
|
|
|
2014-01-29 17:22:42 +00:00
|
|
|
if (!(scsi_shareable && tmp_shareable)) {
|
2013-05-03 18:07:29 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
2014-01-29 17:22:42 +00:00
|
|
|
_("SCSI device %s is already in use by "
|
|
|
|
"other domain(s) as '%s'"),
|
2014-01-30 08:47:19 +00:00
|
|
|
virSCSIDeviceGetName(tmp),
|
|
|
|
tmp_shareable ? "shareable" : "non-shareable");
|
2014-01-29 17:22:42 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2013-05-03 18:07:29 +00:00
|
|
|
|
2014-03-01 06:28:56 +00:00
|
|
|
if (virSCSIDeviceSetUsedBy(tmp, QEMU_DRIVER_NAME, name) < 0)
|
2014-01-29 17:22:42 +00:00
|
|
|
goto error;
|
|
|
|
} else {
|
2014-03-01 06:28:56 +00:00
|
|
|
if (virSCSIDeviceSetUsedBy(scsi, QEMU_DRIVER_NAME, name) < 0)
|
2014-01-29 17:22:42 +00:00
|
|
|
goto error;
|
2013-05-03 18:07:29 +00:00
|
|
|
|
2014-01-29 17:22:42 +00:00
|
|
|
VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi));
|
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
if (virSCSIDeviceListAdd(hostdev_mgr->activeScsiHostdevs, scsi) < 0)
|
2014-01-29 17:22:42 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2013-05-03 18:07:29 +00:00
|
|
|
}
|
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectUnlock(hostdev_mgr->activeScsiHostdevs);
|
2013-05-03 18:07:29 +00:00
|
|
|
|
2013-05-03 18:07:38 +00:00
|
|
|
/* Loop 4: Temporary list was successfully merged with
|
2013-05-03 18:07:29 +00:00
|
|
|
* driver list, so steal all items to avoid freeing them
|
|
|
|
* when freeing temporary list.
|
|
|
|
*/
|
|
|
|
while (virSCSIDeviceListCount(list) > 0) {
|
|
|
|
tmp = virSCSIDeviceListGet(list, 0);
|
|
|
|
virSCSIDeviceListSteal(list, tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
virObjectUnref(list);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
for (j = 0; j < i; j++) {
|
|
|
|
tmp = virSCSIDeviceListGet(list, i);
|
2014-03-05 09:58:54 +00:00
|
|
|
virSCSIDeviceListSteal(hostdev_mgr->activeScsiHostdevs, tmp);
|
2013-05-03 18:07:29 +00:00
|
|
|
}
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectUnlock(hostdev_mgr->activeScsiHostdevs);
|
2013-05-03 18:07:29 +00:00
|
|
|
cleanup:
|
|
|
|
virObjectUnref(list);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-10-08 13:47:36 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
qemuPrepareHostDevices(virQEMUDriverPtr driver,
|
|
|
|
virDomainDefPtr def,
|
2013-09-20 08:39:51 +00:00
|
|
|
virQEMUCapsPtr qemuCaps,
|
2014-03-05 10:56:26 +00:00
|
|
|
unsigned int flags)
|
2010-12-16 16:10:54 +00:00
|
|
|
{
|
|
|
|
if (!def->nhostdevs)
|
|
|
|
return 0;
|
|
|
|
|
2013-01-16 12:09:58 +00:00
|
|
|
if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid,
|
2013-09-20 08:39:51 +00:00
|
|
|
def->hostdevs, def->nhostdevs,
|
2014-03-05 10:56:26 +00:00
|
|
|
qemuCaps, flags) < 0)
|
2010-12-16 16:10:54 +00:00
|
|
|
return -1;
|
|
|
|
|
2014-03-05 06:20:50 +00:00
|
|
|
if (qemuPrepareHostUSBDevices(driver, def->name,
|
2014-03-05 11:12:04 +00:00
|
|
|
def->hostdevs, def->nhostdevs, flags) < 0)
|
2010-12-16 16:10:54 +00:00
|
|
|
return -1;
|
|
|
|
|
2013-05-03 18:07:29 +00:00
|
|
|
if (qemuPrepareHostdevSCSIDevices(driver, def->name,
|
|
|
|
def->hostdevs, def->nhostdevs) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2010-12-16 16:10:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-05 11:53:51 +00:00
|
|
|
void
|
|
|
|
qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver,
|
|
|
|
const char *name,
|
|
|
|
virDomainHostdevDefPtr *hostdevs,
|
|
|
|
int nhostdevs)
|
|
|
|
{
|
|
|
|
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
|
|
|
|
char *oldStateDir = cfg->stateDir;
|
|
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
|
|
|
2014-03-05 11:55:45 +00:00
|
|
|
virHostdevReAttachPCIDevices(hostdev_mgr, QEMU_DRIVER_NAME, name,
|
2014-03-05 11:53:51 +00:00
|
|
|
hostdevs, nhostdevs, oldStateDir);
|
|
|
|
|
2013-01-10 21:03:14 +00:00
|
|
|
virObjectUnref(cfg);
|
2010-12-16 16:10:54 +00:00
|
|
|
}
|
|
|
|
|
2013-10-08 13:47:36 +00:00
|
|
|
|
2014-03-05 06:33:30 +00:00
|
|
|
void
|
2012-11-28 16:43:10 +00:00
|
|
|
qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver,
|
2012-03-26 14:40:01 +00:00
|
|
|
const char *name,
|
|
|
|
virDomainHostdevDefPtr *hostdevs,
|
|
|
|
int nhostdevs)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2014-03-05 09:58:54 +00:00
|
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
2012-03-26 14:40:01 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectLock(hostdev_mgr->activeUsbHostdevs);
|
2012-03-26 14:40:01 +00:00
|
|
|
for (i = 0; i < nhostdevs; i++) {
|
|
|
|
virDomainHostdevDefPtr hostdev = hostdevs[i];
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDevicePtr usb, tmp;
|
2014-03-01 06:28:56 +00:00
|
|
|
const char *usedby_drvname;
|
|
|
|
const char *usedby_domname;
|
2012-03-26 14:40:01 +00:00
|
|
|
|
|
|
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
|
|
continue;
|
|
|
|
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
|
|
|
|
continue;
|
2012-10-04 14:18:16 +00:00
|
|
|
if (hostdev->missing)
|
|
|
|
continue;
|
2012-03-26 14:40:01 +00:00
|
|
|
|
2013-01-14 22:11:44 +00:00
|
|
|
usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus,
|
|
|
|
hostdev->source.subsys.u.usb.device,
|
|
|
|
NULL);
|
2012-03-26 14:40:01 +00:00
|
|
|
|
|
|
|
if (!usb) {
|
|
|
|
VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s",
|
|
|
|
hostdev->source.subsys.u.usb.bus,
|
|
|
|
hostdev->source.subsys.u.usb.device,
|
|
|
|
name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete only those USB devices which belongs
|
|
|
|
* to domain @name because qemuProcessStart() might
|
|
|
|
* have failed because USB device is already taken.
|
|
|
|
* Therefore we want to steal only those devices from
|
|
|
|
* the list which were taken by @name */
|
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
tmp = virUSBDeviceListFind(hostdev_mgr->activeUsbHostdevs, usb);
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDeviceFree(usb);
|
2012-03-26 14:40:01 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-03-01 06:28:56 +00:00
|
|
|
virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname);
|
|
|
|
if (STREQ_NULLABLE(QEMU_DRIVER_NAME, usedby_drvname) &&
|
|
|
|
STREQ_NULLABLE(name, usedby_domname)) {
|
2012-03-26 14:40:01 +00:00
|
|
|
VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs",
|
|
|
|
hostdev->source.subsys.u.usb.bus,
|
|
|
|
hostdev->source.subsys.u.usb.device,
|
|
|
|
name);
|
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virUSBDeviceListDel(hostdev_mgr->activeUsbHostdevs, tmp);
|
2012-03-26 14:40:01 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectUnlock(hostdev_mgr->activeUsbHostdevs);
|
2012-03-26 14:40:01 +00:00
|
|
|
}
|
2010-12-16 16:10:54 +00:00
|
|
|
|
2013-10-08 13:47:36 +00:00
|
|
|
|
2013-05-03 18:07:29 +00:00
|
|
|
void
|
|
|
|
qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver,
|
|
|
|
const char *name,
|
|
|
|
virDomainHostdevDefPtr *hostdevs,
|
|
|
|
int nhostdevs)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2014-03-05 09:58:54 +00:00
|
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
2013-05-03 18:07:29 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectLock(hostdev_mgr->activeScsiHostdevs);
|
2013-05-03 18:07:29 +00:00
|
|
|
for (i = 0; i < nhostdevs; i++) {
|
|
|
|
virDomainHostdevDefPtr hostdev = hostdevs[i];
|
2014-01-29 17:22:42 +00:00
|
|
|
virSCSIDevicePtr scsi;
|
|
|
|
virSCSIDevicePtr tmp;
|
2013-05-03 18:07:38 +00:00
|
|
|
virDomainDeviceDef dev;
|
|
|
|
|
|
|
|
dev.type = VIR_DOMAIN_DEVICE_HOSTDEV;
|
|
|
|
dev.data.hostdev = hostdev;
|
|
|
|
|
|
|
|
ignore_value(qemuRemoveSharedDevice(driver, &dev, name));
|
2013-05-03 18:07:29 +00:00
|
|
|
|
|
|
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
|
|
|
|
hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
|
|
|
|
continue;
|
|
|
|
|
2014-01-30 07:05:59 +00:00
|
|
|
if (!(scsi = virSCSIDeviceNew(NULL,
|
|
|
|
hostdev->source.subsys.u.scsi.adapter,
|
2013-05-03 18:07:29 +00:00
|
|
|
hostdev->source.subsys.u.scsi.bus,
|
|
|
|
hostdev->source.subsys.u.scsi.target,
|
|
|
|
hostdev->source.subsys.u.scsi.unit,
|
2014-01-08 14:51:29 +00:00
|
|
|
hostdev->readonly,
|
|
|
|
hostdev->shareable))) {
|
2013-05-03 18:07:29 +00:00
|
|
|
VIR_WARN("Unable to reattach SCSI device %s:%d:%d:%d on domain %s",
|
|
|
|
hostdev->source.subsys.u.scsi.adapter,
|
|
|
|
hostdev->source.subsys.u.scsi.bus,
|
|
|
|
hostdev->source.subsys.u.scsi.target,
|
|
|
|
hostdev->source.subsys.u.scsi.unit,
|
|
|
|
name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only delete the devices which are marked as being used by @name,
|
|
|
|
* because qemuProcessStart could fail on the half way. */
|
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
if (!(tmp = virSCSIDeviceListFind(hostdev_mgr->activeScsiHostdevs, scsi))) {
|
2013-05-03 18:07:29 +00:00
|
|
|
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);
|
2014-01-29 17:22:42 +00:00
|
|
|
virSCSIDeviceFree(scsi);
|
2013-05-03 18:07:29 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-01-29 17:22:42 +00:00
|
|
|
VIR_DEBUG("Removing %s:%d:%d:%d dom=%s from activeScsiHostdevs",
|
|
|
|
hostdev->source.subsys.u.scsi.adapter,
|
|
|
|
hostdev->source.subsys.u.scsi.bus,
|
|
|
|
hostdev->source.subsys.u.scsi.target,
|
|
|
|
hostdev->source.subsys.u.scsi.unit,
|
|
|
|
name);
|
2013-05-03 18:07:29 +00:00
|
|
|
|
2014-03-05 09:58:54 +00:00
|
|
|
virSCSIDeviceListDel(hostdev_mgr->activeScsiHostdevs, tmp, QEMU_DRIVER_NAME, name);
|
2014-01-29 17:22:42 +00:00
|
|
|
virSCSIDeviceFree(scsi);
|
2013-05-03 18:07:29 +00:00
|
|
|
}
|
2014-03-05 09:58:54 +00:00
|
|
|
virObjectUnlock(hostdev_mgr->activeScsiHostdevs);
|
2013-05-03 18:07:29 +00:00
|
|
|
}
|
|
|
|
|
2013-10-08 13:47:36 +00:00
|
|
|
void
|
|
|
|
qemuDomainReAttachHostDevices(virQEMUDriverPtr driver,
|
|
|
|
virDomainDefPtr def)
|
2010-12-16 16:10:54 +00:00
|
|
|
{
|
|
|
|
if (!def->nhostdevs)
|
|
|
|
return;
|
|
|
|
|
2012-03-06 01:12:44 +00:00
|
|
|
qemuDomainReAttachHostdevDevices(driver, def->name, def->hostdevs,
|
|
|
|
def->nhostdevs);
|
2012-03-26 14:40:01 +00:00
|
|
|
|
|
|
|
qemuDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs,
|
|
|
|
def->nhostdevs);
|
2013-05-03 18:07:29 +00:00
|
|
|
|
|
|
|
qemuDomainReAttachHostScsiDevices(driver, def->name, def->hostdevs,
|
|
|
|
def->nhostdevs);
|
2010-12-16 16:10:54 +00:00
|
|
|
}
|