#define _XOPEN_SOURCE 500 #include #include #define __USE_GNU #include #include "kerncompat.h" #include "radix-tree.h" #include "ctree.h" #include "disk-io.h" #include "print-tree.h" #include "transaction.h" #include "bit-radix.h" static u64 blocks_used = 0; static u64 total_csum_bytes = 0; static u64 total_btree_blocks = 0; static u64 btree_space_waste = 0; struct extent_record { struct btrfs_disk_key parent_key; struct btrfs_disk_key node_key; u64 start; u64 nr; u64 owner; u32 refs; u32 extent_item_refs; }; static int check_node(struct btrfs_root *root, struct btrfs_disk_key *parent_key, struct btrfs_node *node) { int i; u32 nritems = btrfs_header_nritems(&node->header); if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root)) return 1; if (parent_key->flags) { if (memcmp(parent_key, &node->ptrs[0].key, sizeof(struct btrfs_disk_key))) return 1; } for (i = 0; nritems > 1 && i < nritems - 2; i++) { struct btrfs_key cpukey; btrfs_disk_key_to_cpu(&cpukey, &node->ptrs[i + 1].key); if (btrfs_comp_keys(&node->ptrs[i].key, &cpukey) >= 0) return 1; } return 0; } static int check_leaf(struct btrfs_root *root, struct btrfs_disk_key *parent_key, struct btrfs_leaf *leaf) { int i; u32 nritems = btrfs_header_nritems(&leaf->header); if (btrfs_header_level(&leaf->header) != 0) { fprintf(stderr, "leaf is not a leaf %Lu\n", btrfs_header_blocknr(&leaf->header)); return 1; } if (btrfs_leaf_free_space(root, leaf) < 0) { fprintf(stderr, "leaf free space incorrect %Lu %d\n", btrfs_header_blocknr(&leaf->header), btrfs_leaf_free_space(root, leaf)); return 1; } if (nritems == 0) return 0; if (parent_key->flags) { if (memcmp(parent_key, &leaf->items[0].key, sizeof(struct btrfs_disk_key))) { fprintf(stderr, "leaf parent key incorrect %Lu\n", btrfs_header_blocknr(&leaf->header)); return 1; } } for (i = 0; nritems > 1 && i < nritems - 2; i++) { struct btrfs_key cpukey; btrfs_disk_key_to_cpu(&cpukey, &leaf->items[i + 1].key); if (btrfs_comp_keys(&leaf->items[i].key, &cpukey) >= 0) return 1; if (btrfs_item_offset(leaf->items + i) != btrfs_item_end(leaf->items + i + 1)) return 1; if (i == 0) { if (btrfs_item_offset(leaf->items + i) + btrfs_item_size(leaf->items + i) != BTRFS_LEAF_DATA_SIZE(root)) return 1; } } return 0; } static int check_block(struct btrfs_root *root, struct radix_tree_root *extent_radix, struct btrfs_buffer *buf) { struct extent_record *rec; int ret = 1; rec = radix_tree_lookup(extent_radix, buf->blocknr); if (!rec) return 1; if (btrfs_is_leaf(&buf->node)) { ret = check_leaf(root, &rec->parent_key, &buf->leaf); } else { ret = check_node(root, &rec->parent_key, &buf->node); } if (!ret && rec->extent_item_refs == rec->refs && rec->refs > 0) { radix_tree_delete(extent_radix, rec->start); free(rec); return ret; } return ret; } static int add_extent_rec(struct radix_tree_root *extent_radix, struct btrfs_disk_key *parent_key, u64 ref, u64 start, u64 nr, u64 owner, u32 extent_item_refs, int inc_ref) { struct extent_record *rec; int ret = 0; rec = radix_tree_lookup(extent_radix, start); if (rec) { if (inc_ref) rec->refs++; if (start != rec->start) { fprintf(stderr, "warning, start mismatch %Lu %Lu\n", rec->start, start); ret = 1; } if (extent_item_refs) { if (rec->extent_item_refs) { fprintf(stderr, "block %Lu rec extent_item_refs %u, passed %u\n", start, rec->extent_item_refs, extent_item_refs); } rec->extent_item_refs = extent_item_refs; } return ret; } rec = malloc(sizeof(*rec)); if (start == 0) extent_item_refs = 0; rec->start = start; rec->nr = nr; rec->owner = owner; if (inc_ref) rec->refs = 1; else rec->refs = 0; if (extent_item_refs) rec->extent_item_refs = extent_item_refs; else rec->extent_item_refs = 0; if (parent_key) memcpy(&rec->parent_key, parent_key, sizeof(*parent_key)); else memset(&rec->parent_key, 0, sizeof(*parent_key)); ret = radix_tree_insert(extent_radix, start, rec); BUG_ON(ret); blocks_used += nr; return ret; } static int add_pending(struct radix_tree_root *pending, struct radix_tree_root *seen, u64 blocknr) { if (test_radix_bit(seen, blocknr)) return -EEXIST; set_radix_bit(pending, blocknr); set_radix_bit(seen, blocknr); return 0; } static int pick_next_pending(struct radix_tree_root *pending, struct radix_tree_root *reada, struct radix_tree_root *nodes, u64 last, unsigned long *bits, int bits_nr) { unsigned long node_start = last; int ret; ret = find_first_radix_bit(reada, bits, 0, 1); if (ret && ret > 16) { return ret; } if (node_start > 8) node_start -= 8; ret = find_first_radix_bit(nodes, bits, node_start, bits_nr); if (!ret) ret = find_first_radix_bit(nodes, bits, 0, bits_nr); if (ret) { if (bits_nr - ret > 8) { int ret2; u64 sequential; ret2 = find_first_radix_bit(pending, bits + ret, bits[0], bits_nr - ret); sequential = bits[0]; while(ret2 > 0) { if (bits[ret] - sequential > 8) break; sequential = bits[ret]; ret++; ret2--; } } return ret; } return find_first_radix_bit(pending, bits, 0, bits_nr); } static struct btrfs_buffer reada_buf; static int run_next_block(struct btrfs_root *root, unsigned long *bits, int bits_nr, u64 *last, struct radix_tree_root *pending, struct radix_tree_root *seen, struct radix_tree_root *reada, struct radix_tree_root *nodes, struct radix_tree_root *extent_radix) { struct btrfs_buffer *buf; u64 blocknr; int ret; int i; int nritems; struct btrfs_leaf *leaf; struct btrfs_node *node; struct btrfs_disk_key *disk_key; u64 last_block = 0; ret = pick_next_pending(pending, reada, nodes, *last, bits, bits_nr); if (ret == 0) { return 1; } for(i = 0; i < ret; i++) { u64 offset; if (test_radix_bit(reada, bits[i])) continue; set_radix_bit(reada, bits[i]); btrfs_map_bh_to_logical(root, &reada_buf, bits[i]); offset = reada_buf.dev_blocknr * root->blocksize; last_block = bits[i]; readahead(reada_buf.fd, offset, root->blocksize); } *last = bits[0]; blocknr = bits[0]; clear_radix_bit(pending, blocknr); clear_radix_bit(reada, blocknr); clear_radix_bit(nodes, blocknr); buf = read_tree_block(root, blocknr); nritems = btrfs_header_nritems(&buf->node.header); ret = check_block(root, extent_radix, buf); if (ret) { fprintf(stderr, "bad block %Lu\n", blocknr); } if (btrfs_is_leaf(&buf->node)) { leaf = &buf->leaf; btree_space_waste += btrfs_leaf_free_space(root, leaf); for (i = 0; i < nritems; i++) { struct btrfs_file_extent_item *fi; disk_key = &leaf->items[i].key; if (btrfs_disk_key_type(disk_key) == BTRFS_EXTENT_ITEM_KEY) { struct btrfs_key found; struct btrfs_extent_item *ei; btrfs_disk_key_to_cpu(&found, &leaf->items[i].key); ei = btrfs_item_ptr(leaf, i, struct btrfs_extent_item); add_extent_rec(extent_radix, NULL, 0, found.objectid, found.offset, btrfs_extent_owner(ei), btrfs_extent_refs(ei), 0); continue; } if (btrfs_disk_key_type(disk_key) == BTRFS_CSUM_ITEM_KEY) { total_csum_bytes += btrfs_item_size(leaf->items + i); continue; } if (btrfs_disk_key_type(disk_key) == BTRFS_BLOCK_GROUP_ITEM_KEY) { struct btrfs_block_group_item *bi; bi = btrfs_item_ptr(leaf, i, struct btrfs_block_group_item); fprintf(stderr,"block group %Lu %Lu used %Lu\n", btrfs_disk_key_objectid(disk_key), btrfs_disk_key_offset(disk_key), btrfs_block_group_used(bi)); continue; } if (btrfs_disk_key_type(&leaf->items[i].key) != BTRFS_EXTENT_DATA_KEY) continue; fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item); if (btrfs_file_extent_type(fi) != BTRFS_FILE_EXTENT_REG) continue; ret = add_extent_rec(extent_radix, NULL, blocknr, btrfs_file_extent_disk_blocknr(fi), btrfs_file_extent_disk_num_blocks(fi), btrfs_disk_key_objectid(&leaf->items[i].key), 0, 1); BUG_ON(ret); } } else { int level; node = &buf->node; level = btrfs_header_level(&node->header); for (i = 0; i < nritems; i++) { u64 ptr = btrfs_node_blockptr(node, i); ret = add_extent_rec(extent_radix, &node->ptrs[i].key, blocknr, ptr, 1, btrfs_header_owner(&node->header), 0, 1); BUG_ON(ret); if (level > 1) { add_pending(nodes, seen, ptr); } else { add_pending(pending, seen, ptr); } } btree_space_waste += (BTRFS_NODEPTRS_PER_BLOCK(root) - nritems) * sizeof(struct btrfs_key_ptr); } btrfs_block_release(root, buf); total_btree_blocks++; return 0; } static int add_root_to_pending(struct btrfs_buffer *buf, unsigned long *bits, int bits_nr, struct radix_tree_root *extent_radix, struct radix_tree_root *pending, struct radix_tree_root *seen, struct radix_tree_root *reada, struct radix_tree_root *nodes) { if (btrfs_header_level(&buf->node.header) > 0) add_pending(nodes, seen, buf->blocknr); else add_pending(pending, seen, buf->blocknr); add_extent_rec(extent_radix, NULL, 0, buf->blocknr, 1, btrfs_header_owner(&buf->node.header), 0, 1); return 0; } int check_extent_refs(struct btrfs_root *root, struct radix_tree_root *extent_radix) { struct extent_record *rec[64]; int i; int ret; int err = 0; while(1) { ret = radix_tree_gang_lookup(extent_radix, (void **)rec, 0, ARRAY_SIZE(rec)); if (!ret) break; for (i = 0; i < ret; i++) { if (rec[i]->refs != rec[i]->extent_item_refs) { fprintf(stderr, "ref mismatch on [%Lu %Lu] ", rec[i]->start, rec[i]->nr); fprintf(stderr, "extent item %u, found %u\n", rec[i]->extent_item_refs, rec[i]->refs); err = 1; } radix_tree_delete(extent_radix, rec[i]->start); free(rec[i]); } } return err; } int main(int ac, char **av) { struct btrfs_super_block super; struct btrfs_root *root; struct radix_tree_root extent_radix; struct radix_tree_root seen; struct radix_tree_root pending; struct radix_tree_root reada; struct radix_tree_root nodes; struct btrfs_path path; struct btrfs_key key; struct btrfs_key found_key; int ret; u64 last = 0; unsigned long *bits; int bits_nr; struct btrfs_leaf *leaf; int slot; struct btrfs_root_item *ri; radix_tree_init(); INIT_RADIX_TREE(&extent_radix, GFP_NOFS); init_bit_radix(&seen); init_bit_radix(&pending); init_bit_radix(&reada); init_bit_radix(&nodes); root = open_ctree(av[1], &super); bits_nr = 1024 * 1024 / root->blocksize; bits = malloc(bits_nr * sizeof(unsigned long)); if (!bits) { perror("malloc"); exit(1); } add_root_to_pending(root->fs_info->tree_root->node, bits, bits_nr, &extent_radix, &pending, &seen, &reada, &nodes); add_root_to_pending(root->fs_info->dev_root->node, bits, bits_nr, &extent_radix, &pending, &seen, &reada, &nodes); btrfs_init_path(&path); key.offset = 0; key.objectid = 0; key.flags = 0; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); ret = btrfs_search_slot(NULL, root->fs_info->tree_root, &key, &path, 0, 0); BUG_ON(ret < 0); while(1) { leaf = &path.nodes[0]->leaf; slot = path.slots[0]; if (slot >= btrfs_header_nritems(&leaf->header)) { ret = btrfs_next_leaf(root, &path); if (ret != 0) break; leaf = &path.nodes[0]->leaf; slot = path.slots[0]; } btrfs_disk_key_to_cpu(&found_key, &leaf->items[path.slots[0]].key); if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { struct btrfs_buffer *buf; ri = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_root_item); buf = read_tree_block(root->fs_info->tree_root, btrfs_root_blocknr(ri)); add_root_to_pending(buf, bits, bits_nr, &extent_radix, &pending, &seen, &reada, &nodes); btrfs_block_release(root->fs_info->tree_root, buf); } path.slots[0]++; } btrfs_release_path(root, &path); while(1) { ret = run_next_block(root, bits, bits_nr, &last, &pending, &seen, &reada, &nodes, &extent_radix); if (ret != 0) break; } ret = check_extent_refs(root, &extent_radix); close_ctree(root, &super); printf("found %Lu blocks used err is %d\n", blocks_used, ret); printf("total csum bytes: %Lu\n", total_csum_bytes); printf("total tree blocks: %Lu\n", total_btree_blocks); printf("btree space waste bytes: %Lu\n", btree_space_waste); return ret; }