Although virtio-scsi support SCSI PR, the device in host may do not
support this. To avoid losing data, we only allow one scsi hostdev be
passthrough to one guest.
Signed-off-by: Han Cheng <hanc.fnst(a)cn.fujitsu.com>
---
src/qemu/qemu_conf.h | 2 +
src/qemu/qemu_driver.c | 3 +
src/qemu/qemu_hostdev.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_hostdev.h | 10 ++
src/qemu/qemu_process.c | 3 +
5 files changed, 245 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index c5ddaad..28d9685 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -37,6 +37,7 @@
# include "vircgroup.h"
# include "virpci.h"
# include "virusb.h"
+# include "virscsi.h"
# include "cpu_conf.h"
# include "driver.h"
# include "virportallocator.h"
@@ -206,6 +207,7 @@ struct _virQEMUDriver {
virPCIDeviceListPtr activePciHostdevs;
virPCIDeviceListPtr inactivePciHostdevs;
virUSBDeviceListPtr activeUsbHostdevs;
+ virSCSIDeviceListPtr activeScsiHostdevs;
/* Immutable pointer. Unsafe APIs. XXX */
virHashTablePtr sharedDisks;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 96bf235..408a2cb 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -682,6 +682,9 @@ qemuStartup(bool privileged,
if ((qemu_driver->inactivePciHostdevs = virPCIDeviceListNew()) == NULL)
goto error;
+ if ((qemu_driver->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL)
+ goto error;
+
if (!(qemu_driver->sharedDisks = virHashCreate(30, qemuSharedDiskEntryFree)))
goto error;
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index bac38b5..96b3e8f 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -29,6 +29,7 @@
#include "viralloc.h"
#include "virpci.h"
#include "virusb.h"
+#include "virscsi.h"
#include "virnetdev.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -214,6 +215,59 @@ cleanup:
return ret;
}
+int
+qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver,
+ virDomainDefPtr def)
+{
+ virDomainHostdevDefPtr hostdev = NULL;
+ int i;
+ int ret = -1;
+
+ if (!def->nhostdevs)
+ return 0;
+
+ virObjectLock(driver->activeScsiHostdevs);
+ for (i = 0; i < def->nhostdevs; i++) {
+ virSCSIDevicePtr scsi = NULL;
+ hostdev = def->hostdevs[i];
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (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 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,
+ def->name);
+ continue;
+ }
+
+ virSCSIDeviceSetUsedBy(scsi, def->name);
+
+ if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) {
+ virSCSIDeviceFree(scsi);
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to add SCSI device %d:%d:%d:%d to
activeScsiHostdevs"),
+ virSCSIDeviceGetAdapter(scsi), virSCSIDeviceGetBus(scsi),
+ virSCSIDeviceGetTarget(scsi), virSCSIDeviceGetUnit(scsi));
+ goto cleanup;
+ }
+ }
+ ret = 0;
+
+cleanup:
+ virObjectUnlock(driver->activeScsiHostdevs);
+ return ret;
+}
+
static int
qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path)
{
@@ -811,6 +865,103 @@ cleanup:
return ret;
}
+int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver,
+ const char *name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ int i, j, count;
+ virSCSIDeviceListPtr list;
+ virSCSIDevicePtr tmp;
+
+ /* To prevent situation where SCSI device is assigned to two domains
+ * we need to keep a list of currently assigned SCSI devices.
+ * This is done in several loops which cannot be joined into one big
+ * loop. See qemuPrepareHostdevPCIDevices()
+ */
+ 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)
+ continue;
+ if (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;
+
+ if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) {
+ virSCSIDeviceFree(scsi);
+ goto cleanup;
+ }
+ }
+
+ /* Loop 2: Mark devices in temporary list as used by @name
+ * and add them do driver list. However, if something goes
+ * wrong, perform rollback.
+ */
+ virObjectLock(driver->activeScsiHostdevs);
+ count = virSCSIDeviceListCount(list);
+
+ for (i = 0; i < count; i++) {
+ virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i);
+ if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi))) {
+ const char *other_name = virSCSIDeviceGetUsedBy(tmp);
+
+ if (other_name)
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("SCSI device %s is in use by domain %s"),
+ virSCSIDeviceGetName(tmp), other_name);
+ else
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("SCSI device %s is already in use"),
+ virSCSIDeviceGetName(tmp));
+ goto error;
+ }
+
+ virSCSIDeviceSetUsedBy(scsi, name);
+ VIR_DEBUG("Adding %d:%d:%d:%d dom=%s to activeScsiHostdevs",
+ virSCSIDeviceGetAdapter(scsi), virSCSIDeviceGetBus(scsi),
+ virSCSIDeviceGetTarget(scsi), virSCSIDeviceGetUnit(scsi), name);
+ if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0)
+ goto error;
+ }
+
+ virObjectUnlock(driver->activeScsiHostdevs);
+
+ /* Loop 3: Temporary list was successfully merged with
+ * driver list, so steal all items to avoid freeing them
+ * in cleanup label.
+ */
+ while (virSCSIDeviceListCount(list) > 0) {
+ tmp = virSCSIDeviceListGet(list, 0);
+ virSCSIDeviceListSteal(list, tmp);
+ }
+
+ virObjectUnref(list);
+ return 0;
+
+error:
+ for (j = 0; j < i; j++) {
+ tmp = virSCSIDeviceListGet(list, i);
+ virSCSIDeviceListSteal(driver->activeScsiHostdevs, tmp);
+ }
+ virObjectUnlock(driver->activeScsiHostdevs);
+cleanup:
+ virObjectUnref(list);
+ return -1;
+}
+
int qemuPrepareHostDevices(virQEMUDriverPtr driver,
virDomainDefPtr def,
bool coldBoot)
@@ -825,6 +976,10 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver,
if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0)
return -1;
+ if (qemuPrepareHostdevSCSIDevices(driver, def->name,
+ def->hostdevs, def->nhostdevs) < 0)
+ return -1;
+
return 0;
}
@@ -1009,6 +1164,75 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver,
virObjectUnlock(driver->activeUsbHostdevs);
}
+void
+qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver,
+ const char *name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ int i;
+
+ virObjectLock(driver->activeScsiHostdevs);
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virSCSIDevicePtr scsi, tmp;
+ const char *used_by = NULL;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
+ continue;
+ if (hostdev->missing)
+ 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 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;
+ }
+
+ /* Delete only those SCSI devices which belongs
+ * to domain @name because qemuProcessStart() might
+ * have failed because SCSI device is already taken.
+ * Therefore we want to steal only those devices from
+ * the list which were taken by @name */
+
+ tmp = virSCSIDeviceListFind(driver->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;
+ }
+
+ used_by = virSCSIDeviceGetUsedBy(tmp);
+ if (STREQ_NULLABLE(used_by, name)) {
+ 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(driver->activeScsiHostdevs, tmp);
+ }
+ }
+ virObjectUnlock(driver->activeScsiHostdevs);
+}
+
void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver,
virDomainDefPtr def)
{
@@ -1020,4 +1244,7 @@ void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver,
qemuDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs,
def->nhostdevs);
+
+ qemuDomainReAttachHostScsiDevices(driver, def->name, def->hostdevs,
+ def->nhostdevs);
}
diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h
index a1b8b9e..327d4d5 100644
--- a/src/qemu/qemu_hostdev.h
+++ b/src/qemu/qemu_hostdev.h
@@ -31,6 +31,8 @@ int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver,
virDomainDefPtr def);
int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver,
virDomainDefPtr def);
+int qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver,
+ virDomainDefPtr def);
int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver,
const char *name,
const unsigned char *uuid,
@@ -42,9 +44,17 @@ int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
int qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver,
const char *name,
virUSBDeviceListPtr list);
+int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver,
+ const char *name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
int qemuPrepareHostDevices(virQEMUDriverPtr driver,
virDomainDefPtr def,
bool coldBoot);
+void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver,
+ const char *name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
void qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver);
void qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver,
const char *name,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 8c4bfb7..37168e9 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3004,6 +3004,9 @@ qemuProcessReconnect(void *opaque)
if (qemuUpdateActiveUsbHostdevs(driver, obj->def) < 0)
goto error;
+ if (qemuUpdateActiveScsiHostdevs(driver, obj->def) < 0)
+ goto error;
+
/* XXX: Need to change as long as lock is introduced for
* qemu_driver->sharedDisks.
*/
--
1.7.1