From patchwork Mon Oct 24 07:22:33 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qu Wenruo X-Patchwork-Id: 9391535 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 5B8E860231 for ; Mon, 24 Oct 2016 07:23:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 46EB128AA5 for ; Mon, 24 Oct 2016 07:23:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3AE1828BF8; Mon, 24 Oct 2016 07:23:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7104E28AE7 for ; Mon, 24 Oct 2016 07:23:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932854AbcJXHXS (ORCPT ); Mon, 24 Oct 2016 03:23:18 -0400 Received: from cn.fujitsu.com ([222.73.24.84]:19343 "EHLO song.cn.fujitsu.com" rhost-flags-OK-FAIL-OK-OK) by vger.kernel.org with ESMTP id S1752013AbcJXHXH (ORCPT ); Mon, 24 Oct 2016 03:23:07 -0400 X-IronPort-AV: E=Sophos;i="5.20,367,1444665600"; d="scan'208";a="923278" Received: from unknown (HELO cn.fujitsu.com) ([10.167.250.3]) by song.cn.fujitsu.com with ESMTP; 24 Oct 2016 15:22:39 +0800 Received: from adam-work.localdomain (unknown [10.167.226.34]) by cn.fujitsu.com (Postfix) with ESMTP id 055FA401E4CE; Mon, 24 Oct 2016 15:22:38 +0800 (CST) From: Qu Wenruo To: linux-btrfs@vger.kernel.org, dsterba@suse.cz Subject: [PATCH] btrfs-progs: Fix wrong tree block alignment for unalianged block group Date: Mon, 24 Oct 2016 15:22:33 +0800 Message-Id: <20161024072233.31643-1-quwenruo@cn.fujitsu.com> X-Mailer: git-send-email 2.10.1 MIME-Version: 1.0 X-yoursite-MailScanner-ID: 055FA401E4CE.AFAFC X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: quwenruo@cn.fujitsu.com Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Commit 854437ca(btrfs-progs: extent-tree: avoid allocating tree block that crosses stripe boundary) introduces check for logical bytenr not crossing stripe boundary. However that check is not completely correct. It only checks if the logical bytenr and length agaist absolute logical offset. That's to say, it only check if a tree block lies in 64K logical stripe. But in fact, it's possible a block group starts at bytenr unaligned with 64K, just like the following case. Then btrfsck will give false alert. 0 32K 64K 96K 128K 160K ... |--------------- Block group A --------------------- |<-----TB 32K------>| |/Scrub stripe unit/| | WRONG UNIT | In that case, TB(tree block) at bytenr 32K in fact fits into the kernel scrub stripe unit. But doesn't fit into the pure logical 64K stripe. Fix check_crossing_stripes() to compare bytenr to block group start, not to absolute logical bytenr. Reported-by: Jussi Kansanen Signed-off-by: Qu Wenruo --- cmds-check.c | 10 ++++++---- extent-tree.c | 15 ++++++++++++--- volumes.h | 23 ++++++++++++++++++++--- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/cmds-check.c b/cmds-check.c index 670ccd1..907d60c 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -4662,8 +4662,8 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache, bytes_used += rec->nr; if (tmpl->metadata) - rec->crossing_stripes = check_crossing_stripes(rec->start, - global_info->tree_root->nodesize); + rec->crossing_stripes = check_crossing_stripes(global_info, + rec->start, global_info->tree_root->nodesize); check_extent_type(rec); return ret; } @@ -4764,7 +4764,8 @@ static int add_extent_rec(struct cache_tree *extent_cache, */ if (tmpl->metadata) rec->crossing_stripes = check_crossing_stripes( - rec->start, global_info->tree_root->nodesize); + global_info, rec->start, + global_info->tree_root->nodesize); check_extent_type(rec); maybe_free_extent_rec(extent_cache, rec); return ret; @@ -9359,7 +9360,8 @@ static int check_extent_item(struct btrfs_fs_info *fs_info, if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) metadata = 1; - if (metadata && check_crossing_stripes(key.objectid, eb->len)) { + if (metadata && check_crossing_stripes(global_info, key.objectid, + eb->len)) { error("bad metadata [%llu, %llu) crossing stripe boundary", key.objectid, key.objectid + nodesize); err |= CROSSING_STRIPE_BOUNDARY; diff --git a/extent-tree.c b/extent-tree.c index f6d0a7c..3b1577e 100644 --- a/extent-tree.c +++ b/extent-tree.c @@ -2606,11 +2606,20 @@ check_failed: } if (!(data & BTRFS_BLOCK_GROUP_DATA)) { - if (check_crossing_stripes(ins->objectid, num_bytes)) { - search_start = round_down(ins->objectid + num_bytes, - BTRFS_STRIPE_LEN); + if (check_crossing_stripes(info, ins->objectid, num_bytes)) { + struct btrfs_block_group_cache *bg_cache; + u64 bg_offset; + + bg_cache = btrfs_lookup_block_group(info, ins->objectid); + if (!bg_cache) + goto no_bg_cache; + bg_offset = ins->objectid - bg_cache->key.objectid; + + search_start = round_up(bg_offset + num_bytes, + BTRFS_STRIPE_LEN) + bg_offset; goto new_group; } +no_bg_cache: block_group = btrfs_lookup_block_group(info, ins->objectid); if (block_group) trans->block_group = block_group; diff --git a/volumes.h b/volumes.h index d7b7d3c..7cb38b0 100644 --- a/volumes.h +++ b/volumes.h @@ -155,11 +155,28 @@ struct map_lookup { * Check if the given range cross stripes. * To ensure kernel scrub won't causing bug on with METADATA in mixed * block group + * + * Return 1 if the range crosses STRIPE boundary + * Return 0 if the range doesn't cross STRIPE boundar or it + * doesn't belongs to any block group(no boundary to cross) */ -static inline int check_crossing_stripes(u64 start, u64 len) +static inline int check_crossing_stripes(struct btrfs_fs_info *fs_info, + u64 start, u64 len) { - return (start / BTRFS_STRIPE_LEN) != - ((start + len - 1) / BTRFS_STRIPE_LEN); + struct btrfs_block_group_cache *bg_cache; + u64 bg_offset; + + bg_cache = btrfs_lookup_block_group(fs_info, start); + /* + * not belongs to block group, no boundary to cross + * although it's a bigger problem, but here we don't care. + */ + if (!bg_cache) + return 0; + bg_offset = start - bg_cache->key.objectid; + + return (bg_offset / BTRFS_STRIPE_LEN != + (bg_offset + len - 1) / BTRFS_STRIPE_LEN); } int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,