static void vhost_scsi_set_config(VirtIODevice *vdev, const uint8_t *config) { VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); if ((uint32_t) virtio_ldl_p(vdev, &scsiconf->sense_size) != vs->sense_size || (uint32_t) virtio_ldl_p(vdev, &scsiconf->cdb_size) != vs->cdb_size) { error_report("vhost-scsi does not support changing the sense data and CDB sizes"); exit(1); } }
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); VirtQueueElement elem; MemoryRegionSection section; while (virtqueue_pop(vq, &elem)) { size_t offset = 0; uint32_t pfn; while (iov_to_buf(elem.out_sg, elem.out_num, offset, &pfn, 4) == 4) { ram_addr_t pa; ram_addr_t addr; int p = virtio_ldl_p(vdev, &pfn); pa = (ram_addr_t) p << VIRTIO_BALLOON_PFN_SHIFT; offset += 4; /* FIXME: remove get_system_memory(), but how? */ section = memory_region_find(get_system_memory(), pa, 1); if (!int128_nz(section.size) || !memory_region_is_ram(section.mr)) continue; /* Using memory_region_get_ram_ptr is bending the rules a bit, but should be OK because we only want a single page. */ addr = section.offset_within_region; balloon_page(memory_region_get_ram_ptr(section.mr) + addr, !!(vq == s->dvq)); memory_region_unref(section.mr); } virtqueue_push(vq, &elem, offset); virtio_notify(vdev, vq); } }
static void virtio_blk_rw_complete(void *opaque, int ret) { VirtIOBlockReq *next = opaque; while (next) { VirtIOBlockReq *req = next; next = req->mr_next; trace_virtio_blk_rw_complete(req, ret); if (req->qiov.nalloc != -1) { /* If nalloc is != 1 req->qiov is a local copy of the original * external iovec. It was allocated in submit_merged_requests * to be able to merge requests. */ qemu_iovec_destroy(&req->qiov); } if (ret) { int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); bool is_read = !(p & VIRTIO_BLK_T_OUT); if (virtio_blk_handle_rw_error(req, -ret, is_read)) { continue; } } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); block_acct_done(blk_get_stats(req->dev->blk), &req->acct); virtio_blk_free_request(req); } }
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); VirtQueueElement *elem; MemoryRegionSection section; for (;;) { size_t offset = 0; uint32_t pfn; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { return; } while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) { hwaddr pa; int p = virtio_ldl_p(vdev, &pfn); pa = (hwaddr) p << VIRTIO_BALLOON_PFN_SHIFT; offset += 4; section = memory_region_find(get_system_memory(), pa, BALLOON_PAGE_SIZE); if (!section.mr) { trace_virtio_balloon_bad_addr(pa); continue; } if (!memory_region_is_ram(section.mr) || memory_region_is_rom(section.mr) || memory_region_is_romd(section.mr)) { trace_virtio_balloon_bad_addr(pa); memory_region_unref(section.mr); continue; } trace_virtio_balloon_handle_output(memory_region_name(section.mr), pa); if (!qemu_balloon_is_inhibited()) { if (vq == s->ivq) { balloon_inflate_page(s, section.mr, section.offset_within_region); } else if (vq == s->dvq) { balloon_deflate_page(s, section.mr, section.offset_within_region); } else { g_assert_not_reached(); } } memory_region_unref(section.mr); } virtqueue_push(vq, elem, offset); virtio_notify(vdev, vq); g_free(elem); } }
static void virtio_blk_rw_complete(void *opaque, int ret) { VirtIOBlockReq *req = opaque; trace_virtio_blk_rw_complete(req, ret); if (ret) { int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); bool is_read = !(p & VIRTIO_BLK_T_OUT); if (virtio_blk_handle_rw_error(req, -ret, is_read)) return; } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); bdrv_acct_done(req->dev->bs, &req->acct); virtio_blk_free_request(req); }
static void virtio_blk_rw_complete(void *opaque, int ret) { VirtIOBlockReq *next = opaque; while (next) { VirtIOBlockReq *req = next; next = req->mr_next; trace_virtio_blk_rw_complete(req, ret); if (req->qiov.nalloc != -1) { /* If nalloc is != 1 req->qiov is a local copy of the original * external iovec. It was allocated in submit_merged_requests * to be able to merge requests. */ qemu_iovec_destroy(&req->qiov); } if (ret) { int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); bool is_read = !(p & VIRTIO_BLK_T_OUT); /* Note that memory may be dirtied on read failure. If the * virtio request is not completed here, as is the case for * BLOCK_ERROR_ACTION_STOP, the memory may not be copied * correctly during live migration. While this is ugly, * it is acceptable because the device is free to write to * the memory until the request is completed (which will * happen on the other side of the migration). */ if (virtio_blk_handle_rw_error(req, -ret, is_read)) { /* Break the link in case the next request is added to the * restart queue and is going to be parsed from the ring again. */ req->mr_next = NULL; continue; } } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); block_acct_done(blk_get_stats(req->dev->blk), &req->acct); virtio_blk_free_request(req); } }
static void virtio_blk_rw_complete(void *opaque, int ret) { VirtIOBlockReq *next = opaque; VirtIOBlock *s = next->dev; VirtIODevice *vdev = VIRTIO_DEVICE(s); aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); while (next) { VirtIOBlockReq *req = next; next = req->mr_next; trace_virtio_blk_rw_complete(vdev, req, ret); if (req->qiov.nalloc != -1) { /* If nalloc is != -1 req->qiov is a local copy of the original * external iovec. It was allocated in submit_requests to be * able to merge requests. */ qemu_iovec_destroy(&req->qiov); } if (ret) { int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); bool is_read = !(p & VIRTIO_BLK_T_OUT); /* Note that memory may be dirtied on read failure. If the * virtio request is not completed here, as is the case for * BLOCK_ERROR_ACTION_STOP, the memory may not be copied * correctly during live migration. While this is ugly, * it is acceptable because the device is free to write to * the memory until the request is completed (which will * happen on the other side of the migration). */ if (virtio_blk_handle_rw_error(req, -ret, is_read, true)) { continue; } } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); block_acct_done(blk_get_stats(req->dev->blk), &req->acct); virtio_blk_free_request(req); } aio_context_release(blk_get_aio_context(s->conf.conf.blk)); }
static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) { VirtIOBlockReq *req = opaque; VirtIOBlock *s = req->dev; bool is_write_zeroes = (virtio_ldl_p(VIRTIO_DEVICE(s), &req->out.type) & ~VIRTIO_BLK_T_BARRIER) == VIRTIO_BLK_T_WRITE_ZEROES; aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); if (ret) { if (virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) { goto out; } } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); if (is_write_zeroes) { block_acct_done(blk_get_stats(s->blk), &req->acct); } virtio_blk_free_request(req); out: aio_context_release(blk_get_aio_context(s->conf.conf.blk)); }
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; if (req->elem->out_num < 1 || req->elem->in_num < 1) { error_report("virtio-blk missing headers"); exit(1); } if (unlikely(iov_to_buf(iov, out_num, 0, &req->out, sizeof(req->out)) != sizeof(req->out))) { error_report("virtio-blk request outhdr too short"); exit(1); } iov_discard_front(&iov, &out_num, sizeof(req->out)); if (in_num < 1 || in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { error_report("virtio-blk request inhdr too short"); exit(1); } req->in = (void *)in_iov[in_num - 1].iov_base + in_iov[in_num - 1].iov_len - sizeof(struct virtio_blk_inhdr); iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); if (type & VIRTIO_BLK_T_FLUSH) { virtio_blk_handle_flush(req, mrb); } else if (type & VIRTIO_BLK_T_SCSI_CMD) { virtio_blk_handle_scsi(req); } else if (type & VIRTIO_BLK_T_GET_ID) { VirtIOBlock *s = req->dev; /* * 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->blk.serial ? s->blk.serial : "", 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); 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); virtio_blk_handle_read(req); } else { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); virtio_blk_free_request(req); } }