aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/free-space-cache.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 01c26e8ae555..f1d9b6bc23ba 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -306,11 +306,45 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
306 306
307 ret = link_free_space(block_group, info); 307 ret = link_free_space(block_group, info);
308 BUG_ON(ret); 308 BUG_ON(ret);
309 } else if (info && info->offset < offset &&
310 info->offset + info->bytes >= offset + bytes) {
311 u64 old_start = info->offset;
312 /*
313 * we're freeing space in the middle of the info,
314 * this can happen during tree log replay
315 *
316 * first unlink the old info and then
317 * insert it again after the hole we're creating
318 */
319 unlink_free_space(block_group, info);
320 if (offset + bytes < info->offset + info->bytes) {
321 u64 old_end = info->offset + info->bytes;
322
323 info->offset = offset + bytes;
324 info->bytes = old_end - info->offset;
325 ret = link_free_space(block_group, info);
326 BUG_ON(ret);
327 } else {
328 /* the hole we're creating ends at the end
329 * of the info struct, just free the info
330 */
331 kfree(info);
332 }
333
334 /* step two, insert a new info struct to cover anything
335 * before the hole
336 */
337 spin_unlock(&block_group->lock);
338 ret = btrfs_add_free_space(block_group, old_start,
339 offset - old_start);
340 BUG_ON(ret);
341 goto out_nolock;
309 } else { 342 } else {
310 WARN_ON(1); 343 WARN_ON(1);
311 } 344 }
312out: 345out:
313 spin_unlock(&block_group->lock); 346 spin_unlock(&block_group->lock);
347out_nolock:
314 return ret; 348 return ret;
315} 349}
316 350