Commit | Line | Data |
---|---|---|
faa2dbf0 JB |
1 | /* |
2 | * Copyright (C) 2013 Facebook. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public | |
6 | * License v2 as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public | |
14 | * License along with this program; if not, write to the | |
15 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
16 | * Boston, MA 021110-1307, USA. | |
17 | */ | |
18 | ||
19 | #include "btrfs-tests.h" | |
20 | #include "../ctree.h" | |
21 | #include "../transaction.h" | |
22 | #include "../disk-io.h" | |
23 | #include "../qgroup.h" | |
24 | ||
25 | static void init_dummy_trans(struct btrfs_trans_handle *trans) | |
26 | { | |
27 | memset(trans, 0, sizeof(*trans)); | |
28 | trans->transid = 1; | |
29 | INIT_LIST_HEAD(&trans->qgroup_ref_list); | |
30 | trans->type = __TRANS_DUMMY; | |
31 | } | |
32 | ||
33 | static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr, | |
34 | u64 num_bytes, u64 parent, u64 root_objectid) | |
35 | { | |
36 | struct btrfs_trans_handle trans; | |
37 | struct btrfs_extent_item *item; | |
38 | struct btrfs_extent_inline_ref *iref; | |
39 | struct btrfs_tree_block_info *block_info; | |
40 | struct btrfs_path *path; | |
41 | struct extent_buffer *leaf; | |
42 | struct btrfs_key ins; | |
43 | u32 size = sizeof(*item) + sizeof(*iref) + sizeof(*block_info); | |
44 | int ret; | |
45 | ||
46 | init_dummy_trans(&trans); | |
47 | ||
48 | ins.objectid = bytenr; | |
49 | ins.type = BTRFS_EXTENT_ITEM_KEY; | |
50 | ins.offset = num_bytes; | |
51 | ||
52 | path = btrfs_alloc_path(); | |
53 | if (!path) { | |
54 | test_msg("Couldn't allocate path\n"); | |
55 | return -ENOMEM; | |
56 | } | |
57 | ||
58 | path->leave_spinning = 1; | |
59 | ret = btrfs_insert_empty_item(&trans, root, path, &ins, size); | |
60 | if (ret) { | |
61 | test_msg("Couldn't insert ref %d\n", ret); | |
62 | btrfs_free_path(path); | |
63 | return ret; | |
64 | } | |
65 | ||
66 | leaf = path->nodes[0]; | |
67 | item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); | |
68 | btrfs_set_extent_refs(leaf, item, 1); | |
69 | btrfs_set_extent_generation(leaf, item, 1); | |
70 | btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_TREE_BLOCK); | |
71 | block_info = (struct btrfs_tree_block_info *)(item + 1); | |
72 | btrfs_set_tree_block_level(leaf, block_info, 1); | |
73 | iref = (struct btrfs_extent_inline_ref *)(block_info + 1); | |
74 | if (parent > 0) { | |
75 | btrfs_set_extent_inline_ref_type(leaf, iref, | |
76 | BTRFS_SHARED_BLOCK_REF_KEY); | |
77 | btrfs_set_extent_inline_ref_offset(leaf, iref, parent); | |
78 | } else { | |
79 | btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY); | |
80 | btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid); | |
81 | } | |
82 | btrfs_free_path(path); | |
83 | return 0; | |
84 | } | |
85 | ||
86 | static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes, | |
87 | u64 parent, u64 root_objectid) | |
88 | { | |
89 | struct btrfs_trans_handle trans; | |
90 | struct btrfs_extent_item *item; | |
91 | struct btrfs_path *path; | |
92 | struct btrfs_key key; | |
93 | u64 refs; | |
94 | int ret; | |
95 | ||
96 | init_dummy_trans(&trans); | |
97 | ||
98 | key.objectid = bytenr; | |
99 | key.type = BTRFS_EXTENT_ITEM_KEY; | |
100 | key.offset = num_bytes; | |
101 | ||
102 | path = btrfs_alloc_path(); | |
103 | if (!path) { | |
104 | test_msg("Couldn't allocate path\n"); | |
105 | return -ENOMEM; | |
106 | } | |
107 | ||
108 | path->leave_spinning = 1; | |
109 | ret = btrfs_search_slot(&trans, root, &key, path, 0, 1); | |
110 | if (ret) { | |
111 | test_msg("Couldn't find extent ref\n"); | |
112 | btrfs_free_path(path); | |
113 | return ret; | |
114 | } | |
115 | ||
116 | item = btrfs_item_ptr(path->nodes[0], path->slots[0], | |
117 | struct btrfs_extent_item); | |
118 | refs = btrfs_extent_refs(path->nodes[0], item); | |
119 | btrfs_set_extent_refs(path->nodes[0], item, refs + 1); | |
120 | btrfs_release_path(path); | |
121 | ||
122 | key.objectid = bytenr; | |
123 | if (parent) { | |
124 | key.type = BTRFS_SHARED_BLOCK_REF_KEY; | |
125 | key.offset = parent; | |
126 | } else { | |
127 | key.type = BTRFS_TREE_BLOCK_REF_KEY; | |
128 | key.offset = root_objectid; | |
129 | } | |
130 | ||
131 | ret = btrfs_insert_empty_item(&trans, root, path, &key, 0); | |
132 | if (ret) | |
133 | test_msg("Failed to insert backref\n"); | |
134 | btrfs_free_path(path); | |
135 | return ret; | |
136 | } | |
137 | ||
138 | static int remove_extent_item(struct btrfs_root *root, u64 bytenr, | |
139 | u64 num_bytes) | |
140 | { | |
141 | struct btrfs_trans_handle trans; | |
142 | struct btrfs_key key; | |
143 | struct btrfs_path *path; | |
144 | int ret; | |
145 | ||
146 | init_dummy_trans(&trans); | |
147 | ||
148 | key.objectid = bytenr; | |
149 | key.type = BTRFS_EXTENT_ITEM_KEY; | |
150 | key.offset = num_bytes; | |
151 | ||
152 | path = btrfs_alloc_path(); | |
153 | if (!path) { | |
154 | test_msg("Couldn't allocate path\n"); | |
155 | return -ENOMEM; | |
156 | } | |
157 | path->leave_spinning = 1; | |
158 | ||
159 | ret = btrfs_search_slot(&trans, root, &key, path, -1, 1); | |
160 | if (ret) { | |
161 | test_msg("Didn't find our key %d\n", ret); | |
162 | btrfs_free_path(path); | |
163 | return ret; | |
164 | } | |
165 | btrfs_del_item(&trans, root, path); | |
166 | btrfs_free_path(path); | |
167 | return 0; | |
168 | } | |
169 | ||
170 | static int remove_extent_ref(struct btrfs_root *root, u64 bytenr, | |
171 | u64 num_bytes, u64 parent, u64 root_objectid) | |
172 | { | |
173 | struct btrfs_trans_handle trans; | |
174 | struct btrfs_extent_item *item; | |
175 | struct btrfs_path *path; | |
176 | struct btrfs_key key; | |
177 | u64 refs; | |
178 | int ret; | |
179 | ||
180 | init_dummy_trans(&trans); | |
181 | ||
182 | key.objectid = bytenr; | |
183 | key.type = BTRFS_EXTENT_ITEM_KEY; | |
184 | key.offset = num_bytes; | |
185 | ||
186 | path = btrfs_alloc_path(); | |
187 | if (!path) { | |
188 | test_msg("Couldn't allocate path\n"); | |
189 | return -ENOMEM; | |
190 | } | |
191 | ||
192 | path->leave_spinning = 1; | |
193 | ret = btrfs_search_slot(&trans, root, &key, path, 0, 1); | |
194 | if (ret) { | |
195 | test_msg("Couldn't find extent ref\n"); | |
196 | btrfs_free_path(path); | |
197 | return ret; | |
198 | } | |
199 | ||
200 | item = btrfs_item_ptr(path->nodes[0], path->slots[0], | |
201 | struct btrfs_extent_item); | |
202 | refs = btrfs_extent_refs(path->nodes[0], item); | |
203 | btrfs_set_extent_refs(path->nodes[0], item, refs - 1); | |
204 | btrfs_release_path(path); | |
205 | ||
206 | key.objectid = bytenr; | |
207 | if (parent) { | |
208 | key.type = BTRFS_SHARED_BLOCK_REF_KEY; | |
209 | key.offset = parent; | |
210 | } else { | |
211 | key.type = BTRFS_TREE_BLOCK_REF_KEY; | |
212 | key.offset = root_objectid; | |
213 | } | |
214 | ||
215 | ret = btrfs_search_slot(&trans, root, &key, path, -1, 1); | |
216 | if (ret) { | |
217 | test_msg("Couldn't find backref %d\n", ret); | |
218 | btrfs_free_path(path); | |
219 | return ret; | |
220 | } | |
221 | btrfs_del_item(&trans, root, path); | |
222 | btrfs_free_path(path); | |
223 | return ret; | |
224 | } | |
225 | ||
226 | static int test_no_shared_qgroup(struct btrfs_root *root) | |
227 | { | |
228 | struct btrfs_trans_handle trans; | |
229 | struct btrfs_fs_info *fs_info = root->fs_info; | |
230 | int ret; | |
231 | ||
232 | init_dummy_trans(&trans); | |
233 | ||
234 | test_msg("Qgroup basic add\n"); | |
235 | ret = btrfs_create_qgroup(NULL, fs_info, 5, NULL); | |
236 | if (ret) { | |
237 | test_msg("Couldn't create a qgroup %d\n", ret); | |
238 | return ret; | |
239 | } | |
240 | ||
241 | ret = btrfs_qgroup_record_ref(&trans, fs_info, 5, 4096, 4096, | |
242 | BTRFS_QGROUP_OPER_ADD_EXCL, 0); | |
243 | if (ret) { | |
244 | test_msg("Couldn't add space to a qgroup %d\n", ret); | |
245 | return ret; | |
246 | } | |
247 | ||
248 | ret = insert_normal_tree_ref(root, 4096, 4096, 0, 5); | |
249 | if (ret) | |
250 | return ret; | |
251 | ||
252 | ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); | |
253 | if (ret) { | |
254 | test_msg("Delayed qgroup accounting failed %d\n", ret); | |
255 | return ret; | |
256 | } | |
257 | ||
258 | if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 4096)) { | |
259 | test_msg("Qgroup counts didn't match expected values\n"); | |
260 | return -EINVAL; | |
261 | } | |
262 | ||
263 | ret = remove_extent_item(root, 4096, 4096); | |
264 | if (ret) | |
265 | return -EINVAL; | |
266 | ||
267 | ret = btrfs_qgroup_record_ref(&trans, fs_info, 5, 4096, 4096, | |
268 | BTRFS_QGROUP_OPER_SUB_EXCL, 0); | |
269 | if (ret) { | |
270 | test_msg("Couldn't remove space from the qgroup %d\n", ret); | |
271 | return -EINVAL; | |
272 | } | |
273 | ||
274 | ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); | |
275 | if (ret) { | |
276 | test_msg("Qgroup accounting failed %d\n", ret); | |
277 | return -EINVAL; | |
278 | } | |
279 | ||
280 | if (btrfs_verify_qgroup_counts(fs_info, 5, 0, 0)) { | |
281 | test_msg("Qgroup counts didn't match expected values\n"); | |
282 | return -EINVAL; | |
283 | } | |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
288 | /* | |
289 | * Add a ref for two different roots to make sure the shared value comes out | |
290 | * right, also remove one of the roots and make sure the exclusive count is | |
291 | * adjusted properly. | |
292 | */ | |
293 | static int test_multiple_refs(struct btrfs_root *root) | |
294 | { | |
295 | struct btrfs_trans_handle trans; | |
296 | struct btrfs_fs_info *fs_info = root->fs_info; | |
297 | int ret; | |
298 | ||
299 | init_dummy_trans(&trans); | |
300 | ||
301 | test_msg("Qgroup multiple refs test\n"); | |
302 | ||
303 | /* We have 5 created already from the previous test */ | |
304 | ret = btrfs_create_qgroup(NULL, fs_info, 256, NULL); | |
305 | if (ret) { | |
306 | test_msg("Couldn't create a qgroup %d\n", ret); | |
307 | return ret; | |
308 | } | |
309 | ||
310 | ret = insert_normal_tree_ref(root, 4096, 4096, 0, 5); | |
311 | if (ret) | |
312 | return ret; | |
313 | ||
314 | ret = btrfs_qgroup_record_ref(&trans, fs_info, 5, 4096, 4096, | |
315 | BTRFS_QGROUP_OPER_ADD_EXCL, 0); | |
316 | if (ret) { | |
317 | test_msg("Couldn't add space to a qgroup %d\n", ret); | |
318 | return ret; | |
319 | } | |
320 | ||
321 | ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); | |
322 | if (ret) { | |
323 | test_msg("Delayed qgroup accounting failed %d\n", ret); | |
324 | return ret; | |
325 | } | |
326 | ||
327 | if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 4096)) { | |
328 | test_msg("Qgroup counts didn't match expected values\n"); | |
329 | return -EINVAL; | |
330 | } | |
331 | ||
332 | ret = add_tree_ref(root, 4096, 4096, 0, 256); | |
333 | if (ret) | |
334 | return ret; | |
335 | ||
336 | ret = btrfs_qgroup_record_ref(&trans, fs_info, 256, 4096, 4096, | |
337 | BTRFS_QGROUP_OPER_ADD_SHARED, 0); | |
338 | if (ret) { | |
339 | test_msg("Qgroup record ref failed %d\n", ret); | |
340 | return ret; | |
341 | } | |
342 | ||
343 | ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); | |
344 | if (ret) { | |
345 | test_msg("Qgroup accounting failed %d\n", ret); | |
346 | return ret; | |
347 | } | |
348 | ||
349 | if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 0)) { | |
350 | test_msg("Qgroup counts didn't match expected values\n"); | |
351 | return -EINVAL; | |
352 | } | |
353 | ||
354 | if (btrfs_verify_qgroup_counts(fs_info, 256, 4096, 0)) { | |
355 | test_msg("Qgroup counts didn't match expected values\n"); | |
356 | return -EINVAL; | |
357 | } | |
358 | ||
359 | ret = remove_extent_ref(root, 4096, 4096, 0, 256); | |
360 | if (ret) | |
361 | return ret; | |
362 | ||
363 | ret = btrfs_qgroup_record_ref(&trans, fs_info, 256, 4096, 4096, | |
364 | BTRFS_QGROUP_OPER_SUB_SHARED, 0); | |
365 | if (ret) { | |
366 | test_msg("Qgroup record ref failed %d\n", ret); | |
367 | return ret; | |
368 | } | |
369 | ||
370 | ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); | |
371 | if (ret) { | |
372 | test_msg("Qgroup accounting failed %d\n", ret); | |
373 | return ret; | |
374 | } | |
375 | ||
376 | if (btrfs_verify_qgroup_counts(fs_info, 256, 0, 0)) { | |
377 | test_msg("Qgroup counts didn't match expected values\n"); | |
378 | return -EINVAL; | |
379 | } | |
380 | ||
381 | if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 4096)) { | |
382 | test_msg("Qgroup counts didn't match expected values\n"); | |
383 | return -EINVAL; | |
384 | } | |
385 | ||
386 | return 0; | |
387 | } | |
388 | ||
389 | int btrfs_test_qgroups(void) | |
390 | { | |
391 | struct btrfs_root *root; | |
392 | struct btrfs_root *tmp_root; | |
393 | int ret = 0; | |
394 | ||
395 | root = btrfs_alloc_dummy_root(); | |
396 | if (IS_ERR(root)) { | |
397 | test_msg("Couldn't allocate root\n"); | |
398 | return PTR_ERR(root); | |
399 | } | |
400 | ||
401 | root->fs_info = btrfs_alloc_dummy_fs_info(); | |
402 | if (!root->fs_info) { | |
403 | test_msg("Couldn't allocate dummy fs info\n"); | |
404 | ret = -ENOMEM; | |
405 | goto out; | |
406 | } | |
407 | ||
408 | /* | |
409 | * Can't use bytenr 0, some things freak out | |
410 | * *cough*backref walking code*cough* | |
411 | */ | |
412 | root->node = alloc_test_extent_buffer(root->fs_info, 4096, 4096); | |
413 | if (!root->node) { | |
414 | test_msg("Couldn't allocate dummy buffer\n"); | |
415 | ret = -ENOMEM; | |
416 | goto out; | |
417 | } | |
b050f9f6 FM |
418 | btrfs_set_header_level(root->node, 0); |
419 | btrfs_set_header_nritems(root->node, 0); | |
faa2dbf0 JB |
420 | root->alloc_bytenr += 8192; |
421 | ||
422 | tmp_root = btrfs_alloc_dummy_root(); | |
423 | if (IS_ERR(tmp_root)) { | |
424 | test_msg("Couldn't allocate a fs root\n"); | |
425 | ret = PTR_ERR(tmp_root); | |
426 | goto out; | |
427 | } | |
428 | ||
429 | tmp_root->root_key.objectid = 5; | |
430 | root->fs_info->fs_root = tmp_root; | |
431 | ret = btrfs_insert_fs_root(root->fs_info, tmp_root); | |
432 | if (ret) { | |
433 | test_msg("Couldn't insert fs root %d\n", ret); | |
434 | goto out; | |
435 | } | |
436 | ||
437 | tmp_root = btrfs_alloc_dummy_root(); | |
438 | if (IS_ERR(tmp_root)) { | |
439 | test_msg("Couldn't allocate a fs root\n"); | |
440 | ret = PTR_ERR(tmp_root); | |
441 | goto out; | |
442 | } | |
443 | ||
444 | tmp_root->root_key.objectid = 256; | |
445 | ret = btrfs_insert_fs_root(root->fs_info, tmp_root); | |
446 | if (ret) { | |
447 | test_msg("Couldn't insert fs root %d\n", ret); | |
448 | goto out; | |
449 | } | |
450 | ||
451 | /* We are using this root as our extent root */ | |
452 | root->fs_info->extent_root = root; | |
453 | ||
454 | /* | |
455 | * Some of the paths we test assume we have a filled out fs_info, so we | |
456 | * just need to addt he root in there so we don't panic. | |
457 | */ | |
458 | root->fs_info->tree_root = root; | |
459 | root->fs_info->quota_root = root; | |
460 | root->fs_info->quota_enabled = 1; | |
461 | ||
462 | test_msg("Running qgroup tests\n"); | |
463 | ret = test_no_shared_qgroup(root); | |
464 | if (ret) | |
465 | goto out; | |
466 | ret = test_multiple_refs(root); | |
467 | out: | |
468 | btrfs_free_dummy_root(root); | |
469 | return ret; | |
470 | } |