diff mbox series

[1/7] btrfs-progs: tune: implement resume support for metadata checksum

Message ID 592baf7bd93e0fce6a30faa7a216f894c66aab09.1684913599.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs-progs: tune: add resume support for csum conversion | expand

Commit Message

Qu Wenruo May 24, 2023, 7:41 a.m. UTC
For interrupted metadata checksum change, we only need to call the same
change_meta_csums().

Since we don't have any record on the last converted metadata, thus we
have to go through all metadata anyway.
And the existing change_meta_csums() has already implemented the needed
checks to skip converted metadata.

Since we're here, also implement all the surrounding checks, like making
sure the new target csum type matches the interrupted one.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 tune/change-csum.c | 88 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 81 insertions(+), 7 deletions(-)
diff mbox series

Patch

diff --git a/tune/change-csum.c b/tune/change-csum.c
index b5efc3a8807f..7ae618a433cb 100644
--- a/tune/change-csum.c
+++ b/tune/change-csum.c
@@ -45,12 +45,7 @@  static int check_csum_change_requreiment(struct btrfs_fs_info *fs_info)
 		error("no csum change support for extent-tree-v2 feature yet.");
 		return -EOPNOTSUPP;
 	}
-	if (btrfs_super_flags(fs_info->super_copy) &
-	    (BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM |
-	     BTRFS_SUPER_FLAG_CHANGING_META_CSUM)) {
-		error("resume from half converted status is not yet supported");
-		return -EOPNOTSUPP;
-	}
+
 	key.objectid = BTRFS_BALANCE_OBJECTID;
 	key.type = BTRFS_TEMPORARY_ITEM_KEY;
 	key.offset = 0;
@@ -522,7 +517,7 @@  out:
 	return ret;
 }
 
-static int change_meta_csums(struct btrfs_fs_info *fs_info, u32 new_csum_type)
+static int change_meta_csums(struct btrfs_fs_info *fs_info, u16 new_csum_type)
 {
 	struct btrfs_root *extent_root = btrfs_extent_root(fs_info, 0);
 	struct btrfs_path path = { 0 };
@@ -640,6 +635,71 @@  out:
 	return ret;
 }
 
+static int resume_csum_change(struct btrfs_fs_info *fs_info, u16 new_csum_type)
+{
+	const u64 super_flags = btrfs_super_flags(fs_info->super_copy);
+	struct btrfs_root *tree_root = fs_info->tree_root;
+	struct btrfs_path path = { 0 };
+	struct btrfs_key key;
+	int ret;
+
+	if ((super_flags & (BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM |
+			    BTRFS_SUPER_FLAG_CHANGING_META_CSUM)) ==
+	    (BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM |
+	     BTRFS_SUPER_FLAG_CHANGING_META_CSUM)) {
+		error("Invalid super flags, only one bit of CHANGING_DATA_CSUM or CHANGING_META_CSUM can be set");
+		return -EUCLEAN;
+	}
+
+	key.objectid = BTRFS_CSUM_CHANGE_OBJECTID;
+	key.type = BTRFS_TEMPORARY_ITEM_KEY;
+	key.offset = (u64)-1;
+	ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to locate the csum change item: %m");
+		return ret;
+	}
+	assert(ret > 0);
+	ret = btrfs_previous_item(tree_root, &path, BTRFS_CSUM_CHANGE_OBJECTID,
+				  BTRFS_TEMPORARY_ITEM_KEY);
+	if (ret > 0)
+		ret = -ENOENT;
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to locate the csum change item: %m");
+		btrfs_release_path(&path);
+		return ret;
+	}
+	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+	btrfs_release_path(&path);
+
+	if (new_csum_type != key.offset) {
+		ret = -EINVAL;
+		error("target csum type mismatch with interrupted csum type, has %s (%u) expect %s (%llu)",
+		      btrfs_super_csum_name(new_csum_type), new_csum_type,
+		      btrfs_super_csum_name(key.offset), key.offset);
+		return ret;
+	}
+
+	if (super_flags & BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM) {
+		error("resume on data checksum changing is not yet supported");
+		return -EOPNOTSUPP;
+	}
+
+	/*
+	 * For metadata resume, just call the same change_meta_csums(), as we
+	 * have no record on previous converted metadata, thus have to go
+	 * through all metadata anyway.
+	 */
+	ret = change_meta_csums(fs_info, new_csum_type);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to resume metadata csum change: %m");
+	}
+	return ret;
+}
+
 int btrfs_change_csum_type(struct btrfs_fs_info *fs_info, u16 new_csum_type)
 {
 	u16 old_csum_type = fs_info->csum_type;
@@ -650,6 +710,20 @@  int btrfs_change_csum_type(struct btrfs_fs_info *fs_info, u16 new_csum_type)
 	if (ret < 0)
 		return ret;
 
+	if (btrfs_super_flags(fs_info->super_copy) &
+	    (BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM |
+	     BTRFS_SUPER_FLAG_CHANGING_META_CSUM)) {
+		ret = resume_csum_change(fs_info, new_csum_type);
+		if (ret < 0) {
+			errno = -ret;
+			error("failed to resume unfinished csum change: %m");
+			return ret;
+		}
+		printf("converted csum type from %s (%u) to %s (%u)\n",
+		       btrfs_super_csum_name(old_csum_type), old_csum_type,
+		       btrfs_super_csum_name(new_csum_type), new_csum_type);
+		return ret;
+	}
 	/*
 	 * Phase 1, generate new data csums.
 	 *