From 514e9d197df41e2c7e3898a041e5ba89128930cf Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 2 May 2014 16:58:42 -0500 Subject: [PATCH 10/26] qcow2-refcount: Repair OFLAG_COPIED errors RH-Author: Max Reitz Message-id: <1399049936-13496-11-git-send-email-mreitz@redhat.com> Patchwork-id: 58656 O-Subject: [RHEL-6.6 qemu-kvm PATCH v3 10/24] qcow2-refcount: Repair OFLAG_COPIED errors Bugzilla: 1004420 RH-Acked-by: Laszlo Ersek RH-Acked-by: Kevin Wolf RH-Acked-by: Stefan Hajnoczi BZ: 1004420 Since the OFLAG_COPIED checks are now executed after the refcounts have been repaired (if repairing), it is safe to assume that they are correct but the OFLAG_COPIED flag may be not. Therefore, if its value differs from what it should be (considering the according refcount), that discrepancy can be repaired by correctly setting (or clearing that flag. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf (cherry picked from commit e23e400ec62a03dea58ddb38479b4f1ef86f556d) Conflicts: block/qcow2.h qcow2_grow_l1_table() has a different signature upstream (contextual). Signed-off-by: Max Reitz --- block/qcow2-cluster.c | 4 ++-- block/qcow2-refcount.c | 58 ++++++++++++++++++++++++++++++++++++++++++++------ block/qcow2.h | 1 + 3 files changed, 55 insertions(+), 8 deletions(-) Signed-off-by: Jeff E. Nelson --- block/qcow2-cluster.c | 4 +- block/qcow2-refcount.c | 58 +++++++++++++++++++++++++++++++++++++++++++----- block/qcow2.h | 1 + 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index c921dec..6679c33 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -135,7 +135,7 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset, * and we really don't want bdrv_pread to perform a read-modify-write) */ #define L1_ENTRIES_PER_SECTOR (512 / 8) -static int write_l1_entry(BlockDriverState *bs, int l1_index) +int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) { BDRVQcowState *s = bs->opaque; uint64_t buf[L1_ENTRIES_PER_SECTOR]; @@ -238,7 +238,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) /* update the L1 entry */ s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; - ret = write_l1_entry(bs, l1_index); + ret = qcow2_write_l1_entry(bs, l1_index); if (ret < 0) { goto fail; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index da7c566..f15f252 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1064,7 +1064,8 @@ fail: * been already detected and sufficiently signaled by the calling function * (qcow2_check_refcounts) by the time this function is called). */ -static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res) +static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) { BDRVQcowState *s = bs->opaque; uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); @@ -1075,6 +1076,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res) for (i = 0; i < s->l1_size; i++) { uint64_t l1_entry = s->l1_table[i]; uint64_t l2_offset = l1_entry & L1E_OFFSET_MASK; + bool l2_dirty = false; if (!l2_offset) { continue; @@ -1086,10 +1088,24 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res) continue; } if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { - fprintf(stderr, "ERROR OFLAG_COPIED L2 cluster: l1_index=%d " + fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " "l1_entry=%" PRIx64 " refcount=%d\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : + "ERROR", i, l1_entry, refcount); - res->corruptions++; + if (fix & BDRV_FIX_ERRORS) { + s->l1_table[i] = refcount == 1 + ? l1_entry | QCOW_OFLAG_COPIED + : l1_entry & ~QCOW_OFLAG_COPIED; + ret = qcow2_write_l1_entry(bs, i); + if (ret < 0) { + res->check_errors++; + goto fail; + } + res->corruptions_fixed++; + } else { + res->corruptions++; + } } ret = bdrv_pread(bs->file, l2_offset, l2_table, @@ -1113,13 +1129,43 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res) continue; } if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { - fprintf(stderr, "ERROR OFLAG_COPIED data cluster: " + fprintf(stderr, "%s OFLAG_COPIED data cluster: " "l2_entry=%" PRIx64 " refcount=%d\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : + "ERROR", l2_entry, refcount); - res->corruptions++; + if (fix & BDRV_FIX_ERRORS) { + l2_table[j] = cpu_to_be64(refcount == 1 + ? l2_entry | QCOW_OFLAG_COPIED + : l2_entry & ~QCOW_OFLAG_COPIED); + l2_dirty = true; + res->corruptions_fixed++; + } else { + res->corruptions++; + } } } } + + if (l2_dirty) { + ret = qcow2_pre_write_overlap_check(bs, + QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2, l2_offset, + s->cluster_size); + if (ret < 0) { + fprintf(stderr, "ERROR: Could not write L2 table; metadata " + "overlap check failed: %s\n", strerror(-ret)); + res->check_errors++; + goto fail; + } + + ret = bdrv_pwrite(bs->file, l2_offset, l2_table, s->cluster_size); + if (ret < 0) { + fprintf(stderr, "ERROR: Could not write L2 table: %s\n", + strerror(-ret)); + res->check_errors++; + goto fail; + } + } } ret = 0; @@ -1265,7 +1311,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, } /* check OFLAG_COPIED */ - ret = check_oflag_copied(bs, res); + ret = check_oflag_copied(bs, res, fix); if (ret < 0) { goto fail; } diff --git a/block/qcow2.h b/block/qcow2.h index dc437ae..9e2ffa9 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -316,6 +316,7 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int chk, int64_t offset, /* qcow2-cluster.c functions */ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size); +int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); void qcow2_l2_cache_reset(BlockDriverState *bs); int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, -- 1.7.1