diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-02-26 10:40:21 -0500 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-02-26 10:40:21 -0500 |
commit | fec577fb7f516e0d12ff821b1af272fd754e120a (patch) | |
tree | f98b8adbf462cb3a146bcc3ec29e76beba5178ce /fs | |
parent | 97571fd0c939be8ae9cb57a8c57430a244ce13ae (diff) |
Btrfs: Add fsx-style randomized tree tester
Add debug-tree command to print the tree
Add extent-tree.c to the repo
Comment ctree.h
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/Makefile | 10 | ||||
-rw-r--r-- | fs/btrfs/ctree.c | 6 | ||||
-rw-r--r-- | fs/btrfs/ctree.h | 62 | ||||
-rw-r--r-- | fs/btrfs/debug-tree.c | 19 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 296 | ||||
-rw-r--r-- | fs/btrfs/random-test.c | 295 |
6 files changed, 680 insertions, 8 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index fe73ab9d81d6..855e8f499e37 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile | |||
@@ -6,11 +6,17 @@ objects = ctree.o disk-io.o radix-tree.o mkfs.o extent-tree.o print-tree.o | |||
6 | #.c.o: | 6 | #.c.o: |
7 | # $(CC) $(CFLAGS) -c $< | 7 | # $(CC) $(CFLAGS) -c $< |
8 | 8 | ||
9 | ctree : $(objects) | 9 | all: tester debug-tree |
10 | gcc $(CFLAGS) -o ctree $(objects) | 10 | |
11 | debug-tree: $(objects) debug-tree.o | ||
12 | gcc $(CFLAGS) -o debug-tree $(objects) debug-tree.o | ||
13 | |||
14 | tester: $(objects) random-test.o | ||
15 | gcc $(CFLAGS) -o tester $(objects) random-test.o | ||
11 | 16 | ||
12 | $(objects) : $(headers) | 17 | $(objects) : $(headers) |
13 | 18 | ||
14 | clean : | 19 | clean : |
15 | rm ctree *.o | 20 | rm ctree *.o |
16 | 21 | ||
22 | |||
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 7645ab3259ea..25dc7b2f7426 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -1047,14 +1047,14 @@ int next_leaf(struct ctree_root *root, struct ctree_path *path) | |||
1047 | return 0; | 1047 | return 0; |
1048 | } | 1048 | } |
1049 | 1049 | ||
1050 | /* some sample code to insert,search & delete items */ | ||
1051 | #if 0 | ||
1050 | /* for testing only */ | 1052 | /* for testing only */ |
1051 | int next_key(int i, int max_key) { | 1053 | int next_key(int i, int max_key) { |
1052 | return rand() % max_key; | 1054 | return rand() % max_key; |
1053 | //return i; | 1055 | //return i; |
1054 | } | 1056 | } |
1055 | |||
1056 | int main() { | 1057 | int main() { |
1057 | struct ctree_root *root; | ||
1058 | struct key ins; | 1058 | struct key ins; |
1059 | struct key last = { (u64)-1, 0, 0}; | 1059 | struct key last = { (u64)-1, 0, 0}; |
1060 | char *buf; | 1060 | char *buf; |
@@ -1066,6 +1066,7 @@ int main() { | |||
1066 | int tree_size = 0; | 1066 | int tree_size = 0; |
1067 | struct ctree_path path; | 1067 | struct ctree_path path; |
1068 | struct ctree_super_block super; | 1068 | struct ctree_super_block super; |
1069 | struct ctree_root *root; | ||
1069 | 1070 | ||
1070 | radix_tree_init(); | 1071 | radix_tree_init(); |
1071 | 1072 | ||
@@ -1207,3 +1208,4 @@ int main() { | |||
1207 | close_ctree(root); | 1208 | close_ctree(root); |
1208 | return 0; | 1209 | return 0; |
1209 | } | 1210 | } |
1211 | #endif | ||
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index b92fbbb5ecd7..18daccd84535 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1,22 +1,36 @@ | |||
1 | #ifndef __CTREE__ | 1 | #ifndef __CTREE__ |
2 | #define __CTREE__ | 2 | #define __CTREE__ |
3 | 3 | ||
4 | #define CTREE_BLOCKSIZE 4096 | 4 | #define CTREE_BLOCKSIZE 1024 |
5 | 5 | ||
6 | /* | ||
7 | * the key defines the order in the tree, and so it also defines (optimal) | ||
8 | * block layout. objectid corresonds to the inode number. The flags | ||
9 | * tells us things about the object, and is a kind of stream selector. | ||
10 | * so for a given inode, keys with flags of 1 might refer to the inode | ||
11 | * data, flags of 2 may point to file data in the btree and flags == 3 | ||
12 | * may point to extents. | ||
13 | * | ||
14 | * offset is the starting byte offset for this key in the stream. | ||
15 | */ | ||
6 | struct key { | 16 | struct key { |
7 | u64 objectid; | 17 | u64 objectid; |
8 | u32 flags; | 18 | u32 flags; |
9 | u64 offset; | 19 | u64 offset; |
10 | } __attribute__ ((__packed__)); | 20 | } __attribute__ ((__packed__)); |
11 | 21 | ||
22 | /* | ||
23 | * every tree block (leaf or node) starts with this header. | ||
24 | */ | ||
12 | struct header { | 25 | struct header { |
13 | u64 fsid[2]; /* FS specific uuid */ | 26 | u64 fsid[2]; /* FS specific uuid */ |
14 | u64 blocknr; | 27 | u64 blocknr; /* which block this node is supposed to live in */ |
15 | u64 parentid; | 28 | u64 parentid; /* objectid of the tree root */ |
16 | u32 csum; | 29 | u32 csum; |
17 | u32 ham; | 30 | u32 ham; |
18 | u16 nritems; | 31 | u16 nritems; |
19 | u16 flags; | 32 | u16 flags; |
33 | /* generation flags to be added */ | ||
20 | } __attribute__ ((__packed__)); | 34 | } __attribute__ ((__packed__)); |
21 | 35 | ||
22 | #define NODEPTRS_PER_BLOCK ((CTREE_BLOCKSIZE - sizeof(struct header)) / \ | 36 | #define NODEPTRS_PER_BLOCK ((CTREE_BLOCKSIZE - sizeof(struct header)) / \ |
@@ -28,6 +42,11 @@ struct header { | |||
28 | 42 | ||
29 | struct tree_buffer; | 43 | struct tree_buffer; |
30 | 44 | ||
45 | /* | ||
46 | * in ram representation of the tree. extent_root is used for all allocations | ||
47 | * and for the extent tree extent_root root. current_insert is used | ||
48 | * only for the extent tree. | ||
49 | */ | ||
31 | struct ctree_root { | 50 | struct ctree_root { |
32 | struct tree_buffer *node; | 51 | struct tree_buffer *node; |
33 | struct ctree_root *extent_root; | 52 | struct ctree_root *extent_root; |
@@ -36,27 +55,46 @@ struct ctree_root { | |||
36 | struct radix_tree_root cache_radix; | 55 | struct radix_tree_root cache_radix; |
37 | }; | 56 | }; |
38 | 57 | ||
58 | /* | ||
59 | * describes a tree on disk | ||
60 | */ | ||
39 | struct ctree_root_info { | 61 | struct ctree_root_info { |
40 | u64 fsid[2]; /* FS specific uuid */ | 62 | u64 fsid[2]; /* FS specific uuid */ |
41 | u64 blocknr; /* blocknr of this block */ | 63 | u64 blocknr; /* blocknr of this block */ |
42 | u64 objectid; /* inode number of this root */ | 64 | u64 objectid; /* inode number of this root */ |
43 | u64 tree_root; /* the tree root */ | 65 | u64 tree_root; /* the tree root block */ |
44 | u32 csum; | 66 | u32 csum; |
45 | u32 ham; | 67 | u32 ham; |
46 | u64 snapuuid[2]; /* root specific uuid */ | 68 | u64 snapuuid[2]; /* root specific uuid */ |
47 | } __attribute__ ((__packed__)); | 69 | } __attribute__ ((__packed__)); |
48 | 70 | ||
71 | /* | ||
72 | * the super block basically lists the main trees of the FS | ||
73 | * it currently lacks any block count etc etc | ||
74 | */ | ||
49 | struct ctree_super_block { | 75 | struct ctree_super_block { |
50 | struct ctree_root_info root_info; | 76 | struct ctree_root_info root_info; |
51 | struct ctree_root_info extent_info; | 77 | struct ctree_root_info extent_info; |
52 | } __attribute__ ((__packed__)); | 78 | } __attribute__ ((__packed__)); |
53 | 79 | ||
80 | /* | ||
81 | * A leaf is full of items. The exact type of item is defined by | ||
82 | * the key flags parameter. offset and size tell us where to find | ||
83 | * the item in the leaf (relative to the start of the data area) | ||
84 | */ | ||
54 | struct item { | 85 | struct item { |
55 | struct key key; | 86 | struct key key; |
56 | u16 offset; | 87 | u16 offset; |
57 | u16 size; | 88 | u16 size; |
58 | } __attribute__ ((__packed__)); | 89 | } __attribute__ ((__packed__)); |
59 | 90 | ||
91 | /* | ||
92 | * leaves have an item area and a data area: | ||
93 | * [item0, item1....itemN] [free space] [dataN...data1, data0] | ||
94 | * | ||
95 | * The data is separate from the items to get the keys closer together | ||
96 | * during searches. | ||
97 | */ | ||
60 | #define LEAF_DATA_SIZE (CTREE_BLOCKSIZE - sizeof(struct header)) | 98 | #define LEAF_DATA_SIZE (CTREE_BLOCKSIZE - sizeof(struct header)) |
61 | struct leaf { | 99 | struct leaf { |
62 | struct header header; | 100 | struct header header; |
@@ -66,17 +104,33 @@ struct leaf { | |||
66 | }; | 104 | }; |
67 | } __attribute__ ((__packed__)); | 105 | } __attribute__ ((__packed__)); |
68 | 106 | ||
107 | /* | ||
108 | * all non-leaf blocks are nodes, they hold only keys and pointers to | ||
109 | * other blocks | ||
110 | */ | ||
69 | struct node { | 111 | struct node { |
70 | struct header header; | 112 | struct header header; |
71 | struct key keys[NODEPTRS_PER_BLOCK]; | 113 | struct key keys[NODEPTRS_PER_BLOCK]; |
72 | u64 blockptrs[NODEPTRS_PER_BLOCK]; | 114 | u64 blockptrs[NODEPTRS_PER_BLOCK]; |
73 | } __attribute__ ((__packed__)); | 115 | } __attribute__ ((__packed__)); |
74 | 116 | ||
117 | /* | ||
118 | * items in the extent btree are used to record the objectid of the | ||
119 | * owner of the block and the number of references | ||
120 | */ | ||
75 | struct extent_item { | 121 | struct extent_item { |
76 | u32 refs; | 122 | u32 refs; |
77 | u64 owner; | 123 | u64 owner; |
78 | } __attribute__ ((__packed__)); | 124 | } __attribute__ ((__packed__)); |
79 | 125 | ||
126 | /* | ||
127 | * ctree_paths remember the path taken from the root down to the leaf. | ||
128 | * level 0 is always the leaf, and nodes[1...MAX_LEVEL] will point | ||
129 | * to any other levels that are present. | ||
130 | * | ||
131 | * The slots array records the index of the item or block pointer | ||
132 | * used while walking the tree. | ||
133 | */ | ||
80 | struct ctree_path { | 134 | struct ctree_path { |
81 | struct tree_buffer *nodes[MAX_LEVEL]; | 135 | struct tree_buffer *nodes[MAX_LEVEL]; |
82 | int slots[MAX_LEVEL]; | 136 | int slots[MAX_LEVEL]; |
diff --git a/fs/btrfs/debug-tree.c b/fs/btrfs/debug-tree.c new file mode 100644 index 000000000000..b2c5abfdc74a --- /dev/null +++ b/fs/btrfs/debug-tree.c | |||
@@ -0,0 +1,19 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include "kerncompat.h" | ||
4 | #include "radix-tree.h" | ||
5 | #include "ctree.h" | ||
6 | #include "disk-io.h" | ||
7 | #include "print-tree.h" | ||
8 | |||
9 | int main() { | ||
10 | struct ctree_super_block super; | ||
11 | struct ctree_root *root; | ||
12 | radix_tree_init(); | ||
13 | root = open_ctree("dbfile", &super); | ||
14 | printf("root tree\n"); | ||
15 | print_tree(root, root->node); | ||
16 | printf("map tree\n"); | ||
17 | print_tree(root->extent_root, root->extent_root->node); | ||
18 | return 0; | ||
19 | } | ||
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c new file mode 100644 index 000000000000..2b663ef84496 --- /dev/null +++ b/fs/btrfs/extent-tree.c | |||
@@ -0,0 +1,296 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include "kerncompat.h" | ||
4 | #include "radix-tree.h" | ||
5 | #include "ctree.h" | ||
6 | #include "disk-io.h" | ||
7 | #include "print-tree.h" | ||
8 | |||
9 | /* | ||
10 | * pending extents are blocks that we're trying to allocate in the extent | ||
11 | * map while trying to grow the map because of other allocations. To avoid | ||
12 | * recursing, they are tagged in the radix tree and cleaned up after | ||
13 | * other allocations are done. The pending tag is also used in the same | ||
14 | * manner for deletes. | ||
15 | */ | ||
16 | #define CTREE_EXTENT_PENDING 0 | ||
17 | |||
18 | /* | ||
19 | * find all the blocks marked as pending in the radix tree and remove | ||
20 | * them from the extent map | ||
21 | */ | ||
22 | static int del_pending_extents(struct ctree_root *extent_root) | ||
23 | { | ||
24 | int ret; | ||
25 | struct key key; | ||
26 | struct tree_buffer *gang[4]; | ||
27 | int i; | ||
28 | struct ctree_path path; | ||
29 | |||
30 | while(1) { | ||
31 | ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix, | ||
32 | (void **)gang, 0, | ||
33 | ARRAY_SIZE(gang), | ||
34 | CTREE_EXTENT_PENDING); | ||
35 | if (!ret) | ||
36 | break; | ||
37 | for (i = 0; i < ret; i++) { | ||
38 | key.objectid = gang[i]->blocknr; | ||
39 | key.flags = 0; | ||
40 | key.offset = 1; | ||
41 | init_path(&path); | ||
42 | ret = search_slot(extent_root, &key, &path, 0); | ||
43 | if (ret) { | ||
44 | print_tree(extent_root, extent_root->node); | ||
45 | printf("unable to find %lu\n", key.objectid); | ||
46 | BUG(); | ||
47 | // FIXME undo it and return sane | ||
48 | return ret; | ||
49 | } | ||
50 | ret = del_item(extent_root, &path); | ||
51 | if (ret) { | ||
52 | BUG(); | ||
53 | return ret; | ||
54 | } | ||
55 | release_path(extent_root, &path); | ||
56 | radix_tree_tag_clear(&extent_root->cache_radix, | ||
57 | gang[i]->blocknr, | ||
58 | CTREE_EXTENT_PENDING); | ||
59 | tree_block_release(extent_root, gang[i]); | ||
60 | } | ||
61 | } | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * remove an extent from the root, returns 0 on success | ||
67 | */ | ||
68 | int free_extent(struct ctree_root *root, u64 blocknr, u64 num_blocks) | ||
69 | { | ||
70 | struct ctree_path path; | ||
71 | struct key key; | ||
72 | struct ctree_root *extent_root = root->extent_root; | ||
73 | struct tree_buffer *t; | ||
74 | int pending_ret; | ||
75 | int ret; | ||
76 | key.objectid = blocknr; | ||
77 | key.flags = 0; | ||
78 | key.offset = num_blocks; | ||
79 | if (root == extent_root) { | ||
80 | t = read_tree_block(root, key.objectid); | ||
81 | radix_tree_tag_set(&root->cache_radix, key.objectid, | ||
82 | CTREE_EXTENT_PENDING); | ||
83 | return 0; | ||
84 | } | ||
85 | init_path(&path); | ||
86 | ret = search_slot(extent_root, &key, &path, 0); | ||
87 | if (ret) { | ||
88 | print_tree(extent_root, extent_root->node); | ||
89 | printf("failed to find %lu\n", key.objectid); | ||
90 | BUG(); | ||
91 | } | ||
92 | ret = del_item(extent_root, &path); | ||
93 | if (ret) | ||
94 | BUG(); | ||
95 | release_path(extent_root, &path); | ||
96 | pending_ret = del_pending_extents(root->extent_root); | ||
97 | return ret ? ret : pending_ret; | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * walks the btree of allocated extents and find a hole of a given size. | ||
102 | * The key ins is changed to record the hole: | ||
103 | * ins->objectid == block start | ||
104 | * ins->flags = 0 | ||
105 | * ins->offset == number of blocks | ||
106 | * Any available blocks before search_start are skipped. | ||
107 | */ | ||
108 | int find_free_extent(struct ctree_root *orig_root, u64 num_blocks, | ||
109 | u64 search_start, u64 search_end, struct key *ins) | ||
110 | { | ||
111 | struct ctree_path path; | ||
112 | struct key *key; | ||
113 | int ret; | ||
114 | u64 hole_size = 0; | ||
115 | int slot = 0; | ||
116 | u64 last_block; | ||
117 | int start_found; | ||
118 | struct leaf *l; | ||
119 | struct ctree_root * root = orig_root->extent_root; | ||
120 | |||
121 | check_failed: | ||
122 | init_path(&path); | ||
123 | ins->objectid = search_start; | ||
124 | ins->offset = 0; | ||
125 | ins->flags = 0; | ||
126 | start_found = 0; | ||
127 | ret = search_slot(root, ins, &path, 0); | ||
128 | while (1) { | ||
129 | l = &path.nodes[0]->leaf; | ||
130 | slot = path.slots[0]; | ||
131 | if (slot >= l->header.nritems) { | ||
132 | ret = next_leaf(root, &path); | ||
133 | if (ret == 0) | ||
134 | continue; | ||
135 | if (!start_found) { | ||
136 | ins->objectid = search_start; | ||
137 | ins->offset = num_blocks; | ||
138 | start_found = 1; | ||
139 | goto check_pending; | ||
140 | } | ||
141 | ins->objectid = last_block > search_start ? | ||
142 | last_block : search_start; | ||
143 | ins->offset = num_blocks; | ||
144 | goto check_pending; | ||
145 | } | ||
146 | key = &l->items[slot].key; | ||
147 | if (key->objectid >= search_start) { | ||
148 | if (start_found) { | ||
149 | hole_size = key->objectid - last_block; | ||
150 | if (hole_size > num_blocks) { | ||
151 | ins->objectid = last_block; | ||
152 | ins->offset = num_blocks; | ||
153 | goto check_pending; | ||
154 | } | ||
155 | } else | ||
156 | start_found = 1; | ||
157 | last_block = key->objectid + key->offset; | ||
158 | } | ||
159 | path.slots[0]++; | ||
160 | } | ||
161 | // FIXME -ENOSPC | ||
162 | check_pending: | ||
163 | /* we have to make sure we didn't find an extent that has already | ||
164 | * been allocated by the map tree or the original allocation | ||
165 | */ | ||
166 | release_path(root, &path); | ||
167 | BUG_ON(ins->objectid < search_start); | ||
168 | if (orig_root->extent_root == orig_root) { | ||
169 | BUG_ON(num_blocks != 1); | ||
170 | if ((root->current_insert.objectid <= ins->objectid && | ||
171 | root->current_insert.objectid + | ||
172 | root->current_insert.offset > ins->objectid) || | ||
173 | (root->current_insert.objectid > ins->objectid && | ||
174 | root->current_insert.objectid <= ins->objectid + | ||
175 | ins->offset) || | ||
176 | radix_tree_tag_get(&root->cache_radix, ins->objectid, | ||
177 | CTREE_EXTENT_PENDING)) { | ||
178 | search_start = ins->objectid + 1; | ||
179 | goto check_failed; | ||
180 | } | ||
181 | } | ||
182 | if (ins->offset != 1) | ||
183 | BUG(); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * insert all of the pending extents reserved during the original | ||
189 | * allocation. (CTREE_EXTENT_PENDING). Returns zero if it all worked out | ||
190 | */ | ||
191 | static int insert_pending_extents(struct ctree_root *extent_root) | ||
192 | { | ||
193 | int ret; | ||
194 | struct key key; | ||
195 | struct extent_item item; | ||
196 | struct tree_buffer *gang[4]; | ||
197 | int i; | ||
198 | |||
199 | // FIXME -ENOSPC | ||
200 | item.refs = 1; | ||
201 | item.owner = extent_root->node->node.header.parentid; | ||
202 | while(1) { | ||
203 | ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix, | ||
204 | (void **)gang, 0, | ||
205 | ARRAY_SIZE(gang), | ||
206 | CTREE_EXTENT_PENDING); | ||
207 | if (!ret) | ||
208 | break; | ||
209 | for (i = 0; i < ret; i++) { | ||
210 | key.objectid = gang[i]->blocknr; | ||
211 | key.flags = 0; | ||
212 | key.offset = 1; | ||
213 | ret = insert_item(extent_root, &key, &item, | ||
214 | sizeof(item)); | ||
215 | if (ret) { | ||
216 | BUG(); | ||
217 | // FIXME undo it and return sane | ||
218 | return ret; | ||
219 | } | ||
220 | radix_tree_tag_clear(&extent_root->cache_radix, | ||
221 | gang[i]->blocknr, | ||
222 | CTREE_EXTENT_PENDING); | ||
223 | tree_block_release(extent_root, gang[i]); | ||
224 | } | ||
225 | } | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | /* | ||
230 | * finds a free extent and does all the dirty work required for allocation | ||
231 | * returns the key for the extent through ins, and a tree buffer for | ||
232 | * the first block of the extent through buf. | ||
233 | * | ||
234 | * returns 0 if everything worked, non-zero otherwise. | ||
235 | */ | ||
236 | int alloc_extent(struct ctree_root *root, u64 num_blocks, u64 search_start, | ||
237 | u64 search_end, u64 owner, struct key *ins, | ||
238 | struct tree_buffer **buf) | ||
239 | { | ||
240 | int ret; | ||
241 | int pending_ret; | ||
242 | struct extent_item extent_item; | ||
243 | extent_item.refs = 1; | ||
244 | extent_item.owner = owner; | ||
245 | |||
246 | ret = find_free_extent(root, num_blocks, search_start, search_end, ins); | ||
247 | if (ret) | ||
248 | return ret; | ||
249 | if (root != root->extent_root) { | ||
250 | memcpy(&root->extent_root->current_insert, ins, sizeof(*ins)); | ||
251 | ret = insert_item(root->extent_root, ins, &extent_item, | ||
252 | sizeof(extent_item)); | ||
253 | memset(&root->extent_root->current_insert, 0, | ||
254 | sizeof(struct key)); | ||
255 | pending_ret = insert_pending_extents(root->extent_root); | ||
256 | if (ret) | ||
257 | return ret; | ||
258 | if (pending_ret) | ||
259 | return pending_ret; | ||
260 | *buf = find_tree_block(root, ins->objectid); | ||
261 | return 0; | ||
262 | } | ||
263 | /* we're allocating an extent for the extent tree, don't recurse */ | ||
264 | BUG_ON(ins->offset != 1); | ||
265 | *buf = find_tree_block(root, ins->objectid); | ||
266 | BUG_ON(!*buf); | ||
267 | radix_tree_tag_set(&root->cache_radix, ins->objectid, | ||
268 | CTREE_EXTENT_PENDING); | ||
269 | (*buf)->count++; | ||
270 | return 0; | ||
271 | |||
272 | } | ||
273 | |||
274 | /* | ||
275 | * helper function to allocate a block for a given tree | ||
276 | * returns the tree buffer or NULL. | ||
277 | */ | ||
278 | struct tree_buffer *alloc_free_block(struct ctree_root *root) | ||
279 | { | ||
280 | struct key ins; | ||
281 | int ret; | ||
282 | struct tree_buffer *buf = NULL; | ||
283 | |||
284 | ret = alloc_extent(root, 1, 0, (unsigned long)-1, | ||
285 | root->node->node.header.parentid, | ||
286 | &ins, &buf); | ||
287 | |||
288 | if (ret) { | ||
289 | BUG(); | ||
290 | return NULL; | ||
291 | } | ||
292 | if (root != root->extent_root) | ||
293 | BUG_ON(radix_tree_tag_get(&root->extent_root->cache_radix, | ||
294 | buf->blocknr, CTREE_EXTENT_PENDING)); | ||
295 | return buf; | ||
296 | } | ||
diff --git a/fs/btrfs/random-test.c b/fs/btrfs/random-test.c new file mode 100644 index 000000000000..3c8c68d55d2f --- /dev/null +++ b/fs/btrfs/random-test.c | |||
@@ -0,0 +1,295 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <signal.h> | ||
4 | #include "kerncompat.h" | ||
5 | #include "radix-tree.h" | ||
6 | #include "ctree.h" | ||
7 | #include "disk-io.h" | ||
8 | #include "print-tree.h" | ||
9 | |||
10 | int keep_running = 1; | ||
11 | |||
12 | static int setup_key(struct radix_tree_root *root, struct key *key, int exists) | ||
13 | { | ||
14 | int num = rand(); | ||
15 | unsigned long res[2]; | ||
16 | int ret; | ||
17 | |||
18 | key->flags = 0; | ||
19 | key->offset = 0; | ||
20 | again: | ||
21 | ret = radix_tree_gang_lookup(root, (void **)res, num, 2); | ||
22 | if (exists) { | ||
23 | if (ret == 0) | ||
24 | return -1; | ||
25 | num = res[0]; | ||
26 | } else if (ret != 0 && num == res[0]) { | ||
27 | num++; | ||
28 | if (ret > 1 && num == res[1]) { | ||
29 | num++; | ||
30 | goto again; | ||
31 | } | ||
32 | } | ||
33 | key->objectid = num; | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | static int ins_one(struct ctree_root *root, struct radix_tree_root *radix) | ||
38 | { | ||
39 | struct ctree_path path; | ||
40 | struct key key; | ||
41 | int ret; | ||
42 | char buf[128]; | ||
43 | init_path(&path); | ||
44 | ret = setup_key(radix, &key, 0); | ||
45 | sprintf(buf, "str-%lu\n", key.objectid); | ||
46 | ret = insert_item(root, &key, buf, strlen(buf)); | ||
47 | if (ret) | ||
48 | goto error; | ||
49 | radix_tree_preload(GFP_KERNEL); | ||
50 | ret = radix_tree_insert(radix, key.objectid, | ||
51 | (void *)key.objectid); | ||
52 | radix_tree_preload_end(); | ||
53 | if (ret) | ||
54 | goto error; | ||
55 | return ret; | ||
56 | error: | ||
57 | printf("failed to insert %lu\n", key.objectid); | ||
58 | return -1; | ||
59 | } | ||
60 | |||
61 | static int insert_dup(struct ctree_root *root, struct radix_tree_root *radix) | ||
62 | { | ||
63 | struct ctree_path path; | ||
64 | struct key key; | ||
65 | int ret; | ||
66 | char buf[128]; | ||
67 | init_path(&path); | ||
68 | ret = setup_key(radix, &key, 1); | ||
69 | if (ret < 0) | ||
70 | return 0; | ||
71 | sprintf(buf, "str-%lu\n", key.objectid); | ||
72 | ret = insert_item(root, &key, buf, strlen(buf)); | ||
73 | if (ret != -EEXIST) { | ||
74 | printf("insert on %lu gave us %d\n", key.objectid, ret); | ||
75 | return 1; | ||
76 | } | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int del_one(struct ctree_root *root, struct radix_tree_root *radix) | ||
81 | { | ||
82 | struct ctree_path path; | ||
83 | struct key key; | ||
84 | int ret; | ||
85 | unsigned long *ptr; | ||
86 | init_path(&path); | ||
87 | ret = setup_key(radix, &key, 1); | ||
88 | if (ret < 0) | ||
89 | return 0; | ||
90 | ret = search_slot(root, &key, &path, -1); | ||
91 | if (ret) | ||
92 | goto error; | ||
93 | ret = del_item(root, &path); | ||
94 | release_path(root, &path); | ||
95 | if (ret != 0) | ||
96 | goto error; | ||
97 | ptr = radix_tree_delete(radix, key.objectid); | ||
98 | if (!ptr) | ||
99 | goto error; | ||
100 | return 0; | ||
101 | error: | ||
102 | printf("failed to delete %lu\n", key.objectid); | ||
103 | return -1; | ||
104 | } | ||
105 | |||
106 | static int lookup_item(struct ctree_root *root, struct radix_tree_root *radix) | ||
107 | { | ||
108 | struct ctree_path path; | ||
109 | struct key key; | ||
110 | int ret; | ||
111 | init_path(&path); | ||
112 | ret = setup_key(radix, &key, 1); | ||
113 | if (ret < 0) | ||
114 | return 0; | ||
115 | ret = search_slot(root, &key, &path, 0); | ||
116 | release_path(root, &path); | ||
117 | if (ret) | ||
118 | goto error; | ||
119 | return 0; | ||
120 | error: | ||
121 | printf("unable to find key %lu\n", key.objectid); | ||
122 | return -1; | ||
123 | } | ||
124 | |||
125 | static int lookup_enoent(struct ctree_root *root, struct radix_tree_root *radix) | ||
126 | { | ||
127 | struct ctree_path path; | ||
128 | struct key key; | ||
129 | int ret; | ||
130 | init_path(&path); | ||
131 | ret = setup_key(radix, &key, 0); | ||
132 | if (ret < 0) | ||
133 | return ret; | ||
134 | ret = search_slot(root, &key, &path, 0); | ||
135 | release_path(root, &path); | ||
136 | if (ret == 0) | ||
137 | goto error; | ||
138 | return 0; | ||
139 | error: | ||
140 | printf("able to find key that should not exist %lu\n", key.objectid); | ||
141 | return -1; | ||
142 | } | ||
143 | |||
144 | int (*ops[])(struct ctree_root *root, struct radix_tree_root *radix) = | ||
145 | { ins_one, insert_dup, del_one, lookup_item, lookup_enoent }; | ||
146 | |||
147 | static int fill_radix(struct ctree_root *root, struct radix_tree_root *radix) | ||
148 | { | ||
149 | struct ctree_path path; | ||
150 | struct key key; | ||
151 | u64 found; | ||
152 | int ret; | ||
153 | int slot; | ||
154 | int i; | ||
155 | key.offset = 0; | ||
156 | key.flags = 0; | ||
157 | key.objectid = (unsigned long)-1; | ||
158 | while(1) { | ||
159 | init_path(&path); | ||
160 | ret = search_slot(root, &key, &path, 0); | ||
161 | slot = path.slots[0]; | ||
162 | if (ret != 0) { | ||
163 | if (slot == 0) { | ||
164 | release_path(root, &path); | ||
165 | break; | ||
166 | } | ||
167 | slot -= 1; | ||
168 | } | ||
169 | for (i = slot; i >= 0; i--) { | ||
170 | found = path.nodes[0]->leaf.items[i].key.objectid; | ||
171 | radix_tree_preload(GFP_KERNEL); | ||
172 | ret = radix_tree_insert(radix, found, (void *)found); | ||
173 | if (ret) { | ||
174 | fprintf(stderr, | ||
175 | "failed to insert %lu into radix\n", | ||
176 | found); | ||
177 | exit(1); | ||
178 | } | ||
179 | |||
180 | radix_tree_preload_end(); | ||
181 | } | ||
182 | release_path(root, &path); | ||
183 | key.objectid = found - 1; | ||
184 | if (key.objectid > found) | ||
185 | break; | ||
186 | } | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | void sigstopper(int ignored) | ||
191 | { | ||
192 | keep_running = 0; | ||
193 | fprintf(stderr, "caught exit signal, stopping\n"); | ||
194 | } | ||
195 | |||
196 | int print_usage(void) | ||
197 | { | ||
198 | printf("usage: tester [-ih] [-c count] [-f count]\n"); | ||
199 | printf("\t -c count -- iteration count after filling\n"); | ||
200 | printf("\t -f count -- run this many random inserts before starting\n"); | ||
201 | printf("\t -i -- only do initial fill\n"); | ||
202 | printf("\t -h -- this help text\n"); | ||
203 | exit(1); | ||
204 | } | ||
205 | int main(int ac, char **av) | ||
206 | { | ||
207 | RADIX_TREE(radix, GFP_KERNEL); | ||
208 | struct ctree_super_block super; | ||
209 | struct ctree_root *root; | ||
210 | int i; | ||
211 | int ret; | ||
212 | int count; | ||
213 | int op; | ||
214 | int iterations = 20000; | ||
215 | int init_fill_count = 800000; | ||
216 | int err = 0; | ||
217 | int initial_only = 0; | ||
218 | radix_tree_init(); | ||
219 | root = open_ctree("dbfile", &super); | ||
220 | fill_radix(root, &radix); | ||
221 | |||
222 | signal(SIGTERM, sigstopper); | ||
223 | signal(SIGINT, sigstopper); | ||
224 | |||
225 | for (i = 1 ; i < ac ; i++) { | ||
226 | if (strcmp(av[i], "-i") == 0) { | ||
227 | initial_only = 1; | ||
228 | } else if (strcmp(av[i], "-c") == 0) { | ||
229 | iterations = atoi(av[i+1]); | ||
230 | i++; | ||
231 | } else if (strcmp(av[i], "-f") == 0) { | ||
232 | init_fill_count = atoi(av[i+1]); | ||
233 | i++; | ||
234 | } else { | ||
235 | print_usage(); | ||
236 | } | ||
237 | } | ||
238 | for (i = 0; i < init_fill_count; i++) { | ||
239 | ret = ins_one(root, &radix); | ||
240 | if (ret) { | ||
241 | printf("initial fill failed\n"); | ||
242 | err = ret; | ||
243 | goto out; | ||
244 | } | ||
245 | if (i % 10000 == 0) { | ||
246 | printf("initial fill %d level %d count %d\n", i, | ||
247 | node_level(root->node->node.header.flags), | ||
248 | root->node->node.header.nritems); | ||
249 | } | ||
250 | if (keep_running == 0) { | ||
251 | err = 0; | ||
252 | goto out; | ||
253 | } | ||
254 | } | ||
255 | if (initial_only == 1) { | ||
256 | goto out; | ||
257 | } | ||
258 | for (i = 0; i < iterations; i++) { | ||
259 | op = rand() % ARRAY_SIZE(ops); | ||
260 | count = rand() % 128; | ||
261 | if (i % 2000 == 0) { | ||
262 | printf("%d\n", i); | ||
263 | fflush(stdout); | ||
264 | } | ||
265 | if (i && i % 5000 == 0) { | ||
266 | printf("open & close, root level %d nritems %d\n", | ||
267 | node_level(root->node->node.header.flags), | ||
268 | root->node->node.header.nritems); | ||
269 | write_ctree_super(root, &super); | ||
270 | close_ctree(root); | ||
271 | root = open_ctree("dbfile", &super); | ||
272 | } | ||
273 | while(count--) { | ||
274 | ret = ops[op](root, &radix); | ||
275 | if (ret) { | ||
276 | fprintf(stderr, "op %d failed %d:%d\n", | ||
277 | op, i, iterations); | ||
278 | print_tree(root, root->node); | ||
279 | fprintf(stderr, "op %d failed %d:%d\n", | ||
280 | op, i, iterations); | ||
281 | err = ret; | ||
282 | goto out; | ||
283 | } | ||
284 | if (keep_running == 0) { | ||
285 | err = 0; | ||
286 | goto out; | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | out: | ||
291 | write_ctree_super(root, &super); | ||
292 | close_ctree(root); | ||
293 | return err; | ||
294 | } | ||
295 | |||