GNU Linux-libre 6.8.9-gnu
[releases.git] / fs / btrfs / tests / free-space-tree-tests.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2015 Facebook.  All rights reserved.
4  */
5
6 #include <linux/types.h>
7 #include "btrfs-tests.h"
8 #include "../ctree.h"
9 #include "../disk-io.h"
10 #include "../free-space-tree.h"
11 #include "../transaction.h"
12 #include "../block-group.h"
13 #include "../accessors.h"
14
15 struct free_space_extent {
16         u64 start;
17         u64 length;
18 };
19
20 static int __check_free_space_extents(struct btrfs_trans_handle *trans,
21                                       struct btrfs_fs_info *fs_info,
22                                       struct btrfs_block_group *cache,
23                                       struct btrfs_path *path,
24                                       const struct free_space_extent * const extents,
25                                       unsigned int num_extents)
26 {
27         struct btrfs_free_space_info *info;
28         struct btrfs_key key;
29         int prev_bit = 0, bit;
30         u64 extent_start = 0, offset, end;
31         u32 flags, extent_count;
32         unsigned int i;
33         int ret;
34
35         info = search_free_space_info(trans, cache, path, 0);
36         if (IS_ERR(info)) {
37                 test_err("could not find free space info");
38                 ret = PTR_ERR(info);
39                 goto out;
40         }
41         flags = btrfs_free_space_flags(path->nodes[0], info);
42         extent_count = btrfs_free_space_extent_count(path->nodes[0], info);
43
44         if (extent_count != num_extents) {
45                 test_err("extent count is wrong");
46                 ret = -EINVAL;
47                 goto out;
48         }
49         if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) {
50                 if (path->slots[0] != 0)
51                         goto invalid;
52                 end = cache->start + cache->length;
53                 i = 0;
54                 while (++path->slots[0] < btrfs_header_nritems(path->nodes[0])) {
55                         btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
56                         if (key.type != BTRFS_FREE_SPACE_BITMAP_KEY)
57                                 goto invalid;
58                         offset = key.objectid;
59                         while (offset < key.objectid + key.offset) {
60                                 bit = free_space_test_bit(cache, path, offset);
61                                 if (prev_bit == 0 && bit == 1) {
62                                         extent_start = offset;
63                                 } else if (prev_bit == 1 && bit == 0) {
64                                         if (i >= num_extents ||
65                                             extent_start != extents[i].start ||
66                                             offset - extent_start != extents[i].length)
67                                                 goto invalid;
68                                         i++;
69                                 }
70                                 prev_bit = bit;
71                                 offset += fs_info->sectorsize;
72                         }
73                 }
74                 if (prev_bit == 1) {
75                         if (i >= num_extents ||
76                             extent_start != extents[i].start ||
77                             end - extent_start != extents[i].length)
78                                 goto invalid;
79                         i++;
80                 }
81                 if (i != num_extents)
82                         goto invalid;
83         } else {
84                 if (btrfs_header_nritems(path->nodes[0]) != num_extents + 1 ||
85                     path->slots[0] != 0)
86                         goto invalid;
87                 for (i = 0; i < num_extents; i++) {
88                         path->slots[0]++;
89                         btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
90                         if (key.type != BTRFS_FREE_SPACE_EXTENT_KEY ||
91                             key.objectid != extents[i].start ||
92                             key.offset != extents[i].length)
93                                 goto invalid;
94                 }
95         }
96
97         ret = 0;
98 out:
99         btrfs_release_path(path);
100         return ret;
101 invalid:
102         test_err("free space tree is invalid");
103         ret = -EINVAL;
104         goto out;
105 }
106
107 static int check_free_space_extents(struct btrfs_trans_handle *trans,
108                                     struct btrfs_fs_info *fs_info,
109                                     struct btrfs_block_group *cache,
110                                     struct btrfs_path *path,
111                                     const struct free_space_extent * const extents,
112                                     unsigned int num_extents)
113 {
114         struct btrfs_free_space_info *info;
115         u32 flags;
116         int ret;
117
118         info = search_free_space_info(trans, cache, path, 0);
119         if (IS_ERR(info)) {
120                 test_err("could not find free space info");
121                 btrfs_release_path(path);
122                 return PTR_ERR(info);
123         }
124         flags = btrfs_free_space_flags(path->nodes[0], info);
125         btrfs_release_path(path);
126
127         ret = __check_free_space_extents(trans, fs_info, cache, path, extents,
128                                          num_extents);
129         if (ret)
130                 return ret;
131
132         /* Flip it to the other format and check that for good measure. */
133         if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) {
134                 ret = convert_free_space_to_extents(trans, cache, path);
135                 if (ret) {
136                         test_err("could not convert to extents");
137                         return ret;
138                 }
139         } else {
140                 ret = convert_free_space_to_bitmaps(trans, cache, path);
141                 if (ret) {
142                         test_err("could not convert to bitmaps");
143                         return ret;
144                 }
145         }
146         return __check_free_space_extents(trans, fs_info, cache, path, extents,
147                                           num_extents);
148 }
149
150 static int test_empty_block_group(struct btrfs_trans_handle *trans,
151                                   struct btrfs_fs_info *fs_info,
152                                   struct btrfs_block_group *cache,
153                                   struct btrfs_path *path,
154                                   u32 alignment)
155 {
156         const struct free_space_extent extents[] = {
157                 {cache->start, cache->length},
158         };
159
160         return check_free_space_extents(trans, fs_info, cache, path,
161                                         extents, ARRAY_SIZE(extents));
162 }
163
164 static int test_remove_all(struct btrfs_trans_handle *trans,
165                            struct btrfs_fs_info *fs_info,
166                            struct btrfs_block_group *cache,
167                            struct btrfs_path *path,
168                            u32 alignment)
169 {
170         const struct free_space_extent extents[] = {};
171         int ret;
172
173         ret = __remove_from_free_space_tree(trans, cache, path,
174                                             cache->start,
175                                             cache->length);
176         if (ret) {
177                 test_err("could not remove free space");
178                 return ret;
179         }
180
181         return check_free_space_extents(trans, fs_info, cache, path,
182                                         extents, ARRAY_SIZE(extents));
183 }
184
185 static int test_remove_beginning(struct btrfs_trans_handle *trans,
186                                  struct btrfs_fs_info *fs_info,
187                                  struct btrfs_block_group *cache,
188                                  struct btrfs_path *path,
189                                  u32 alignment)
190 {
191         const struct free_space_extent extents[] = {
192                 {cache->start + alignment, cache->length - alignment},
193         };
194         int ret;
195
196         ret = __remove_from_free_space_tree(trans, cache, path,
197                                             cache->start, alignment);
198         if (ret) {
199                 test_err("could not remove free space");
200                 return ret;
201         }
202
203         return check_free_space_extents(trans, fs_info, cache, path,
204                                         extents, ARRAY_SIZE(extents));
205
206 }
207
208 static int test_remove_end(struct btrfs_trans_handle *trans,
209                            struct btrfs_fs_info *fs_info,
210                            struct btrfs_block_group *cache,
211                            struct btrfs_path *path,
212                            u32 alignment)
213 {
214         const struct free_space_extent extents[] = {
215                 {cache->start, cache->length - alignment},
216         };
217         int ret;
218
219         ret = __remove_from_free_space_tree(trans, cache, path,
220                                     cache->start + cache->length - alignment,
221                                     alignment);
222         if (ret) {
223                 test_err("could not remove free space");
224                 return ret;
225         }
226
227         return check_free_space_extents(trans, fs_info, cache, path,
228                                         extents, ARRAY_SIZE(extents));
229 }
230
231 static int test_remove_middle(struct btrfs_trans_handle *trans,
232                               struct btrfs_fs_info *fs_info,
233                               struct btrfs_block_group *cache,
234                               struct btrfs_path *path,
235                               u32 alignment)
236 {
237         const struct free_space_extent extents[] = {
238                 {cache->start, alignment},
239                 {cache->start + 2 * alignment, cache->length - 2 * alignment},
240         };
241         int ret;
242
243         ret = __remove_from_free_space_tree(trans, cache, path,
244                                             cache->start + alignment,
245                                             alignment);
246         if (ret) {
247                 test_err("could not remove free space");
248                 return ret;
249         }
250
251         return check_free_space_extents(trans, fs_info, cache, path,
252                                         extents, ARRAY_SIZE(extents));
253 }
254
255 static int test_merge_left(struct btrfs_trans_handle *trans,
256                            struct btrfs_fs_info *fs_info,
257                            struct btrfs_block_group *cache,
258                            struct btrfs_path *path,
259                            u32 alignment)
260 {
261         const struct free_space_extent extents[] = {
262                 {cache->start, 2 * alignment},
263         };
264         int ret;
265
266         ret = __remove_from_free_space_tree(trans, cache, path,
267                                             cache->start, cache->length);
268         if (ret) {
269                 test_err("could not remove free space");
270                 return ret;
271         }
272
273         ret = __add_to_free_space_tree(trans, cache, path, cache->start,
274                                        alignment);
275         if (ret) {
276                 test_err("could not add free space");
277                 return ret;
278         }
279
280         ret = __add_to_free_space_tree(trans, cache, path,
281                                        cache->start + alignment,
282                                        alignment);
283         if (ret) {
284                 test_err("could not add free space");
285                 return ret;
286         }
287
288         return check_free_space_extents(trans, fs_info, cache, path,
289                                         extents, ARRAY_SIZE(extents));
290 }
291
292 static int test_merge_right(struct btrfs_trans_handle *trans,
293                            struct btrfs_fs_info *fs_info,
294                            struct btrfs_block_group *cache,
295                            struct btrfs_path *path,
296                            u32 alignment)
297 {
298         const struct free_space_extent extents[] = {
299                 {cache->start + alignment, 2 * alignment},
300         };
301         int ret;
302
303         ret = __remove_from_free_space_tree(trans, cache, path,
304                                             cache->start, cache->length);
305         if (ret) {
306                 test_err("could not remove free space");
307                 return ret;
308         }
309
310         ret = __add_to_free_space_tree(trans, cache, path,
311                                        cache->start + 2 * alignment,
312                                        alignment);
313         if (ret) {
314                 test_err("could not add free space");
315                 return ret;
316         }
317
318         ret = __add_to_free_space_tree(trans, cache, path,
319                                        cache->start + alignment,
320                                        alignment);
321         if (ret) {
322                 test_err("could not add free space");
323                 return ret;
324         }
325
326         return check_free_space_extents(trans, fs_info, cache, path,
327                                         extents, ARRAY_SIZE(extents));
328 }
329
330 static int test_merge_both(struct btrfs_trans_handle *trans,
331                            struct btrfs_fs_info *fs_info,
332                            struct btrfs_block_group *cache,
333                            struct btrfs_path *path,
334                            u32 alignment)
335 {
336         const struct free_space_extent extents[] = {
337                 {cache->start, 3 * alignment},
338         };
339         int ret;
340
341         ret = __remove_from_free_space_tree(trans, cache, path,
342                                             cache->start, cache->length);
343         if (ret) {
344                 test_err("could not remove free space");
345                 return ret;
346         }
347
348         ret = __add_to_free_space_tree(trans, cache, path, cache->start,
349                                        alignment);
350         if (ret) {
351                 test_err("could not add free space");
352                 return ret;
353         }
354
355         ret = __add_to_free_space_tree(trans, cache, path,
356                                        cache->start + 2 * alignment, alignment);
357         if (ret) {
358                 test_err("could not add free space");
359                 return ret;
360         }
361
362         ret = __add_to_free_space_tree(trans, cache, path,
363                                        cache->start + alignment, alignment);
364         if (ret) {
365                 test_err("could not add free space");
366                 return ret;
367         }
368
369         return check_free_space_extents(trans, fs_info, cache, path,
370                                         extents, ARRAY_SIZE(extents));
371 }
372
373 static int test_merge_none(struct btrfs_trans_handle *trans,
374                            struct btrfs_fs_info *fs_info,
375                            struct btrfs_block_group *cache,
376                            struct btrfs_path *path,
377                            u32 alignment)
378 {
379         const struct free_space_extent extents[] = {
380                 {cache->start, alignment},
381                 {cache->start + 2 * alignment, alignment},
382                 {cache->start + 4 * alignment, alignment},
383         };
384         int ret;
385
386         ret = __remove_from_free_space_tree(trans, cache, path,
387                                             cache->start, cache->length);
388         if (ret) {
389                 test_err("could not remove free space");
390                 return ret;
391         }
392
393         ret = __add_to_free_space_tree(trans, cache, path, cache->start,
394                                        alignment);
395         if (ret) {
396                 test_err("could not add free space");
397                 return ret;
398         }
399
400         ret = __add_to_free_space_tree(trans, cache, path,
401                                        cache->start + 4 * alignment, alignment);
402         if (ret) {
403                 test_err("could not add free space");
404                 return ret;
405         }
406
407         ret = __add_to_free_space_tree(trans, cache, path,
408                                        cache->start + 2 * alignment, alignment);
409         if (ret) {
410                 test_err("could not add free space");
411                 return ret;
412         }
413
414         return check_free_space_extents(trans, fs_info, cache, path,
415                                         extents, ARRAY_SIZE(extents));
416 }
417
418 typedef int (*test_func_t)(struct btrfs_trans_handle *,
419                            struct btrfs_fs_info *,
420                            struct btrfs_block_group *,
421                            struct btrfs_path *,
422                            u32 alignment);
423
424 static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize,
425                     u32 nodesize, u32 alignment)
426 {
427         struct btrfs_fs_info *fs_info;
428         struct btrfs_root *root = NULL;
429         struct btrfs_block_group *cache = NULL;
430         struct btrfs_trans_handle trans;
431         struct btrfs_path *path = NULL;
432         int ret;
433
434         fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize);
435         if (!fs_info) {
436                 test_std_err(TEST_ALLOC_FS_INFO);
437                 ret = -ENOMEM;
438                 goto out;
439         }
440
441         root = btrfs_alloc_dummy_root(fs_info);
442         if (IS_ERR(root)) {
443                 test_std_err(TEST_ALLOC_ROOT);
444                 ret = PTR_ERR(root);
445                 goto out;
446         }
447
448         btrfs_set_super_compat_ro_flags(root->fs_info->super_copy,
449                                         BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE);
450         root->root_key.objectid = BTRFS_FREE_SPACE_TREE_OBJECTID;
451         root->root_key.type = BTRFS_ROOT_ITEM_KEY;
452         root->root_key.offset = 0;
453         btrfs_global_root_insert(root);
454         root->fs_info->tree_root = root;
455
456         root->node = alloc_test_extent_buffer(root->fs_info, nodesize);
457         if (IS_ERR(root->node)) {
458                 test_std_err(TEST_ALLOC_EXTENT_BUFFER);
459                 ret = PTR_ERR(root->node);
460                 goto out;
461         }
462         btrfs_set_header_level(root->node, 0);
463         btrfs_set_header_nritems(root->node, 0);
464         root->alloc_bytenr += 2 * nodesize;
465
466         cache = btrfs_alloc_dummy_block_group(fs_info, 8 * alignment);
467         if (!cache) {
468                 test_std_err(TEST_ALLOC_BLOCK_GROUP);
469                 ret = -ENOMEM;
470                 goto out;
471         }
472         cache->bitmap_low_thresh = 0;
473         cache->bitmap_high_thresh = (u32)-1;
474         set_bit(BLOCK_GROUP_FLAG_NEEDS_FREE_SPACE, &cache->runtime_flags);
475         cache->fs_info = root->fs_info;
476
477         btrfs_init_dummy_trans(&trans, root->fs_info);
478
479         path = btrfs_alloc_path();
480         if (!path) {
481                 test_std_err(TEST_ALLOC_ROOT);
482                 ret = -ENOMEM;
483                 goto out;
484         }
485
486         ret = add_block_group_free_space(&trans, cache);
487         if (ret) {
488                 test_err("could not add block group free space");
489                 goto out;
490         }
491
492         if (bitmaps) {
493                 ret = convert_free_space_to_bitmaps(&trans, cache, path);
494                 if (ret) {
495                         test_err("could not convert block group to bitmaps");
496                         goto out;
497                 }
498         }
499
500         ret = test_func(&trans, root->fs_info, cache, path, alignment);
501         if (ret)
502                 goto out;
503
504         ret = remove_block_group_free_space(&trans, cache);
505         if (ret) {
506                 test_err("could not remove block group free space");
507                 goto out;
508         }
509
510         if (btrfs_header_nritems(root->node) != 0) {
511                 test_err("free space tree has leftover items");
512                 ret = -EINVAL;
513                 goto out;
514         }
515
516         ret = 0;
517 out:
518         btrfs_free_path(path);
519         btrfs_free_dummy_block_group(cache);
520         btrfs_free_dummy_root(root);
521         btrfs_free_dummy_fs_info(fs_info);
522         return ret;
523 }
524
525 static int run_test_both_formats(test_func_t test_func, u32 sectorsize,
526                                  u32 nodesize, u32 alignment)
527 {
528         int test_ret = 0;
529         int ret;
530
531         ret = run_test(test_func, 0, sectorsize, nodesize, alignment);
532         if (ret) {
533                 test_err(
534         "%ps failed with extents, sectorsize=%u, nodesize=%u, alignment=%u",
535                          test_func, sectorsize, nodesize, alignment);
536                 test_ret = ret;
537         }
538
539         ret = run_test(test_func, 1, sectorsize, nodesize, alignment);
540         if (ret) {
541                 test_err(
542         "%ps failed with bitmaps, sectorsize=%u, nodesize=%u, alignment=%u",
543                          test_func, sectorsize, nodesize, alignment);
544                 test_ret = ret;
545         }
546
547         return test_ret;
548 }
549
550 int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize)
551 {
552         test_func_t tests[] = {
553                 test_empty_block_group,
554                 test_remove_all,
555                 test_remove_beginning,
556                 test_remove_end,
557                 test_remove_middle,
558                 test_merge_left,
559                 test_merge_right,
560                 test_merge_both,
561                 test_merge_none,
562         };
563         u32 bitmap_alignment;
564         int test_ret = 0;
565         int i;
566
567         /*
568          * Align some operations to a page to flush out bugs in the extent
569          * buffer bitmap handling of highmem.
570          */
571         bitmap_alignment = BTRFS_FREE_SPACE_BITMAP_BITS * PAGE_SIZE;
572
573         test_msg("running free space tree tests");
574         for (i = 0; i < ARRAY_SIZE(tests); i++) {
575                 int ret;
576
577                 ret = run_test_both_formats(tests[i], sectorsize, nodesize,
578                                             sectorsize);
579                 if (ret)
580                         test_ret = ret;
581
582                 ret = run_test_both_formats(tests[i], sectorsize, nodesize,
583                                             bitmap_alignment);
584                 if (ret)
585                         test_ret = ret;
586         }
587
588         return test_ret;
589 }