summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2023-12-21 18:54:09 -0500
committerKent Overstreet <kent.overstreet@linux.dev>2023-12-22 00:01:17 -0500
commitaaa55da315fcbb0387271d473303f52b5f7f3d45 (patch)
tree13845914075d81cd6813270182e8e90c676675c2
parent22c4372b161cb9361b0ddc87785f85ae29fa15fa (diff)
bcachefs: Add lockdep support for btree node locks
This adds lockdep tracking for held btree locks with a single dep_map in btree_trans, i.e. tracking all held btree locks as one object. This is more practical and more useful than having lockdep track held btree locks individually, because - we can take more locks than lockdep can track (unbounded, now that we have dynamically resizable btree paths) - there's no lock ordering between btree locks for lockdep to track (we do cycle detection) - and this makes it easy to teach lockdep that btree locks are not safe to hold while invoking memory reclaim. The last rule is one that lockdep would never learn, because we only do trylock() from within shrinkers - but we very much do not want to be invoking memory reclaim while holding btree node locks. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r--fs/bcachefs/btree_iter.c21
-rw-r--r--fs/bcachefs/btree_locking.c14
-rw-r--r--fs/bcachefs/btree_locking.h9
-rw-r--r--fs/bcachefs/btree_types.h6
4 files changed, 45 insertions, 5 deletions
diff --git a/fs/bcachefs/btree_iter.c b/fs/bcachefs/btree_iter.c
index 47d203cdb7b2..f1f4efc94589 100644
--- a/fs/bcachefs/btree_iter.c
+++ b/fs/bcachefs/btree_iter.c
@@ -2976,6 +2976,9 @@ got_trans:
trans->paths_allocated[0] = 1;
+ static struct lock_class_key lockdep_key;
+ lockdep_init_map(&trans->dep_map, "bcachefs_btree", &lockdep_key, 0);
+
if (fn_idx < BCH_TRANSACTIONS_NR) {
trans->fn = bch2_btree_transaction_fns[fn_idx];
@@ -3236,7 +3239,19 @@ int bch2_fs_btree_iter_init(struct bch_fs *c)
mempool_init_kmalloc_pool(&c->btree_trans_mem_pool, 1,
BTREE_TRANS_MEM_MAX) ?:
init_srcu_struct(&c->btree_trans_barrier);
- if (!ret)
- c->btree_trans_barrier_initialized = true;
- return ret;
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_LOCKDEP
+ fs_reclaim_acquire(GFP_KERNEL);
+ struct btree_trans *trans = bch2_trans_get(c);
+ lock_acquire_exclusive(&trans->dep_map, 0, 0, NULL, _THIS_IP_);
+ trans->locks_held = true;
+ bch2_trans_put(trans);
+ fs_reclaim_release(GFP_KERNEL);
+
+#endif
+ c->btree_trans_barrier_initialized = true;
+ return 0;
+
}
diff --git a/fs/bcachefs/btree_locking.c b/fs/bcachefs/btree_locking.c
index 1ed8327a9fa2..a5ae421073d0 100644
--- a/fs/bcachefs/btree_locking.c
+++ b/fs/bcachefs/btree_locking.c
@@ -786,6 +786,13 @@ void bch2_trans_unlock_noassert(struct btree_trans *trans)
trans_for_each_path(trans, path, i)
__bch2_btree_path_unlock(trans, path);
+
+#ifdef CONFIG_LOCKDEP
+ if (trans->locks_held) {
+ lock_release(&trans->dep_map, _THIS_IP_);
+ trans->locks_held = false;
+ }
+#endif
}
void bch2_trans_unlock(struct btree_trans *trans)
@@ -795,6 +802,13 @@ void bch2_trans_unlock(struct btree_trans *trans)
trans_for_each_path(trans, path, i)
__bch2_btree_path_unlock(trans, path);
+
+#ifdef CONFIG_LOCKDEP
+ if (trans->locks_held) {
+ lock_release(&trans->dep_map, _THIS_IP_);
+ trans->locks_held = false;
+ }
+#endif
}
void bch2_trans_unlock_long(struct btree_trans *trans)
diff --git a/fs/bcachefs/btree_locking.h b/fs/bcachefs/btree_locking.h
index 64810ea544c9..962fa8f5cf0b 100644
--- a/fs/bcachefs/btree_locking.h
+++ b/fs/bcachefs/btree_locking.h
@@ -202,13 +202,18 @@ static inline int __btree_node_lock_nopath(struct btree_trans *trans,
bool lock_may_not_fail,
unsigned long ip)
{
- int ret;
+#ifdef CONFIG_LOCKDEP
+ if (!trans->locks_held) {
+ lock_acquire_exclusive(&trans->dep_map, 0, 0, NULL, ip);
+ trans->locks_held = true;
+ }
+#endif
trans->lock_may_not_fail = lock_may_not_fail;
trans->lock_must_abort = false;
trans->locking = b;
- ret = six_lock_ip_waiter(&b->lock, type, &trans->locking_wait,
+ int ret = six_lock_ip_waiter(&b->lock, type, &trans->locking_wait,
bch2_six_check_for_deadlock, trans, ip);
WRITE_ONCE(trans->locking, NULL);
WRITE_ONCE(trans->locking_wait.start_time, 0);
diff --git a/fs/bcachefs/btree_types.h b/fs/bcachefs/btree_types.h
index e4ebfc25df8e..b2d428bbcf73 100644
--- a/fs/bcachefs/btree_types.h
+++ b/fs/bcachefs/btree_types.h
@@ -397,6 +397,9 @@ struct btree_trans {
u8 lock_must_abort;
bool lock_may_not_fail:1;
bool srcu_held:1;
+#ifdef CONFIG_LOCKDEP
+ bool locks_held:1;
+#endif
bool used_mempool:1;
bool in_traverse_all:1;
bool paths_sorted:1;
@@ -433,6 +436,9 @@ struct btree_trans {
unsigned extra_disk_res; /* XXX kill */
struct replicas_delta_list *fs_usage_deltas;
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ struct lockdep_map dep_map;
+#endif
/* Entries before this are zeroed out on every bch2_trans_get() call */
struct list_head list;