diff options
Diffstat (limited to 'qemu-img.c')
-rw-r--r-- | qemu-img.c | 74 |
1 files changed, 54 insertions, 20 deletions
@@ -3530,6 +3530,7 @@ static int img_rebase(int argc, char **argv) uint8_t *buf_new = NULL; BlockDriverState *bs = NULL, *prefix_chain_bs = NULL; BlockDriverState *unfiltered_bs; + BlockDriverInfo bdi = {0}; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; @@ -3540,6 +3541,7 @@ static int img_rebase(int argc, char **argv) bool quiet = false; Error *local_err = NULL; bool image_opts = false; + int64_t write_align; /* Parse commandline parameters */ fmt = NULL; @@ -3663,6 +3665,20 @@ static int img_rebase(int argc, char **argv) } } + /* + * We need overlay subcluster size to make sure write requests are + * aligned. + */ + ret = bdrv_get_info(unfiltered_bs, &bdi); + if (ret < 0) { + error_report("could not get block driver info"); + goto out; + } else if (bdi.subcluster_size == 0) { + bdi.subcluster_size = 1; + } + + write_align = bdi.subcluster_size; + /* For safe rebasing we need to compare old and new backing file */ if (!unsafe) { QDict *options = NULL; @@ -3762,7 +3778,7 @@ static int img_rebase(int argc, char **argv) int64_t old_backing_size = 0; int64_t new_backing_size = 0; uint64_t offset; - int64_t n; + int64_t n, n_old = 0, n_new = 0; float local_progress = 0; if (blk_old_backing && bdrv_opt_mem_align(blk_bs(blk_old_backing)) > @@ -3808,7 +3824,8 @@ static int img_rebase(int argc, char **argv) } for (offset = 0; offset < size; offset += n) { - bool buf_old_is_zero = false; + bool old_backing_eof = false; + int64_t n_alloc; /* How many bytes can we handle with the next read? */ n = MIN(IO_BUF_SIZE, size - offset); @@ -3854,32 +3871,45 @@ static int img_rebase(int argc, char **argv) } /* + * At this point we know that the region [offset; offset + n) + * is unallocated within the target image. This region might be + * unaligned to the target image's (sub)cluster boundaries, as + * old backing may have smaller clusters (or have subclusters). + * We extend it to the aligned boundaries to avoid CoW on + * partial writes in blk_pwrite(), + */ + n += offset - QEMU_ALIGN_DOWN(offset, write_align); + offset = QEMU_ALIGN_DOWN(offset, write_align); + n += QEMU_ALIGN_UP(offset + n, write_align) - (offset + n); + n = MIN(n, size - offset); + assert(!bdrv_is_allocated(unfiltered_bs, offset, n, &n_alloc) && + n_alloc == n); + + /* + * Much like with the target image, we'll try to read as much + * of the old and new backings as we can. + */ + n_old = MIN(n, MAX(0, old_backing_size - (int64_t) offset)); + n_new = MIN(n, MAX(0, new_backing_size - (int64_t) offset)); + + /* * Read old and new backing file and take into consideration that * backing files may be smaller than the COW image. */ - if (offset >= old_backing_size) { - memset(buf_old, 0, n); - buf_old_is_zero = true; + memset(buf_old + n_old, 0, n - n_old); + if (!n_old) { + old_backing_eof = true; } else { - if (offset + n > old_backing_size) { - n = old_backing_size - offset; - } - - ret = blk_pread(blk_old_backing, offset, n, buf_old, 0); + ret = blk_pread(blk_old_backing, offset, n_old, buf_old, 0); if (ret < 0) { error_report("error while reading from old backing file"); goto out; } } - if (offset >= new_backing_size || !blk_new_backing) { - memset(buf_new, 0, n); - } else { - if (offset + n > new_backing_size) { - n = new_backing_size - offset; - } - - ret = blk_pread(blk_new_backing, offset, n, buf_new, 0); + memset(buf_new + n_new, 0, n - n_new); + if (n_new) { + ret = blk_pread(blk_new_backing, offset, n_new, buf_new, 0); if (ret < 0) { error_report("error while reading from new backing file"); goto out; @@ -3893,11 +3923,12 @@ static int img_rebase(int argc, char **argv) int64_t pnum; if (compare_buffers(buf_old + written, buf_new + written, - n - written, 0, &pnum)) + n - written, write_align, &pnum)) { - if (buf_old_is_zero) { + if (old_backing_eof) { ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0); } else { + assert(written + pnum <= IO_BUF_SIZE); ret = blk_pwrite(blk, offset + written, pnum, buf_old + written, 0); } @@ -3909,6 +3940,9 @@ static int img_rebase(int argc, char **argv) } written += pnum; + if (offset + written >= old_backing_size) { + old_backing_eof = true; + } } qemu_progress_print(local_progress, 100); } |