#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
+#include <math.h>
#include "kerncompat.h"
#include "radix-tree.h"
#include "ctree.h"
#include "crc32c.h"
#include "volumes.h"
#include "free-space-cache.h"
-#include "math.h"
#include "utils.h"
#define PENDING_EXTENT_INSERT 0
GFP_NOFS);
}
if (key.type == BTRFS_METADATA_ITEM_KEY)
- last = key.objectid + root->leafsize;
+ last = key.objectid + root->nodesize;
else
last = key.objectid + key.offset;
}
}
#endif
-static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
+u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
{
u32 high_crc = ~(u32)0;
u32 low_crc = ~(u32)0;
if (metadata &&
!btrfs_fs_incompat(root->fs_info,
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) {
- offset = root->leafsize;
+ offset = root->nodesize;
metadata = 0;
}
path->slots[0]);
if (key.objectid == bytenr &&
key.type == BTRFS_EXTENT_ITEM_KEY &&
- key.offset == root->leafsize)
+ key.offset == root->nodesize)
ret = 0;
}
if (ret) {
btrfs_release_path(path);
key.type = BTRFS_EXTENT_ITEM_KEY;
- key.offset = root->leafsize;
+ key.offset = root->nodesize;
metadata = 0;
goto again;
}
key.offset = level;
key.type = BTRFS_METADATA_ITEM_KEY;
} else {
- key.offset = root->leafsize;
+ key.offset = root->nodesize;
key.type = BTRFS_EXTENT_ITEM_KEY;
}
btrfs_item_key_to_cpu(path->nodes[0], &key,
path->slots[0]);
if (key.objectid == bytenr &&
- key.offset == root->leafsize &&
+ key.offset == root->nodesize &&
key.type == BTRFS_EXTENT_ITEM_KEY)
ret = 0;
}
if (ret) {
btrfs_release_path(path);
- key.offset = root->leafsize;
+ key.offset = root->nodesize;
key.type = BTRFS_EXTENT_ITEM_KEY;
goto again;
}
}
} else {
bytenr = btrfs_node_blockptr(buf, i);
- num_bytes = btrfs_level_size(root, level - 1);
+ num_bytes = root->nodesize;
ret = process_func(trans, root, bytenr, num_bytes,
parent, ref_root, level - 1, 0);
if (ret) {
thresh)
return 0;
+ /*
+ * Avoid allocating given chunk type
+ */
+ if (extent_root->fs_info->avoid_meta_chunk_alloc &&
+ (flags & BTRFS_BLOCK_GROUP_METADATA))
+ return 0;
+ if (extent_root->fs_info->avoid_sys_chunk_alloc &&
+ (flags & BTRFS_BLOCK_GROUP_SYSTEM))
+ return 0;
+
ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes,
space_info->flags);
if (ret == -ENOSPC) {
struct btrfs_key key;
struct btrfs_path *path;
- struct btrfs_extent_ops *ops = root->fs_info->extent_ops;
struct btrfs_root *extent_root = root->fs_info->extent_root;
struct extent_buffer *leaf;
struct btrfs_extent_item *ei;
}
}
- if (ops && ops->free_extent) {
- ret = ops->free_extent(root, bytenr, num_bytes);
- if (ret > 0) {
- pin = 0;
- mark_free = 0;
- }
- }
-
if (pin) {
ret = pin_down_bytes(trans, root, bytenr, num_bytes,
is_data);
u64 alloc_profile;
struct btrfs_fs_info *info = root->fs_info;
- if (info->extent_ops) {
- struct btrfs_extent_ops *ops = info->extent_ops;
- ret = ops->alloc_extent(root, num_bytes, hint_byte, ins);
- BUG_ON(ret);
- goto found;
- }
-
if (data) {
alloc_profile = info->avail_data_alloc_bits &
info->data_alloc_profile;
trans->alloc_exclude_start,
trans->alloc_exclude_nr, data);
BUG_ON(ret);
-found:
clear_extent_dirty(&root->fs_info->free_space_cache,
ins->objectid, ins->objectid + ins->offset - 1,
GFP_NOFS);
btrfs_mark_buffer_dirty(leaf);
btrfs_free_path(path);
- ret = update_block_group(trans, root, ins->objectid, root->leafsize,
+ ret = update_block_group(trans, root, ins->objectid, root->nodesize,
1, 0);
return ret;
}
return ERR_PTR(ret);
}
- buf = btrfs_find_create_tree_block(root, ins.objectid, blocksize);
+ buf = btrfs_find_create_tree_block(root->fs_info, ins.objectid,
+ blocksize);
if (!buf) {
btrfs_free_extent(trans, root, ins.objectid, ins.offset,
0, root->root_key.objectid, level, 0);
cache = kzalloc(sizeof(*cache), GFP_NOFS);
if (!cache) {
ret = -ENOMEM;
- break;
+ goto error;
}
read_extent_buffer(leaf, &cache->item,
key.objectid, key.offset, 1, 0);
BUG_ON(ret);
} else if (key.type == BTRFS_METADATA_ITEM_KEY) {
- bytes_used += root->leafsize;
+ bytes_used += root->nodesize;
ret = btrfs_update_block_group(trans, root,
- key.objectid, root->leafsize, 1, 0);
+ key.objectid, root->nodesize, 1, 0);
BUG_ON(ret);
}
path.slots[0]++;
return 0;
}
+static void __get_extent_size(struct btrfs_root *root, struct btrfs_path *path,
+ u64 *start, u64 *len)
+{
+ struct btrfs_key key;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ BUG_ON(!(key.type == BTRFS_EXTENT_ITEM_KEY ||
+ key.type == BTRFS_METADATA_ITEM_KEY));
+ *start = key.objectid;
+ if (key.type == BTRFS_EXTENT_ITEM_KEY)
+ *len = key.offset;
+ else
+ *len = root->nodesize;
+}
+
/*
- * Record a file extent. Do all the required works, such as inserting
- * file extent item, inserting extent item and backref item into extent
- * tree and updating block accounting.
+ * Find first overlap extent for range [bytenr, bytenr + len)
+ * Return 0 for found and point path to it.
+ * Return >0 for not found.
+ * Return <0 for err
*/
-int btrfs_record_file_extent(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- struct btrfs_inode_item *inode,
- u64 file_pos, u64 disk_bytenr,
- u64 num_bytes)
+int btrfs_search_overlap_extent(struct btrfs_root *root,
+ struct btrfs_path *path, u64 bytenr, u64 len)
+{
+ struct btrfs_key key;
+ u64 cur_start;
+ u64 cur_len;
+ int ret;
+
+ key.objectid = bytenr;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = (u64)-1;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ return ret;
+ BUG_ON(ret == 0);
+
+ ret = btrfs_previous_extent_item(root, path, 0);
+ if (ret < 0)
+ return ret;
+ /* no previous, check next extent */
+ if (ret > 0)
+ goto next;
+ __get_extent_size(root, path, &cur_start, &cur_len);
+ /* Tail overlap */
+ if (cur_start + cur_len > bytenr)
+ return 1;
+
+next:
+ ret = btrfs_next_extent_item(root, path, bytenr + len);
+ if (ret < 0)
+ return ret;
+ /* No next, prev already checked, no overlap */
+ if (ret > 0)
+ return 0;
+ __get_extent_size(root, path, &cur_start, &cur_len);
+ /* head overlap*/
+ if (cur_start < bytenr + len)
+ return 1;
+ return 0;
+}
+
+static int __btrfs_record_file_extent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ struct btrfs_inode_item *inode,
+ u64 file_pos, u64 disk_bytenr,
+ u64 *ret_num_bytes)
{
int ret;
struct btrfs_fs_info *info = root->fs_info;
struct extent_buffer *leaf;
struct btrfs_file_extent_item *fi;
struct btrfs_key ins_key;
- struct btrfs_path path;
+ struct btrfs_path *path;
struct btrfs_extent_item *ei;
u64 nbytes;
+ u64 extent_num_bytes;
+ u64 extent_bytenr;
+ u64 extent_offset;
+ u64 num_bytes = *ret_num_bytes;
+ /*
+ * All supported file system should not use its 0 extent.
+ * As it's for hole
+ *
+ * And hole extent has no size limit, no need to loop.
+ */
if (disk_bytenr == 0) {
ret = btrfs_insert_file_extent(trans, root, objectid,
file_pos, disk_bytenr,
num_bytes, num_bytes);
return ret;
}
+ num_bytes = min_t(u64, num_bytes, BTRFS_MAX_EXTENT_SIZE);
- btrfs_init_path(&path);
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ /* First to check extent overlap */
+ ret = btrfs_search_overlap_extent(extent_root, path, disk_bytenr,
+ num_bytes);
+ if (ret < 0)
+ goto fail;
+ if (ret > 0) {
+ /* Found overlap */
+ u64 cur_start;
+ u64 cur_len;
+
+ __get_extent_size(extent_root, path, &cur_start, &cur_len);
+ /*
+ * For convert case, this extent should be a subset of
+ * existing one.
+ */
+ BUG_ON(disk_bytenr < cur_start);
+
+ extent_bytenr = cur_start;
+ extent_num_bytes = cur_len;
+ extent_offset = disk_bytenr - extent_bytenr;
+ } else {
+ /* No overlap, create new extent */
+ btrfs_release_path(path);
+ ins_key.objectid = disk_bytenr;
+ ins_key.offset = num_bytes;
+ ins_key.type = BTRFS_EXTENT_ITEM_KEY;
+
+ ret = btrfs_insert_empty_item(trans, extent_root, path,
+ &ins_key, sizeof(*ei));
+ if (ret == 0) {
+ leaf = path->nodes[0];
+ ei = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_extent_item);
+
+ btrfs_set_extent_refs(leaf, ei, 0);
+ btrfs_set_extent_generation(leaf, ei, 0);
+ btrfs_set_extent_flags(leaf, ei,
+ BTRFS_EXTENT_FLAG_DATA);
+ btrfs_mark_buffer_dirty(leaf);
+
+ ret = btrfs_update_block_group(trans, root, disk_bytenr,
+ num_bytes, 1, 0);
+ if (ret)
+ goto fail;
+ } else if (ret != -EEXIST) {
+ goto fail;
+ }
+ btrfs_extent_post_op(trans, extent_root);
+ extent_bytenr = disk_bytenr;
+ extent_num_bytes = num_bytes;
+ extent_offset = 0;
+ }
+ btrfs_release_path(path);
ins_key.objectid = objectid;
ins_key.offset = file_pos;
btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY);
- ret = btrfs_insert_empty_item(trans, root, &path, &ins_key,
+ ret = btrfs_insert_empty_item(trans, root, path, &ins_key,
sizeof(*fi));
if (ret)
goto fail;
- leaf = path.nodes[0];
- fi = btrfs_item_ptr(leaf, path.slots[0],
+ leaf = path->nodes[0];
+ fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
btrfs_set_file_extent_generation(leaf, fi, trans->transid);
btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG);
- btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr);
- btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes);
- btrfs_set_file_extent_offset(leaf, fi, 0);
+ btrfs_set_file_extent_disk_bytenr(leaf, fi, extent_bytenr);
+ btrfs_set_file_extent_disk_num_bytes(leaf, fi, extent_num_bytes);
+ btrfs_set_file_extent_offset(leaf, fi, extent_offset);
btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
- btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
+ btrfs_set_file_extent_ram_bytes(leaf, fi, extent_num_bytes);
btrfs_set_file_extent_compression(leaf, fi, 0);
btrfs_set_file_extent_encryption(leaf, fi, 0);
btrfs_set_file_extent_other_encoding(leaf, fi, 0);
nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes;
btrfs_set_stack_inode_nbytes(inode, nbytes);
+ btrfs_release_path(path);
- btrfs_release_path(&path);
+ ret = btrfs_inc_extent_ref(trans, root, extent_bytenr, extent_num_bytes,
+ 0, root->root_key.objectid, objectid,
+ file_pos - extent_offset);
+ if (ret)
+ goto fail;
+ ret = 0;
+ *ret_num_bytes = min(extent_num_bytes - extent_offset, num_bytes);
+fail:
+ btrfs_free_path(path);
+ return ret;
+}
+
+/*
+ * Record a file extent. Do all the required works, such as inserting
+ * file extent item, inserting extent item and backref item into extent
+ * tree and updating block accounting.
+ */
+int btrfs_record_file_extent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ struct btrfs_inode_item *inode,
+ u64 file_pos, u64 disk_bytenr,
+ u64 num_bytes)
+{
+ u64 cur_disk_bytenr = disk_bytenr;
+ u64 cur_file_pos = file_pos;
+ u64 cur_num_bytes = num_bytes;
+ int ret = 0;
+
+ while (num_bytes > 0) {
+ ret = __btrfs_record_file_extent(trans, root, objectid,
+ inode, cur_file_pos,
+ cur_disk_bytenr,
+ &cur_num_bytes);
+ if (ret < 0)
+ break;
+ cur_disk_bytenr += cur_num_bytes;
+ cur_file_pos += cur_num_bytes;
+ num_bytes -= cur_num_bytes;
+ }
+ return ret;
+}
- ins_key.objectid = disk_bytenr;
- ins_key.offset = num_bytes;
- ins_key.type = BTRFS_EXTENT_ITEM_KEY;
- ret = btrfs_insert_empty_item(trans, extent_root, &path,
- &ins_key, sizeof(*ei));
- if (ret == 0) {
- leaf = path.nodes[0];
- ei = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_extent_item);
+static int add_excluded_extent(struct btrfs_root *root,
+ u64 start, u64 num_bytes)
+{
+ u64 end = start + num_bytes - 1;
+ set_extent_bits(&root->fs_info->pinned_extents,
+ start, end, EXTENT_UPTODATE, GFP_NOFS);
+ return 0;
+}
+
+void free_excluded_extents(struct btrfs_root *root,
+ struct btrfs_block_group_cache *cache)
+{
+ u64 start, end;
- btrfs_set_extent_refs(leaf, ei, 0);
- btrfs_set_extent_generation(leaf, ei, 0);
- btrfs_set_extent_flags(leaf, ei, BTRFS_EXTENT_FLAG_DATA);
+ start = cache->key.objectid;
+ end = start + cache->key.offset - 1;
- btrfs_mark_buffer_dirty(leaf);
+ clear_extent_bits(&root->fs_info->pinned_extents,
+ start, end, EXTENT_UPTODATE, GFP_NOFS);
+}
- ret = btrfs_update_block_group(trans, root, disk_bytenr,
- num_bytes, 1, 0);
+int exclude_super_stripes(struct btrfs_root *root,
+ struct btrfs_block_group_cache *cache)
+{
+ u64 bytenr;
+ u64 *logical;
+ int stripe_len;
+ int i, nr, ret;
+
+ if (cache->key.objectid < BTRFS_SUPER_INFO_OFFSET) {
+ stripe_len = BTRFS_SUPER_INFO_OFFSET - cache->key.objectid;
+ cache->bytes_super += stripe_len;
+ ret = add_excluded_extent(root, cache->key.objectid,
+ stripe_len);
if (ret)
- goto fail;
- } else if (ret != -EEXIST) {
- goto fail;
+ return ret;
}
- btrfs_extent_post_op(trans, extent_root);
- ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0,
- root->root_key.objectid,
- objectid, file_pos);
- if (ret)
- goto fail;
- ret = 0;
-fail:
- btrfs_release_path(&path);
- return ret;
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ bytenr = btrfs_sb_offset(i);
+ ret = btrfs_rmap_block(&root->fs_info->mapping_tree,
+ cache->key.objectid, bytenr,
+ 0, &logical, &nr, &stripe_len);
+ if (ret)
+ return ret;
+
+ while (nr--) {
+ u64 start, len;
+
+ if (logical[nr] > cache->key.objectid +
+ cache->key.offset)
+ continue;
+
+ if (logical[nr] + stripe_len <= cache->key.objectid)
+ continue;
+
+ start = logical[nr];
+ if (start < cache->key.objectid) {
+ start = cache->key.objectid;
+ len = (logical[nr] + stripe_len) - start;
+ } else {
+ len = min_t(u64, stripe_len,
+ cache->key.objectid +
+ cache->key.offset - start);
+ }
+
+ cache->bytes_super += len;
+ ret = add_excluded_extent(root, start, len);
+ if (ret) {
+ kfree(logical);
+ return ret;
+ }
+ }
+
+ kfree(logical);
+ }
+ return 0;
+}
+
+u64 add_new_free_space(struct btrfs_block_group_cache *block_group,
+ struct btrfs_fs_info *info, u64 start, u64 end)
+{
+ u64 extent_start, extent_end, size, total_added = 0;
+ int ret;
+
+ while (start < end) {
+ ret = find_first_extent_bit(&info->pinned_extents, start,
+ &extent_start, &extent_end,
+ EXTENT_DIRTY | EXTENT_UPTODATE);
+ if (ret)
+ break;
+
+ if (extent_start <= start) {
+ start = extent_end + 1;
+ } else if (extent_start > start && extent_start < end) {
+ size = extent_start - start;
+ total_added += size;
+ ret = btrfs_add_free_space(block_group->free_space_ctl,
+ start, size);
+ BUG_ON(ret); /* -ENOMEM or logic error */
+ start = extent_end + 1;
+ } else {
+ break;
+ }
+ }
+
+ if (start < end) {
+ size = end - start;
+ total_added += size;
+ ret = btrfs_add_free_space(block_group->free_space_ctl, start,
+ size);
+ BUG_ON(ret); /* -ENOMEM or logic error */
+ }
+
+ return total_added;
}