bcachefs: Ensure bch2_btree_node_lock_write_nofail() never fails
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 6 Mar 2023 13:58:02 +0000 (08:58 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:41 +0000 (17:09 -0400)
In order for bch2_btree_node_lock_write_nofail() to never produce a
deadlock, we must ensure we're never holding read locks when using it.
Fortunately, it's only used from code paths where any read locks may be
safely dropped.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/btree_locking.c
fs/bcachefs/btree_locking.h

index 11f83a9..6793d7d 100644 (file)
@@ -320,6 +320,40 @@ int __bch2_btree_node_lock_write(struct btree_trans *trans, struct btree_path *p
        return ret;
 }
 
+void bch2_btree_node_lock_write_nofail(struct btree_trans *trans,
+                                      struct btree_path *path,
+                                      struct btree_bkey_cached_common *b)
+{
+       struct btree_path *linked;
+       unsigned i;
+       int ret;
+
+       /*
+        * XXX BIG FAT NOTICE
+        *
+        * Drop all read locks before taking a write lock:
+        *
+        * This is a hack, because bch2_btree_node_lock_write_nofail() is a
+        * hack - but by dropping read locks first, this should never fail, and
+        * we only use this in code paths where whatever read locks we've
+        * already taken are no longer needed:
+        */
+
+       trans_for_each_path(trans, linked) {
+               if (!linked->nodes_locked)
+                       continue;
+
+               for (i = 0; i < BTREE_MAX_DEPTH; i++)
+                       if (btree_node_read_locked(linked, i)) {
+                               btree_node_unlock(trans, linked, i);
+                               btree_path_set_dirty(linked, BTREE_ITER_NEED_RELOCK);
+                       }
+       }
+
+       ret = __btree_node_lock_write(trans, path, b, true);
+       BUG_ON(ret);
+}
+
 /* relock */
 
 static inline bool btree_path_get_locks(struct btree_trans *trans,
index 6d8df25..9508969 100644 (file)
@@ -290,14 +290,6 @@ static inline int __btree_node_lock_write(struct btree_trans *trans,
                : __bch2_btree_node_lock_write(trans, path, b, lock_may_not_fail);
 }
 
-static inline void bch2_btree_node_lock_write_nofail(struct btree_trans *trans,
-                                             struct btree_path *path,
-                                             struct btree_bkey_cached_common *b)
-{
-       int ret = __btree_node_lock_write(trans, path, b, true);
-       BUG_ON(ret);
-}
-
 static inline int __must_check
 bch2_btree_node_lock_write(struct btree_trans *trans,
                           struct btree_path *path,
@@ -306,6 +298,10 @@ bch2_btree_node_lock_write(struct btree_trans *trans,
        return __btree_node_lock_write(trans, path, b, false);
 }
 
+void bch2_btree_node_lock_write_nofail(struct btree_trans *,
+                                      struct btree_path *,
+                                      struct btree_bkey_cached_common *);
+
 /* relock: */
 
 bool bch2_btree_path_relock_norestart(struct btree_trans *,