diff options
Diffstat (limited to 'hw/block/virtio-blk.c')
-rw-r--r-- | hw/block/virtio-blk.c | 95 |
1 files changed, 54 insertions, 41 deletions
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index aec3146..c241c50 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; |