if (WARN_ON(cache->length == 0))
btrfs_warn(cache->fs_info, "block group %llu length is zero",
cache->start);
/* * We convert to bitmaps when the disk space required for using extents * exceeds that required for using bitmaps.
*/
bitmap_range = cache->fs_info->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS;
num_bitmaps = div_u64(cache->length + bitmap_range - 1, bitmap_range);
bitmap_size = sizeof(struct btrfs_item) + BTRFS_FREE_SPACE_BITMAP_SIZE;
total_bitmap_size = num_bitmaps * bitmap_size;
cache->bitmap_high_thresh = div_u64(total_bitmap_size, sizeof(struct btrfs_item));
/* * We allow for a small buffer between the high threshold and low * threshold to avoid thrashing back and forth between the two formats.
*/ if (cache->bitmap_high_thresh > 100)
cache->bitmap_low_thresh = cache->bitmap_high_thresh - 100; else
cache->bitmap_low_thresh = 0;
}
/* * btrfs_search_slot() but we're looking for the greatest key less than the * passed key.
*/ staticint btrfs_search_prev_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key, struct btrfs_path *p, int ins_len, int cow)
{ int ret;
ret = btrfs_search_slot(trans, root, key, p, ins_len, cow); if (ret < 0) return ret;
/* * GFP_NOFS doesn't work with kvmalloc(), but we really can't recurse * into the filesystem as the free space bitmap can be modified in the * critical section of a transaction commit. * * TODO: push the memalloc_nofs_{save,restore}() to the caller where we * know that recursion is unsafe.
*/
nofs_flag = memalloc_nofs_save();
ret = kvzalloc(bitmap_rounded_size, GFP_KERNEL);
memalloc_nofs_restore(nofs_flag); return ret;
}
ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
first = (*start - found_start) >> fs_info->sectorsize_bits;
last = (end - found_start) >> fs_info->sectorsize_bits; if (set_bits)
extent_buffer_bitmap_set(leaf, ptr, first, last - first); else
extent_buffer_bitmap_clear(leaf, ptr, first, last - first);
btrfs_mark_buffer_dirty(trans, leaf);
*size -= end - *start;
*start = end;
}
/* * We can't use btrfs_next_item() in modify_free_space_bitmap() because * btrfs_next_leaf() doesn't get the path for writing. We can forgo the fancy * tree walking in btrfs_next_leaf() anyways because we know exactly what we're * looking for.
*/ staticint free_space_next_bitmap(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *p)
{ struct btrfs_key key;
/* * 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.
*/ staticint modify_free_space_bitmap(struct btrfs_trans_handle *trans, struct btrfs_block_group *block_group, struct btrfs_path *path,
u64 start, u64 size, bool remove)
{ struct btrfs_root *root = btrfs_free_space_root(block_group); struct btrfs_key key;
u64 end = start + size;
u64 cur_start, cur_size; bool prev_bit_set = false; bool next_bit_set = false; 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->start) {
u64 prev_block = start - block_group->fs_info->sectorsize;
/* 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) return ret;
}
} 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) return ret;
}
/* * 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_modify_bits(trans, block_group, path, &cur_start,
&cur_size, !remove); if (cur_size == 0) break;
ret = free_space_next_bitmap(trans, root, path); if (ret) return ret;
}
/* * Read the bit for the block immediately after the extent of space if * that block is within the block group.
*/ if (end < block_group->start + block_group->length) { /* 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) return ret;
}
if (remove) {
new_extents = -1; if (prev_bit_set) { /* Leftover on the left. */
new_extents++;
} if (next_bit_set) { /* Leftover on the right. */
new_extents++;
}
} else {
new_extents = 1; if (prev_bit_set) { /* Merging with neighbor on the left. */
new_extents--;
} if (next_bit_set) { /* Merging with neighbor on the right. */
new_extents--;
}
}
/* * Okay, now that we've found the free space extent which contains the * free space that we are removing, there are four cases: * * 1. We're using the whole extent: delete the key we found and * decrement the free space extent count. * 2. We are using part of the extent starting at the beginning: delete * the key we found and insert a new key representing the leftover at * the end. There is no net change in the number of extents. * 3. We are using part of the extent ending at the end: delete the key * we found and insert a new key representing the leftover at the * beginning. There is no net change in the number of extents. * 4. We are using part of the extent in the middle: delete the key we * found and insert two new keys representing the leftovers on each * side. Where we used to have one extent, we now have two, so increment * the extent count. We may need to convert the block group to bitmaps * as a result.
*/
/* Delete the existing key (cases 1-4). */
ret = btrfs_del_item(trans, root, path); if (ret) return ret;
/* Add a key for leftovers at the beginning (cases 3 and 4). */ if (start > found_start) {
key.objectid = found_start;
key.type = BTRFS_FREE_SPACE_EXTENT_KEY;
key.offset = start - found_start;
btrfs_release_path(path);
ret = btrfs_insert_empty_item(trans, root, path, &key, 0); if (ret) return ret;
new_extents++;
}
/* Add a key for leftovers at the end (cases 2 and 4). */ if (end < found_end) {
key.objectid = end;
key.type = BTRFS_FREE_SPACE_EXTENT_KEY;
key.offset = found_end - end;
btrfs_release_path(path);
ret = btrfs_insert_empty_item(trans, root, path, &key, 0); if (ret) return ret;
new_extents++;
}
int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans,
u64 start, u64 size)
{ struct btrfs_block_group *block_group; struct btrfs_path *path; int ret;
if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) return 0;
path = btrfs_alloc_path(); if (!path) {
ret = -ENOMEM;
btrfs_abort_transaction(trans, ret); goto out;
}
block_group = btrfs_lookup_block_group(trans->fs_info, start); if (!block_group) {
DEBUG_WARN("no block group found for start=%llu", start);
ret = -ENOENT;
btrfs_abort_transaction(trans, ret); goto out;
}
mutex_lock(&block_group->free_space_lock);
ret = __btrfs_remove_from_free_space_tree(trans, block_group, path, start, size);
mutex_unlock(&block_group->free_space_lock); if (ret)
btrfs_abort_transaction(trans, ret);
/* * We are adding a new extent of free space, but we need to merge * extents. There are four cases here: * * 1. The new extent does not have any immediate neighbors to merge * with: add the new key and increment the free space extent count. We * may need to convert the block group to bitmaps as a result. * 2. The new extent has an immediate neighbor before it: remove the * previous key and insert a new key combining both of them. There is no * net change in the number of extents. * 3. The new extent has an immediate neighbor after it: remove the next * key and insert a new key combining both of them. There is no net * change in the number of extents. * 4. The new extent has immediate neighbors on both sides: remove both * of the keys and insert a new key combining all of them. Where we used * to have two extents, we now have one, so decrement the extent count.
*/
/* * Delete the neighbor on the left and absorb it into the new key (cases * 2 and 4).
*/ if (found_end == start) {
ret = btrfs_del_item(trans, root, path); if (ret) return ret;
new_key.objectid = found_start;
new_key.offset += key.offset;
new_extents--;
}
btrfs_release_path(path);
right: /* Search for a neighbor on the right. */ if (end == block_group->start + block_group->length) goto insert;
key.objectid = end;
key.type = (u8)-1;
key.offset = (u64)-1;
ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1); if (ret) return ret;
/* * Delete the neighbor on the right and absorb it into the new key * (cases 3 and 4).
*/ if (found_start == end) {
ret = btrfs_del_item(trans, root, path); if (ret) return ret;
new_key.offset += key.offset;
new_extents--;
}
btrfs_release_path(path);
insert: /* Insert the new key (cases 1-4). */
ret = btrfs_insert_empty_item(trans, root, path, &new_key, 0); if (ret) return ret;
int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans,
u64 start, u64 size)
{ struct btrfs_block_group *block_group; struct btrfs_path *path; int ret;
if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) return 0;
path = btrfs_alloc_path(); if (!path) {
ret = -ENOMEM;
btrfs_abort_transaction(trans, ret); goto out;
}
block_group = btrfs_lookup_block_group(trans->fs_info, start); if (!block_group) {
DEBUG_WARN("no block group found for start=%llu", start);
ret = -ENOENT;
btrfs_abort_transaction(trans, ret); goto out;
}
mutex_lock(&block_group->free_space_lock);
ret = __btrfs_add_to_free_space_tree(trans, block_group, path, start, size);
mutex_unlock(&block_group->free_space_lock); if (ret)
btrfs_abort_transaction(trans, ret);
/* * Populate the free space tree by walking the extent tree. Operations on the * extent tree that happen as a result of writes to the free space tree will go * through the normal add/remove hooks.
*/ staticint populate_free_space_tree(struct btrfs_trans_handle *trans, struct btrfs_block_group *block_group)
{ struct btrfs_root *extent_root;
BTRFS_PATH_AUTO_FREE(path);
BTRFS_PATH_AUTO_FREE(path2); struct btrfs_key key;
u64 start, end; int ret;
path = btrfs_alloc_path(); if (!path) return -ENOMEM;
path2 = btrfs_alloc_path(); if (!path2) return -ENOMEM;
path->reada = READA_FORWARD;
ret = add_new_free_space_info(trans, block_group, path2); if (ret) return ret;
mutex_lock(&block_group->free_space_lock);
/* * Iterate through all of the extent and metadata items in this block * group, adding the free space between them and the free space at the * end. Note that EXTENT_ITEM and METADATA_ITEM are less than * BLOCK_GROUP_ITEM, so an extent may precede the block group that it's * contained in.
*/
key.objectid = block_group->start;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
extent_root = btrfs_extent_root(trans->fs_info, key.objectid);
ret = btrfs_search_slot_for_read(extent_root, &key, path, 1, 0); if (ret < 0) goto out_locked; /* * If ret is 1 (no key found), it means this is an empty block group, * without any extents allocated from it and there's no block group * item (key BTRFS_BLOCK_GROUP_ITEM_KEY) located in the extent tree * because we are using the block group tree feature (so block group * items are stored in the block group tree) or this is a new block * group created in the current transaction and its block group item * was not yet inserted in the extent tree (that happens in * btrfs_create_pending_block_groups() -> insert_block_group_item()). * It also means there are no extents allocated for block groups with a * start offset beyond this block group's end offset (this is the last, * highest, block group).
*/
start = block_group->start;
end = block_group->start + block_group->length; while (ret == 0) {
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
if (key.type == BTRFS_EXTENT_ITEM_KEY ||
key.type == BTRFS_METADATA_ITEM_KEY) { if (key.objectid >= end) break;
if (start < key.objectid) {
ret = __btrfs_add_to_free_space_tree(trans,
block_group,
path2, start,
key.objectid -
start); if (ret) goto out_locked;
}
start = key.objectid; if (key.type == BTRFS_METADATA_ITEM_KEY)
start += trans->fs_info->nodesize; else
start += key.offset;
} elseif (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) { if (key.objectid != block_group->start) break;
}
ret = btrfs_next_item(extent_root, path); if (ret < 0) goto out_locked;
} if (start < end) {
ret = __btrfs_add_to_free_space_tree(trans, block_group, path2,
start, end - start); if (ret) goto out_locked;
}
ret = 0;
out_locked:
mutex_unlock(&block_group->free_space_lock);
trans = btrfs_start_transaction(tree_root, 0); if (IS_ERR(trans)) return PTR_ERR(trans);
set_bit(BTRFS_FS_CREATING_FREE_SPACE_TREE, &fs_info->flags);
set_bit(BTRFS_FS_FREE_SPACE_TREE_UNTRUSTED, &fs_info->flags);
free_space_root = btrfs_create_tree(trans,
BTRFS_FREE_SPACE_TREE_OBJECTID); if (IS_ERR(free_space_root)) {
ret = PTR_ERR(free_space_root);
btrfs_abort_transaction(trans, ret);
btrfs_end_transaction(trans); goto out_clear;
}
ret = btrfs_global_root_insert(free_space_root); if (ret) {
btrfs_put_root(free_space_root);
btrfs_abort_transaction(trans, ret);
btrfs_end_transaction(trans); goto out_clear;
}
node = rb_first_cached(&fs_info->block_group_cache_tree); while (node) {
block_group = rb_entry(node, struct btrfs_block_group,
cache_node);
ret = populate_free_space_tree(trans, block_group); if (ret) {
btrfs_abort_transaction(trans, ret);
btrfs_end_transaction(trans); goto out_clear;
}
node = rb_next(node);
}
btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE);
btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
clear_bit(BTRFS_FS_CREATING_FREE_SPACE_TREE, &fs_info->flags);
ret = btrfs_commit_transaction(trans);
/* * Now that we've committed the transaction any reading of our commit * root will be safe, so we can cache from the free space tree now.
*/
clear_bit(BTRFS_FS_FREE_SPACE_TREE_UNTRUSTED, &fs_info->flags); return ret;
if (!test_and_clear_bit(BLOCK_GROUP_FLAG_NEEDS_FREE_SPACE,
&block_group->runtime_flags)) return 0;
/* * While rebuilding the free space tree we may allocate new metadata * block groups while modifying the free space tree. * * Because during the rebuild (at btrfs_rebuild_free_space_tree()) we * can use multiple transactions, every time btrfs_end_transaction() is * called at btrfs_rebuild_free_space_tree() we finish the creation of * new block groups by calling btrfs_create_pending_block_groups(), and * that in turn calls us, through add_block_group_free_space(), to add * a free space info item and a free space extent item for the block * group. * * Then later btrfs_rebuild_free_space_tree() may find such new block * groups and processes them with populate_free_space_tree(), which can * fail with EEXIST since there are already items for the block group in * the free space tree. Notice that we say "may find" because a new * block group may be added to the block groups rbtree in a node before * or after the block group currently being processed by the rebuild * process. So signal the rebuild process to skip such new block groups * if it finds them.
*/
set_bit(BLOCK_GROUP_FLAG_FREE_SPACE_ADDED, &block_group->runtime_flags);
if (!path) {
path = btrfs_alloc_path(); if (!path) {
btrfs_abort_transaction(trans, -ENOMEM); return -ENOMEM;
}
own_path = true;
}
ret = add_new_free_space_info(trans, block_group, path); if (ret) {
btrfs_abort_transaction(trans, ret); goto out;
}
ret = __btrfs_add_to_free_space_tree(trans, block_group, path,
block_group->start, block_group->length); if (ret)
btrfs_abort_transaction(trans, ret);
out: if (own_path)
btrfs_free_path(path);
return ret;
}
int btrfs_add_block_group_free_space(struct btrfs_trans_handle *trans, struct btrfs_block_group *block_group)
{ int ret;
if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) return 0;
mutex_lock(&block_group->free_space_lock);
ret = __add_block_group_free_space(trans, block_group, NULL);
mutex_unlock(&block_group->free_space_lock); return ret;
}
if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) return 0;
if (test_bit(BLOCK_GROUP_FLAG_NEEDS_FREE_SPACE, &block_group->runtime_flags)) { /* We never added this block group to the free space tree. */ return 0;
}
path = btrfs_alloc_path(); if (!path) {
ret = -ENOMEM;
btrfs_abort_transaction(trans, ret); goto out;
}
start = block_group->start;
end = block_group->start + block_group->length;
path = btrfs_alloc_path(); if (!path) return -ENOMEM;
/* * Just like caching_thread() doesn't want to deadlock on the extent * tree, we don't want to deadlock on the free space tree.
*/
path->skip_locking = 1;
path->search_commit_root = 1;
path->reada = READA_FORWARD;
info = btrfs_search_free_space_info(NULL, block_group, path, 0); if (IS_ERR(info)) return PTR_ERR(info);
/* * We left path pointing to the free space info item, so now * load_free_space_foo can just iterate through the free space tree from * there.
*/ if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) return load_free_space_bitmaps(caching_ctl, path, extent_count); else return load_free_space_extents(caching_ctl, path, extent_count);
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.