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