Skip to content

Commit 8f23ca0

Browse files
adam900710kdave
authored andcommitted
btrfs-progs: tune: implement resume support for csum tree without any new csum item
There are two possible situations where there is no new csum items at all: - We just inserted csum change item This means all csums are really old csum type, and we can start the conversion from the beginning, only need to skip the csum change item insert. - We finished data csum conversion but not yet started metadata conversion This means all csums are already new csum type, and we can resume by starting changing metadata csums. To distinguish the two cases, we need to read the first sector, and verify the data content against both csum types. If the csum matches with old csum type, we resume from generating new data csum. If the csum matches with new csum type, we resume from rewriting metadata csum. If the csum doesn't match either csum type, we have some big problems then. Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent ba96571 commit 8f23ca0

File tree

1 file changed

+102
-3
lines changed

1 file changed

+102
-3
lines changed

tune/change-csum.c

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ static int get_last_csum_bytenr(struct btrfs_fs_info *fs_info, u64 *result)
115115

116116
static int read_verify_one_data_sector(struct btrfs_fs_info *fs_info,
117117
u64 logical, void *data_buf,
118-
const void *old_csums)
118+
const void *old_csums, u16 old_csum_type,
119+
bool output_error)
119120
{
120121
const u32 sectorsize = fs_info->sectorsize;
121122
int num_copies = btrfs_num_copies(fs_info, logical, sectorsize);
@@ -138,7 +139,7 @@ static int read_verify_one_data_sector(struct btrfs_fs_info *fs_info,
138139
if (memcmp(csum_has, old_csums, fs_info->csum_size) == 0) {
139140
found_good = true;
140141
break;
141-
} else {
142+
} else if (output_error){
142143
char found[BTRFS_CSUM_STRING_LEN];
143144
char want[BTRFS_CSUM_STRING_LEN];
144145

@@ -168,7 +169,8 @@ static int generate_new_csum_range(struct btrfs_trans_handle *trans,
168169

169170
for (u64 cur = logical; cur < logical + length; cur += sectorsize) {
170171
ret = read_verify_one_data_sector(fs_info, cur, buf, old_csums +
171-
(cur - logical) / sectorsize * fs_info->csum_size);
172+
(cur - logical) / sectorsize * fs_info->csum_size,
173+
fs_info->csum_type, true);
172174

173175
if (ret < 0) {
174176
error("failed to recover a good copy for data at logical %llu",
@@ -532,8 +534,20 @@ static int change_meta_csums(struct btrfs_fs_info *fs_info, u16 new_csum_type)
532534
struct btrfs_root *extent_root = btrfs_extent_root(fs_info, 0);
533535
struct btrfs_path path = { 0 };
534536
struct btrfs_key key;
537+
u64 super_flags;
535538
int ret;
536539

540+
/* Re-set the super flags, this is for resume cases. */
541+
super_flags = btrfs_super_flags(fs_info->super_copy);
542+
super_flags &= ~BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM;
543+
super_flags |= BTRFS_SUPER_FLAG_CHANGING_META_CSUM;
544+
btrfs_set_super_flags(fs_info->super_copy, super_flags);
545+
ret = write_all_supers(fs_info);
546+
if (ret < 0) {
547+
errno = -ret;
548+
error("failed to update super flags: %m");
549+
}
550+
537551
/*
538552
* Disable metadata csum checks first, as we may hit tree blocks with
539553
* either old or new csums.
@@ -728,6 +742,63 @@ static int get_csum_items_range(struct btrfs_fs_info *fs_info,
728742
return 0;
729743
}
730744

745+
/*
746+
* Verify one data sector to determine which csum type matches the csum.
747+
*
748+
* Return >0 if the current csum type doesn't pass the check (including csum
749+
* item too small compared to csum type).
750+
* Return 0 if the current csum type passes the check.
751+
* Return <0 for other errors.
752+
*/
753+
static int determine_csum_type(struct btrfs_fs_info *fs_info, u64 logical,
754+
u16 csum_type)
755+
{
756+
struct btrfs_root *csum_root = btrfs_csum_root(fs_info, logical);
757+
struct btrfs_path path = { 0 };
758+
struct btrfs_key key;
759+
u16 csum_size = btrfs_csum_type_size(csum_type);
760+
u8 csum_expected[BTRFS_CSUM_SIZE];
761+
void *buf;
762+
int ret;
763+
764+
key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
765+
key.type = BTRFS_EXTENT_CSUM_KEY;
766+
key.offset = logical;
767+
768+
ret = btrfs_search_slot(NULL, csum_root, &key, &path, 0, 0);
769+
if (ret > 0)
770+
ret = -ENOENT;
771+
if (ret < 0) {
772+
errno = -ret;
773+
error("failed to search csum tree: %m");
774+
btrfs_release_path(&path);
775+
return ret;
776+
}
777+
778+
/*
779+
* The csum item size is smaller than expected csum size, no
780+
* more need to check.
781+
*/
782+
if (btrfs_item_size(path.nodes[0], path.slots[0]) < csum_size) {
783+
btrfs_release_path(&path);
784+
return 1;
785+
}
786+
read_extent_buffer(path.nodes[0], csum_expected,
787+
btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
788+
csum_size);
789+
btrfs_release_path(&path);
790+
791+
buf = malloc(fs_info->sectorsize);
792+
if (!buf)
793+
return -ENOMEM;
794+
ret = read_verify_one_data_sector(fs_info, logical, buf, csum_expected,
795+
csum_type, false);
796+
if (ret < 0)
797+
ret = 1;
798+
free(buf);
799+
return ret;
800+
}
801+
731802
static int resume_data_csum_change(struct btrfs_fs_info *fs_info, u16 new_csum_type)
732803
{
733804
u64 old_csum_first;
@@ -756,6 +827,33 @@ static int resume_data_csum_change(struct btrfs_fs_info *fs_info, u16 new_csum_t
756827
if (ret == 0)
757828
new_csum_found = true;
758829

830+
/*
831+
* Only old csums exists. This can be one of the two cases:
832+
* - Only the csum change item inserted, no new csum generated.
833+
* - All data csum is converted to the new type.
834+
*
835+
* Here we need to check if the csum item is in old or new type.
836+
*/
837+
if (old_csum_found && !new_csum_found) {
838+
ret = determine_csum_type(fs_info, old_csum_first, fs_info->csum_type);
839+
if (ret == 0) {
840+
/* All old data csums, restart generation. */
841+
resume_start = 0;
842+
goto new_data_csums;
843+
}
844+
ret = determine_csum_type(fs_info, old_csum_first, new_csum_type);
845+
if (ret == 0) {
846+
/*
847+
* All new data csums, just go metadata csum change, which
848+
* would drop the CHANGING_DATA_CSUM flag for us.
849+
*/
850+
goto new_meta_csum;
851+
}
852+
error("The data checksum for logical %llu doesn't match either old or new csum type, unable to resume",
853+
old_csum_first);
854+
return -EUCLEAN;
855+
}
856+
759857
/*
760858
* Both old and new csum exist, and new csum is only a subset of the
761859
* old ones.
@@ -786,6 +884,7 @@ static int resume_data_csum_change(struct btrfs_fs_info *fs_info, u16 new_csum_t
786884
ret = change_csum_objectids(fs_info);
787885
if (ret < 0)
788886
return ret;
887+
new_meta_csum:
789888
ret = change_meta_csums(fs_info, new_csum_type);
790889
return ret;
791890
}

0 commit comments

Comments
 (0)