transaction handles everywhere
[platform/upstream/btrfs-progs.git] / dir-test.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <unistd.h>
5 #include "kerncompat.h"
6 #include "radix-tree.h"
7 #include "ctree.h"
8 #include "disk-io.h"
9 #include "print-tree.h"
10 #include "hash.h"
11 #include "transaction.h"
12
13 int keep_running = 1;
14 struct btrfs_super_block super;
15 static u64 dir_oid = 44556;
16 static u64 file_oid = 33778;
17
18 static int find_num(struct radix_tree_root *root, unsigned long *num_ret,
19                      int exists)
20 {
21         unsigned long num = rand();
22         unsigned long res[2];
23         int ret;
24
25 again:
26         ret = radix_tree_gang_lookup(root, (void **)res, num, 2);
27         if (exists) {
28                 if (ret == 0)
29                         return -1;
30                 num = res[0];
31         } else if (ret != 0 && num == res[0]) {
32                 num++;
33                 if (ret > 1 && num == res[1]) {
34                         num++;
35                         goto again;
36                 }
37         }
38         *num_ret = num;
39         return 0;
40 }
41
42 static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
43                    struct radix_tree_root *radix)
44 {
45         int ret;
46         char buf[128];
47         unsigned long oid;
48         struct btrfs_path path;
49
50         find_num(radix, &oid, 0);
51         sprintf(buf, "str-%lu", oid);
52
53         ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid,
54                                     file_oid, 1);
55         if (ret)
56                 goto error;
57
58         radix_tree_preload(GFP_KERNEL);
59         ret = radix_tree_insert(radix, oid, (void *)oid);
60         radix_tree_preload_end();
61         if (ret)
62                 goto error;
63         return ret;
64 error:
65         if (ret != -EEXIST)
66                 goto fatal;
67
68         /*
69          * if we got an EEXIST, it may be due to hash collision, double
70          * check
71          */
72         btrfs_init_path(&path);
73         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
74                                     strlen(buf), 0);
75         if (ret)
76                 goto fatal_release;
77         if (!btrfs_match_dir_item_name(root, &path, buf, strlen(buf))) {
78                 struct btrfs_dir_item *di;
79                 char *found;
80                 u32 found_len;
81                 u64 myhash;
82                 u64 foundhash;
83
84                 di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0],
85                                     struct btrfs_dir_item);
86                 found = (char *)(di + 1);
87                 found_len = btrfs_dir_name_len(di);
88                 btrfs_name_hash(buf, strlen(buf), &myhash);
89                 btrfs_name_hash(found, found_len, &foundhash);
90                 if (myhash != foundhash)
91                         goto fatal_release;
92                 btrfs_release_path(root, &path);
93                 return 0;
94         }
95 fatal_release:
96         btrfs_release_path(root, &path);
97 fatal:
98         printf("failed to insert %lu ret %d\n", oid, ret);
99         return -1;
100 }
101
102 static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root
103                       *root, struct radix_tree_root *radix)
104 {
105         int ret;
106         char buf[128];
107         unsigned long oid;
108
109         ret = find_num(radix, &oid, 1);
110         if (ret < 0)
111                 return 0;
112         sprintf(buf, "str-%lu", oid);
113
114         ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid,
115                                     file_oid, 1);
116         if (ret != -EEXIST) {
117                 printf("insert on %s gave us %d\n", buf, ret);
118                 return 1;
119         }
120         return 0;
121 }
122
123 static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
124                    struct radix_tree_root *radix)
125 {
126         int ret;
127         char buf[128];
128         unsigned long oid;
129         struct btrfs_path path;
130         unsigned long *ptr;
131
132         ret = find_num(radix, &oid, 1);
133         if (ret < 0)
134                 return 0;
135         sprintf(buf, "str-%lu", oid);
136         btrfs_init_path(&path);
137         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
138                                     strlen(buf), -1);
139         if (ret)
140                 goto out_release;
141         ret = btrfs_del_item(trans, root, &path);
142         if (ret)
143                 goto out_release;
144         btrfs_release_path(root, &path);
145         ptr = radix_tree_delete(radix, oid);
146         if (!ptr) {
147                 ret = -5555;
148                 goto out;
149         }
150         return 0;
151 out_release:
152         btrfs_release_path(root, &path);
153 out:
154         printf("failed to delete %lu %d\n", oid, ret);
155         return -1;
156 }
157
158 static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root
159                        *root, struct radix_tree_root *radix)
160 {
161         struct btrfs_path path;
162         char buf[128];
163         int ret;
164         unsigned long oid;
165
166         ret = find_num(radix, &oid, 1);
167         if (ret < 0)
168                 return 0;
169         sprintf(buf, "str-%lu", oid);
170         btrfs_init_path(&path);
171         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
172                                     strlen(buf), 0);
173         btrfs_release_path(root, &path);
174         if (ret) {
175                 printf("unable to find key %lu\n", oid);
176                 return -1;
177         }
178         return 0;
179 }
180
181 static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root
182                          *root, struct radix_tree_root *radix)
183 {
184         struct btrfs_path path;
185         char buf[128];
186         int ret;
187         unsigned long oid;
188
189         ret = find_num(radix, &oid, 0);
190         if (ret < 0)
191                 return 0;
192         sprintf(buf, "str-%lu", oid);
193         btrfs_init_path(&path);
194         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
195                                     strlen(buf), 0);
196         btrfs_release_path(root, &path);
197         if (!ret) {
198                 printf("able to find key that should not exist %lu\n", oid);
199                 return -1;
200         }
201         return 0;
202 }
203
204 static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root
205                       *root, struct radix_tree_root *radix, int nr)
206 {
207         struct btrfs_path path;
208         struct btrfs_key key;
209         unsigned long found = 0;
210         u32 found_len;
211         int ret;
212         int slot;
213         int *ptr;
214         int count = 0;
215         char buf[128];
216         struct btrfs_dir_item *di;
217
218         key.offset = (u64)-1;
219         key.flags = 0;
220         btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
221         key.objectid = dir_oid;
222         while(nr-- >= 0) {
223                 btrfs_init_path(&path);
224                 ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
225                 if (ret < 0) {
226                         btrfs_release_path(root, &path);
227                         return ret;
228                 }
229                 if (ret != 0) {
230                         if (path.slots[0] == 0) {
231                                 btrfs_release_path(root, &path);
232                                 break;
233                         }
234                         path.slots[0] -= 1;
235                 }
236                 slot = path.slots[0];
237                 di = btrfs_item_ptr(&path.nodes[0]->leaf, slot,
238                                     struct btrfs_dir_item);
239                 found_len = btrfs_dir_name_len(di);
240                 memcpy(buf, (char *)(di + 1), found_len);
241                 BUG_ON(found_len > 128);
242                 buf[found_len] = '\0';
243                 found = atoi(buf + 4);
244                 ret = btrfs_del_item(trans, root, &path);
245                 count++;
246                 if (ret) {
247                         fprintf(stderr,
248                                 "failed to remove %lu from tree\n",
249                                 found);
250                         return -1;
251                 }
252                 btrfs_release_path(root, &path);
253                 ptr = radix_tree_delete(radix, found);
254                 if (!ptr)
255                         goto error;
256                 if (!keep_running)
257                         break;
258         }
259         return 0;
260 error:
261         fprintf(stderr, "failed to delete from the radix %lu\n", found);
262         return -1;
263 }
264
265 static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root,
266                      struct radix_tree_root *radix, int count)
267 {
268         int i;
269         int ret = 0;
270         for (i = 0; i < count; i++) {
271                 ret = ins_one(trans, root, radix);
272                 if (ret) {
273                         fprintf(stderr, "fill failed\n");
274                         goto out;
275                 }
276                 if (i % 1000 == 0) {
277                         ret = btrfs_commit_transaction(trans, root, &super);
278                         if (ret) {
279                                 fprintf(stderr, "fill commit failed\n");
280                                 return ret;
281                         }
282                 }
283                 if (i && i % 10000 == 0) {
284                         printf("bigfill %d\n", i);
285                 }
286                 if (!keep_running)
287                         break;
288         }
289 out:
290         return ret;
291 }
292
293 static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root,
294                    struct radix_tree_root *radix)
295 {
296         int ret;
297         int nr = rand() % 5000;
298         static int run_nr = 0;
299
300         /* do the bulk op much less frequently */
301         if (run_nr++ % 100)
302                 return 0;
303         ret = empty_tree(trans, root, radix, nr);
304         if (ret)
305                 return ret;
306         ret = fill_tree(trans, root, radix, nr);
307         if (ret)
308                 return ret;
309         return 0;
310 }
311
312
313 int (*ops[])(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct
314              radix_tree_root *radix) =
315         { ins_one, insert_dup, del_one, lookup_item,
316           lookup_enoent, bulk_op };
317
318 void sigstopper(int ignored)
319 {
320         keep_running = 0;
321         fprintf(stderr, "caught exit signal, stopping\n");
322 }
323
324 int print_usage(void)
325 {
326         printf("usage: tester [-ih] [-c count] [-f count]\n");
327         printf("\t -c count -- iteration count after filling\n");
328         printf("\t -f count -- run this many random inserts before starting\n");
329         printf("\t -i       -- only do initial fill\n");
330         printf("\t -h       -- this help text\n");
331         exit(1);
332 }
333 int main(int ac, char **av)
334 {
335         RADIX_TREE(radix, GFP_KERNEL);
336         struct btrfs_root *root;
337         int i;
338         int ret;
339         int count;
340         int op;
341         int iterations = 20000;
342         int init_fill_count = 800000;
343         int err = 0;
344         int initial_only = 0;
345         struct btrfs_trans_handle *trans;
346         radix_tree_init();
347
348         printf("removing old tree\n");
349         unlink("dbfile");
350         root = open_ctree("dbfile", &super);
351         trans = btrfs_start_transaction(root, 1);
352
353         signal(SIGTERM, sigstopper);
354         signal(SIGINT, sigstopper);
355
356         for (i = 1 ; i < ac ; i++) {
357                 if (strcmp(av[i], "-i") == 0) {
358                         initial_only = 1;
359                 } else if (strcmp(av[i], "-c") == 0) {
360                         iterations = atoi(av[i+1]);
361                         i++;
362                 } else if (strcmp(av[i], "-f") == 0) {
363                         init_fill_count = atoi(av[i+1]);
364                         i++;
365                 } else {
366                         print_usage();
367                 }
368         }
369         printf("initial fill\n");
370         ret = fill_tree(trans, root, &radix, init_fill_count);
371         printf("starting run\n");
372         if (ret) {
373                 err = ret;
374                 goto out;
375         }
376         if (initial_only == 1) {
377                 goto out;
378         }
379         for (i = 0; i < iterations; i++) {
380                 op = rand() % ARRAY_SIZE(ops);
381                 count = rand() % 128;
382                 if (i % 2000 == 0) {
383                         printf("%d\n", i);
384                         fflush(stdout);
385                 }
386                 if (i && i % 5000 == 0) {
387                         printf("open & close, root level %d nritems %d\n",
388                                 btrfs_header_level(&root->node->node.header),
389                                 btrfs_header_nritems(&root->node->node.header));
390                         close_ctree(root, &super);
391                         root = open_ctree("dbfile", &super);
392                 }
393                 while(count--) {
394                         ret = ops[op](trans, root, &radix);
395                         if (ret) {
396                                 fprintf(stderr, "op %d failed %d:%d\n",
397                                         op, i, iterations);
398                                 btrfs_print_tree(root, root->node);
399                                 fprintf(stderr, "op %d failed %d:%d\n",
400                                         op, i, iterations);
401                                 err = ret;
402                                 goto out;
403                         }
404                         if (ops[op] == bulk_op)
405                                 break;
406                         if (keep_running == 0) {
407                                 err = 0;
408                                 goto out;
409                         }
410                 }
411         }
412 out:
413         close_ctree(root, &super);
414         return err;
415 }
416