From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:35425) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QOx4m-00008y-V1 for qemu-devel@nongnu.org; Tue, 24 May 2011 15:15:35 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QOx4j-0000El-TX for qemu-devel@nongnu.org; Tue, 24 May 2011 15:15:32 -0400 Received: from mail-qy0-f180.google.com ([209.85.216.180]:35209) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QOx4j-0000Ef-NF for qemu-devel@nongnu.org; Tue, 24 May 2011 15:15:29 -0400 Received: by qyk10 with SMTP id 10so4339411qyk.4 for ; Tue, 24 May 2011 12:15:29 -0700 (PDT) MIME-Version: 1.0 In-Reply-To: <20110523213411.003695437@amt.cnet> References: <20110523213115.164535428@amt.cnet> <20110523213411.003695437@amt.cnet> From: Blue Swirl Date: Tue, 24 May 2011 22:15:09 +0300 Message-ID: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [patch 6/7] QEMU live block copy List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Marcelo Tosatti Cc: kwolf@redhat.com, Jes.Sorensen@redhat.com, dlaor@redhat.com, qemu-devel@nongnu.org, avi@redhat.com On Tue, May 24, 2011 at 12:31 AM, Marcelo Tosatti wro= te: > Support live image copy + switch. That is, copy an image backing > a guest hard disk to a destination image (destination image must > be created separately), and switch to this copy. > > Command syntax: > > block_copy device filename [-i] -- live block copy device to image > =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 -i for incremental copy (base i= mage shared between src and destination) > > Please refer to qmp-commands diff for more details. > > Signed-off-by: Marcelo Tosatti > > Index: qemu-block-copy/block-copy.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- /dev/null > +++ qemu-block-copy/block-copy.c > @@ -0,0 +1,754 @@ > +/* > + * QEMU live block copy > + * > + * Copyright (C) 2010 Red Hat Inc. > + * > + * Authors: Marcelo Tosatti > + * > + * This work is licensed under the terms of the GNU GPL, version 2. =C2= =A0See > + * the COPYING file in the top-level directory. > + * > + */ > + > +#include "qemu-common.h" > +#include "block_int.h" > +#include "blockdev.h" > +#include "qemu-queue.h" > +#include "qemu-timer.h" > +#include "monitor.h" > +#include "block-copy.h" > +#include "migration.h" > +#include "sysemu.h" > +#include "qjson.h" > +#include > + > +#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS) > +#define MAX_IS_ALLOCATED_SEARCH 65536 > + > +/* > + * Stages: > + * > + * STAGE_BULK: bulk reads/writes in progress > + * STAGE_BULK_FINISHED: bulk reads finished, bulk writes in progress > + * STAGE_DIRTY: bulk writes finished, dirty reads/writes in progress > + * STAGE_MIRROR_WRITES: copy finished, writes mirrored to both images. > + * STAGE_SWITCH_FINISHED: switched to new image. > + */ > + > +enum BdrvCopyStage { > + =C2=A0 =C2=A0STAGE_BULK, > + =C2=A0 =C2=A0STAGE_BULK_FINISHED, > + =C2=A0 =C2=A0STAGE_DIRTY, > + =C2=A0 =C2=A0STAGE_MIRROR_WRITES, > + =C2=A0 =C2=A0STAGE_SWITCH_FINISHED, > +}; > + > +typedef struct BdrvCopyState { > + =C2=A0 =C2=A0BlockDriverState *src; > + =C2=A0 =C2=A0BlockDriverState *dst; > + =C2=A0 =C2=A0bool shared_base; > + > + =C2=A0 =C2=A0int64_t curr_sector; > + =C2=A0 =C2=A0int64_t completed_sectors; > + =C2=A0 =C2=A0int64_t nr_sectors; > + > + =C2=A0 =C2=A0enum BdrvCopyStage stage; > + =C2=A0 =C2=A0int inflight_reads; > + =C2=A0 =C2=A0int error; > + =C2=A0 =C2=A0int failed; > + =C2=A0 =C2=A0int cancelled; > + =C2=A0 =C2=A0QLIST_HEAD(, BdrvCopyBlock) io_list; > + =C2=A0 =C2=A0unsigned long *aio_bitmap; > + =C2=A0 =C2=A0QEMUTimer *aio_timer; > + =C2=A0 =C2=A0QLIST_ENTRY(BdrvCopyState) list; > + > + =C2=A0 =C2=A0int64_t blocks; > + =C2=A0 =C2=A0int64_t total_time; > + > + =C2=A0 =C2=A0char src_device_name[32]; > + =C2=A0 =C2=A0char src_filename[1024]; A #define for the buffer size would be nice. > +} BdrvCopyState; > + > +typedef struct BdrvCopyBlock { > + =C2=A0 =C2=A0BdrvCopyState *state; > + =C2=A0 =C2=A0uint8_t *buf; > + =C2=A0 =C2=A0int64_t sector; > + =C2=A0 =C2=A0int64_t nr_sectors; > + =C2=A0 =C2=A0struct iovec iov; > + =C2=A0 =C2=A0QEMUIOVector qiov; > + =C2=A0 =C2=A0BlockDriverAIOCB *aiocb; > + =C2=A0 =C2=A0int64_t time; > + =C2=A0 =C2=A0QLIST_ENTRY(BdrvCopyBlock) list; > +} BdrvCopyBlock; > + > +static QLIST_HEAD(, BdrvCopyState) block_copy_list =3D > + =C2=A0 =C2=A0QLIST_HEAD_INITIALIZER(block_copy_list); > + > +static void alloc_aio_bitmap(BdrvCopyState *s) > +{ > + =C2=A0 =C2=A0BlockDriverState *bs =3D s->src; > + =C2=A0 =C2=A0int64_t bitmap_size; > + > + =C2=A0 =C2=A0bitmap_size =3D (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0BDRV_SECTORS_PER_DIRTY_CHUNK *= 8 - 1; > + =C2=A0 =C2=A0bitmap_size /=3D BDRV_SECTORS_PER_DIRTY_CHUNK * 8; > + > + =C2=A0 =C2=A0s->aio_bitmap =3D qemu_mallocz(bitmap_size); > +} > + > +static bool aio_inflight(BdrvCopyState *s, int64_t sector) > +{ > + =C2=A0 =C2=A0int64_t chunk =3D sector / (int64_t)BDRV_SECTORS_PER_DIRTY= _CHUNK; > + > + =C2=A0 =C2=A0if (s->aio_bitmap && > + =C2=A0 =C2=A0 =C2=A0 =C2=A0(sector << BDRV_SECTOR_BITS) < bdrv_getlengt= h(s->src)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return !!(s->aio_bitmap[chunk / (sizeof(unsi= gned long) * 8)] & > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(1UL << (chunk % (sizeof(unsig= ned long) * 8)))); Please use the bitmap functions in bitmap.h, also in the next function. > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0; > + =C2=A0 =C2=A0} > +} > + > +static void set_aio_inflight(BdrvCopyState *s, int64_t sector_num, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 int nb_sectors, int set) > +{ > + =C2=A0 =C2=A0int64_t start, end; > + =C2=A0 =C2=A0unsigned long val, idx, bit; > + > + =C2=A0 =C2=A0start =3D sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK; > + =C2=A0 =C2=A0end =3D (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_D= IRTY_CHUNK; > + > + =C2=A0 =C2=A0for (; start <=3D end; start++) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0idx =3D start / (sizeof(unsigned long) * 8); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0bit =3D start % (sizeof(unsigned long) * 8); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0val =3D s->aio_bitmap[idx]; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (set) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!(val & (1UL << bit))) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0val |=3D 1UL << = bit; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (val & (1UL << bit)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0val &=3D ~(1UL <= < bit); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->aio_bitmap[idx] =3D val; > + =C2=A0 =C2=A0} > +} > + > +static void blkcopy_set_stage(BdrvCopyState *s, enum BdrvCopyStage stage= ) > +{ > + =C2=A0 =C2=A0s->stage =3D stage; > + > + =C2=A0 =C2=A0switch (stage) { > + =C2=A0 =C2=A0case STAGE_BULK: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0BLKDBG_EVENT(s->dst->file, BLKDBG_BLKCOPY_ST= AGE_BULK); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case STAGE_BULK_FINISHED: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0BLKDBG_EVENT(s->dst->file, BLKDBG_BLKCOPY_ST= AGE_BULK_FINISHED); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case STAGE_DIRTY: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0BLKDBG_EVENT(s->dst->file, BLKDBG_BLKCOPY_ST= AGE_DIRTY); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case STAGE_MIRROR_WRITES: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0BLKDBG_EVENT(s->dst->file, BLKDBG_BLKCOPY_ST= AGE_MIRROR_WRITES); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case STAGE_SWITCH_FINISHED: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0BLKDBG_EVENT(s->dst->file, BLKDBG_BLKCOPY_ST= AGE_SWITCH_FINISHED); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0} > +} > + > +static void blk_copy_handle_cb_error(BdrvCopyState *s, int ret) > +{ > + =C2=A0 =C2=A0s->error =3D ret; > + =C2=A0 =C2=A0qemu_mod_timer(s->aio_timer, qemu_get_clock_ms(rt_clock)); > +} > + > +static inline void add_avg_transfer_time(BdrvCopyState *s, int64_t time) > +{ > + =C2=A0 =C2=A0s->blocks++; > + =C2=A0 =C2=A0s->total_time +=3D time; > +} > + > +static void blk_copy_write_cb(void *opaque, int ret) > +{ > + =C2=A0 =C2=A0BdrvCopyBlock *blk =3D opaque; > + =C2=A0 =C2=A0BdrvCopyState *s =3D blk->state; > + > + =C2=A0 =C2=A0if (ret < 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0QLIST_REMOVE(blk, list); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_free(blk->buf); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_free(blk); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0blk_copy_handle_cb_error(s, ret); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0QLIST_REMOVE(blk, list); > + =C2=A0 =C2=A0add_avg_transfer_time(s, qemu_get_clock_ns(rt_clock) - blk= ->time); > + > + =C2=A0 =C2=A0/* schedule switch to STAGE_DIRTY on last bulk write compl= etion */ > + =C2=A0 =C2=A0if (blk->state->stage =3D=3D STAGE_BULK_FINISHED) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_mod_timer(s->aio_timer, qemu_get_clock_= ms(rt_clock)); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (blk->state->stage > STAGE_BULK_FINISHED) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0set_aio_inflight(blk->state, blk->sector, bl= k->nr_sectors, 0); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0qemu_free(blk->buf); > + =C2=A0 =C2=A0qemu_free(blk); > +} > + > +static void blk_copy_issue_write(BdrvCopyState *s, BdrvCopyBlock *read_b= lk) > +{ > + =C2=A0 =C2=A0BdrvCopyBlock *blk =3D qemu_mallocz(sizeof(BdrvCopyBlock))= ; > + =C2=A0 =C2=A0blk->state =3D s; > + =C2=A0 =C2=A0blk->sector =3D read_blk->sector; > + =C2=A0 =C2=A0blk->nr_sectors =3D read_blk->nr_sectors; > + =C2=A0 =C2=A0blk->time =3D read_blk->time; > + =C2=A0 =C2=A0blk->buf =3D read_blk->buf; > + =C2=A0 =C2=A0QLIST_INSERT_HEAD(&s->io_list, blk, list); > + > + =C2=A0 =C2=A0blk->iov.iov_base =3D read_blk->buf; > + =C2=A0 =C2=A0blk->iov.iov_len =3D read_blk->iov.iov_len; > + =C2=A0 =C2=A0qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); > + > + =C2=A0 =C2=A0BLKDBG_EVENT(s->dst->file, BLKDBG_BLKCOPY_AIO_WRITE); > + =C2=A0 =C2=A0blk->aiocb =3D bdrv_aio_writev(s->dst, blk->sector, &blk->= qiov, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 blk->iov.iov_len / BDRV_SECTOR_SI= ZE, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 blk_copy_write_cb, blk); > + =C2=A0 =C2=A0if (!blk->aiocb) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->error =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto error; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return; > + > +error: > + =C2=A0 =C2=A0QLIST_REMOVE(blk, list); > + =C2=A0 =C2=A0qemu_free(read_blk->buf); > + =C2=A0 =C2=A0qemu_free(blk); > +} > + > +static void blk_copy_read_cb(void *opaque, int ret) > +{ > + =C2=A0 =C2=A0BdrvCopyBlock *blk =3D opaque; > + =C2=A0 =C2=A0BdrvCopyState *s =3D blk->state; > + > + =C2=A0 =C2=A0s->inflight_reads--; > + =C2=A0 =C2=A0if (ret < 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0QLIST_REMOVE(blk, list); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_free(blk->buf); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_free(blk); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0blk_copy_handle_cb_error(s, ret); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0blk_copy_issue_write(s, blk); > + =C2=A0 =C2=A0QLIST_REMOVE(blk, list); > + =C2=A0 =C2=A0qemu_free(blk); > + =C2=A0 =C2=A0qemu_mod_timer(s->aio_timer, qemu_get_clock_ms(rt_clock)); > +} > + > +static void blk_copy_issue_read(BdrvCopyState *s, int64_t sector, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int nr_sectors) > +{ > + =C2=A0 =C2=A0BdrvCopyBlock *blk =3D qemu_mallocz(sizeof(BdrvCopyBlock))= ; > + =C2=A0 =C2=A0blk->buf =3D qemu_mallocz(BLOCK_SIZE); > + =C2=A0 =C2=A0blk->state =3D s; > + =C2=A0 =C2=A0blk->sector =3D sector; > + =C2=A0 =C2=A0blk->nr_sectors =3D nr_sectors; > + =C2=A0 =C2=A0QLIST_INSERT_HEAD(&s->io_list, blk, list); > + > + =C2=A0 =C2=A0blk->iov.iov_base =3D blk->buf; > + =C2=A0 =C2=A0blk->iov.iov_len =3D nr_sectors * BDRV_SECTOR_SIZE; > + =C2=A0 =C2=A0qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); > + > + =C2=A0 =C2=A0s->inflight_reads++; > + =C2=A0 =C2=A0blk->time =3D qemu_get_clock_ns(rt_clock); > + =C2=A0 =C2=A0blk->aiocb =3D bdrv_aio_readv(s->src, sector, &blk->qiov, = nr_sectors, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0blk_copy_read_cb, blk); > + =C2=A0 =C2=A0if (!blk->aiocb) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->error =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto error; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return; > + > +error: > + =C2=A0 =C2=A0s->inflight_reads--; > + =C2=A0 =C2=A0QLIST_REMOVE(blk, list); > + =C2=A0 =C2=A0qemu_free(blk->buf); > + =C2=A0 =C2=A0qemu_free(blk); > +} > + > +static bool blkcopy_can_switch(BdrvCopyState *s) > +{ > + =C2=A0 =C2=A0int64_t remaining_dirty; > + =C2=A0 =C2=A0int64_t avg_transfer_time; > + > + =C2=A0 =C2=A0remaining_dirty =3D bdrv_get_dirty_count(s->src); > + =C2=A0 =C2=A0if (remaining_dirty =3D=3D 0 || s->blocks =3D=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return true; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0avg_transfer_time =3D s->total_time / s->blocks; > + =C2=A0 =C2=A0if ((remaining_dirty * avg_transfer_time) <=3D migrate_max= _downtime()) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return true; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return false; > +} > + > +static int blk_issue_reads_dirty(BdrvCopyState *s) > +{ > + =C2=A0 =C2=A0int64_t sector; > + > + =C2=A0 =C2=A0for (sector =3D s->curr_sector; sector < s->nr_sectors;) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (bdrv_get_dirty(s->src, sector) && !aio_i= nflight(s, sector)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int nr_sectors =3D MIN(s->nr_s= ectors - s->curr_sector, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 BDRV_SECTORS_PER_DIRTY_CHUNK); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0blk_copy_issue_read(s, sector,= nr_sectors); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0bdrv_reset_dirty(s->src, secto= r, nr_sectors); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0set_aio_inflight(s, sector, nr= _sectors, 1); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0sector +=3D BDRV_SECTORS_PER_DIRTY_CHUNK; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->curr_sector =3D sector; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (sector >=3D s->nr_sectors) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->curr_sector =3D 0; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return 0; > +} > + > +static int blk_issue_reads_bulk(BdrvCopyState *s) > +{ > + =C2=A0 =C2=A0int nr_sectors; > + =C2=A0 =C2=A0int64_t curr_sector =3D s->curr_sector; > + > + =C2=A0 =C2=A0if (s->shared_base) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0while (curr_sector < s->nr_sectors && > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0!bdrv_is_allocat= ed(s->src, curr_sector, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 MAX_IS_ALLOCATED_SEARCH, &= nr_sectors)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0curr_sector +=3D= nr_sectors; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (curr_sector >=3D s->nr_sectors) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->curr_sector =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return 1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0curr_sector &=3D ~((int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK -= 1); > + =C2=A0 =C2=A0nr_sectors =3D BDRV_SECTORS_PER_DIRTY_CHUNK; > + > + =C2=A0 =C2=A0blk_copy_issue_read(s, s->curr_sector, nr_sectors); > + =C2=A0 =C2=A0s->curr_sector +=3D nr_sectors; > + =C2=A0 =C2=A0s->completed_sectors =3D curr_sector; > + =C2=A0 =C2=A0return 0; > +} > + > +static void blkcopy_finish(BdrvCopyState *s) > +{ > + =C2=A0 =C2=A0int64_t sector; > + =C2=A0 =C2=A0uint8_t *buf; > + > + =C2=A0 =C2=A0buf =3D qemu_malloc(BLOCK_SIZE); > + > + =C2=A0 =C2=A0/* FIXME: speed up loop, get_next_dirty_block? */ > + =C2=A0 =C2=A0for (sector =3D 0; sector < s->nr_sectors; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 sector +=3D BDRV_SECTORS_PER_DIRTY_CHUNK) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (bdrv_get_dirty(s->src, sector)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int nr_sectors =3D MIN(s->nr_s= ectors - sector, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 BDRV_SECTORS_PER_DIRTY_CHUNK); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0memset(buf, 0, BLOCK_SIZE); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (bdrv_read(s->src, sector, = buf, nr_sectors) < 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto error; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (bdrv_write(s->dst, sector,= buf, nr_sectors) < 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto error; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0bdrv_reset_dirty(s->src, secto= r, nr_sectors); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (bdrv_get_dirty_count(s->src) =3D=3D 0) > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; Braces, please use checkpatch.pl. > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0qemu_free(buf); > + =C2=A0 =C2=A0return; > + > +error: > + =C2=A0 =C2=A0qemu_free(buf); > + =C2=A0 =C2=A0s->error =3D 1; > +} > + > +static void blkcopy_cleanup(BdrvCopyState *s) > +{ > + =C2=A0 =C2=A0assert(s->inflight_reads =3D=3D 0); > + =C2=A0 =C2=A0assert(QLIST_EMPTY(&s->io_list)); > + =C2=A0 =C2=A0bdrv_set_dirty_tracking(s->src, 0); > + =C2=A0 =C2=A0drive_put_ref(drive_get_by_blockdev(s->src)); > + =C2=A0 =C2=A0bdrv_set_in_use(s->src, 0); > + =C2=A0 =C2=A0if (s->stage >=3D STAGE_DIRTY) > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_free(s->aio_bitmap); > + =C2=A0 =C2=A0qemu_del_timer(s->aio_timer); > +} > + > +static void blkcopy_free(BdrvCopyState *s) > +{ > + =C2=A0 =C2=A0QLIST_REMOVE(s, list); > + =C2=A0 =C2=A0qemu_free(s); > +} > + > +static void handle_error(BdrvCopyState *s) > +{ > + =C2=A0 =C2=A0if (!QLIST_EMPTY(&s->io_list)) > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0s->failed =3D 1; > + =C2=A0 =C2=A0blkcopy_cleanup(s); > +} > + > +static void blkcopy_switch(BdrvCopyState *s) > +{ > + =C2=A0 =C2=A0char src_filename[1024], mirror_name[2048]; #defines instead of magic constants, please. > + =C2=A0 =C2=A0int open_flags, ret; > + > + =C2=A0 =C2=A0strncpy(src_filename, s->src->filename, sizeof(src_filenam= e)); Are the lengths checked somewhere? > + =C2=A0 =C2=A0open_flags =3D s->src->open_flags; > + > + =C2=A0 =C2=A0assert(s->stage =3D=3D STAGE_DIRTY); > + > + =C2=A0 =C2=A0vm_stop(VMSTOP_BLOCKCOPY); > + =C2=A0 =C2=A0/* flush any guest writes, dirty bitmap uptodate after thi= s. > + =C2=A0 =C2=A0 * copy AIO also finished. > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0qemu_aio_flush(); > + =C2=A0 =C2=A0assert(QLIST_EMPTY(&s->io_list)); > + =C2=A0 =C2=A0if (s->error) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0handle_error(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto vm_start; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0blkcopy_finish(s); > + =C2=A0 =C2=A0if (s->error) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0handle_error(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto vm_start; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0assert(bdrv_get_dirty_count(s->src) =3D=3D 0); > + =C2=A0 =C2=A0/* turn dirty bitmap off */ > + =C2=A0 =C2=A0bdrv_set_dirty_tracking(s->src, 0); > + =C2=A0 =C2=A0/* switch to double writes */ > + =C2=A0 =C2=A0bdrv_flush_all(); > + =C2=A0 =C2=A0bdrv_close(s->src); > + =C2=A0 =C2=A0bdrv_close(s->dst); > + > + =C2=A0 =C2=A0snprintf(mirror_name, sizeof(mirror_name)-1, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "blkmirror:%s:%s", s->dst->fi= lename, s->src->filename); > + > + =C2=A0 =C2=A0ret =3D bdrv_open(s->src, mirror_name, s->src->open_flags,= NULL); > + =C2=A0 =C2=A0if (ret < 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0error_report("%s: cannot open blkmirror devi= ce, err %d", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0mirror_name, ret); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->failed =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0blkcopy_set_stage(s, STAGE_MIRROR_WRITES); > + =C2=A0 =C2=A0qemu_del_timer(s->aio_timer); > + > +vm_start: > + =C2=A0 =C2=A0vm_start(); > + =C2=A0 =C2=A0return; > + > +err: > + =C2=A0 =C2=A0if (bdrv_open(s->src, src_filename, open_flags, NULL) < 0)= { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0error_report("%s: %s: cannot fallback to sou= rce image\n", __func__, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s= ->src_filename); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0abort(); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0blkcopy_cleanup(s); > + =C2=A0 =C2=A0goto vm_start; > +} > + > +#define BLKCOPY_INFLIGHT 2 > + > +/* > + * To simplify the implementation, the IO completion callbacks do not > + * handle stage control or submit IO for further blocks. A timer is used > + * for such purpose. > + */ > + > +static void aio_timer(void *opaque) > +{ > + =C2=A0 =C2=A0BdrvCopyState *s =3D opaque; > + > + =C2=A0 =C2=A0assert(s->cancelled =3D=3D 0); > + =C2=A0 =C2=A0assert(s->stage < STAGE_MIRROR_WRITES); > + > + =C2=A0 =C2=A0if (s->error) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0handle_error(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0while (s->stage =3D=3D STAGE_BULK) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->inflight_reads >=3D BLKCOPY_INFLIGHT)= { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (blk_issue_reads_bulk(s)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0blkcopy_set_stage(s, STAGE_BUL= K_FINISHED); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (s->stage =3D=3D STAGE_BULK_FINISHED) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (QLIST_EMPTY(&s->io_list)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0blkcopy_set_stage(s, STAGE_DIR= TY); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0alloc_aio_bitmap(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0while (s->stage =3D=3D STAGE_DIRTY) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->inflight_reads >=3D BLKCOPY_INFLIGHT)= { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0blk_issue_reads_dirty(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (blkcopy_can_switch(s)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0BLKDBG_EVENT(s->dst->file, BLK= DBG_BLKCOPY_SWITCH_START); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0blkcopy_switch(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > +} > + > + > +int do_bdrv_copy_switch(Monitor *mon, const QDict *qdict, QObject **ret_= data) > +{ > + =C2=A0 =C2=A0const char *device =3D qdict_get_str(qdict, "device"); > + =C2=A0 =C2=A0BdrvCopyState *s =3D NULL; > + =C2=A0 =C2=A0int open_flags; > + > + =C2=A0 =C2=A0QLIST_FOREACH(s, &block_copy_list, list) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!strcmp(s->src_device_name, device)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->stage !=3D STAGE_MIRROR= _WRITES) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qerror_report(QE= RR_IN_PROGRESS, "block copy"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (!s) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qerror_report(QERR_DEVICE_NOT_FOUND, device)= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0open_flags =3D s->src->open_flags; > + > + =C2=A0 =C2=A0/* switch from mirrored writes to destination only */ > + =C2=A0 =C2=A0bdrv_flush_all(); > + =C2=A0 =C2=A0bdrv_close(s->src); > + =C2=A0 =C2=A0if (bdrv_open(s->src, s->dst->filename, s->src->open_flags= , NULL) < 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->failed =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0blkcopy_set_stage(s, STAGE_SWITCH_FINISHED); > + =C2=A0 =C2=A0blkcopy_cleanup(s); > + =C2=A0 =C2=A0return 0; > + > +err: > + =C2=A0 =C2=A0if (bdrv_open(s->src, s->src_filename, open_flags, NULL) <= 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0error_report("%s: %s: cannot fallback to sou= rce image\n", __func__, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s= ->src_filename); Below qerror_report() is used. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0abort(); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return -1; > +} > + > +static int bdrv_copy(Monitor *mon, const char * device, BlockDriverState= *src, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 B= lockDriverState *dst, bool shared_base) > +{ > + =C2=A0 =C2=A0int64_t sectors; > + =C2=A0 =C2=A0BdrvCopyState *blkcopy, *safe; > + > + =C2=A0 =C2=A0QLIST_FOREACH_SAFE(blkcopy, &block_copy_list, list, safe) = { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!strcmp(blkcopy->src_device_name, src->d= evice_name)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (blkcopy->stage =3D=3D STAG= E_SWITCH_FINISHED || blkcopy->failed) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0blkcopy_free(blk= copy); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qerror_report(QE= RR_IN_PROGRESS, "block copy"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0sectors =3D bdrv_getlength(src) >> BDRV_SECTOR_BITS; > + =C2=A0 =C2=A0if (sectors !=3D bdrv_getlength(dst) >> BDRV_SECTOR_BITS) = { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qerror_report(QERR_BLOCKCOPY_IMAGE_SIZE_DIFF= ERS); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0blkcopy =3D qemu_mallocz(sizeof(BdrvCopyState)); > + =C2=A0 =C2=A0blkcopy->src =3D src; > + =C2=A0 =C2=A0blkcopy->dst =3D dst; > + =C2=A0 =C2=A0blkcopy->curr_sector =3D 0; > + =C2=A0 =C2=A0blkcopy->nr_sectors =3D sectors; > + =C2=A0 =C2=A0blkcopy_set_stage(blkcopy, STAGE_BULK); > + =C2=A0 =C2=A0blkcopy->aio_timer =3D qemu_new_timer_ms(rt_clock, aio_tim= er, blkcopy); > + =C2=A0 =C2=A0blkcopy->shared_base =3D shared_base; > + =C2=A0 =C2=A0strncpy(blkcopy->src_device_name, blkcopy->src->device_nam= e, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sizeof(blkcopy->src_device_nam= e) - 1); > + =C2=A0 =C2=A0strncpy(blkcopy->src_filename, blkcopy->src->filename, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sizeof(blkcopy->src_filename) = - 1); > + > + =C2=A0 =C2=A0bdrv_set_dirty_tracking(src, 1); > + =C2=A0 =C2=A0qemu_mod_timer(blkcopy->aio_timer, qemu_get_clock_ms(rt_cl= ock)); > + =C2=A0 =C2=A0drive_get_ref(drive_get_by_blockdev(src)); > + =C2=A0 =C2=A0bdrv_set_in_use(src, 1); > + > + =C2=A0 =C2=A0QLIST_INSERT_HEAD(&block_copy_list, blkcopy, list); > + =C2=A0 =C2=A0return 0; > +} > + > +int do_bdrv_copy(Monitor *mon, const QDict *qdict, QObject **ret_data) > +{ > + =C2=A0 =C2=A0const char *device =3D qdict_get_str(qdict, "device"); > + =C2=A0 =C2=A0const char *filename =3D qdict_get_str(qdict, "filename"); > + =C2=A0 =C2=A0bool shared_base =3D qdict_get_try_bool(qdict, "incrementa= l", 0); > + =C2=A0 =C2=A0BlockDriverState *new_bs, *bs; > + =C2=A0 =C2=A0int ret; > + > + =C2=A0 =C2=A0if (migration_active()) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qerror_report(QERR_IN_PROGRESS, "migration")= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0bs =3D bdrv_find(device); > + =C2=A0 =C2=A0if (!bs) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qerror_report(QERR_DEVICE_NOT_FOUND, device)= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0new_bs =3D bdrv_new(""); > + =C2=A0 =C2=A0if (bdrv_open(new_bs, filename, bs->open_flags, NULL) < 0)= { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0bdrv_delete(new_bs); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qerror_report(QERR_OPEN_FILE_FAILED, filenam= e); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0ret =3D bdrv_copy(mon, device, bs, new_bs, shared_base); > + =C2=A0 =C2=A0return ret; > +} > + > +int do_bdrv_copy_cancel(Monitor *mon, const QDict *qdict, QObject **ret_= data) > +{ > + =C2=A0 =C2=A0BdrvCopyState *blkcopy, *s =3D NULL; > + =C2=A0 =C2=A0const char *device =3D qdict_get_str(qdict, "device"); > + > + =C2=A0 =C2=A0QLIST_FOREACH(blkcopy, &block_copy_list, list) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!strcmp(blkcopy->src_device_name, device= )) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s =3D blkcopy; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (!s || s->stage =3D=3D STAGE_SWITCH_FINISHED || s->fail= ed) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qerror_report(QERR_DEVICE_NOT_FOUND, device)= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0s->cancelled =3D 1; > + =C2=A0 =C2=A0do { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_aio_flush(); > + =C2=A0 =C2=A0} while (!QLIST_EMPTY(&s->io_list)); > + =C2=A0 =C2=A0blkcopy_cleanup(s); > + =C2=A0 =C2=A0blkcopy_free(s); > + > + =C2=A0 =C2=A0return 0; > +} > + > +static void blockcopy_print_dict(QObject *obj, void *opaque) > +{ > + =C2=A0 =C2=A0QDict *c_dict; > + =C2=A0 =C2=A0Monitor *mon =3D opaque; > + > + =C2=A0 =C2=A0c_dict =3D qobject_to_qdict(obj); > + > + =C2=A0 =C2=A0monitor_printf(mon, "%s: status=3D%s ", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0qdict_get_str(c_dict, "device"), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0qdict_get_str(c_dict, "status")); > + > + =C2=A0 =C2=A0if (qdict_haskey(c_dict, "info")) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0QDict *qdict =3D qobject_to_qdict(qdict_get(= c_dict, "info")); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0monitor_printf(mon, "percentage=3D%ld %%", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 qdict_get_int(qdict, "percentage")); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0monitor_printf(mon, "\n"); > +} > + > +void do_info_blockcopy_print(Monitor *mon, const QObject *data) > +{ > + =C2=A0 =C2=A0qlist_iter(qobject_to_qlist(data), blockcopy_print_dict, m= on); > +} > + > +void do_info_blockcopy(Monitor *mon, QObject **ret_data) > +{ > + =C2=A0 =C2=A0QList *c_list; > + =C2=A0 =C2=A0BdrvCopyState *s; > + > + =C2=A0 =C2=A0c_list =3D qlist_new(); > + > + =C2=A0 =C2=A0QLIST_FOREACH(s, &block_copy_list, list) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0QObject *c_obj; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0static const char *status[] =3D { "failed", = "active", "mirrored", "completed" }; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0int i; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->failed) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0i =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else if (s->stage < STAGE_MIRROR_WRITES) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0i =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else if (s->stage < STAGE_SWITCH_FINISHED)= { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0i =3D 2; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0i =3D 3; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0c_obj =3D qobject_from_jsonf("{ 'device': %s= , 'status': %s }", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->src_device_name, = status[i]); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (i =3D=3D 1) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0QDict *dict =3D qobject_to_qdi= ct(c_obj); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0QObject *obj; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* FIXME: add dirty stage prog= ress? */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0obj =3D qobject_from_jsonf("{ = 'percentage': %" PRId64 "}", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->completed_sector= s * 100 / s->nr_sectors); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qdict_put_obj(dict, "info", ob= j); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qlist_append_obj(c_list, c_obj); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0*ret_data =3D QOBJECT(c_list); > +} > + > +bool block_copy_active(void) > +{ > + =C2=A0 =C2=A0BdrvCopyState *s; > + > + =C2=A0 =C2=A0QLIST_FOREACH(s, &block_copy_list, list) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->failed) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0continue; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->stage < STAGE_SWITCH_FINISHED) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return true; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return false; > +} > + > Index: qemu-block-copy/block-copy.h > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- /dev/null > +++ qemu-block-copy/block-copy.h > @@ -0,0 +1,26 @@ > +/* > + * QEMU live block copy > + * > + * Copyright (C) 2010 Red Hat Inc. > + * > + * Authors: Marcelo Tosatti > + * > + * This work is licensed under the terms of the GNU GPL, version 2. =C2= =A0See > + * the COPYING file in the top-level directory. > + * > + */ > + > +#ifndef BLOCK_COPY_H > +#define BLOCK_COPY_H > + > +int do_bdrv_copy(Monitor *mon, const QDict *qdict, QObject **ret_data); > +int do_bdrv_copy_cancel(Monitor *mon, const QDict *qdict, QObject **ret_= data); > +int do_bdrv_copy_switch(Monitor *mon, const QDict *qdict, QObject **ret_= data); > + > +void do_info_blockcopy_print(Monitor *mon, const QObject *data); > +void do_info_blockcopy(Monitor *mon, QObject **ret_data); > + > +bool block_copy_active(void); > + > +#endif /* BLOCK_COPY_H */ > + > Index: qemu-block-copy/hmp-commands.hx > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- qemu-block-copy.orig/hmp-commands.hx > +++ qemu-block-copy/hmp-commands.hx > @@ -806,6 +806,63 @@ Set maximum speed to @var{value} (in byt > =C2=A0ETEXI > > =C2=A0 =C2=A0 { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =C2=A0 =C2=A0 =C2=A0 =3D "block_copy", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.args_type =C2=A0=3D "device:s,filename:s,in= cremental:-i", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.params =C2=A0 =C2=A0 =3D "device filename [= -i]", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.help =C2=A0 =C2=A0 =C2=A0 =3D "live block c= opy device to image" > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0"\n\t\t\t -i for incremental copy " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0"(base image shared between original and destination)", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.user_print =3D monitor_user_noop, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.mhandler.cmd_new =3D do_bdrv_copy, > + =C2=A0 =C2=A0}, > + > +STEXI > +@item block_copy @var{device} @var{filename} [-i] > +@findex block_copy > +Live copy block device @var{device} to image @var{filename}. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0-i for incremental copy (base image is share= d) > + > +Destination image @var{filename} must be created with qemu-img prior > +to execution of this command, with image size equal to the original > +image size. > + > +Incremental copy allows the destination image @var{filename} to share > +a common base image with the original image. This option skips copying > +blocks which are not allocated in the original image. > +ETEXI > + > + =C2=A0 =C2=A0{ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =C2=A0 =C2=A0 =C2=A0 =3D "block_copy_c= ancel", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.args_type =C2=A0=3D "device:s", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.params =C2=A0 =C2=A0 =3D "device", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.help =C2=A0 =C2=A0 =C2=A0 =3D "cancel live = block copy", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.user_print =3D monitor_user_noop, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.mhandler.cmd_new =3D do_bdrv_copy_cancel, > + =C2=A0 =C2=A0}, > + > +STEXI > +@item block_copy_cancel @var{device} > +@findex block_copy_cancel > +Cancel live block copy on @var{device}. > +ETEXI > + > + =C2=A0 =C2=A0{ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =C2=A0 =C2=A0 =C2=A0 =3D "block_copy_s= witch", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.args_type =C2=A0=3D "device:s", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.params =C2=A0 =C2=A0 =3D "device", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.help =C2=A0 =C2=A0 =C2=A0 =3D "finish live = block copy", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.user_print =3D monitor_user_noop, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.mhandler.cmd_new =3D do_bdrv_copy_switch, > + =C2=A0 =C2=A0}, > + > +STEXI > +@item block_copy_switch @var{device} > +@findex block_copy_switch > +Finish live block copy on @var{device} by switching > +to destination image. > +ETEXI > + > + =C2=A0 =C2=A0{ > =C2=A0 =C2=A0 =C2=A0 =C2=A0 .name =C2=A0 =C2=A0 =C2=A0 =3D "migrate_set_d= owntime", > =C2=A0 =C2=A0 =C2=A0 =C2=A0 .args_type =C2=A0=3D "value:T", > =C2=A0 =C2=A0 =C2=A0 =C2=A0 .params =C2=A0 =C2=A0 =3D "value", > @@ -1352,6 +1409,8 @@ show device tree > =C2=A0show qdev device model list > =C2=A0@item info roms > =C2=A0show roms > +@item info block-copy > +show block copy status > =C2=A0@end table > =C2=A0ETEXI > > Index: qemu-block-copy/monitor.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- qemu-block-copy.orig/monitor.c > +++ qemu-block-copy/monitor.c > @@ -45,6 +45,7 @@ > =C2=A0#include "balloon.h" > =C2=A0#include "qemu-timer.h" > =C2=A0#include "migration.h" > +#include "block-copy.h" > =C2=A0#include "kvm.h" > =C2=A0#include "acl.h" > =C2=A0#include "qint.h" > @@ -3101,6 +3102,14 @@ static const mon_cmd_t info_cmds[] =3D { > =C2=A0 =C2=A0 }, > =C2=A0#endif > =C2=A0 =C2=A0 { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =C2=A0 =C2=A0 =C2=A0 =3D "block-copy", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.args_type =C2=A0=3D "", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.params =C2=A0 =C2=A0 =3D "", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.help =C2=A0 =C2=A0 =C2=A0 =3D "show block c= opy status", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.user_print =3D do_info_blockcopy_print, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.mhandler.info_new =3D do_info_blockcopy, > + =C2=A0 =C2=A0}, > + =C2=A0 =C2=A0{ > =C2=A0 =C2=A0 =C2=A0 =C2=A0 .name =C2=A0 =C2=A0 =C2=A0 =3D NULL, > =C2=A0 =C2=A0 }, > =C2=A0}; > @@ -3242,6 +3251,14 @@ static const mon_cmd_t qmp_query_cmds[] > =C2=A0 =C2=A0 =C2=A0 =C2=A0 .mhandler.info_async =3D do_info_balloon, > =C2=A0 =C2=A0 =C2=A0 =C2=A0 .flags =C2=A0 =C2=A0 =C2=A0=3D MONITOR_CMD_AS= YNC, > =C2=A0 =C2=A0 }, > + =C2=A0 =C2=A0{ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =C2=A0 =C2=A0 =C2=A0 =3D "block-copy", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.args_type =C2=A0=3D "", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.params =C2=A0 =C2=A0 =3D "", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.help =C2=A0 =C2=A0 =C2=A0 =3D "show block c= opy status", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.user_print =3D do_info_blockcopy_print, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.mhandler.info_new =3D do_info_blockcopy, > + =C2=A0 =C2=A0}, > =C2=A0 =C2=A0 { /* NULL */ }, > =C2=A0}; > > Index: qemu-block-copy/qmp-commands.hx > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- qemu-block-copy.orig/qmp-commands.hx > +++ qemu-block-copy/qmp-commands.hx > @@ -581,6 +581,98 @@ Example: > =C2=A0EQMP > > =C2=A0 =C2=A0 { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =C2=A0 =C2=A0 =C2=A0 =3D "block_copy", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.args_type =C2=A0=3D "device:s,filename:s,in= c:-i", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.params =C2=A0 =C2=A0 =3D "device filename [= -i]", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.help =C2=A0 =C2=A0 =C2=A0 =3D "live block c= opy device to image" > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0"\n\t\t\t -i for incremental copy " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0"(base image shared between src and destination)", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.user_print =3D monitor_user_noop, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.mhandler.cmd_new =3D do_bdrv_copy, > + =C2=A0 =C2=A0}, > + > +SQMP > +block-copy > +------- > + > +Live block copy. > + > +Arguments: > + > +- "device": device name (json-string) > +- "filename": target image filename (json-string) > +- "incremental": incremental disk copy (json-bool, optional) > + > +Example: > + > +-> { "execute": "block_copy", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0"arguments": { "device": "ide0-hd1", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "filename": "/mnt/new-disk.img", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 } } > + > +<- { "return": {} } > + > +Notes: > + > +(1) The 'query-block-copy' command should be used to check block copy pr= ogress > + =C2=A0 =C2=A0and final result (this information is provided by the 'sta= tus' member) > +(2) Boolean argument "incremental" defaults to false > + > +EQMP > + > + =C2=A0 =C2=A0{ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =C2=A0 =C2=A0 =C2=A0 =3D "block_copy_c= ancel", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.args_type =C2=A0=3D "device:s", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.params =C2=A0 =C2=A0 =3D "device", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.help =C2=A0 =C2=A0 =C2=A0 =3D "cancel live = block copy", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.user_print =3D monitor_user_noop, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.mhandler.cmd_new =3D do_bdrv_copy_cancel, > + =C2=A0 =C2=A0}, > + > +SQMP > +block_copy_cancel > +-------------- > + > +Cancel live block copy. > + > +Arguments: > + > +- device: device name (json-string) > + > +Example: > + > +-> { "execute": "block_copy_cancel", "arguments": { "device": "ide0-hd1"= } } > +<- { "return": {} } > + > +EQMP > + > + =C2=A0 =C2=A0{ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =C2=A0 =C2=A0 =C2=A0 =3D "block_copy_s= witch", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.args_type =C2=A0=3D "device:s", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.params =C2=A0 =C2=A0 =3D "device", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.help =C2=A0 =C2=A0 =C2=A0 =3D "finish live = block copy", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.user_print =3D monitor_user_noop, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0.mhandler.cmd_new =3D do_bdrv_copy_switch, > + =C2=A0 =C2=A0}, > + > +SQMP > +block_copy_switch > +-------------- > + > +Finish live block copy, switching device to destination image. > + > +Arguments: > + > +- device: device name (json-string) > + > +Example: > + > +-> { "execute": "block_copy_switch", "arguments": { "device": "ide0-hd1"= } } > +<- { "return": {} } > + > +EQMP > + > + =C2=A0 =C2=A0{ > =C2=A0 =C2=A0 =C2=A0 =C2=A0 .name =C2=A0 =C2=A0 =C2=A0 =3D "netdev_add", > =C2=A0 =C2=A0 =C2=A0 =C2=A0 .args_type =C2=A0=3D "netdev:O", > =C2=A0 =C2=A0 =C2=A0 =C2=A0 .params =C2=A0 =C2=A0 =3D "[user|tap|socket],= id=3Dstr[,prop=3Dvalue][,...]", > @@ -1744,6 +1836,44 @@ Examples: > =C2=A0EQMP > > =C2=A0SQMP > +query-block-copy > +------------- > + > +Live block copy status. > + > +Each block copy instance information is stored in a json-object and the = returned > +value is a json-array of all instances. > + > +Each json-object contains the following: > + > +- "device": device name (json-string) > +- "status": block copy status (json-string) > + =C2=A0 =C2=A0- Possible values: "active", "failed", "mirrored", "comple= ted" > +- "info": A json-object with the statistics information, if status is "a= ctive": > + =C2=A0 =C2=A0- "percentage": percentage completed (json-int) > + > +Example: > + > +Block copy for "ide1-hd0" active and block copy for "ide1-hd1" failed: > + > +-> { "execute": "query-block-copy" } > +<- { > + =C2=A0 =C2=A0 =C2=A0"return":[ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0{"device":"ide1-hd0", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"status":"active", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"info":{ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "percentage":23, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0}, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0{"device":"ide1-hd1", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 "status":"failed" > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0] > + =C2=A0 } > + > +EQMP > + > +SQMP > =C2=A0query-balloon > =C2=A0------------- > > Index: qemu-block-copy/Makefile.objs > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- qemu-block-copy.orig/Makefile.objs > +++ qemu-block-copy/Makefile.objs > @@ -98,7 +98,7 @@ common-obj-y +=3D buffered_file.o migratio > =C2=A0common-obj-y +=3D qemu-char.o savevm.o #aio.o > =C2=A0common-obj-y +=3D msmouse.o ps2.o > =C2=A0common-obj-y +=3D qdev.o qdev-properties.o > -common-obj-y +=3D block-migration.o iohandler.o > +common-obj-y +=3D block-migration.o iohandler.o block-copy.o > =C2=A0common-obj-y +=3D pflib.o > =C2=A0common-obj-y +=3D bitmap.o bitops.o > > > > >