Skip to content

Commit c4accde

Browse files
author
Kent Overstreet
committed
bcachefs: Ensure srcu lock is not held too long
The SRCU read lock that btree_trans takes exists to make it safe for bch2_trans_relock() to deref pointers to btree nodes/key cache items we don't have locked, but as a side effect it blocks reclaim from freeing those items. Thus, it's important to not hold it for too long: we need to differentiate between bch2_trans_unlock() calls that will be only for a short duration, and ones that will be for an unbounded duration. This introduces bch2_trans_unlock_long(), to be used mainly by the data move paths. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
1 parent 6dfa10a commit c4accde

4 files changed

Lines changed: 40 additions & 13 deletions

File tree

fs/bcachefs/btree_iter.c

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,9 @@ int bch2_btree_path_traverse_one(struct btree_trans *trans,
11091109
if (unlikely(ret))
11101110
goto out;
11111111

1112+
if (unlikely(!trans->srcu_held))
1113+
bch2_trans_srcu_lock(trans);
1114+
11121115
/*
11131116
* Ensure we obey path->should_be_locked: if it's set, we can't unlock
11141117
* and re-traverse the path without a transaction restart:
@@ -2830,18 +2833,28 @@ void *__bch2_trans_kmalloc(struct btree_trans *trans, size_t size)
28302833
return p;
28312834
}
28322835

2833-
static noinline void bch2_trans_reset_srcu_lock(struct btree_trans *trans)
2836+
void bch2_trans_srcu_unlock(struct btree_trans *trans)
28342837
{
2835-
struct bch_fs *c = trans->c;
2836-
struct btree_path *path;
2838+
if (trans->srcu_held) {
2839+
struct bch_fs *c = trans->c;
2840+
struct btree_path *path;
28372841

2838-
trans_for_each_path(trans, path)
2839-
if (path->cached && !btree_node_locked(path, 0))
2840-
path->l[0].b = ERR_PTR(-BCH_ERR_no_btree_node_srcu_reset);
2842+
trans_for_each_path(trans, path)
2843+
if (path->cached && !btree_node_locked(path, 0))
2844+
path->l[0].b = ERR_PTR(-BCH_ERR_no_btree_node_srcu_reset);
28412845

2842-
srcu_read_unlock(&c->btree_trans_barrier, trans->srcu_idx);
2843-
trans->srcu_idx = srcu_read_lock(&c->btree_trans_barrier);
2844-
trans->srcu_lock_time = jiffies;
2846+
srcu_read_unlock(&c->btree_trans_barrier, trans->srcu_idx);
2847+
trans->srcu_held = false;
2848+
}
2849+
}
2850+
2851+
void bch2_trans_srcu_lock(struct btree_trans *trans)
2852+
{
2853+
if (!trans->srcu_held) {
2854+
trans->srcu_idx = srcu_read_lock(&trans->c->btree_trans_barrier);
2855+
trans->srcu_lock_time = jiffies;
2856+
trans->srcu_held = true;
2857+
}
28452858
}
28462859

28472860
/**
@@ -2895,8 +2908,9 @@ u32 bch2_trans_begin(struct btree_trans *trans)
28952908
}
28962909
trans->last_begin_time = now;
28972910

2898-
if (unlikely(time_after(jiffies, trans->srcu_lock_time + msecs_to_jiffies(10))))
2899-
bch2_trans_reset_srcu_lock(trans);
2911+
if (unlikely(trans->srcu_held &&
2912+
time_after(jiffies, trans->srcu_lock_time + msecs_to_jiffies(10))))
2913+
bch2_trans_srcu_unlock(trans);
29002914

29012915
trans->last_begin_ip = _RET_IP_;
29022916
if (trans->restarted) {
@@ -2981,8 +2995,9 @@ struct btree_trans *__bch2_trans_get(struct bch_fs *c, unsigned fn_idx)
29812995
trans->wb_updates_size = s->wb_updates_size;
29822996
}
29832997

2984-
trans->srcu_idx = srcu_read_lock(&c->btree_trans_barrier);
2998+
trans->srcu_idx = srcu_read_lock(&c->btree_trans_barrier);
29852999
trans->srcu_lock_time = jiffies;
3000+
trans->srcu_held = true;
29863001

29873002
if (IS_ENABLED(CONFIG_BCACHEFS_DEBUG_TRANSACTIONS)) {
29883003
struct btree_trans *pos;
@@ -3059,7 +3074,8 @@ void bch2_trans_put(struct btree_trans *trans)
30593074

30603075
check_btree_paths_leaked(trans);
30613076

3062-
srcu_read_unlock(&c->btree_trans_barrier, trans->srcu_idx);
3077+
if (trans->srcu_held)
3078+
srcu_read_unlock(&c->btree_trans_barrier, trans->srcu_idx);
30633079

30643080
bch2_journal_preres_put(&c->journal, &trans->journal_preres);
30653081

fs/bcachefs/btree_iter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ void bch2_path_put(struct btree_trans *, struct btree_path *, bool);
274274
int bch2_trans_relock(struct btree_trans *);
275275
int bch2_trans_relock_notrace(struct btree_trans *);
276276
void bch2_trans_unlock(struct btree_trans *);
277+
void bch2_trans_unlock_long(struct btree_trans *);
277278
bool bch2_trans_locked(struct btree_trans *);
278279

279280
static inline int trans_was_restarted(struct btree_trans *trans, u32 restart_count)
@@ -579,6 +580,9 @@ static inline int __bch2_bkey_get_val_typed(struct btree_trans *trans,
579580
__bch2_bkey_get_val_typed(_trans, _btree_id, _pos, _flags, \
580581
KEY_TYPE_##_type, sizeof(*_val), _val)
581582

583+
void bch2_trans_srcu_unlock(struct btree_trans *);
584+
void bch2_trans_srcu_lock(struct btree_trans *);
585+
582586
u32 bch2_trans_begin(struct btree_trans *);
583587

584588
/*

fs/bcachefs/btree_locking.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,12 @@ void bch2_trans_unlock(struct btree_trans *trans)
753753
__bch2_btree_path_unlock(trans, path);
754754
}
755755

756+
void bch2_trans_unlock_long(struct btree_trans *trans)
757+
{
758+
bch2_trans_unlock(trans);
759+
bch2_trans_srcu_unlock(trans);
760+
}
761+
756762
bool bch2_trans_locked(struct btree_trans *trans)
757763
{
758764
struct btree_path *path;

fs/bcachefs/btree_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ struct btree_trans {
426426
u8 nr_updates;
427427
u8 nr_wb_updates;
428428
u8 wb_updates_size;
429+
bool srcu_held:1;
429430
bool used_mempool:1;
430431
bool in_traverse_all:1;
431432
bool paths_sorted:1;

0 commit comments

Comments
 (0)