Skip to content

Commit de611ab

Browse files
author
Kent Overstreet
committed
bcachefs: Fix race between trans_put() and btree_transactions_read()
debug.c was using closure_get() on a different thread's closure where the we don't know if the object being refcounted is alive. We keep btree_trans objects on a list so they can be printed by debug code, and because it is cost prohibitive to touch the btree_trans list every time we allocate and free btree_trans objects, cached objects are also on this list. However, we do not want the debug code to see cached but not in use btree_trans objects - critically because the btree_paths array will have been freed (if it was reallocated). closure_get() is also incorrect to use when that get may race with it hitting zero, i.e. we must already have a ref on the object or know the ref can't currently hit 0 for other reasons (as used in the cycle detector). to fix this, use the previously introduced closure_get_not_zero(), closure_return_sync(), and closure_init_stack_release(); the debug code now can only take a ref on a trans object if it's alive and in use. Signed-off-by: Kent Overstreet <[email protected]>
1 parent 06efa5f commit de611ab

File tree

2 files changed

+13
-16
lines changed

2 files changed

+13
-16
lines changed

fs/bcachefs/btree_iter.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3130,7 +3130,6 @@ struct btree_trans *__bch2_trans_get(struct bch_fs *c, unsigned fn_idx)
31303130

31313131
trans = mempool_alloc(&c->btree_trans_pool, GFP_NOFS);
31323132
memset(trans, 0, sizeof(*trans));
3133-
closure_init_stack(&trans->ref);
31343133

31353134
seqmutex_lock(&c->btree_trans_lock);
31363135
if (IS_ENABLED(CONFIG_BCACHEFS_DEBUG)) {
@@ -3161,7 +3160,6 @@ struct btree_trans *__bch2_trans_get(struct bch_fs *c, unsigned fn_idx)
31613160
list_add_done:
31623161
seqmutex_unlock(&c->btree_trans_lock);
31633162
got_trans:
3164-
trans->ref.closure_get_happened = false;
31653163
trans->c = c;
31663164
trans->last_begin_time = local_clock();
31673165
trans->fn_idx = fn_idx;
@@ -3200,6 +3198,8 @@ struct btree_trans *__bch2_trans_get(struct bch_fs *c, unsigned fn_idx)
32003198
trans->srcu_idx = srcu_read_lock(&c->btree_trans_barrier);
32013199
trans->srcu_lock_time = jiffies;
32023200
trans->srcu_held = true;
3201+
3202+
closure_init_stack_release(&trans->ref);
32033203
return trans;
32043204
}
32053205

@@ -3257,10 +3257,10 @@ void bch2_trans_put(struct btree_trans *trans)
32573257
bch2_journal_keys_put(c);
32583258

32593259
/*
3260-
* trans->ref protects trans->locking_wait.task, btree_paths arary; used
3260+
* trans->ref protects trans->locking_wait.task, btree_paths array; used
32613261
* by cycle detector
32623262
*/
3263-
closure_sync(&trans->ref);
3263+
closure_return_sync(&trans->ref);
32643264
trans->locking_wait.task = NULL;
32653265

32663266
unsigned long *paths_allocated = trans->paths_allocated;
@@ -3385,8 +3385,6 @@ void bch2_fs_btree_iter_exit(struct bch_fs *c)
33853385
per_cpu_ptr(c->btree_trans_bufs, cpu)->trans;
33863386

33873387
if (trans) {
3388-
closure_sync(&trans->ref);
3389-
33903388
seqmutex_lock(&c->btree_trans_lock);
33913389
list_del(&trans->list);
33923390
seqmutex_unlock(&c->btree_trans_lock);

fs/bcachefs/debug.c

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -587,14 +587,10 @@ static ssize_t bch2_btree_transactions_read(struct file *file, char __user *buf,
587587
if (!task || task->pid <= i->iter)
588588
continue;
589589

590-
closure_get(&trans->ref);
591-
u32 seq = seqmutex_unlock(&c->btree_trans_lock);
590+
if (!closure_get_not_zero(&trans->ref))
591+
continue;
592592

593-
ret = flush_buf(i);
594-
if (ret) {
595-
closure_put(&trans->ref);
596-
goto unlocked;
597-
}
593+
u32 seq = seqmutex_unlock(&c->btree_trans_lock);
598594

599595
bch2_btree_trans_to_text(&i->buf, trans);
600596

@@ -604,10 +600,12 @@ static ssize_t bch2_btree_transactions_read(struct file *file, char __user *buf,
604600
printbuf_indent_sub(&i->buf, 2);
605601
prt_newline(&i->buf);
606602

607-
i->iter = task->pid;
608-
609603
closure_put(&trans->ref);
610604

605+
ret = flush_buf(i);
606+
if (ret)
607+
goto unlocked;
608+
611609
if (!seqmutex_relock(&c->btree_trans_lock, seq))
612610
goto restart;
613611
}
@@ -817,7 +815,8 @@ static void btree_deadlock_to_text(struct printbuf *out, struct bch_fs *c)
817815

818816
iter = task->pid;
819817

820-
closure_get(&trans->ref);
818+
if (!closure_get_not_zero(&trans->ref))
819+
continue;
821820

822821
u32 seq = seqmutex_unlock(&c->btree_trans_lock);
823822

0 commit comments

Comments
 (0)