From patchwork Thu Oct 13 09:22:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qu Wenruo X-Patchwork-Id: 9374593 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 C49BE607FD for ; Thu, 13 Oct 2016 09:23:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B501E29EB0 for ; Thu, 13 Oct 2016 09:23:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A9C5B29EB2; Thu, 13 Oct 2016 09:23:44 +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 E3F3329EB0 for ; Thu, 13 Oct 2016 09:23:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752959AbcJMJXf (ORCPT ); Thu, 13 Oct 2016 05:23:35 -0400 Received: from cn.fujitsu.com ([222.73.24.84]:16152 "EHLO song.cn.fujitsu.com" rhost-flags-OK-FAIL-OK-OK) by vger.kernel.org with ESMTP id S1752782AbcJMJXS (ORCPT ); Thu, 13 Oct 2016 05:23:18 -0400 X-IronPort-AV: E=Sophos;i="5.20,367,1444665600"; d="scan'208";a="895724" Received: from unknown (HELO cn.fujitsu.com) ([10.167.250.3]) by song.cn.fujitsu.com with ESMTP; 13 Oct 2016 17:22:36 +0800 Received: from adam-work.localdomain (unknown [10.167.226.34]) by cn.fujitsu.com (Postfix) with ESMTP id 30FE641B4BC8; Thu, 13 Oct 2016 17:22:34 +0800 (CST) From: Qu Wenruo To: linux-btrfs@vger.kernel.org Cc: dsterba@suse.cz Subject: [PATCH 1/2] btrfs-progs: fsck: Add support to clear v1 free space cache. Date: Thu, 13 Oct 2016 17:22:26 +0800 Message-Id: <20161013092227.19761-1-quwenruo@cn.fujitsu.com> X-Mailer: git-send-email 2.10.0 MIME-Version: 1.0 X-yoursite-MailScanner-ID: 30FE641B4BC8.AF74A 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 Kernel clear_cache mount option will only rebuilt free space cache if used space of that chunk has changed. So it won't ensure any corrupted free space cache get cleared. So add a new option "--clear-space-cache v1|v2" to btrfsck, to completely wipe out free space cache. So kernel won't complain again. Reported-by: Ivan P Signed-off-by: Qu Wenruo --- Documentation/btrfs-check.asciidoc | 9 +++ cmds-check.c | 63 ++++++++++++++++++- free-space-cache.c | 124 +++++++++++++++++++++++++++++++++++++ free-space-cache.h | 2 + 4 files changed, 197 insertions(+), 1 deletion(-) diff --git a/Documentation/btrfs-check.asciidoc b/Documentation/btrfs-check.asciidoc index a32e1c7..ef1e464 100644 --- a/Documentation/btrfs-check.asciidoc +++ b/Documentation/btrfs-check.asciidoc @@ -78,6 +78,15 @@ respective superblock offset is within the device size This can be used to use a different starting point if some of the primary superblock is damaged. +--clear-space-cache v1|v2:: +completely wipe out all free space cache. +Only v1(file based) free space cache is supported yet. ++ +NOTE: Kernel mount option 'clear_cache' is only designed to rebuild free space cache +which is modified during the lifetime of that mount option. +It doesn't rebuild all free space cache, nor clear them out. + + DANGEROUS OPTIONS ----------------- diff --git a/cmds-check.c b/cmds-check.c index 670ccd1..f62fc62 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -11206,6 +11206,36 @@ out: return bad_roots; } +static int clear_free_space_cache(struct btrfs_fs_info *fs_info) +{ + struct btrfs_trans_handle *trans; + struct btrfs_block_group_cache *bg_cache; + u64 current = 0; + int ret = 0; + + /* Clear all free space cache inodes and its extent data */ + while (1) { + bg_cache = btrfs_lookup_first_block_group(fs_info, current); + if (!bg_cache) + break; + ret = btrfs_clear_free_space_cache(fs_info, bg_cache); + if (ret < 0) + return ret; + current = bg_cache->key.objectid + bg_cache->key.offset; + } + + /* Don't forget to set cache_generation to -1 */ + trans = btrfs_start_transaction(fs_info->tree_root, 0); + if (IS_ERR(trans)) { + error("failed to update super block cache generation"); + return PTR_ERR(trans); + } + btrfs_set_super_cache_generation(fs_info->super_copy, (u64)-1); + btrfs_commit_transaction(trans, fs_info->tree_root); + + return ret; +} + const char * const cmd_check_usage[] = { "btrfs check [options] ", "Check structural integrity of a filesystem (unmounted).", @@ -11233,6 +11263,9 @@ const char * const cmd_check_usage[] = { "-r|--tree-root use the given bytenr for the tree root", "--chunk-root use the given bytenr for the chunk tree root", "-p|--progress indicate progress", + "--clear-space-cache v1|v2 clear space cache for v1(file based) or ", + " v2(tree based).", + " Only support v1 yet", NULL }; @@ -11250,6 +11283,7 @@ int cmd_check(int argc, char **argv) u64 num; int init_csum_tree = 0; int readonly = 0; + int clear_space_cache = 0; int qgroup_report = 0; int qgroups_repaired = 0; unsigned ctree_flags = OPEN_CTREE_EXCLUSIVE; @@ -11259,7 +11293,7 @@ int cmd_check(int argc, char **argv) enum { GETOPT_VAL_REPAIR = 257, GETOPT_VAL_INIT_CSUM, GETOPT_VAL_INIT_EXTENT, GETOPT_VAL_CHECK_CSUM, GETOPT_VAL_READONLY, GETOPT_VAL_CHUNK_TREE, - GETOPT_VAL_MODE }; + GETOPT_VAL_MODE, GETOPT_VAL_CLEAR_SPACE_CACHE }; static const struct option long_options[] = { { "super", required_argument, NULL, 's' }, { "repair", no_argument, NULL, GETOPT_VAL_REPAIR }, @@ -11279,6 +11313,8 @@ int cmd_check(int argc, char **argv) { "progress", no_argument, NULL, 'p' }, { "mode", required_argument, NULL, GETOPT_VAL_MODE }, + { "clear-space-cache", required_argument, NULL, + GETOPT_VAL_CLEAR_SPACE_CACHE}, { NULL, 0, NULL, 0} }; @@ -11350,6 +11386,14 @@ int cmd_check(int argc, char **argv) exit(1); } break; + case GETOPT_VAL_CLEAR_SPACE_CACHE: + if (strcmp(optarg, "v1")) { + error("only support to clear 'v1' space cache"); + exit(1); + } + clear_space_cache = 1; + ctree_flags |= OPEN_CTREE_WRITES; + break; } } @@ -11401,6 +11445,23 @@ int cmd_check(int argc, char **argv) global_info = info; root = info->fs_root; + if (clear_space_cache) { + if (btrfs_fs_compat_ro(info, + BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) { + error("doesn't support free space cache v2(tree based) yet"); + ret = 1; + goto close_out; + } + printf("Clearing free space cache\n"); + ret = clear_free_space_cache(info); + if (ret) { + error("failed to clear free space cache"); + ret = 1; + } else { + printf("Free space cache cleared\n"); + } + goto close_out; + } /* * repair mode will force us to commit transaction which diff --git a/free-space-cache.c b/free-space-cache.c index 1919d90..88a1013 100644 --- a/free-space-cache.c +++ b/free-space-cache.c @@ -25,6 +25,7 @@ #include "crc32c.h" #include "bitops.h" #include "internal.h" +#include "utils.h" /* * Kernel always uses PAGE_CACHE_SIZE for sectorsize, but we don't have @@ -877,3 +878,126 @@ next: prev = e; } } + +int btrfs_clear_free_space_cache(struct btrfs_fs_info *fs_info, + struct btrfs_block_group_cache *bg) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_disk_key location; + struct btrfs_free_space_header *sc_header; + struct extent_buffer *node; + u64 ino; + int slot; + int ret; + + trans = btrfs_start_transaction(tree_root, 1); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + btrfs_init_path(&path); + + key.objectid = BTRFS_FREE_SPACE_OBJECTID; + key.type = 0; + key.offset = bg->key.objectid; + + ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1); + if (ret > 0) { + ret = 0; + goto out; + } + if (ret < 0) + goto out; + + node = path.nodes[0]; + slot = path.slots[0]; + sc_header = btrfs_item_ptr(node, slot, struct btrfs_free_space_header); + btrfs_free_space_key(node, sc_header, &location); + ino = location.objectid; + + /* Delete the free space header, as we have the ino to continue */ + ret = btrfs_del_item(trans, tree_root, &path); + if (ret < 0) { + error("failed to remove free space header for block group %llu", + bg->key.objectid); + goto out; + } + btrfs_release_path(&path); + + /* Iterate from the end of the free space cache inode */ + key.objectid = ino; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = (u64)-1; + ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1); + if (ret < 0) { + error("failed to locate free space cache extent for block group %llu", + bg->key.objectid); + goto out; + } + while (1) { + struct btrfs_file_extent_item *fi; + u64 disk_bytenr; + u64 disk_num_bytes; + + + ret = btrfs_previous_item(tree_root, &path, ino, + BTRFS_EXTENT_DATA_KEY); + if (ret > 0) { + ret = 0; + break; + } + if (ret < 0) { + error("failed to locate free space cache extent for block group %llu", + bg->key.objectid); + goto out; + } + node = path.nodes[0]; + slot = path.slots[0]; + btrfs_item_key_to_cpu(node, &key, slot); + fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); + disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi); + disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi); + + ret = btrfs_free_extent(trans, tree_root, disk_bytenr, + disk_num_bytes, 0, tree_root->objectid, + ino, key.offset); + if (ret < 0) { + error("failed to remove backref for disk bytenr %llu", + disk_bytenr); + goto out; + } + ret = btrfs_del_item(trans, tree_root, &path); + if (ret < 0) { + error("failed to remove free space extent data for ino %llu offset %llu", + ino, key.offset); + goto out; + } + } + btrfs_release_path(&path); + + /* Now delete free space cache inode item */ + key.objectid = ino; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1); + if (ret > 0) + warning("free space inode %llu not found, ignore", ino); + if (ret < 0) { + error("failed to locate free space cache inode %llu for block group %llu", + ino, bg->key.objectid); + goto out; + } + ret = btrfs_del_item(trans, tree_root, &path); + if (ret < 0) { + error("failed to delete free space cache inode %llu for block group %llu", + ino, bg->key.objectid); + } +out: + btrfs_release_path(&path); + if (!ret) + btrfs_commit_transaction(trans, tree_root); + return ret; +} diff --git a/free-space-cache.h b/free-space-cache.h index 9214077..707fb6d 100644 --- a/free-space-cache.h +++ b/free-space-cache.h @@ -59,4 +59,6 @@ void unlink_free_space(struct btrfs_free_space_ctl *ctl, struct btrfs_free_space *info); int btrfs_add_free_space(struct btrfs_free_space_ctl *ctl, u64 offset, u64 bytes); +int btrfs_clear_free_space_cache(struct btrfs_fs_info *fs_info, + struct btrfs_block_group_cache *bg); #endif