[libvirt] [PATCH v2 07/10] qemu: Basic management functions for scsi hostdev

Han Cheng hanc.fnst at cn.fujitsu.com
Mon Apr 1 12:00:59 UTC 2013


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 at 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




More information about the libvir-list mailing list