[Top][All Lists]
[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)