diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2012-04-23 09:57:47 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2012-04-23 09:57:47 -0500 |
commit | 4a1873fc8c3f692fcae25ed1ebf10003d5d5feb0 (patch) | |
tree | fbc85c54f41671fc2c963f4327918e502b0e5521 /hw | |
parent | e5e3895702514253120965cfda75c58f28c39a00 (diff) | |
parent | 3e46d87d665bd9114f18e8d4a1b6de3641147775 (diff) |
Merge remote-tracking branch 'bonzini/scsi-next' into staging
* bonzini/scsi-next:
scsi: add SANITIZE command
SCSI emulation: should tell the guest that we actually support thin provisioning
SCSI emulation: Support unmap via WRITE_SAME_10.
scsi: advertise DPOFUA
scsi: small refactoring of MMC mode-sense
scsi: support FUA on reads
scsi: add a started field to SCSIDiskReq
scsi: force unit access on VERIFY
scsi: add support for FUA on writes
scsi: move scsi_flush_complete around
scsi: make code more homogeneous in AIO callback functions
scsi: add missing test for cancelled request
virtio-scsi: add multiqueue capability
virtio: add virtio_queue_get_id
virtio-scsi: prepare migration format for multiqueue
scsi: fix memory leak
Diffstat (limited to 'hw')
-rw-r--r-- | hw/scsi-bus.c | 8 | ||||
-rw-r--r-- | hw/scsi-defs.h | 1 | ||||
-rw-r--r-- | hw/scsi-disk.c | 170 | ||||
-rw-r--r-- | hw/virtio-scsi.c | 26 | ||||
-rw-r--r-- | hw/virtio.c | 7 | ||||
-rw-r--r-- | hw/virtio.h | 1 |
6 files changed, 171 insertions, 42 deletions
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 8e76c5d32c..dbdb99ce35 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1200,6 +1200,7 @@ static const char *scsi_command_name(uint8_t cmd) [ UNMAP ] = "UNMAP", [ READ_TOC ] = "READ_TOC", [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", + [ SANITIZE ] = "SANITIZE", [ GET_CONFIGURATION ] = "GET_CONFIGURATION", [ LOG_SELECT ] = "LOG_SELECT", [ LOG_SENSE ] = "LOG_SENSE", @@ -1430,15 +1431,18 @@ static char *scsibus_get_dev_path(DeviceState *dev) SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev); DeviceState *hba = dev->parent_bus->parent; char *id = NULL; + char *path; if (hba && hba->parent_bus && hba->parent_bus->info->get_dev_path) { id = hba->parent_bus->info->get_dev_path(hba); } if (id) { - return g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun); + path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun); } else { - return g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun); + path = g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun); } + g_free(id); + return path; } static char *scsibus_get_fw_dev_path(DeviceState *dev) diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index 354ed7b55b..ca24192d53 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -78,6 +78,7 @@ #define READ_TOC 0x43 #define REPORT_DENSITY_SUPPORT 0x44 #define GET_CONFIGURATION 0x46 +#define SANITIZE 0x48 #define GET_EVENT_STATUS_NOTIFICATION 0x4a #define LOG_SELECT 0x4c #define LOG_SENSE 0x4d 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: diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index 45d54faeb5..e8328f4652 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -129,12 +129,12 @@ typedef struct { VirtIOSCSIConf *conf; SCSIBus bus; - VirtQueue *ctrl_vq; - VirtQueue *event_vq; - VirtQueue *cmd_vq; uint32_t sense_size; uint32_t cdb_size; int resetting; + VirtQueue *ctrl_vq; + VirtQueue *event_vq; + VirtQueue *cmd_vqs[0]; } VirtIOSCSI; typedef struct VirtIOSCSIReq { @@ -240,7 +240,10 @@ static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq) { VirtIOSCSIReq *req = sreq->hba_private; + uint32_t n = virtio_queue_get_id(req->vq) - 2; + assert(n < req->dev->conf->num_queues); + qemu_put_be32s(f, &n); qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); } @@ -249,10 +252,13 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) SCSIBus *bus = sreq->bus; VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIOSCSIReq *req; + uint32_t n; req = g_malloc(sizeof(*req)); + qemu_get_be32s(f, &n); + assert(n < s->conf->num_queues); qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); - virtio_scsi_parse_req(s, s->cmd_vq, req); + virtio_scsi_parse_req(s, s->cmd_vqs[n], req); scsi_req_ref(sreq); req->sreq = sreq; @@ -579,10 +585,12 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf) { VirtIOSCSI *s; static int virtio_scsi_id; + size_t sz; + int i; + sz = sizeof(VirtIOSCSI) + proxyconf->num_queues * sizeof(VirtQueue *); s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI, - sizeof(VirtIOSCSIConfig), - sizeof(VirtIOSCSI)); + sizeof(VirtIOSCSIConfig), sz); s->qdev = dev; s->conf = proxyconf; @@ -597,8 +605,10 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf) virtio_scsi_handle_ctrl); s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, NULL); - s->cmd_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, - virtio_scsi_handle_cmd); + for (i = 0; i < s->conf->num_queues; i++) { + s->cmd_vqs[i] = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, + virtio_scsi_handle_cmd); + } scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info); if (!dev->hotplugged) { diff --git a/hw/virtio.c b/hw/virtio.c index 064aecf553..314abf8a18 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -624,6 +624,13 @@ int virtio_queue_get_num(VirtIODevice *vdev, int n) return vdev->vq[n].vring.num; } +int virtio_queue_get_id(VirtQueue *vq) +{ + VirtIODevice *vdev = vq->vdev; + assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_PCI_QUEUE_MAX]); + return vq - &vdev->vq[0]; +} + void virtio_queue_notify_vq(VirtQueue *vq) { if (vq->vring.desc) { diff --git a/hw/virtio.h b/hw/virtio.h index 400c092c95..0aef7d1bc0 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -229,6 +229,7 @@ target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n); uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n); void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx); VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n); +int virtio_queue_get_id(VirtQueue *vq); EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq); EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq); void virtio_queue_notify_vq(VirtQueue *vq); |