void *rq;
char serial_str[BLOCK_SERIAL_STRLEN + 1];
QEMUBH *bh;
+ size_t config_size;
} VirtIOBlock;
static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
qemu_free(req);
}
-static int virtio_blk_handle_write_error(VirtIOBlockReq *req, int error)
+static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
+ int is_read)
{
- BlockInterfaceErrorAction action = drive_get_onerror(req->dev->bs);
+ BlockInterfaceErrorAction action =
+ drive_get_on_error(req->dev->bs, is_read);
VirtIOBlock *s = req->dev;
if (action == BLOCK_ERR_IGNORE)
{
VirtIOBlockReq *req = opaque;
- if (ret && (req->out->type & VIRTIO_BLK_T_OUT)) {
- if (virtio_blk_handle_write_error(req, -ret))
+ if (ret) {
+ int is_read = !(req->out->type & VIRTIO_BLK_T_OUT);
+ if (virtio_blk_handle_rw_error(req, -ret, is_read))
return;
}
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
}
+static void virtio_blk_flush_complete(void *opaque, int ret)
+{
+ VirtIOBlockReq *req = opaque;
+
+ virtio_blk_req_complete(req, ret ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK);
+}
+
static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
{
VirtIOBlockReq *req = qemu_mallocz(sizeof(*req));
}
#endif /* __linux__ */
-static void virtio_blk_handle_write(VirtIOBlockReq *req)
+static void do_multiwrite(BlockDriverState *bs, BlockRequest *blkreq,
+ int num_writes)
{
- bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov,
- req->qiov.size / 512, virtio_blk_rw_complete, req);
+ int i, ret;
+ ret = bdrv_aio_multiwrite(bs, blkreq, num_writes);
+
+ if (ret != 0) {
+ for (i = 0; i < num_writes; i++) {
+ if (blkreq[i].error) {
+ virtio_blk_req_complete(blkreq[i].opaque, VIRTIO_BLK_S_IOERR);
+ }
+ }
+ }
+}
+
+static void virtio_blk_handle_flush(VirtIOBlockReq *req)
+{
+ BlockDriverAIOCB *acb;
+
+ acb = bdrv_aio_flush(req->dev->bs, virtio_blk_flush_complete, req);
+ if (!acb) {
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
+ }
+}
+
+static void virtio_blk_handle_write(BlockRequest *blkreq, int *num_writes,
+ VirtIOBlockReq *req, BlockDriverState **old_bs)
+{
+ if (req->dev->bs != *old_bs || *num_writes == 32) {
+ if (*old_bs != NULL) {
+ do_multiwrite(*old_bs, blkreq, *num_writes);
+ }
+ *num_writes = 0;
+ *old_bs = req->dev->bs;
+ }
+
+ blkreq[*num_writes].sector = req->out->sector;
+ blkreq[*num_writes].nb_sectors = req->qiov.size / 512;
+ blkreq[*num_writes].qiov = &req->qiov;
+ blkreq[*num_writes].cb = virtio_blk_rw_complete;
+ blkreq[*num_writes].opaque = req;
+ blkreq[*num_writes].error = 0;
+
+ (*num_writes)++;
}
static void virtio_blk_handle_read(VirtIOBlockReq *req)
{
- bdrv_aio_readv(req->dev->bs, req->out->sector, &req->qiov,
- req->qiov.size / 512, virtio_blk_rw_complete, req);
+ BlockDriverAIOCB *acb;
+
+ acb = bdrv_aio_readv(req->dev->bs, req->out->sector, &req->qiov,
+ req->qiov.size / 512, virtio_blk_rw_complete, req);
+ if (!acb) {
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
+ }
}
static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBlock *s = to_virtio_blk(vdev);
VirtIOBlockReq *req;
+ BlockRequest blkreq[32];
+ int num_writes = 0;
+ BlockDriverState *old_bs = NULL;
while ((req = virtio_blk_get_request(s))) {
if (req->elem.out_num < 1 || req->elem.in_num < 1) {
req->out = (void *)req->elem.out_sg[0].iov_base;
req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
- if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
+ if (req->out->type & VIRTIO_BLK_T_FLUSH) {
+ virtio_blk_handle_flush(req);
+ } else if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
virtio_blk_handle_scsi(req);
} else if (req->out->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);
+ virtio_blk_handle_write(blkreq, &num_writes, req, &old_bs);
} else {
qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
req->elem.in_num - 1);
virtio_blk_handle_read(req);
}
}
+
+ if (num_writes > 0) {
+ do_multiwrite(old_bs, blkreq, num_writes);
+ }
+
/*
* FIXME: Want to check for completions before returning to guest mode,
* so cached reads and writes are reported as quickly as possible. But
s->rq = NULL;
while (req) {
- virtio_blk_handle_write(req);
+ bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov,
+ req->qiov.size / 512, virtio_blk_rw_complete, req);
req = req->next;
}
}
virtio_identify_template(&blkcfg);
memcpy(&blkcfg.identify[VIRTIO_BLK_ID_SN], s->serial_str,
VIRTIO_BLK_ID_SN_BYTES);
- memcpy(config, &blkcfg, sizeof(blkcfg));
+ memcpy(config, &blkcfg, s->config_size);
}
static uint32_t virtio_blk_get_features(VirtIODevice *vdev)
features |= (1 << VIRTIO_BLK_F_SEG_MAX);
features |= (1 << VIRTIO_BLK_F_GEOMETRY);
+
+ if (bdrv_enable_write_cache(s->bs))
+ features |= (1 << VIRTIO_BLK_F_WCACHE);
#ifdef __linux__
features |= (1 << VIRTIO_BLK_F_SCSI);
#endif
if (strcmp(s->serial_str, "0"))
features |= 1 << VIRTIO_BLK_F_IDENTIFY;
+
+ if (bdrv_is_read_only(s->bs))
+ features |= 1 << VIRTIO_BLK_F_RO;
return features;
}
VirtIOBlock *s;
int cylinders, heads, secs;
static int virtio_blk_id;
- char *ps;
+ char *ps = (char *)drive_get_serial(dinfo->bdrv);
+ size_t size = strlen(ps) ? sizeof(struct virtio_blk_config) :
+ offsetof(struct virtio_blk_config, _blk_size);
s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK,
- sizeof(struct virtio_blk_config),
+ size,
sizeof(VirtIOBlock));
+ s->config_size = size;
s->vdev.get_config = virtio_blk_update_config;
s->vdev.get_features = virtio_blk_get_features;
s->vdev.reset = virtio_blk_reset;
s->bs = dinfo->bdrv;
s->rq = NULL;
- if (strlen(ps = (char *)drive_get_serial(s->bs)))
+ if (strlen(ps))
strncpy(s->serial_str, ps, sizeof(s->serial_str));
else
snprintf(s->serial_str, sizeof(s->serial_str), "0");
- s->bs->private = dev;
bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs);
bdrv_set_geometry_hint(s->bs, cylinders, heads, secs);