]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: Introduce activeScsiHostdevs list for scsi host devices
authorHan Cheng <hanc.fnst@cn.fujitsu.com>
Fri, 3 May 2013 18:07:29 +0000 (02:07 +0800)
committerOsier Yang <jyang@redhat.com>
Mon, 13 May 2013 13:26:06 +0000 (21:26 +0800)
Although virtio-scsi supports SCSI PR (Persistent Reservations),
the device on host may do not support it. To avoid losing data,
Just like PCI and USB pass through devices, only one live guest
is allowed per SCSI host pass through device."

Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com>
src/qemu/qemu_conf.h
src/qemu/qemu_driver.c
src/qemu/qemu_hostdev.c
src/qemu/qemu_hostdev.h
src/qemu/qemu_process.c

index 77d3d2f34936a173588d47c680849c36f67511e1..30a2a2209416eefc9dee0cd6c469e96cff4fc667 100644 (file)
@@ -36,6 +36,7 @@
 # include "security/security_manager.h"
 # include "virpci.h"
 # include "virusb.h"
+# include "virscsi.h"
 # include "cpu_conf.h"
 # include "driver.h"
 # include "virportallocator.h"
@@ -203,6 +204,7 @@ struct _virQEMUDriver {
     virPCIDeviceListPtr activePciHostdevs;
     virPCIDeviceListPtr inactivePciHostdevs;
     virUSBDeviceListPtr activeUsbHostdevs;
+    virSCSIDeviceListPtr activeScsiHostdevs;
 
     /* Immutable pointer. Unsafe APIs. XXX */
     virHashTablePtr sharedDisks;
index ab52693ee4da9f761d9c0b52191c71092f9d1422..9453c2284a5d2e5945feaa91504a55599e23b417 100644 (file)
@@ -675,6 +675,9 @@ qemuStateInitialize(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;
 
index e4af03666e0f151d3b67cd4a7b652e181031c3a2..d5f94d5f1d1bb6baa8f94929970fd0f8f65c3a02 100644 (file)
@@ -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
@@ -226,6 +227,47 @@ 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 ||
+            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;
+
+        virSCSIDeviceSetUsedBy(scsi, def->name);
+
+        if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) {
+            virSCSIDeviceFree(scsi);
+            goto cleanup;
+        }
+    }
+    ret = 0;
+
+cleanup:
+    virObjectUnlock(driver->activeScsiHostdevs);
+    return ret;
+}
+
 static int
 qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path)
 {
@@ -827,6 +869,107 @@ 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 ||
+            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;
+        }
+
+        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 to 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 %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi));
+
+        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
+     * 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);
+        virSCSIDeviceListSteal(driver->activeScsiHostdevs, tmp);
+    }
+    virObjectUnlock(driver->activeScsiHostdevs);
+cleanup:
+    virObjectUnref(list);
+    return -1;
+}
+
 int qemuPrepareHostDevices(virQEMUDriverPtr driver,
                            virDomainDefPtr def,
                            bool coldBoot)
@@ -841,6 +984,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;
 }
 
@@ -1025,6 +1172,69 @@ 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 ||
+            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,
+                     name);
+            continue;
+        }
+
+        /* Only delete the devices which are marked as being used by @name,
+         * because qemuProcessStart could fail on the half way. */
+
+        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)
 {
@@ -1036,4 +1246,7 @@ void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver,
 
     qemuDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs,
                                      def->nhostdevs);
+
+    qemuDomainReAttachHostScsiDevices(driver, def->name, def->hostdevs,
+                                      def->nhostdevs);
 }
index a1b8b9ea70da53657a8243d777d7bb10987b0091..327d4d567979bf4ab433c100114838da314208e5 100644 (file)
@@ -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,
index d7b0aeed3c164f7d087daac1a08c47fa03d49923..54a79df13fb9ceed09a165548fb0ae2c3ab1aa70 100644 (file)
@@ -2972,6 +2972,9 @@ qemuProcessReconnect(void *opaque)
     if (qemuUpdateActiveUsbHostdevs(driver, obj->def) < 0)
         goto error;
 
+    if (qemuUpdateActiveScsiHostdevs(driver, obj->def) < 0)
+        goto error;
+
     if (qemuInitCgroup(driver, obj, false) < 0)
         goto error;