summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManuel Bentele2019-08-08 12:29:05 +0200
committerManuel Bentele2019-08-21 22:04:10 +0200
commit84719b6a8d490150d73f355ba231ebe01cc27cc4 (patch)
tree9b6a40e8b62285465befd3457e08caf0f20b0911
parentblock: loop: file_fmt_qcow: fix offset during the read of a cache miss (diff)
downloadkernel-qcow2-linux-84719b6a8d490150d73f355ba231ebe01cc27cc4.tar.gz
kernel-qcow2-linux-84719b6a8d490150d73f355ba231ebe01cc27cc4.tar.xz
kernel-qcow2-linux-84719b6a8d490150d73f355ba231ebe01cc27cc4.zip
block: loop: file_fmt_qcow: write decompressed data to correct position in bvec
The decompressed data of a compressed cluster is written to the beginning of each bvec. This strategy only works if the decompressed data size is greater or equal to the bvec's data size. If the decompressed data size is lower than the bvec's data size, the data of a second iteration to fill the bvec overwrites the already written data from the previous iteration. This patch adds an position offset to avoid this loss of data during reading of the QCOW disk image file. The position offset is updated in each iteration with the number of successfully written bytes of the previous rounds. The offset is considered during filling the bvec with the decompressed data. Signed-off-by: Manuel Bentele <development@manuel-bentele.de>
-rw-r--r--drivers/block/loop/loop_file_fmt_qcow_main.c18
-rw-r--r--drivers/block/loop/loop_file_fmt_qcow_main.h4
2 files changed, 14 insertions, 8 deletions
diff --git a/drivers/block/loop/loop_file_fmt_qcow_main.c b/drivers/block/loop/loop_file_fmt_qcow_main.c
index dc2a7b0ee287..4a1fb5bf346d 100644
--- a/drivers/block/loop/loop_file_fmt_qcow_main.c
+++ b/drivers/block/loop/loop_file_fmt_qcow_main.c
@@ -148,13 +148,13 @@ static int __qcow_file_fmt_compression_init(struct loop_file_fmt *lo_fmt)
struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data;
int ret = 0;
- qcow_data->strm = kmalloc(sizeof(*qcow_data->strm), GFP_KERNEL);
+ qcow_data->strm = kzalloc(sizeof(*qcow_data->strm), GFP_KERNEL);
if (!qcow_data->strm) {
ret = -ENOMEM;
goto out;
}
- qcow_data->strm->workspace = vmalloc(zlib_inflate_workspacesize());
+ qcow_data->strm->workspace = vzalloc(zlib_inflate_workspacesize());
if (!qcow_data->strm->workspace) {
ret = -ENOMEM;
goto out_free_strm;
@@ -724,7 +724,8 @@ static int __qcow_file_fmt_read_compressed(struct loop_file_fmt *lo_fmt,
struct bio_vec *bvec,
u64 file_cluster_offset,
u64 offset,
- u64 bytes)
+ u64 bytes,
+ u64 bytes_done)
{
struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data;
struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt);
@@ -732,15 +733,16 @@ static int __qcow_file_fmt_read_compressed(struct loop_file_fmt *lo_fmt,
u64 coffset;
u8 *in_buf, *out_buf;
ssize_t len;
- int offset_in_cluster = loop_file_fmt_qcow_offset_into_cluster(
- qcow_data, offset);
void *data;
unsigned long irq_flags;
+ int offset_in_cluster = loop_file_fmt_qcow_offset_into_cluster(
+ qcow_data, offset);
coffset = file_cluster_offset & qcow_data->cluster_offset_mask;
nb_csectors = ((file_cluster_offset >> qcow_data->csize_shift) &
qcow_data->csize_mask) + 1;
- csize = nb_csectors * 512 - (coffset & 511);
+ csize = nb_csectors * QCOW_COMPRESSED_SECTOR_SIZE -
+ (coffset & ~QCOW_COMPRESSED_SECTOR_MASK);
in_buf = vmalloc(csize);
if (!in_buf) {
@@ -766,7 +768,7 @@ static int __qcow_file_fmt_read_compressed(struct loop_file_fmt *lo_fmt,
}
ASSERT(bytes <= bvec->bv_len);
- data = bvec_kmap_irq(bvec, &irq_flags);
+ data = bvec_kmap_irq(bvec, &irq_flags) + bytes_done;
memcpy(data, out_buf + offset_in_cluster, bytes);
flush_dcache_page(bvec->bv_page);
bvec_kunmap_irq(data, &irq_flags);
@@ -824,7 +826,7 @@ static int __qcow_file_fmt_read_bvec(struct loop_file_fmt *lo_fmt,
case QCOW_CLUSTER_COMPRESSED:
ret = __qcow_file_fmt_read_compressed(lo_fmt, bvec,
- cluster_offset, *ppos, cur_bytes);
+ cluster_offset, *ppos, cur_bytes, bytes_done);
if (ret < 0) {
goto fail;
}
diff --git a/drivers/block/loop/loop_file_fmt_qcow_main.h b/drivers/block/loop/loop_file_fmt_qcow_main.h
index 8f2fa32cb422..9e4951fba079 100644
--- a/drivers/block/loop/loop_file_fmt_qcow_main.h
+++ b/drivers/block/loop/loop_file_fmt_qcow_main.h
@@ -83,6 +83,10 @@ do { \
#define QCOW_MIN_CLUSTER_BITS 9
#define QCOW_MAX_CLUSTER_BITS 21
+/* Defined in the qcow2 spec (compressed cluster descriptor) */
+#define QCOW_COMPRESSED_SECTOR_SIZE 512U
+#define QCOW_COMPRESSED_SECTOR_MASK (~(QCOW_COMPRESSED_SECTOR_SIZE - 1))
+
/* Must be at least 2 to cover COW */
#define QCOW_MIN_L2_CACHE_SIZE 2 /* cache entries */