[libvirt] [PATCH 04/10] qemu: Build qemu command line for scsi host device

Osier Yang jyang at redhat.com
Fri Apr 26 20:15:28 UTC 2013


From: Han Cheng <hanc.fnst at cn.fujitsu.com>

Except the scsi host device's controller is "lsilogic", mapping
between the libvirt attributes and scsi-generic properties is:

  libvirt     qemu
-----------------------------------------
  controller  bus ($libvirt_controller.0)
  bus         channel
  target      scsi-id
  unit        lun

For scsi host device with "lsilogic" controller, the mapping is:
('target (libvirt)' must be 0, as it's not used; 'unit (libvirt)
must <= 7).

  libvirt            qemu
----------------------------------------------------------
  controller && bus  bus ($libvirt_controller.$libvirt_bus)
  unit               scsi-id

Regardless of whether the controller is "lsilogic" or not, the
"bus (libvirt)" must be 0. (It's not good to hardcode/hard-check
limits of these attributes, and even worse, these limits are not
documented, one has to find out by either testing or reading the
qemu code, I'm looking forward to qemu expose limits like these
one day). For example, exposing "max_target", "max_lun" for megasas:

static const struct SCSIBusInfo megasas_scsi_info = {
    .tcq = true,
    .max_target = MFI_MAX_LD,
    .max_lun = 255,

    .transfer_data = megasas_xfer_complete,
    .get_sg_list = megasas_get_sg_list,
    .complete = megasas_command_complete,
    .cancel = megasas_command_cancel,
};

Example of the qemu command line (lsilogic controller):

  -drive file=/dev/sg2,if=none,id=drive-hostdev-scsi_host7-0-0-0 \
  -device scsi-generic,bus=scsi0.0,scsi-id=8,\
  drive=drive-hostdev-scsi_host7-0-0-0,id=hostdev-scsi_host7-0-0-0

Example of the qemu command line (virtio-scsi controller):

  -drive file=/dev/sg2,if=none,id=drive-hostdev-scsi_host7-0-0-0 \
  -device scsi-generic,bus=scsi0.0,channel=0,scsi-id=128,lun=128,\
  drive=drive-hostdev-scsi_host7-0-0-0,id=hostdev-scsi_host7-0-0-0

Signed-off-by: Han Cheng <hanc.fnst at cn.fujitsu.com>
Signed-off-by: Osier Yang <jyang at redhat.com>

---
v2.5 - v3:
  * Add support for all other controllers, but not only virtio-scsi
  * Add checking for "bus == 0"
  * Add checking for "target == 0" && "unit <= 7" for scsi host device
    which is on "lsilogic" controller.
  * Integrate xml2argv test from 10/10 of v2.5 into this patch
---
 src/qemu/qemu_command.c                            | 154 ++++++++++++++++++++-
 src/qemu/qemu_command.h                            |   6 +
 .../qemuxml2argv-hostdev-scsi-boot.args            |   9 ++
 .../qemuxml2argv-hostdev-scsi-boot.xml             |  34 +++++
 .../qemuxml2argv-hostdev-scsi-readonly.args        |   9 ++
 .../qemuxml2argv-hostdev-scsi-readonly.xml         |   4 +-
 tests/qemuxml2argvtest.c                           |   9 ++
 tests/qemuxml2xmltest.c                            |   1 +
 8 files changed, 221 insertions(+), 5 deletions(-)
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.args
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.xml
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.args

diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 6cca229..1398664 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -47,6 +47,7 @@
 #include "device_conf.h"
 #include "virstoragefile.h"
 #include "virtpm.h"
+#include "virscsi.h"
 
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -664,7 +665,16 @@ qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev
         }
     }
 
-    if (virAsprintf(&hostdev->info->alias, "hostdev%d", idx) < 0) {
+    if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
+        if (virAsprintf(&hostdev->info->alias, "hostdev-%s-%d-%d-%d",
+                        hostdev->source.subsys.u.scsi.adapter,
+                        hostdev->source.subsys.u.scsi.bus,
+                        hostdev->source.subsys.u.scsi.target,
+                        hostdev->source.subsys.u.scsi.unit) < 0) {
+            virReportOOMError();
+            return -1;
+        }
+    } else if (virAsprintf(&hostdev->info->alias, "hostdev%d", idx) < 0) {
         virReportOOMError();
         return -1;
     }
@@ -4408,6 +4418,110 @@ qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev)
     return ret;
 }
 
