On 22.02.2016 17:43, Max Reitz wrote: > On 22.02.2016 13:50, Kevin Wolf wrote: >> Am 20.02.2016 um 18:39 hat Max Reitz geschrieben: >>> When passing -S 0 to qemu-img convert, the target image is supposed to >>> be fully allocated. Right now, this is not the case if the source image >>> contains areas which bdrv_get_block_status() reports as being zero. >>> >>> This patch introduces a new ImgConvertBlockStatus named BLK_ZERO_DATA >>> which is set by convert_iteration_sectors() if an area is detected to be >>> zero and min_sparse is 0. Simply using BLK_DATA is not optimal, because >>> knowing an area to be zero allows us to memset() the buffer to be >>> written directly rather than having to use blk_read(). >>> >>> Other than that, BLK_ZERO_DATA is handled exactly like BLK_DATA. >>> >>> This patch changes the reference output for iotest 122; contrary to what >>> it assumed, -S 0 really should allocate everything in the output, not >>> just areas that are filled with zeros (as opposed to being zeroed). >>> >>> Signed-off-by: Max Reitz <[email protected]> >> >> I don't like how you touch the conversion code all over the place. >> Specifically, convert_iteration_sectors() and convert_read() (and >> consequently s->status) are supposed to be only about the source file. >> -S 0 doesn't make a difference for what the source file looks like, so >> we shouldn't need to change anything there. >> >> The following change should do the same thing (it passes your test case >> anyway) and is more contained to the actual writeout of image data. > > Well, I briefly considered making @buf non-const in convert_write(), but > I discarded that idea, and I'm still not comfortable with that. If you > argue that convert_read() should only deal with the source image, I'll > argue that convert_write() should only deal with the target image. I > know that you're making @buf non-const because we need some scratch > buffer and, well, @buf is available, so why not use that? But it still > doesn't sit right with me. > > So I'd like to pull that memset() out of convert_write() and just tell > convert_write() to write that buffer as data. In fact, that is basically > what my patch does. But why does it then not just use BLK_DATA but this > new status? > > Because of two reasons: First, another issue with your patch is that > zeroed areas are not counted in the progress update. If we are writing > them as zeros, we should count them, however. Therefore, we need some > special-casing in convert_do_copy(). Effectively, we end up with stuff like: > >> if (s->status == BLK_DATA || >> (!s->min_sparse && s->status == BLK_ZERO)) > > I found that combination of (min_sparse && BLK_ZERO) to be ugly, and > liked it much better if we could do that test a single time in > convert_read and be done with it. This is why I added the new status.* > > And if you pull the memset() out of convert_write() and add a new > status, what you end up with is basically my patch. > > *Note that I initially thought we'd have this test in many more places > than we actually would, as it turned out. I'll take a look at how much > simpler this patch becomes if I drop the BLK_ZERO_DATA status.
Said patch would look like this:
diff --git a/qemu-img.c b/qemu-img.c
index 2edb139..b696ba4 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1509,10 +1509,6 @@ static int convert_read(ImgConvertState *s,
int64_t sector_num, int nb_sectors,
int n;
int ret;
- if (s->status == BLK_ZERO || s->status == BLK_BACKING_FILE) {
- return 0;
- }
-
assert(nb_sectors <= s->buf_sectors);
while (nb_sectors > 0) {
BlockBackend *blk;
@@ -1650,7 +1646,8 @@ static int convert_do_copy(ImgConvertState *s)
ret = n;
goto fail;
}
- if (s->status == BLK_DATA) {
+ if (s->status == BLK_DATA || (!s->min_sparse && s->status ==
BLK_ZERO))
+ {
s->allocated_sectors += n;
}
sector_num += n;
@@ -1670,17 +1667,24 @@ static int convert_do_copy(ImgConvertState *s)
ret = n;
goto fail;
}
- if (s->status == BLK_DATA) {
+ if (s->status == BLK_DATA || (!s->min_sparse && s->status ==
BLK_ZERO))
+ {
allocated_done += n;
qemu_progress_print(100.0 * allocated_done /
s->allocated_sectors,
0);
}
- ret = convert_read(s, sector_num, n, buf);
- if (ret < 0) {
- error_report("error while reading sector %" PRId64
- ": %s", sector_num, strerror(-ret));
- goto fail;
+ if (s->status == BLK_DATA) {
+ ret = convert_read(s, sector_num, n, buf);
+ if (ret < 0) {
+ error_report("error while reading sector %" PRId64
+ ": %s", sector_num, strerror(-ret));
+ goto fail;
+ }
+ } else if (!s->min_sparse && s->status == BLK_ZERO) {
+ n = MIN(n, s->buf_sectors);
+ memset(buf, 0, n * BDRV_SECTOR_SIZE);
+ s->status = BLK_DATA;
}
ret = convert_write(s, sector_num, n, buf);
I'd get the diffcount even smaller by keeping convert_read() unchanged
and continuing to call it unconditionally, but I like that change in
itself because I find it makes the logic clearer. I can be persuaded to
split this patch into two, however (one pulling the condition out of
convert_read(), and another one doing the rest of this patch).
Max
signature.asc
Description: OpenPGP digital signature
