From b7c8c35f0afb62efcacd18a64067fe164e3206b6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 6 Apr 2012 14:12:42 +0200 Subject: [PATCH 01/16] scsi: fix memory leak scsibus_get_dev_path is leaking id if it is not NULL. Fix it. Reported-by: Laszlo Ersek Signed-off-by: Paolo Bonzini --- hw/scsi-bus.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 8e76c5d32c..d847396c53 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1430,15 +1430,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) From fcf104a74cb8d6d39dc935ab692afface3beab30 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 6 Apr 2012 10:20:43 +0200 Subject: [PATCH 02/16] virtio-scsi: prepare migration format for multiqueue In order to restore requests correctly from a multitude of virtqueues, we need to store the id of the request queue that each request came from. Do this even for single-queue, by storing a hard-coded zero, to simplify future implementation of multiqueue. Signed-off-by: Paolo Bonzini --- hw/virtio-scsi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index 45d54faeb5..0d90d9c159 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -240,7 +240,9 @@ 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 = 0; + qemu_put_be32s(f, &n); qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); } @@ -249,8 +251,11 @@ 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 == 0); qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); virtio_scsi_parse_req(s, s->cmd_vq, req); From c80decdbd9ea679a17f6b0202ea1df0f840e4828 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 6 Apr 2012 10:38:37 +0200 Subject: [PATCH 03/16] virtio: add virtio_queue_get_id Serializing virtio-scsi requests needs a simple way to get from a VirtQueue to the number of the queue. The virtio_queue_get_id provides this. Signed-off-by: Paolo Bonzini --- hw/virtio.c | 7 +++++++ hw/virtio.h | 1 + 2 files changed, 8 insertions(+) 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); From d2ad7dd46e72118a577e16b3c6dffdc43c961476 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 6 Apr 2012 10:39:46 +0200 Subject: [PATCH 04/16] virtio-scsi: add multiqueue capability Adding multiqueue is as simple as creating more than one virtqueues, and saving the queue number for each request. Signed-off-by: Paolo Bonzini --- hw/virtio-scsi.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index 0d90d9c159..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,8 +240,9 @@ 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 = 0; + 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)); } @@ -255,9 +256,9 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) req = g_malloc(sizeof(*req)); qemu_get_be32s(f, &n); - assert(n == 0); + 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; @@ -584,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; @@ -602,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) { From b8aba8d7e3031ee3411a8e5eb07ac61f5b18f045 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 10:09:49 +0200 Subject: [PATCH 05/16] scsi: add missing test for cancelled request Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 9949786e52..792e9d045a 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -171,7 +171,9 @@ static void scsi_dma_complete(void *opaque, int ret) 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) From 80624c938d2d9d2b2cca56326876f213c31e1202 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 14:00:24 +0200 Subject: [PATCH 06/16] scsi: make code more homogeneous in AIO callback functions First scsi_flush_complete, like scsi_dma_complete, is always called with an active AIOCB. Second, always test for "ret < 0" to check for errors. Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 792e9d045a..1664793387 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -160,7 +160,7 @@ static void scsi_dma_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; } @@ -187,7 +187,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; } @@ -211,10 +211,7 @@ static void scsi_flush_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); - } + bdrv_acct_done(s->qdev.conf.bs, &r->acct); if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -335,7 +332,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; } From b77912a77a613451e9aac9a12f721eb5e9f09185 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 10:05:04 +0200 Subject: [PATCH 07/16] scsi: move scsi_flush_complete around Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 1664793387..158ed5b2fb 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -153,6 +153,27 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) qemu_iovec_init_external(&r->qiov, &r->iov, 1); } +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 < 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 void scsi_dma_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -206,27 +227,6 @@ done: } } -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 < 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); - } -} - /* Read more data from scsi device into buffer. */ static void scsi_read_data(SCSIRequest *req) { From 7e8c49c56154ab5c45d4f07edf0c22728735da35 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 10:10:54 +0200 Subject: [PATCH 08/16] scsi: add support for FUA on writes To force unit access, add a flush operation after the actual write. WRITE AND VERIFY commands always flush according to SBC, so do it even though we do not perform the reread. Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 158ed5b2fb..7e6e17dfad 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -174,6 +174,45 @@ done: } } +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 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; @@ -189,7 +228,12 @@ 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: if (!r->req.io_canceled) { @@ -342,7 +386,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); From 7f64f8e2c3c5a02636c2a6b8d6e6c5f7a62b89f7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 11:46:13 +0200 Subject: [PATCH 09/16] scsi: force unit access on VERIFY Also DMA data from the host, to avoid that the host reports an underrun. Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 7e6e17dfad..09c2715aec 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -185,6 +185,9 @@ static bool scsi_is_cmd_fua(SCSICommand *cmd) 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: @@ -218,7 +221,10 @@ static void scsi_dma_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 (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)) { @@ -427,6 +433,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; @@ -1350,8 +1366,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; @@ -1435,7 +1449,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; @@ -1461,6 +1474,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: @@ -1811,6 +1827,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: From a0e66a699e41f27cd70833045a71ddc52801dbb3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 11:51:42 +0200 Subject: [PATCH 10/16] scsi: add a started field to SCSIDiskReq Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 09c2715aec..428d83132f 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; @@ -287,6 +288,7 @@ static void scsi_read_data(SCSIRequest *req) 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; } @@ -313,6 +315,7 @@ static void scsi_read_data(SCSIRequest *req) return; } + r->started = true; 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; @@ -425,6 +428,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; } From ac6684264642f1aea7cba5c0c3907409b1f7f904 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 11:55:28 +0200 Subject: [PATCH 11/16] scsi: support FUA on reads To force unit access on reads, flush the cache *before* doing the read. Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 52 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 428d83132f..30ed3afa01 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -278,12 +278,48 @@ done: } } +/* Actually issue a read to the block device. */ +static void scsi_do_read(void *opaque, int ret) +{ + SCSIDiskReq *r = opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + uint32_t n; + + 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; + } + } + + 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) { + scsi_req_unref(&r->req); + } +} + /* Read more data from scsi device into buffer. */ 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); @@ -315,17 +351,13 @@ static void scsi_read_data(SCSIRequest *req) return; } + first = !r->started; r->started = true; - 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); + 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); } } From e590ecbed5da8716b83b348d26edb9e60b4e91c4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 10:24:18 +0200 Subject: [PATCH 12/16] scsi: small refactoring of MMC mode-sense Make DBD a boolean value, and force device-specific parameter to zero. Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 30ed3afa01..2d9ac2d2a8 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -1080,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", @@ -1092,10 +1093,15 @@ 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. */ + dev_specific_param = 0x00; + if (s->qdev.type == TYPE_DISK) { + if (bdrv_is_read_only(s->qdev.conf.bs)) { + dev_specific_param |= 0x80; /* Readonly. */ + } } else { - dev_specific_param = 0x00; + /* MMC prescribes that CD/DVD drives have no block descriptors, + * and defines no device-specific parameter. */ + dbd = true; } if (r->req.cmd.buf[0] == MODE_SENSE) { @@ -1110,9 +1116,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 */ From 6a2de0f2038349716049c9b9a972d6777c85424b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 10:28:15 +0200 Subject: [PATCH 13/16] scsi: advertise DPOFUA Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 2d9ac2d2a8..fca31fa6e7 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -1093,14 +1093,15 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) memset(outbuf, 0, r->req.cmd.xfer); p = outbuf; - dev_specific_param = 0x00; 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; } From c9e4d8284e1f3205aa3c05dc18932f9ca2c05f53 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 19 Apr 2012 20:41:16 +1000 Subject: [PATCH 14/16] SCSI emulation: Support unmap via WRITE_SAME_10. This was added in SBC r26 in place of the reserved bits that were present up to that version. It is the same as WRITE_SAME_16 as far as QEMU is concerned. Signed-off-by: Ronnie Sahlberg Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index fca31fa6e7..85a75c4b29 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -627,7 +627,7 @@ 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[5] = 0x60; /* write_same 10/16 supported */ outbuf[6] = 0; outbuf[7] = 0; break; @@ -1558,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) { From f644a2904d5287332e4007f30c105a8622fb3db8 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 19 Apr 2012 20:41:17 +1000 Subject: [PATCH 15/16] SCSI emulation: should tell the guest that we actually support thin provisioning Signed-off-by: Ronnie Sahlberg [Actually, we should report it only if discard_granularity is nonzero. Older SBC drafts assigned 0 to thin provisioning and 1 to thick (resource-provisioned, they call it). Newer drafts assign respectively 1 and 2 - Paolo] Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 85a75c4b29..a029ab6e84 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -628,7 +628,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) outbuf[3] = buflen = 8; outbuf[4] = 0; outbuf[5] = 0x60; /* write_same 10/16 supported */ - outbuf[6] = 0; + outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; outbuf[7] = 0; break; } From 3e46d87d665bd9114f18e8d4a1b6de3641147775 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Apr 2012 15:43:41 +0200 Subject: [PATCH 16/16] scsi: add SANITIZE command Signed-off-by: Paolo Bonzini --- hw/scsi-bus.c | 1 + hw/scsi-defs.h | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index d847396c53..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", 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