From: Marcelo Tosatti <mtosa...@redhat.com> Mirrored writes are used by live block copy.
Signed-off-by: Marcelo Tosatti <mtosa...@redhat.com> Signed-off-by: Federico Simoncelli <fsimo...@redhat.com> Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- Makefile.objs | 2 +- block/blkmirror.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+), 1 deletions(-) create mode 100644 block/blkmirror.c diff --git a/Makefile.objs b/Makefile.objs index 808de6a..982f44b 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -74,7 +74,7 @@ fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y)) # suppress *all* target specific code in case of system emulation, i.e. a # single QEMU executable should support all CPUs and machines. -common-obj-y = $(block-obj-y) blockdev.o +common-obj-y = $(block-obj-y) blockdev.o block/blkmirror.o common-obj-y += $(net-obj-y) common-obj-y += $(qobject-obj-y) common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX)) diff --git a/block/blkmirror.c b/block/blkmirror.c new file mode 100644 index 0000000..0ee2ca6 --- /dev/null +++ b/block/blkmirror.c @@ -0,0 +1,239 @@ +/* + * Block driver for mirrored writes. + * + * Copyright (C) 2011-2012 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <stdarg.h> +#include "block_int.h" + +typedef struct DupAIOCB DupAIOCB; + +typedef struct SingleAIOCB { + BlockDriverAIOCB *aiocb; + DupAIOCB *parent; +} SingleAIOCB; + +struct DupAIOCB { + BlockDriverAIOCB common; + int count; + int canceled; + + BlockDriverCompletionFunc *cb; + SingleAIOCB aios[2]; + int ret; +}; + +/* Valid blkmirror filenames look like blkmirror:format:path/to/target. + * + * The driver is not intended for general usage. It expects bdrv_append + * to tack it onto an existing image, which is used as the primary + * source and hardcoded to be the backing file for the target. + */ +static int blkmirror_open(BlockDriverState *bs, const char *filename, int flags) +{ + int ret, n; + const char *filename2; + char *format; + BlockDriver *drv; + + /* Parse the blkmirror: prefix */ + if (strncmp(filename, "blkmirror:", strlen("blkmirror:"))) { + return -EINVAL; + } + filename += strlen("blkmirror:"); + + /* The source image filename is added by bdrv_append. We only need + * to parse and open the destination image and format. */ + n = strcspn(filename, ":"); + if (filename[n] == 0) { + format = NULL; + filename2 = filename; + } else { + format = g_strdup(filename); + format[n] = 0; + filename2 = format + n + 1; + } + + drv = bdrv_find_whitelisted_format(format); + if (!drv) { + ret = -ENOENT; + goto out; + } + + bs->file = bdrv_new(""); + if (bs->file == NULL) { + ret = -ENOMEM; + goto out; + } + + /* If we crash, we cannot assume that the destination is a + * valid mirror and we have to start over. So speed up things + * by effectively operating on the destination in cache=unsafe + * mode. + */ + ret = bdrv_open(bs->file, filename2, + flags | BDRV_O_NO_BACKING | BDRV_O_NO_FLUSH | BDRV_O_CACHE_WB, + drv); + if (ret < 0) { + goto out; + } + +out: + g_free(format); + return ret; +} + +static void blkmirror_close(BlockDriverState *bs) +{ + bs->file->backing_hd = NULL; + + /* backing_hd and file closed by the caller. */ +} + +static coroutine_fn int blkmirror_co_flush(BlockDriverState *bs) +{ + return bdrv_co_flush(bs->backing_hd); +} + +static int64_t blkmirror_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file); +} + +static int coroutine_fn blkmirror_co_is_allocated(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + return bdrv_is_allocated(bs->file, sector_num, nb_sectors, pnum); +} + +static BlockDriverAIOCB *blkmirror_aio_readv(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + return bdrv_aio_readv(bs->backing_hd, sector_num, qiov, nb_sectors, + cb, opaque); +} + +static void dup_aio_cancel(BlockDriverAIOCB *acb) +{ + DupAIOCB *dcb = container_of(acb, DupAIOCB, common); + int i; + + dcb->canceled = true; + for (i = 0 ; i < 2; i++) { + if (dcb->aios[i].aiocb) { + bdrv_aio_cancel(dcb->aios[i].aiocb); + } + } + qemu_aio_release(dcb); +} + +static AIOPool dup_aio_pool = { + .aiocb_size = sizeof(DupAIOCB), + .cancel = dup_aio_cancel, +}; + +static void blkmirror_aio_cb(void *opaque, int ret) +{ + SingleAIOCB *scb = opaque; + DupAIOCB *dcb = scb->parent; + + scb->aiocb = NULL; + + assert(dcb->count > 0); + if (ret < 0 && dcb->ret == 0) { + dcb->ret = ret; + } + if (--dcb->count == 0) { + dcb->common.cb(dcb->common.opaque, dcb->ret); + if (!dcb->canceled) { + qemu_aio_release(dcb); + } + } +} + +static DupAIOCB *dup_aio_get(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + DupAIOCB *dcb = qemu_aio_get(&dup_aio_pool, bs, cb, opaque); + + dcb->canceled = false; + dcb->aios[0].parent = dcb; + dcb->aios[1].parent = dcb; + dcb->count = 2; + dcb->ret = 0; + return dcb; +} + +static BlockDriverAIOCB *blkmirror_aio_writev(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + DupAIOCB *dcb = dup_aio_get(bs, cb, opaque); + + /* bs->backing_hd is set after initialization. */ + bs->file->backing_hd = bs->backing_hd; + + dcb->aios[0].aiocb = bdrv_aio_writev(bs->backing_hd, sector_num, qiov, + nb_sectors, blkmirror_aio_cb, + &dcb->aios[0]); + dcb->aios[1].aiocb = bdrv_aio_writev(bs->file, sector_num, qiov, + nb_sectors, blkmirror_aio_cb, + &dcb->aios[1]); + + return &dcb->common; +} + +static BlockDriverAIOCB *blkmirror_aio_discard(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + DupAIOCB *dcb = dup_aio_get(bs, cb, opaque); + + dcb->aios[0].aiocb = bdrv_aio_discard(bs->backing_hd, sector_num, + nb_sectors, blkmirror_aio_cb, + &dcb->aios[0]); + dcb->aios[1].aiocb = bdrv_aio_discard(bs->file, sector_num, + nb_sectors, blkmirror_aio_cb, + &dcb->aios[1]); + + return &dcb->common; +} + + +static BlockDriver bdrv_blkmirror = { + .format_name = "blkmirror", + .protocol_name = "blkmirror", + .instance_size = 0, + + .bdrv_getlength = blkmirror_getlength, + + .bdrv_file_open = blkmirror_open, + .bdrv_close = blkmirror_close, + .bdrv_co_flush_to_disk = blkmirror_co_flush, + .bdrv_co_is_allocated = blkmirror_co_is_allocated, + + .bdrv_aio_readv = blkmirror_aio_readv, + .bdrv_aio_writev = blkmirror_aio_writev, + .bdrv_aio_discard = blkmirror_aio_discard, +}; + +static void bdrv_blkmirror_init(void) +{ + bdrv_register(&bdrv_blkmirror); +} + +block_init(bdrv_blkmirror_init); -- 1.7.7.6