diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2023-06-09 22:41:41 +0300 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-23 00:10:03 +0300 |
commit | b0e8c75e40a863dd40ecdf8fd6f8cdceacb965e5 (patch) | |
tree | 337926d3fca8ddf4550eca6f026462ed2f606384 /fs | |
parent | 5bc740820e7ae01b26a4dbb612df086f41f79785 (diff) | |
download | linux-b0e8c75e40a863dd40ecdf8fd6f8cdceacb965e5.tar.xz |
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')
-rw-r--r-- | fs/bcachefs/fs.c | 26 |
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) { |