Patchwork [35/35] scsi-disk: add scsi-block for device passthrough

login
register
mail settings
Submitter Paolo Bonzini
Date Oct. 13, 2011, 11:04 a.m.
Message ID <1318503845-11473-36-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/119430/
State New
Headers show

Comments

Paolo Bonzini - Oct. 13, 2011, 11:04 a.m.
scsi-block is a new device that supports device passthrough of Linux
block devices (i.e. /dev/sda, not /dev/sg0).  It uses SG_IO for commands
other than I/O commands, and regular AIO read/writes for I/O commands.
Besides being simpler to configure (no mapping required to scsi-generic
device names), this removes the need for a large bounce buffer and,
in the future, will get scatter/gather support for free from scsi-disk.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/scsi-disk.c |  111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 111 insertions(+), 0 deletions(-)
Paolo Bonzini - Oct. 24, 2011, 3:28 p.m.
On 10/24/2011 05:28 PM, Kevin Wolf wrote:
>> scsi-block is a new device that supports device passthrough of Linux
>> >  block devices (i.e. /dev/sda, not /dev/sg0).  It uses SG_IO for commands
>> >  other than I/O commands, and regular AIO read/writes for I/O commands.
>> >  Besides being simpler to configure (no mapping required to scsi-generic
>> >  device names), this removes the need for a large bounce buffer and,
>> >  in the future, will get scatter/gather support for free from scsi-disk.
>> >
>> >  Signed-off-by: Paolo Bonzini<pbonzini@redhat.com>
>
> This doesn't seem to use much of scsi-disk, so what about exporting
> &scsi_disk_reqops and adding a separate file scsi-block.c? Would make
> things a bit more symmetrical between scsi-disk and scsi-generic.
>
> Or will future patches add code that depends on internal interfaces of
> scsi-disk?

It already uses some internal interfaces: scsi_initfn, scsi_disk_reset, 
scsi_destroy, sizeof(SCSIDiskState).

Paolo
Kevin Wolf - Oct. 24, 2011, 3:28 p.m.
Am 13.10.2011 13:04, schrieb Paolo Bonzini:
> scsi-block is a new device that supports device passthrough of Linux
> block devices (i.e. /dev/sda, not /dev/sg0).  It uses SG_IO for commands
> other than I/O commands, and regular AIO read/writes for I/O commands.
> Besides being simpler to configure (no mapping required to scsi-generic
> device names), this removes the need for a large bounce buffer and,
> in the future, will get scatter/gather support for free from scsi-disk.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

This doesn't seem to use much of scsi-disk, so what about exporting
&scsi_disk_reqops and adding a separate file scsi-block.c? Would make
things a bit more symmetrical between scsi-disk and scsi-generic.

Or will future patches add code that depends on internal interfaces of
scsi-disk?

Kevin
Kevin Wolf - Oct. 24, 2011, 3:38 p.m.
Am 24.10.2011 17:28, schrieb Paolo Bonzini:
> On 10/24/2011 05:28 PM, Kevin Wolf wrote:
>>> scsi-block is a new device that supports device passthrough of Linux
>>>>  block devices (i.e. /dev/sda, not /dev/sg0).  It uses SG_IO for commands
>>>>  other than I/O commands, and regular AIO read/writes for I/O commands.
>>>>  Besides being simpler to configure (no mapping required to scsi-generic
>>>>  device names), this removes the need for a large bounce buffer and,
>>>>  in the future, will get scatter/gather support for free from scsi-disk.
>>>>
>>>>  Signed-off-by: Paolo Bonzini<pbonzini@redhat.com>
>>
>> This doesn't seem to use much of scsi-disk, so what about exporting
>> &scsi_disk_reqops and adding a separate file scsi-block.c? Would make
>> things a bit more symmetrical between scsi-disk and scsi-generic.
>>
>> Or will future patches add code that depends on internal interfaces of
>> scsi-disk?
> 
> It already uses some internal interfaces: scsi_initfn, scsi_disk_reset, 
> scsi_destroy, sizeof(SCSIDiskState).

Right... I don't like it much in scsi-disk.c, but what can you do.
Exporting everything wouldn't be nicer.

Kevin

Patch

diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 418b30d..b21fe1b 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -39,6 +39,10 @@  do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
 #include "blockdev.h"
 #include "block_int.h"
 
+#ifdef __linux
+#include <scsi/sg.h>
+#endif
+
 #define SCSI_DMA_BUF_SIZE    131072
 #define SCSI_MAX_INQUIRY_LEN 256
 
@@ -1601,6 +1605,98 @@  static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
     return req;
 }
 
+#ifdef __linux__
+static int get_device_type(SCSIDiskState *s)
+{
+    BlockDriverState *bdrv = s->qdev.conf.bs;
+    uint8_t cmd[16];
+    uint8_t buf[36];
+    uint8_t sensebuf[8];
+    sg_io_hdr_t io_header;
+    int ret;
+
+    memset(cmd, 0, sizeof(cmd));
+    memset(buf, 0, sizeof(buf));
+    cmd[0] = INQUIRY;
+    cmd[4] = sizeof(buf);
+
+    memset(&io_header, 0, sizeof(io_header));
+    io_header.interface_id = 'S';
+    io_header.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_header.dxfer_len = sizeof(buf);
+    io_header.dxferp = buf;
+    io_header.cmdp = cmd;
+    io_header.cmd_len = sizeof(cmd);
+    io_header.mx_sb_len = sizeof(sensebuf);
+    io_header.sbp = sensebuf;
+    io_header.timeout = 6000; /* XXX */
+
+    ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
+    if (ret < 0 || io_header.driver_status || io_header.host_status) {
+        return -1;
+    }
+    s->qdev.type = buf[0];
+    s->removable = (buf[1] & 0x80) != 0;
+    s->qdev.blocksize = 0;
+    s->qdev.max_lba = 0;
+    return 0;
+}
+
+static int scsi_block_initfn(SCSIDevice *dev)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+    int sg_version;
+    int rc;
+
+    if (!s->qdev.conf.bs) {
+        error_report("scsi-block: drive property not set");
+        return -1;
+    }
+
+    /* check we are using a driver managing SG_IO (version 3 and after) */
+    if (bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
+        sg_version < 30000) {
+        error_report("scsi-block: scsi generic interface too old");
+        return -1;
+    }
+
+    /* get device type from INQUIRY data */
+    rc = get_device_type(s);
+    if (rc < 0) {
+        error_report("scsi-block: INQUIRY failed");
+        return -1;
+    }
+
+    return scsi_initfn(&s->qdev);
+}
+
+static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
+                                           uint32_t lun, uint8_t *buf,
+                                           void *hba_private)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
+
+    switch (buf[0]) {
+    case READ_6:
+    case READ_10:
+    case READ_12:
+    case READ_16:
+    case WRITE_6:
+    case WRITE_10:
+    case WRITE_12:
+    case WRITE_16:
+    case WRITE_VERIFY_10:
+    case WRITE_VERIFY_12:
+    case WRITE_VERIFY_16:
+        return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun,
+                              hba_private);
+    }
+
+    return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,
+                          hba_private);
+}
+#endif
+
 #define DEFINE_SCSI_DISK_PROPERTIES()                           \
     DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf),          \
     DEFINE_PROP_STRING("ver",  SCSIDiskState, version),         \
@@ -1636,6 +1732,21 @@  static SCSIDeviceInfo scsi_disk_info[] = {
             DEFINE_SCSI_DISK_PROPERTIES(),
             DEFINE_PROP_END_OF_LIST(),
         },
+#ifdef __linux__
+    },{
+        .qdev.name    = "scsi-block",
+        .qdev.fw_name = "disk",
+        .qdev.desc    = "SCSI block device passthrough",
+        .qdev.size    = sizeof(SCSIDiskState),
+        .qdev.reset   = scsi_disk_reset,
+        .init         = scsi_block_initfn,
+        .destroy      = scsi_destroy,
+        .alloc_req    = scsi_block_new_request,
+        .qdev.props   = (Property[]) {
+            DEFINE_SCSI_DISK_PROPERTIES(),
+            DEFINE_PROP_END_OF_LIST(),
+        },
+#endif
     },{
         .qdev.name    = "scsi-disk", /* legacy -device scsi-disk */
         .qdev.fw_name = "disk",