xref: /openbmc/linux/fs/btrfs/tests/qgroup-tests.c (revision c819e2cf)
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 	}
418 	btrfs_set_header_level(root->node, 0);
419 	btrfs_set_header_nritems(root->node, 0);
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 }
471