int btrfs_test_free_space_cache(void) { struct btrfs_block_group_cache *cache; struct btrfs_root *root = NULL; int ret = -ENOMEM; test_msg("Running btrfs free space cache tests\n"); cache = init_test_block_group(); if (!cache) { test_msg("Couldn't run the tests\n"); return 0; } root = btrfs_alloc_dummy_root(); if (!root) goto out; root->fs_info = btrfs_alloc_dummy_fs_info(); if (!root->fs_info) goto out; root->fs_info->extent_root = root; cache->fs_info = root->fs_info; ret = test_extents(cache); if (ret) goto out; ret = test_bitmaps(cache); if (ret) goto out; ret = test_bitmaps_and_extents(cache); if (ret) goto out; ret = test_steal_space_from_bitmap_to_extent(cache); out: __btrfs_remove_free_space_cache(cache->free_space_ctl); kfree(cache->free_space_ctl); kfree(cache); btrfs_free_dummy_root(root); test_msg("Free space cache tests finished\n"); return ret; }
int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info = NULL; struct btrfs_root *root; struct btrfs_root *tmp_root; int ret = 0; fs_info = btrfs_alloc_dummy_fs_info(); if (!fs_info) { test_msg("Couldn't allocate dummy fs info\n"); return -ENOMEM; } root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(root)) { test_msg("Couldn't allocate root\n"); ret = PTR_ERR(root); goto out; } /* We are using this root as our extent root */ root->fs_info->extent_root = root; /* * Some of the paths we test assume we have a filled out fs_info, so we * just need to add the root in there so we don't panic. */ root->fs_info->tree_root = root; root->fs_info->quota_root = root; set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags); /* * Can't use bytenr 0, some things freak out * *cough*backref walking code*cough* */ root->node = alloc_test_extent_buffer(root->fs_info, nodesize, nodesize); if (!root->node) { test_msg("Couldn't allocate dummy buffer\n"); ret = -ENOMEM; goto out; } btrfs_set_header_level(root->node, 0); btrfs_set_header_nritems(root->node, 0); root->alloc_bytenr += 2 * nodesize; tmp_root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(tmp_root)) { test_msg("Couldn't allocate a fs root\n"); ret = PTR_ERR(tmp_root); goto out; } tmp_root->root_key.objectid = BTRFS_FS_TREE_OBJECTID; root->fs_info->fs_root = tmp_root; ret = btrfs_insert_fs_root(root->fs_info, tmp_root); if (ret) { test_msg("Couldn't insert fs root %d\n", ret); goto out; } tmp_root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(tmp_root)) { test_msg("Couldn't allocate a fs root\n"); ret = PTR_ERR(tmp_root); goto out; } tmp_root->root_key.objectid = BTRFS_FIRST_FREE_OBJECTID; ret = btrfs_insert_fs_root(root->fs_info, tmp_root); if (ret) { test_msg("Couldn't insert fs root %d\n", ret); goto out; } test_msg("Running qgroup tests\n"); ret = test_no_shared_qgroup(root, sectorsize, nodesize); if (ret) goto out; ret = test_multiple_refs(root, sectorsize, nodesize); out: btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; }
static int test_extent_accounting(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info = NULL; struct inode *inode = NULL; struct btrfs_root *root = NULL; int ret = -ENOMEM; inode = btrfs_new_test_inode(); if (!inode) { test_msg("Couldn't allocate inode\n"); return ret; } fs_info = btrfs_alloc_dummy_fs_info(); if (!fs_info) { test_msg("Couldn't allocate dummy fs info\n"); goto out; } root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(root)) { test_msg("Couldn't allocate root\n"); goto out; } BTRFS_I(inode)->root = root; btrfs_test_inode_set_ops(inode); /* [BTRFS_MAX_EXTENT_SIZE] */ BTRFS_I(inode)->outstanding_extents++; ret = btrfs_set_extent_delalloc(inode, 0, BTRFS_MAX_EXTENT_SIZE - 1, NULL); if (ret) { test_msg("btrfs_set_extent_delalloc returned %d\n", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 1) { ret = -EINVAL; test_msg("Miscount, wanted 1, got %u\n", BTRFS_I(inode)->outstanding_extents); goto out; } /* [BTRFS_MAX_EXTENT_SIZE][sectorsize] */ BTRFS_I(inode)->outstanding_extents++; ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE, BTRFS_MAX_EXTENT_SIZE + sectorsize - 1, NULL); if (ret) { test_msg("btrfs_set_extent_delalloc returned %d\n", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 2) { ret = -EINVAL; test_msg("Miscount, wanted 2, got %u\n", BTRFS_I(inode)->outstanding_extents); goto out; } /* [BTRFS_MAX_EXTENT_SIZE/2][sectorsize HOLE][the rest] */ ret = clear_extent_bit(&BTRFS_I(inode)->io_tree, BTRFS_MAX_EXTENT_SIZE >> 1, (BTRFS_MAX_EXTENT_SIZE >> 1) + sectorsize - 1, EXTENT_DELALLOC | EXTENT_DIRTY | EXTENT_UPTODATE | EXTENT_DO_ACCOUNTING, 0, 0, NULL, GFP_KERNEL); if (ret) { test_msg("clear_extent_bit returned %d\n", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 2) { ret = -EINVAL; test_msg("Miscount, wanted 2, got %u\n", BTRFS_I(inode)->outstanding_extents); goto out; } /* [BTRFS_MAX_EXTENT_SIZE][sectorsize] */ BTRFS_I(inode)->outstanding_extents++; ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE >> 1, (BTRFS_MAX_EXTENT_SIZE >> 1) + sectorsize - 1, NULL); if (ret) { test_msg("btrfs_set_extent_delalloc returned %d\n", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 2) { ret = -EINVAL; test_msg("Miscount, wanted 2, got %u\n", BTRFS_I(inode)->outstanding_extents); goto out; } /* * [BTRFS_MAX_EXTENT_SIZE+sectorsize][sectorsize HOLE][BTRFS_MAX_EXTENT_SIZE+sectorsize] * * I'm artificially adding 2 to outstanding_extents because in the * buffered IO case we'd add things up as we go, but I don't feel like * doing that here, this isn't the interesting case we want to test. */ BTRFS_I(inode)->outstanding_extents += 2; ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize, (BTRFS_MAX_EXTENT_SIZE << 1) + 3 * sectorsize - 1, NULL); if (ret) { test_msg("btrfs_set_extent_delalloc returned %d\n", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 4) { ret = -EINVAL; test_msg("Miscount, wanted 4, got %u\n", BTRFS_I(inode)->outstanding_extents); goto out; } /* * [BTRFS_MAX_EXTENT_SIZE+sectorsize][sectorsize][BTRFS_MAX_EXTENT_SIZE+sectorsize] */ BTRFS_I(inode)->outstanding_extents++; ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE + sectorsize, BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, NULL); if (ret) { test_msg("btrfs_set_extent_delalloc returned %d\n", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 3) { ret = -EINVAL; test_msg("Miscount, wanted 3, got %u\n", BTRFS_I(inode)->outstanding_extents); goto out; } /* [BTRFS_MAX_EXTENT_SIZE+4k][4K HOLE][BTRFS_MAX_EXTENT_SIZE+4k] */ ret = clear_extent_bit(&BTRFS_I(inode)->io_tree, BTRFS_MAX_EXTENT_SIZE + sectorsize, BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_UPTODATE, 0, 0, NULL, GFP_KERNEL); if (ret) { test_msg("clear_extent_bit returned %d\n", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 4) { ret = -EINVAL; test_msg("Miscount, wanted 4, got %u\n", BTRFS_I(inode)->outstanding_extents); goto out; } /* * Refill the hole again just for good measure, because I thought it * might fail and I'd rather satisfy my paranoia at this point. */ BTRFS_I(inode)->outstanding_extents++; ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE + sectorsize, BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, NULL); if (ret) { test_msg("btrfs_set_extent_delalloc returned %d\n", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 3) { ret = -EINVAL; test_msg("Miscount, wanted 3, got %u\n", BTRFS_I(inode)->outstanding_extents); goto out; } /* Empty */ ret = clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, (u64)-1, EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_UPTODATE, 0, 0, NULL, GFP_KERNEL); if (ret) { test_msg("clear_extent_bit returned %d\n", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents) { ret = -EINVAL; test_msg("Miscount, wanted 0, got %u\n", BTRFS_I(inode)->outstanding_extents); goto out; } ret = 0; out: if (ret) clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, (u64)-1, EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_UPTODATE, 0, 0, NULL, GFP_KERNEL); iput(inode); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; }
static int test_hole_first(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info = NULL; struct inode *inode = NULL; struct btrfs_root *root = NULL; struct extent_map *em = NULL; int ret = -ENOMEM; inode = btrfs_new_test_inode(); if (!inode) { test_msg("Couldn't allocate inode\n"); return ret; } BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY; BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID; BTRFS_I(inode)->location.offset = 0; fs_info = btrfs_alloc_dummy_fs_info(); if (!fs_info) { test_msg("Couldn't allocate dummy fs info\n"); goto out; } root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(root)) { test_msg("Couldn't allocate root\n"); goto out; } root->node = alloc_dummy_extent_buffer(NULL, nodesize, nodesize); if (!root->node) { test_msg("Couldn't allocate dummy buffer\n"); goto out; } extent_buffer_get(root->node); btrfs_set_header_nritems(root->node, 0); btrfs_set_header_level(root->node, 0); BTRFS_I(inode)->root = root; ret = -EINVAL; /* * Need a blank inode item here just so we don't confuse * btrfs_get_extent. */ insert_inode_item_key(root); insert_extent(root, sectorsize, sectorsize, sectorsize, 0, sectorsize, sectorsize, BTRFS_FILE_EXTENT_REG, 0, 1); em = btrfs_get_extent(inode, NULL, 0, 0, 2 * sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != 0 || em->len != sectorsize) { test_msg("Unexpected extent wanted start 0 len %u, " "got start %llu len %llu\n", sectorsize, em->start, em->len); goto out; } if (em->flags != vacancy_only) { test_msg("Wrong flags, wanted %lu, have %lu\n", vacancy_only, em->flags); goto out; } free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, sectorsize, 2 * sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != sectorsize) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != sectorsize || em->len != sectorsize) { test_msg("Unexpected extent wanted start %u len %u, " "got start %llu len %llu\n", sectorsize, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, wanted 0 got %lu\n", em->flags); goto out; } ret = 0; out: if (!IS_ERR(em)) free_extent_map(em); iput(inode); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; }
static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info = NULL; struct inode *inode = NULL; struct btrfs_root *root = NULL; struct extent_map *em = NULL; u64 orig_start; u64 disk_bytenr; u64 offset; int ret = -ENOMEM; inode = btrfs_new_test_inode(); if (!inode) { test_msg("Couldn't allocate inode\n"); return ret; } BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY; BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID; BTRFS_I(inode)->location.offset = 0; fs_info = btrfs_alloc_dummy_fs_info(); if (!fs_info) { test_msg("Couldn't allocate dummy fs info\n"); goto out; } root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(root)) { test_msg("Couldn't allocate root\n"); goto out; } root->node = alloc_dummy_extent_buffer(NULL, nodesize, nodesize); if (!root->node) { test_msg("Couldn't allocate dummy buffer\n"); goto out; } /* * We will just free a dummy node if it's ref count is 2 so we need an * extra ref so our searches don't accidentally release our page. */ extent_buffer_get(root->node); btrfs_set_header_nritems(root->node, 0); btrfs_set_header_level(root->node, 0); ret = -EINVAL; /* First with no extents */ BTRFS_I(inode)->root = root; em = btrfs_get_extent(inode, NULL, 0, 0, sectorsize, 0); if (IS_ERR(em)) { em = NULL; test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (!test_bit(EXTENT_FLAG_VACANCY, &em->flags)) { test_msg("Vacancy flag wasn't set properly\n"); goto out; } free_extent_map(em); btrfs_drop_extent_cache(inode, 0, (u64)-1, 0); /* * All of the magic numbers are based on the mapping setup in * setup_file_extents, so if you change anything there you need to * update the comment and update the expected values below. */ setup_file_extents(root, sectorsize); em = btrfs_get_extent(inode, NULL, 0, 0, (u64)-1, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != 0 || em->len != 5) { test_msg("Unexpected extent wanted start 0 len 5, got start " "%llu len %llu\n", em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_INLINE) { test_msg("Expected an inline, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != (sectorsize - 5)) { test_msg("Unexpected extent wanted start %llu len 1, got start " "%llu len %llu\n", offset, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } /* * We don't test anything else for inline since it doesn't get set * unless we have a page for it to write into. Maybe we should change * this? */ offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 4) { test_msg("Unexpected extent wanted start %llu len 4, got start " "%llu len %llu\n", offset, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } offset = em->start + em->len; free_extent_map(em); /* Regular extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize - 1) { test_msg("Unexpected extent wanted start %llu len 4095, got " "start %llu len %llu\n", offset, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* The next 3 are split extents */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } disk_bytenr = em->block_start; orig_start = em->start; offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Wrong orig offset, want %llu, have %llu\n", orig_start, em->orig_start); goto out; } disk_bytenr += (em->start - orig_start); if (em->block_start != disk_bytenr) { test_msg("Wrong block start, want %llu, have %llu\n", disk_bytenr, em->block_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* Prealloc extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { test_msg("Unexpected flags set, want %lu have %lu\n", prealloc_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* The next 3 are a half written prealloc extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { test_msg("Unexpected flags set, want %lu have %lu\n", prealloc_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } disk_bytenr = em->block_start; orig_start = em->start; offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_HOLE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Unexpected orig offset, wanted %llu, have %llu\n", orig_start, em->orig_start); goto out; } if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) { test_msg("Unexpected block start, wanted %llu, have %llu\n", disk_bytenr + (em->start - em->orig_start), em->block_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { test_msg("Unexpected flags set, want %lu have %lu\n", prealloc_only, em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Wrong orig offset, want %llu, have %llu\n", orig_start, em->orig_start); goto out; } if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) { test_msg("Unexpected block start, wanted %llu, have %llu\n", disk_bytenr + (em->start - em->orig_start), em->block_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* Now for the compressed extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u," "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { test_msg("Unexpected flags set, want %lu have %lu\n", compressed_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { test_msg("Unexpected compress type, wanted %d, got %d\n", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } offset = em->start + em->len; free_extent_map(em); /* Split compressed extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u," "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { test_msg("Unexpected flags set, want %lu have %lu\n", compressed_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { test_msg("Unexpected compress type, wanted %d, got %d\n", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } disk_bytenr = em->block_start; orig_start = em->start; offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != disk_bytenr) { test_msg("Block start does not match, want %llu got %llu\n", disk_bytenr, em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { test_msg("Unexpected flags set, want %lu have %lu\n", compressed_only, em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { test_msg("Unexpected compress type, wanted %d, got %d\n", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } offset = em->start + em->len; free_extent_map(em); /* A hole between regular extents but no hole extent */ em = btrfs_get_extent(inode, NULL, 0, offset + 6, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, 4096 * 1024, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole extent, got %llu\n", em->block_start); goto out; } /* * Currently we just return a length that we requested rather than the * length of the actual hole, if this changes we'll have to change this * test. */ if (em->start != offset || em->len != 3 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 3 * sectorsize, em->start, em->len); goto out; } if (em->flags != vacancy_only) { test_msg("Unexpected flags set, want %lu have %lu\n", vacancy_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u," "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } ret = 0; out: if (!IS_ERR(em)) free_extent_map(em); iput(inode); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; }
static int run_test(test_func_t test_func, int bitmaps) { struct btrfs_root *root = NULL; struct btrfs_block_group_cache *cache = NULL; struct btrfs_trans_handle trans; struct btrfs_path *path = NULL; int ret; root = btrfs_alloc_dummy_root(); if (IS_ERR(root)) { test_msg("Couldn't allocate dummy root\n"); ret = PTR_ERR(root); goto out; } root->fs_info = btrfs_alloc_dummy_fs_info(); if (!root->fs_info) { test_msg("Couldn't allocate dummy fs info\n"); ret = -ENOMEM; goto out; } btrfs_set_super_compat_ro_flags(root->fs_info->super_copy, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE); root->fs_info->free_space_root = root; root->fs_info->tree_root = root; root->node = alloc_test_extent_buffer(root->fs_info, 4096); if (!root->node) { test_msg("Couldn't allocate dummy buffer\n"); ret = -ENOMEM; goto out; } btrfs_set_header_level(root->node, 0); btrfs_set_header_nritems(root->node, 0); root->alloc_bytenr += 8192; cache = btrfs_alloc_dummy_block_group(8 * BITMAP_RANGE); if (!cache) { test_msg("Couldn't allocate dummy block group cache\n"); ret = -ENOMEM; goto out; } cache->bitmap_low_thresh = 0; cache->bitmap_high_thresh = (u32)-1; cache->needs_free_space = 1; btrfs_init_dummy_trans(&trans); path = btrfs_alloc_path(); if (!path) { test_msg("Couldn't allocate path\n"); return -ENOMEM; } ret = add_block_group_free_space(&trans, root->fs_info, cache); if (ret) { test_msg("Could not add block group free space\n"); goto out; } if (bitmaps) { ret = convert_free_space_to_bitmaps(&trans, root->fs_info, cache, path); if (ret) { test_msg("Could not convert block group to bitmaps\n"); goto out; } } ret = test_func(&trans, root->fs_info, cache, path); if (ret) goto out; ret = remove_block_group_free_space(&trans, root->fs_info, cache); if (ret) { test_msg("Could not remove block group free space\n"); goto out; } if (btrfs_header_nritems(root->node) != 0) { test_msg("Free space tree has leftover items\n"); ret = -EINVAL; goto out; } ret = 0; out: btrfs_free_path(path); btrfs_free_dummy_block_group(cache); btrfs_free_dummy_root(root); return ret; }
/* * Before we were able to steal free space from a bitmap entry to an extent * entry, we could end up with 2 entries representing a contiguous free space. * One would be an extent entry and the other a bitmap entry. Since in order * to allocate space to a caller we use only 1 entry, we couldn't return that * whole range to the caller if it was requested. This forced the caller to * either assume ENOSPC or perform several smaller space allocations, which * wasn't optimal as they could be spread all over the block group while under * concurrency (extra overhead and fragmentation). * * This stealing approach is beneficial, since we always prefer to allocate * from extent entries, both for clustered and non-clustered allocation * requests. */ static int test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, u32 sectorsize) { int ret; u64 offset; u64 max_extent_size; const struct btrfs_free_space_op test_free_space_ops = { .recalc_thresholds = cache->free_space_ctl->op->recalc_thresholds, .use_bitmap = test_use_bitmap, }; const struct btrfs_free_space_op *orig_free_space_ops; test_msg("Running space stealing from bitmap to extent\n"); /* * For this test, we want to ensure we end up with an extent entry * immediately adjacent to a bitmap entry, where the bitmap starts * at an offset where the extent entry ends. We keep adding and * removing free space to reach into this state, but to get there * we need to reach a point where marking new free space doesn't * result in adding new extent entries or merging the new space * with existing extent entries - the space ends up being marked * in an existing bitmap that covers the new free space range. * * To get there, we need to reach the threshold defined set at * cache->free_space_ctl->extents_thresh, which currently is * 256 extents on a x86_64 system at least, and a few other * conditions (check free_space_cache.c). Instead of making the * test much longer and complicated, use a "use_bitmap" operation * that forces use of bitmaps as soon as we have at least 1 * extent entry. */ orig_free_space_ops = cache->free_space_ctl->op; cache->free_space_ctl->op = &test_free_space_ops; /* * Extent entry covering free space range [128Mb - 256Kb, 128Mb - 128Kb[ */ ret = test_add_free_space_entry(cache, SZ_128M - SZ_256K, SZ_128K, 0); if (ret) { test_msg("Couldn't add extent entry %d\n", ret); return ret; } /* Bitmap entry covering free space range [128Mb + 512Kb, 256Mb[ */ ret = test_add_free_space_entry(cache, SZ_128M + SZ_512K, SZ_128M - SZ_512K, 1); if (ret) { test_msg("Couldn't add bitmap entry %d\n", ret); return ret; } ret = check_num_extents_and_bitmaps(cache, 2, 1); if (ret) return ret; /* * Now make only the first 256Kb of the bitmap marked as free, so that * we end up with only the following ranges marked as free space: * * [128Mb - 256Kb, 128Mb - 128Kb[ * [128Mb + 512Kb, 128Mb + 768Kb[ */ ret = btrfs_remove_free_space(cache, SZ_128M + 768 * SZ_1K, SZ_128M - 768 * SZ_1K); if (ret) { test_msg("Failed to free part of bitmap space %d\n", ret); return ret; } /* Confirm that only those 2 ranges are marked as free. */ if (!test_check_exists(cache, SZ_128M - SZ_256K, SZ_128K)) { test_msg("Free space range missing\n"); return -ENOENT; } if (!test_check_exists(cache, SZ_128M + SZ_512K, SZ_256K)) { test_msg("Free space range missing\n"); return -ENOENT; } /* * Confirm that the bitmap range [128Mb + 768Kb, 256Mb[ isn't marked * as free anymore. */ if (test_check_exists(cache, SZ_128M + 768 * SZ_1K, SZ_128M - 768 * SZ_1K)) { test_msg("Bitmap region not removed from space cache\n"); return -EINVAL; } /* * Confirm that the region [128Mb + 256Kb, 128Mb + 512Kb[, which is * covered by the bitmap, isn't marked as free. */ if (test_check_exists(cache, SZ_128M + SZ_256K, SZ_256K)) { test_msg("Invalid bitmap region marked as free\n"); return -EINVAL; } /* * Confirm that the region [128Mb, 128Mb + 256Kb[, which is covered * by the bitmap too, isn't marked as free either. */ if (test_check_exists(cache, SZ_128M, SZ_256K)) { test_msg("Invalid bitmap region marked as free\n"); return -EINVAL; } /* * Now lets mark the region [128Mb, 128Mb + 512Kb[ as free too. But, * lets make sure the free space cache marks it as free in the bitmap, * and doesn't insert a new extent entry to represent this region. */ ret = btrfs_add_free_space(cache, SZ_128M, SZ_512K); if (ret) { test_msg("Error adding free space: %d\n", ret); return ret; } /* Confirm the region is marked as free. */ if (!test_check_exists(cache, SZ_128M, SZ_512K)) { test_msg("Bitmap region not marked as free\n"); return -ENOENT; } /* * Confirm that no new extent entries or bitmap entries were added to * the cache after adding that free space region. */ ret = check_num_extents_and_bitmaps(cache, 2, 1); if (ret) return ret; /* * Now lets add a small free space region to the right of the previous * one, which is not contiguous with it and is part of the bitmap too. * The goal is to test that the bitmap entry space stealing doesn't * steal this space region. */ ret = btrfs_add_free_space(cache, SZ_128M + SZ_16M, sectorsize); if (ret) { test_msg("Error adding free space: %d\n", ret); return ret; } /* * Confirm that no new extent entries or bitmap entries were added to * the cache after adding that free space region. */ ret = check_num_extents_and_bitmaps(cache, 2, 1); if (ret) return ret; /* * Now mark the region [128Mb - 128Kb, 128Mb[ as free too. This will * expand the range covered by the existing extent entry that represents * the free space [128Mb - 256Kb, 128Mb - 128Kb[. */ ret = btrfs_add_free_space(cache, SZ_128M - SZ_128K, SZ_128K); if (ret) { test_msg("Error adding free space: %d\n", ret); return ret; } /* Confirm the region is marked as free. */ if (!test_check_exists(cache, SZ_128M - SZ_128K, SZ_128K)) { test_msg("Extent region not marked as free\n"); return -ENOENT; } /* * Confirm that our extent entry didn't stole all free space from the * bitmap, because of the small 4Kb free space region. */ ret = check_num_extents_and_bitmaps(cache, 2, 1); if (ret) return ret; /* * So now we have the range [128Mb - 256Kb, 128Mb + 768Kb[ as free * space. Without stealing bitmap free space into extent entry space, * we would have all this free space represented by 2 entries in the * cache: * * extent entry covering range: [128Mb - 256Kb, 128Mb[ * bitmap entry covering range: [128Mb, 128Mb + 768Kb[ * * Attempting to allocate the whole free space (1Mb) would fail, because * we can't allocate from multiple entries. * With the bitmap free space stealing, we get a single extent entry * that represents the 1Mb free space, and therefore we're able to * allocate the whole free space at once. */ if (!test_check_exists(cache, SZ_128M - SZ_256K, SZ_1M)) { test_msg("Expected region not marked as free\n"); return -ENOENT; } if (cache->free_space_ctl->free_space != (SZ_1M + sectorsize)) { test_msg("Cache free space is not 1Mb + %u\n", sectorsize); return -EINVAL; } offset = btrfs_find_space_for_alloc(cache, 0, SZ_1M, 0, &max_extent_size); if (offset != (SZ_128M - SZ_256K)) { test_msg("Failed to allocate 1Mb from space cache, returned offset is: %llu\n", offset); return -EINVAL; } /* * All that remains is a sectorsize free space region in a bitmap. * Confirm. */ ret = check_num_extents_and_bitmaps(cache, 1, 1); if (ret) return ret; if (cache->free_space_ctl->free_space != sectorsize) { test_msg("Cache free space is not %u\n", sectorsize); return -EINVAL; } offset = btrfs_find_space_for_alloc(cache, 0, sectorsize, 0, &max_extent_size); if (offset != (SZ_128M + SZ_16M)) { test_msg("Failed to allocate %u, returned offset : %llu\n", sectorsize, offset); return -EINVAL; } ret = check_cache_empty(cache); if (ret) return ret; __btrfs_remove_free_space_cache(cache->free_space_ctl); /* * Now test a similar scenario, but where our extent entry is located * to the right of the bitmap entry, so that we can check that stealing * space from a bitmap to the front of an extent entry works. */ /* * Extent entry covering free space range [128Mb + 128Kb, 128Mb + 256Kb[ */ ret = test_add_free_space_entry(cache, SZ_128M + SZ_128K, SZ_128K, 0); if (ret) { test_msg("Couldn't add extent entry %d\n", ret); return ret; } /* Bitmap entry covering free space range [0, 128Mb - 512Kb[ */ ret = test_add_free_space_entry(cache, 0, SZ_128M - SZ_512K, 1); if (ret) { test_msg("Couldn't add bitmap entry %d\n", ret); return ret; } ret = check_num_extents_and_bitmaps(cache, 2, 1); if (ret) return ret; /* * Now make only the last 256Kb of the bitmap marked as free, so that * we end up with only the following ranges marked as free space: * * [128Mb + 128b, 128Mb + 256Kb[ * [128Mb - 768Kb, 128Mb - 512Kb[ */ ret = btrfs_remove_free_space(cache, 0, SZ_128M - 768 * SZ_1K); if (ret) { test_msg("Failed to free part of bitmap space %d\n", ret); return ret; } /* Confirm that only those 2 ranges are marked as free. */ if (!test_check_exists(cache, SZ_128M + SZ_128K, SZ_128K)) { test_msg("Free space range missing\n"); return -ENOENT; } if (!test_check_exists(cache, SZ_128M - 768 * SZ_1K, SZ_256K)) { test_msg("Free space range missing\n"); return -ENOENT; } /* * Confirm that the bitmap range [0, 128Mb - 768Kb[ isn't marked * as free anymore. */ if (test_check_exists(cache, 0, SZ_128M - 768 * SZ_1K)) { test_msg("Bitmap region not removed from space cache\n"); return -EINVAL; } /* * Confirm that the region [128Mb - 512Kb, 128Mb[, which is * covered by the bitmap, isn't marked as free. */ if (test_check_exists(cache, SZ_128M - SZ_512K, SZ_512K)) { test_msg("Invalid bitmap region marked as free\n"); return -EINVAL; } /* * Now lets mark the region [128Mb - 512Kb, 128Mb[ as free too. But, * lets make sure the free space cache marks it as free in the bitmap, * and doesn't insert a new extent entry to represent this region. */ ret = btrfs_add_free_space(cache, SZ_128M - SZ_512K, SZ_512K); if (ret) { test_msg("Error adding free space: %d\n", ret); return ret; } /* Confirm the region is marked as free. */ if (!test_check_exists(cache, SZ_128M - SZ_512K, SZ_512K)) { test_msg("Bitmap region not marked as free\n"); return -ENOENT; } /* * Confirm that no new extent entries or bitmap entries were added to * the cache after adding that free space region. */ ret = check_num_extents_and_bitmaps(cache, 2, 1); if (ret) return ret; /* * Now lets add a small free space region to the left of the previous * one, which is not contiguous with it and is part of the bitmap too. * The goal is to test that the bitmap entry space stealing doesn't * steal this space region. */ ret = btrfs_add_free_space(cache, SZ_32M, 2 * sectorsize); if (ret) { test_msg("Error adding free space: %d\n", ret); return ret; } /* * Now mark the region [128Mb, 128Mb + 128Kb[ as free too. This will * expand the range covered by the existing extent entry that represents * the free space [128Mb + 128Kb, 128Mb + 256Kb[. */ ret = btrfs_add_free_space(cache, SZ_128M, SZ_128K); if (ret) { test_msg("Error adding free space: %d\n", ret); return ret; } /* Confirm the region is marked as free. */ if (!test_check_exists(cache, SZ_128M, SZ_128K)) { test_msg("Extent region not marked as free\n"); return -ENOENT; } /* * Confirm that our extent entry didn't stole all free space from the * bitmap, because of the small 2 * sectorsize free space region. */ ret = check_num_extents_and_bitmaps(cache, 2, 1); if (ret) return ret; /* * So now we have the range [128Mb - 768Kb, 128Mb + 256Kb[ as free * space. Without stealing bitmap free space into extent entry space, * we would have all this free space represented by 2 entries in the * cache: * * extent entry covering range: [128Mb, 128Mb + 256Kb[ * bitmap entry covering range: [128Mb - 768Kb, 128Mb[ * * Attempting to allocate the whole free space (1Mb) would fail, because * we can't allocate from multiple entries. * With the bitmap free space stealing, we get a single extent entry * that represents the 1Mb free space, and therefore we're able to * allocate the whole free space at once. */ if (!test_check_exists(cache, SZ_128M - 768 * SZ_1K, SZ_1M)) { test_msg("Expected region not marked as free\n"); return -ENOENT; } if (cache->free_space_ctl->free_space != (SZ_1M + 2 * sectorsize)) { test_msg("Cache free space is not 1Mb + %u\n", 2 * sectorsize); return -EINVAL; } offset = btrfs_find_space_for_alloc(cache, 0, SZ_1M, 0, &max_extent_size); if (offset != (SZ_128M - 768 * SZ_1K)) { test_msg("Failed to allocate 1Mb from space cache, returned offset is: %llu\n", offset); return -EINVAL; } /* * All that remains is 2 * sectorsize free space region * in a bitmap. Confirm. */ ret = check_num_extents_and_bitmaps(cache, 1, 1); if (ret) return ret; if (cache->free_space_ctl->free_space != 2 * sectorsize) { test_msg("Cache free space is not %u\n", 2 * sectorsize); return -EINVAL; } offset = btrfs_find_space_for_alloc(cache, 0, 2 * sectorsize, 0, &max_extent_size); if (offset != SZ_32M) { test_msg("Failed to allocate %u, offset: %llu\n", 2 * sectorsize, offset); return -EINVAL; } ret = check_cache_empty(cache); if (ret) return ret; cache->free_space_ctl->op = orig_free_space_ops; __btrfs_remove_free_space_cache(cache->free_space_ctl); return 0; } int btrfs_test_free_space_cache(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info; struct btrfs_block_group_cache *cache; struct btrfs_root *root = NULL; int ret = -ENOMEM; test_msg("Running btrfs free space cache tests\n"); fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); if (!fs_info) return -ENOMEM; /* * For ppc64 (with 64k page size), bytes per bitmap might be * larger than 1G. To make bitmap test available in ppc64, * alloc dummy block group whose size cross bitmaps. */ cache = btrfs_alloc_dummy_block_group(fs_info, BITS_PER_BITMAP * sectorsize + PAGE_SIZE); if (!cache) { test_msg("Couldn't run the tests\n"); btrfs_free_dummy_fs_info(fs_info); return 0; } root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(root)) { ret = PTR_ERR(root); goto out; } root->fs_info->extent_root = root; ret = test_extents(cache); if (ret) goto out; ret = test_bitmaps(cache, sectorsize); if (ret) goto out; ret = test_bitmaps_and_extents(cache, sectorsize); if (ret) goto out; ret = test_steal_space_from_bitmap_to_extent(cache, sectorsize); out: btrfs_free_dummy_block_group(cache); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); test_msg("Free space cache tests finished\n"); return ret; }
static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info; struct btrfs_path *path = NULL; struct btrfs_root *root = NULL; struct extent_buffer *eb; struct btrfs_item *item; char *value = "mary had a little lamb"; char *split1 = "mary had a little"; char *split2 = " lamb"; char *split3 = "mary"; char *split4 = " had a little"; char buf[32]; struct btrfs_key key; u32 value_len = strlen(value); int ret = 0; test_msg("Running btrfs_split_item tests\n"); fs_info = btrfs_alloc_dummy_fs_info(); if (!fs_info) { test_msg("Could not allocate fs_info\n"); return -ENOMEM; } root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(root)) { test_msg("Could not allocate root\n"); ret = PTR_ERR(root); goto out; } path = btrfs_alloc_path(); if (!path) { test_msg("Could not allocate path\n"); ret = -ENOMEM; goto out; } path->nodes[0] = eb = alloc_dummy_extent_buffer(NULL, nodesize, nodesize); if (!eb) { test_msg("Could not allocate dummy buffer\n"); ret = -ENOMEM; goto out; } path->slots[0] = 0; key.objectid = 0; key.type = BTRFS_EXTENT_CSUM_KEY; key.offset = 0; setup_items_for_insert(root, path, &key, &value_len, value_len, value_len + sizeof(struct btrfs_item), 1); item = btrfs_item_nr(0); write_extent_buffer(eb, value, btrfs_item_ptr_offset(eb, 0), value_len); key.offset = 3; /* * Passing NULL trans here should be safe because we have plenty of * space in this leaf to split the item without having to split the * leaf. */ ret = btrfs_split_item(NULL, root, path, &key, 17); if (ret) { test_msg("Split item failed %d\n", ret); goto out; } /* * Read the first slot, it should have the original key and contain only * 'mary had a little' */ btrfs_item_key_to_cpu(eb, &key, 0); if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || key.offset != 0) { test_msg("Invalid key at slot 0\n"); ret = -EINVAL; goto out; } item = btrfs_item_nr(0); if (btrfs_item_size(eb, item) != strlen(split1)) { test_msg("Invalid len in the first split\n"); ret = -EINVAL; goto out; } read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0), strlen(split1)); if (memcmp(buf, split1, strlen(split1))) { test_msg("Data in the buffer doesn't match what it should " "in the first split have='%.*s' want '%s'\n", (int)strlen(split1), buf, split1); ret = -EINVAL; goto out; } btrfs_item_key_to_cpu(eb, &key, 1); if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || key.offset != 3) { test_msg("Invalid key at slot 1\n"); ret = -EINVAL; goto out; } item = btrfs_item_nr(1); if (btrfs_item_size(eb, item) != strlen(split2)) { test_msg("Invalid len in the second split\n"); ret = -EINVAL; goto out; } read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1), strlen(split2)); if (memcmp(buf, split2, strlen(split2))) { test_msg("Data in the buffer doesn't match what it should " "in the second split\n"); ret = -EINVAL; goto out; } key.offset = 1; /* Do it again so we test memmoving the other items in the leaf */ ret = btrfs_split_item(NULL, root, path, &key, 4); if (ret) { test_msg("Second split item failed %d\n", ret); goto out; } btrfs_item_key_to_cpu(eb, &key, 0); if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || key.offset != 0) { test_msg("Invalid key at slot 0\n"); ret = -EINVAL; goto out; } item = btrfs_item_nr(0); if (btrfs_item_size(eb, item) != strlen(split3)) { test_msg("Invalid len in the first split\n"); ret = -EINVAL; goto out; } read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0), strlen(split3)); if (memcmp(buf, split3, strlen(split3))) { test_msg("Data in the buffer doesn't match what it should " "in the third split"); ret = -EINVAL; goto out; } btrfs_item_key_to_cpu(eb, &key, 1); if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || key.offset != 1) { test_msg("Invalid key at slot 1\n"); ret = -EINVAL; goto out; } item = btrfs_item_nr(1); if (btrfs_item_size(eb, item) != strlen(split4)) { test_msg("Invalid len in the second split\n"); ret = -EINVAL; goto out; } read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1), strlen(split4)); if (memcmp(buf, split4, strlen(split4))) { test_msg("Data in the buffer doesn't match what it should " "in the fourth split\n"); ret = -EINVAL; goto out; } btrfs_item_key_to_cpu(eb, &key, 2); if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || key.offset != 3) { test_msg("Invalid key at slot 2\n"); ret = -EINVAL; goto out; } item = btrfs_item_nr(2); if (btrfs_item_size(eb, item) != strlen(split2)) { test_msg("Invalid len in the second split\n"); ret = -EINVAL; goto out; } read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 2), strlen(split2)); if (memcmp(buf, split2, strlen(split2))) { test_msg("Data in the buffer doesn't match what it should " "in the last chunk\n"); ret = -EINVAL; goto out; } out: btrfs_free_path(path); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; }