summaryrefslogtreecommitdiff
path: root/fs/bcachefs/fs.c
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2023-06-09 15:41:41 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2023-10-22 17:10:03 -0400
commitb0e8c75e40a863dd40ecdf8fd6f8cdceacb965e5 (patch)
tree337926d3fca8ddf4550eca6f026462ed2f606384 /fs/bcachefs/fs.c
parent5bc740820e7ae01b26a4dbb612df086f41f79785 (diff)
bcachefs: Fix subvol deletion deadlock
d_prune_aliases() may call bch2_evict_inode(), which needs c->vfs_inodes_list_lock. Fix this by always calling igrab() before putting the inodes onto our disposal list, and then calling d_prune_aliases() with c->vfs_inodes_lock dropped. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'fs/bcachefs/fs.c')
-rw-r--r--fs/bcachefs/fs.c26
1 files changed, 11 insertions, 15 deletions
diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c
index ba7aff6b8a51..f417889eba08 100644
--- a/fs/bcachefs/fs.c
+++ b/fs/bcachefs/fs.c
@@ -1481,22 +1481,14 @@ again:
continue;
if (!(inode->v.i_state & I_DONTCACHE) &&
- !(inode->v.i_state & I_FREEING)) {
+ !(inode->v.i_state & I_FREEING) &&
+ igrab(&inode->v)) {
this_pass_clean = false;
- d_mark_dontcache(&inode->v);
- d_prune_aliases(&inode->v);
-
- /*
- * If i_count was zero, we have to take and release a
- * ref in order for I_DONTCACHE to be noticed and the
- * inode to be dropped;
- */
-
- if (!atomic_read(&inode->v.i_count) &&
- igrab(&inode->v) &&
- darray_push_gfp(&grabbed, inode, GFP_ATOMIC|__GFP_NOWARN))
+ if (darray_push_gfp(&grabbed, inode, GFP_ATOMIC|__GFP_NOWARN)) {
+ iput(&inode->v);
break;
+ }
} else if (clean_pass && this_pass_clean) {
wait_queue_head_t *wq = bit_waitqueue(&inode->v.i_state, __I_NEW);
DEFINE_WAIT_BIT(wait, &inode->v.i_state, __I_NEW);
@@ -1511,8 +1503,12 @@ again:
}
mutex_unlock(&c->vfs_inodes_lock);
- darray_for_each(grabbed, i)
- iput(&(*i)->v);
+ darray_for_each(grabbed, i) {
+ inode = *i;
+ d_mark_dontcache(&inode->v);
+ d_prune_aliases(&inode->v);
+ iput(&inode->v);
+ }
grabbed.nr = 0;
if (!clean_pass || !this_pass_clean) {