GNU Linux-libre 6.1.90-gnu
[releases.git] / fs / btrfs / tests / qgroup-tests.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2013 Facebook.  All rights reserved.
4  */
5
6 #include <linux/types.h>
7 #include "btrfs-tests.h"
8 #include "../ctree.h"
9 #include "../transaction.h"
10 #include "../disk-io.h"
11 #include "../qgroup.h"
12 #include "../backref.h"
13
14 static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr,
15                                   u64 num_bytes, u64 parent, u64 root_objectid)
16 {
17         struct btrfs_trans_handle trans;
18         struct btrfs_extent_item *item;
19         struct btrfs_extent_inline_ref *iref;
20         struct btrfs_tree_block_info *block_info;
21         struct btrfs_path *path;
22         struct extent_buffer *leaf;
23         struct btrfs_key ins;
24         u32 size = sizeof(*item) + sizeof(*iref) + sizeof(*block_info);
25         int ret;
26
27         btrfs_init_dummy_trans(&trans, NULL);
28
29         ins.objectid = bytenr;
30         ins.type = BTRFS_EXTENT_ITEM_KEY;
31         ins.offset = num_bytes;
32
33         path = btrfs_alloc_path();
34         if (!path) {
35                 test_std_err(TEST_ALLOC_ROOT);
36                 return -ENOMEM;
37         }
38
39         ret = btrfs_insert_empty_item(&trans, root, path, &ins, size);
40         if (ret) {
41                 test_err("couldn't insert ref %d", ret);
42                 btrfs_free_path(path);
43                 return ret;
44         }
45
46         leaf = path->nodes[0];
47         item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
48         btrfs_set_extent_refs(leaf, item, 1);
49         btrfs_set_extent_generation(leaf, item, 1);
50         btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_TREE_BLOCK);
51         block_info = (struct btrfs_tree_block_info *)(item + 1);
52         btrfs_set_tree_block_level(leaf, block_info, 0);
53         iref = (struct btrfs_extent_inline_ref *)(block_info + 1);
54         if (parent > 0) {
55                 btrfs_set_extent_inline_ref_type(leaf, iref,
56                                                  BTRFS_SHARED_BLOCK_REF_KEY);
57                 btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
58         } else {
59                 btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY);
60                 btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
61         }
62         btrfs_free_path(path);
63         return 0;
64 }
65
66 static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes,
67                         u64 parent, u64 root_objectid)
68 {
69         struct btrfs_trans_handle trans;
70         struct btrfs_extent_item *item;
71         struct btrfs_path *path;
72         struct btrfs_key key;
73         u64 refs;
74         int ret;
75
76         btrfs_init_dummy_trans(&trans, NULL);
77
78         key.objectid = bytenr;
79         key.type = BTRFS_EXTENT_ITEM_KEY;
80         key.offset = num_bytes;
81
82         path = btrfs_alloc_path();
83         if (!path) {
84                 test_std_err(TEST_ALLOC_ROOT);
85                 return -ENOMEM;
86         }
87
88         ret = btrfs_search_slot(&trans, root, &key, path, 0, 1);
89         if (ret) {
90                 test_err("couldn't find extent ref");
91                 btrfs_free_path(path);
92                 return ret;
93         }
94
95         item = btrfs_item_ptr(path->nodes[0], path->slots[0],
96                               struct btrfs_extent_item);
97         refs = btrfs_extent_refs(path->nodes[0], item);
98         btrfs_set_extent_refs(path->nodes[0], item, refs + 1);
99         btrfs_release_path(path);
100
101         key.objectid = bytenr;
102         if (parent) {
103                 key.type = BTRFS_SHARED_BLOCK_REF_KEY;
104                 key.offset = parent;
105         } else {
106                 key.type = BTRFS_TREE_BLOCK_REF_KEY;
107                 key.offset = root_objectid;
108         }
109
110         ret = btrfs_insert_empty_item(&trans, root, path, &key, 0);
111         if (ret)
112                 test_err("failed to insert backref");
113         btrfs_free_path(path);
114         return ret;
115 }
116
117 static int remove_extent_item(struct btrfs_root *root, u64 bytenr,
118                               u64 num_bytes)
119 {
120         struct btrfs_trans_handle trans;
121         struct btrfs_key key;
122         struct btrfs_path *path;
123         int ret;
124
125         btrfs_init_dummy_trans(&trans, NULL);
126
127         key.objectid = bytenr;
128         key.type = BTRFS_EXTENT_ITEM_KEY;
129         key.offset = num_bytes;
130
131         path = btrfs_alloc_path();
132         if (!path) {
133                 test_std_err(TEST_ALLOC_ROOT);
134                 return -ENOMEM;
135         }
136
137         ret = btrfs_search_slot(&trans, root, &key, path, -1, 1);
138         if (ret) {
139                 test_err("didn't find our key %d", ret);
140                 btrfs_free_path(path);
141                 return ret;
142         }
143         btrfs_del_item(&trans, root, path);
144         btrfs_free_path(path);
145         return 0;
146 }
147
148 static int remove_extent_ref(struct btrfs_root *root, u64 bytenr,
149                              u64 num_bytes, u64 parent, u64 root_objectid)
150 {
151         struct btrfs_trans_handle trans;
152         struct btrfs_extent_item *item;
153         struct btrfs_path *path;
154         struct btrfs_key key;
155         u64 refs;
156         int ret;
157
158         btrfs_init_dummy_trans(&trans, NULL);
159
160         key.objectid = bytenr;
161         key.type = BTRFS_EXTENT_ITEM_KEY;
162         key.offset = num_bytes;
163
164         path = btrfs_alloc_path();
165         if (!path) {
166                 test_std_err(TEST_ALLOC_ROOT);
167                 return -ENOMEM;
168         }
169
170         ret = btrfs_search_slot(&trans, root, &key, path, 0, 1);
171         if (ret) {
172                 test_err("couldn't find extent ref");
173                 btrfs_free_path(path);
174                 return ret;
175         }
176
177         item = btrfs_item_ptr(path->nodes[0], path->slots[0],
178                               struct btrfs_extent_item);
179         refs = btrfs_extent_refs(path->nodes[0], item);
180         btrfs_set_extent_refs(path->nodes[0], item, refs - 1);
181         btrfs_release_path(path);
182
183         key.objectid = bytenr;
184         if (parent) {
185                 key.type = BTRFS_SHARED_BLOCK_REF_KEY;
186                 key.offset = parent;
187         } else {
188                 key.type = BTRFS_TREE_BLOCK_REF_KEY;
189                 key.offset = root_objectid;
190         }
191
192         ret = btrfs_search_slot(&trans, root, &key, path, -1, 1);
193         if (ret) {
194                 test_err("couldn't find backref %d", ret);
195                 btrfs_free_path(path);
196                 return ret;
197         }
198         btrfs_del_item(&trans, root, path);
199         btrfs_free_path(path);
200         return ret;
201 }
202
203 static int test_no_shared_qgroup(struct btrfs_root *root,
204                 u32 sectorsize, u32 nodesize)
205 {
206         struct btrfs_trans_handle trans;
207         struct btrfs_fs_info *fs_info = root->fs_info;
208         struct ulist *old_roots = NULL;
209         struct ulist *new_roots = NULL;
210         int ret;
211
212         btrfs_init_dummy_trans(&trans, fs_info);
213
214         test_msg("running qgroup add/remove tests");
215         ret = btrfs_create_qgroup(&trans, BTRFS_FS_TREE_OBJECTID);
216         if (ret) {
217                 test_err("couldn't create a qgroup %d", ret);
218                 return ret;
219         }
220
221         /*
222          * Since the test trans doesn't have the complicated delayed refs,
223          * we can only call btrfs_qgroup_account_extent() directly to test
224          * quota.
225          */
226         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false);
227         if (ret) {
228                 test_err("couldn't find old roots: %d", ret);
229                 return ret;
230         }
231
232         ret = insert_normal_tree_ref(root, nodesize, nodesize, 0,
233                                 BTRFS_FS_TREE_OBJECTID);
234         if (ret) {
235                 ulist_free(old_roots);
236                 return ret;
237         }
238
239         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false);
240         if (ret) {
241                 ulist_free(old_roots);
242                 test_err("couldn't find old roots: %d", ret);
243                 return ret;
244         }
245
246         ret = btrfs_qgroup_account_extent(&trans, nodesize, nodesize, old_roots,
247                                           new_roots);
248         if (ret) {
249                 test_err("couldn't account space for a qgroup %d", ret);
250                 return ret;
251         }
252
253         /* btrfs_qgroup_account_extent() always frees the ulists passed to it. */
254         old_roots = NULL;
255         new_roots = NULL;
256
257         if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID,
258                                 nodesize, nodesize)) {
259                 test_err("qgroup counts didn't match expected values");
260                 return -EINVAL;
261         }
262
263         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false);
264         if (ret) {
265                 test_err("couldn't find old roots: %d", ret);
266                 return ret;
267         }
268
269         ret = remove_extent_item(root, nodesize, nodesize);
270         if (ret) {
271                 ulist_free(old_roots);
272                 return -EINVAL;
273         }
274
275         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false);
276         if (ret) {
277                 ulist_free(old_roots);
278                 test_err("couldn't find old roots: %d", ret);
279                 return ret;
280         }
281
282         ret = btrfs_qgroup_account_extent(&trans, nodesize, nodesize, old_roots,
283                                           new_roots);
284         if (ret) {
285                 test_err("couldn't account space for a qgroup %d", ret);
286                 return -EINVAL;
287         }
288
289         if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID, 0, 0)) {
290                 test_err("qgroup counts didn't match expected values");
291                 return -EINVAL;
292         }
293
294         return 0;
295 }
296
297 /*
298  * Add a ref for two different roots to make sure the shared value comes out
299  * right, also remove one of the roots and make sure the exclusive count is
300  * adjusted properly.
301  */
302 static int test_multiple_refs(struct btrfs_root *root,
303                 u32 sectorsize, u32 nodesize)
304 {
305         struct btrfs_trans_handle trans;
306         struct btrfs_fs_info *fs_info = root->fs_info;
307         struct ulist *old_roots = NULL;
308         struct ulist *new_roots = NULL;
309         int ret;
310
311         btrfs_init_dummy_trans(&trans, fs_info);
312
313         test_msg("running qgroup multiple refs test");
314
315         /*
316          * We have BTRFS_FS_TREE_OBJECTID created already from the
317          * previous test.
318          */
319         ret = btrfs_create_qgroup(&trans, BTRFS_FIRST_FREE_OBJECTID);
320         if (ret) {
321                 test_err("couldn't create a qgroup %d", ret);
322                 return ret;
323         }
324
325         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false);
326         if (ret) {
327                 test_err("couldn't find old roots: %d", ret);
328                 return ret;
329         }
330
331         ret = insert_normal_tree_ref(root, nodesize, nodesize, 0,
332                                 BTRFS_FS_TREE_OBJECTID);
333         if (ret) {
334                 ulist_free(old_roots);
335                 return ret;
336         }
337
338         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false);
339         if (ret) {
340                 ulist_free(old_roots);
341                 test_err("couldn't find old roots: %d", ret);
342                 return ret;
343         }
344
345         ret = btrfs_qgroup_account_extent(&trans, nodesize, nodesize, old_roots,
346                                           new_roots);
347         if (ret) {
348                 test_err("couldn't account space for a qgroup %d", ret);
349                 return ret;
350         }
351
352         if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID,
353                                        nodesize, nodesize)) {
354                 test_err("qgroup counts didn't match expected values");
355                 return -EINVAL;
356         }
357
358         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false);
359         if (ret) {
360                 test_err("couldn't find old roots: %d", ret);
361                 return ret;
362         }
363
364         ret = add_tree_ref(root, nodesize, nodesize, 0,
365                         BTRFS_FIRST_FREE_OBJECTID);
366         if (ret) {
367                 ulist_free(old_roots);
368                 return ret;
369         }
370
371         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false);
372         if (ret) {
373                 ulist_free(old_roots);
374                 test_err("couldn't find old roots: %d", ret);
375                 return ret;
376         }
377
378         ret = btrfs_qgroup_account_extent(&trans, nodesize, nodesize, old_roots,
379                                           new_roots);
380         if (ret) {
381                 test_err("couldn't account space for a qgroup %d", ret);
382                 return ret;
383         }
384
385         if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID,
386                                         nodesize, 0)) {
387                 test_err("qgroup counts didn't match expected values");
388                 return -EINVAL;
389         }
390
391         if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FIRST_FREE_OBJECTID,
392                                         nodesize, 0)) {
393                 test_err("qgroup counts didn't match expected values");
394                 return -EINVAL;
395         }
396
397         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false);
398         if (ret) {
399                 test_err("couldn't find old roots: %d", ret);
400                 return ret;
401         }
402
403         ret = remove_extent_ref(root, nodesize, nodesize, 0,
404                                 BTRFS_FIRST_FREE_OBJECTID);
405         if (ret) {
406                 ulist_free(old_roots);
407                 return ret;
408         }
409
410         ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false);
411         if (ret) {
412                 ulist_free(old_roots);
413                 test_err("couldn't find old roots: %d", ret);
414                 return ret;
415         }
416
417         ret = btrfs_qgroup_account_extent(&trans, nodesize, nodesize, old_roots,
418                                           new_roots);
419         if (ret) {
420                 test_err("couldn't account space for a qgroup %d", ret);
421                 return ret;
422         }
423
424         if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FIRST_FREE_OBJECTID,
425                                         0, 0)) {
426                 test_err("qgroup counts didn't match expected values");
427                 return -EINVAL;
428         }
429
430         if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID,
431                                         nodesize, nodesize)) {
432                 test_err("qgroup counts didn't match expected values");
433                 return -EINVAL;
434         }
435
436         return 0;
437 }
438
439 int btrfs_test_qgroups(u32 sectorsize, u32 nodesize)
440 {
441         struct btrfs_fs_info *fs_info = NULL;
442         struct btrfs_root *root;
443         struct btrfs_root *tmp_root;
444         int ret = 0;
445
446         fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize);
447         if (!fs_info) {
448                 test_std_err(TEST_ALLOC_FS_INFO);
449                 return -ENOMEM;
450         }
451
452         root = btrfs_alloc_dummy_root(fs_info);
453         if (IS_ERR(root)) {
454                 test_std_err(TEST_ALLOC_ROOT);
455                 ret = PTR_ERR(root);
456                 goto out;
457         }
458
459         /* We are using this root as our extent root */
460         root->root_key.objectid = BTRFS_EXTENT_TREE_OBJECTID;
461         root->root_key.type = BTRFS_ROOT_ITEM_KEY;
462         root->root_key.offset = 0;
463         btrfs_global_root_insert(root);
464
465         /*
466          * Some of the paths we test assume we have a filled out fs_info, so we
467          * just need to add the root in there so we don't panic.
468          */
469         root->fs_info->tree_root = root;
470         root->fs_info->quota_root = root;
471         set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
472
473         /*
474          * Can't use bytenr 0, some things freak out
475          * *cough*backref walking code*cough*
476          */
477         root->node = alloc_test_extent_buffer(root->fs_info, nodesize);
478         if (IS_ERR(root->node)) {
479                 test_err("couldn't allocate dummy buffer");
480                 ret = PTR_ERR(root->node);
481                 goto out;
482         }
483         btrfs_set_header_level(root->node, 0);
484         btrfs_set_header_nritems(root->node, 0);
485         root->alloc_bytenr += 2 * nodesize;
486
487         tmp_root = btrfs_alloc_dummy_root(fs_info);
488         if (IS_ERR(tmp_root)) {
489                 test_std_err(TEST_ALLOC_ROOT);
490                 ret = PTR_ERR(tmp_root);
491                 goto out;
492         }
493
494         tmp_root->root_key.objectid = BTRFS_FS_TREE_OBJECTID;
495         root->fs_info->fs_root = tmp_root;
496         ret = btrfs_insert_fs_root(root->fs_info, tmp_root);
497         if (ret) {
498                 test_err("couldn't insert fs root %d", ret);
499                 goto out;
500         }
501         btrfs_put_root(tmp_root);
502
503         tmp_root = btrfs_alloc_dummy_root(fs_info);
504         if (IS_ERR(tmp_root)) {
505                 test_std_err(TEST_ALLOC_ROOT);
506                 ret = PTR_ERR(tmp_root);
507                 goto out;
508         }
509
510         tmp_root->root_key.objectid = BTRFS_FIRST_FREE_OBJECTID;
511         ret = btrfs_insert_fs_root(root->fs_info, tmp_root);
512         if (ret) {
513                 test_err("couldn't insert fs root %d", ret);
514                 goto out;
515         }
516         btrfs_put_root(tmp_root);
517
518         test_msg("running qgroup tests");
519         ret = test_no_shared_qgroup(root, sectorsize, nodesize);
520         if (ret)
521                 goto out;
522         ret = test_multiple_refs(root, sectorsize, nodesize);
523 out:
524         btrfs_free_dummy_root(root);
525         btrfs_free_dummy_fs_info(fs_info);
526         return ret;
527 }