@@ -758,7 +758,7 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
BDRVQcow2State *s = bs->opaque;
int ret;
- if (r->nb_bytes == 0) {
+ if (r->nb_bytes == 0 || r->reduced) {
return 0;
}
@@ -1267,10 +1267,12 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
.cow_start = {
.offset = 0,
.nb_bytes = offset_into_cluster(s, guest_offset),
+ .reduced = false,
},
.cow_end = {
.offset = nb_bytes,
.nb_bytes = avail_bytes - nb_bytes,
+ .reduced = false,
},
};
qemu_co_queue_init(&(*m)->dependent_requests);
@@ -64,6 +64,9 @@ typedef struct {
#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
+static bool is_zero_sectors(BlockDriverState *bs, int64_t start,
+ uint32_t count);
+
static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
{
const QCowHeader *cow_header = (const void *)buf;
@@ -1575,10 +1578,30 @@ fail:
return ret;
}
+static void handle_cow_reduce(BlockDriverState *bs, QCowL2Meta *m)
+{
+ if (bs->encrypted) {
+ return;
+ }
+ if (!m->cow_start.reduced && m->cow_start.nb_bytes != 0 &&
+ is_zero_sectors(bs,
+ (m->offset + m->cow_start.offset) >> BDRV_SECTOR_BITS,
+ m->cow_start.nb_bytes >> BDRV_SECTOR_BITS)) {
+ m->cow_start.reduced = true;
+ }
+ if (!m->cow_end.reduced && m->cow_end.nb_bytes != 0 &&
+ is_zero_sectors(bs,
+ (m->offset + m->cow_end.offset) >> BDRV_SECTOR_BITS,
+ m->cow_end.nb_bytes >> BDRV_SECTOR_BITS)) {
+ m->cow_end.reduced = true;
+ }
+}
+
static void handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta)
{
BDRVQcow2State *s = bs->opaque;
QCowL2Meta *m;
+ int ret;
for (m = l2meta; m != NULL; m = m->next) {
uint64_t bytes = m->nb_clusters << s->cluster_bits;
@@ -1588,8 +1611,13 @@ static void handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta)
}
/* try to alloc host space in one chunk for better locality */
- bdrv_co_pwrite_zeroes(bs->file, m->alloc_offset, bytes,
- BDRV_REQ_ALLOCATE);
+ ret = bdrv_co_pwrite_zeroes(bs->file, m->alloc_offset, bytes,
+ BDRV_REQ_ALLOCATE);
+ if (ret != 0) {
+ continue;
+ }
+
+ handle_cow_reduce(bs, m);
}
}
@@ -305,6 +305,10 @@ typedef struct Qcow2COWRegion {
/** Number of bytes to copy */
int nb_bytes;
+
+ /** The region is filled with zeroes and does not require COW
+ */
+ bool reduced;
} Qcow2COWRegion;
/**
@@ -160,7 +160,7 @@ poke_file "$TEST_IMG" '131084' "\x00\x00" # 0x2000c
# any unallocated cluster, leading to an attempt to overwrite the second L2
# table. Finally, resume the COW write and see it fail (but not crash).
echo "open -o file.driver=blkdebug $TEST_IMG
-break cow_read 0
+break write_aio 0
aio_write 0k 1k
wait_break 0
write 64k 64k
@@ -107,7 +107,8 @@ qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps
blkdebug: Suspended request '0'
write failed: Input/output error
blkdebug: Resuming request '0'
-aio_write failed: No medium found
+wrote 1024/1024 bytes at offset 0
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing unallocated image header ===
@@ -71,7 +71,7 @@ echo
_make_test_img $IMG_SIZE
# Create data clusters (not aligned to an L2 table)
-$QEMU_IO -c 'write -P 42 1M 256k' "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "write -P 42 $(((1024 + 32) * 1024)) 192k" "$TEST_IMG" | _filter_qemu_io
orig_map=$($QEMU_IMG map --output=json "$TEST_IMG")
# Convert the data clusters to preallocated zero clusters
@@ -19,8 +19,8 @@ Offset Length Mapped to File
=== Writing to preallocated zero clusters ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67109376
-wrote 262144/262144 bytes at offset 1048576
-256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 196608/196608 bytes at offset 1081344
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 262144/262144 bytes at offset 1048576
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 196608/196608 bytes at offset 1081344