Better block record keeping, real mkfs
[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 void initial_inode_init(struct btrfs_root *root,
43                                struct btrfs_inode_item *inode_item)
44 {
45         memset(inode_item, 0, sizeof(*inode_item));
46         btrfs_set_inode_generation(inode_item, root->fs_info->generation);
47 }
48
49 static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
50                    struct radix_tree_root *radix)
51 {
52         int ret;
53         char buf[128];
54         unsigned long oid;
55         u64 objectid;
56         struct btrfs_path path;
57         struct btrfs_key inode_map;
58         struct btrfs_inode_item inode_item;
59
60         find_num(radix, &oid, 0);
61         sprintf(buf, "str-%lu", oid);
62
63         ret = btrfs_find_free_objectid(trans, root, dir_oid + 1, &objectid);
64         if (ret)
65                 goto error;
66
67         inode_map.objectid = objectid;
68         inode_map.flags = 0;
69         inode_map.offset = 0;
70
71         ret = btrfs_insert_inode_map(trans, root, objectid, &inode_map);
72         if (ret)
73                 goto error;
74
75         initial_inode_init(root, &inode_item);
76         ret = btrfs_insert_inode(trans, root, objectid, &inode_item);
77         if (ret)
78                 goto error;
79         ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid,
80                                     objectid, 1);
81         if (ret)
82                 goto error;
83
84         radix_tree_preload(GFP_KERNEL);
85         ret = radix_tree_insert(radix, oid, (void *)oid);
86         radix_tree_preload_end();
87         if (ret)
88                 goto error;
89         return ret;
90 error:
91         if (ret != -EEXIST)
92                 goto fatal;
93
94         /*
95          * if we got an EEXIST, it may be due to hash collision, double
96          * check
97          */
98         btrfs_init_path(&path);
99         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
100                                     strlen(buf), 0);
101         if (ret)
102                 goto fatal_release;
103         if (!btrfs_match_dir_item_name(root, &path, buf, strlen(buf))) {
104                 struct btrfs_dir_item *di;
105                 char *found;
106                 u32 found_len;
107                 u64 myhash;
108                 u64 foundhash;
109
110                 di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0],
111                                     struct btrfs_dir_item);
112                 found = (char *)(di + 1);
113                 found_len = btrfs_dir_name_len(di);
114                 btrfs_name_hash(buf, strlen(buf), &myhash);
115                 btrfs_name_hash(found, found_len, &foundhash);
116                 if (myhash != foundhash)
117                         goto fatal_release;
118                 btrfs_release_path(root, &path);
119                 return 0;
120         }
121 fatal_release:
122         btrfs_release_path(root, &path);
123 fatal:
124         printf("failed to insert %lu ret %d\n", oid, ret);
125         return -1;
126 }
127
128 static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root
129                       *root, struct radix_tree_root *radix)
130 {
131         int ret;
132         char buf[128];
133         unsigned long oid;
134
135         ret = find_num(radix, &oid, 1);
136         if (ret < 0)
137                 return 0;
138         sprintf(buf, "str-%lu", oid);
139
140         ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid,
141                                     file_oid, 1);
142         if (ret != -EEXIST) {
143                 printf("insert on %s gave us %d\n", buf, ret);
144                 return 1;
145         }
146         return 0;
147 }
148
149 static int del_dir_item(struct btrfs_trans_handle *trans,
150                         struct btrfs_root *root,
151                         struct radix_tree_root *radix,
152                         unsigned long radix_index,
153                         struct btrfs_path *path)
154 {
155         int ret;
156         unsigned long *ptr;
157         u64 file_objectid;
158         struct btrfs_dir_item *di;
159
160         /* find the inode number of the file */
161         di = btrfs_item_ptr(&path->nodes[0]->leaf, path->slots[0],
162                             struct btrfs_dir_item);
163         file_objectid = btrfs_dir_objectid(di);
164
165         /* delete the directory item */
166         ret = btrfs_del_item(trans, root, path);
167         if (ret)
168                 goto out_release;
169         btrfs_release_path(root, path);
170
171         /* delete the inode */
172         btrfs_init_path(path);
173         ret = btrfs_lookup_inode(trans, root, path, file_objectid, -1);
174         if (ret)
175                 goto out_release;
176         ret = btrfs_del_item(trans, root, path);
177         if (ret)
178                 goto out_release;
179         btrfs_release_path(root, path);
180
181         /* delete the inode mapping */
182         btrfs_init_path(path);
183         ret = btrfs_lookup_inode_map(trans, root, path, file_objectid, -1);
184         if (ret)
185                 goto out_release;
186         ret = btrfs_del_item(trans, root->fs_info->inode_root, path);
187         if (ret)
188                 goto out_release;
189
190         if (root->fs_info->last_inode_alloc > file_objectid)
191                 root->fs_info->last_inode_alloc = file_objectid;
192         btrfs_release_path(root, path);
193         ptr = radix_tree_delete(radix, radix_index);
194         if (!ptr) {
195                 ret = -5555;
196                 goto out;
197         }
198         return 0;
199 out_release:
200         btrfs_release_path(root, path);
201 out:
202         printf("failed to delete %lu %d\n", radix_index, ret);
203         return -1;
204 }
205
206 static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
207                    struct radix_tree_root *radix)
208 {
209         int ret;
210         char buf[128];
211         unsigned long oid;
212         struct btrfs_path path;
213
214         ret = find_num(radix, &oid, 1);
215         if (ret < 0)
216                 return 0;
217         sprintf(buf, "str-%lu", oid);
218         btrfs_init_path(&path);
219         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
220                                     strlen(buf), -1);
221         if (ret)
222                 goto out_release;
223
224         ret = del_dir_item(trans, root, radix, oid, &path);
225         if (ret)
226                 goto out_release;
227         return ret;
228 out_release:
229         btrfs_release_path(root, &path);
230         printf("failed to delete %lu %d\n", oid, ret);
231         return -1;
232 }
233
234 static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root
235                        *root, struct radix_tree_root *radix)
236 {
237         struct btrfs_path path;
238         char buf[128];
239         int ret;
240         unsigned long oid;
241         u64 objectid;
242         struct btrfs_dir_item *di;
243
244         ret = find_num(radix, &oid, 1);
245         if (ret < 0)
246                 return 0;
247         sprintf(buf, "str-%lu", oid);
248         btrfs_init_path(&path);
249         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
250                                     strlen(buf), 0);
251         if (!ret) {
252                 di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0],
253                                     struct btrfs_dir_item);
254                 objectid = btrfs_dir_objectid(di);
255                 btrfs_release_path(root, &path);
256                 btrfs_init_path(&path);
257                 ret = btrfs_lookup_inode_map(trans, root, &path, objectid, 0);
258         }
259         btrfs_release_path(root, &path);
260         if (ret) {
261                 printf("unable to find key %lu\n", oid);
262                 return -1;
263         }
264         return 0;
265 }
266
267 static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root
268                          *root, struct radix_tree_root *radix)
269 {
270         struct btrfs_path path;
271         char buf[128];
272         int ret;
273         unsigned long oid;
274
275         ret = find_num(radix, &oid, 0);
276         if (ret < 0)
277                 return 0;
278         sprintf(buf, "str-%lu", oid);
279         btrfs_init_path(&path);
280         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
281                                     strlen(buf), 0);
282         btrfs_release_path(root, &path);
283         if (!ret) {
284                 printf("able to find key that should not exist %lu\n", oid);
285                 return -1;
286         }
287         return 0;
288 }
289
290 static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root
291                       *root, struct radix_tree_root *radix, int nr)
292 {
293         struct btrfs_path path;
294         struct btrfs_key key;
295         unsigned long found = 0;
296         u32 found_len;
297         int ret;
298         int slot;
299         int count = 0;
300         char buf[128];
301         struct btrfs_dir_item *di;
302
303         key.offset = (u64)-1;
304         key.flags = 0;
305         btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
306         key.objectid = dir_oid;
307         while(nr-- >= 0) {
308                 btrfs_init_path(&path);
309                 ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
310                 if (ret < 0) {
311                         btrfs_release_path(root, &path);
312                         return ret;
313                 }
314                 if (ret != 0) {
315                         if (path.slots[0] == 0) {
316                                 btrfs_release_path(root, &path);
317                                 break;
318                         }
319                         path.slots[0] -= 1;
320                 }
321                 slot = path.slots[0];
322                 di = btrfs_item_ptr(&path.nodes[0]->leaf, slot,
323                                     struct btrfs_dir_item);
324                 found_len = btrfs_dir_name_len(di);
325                 memcpy(buf, (char *)(di + 1), found_len);
326                 BUG_ON(found_len > 128);
327                 buf[found_len] = '\0';
328                 found = atoi(buf + 4);
329                 ret = del_dir_item(trans, root, radix, found, &path);
330                 count++;
331                 if (ret) {
332                         fprintf(stderr,
333                                 "failed to remove %lu from tree\n",
334                                 found);
335                         return -1;
336                 }
337                 if (!keep_running)
338                         break;
339         }
340         return 0;
341         fprintf(stderr, "failed to delete from the radix %lu\n", found);
342         return -1;
343 }
344
345 static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root,
346                      struct radix_tree_root *radix, int count)
347 {
348         int i;
349         int ret = 0;
350         for (i = 0; i < count; i++) {
351                 ret = ins_one(trans, root, radix);
352                 if (ret) {
353                         fprintf(stderr, "fill failed\n");
354                         goto out;
355                 }
356                 if (i % 1000 == 0) {
357                         ret = btrfs_commit_transaction(trans, root, &super);
358                         if (ret) {
359                                 fprintf(stderr, "fill commit failed\n");
360                                 return ret;
361                         }
362                 }
363                 if (i && i % 10000 == 0) {
364                         printf("bigfill %d\n", i);
365                 }
366                 if (!keep_running)
367                         break;
368         }
369 out:
370         return ret;
371 }
372
373 static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root,
374                    struct radix_tree_root *radix)
375 {
376         int ret;
377         int nr = rand() % 5000;
378         static int run_nr = 0;
379
380         /* do the bulk op much less frequently */
381         if (run_nr++ % 100)
382                 return 0;
383         ret = empty_tree(trans, root, radix, nr);
384         if (ret)
385                 return ret;
386         ret = fill_tree(trans, root, radix, nr);
387         if (ret)
388                 return ret;
389         return 0;
390 }
391
392
393 int (*ops[])(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct
394              radix_tree_root *radix) =
395         { ins_one, insert_dup, del_one, lookup_item,
396           lookup_enoent, bulk_op };
397
398 void sigstopper(int ignored)
399 {
400         keep_running = 0;
401         fprintf(stderr, "caught exit signal, stopping\n");
402 }
403
404 int print_usage(void)
405 {
406         printf("usage: tester [-ih] [-c count] [-f count]\n");
407         printf("\t -c count -- iteration count after filling\n");
408         printf("\t -f count -- run this many random inserts before starting\n");
409         printf("\t -i       -- only do initial fill\n");
410         printf("\t -h       -- this help text\n");
411         exit(1);
412 }
413 int main(int ac, char **av)
414 {
415         RADIX_TREE(radix, GFP_KERNEL);
416         struct btrfs_root *root;
417         int i;
418         int ret;
419         int count;
420         int op;
421         int iterations = 20000;
422         int init_fill_count = 800000;
423         int err = 0;
424         int initial_only = 0;
425         struct btrfs_trans_handle *trans;
426         radix_tree_init();
427
428         root = open_ctree("dbfile", &super);
429         trans = btrfs_start_transaction(root, 1);
430
431         signal(SIGTERM, sigstopper);
432         signal(SIGINT, sigstopper);
433
434         for (i = 1 ; i < ac ; i++) {
435                 if (strcmp(av[i], "-i") == 0) {
436                         initial_only = 1;
437                 } else if (strcmp(av[i], "-c") == 0) {
438                         iterations = atoi(av[i+1]);
439                         i++;
440                 } else if (strcmp(av[i], "-f") == 0) {
441                         init_fill_count = atoi(av[i+1]);
442                         i++;
443                 } else {
444                         print_usage();
445                 }
446         }
447         printf("initial fill\n");
448         ret = fill_tree(trans, root, &radix, init_fill_count);
449         printf("starting run\n");
450         if (ret) {
451                 err = ret;
452                 goto out;
453         }
454         if (initial_only == 1) {
455                 goto out;
456         }
457         for (i = 0; i < iterations; i++) {
458                 op = rand() % ARRAY_SIZE(ops);
459                 count = rand() % 128;
460                 if (i % 2000 == 0) {
461                         printf("%d\n", i);
462                         fflush(stdout);
463                 }
464                 if (i && i % 5000 == 0) {
465                         printf("open & close, root level %d nritems %d\n",
466                                 btrfs_header_level(&root->node->node.header),
467                                 btrfs_header_nritems(&root->node->node.header));
468                         close_ctree(root, &super);
469                         root = open_ctree("dbfile", &super);
470                 }
471                 while(count--) {
472                         ret = ops[op](trans, root, &radix);
473                         if (ret) {
474                                 fprintf(stderr, "op %d failed %d:%d\n",
475                                         op, i, iterations);
476                                 btrfs_print_tree(root, root->node);
477                                 fprintf(stderr, "op %d failed %d:%d\n",
478                                         op, i, iterations);
479                                 err = ret;
480                                 goto out;
481                         }
482                         if (ops[op] == bulk_op)
483                                 break;
484                         if (keep_running == 0) {
485                                 err = 0;
486                                 goto out;
487                         }
488                 }
489         }
490 out:
491         close_ctree(root, &super);
492         return err;
493 }
494