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