bcachefs: Fix lock ordering with new btree cache code

The code that checks lock ordering was recently changed to go off of the
pos of the btree node, rather than the iterator, but the btree cache
code didn't update to handle iterators that point to cached bkeys. Oops

Also, update various debug code.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2020-06-15 19:53:46 -04:00 committed by Kent Overstreet
parent 1d18678962
commit d211b408ab
4 changed files with 111 additions and 23 deletions

View file

@ -185,6 +185,14 @@ static inline bool btree_iter_get_locks(struct btree_iter *iter,
return iter->uptodate < BTREE_ITER_NEED_RELOCK;
}
static struct bpos btree_node_pos(struct btree_bkey_cached_common *_b,
enum btree_iter_type type)
{
return type != BTREE_ITER_CACHED
? container_of(_b, struct btree, c)->key.k.p
: container_of(_b, struct bkey_cached, c)->key.pos;
}
/* Slowpath: */
bool __bch2_btree_node_lock(struct btree *b, struct bpos pos,
unsigned level, struct btree_iter *iter,
@ -253,7 +261,8 @@ bool __bch2_btree_node_lock(struct btree *b, struct bpos pos,
if (iter->btree_id == linked->btree_id &&
btree_node_locked(linked, level) &&
bkey_cmp(pos, linked->l[level].b->key.k.p) <= 0)
bkey_cmp(pos, btree_node_pos((void *) linked->l[level].b,
btree_iter_type(linked))) <= 0)
ret = false;
/*
@ -435,6 +444,22 @@ void bch2_trans_unlock(struct btree_trans *trans)
#ifdef CONFIG_BCACHEFS_DEBUG
static void bch2_btree_iter_verify_cached(struct btree_iter *iter)
{
struct bkey_cached *ck;
bool locked = btree_node_locked(iter, 0);
if (!bch2_btree_node_relock(iter, 0))
return;
ck = (void *) iter->l[0].b;
BUG_ON(ck->key.btree_id != iter->btree_id ||
bkey_cmp(ck->key.pos, iter->pos));
if (!locked)
btree_node_unlock(iter, 0);
}
static void bch2_btree_iter_verify_level(struct btree_iter *iter,
unsigned level)
{
@ -449,6 +474,12 @@ static void bch2_btree_iter_verify_level(struct btree_iter *iter,
if (!debug_check_iterators(iter->trans->c))
return;
if (btree_iter_type(iter) == BTREE_ITER_CACHED) {
if (!level)
bch2_btree_iter_verify_cached(iter);
return;
}
BUG_ON(iter->level < iter->min_depth);
if (!btree_iter_node(iter, level))
@ -1257,13 +1288,14 @@ int __must_check __bch2_btree_iter_traverse(struct btree_iter *iter)
return ret;
}
static inline void bch2_btree_iter_checks(struct btree_iter *iter,
enum btree_iter_type type)
static inline void bch2_btree_iter_checks(struct btree_iter *iter)
{
EBUG_ON(iter->btree_id >= BTREE_ID_NR);
EBUG_ON(btree_iter_type(iter) != type);
enum btree_iter_type type = btree_iter_type(iter);
BUG_ON(type == BTREE_ITER_KEYS &&
EBUG_ON(iter->btree_id >= BTREE_ID_NR);
BUG_ON((type == BTREE_ITER_KEYS ||
type == BTREE_ITER_CACHED) &&
(bkey_cmp(iter->pos, bkey_start_pos(&iter->k)) < 0 ||
bkey_cmp(iter->pos, iter->k.p) > 0));
@ -1278,7 +1310,8 @@ struct btree *bch2_btree_iter_peek_node(struct btree_iter *iter)
struct btree *b;
int ret;
bch2_btree_iter_checks(iter, BTREE_ITER_NODES);
EBUG_ON(btree_iter_type(iter) != BTREE_ITER_NODES);
bch2_btree_iter_checks(iter);
if (iter->uptodate == BTREE_ITER_UPTODATE)
return iter->l[iter->level].b;
@ -1306,7 +1339,8 @@ struct btree *bch2_btree_iter_next_node(struct btree_iter *iter)
struct btree *b;
int ret;
bch2_btree_iter_checks(iter, BTREE_ITER_NODES);
EBUG_ON(btree_iter_type(iter) != BTREE_ITER_NODES);
bch2_btree_iter_checks(iter);
/* already got to end? */
if (!btree_iter_node(iter, iter->level))
@ -1534,7 +1568,8 @@ struct bkey_s_c bch2_btree_iter_peek(struct btree_iter *iter)
struct bkey_s_c k;
int ret;
bch2_btree_iter_checks(iter, BTREE_ITER_KEYS);
EBUG_ON(btree_iter_type(iter) != BTREE_ITER_KEYS);
bch2_btree_iter_checks(iter);
if (iter->uptodate == BTREE_ITER_UPTODATE &&
!bkey_deleted(&iter->k))
@ -1621,7 +1656,8 @@ struct bkey_s_c bch2_btree_iter_peek_with_updates(struct btree_iter *iter)
struct bkey_s_c k;
int ret;
bch2_btree_iter_checks(iter, BTREE_ITER_KEYS);
EBUG_ON(btree_iter_type(iter) != BTREE_ITER_KEYS);
bch2_btree_iter_checks(iter);
while (1) {
ret = bch2_btree_iter_traverse(iter);
@ -1681,7 +1717,8 @@ struct bkey_s_c bch2_btree_iter_peek_prev(struct btree_iter *iter)
struct bkey_s_c k;
int ret;
bch2_btree_iter_checks(iter, BTREE_ITER_KEYS);
EBUG_ON(btree_iter_type(iter) != BTREE_ITER_KEYS);
bch2_btree_iter_checks(iter);
if (iter->uptodate == BTREE_ITER_UPTODATE &&
!bkey_deleted(&iter->k))
@ -1717,7 +1754,8 @@ struct bkey_s_c bch2_btree_iter_prev(struct btree_iter *iter)
{
struct bpos pos = bkey_start_pos(&iter->k);
bch2_btree_iter_checks(iter, BTREE_ITER_KEYS);
EBUG_ON(btree_iter_type(iter) != BTREE_ITER_KEYS);
bch2_btree_iter_checks(iter);
if (unlikely(!bkey_cmp(pos, POS_MIN)))
return bkey_s_c_null;
@ -1798,7 +1836,8 @@ struct bkey_s_c bch2_btree_iter_peek_slot(struct btree_iter *iter)
struct bkey_s_c k;
int ret;
bch2_btree_iter_checks(iter, BTREE_ITER_KEYS);
EBUG_ON(btree_iter_type(iter) != BTREE_ITER_KEYS);
bch2_btree_iter_checks(iter);
if (iter->uptodate == BTREE_ITER_UPTODATE)
return btree_iter_peek_uptodate(iter);
@ -1844,7 +1883,8 @@ struct bkey_s_c bch2_btree_iter_peek_cached(struct btree_iter *iter)
struct bkey_cached *ck;
int ret;
bch2_btree_iter_checks(iter, BTREE_ITER_CACHED);
EBUG_ON(btree_iter_type(iter) != BTREE_ITER_CACHED);
bch2_btree_iter_checks(iter);
ret = bch2_btree_iter_traverse(iter);
if (unlikely(ret))
@ -2324,6 +2364,15 @@ int bch2_trans_exit(struct btree_trans *trans)
return trans->error ? -EIO : 0;
}
static void bch2_btree_iter_node_to_text(struct printbuf *out,
struct btree_bkey_cached_common *_b,
enum btree_iter_type type)
{
pr_buf(out, " %px l=%u %s:",
_b, _b->level, bch2_btree_ids[_b->btree_id]);
bch2_bpos_to_text(out, btree_node_pos(_b, type));
}
void bch2_btree_trans_to_text(struct printbuf *out, struct bch_fs *c)
{
#ifdef CONFIG_BCACHEFS_DEBUG
@ -2348,11 +2397,11 @@ void bch2_btree_trans_to_text(struct printbuf *out, struct bch_fs *c)
for (l = 0; l < BTREE_MAX_DEPTH; l++) {
if (btree_node_locked(iter, l)) {
b = iter->l[l].b;
pr_buf(out, " %px %s l=%u ",
b, btree_node_intent_locked(iter, l) ? "i" : "r", l);
bch2_bpos_to_text(out, b->key.k.p);
pr_buf(out, " %s l=%u ",
btree_node_intent_locked(iter, l) ? "i" : "r", l);
bch2_btree_iter_node_to_text(out,
(void *) iter->l[l].b,
btree_iter_type(iter));
pr_buf(out, "\n");
}
}
@ -2366,10 +2415,11 @@ void bch2_btree_trans_to_text(struct printbuf *out, struct bch_fs *c)
bch2_btree_ids[trans->locking_btree_id]);
bch2_bpos_to_text(out, trans->locking_pos);
pr_buf(out, " node %px l=%u %s:",
b, b->c.level,
bch2_btree_ids[b->c.btree_id]);
bch2_bpos_to_text(out, b->key.k.p);
pr_buf(out, " node ");
bch2_btree_iter_node_to_text(out,
(void *) b,
btree_iter_type(&trans->iters[trans->locking_iter_idx]));
pr_buf(out, "\n");
}
}

