History

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,

Josef Bacik committed 13 years ago

Commit 294e30f1 parent 857cc2f9 files · +389 −9Browse files

Unified · Split

9 files
  1. fs/btrfs/Makefile +1 −1
  2. fs/btrfs/ctree.h +6 −0
  3. fs/btrfs/extent_io.c +4 −5
  4. fs/btrfs/extent_io.h +6 −0
  5. fs/btrfs/super.c +12 −2
  6. fs/btrfs/tests/btrfs-tests.c +68 −0
  7. fs/btrfs/tests/btrfs-tests.h +15 −0
  8. fs/btrfs/tests/extent-io-tests.c +276 −0
  9. include/uapi/linux/magic.h +1 −1
fs/btrfs/Makefile+1−1
@@ -15,4 +15,4 @@ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o · 14 unmodified lines
1515 btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
1616
1717 btrfs-$(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
fs/btrfs/ctree.h+6−0
@@ -47,6 +47,12 @@ extern struct kmem_cache *btrfs_path_cachep; · 46 unmodified lines
4747 extern struct kmem_cache *btrfs_free_space_cachep;
4848 struct btrfs_ordered_sum;
4949
50+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
51+#define STATIC noinline
52+#else
53+#define STATIC static noinline
54+#endif
55+
5056 #define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */
5157
5258 #define BTRFS_MAX_MIRRORS 3
fs/btrfs/extent_io.c+4−5
@@ -1598,11 +1598,10 @@ done: · 1597 unmodified lines
15981598 *
15991599 * 1 is returned if we find something, 0 if nothing was in the tree
16001600 */
1601−static noinline u64 find_lock_delalloc_range(struct inode *inode,
1602− struct extent_io_tree *tree,
1603− struct page *locked_page,
1604− u64 *start, u64 *end,
1605− u64 max_bytes)
1601+STATIC u64 find_lock_delalloc_range(struct inode *inode,
1602+ struct extent_io_tree *tree,
1603+ struct page *locked_page, u64 *start,
1604+ u64 *end, u64 max_bytes)
16061605 {
16071606 u64 delalloc_start;
16081607 u64 delalloc_end;
fs/btrfs/extent_io.h+6−0
@@ -345,4 +345,10 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start, · 344 unmodified lines
345345 int end_extent_writepage(struct page *page, int err, u64 start, u64 end);
346346 int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
347347 int mirror_num);
348+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
349+noinline 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
348354 #endif
fs/btrfs/super.c+12−2
@@ -1791,10 +1791,20 @@ static int btrfs_run_sanity_tests(void) · 1790 unmodified lines
17911791 {
17921792 int ret;
17931793
1794− ret = btrfs_test_free_space_cache();
1794+ ret = btrfs_init_test_fs();
17951795 if (ret)
17961796 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();
1805+out:
1806+ btrfs_destroy_test_fs();
1807+ return ret;
17981808 }
17991809
18001810 static int __init init_btrfs_fs(void)
fs/btrfs/tests/btrfs-tests.c+68−0
@@ -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+
25+static struct vfsmount *test_mnt = NULL;
26+
27+static 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+
34+static struct file_system_type test_type = {
35+ .name = "btrfs_test_fs",
36+ .mount = btrfs_test_mount,
37+ .kill_sb = kill_anon_super,
38+};
39+
40+struct inode *btrfs_new_test_inode(void)
41+{
42+ return new_inode(test_mnt->mnt_sb);
43+}
44+
45+int 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+
64+void btrfs_destroy_test_fs(void)
65+{
66+ kern_unmount(test_mnt);
67+ unregister_filesystem(&test_type);
68+}
fs/btrfs/tests/btrfs-tests.h+15−0
@@ -25,6 +25,10 @@ · 24 unmodified lines
2525
2626 int btrfs_test_free_space_cache(void);
2727 int btrfs_test_extent_buffer_operations(void);
28+int btrfs_test_extent_io(void);
29+int btrfs_init_test_fs(void);
30+void btrfs_destroy_test_fs(void);
31+struct inode *btrfs_new_test_inode(void);
2832 #else
2933 static inline int btrfs_test_free_space_cache(void)
3034 {
@@ -34,6 +38,17 @@ static inline int btrfs_test_extent_buffer_operations(void) · 3 unmodified lines
3438 {
3539 return 0;
3640 }
41+static inline int btrfs_init_test_fs(void)
42+{
43+ return 0;
44+}
45+static inline void btrfs_destroy_test_fs(void)
46+{
47+}
48+static inline int btrfs_test_extent_io(void)
49+{
50+ return 0;
51+}
3752 #endif
3853
3954 #endif
fs/btrfs/tests/extent-io-tests.c+276−0
@@ -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+
28+static 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+
66+static 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;
260+out_bits:
261+ clear_extent_bits(&tmp, 0, total_dirty - 1,
262+ (unsigned long)-1, GFP_NOFS);
263+out:
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+
272+int btrfs_test_extent_io(void)
273+{
274+ test_msg("Running find delalloc tests\n");
275+ return test_find_delalloc();
276+}
include/uapi/linux/magic.h+1−1
@@ -71,6 +71,6 @@ · 70 unmodified lines
7171 #define USBDEVICE_SUPER_MAGIC 0x9fa2
7272 #define MTD_INODE_FS_MAGIC 0x11307854
7373 #define ANON_INODE_FS_MAGIC 0x09041934
74−
74+#define BTRFS_TEST_MAGIC 0x73727279
7575
7676 #endif /* __LINUX_MAGIC_H__ */

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t