qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 5/5] SCSI device DMA split


From: Laurent Vivier
Subject: [Qemu-devel] [PATCH 5/5] SCSI device DMA split
Date: Wed, 23 Jan 2008 17:12:55 +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
---
 block-sg.c        |    1 
 hw/scsi-generic.c |  110 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 105 insertions(+), 6 deletions(-)

Index: qemu/hw/scsi-generic.c
===================================================================
--- qemu.orig/hw/scsi-generic.c 2008-01-23 15:29:26.000000000 +0100
+++ qemu/hw/scsi-generic.c      2008-01-23 15:29:26.000000000 +0100
@@ -60,6 +60,8 @@ do { fprintf(stderr, "scsi-generic: " fm
 #define MAX_UINT ((unsigned int)-1)
 #endif
 
+#define MAX_CHUNK 65536
+
 typedef struct SCSIRequest {
     SGRequest sg;
     struct SCSIRequest *next;
@@ -70,6 +72,8 @@ typedef struct SCSIRequest {
     uint8_t *buf;
     int buflen;
     int len;
+    int remaining;
+    int offset;
 } SCSIRequest;
 
 struct SCSIDeviceState
@@ -81,6 +85,7 @@ struct SCSIDeviceState
     void *card;
     int driver_status;
     uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
+    int max_chunk;
 };
 
 /* Global pool of SCSIRequest structures.  */
@@ -98,6 +103,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));
@@ -186,23 +192,93 @@ 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)
 {
+    int ret;
+    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;
+    }
     memset(&r->sg, 0, sizeof(r->sg));
     r->sg.io_header.interface_id = 'S';
+    r->sg.io_header.dxferp = r->buf + r->offset;
     r->sg.io_header.dxfer_direction = direction;
     r->sg.io_header.cmd_len = r->cmdlen;
-    r->sg.io_header.mx_sb_len = sizeof(r->dev->sensebuf);
     r->sg.io_header.dxfer_len = r->buflen;
-    r->sg.io_header.dxferp = r->buf;
+    r->sg.io_header.mx_sb_len = sizeof(s->sensebuf);
+    r->sg.io_header.sbp = s->sensebuf;
     r->sg.io_header.cmdp = r->cmd;
-    r->sg.io_header.sbp = r->dev->sensebuf;
     r->sg.io_header.timeout = MAX_UINT;
     r->sg.io_header.flags |= SG_FLAG_DIRECT_IO;
 
-    return bdrv_execute(bdrv, &r->sg, complete);
+    ret = bdrv_execute(bdrv, &r->sg, complete);
+    if (ret == -1 && 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;
+        }
+    }
+    return ret;
 }
 
 static void scsi_read_complete(void *request, int ret)
@@ -216,7 +292,18 @@ static void scsi_read_complete(void *req
         scsi_command_complete(r, ret);
         return;
     }
-    len = r->sg.io_header.dxfer_len - r->sg.io_header.resid;
+    r->offset += r->sg.io_header.dxfer_len - r->sg.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;
@@ -263,12 +350,24 @@ static void scsi_read_data(SCSIDevice *d
 static void scsi_write_complete(void* request, int ret)
 {
     SCSIRequest* r = (SCSIRequest*)request;
+    SCSIDeviceState *s = r->dev;
     DPRINTF("scsi_write_complete() ret = %d\n", ret);
     if (ret) {
         DPRINTF("IO error\n");
         scsi_command_complete(r, ret);
         return;
     }
+    r->offset += r->sg.io_header.dxfer_len - r->sg.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);
 }
@@ -591,6 +690,7 @@ SCSIDevice *scsi_generic_init(BlockDrive
     s->card = card;
     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)
Index: qemu/block-sg.c
===================================================================
--- qemu.orig/block-sg.c        2008-01-23 15:33:03.000000000 +0100
+++ qemu/block-sg.c     2008-01-23 15:33:28.000000000 +0100
@@ -151,7 +151,6 @@ int sg_execute(BlockDriverState *bs, voi
 
     if (sg_write(bs, (const uint8_t *)&r->io_header,
                  sizeof(r->io_header)) == -1) {
-        BADF("execute_command: write failed ! (%d)\n", errno);
         return -1;
     }
     if (complete == NULL) {





reply via email to

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