View file

@ -1,5 +1,6 @@
#include "bcachefs.h"
#include "btree_cache.h"
#include "btree_iter.h"
#include "btree_key_cache.h"
#include "btree_locking.h"
@ -492,3 +493,27 @@ int bch2_fs_btree_key_cache_init(struct btree_key_cache *c)
{
return rhashtable_init(&c->table, &bch2_btree_key_cache_params);
}
void bch2_btree_key_cache_to_text(struct printbuf *out, struct btree_key_cache *c)
{
struct bucket_table *tbl;
struct bkey_cached *ck;
struct rhash_head *pos;
size_t i;
mutex_lock(&c->lock);
tbl = rht_dereference_rcu(c->table.tbl, &c->table);
for (i = 0; i < tbl->size; i++) {
rht_for_each_entry_rcu(ck, pos, tbl, i, hash) {
pr_buf(out, "%s:",
bch2_btree_ids[ck->key.btree_id]);
bch2_bpos_to_text(out, ck->key.pos);
if (test_bit(BKEY_CACHED_DIRTY, &ck->flags))
pr_buf(out, " journal seq %llu", ck->journal.seq);
pr_buf(out, "\n");
}
}
mutex_unlock(&c->lock);
}

View file

@ -20,4 +20,6 @@ void bch2_fs_btree_key_cache_exit(struct btree_key_cache *);
void bch2_fs_btree_key_cache_init_early(struct btree_key_cache *);
int bch2_fs_btree_key_cache_init(struct btree_key_cache *);
void bch2_btree_key_cache_to_text(struct printbuf *, struct btree_key_cache *);
#endif /* _BCACHEFS_BTREE_KEY_CACHE_H */

