[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v4 07/10] Add blkmirror block driver
From: |
Paolo Bonzini |
Subject: |
[Qemu-devel] [PATCH v4 07/10] Add blkmirror block driver |
Date: |
Tue, 6 Mar 2012 18:56:00 +0100 |
From: Marcelo Tosatti <address@hidden>
Mirrored writes are used by live block copy.
Signed-off-by: Marcelo Tosatti <address@hidden>
Signed-off-by: Federico Simoncelli <address@hidden>
Signed-off-by: Paolo Bonzini <address@hidden>
---
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
- [Qemu-devel] [PATCH v4 00/10] Mirrored block writes, Paolo Bonzini, 2012/03/06
- [Qemu-devel] [PATCH v4 03/10] qapi: complete implementation of unions, Paolo Bonzini, 2012/03/06
- [Qemu-devel] [PATCH v4 05/10] add mode field to blockdev-snapshot-sync transaction item, Paolo Bonzini, 2012/03/06
- [Qemu-devel] [PATCH v4 06/10] qmp: convert blockdev-snapshot-sync to a wrapper around transactions, Paolo Bonzini, 2012/03/06
- [Qemu-devel] [PATCH v4 02/10] fix format name for backing file, Paolo Bonzini, 2012/03/06
- [Qemu-devel] [PATCH v4 01/10] use QSIMPLEQ_FOREACH_SAFE when freeing list elements, Paolo Bonzini, 2012/03/06
- [Qemu-devel] [PATCH v4 04/10] rename blockdev-group-snapshot-sync, Paolo Bonzini, 2012/03/06
- [Qemu-devel] [PATCH v4 07/10] Add blkmirror block driver,
Paolo Bonzini <=
- [Qemu-devel] [PATCH v4 10/10] Add the drive-reopen command, Paolo Bonzini, 2012/03/06
- Re: [Qemu-devel] [PATCH v4 10/10] Add the drive-reopen command, Kevin Wolf, 2012/03/14
- Re: [Qemu-devel] [PATCH v4 10/10] Add the drive-reopen command, Paolo Bonzini, 2012/03/14
- Re: [Qemu-devel] [PATCH v4 10/10] Add the drive-reopen command, Kevin Wolf, 2012/03/14