Skip to content

Commit

Permalink
Btrfs: prevent list corruption during free space cache processing
Browse files Browse the repository at this point in the history
__btrfs_write_out_cache is holding the ctl->tree_lock while it prepares
a list of bitmaps to record in the free space cache.  It was dropping
the lock while it worked on other components, which made a window for
free_bitmap() to free the bitmap struct without removing it from the
list.

This changes things to hold the lock the whole time, and also makes sure
we hold the lock during enospc cleanup.

Reported-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Chris Mason <clm@fb.com>
  • Loading branch information
masoncl committed Apr 24, 2015
1 parent 85db36c commit a3bdccc
Showing 1 changed file with 18 additions and 14 deletions.
32 changes: 18 additions & 14 deletions fs/btrfs/free-space-cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -1119,10 +1119,7 @@ static int flush_dirty_cache(struct inode *inode)
}

static void noinline_for_stack
cleanup_write_cache_enospc(struct inode *inode,
struct btrfs_io_ctl *io_ctl,
struct extent_state **cached_state,
struct list_head *bitmap_list)
cleanup_bitmap_list(struct list_head *bitmap_list)
{
struct list_head *pos, *n;

Expand All @@ -1131,6 +1128,14 @@ cleanup_write_cache_enospc(struct inode *inode,
list_entry(pos, struct btrfs_free_space, list);
list_del_init(&entry->list);
}
}

static void noinline_for_stack
cleanup_write_cache_enospc(struct inode *inode,
struct btrfs_io_ctl *io_ctl,
struct extent_state **cached_state,
struct list_head *bitmap_list)
{
io_ctl_drop_pages(io_ctl);
unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
i_size_read(inode) - 1, cached_state,
Expand Down Expand Up @@ -1266,11 +1271,8 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
ret = write_cache_extent_entries(io_ctl, ctl,
block_group, &entries, &bitmaps,
&bitmap_list);
spin_unlock(&ctl->tree_lock);
if (ret) {
mutex_unlock(&ctl->cache_writeout_mutex);
goto out_nospc;
}
if (ret)
goto out_nospc_locked;

/*
* Some spaces that are freed in the current transaction are pinned,
Expand All @@ -1281,17 +1283,14 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
* the dirty list and redo it. No locking needed
*/
ret = write_pinned_extent_entries(root, block_group, io_ctl, &entries);
if (ret) {
mutex_unlock(&ctl->cache_writeout_mutex);
goto out_nospc;
}
if (ret)
goto out_nospc_locked;

/*
* At last, we write out all the bitmaps and keep cache_writeout_mutex
* locked while doing it because a concurrent trim can be manipulating
* or freeing the bitmap.
*/
spin_lock(&ctl->tree_lock);
ret = write_bitmap_entries(io_ctl, &bitmap_list);
spin_unlock(&ctl->tree_lock);
mutex_unlock(&ctl->cache_writeout_mutex);
Expand Down Expand Up @@ -1344,6 +1343,11 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
iput(inode);
return ret;

out_nospc_locked:
cleanup_bitmap_list(&bitmap_list);
spin_unlock(&ctl->tree_lock);
mutex_unlock(&ctl->cache_writeout_mutex);

out_nospc:
cleanup_write_cache_enospc(inode, io_ctl, &cached_state, &bitmap_list);

Expand Down

0 comments on commit a3bdccc

Please sign in to comment.