aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2007-03-01 15:16:26 -0500
committerDavid Woodhouse <dwmw2@hera.kernel.org>2007-03-01 15:16:26 -0500
commit79f95c82dca7665f32bafd68b7cdf4a01fab0840 (patch)
tree255a204980ad2c6481b36a1d8257d5ee84277d74 /fs
parentbb8039515d7c1b521ea22f095b43618ccc771885 (diff)
Btrfs: Fixup the code to merge during path walks
Add a bulk insert/remove test to random-test Add the quick-test code back as another regression test Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/Makefile5
-rw-r--r--fs/btrfs/ctree.c137
-rw-r--r--fs/btrfs/quick-test.c165
-rw-r--r--fs/btrfs/random-test.c117
4 files changed, 381 insertions, 43 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 58d4260a04b2..756b1cd4cf4d 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -14,7 +14,7 @@ check=sparse $(CHECKFLAGS)
14 $(check) $< 14 $(check) $<
15 $(CC) $(CFLAGS) -c $< 15 $(CC) $(CFLAGS) -c $<
16 16
17all: tester debug-tree 17all: tester debug-tree quick-test
18 18
19debug-tree: $(objects) debug-tree.o 19debug-tree: $(objects) debug-tree.o
20 gcc $(CFLAGS) -o debug-tree $(objects) debug-tree.o 20 gcc $(CFLAGS) -o debug-tree $(objects) debug-tree.o
@@ -22,6 +22,9 @@ debug-tree: $(objects) debug-tree.o
22tester: $(objects) random-test.o 22tester: $(objects) random-test.o
23 gcc $(CFLAGS) -o tester $(objects) random-test.o 23 gcc $(CFLAGS) -o tester $(objects) random-test.o
24 24
25quick-test: $(objects) quick-test.o
26 gcc $(CFLAGS) -o quick-test $(objects) quick-test.o
27
25$(objects) : $(headers) 28$(objects) : $(headers)
26 29
27clean : 30clean :
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index df4a19d65206..afa5bc5c7c1a 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -12,6 +12,9 @@ static int split_leaf(struct ctree_root *root, struct ctree_path *path,
12 int data_size); 12 int data_size);
13static int push_node_left(struct ctree_root *root, struct tree_buffer *dst, 13static int push_node_left(struct ctree_root *root, struct tree_buffer *dst,
14 struct tree_buffer *src); 14 struct tree_buffer *src);
15static int balance_node_right(struct ctree_root *root,
16 struct tree_buffer *dst_buf,
17 struct tree_buffer *src_buf);
15static int del_ptr(struct ctree_root *root, struct ctree_path *path, int level, 18static int del_ptr(struct ctree_root *root, struct ctree_path *path, int level,
16 int slot); 19 int slot);
17 20
@@ -217,15 +220,16 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path,
217 int ret = 0; 220 int ret = 0;
218 int wret; 221 int wret;
219 int pslot; 222 int pslot;
220 int used = 0;
221 int count;
222 int orig_slot = path->slots[level]; 223 int orig_slot = path->slots[level];
224 u64 orig_ptr;
223 225
224 if (level == 0) 226 if (level == 0)
225 return 0; 227 return 0;
226 228
227 mid_buf = path->nodes[level]; 229 mid_buf = path->nodes[level];
228 mid = &mid_buf->node; 230 mid = &mid_buf->node;
231 orig_ptr = mid->blockptrs[orig_slot];
232
229 if (level < MAX_LEVEL - 1) 233 if (level < MAX_LEVEL - 1)
230 parent_buf = path->nodes[level + 1]; 234 parent_buf = path->nodes[level + 1];
231 pslot = path->slots[level + 1]; 235 pslot = path->slots[level + 1];
@@ -253,24 +257,26 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path,
253 if (mid->header.nritems > NODEPTRS_PER_BLOCK / 4) 257 if (mid->header.nritems > NODEPTRS_PER_BLOCK / 4)
254 return 0; 258 return 0;
255 259
256 // print_tree(root, root->node);
257 left_buf = read_node_slot(root, parent_buf, pslot - 1); 260 left_buf = read_node_slot(root, parent_buf, pslot - 1);
258 right_buf = read_node_slot(root, parent_buf, pslot + 1); 261 right_buf = read_node_slot(root, parent_buf, pslot + 1);
259 if (right_buf) { 262
260 right = &right_buf->node; 263 /* first, try to make some room in the middle buffer */
261 used = right->header.nritems;
262 count = 1;
263 }
264 if (left_buf) { 264 if (left_buf) {
265 left = &left_buf->node; 265 left = &left_buf->node;
266 used += left->header.nritems;
267 orig_slot += left->header.nritems; 266 orig_slot += left->header.nritems;
268 count++; 267 wret = push_node_left(root, left_buf, mid_buf);
268 if (wret < 0)
269 ret = wret;
269 } 270 }
270 if (left_buf) 271
271 push_node_left(root, left_buf, mid_buf); 272 /*
273 * then try to empty the right most buffer into the middle
274 */
272 if (right_buf) { 275 if (right_buf) {
273 push_node_left(root, mid_buf, right_buf); 276 right = &right_buf->node;
277 wret = push_node_left(root, mid_buf, right_buf);
278 if (wret < 0)
279 ret = wret;
274 if (right->header.nritems == 0) { 280 if (right->header.nritems == 0) {
275 u64 blocknr = right_buf->blocknr; 281 u64 blocknr = right_buf->blocknr;
276 tree_block_release(root, right_buf); 282 tree_block_release(root, right_buf);
@@ -285,9 +291,29 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path,
285 } else { 291 } else {
286 memcpy(parent->keys + pslot + 1, right->keys, 292 memcpy(parent->keys + pslot + 1, right->keys,
287 sizeof(struct key)); 293 sizeof(struct key));
294 wret = write_tree_block(root, parent_buf);
295 if (wret)
296 ret = wret;
288 } 297 }
289 } 298 }
299 if (mid->header.nritems == 1) {
300 /*
301 * we're not allowed to leave a node with one item in the
302 * tree during a delete. A deletion from lower in the tree
303 * could try to delete the only pointer in this node.
304 * So, pull some keys from the left.
305 * There has to be a left pointer at this point because
306 * otherwise we would have pulled some pointers from the
307 * right
308 */
309 BUG_ON(!left_buf);
310 wret = balance_node_right(root, mid_buf, left_buf);
311 if (wret < 0)
312 ret = wret;
313 BUG_ON(wret == 1);
314 }
290 if (mid->header.nritems == 0) { 315 if (mid->header.nritems == 0) {
316 /* we've managed to empty the middle node, drop it */
291 u64 blocknr = mid_buf->blocknr; 317 u64 blocknr = mid_buf->blocknr;
292 tree_block_release(root, mid_buf); 318 tree_block_release(root, mid_buf);
293 mid_buf = NULL; 319 mid_buf = NULL;
@@ -298,11 +324,17 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path,
298 wret = free_extent(root, blocknr, 1); 324 wret = free_extent(root, blocknr, 1);
299 if (wret) 325 if (wret)
300 ret = wret; 326 ret = wret;
301 } else 327 } else {
328 /* update the parent key to reflect our changes */
302 memcpy(parent->keys + pslot, mid->keys, sizeof(struct key)); 329 memcpy(parent->keys + pslot, mid->keys, sizeof(struct key));
330 wret = write_tree_block(root, parent_buf);
331 if (wret)
332 ret = wret;
333 }
303 334
335 /* update the path */
304 if (left_buf) { 336 if (left_buf) {
305 if (left->header.nritems >= orig_slot) { 337 if (left->header.nritems > orig_slot) {
306 left_buf->count++; // released below 338 left_buf->count++; // released below
307 path->nodes[level] = left_buf; 339 path->nodes[level] = left_buf;
308 path->slots[level + 1] -= 1; 340 path->slots[level + 1] -= 1;
@@ -314,12 +346,15 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path,
314 path->slots[level] = orig_slot; 346 path->slots[level] = orig_slot;
315 } 347 }
316 } 348 }
349 /* double check we haven't messed things up */
350 check_block(path, level);
351 if (orig_ptr != path->nodes[level]->node.blockptrs[path->slots[level]])
352 BUG();
317 353
318 if (right_buf) 354 if (right_buf)
319 tree_block_release(root, right_buf); 355 tree_block_release(root, right_buf);
320 if (left_buf) 356 if (left_buf)
321 tree_block_release(root, left_buf); 357 tree_block_release(root, left_buf);
322
323 return ret; 358 return ret;
324} 359}
325 360
@@ -378,6 +413,7 @@ again:
378 goto again; 413 goto again;
379 c = &b->node; 414 c = &b->node;
380 slot = p->slots[level]; 415 slot = p->slots[level];
416 BUG_ON(c->header.nritems == 1);
381 } 417 }
382 b = read_tree_block(root, c->blockptrs[slot]); 418 b = read_tree_block(root, c->blockptrs[slot]);
383 } else { 419 } else {
@@ -433,13 +469,7 @@ static int fixup_low_keys(struct ctree_root *root,
433 469
434/* 470/*
435 * try to push data from one node into the next node left in the 471 * try to push data from one node into the next node left in the
436 * tree. The src node is found at specified level in the path. 472 * tree.
437 * If some bytes were pushed, return 0, otherwise return 1.
438 *
439 * Lower nodes/leaves in the path are not touched, higher nodes may
440 * be modified to reflect the push.
441 *
442 * The path is altered to reflect the push.
443 * 473 *
444 * returns 0 if some ptrs were pushed left, < 0 if there was some horrible 474 * returns 0 if some ptrs were pushed left, < 0 if there was some horrible
445 * error, and > 0 if there was no room in the left hand block. 475 * error, and > 0 if there was no room in the left hand block.
@@ -463,7 +493,8 @@ static int push_node_left(struct ctree_root *root, struct tree_buffer *dst_buf,
463 } 493 }
464 494
465 if (src_nritems < push_items) 495 if (src_nritems < push_items)
466 push_items =src_nritems; 496 push_items = src_nritems;
497
467 memcpy(dst->keys + dst_nritems, src->keys, 498 memcpy(dst->keys + dst_nritems, src->keys,
468 push_items * sizeof(struct key)); 499 push_items * sizeof(struct key));
469 memcpy(dst->blockptrs + dst_nritems, src->blockptrs, 500 memcpy(dst->blockptrs + dst_nritems, src->blockptrs,
@@ -488,6 +519,64 @@ static int push_node_left(struct ctree_root *root, struct tree_buffer *dst_buf,
488} 519}
489 520
490/* 521/*
522 * try to push data from one node into the next node right in the
523 * tree.
524 *
525 * returns 0 if some ptrs were pushed, < 0 if there was some horrible
526 * error, and > 0 if there was no room in the right hand block.
527 *
528 * this will only push up to 1/2 the contents of the left node over
529 */
530static int balance_node_right(struct ctree_root *root,
531 struct tree_buffer *dst_buf,
532 struct tree_buffer *src_buf)
533{
534 struct node *src = &src_buf->node;
535 struct node *dst = &dst_buf->node;
536 int push_items = 0;
537 int max_push;
538 int src_nritems;
539 int dst_nritems;
540 int ret = 0;
541 int wret;
542
543 src_nritems = src->header.nritems;
544 dst_nritems = dst->header.nritems;
545 push_items = NODEPTRS_PER_BLOCK - dst_nritems;
546 if (push_items <= 0) {
547 return 1;
548 }
549
550 max_push = src_nritems / 2 + 1;
551 /* don't try to empty the node */
552 if (max_push > src_nritems)
553 return 1;
554 if (max_push < push_items)
555 push_items = max_push;
556
557 memmove(dst->keys + push_items, dst->keys,
558 dst_nritems * sizeof(struct key));
559 memmove(dst->blockptrs + push_items, dst->blockptrs,
560 dst_nritems * sizeof(u64));
561 memcpy(dst->keys, src->keys + src_nritems - push_items,
562 push_items * sizeof(struct key));
563 memcpy(dst->blockptrs, src->blockptrs + src_nritems - push_items,
564 push_items * sizeof(u64));
565
566 src->header.nritems -= push_items;
567 dst->header.nritems += push_items;
568
569 wret = write_tree_block(root, src_buf);
570 if (wret < 0)
571 ret = wret;
572
573 wret = write_tree_block(root, dst_buf);
574 if (wret < 0)
575 ret = wret;
576 return ret;
577}
578
579/*
491 * helper function to insert a new root level in the tree. 580 * helper function to insert a new root level in the tree.
492 * A new node is allocated, and a single item is inserted to 581 * A new node is allocated, and a single item is inserted to
493 * point to the existing root 582 * point to the existing root
diff --git a/fs/btrfs/quick-test.c b/fs/btrfs/quick-test.c
new file mode 100644
index 000000000000..dbd00c3b7ab4
--- /dev/null
+++ b/fs/btrfs/quick-test.c
@@ -0,0 +1,165 @@
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/* for testing only */
10int next_key(int i, int max_key) {
11 return rand() % max_key;
12 //return i;
13}
14
15int main(int ac, char **av) {
16 struct key ins;
17 struct key last = { (u64)-1, 0, 0};
18 char *buf;
19 int i;
20 int num;
21 int ret;
22 int run_size = 100000;
23 int max_key = 100000000;
24 int tree_size = 0;
25 struct ctree_path path;
26 struct ctree_super_block super;
27 struct ctree_root *root;
28
29 radix_tree_init();
30
31 root = open_ctree("dbfile", &super);
32 srand(55);
33 for (i = 0; i < run_size; i++) {
34 buf = malloc(64);
35 num = next_key(i, max_key);
36 // num = i;
37 sprintf(buf, "string-%d", num);
38 if (i % 10000 == 0)
39 fprintf(stderr, "insert %d:%d\n", num, i);
40 ins.objectid = num;
41 ins.offset = 0;
42 ins.flags = 0;
43 ret = insert_item(root, &ins, buf, strlen(buf));
44 if (!ret)
45 tree_size++;
46 free(buf);
47 }
48 write_ctree_super(root, &super);
49 close_ctree(root);
50
51 root = open_ctree("dbfile", &super);
52 printf("starting search\n");
53 srand(55);
54 for (i = 0; i < run_size; i++) {
55 num = next_key(i, max_key);
56 ins.objectid = num;
57 init_path(&path);
58 if (i % 10000 == 0)
59 fprintf(stderr, "search %d:%d\n", num, i);
60 ret = search_slot(root, &ins, &path, 0);
61 if (ret) {
62 print_tree(root, root->node);
63 printf("unable to find %d\n", num);
64 exit(1);
65 }
66 release_path(root, &path);
67 }
68 write_ctree_super(root, &super);
69 close_ctree(root);
70 root = open_ctree("dbfile", &super);
71 printf("node %p level %d total ptrs %d free spc %lu\n", root->node,
72 node_level(root->node->node.header.flags),
73 root->node->node.header.nritems,
74 NODEPTRS_PER_BLOCK - root->node->node.header.nritems);
75 printf("all searches good, deleting some items\n");
76 i = 0;
77 srand(55);
78 for (i = 0 ; i < run_size/4; i++) {
79 num = next_key(i, max_key);
80 ins.objectid = num;
81 init_path(&path);
82 ret = search_slot(root, &ins, &path, -1);
83 if (!ret) {
84 if (i % 10000 == 0)
85 fprintf(stderr, "del %d:%d\n", num, i);
86 ret = del_item(root, &path);
87 if (ret != 0)
88 BUG();
89 tree_size--;
90 }
91 release_path(root, &path);
92 }
93 write_ctree_super(root, &super);
94 close_ctree(root);
95 root = open_ctree("dbfile", &super);
96 srand(128);
97 for (i = 0; i < run_size; i++) {
98 buf = malloc(64);
99 num = next_key(i, max_key);
100 sprintf(buf, "string-%d", num);
101 ins.objectid = num;
102 if (i % 10000 == 0)
103 fprintf(stderr, "insert %d:%d\n", num, i);
104 ret = insert_item(root, &ins, buf, strlen(buf));
105 if (!ret)
106 tree_size++;
107 free(buf);
108 }
109 write_ctree_super(root, &super);
110 close_ctree(root);
111 root = open_ctree("dbfile", &super);
112 srand(128);
113 printf("starting search2\n");
114 for (i = 0; i < run_size; i++) {
115 num = next_key(i, max_key);
116 ins.objectid = num;
117 init_path(&path);
118 if (i % 10000 == 0)
119 fprintf(stderr, "search %d:%d\n", num, i);
120 ret = search_slot(root, &ins, &path, 0);
121 if (ret) {
122 print_tree(root, root->node);
123 printf("unable to find %d\n", num);
124 exit(1);
125 }
126 release_path(root, &path);
127 }
128 printf("starting big long delete run\n");
129 while(root->node && root->node->node.header.nritems > 0) {
130 struct leaf *leaf;
131 int slot;
132 ins.objectid = (u64)-1;
133 init_path(&path);
134 ret = search_slot(root, &ins, &path, -1);
135 if (ret == 0)
136 BUG();
137
138 leaf = &path.nodes[0]->leaf;
139 slot = path.slots[0];
140 if (slot != leaf->header.nritems)
141 BUG();
142 while(path.slots[0] > 0) {
143 path.slots[0] -= 1;
144 slot = path.slots[0];
145 leaf = &path.nodes[0]->leaf;
146
147 memcpy(&last, &leaf->items[slot].key, sizeof(last));
148 if (tree_size % 10000 == 0)
149 printf("big del %d:%d\n", tree_size, i);
150 ret = del_item(root, &path);
151 if (ret != 0) {
152 printf("del_item returned %d\n", ret);
153 BUG();
154 }
155 tree_size--;
156 }
157 release_path(root, &path);
158 }
159 printf("tree size is now %d\n", tree_size);
160 printf("map tree\n");
161 print_tree(root->extent_root, root->extent_root->node);
162 write_ctree_super(root, &super);
163 close_ctree(root);
164 return 0;
165}
diff --git a/fs/btrfs/random-test.c b/fs/btrfs/random-test.c
index cebaf6483927..bbd554e88db2 100644
--- a/fs/btrfs/random-test.c
+++ b/fs/btrfs/random-test.c
@@ -142,8 +142,98 @@ error:
142 return -1; 142 return -1;
143} 143}
144 144
145static int empty_tree(struct ctree_root *root, struct radix_tree_root *radix,
146 int nr)
147{
148 struct ctree_path path;
149 struct key key;
150 unsigned long found = 0;
151 int ret;
152 int slot;
153 int *ptr;
154 int count = 0;
155
156 key.offset = 0;
157 key.flags = 0;
158 key.objectid = (unsigned long)-1;
159 while(nr-- >= 0) {
160 init_path(&path);
161 ret = search_slot(root, &key, &path, -1);
162 if (ret < 0) {
163 release_path(root, &path);
164 return ret;
165 }
166 if (ret != 0) {
167 if (path.slots[0] == 0) {
168 release_path(root, &path);
169 break;
170 }
171 path.slots[0] -= 1;
172 }
173 slot = path.slots[0];
174 found = path.nodes[0]->leaf.items[slot].key.objectid;
175 ret = del_item(root, &path);
176 count++;
177 if (ret) {
178 fprintf(stderr,
179 "failed to remove %lu from tree\n",
180 found);
181 return -1;
182 }
183 release_path(root, &path);
184 ptr = radix_tree_delete(radix, found);
185 if (!ptr)
186 goto error;
187 if (!keep_running)
188 break;
189 }
190 return 0;
191error:
192 fprintf(stderr, "failed to delete from the radix %lu\n", found);
193 return -1;
194}
195
196static int fill_tree(struct ctree_root *root, struct radix_tree_root *radix,
197 int count)
198{
199 int i;
200 int err;
201 int ret = 0;
202 for (i = 0; i < count; i++) {
203 ret = ins_one(root, radix);
204 if (ret) {
205 printf("fill failed\n");
206 err = ret;
207 goto out;
208 }
209 if (!keep_running)
210 break;
211 }
212out:
213 return ret;
214}
215
216static int bulk_op(struct ctree_root *root, struct radix_tree_root *radix)
217{
218 int ret;
219 int nr = rand() % 20000;
220 static int run_nr = 0;
221
222 /* do the bulk op much less frequently */
223 if (run_nr++ % 100)
224 return 0;
225 ret = empty_tree(root, radix, nr);
226 if (ret)
227 return ret;
228 ret = fill_tree(root, radix, nr);
229 if (ret)
230 return ret;
231 return 0;
232}
233
234
145int (*ops[])(struct ctree_root *root, struct radix_tree_root *radix) = 235int (*ops[])(struct ctree_root *root, struct radix_tree_root *radix) =
146{ ins_one, insert_dup, del_one, lookup_item, lookup_enoent }; 236{ ins_one, insert_dup, del_one, lookup_item, lookup_enoent, bulk_op };
147 237
148static int fill_radix(struct ctree_root *root, struct radix_tree_root *radix) 238static int fill_radix(struct ctree_root *root, struct radix_tree_root *radix)
149{ 239{
@@ -192,7 +282,6 @@ static int fill_radix(struct ctree_root *root, struct radix_tree_root *radix)
192 } 282 }
193 return 0; 283 return 0;
194} 284}
195
196void sigstopper(int ignored) 285void sigstopper(int ignored)
197{ 286{
198 keep_running = 0; 287 keep_running = 0;
@@ -241,22 +330,12 @@ int main(int ac, char **av)
241 print_usage(); 330 print_usage();
242 } 331 }
243 } 332 }
244 for (i = 0; i < init_fill_count; i++) { 333 printf("initial fill\n");
245 ret = ins_one(root, &radix); 334 ret = fill_tree(root, &radix, init_fill_count);
246 if (ret) { 335 printf("starting run\n");
247 printf("initial fill failed\n"); 336 if (ret) {
248 err = ret; 337 err = ret;
249 goto out; 338 goto out;
250 }
251 if (i % 10000 == 0) {
252 printf("initial fill %d level %d count %d\n", i,
253 node_level(root->node->node.header.flags),
254 root->node->node.header.nritems);
255 }
256 if (keep_running == 0) {
257 err = 0;
258 goto out;
259 }
260 } 339 }
261 if (initial_only == 1) { 340 if (initial_only == 1) {
262 goto out; 341 goto out;
@@ -287,6 +366,8 @@ int main(int ac, char **av)
287 err = ret; 366 err = ret;
288 goto out; 367 goto out;
289 } 368 }
369 if (ops[op] == bulk_op)
370 break;
290 if (keep_running == 0) { 371 if (keep_running == 0) {
291 err = 0; 372 err = 0;
292 goto out; 373 goto out;