diff options
Diffstat (limited to 'hw/scsi-disk.c')
-rw-r--r-- | hw/scsi-disk.c | 170 |
1 files changed, 138 insertions, 32 deletions
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 9949786e52..a029ab6e84 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -55,6 +55,7 @@ typedef struct SCSIDiskReq { uint64_t sector; uint32_t sector_count; uint32_t buflen; + bool started; struct iovec iov; QEMUIOVector qiov; BlockAcctCookie acct; @@ -153,14 +154,80 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) qemu_iovec_init_external(&r->qiov, &r->iov, 1); } -static void scsi_dma_complete(void *opaque, int ret) +static void scsi_flush_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); bdrv_acct_done(s->qdev.conf.bs, &r->acct); - if (ret) { + if (ret < 0) { + if (scsi_handle_rw_error(r, -ret)) { + goto done; + } + } + + scsi_req_complete(&r->req, GOOD); + +done: + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } +} + +static bool scsi_is_cmd_fua(SCSICommand *cmd) +{ + switch (cmd->buf[0]) { + case READ_10: + case READ_12: + case READ_16: + case WRITE_10: + case WRITE_12: + case WRITE_16: + return (cmd->buf[1] & 8) != 0; + + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: + case WRITE_VERIFY_10: + case WRITE_VERIFY_12: + case WRITE_VERIFY_16: + return true; + + case READ_6: + case WRITE_6: + default: + return false; + } +} + +static void scsi_write_do_fua(SCSIDiskReq *r) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + + if (scsi_is_cmd_fua(&r->req.cmd)) { + bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH); + r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r); + return; + } + + scsi_req_complete(&r->req, GOOD); + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } +} + +static void scsi_dma_complete(void *opaque, int ret) +{ + SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + + if (r->req.aiocb != NULL) { + r->req.aiocb = NULL; + bdrv_acct_done(s->qdev.conf.bs, &r->acct); + } + + if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { goto done; } @@ -168,10 +235,17 @@ static void scsi_dma_complete(void *opaque, int ret) r->sector += r->sector_count; r->sector_count = 0; - scsi_req_complete(&r->req, GOOD); + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { + scsi_write_do_fua(r); + return; + } else { + scsi_req_complete(&r->req, GOOD); + } done: - scsi_req_unref(&r->req); + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } static void scsi_read_complete(void * opaque, int ret) @@ -185,7 +259,7 @@ static void scsi_read_complete(void * opaque, int ret) bdrv_acct_done(s->qdev.conf.bs, &r->acct); } - if (ret) { + if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { goto done; } @@ -204,10 +278,12 @@ done: } } -static void scsi_flush_complete(void * opaque, int ret) +/* Actually issue a read to the block device. */ +static void scsi_do_read(void *opaque, int ret) { - SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskReq *r = opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + uint32_t n; if (r->req.aiocb != NULL) { r->req.aiocb = NULL; @@ -220,7 +296,17 @@ static void scsi_flush_complete(void * opaque, int ret) } } - scsi_req_complete(&r->req, GOOD); + if (r->req.sg) { + dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ); + r->req.resid -= r->req.sg->size; + r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector, + scsi_dma_complete, r); + } else { + n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); + bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n, + scsi_read_complete, r); + } done: if (!r->req.io_canceled) { @@ -233,11 +319,12 @@ static void scsi_read_data(SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint32_t n; + bool first; if (r->sector_count == (uint32_t)-1) { DPRINTF("Read buf_len=%zd\n", r->iov.iov_len); r->sector_count = 0; + r->started = true; scsi_req_data(&r->req, r->iov.iov_len); return; } @@ -264,16 +351,13 @@ static void scsi_read_data(SCSIRequest *req) return; } - if (r->req.sg) { - dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ); - r->req.resid -= r->req.sg->size; - r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector, - scsi_dma_complete, r); + first = !r->started; + r->started = true; + if (first && scsi_is_cmd_fua(&r->req.cmd)) { + bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH); + r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_do_read, r); } else { - n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); - r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n, - scsi_read_complete, r); + scsi_do_read(r, 0); } } @@ -333,7 +417,7 @@ static void scsi_write_complete(void * opaque, int ret) bdrv_acct_done(s->qdev.conf.bs, &r->acct); } - if (ret) { + if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { goto done; } @@ -343,7 +427,8 @@ static void scsi_write_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_req_complete(&r->req, GOOD); + scsi_write_do_fua(r); + return; } else { scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size); @@ -375,6 +460,7 @@ static void scsi_write_data(SCSIRequest *req) if (!r->req.sg && !r->qiov.size) { /* Called for the first time. Ask the driver to send us more data. */ + r->started = true; scsi_write_complete(r, 0); return; } @@ -383,6 +469,16 @@ static void scsi_write_data(SCSIRequest *req) return; } + if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 || + r->req.cmd.buf[0] == VERIFY_16) { + if (r->req.sg) { + scsi_dma_complete(r, 0); + } else { + scsi_write_complete(r, 0); + } + return; + } + if (r->req.sg) { dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE); r->req.resid -= r->req.sg->size; @@ -531,8 +627,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { outbuf[3] = buflen = 8; outbuf[4] = 0; - outbuf[5] = 0x40; /* write same with unmap supported */ - outbuf[6] = 0; + outbuf[5] = 0x60; /* write_same 10/16 supported */ + outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; outbuf[7] = 0; break; } @@ -984,11 +1080,12 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint64_t nb_sectors; - int page, dbd, buflen, ret, page_control; + bool dbd; + int page, buflen, ret, page_control; uint8_t *p; uint8_t dev_specific_param; - dbd = r->req.cmd.buf[1] & 0x8; + dbd = (r->req.cmd.buf[1] & 0x8) != 0; page = r->req.cmd.buf[2] & 0x3f; page_control = (r->req.cmd.buf[2] & 0xc0) >> 6; DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n", @@ -996,10 +1093,16 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) memset(outbuf, 0, r->req.cmd.xfer); p = outbuf; - if (bdrv_is_read_only(s->qdev.conf.bs)) { - dev_specific_param = 0x80; /* Readonly. */ + if (s->qdev.type == TYPE_DISK) { + dev_specific_param = 0x10; /* DPOFUA */ + if (bdrv_is_read_only(s->qdev.conf.bs)) { + dev_specific_param |= 0x80; /* Readonly. */ + } } else { + /* MMC prescribes that CD/DVD drives have no block descriptors, + * and defines no device-specific parameter. */ dev_specific_param = 0x00; + dbd = true; } if (r->req.cmd.buf[0] == MODE_SENSE) { @@ -1014,9 +1117,8 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) p += 8; } - /* MMC prescribes that CD/DVD drives have no block descriptors. */ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); - if (!dbd && s->qdev.type == TYPE_DISK && nb_sectors) { + if (!dbd && nb_sectors) { if (r->req.cmd.buf[0] == MODE_SENSE) { outbuf[3] = 8; /* Block descriptor length */ } else { /* MODE_SENSE_10 */ @@ -1306,8 +1408,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) } DPRINTF("Unsupported Service Action In\n"); goto illegal_request; - case VERIFY_10: - break; default: scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); return -1; @@ -1391,7 +1491,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case MECHANISM_STATUS: case SERVICE_ACTION_IN_16: case REQUEST_SENSE: - case VERIFY_10: rc = scsi_disk_emulate_command(r); if (rc < 0) { return 0; @@ -1417,6 +1516,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); r->sector_count = len * (s->qdev.blocksize / 512); break; + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: case WRITE_6: case WRITE_10: case WRITE_12: @@ -1456,10 +1558,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) goto illegal_lba; } break; + case WRITE_SAME_10: case WRITE_SAME_16: len = r->req.cmd.xfer / s->qdev.blocksize; - DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n", + DPRINTF("WRITE SAME() (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); if (r->req.cmd.lba > s->qdev.max_lba) { @@ -1767,6 +1870,9 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, case READ_10: case READ_12: case READ_16: + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: case WRITE_6: case WRITE_10: case WRITE_12: |