View file

@ -14,6 +14,7 @@
#include "btree_cache.h"
#include "btree_io.h"
#include "btree_iter.h"
#include "btree_key_cache.h"
#include "btree_update.h"
#include "btree_update_interior.h"
#include "btree_gc.h"
@ -165,6 +166,7 @@ read_attribute(journal_debug);
read_attribute(journal_pins);
read_attribute(btree_updates);
read_attribute(dirty_btree_nodes);
read_attribute(btree_key_cache);
read_attribute(btree_transactions);
read_attribute(internal_uuid);
@ -401,6 +403,14 @@ SHOW(bch2_fs)
if (attr == &sysfs_dirty_btree_nodes)
return bch2_dirty_btree_nodes_print(c, buf);
if (attr == &sysfs_btree_key_cache) {
struct printbuf out = _PBUF(buf, PAGE_SIZE);
bch2_btree_key_cache_to_text(&out, &c->btree_key_cache);
return out.pos - buf;
}
if (attr == &sysfs_btree_transactions) {
struct printbuf out = _PBUF(buf, PAGE_SIZE);
@ -571,6 +581,7 @@ struct attribute *bch2_fs_internal_files[] = {
&sysfs_journal_pins,
&sysfs_btree_updates,
&sysfs_dirty_btree_nodes,
&sysfs_btree_key_cache,
&sysfs_btree_transactions,
&sysfs_read_realloc_races,