qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 2/2] Real SCSI device DMA split (v5)


From: Laurent Vivier
Subject: [Qemu-devel] [PATCH 2/2] Real SCSI device DMA split (v5)
Date: Fri, 21 Dec 2007 17:40:41 +0100

With some emulated SCSI devices, like usb-storage or ide-scsi, DMA transfers
are limited to 64 kiB or 32 kiB.
This patch allows to split a READ or WRITE into several READ or WRITE.

Laurent
---
 hw/scsi-generic.c |  104 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 100 insertions(+), 4 deletions(-)

Index: qemu/hw/scsi-generic.c
===================================================================
--- qemu.orig/hw/scsi-generic.c 2007-12-20 10:55:24.000000000 +0100
+++ qemu/hw/scsi-generic.c      2007-12-20 16:23:24.000000000 +0100
@@ -61,6 +61,8 @@ do { fprintf(stderr, "scsi-generic: " fm
 #define MAX_UINT ((unsigned int)-1)
 #endif
 
+#define MAX_CHUNK 65536
+
 typedef struct SCSIRequest {
     BlockDriverAIOCB *aiocb;
     struct SCSIRequest *next;
@@ -72,6 +74,8 @@ typedef struct SCSIRequest {
     int buflen;
     int len;
     sg_io_hdr_t io_header;
+    int remaining;
+    int offset;
 } SCSIRequest;
 
 struct SCSIDeviceState
@@ -84,6 +88,7 @@ struct SCSIDeviceState
     void *opaque;
     int driver_status;
     uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
+    int max_chunk;
 };
 
 /* Global pool of SCSIRequest structures.  */
@@ -101,6 +106,7 @@ static SCSIRequest *scsi_new_request(SCS
         r->buf = NULL;
         r->buflen = 0;
     }
+    r->offset = 0;
     r->dev = s;
     r->tag = tag;
     memset(r->cmd, 0, sizeof(r->cmd));
@@ -191,24 +197,90 @@ static void scsi_cancel_io(SCSIDevice *d
     }
 }
 
+static void scsi_cmd_next(uint8_t *cmd, uint32_t inc)
+{
+    uint32_t addr;
+    switch (cmd[0] >> 5) {
+    case 0:
+        addr = cmd[3] | (cmd[2] << 8);
+        addr += inc;
+        cmd[2] = addr >> 8;
+        cmd[3] = addr;
+        break;
+    case 1:
+    case 2:
+    case 4:
+    case 5:
+        addr = cmd[5] | ((cmd[4] << 8) | ((cmd[3] << 16) | (cmd[2] << 24)));
+        addr += inc;
+        cmd[2] = addr >> 24;
+        cmd[3] = addr >> 16;
+        cmd[4] = addr >> 8;
+        cmd[5] = addr;
+        break;
+    }
+}
+static void scsi_set_length(uint8_t *cmd, uint32_t len)
+{
+    switch (cmd[0] >> 5) {
+    case 0:
+        cmd[4] = len;
+        break;
+    case 1:
+    case 2:
+        cmd[7] = (len >> 8);
+        cmd[8] = len;
+        break;
+    case 4:
+        cmd[10] = len >> 24;
+        cmd[11] = len >> 16;
+        cmd[12] = len >> 8;
+        cmd[13] = len;
+        break;
+    case 5:
+        cmd[6] = len >> 24;
+        cmd[7] = len >> 16;
+        cmd[8] = len >> 8;
+        cmd[9] = len;
+        break;
+    }
+}
+
 static int execute_command(BlockDriverState *bdrv,
                            SCSIRequest *r, int direction,
                           BlockDriverCompletionFunc *complete)
 {
+    SCSIDeviceState *s = r->dev;
 
+    r->remaining = 0;
+retry:
+    if (s->max_chunk && r->buflen > s->max_chunk) {
+        r->remaining = r->buflen - s->max_chunk;
+        scsi_set_length(r->cmd, s->max_chunk / s->blocksize);
+        r->buflen = s->max_chunk;
+    }
     r->io_header.interface_id = 'S';
     r->io_header.dxfer_direction = direction;
-    r->io_header.dxferp = r->buf;
+    r->io_header.dxferp = r->buf + r->offset;
     r->io_header.dxfer_len = r->buflen;
     r->io_header.cmdp = r->cmd;
     r->io_header.cmd_len = r->cmdlen;
-    r->io_header.mx_sb_len = sizeof(r->dev->sensebuf);
-    r->io_header.sbp = r->dev->sensebuf;
+    r->io_header.mx_sb_len = sizeof(s->sensebuf);
+    r->io_header.sbp = s->sensebuf;
     r->io_header.timeout = MAX_UINT;
     r->io_header.usr_ptr = r;
     r->io_header.flags |= SG_FLAG_DIRECT_IO;
 
     if (bdrv_pwrite(bdrv, -1, &r->io_header, sizeof(r->io_header)) == -1) {
+        if (errno == 12) {
+            if (!s->max_chunk) {
+                s->max_chunk = MAX_CHUNK;
+                goto retry;
+            } else if (s->max_chunk > s->blocksize) {
+                s->max_chunk >>= 1;
+                goto retry;
+            }
+        }
         BADF("execute_command: write failed ! (%d)\n", errno);
         return -1;
     }
@@ -246,7 +318,18 @@ static void scsi_read_complete(void * op
         scsi_command_complete(r, ret);
         return;
     }
-    len = r->io_header.dxfer_len - r->io_header.resid;
+    r->offset += r->io_header.dxfer_len - r->io_header.resid;
+    if (r->remaining != 0) {
+        scsi_cmd_next(r->cmd, r->buflen / s->blocksize);
+        scsi_set_length(r->cmd, r->remaining / s->blocksize);
+        r->buflen = r->remaining;
+        ret = execute_command(s->bdrv, r, SG_DXFER_FROM_DEV,
+                              scsi_read_complete);
+        if (ret == -1)
+            scsi_command_complete(r, -EINVAL);
+        return;
+    }
+    len = r->offset;
     DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, len);
 
     r->len = -1;
@@ -293,6 +376,7 @@ static void scsi_read_data(SCSIDevice *d
 static void scsi_write_complete(void * opaque, int ret)
 {
     SCSIRequest *r = (SCSIRequest *)opaque;
+    SCSIDeviceState *s = r->dev;
 
     DPRINTF("scsi_write_complete() ret = %d\n", ret);
     if (ret) {
@@ -300,6 +384,17 @@ static void scsi_write_complete(void * o
         scsi_command_complete(r, ret);
         return;
     }
+    r->offset += r->io_header.dxfer_len - r->io_header.resid;
+    if (r->remaining != 0) {
+        scsi_cmd_next(r->cmd, r->buflen / s->blocksize);
+        scsi_set_length(r->cmd, r->remaining / s->blocksize);
+        r->buflen = r->remaining;
+        ret = execute_command(s->bdrv, r, SG_DXFER_TO_DEV,
+                              scsi_write_complete);
+        if (ret == -1)
+            scsi_command_complete(r, -EINVAL);
+        return;
+    }
 
     scsi_command_complete(r, ret);
 }
@@ -641,6 +736,7 @@ SCSIDevice *scsi_generic_init(BlockDrive
     s->lun = scsiid.lun;
     s->blocksize = get_blocksize(s->bdrv);
     s->driver_status = 0;
+    s->max_chunk = 0;
     memset(s->sensebuf, 0, sizeof(s->sensebuf));
     /* removable media returns 0 if not present */
     if (s->blocksize <= 0)





reply via email to

[Prev in Thread] Current Thread [Next in Thread]