summaryrefslogtreecommitdiff
path: root/btrfs-convert.c
diff options
context:
space:
mode:
Diffstat (limited to 'btrfs-convert.c')
-rw-r--r--btrfs-convert.c81
1 files changed, 74 insertions, 7 deletions
diff --git a/btrfs-convert.c b/btrfs-convert.c
index 856fac60..f78bfbf4 100644
--- a/btrfs-convert.c
+++ b/btrfs-convert.c
@@ -1928,6 +1928,57 @@ static int convert_open_fs(const char *devname,
}
/*
+ * Helper for expand and merge extent_cache for wipe_one_reserved_range() to
+ * handle wiping a range that exists in cache.
+ */
+static int _expand_extent_cache(struct cache_tree *tree,
+ struct cache_extent *entry,
+ u64 min_stripe_size, int backward)
+{
+ struct cache_extent *ce;
+ int diff;
+
+ if (entry->size >= min_stripe_size)
+ return 0;
+ diff = min_stripe_size - entry->size;
+
+ if (backward) {
+ ce = prev_cache_extent(entry);
+ if (!ce)
+ goto expand_back;
+ if (ce->start + ce->size >= entry->start - diff) {
+ /* Directly merge with previous extent */
+ ce->size = entry->start + entry->size - ce->start;
+ remove_cache_extent(tree, entry);
+ free(entry);
+ return 0;
+ }
+expand_back:
+ /* No overlap, normal extent */
+ if (entry->start < diff) {
+ error("cannot find space for data chunk layout");
+ return -ENOSPC;
+ }
+ entry->start -= diff;
+ entry->size += diff;
+ return 0;
+ }
+ ce = next_cache_extent(entry);
+ if (!ce)
+ goto expand_after;
+ if (entry->start + entry->size + diff >= ce->start) {
+ /* Directly merge with next extent */
+ entry->size = ce->start + ce->size - entry->start;
+ remove_cache_extent(tree, ce);
+ free(ce);
+ return 0;
+ }
+expand_after:
+ entry->size += diff;
+ return 0;
+}
+
+/*
* Remove one reserve range from given cache tree
* if min_stripe_size is non-zero, it will ensure for split case,
* all its split cache extent is no smaller than @min_strip_size / 2.
@@ -1986,21 +2037,37 @@ static int wipe_one_reserved_range(struct cache_tree *tree,
* |-------cache-----|
* |-wipe-|
*/
+ u64 old_start = cache->start;
u64 old_len = cache->size;
u64 insert_start = start + len;
u64 insert_len;
cache->size = start - cache->start;
- if (ensure_size)
- cache->size = max(cache->size, min_stripe_size);
- cache->start = start - cache->size;
+ /* Expand the leading half part if needed */
+ if (ensure_size && cache->size < min_stripe_size) {
+ ret = _expand_extent_cache(tree, cache,
+ min_stripe_size, 1);
+ if (ret < 0)
+ return ret;
+ }
/* And insert the new one */
- insert_len = old_len - start - len;
- if (ensure_size)
- insert_len = max(insert_len, min_stripe_size);
-
+ insert_len = old_start + old_len - start - len;
ret = add_merge_cache_extent(tree, insert_start, insert_len);
+ if (ret < 0)
+ return ret;
+
+ /* Expand the last half part if needed */
+ if (ensure_size && insert_len < min_stripe_size) {
+ cache = lookup_cache_extent(tree, insert_start,
+ insert_len);
+ if (!cache || cache->start != insert_start ||
+ cache->size != insert_len)
+ return -ENOENT;
+ ret = _expand_extent_cache(tree, cache,
+ min_stripe_size, 0);
+ }
+
return ret;
}
/*