diff --git a/btrfs-convert.c b/btrfs-convert.c index 856fac60..f78bfbf4 100644 --- a/btrfs-convert.c +++ b/btrfs-convert.c @@ -1927,6 +1927,57 @@ static int convert_open_fs(const char *devname, return -1; } +/* + * 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, @@ -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; } /*