@@ -115,7 +115,8 @@ static int get_last_csum_bytenr(struct btrfs_fs_info *fs_info, u64 *result)
115115
116116static 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+
731802static 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