static int load_free_space_bitmaps(struct btrfs_caching_control *caching_ctl, struct btrfs_path *path, u32 expected_extent_count) { struct btrfs_block_group_cache *block_group; struct btrfs_fs_info *fs_info; struct btrfs_root *root; struct btrfs_key key; int prev_bit = 0, bit; /* Initialize to silence GCC. */ u64 extent_start = 0; u64 end, offset; u64 total_found = 0; u32 extent_count = 0; int ret; block_group = caching_ctl->block_group; fs_info = block_group->fs_info; root = fs_info->free_space_root; end = block_group->key.objectid + block_group->key.offset; while (1) { ret = btrfs_next_item(root, path); if (ret < 0) goto out; if (ret) break; btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if (key.type == BTRFS_FREE_SPACE_INFO_KEY) break; ASSERT(key.type == BTRFS_FREE_SPACE_BITMAP_KEY); ASSERT(key.objectid < end && key.objectid + key.offset <= end); caching_ctl->progress = key.objectid; offset = key.objectid; while (offset < key.objectid + key.offset) { bit = free_space_test_bit(block_group, path, offset); if (prev_bit == 0 && bit == 1) { extent_start = offset; } else if (prev_bit == 1 && bit == 0) { total_found += add_new_free_space(block_group, fs_info, extent_start, offset); if (total_found > CACHING_CTL_WAKE_UP) { total_found = 0; wake_up(&caching_ctl->wait); } extent_count++; } prev_bit = bit; offset += block_group->sectorsize; } } if (prev_bit == 1) { total_found += add_new_free_space(block_group, fs_info, extent_start, end); extent_count++; } if (extent_count != expected_extent_count) { btrfs_err(fs_info, "incorrect extent count for %llu; counted %u, expected %u", block_group->key.objectid, extent_count, expected_extent_count); ASSERT(0); ret = -EIO; goto out; } caching_ctl->progress = (u64)-1; ret = 0; out: return ret; }
/* * If remove is 1, then we are removing free space, thus clearing bits in the * bitmap. If remove is 0, then we are adding free space, thus setting bits in * the bitmap. */ static int modify_free_space_bitmap(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size, int remove) { struct btrfs_root *root = fs_info->free_space_root; struct btrfs_key key; u64 end = start + size; u64 cur_start, cur_size; int prev_bit, next_bit; int new_extents; int ret; /* * Read the bit for the block immediately before the extent of space if * that block is within the block group. */ if (start > block_group->key.objectid) { u64 prev_block = start - block_group->sectorsize; key.objectid = prev_block; key.type = (u8)-1; key.offset = (u64)-1; ret = btrfs_search_prev_slot(trans, root, &key, path, 0, 1); if (ret) goto out; prev_bit = free_space_test_bit(block_group, path, prev_block); /* The previous block may have been in the previous bitmap. */ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if (start >= key.objectid + key.offset) { ret = free_space_next_bitmap(trans, root, path); if (ret) goto out; } } else { key.objectid = start; key.type = (u8)-1; key.offset = (u64)-1; ret = btrfs_search_prev_slot(trans, root, &key, path, 0, 1); if (ret) goto out; prev_bit = -1; } /* * Iterate over all of the bitmaps overlapped by the extent of space, * clearing/setting bits as required. */ cur_start = start; cur_size = size; while (1) { free_space_set_bits(block_group, path, &cur_start, &cur_size, !remove); if (cur_size == 0) break; ret = free_space_next_bitmap(trans, root, path); if (ret) goto out; } /* * Read the bit for the block immediately after the extent of space if * that block is within the block group. */ if (end < block_group->key.objectid + block_group->key.offset) { /* The next block may be in the next bitmap. */ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if (end >= key.objectid + key.offset) { ret = free_space_next_bitmap(trans, root, path); if (ret) goto out; } next_bit = free_space_test_bit(block_group, path, end); } else { next_bit = -1; } if (remove) { new_extents = -1; if (prev_bit == 1) { /* Leftover on the left. */ new_extents++; } if (next_bit == 1) { /* Leftover on the right. */ new_extents++; } } else { new_extents = 1; if (prev_bit == 1) { /* Merging with neighbor on the left. */ new_extents--; } if (next_bit == 1) { /* Merging with neighbor on the right. */ new_extents--; } } btrfs_release_path(path); ret = update_free_space_extent_count(trans, fs_info, block_group, path, new_extents); out: return ret; }
static int __check_free_space_extents(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *cache, struct btrfs_path *path, struct free_space_extent *extents, unsigned int num_extents) { struct btrfs_free_space_info *info; struct btrfs_key key; int prev_bit = 0, bit; u64 extent_start = 0, offset, end; u32 flags, extent_count; unsigned int i; int ret; info = search_free_space_info(trans, fs_info, cache, path, 0); if (IS_ERR(info)) { test_msg("Could not find free space info\n"); ret = PTR_ERR(info); goto out; } flags = btrfs_free_space_flags(path->nodes[0], info); extent_count = btrfs_free_space_extent_count(path->nodes[0], info); if (extent_count != num_extents) { test_msg("Extent count is wrong\n"); ret = -EINVAL; goto out; } if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) { if (path->slots[0] != 0) goto invalid; end = cache->key.objectid + cache->key.offset; i = 0; while (++path->slots[0] < btrfs_header_nritems(path->nodes[0])) { btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if (key.type != BTRFS_FREE_SPACE_BITMAP_KEY) goto invalid; offset = key.objectid; while (offset < key.objectid + key.offset) { bit = free_space_test_bit(cache, path, offset); if (prev_bit == 0 && bit == 1) { extent_start = offset; } else if (prev_bit == 1 && bit == 0) { if (i >= num_extents) goto invalid; if (i >= num_extents || extent_start != extents[i].start || offset - extent_start != extents[i].length) goto invalid; i++; } prev_bit = bit; offset += cache->sectorsize; } } if (prev_bit == 1) { if (i >= num_extents || extent_start != extents[i].start || end - extent_start != extents[i].length) goto invalid; i++; } if (i != num_extents) goto invalid; } else { if (btrfs_header_nritems(path->nodes[0]) != num_extents + 1 || path->slots[0] != 0) goto invalid; for (i = 0; i < num_extents; i++) { path->slots[0]++; btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if (key.type != BTRFS_FREE_SPACE_EXTENT_KEY || key.objectid != extents[i].start || key.offset != extents[i].length) goto invalid; } } ret = 0; out: btrfs_release_path(path); return ret; invalid: test_msg("Free space tree is invalid\n"); ret = -EINVAL; goto out; }