From: Gleb Natapov <gleb@redhat.com>
Date: Thu, 7 May 2009 08:31:44 +0000 (+0300)
Subject: Fix cluster freeing in qcow2
X-Git-Tag: v0.10.4~6
X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=8bc2ad6a6aec73844fb0091f9daf73dc8ee4d61c;p=users%2Fdwmw2%2Fqemu.git

Fix cluster freeing in qcow2

Need to drop QCOW_OFLAG_COPIED from a cluster pointer before freeing it.

Add an explanation how thing meant to work.

Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
---

diff --git a/block-qcow2.c b/block-qcow2.c
index 74a0dfa156..a984611a9a 100644
--- a/block-qcow2.c
+++ b/block-qcow2.c
@@ -903,6 +903,12 @@ static int alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset,
         goto err;
 
     for (i = 0; i < m->nb_clusters; i++) {
+        /* if two concurrent writes happen to the same unallocated cluster
+	 * each write allocates separate cluster and writes data concurrently.
+	 * The first one to complete updates l2 table with pointer to its
+	 * cluster the second one has to do RMW (which is done above by
+	 * copy_sectors()), update l2 table with its cluster pointer and free
+	 * old cluster. This is what this loop does */
         if(l2_table[l2_index + i] != 0)
             old_cluster[j++] = l2_table[l2_index + i];
 
@@ -916,7 +922,8 @@ static int alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset,
         goto err;
 
     for (i = 0; i < j; i++)
-        free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1);
+        free_any_clusters(bs, be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED,
+                          1);
 
     ret = 0;
 err: