[Top][All Lists]
[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) {
Re: [Qemu-devel] [PATCH 0/5] SCSI passthrough cleanup, Fabrice Bellard, 2008/01/23