@@ -1800,6 +1800,85 @@ tree_search_offset(struct btrfs_free_space_ctl *ctl,
return entry;
}
+static bool _need_unlink_offset_tree(struct btrfs_free_space_ctl *ctl,
+ struct btrfs_free_space *info,
+ u64 new_offset, u64 new_bytes)
+{
+ struct rb_node *node = NULL;
+ struct btrfs_free_space *entry = NULL;
+
+ if (new_bytes == 0)
+ return true;
+
+ if (!(info->flags & FREE_SPACE_FLAG_CROSS_BITMAP))
+ return false;
+
+ node = rb_next(&info->offset_index);
+ if (!node)
+ return false;
+
+ entry = rb_entry(node, struct btrfs_free_space, offset_index);
+
+ if (!entry->bitmap)
+ return false;
+
+ if (new_offset <= entry->offset)
+ return false;
+
+ return true;
+}
+
+static bool _need_unlink_bytes_tree(struct btrfs_free_space_ctl *ctl,
+ struct btrfs_free_space *info,
+ u64 new_offset, u64 new_bytes)
+{
+ struct rb_node *node = NULL;
+ struct btrfs_free_space *entry = NULL;
+
+ if (new_bytes == 0)
+ return true;
+
+ node = rb_next(&info->bytes_index);
+ if (!node)
+ return false;
+
+ entry = rb_entry(node, struct btrfs_free_space, bytes_index);
+
+ if (new_bytes >= entry->bytes)
+ return false;
+
+ return true;
+}
+
+static void _do_alloc_and_relink_free_space(struct btrfs_free_space_ctl *ctl,
+ struct btrfs_free_space *info,
+ u64 new_offset, u64 new_bytes)
+{
+ bool flag_need_unlink_offset_tree;
+ bool flag_need_unlink_bytes_tree;
+
+ flag_need_unlink_offset_tree = _need_unlink_offset_tree(ctl,
+ info, new_offset, new_bytes);
+ if (flag_need_unlink_offset_tree)
+ rb_erase(&info->offset_index, &ctl->free_space_offset);
+
+ flag_need_unlink_bytes_tree = _need_unlink_bytes_tree(ctl,
+ info, new_offset, new_bytes);
+ if (flag_need_unlink_bytes_tree)
+ rb_erase_cached(&info->bytes_index, &ctl->free_space_bytes);
+
+ info->offset = new_offset;
+ info->bytes = new_bytes;
+
+ if (flag_need_unlink_offset_tree)
+ tree_insert_offset(&ctl->free_space_offset, info->offset,
+ &info->offset_index, (info->bitmap != NULL));
+
+ if (flag_need_unlink_bytes_tree)
+ rb_add_cached(&info->bytes_index, &ctl->free_space_bytes,
+ entry_less);
+}
+
static inline void unlink_free_space(struct btrfs_free_space_ctl *ctl,
struct btrfs_free_space *info,
bool update_stat)
@@ -1821,8 +1900,18 @@ static int link_free_space(struct btrfs_free_space_ctl *ctl,
struct btrfs_free_space *info)
{
int ret = 0;
+ u64 b_offset;
ASSERT(info->bytes || info->bitmap);
+
+ if (info->bitmap == NULL) {
+ b_offset = offset_to_bitmap(ctl, info->offset + info->bytes);
+ if (b_offset >= info->offset)
+ info->flags |= FREE_SPACE_FLAG_CROSS_BITMAP;
+ else
+ info->flags &= ~FREE_SPACE_FLAG_CROSS_BITMAP;
+ }
+
ret = tree_insert_offset(&ctl->free_space_offset, info->offset,
&info->offset_index, (info->bitmap != NULL));
if (ret)
@@ -3073,6 +3162,7 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group *block_group,
u64 align_gap_len = 0;
enum btrfs_trim_state align_gap_trim_state = BTRFS_TRIM_STATE_UNTRIMMED;
bool use_bytes_index = (offset == block_group->start);
+ u64 new_offset, new_bytes;
ASSERT(!btrfs_is_zoned(block_group->fs_info));
@@ -3093,7 +3183,6 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group *block_group,
if (!entry->bytes)
free_bitmap(ctl, entry);
} else {
- unlink_free_space(ctl, entry, true);
align_gap_len = offset - entry->offset;
align_gap = entry->offset;
align_gap_trim_state = entry->trim_state;
@@ -3101,14 +3190,17 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group *block_group,
if (!btrfs_free_space_trimmed(entry))
atomic64_add(bytes, &discard_ctl->discard_bytes_saved);
- entry->offset = offset + bytes;
+ new_offset = offset + bytes;
WARN_ON(entry->bytes < bytes + align_gap_len);
- entry->bytes -= bytes + align_gap_len;
- if (!entry->bytes)
+ new_bytes = entry->bytes - (bytes + align_gap_len);
+
+ if (!new_bytes) {
+ unlink_free_space(ctl, entry, true);
kmem_cache_free(btrfs_free_space_cachep, entry);
- else
- link_free_space(ctl, entry);
+ } else
+ _do_alloc_and_relink_free_space(ctl, entry,
+ new_offset, new_bytes);
}
out:
btrfs_discard_update_discardable(block_group);
@@ -20,6 +20,8 @@ enum btrfs_trim_state {
BTRFS_TRIM_STATE_TRIMMING,
};
+#define FREE_SPACE_FLAG_CROSS_BITMAP (1ULL << 5)
+
struct btrfs_free_space {
struct rb_node offset_index;
struct rb_node bytes_index;
@@ -30,8 +32,10 @@ struct btrfs_free_space {
struct list_head list;
enum btrfs_trim_state trim_state;
s32 bitmap_extents;
+ u32 flags;
};
+
static inline bool btrfs_free_space_trimmed(struct btrfs_free_space *info)
{
return (info->trim_state == BTRFS_TRIM_STATE_TRIMMED);