|
@@ -3530,6 +3530,7 @@ static int img_rebase(int argc, char **argv)
|
|
uint8_t *buf_new = NULL;
|
|
uint8_t *buf_new = NULL;
|
|
BlockDriverState *bs = NULL, *prefix_chain_bs = NULL;
|
|
BlockDriverState *bs = NULL, *prefix_chain_bs = NULL;
|
|
BlockDriverState *unfiltered_bs;
|
|
BlockDriverState *unfiltered_bs;
|
|
|
|
+ BlockDriverInfo bdi = {0};
|
|
char *filename;
|
|
char *filename;
|
|
const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg;
|
|
const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg;
|
|
int c, flags, src_flags, ret;
|
|
int c, flags, src_flags, ret;
|
|
@@ -3540,6 +3541,7 @@ static int img_rebase(int argc, char **argv)
|
|
bool quiet = false;
|
|
bool quiet = false;
|
|
Error *local_err = NULL;
|
|
Error *local_err = NULL;
|
|
bool image_opts = false;
|
|
bool image_opts = false;
|
|
|
|
+ int64_t write_align;
|
|
|
|
|
|
/* Parse commandline parameters */
|
|
/* Parse commandline parameters */
|
|
fmt = NULL;
|
|
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 */
|
|
/* For safe rebasing we need to compare old and new backing file */
|
|
if (!unsafe) {
|
|
if (!unsafe) {
|
|
QDict *options = NULL;
|
|
QDict *options = NULL;
|
|
@@ -3762,7 +3778,7 @@ static int img_rebase(int argc, char **argv)
|
|
int64_t old_backing_size = 0;
|
|
int64_t old_backing_size = 0;
|
|
int64_t new_backing_size = 0;
|
|
int64_t new_backing_size = 0;
|
|
uint64_t offset;
|
|
uint64_t offset;
|
|
- int64_t n;
|
|
|
|
|
|
+ int64_t n, n_old = 0, n_new = 0;
|
|
float local_progress = 0;
|
|
float local_progress = 0;
|
|
|
|
|
|
if (blk_old_backing && bdrv_opt_mem_align(blk_bs(blk_old_backing)) >
|
|
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) {
|
|
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? */
|
|
/* How many bytes can we handle with the next read? */
|
|
n = MIN(IO_BUF_SIZE, size - offset);
|
|
n = MIN(IO_BUF_SIZE, size - offset);
|
|
@@ -3853,33 +3870,46 @@ 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
|
|
* Read old and new backing file and take into consideration that
|
|
* backing files may be smaller than the COW image.
|
|
* 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 {
|
|
} 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) {
|
|
if (ret < 0) {
|
|
error_report("error while reading from old backing file");
|
|
error_report("error while reading from old backing file");
|
|
goto out;
|
|
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) {
|
|
if (ret < 0) {
|
|
error_report("error while reading from new backing file");
|
|
error_report("error while reading from new backing file");
|
|
goto out;
|
|
goto out;
|
|
@@ -3893,11 +3923,12 @@ static int img_rebase(int argc, char **argv)
|
|
int64_t pnum;
|
|
int64_t pnum;
|
|
|
|
|
|
if (compare_buffers(buf_old + written, buf_new + written,
|
|
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);
|
|
ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0);
|
|
} else {
|
|
} else {
|
|
|
|
+ assert(written + pnum <= IO_BUF_SIZE);
|
|
ret = blk_pwrite(blk, offset + written, pnum,
|
|
ret = blk_pwrite(blk, offset + written, pnum,
|
|
buf_old + written, 0);
|
|
buf_old + written, 0);
|
|
}
|
|
}
|
|
@@ -3909,6 +3940,9 @@ static int img_rebase(int argc, char **argv)
|
|
}
|
|
}
|
|
|
|
|
|
written += pnum;
|
|
written += pnum;
|
|
|
|
+ if (offset + written >= old_backing_size) {
|
|
|
|
+ old_backing_eof = true;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
qemu_progress_print(local_progress, 100);
|
|
qemu_progress_print(local_progress, 100);
|
|
}
|
|
}
|