20
20
#include <stdlib.h>
21
21
#include "kernel-shared/ctree.h"
22
22
#include "kernel-shared/disk-io.h"
23
+ #include "kernel-shared/volumes.h"
23
24
#include "kernel-shared/extent_io.h"
24
25
#include "kernel-shared/transaction.h"
25
26
#include "common/messages.h"
26
27
#include "common/internal.h"
28
+ #include "common/utils.h"
27
29
#include "tune/tune.h"
28
30
29
31
static 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)
80
82
return 0 ;
81
83
}
82
84
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
+
83
321
int btrfs_change_csum_type (struct btrfs_fs_info * fs_info , u16 new_csum_type )
84
322
{
85
323
int ret ;
@@ -96,6 +334,12 @@ int btrfs_change_csum_type(struct btrfs_fs_info *fs_info, u16 new_csum_type)
96
334
* will be a temporary item in root tree to indicate the new checksum
97
335
* algo.
98
336
*/
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
+ }
99
343
100
344
/* Phase 2, delete the old data csums. */
101
345
0 commit comments