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
25static 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
33static 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
86static 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
138static 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
170static 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
226static 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);
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 */
293static 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);
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
389int 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	/* We are using this root as our extent root */
408	root->fs_info->extent_root = root;
409
410	/*
411	 * Some of the paths we test assume we have a filled out fs_info, so we
412	 * just need to add the root in there so we don't panic.
413	 */
414	root->fs_info->tree_root = root;
415	root->fs_info->quota_root = root;
416	root->fs_info->quota_enabled = 1;
417
418	/*
419	 * Can't use bytenr 0, some things freak out
420	 * *cough*backref walking code*cough*
421	 */
422	root->node = alloc_test_extent_buffer(root->fs_info, 4096);
423	if (!root->node) {
424		test_msg("Couldn't allocate dummy buffer\n");
425		ret = -ENOMEM;
426		goto out;
427	}
428	btrfs_set_header_level(root->node, 0);
429	btrfs_set_header_nritems(root->node, 0);
430	root->alloc_bytenr += 8192;
431
432	tmp_root = btrfs_alloc_dummy_root();
433	if (IS_ERR(tmp_root)) {
434		test_msg("Couldn't allocate a fs root\n");
435		ret = PTR_ERR(tmp_root);
436		goto out;
437	}
438
439	tmp_root->root_key.objectid = 5;
440	root->fs_info->fs_root = tmp_root;
441	ret = btrfs_insert_fs_root(root->fs_info, tmp_root);
442	if (ret) {
443		test_msg("Couldn't insert fs root %d\n", ret);
444		goto out;
445	}
446
447	tmp_root = btrfs_alloc_dummy_root();
448	if (IS_ERR(tmp_root)) {
449		test_msg("Couldn't allocate a fs root\n");
450		ret = PTR_ERR(tmp_root);
451		goto out;
452	}
453
454	tmp_root->root_key.objectid = 256;
455	ret = btrfs_insert_fs_root(root->fs_info, tmp_root);
456	if (ret) {
457		test_msg("Couldn't insert fs root %d\n", ret);
458		goto out;
459	}
460
461	test_msg("Running qgroup tests\n");
462	ret = test_no_shared_qgroup(root);
463	if (ret)
464		goto out;
465	ret = test_multiple_refs(root);
466out:
467	btrfs_free_dummy_root(root);
468	return ret;
469}
470