Commit f154c3eb authored by Kent Overstreet's avatar Kent Overstreet

bcachefs: trans_for_each_path_safe()

bch2_btree_trans_to_text() is used on btree_trans objects that are owned
by different threads - when printing out deadlock cycles - so we need a
safe version of trans_for_each_path(), else we race with seeing a
btree_path that was just allocated and not fully initialized:
Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent e7ffda56
...@@ -2914,6 +2914,10 @@ static void bch2_trans_alloc_paths(struct btree_trans *trans, struct bch_fs *c) ...@@ -2914,6 +2914,10 @@ static void bch2_trans_alloc_paths(struct btree_trans *trans, struct bch_fs *c)
#endif #endif
if (!p) if (!p)
p = mempool_alloc(&trans->c->btree_paths_pool, GFP_NOFS); p = mempool_alloc(&trans->c->btree_paths_pool, GFP_NOFS);
/*
* paths need to be zeroed, bch2_check_for_deadlock looks at paths in
* other threads
*/
trans->paths = p; p += paths_bytes; trans->paths = p; p += paths_bytes;
trans->updates = p; p += updates_bytes; trans->updates = p; p += updates_bytes;
...@@ -3111,7 +3115,7 @@ void bch2_btree_trans_to_text(struct printbuf *out, struct btree_trans *trans) ...@@ -3111,7 +3115,7 @@ void bch2_btree_trans_to_text(struct printbuf *out, struct btree_trans *trans)
struct btree_path *path; struct btree_path *path;
struct btree_bkey_cached_common *b; struct btree_bkey_cached_common *b;
static char lock_types[] = { 'r', 'i', 'w' }; static char lock_types[] = { 'r', 'i', 'w' };
unsigned l; unsigned l, idx;
if (!out->nr_tabstops) { if (!out->nr_tabstops) {
printbuf_tabstop_push(out, 16); printbuf_tabstop_push(out, 16);
...@@ -3120,7 +3124,7 @@ void bch2_btree_trans_to_text(struct printbuf *out, struct btree_trans *trans) ...@@ -3120,7 +3124,7 @@ void bch2_btree_trans_to_text(struct printbuf *out, struct btree_trans *trans)
prt_printf(out, "%i %s\n", trans->locking_wait.task->pid, trans->fn); prt_printf(out, "%i %s\n", trans->locking_wait.task->pid, trans->fn);
trans_for_each_path(trans, path) { trans_for_each_path_safe(trans, path, idx) {
if (!path->nodes_locked) if (!path->nodes_locked)
continue; continue;
......
...@@ -89,6 +89,35 @@ __trans_next_path(struct btree_trans *trans, unsigned idx) ...@@ -89,6 +89,35 @@ __trans_next_path(struct btree_trans *trans, unsigned idx)
#define trans_for_each_path(_trans, _path) \ #define trans_for_each_path(_trans, _path) \
trans_for_each_path_from(_trans, _path, 0) trans_for_each_path_from(_trans, _path, 0)
static inline struct btree_path *
__trans_next_path_safe(struct btree_trans *trans, unsigned *idx)
{
u64 l;
if (*idx == BTREE_ITER_MAX)
return NULL;
l = trans->paths_allocated >> *idx;
if (!l)
return NULL;
*idx += __ffs64(l);
EBUG_ON(*idx >= BTREE_ITER_MAX);
return &trans->paths[*idx];
}
/*
* This version is intended to be safe for use on a btree_trans that is owned by
* another thread, for bch2_btree_trans_to_text();
*/
#define trans_for_each_path_safe_from(_trans, _path, _idx, _start) \
for (_idx = _start; \
(_path = __trans_next_path_safe((_trans), &_idx)); \
_idx++)
#define trans_for_each_path_safe(_trans, _path, _idx) \
trans_for_each_path_safe_from(_trans, _path, _idx, 0)
static inline struct btree_path *next_btree_path(struct btree_trans *trans, struct btree_path *path) static inline struct btree_path *next_btree_path(struct btree_trans *trans, struct btree_path *path)
{ {
unsigned idx = path ? path->sorted_idx + 1 : 0; unsigned idx = path ? path->sorted_idx + 1 : 0;
......
...@@ -255,6 +255,7 @@ int bch2_check_for_deadlock(struct btree_trans *trans, struct printbuf *cycle) ...@@ -255,6 +255,7 @@ int bch2_check_for_deadlock(struct btree_trans *trans, struct printbuf *cycle)
struct trans_waiting_for_lock *top; struct trans_waiting_for_lock *top;
struct btree_bkey_cached_common *b; struct btree_bkey_cached_common *b;
struct btree_path *path; struct btree_path *path;
unsigned path_idx;
int ret; int ret;
if (trans->lock_must_abort) { if (trans->lock_must_abort) {
...@@ -273,12 +274,12 @@ int bch2_check_for_deadlock(struct btree_trans *trans, struct printbuf *cycle) ...@@ -273,12 +274,12 @@ int bch2_check_for_deadlock(struct btree_trans *trans, struct printbuf *cycle)
top = &g.g[g.nr - 1]; top = &g.g[g.nr - 1];
trans_for_each_path_from(top->trans, path, top->path_idx) { trans_for_each_path_safe_from(top->trans, path, path_idx, top->path_idx) {
if (!path->nodes_locked) if (!path->nodes_locked)
continue; continue;
if (top->path_idx != path->idx) { if (path_idx != top->path_idx) {
top->path_idx = path->idx; top->path_idx = path_idx;
top->level = 0; top->level = 0;
top->lock_start_time = 0; top->lock_start_time = 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment