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