diff options
Diffstat (limited to 'fs/btrfs/extent-cache.c')
| -rw-r--r-- | fs/btrfs/extent-cache.c | 318 | 
1 files changed, 318 insertions, 0 deletions
| diff --git a/fs/btrfs/extent-cache.c b/fs/btrfs/extent-cache.c new file mode 100644 index 00000000000..bc8cf3a5227 --- /dev/null +++ b/fs/btrfs/extent-cache.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Crossported from the same named file of btrfs-progs. + * + * Minor modification to include headers. + */ +#include <linux/kernel.h> +#include <linux/rbtree.h> +#include <linux/errno.h> +#include <linux/bug.h> +#include <stdlib.h> +#include "extent-cache.h" +#include "common/rbtree-utils.h" + +struct cache_extent_search_range { +	u64 objectid; +	u64 start; +	u64 size; +}; + +static int cache_tree_comp_range(struct rb_node *node, void *data) +{ +	struct cache_extent *entry; +	struct cache_extent_search_range *range; + +	range = (struct cache_extent_search_range *)data; +	entry = rb_entry(node, struct cache_extent, rb_node); + +	if (entry->start + entry->size <= range->start) +		return 1; +	else if (range->start + range->size <= entry->start) +		return -1; +	else +		return 0; +} + +static int cache_tree_comp_nodes(struct rb_node *node1, struct rb_node *node2) +{ +	struct cache_extent *entry; +	struct cache_extent_search_range range; + +	entry = rb_entry(node2, struct cache_extent, rb_node); +	range.start = entry->start; +	range.size = entry->size; + +	return cache_tree_comp_range(node1, (void *)&range); +} + +static int cache_tree_comp_range2(struct rb_node *node, void *data) +{ +	struct cache_extent *entry; +	struct cache_extent_search_range *range; + +	range = (struct cache_extent_search_range *)data; +	entry = rb_entry(node, struct cache_extent, rb_node); + +	if (entry->objectid < range->objectid) +		return 1; +	else if (entry->objectid > range->objectid) +		return -1; +	else if (entry->start + entry->size <= range->start) +		return 1; +	else if (range->start + range->size <= entry->start) +		return -1; +	else +		return 0; +} + +static int cache_tree_comp_nodes2(struct rb_node *node1, struct rb_node *node2) +{ +	struct cache_extent *entry; +	struct cache_extent_search_range range; + +	entry = rb_entry(node2, struct cache_extent, rb_node); +	range.objectid = entry->objectid; +	range.start = entry->start; +	range.size = entry->size; + +	return cache_tree_comp_range2(node1, (void *)&range); +} + +void cache_tree_init(struct cache_tree *tree) +{ +	tree->root = RB_ROOT; +} + +static struct cache_extent *alloc_cache_extent(u64 start, u64 size) +{ +	struct cache_extent *pe = malloc(sizeof(*pe)); + +	if (!pe) +		return pe; + +	pe->objectid = 0; +	pe->start = start; +	pe->size = size; +	return pe; +} + +int add_cache_extent(struct cache_tree *tree, u64 start, u64 size) +{ +	struct cache_extent *pe = alloc_cache_extent(start, size); +	int ret; + +	if (!pe) +		return -ENOMEM; + +	ret = insert_cache_extent(tree, pe); +	if (ret) +		free(pe); + +	return ret; +} + +int insert_cache_extent(struct cache_tree *tree, struct cache_extent *pe) +{ +	return rb_insert(&tree->root, &pe->rb_node, cache_tree_comp_nodes); +} + +int insert_cache_extent2(struct cache_tree *tree, struct cache_extent *pe) +{ +	return rb_insert(&tree->root, &pe->rb_node, cache_tree_comp_nodes2); +} + +struct cache_extent *lookup_cache_extent(struct cache_tree *tree, +					 u64 start, u64 size) +{ +	struct rb_node *node; +	struct cache_extent *entry; +	struct cache_extent_search_range range; + +	range.start = start; +	range.size = size; +	node = rb_search(&tree->root, &range, cache_tree_comp_range, NULL); +	if (!node) +		return NULL; + +	entry = rb_entry(node, struct cache_extent, rb_node); +	return entry; +} + +struct cache_extent *lookup_cache_extent2(struct cache_tree *tree, +					 u64 objectid, u64 start, u64 size) +{ +	struct rb_node *node; +	struct cache_extent *entry; +	struct cache_extent_search_range range; + +	range.objectid = objectid; +	range.start = start; +	range.size = size; +	node = rb_search(&tree->root, &range, cache_tree_comp_range2, NULL); +	if (!node) +		return NULL; + +	entry = rb_entry(node, struct cache_extent, rb_node); +	return entry; +} + +struct cache_extent *search_cache_extent(struct cache_tree *tree, u64 start) +{ +	struct rb_node *next; +	struct rb_node *node; +	struct cache_extent *entry; +	struct cache_extent_search_range range; + +	range.start = start; +	range.size = 1; +	node = rb_search(&tree->root, &range, cache_tree_comp_range, &next); +	if (!node) +		node = next; +	if (!node) +		return NULL; + +	entry = rb_entry(node, struct cache_extent, rb_node); +	return entry; +} + +struct cache_extent *search_cache_extent2(struct cache_tree *tree, +					 u64 objectid, u64 start) +{ +	struct rb_node *next; +	struct rb_node *node; +	struct cache_extent *entry; +	struct cache_extent_search_range range; + +	range.objectid = objectid; +	range.start = start; +	range.size = 1; +	node = rb_search(&tree->root, &range, cache_tree_comp_range2, &next); +	if (!node) +		node = next; +	if (!node) +		return NULL; + +	entry = rb_entry(node, struct cache_extent, rb_node); +	return entry; +} + +struct cache_extent *first_cache_extent(struct cache_tree *tree) +{ +	struct rb_node *node = rb_first(&tree->root); + +	if (!node) +		return NULL; +	return rb_entry(node, struct cache_extent, rb_node); +} + +struct cache_extent *last_cache_extent(struct cache_tree *tree) +{ +	struct rb_node *node = rb_last(&tree->root); + +	if (!node) +		return NULL; +	return rb_entry(node, struct cache_extent, rb_node); +} + +struct cache_extent *prev_cache_extent(struct cache_extent *pe) +{ +	struct rb_node *node = rb_prev(&pe->rb_node); + +	if (!node) +		return NULL; +	return rb_entry(node, struct cache_extent, rb_node); +} + +struct cache_extent *next_cache_extent(struct cache_extent *pe) +{ +	struct rb_node *node = rb_next(&pe->rb_node); + +	if (!node) +		return NULL; +	return rb_entry(node, struct cache_extent, rb_node); +} + +void remove_cache_extent(struct cache_tree *tree, struct cache_extent *pe) +{ +	rb_erase(&pe->rb_node, &tree->root); +} + +void cache_tree_free_extents(struct cache_tree *tree, +			     free_cache_extent free_func) +{ +	struct cache_extent *ce; + +	while ((ce = first_cache_extent(tree))) { +		remove_cache_extent(tree, ce); +		free_func(ce); +	} +} + +static void free_extent_cache(struct cache_extent *pe) +{ +	free(pe); +} + +void free_extent_cache_tree(struct cache_tree *tree) +{ +	cache_tree_free_extents(tree, free_extent_cache); +} + +int add_merge_cache_extent(struct cache_tree *tree, u64 start, u64 size) +{ +	struct cache_extent *cache; +	struct cache_extent *next = NULL; +	struct cache_extent *prev = NULL; +	int next_merged = 0; +	int prev_merged = 0; +	int ret = 0; + +	if (cache_tree_empty(tree)) +		goto insert; + +	cache = search_cache_extent(tree, start); +	if (!cache) { +		/* +		 * Either the tree is completely empty, or the no range after +		 * start. +		 * Either way, the last cache_extent should be prev. +		 */ +		prev = last_cache_extent(tree); +	} else if (start <= cache->start) { +		next = cache; +		prev = prev_cache_extent(cache); +	} else { +		prev = cache; +		next = next_cache_extent(cache); +	} + +	/* +	 * Ensure the range to be inserted won't cover with existings +	 * Or we will need extra loop to do merge +	 */ +	BUG_ON(next && start + size > next->start); +	BUG_ON(prev && prev->start + prev->size > start); + +	if (next && start + size == next->start) { +		next_merged = 1; +		next->size = next->start + next->size - start; +		next->start = start; +	} +	if (prev && prev->start + prev->size == start) { +		prev_merged = 1; +		if (next_merged) { +			next->size = next->start + next->size - prev->start; +			next->start = prev->start; +			remove_cache_extent(tree, prev); +			free(prev); +		} else { +			prev->size = start + size - prev->start; +		} +	} +insert: +	if (!prev_merged && !next_merged) +		ret = add_cache_extent(tree, start, size); +	return ret; +} | 
