| From: Josef Bacik <josef@toxicpanda.com> |
| Date: Tue, 19 Nov 2019 13:59:35 -0500 |
| Subject: btrfs: do not call synchronize_srcu() in inode_tree_del |
| |
| commit f72ff01df9cf5db25c76674cac16605992d15467 upstream. |
| |
| Testing with the new fsstress uncovered a pretty nasty deadlock with |
| lookup and snapshot deletion. |
| |
| Process A |
| unlink |
| -> final iput |
| -> inode_tree_del |
| -> synchronize_srcu(subvol_srcu) |
| |
| Process B |
| btrfs_lookup <- srcu_read_lock() acquired here |
| -> btrfs_iget |
| -> find inode that has I_FREEING set |
| -> __wait_on_freeing_inode() |
| |
| We're holding the srcu_read_lock() while doing the iget in order to make |
| sure our fs root doesn't go away, and then we are waiting for the inode |
| to finish freeing. However because the free'ing process is doing a |
| synchronize_srcu() we deadlock. |
| |
| Fix this by dropping the synchronize_srcu() in inode_tree_del(). We |
| don't need people to stop accessing the fs root at this point, we're |
| only adding our empty root to the dead roots list. |
| |
| A larger much more invasive fix is forthcoming to address how we deal |
| with fs roots, but this fixes the immediate problem. |
| |
| Fixes: 76dda93c6ae2 ("Btrfs: add snapshot/subvolume destroy ioctl") |
| Signed-off-by: Josef Bacik <josef@toxicpanda.com> |
| Reviewed-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| [bwh: Backported to 3.16: No fs_info variable was used here] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| --- a/fs/btrfs/inode.c |
| +++ b/fs/btrfs/inode.c |
| @@ -5140,7 +5140,6 @@ static void inode_tree_del(struct inode |
| spin_unlock(&root->inode_lock); |
| |
| if (empty && btrfs_root_refs(&root->root_item) == 0) { |
| - synchronize_srcu(&root->fs_info->subvol_srcu); |
| spin_lock(&root->inode_lock); |
| empty = RB_EMPTY_ROOT(&root->inode_tree); |
| spin_unlock(&root->inode_lock); |