bcachefs: Fix subvol deletion deadlock
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 9 Jun 2023 19:41:41 +0000 (15:41 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:10:03 +0000 (17:10 -0400)
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>
fs/bcachefs/fs.c

index ba7aff6b8a51b6d20410eba7f6027f6c42c7bb93..f417889eba08b38884bc2f7c0b97bce094bd0a35 100644 (file)
@@ -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) {