[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

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



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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]