6d739f7
From 1df18d4a961a66b9ea28ab83b409f4d9d470f148 Mon Sep 17 00:00:00 2001
6d739f7
From: Kevin Wolf <kwolf@redhat.com>
6d739f7
Date: Thu, 8 Oct 2009 15:02:08 +0200
6d739f7
Subject: [PATCH] qcow2: Bring synchronous read/write back to life
6d739f7
6d739f7
When the synchronous read and write functions were dropped, they were replaced
6d739f7
by generic emulation functions. Unfortunately, these emulation functions don't
6d739f7
provide the same semantics as the original functions did.
6d739f7
6d739f7
The original bdrv_read would mean that we read some data synchronously and that
6d739f7
we won't be interrupted during this read. The latter assumption is no longer
6d739f7
true with the emulation function which needs to use qemu_aio_poll and therefore
6d739f7
allows the callback of any other concurrent AIO request to be run during the
6d739f7
read. Which in turn means that (meta)data read earlier could have changed and
6d739f7
be invalid now. qcow2 is not prepared to work in this way and it's just scary
6d739f7
how many places there are where other requests could run.
6d739f7
6d739f7
I'm not sure yet where exactly it breaks, but you'll see breakage with virtio
6d739f7
on qcow2 with a backing file. Providing synchronous functions again fixes the
6d739f7
problem for me.
6d739f7
6d739f7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6d739f7
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
6d739f7
Fedora-patch: qemu-fix-qcow2-backing-file-with-virtio.patch
6d739f7
---
6d739f7
 block/qcow2-cluster.c |    6 ++--
6d739f7
 block/qcow2.c         |   51 +++++++++++++++++++++++++++++++++++++++++++++++-
6d739f7
 block/qcow2.h         |    3 ++
6d739f7
 3 files changed, 55 insertions(+), 5 deletions(-)
6d739f7
6d739f7
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
6d739f7
index d4631c3..4d0ce16 100644
6d739f7
--- a/block/qcow2-cluster.c
6d739f7
+++ b/block/qcow2-cluster.c
6d739f7
@@ -306,8 +306,8 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
6d739f7
 }
6d739f7
 
6d739f7
 
6d739f7
-static int qcow_read(BlockDriverState *bs, int64_t sector_num,
6d739f7
-                     uint8_t *buf, int nb_sectors)
6d739f7
+int qcow2_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
6d739f7
+    int nb_sectors)
6d739f7
 {
6d739f7
     BDRVQcowState *s = bs->opaque;
6d739f7
     int ret, index_in_cluster, n, n1;
6d739f7
@@ -358,7 +358,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
6d739f7
     n = n_end - n_start;
6d739f7
     if (n <= 0)
6d739f7
         return 0;
6d739f7
-    ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n);
6d739f7
+    ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n);
6d739f7
     if (ret < 0)
6d739f7
         return ret;
6d739f7
     if (s->crypt_method) {
6d739f7
diff --git a/block/qcow2.c b/block/qcow2.c
6d739f7
index dd32ea2..ced257e 100644
6d739f7
--- a/block/qcow2.c
6d739f7
+++ b/block/qcow2.c
6d739f7
@@ -855,6 +855,51 @@ static int qcow_make_empty(BlockDriverState *bs)
6d739f7
     return 0;
6d739f7
 }
6d739f7
 
6d739f7
+static int qcow2_write(BlockDriverState *bs, int64_t sector_num,
6d739f7
+                     const uint8_t *buf, int nb_sectors)
6d739f7
+{
6d739f7
+    BDRVQcowState *s = bs->opaque;
6d739f7
+    int ret, index_in_cluster, n;
6d739f7
+    uint64_t cluster_offset;
6d739f7
+    int n_end;
6d739f7
+    QCowL2Meta l2meta;
6d739f7
+
6d739f7
+    while (nb_sectors > 0) {
6d739f7
+        memset(&l2meta, 0, sizeof(l2meta));
6d739f7
+
6d739f7
+        index_in_cluster = sector_num & (s->cluster_sectors - 1);
6d739f7
+        n_end = index_in_cluster + nb_sectors;
6d739f7
+        if (s->crypt_method &&
6d739f7
+            n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors)
6d739f7
+            n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors;
6d739f7
+        cluster_offset = qcow2_alloc_cluster_offset(bs, sector_num << 9,
6d739f7
+                                              index_in_cluster,
6d739f7
+                                              n_end, &n, &l2meta);
6d739f7
+        if (!cluster_offset)
6d739f7
+            return -1;
6d739f7
+        if (s->crypt_method) {
6d739f7
+            qcow2_encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1,
6d739f7
+                            &s->aes_encrypt_key);
6d739f7
+            ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512,
6d739f7
+                              s->cluster_data, n * 512);
6d739f7
+        } else {
6d739f7
+            ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
6d739f7
+        }
6d739f7
+        if (ret != n * 512 || qcow2_alloc_cluster_link_l2(bs, cluster_offset, &l2meta) < 0) {
6d739f7
+            qcow2_free_any_clusters(bs, cluster_offset, l2meta.nb_clusters);
6d739f7
+            return -1;
6d739f7
+        }
6d739f7
+        nb_sectors -= n;
6d739f7
+        sector_num += n;
6d739f7
+        buf += n * 512;
6d739f7
+        if (l2meta.nb_clusters != 0) {
6d739f7
+            LIST_REMOVE(&l2meta, next_in_flight);
6d739f7
+        }
6d739f7
+    }
6d739f7
+    s->cluster_cache_offset = -1; /* disable compressed cache */
6d739f7
+    return 0;
6d739f7
+}
6d739f7
+
6d739f7
 /* XXX: put compressed sectors first, then all the cluster aligned
6d739f7
    tables to avoid losing bytes in alignment */
6d739f7
 static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
6d739f7
@@ -1037,8 +1082,10 @@ static BlockDriver bdrv_qcow2 = {
6d739f7
     .bdrv_set_key	= qcow_set_key,
6d739f7
     .bdrv_make_empty	= qcow_make_empty,
6d739f7
 
6d739f7
-    .bdrv_aio_readv	= qcow_aio_readv,
6d739f7
-    .bdrv_aio_writev	= qcow_aio_writev,
6d739f7
+    .bdrv_read          = qcow2_read,
6d739f7
+    .bdrv_write         = qcow2_write,
6d739f7
+    .bdrv_aio_readv     = qcow_aio_readv,
6d739f7
+    .bdrv_aio_writev    = qcow_aio_writev,
6d739f7
     .bdrv_write_compressed = qcow_write_compressed,
6d739f7
 
6d739f7
     .bdrv_snapshot_create   = qcow2_snapshot_create,
6d739f7
diff --git a/block/qcow2.h b/block/qcow2.h
6d739f7
index 965a2f4..b41aa63 100644
6d739f7
--- a/block/qcow2.h
6d739f7
+++ b/block/qcow2.h
6d739f7
@@ -202,6 +202,9 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
6d739f7
 int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset,
6d739f7
     QCowL2Meta *m);
6d739f7
 
6d739f7
+int qcow2_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
6d739f7
+    int nb_sectors);
6d739f7
+
6d739f7
 /* qcow2-snapshot.c functions */
6d739f7
 int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
6d739f7
 int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
6d739f7
-- 
6d739f7
1.6.2.5
6d739f7