From: Marcelo Tosatti <mtosa...@redhat.com> Mirrored writes are used by live block copy.
The blkmirror driver is for internal use only, because it requires bdrv_append to set up a backing_hd for it. It relies on a quirk of bdrv_append, which leaves the old image open for writes. The source is hardcoded as the backing_hd for the destination, so that copy-on-write functions properly. Since the source is not yet available at the time blkmirror_open is called, the backing_hd is set later. Signed-off-by: Marcelo Tosatti <mtosa...@redhat.com> Signed-off-by: Federico Simoncelli <fsimo...@redhat.com> Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- This version of the driver is almost entirely rewritten to use bs->backing_hd and bs->file. This is necessary in order to share as much code as possible with group snapshots. Makefile.objs | 2 +- block/blkmirror.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++ docs/blkmirror.txt | 16 ++++++ 3 files changed, 170 insertions(+), 1 deletions(-) create mode 100644 block/blkmirror.c create mode 100644 docs/blkmirror.txt 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..4862364 --- /dev/null +++ b/block/blkmirror.c @@ -0,0 +1,153 @@ +/* + * Block driver for mirrored writes. + * + * Copyright (C) 2011 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" + +/* Valid blkmirror filenames look like + * blkmirror:path/to/image1:path/to/image2 */ +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 int blkmirror_co_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov) +{ + return bdrv_co_readv(bs->backing_hd, sector_num, nb_sectors, qiov); +} + +static int blkmirror_co_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov) +{ + int ret; + + /* bs->backing_hd is set after initialization. */ + bs->file->backing_hd = bs->backing_hd; + + ret = bdrv_co_writev(bs->backing_hd, sector_num, nb_sectors, qiov); + if (ret >= 0) { + ret = bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); + } + + return ret; +} + +static coroutine_fn int blkmirror_co_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors) +{ + int ret; + + ret = bdrv_co_discard(bs->backing_hd, sector_num, nb_sectors); + if (ret >= 0) { + ret = bdrv_co_discard(bs->file, sector_num, nb_sectors); + } + + return ret; +} + + +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_discard = blkmirror_co_discard, + + .bdrv_co_is_allocated = blkmirror_co_is_allocated, + .bdrv_co_readv = blkmirror_co_readv, + .bdrv_co_writev = blkmirror_co_writev, +}; + +static void bdrv_blkmirror_init(void) +{ + bdrv_register(&bdrv_blkmirror); +} + +block_init(bdrv_blkmirror_init); diff --git a/docs/blkmirror.txt b/docs/blkmirror.txt new file mode 100644 index 0000000..cf73f3f --- /dev/null +++ b/docs/blkmirror.txt @@ -0,0 +1,16 @@ +Block mirror driver +------------------- + +This driver will mirror writes to two distinct images. +It's used internally by live block copy. + +Format +------ + +blkmirror:/image1.img:/image2.img + +'\' (backslash) can be used to escape colon processing +as a separator, in the first image filename. +Backslashes themselves also can be escaped as '\\'. + + -- 1.7.7.6