From 3cf00a0a38c8304ca4ef79835825cd99c894d188 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 18 Jan 2023 16:02:17 +0100 Subject: [PATCH] btrfs-progs: tune: factor out checksum change to own file Signed-off-by: David Sterba --- Makefile | 2 +- tune/change-csum.c | 332 +++++++++++++++++++++++++++++++++++++++++++++ tune/main.c | 307 ----------------------------------------- tune/tune.h | 2 + 4 files changed, 335 insertions(+), 308 deletions(-) create mode 100644 tune/change-csum.c diff --git a/Makefile b/Makefile index bca3590f..26bd8f4c 100644 --- a/Makefile +++ b/Makefile @@ -235,7 +235,7 @@ convert_objects = convert/main.o convert/common.o convert/source-fs.o \ mkfs_objects = mkfs/main.o mkfs/common.o mkfs/rootdir.o image_objects = image/main.o image/sanitize.o tune_objects = tune/main.o tune/seeding.o tune/change-uuid.o tune/change-metadata-uuid.o \ - tune/convert-bgt.o + tune/convert-bgt.o tune/change-csum.o all_objects = $(objects) $(cmds_objects) $(libbtrfs_objects) $(convert_objects) \ $(mkfs_objects) $(image_objects) $(tune_objects) $(libbtrfsutil_objects) diff --git a/tune/change-csum.c b/tune/change-csum.c new file mode 100644 index 00000000..0c0cb723 --- /dev/null +++ b/tune/change-csum.c @@ -0,0 +1,332 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include "kerncompat.h" +#include +#include +#include +#include "kernel-shared/ctree.h" +#include "kernel-shared/disk-io.h" +#include "kernel-shared/extent_io.h" +#include "kernel-shared/transaction.h" +#include "common/messages.h" + +static int delete_csum_items(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info) +{ + struct btrfs_root *root = btrfs_csum_root(fs_info, 0); + struct btrfs_path path; + struct btrfs_key key; + int nr; + int ret; + + btrfs_init_path(&path); + + key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; + key.type = BTRFS_EXTENT_CSUM_KEY; + key.offset = 0; + + while (1) { + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); + if (ret < 0) + goto out; + + nr = btrfs_header_nritems(path.nodes[0]); + if (!nr) + break; + + path.slots[0] = 0; + ret = btrfs_del_items(trans, root, &path, 0, nr); + if (ret) + goto out; + + btrfs_release_path(&path); + } + + ret = 0; +out: + btrfs_release_path(&path); + return ret; +} + +static int change_extents_csum(struct btrfs_fs_info *fs_info, int csum_type) +{ + struct btrfs_root *root = btrfs_extent_root(fs_info, 0); + struct btrfs_path path; + struct btrfs_key key = {0, 0, 0}; + int ret = 0; + + btrfs_init_path(&path); + /* + * Here we don't use transaction as it will takes a lot of reserve + * space, and that will make a near-full btrfs unable to change csums + */ + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) + goto out; + + while (1) { + struct btrfs_extent_item *ei; + struct extent_buffer *eb; + u64 flags; + u64 bytenr; + + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (key.type != BTRFS_EXTENT_ITEM_KEY && + key.type != BTRFS_METADATA_ITEM_KEY) + goto next; + ei = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_extent_item); + flags = btrfs_extent_flags(path.nodes[0], ei); + if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)) + goto next; + + bytenr = key.objectid; + eb = read_tree_block(fs_info, bytenr, 0); + if (IS_ERR(eb)) { + error("failed to read tree block: %llu", bytenr); + ret = PTR_ERR(eb); + goto out; + } + /* Only rewrite block */ + /* printf("CSUM: start %llu\n", eb->start); */ + ret = write_tree_block(NULL, fs_info, eb); + free_extent_buffer(eb); + if (ret < 0) { + error("failed to change csum of tree block: %llu", bytenr); + goto out; + } +next: + ret = btrfs_next_item(root, &path); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + goto out; + } + } + +out: + btrfs_release_path(&path); + return ret; +} + +static int change_devices_csum(struct btrfs_root *root, int csum_type) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_path path; + struct btrfs_key key = {0, 0, 0}; + int ret = 0; + + btrfs_init_path(&path); + /* No transaction again */ + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) + goto out; + + while (1) { + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (key.type != BTRFS_DEV_ITEM_KEY || + key.objectid != BTRFS_DEV_ITEMS_OBJECTID) + goto next; + /* Only rewrite block */ + ret = write_tree_block(NULL, fs_info, path.nodes[0]); + if (ret < 0) + goto out; +next: + ret = btrfs_next_item(root, &path); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + goto out; + } + } +out: + btrfs_release_path(&path); + return ret; +} + +static int populate_csum(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, char *buf, u64 start, + u64 len) +{ + u64 offset = 0; + u64 sectorsize; + int ret = 0; + + while (offset < len) { + sectorsize = fs_info->sectorsize; + ret = read_data_from_disk(fs_info, buf, start + offset, + §orsize, 0); + if (ret) + break; + ret = btrfs_csum_file_block(trans, start + len, start + offset, + buf, sectorsize); + if (ret) + break; + offset += sectorsize; + } + return ret; +} + +static int fill_csum_tree_from_extent(struct btrfs_fs_info *fs_info) +{ + struct btrfs_root *extent_root = btrfs_extent_root(fs_info, 0); + struct btrfs_trans_handle *trans; + struct btrfs_path path; + struct btrfs_extent_item *ei; + struct extent_buffer *leaf; + char *buf; + struct btrfs_key key; + int ret; + + trans = btrfs_start_transaction(extent_root, 1); + if (trans == NULL) { + /* fixme */ + printf("cannot start transaction\n"); + return -EINVAL; + } + + btrfs_init_path(&path); + key.objectid = 0; + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = 0; + ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0); + if (ret < 0) { + btrfs_release_path(&path); + return ret; + } + + buf = malloc(fs_info->sectorsize); + if (!buf) { + btrfs_release_path(&path); + return -ENOMEM; + } + + ret = delete_csum_items(trans, fs_info); + if (ret) { + error("unable to delete all checksum items: %d", ret); + return -EIO; + } + + while (1) { + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(extent_root, &path); + if (ret < 0) + break; + if (ret) { + ret = 0; + break; + } + } + leaf = path.nodes[0]; + + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.type != BTRFS_EXTENT_ITEM_KEY) { + path.slots[0]++; + continue; + } + + ei = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_extent_item); + if (!(btrfs_extent_flags(leaf, ei) & BTRFS_EXTENT_FLAG_DATA)) { + path.slots[0]++; + continue; + } + + ret = populate_csum(trans, fs_info, buf, key.objectid, key.offset); + if (ret) + break; + path.slots[0]++; + } + + btrfs_release_path(&path); + free(buf); + + /* dont' commit if thre's error */ + ret = btrfs_commit_transaction(trans, extent_root); + + return ret; +} + +int rewrite_checksums(struct btrfs_root *root, int csum_type) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_super_block *disk_super; + struct btrfs_trans_handle *trans; + u64 super_flags; + int ret; + + disk_super = root->fs_info->super_copy; + super_flags = btrfs_super_flags(disk_super); + + /* FIXME: Sanity checks */ + if (0) { + error("UUID rewrite in progress, cannot change fsid"); + return 1; + } + + fs_info->force_csum_type = csum_type; + + /* Step 1 sets the in progress flag, no other change to the sb */ + printf("Set superblock flag CHANGING_CSUM\n"); + trans = btrfs_start_transaction(root, 1); + super_flags |= BTRFS_SUPER_FLAG_CHANGING_CSUM; + btrfs_set_super_flags(disk_super, super_flags); + ret = btrfs_commit_transaction(trans, root); + if (ret < 0) + return ret; + + /* Change extents first */ + printf("Change fsid in extents\n"); + ret = change_extents_csum(fs_info, csum_type); + if (ret < 0) { + error("failed to change csum of metadata: %d", ret); + goto out; + } + + /* Then devices */ + printf("Change csum in chunk tree\n"); + ret = change_devices_csum(fs_info->chunk_root, csum_type); + if (ret < 0) { + error("failed to change UUID of devices: %d", ret); + goto out; + } + + /* DATA */ + printf("Change csum of data blocks\n"); + ret = fill_csum_tree_from_extent(fs_info); + if (ret < 0) + goto out; + + /* Last, change fsid in super */ + ret = write_all_supers(fs_info); + if (ret < 0) + goto out; + + /* All checksums done, drop the flag, super block csum will get updated */ + printf("Clear superblock flag CHANGING_CSUM\n"); + super_flags = btrfs_super_flags(fs_info->super_copy); + super_flags &= ~BTRFS_SUPER_FLAG_CHANGING_CSUM; + btrfs_set_super_flags(fs_info->super_copy, super_flags); + btrfs_set_super_csum_type(disk_super, csum_type); + ret = write_all_supers(fs_info); + printf("Checksum change finished\n"); +out: + /* check errors */ + + return ret; +} diff --git a/tune/main.c b/tune/main.c index 4bad042e..9db686a1 100644 --- a/tune/main.c +++ b/tune/main.c @@ -47,313 +47,6 @@ static char *device; static int force = 0; -static int delete_csum_items(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info) -{ - struct btrfs_root *root = btrfs_csum_root(fs_info, 0); - struct btrfs_path path; - struct btrfs_key key; - int nr; - int ret; - - btrfs_init_path(&path); - - key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; - key.type = BTRFS_EXTENT_CSUM_KEY; - key.offset = 0; - - while (1) { - ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); - if (ret < 0) - goto out; - - nr = btrfs_header_nritems(path.nodes[0]); - if (!nr) - break; - - path.slots[0] = 0; - ret = btrfs_del_items(trans, root, &path, 0, nr); - if (ret) - goto out; - - btrfs_release_path(&path); - } - - ret = 0; -out: - btrfs_release_path(&path); - return ret; -} - -static int change_extents_csum(struct btrfs_fs_info *fs_info, int csum_type) -{ - struct btrfs_root *root = btrfs_extent_root(fs_info, 0); - struct btrfs_path path; - struct btrfs_key key = {0, 0, 0}; - int ret = 0; - - btrfs_init_path(&path); - /* - * Here we don't use transaction as it will takes a lot of reserve - * space, and that will make a near-full btrfs unable to change csums - */ - ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); - if (ret < 0) - goto out; - - while (1) { - struct btrfs_extent_item *ei; - struct extent_buffer *eb; - u64 flags; - u64 bytenr; - - btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); - if (key.type != BTRFS_EXTENT_ITEM_KEY && - key.type != BTRFS_METADATA_ITEM_KEY) - goto next; - ei = btrfs_item_ptr(path.nodes[0], path.slots[0], - struct btrfs_extent_item); - flags = btrfs_extent_flags(path.nodes[0], ei); - if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)) - goto next; - - bytenr = key.objectid; - eb = read_tree_block(fs_info, bytenr, 0); - if (IS_ERR(eb)) { - error("failed to read tree block: %llu", bytenr); - ret = PTR_ERR(eb); - goto out; - } - /* Only rewrite block */ - /* printf("CSUM: start %llu\n", eb->start); */ - ret = write_tree_block(NULL, fs_info, eb); - free_extent_buffer(eb); - if (ret < 0) { - error("failed to change csum of tree block: %llu", bytenr); - goto out; - } -next: - ret = btrfs_next_item(root, &path); - if (ret < 0) - goto out; - if (ret > 0) { - ret = 0; - goto out; - } - } - -out: - btrfs_release_path(&path); - return ret; -} - -static int change_devices_csum(struct btrfs_root *root, int csum_type) -{ - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_path path; - struct btrfs_key key = {0, 0, 0}; - int ret = 0; - - btrfs_init_path(&path); - /* No transaction again */ - ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); - if (ret < 0) - goto out; - - while (1) { - btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); - if (key.type != BTRFS_DEV_ITEM_KEY || - key.objectid != BTRFS_DEV_ITEMS_OBJECTID) - goto next; - /* Only rewrite block */ - ret = write_tree_block(NULL, fs_info, path.nodes[0]); - if (ret < 0) - goto out; -next: - ret = btrfs_next_item(root, &path); - if (ret < 0) - goto out; - if (ret > 0) { - ret = 0; - goto out; - } - } -out: - btrfs_release_path(&path); - return ret; -} - -static int populate_csum(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, char *buf, u64 start, - u64 len) -{ - u64 offset = 0; - u64 sectorsize; - int ret = 0; - - while (offset < len) { - sectorsize = fs_info->sectorsize; - ret = read_data_from_disk(fs_info, buf, start + offset, - §orsize, 0); - if (ret) - break; - ret = btrfs_csum_file_block(trans, start + len, start + offset, - buf, sectorsize); - if (ret) - break; - offset += sectorsize; - } - return ret; -} - -static int fill_csum_tree_from_extent(struct btrfs_fs_info *fs_info) -{ - struct btrfs_root *extent_root = btrfs_extent_root(fs_info, 0); - struct btrfs_trans_handle *trans; - struct btrfs_path path; - struct btrfs_extent_item *ei; - struct extent_buffer *leaf; - char *buf; - struct btrfs_key key; - int ret; - - trans = btrfs_start_transaction(extent_root, 1); - if (trans == NULL) { - /* fixme */ - printf("cannot start transaction\n"); - return -EINVAL; - } - - btrfs_init_path(&path); - key.objectid = 0; - key.type = BTRFS_EXTENT_ITEM_KEY; - key.offset = 0; - ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0); - if (ret < 0) { - btrfs_release_path(&path); - return ret; - } - - buf = malloc(fs_info->sectorsize); - if (!buf) { - btrfs_release_path(&path); - return -ENOMEM; - } - - ret = delete_csum_items(trans, fs_info); - if (ret) { - error("unable to delete all checksum items: %d", ret); - return -EIO; - } - - while (1) { - if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { - ret = btrfs_next_leaf(extent_root, &path); - if (ret < 0) - break; - if (ret) { - ret = 0; - break; - } - } - leaf = path.nodes[0]; - - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (key.type != BTRFS_EXTENT_ITEM_KEY) { - path.slots[0]++; - continue; - } - - ei = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_extent_item); - if (!(btrfs_extent_flags(leaf, ei) & BTRFS_EXTENT_FLAG_DATA)) { - path.slots[0]++; - continue; - } - - ret = populate_csum(trans, fs_info, buf, key.objectid, key.offset); - if (ret) - break; - path.slots[0]++; - } - - btrfs_release_path(&path); - free(buf); - - /* dont' commit if thre's error */ - ret = btrfs_commit_transaction(trans, extent_root); - - return ret; -} - -static int rewrite_checksums(struct btrfs_root *root, int csum_type) -{ - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_super_block *disk_super; - struct btrfs_trans_handle *trans; - u64 super_flags; - int ret; - - disk_super = root->fs_info->super_copy; - super_flags = btrfs_super_flags(disk_super); - - /* FIXME: Sanity checks */ - if (0) { - error("UUID rewrite in progress, cannot change fsid"); - return 1; - } - - fs_info->force_csum_type = csum_type; - - /* Step 1 sets the in progress flag, no other change to the sb */ - printf("Set superblock flag CHANGING_CSUM\n"); - trans = btrfs_start_transaction(root, 1); - super_flags |= BTRFS_SUPER_FLAG_CHANGING_CSUM; - btrfs_set_super_flags(disk_super, super_flags); - ret = btrfs_commit_transaction(trans, root); - if (ret < 0) - return ret; - - /* Change extents first */ - printf("Change fsid in extents\n"); - ret = change_extents_csum(fs_info, csum_type); - if (ret < 0) { - error("failed to change csum of metadata: %d", ret); - goto out; - } - - /* Then devices */ - printf("Change csum in chunk tree\n"); - ret = change_devices_csum(fs_info->chunk_root, csum_type); - if (ret < 0) { - error("failed to change UUID of devices: %d", ret); - goto out; - } - - /* DATA */ - printf("Change csum of data blocks\n"); - ret = fill_csum_tree_from_extent(fs_info); - if (ret < 0) - goto out; - - /* Last, change fsid in super */ - ret = write_all_supers(fs_info); - if (ret < 0) - goto out; - - /* All checksums done, drop the flag, super block csum will get updated */ - printf("Clear superblock flag CHANGING_CSUM\n"); - super_flags = btrfs_super_flags(fs_info->super_copy); - super_flags &= ~BTRFS_SUPER_FLAG_CHANGING_CSUM; - btrfs_set_super_flags(fs_info->super_copy, super_flags); - btrfs_set_super_csum_type(disk_super, csum_type); - ret = write_all_supers(fs_info); - printf("Checksum change finished\n"); -out: - /* check errors */ - - return ret; -} - static int set_super_incompat_flags(struct btrfs_root *root, u64 flags) { struct btrfs_trans_handle *trans; diff --git a/tune/tune.h b/tune/tune.h index 0b93cb97..252b4fcb 100644 --- a/tune/tune.h +++ b/tune/tune.h @@ -29,4 +29,6 @@ int set_metadata_uuid(struct btrfs_root *root, const char *uuid_string); int convert_to_bg_tree(struct btrfs_fs_info *fs_info); +int rewrite_checksums(struct btrfs_root *root, int csum_type); + #endif