summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2014-12-25 22:33:56 +0100
committerPeter Wu <peter@lekensteyn.nl>2014-12-25 22:33:56 +0100
commit984a6899dcffc94ea0e28f1116a233bd3d6cd51f (patch)
treea09ff841b54bfe69672f888bce0051b12b878355
parente8815356e6b7988edd523163ff72a8f2a8cacc8b (diff)
downloadqemu-984a6899dcffc94ea0e28f1116a233bd3d6cd51f.tar.gz
block/dmg: avoid huge allocation for zeroes
Disk images may contain large all-zeroes gaps (1.66k sectors or 812 MiB is seen in the real world). To avoid having to take such an amount from the heap, treat this case specially. This lowers the maximum memory need to 1054371 bytes (probably for a bzip2-compressed block). Signed-off-by: Peter Wu <peter@lekensteyn.nl>
-rw-r--r--block/dmg.c14
1 files changed, 10 insertions, 4 deletions
diff --git a/block/dmg.c b/block/dmg.c
index 362e6747d4..f94b6d96ed 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -137,7 +137,9 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk,
uncompressed_sectors = (s->lengths[chunk] + 511) / 512;
break;
case 2: /* zero */
- uncompressed_sectors = s->sectorcounts[chunk];
+ /* as the all-zeroes block may be large, it is treated specially: the
+ * sector is not copied from a large buffer, a simple memset is used
+ * instead. Therefore uncompressed_sectors does not need to be set. */
break;
}
@@ -574,9 +576,6 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
return -1;
}
break;
- case 2: /* zero */
- memset(s->uncompressed_chunk, 0, 512 * s->sectorcounts[chunk]);
- break;
}
s->current_chunk = chunk;
}
@@ -594,6 +593,13 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num,
if (dmg_read_chunk(bs, sector_num + i) != 0) {
return -1;
}
+ /* Special case: current chunk is all zeroes. Do not perform a memcpy as
+ * s->uncompressed_chunk may be too small to cover the large all-zeroes
+ * section. */
+ if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */
+ memset(buf + i * 512, 0, 512);
+ continue;
+ }
sector_offset_in_chunk = sector_num + i - s->sectors[s->current_chunk];
memcpy(buf + i * 512,
s->uncompressed_chunk + sector_offset_in_chunk * 512, 512);