This patch modifies scsi_read_data() and scsi_write_data() to use asynchronous I/O whith SCSI Generic.
block-raw.s has been modified to define the number of bytes to transfer instead of the number of blocks: if nb_sectors is less than zero the nb_sectors is a number of bytes. --- block-raw.c | 5 + hw/lsi53c895a.c | 2 hw/scsi-generic.c | 198 ++++++++++++++++++++++++++++++++++-------------------- 3 files changed, 133 insertions(+), 72 deletions(-) Index: qemu/block-raw.c =================================================================== --- qemu.orig/block-raw.c 2007-11-28 13:12:02.000000000 +0100 +++ qemu/block-raw.c 2007-11-28 13:12:22.000000000 +0100 @@ -385,7 +385,10 @@ static RawAIOCB *raw_aio_setup(BlockDriv acb->aiocb.aio_sigevent.sigev_signo = aio_sig_num; acb->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; acb->aiocb.aio_buf = buf; - acb->aiocb.aio_nbytes = nb_sectors * 512; + if (nb_sectors < 0) + acb->aiocb.aio_nbytes = -nb_sectors; + else + acb->aiocb.aio_nbytes = nb_sectors * 512; acb->aiocb.aio_offset = sector_num * 512; acb->next = first_aio; first_aio = acb; Index: qemu/hw/scsi-generic.c =================================================================== --- qemu.orig/hw/scsi-generic.c 2007-11-28 13:12:12.000000000 +0100 +++ qemu/hw/scsi-generic.c 2007-11-28 13:13:13.000000000 +0100 @@ -53,6 +53,7 @@ do { fprintf(stderr, "scsi-generic: " fm #define SG_ERR_DRIVER_SENSE 0x08 typedef struct SCSIRequest { + BlockDriverAIOCB *aiocb; struct SCSIRequest *next; SCSIDeviceState *dev; uint32_t tag; @@ -61,8 +62,8 @@ typedef struct SCSIRequest { uint8_t *buf; int buflen; int len; - int driver_status; uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; + sg_io_hdr_t io_header; } SCSIRequest; struct SCSIDeviceState @@ -94,13 +95,16 @@ static SCSIRequest *scsi_new_request(SCS r->tag = tag; memset(r->cmd, 0, sizeof(r->cmd)); memset(r->sensebuf, 0, sizeof(r->sensebuf)); + memset(&r->io_header, 0, sizeof(r->io_header)); r->cmdlen = 0; r->len = 0; + r->aiocb = NULL; /* link */ r->next = s->requests; s->requests = r; + DPRINTF("scsi_new_request tag=0x%x\n", tag); return r; } @@ -109,6 +113,7 @@ static void scsi_remove_request(SCSIRequ SCSIRequest *last; SCSIDeviceState *s = r->dev; + DPRINTF("scsi_remove_request tag=0x%x\n", r->tag); if (s->requests == r) { s->requests = r->next; } else { @@ -138,16 +143,24 @@ static SCSIRequest *scsi_find_request(SC static int scsi_get_sense(SCSIRequest *r) { - if ((r->driver_status & SG_ERR_DRIVER_SENSE) == 0) + if ((r->io_header.driver_status & SG_ERR_DRIVER_SENSE) == 0) return NO_SENSE; return r->sensebuf[2] & 0x0f; } /* Helper function for command completion. */ -static void scsi_command_complete(SCSIRequest *r, int sense) +static void scsi_command_complete(void *opaque, int ret) { + SCSIRequest *r = (SCSIRequest *)opaque; SCSIDeviceState *s = r->dev; uint32_t tag; + int sense; + + if (ret != 0) + sense = HARDWARE_ERROR; + else + sense = scsi_get_sense(r); + DPRINTF("Command complete 0x%p tag=0x%x sense=%d\n", r, r->tag, sense); tag = r->tag; scsi_remove_request(r); @@ -163,46 +176,70 @@ static void scsi_cancel_io(SCSIDevice *d DPRINTF("Cancel tag=0x%x\n", tag); r = scsi_find_request(s, tag); if (r) { + if (r->aiocb) + bdrv_aio_cancel(r->aiocb); + r->aiocb = NULL; scsi_remove_request(r); } } -static int execute_command(BlockDriverState *bdrv, uint8_t *cmdbuf, int cmdlen, - uint8_t *outbuf, uint32_t outlen, - uint8_t *sensebuf, int senselen, - int direction, int *len) -{ - sg_io_hdr_t io_header; - int ret; - - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = direction; - io_header.dxfer_len = outlen; - io_header.dxferp = outbuf; - io_header.cmdp = cmdbuf; - io_header.cmd_len = cmdlen; - io_header.mx_sb_len = senselen; - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ +static int execute_command(BlockDriverState *bdrv, + SCSIRequest *r, int direction, + BlockDriverCompletionFunc *complete) +{ + r->io_header.interface_id = 'S'; + r->io_header.dxfer_direction = direction; + r->io_header.dxfer_len = r->buflen; + r->io_header.dxferp = r->buf; + r->io_header.cmdp = r->cmd; + r->io_header.cmd_len = r->cmdlen; + r->io_header.mx_sb_len = sizeof(r->sensebuf); + r->io_header.sbp = r->sensebuf; + r->io_header.timeout = 6000; /* XXX */ - ret = bdrv_pwrite(bdrv, -1, &io_header, sizeof(io_header)); - if (ret == -1) { + if (bdrv_pwrite(bdrv, -1, &r->io_header, sizeof(r->io_header)) == -1) { BADF("execute_command: write failed ! (%d)\n", errno); return -1; } - while ((ret = bdrv_pread(bdrv, -1, &io_header, sizeof(io_header))) == -1 && - errno == EINTR); + if (complete == NULL) { + int ret; + r->aiocb = NULL; + while ((ret = bdrv_pread(bdrv, -1, &r->io_header, + sizeof(r->io_header))) == -1 && + errno == EINTR); + if (ret == -1) { + BADF("execute_command: read failed !\n"); + return -1; + } + return 0; + } - if (ret == -1) { + r->aiocb = bdrv_aio_read(bdrv, 0, (uint8_t*)&r->io_header, + -(int64_t)sizeof(r->io_header), complete, r); + if (r->aiocb == NULL) { BADF("execute_command: read failed !\n"); return -1; } - if (len != NULL) - *len = io_header.dxfer_len - io_header.resid; + return 0; +} + +static void scsi_read_complete(void * opaque, int ret) +{ + SCSIRequest *r = (SCSIRequest *)opaque; + SCSIDeviceState *s = r->dev; + int len; - return io_header.driver_status; + if (ret) { + DPRINTF("IO error\n"); + scsi_command_complete(r, ret); + return; + } + len = r->io_header.dxfer_len - r->io_header.resid; + DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, len); + + r->len = -1; + s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len); } /* Read more data from scsi device into buffer. */ @@ -210,25 +247,40 @@ static void scsi_read_data(SCSIDevice *d { SCSIDeviceState *s = d->state; SCSIRequest *r; - int len; + int ret; DPRINTF("scsi_read_data 0x%x\n", tag); r = scsi_find_request(s, tag); if (!r) { BADF("Bad read tag 0x%x\n", tag); /* ??? This is the wrong error. */ - scsi_command_complete(r, HARDWARE_ERROR); + scsi_command_complete(r, -EINVAL); return; } if (r->len == -1) { - scsi_command_complete(r, scsi_get_sense(r)); + scsi_command_complete(r, 0); return; } - len = r->len; - r->len = -1; - s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len); + ret = execute_command(s->bdrv, r, SG_DXFER_FROM_DEV, scsi_read_complete); + if (ret == -1) { + scsi_command_complete(r, -EINVAL); + return; + } +} + +static void scsi_write_complete(void * opaque, int ret) +{ + SCSIRequest *r = (SCSIRequest *)opaque; + + if (ret) { + DPRINTF("IO error\n"); + scsi_command_complete(r, ret); + return; + } + + scsi_command_complete(r, ret); } /* Write data to a scsi device. Returns nonzero on failure. @@ -244,7 +296,7 @@ static int scsi_write_data(SCSIDevice *d if (!r) { BADF("Bad write tag 0x%x\n", tag); /* ??? This is the wrong error. */ - scsi_command_complete(r, HARDWARE_ERROR); + scsi_command_complete(r, -EINVAL); return 0; } @@ -254,15 +306,11 @@ static int scsi_write_data(SCSIDevice *d return 0; } - ret = execute_command(s->bdrv, r->cmd, r->cmdlen, r->buf, r->len, - r->sensebuf, sizeof(r->sensebuf), - SG_DXFER_TO_DEV, NULL); + ret = execute_command(s->bdrv, r, SG_DXFER_TO_DEV, scsi_write_complete); if (ret == -1) { - scsi_command_complete(r, HARDWARE_ERROR); + scsi_command_complete(r, -EINVAL); return 1; } - r->driver_status = ret; - scsi_command_complete(r, scsi_get_sense(r)); return 0; } @@ -416,19 +464,12 @@ static int32_t scsi_send_command(SCSIDev SCSIRequest *r; int ret; - r = scsi_find_request(s, tag); - if (r) { - BADF("Tag 0x%x already in use %p\n", tag, r); - scsi_cancel_io(d, tag); - } - r = scsi_new_request(s, tag); - /* ??? Tags are not unique for different luns. We only implement a single lun, so this should not matter. */ if (lun != s->lun || (cmd[1] >> 5) != s->lun) { DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5); - scsi_command_complete(r, ILLEGAL_REQUEST); + s->completion(s->opaque, SCSI_REASON_DONE, tag, HARDWARE_ERROR); return 0; } @@ -438,21 +479,28 @@ static int32_t scsi_send_command(SCSIDev } DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, - command, len); + cmd[0], len); + + r = scsi_find_request(s, tag); + if (r) { + BADF("Tag 0x%x already in use %p\n", tag, r); + scsi_cancel_io(d, tag); + } + r = scsi_new_request(s, tag); memcpy(r->cmd, cmd, cmdlen); r->cmdlen = cmdlen; if (len == 0) { - ret = execute_command(s->bdrv, r->cmd, r->cmdlen, NULL, 0, - r->sensebuf, sizeof(r->sensebuf), - SG_DXFER_NONE, NULL); + if (r->buf != NULL) + free(r->buf); + r->buflen = 0; + r->buf = NULL; + ret = execute_command(s->bdrv, r, SG_DXFER_NONE, NULL); if (ret == -1) { - scsi_command_complete(r, HARDWARE_ERROR); + scsi_command_complete(r, -EINVAL); return 0; } - r->driver_status = ret; - scsi_command_complete(r, scsi_get_sense(r)); return 0; } @@ -471,16 +519,6 @@ static int32_t scsi_send_command(SCSIDev return -len; } - ret = execute_command(s->bdrv, r->cmd, r->cmdlen, r->buf, r->len, - r->sensebuf, sizeof(r->sensebuf), - SG_DXFER_FROM_DEV, &len); - if (ret == -1) { - scsi_command_complete(r, HARDWARE_ERROR); - return 0; - } - r->len = len; - r->driver_status = ret; - return len; } @@ -489,16 +527,34 @@ static int get_blocksize(BlockDriverStat uint8_t cmd[10]; uint8_t buf[8]; uint8_t sensebuf[8]; + sg_io_hdr_t io_header; int ret; memset(cmd, sizeof(cmd), 0); memset(buf, sizeof(buf), 0); cmd[0] = READ_CAPACITY; - ret = execute_command(bdrv, cmd, sizeof(cmd), buf, sizeof(buf), - sensebuf, sizeof(sensebuf), - SG_DXFER_FROM_DEV, NULL); - if (ret < 0) + + memset(&io_header, 0, sizeof(io_header)); + io_header.interface_id = 'S'; + io_header.dxfer_direction = SG_DXFER_FROM_DEV; + io_header.dxfer_len = sizeof(buf); + io_header.dxferp = buf; + io_header.cmdp = cmd; + io_header.cmd_len = sizeof(cmd); + io_header.mx_sb_len = sizeof(sensebuf); + io_header.sbp = sensebuf; + io_header.timeout = 6000; /* XXX */ + + ret = bdrv_pwrite(bdrv, -1, &io_header, sizeof(io_header)); + if (ret == -1) + return -1; + + while ((ret = bdrv_pread(bdrv, -1, &io_header, sizeof(io_header))) == -1 && + errno == EINTR); + + if (ret == -1) return -1; + return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; } Index: qemu/hw/lsi53c895a.c =================================================================== --- qemu.orig/hw/lsi53c895a.c 2007-11-28 13:12:02.000000000 +0100 +++ qemu/hw/lsi53c895a.c 2007-11-28 13:12:22.000000000 +0100 @@ -1225,6 +1225,8 @@ static uint8_t lsi_reg_readb(LSIState *s return s->sdid; case 0x07: /* GPREG0 */ return 0x7f; + case 0x08: /* Revision ID */ + return 0x00; case 0xa: /* SSID */ return s->ssid; case 0xb: /* SBCL */