aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2007-02-26 10:40:21 -0500
committerDavid Woodhouse <dwmw2@hera.kernel.org>2007-02-26 10:40:21 -0500
commitfec577fb7f516e0d12ff821b1af272fd754e120a (patch)
treef98b8adbf462cb3a146bcc3ec29e76beba5178ce /fs
parent97571fd0c939be8ae9cb57a8c57430a244ce13ae (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/Makefile10
-rw-r--r--fs/btrfs/ctree.c6
-rw-r--r--fs/btrfs/ctree.h62
-rw-r--r--fs/btrfs/debug-tree.c19
-rw-r--r--fs/btrfs/extent-tree.c296
-rw-r--r--fs/btrfs/random-test.c295
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
9ctree : $(objects) 9all: tester debug-tree
10 gcc $(CFLAGS) -o ctree $(objects) 10
11debug-tree: $(objects) debug-tree.o
12 gcc $(CFLAGS) -o debug-tree $(objects) debug-tree.o
13
14tester: $(objects) random-test.o
15 gcc $(CFLAGS) -o tester $(objects) random-test.o
11 16
12$(objects) : $(headers) 17$(objects) : $(headers)
13 18
14clean : 19clean :
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 */
1051int next_key(int i, int max_key) { 1053int 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
1056int main() { 1057int 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 */
6struct key { 16struct 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 */
12struct header { 25struct 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
29struct tree_buffer; 43struct 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 */
31struct ctree_root { 50struct 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 */
39struct ctree_root_info { 61struct 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 */
49struct ctree_super_block { 75struct 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 */
54struct item { 85struct 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))
61struct leaf { 99struct 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 */
69struct node { 111struct 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 */
75struct extent_item { 121struct 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 */
80struct ctree_path { 134struct 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
9int 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 */
22static 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 */
68int 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 */
108int 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
121check_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
162check_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 */
191static 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 */
236int 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 */
278struct 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
10int keep_running = 1;
11
12static 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;
20again:
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
37static 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;
56error:
57 printf("failed to insert %lu\n", key.objectid);
58 return -1;
59}
60
61static 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
80static 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;
101error:
102 printf("failed to delete %lu\n", key.objectid);
103 return -1;
104}
105
106static 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;
120error:
121 printf("unable to find key %lu\n", key.objectid);
122 return -1;
123}
124
125static 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;
139error:
140 printf("able to find key that should not exist %lu\n", key.objectid);
141 return -1;
142}
143
144int (*ops[])(struct ctree_root *root, struct radix_tree_root *radix) =
145{ ins_one, insert_dup, del_one, lookup_item, lookup_enoent };
146
147static 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
190void sigstopper(int ignored)
191{
192 keep_running = 0;
193 fprintf(stderr, "caught exit signal, stopping\n");
194}
195
196int 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}
205int 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 }
290out:
291 write_ctree_super(root, &super);
292 close_ctree(root);
293 return err;
294}
295