On 01/04/13 20:00, Han Cheng wrote:
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;
We have to have a share module to manage these list, Chunyan Liu is
doing it.
But it doesn't affect this patch, unless there is conflicts.
/* 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;
Can be more compact:
if (hostdev->model != 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 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
+ */
/* 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
s/do/to/
+ * 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;
These "continues" can be more compact.
+
+ 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.
s/because/for/
+ * Therefore we want to steal only those devices from
s/steal/delete/
+ * the list which were taken by @name */
s/taken/used/
How about:
/* Only delete the devices which are marked as being used by @name,
* because qemuProcessStart could fail on the half way.
*/
How about one specify (managed='no|yes') for scsi hostdev?
Osier