X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/cfb41c82ab9c468e599d3603ffcebeb81b6577ca..2ff6458116546ced7ce00cc39423ee30b1477e67:/hw/virtio-blk.c diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index ffac5a4d8f..2a5d1a92b3 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -11,7 +11,7 @@ * */ -#include +#include "qemu-common.h" #include "qemu-error.h" #include "trace.h" #include "blockdev.h" @@ -28,8 +28,8 @@ typedef struct VirtIOBlock void *rq; QEMUBH *bh; BlockConf *conf; + char *serial; unsigned short sector_mask; - char sn[BLOCK_SERIAL_STRLEN]; DeviceState *qdev; } VirtIOBlock; @@ -47,6 +47,7 @@ typedef struct VirtIOBlockReq struct virtio_scsi_inhdr *scsi; QEMUIOVector qiov; struct VirtIOBlockReq *next; + BlockAcctCookie acct; } VirtIOBlockReq; static void virtio_blk_req_complete(VirtIOBlockReq *req, int status) @@ -58,8 +59,6 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, int status) stb_p(&req->in->status, status); virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in)); virtio_notify(&s->vdev, s->vq); - - qemu_free(req); } static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, @@ -78,9 +77,12 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, req->next = s->rq; s->rq = req; bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read); - vm_stop(0); + vm_stop(RUN_STATE_IO_ERROR); + bdrv_iostatus_set_err(s->bs, error); } else { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + bdrv_acct_done(s->bs, &req->acct); + g_free(req); bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); } @@ -100,6 +102,8 @@ static void virtio_blk_rw_complete(void *opaque, int ret) } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + bdrv_acct_done(req->dev->bs, &req->acct); + g_free(req); } static void virtio_blk_flush_complete(void *opaque, int ret) @@ -113,11 +117,13 @@ static void virtio_blk_flush_complete(void *opaque, int ret) } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + bdrv_acct_done(req->dev->bs, &req->acct); + g_free(req); } static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) { - VirtIOBlockReq *req = qemu_malloc(sizeof(*req)); + VirtIOBlockReq *req = g_malloc(sizeof(*req)); req->dev = s; req->qiov.size = 0; req->next = NULL; @@ -130,7 +136,7 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) if (req != NULL) { if (!virtqueue_pop(s->vq, &req->elem)) { - qemu_free(req); + g_free(req); return NULL; } } @@ -155,6 +161,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) */ if (req->elem.out_num < 2 || req->elem.in_num < 3) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + g_free(req); return; } @@ -163,6 +170,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) */ if (req->elem.out_num > 2 && req->elem.in_num > 3) { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + g_free(req); return; } @@ -229,11 +237,13 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) stl_p(&req->scsi->data_len, hdr.dxfer_len); virtio_blk_req_complete(req, status); + g_free(req); } #else static void virtio_blk_handle_scsi(VirtIOBlockReq *req) { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + g_free(req); } #endif /* __linux__ */ @@ -266,6 +276,8 @@ static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) { BlockDriverAIOCB *acb; + bdrv_acct_start(req->dev->bs, &req->acct, 0, BDRV_ACCT_FLUSH); + /* * Make sure all outstanding writes are posted to the backing device. */ @@ -284,12 +296,18 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) sector = ldq_p(&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); + return; + } if (mrb->num_writes == 32) { virtio_submit_multiwrite(req->dev->bs, mrb); @@ -313,10 +331,16 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req) sector = ldq_p(&req->out->sector); + bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_READ); + 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); + return; + } acb = bdrv_aio_readv(req->dev->bs, sector, &req->qiov, req->qiov.size / BDRV_SECTOR_SIZE, @@ -354,9 +378,15 @@ static void virtio_blk_handle_request(VirtIOBlockReq *req, } else if (type & VIRTIO_BLK_T_GET_ID) { VirtIOBlock *s = req->dev; - memcpy(req->elem.in_sg[0].iov_base, s->sn, - MIN(req->elem.in_sg[0].iov_len, sizeof(s->sn))); + /* + * 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, + s->serial ? s->serial : "", + MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + g_free(req); } else if (type & VIRTIO_BLK_T_OUT) { qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], req->elem.out_num - 1); @@ -410,7 +440,8 @@ static void virtio_blk_dma_restart_bh(void *opaque) virtio_submit_multiwrite(s->bs, &mrb); } -static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason) +static void virtio_blk_dma_restart_cb(void *opaque, int running, + RunState state) { VirtIOBlock *s = opaque; @@ -514,16 +545,19 @@ static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static void virtio_blk_change_cb(void *opaque, int reason) +static void virtio_blk_resize(void *opaque) { VirtIOBlock *s = opaque; - if (reason & CHANGE_SIZE) { - virtio_notify_config(&s->vdev); - } + virtio_notify_config(&s->vdev); } -VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) +static const BlockDevOps virtio_block_ops = { + .resize_cb = virtio_blk_resize, +}; + +VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf, + char **serial) { VirtIOBlock *s; int cylinders, heads, secs; @@ -539,6 +573,14 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) return NULL; } + if (!*serial) { + /* try to fall back to value set with legacy -drive serial=... */ + dinfo = drive_get_by_blockdev(conf->bs); + if (*dinfo->serial) { + *serial = strdup(dinfo->serial); + } + } + s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config), sizeof(VirtIOBlock)); @@ -548,26 +590,21 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) s->vdev.reset = virtio_blk_reset; s->bs = conf->bs; s->conf = conf; + s->serial = *serial; s->rq = NULL; s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1; bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs); - /* NB: per existing s/n string convention the string is terminated - * by '\0' only when less than sizeof (s->sn) - */ - dinfo = drive_get_by_blockdev(s->bs); - strncpy(s->sn, dinfo->serial, sizeof (s->sn)); - s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output); qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); s->qdev = dev; register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, virtio_blk_save, virtio_blk_load, s); - bdrv_set_removable(s->bs, 0); - bdrv_set_change_cb(s->bs, virtio_blk_change_cb, s); - s->bs->buffer_alignment = conf->logical_block_size; + bdrv_set_dev_ops(s->bs, &virtio_block_ops, s); + bdrv_set_buffer_alignment(s->bs, conf->logical_block_size); + bdrv_iostatus_enable(s->bs); add_boot_device_path(conf->bootindex, dev, "/disk@0,0"); return &s->vdev; @@ -577,4 +614,5 @@ void virtio_blk_exit(VirtIODevice *vdev) { VirtIOBlock *s = to_virtio_blk(vdev); unregister_savevm(s->qdev, "virtio-blk", s); + virtio_cleanup(vdev); }