libvirt/src/qemu/qemu_hostdev.c

416 lines
13 KiB
C
Raw Normal View History

/*
* qemu_hostdev.c: QEMU hostdev management
*
* Copyright (C) 2006-2007, 2009-2013 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include "qemu_hostdev.h"
2012-12-12 17:59:27 +00:00
#include "virlog.h"
#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"
#include "virscsi.h"
#include "virnetdev.h"
#include "virfile.h"
#include "virhostdev.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
int
qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver,
virDomainDefPtr def)
{
virHostdevManagerPtr mgr = driver->hostdevMgr;
if (!def->nhostdevs)
return 0;
return virHostdevUpdateActivePciHostdevs(mgr, QEMU_DRIVER_NAME, def);
}
int
qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver,
virDomainDefPtr def)
{
virHostdevManagerPtr mgr = driver->hostdevMgr;
if (!def->nhostdevs)
return 0;
return virHostdevUpdateActiveUsbHostdevs(mgr, QEMU_DRIVER_NAME, def);
}
int
qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver,
virDomainDefPtr def)
{
virHostdevManagerPtr mgr = driver->hostdevMgr;
if (!def->nhostdevs)
return 0;
return virHostdevUpdateActiveScsiHostdevs(mgr, QEMU_DRIVER_NAME, def);
}
bool
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>
bool
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
bool
qemuHostdevHostSupportsPassthroughLegacy(void)
{
return false;
}
#endif
static bool
qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs,
size_t nhostdevs,
virQEMUCapsPtr qemuCaps)
{
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) {
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;
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;
}
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;
ret = virHostdevPreparePCIDevices(hostdev_mgr, QEMU_DRIVER_NAME,
name, uuid, hostdevs,
nhostdevs, flags);
out:
return ret;
}
int
qemuPrepareHostUSBDevices(virQEMUDriverPtr driver,
const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs,
unsigned int flags)
{
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
return virHostdevPrepareUSBDevices(hostdev_mgr, QEMU_DRIVER_NAME, name,
hostdevs, nhostdevs, flags);
}
int
qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver,
const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
{
size_t i;
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
/* Loop 1: Add the shared scsi host device to shared device
* table.
*/
for (i = 0; i < nhostdevs; i++) {
virDomainDeviceDef dev;
dev.type = VIR_DOMAIN_DEVICE_HOSTDEV;
dev.data.hostdev = hostdevs[i];
if (qemuAddSharedDevice(driver, &dev, name) < 0)
return -1;
if (qemuSetUnprivSGIO(&dev) < 0)
return -1;
}
return virHostdevPrepareSCSIDevices(hostdev_mgr, QEMU_DRIVER_NAME,
name, hostdevs, nhostdevs);
}
int
qemuPrepareHostDevices(virQEMUDriverPtr driver,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
unsigned int flags)
{
if (!def->nhostdevs)
return 0;
if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid,
def->hostdevs, def->nhostdevs,
qemuCaps, flags) < 0)
return -1;
if (qemuPrepareHostUSBDevices(driver, def->name,
def->hostdevs, def->nhostdevs, flags) < 0)
return -1;
if (qemuPrepareHostdevSCSIDevices(driver, def->name,
def->hostdevs, def->nhostdevs) < 0)
return -1;
return 0;
}
void
qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver,
const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
{
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
char *oldStateDir = cfg->stateDir;
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
virHostdevReAttachPCIDevices(hostdev_mgr, QEMU_DRIVER_NAME, name,
hostdevs, nhostdevs, oldStateDir);
virObjectUnref(cfg);
}
void
qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver,
const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
{
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
virHostdevReAttachUsbHostdevs(hostdev_mgr, QEMU_DRIVER_NAME,
name, hostdevs, nhostdevs);
}
void
qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver,
const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
{
size_t i;
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
virObjectLock(hostdev_mgr->activeScsiHostdevs);
for (i = 0; i < nhostdevs; i++) {
virDomainHostdevDefPtr hostdev = hostdevs[i];
qemu: Don't fail if the SCSI host device is shareable between domains It doesn't make sense to fail if the SCSI host device is specified as "shareable" explicitly between domains (NB, it works if and only if the device is specified as "shareable" for *all* domains, otherwise it fails). To fix the problem, this patch introduces an array for virSCSIDevice struct, which records all the names of domain which are using the device (note that the recorded domains must specify the device as shareable). And the change on the data struct brings on many subsequent changes in the code. Prior to this patch, the "shareable" tag didn't work as expected, it actually work like "non-shareable". So this patch also added notes in formatdomain.html to declare the fact. * src/util/virscsi.h: - Remove virSCSIDeviceGetUsedBy - Change definition of virSCSIDeviceGetUsedBy and virSCSIDeviceListDel - Add virSCSIDeviceIsAvailable * src/util/virscsi.c: - struct virSCSIDevice: Change "used_by" to be an array; Add "n_used_by" as the array count - virSCSIDeviceGetUsedBy: Removed - virSCSIDeviceFree: frees the "used_by" array - virSCSIDeviceSetUsedBy: Copy the domain name to avoid potential memory corruption - virSCSIDeviceIsAvailable: New - virSCSIDeviceListDel: Change the logic, for device which is already in the list, just remove the corresponding entry in "used_by". And since it's only used in one place, we can safely removing the code to find out the dev in the list first. - Copyright updating * src/libvirt_private.sys: - virSCSIDeviceGetUsedBy: Remove - virSCSIDeviceIsAvailable: New * src/qemu/qemu_hostdev.c: - qemuUpdateActiveScsiHostdevs: Check if the device existing before adding it to the list; - qemuPrepareHostdevSCSIDevices: Error out if the not all domains use the device as "shareable"; Also don't try to add the device to the activeScsiHostdevs list if it already there; And make more sensible error w.r.t the current "shareable" value in driver->activeScsiHostdevs. - qemuDomainReAttachHostScsiDevices: Change the logic according to the changes on helpers. Signed-off-by: Osier Yang <jyang@redhat.com>
2014-01-29 17:22:42 +00:00
virSCSIDevicePtr scsi;
virSCSIDevicePtr tmp;
virDomainDeviceDef dev;
dev.type = VIR_DOMAIN_DEVICE_HOSTDEV;
dev.data.hostdev = hostdev;
ignore_value(qemuRemoveSharedDevice(driver, &dev, name));
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
continue;
if (!(scsi = virSCSIDeviceNew(NULL,
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,
hostdev->shareable))) {
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. */
if (!(tmp = virSCSIDeviceListFind(hostdev_mgr->activeScsiHostdevs, scsi))) {
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);
qemu: Don't fail if the SCSI host device is shareable between domains It doesn't make sense to fail if the SCSI host device is specified as "shareable" explicitly between domains (NB, it works if and only if the device is specified as "shareable" for *all* domains, otherwise it fails). To fix the problem, this patch introduces an array for virSCSIDevice struct, which records all the names of domain which are using the device (note that the recorded domains must specify the device as shareable). And the change on the data struct brings on many subsequent changes in the code. Prior to this patch, the "shareable" tag didn't work as expected, it actually work like "non-shareable". So this patch also added notes in formatdomain.html to declare the fact. * src/util/virscsi.h: - Remove virSCSIDeviceGetUsedBy - Change definition of virSCSIDeviceGetUsedBy and virSCSIDeviceListDel - Add virSCSIDeviceIsAvailable * src/util/virscsi.c: - struct virSCSIDevice: Change "used_by" to be an array; Add "n_used_by" as the array count - virSCSIDeviceGetUsedBy: Removed - virSCSIDeviceFree: frees the "used_by" array - virSCSIDeviceSetUsedBy: Copy the domain name to avoid potential memory corruption - virSCSIDeviceIsAvailable: New - virSCSIDeviceListDel: Change the logic, for device which is already in the list, just remove the corresponding entry in "used_by". And since it's only used in one place, we can safely removing the code to find out the dev in the list first. - Copyright updating * src/libvirt_private.sys: - virSCSIDeviceGetUsedBy: Remove - virSCSIDeviceIsAvailable: New * src/qemu/qemu_hostdev.c: - qemuUpdateActiveScsiHostdevs: Check if the device existing before adding it to the list; - qemuPrepareHostdevSCSIDevices: Error out if the not all domains use the device as "shareable"; Also don't try to add the device to the activeScsiHostdevs list if it already there; And make more sensible error w.r.t the current "shareable" value in driver->activeScsiHostdevs. - qemuDomainReAttachHostScsiDevices: Change the logic according to the changes on helpers. Signed-off-by: Osier Yang <jyang@redhat.com>
2014-01-29 17:22:42 +00:00
virSCSIDeviceFree(scsi);
continue;
}
qemu: Don't fail if the SCSI host device is shareable between domains It doesn't make sense to fail if the SCSI host device is specified as "shareable" explicitly between domains (NB, it works if and only if the device is specified as "shareable" for *all* domains, otherwise it fails). To fix the problem, this patch introduces an array for virSCSIDevice struct, which records all the names of domain which are using the device (note that the recorded domains must specify the device as shareable). And the change on the data struct brings on many subsequent changes in the code. Prior to this patch, the "shareable" tag didn't work as expected, it actually work like "non-shareable". So this patch also added notes in formatdomain.html to declare the fact. * src/util/virscsi.h: - Remove virSCSIDeviceGetUsedBy - Change definition of virSCSIDeviceGetUsedBy and virSCSIDeviceListDel - Add virSCSIDeviceIsAvailable * src/util/virscsi.c: - struct virSCSIDevice: Change "used_by" to be an array; Add "n_used_by" as the array count - virSCSIDeviceGetUsedBy: Removed - virSCSIDeviceFree: frees the "used_by" array - virSCSIDeviceSetUsedBy: Copy the domain name to avoid potential memory corruption - virSCSIDeviceIsAvailable: New - virSCSIDeviceListDel: Change the logic, for device which is already in the list, just remove the corresponding entry in "used_by". And since it's only used in one place, we can safely removing the code to find out the dev in the list first. - Copyright updating * src/libvirt_private.sys: - virSCSIDeviceGetUsedBy: Remove - virSCSIDeviceIsAvailable: New * src/qemu/qemu_hostdev.c: - qemuUpdateActiveScsiHostdevs: Check if the device existing before adding it to the list; - qemuPrepareHostdevSCSIDevices: Error out if the not all domains use the device as "shareable"; Also don't try to add the device to the activeScsiHostdevs list if it already there; And make more sensible error w.r.t the current "shareable" value in driver->activeScsiHostdevs. - qemuDomainReAttachHostScsiDevices: Change the logic according to the changes on helpers. Signed-off-by: Osier Yang <jyang@redhat.com>
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);
virSCSIDeviceListDel(hostdev_mgr->activeScsiHostdevs, tmp, QEMU_DRIVER_NAME, name);
qemu: Don't fail if the SCSI host device is shareable between domains It doesn't make sense to fail if the SCSI host device is specified as "shareable" explicitly between domains (NB, it works if and only if the device is specified as "shareable" for *all* domains, otherwise it fails). To fix the problem, this patch introduces an array for virSCSIDevice struct, which records all the names of domain which are using the device (note that the recorded domains must specify the device as shareable). And the change on the data struct brings on many subsequent changes in the code. Prior to this patch, the "shareable" tag didn't work as expected, it actually work like "non-shareable". So this patch also added notes in formatdomain.html to declare the fact. * src/util/virscsi.h: - Remove virSCSIDeviceGetUsedBy - Change definition of virSCSIDeviceGetUsedBy and virSCSIDeviceListDel - Add virSCSIDeviceIsAvailable * src/util/virscsi.c: - struct virSCSIDevice: Change "used_by" to be an array; Add "n_used_by" as the array count - virSCSIDeviceGetUsedBy: Removed - virSCSIDeviceFree: frees the "used_by" array - virSCSIDeviceSetUsedBy: Copy the domain name to avoid potential memory corruption - virSCSIDeviceIsAvailable: New - virSCSIDeviceListDel: Change the logic, for device which is already in the list, just remove the corresponding entry in "used_by". And since it's only used in one place, we can safely removing the code to find out the dev in the list first. - Copyright updating * src/libvirt_private.sys: - virSCSIDeviceGetUsedBy: Remove - virSCSIDeviceIsAvailable: New * src/qemu/qemu_hostdev.c: - qemuUpdateActiveScsiHostdevs: Check if the device existing before adding it to the list; - qemuPrepareHostdevSCSIDevices: Error out if the not all domains use the device as "shareable"; Also don't try to add the device to the activeScsiHostdevs list if it already there; And make more sensible error w.r.t the current "shareable" value in driver->activeScsiHostdevs. - qemuDomainReAttachHostScsiDevices: Change the logic according to the changes on helpers. Signed-off-by: Osier Yang <jyang@redhat.com>
2014-01-29 17:22:42 +00:00
virSCSIDeviceFree(scsi);
}
virObjectUnlock(hostdev_mgr->activeScsiHostdevs);
}
void
qemuDomainReAttachHostDevices(virQEMUDriverPtr driver,
virDomainDefPtr def)
{
if (!def->nhostdevs)
return;
qemuDomainReAttachHostdevDevices(driver, def->name, def->hostdevs,
def->nhostdevs);
qemuDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs,
def->nhostdevs);
qemuDomainReAttachHostScsiDevices(driver, def->name, def->hostdevs,
def->nhostdevs);
}