aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-09-19 16:07:01 -0400
committerChris Mason <chris.mason@fusionio.com>2013-11-11 21:51:02 -0500
commit06ea65a398a2501e94beee3a425d07e1846ff25a (patch)
treeda8445a365ceecd4df89a9d6a3da79afaa320765 /fs/btrfs
parentdd3cc16b8750251ea9b1a843ce7806e82b015d5e (diff)
Btrfs: add a sanity test for btrfs_split_item
While looking at somebodys corruption I became completely convinced that btrfs_split_item was broken, so I wrote this test to verify that it was working as it was supposed to. Thankfully it appears to be working as intended, so just add this test to make sure nobody breaks it in the future. 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/Makefile3
-rw-r--r--fs/btrfs/ctree.h4
-rw-r--r--fs/btrfs/disk-io.c38
-rw-r--r--fs/btrfs/disk-io.h4
-rw-r--r--fs/btrfs/super.c7
-rw-r--r--fs/btrfs/tests/btrfs-tests.h5
-rw-r--r--fs/btrfs/tests/extent-buffer-tests.c229
7 files changed, 283 insertions, 7 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index a91a6a355cc5..4c7dfbfaa3b3 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -14,4 +14,5 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
14btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o 14btrfs-$(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
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index fa117f7d5a8f..ee0b8aa82828 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1724,7 +1724,9 @@ struct btrfs_root {
1724 int ref_cows; 1724 int ref_cows;
1725 int track_dirty; 1725 int track_dirty;
1726 int in_radix; 1726 int in_radix;
1727 1727#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
1728 int dummy_root;
1729#endif
1728 u64 defrag_trans_start; 1730 u64 defrag_trans_start;
1729 struct btrfs_key defrag_progress; 1731 struct btrfs_key defrag_progress;
1730 struct btrfs_key defrag_max; 1732 struct btrfs_key defrag_max;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index f724397b396b..db2095486a4f 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1229,14 +1229,18 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
1229 atomic_set(&root->refs, 1); 1229 atomic_set(&root->refs, 1);
1230 root->log_transid = 0; 1230 root->log_transid = 0;
1231 root->last_log_commit = 0; 1231 root->last_log_commit = 0;
1232 extent_io_tree_init(&root->dirty_log_pages, 1232 if (fs_info)
1233 fs_info->btree_inode->i_mapping); 1233 extent_io_tree_init(&root->dirty_log_pages,
1234 fs_info->btree_inode->i_mapping);
1234 1235
1235 memset(&root->root_key, 0, sizeof(root->root_key)); 1236 memset(&root->root_key, 0, sizeof(root->root_key));
1236 memset(&root->root_item, 0, sizeof(root->root_item)); 1237 memset(&root->root_item, 0, sizeof(root->root_item));
1237 memset(&root->defrag_progress, 0, sizeof(root->defrag_progress)); 1238 memset(&root->defrag_progress, 0, sizeof(root->defrag_progress));
1238 memset(&root->root_kobj, 0, sizeof(root->root_kobj)); 1239 memset(&root->root_kobj, 0, sizeof(root->root_kobj));
1239 root->defrag_trans_start = fs_info->generation; 1240 if (fs_info)
1241 root->defrag_trans_start = fs_info->generation;
1242 else
1243 root->defrag_trans_start = 0;
1240 init_completion(&root->kobj_unregister); 1244 init_completion(&root->kobj_unregister);
1241 root->defrag_running = 0; 1245 root->defrag_running = 0;
1242 root->root_key.objectid = objectid; 1246 root->root_key.objectid = objectid;
@@ -1253,6 +1257,22 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info)
1253 return root; 1257 return root;
1254} 1258}
1255 1259
1260#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
1261/* Should only be used by the testing infrastructure */
1262struct btrfs_root *btrfs_alloc_dummy_root(void)
1263{
1264 struct btrfs_root *root;
1265
1266 root = btrfs_alloc_root(NULL);
1267 if (!root)
1268 return ERR_PTR(-ENOMEM);
1269 __setup_root(4096, 4096, 4096, 4096, root, NULL, 1);
1270 root->dummy_root = 1;
1271
1272 return root;
1273}
1274#endif
1275
1256struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans, 1276struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
1257 struct btrfs_fs_info *fs_info, 1277 struct btrfs_fs_info *fs_info,
1258 u64 objectid) 1278 u64 objectid)
@@ -3670,10 +3690,20 @@ int btrfs_set_buffer_uptodate(struct extent_buffer *buf)
3670 3690
3671void btrfs_mark_buffer_dirty(struct extent_buffer *buf) 3691void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
3672{ 3692{
3673 struct btrfs_root *root = BTRFS_I(buf->pages[0]->mapping->host)->root; 3693 struct btrfs_root *root;
3674 u64 transid = btrfs_header_generation(buf); 3694 u64 transid = btrfs_header_generation(buf);
3675 int was_dirty; 3695 int was_dirty;
3676 3696
3697#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
3698 /*
3699 * This is a fast path so only do this check if we have sanity tests
3700 * enabled. Normal people shouldn't be marking dummy buffers as dirty
3701 * outside of the sanity tests.
3702 */
3703 if (unlikely(test_bit(EXTENT_BUFFER_DUMMY, &buf->bflags)))
3704 return;
3705#endif
3706 root = BTRFS_I(buf->pages[0]->mapping->host)->root;
3677 btrfs_assert_tree_locked(buf); 3707 btrfs_assert_tree_locked(buf);
3678 if (transid != root->fs_info->generation) 3708 if (transid != root->fs_info->generation)
3679 WARN(1, KERN_CRIT "btrfs transid mismatch buffer %llu, " 3709 WARN(1, KERN_CRIT "btrfs transid mismatch buffer %llu, "
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 5ce2a7da8b11..53059df350f8 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -86,6 +86,10 @@ void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
86 struct btrfs_root *root); 86 struct btrfs_root *root);
87void btrfs_free_fs_root(struct btrfs_root *root); 87void btrfs_free_fs_root(struct btrfs_root *root);
88 88
89#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
90struct btrfs_root *btrfs_alloc_dummy_root(void);
91#endif
92
89/* 93/*
90 * This function is used to grab the root, and avoid it is freed when we 94 * This function is used to grab the root, and avoid it is freed when we
91 * access it. But it doesn't ensure that the tree is not dropped. 95 * access it. But it doesn't ensure that the tree is not dropped.
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index e913328d0f2a..0e398657d759 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1789,7 +1789,12 @@ static void btrfs_print_info(void)
1789 1789
1790static int btrfs_run_sanity_tests(void) 1790static int btrfs_run_sanity_tests(void)
1791{ 1791{
1792 return btrfs_test_free_space_cache(); 1792 int ret;
1793
1794 ret = btrfs_test_free_space_cache();
1795 if (ret)
1796 return ret;
1797 return btrfs_test_extent_buffer_operations();
1793} 1798}
1794 1799
1795static int __init init_btrfs_fs(void) 1800static int __init init_btrfs_fs(void)
diff --git a/fs/btrfs/tests/btrfs-tests.h b/fs/btrfs/tests/btrfs-tests.h
index 580877625776..04f2cd2ca568 100644
--- a/fs/btrfs/tests/btrfs-tests.h
+++ b/fs/btrfs/tests/btrfs-tests.h
@@ -24,11 +24,16 @@
24#define test_msg(fmt, ...) pr_info("btrfs: selftest: " fmt, ##__VA_ARGS__) 24#define test_msg(fmt, ...) pr_info("btrfs: selftest: " fmt, ##__VA_ARGS__)
25 25
26int btrfs_test_free_space_cache(void); 26int btrfs_test_free_space_cache(void);
27int btrfs_test_extent_buffer_operations(void);
27#else 28#else
28static inline int btrfs_test_free_space_cache(void) 29static inline int btrfs_test_free_space_cache(void)
29{ 30{
30 return 0; 31 return 0;
31} 32}
33static inline int btrfs_test_extent_buffer_operations(void)
34{
35 return 0;
36}
32#endif 37#endif
33 38
34#endif 39#endif
diff --git a/fs/btrfs/tests/extent-buffer-tests.c b/fs/btrfs/tests/extent-buffer-tests.c
new file mode 100644
index 000000000000..cc286ce97d1e
--- /dev/null
+++ b/fs/btrfs/tests/extent-buffer-tests.c
@@ -0,0 +1,229 @@
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/slab.h>
20#include "btrfs-tests.h"
21#include "../ctree.h"
22#include "../extent_io.h"
23#include "../disk-io.h"
24
25static int test_btrfs_split_item(void)
26{
27 struct btrfs_path *path;
28 struct btrfs_root *root;
29 struct extent_buffer *eb;
30 struct btrfs_item *item;
31 char *value = "mary had a little lamb";
32 char *split1 = "mary had a little";
33 char *split2 = " lamb";
34 char *split3 = "mary";
35 char *split4 = " had a little";
36 char buf[32];
37 struct btrfs_key key;
38 u32 value_len = strlen(value);
39 int ret = 0;
40
41 test_msg("Running btrfs_split_item tests\n");
42
43 root = btrfs_alloc_dummy_root();
44 if (IS_ERR(root)) {
45 test_msg("Could not allocate root\n");
46 return PTR_ERR(root);
47 }
48
49 path = btrfs_alloc_path();
50 if (!path) {
51 test_msg("Could not allocate path\n");
52 kfree(root);
53 return -ENOMEM;
54 }
55
56 path->nodes[0] = eb = alloc_dummy_extent_buffer(0, 4096);
57 if (!eb) {
58 test_msg("Could not allocate dummy buffer\n");
59 ret = -ENOMEM;
60 goto out;
61 }
62 path->slots[0] = 0;
63
64 key.objectid = 0;
65 key.type = BTRFS_EXTENT_CSUM_KEY;
66 key.offset = 0;
67
68 setup_items_for_insert(root, path, &key, &value_len, value_len,
69 value_len + sizeof(struct btrfs_item), 1);
70 item = btrfs_item_nr(0);
71 write_extent_buffer(eb, value, btrfs_item_ptr_offset(eb, 0),
72 value_len);
73
74 key.offset = 3;
75
76 /*
77 * Passing NULL trans here should be safe because we have plenty of
78 * space in this leaf to split the item without having to split the
79 * leaf.
80 */
81 ret = btrfs_split_item(NULL, root, path, &key, 17);
82 if (ret) {
83 test_msg("Split item failed %d\n", ret);
84 goto out;
85 }
86
87 /*
88 * Read the first slot, it should have the original key and contain only
89 * 'mary had a little'
90 */
91 btrfs_item_key_to_cpu(eb, &key, 0);
92 if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
93 key.offset != 0) {
94 test_msg("Invalid key at slot 0\n");
95 ret = -EINVAL;
96 goto out;
97 }
98
99 item = btrfs_item_nr(0);
100 if (btrfs_item_size(eb, item) != strlen(split1)) {
101 test_msg("Invalid len in the first split\n");
102 ret = -EINVAL;
103 goto out;
104 }
105
106 read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0),
107 strlen(split1));
108 if (memcmp(buf, split1, strlen(split1))) {
109 test_msg("Data in the buffer doesn't match what it should "
110 "in the first split have='%.*s' want '%s'\n",
111 (int)strlen(split1), buf, split1);
112 ret = -EINVAL;
113 goto out;
114 }
115
116 btrfs_item_key_to_cpu(eb, &key, 1);
117 if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
118 key.offset != 3) {
119 test_msg("Invalid key at slot 1\n");
120 ret = -EINVAL;
121 goto out;
122 }
123
124 item = btrfs_item_nr(1);
125 if (btrfs_item_size(eb, item) != strlen(split2)) {
126 test_msg("Invalid len in the second split\n");
127 ret = -EINVAL;
128 goto out;
129 }
130
131 read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1),
132 strlen(split2));
133 if (memcmp(buf, split2, strlen(split2))) {
134 test_msg("Data in the buffer doesn't match what it should "
135 "in the second split\n");
136 ret = -EINVAL;
137 goto out;
138 }
139
140 key.offset = 1;
141 /* Do it again so we test memmoving the other items in the leaf */
142 ret = btrfs_split_item(NULL, root, path, &key, 4);
143 if (ret) {
144 test_msg("Second split item failed %d\n", ret);
145 goto out;
146 }
147
148 btrfs_item_key_to_cpu(eb, &key, 0);
149 if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
150 key.offset != 0) {
151 test_msg("Invalid key at slot 0\n");
152 ret = -EINVAL;
153 goto out;
154 }
155
156 item = btrfs_item_nr(0);
157 if (btrfs_item_size(eb, item) != strlen(split3)) {
158 test_msg("Invalid len in the first split\n");
159 ret = -EINVAL;
160 goto out;
161 }
162
163 read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0),
164 strlen(split3));
165 if (memcmp(buf, split3, strlen(split3))) {
166 test_msg("Data in the buffer doesn't match what it should "
167 "in the third split");
168 ret = -EINVAL;
169 goto out;
170 }
171
172 btrfs_item_key_to_cpu(eb, &key, 1);
173 if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
174 key.offset != 1) {
175 test_msg("Invalid key at slot 1\n");
176 ret = -EINVAL;
177 goto out;
178 }
179
180 item = btrfs_item_nr(1);
181 if (btrfs_item_size(eb, item) != strlen(split4)) {
182 test_msg("Invalid len in the second split\n");
183 ret = -EINVAL;
184 goto out;
185 }
186
187 read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1),
188 strlen(split4));
189 if (memcmp(buf, split4, strlen(split4))) {
190 test_msg("Data in the buffer doesn't match what it should "
191 "in the fourth split\n");
192 ret = -EINVAL;
193 goto out;
194 }
195
196 btrfs_item_key_to_cpu(eb, &key, 2);
197 if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
198 key.offset != 3) {
199 test_msg("Invalid key at slot 2\n");
200 ret = -EINVAL;
201 goto out;
202 }
203
204 item = btrfs_item_nr(2);
205 if (btrfs_item_size(eb, item) != strlen(split2)) {
206 test_msg("Invalid len in the second split\n");
207 ret = -EINVAL;
208 goto out;
209 }
210
211 read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 2),
212 strlen(split2));
213 if (memcmp(buf, split2, strlen(split2))) {
214 test_msg("Data in the buffer doesn't match what it should "
215 "in the last chunk\n");
216 ret = -EINVAL;
217 goto out;
218 }
219out:
220 btrfs_free_path(path);
221 kfree(root);
222 return ret;
223}
224
225int btrfs_test_extent_buffer_operations(void)
226{
227 test_msg("Running extent buffer operation tests");
228 return test_btrfs_split_item();
229}