aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-10-09 12:00:56 -0400
committerChris Mason <chris.mason@fusionio.com>2013-11-11 21:56:51 -0500
commit294e30fee35d3151d100cfe59e839c2dbc16a374 (patch)
tree3906ec3d135ec29dc7700f8e35e494909b210848 /fs/btrfs
parent857cc2fc29cfaf4ee98fe9967bbf6a3942191136 (diff)
Btrfs: add tests for find_lock_delalloc_range
So both Liu and I made huge messes of find_lock_delalloc_range trying to fix stuff, me first by fixing extent size, then him by fixing something I broke and then me again telling him to fix it a different way. So this is obviously a candidate for some testing. This patch adds a pseudo fs so we can allocate fake inodes for tests that need an inode or pages. Then it addes a bunch of tests to make sure find_lock_delalloc_range is acting the way it is supposed to. With this patch and all of our previous patches to find_lock_delalloc_range I am sure it is working as expected now. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/Makefile2
-rw-r--r--fs/btrfs/ctree.h6
-rw-r--r--fs/btrfs/extent_io.c9
-rw-r--r--fs/btrfs/extent_io.h6
-rw-r--r--fs/btrfs/super.c14
-rw-r--r--fs/btrfs/tests/btrfs-tests.c68
-rw-r--r--fs/btrfs/tests/btrfs-tests.h15
-rw-r--r--fs/btrfs/tests/extent-io-tests.c276
8 files changed, 388 insertions, 8 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 4c7dfbfaa3b3..cac4f2d001a3 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -15,4 +15,4 @@ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
15btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o 15btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
16 16
17btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \ 17btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \
18 tests/extent-buffer-tests.o 18 tests/extent-buffer-tests.o tests/btrfs-tests.o tests/extent-io-tests.o
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 9ca15a84cc5a..2f398062b942 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -47,6 +47,12 @@ extern struct kmem_cache *btrfs_path_cachep;
47extern struct kmem_cache *btrfs_free_space_cachep; 47extern struct kmem_cache *btrfs_free_space_cachep;
48struct btrfs_ordered_sum; 48struct btrfs_ordered_sum;
49 49
50#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
51#define STATIC noinline
52#else
53#define STATIC static noinline
54#endif
55
50#define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */ 56#define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */
51 57
52#define BTRFS_MAX_MIRRORS 3 58#define BTRFS_MAX_MIRRORS 3
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 2bf6f46fae44..c10291cc4fd1 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -1598,11 +1598,10 @@ done:
1598 * 1598 *
1599 * 1 is returned if we find something, 0 if nothing was in the tree 1599 * 1 is returned if we find something, 0 if nothing was in the tree
1600 */ 1600 */
1601static noinline u64 find_lock_delalloc_range(struct inode *inode, 1601STATIC u64 find_lock_delalloc_range(struct inode *inode,
1602 struct extent_io_tree *tree, 1602 struct extent_io_tree *tree,
1603 struct page *locked_page, 1603 struct page *locked_page, u64 *start,
1604 u64 *start, u64 *end, 1604 u64 *end, u64 max_bytes)
1605 u64 max_bytes)
1606{ 1605{
1607 u64 delalloc_start; 1606 u64 delalloc_start;
1608 u64 delalloc_end; 1607 u64 delalloc_end;
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 6dbc645f1f3d..f98602eac808 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -345,4 +345,10 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start,
345int end_extent_writepage(struct page *page, int err, u64 start, u64 end); 345int end_extent_writepage(struct page *page, int err, u64 start, u64 end);
346int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb, 346int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
347 int mirror_num); 347 int mirror_num);
348#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
349noinline u64 find_lock_delalloc_range(struct inode *inode,
350 struct extent_io_tree *tree,
351 struct page *locked_page, u64 *start,
352 u64 *end, u64 max_bytes);
353#endif
348#endif 354#endif
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 0e398657d759..78041e34d15a 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1791,10 +1791,20 @@ static int btrfs_run_sanity_tests(void)
1791{ 1791{
1792 int ret; 1792 int ret;
1793 1793
1794 ret = btrfs_test_free_space_cache(); 1794 ret = btrfs_init_test_fs();
1795 if (ret) 1795 if (ret)
1796 return ret; 1796 return ret;
1797 return btrfs_test_extent_buffer_operations(); 1797
1798 ret = btrfs_test_free_space_cache();
1799 if (ret)
1800 goto out;
1801 ret = btrfs_test_extent_buffer_operations();
1802 if (ret)
1803 goto out;
1804 ret = btrfs_test_extent_io();
1805out:
1806 btrfs_destroy_test_fs();
1807 return ret;
1798} 1808}
1799 1809
1800static int __init init_btrfs_fs(void) 1810static int __init init_btrfs_fs(void)
diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c
new file mode 100644
index 000000000000..697d527377c1
--- /dev/null
+++ b/fs/btrfs/tests/btrfs-tests.c
@@ -0,0 +1,68 @@
1/*
2 * Copyright (C) 2013 Fusion IO. 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/fs.h>
20#include <linux/mount.h>
21#include <linux/magic.h>
22#include "btrfs-tests.h"
23#include "../ctree.h"
24
25static struct vfsmount *test_mnt = NULL;
26
27static struct dentry *btrfs_test_mount(struct file_system_type *fs_type,
28 int flags, const char *dev_name,
29 void *data)
30{
31 return mount_pseudo(fs_type, "btrfs_test:", NULL, NULL, BTRFS_TEST_MAGIC);
32}
33
34static struct file_system_type test_type = {
35 .name = "btrfs_test_fs",
36 .mount = btrfs_test_mount,
37 .kill_sb = kill_anon_super,
38};
39
40struct inode *btrfs_new_test_inode(void)
41{
42 return new_inode(test_mnt->mnt_sb);
43}
44
45int btrfs_init_test_fs(void)
46{
47 int ret;
48
49 ret = register_filesystem(&test_type);
50 if (ret) {
51 printk(KERN_ERR "btrfs: cannot register test file system\n");
52 return ret;
53 }
54
55 test_mnt = kern_mount(&test_type);
56 if (IS_ERR(test_mnt)) {
57 printk(KERN_ERR "btrfs: cannot mount test file system\n");
58 unregister_filesystem(&test_type);
59 return ret;
60 }
61 return 0;
62}
63
64void btrfs_destroy_test_fs(void)
65{
66 kern_unmount(test_mnt);
67 unregister_filesystem(&test_type);
68}
diff --git a/fs/btrfs/tests/btrfs-tests.h b/fs/btrfs/tests/btrfs-tests.h
index 04f2cd2ca568..e935fe5291e7 100644
--- a/fs/btrfs/tests/btrfs-tests.h
+++ b/fs/btrfs/tests/btrfs-tests.h
@@ -25,6 +25,10 @@
25 25
26int btrfs_test_free_space_cache(void); 26int btrfs_test_free_space_cache(void);
27int btrfs_test_extent_buffer_operations(void); 27int btrfs_test_extent_buffer_operations(void);
28int btrfs_test_extent_io(void);
29int btrfs_init_test_fs(void);
30void btrfs_destroy_test_fs(void);
31struct inode *btrfs_new_test_inode(void);
28#else 32#else
29static inline int btrfs_test_free_space_cache(void) 33static inline int btrfs_test_free_space_cache(void)
30{ 34{
@@ -34,6 +38,17 @@ static inline int btrfs_test_extent_buffer_operations(void)
34{ 38{
35 return 0; 39 return 0;
36} 40}
41static inline int btrfs_init_test_fs(void)
42{
43 return 0;
44}
45static inline void btrfs_destroy_test_fs(void)
46{
47}
48static inline int btrfs_test_extent_io(void)
49{
50 return 0;
51}
37#endif 52#endif
38 53
39#endif 54#endif
diff --git a/fs/btrfs/tests/extent-io-tests.c b/fs/btrfs/tests/extent-io-tests.c
new file mode 100644
index 000000000000..7e99c2f98dd0
--- /dev/null
+++ b/fs/btrfs/tests/extent-io-tests.c
@@ -0,0 +1,276 @@
1/*
2 * Copyright (C) 2013 Fusion IO. 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/pagemap.h>
20#include <linux/sched.h>
21#include "btrfs-tests.h"
22#include "../extent_io.h"
23
24#define PROCESS_UNLOCK (1 << 0)
25#define PROCESS_RELEASE (1 << 1)
26#define PROCESS_TEST_LOCKED (1 << 2)
27
28static noinline int process_page_range(struct inode *inode, u64 start, u64 end,
29 unsigned long flags)
30{
31 int ret;
32 struct page *pages[16];
33 unsigned long index = start >> PAGE_CACHE_SHIFT;
34 unsigned long end_index = end >> PAGE_CACHE_SHIFT;
35 unsigned long nr_pages = end_index - index + 1;
36 int i;
37 int count = 0;
38 int loops = 0;
39
40 while (nr_pages > 0) {
41 ret = find_get_pages_contig(inode->i_mapping, index,
42 min_t(unsigned long, nr_pages,
43 ARRAY_SIZE(pages)), pages);
44 for (i = 0; i < ret; i++) {
45 if (flags & PROCESS_TEST_LOCKED &&
46 !PageLocked(pages[i]))
47 count++;
48 if (flags & PROCESS_UNLOCK && PageLocked(pages[i]))
49 unlock_page(pages[i]);
50 page_cache_release(pages[i]);
51 if (flags & PROCESS_RELEASE)
52 page_cache_release(pages[i]);
53 }
54 nr_pages -= ret;
55 index += ret;
56 cond_resched();
57 loops++;
58 if (loops > 100000) {
59 printk(KERN_ERR "stuck in a loop, start %Lu, end %Lu, nr_pages %lu, ret %d\n", start, end, nr_pages, ret);
60 break;
61 }
62 }
63 return count;
64}
65
66static int test_find_delalloc(void)
67{
68 struct inode *inode;
69 struct extent_io_tree tmp;
70 struct page *page;
71 struct page *locked_page = NULL;
72 unsigned long index = 0;
73 u64 total_dirty = 256 * 1024 * 1024;
74 u64 max_bytes = 128 * 1024 * 1024;
75 u64 start, end, test_start;
76 u64 found;
77 int ret = -EINVAL;
78
79 inode = btrfs_new_test_inode();
80 if (!inode) {
81 test_msg("Failed to allocate test inode\n");
82 return -ENOMEM;
83 }
84
85 extent_io_tree_init(&tmp, &inode->i_data);
86
87 /*
88 * First go through and create and mark all of our pages dirty, we pin
89 * everything to make sure our pages don't get evicted and screw up our
90 * test.
91 */
92 for (index = 0; index < (total_dirty >> PAGE_CACHE_SHIFT); index++) {
93 page = find_or_create_page(inode->i_mapping, index, GFP_NOFS);
94 if (!page) {
95 test_msg("Failed to allocate test page\n");
96 ret = -ENOMEM;
97 goto out;
98 }
99 SetPageDirty(page);
100 if (index) {
101 unlock_page(page);
102 } else {
103 page_cache_get(page);
104 locked_page = page;
105 }
106 }
107
108 /* Test this scenario
109 * |--- delalloc ---|
110 * |--- search ---|
111 */
112 set_extent_delalloc(&tmp, 0, 4095, NULL, GFP_NOFS);
113 start = 0;
114 end = 0;
115 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
116 &end, max_bytes);
117 if (!found) {
118 test_msg("Should have found at least one delalloc\n");
119 goto out_bits;
120 }
121 if (start != 0 || end != 4095) {
122 test_msg("Expected start 0 end 4095, got start %Lu end %Lu\n",
123 start, end);
124 goto out_bits;
125 }
126 unlock_extent(&tmp, start, end);
127 unlock_page(locked_page);
128 page_cache_release(locked_page);
129
130 /*
131 * Test this scenario
132 *
133 * |--- delalloc ---|
134 * |--- search ---|
135 */
136 test_start = 64 * 1024 * 1024;
137 locked_page = find_lock_page(inode->i_mapping,
138 test_start >> PAGE_CACHE_SHIFT);
139 if (!locked_page) {
140 test_msg("Couldn't find the locked page\n");
141 goto out_bits;
142 }
143 set_extent_delalloc(&tmp, 4096, max_bytes - 1, NULL, GFP_NOFS);
144 start = test_start;
145 end = 0;
146 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
147 &end, max_bytes);
148 if (!found) {
149 test_msg("Couldn't find delalloc in our range\n");
150 goto out_bits;
151 }
152 if (start != test_start || end != max_bytes - 1) {
153 test_msg("Expected start %Lu end %Lu, got start %Lu, end "
154 "%Lu\n", test_start, max_bytes - 1, start, end);
155 goto out_bits;
156 }
157 if (process_page_range(inode, start, end,
158 PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) {
159 test_msg("There were unlocked pages in the range\n");
160 goto out_bits;
161 }
162 unlock_extent(&tmp, start, end);
163 /* locked_page was unlocked above */
164 page_cache_release(locked_page);
165
166 /*
167 * Test this scenario
168 * |--- delalloc ---|
169 * |--- search ---|
170 */
171 test_start = max_bytes + 4096;
172 locked_page = find_lock_page(inode->i_mapping, test_start >>
173 PAGE_CACHE_SHIFT);
174 if (!locked_page) {
175 test_msg("Could'nt find the locked page\n");
176 goto out_bits;
177 }
178 start = test_start;
179 end = 0;
180 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
181 &end, max_bytes);
182 if (found) {
183 test_msg("Found range when we shouldn't have\n");
184 goto out_bits;
185 }
186 if (end != (u64)-1) {
187 test_msg("Did not return the proper end offset\n");
188 goto out_bits;
189 }
190
191 /*
192 * Test this scenario
193 * [------- delalloc -------|
194 * [max_bytes]|-- search--|
195 *
196 * We are re-using our test_start from above since it works out well.
197 */
198 set_extent_delalloc(&tmp, max_bytes, total_dirty - 1, NULL, GFP_NOFS);
199 start = test_start;
200 end = 0;
201 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
202 &end, max_bytes);
203 if (!found) {
204 test_msg("Didn't find our range\n");
205 goto out_bits;
206 }
207 if (start != test_start || end != total_dirty - 1) {
208 test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n",
209 test_start, total_dirty - 1, start, end);
210 goto out_bits;
211 }
212 if (process_page_range(inode, start, end,
213 PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) {
214 test_msg("Pages in range were not all locked\n");
215 goto out_bits;
216 }
217 unlock_extent(&tmp, start, end);
218
219 /*
220 * Now to test where we run into a page that is no longer dirty in the
221 * range we want to find.
222 */
223 page = find_get_page(inode->i_mapping, (max_bytes + (1 * 1024 * 1024))
224 >> PAGE_CACHE_SHIFT);
225 if (!page) {
226 test_msg("Couldn't find our page\n");
227 goto out_bits;
228 }
229 ClearPageDirty(page);
230 page_cache_release(page);
231
232 /* We unlocked it in the previous test */
233 lock_page(locked_page);
234 start = test_start;
235 end = 0;
236 /*
237 * Currently if we fail to find dirty pages in the delalloc range we
238 * will adjust max_bytes down to PAGE_CACHE_SIZE and then re-search. If
239 * this changes at any point in the future we will need to fix this
240 * tests expected behavior.
241 */
242 found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
243 &end, max_bytes);
244 if (!found) {
245 test_msg("Didn't find our range\n");
246 goto out_bits;
247 }
248 if (start != test_start && end != test_start + PAGE_CACHE_SIZE - 1) {
249 test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n",
250 test_start, test_start + PAGE_CACHE_SIZE - 1, start,
251 end);
252 goto out_bits;
253 }
254 if (process_page_range(inode, start, end, PROCESS_TEST_LOCKED |
255 PROCESS_UNLOCK)) {
256 test_msg("Pages in range were not all locked\n");
257 goto out_bits;
258 }
259 ret = 0;
260out_bits:
261 clear_extent_bits(&tmp, 0, total_dirty - 1,
262 (unsigned long)-1, GFP_NOFS);
263out:
264 if (locked_page)
265 page_cache_release(locked_page);
266 process_page_range(inode, 0, total_dirty - 1,
267 PROCESS_UNLOCK | PROCESS_RELEASE);
268 iput(inode);
269 return ret;
270}
271
272int btrfs_test_extent_io(void)
273{
274 test_msg("Running find delalloc tests\n");
275 return test_find_delalloc();
276}