+char *
+qemuBuildSCSIHostdevDrvStr(virDomainHostdevDefPtr dev,
+                           virQEMUCapsPtr qemuCaps)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    char *sg = NULL;
+
+    if (!(sg = virSCSIDeviceGetDevStr(dev->source.subsys.u.scsi.adapter,
+                                      dev->source.subsys.u.scsi.bus,
+                                      dev->source.subsys.u.scsi.target,
+                                      dev->source.subsys.u.scsi.unit))) {
+        goto error;
+    }
+
+    virBufferAsprintf(&buf, "file=/dev/%s,if=none", sg);
+    virBufferAsprintf(&buf, ",id=%s-%s",
+                      virDomainDeviceAddressTypeToString(dev->info->type),
+                      dev->info->alias);
+
+    if (dev->readonly &&
+        virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY))
+        virBufferAsprintf(&buf, ",readonly=on");
+
+    if (virBufferError(&buf)) {
+        virReportOOMError();
+        goto error;
+    }
+
+    return virBufferContentAndReset(&buf);
+error:
+    VIR_FREE(sg);
+    virBufferFreeAndReset(&buf);
+    return NULL;
+}
+
+
+char *
+qemuBuildSCSIHostdevDevStr(virDomainDefPtr def,
+                           virDomainHostdevDefPtr dev,
+                           virQEMUCapsPtr qemuCaps)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    int model = -1;
+
+    model = virDomainDeviceFindControllerModel(def, dev->info,
+                                               VIR_DOMAIN_CONTROLLER_TYPE_SCSI);
+
+    if (qemuSetScsiControllerModel(def, qemuCaps, &model) < 0)
+        goto error;
+
+    if (dev->info->addr.drive.bus != 0) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("bus must be 0 for scsi host device"));
+        goto error;
+    }
+
+    if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) {
+        if (dev->info->addr.drive.target != 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("target must be 0 for scsi host device "
+                             "if its controller model is 'lsilogic'"));
+            goto error;
+        }
+
+        if (dev->info->addr.drive.unit > 7) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("unit must be not more than 7 for scsi host "
+                             "device if its controller model is 'lsilogic'"));
+            goto error;
+        }
+    }
+
+    virBufferAsprintf(&buf, "scsi-generic");
+
+    if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) {
+        virBufferAsprintf(&buf, ",bus=scsi%d.%d,scsi-id=%d",
+                          dev->info->addr.drive.controller,
+                          dev->info->addr.drive.bus,
+                          dev->info->addr.drive.unit);
+    } else {
+        virBufferAsprintf(&buf, ",bus=scsi%d.0,channel=%d,scsi-id=%d,lun=%d",
+                          dev->info->addr.drive.controller,
+                          dev->info->addr.drive.bus,
+                          dev->info->addr.drive.target,
+                          dev->info->addr.drive.unit);
+    }
+
+    virBufferAsprintf(&buf, ",drive=%s-%s,id=%s",
+                      virDomainDeviceAddressTypeToString(dev->info->type),
+                      dev->info->alias, dev->info->alias);
+
+    if (dev->info->bootIndex)
+        virBufferAsprintf(&buf, ",bootindex=%d", dev->info->bootIndex);
+
+    if (virBufferError(&buf)) {
+        virReportOOMError();
+        goto error;
+    }
+
+    return virBufferContentAndReset(&buf);
+error:
+    virBufferFreeAndReset(&buf);
+    return NULL;
+}
 
 
 /* This function outputs a -chardev command line option which describes only the
@@ -7661,10 +7775,11 @@ qemuBuildCommandLine(virConnectPtr conn,
         if (hostdev->info->bootIndex) {
             if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
                 (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI &&
-                 hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)) {
+                 hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
+                 hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)) {
                 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                                _("booting from assigned devices is only"
-                                 " supported for PCI and USB devices"));
+                                 " supported for PCI, USB and SCSI devices"));
                 goto error;
             } else {
                 if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI &&
@@ -7681,6 +7796,39 @@ qemuBuildCommandLine(virConnectPtr conn,
                                      " supported with this version of qemu"));
                     goto error;
                 }
+                if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI &&
+                    !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX)) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                                   _("booting from assigned SCSI devices is not"
+                                     " supported with this version of qemu"));
+                    goto error;
+                }
+            }
+        }
+
+        /* SCSI */
+        if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+            hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
+            if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE) &&
+                virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE) &&
+                virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC)) {
+                char *drvstr;
+
+                virCommandAddArg(cmd, "-drive");
+                if (!(drvstr = qemuBuildSCSIHostdevDrvStr(hostdev, qemuCaps)))
+                    goto error;
+                virCommandAddArg(cmd, drvstr);
+                VIR_FREE(drvstr);
+
+                virCommandAddArg(cmd, "-device");
+                if (!(devstr = qemuBuildSCSIHostdevDevStr(def, hostdev, qemuCaps)))
+                    goto error;
+                virCommandAddArg(cmd, devstr);
+                VIR_FREE(devstr);
+            } else {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                               _("SCSI passthrough is not supported by this version of qemu"));
+                goto error;
             }
         }
 
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 1789c20..fcf9892 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -139,6 +139,12 @@ char * qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev);
 char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
                                  virQEMUCapsPtr qemuCaps);
 
