]> git.dujemihanovic.xyz Git - linux.git/commitdiff
bcachefs: Add missing wakeup to bch2_inode_hash_remove()
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 4 Oct 2024 23:44:32 +0000 (19:44 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sat, 5 Oct 2024 00:25:31 +0000 (20:25 -0400)
This fixes two different bugs:

- Looser locking with the rhashtable means we need to recheck if the
  inode is still hashed after prepare_to_wait(), and add a corresponding
  wakeup after removing from the hash table.

da18ecbf0fb6 ("fs: add i_state helpers") changed the bit waitqueues
  used for inodes, and bcachefs wasn't updated and thus broke; this
  updates bcachefs to the new helper.

Fixes: 112d21fd1a12 ("bcachefs: switch to rhashtable for vfs inodes hash")
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/fs.c

index 4a1bb07a2574481e9bbffcd4d32c7dbf7ffb3929..5bfc26d5827017fec2fb8c5c35f5bd021455870c 100644 (file)
@@ -174,20 +174,24 @@ static const struct rhashtable_params bch2_vfs_inodes_params = {
        .automatic_shrinking    = true,
 };
 
-static void __wait_on_freeing_inode(struct inode *inode)
+struct bch_inode_info *__bch2_inode_hash_find(struct bch_fs *c, subvol_inum inum)
 {
-       wait_queue_head_t *wq;
-       DEFINE_WAIT_BIT(wait, &inode->i_state, __I_NEW);
-       wq = bit_waitqueue(&inode->i_state, __I_NEW);
-       prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE);
-       spin_unlock(&inode->i_lock);
-       schedule();
-       finish_wait(wq, &wait.wq_entry);
+       return rhashtable_lookup_fast(&c->vfs_inodes_table, &inum, bch2_vfs_inodes_params);
 }
 
-struct bch_inode_info *__bch2_inode_hash_find(struct bch_fs *c, subvol_inum inum)
+static void __wait_on_freeing_inode(struct bch_fs *c,
+                                   struct bch_inode_info *inode,
+                                   subvol_inum inum)
 {
-       return rhashtable_lookup_fast(&c->vfs_inodes_table, &inum, bch2_vfs_inodes_params);
+       wait_queue_head_t *wq;
+       DEFINE_WAIT_BIT(wait, &inode->v.i_state, __I_NEW);
+       wq = inode_bit_waitqueue(&wait, &inode->v, __I_NEW);
+       prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE);
+       spin_unlock(&inode->v.i_lock);
+
+       if (__bch2_inode_hash_find(c, inum) == inode)
+               schedule_timeout(HZ * 10);
+       finish_wait(wq, &wait.wq_entry);
 }
 
 static struct bch_inode_info *bch2_inode_hash_find(struct bch_fs *c, struct btree_trans *trans,
@@ -204,10 +208,10 @@ repeat:
                }
                if ((inode->v.i_state & (I_FREEING|I_WILL_FREE))) {
                        if (!trans) {
-                               __wait_on_freeing_inode(&inode->v);
+                               __wait_on_freeing_inode(c, inode, inum);
                        } else {
                                bch2_trans_unlock(trans);
-                               __wait_on_freeing_inode(&inode->v);
+                               __wait_on_freeing_inode(c, inode, inum);
                                int ret = bch2_trans_relock(trans);
                                if (ret)
                                        return ERR_PTR(ret);
@@ -232,6 +236,11 @@ static void bch2_inode_hash_remove(struct bch_fs *c, struct bch_inode_info *inod
                                        &inode->hash, bch2_vfs_inodes_params);
                BUG_ON(ret);
                inode->v.i_hash.pprev = NULL;
+               /*
+                * This pairs with the bch2_inode_hash_find() ->
+                * __wait_on_freeing_inode() path
+                */
+               inode_wake_up_bit(&inode->v, __I_NEW);
        }
 }