Re: [PATCH 1/2] btrfs-progs: fsck: Add support to clear v1 free space cache.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Oct 13, 2016 at 05:22:26PM +0800, Qu Wenruo wrote:
> 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 <chrnosphered@xxxxxxxxx>
> Signed-off-by: Qu Wenruo <quwenruo@xxxxxxxxxxxxxx>
> ---
>  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);

The function can fail for a lot of reasons, what would be the filesystem
state when we exit here? Some of the inodes could be cleared completely,
the last one partially.  The function copes with a missing inode item
but I don't know how many other intermediate states could be left.

> +		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] <device>",
>  	"Check structural integrity of a filesystem (unmounted).",
> @@ -11233,6 +11263,9 @@ const char * const cmd_check_usage[] = {
>  	"-r|--tree-root <bytenr>     use the given bytenr for the tree root",
>  	"--chunk-root <bytenr>       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;
> +}
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Filesystem Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux