xref: /openbmc/linux/fs/btrfs/tests/extent-map-tests.c (revision 93ead46b038037cd717cf46c9170af2531a6eb58)
1c1d7c514SDavid Sterba // SPDX-License-Identifier: GPL-2.0
272b28077SLiu Bo /*
372b28077SLiu Bo  * Copyright (C) 2017 Oracle.  All rights reserved.
472b28077SLiu Bo  */
572b28077SLiu Bo 
672b28077SLiu Bo #include <linux/types.h>
772b28077SLiu Bo #include "btrfs-tests.h"
872b28077SLiu Bo #include "../ctree.h"
972b28077SLiu Bo 
1072b28077SLiu Bo static void free_extent_map_tree(struct extent_map_tree *em_tree)
1172b28077SLiu Bo {
1272b28077SLiu Bo 	struct extent_map *em;
1372b28077SLiu Bo 	struct rb_node *node;
1472b28077SLiu Bo 
1507e1ce09SLiu Bo 	while (!RB_EMPTY_ROOT(&em_tree->map.rb_root)) {
1607e1ce09SLiu Bo 		node = rb_first_cached(&em_tree->map);
1772b28077SLiu Bo 		em = rb_entry(node, struct extent_map, rb_node);
1872b28077SLiu Bo 		remove_extent_mapping(em_tree, em);
1972b28077SLiu Bo 
2072b28077SLiu Bo #ifdef CONFIG_BTRFS_DEBUG
2172b28077SLiu Bo 		if (refcount_read(&em->refs) != 1) {
223c7251f2SDavid Sterba 			test_err(
233c7251f2SDavid Sterba "em leak: em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx) refs %d",
2472b28077SLiu Bo 				 em->start, em->len, em->block_start,
2572b28077SLiu Bo 				 em->block_len, refcount_read(&em->refs));
2672b28077SLiu Bo 
2772b28077SLiu Bo 			refcount_set(&em->refs, 1);
2872b28077SLiu Bo 		}
2972b28077SLiu Bo #endif
3072b28077SLiu Bo 		free_extent_map(em);
3172b28077SLiu Bo 	}
3272b28077SLiu Bo }
3372b28077SLiu Bo 
3472b28077SLiu Bo /*
3572b28077SLiu Bo  * Test scenario:
3672b28077SLiu Bo  *
3772b28077SLiu Bo  * Suppose that no extent map has been loaded into memory yet, there is a file
3872b28077SLiu Bo  * extent [0, 16K), followed by another file extent [16K, 20K), two dio reads
3972b28077SLiu Bo  * are entering btrfs_get_extent() concurrently, t1 is reading [8K, 16K), t2 is
4072b28077SLiu Bo  * reading [0, 8K)
4172b28077SLiu Bo  *
4272b28077SLiu Bo  *     t1                            t2
4372b28077SLiu Bo  *  btrfs_get_extent()              btrfs_get_extent()
4472b28077SLiu Bo  *    -> lookup_extent_mapping()      ->lookup_extent_mapping()
4572b28077SLiu Bo  *    -> add_extent_mapping(0, 16K)
4672b28077SLiu Bo  *    -> return em
4772b28077SLiu Bo  *                                    ->add_extent_mapping(0, 16K)
4872b28077SLiu Bo  *                                    -> #handle -EEXIST
4972b28077SLiu Bo  */
50488f6730SDavid Sterba static int test_case_1(struct btrfs_fs_info *fs_info,
510e08eb9bSDavid Sterba 		struct extent_map_tree *em_tree)
5272b28077SLiu Bo {
5372b28077SLiu Bo 	struct extent_map *em;
5472b28077SLiu Bo 	u64 start = 0;
5572b28077SLiu Bo 	u64 len = SZ_8K;
5672b28077SLiu Bo 	int ret;
5772b28077SLiu Bo 
5872b28077SLiu Bo 	em = alloc_extent_map();
596c304746SDavid Sterba 	if (!em) {
606c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
61488f6730SDavid Sterba 		return -ENOMEM;
626c304746SDavid Sterba 	}
6372b28077SLiu Bo 
6472b28077SLiu Bo 	/* Add [0, 16K) */
6572b28077SLiu Bo 	em->start = 0;
6672b28077SLiu Bo 	em->len = SZ_16K;
6772b28077SLiu Bo 	em->block_start = 0;
6872b28077SLiu Bo 	em->block_len = SZ_16K;
69*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
7072b28077SLiu Bo 	ret = add_extent_mapping(em_tree, em, 0);
71*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
72d7de4b08SDavid Sterba 	if (ret < 0) {
73d7de4b08SDavid Sterba 		test_err("cannot add extent range [0, 16K)");
74d7de4b08SDavid Sterba 		goto out;
75d7de4b08SDavid Sterba 	}
7672b28077SLiu Bo 	free_extent_map(em);
7772b28077SLiu Bo 
7872b28077SLiu Bo 	/* Add [16K, 20K) following [0, 16K)  */
7972b28077SLiu Bo 	em = alloc_extent_map();
80488f6730SDavid Sterba 	if (!em) {
816c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
82488f6730SDavid Sterba 		ret = -ENOMEM;
8372b28077SLiu Bo 		goto out;
84488f6730SDavid Sterba 	}
8572b28077SLiu Bo 
8672b28077SLiu Bo 	em->start = SZ_16K;
8772b28077SLiu Bo 	em->len = SZ_4K;
8872b28077SLiu Bo 	em->block_start = SZ_32K; /* avoid merging */
8972b28077SLiu Bo 	em->block_len = SZ_4K;
90*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
9172b28077SLiu Bo 	ret = add_extent_mapping(em_tree, em, 0);
92*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
93d7de4b08SDavid Sterba 	if (ret < 0) {
94d7de4b08SDavid Sterba 		test_err("cannot add extent range [16K, 20K)");
95d7de4b08SDavid Sterba 		goto out;
96d7de4b08SDavid Sterba 	}
9772b28077SLiu Bo 	free_extent_map(em);
9872b28077SLiu Bo 
9972b28077SLiu Bo 	em = alloc_extent_map();
100488f6730SDavid Sterba 	if (!em) {
1016c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
102488f6730SDavid Sterba 		ret = -ENOMEM;
10372b28077SLiu Bo 		goto out;
104488f6730SDavid Sterba 	}
10572b28077SLiu Bo 
10672b28077SLiu Bo 	/* Add [0, 8K), should return [0, 16K) instead. */
10772b28077SLiu Bo 	em->start = start;
10872b28077SLiu Bo 	em->len = len;
10972b28077SLiu Bo 	em->block_start = start;
11072b28077SLiu Bo 	em->block_len = len;
111*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
112f46b24c9SDavid Sterba 	ret = btrfs_add_extent_mapping(fs_info, em_tree, &em, em->start, em->len);
113*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
114d7de4b08SDavid Sterba 	if (ret) {
1153c7251f2SDavid Sterba 		test_err("case1 [%llu %llu]: ret %d", start, start + len, ret);
116d7de4b08SDavid Sterba 		goto out;
117d7de4b08SDavid Sterba 	}
11872b28077SLiu Bo 	if (em &&
11972b28077SLiu Bo 	    (em->start != 0 || extent_map_end(em) != SZ_16K ||
120d7de4b08SDavid Sterba 	     em->block_start != 0 || em->block_len != SZ_16K)) {
1213c7251f2SDavid Sterba 		test_err(
1223c7251f2SDavid Sterba "case1 [%llu %llu]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu",
12372b28077SLiu Bo 			 start, start + len, ret, em->start, em->len,
12472b28077SLiu Bo 			 em->block_start, em->block_len);
125d7de4b08SDavid Sterba 		ret = -EINVAL;
126d7de4b08SDavid Sterba 	}
12772b28077SLiu Bo 	free_extent_map(em);
12872b28077SLiu Bo out:
12972b28077SLiu Bo 	free_extent_map_tree(em_tree);
130488f6730SDavid Sterba 
131488f6730SDavid Sterba 	return ret;
13272b28077SLiu Bo }
13372b28077SLiu Bo 
13472b28077SLiu Bo /*
13572b28077SLiu Bo  * Test scenario:
13672b28077SLiu Bo  *
13772b28077SLiu Bo  * Reading the inline ending up with EEXIST, ie. read an inline
13872b28077SLiu Bo  * extent and discard page cache and read it again.
13972b28077SLiu Bo  */
140488f6730SDavid Sterba static int test_case_2(struct btrfs_fs_info *fs_info,
1410e08eb9bSDavid Sterba 		struct extent_map_tree *em_tree)
14272b28077SLiu Bo {
14372b28077SLiu Bo 	struct extent_map *em;
14472b28077SLiu Bo 	int ret;
14572b28077SLiu Bo 
14672b28077SLiu Bo 	em = alloc_extent_map();
1476c304746SDavid Sterba 	if (!em) {
1486c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
149488f6730SDavid Sterba 		return -ENOMEM;
1506c304746SDavid Sterba 	}
15172b28077SLiu Bo 
15272b28077SLiu Bo 	/* Add [0, 1K) */
15372b28077SLiu Bo 	em->start = 0;
15472b28077SLiu Bo 	em->len = SZ_1K;
15572b28077SLiu Bo 	em->block_start = EXTENT_MAP_INLINE;
15672b28077SLiu Bo 	em->block_len = (u64)-1;
157*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
15872b28077SLiu Bo 	ret = add_extent_mapping(em_tree, em, 0);
159*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
160e71f2e17SDavid Sterba 	if (ret < 0) {
161e71f2e17SDavid Sterba 		test_err("cannot add extent range [0, 1K)");
162e71f2e17SDavid Sterba 		goto out;
163e71f2e17SDavid Sterba 	}
16472b28077SLiu Bo 	free_extent_map(em);
16572b28077SLiu Bo 
1663173fd92SDavid Sterba 	/* Add [4K, 8K) following [0, 1K)  */
16772b28077SLiu Bo 	em = alloc_extent_map();
168488f6730SDavid Sterba 	if (!em) {
1696c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
170488f6730SDavid Sterba 		ret = -ENOMEM;
17172b28077SLiu Bo 		goto out;
172488f6730SDavid Sterba 	}
17372b28077SLiu Bo 
17472b28077SLiu Bo 	em->start = SZ_4K;
17572b28077SLiu Bo 	em->len = SZ_4K;
17672b28077SLiu Bo 	em->block_start = SZ_4K;
17772b28077SLiu Bo 	em->block_len = SZ_4K;
178*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
17972b28077SLiu Bo 	ret = add_extent_mapping(em_tree, em, 0);
180*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
181e71f2e17SDavid Sterba 	if (ret < 0) {
182e71f2e17SDavid Sterba 		test_err("cannot add extent range [4K, 8K)");
183e71f2e17SDavid Sterba 		goto out;
184e71f2e17SDavid Sterba 	}
18572b28077SLiu Bo 	free_extent_map(em);
18672b28077SLiu Bo 
18772b28077SLiu Bo 	em = alloc_extent_map();
188488f6730SDavid Sterba 	if (!em) {
1896c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
190488f6730SDavid Sterba 		ret = -ENOMEM;
19172b28077SLiu Bo 		goto out;
192488f6730SDavid Sterba 	}
19372b28077SLiu Bo 
19472b28077SLiu Bo 	/* Add [0, 1K) */
19572b28077SLiu Bo 	em->start = 0;
19672b28077SLiu Bo 	em->len = SZ_1K;
19772b28077SLiu Bo 	em->block_start = EXTENT_MAP_INLINE;
19872b28077SLiu Bo 	em->block_len = (u64)-1;
199*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
200f46b24c9SDavid Sterba 	ret = btrfs_add_extent_mapping(fs_info, em_tree, &em, em->start, em->len);
201*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
202e71f2e17SDavid Sterba 	if (ret) {
2033c7251f2SDavid Sterba 		test_err("case2 [0 1K]: ret %d", ret);
204e71f2e17SDavid Sterba 		goto out;
205e71f2e17SDavid Sterba 	}
20672b28077SLiu Bo 	if (em &&
20772b28077SLiu Bo 	    (em->start != 0 || extent_map_end(em) != SZ_1K ||
208e71f2e17SDavid Sterba 	     em->block_start != EXTENT_MAP_INLINE || em->block_len != (u64)-1)) {
2093c7251f2SDavid Sterba 		test_err(
2103c7251f2SDavid Sterba "case2 [0 1K]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu",
21172b28077SLiu Bo 			 ret, em->start, em->len, em->block_start,
21272b28077SLiu Bo 			 em->block_len);
213e71f2e17SDavid Sterba 		ret = -EINVAL;
214e71f2e17SDavid Sterba 	}
21572b28077SLiu Bo 	free_extent_map(em);
21672b28077SLiu Bo out:
21772b28077SLiu Bo 	free_extent_map_tree(em_tree);
218488f6730SDavid Sterba 
219488f6730SDavid Sterba 	return ret;
22072b28077SLiu Bo }
22172b28077SLiu Bo 
222488f6730SDavid Sterba static int __test_case_3(struct btrfs_fs_info *fs_info,
2230e08eb9bSDavid Sterba 		struct extent_map_tree *em_tree, u64 start)
224fd87526fSLiu Bo {
225fd87526fSLiu Bo 	struct extent_map *em;
226fd87526fSLiu Bo 	u64 len = SZ_4K;
227fd87526fSLiu Bo 	int ret;
228fd87526fSLiu Bo 
229fd87526fSLiu Bo 	em = alloc_extent_map();
2306c304746SDavid Sterba 	if (!em) {
2316c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
232488f6730SDavid Sterba 		return -ENOMEM;
2336c304746SDavid Sterba 	}
234fd87526fSLiu Bo 
235fd87526fSLiu Bo 	/* Add [4K, 8K) */
236fd87526fSLiu Bo 	em->start = SZ_4K;
237fd87526fSLiu Bo 	em->len = SZ_4K;
238fd87526fSLiu Bo 	em->block_start = SZ_4K;
239fd87526fSLiu Bo 	em->block_len = SZ_4K;
240*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
241fd87526fSLiu Bo 	ret = add_extent_mapping(em_tree, em, 0);
242*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
243992dce74SDavid Sterba 	if (ret < 0) {
244992dce74SDavid Sterba 		test_err("cannot add extent range [4K, 8K)");
245992dce74SDavid Sterba 		goto out;
246992dce74SDavid Sterba 	}
247fd87526fSLiu Bo 	free_extent_map(em);
248fd87526fSLiu Bo 
249fd87526fSLiu Bo 	em = alloc_extent_map();
250488f6730SDavid Sterba 	if (!em) {
2516c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
252488f6730SDavid Sterba 		ret = -ENOMEM;
253fd87526fSLiu Bo 		goto out;
254488f6730SDavid Sterba 	}
255fd87526fSLiu Bo 
256fd87526fSLiu Bo 	/* Add [0, 16K) */
257fd87526fSLiu Bo 	em->start = 0;
258fd87526fSLiu Bo 	em->len = SZ_16K;
259fd87526fSLiu Bo 	em->block_start = 0;
260fd87526fSLiu Bo 	em->block_len = SZ_16K;
261*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
262f46b24c9SDavid Sterba 	ret = btrfs_add_extent_mapping(fs_info, em_tree, &em, start, len);
263*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
264992dce74SDavid Sterba 	if (ret) {
2653c7251f2SDavid Sterba 		test_err("case3 [0x%llx 0x%llx): ret %d",
266fd87526fSLiu Bo 			 start, start + len, ret);
267992dce74SDavid Sterba 		goto out;
268992dce74SDavid Sterba 	}
269fd87526fSLiu Bo 	/*
270fd87526fSLiu Bo 	 * Since bytes within em are contiguous, em->block_start is identical to
271fd87526fSLiu Bo 	 * em->start.
272fd87526fSLiu Bo 	 */
273fd87526fSLiu Bo 	if (em &&
274fd87526fSLiu Bo 	    (start < em->start || start + len > extent_map_end(em) ||
275992dce74SDavid Sterba 	     em->start != em->block_start || em->len != em->block_len)) {
2763c7251f2SDavid Sterba 		test_err(
2773c7251f2SDavid Sterba "case3 [0x%llx 0x%llx): ret %d em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx)",
278fd87526fSLiu Bo 			 start, start + len, ret, em->start, em->len,
279fd87526fSLiu Bo 			 em->block_start, em->block_len);
280992dce74SDavid Sterba 		ret = -EINVAL;
281992dce74SDavid Sterba 	}
282fd87526fSLiu Bo 	free_extent_map(em);
283fd87526fSLiu Bo out:
284fd87526fSLiu Bo 	free_extent_map_tree(em_tree);
285488f6730SDavid Sterba 
286488f6730SDavid Sterba 	return ret;
287fd87526fSLiu Bo }
288fd87526fSLiu Bo 
289fd87526fSLiu Bo /*
290fd87526fSLiu Bo  * Test scenario:
291fd87526fSLiu Bo  *
292fd87526fSLiu Bo  * Suppose that no extent map has been loaded into memory yet.
293fd87526fSLiu Bo  * There is a file extent [0, 16K), two jobs are running concurrently
294fd87526fSLiu Bo  * against it, t1 is buffered writing to [4K, 8K) and t2 is doing dio
295fd87526fSLiu Bo  * read from [0, 4K) or [8K, 12K) or [12K, 16K).
296fd87526fSLiu Bo  *
297fd87526fSLiu Bo  * t1 goes ahead of t2 and adds em [4K, 8K) into tree.
298fd87526fSLiu Bo  *
299fd87526fSLiu Bo  *         t1                       t2
300fd87526fSLiu Bo  *  cow_file_range()	     btrfs_get_extent()
301fd87526fSLiu Bo  *                            -> lookup_extent_mapping()
302fd87526fSLiu Bo  *   -> add_extent_mapping()
303fd87526fSLiu Bo  *                            -> add_extent_mapping()
304fd87526fSLiu Bo  */
305ccfada1fSDavid Sterba static int test_case_3(struct btrfs_fs_info *fs_info,
3060e08eb9bSDavid Sterba 		struct extent_map_tree *em_tree)
307fd87526fSLiu Bo {
308ccfada1fSDavid Sterba 	int ret;
309ccfada1fSDavid Sterba 
310ccfada1fSDavid Sterba 	ret = __test_case_3(fs_info, em_tree, 0);
311ccfada1fSDavid Sterba 	if (ret)
312ccfada1fSDavid Sterba 		return ret;
313ccfada1fSDavid Sterba 	ret = __test_case_3(fs_info, em_tree, SZ_8K);
314ccfada1fSDavid Sterba 	if (ret)
315ccfada1fSDavid Sterba 		return ret;
31643f7cddcSDavid Sterba 	ret = __test_case_3(fs_info, em_tree, (12 * SZ_1K));
317ccfada1fSDavid Sterba 
318ccfada1fSDavid Sterba 	return ret;
319fd87526fSLiu Bo }
320fd87526fSLiu Bo 
321488f6730SDavid Sterba static int __test_case_4(struct btrfs_fs_info *fs_info,
3220e08eb9bSDavid Sterba 		struct extent_map_tree *em_tree, u64 start)
323cd77f4f8SLiu Bo {
324cd77f4f8SLiu Bo 	struct extent_map *em;
325cd77f4f8SLiu Bo 	u64 len = SZ_4K;
326cd77f4f8SLiu Bo 	int ret;
327cd77f4f8SLiu Bo 
328cd77f4f8SLiu Bo 	em = alloc_extent_map();
3296c304746SDavid Sterba 	if (!em) {
3306c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
331488f6730SDavid Sterba 		return -ENOMEM;
3326c304746SDavid Sterba 	}
333cd77f4f8SLiu Bo 
334cd77f4f8SLiu Bo 	/* Add [0K, 8K) */
335cd77f4f8SLiu Bo 	em->start = 0;
336cd77f4f8SLiu Bo 	em->len = SZ_8K;
337cd77f4f8SLiu Bo 	em->block_start = 0;
338cd77f4f8SLiu Bo 	em->block_len = SZ_8K;
339*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
340cd77f4f8SLiu Bo 	ret = add_extent_mapping(em_tree, em, 0);
341*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
3427c6f6700SDavid Sterba 	if (ret < 0) {
3437c6f6700SDavid Sterba 		test_err("cannot add extent range [0, 8K)");
3447c6f6700SDavid Sterba 		goto out;
3457c6f6700SDavid Sterba 	}
346cd77f4f8SLiu Bo 	free_extent_map(em);
347cd77f4f8SLiu Bo 
348cd77f4f8SLiu Bo 	em = alloc_extent_map();
349488f6730SDavid Sterba 	if (!em) {
3506c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
351488f6730SDavid Sterba 		ret = -ENOMEM;
352cd77f4f8SLiu Bo 		goto out;
353488f6730SDavid Sterba 	}
354cd77f4f8SLiu Bo 
3553173fd92SDavid Sterba 	/* Add [8K, 32K) */
356cd77f4f8SLiu Bo 	em->start = SZ_8K;
35743f7cddcSDavid Sterba 	em->len = 24 * SZ_1K;
358cd77f4f8SLiu Bo 	em->block_start = SZ_16K; /* avoid merging */
35943f7cddcSDavid Sterba 	em->block_len = 24 * SZ_1K;
360*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
361cd77f4f8SLiu Bo 	ret = add_extent_mapping(em_tree, em, 0);
362*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
3637c6f6700SDavid Sterba 	if (ret < 0) {
3647c6f6700SDavid Sterba 		test_err("cannot add extent range [8K, 32K)");
3657c6f6700SDavid Sterba 		goto out;
3667c6f6700SDavid Sterba 	}
367cd77f4f8SLiu Bo 	free_extent_map(em);
368cd77f4f8SLiu Bo 
369cd77f4f8SLiu Bo 	em = alloc_extent_map();
370488f6730SDavid Sterba 	if (!em) {
3716c304746SDavid Sterba 		test_std_err(TEST_ALLOC_EXTENT_MAP);
372488f6730SDavid Sterba 		ret = -ENOMEM;
373cd77f4f8SLiu Bo 		goto out;
374488f6730SDavid Sterba 	}
375cd77f4f8SLiu Bo 	/* Add [0K, 32K) */
376cd77f4f8SLiu Bo 	em->start = 0;
377cd77f4f8SLiu Bo 	em->len = SZ_32K;
378cd77f4f8SLiu Bo 	em->block_start = 0;
379cd77f4f8SLiu Bo 	em->block_len = SZ_32K;
380*93ead46bSDavid Sterba 	write_lock(&em_tree->lock);
381f46b24c9SDavid Sterba 	ret = btrfs_add_extent_mapping(fs_info, em_tree, &em, start, len);
382*93ead46bSDavid Sterba 	write_unlock(&em_tree->lock);
3837c6f6700SDavid Sterba 	if (ret) {
3843c7251f2SDavid Sterba 		test_err("case4 [0x%llx 0x%llx): ret %d",
385cd77f4f8SLiu Bo 			 start, len, ret);
3867c6f6700SDavid Sterba 		goto out;
3877c6f6700SDavid Sterba 	}
3887c6f6700SDavid Sterba 	if (em && (start < em->start || start + len > extent_map_end(em))) {
3893c7251f2SDavid Sterba 		test_err(
3903c7251f2SDavid Sterba "case4 [0x%llx 0x%llx): ret %d, added wrong em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx)",
391cd77f4f8SLiu Bo 			 start, len, ret, em->start, em->len, em->block_start,
392cd77f4f8SLiu Bo 			 em->block_len);
3937c6f6700SDavid Sterba 		ret = -EINVAL;
3947c6f6700SDavid Sterba 	}
395cd77f4f8SLiu Bo 	free_extent_map(em);
396cd77f4f8SLiu Bo out:
397cd77f4f8SLiu Bo 	free_extent_map_tree(em_tree);
398488f6730SDavid Sterba 
399488f6730SDavid Sterba 	return ret;
400cd77f4f8SLiu Bo }
401cd77f4f8SLiu Bo 
402cd77f4f8SLiu Bo /*
403cd77f4f8SLiu Bo  * Test scenario:
404cd77f4f8SLiu Bo  *
405cd77f4f8SLiu Bo  * Suppose that no extent map has been loaded into memory yet.
406cd77f4f8SLiu Bo  * There is a file extent [0, 32K), two jobs are running concurrently
407cd77f4f8SLiu Bo  * against it, t1 is doing dio write to [8K, 32K) and t2 is doing dio
408cd77f4f8SLiu Bo  * read from [0, 4K) or [4K, 8K).
409cd77f4f8SLiu Bo  *
410cd77f4f8SLiu Bo  * t1 goes ahead of t2 and splits em [0, 32K) to em [0K, 8K) and [8K 32K).
411cd77f4f8SLiu Bo  *
412cd77f4f8SLiu Bo  *         t1                                t2
413cd77f4f8SLiu Bo  *  btrfs_get_blocks_direct()	       btrfs_get_blocks_direct()
414cd77f4f8SLiu Bo  *   -> btrfs_get_extent()              -> btrfs_get_extent()
415cd77f4f8SLiu Bo  *       -> lookup_extent_mapping()
416cd77f4f8SLiu Bo  *       -> add_extent_mapping()            -> lookup_extent_mapping()
417cd77f4f8SLiu Bo  *          # load [0, 32K)
418cd77f4f8SLiu Bo  *   -> btrfs_new_extent_direct()
419cd77f4f8SLiu Bo  *       -> btrfs_drop_extent_cache()
420cd77f4f8SLiu Bo  *          # split [0, 32K)
421cd77f4f8SLiu Bo  *       -> add_extent_mapping()
422cd77f4f8SLiu Bo  *          # add [8K, 32K)
423cd77f4f8SLiu Bo  *                                          -> add_extent_mapping()
424cd77f4f8SLiu Bo  *                                             # handle -EEXIST when adding
425cd77f4f8SLiu Bo  *                                             # [0, 32K)
426cd77f4f8SLiu Bo  */
427ccfada1fSDavid Sterba static int test_case_4(struct btrfs_fs_info *fs_info,
4280e08eb9bSDavid Sterba 		struct extent_map_tree *em_tree)
429cd77f4f8SLiu Bo {
430ccfada1fSDavid Sterba 	int ret;
431ccfada1fSDavid Sterba 
432ccfada1fSDavid Sterba 	ret = __test_case_4(fs_info, em_tree, 0);
433ccfada1fSDavid Sterba 	if (ret)
434ccfada1fSDavid Sterba 		return ret;
435ccfada1fSDavid Sterba 	ret = __test_case_4(fs_info, em_tree, SZ_4K);
436ccfada1fSDavid Sterba 
437ccfada1fSDavid Sterba 	return ret;
438cd77f4f8SLiu Bo }
439cd77f4f8SLiu Bo 
44097dc231eSColin Ian King int btrfs_test_extent_map(void)
44172b28077SLiu Bo {
4420e08eb9bSDavid Sterba 	struct btrfs_fs_info *fs_info = NULL;
44372b28077SLiu Bo 	struct extent_map_tree *em_tree;
444488f6730SDavid Sterba 	int ret = 0;
44572b28077SLiu Bo 
446315b76b4SDavid Sterba 	test_msg("running extent_map tests");
44772b28077SLiu Bo 
4480e08eb9bSDavid Sterba 	/*
4490e08eb9bSDavid Sterba 	 * Note: the fs_info is not set up completely, we only need
4500e08eb9bSDavid Sterba 	 * fs_info::fsid for the tracepoint.
4510e08eb9bSDavid Sterba 	 */
4520e08eb9bSDavid Sterba 	fs_info = btrfs_alloc_dummy_fs_info(PAGE_SIZE, PAGE_SIZE);
4530e08eb9bSDavid Sterba 	if (!fs_info) {
45437b2a7bcSDavid Sterba 		test_std_err(TEST_ALLOC_FS_INFO);
4550e08eb9bSDavid Sterba 		return -ENOMEM;
4560e08eb9bSDavid Sterba 	}
4570e08eb9bSDavid Sterba 
45872b28077SLiu Bo 	em_tree = kzalloc(sizeof(*em_tree), GFP_KERNEL);
459488f6730SDavid Sterba 	if (!em_tree) {
460488f6730SDavid Sterba 		ret = -ENOMEM;
4610e08eb9bSDavid Sterba 		goto out;
462488f6730SDavid Sterba 	}
46372b28077SLiu Bo 
46472b28077SLiu Bo 	extent_map_tree_init(em_tree);
46572b28077SLiu Bo 
466ccfada1fSDavid Sterba 	ret = test_case_1(fs_info, em_tree);
467ccfada1fSDavid Sterba 	if (ret)
468ccfada1fSDavid Sterba 		goto out;
469ccfada1fSDavid Sterba 	ret = test_case_2(fs_info, em_tree);
470ccfada1fSDavid Sterba 	if (ret)
471ccfada1fSDavid Sterba 		goto out;
472ccfada1fSDavid Sterba 	ret = test_case_3(fs_info, em_tree);
473ccfada1fSDavid Sterba 	if (ret)
474ccfada1fSDavid Sterba 		goto out;
475ccfada1fSDavid Sterba 	ret = test_case_4(fs_info, em_tree);
47672b28077SLiu Bo 
4770e08eb9bSDavid Sterba out:
478ccfada1fSDavid Sterba 	kfree(em_tree);
4790e08eb9bSDavid Sterba 	btrfs_free_dummy_fs_info(fs_info);
4800e08eb9bSDavid Sterba 
481488f6730SDavid Sterba 	return ret;
48272b28077SLiu Bo }
483