summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiu Bo <bo.li.liu@oracle.com>2018-01-05 14:51:12 -0500
committerDavid Sterba <dsterba@suse.com>2018-01-22 10:08:22 -0500
commit72b28077a20a6a1f14494602466c219241f45d89 (patch)
tree9cf7c34853eeb564ff4f906a9d1d4a48ef562975
parentc04e61b5e41b0e8ace4aa4b67685fbe68ac37a46 (diff)
Btrfs: add extent map selftests
We've observed that btrfs_get_extent() and merge_extent_mapping() could return -EEXIST in several cases, and they are caused by some racy condition, e.g dio read vs dio write, which makes the problem very tricky to reproduce. This adds extent map selftests in order to simulate those racy situations. Signed-off-by: Liu Bo <bo.li.liu@oracle.com> Reviewed-by: Josef Bacik <jbacik@fb.com> [ minor string adjustments ] Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/Makefile2
-rw-r--r--fs/btrfs/tests/btrfs-tests.c3
-rw-r--r--fs/btrfs/tests/btrfs-tests.h1
-rw-r--r--fs/btrfs/tests/extent-map-tests.c203
4 files changed, 208 insertions, 1 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 6fe881d5cb38..0c4373628eb4 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -19,4 +19,4 @@ btrfs-$(CONFIG_BTRFS_FS_REF_VERIFY) += ref-verify.o
19btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \ 19btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \
20 tests/extent-buffer-tests.o tests/btrfs-tests.o \ 20 tests/extent-buffer-tests.o tests/btrfs-tests.o \
21 tests/extent-io-tests.o tests/inode-tests.o tests/qgroup-tests.o \ 21 tests/extent-io-tests.o tests/inode-tests.o tests/qgroup-tests.o \
22 tests/free-space-tree-tests.o 22 tests/free-space-tree-tests.o tests/extent-map-tests.o
diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c
index d3f25376a0f8..9786d8cd0aa6 100644
--- a/fs/btrfs/tests/btrfs-tests.c
+++ b/fs/btrfs/tests/btrfs-tests.c
@@ -277,6 +277,9 @@ int btrfs_run_sanity_tests(void)
277 goto out; 277 goto out;
278 } 278 }
279 } 279 }
280 ret = btrfs_test_extent_map();
281 if (ret)
282 goto out;
280out: 283out:
281 btrfs_destroy_test_fs(); 284 btrfs_destroy_test_fs();
282 return ret; 285 return ret;
diff --git a/fs/btrfs/tests/btrfs-tests.h b/fs/btrfs/tests/btrfs-tests.h
index 266f1e3d1784..bc0615bac3cc 100644
--- a/fs/btrfs/tests/btrfs-tests.h
+++ b/fs/btrfs/tests/btrfs-tests.h
@@ -33,6 +33,7 @@ int btrfs_test_extent_io(u32 sectorsize, u32 nodesize);
33int btrfs_test_inodes(u32 sectorsize, u32 nodesize); 33int btrfs_test_inodes(u32 sectorsize, u32 nodesize);
34int btrfs_test_qgroups(u32 sectorsize, u32 nodesize); 34int btrfs_test_qgroups(u32 sectorsize, u32 nodesize);
35int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize); 35int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize);
36int btrfs_test_extent_map(void);
36struct inode *btrfs_new_test_inode(void); 37struct inode *btrfs_new_test_inode(void);
37struct btrfs_fs_info *btrfs_alloc_dummy_fs_info(u32 nodesize, u32 sectorsize); 38struct btrfs_fs_info *btrfs_alloc_dummy_fs_info(u32 nodesize, u32 sectorsize);
38void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info); 39void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info);
diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c
new file mode 100644
index 000000000000..e6f0dd18392e
--- /dev/null
+++ b/fs/btrfs/tests/extent-map-tests.c
@@ -0,0 +1,203 @@
1/*
2 * Copyright (C) 2017 Oracle. 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 <linux/types.h>
20#include "btrfs-tests.h"
21#include "../ctree.h"
22
23static void free_extent_map_tree(struct extent_map_tree *em_tree)
24{
25 struct extent_map *em;
26 struct rb_node *node;
27
28 while (!RB_EMPTY_ROOT(&em_tree->map)) {
29 node = rb_first(&em_tree->map);
30 em = rb_entry(node, struct extent_map, rb_node);
31 remove_extent_mapping(em_tree, em);
32
33#ifdef CONFIG_BTRFS_DEBUG
34 if (refcount_read(&em->refs) != 1) {
35 test_msg(
36"em leak: em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx) refs %d\n",
37 em->start, em->len, em->block_start,
38 em->block_len, refcount_read(&em->refs));
39
40 refcount_set(&em->refs, 1);
41 }
42#endif
43 free_extent_map(em);
44 }
45}
46
47/*
48 * Test scenario:
49 *
50 * Suppose that no extent map has been loaded into memory yet, there is a file
51 * extent [0, 16K), followed by another file extent [16K, 20K), two dio reads
52 * are entering btrfs_get_extent() concurrently, t1 is reading [8K, 16K), t2 is
53 * reading [0, 8K)
54 *
55 * t1 t2
56 * btrfs_get_extent() btrfs_get_extent()
57 * -> lookup_extent_mapping() ->lookup_extent_mapping()
58 * -> add_extent_mapping(0, 16K)
59 * -> return em
60 * ->add_extent_mapping(0, 16K)
61 * -> #handle -EEXIST
62 */
63static void test_case_1(struct extent_map_tree *em_tree)
64{
65 struct extent_map *em;
66 u64 start = 0;
67 u64 len = SZ_8K;
68 int ret;
69
70 em = alloc_extent_map();
71 if (!em)
72 /* Skip the test on error. */
73 return;
74
75 /* Add [0, 16K) */
76 em->start = 0;
77 em->len = SZ_16K;
78 em->block_start = 0;
79 em->block_len = SZ_16K;
80 ret = add_extent_mapping(em_tree, em, 0);
81 ASSERT(ret == 0);
82 free_extent_map(em);
83
84 /* Add [16K, 20K) following [0, 16K) */
85 em = alloc_extent_map();
86 if (!em)
87 goto out;
88
89 em->start = SZ_16K;
90 em->len = SZ_4K;
91 em->block_start = SZ_32K; /* avoid merging */
92 em->block_len = SZ_4K;
93 ret = add_extent_mapping(em_tree, em, 0);
94 ASSERT(ret == 0);
95 free_extent_map(em);
96
97 em = alloc_extent_map();
98 if (!em)
99 goto out;
100
101 /* Add [0, 8K), should return [0, 16K) instead. */
102 em->start = start;
103 em->len = len;
104 em->block_start = start;
105 em->block_len = len;
106 ret = btrfs_add_extent_mapping(em_tree, &em, em->start, em->len);
107 if (ret)
108 test_msg("case1 [%llu %llu]: ret %d\n", start, start + len, ret);
109 if (em &&
110 (em->start != 0 || extent_map_end(em) != SZ_16K ||
111 em->block_start != 0 || em->block_len != SZ_16K))
112 test_msg(
113"case1 [%llu %llu]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu\n",
114 start, start + len, ret, em->start, em->len,
115 em->block_start, em->block_len);
116 free_extent_map(em);
117out:
118 /* free memory */
119 free_extent_map_tree(em_tree);
120}
121
122/*
123 * Test scenario:
124 *
125 * Reading the inline ending up with EEXIST, ie. read an inline
126 * extent and discard page cache and read it again.
127 */
128static void test_case_2(struct extent_map_tree *em_tree)
129{
130 struct extent_map *em;
131 int ret;
132
133 em = alloc_extent_map();
134 if (!em)
135 /* Skip the test on error. */
136 return;
137
138 /* Add [0, 1K) */
139 em->start = 0;
140 em->len = SZ_1K;
141 em->block_start = EXTENT_MAP_INLINE;
142 em->block_len = (u64)-1;
143 ret = add_extent_mapping(em_tree, em, 0);
144 ASSERT(ret == 0);
145 free_extent_map(em);
146
147 /* Add [4K, 4K) following [0, 1K) */
148 em = alloc_extent_map();
149 if (!em)
150 goto out;
151
152 em->start = SZ_4K;
153 em->len = SZ_4K;
154 em->block_start = SZ_4K;
155 em->block_len = SZ_4K;
156 ret = add_extent_mapping(em_tree, em, 0);
157 ASSERT(ret == 0);
158 free_extent_map(em);
159
160 em = alloc_extent_map();
161 if (!em)
162 goto out;
163
164 /* Add [0, 1K) */
165 em->start = 0;
166 em->len = SZ_1K;
167 em->block_start = EXTENT_MAP_INLINE;
168 em->block_len = (u64)-1;
169 ret = btrfs_add_extent_mapping(em_tree, &em, em->start, em->len);
170 if (ret)
171 test_msg("case2 [0 1K]: ret %d\n", ret);
172 if (em &&
173 (em->start != 0 || extent_map_end(em) != SZ_1K ||
174 em->block_start != EXTENT_MAP_INLINE || em->block_len != (u64)-1))
175 test_msg(
176"case2 [0 1K]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu\n",
177 ret, em->start, em->len, em->block_start,
178 em->block_len);
179 free_extent_map(em);
180out:
181 /* free memory */
182 free_extent_map_tree(em_tree);
183}
184
185int btrfs_test_extent_map()
186{
187 struct extent_map_tree *em_tree;
188
189 test_msg("Running extent_map tests\n");
190
191 em_tree = kzalloc(sizeof(*em_tree), GFP_KERNEL);
192 if (!em_tree)
193 /* Skip the test on error. */
194 return 0;
195
196 extent_map_tree_init(em_tree);
197
198 test_case_1(em_tree);
199 test_case_2(em_tree);
200
201 kfree(em_tree);
202 return 0;
203}