/* * Copyright (C) 2007 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License v2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. */ #include #include #include #include "kerncompat.h" #include "radix-tree.h" #include "ctree.h" #include "disk-io.h" #include "print-tree.h" #include "transaction.h" int keep_running = 1; struct btrfs_super_block super; static int setup_key(struct radix_tree_root *root, struct btrfs_key *key, int exists) { int num = rand(); unsigned long res[2]; int ret; key->flags = 0; key->type = BTRFS_STRING_ITEM_KEY; key->offset = 0; again: ret = radix_tree_gang_lookup(root, (void **)res, num, 2); if (exists) { if (ret == 0) return -EEXIST; num = res[0]; } else if (ret != 0 && num == res[0]) { num++; if (ret > 1 && num == res[1]) { num++; goto again; } } key->objectid = num; return 0; } static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct radix_tree_root *radix) { struct btrfs_path path; struct btrfs_key key; int ret; char buf[128]; unsigned long oid; btrfs_init_path(&path); ret = setup_key(radix, &key, 0); sprintf(buf, "str-%llu\n", (unsigned long long)key.objectid); ret = btrfs_insert_item(trans, root, &key, buf, strlen(buf)); if (ret) goto error; oid = (unsigned long)key.objectid; radix_tree_preload(GFP_KERNEL); ret = radix_tree_insert(radix, oid, (void *)oid); radix_tree_preload_end(); if (ret) goto error; return ret; error: printf("failed to insert %llu\n", (unsigned long long)key.objectid); return ret; } static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct radix_tree_root *radix) { struct btrfs_path path; struct btrfs_key key; int ret; char buf[128]; btrfs_init_path(&path); ret = setup_key(radix, &key, 1); if (ret < 0) return 0; sprintf(buf, "str-%llu\n", (unsigned long long)key.objectid); ret = btrfs_insert_item(trans, root, &key, buf, strlen(buf)); if (ret != -EEXIST) { printf("insert on %llu gave us %d\n", (unsigned long long)key.objectid, ret); return ret; } return 0; } static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct radix_tree_root *radix) { struct btrfs_path path; struct btrfs_key key; int ret; unsigned long *ptr; btrfs_init_path(&path); ret = setup_key(radix, &key, 1); if (ret < 0) return 0; ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret) goto error; ret = btrfs_del_item(trans, root, &path); btrfs_release_path(&path); if (ret != 0) goto error; ptr = radix_tree_delete(radix, key.objectid); if (!ptr) goto error; return 0; error: printf("failed to delete %llu\n", (unsigned long long)key.objectid); return ret; } static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct radix_tree_root *radix) { struct btrfs_path path; struct btrfs_key key; int ret; btrfs_init_path(&path); ret = setup_key(radix, &key, 1); if (ret < 0) return 0; ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); btrfs_release_path(&path); if (ret) goto error; return 0; error: printf("unable to find key %llu\n", (unsigned long long)key.objectid); return ret; } static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct radix_tree_root *radix) { struct btrfs_path path; struct btrfs_key key; int ret; btrfs_init_path(&path); ret = setup_key(radix, &key, 0); if (ret < 0) return ret; ret = btrfs_search_slot(trans, root, &key, &path, 0, 0); btrfs_release_path(&path); if (ret <= 0) goto error; return 0; error: printf("able to find key that should not exist %llu\n", (unsigned long long)key.objectid); return -EEXIST; } static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct radix_tree_root *radix, int nr) { struct btrfs_path path; struct btrfs_key key; unsigned long found = 0; int ret; int slot; int *ptr; int count = 0; key.offset = 0; key.flags = 0; key.type = BTRFS_STRING_ITEM_KEY; key.objectid = (unsigned long)-1; while(nr-- >= 0) { btrfs_init_path(&path); ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret < 0) { btrfs_release_path(&path); return ret; } if (ret != 0) { if (path.slots[0] == 0) { btrfs_release_path(&path); break; } path.slots[0] -= 1; } slot = path.slots[0]; found = btrfs_disk_key_objectid( &path.nodes[0]->leaf.items[slot].key); ret = btrfs_del_item(trans, root, &path); count++; if (ret) { fprintf(stderr, "failed to remove %lu from tree\n", found); return ret; } btrfs_release_path(&path); ptr = radix_tree_delete(radix, found); if (!ptr) goto error; if (!keep_running) break; } return 0; error: fprintf(stderr, "failed to delete from the radix %lu\n", found); return -ENOENT; } static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct radix_tree_root *radix, int count) { int i; int ret = 0; for (i = 0; i < count; i++) { ret = ins_one(trans, root, radix); if (ret) { fprintf(stderr, "fill failed\n"); goto out; } if (i % 1000 == 0) { ret = btrfs_commit_transaction(trans, root, &super); if (ret) { fprintf(stderr, "fill commit failed\n"); return ret; } } if (i && i % 10000 == 0) { printf("bigfill %d\n", i); } if (!keep_running) break; } out: return ret; } static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct radix_tree_root *radix) { int ret; int nr = rand() % 5000; static int run_nr = 0; /* do the bulk op much less frequently */ if (run_nr++ % 100) return 0; ret = empty_tree(trans, root, radix, nr); if (ret) return ret; ret = fill_tree(trans, root, radix, nr); if (ret) return ret; return 0; } int (*ops[])(struct btrfs_trans_handle *, struct btrfs_root *root, struct radix_tree_root *radix) = { ins_one, insert_dup, del_one, lookup_item, lookup_enoent, bulk_op }; static int fill_radix(struct btrfs_root *root, struct radix_tree_root *radix) { struct btrfs_path path; struct btrfs_key key; unsigned long found = 0; int ret; int slot; int i; key.offset = 0; key.flags = 0; key.type = BTRFS_STRING_ITEM_KEY; key.objectid = (unsigned long)-1; while(1) { btrfs_init_path(&path); ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) { btrfs_release_path(&path); return ret; } slot = path.slots[0]; if (ret != 0) { if (slot == 0) { btrfs_release_path(&path); break; } slot -= 1; } for (i = slot; i >= 0; i--) { found = btrfs_disk_key_objectid(&path.nodes[0]-> leaf.items[i].key); radix_tree_preload(GFP_KERNEL); ret = radix_tree_insert(radix, found, (void *)found); if (ret) { fprintf(stderr, "failed to insert %lu into radix\n", found); exit(1); } radix_tree_preload_end(); } btrfs_release_path(&path); key.objectid = found - 1; if (key.objectid > found) break; } return 0; } void sigstopper(int ignored) { keep_running = 0; fprintf(stderr, "caught exit signal, stopping\n"); } int print_usage(void) { printf("usage: tester [-ih] [-c count] [-f count]\n"); printf("\t -c count -- iteration count after filling\n"); printf("\t -f count -- run this many random inserts before starting\n"); printf("\t -i -- only do initial fill\n"); printf("\t -h -- this help text\n"); exit(1); } int main(int ac, char **av) { RADIX_TREE(radix, GFP_KERNEL); struct btrfs_root *root; int i; int ret; int count; int op; int iterations = 20000; int init_fill_count = 800000; int err = 0; int initial_only = 0; struct btrfs_trans_handle *trans; radix_tree_init(); root = open_ctree("dbfile", &super); if (!root) { fprintf(stderr, "Open ctree failed\n"); exit(1); } fill_radix(root, &radix); signal(SIGTERM, sigstopper); signal(SIGINT, sigstopper); for (i = 1 ; i < ac ; i++) { if (strcmp(av[i], "-i") == 0) { initial_only = 1; } else if (strcmp(av[i], "-c") == 0) { iterations = atoi(av[i+1]); i++; } else if (strcmp(av[i], "-f") == 0) { init_fill_count = atoi(av[i+1]); i++; } else { print_usage(); } } printf("initial fill\n"); trans = btrfs_start_transaction(root, 1); BUG_ON(IS_ERR(trans)); ret = fill_tree(trans, root, &radix, init_fill_count); printf("starting run\n"); if (ret) { err = ret; goto out; } if (initial_only == 1) { goto out; } for (i = 0; i < iterations; i++) { op = rand() % ARRAY_SIZE(ops); count = rand() % 128; if (i % 2000 == 0) { printf("%d\n", i); fflush(stdout); } if (i && i % 5000 == 0) { printf("open & close, root level %d nritems %d\n", btrfs_header_level(&root->node->node.header), btrfs_header_nritems(&root->node->node.header)); close_ctree(root, &super); root = open_ctree("dbfile", &super); if (!root) { fprintf(stderr, "Open ctree failed\n"); goto out; } } while(count--) { ret = ops[op](trans, root, &radix); if (ret) { fprintf(stderr, "op %d failed %d:%d\n", op, i, iterations); btrfs_print_tree(root, root->node, 1); fprintf(stderr, "op %d failed %d:%d\n", op, i, iterations); err = ret; goto out; } if (ops[op] == bulk_op) break; if (keep_running == 0) { err = 0; goto out; } } } out: close_ctree(root, &super); return !!err; }