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