From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1J5kvK-0006sk-39 for qemu-devel@nongnu.org; Fri, 21 Dec 2007 11:40:34 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1J5kvJ-0006rP-0U for qemu-devel@nongnu.org; Fri, 21 Dec 2007 11:40:33 -0500 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1J5kvI-0006r9-Qc for qemu-devel@nongnu.org; Fri, 21 Dec 2007 11:40:32 -0500 Received: from ecfrec.frec.bull.fr ([129.183.4.8]) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1J5kvI-0004aj-EV for qemu-devel@nongnu.org; Fri, 21 Dec 2007 11:40:32 -0500 Received: from localhost (localhost [127.0.0.1]) by ecfrec.frec.bull.fr (Postfix) with ESMTP id 261C519D958 for ; Fri, 21 Dec 2007 17:40:34 +0100 (CET) Received: from ecfrec.frec.bull.fr ([127.0.0.1]) by localhost (ecfrec.frec.bull.fr [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 11186-07 for ; Fri, 21 Dec 2007 17:40:31 +0100 (CET) Received: from ecn002.frec.bull.fr (ecn002.frec.bull.fr [129.183.4.6]) by ecfrec.frec.bull.fr (Postfix) with ESMTP id 40FFA19D952 for ; Fri, 21 Dec 2007 17:40:29 +0100 (CET) In-Reply-To: <11982552391590@bull.net> Date: Fri, 21 Dec 2007 17:40:41 +0100 Message-Id: <11982552412651@bull.net> Mime-Version: 1.0 From: Laurent Vivier Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="iso-8859-1" Subject: [Qemu-devel] [PATCH 2/2] Real SCSI device DMA split (v5) Reply-To: Laurent Vivier , qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org 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 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- 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 =20 +#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; =20 struct SCSIDeviceState @@ -84,6 +88,7 @@ struct SCSIDeviceState void *opaque; int driver_status; uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; + int max_chunk; }; =20 /* Global pool of SCSIRequest structures. */ @@ -101,6 +106,7 @@ static SCSIRequest *scsi_new_request(SCS r->buf =3D NULL; r->buflen =3D 0; } + r->offset =3D 0; r->dev =3D s; r->tag =3D tag; memset(r->cmd, 0, sizeof(r->cmd)); @@ -191,24 +197,90 @@ static void scsi_cancel_io(SCSIDevice *d } } =20 +static void scsi_cmd_next(uint8_t *cmd, uint32_t inc) +{ + uint32_t addr; + switch (cmd[0] >> 5) { + case 0: + addr =3D cmd[3] | (cmd[2] << 8); + addr +=3D inc; + cmd[2] =3D addr >> 8; + cmd[3] =3D addr; + break; + case 1: + case 2: + case 4: + case 5: + addr =3D cmd[5] | ((cmd[4] << 8) | ((cmd[3] << 16) | (cmd[2] << 24= ))); + addr +=3D inc; + cmd[2] =3D addr >> 24; + cmd[3] =3D addr >> 16; + cmd[4] =3D addr >> 8; + cmd[5] =3D addr; + break; + } +} +static void scsi_set_length(uint8_t *cmd, uint32_t len) +{ + switch (cmd[0] >> 5) { + case 0: + cmd[4] =3D len; + break; + case 1: + case 2: + cmd[7] =3D (len >> 8); + cmd[8] =3D len; + break; + case 4: + cmd[10] =3D len >> 24; + cmd[11] =3D len >> 16; + cmd[12] =3D len >> 8; + cmd[13] =3D len; + break; + case 5: + cmd[6] =3D len >> 24; + cmd[7] =3D len >> 16; + cmd[8] =3D len >> 8; + cmd[9] =3D len; + break; + } +} + static int execute_command(BlockDriverState *bdrv, SCSIRequest *r, int direction, BlockDriverCompletionFunc *complete) { + SCSIDeviceState *s =3D r->dev; =20 + r->remaining =3D 0; +retry: + if (s->max_chunk && r->buflen > s->max_chunk) { + r->remaining =3D r->buflen - s->max_chunk; + scsi_set_length(r->cmd, s->max_chunk / s->blocksize); + r->buflen =3D s->max_chunk; + } r->io_header.interface_id =3D 'S'; r->io_header.dxfer_direction =3D direction; - r->io_header.dxferp =3D r->buf; + r->io_header.dxferp =3D r->buf + r->offset; r->io_header.dxfer_len =3D r->buflen; r->io_header.cmdp =3D r->cmd; r->io_header.cmd_len =3D r->cmdlen; - r->io_header.mx_sb_len =3D sizeof(r->dev->sensebuf); - r->io_header.sbp =3D r->dev->sensebuf; + r->io_header.mx_sb_len =3D sizeof(s->sensebuf); + r->io_header.sbp =3D s->sensebuf; r->io_header.timeout =3D MAX_UINT; r->io_header.usr_ptr =3D r; r->io_header.flags |=3D SG_FLAG_DIRECT_IO; =20 if (bdrv_pwrite(bdrv, -1, &r->io_header, sizeof(r->io_header)) =3D=3D = -1) { + if (errno =3D=3D 12) { + if (!s->max_chunk) { + s->max_chunk =3D MAX_CHUNK; + goto retry; + } else if (s->max_chunk > s->blocksize) { + s->max_chunk >>=3D 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 =3D r->io_header.dxfer_len - r->io_header.resid; + r->offset +=3D r->io_header.dxfer_len - r->io_header.resid; + if (r->remaining !=3D 0) { + scsi_cmd_next(r->cmd, r->buflen / s->blocksize); + scsi_set_length(r->cmd, r->remaining / s->blocksize); + r->buflen =3D r->remaining; + ret =3D execute_command(s->bdrv, r, SG_DXFER_FROM_DEV, + scsi_read_complete); + if (ret =3D=3D -1) + scsi_command_complete(r, -EINVAL); + return; + } + len =3D r->offset; DPRINTF("Data ready tag=3D0x%x len=3D%d\n", r->tag, len); =20 r->len =3D -1; @@ -293,6 +376,7 @@ static void scsi_read_data(SCSIDevice *d static void scsi_write_complete(void * opaque, int ret) { SCSIRequest *r =3D (SCSIRequest *)opaque; + SCSIDeviceState *s =3D r->dev; =20 DPRINTF("scsi_write_complete() ret =3D %d\n", ret); if (ret) { @@ -300,6 +384,17 @@ static void scsi_write_complete(void * o scsi_command_complete(r, ret); return; } + r->offset +=3D r->io_header.dxfer_len - r->io_header.resid; + if (r->remaining !=3D 0) { + scsi_cmd_next(r->cmd, r->buflen / s->blocksize); + scsi_set_length(r->cmd, r->remaining / s->blocksize); + r->buflen =3D r->remaining; + ret =3D execute_command(s->bdrv, r, SG_DXFER_TO_DEV, + scsi_write_complete); + if (ret =3D=3D -1) + scsi_command_complete(r, -EINVAL); + return; + } =20 scsi_command_complete(r, ret); } @@ -641,6 +736,7 @@ SCSIDevice *scsi_generic_init(BlockDrive s->lun =3D scsiid.lun; s->blocksize =3D get_blocksize(s->bdrv); s->driver_status =3D 0; + s->max_chunk =3D 0; memset(s->sensebuf, 0, sizeof(s->sensebuf)); /* removable media returns 0 if not present */ if (s->blocksize <=3D 0)