On Wed, May 14, 2025 at 11:37:54PM +0800, Sunny Zhu wrote:
> On Fri, 9 May 2025 15:40:28 -0500, Eric Blake wrote:
> > @@ -847,8 +887,10 @@ static int coroutine_fn GRAPH_UNLOCKED 
> > mirror_dirty_init(MirrorBlockJob *s)
> >      bool punch_holes =
> >          target_bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
> >          bdrv_can_write_zeroes_with_unmap(target_bs);
> > +    int64_t bitmap_length = DIV_ROUND_UP(s->bdev_length, s->granularity);
> > 
> >      /* Determine if the image is already zero, regardless of sync mode.  */
> > +    s->zero_bitmap = bitmap_new(bitmap_length);
> >      bdrv_graph_co_rdlock();
> >      bs = s->mirror_top_bs->backing->bs;
> >      if (s->target_is_zero) {
> > @@ -862,7 +904,14 @@ static int coroutine_fn GRAPH_UNLOCKED 
> > mirror_dirty_init(MirrorBlockJob *s)
> >      if (ret < 0) {
> >          return ret;
> >      } else if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
> > -        /* In TOP mode, there is no benefit to a pre-zeroing pass.  */
> > +        /*
> > +         * In TOP mode, there is no benefit to a pre-zeroing pass, but
> > +         * the zero bitmap can be set if the destination already reads
> > +         * as zero and we are not punching holes.
> > +         */
> > +        if (ret > 0 && !punch_holes) {
> > +            bitmap_set(s->zero_bitmap, 0, bitmap_length);
> 
> It's ok when ret > 0 is obtained through bdrv_co_is_all_zeroes(target_bs). 
> However,
> if ret = 1 originates by target-is-zero == true from qmp_blockdev_mirror, 
> this means
> that target-is-zero also takes effect under sync=TOP. I am uncertain whether 
> this
> aligns with our intended behavior.
> 
> Under sync=TOP, target-is-zero could carry two distinct meanings:
> [1] The top snapshot reads as fully zero.
> [2] The entire destination snapshot chain reads as fully zero.
> 
> Currently, target-is-zero is designed to represent scenario [2] on sync=TOP.

As written, we are telling the user they should not set target-is-zero
unless the entire destination (including through any snapshot chain)
reads as zero.  Offhand (but untested) I think you can get by with
passing target-is-zero:true even when the destination is not fully
zero, _IF_ all portions of the image that will be mirrored read as
zero (and by doing that, the portions that will not be mirrored are
not cleared as a result).  If my assumption is plausible, that would
be a slick way to do a sync=TOP mirror to copy just the dirty portions
of an image into a destination that already contains all contents from
the clean portions of the image prior to the point in time of the
dirty bitmap, in order to rehydrate a full image from an incremental
mirror.  But given that it is not tested, I wouldn't recommend trying
it in a production environment.

I doubt that sync=TOP users will ever want to manually set
target-is-zero:true, but don't see any reason to change the series as
reviewed, because the documentation for target-is-zero was clear that
that manually setting it when the image is not actually zero can
possibly cause corruption (even if it ends up working without
corruption in some corner cases).

Meanwhile, the only effect of having the zero bitmap set when
target-is-zero was passed to sync=TOP (even if that is not what most
users will do) is that _if_ the portion of the image that is dirty and
needs to be mirrored reads as zero on the source, it will not be
written to the destination.  It doesn't punch holes in the portions of
the destionation that correspond to unallocated bits of the source.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org


Reply via email to