diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2014-07-14 13:09:29 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2014-07-14 13:09:29 +0100 |
commit | 7a6d04e73fdd571234e05dcad96895fafb3f22f0 (patch) | |
tree | acf7065c975ca4056f3041c58907803239b9941e /hw | |
parent | c15a34eda0f270888a0e4676997317e1bd7894b8 (diff) | |
parent | 58ac321135af890b503ebe56d0d00e184779918f (diff) |
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block patches for 2.1.0-rc2 (v2)
# gpg: Signature made Mon 14 Jul 2014 11:04:12 BST using RSA key ID C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
* remotes/kevin/tags/for-upstream: (22 commits)
ide: Treat read/write beyond end as invalid
virtio-blk: Treat read/write beyond end as invalid
virtio-blk: Bypass error action and I/O accounting on invalid r/w
virtio-blk: Factor common checks out of virtio_blk_handle_read/write()
dma-helpers: Fix too long qiov
qtest: fix vhost-user-test compilation with old GLib
tests: Fix unterminated string output visitor enum human string
AioContext: do not rely on aio_poll(ctx, true) result to end a loop
virtio-blk: embed VirtQueueElement in VirtIOBlockReq
virtio-blk: avoid g_slice_new0() for VirtIOBlockReq and VirtQueueElement
dataplane: do not free VirtQueueElement in vring_push()
virtio-blk: avoid dataplane VirtIOBlockReq early free
block: Assert qiov length matches request length
qed: Make qiov match request size until backing file EOF
qcow2: Make qiov match request size until backing file EOF
block: Make qiov match the request size until EOF
AioContext: speed up aio_notify
test-aio: fix GSource-based timer test
block: drop aio functions that operate on the main AioContext
block: prefer aio_poll to qemu_aio_wait
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/block/dataplane/virtio-blk.c | 30 | ||||
-rw-r--r-- | hw/block/virtio-blk.c | 95 | ||||
-rw-r--r-- | hw/ide/core.c | 28 | ||||
-rw-r--r-- | hw/virtio/dataplane/vring.c | 22 |
4 files changed, 104 insertions, 71 deletions
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 4bc0729bf7..227bb15efc 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -65,43 +65,41 @@ static void complete_request_vring(VirtIOBlockReq *req, unsigned char status) { stb_p(&req->in->status, status); - vring_push(&req->dev->dataplane->vring, req->elem, + vring_push(&req->dev->dataplane->vring, &req->elem, req->qiov.size + sizeof(*req->in)); notify_guest(req->dev->dataplane); - g_slice_free(VirtIOBlockReq, req); } static void handle_notify(EventNotifier *e) { VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane, host_notifier); - - VirtQueueElement *elem; - VirtIOBlockReq *req; - int ret; - MultiReqBuffer mrb = { - .num_writes = 0, - }; + VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); event_notifier_test_and_clear(&s->host_notifier); bdrv_io_plug(s->blk->conf.bs); for (;;) { + MultiReqBuffer mrb = { + .num_writes = 0, + }; + int ret; + /* Disable guest->host notifies to avoid unnecessary vmexits */ vring_disable_notification(s->vdev, &s->vring); for (;;) { - ret = vring_pop(s->vdev, &s->vring, &elem); + VirtIOBlockReq *req = virtio_blk_alloc_request(vblk); + + ret = vring_pop(s->vdev, &s->vring, &req->elem); if (ret < 0) { - assert(elem == NULL); + virtio_blk_free_request(req); break; /* no more requests */ } - trace_virtio_blk_data_plane_process_request(s, elem->out_num, - elem->in_num, elem->index); + trace_virtio_blk_data_plane_process_request(s, req->elem.out_num, + req->elem.in_num, + req->elem.index); - req = g_slice_new(VirtIOBlockReq); - req->dev = VIRTIO_BLK(s->vdev); - req->elem = elem; virtio_blk_handle_request(req, &mrb); } diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index aec3146755..c241c5002b 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -29,18 +29,18 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" -static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) +VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) { - VirtIOBlockReq *req = g_slice_new0(VirtIOBlockReq); + VirtIOBlockReq *req = g_slice_new(VirtIOBlockReq); req->dev = s; - req->elem = g_slice_new0(VirtQueueElement); + req->qiov.size = 0; + req->next = NULL; return req; } -static void virtio_blk_free_request(VirtIOBlockReq *req) +void virtio_blk_free_request(VirtIOBlockReq *req) { if (req) { - g_slice_free(VirtQueueElement, req->elem); g_slice_free(VirtIOBlockReq, req); } } @@ -54,7 +54,7 @@ static void virtio_blk_complete_request(VirtIOBlockReq *req, trace_virtio_blk_req_complete(req, status); stb_p(&req->in->status, status); - virtqueue_push(s->vq, req->elem, req->qiov.size + sizeof(*req->in)); + virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in)); virtio_notify(vdev, s->vq); } @@ -119,7 +119,7 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) { VirtIOBlockReq *req = virtio_blk_alloc_request(s); - if (!virtqueue_pop(s->vq, req->elem)) { + if (!virtqueue_pop(s->vq, &req->elem)) { virtio_blk_free_request(req); return NULL; } @@ -252,7 +252,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) { int status; - status = virtio_blk_handle_scsi_req(req->dev, req->elem); + status = virtio_blk_handle_scsi_req(req->dev, &req->elem); virtio_blk_req_complete(req, status); virtio_blk_free_request(req); } @@ -288,6 +288,25 @@ static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) bdrv_aio_flush(req->dev->bs, virtio_blk_flush_complete, req); } +static bool virtio_blk_sect_range_ok(VirtIOBlock *dev, + uint64_t sector, size_t size) +{ + uint64_t nb_sectors = size >> BDRV_SECTOR_BITS; + uint64_t total_sectors; + + if (sector & dev->sector_mask) { + return false; + } + if (size % dev->conf->logical_block_size) { + return false; + } + bdrv_get_geometry(dev->bs, &total_sectors); + if (sector > total_sectors || nb_sectors > total_sectors - sector) { + return false; + } + return true; +} + static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) { BlockRequest *blkreq; @@ -295,19 +314,16 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) sector = virtio_ldq_p(VIRTIO_DEVICE(req->dev), &req->out.sector); - bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_WRITE); - trace_virtio_blk_handle_write(req, sector, req->qiov.size / 512); - if (sector & req->dev->sector_mask) { - virtio_blk_rw_complete(req, -EIO); - return; - } - if (req->qiov.size % req->dev->conf->logical_block_size) { - virtio_blk_rw_complete(req, -EIO); + if (!virtio_blk_sect_range_ok(req->dev, sector, req->qiov.size)) { + virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + virtio_blk_free_request(req); return; } + bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_WRITE); + if (mrb->num_writes == 32) { virtio_submit_multiwrite(req->dev->bs, mrb); } @@ -329,18 +345,15 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req) sector = virtio_ldq_p(VIRTIO_DEVICE(req->dev), &req->out.sector); - bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_READ); - trace_virtio_blk_handle_read(req, sector, req->qiov.size / 512); - if (sector & req->dev->sector_mask) { - virtio_blk_rw_complete(req, -EIO); - return; - } - if (req->qiov.size % req->dev->conf->logical_block_size) { - virtio_blk_rw_complete(req, -EIO); + if (!virtio_blk_sect_range_ok(req->dev, sector, req->qiov.size)) { + virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + virtio_blk_free_request(req); return; } + + bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_READ); bdrv_aio_readv(req->dev->bs, sector, &req->qiov, req->qiov.size / BDRV_SECTOR_SIZE, virtio_blk_rw_complete, req); @@ -349,12 +362,12 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req) void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) { uint32_t type; - struct iovec *in_iov = req->elem->in_sg; - struct iovec *iov = req->elem->out_sg; - unsigned in_num = req->elem->in_num; - unsigned out_num = req->elem->out_num; + struct iovec *in_iov = req->elem.in_sg; + struct iovec *iov = req->elem.out_sg; + unsigned in_num = req->elem.in_num; + unsigned out_num = req->elem.out_num; - if (req->elem->out_num < 1 || req->elem->in_num < 1) { + if (req->elem.out_num < 1 || req->elem.in_num < 1) { error_report("virtio-blk missing headers"); exit(1); } @@ -391,19 +404,19 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) * NB: per existing s/n string convention the string is * terminated by '\0' only when shorter than buffer. */ - strncpy(req->elem->in_sg[0].iov_base, + strncpy(req->elem.in_sg[0].iov_base, s->blk.serial ? s->blk.serial : "", - MIN(req->elem->in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); + MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); virtio_blk_free_request(req); } else if (type & VIRTIO_BLK_T_OUT) { - qemu_iovec_init_external(&req->qiov, &req->elem->out_sg[1], - req->elem->out_num - 1); + qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], + req->elem.out_num - 1); virtio_blk_handle_write(req, mrb); } else if (type == VIRTIO_BLK_T_IN || type == VIRTIO_BLK_T_BARRIER) { /* VIRTIO_BLK_T_IN is 0, so we can't just & it. */ - qemu_iovec_init_external(&req->qiov, &req->elem->in_sg[0], - req->elem->in_num - 1); + qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0], + req->elem.in_num - 1); virtio_blk_handle_read(req); } else { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); @@ -627,7 +640,7 @@ static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) while (req) { qemu_put_sbyte(f, 1); - qemu_put_buffer(f, (unsigned char *)req->elem, + qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(VirtQueueElement)); req = req->next; } @@ -652,15 +665,15 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, while (qemu_get_sbyte(f)) { VirtIOBlockReq *req = virtio_blk_alloc_request(s); - qemu_get_buffer(f, (unsigned char *)req->elem, + qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(VirtQueueElement)); req->next = s->rq; s->rq = req; - virtqueue_map_sg(req->elem->in_sg, req->elem->in_addr, - req->elem->in_num, 1); - virtqueue_map_sg(req->elem->out_sg, req->elem->out_addr, - req->elem->out_num, 0); + virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr, + req->elem.in_num, 1); + virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr, + req->elem.out_num, 0); } return 0; diff --git a/hw/ide/core.c b/hw/ide/core.c index 3a38f1e599..db191a6c3e 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -499,6 +499,18 @@ static void ide_rw_error(IDEState *s) { ide_set_irq(s->bus); } +static bool ide_sect_range_ok(IDEState *s, + uint64_t sector, uint64_t nb_sectors) +{ + uint64_t total_sectors; + + bdrv_get_geometry(s->bs, &total_sectors); + if (sector > total_sectors || nb_sectors > total_sectors - sector) { + return false; + } + return true; +} + static void ide_sector_read_cb(void *opaque, int ret) { IDEState *s = opaque; @@ -554,6 +566,11 @@ void ide_sector_read(IDEState *s) printf("sector=%" PRId64 "\n", sector_num); #endif + if (!ide_sect_range_ok(s, sector_num, n)) { + ide_rw_error(s); + return; + } + s->iov.iov_base = s->io_buffer; s->iov.iov_len = n * BDRV_SECTOR_SIZE; qemu_iovec_init_external(&s->qiov, &s->iov, 1); @@ -671,6 +688,12 @@ void ide_dma_cb(void *opaque, int ret) sector_num, n, s->dma_cmd); #endif + if (!ide_sect_range_ok(s, sector_num, n)) { + dma_buf_commit(s); + ide_dma_error(s); + return; + } + switch (s->dma_cmd) { case IDE_DMA_READ: s->bus->dma->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, @@ -790,6 +813,11 @@ void ide_sector_write(IDEState *s) n = s->req_nb_sectors; } + if (!ide_sect_range_ok(s, sector_num, n)) { + ide_rw_error(s); + return; + } + s->iov.iov_base = s->io_buffer; s->iov.iov_len = n * BDRV_SECTOR_SIZE; qemu_iovec_init_external(&s->qiov, &s->iov, 1); diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index 665a1ffcb3..67cb2b823e 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -272,7 +272,7 @@ static int get_indirect(Vring *vring, VirtQueueElement *elem, return 0; } -void vring_free_element(VirtQueueElement *elem) +static void vring_unmap_element(VirtQueueElement *elem) { int i; @@ -287,8 +287,6 @@ void vring_free_element(VirtQueueElement *elem) for (i = 0; i < elem->in_num; i++) { vring_unmap(elem->in_sg[i].iov_base, true); } - - g_slice_free(VirtQueueElement, elem); } /* This looks in the virtqueue and for the first available buffer, and converts @@ -303,14 +301,16 @@ void vring_free_element(VirtQueueElement *elem) * Stolen from linux/drivers/vhost/vhost.c. */ int vring_pop(VirtIODevice *vdev, Vring *vring, - VirtQueueElement **p_elem) + VirtQueueElement *elem) { struct vring_desc desc; unsigned int i, head, found = 0, num = vring->vr.num; uint16_t avail_idx, last_avail_idx; - VirtQueueElement *elem = NULL; int ret; + /* Initialize elem so it can be safely unmapped */ + elem->in_num = elem->out_num = 0; + /* If there was a fatal error then refuse operation */ if (vring->broken) { ret = -EFAULT; @@ -342,10 +342,8 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, * the index we've seen. */ head = vring->vr.avail->ring[last_avail_idx % num]; - elem = g_slice_new(VirtQueueElement); elem->index = head; - elem->in_num = elem->out_num = 0; - + /* If their number is silly, that's an error. */ if (unlikely(head >= num)) { error_report("Guest says index %u > %u is available", head, num); @@ -393,7 +391,6 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, /* On success, increment avail index. */ vring->last_avail_idx++; - *p_elem = elem; return head; out: @@ -401,10 +398,7 @@ out: if (ret == -EFAULT) { vring->broken = true; } - if (elem) { - vring_free_element(elem); - } - *p_elem = NULL; + vring_unmap_element(elem); return ret; } @@ -418,7 +412,7 @@ void vring_push(Vring *vring, VirtQueueElement *elem, int len) unsigned int head = elem->index; uint16_t new; - vring_free_element(elem); + vring_unmap_element(elem); /* Don't touch vring if a fatal error occurred */ if (vring->broken) { |