diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2023-12-21 18:54:09 -0500 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-12-22 00:01:17 -0500 |
commit | aaa55da315fcbb0387271d473303f52b5f7f3d45 (patch) | |
tree | 13845914075d81cd6813270182e8e90c676675c2 | |
parent | 22c4372b161cb9361b0ddc87785f85ae29fa15fa (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.c | 21 | ||||
-rw-r--r-- | fs/bcachefs/btree_locking.c | 14 | ||||
-rw-r--r-- | fs/bcachefs/btree_locking.h | 9 | ||||
-rw-r--r-- | fs/bcachefs/btree_types.h | 6 |
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; |