2020#include <stdlib.h>
2121#include "kernel-shared/ctree.h"
2222#include "kernel-shared/disk-io.h"
23+ #include "kernel-shared/volumes.h"
2324#include "kernel-shared/extent_io.h"
2425#include "kernel-shared/transaction.h"
2526#include "common/messages.h"
2627#include "common/internal.h"
28+ #include "common/utils.h"
2729#include "tune/tune.h"
2830
2931static int check_csum_change_requreiment (struct btrfs_fs_info * fs_info )
@@ -80,6 +82,242 @@ static int check_csum_change_requreiment(struct btrfs_fs_info *fs_info)
8082 return 0 ;
8183}
8284
85+ static int get_last_csum_bytenr (struct btrfs_fs_info * fs_info , u64 * result )
86+ {
87+ struct btrfs_root * csum_root = btrfs_csum_root (fs_info , 0 );
88+ struct btrfs_path path = { 0 };
89+ struct btrfs_key key ;
90+ int ret ;
91+
92+ key .objectid = BTRFS_EXTENT_CSUM_OBJECTID ;
93+ key .type = BTRFS_EXTENT_CSUM_KEY ;
94+ key .offset = (u64 )- 1 ;
95+
96+ ret = btrfs_search_slot (NULL , csum_root , & key , & path , 0 , 0 );
97+ if (ret < 0 )
98+ return ret ;
99+ assert (ret > 0 );
100+ ret = btrfs_previous_item (csum_root , & path , BTRFS_EXTENT_CSUM_OBJECTID ,
101+ BTRFS_EXTENT_CSUM_KEY );
102+ if (ret < 0 )
103+ return ret ;
104+ /*
105+ * Emptry csum tree, set last csum byte to 0 so we can skip new data
106+ * csum generation.
107+ */
108+ if (ret > 0 ) {
109+ * result = 0 ;
110+ btrfs_release_path (& path );
111+ return 0 ;
112+ }
113+ btrfs_item_key_to_cpu (path .nodes [0 ], & key , path .slots [0 ]);
114+ * result = key .offset + btrfs_item_size (path .nodes [0 ], path .slots [0 ]) /
115+ fs_info -> csum_size * fs_info -> sectorsize ;
116+ btrfs_release_path (& path );
117+ return 0 ;
118+ }
119+
120+ static int read_verify_one_data_sector (struct btrfs_fs_info * fs_info ,
121+ u64 logical , void * data_buf ,
122+ const void * old_csums )
123+ {
124+ const u32 sectorsize = fs_info -> sectorsize ;
125+ int num_copies = btrfs_num_copies (fs_info , logical , sectorsize );
126+ bool found_good = false;
127+
128+ for (int mirror = 1 ; mirror <= num_copies ; mirror ++ ) {
129+ u8 csum_has [BTRFS_CSUM_SIZE ];
130+ u64 readlen = sectorsize ;
131+ int ret ;
132+
133+ ret = read_data_from_disk (fs_info , data_buf , logical , & readlen ,
134+ mirror );
135+ if (ret < 0 ) {
136+ errno = - ret ;
137+ error ("failed to read logical %llu: %m" , logical );
138+ continue ;
139+ }
140+ btrfs_csum_data (fs_info , fs_info -> csum_type , data_buf , csum_has ,
141+ sectorsize );
142+ if (memcmp (csum_has , old_csums , fs_info -> csum_size ) == 0 ) {
143+ found_good = true;
144+ break ;
145+ } else {
146+ char found [BTRFS_CSUM_STRING_LEN ];
147+ char want [BTRFS_CSUM_STRING_LEN ];
148+
149+ btrfs_format_csum (fs_info -> csum_type , old_csums , want );
150+ btrfs_format_csum (fs_info -> csum_type , csum_has , found );
151+ error ("csum mismatch for logical %llu mirror %u, has %s expected %s" ,
152+ logical , mirror , found , want );
153+ }
154+ }
155+ if (!found_good )
156+ return - EIO ;
157+ return 0 ;
158+ }
159+
160+ static int generate_new_csum_range (struct btrfs_trans_handle * trans ,
161+ u64 logical , u64 length , u16 new_csum_type ,
162+ const void * old_csums )
163+ {
164+ struct btrfs_fs_info * fs_info = trans -> fs_info ;
165+ const u32 sectorsize = fs_info -> sectorsize ;
166+ int ret = 0 ;
167+ void * buf ;
168+
169+ buf = malloc (fs_info -> sectorsize );
170+ if (!buf )
171+ return - ENOMEM ;
172+
173+ for (u64 cur = logical ; cur < logical + length ; cur += sectorsize ) {
174+ ret = read_verify_one_data_sector (fs_info , cur , buf , old_csums +
175+ (cur - logical ) / sectorsize * fs_info -> csum_size );
176+
177+ if (ret < 0 ) {
178+ error ("failed to recover a good copy for data at logical %llu" ,
179+ logical );
180+ goto out ;
181+ }
182+ /* Calculate new csum and insert it into the csum tree. */
183+ ret = - EOPNOTSUPP ;
184+ }
185+ out :
186+ free (buf );
187+ return ret ;
188+ }
189+
190+ /*
191+ * After reading this many bytes of data, commit the current transaction.
192+ *
193+ * Only a soft cap, we can exceed the threshold if hitting a large enough csum
194+ * item.
195+ */
196+ #define CSUM_CHANGE_BYTES_THRESHOLD (SZ_2M)
197+ static int generate_new_data_csums (struct btrfs_fs_info * fs_info , u16 new_csum_type )
198+ {
199+ struct btrfs_root * tree_root = fs_info -> tree_root ;
200+ struct btrfs_root * csum_root = btrfs_csum_root (fs_info , 0 );
201+ struct btrfs_trans_handle * trans ;
202+ struct btrfs_path path = { 0 };
203+ struct btrfs_key key ;
204+ const u32 new_csum_size = btrfs_csum_type_size (new_csum_type );
205+ void * csum_buffer ;
206+ u64 converted_bytes = 0 ;
207+ u64 last_csum ;
208+ u64 cur = 0 ;
209+ int ret ;
210+
211+ ret = get_last_csum_bytenr (fs_info , & last_csum );
212+ if (ret < 0 ) {
213+ errno = - ret ;
214+ error ("failed to get the last csum item: %m" );
215+ return ret ;
216+ }
217+ csum_buffer = malloc (fs_info -> nodesize );
218+ if (!csum_buffer )
219+ return - ENOMEM ;
220+
221+ trans = btrfs_start_transaction (tree_root , 1 );
222+ if (IS_ERR (trans )) {
223+ ret = PTR_ERR (trans );
224+ errno = - ret ;
225+ error ("failed to start transaction: %m" );
226+ goto out ;
227+ }
228+ key .objectid = BTRFS_CSUM_CHANGE_OBJECTID ;
229+ key .type = BTRFS_TEMPORARY_ITEM_KEY ;
230+ key .offset = new_csum_type ;
231+ ret = btrfs_insert_empty_item (trans , tree_root , & path , & key , 0 );
232+ btrfs_release_path (& path );
233+ if (ret < 0 ) {
234+ errno = - ret ;
235+ error ("failed to insert csum change item: %m" );
236+ btrfs_abort_transaction (trans , ret );
237+ goto out ;
238+ }
239+ btrfs_set_super_flags (fs_info -> super_copy ,
240+ btrfs_super_flags (fs_info -> super_copy ) |
241+ BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM );
242+ ret = btrfs_commit_transaction (trans , tree_root );
243+ if (ret < 0 ) {
244+ errno = - ret ;
245+ error ("failed to commit the initial transaction: %m" );
246+ goto out ;
247+ }
248+
249+ trans = btrfs_start_transaction (csum_root ,
250+ CSUM_CHANGE_BYTES_THRESHOLD / fs_info -> sectorsize *
251+ new_csum_size );
252+ if (IS_ERR (trans )) {
253+ ret = PTR_ERR (trans );
254+ errno = - ret ;
255+ error ("failed to start transaction: %m" );
256+ return ret ;
257+ }
258+
259+ while (cur < last_csum ) {
260+ u64 start ;
261+ u64 len ;
262+ u32 item_size ;
263+
264+ key .objectid = BTRFS_EXTENT_CSUM_OBJECTID ;
265+ key .type = BTRFS_EXTENT_CSUM_KEY ;
266+ key .offset = cur ;
267+
268+ ret = btrfs_search_slot (NULL , csum_root , & key , & path , 0 , 0 );
269+ if (ret < 0 )
270+ goto out ;
271+ if (ret > 0 && path .slots [0 ] >=
272+ btrfs_header_nritems (path .nodes [0 ])) {
273+ ret = btrfs_next_leaf (csum_root , & path );
274+ if (ret > 0 ) {
275+ ret = 0 ;
276+ btrfs_release_path (& path );
277+ break ;
278+ }
279+ if (ret < 0 ) {
280+ btrfs_release_path (& path );
281+ goto out ;
282+ }
283+ }
284+ btrfs_item_key_to_cpu (path .nodes [0 ], & key , path .slots [0 ]);
285+ assert (key .offset >= cur );
286+ item_size = btrfs_item_size (path .nodes [0 ], path .slots [0 ]);
287+
288+ start = key .offset ;
289+ len = item_size / fs_info -> csum_size * fs_info -> sectorsize ;
290+ read_extent_buffer (path .nodes [0 ], csum_buffer ,
291+ btrfs_item_ptr_offset (path .nodes [0 ], path .slots [0 ]),
292+ item_size );
293+ btrfs_release_path (& path );
294+
295+ ret = generate_new_csum_range (trans , start , len , new_csum_type ,
296+ csum_buffer );
297+ if (ret < 0 )
298+ goto out ;
299+ converted_bytes += len ;
300+ if (converted_bytes >= CSUM_CHANGE_BYTES_THRESHOLD ) {
301+ converted_bytes = 0 ;
302+ ret = btrfs_commit_transaction (trans , csum_root );
303+ if (ret < 0 )
304+ goto out ;
305+ trans = btrfs_start_transaction (csum_root ,
306+ CSUM_CHANGE_BYTES_THRESHOLD /
307+ fs_info -> sectorsize * new_csum_size );
308+ if (IS_ERR (trans )) {
309+ ret = PTR_ERR (trans );
310+ goto out ;
311+ }
312+ }
313+ cur = start + len ;
314+ }
315+ ret = btrfs_commit_transaction (trans , csum_root );
316+ out :
317+ free (csum_buffer );
318+ return ret ;
319+ }
320+
83321int btrfs_change_csum_type (struct btrfs_fs_info * fs_info , u16 new_csum_type )
84322{
85323 int ret ;
@@ -96,6 +334,12 @@ int btrfs_change_csum_type(struct btrfs_fs_info *fs_info, u16 new_csum_type)
96334 * will be a temporary item in root tree to indicate the new checksum
97335 * algo.
98336 */
337+ ret = generate_new_data_csums (fs_info , new_csum_type );
338+ if (ret < 0 ) {
339+ errno = - ret ;
340+ error ("failed to generate new data csums: %m" );
341+ return ret ;
342+ }
99343
100344 /* Phase 2, delete the old data csums. */
101345
0 commit comments