+char * qemuBuildSCSIHostdevDrvStr(virDomainHostdevDefPtr dev,
+                                  virQEMUCapsPtr qemuCaps);
+char * qemuBuildSCSIHostdevDevStr(virDomainDefPtr def,
+                                  virDomainHostdevDefPtr dev,
+                                  virQEMUCapsPtr qemuCaps);
+
 char * qemuBuildHubDevStr(virDomainHubDefPtr dev, virQEMUCapsPtr qemuCaps);
 char * qemuBuildRedirdevDevStr(virDomainDefPtr def,
                                virDomainRedirdevDefPtr dev,
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.args b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.args
new file mode 100644
index 0000000..cd22672
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.args
@@ -0,0 +1,9 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \
+pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -monitor \
+unix:/tmp/test-monitor,server,nowait -no-acpi \
+-device virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3 -usb \
+-drive file=/dev/HostVG/QEMUGuest2,if=none,id=drive-ide0-0-0 \
+-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \
+-drive file=/dev/sg0,if=none,id=drive-hostdev-scsi_host0-0-0-0 \
+-device scsi-generic,bus=scsi0.0,channel=0,scsi-id=4,lun=8,drive=drive-hostdev-scsi_host0-0-0-0,id=hostdev-scsi_host0-0-0-0,bootindex=1 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.xml
new file mode 100644
index 0000000..e3de719
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.xml
@@ -0,0 +1,34 @@
+<domain type='qemu'>
+  <name>QEMUGuest2</name>
+  <uuid>c7a5fdbd-edaf-9466-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219100</memory>
+  <currentMemory unit='KiB'>219100</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type arch='i686' machine='pc'>hvm</type>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu</emulator>
+    <disk type='block' device='disk'>
+      <source dev='/dev/HostVG/QEMUGuest2'/>
+      <target dev='hda' bus='ide'/>
+      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+    </disk>
+    <controller type='scsi' index='0' model='virtio-scsi'/>
+    <controller type='usb' index='0'/>
+    <controller type='ide' index='0'/>
+    <hostdev mode='subsystem' type='scsi' managed='yes'>
+      <source>
+        <adapter name='scsi_host0'/>
+        <address bus='0' target='0' unit='0'/>
+      </source>
+      <boot order='1'/>
+      <address type='drive' controller='0' bus='0' target='4' unit='8'/>
+    </hostdev>
+    <memballoon model='virtio'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.args b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.args
new file mode 100644
index 0000000..42fd60b
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.args
@@ -0,0 +1,9 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc \
+-m 214 -smp 1 -nographic -nodefconfig -nodefaults -monitor \
+unix:/tmp/test-monitor,server,nowait -no-acpi -boot c \
+-device lsi,id=scsi0,bus=pci.0,addr=0x3 -usb \
+-drive file=/dev/HostVG/QEMUGuest2,if=none,id=drive-ide0-0-0 \
+-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \
+-drive file=/dev/sg0,if=none,id=drive-hostdev-scsi_host0-0-0-0,readonly=on \
+-device scsi-generic,bus=scsi0.0,scsi-id=7,drive=drive-hostdev-scsi_host0-0-0-0,id=hostdev-scsi_host0-0-0-0 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml
index 11d1712..83936b1 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml
+++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml
@@ -19,7 +19,7 @@
       <target dev='hda' bus='ide'/>
       <address type='drive' controller='0' bus='0' target='0' unit='0'/>
     </disk>
-    <controller type='scsi' index='0' model='virtio-scsi'/>
+    <controller type='scsi' index='0'/>
     <controller type='usb' index='0'/>
     <controller type='ide' index='0'/>
     <hostdev mode='subsystem' type='scsi' managed='yes'>
@@ -28,7 +28,7 @@
         <address bus='0' target='0' unit='0'/>
       </source>
       <readonly/>
-      <address type='drive' controller='0' bus='0' target='4' unit='8'/>
+      <address type='drive' controller='0' bus='0' target='0' unit='7'/>
     </hostdev>
     <memballoon model='virtio'/>
   </devices>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index f40d002..93a5a9a 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -951,6 +951,15 @@ mymain(void)
     DO_TEST("tpm-passthrough", QEMU_CAPS_DEVICE,
             QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, QEMU_CAPS_DEVICE_TPM_TIS);
 
+    DO_TEST("hostdev-scsi-boot",
+            QEMU_CAPS_DRIVE, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG,
+            QEMU_CAPS_VIRTIO_SCSI, QEMU_CAPS_DEVICE_SCSI_GENERIC,
+            QEMU_CAPS_BOOTINDEX, QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX);
+    DO_TEST("hostdev-scsi-readonly",
+            QEMU_CAPS_DRIVE, QEMU_CAPS_DRIVE_READONLY,
+            QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG,
+            QEMU_CAPS_SCSI_LSI, QEMU_CAPS_DEVICE_SCSI_GENERIC);
+
     virObjectUnref(driver.config);
     virObjectUnref(driver.caps);
     virObjectUnref(driver.xmlopt);
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index a2727b0..bcd26df 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -231,6 +231,7 @@ mymain(void)
     DO_TEST("hostdev-usb-address");
     DO_TEST("hostdev-pci-address");
     DO_TEST("hostdev-scsi-readonly");
+    DO_TEST("hostdev-scsi-boot");
     DO_TEST("pci-rom");
 
     DO_TEST("encrypted-disk");
-- 
1.8.1.4




More information about the libvir-list mailing list