Fixed that errors had been counted multiple times. Signed-off-by: Stefan Behrens <sbehrens@giantdisaster.de> --- fs/btrfs/ioctl.h | 30 ++++++++------ fs/btrfs/scrub.c | 115 ++++++++++++++++++++++++++++++++---------------------- 2 files changed, 85 insertions(+), 60 deletions(-) diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h index 4f69028..48b926c 100644 --- a/fs/btrfs/ioctl.h +++ b/fs/btrfs/ioctl.h @@ -49,17 +49,20 @@ struct btrfs_ioctl_vol_args_v2 { * result of a finished scrub, a canceled scrub or a progress inquiry */ struct btrfs_scrub_progress { - __u64 data_extents_scrubbed; /* # of data extents scrubbed */ - __u64 tree_extents_scrubbed; /* # of tree extents scrubbed */ + __u64 data_extents_scrubbed; /* # of 4k data data extents scrubbed */ + __u64 tree_extents_scrubbed; /* # of 4k data tree extents scrubbed */ __u64 data_bytes_scrubbed; /* # of data bytes scrubbed */ __u64 tree_bytes_scrubbed; /* # of tree bytes scrubbed */ - __u64 read_errors; /* # of read errors encountered (EIO) */ - __u64 csum_errors; /* # of failed csum checks */ - __u64 verify_errors; /* # of occurences, where the metadata - * of a tree block did not match the - * expected values, like generation or - * logical */ - __u64 no_csum; /* # of 4k data block for which no csum + __u64 read_errors; /* # of 4k data blocks which encountered + * read errors (EIO) */ + __u64 csum_errors; /* # of 4k data blocks which failed csum + * checks */ + __u64 verify_errors; /* # of 4k data blocks, where the + * metadata of a tree block did not + * match the expected values, like + * generation or logical and the + * checksum was not incorrect */ + __u64 no_csum; /* # of 4k data blocks for which no csum * is present, probably the result of * data written with nodatasum */ __u64 csum_discards; /* # of csum for which no data was found @@ -68,10 +71,11 @@ struct btrfs_scrub_progress { __u64 malloc_errors; /* # of internal kmalloc errors. These * will likely cause an incomplete * scrub */ - __u64 uncorrectable_errors; /* # of errors where either no intact - * copy was found or the writeback - * failed */ - __u64 corrected_errors; /* # of errors corrected */ + __u64 uncorrectable_errors; /* # of 4k data blocks with errors where + * either no intact copy was found or + * the writeback failed */ + __u64 corrected_errors; /* # of 4k data blocks with corrected + * errors */ __u64 last_physical; /* last physical address scrubbed. In * case a scrub was aborted, this can * be used to restart the scrub */ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 9770cc5..21ea2ab 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -48,10 +48,11 @@ struct scrub_dev; static void scrub_bio_end_io(struct bio *bio, int err); static void scrub_checksum(struct btrfs_work *work); static int scrub_checksum_data(struct scrub_dev *sdev, - struct scrub_page *spag, void *buffer); + struct scrub_page *spag, void *buffer, + int modify_stats); static int scrub_checksum_tree_block(struct scrub_dev *sdev, struct scrub_page *spag, u64 logical, - void *buffer); + void *buffer, int modify_stats); static int scrub_checksum_super(struct scrub_bio *sbio, void *buffer); static int scrub_fixup_check(struct scrub_bio *sbio, int ix); static void scrub_fixup_end_io(struct bio *bio, int err); @@ -555,7 +556,8 @@ out: * recheck_error gets called for every page in the bio, even though only * one may be bad */ -static int scrub_recheck_error(struct scrub_bio *sbio, int ix) +static int scrub_recheck_error(struct scrub_bio *sbio, int ix, + int modify_stats) { struct scrub_dev *sdev = sbio->sdev; u64 sector = (sbio->physical + ix * PAGE_SIZE) >> 9; @@ -575,9 +577,11 @@ static int scrub_recheck_error(struct scrub_bio *sbio, int ix) scrub_print_warning("checksum error", sbio, ix); } - spin_lock(&sdev->stat_lock); - ++sdev->stat.read_errors; - spin_unlock(&sdev->stat_lock); + if (modify_stats) { + spin_lock(&sdev->stat_lock); + ++sdev->stat.read_errors; + spin_unlock(&sdev->stat_lock); + } scrub_fixup(sbio, ix); return 1; @@ -594,12 +598,12 @@ static int scrub_fixup_check(struct scrub_bio *sbio, int ix) buffer = kmap_atomic(page, KM_USER0); if (flags & BTRFS_EXTENT_FLAG_DATA) { ret = scrub_checksum_data(sbio->sdev, - sbio->spag + ix, buffer); + sbio->spag + ix, buffer, 0); } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { ret = scrub_checksum_tree_block(sbio->sdev, sbio->spag + ix, sbio->logical + ix * PAGE_SIZE, - buffer); + buffer, 0); } else { WARN_ON(1); } @@ -700,15 +704,15 @@ static void scrub_fixup(struct scrub_bio *sbio, int ix) /* I/O-error, writeback failed, give up */ goto uncorrectable; } + + spin_lock(&sdev->stat_lock); + ++sdev->stat.corrected_errors; + spin_unlock(&sdev->stat_lock); + printk_ratelimited(KERN_ERR "btrfs: fixed up error at logical " + "%llu\n", (unsigned long long)logical); } kfree(bbio); - spin_lock(&sdev->stat_lock); - ++sdev->stat.corrected_errors; - spin_unlock(&sdev->stat_lock); - - printk_ratelimited(KERN_ERR "btrfs: fixed up error at logical %llu\n", - (unsigned long long)logical); return; uncorrectable: @@ -766,16 +770,15 @@ static void scrub_checksum(struct btrfs_work *work) u64 flags; u64 logical; int ret; + int recheck_succeeded_at_least_once = 0; + int recheck_failed_at_least_once = 0; if (sbio->err) { - ret = 0; for (i = 0; i < sbio->count; ++i) - ret |= scrub_recheck_error(sbio, i); - if (!ret) { - spin_lock(&sdev->stat_lock); - ++sdev->stat.unverified_errors; - spin_unlock(&sdev->stat_lock); - } + if (scrub_recheck_error(sbio, i, 1)) + recheck_failed_at_least_once = 1; + else + recheck_succeeded_at_least_once = 1; sbio->bio->bi_flags &= ~(BIO_POOL_MASK - 1); sbio->bio->bi_flags |= 1 << BIO_UPTODATE; @@ -797,10 +800,11 @@ static void scrub_checksum(struct btrfs_work *work) logical = sbio->logical + i * PAGE_SIZE; ret = 0; if (flags & BTRFS_EXTENT_FLAG_DATA) { - ret = scrub_checksum_data(sdev, sbio->spag + i, buffer); + ret = scrub_checksum_data(sdev, sbio->spag + i, buffer, + 1); } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { ret = scrub_checksum_tree_block(sdev, sbio->spag + i, - logical, buffer); + logical, buffer, 1); } else if (flags & BTRFS_EXTENT_FLAG_SUPER) { BUG_ON(i); (void)scrub_checksum_super(sbio, buffer); @@ -809,16 +813,19 @@ static void scrub_checksum(struct btrfs_work *work) } kunmap_atomic(buffer, KM_USER0); if (ret) { - ret = scrub_recheck_error(sbio, i); - if (!ret) { - spin_lock(&sdev->stat_lock); - ++sdev->stat.unverified_errors; - spin_unlock(&sdev->stat_lock); - } + if (scrub_recheck_error(sbio, i, 0)) + recheck_failed_at_least_once = 1; + else + recheck_succeeded_at_least_once = 1; } } out: + if (recheck_succeeded_at_least_once && !recheck_failed_at_least_once) { + spin_lock(&sdev->stat_lock); + ++sdev->stat.unverified_errors; + spin_unlock(&sdev->stat_lock); + } scrub_free_bio(sbio->bio); sbio->bio = NULL; spin_lock(&sdev->list_lock); @@ -830,7 +837,8 @@ out: } static int scrub_checksum_data(struct scrub_dev *sdev, - struct scrub_page *spag, void *buffer) + struct scrub_page *spag, void *buffer, + int modify_stats) { u8 csum[BTRFS_CSUM_SIZE]; u32 crc = ~(u32)0; @@ -842,22 +850,21 @@ static int scrub_checksum_data(struct scrub_dev *sdev, crc = btrfs_csum_data(root, buffer, crc, PAGE_SIZE); btrfs_csum_final(crc, csum); - if (memcmp(csum, spag->csum, sdev->csum_size)) + if (memcmp(csum, spag->csum, sdev->csum_size)) { fail = 1; - - spin_lock(&sdev->stat_lock); - ++sdev->stat.data_extents_scrubbed; - sdev->stat.data_bytes_scrubbed += PAGE_SIZE; - if (fail) - ++sdev->stat.csum_errors; - spin_unlock(&sdev->stat_lock); + if (modify_stats) { + spin_lock(&sdev->stat_lock); + ++sdev->stat.csum_errors; + spin_unlock(&sdev->stat_lock); + } + } return fail; } static int scrub_checksum_tree_block(struct scrub_dev *sdev, struct scrub_page *spag, u64 logical, - void *buffer) + void *buffer, int modify_stats) { struct btrfs_header *h; struct btrfs_root *root = sdev->dev->dev_root; @@ -893,14 +900,17 @@ static int scrub_checksum_tree_block(struct scrub_dev *sdev, if (memcmp(csum, h->csum, sdev->csum_size)) ++crc_fail; - spin_lock(&sdev->stat_lock); - ++sdev->stat.tree_extents_scrubbed; - sdev->stat.tree_bytes_scrubbed += PAGE_SIZE; - if (crc_fail) - ++sdev->stat.csum_errors; - if (fail) - ++sdev->stat.verify_errors; - spin_unlock(&sdev->stat_lock); + if (modify_stats) { + if (crc_fail) { + spin_lock(&sdev->stat_lock); + ++sdev->stat.csum_errors; + spin_unlock(&sdev->stat_lock); + } else if (fail) { + spin_lock(&sdev->stat_lock); + ++sdev->stat.verify_errors; + spin_unlock(&sdev->stat_lock); + } + } return fail || crc_fail; } @@ -1106,6 +1116,17 @@ static int scrub_extent(struct scrub_dev *sdev, u64 logical, u64 len, } ret = scrub_page(sdev, logical, l, physical, flags, gen, mirror_num, have_csum ? csum : NULL, 0); + spin_lock(&sdev->stat_lock); + if (flags & BTRFS_EXTENT_FLAG_DATA) { + ++sdev->stat.data_extents_scrubbed; + sdev->stat.data_bytes_scrubbed += l; + } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { + ++sdev->stat.tree_extents_scrubbed; + sdev->stat.tree_bytes_scrubbed += l; + } else { + WARN_ON(1); + } + spin_unlock(&sdev->stat_lock); if (ret) return ret; len -= l; -- 1.7.3.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html