From 0243e4d41c980d2cf8771836f9fa2b43b69d1e29 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Fri, 26 Jun 2020 14:51:58 -0400 Subject: [PATCH] Update bcachefs sources to b1708f0191 bcachefs: Fix a null ptr deref in bch2_btree_iter_traverse_one() --- .bcachefs_revision | 2 +- libbcachefs/alloc_background.c | 11 +++- libbcachefs/bcachefs.h | 2 +- libbcachefs/bcachefs_ioctl.h | 4 ++ libbcachefs/bset.c | 41 ++++++------ libbcachefs/bset.h | 4 +- libbcachefs/btree_cache.c | 2 +- libbcachefs/btree_gc.c | 23 +++++-- libbcachefs/btree_io.c | 2 +- libbcachefs/btree_iter.c | 98 ++++++++++++++++++++++------- libbcachefs/btree_key_cache.c | 25 ++++++++ libbcachefs/btree_key_cache.h | 2 + libbcachefs/btree_update_interior.c | 6 +- libbcachefs/buckets.c | 38 ++++++----- libbcachefs/buckets_types.h | 4 +- libbcachefs/chardev.c | 9 ++- libbcachefs/debug.c | 6 +- libbcachefs/sysfs.c | 13 ++++ 18 files changed, 207 insertions(+), 85 deletions(-) diff --git a/.bcachefs_revision b/.bcachefs_revision index 7354361c..fea7a44d 100644 --- a/.bcachefs_revision +++ b/.bcachefs_revision @@ -1 +1 @@ -8a316f4112c5aa4b3c2c4745fa83475dbec8a959 +b1708f0191fcad1b7afa47dd6a7c6b1104c4639d diff --git a/libbcachefs/alloc_background.c b/libbcachefs/alloc_background.c index 0d64ba63..cb720ee0 100644 --- a/libbcachefs/alloc_background.c +++ b/libbcachefs/alloc_background.c @@ -501,6 +501,7 @@ static void bch2_bucket_clock_init(struct bch_fs *c, int rw) static int wait_buckets_available(struct bch_fs *c, struct bch_dev *ca) { unsigned long gc_count = c->gc_count; + u64 available; int ret = 0; ca->allocator_state = ALLOCATOR_BLOCKED; @@ -516,9 +517,11 @@ static int wait_buckets_available(struct bch_fs *c, struct bch_dev *ca) if (gc_count != c->gc_count) ca->inc_gen_really_needs_gc = 0; - if ((ssize_t) (dev_buckets_available(c, ca) - - ca->inc_gen_really_needs_gc) >= - (ssize_t) fifo_free(&ca->free_inc)) + available = max_t(s64, 0, dev_buckets_available(c, ca) - + ca->inc_gen_really_needs_gc); + + if (available > fifo_free(&ca->free_inc) || + (available && !fifo_full(&ca->free[RESERVE_BTREE]))) break; up_read(&c->gc_lock); @@ -1101,6 +1104,8 @@ static int bch2_allocator_thread(void *arg) while (1) { cond_resched(); + if (kthread_should_stop()) + break; pr_debug("discarding %zu invalidated buckets", fifo_used(&ca->free_inc)); diff --git a/libbcachefs/bcachefs.h b/libbcachefs/bcachefs.h index 5333b4bb..893c89db 100644 --- a/libbcachefs/bcachefs.h +++ b/libbcachefs/bcachefs.h @@ -339,7 +339,7 @@ enum bch_time_stats { #define BTREE_RESERVE_MAX (BTREE_MAX_DEPTH + (BTREE_MAX_DEPTH - 1)) /* Size of the freelist we allocate btree nodes from: */ -#define BTREE_NODE_RESERVE BTREE_RESERVE_MAX +#define BTREE_NODE_RESERVE (BTREE_RESERVE_MAX * 4) #define BTREE_NODE_OPEN_BUCKET_RESERVE (BTREE_RESERVE_MAX * BCH_REPLICAS_MAX) diff --git a/libbcachefs/bcachefs_ioctl.h b/libbcachefs/bcachefs_ioctl.h index ba8c7570..d71157a3 100644 --- a/libbcachefs/bcachefs_ioctl.h +++ b/libbcachefs/bcachefs_ioctl.h @@ -275,9 +275,13 @@ struct bch_ioctl_dev_usage { __u32 bucket_size; __u64 nr_buckets; + __u64 available_buckets; __u64 buckets[BCH_DATA_NR]; __u64 sectors[BCH_DATA_NR]; + + __u64 ec_buckets; + __u64 ec_sectors; }; /* diff --git a/libbcachefs/bset.c b/libbcachefs/bset.c index 6360b2e8..6fc91e6a 100644 --- a/libbcachefs/bset.c +++ b/libbcachefs/bset.c @@ -64,21 +64,27 @@ struct bset_tree *bch2_bkey_to_bset(struct btree *b, struct bkey_packed *k) * by the time we actually do the insert will all be deleted. */ -void bch2_dump_bset(struct btree *b, struct bset *i, unsigned set) +void bch2_dump_bset(struct bch_fs *c, struct btree *b, + struct bset *i, unsigned set) { struct bkey_packed *_k, *_n; - struct bkey k, n; - char buf[120]; + struct bkey uk, n; + struct bkey_s_c k; + char buf[200]; if (!i->u64s) return; - for (_k = i->start, k = bkey_unpack_key(b, _k); + for (_k = i->start; _k < vstruct_last(i); - _k = _n, k = n) { + _k = _n) { _n = bkey_next_skip_noops(_k, vstruct_last(i)); - bch2_bkey_to_text(&PBUF(buf), &k); + k = bkey_disassemble(b, _k, &uk); + if (c) + bch2_bkey_val_to_text(&PBUF(buf), c, k); + else + bch2_bkey_to_text(&PBUF(buf), k.k); printk(KERN_ERR "block %u key %5zu: %s\n", set, _k->_data - i->_data, buf); @@ -87,31 +93,24 @@ void bch2_dump_bset(struct btree *b, struct bset *i, unsigned set) n = bkey_unpack_key(b, _n); - if (bkey_cmp(bkey_start_pos(&n), k.p) < 0) { + if (bkey_cmp(bkey_start_pos(&n), k.k->p) < 0) { printk(KERN_ERR "Key skipped backwards\n"); continue; } - /* - * Weird check for duplicate non extent keys: extents are - * deleted iff they have 0 size, so if it has zero size and it's - * not deleted these aren't extents: - */ - if (((!k.size && !bkey_deleted(&k)) || - (!n.size && !bkey_deleted(&n))) && - !bkey_deleted(&k) && - !bkey_cmp(n.p, k.p)) + if (!bkey_deleted(k.k) && + !bkey_cmp(n.p, k.k->p)) printk(KERN_ERR "Duplicate keys\n"); } } -void bch2_dump_btree_node(struct btree *b) +void bch2_dump_btree_node(struct bch_fs *c, struct btree *b) { struct bset_tree *t; console_lock(); for_each_bset(b, t) - bch2_dump_bset(b, bset(b, t), t - b->set); + bch2_dump_bset(c, b, bset(b, t), t - b->set); console_unlock(); } @@ -170,7 +169,7 @@ static void bch2_btree_node_iter_next_check(struct btree_node_iter *_iter, struct bkey nu = bkey_unpack_key(b, n); char buf1[80], buf2[80]; - bch2_dump_btree_node(b); + bch2_dump_btree_node(NULL, b); bch2_bkey_to_text(&PBUF(buf1), &ku); bch2_bkey_to_text(&PBUF(buf2), &nu); printk(KERN_ERR "out of order/overlapping:\n%s\n%s\n", @@ -248,7 +247,7 @@ void bch2_verify_insert_pos(struct btree *b, struct bkey_packed *where, char buf1[100]; char buf2[100]; - bch2_dump_btree_node(b); + bch2_dump_btree_node(NULL, b); bch2_bkey_to_text(&PBUF(buf1), &k1); bch2_bkey_to_text(&PBUF(buf2), &k2); @@ -269,7 +268,7 @@ void bch2_verify_insert_pos(struct btree *b, struct bkey_packed *where, char buf1[100]; char buf2[100]; - bch2_dump_btree_node(b); + bch2_dump_btree_node(NULL, b); bch2_bkey_to_text(&PBUF(buf1), &k1); bch2_bkey_to_text(&PBUF(buf2), &k2); diff --git a/libbcachefs/bset.h b/libbcachefs/bset.h index 7338ccbc..652ffed4 100644 --- a/libbcachefs/bset.h +++ b/libbcachefs/bset.h @@ -600,8 +600,8 @@ void bch2_bfloat_to_text(struct printbuf *, struct btree *, /* Debug stuff */ -void bch2_dump_bset(struct btree *, struct bset *, unsigned); -void bch2_dump_btree_node(struct btree *); +void bch2_dump_bset(struct bch_fs *, struct btree *, struct bset *, unsigned); +void bch2_dump_btree_node(struct bch_fs *, struct btree *); void bch2_dump_btree_node_iter(struct btree *, struct btree_node_iter *); #ifdef CONFIG_BCACHEFS_DEBUG diff --git a/libbcachefs/btree_cache.c b/libbcachefs/btree_cache.c index b6a716cd..d3addd3a 100644 --- a/libbcachefs/btree_cache.c +++ b/libbcachefs/btree_cache.c @@ -309,7 +309,7 @@ restart: if (freed >= nr) goto out; - if (sc->gfp_mask & __GFP_IO) + if (sc->gfp_mask & __GFP_FS) mutex_lock(&bc->lock); else if (!mutex_trylock(&bc->lock)) goto out; diff --git a/libbcachefs/btree_gc.c b/libbcachefs/btree_gc.c index e8abc193..8771ef1f 100644 --- a/libbcachefs/btree_gc.c +++ b/libbcachefs/btree_gc.c @@ -902,6 +902,7 @@ static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id id) struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k); const struct bch_extent_ptr *ptr; + percpu_down_read(&c->mark_lock); bkey_for_each_ptr(ptrs, ptr) { struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev); struct bucket *g = PTR_BUCKET(ca, ptr, false); @@ -914,6 +915,7 @@ static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id id) } } + percpu_up_read(&c->mark_lock); } bch2_trans_exit(&trans); @@ -923,17 +925,25 @@ static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id id) int bch2_gc_gens(struct bch_fs *c) { struct bch_dev *ca; + struct bucket_array *buckets; + struct bucket *g; unsigned i; int ret; - down_read(&c->state_lock); + /* + * Ideally we would be using state_lock and not gc_lock here, but that + * introduces a deadlock in the RO path - we currently take the state + * lock at the start of going RO, thus the gc thread may get stuck: + */ + down_read(&c->gc_lock); for_each_member_device(ca, c, i) { - struct bucket_array *buckets = bucket_array(ca); - struct bucket *g; + down_read(&ca->bucket_lock); + buckets = bucket_array(ca); for_each_bucket(g, buckets) g->gc_gen = g->mark.gen; + up_read(&ca->bucket_lock); } for (i = 0; i < BTREE_ID_NR; i++) @@ -944,14 +954,15 @@ int bch2_gc_gens(struct bch_fs *c) } for_each_member_device(ca, c, i) { - struct bucket_array *buckets = bucket_array(ca); - struct bucket *g; + down_read(&ca->bucket_lock); + buckets = bucket_array(ca); for_each_bucket(g, buckets) g->oldest_gen = g->gc_gen; + up_read(&ca->bucket_lock); } err: - up_read(&c->state_lock); + up_read(&c->gc_lock); return ret; } diff --git a/libbcachefs/btree_io.c b/libbcachefs/btree_io.c index 5fc9137b..bb3aecce 100644 --- a/libbcachefs/btree_io.c +++ b/libbcachefs/btree_io.c @@ -897,7 +897,7 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b, bch2_bkey_to_text(&PBUF(buf1), &up); bch2_bkey_to_text(&PBUF(buf2), u.k); - bch2_dump_bset(b, i, 0); + bch2_dump_bset(c, b, i, 0); btree_err(BTREE_ERR_FATAL, c, b, i, "keys out of order: %s > %s", buf1, buf2); diff --git a/libbcachefs/btree_iter.c b/libbcachefs/btree_iter.c index e620088d..6fab76c3 100644 --- a/libbcachefs/btree_iter.c +++ b/libbcachefs/btree_iter.c @@ -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)) @@ -1204,7 +1235,7 @@ static int btree_iter_traverse_one(struct btree_iter *iter) * * XXX correctly using BTREE_ITER_UPTODATE should make this unnecessary */ - if (btree_iter_node(iter, iter->level)) { + if (is_btree_node(iter, iter->level)) { BUG_ON(!btree_iter_pos_in_node(iter, iter->l[iter->level].b)); btree_iter_advance_to_pos(iter, &iter->l[iter->level], -1); @@ -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)) @@ -2323,6 +2363,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 @@ -2347,11 +2396,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"); } } @@ -2365,10 +2414,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"); } } diff --git a/libbcachefs/btree_key_cache.c b/libbcachefs/btree_key_cache.c index 1e533514..d73cc8dd 100644 --- a/libbcachefs/btree_key_cache.c +++ b/libbcachefs/btree_key_cache.c @@ -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); +} diff --git a/libbcachefs/btree_key_cache.h b/libbcachefs/btree_key_cache.h index fbc29336..b1756c6c 100644 --- a/libbcachefs/btree_key_cache.h +++ b/libbcachefs/btree_key_cache.h @@ -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 */ diff --git a/libbcachefs/btree_update_interior.c b/libbcachefs/btree_update_interior.c index 9e6006d0..a8cd6ffb 100644 --- a/libbcachefs/btree_update_interior.c +++ b/libbcachefs/btree_update_interior.c @@ -1398,14 +1398,14 @@ int bch2_btree_split_leaf(struct bch_fs *c, struct btree_iter *iter, struct btree_update *as; struct closure cl; int ret = 0; - struct btree_iter *linked; + struct btree_insert_entry *i; /* * We already have a disk reservation and open buckets pinned; this * allocation must not block: */ - trans_for_each_iter(trans, linked) - if (linked->btree_id == BTREE_ID_EXTENTS) + trans_for_each_update(trans, i) + if (btree_node_type_needs_gc(i->iter->btree_id)) flags |= BTREE_INSERT_USE_RESERVE; closure_init_stack(&cl); diff --git a/libbcachefs/buckets.c b/libbcachefs/buckets.c index 085e0af3..4c937199 100644 --- a/libbcachefs/buckets.c +++ b/libbcachefs/buckets.c @@ -374,6 +374,11 @@ static inline int is_fragmented_bucket(struct bucket_mark m, return 0; } +static inline int bucket_stripe_sectors(struct bucket_mark m) +{ + return m.stripe ? m.dirty_sectors : 0; +} + static inline enum bch_data_type bucket_type(struct bucket_mark m) { return m.cached_sectors && !m.dirty_sectors @@ -441,33 +446,35 @@ static void bch2_dev_usage_update(struct bch_fs *c, struct bch_dev *ca, struct bucket_mark old, struct bucket_mark new, bool gc) { - struct bch_dev_usage *dev_usage; + struct bch_dev_usage *u; percpu_rwsem_assert_held(&c->mark_lock); preempt_disable(); - dev_usage = this_cpu_ptr(ca->usage[gc]); + u = this_cpu_ptr(ca->usage[gc]); if (bucket_type(old)) - account_bucket(fs_usage, dev_usage, bucket_type(old), + account_bucket(fs_usage, u, bucket_type(old), -1, -ca->mi.bucket_size); if (bucket_type(new)) - account_bucket(fs_usage, dev_usage, bucket_type(new), + account_bucket(fs_usage, u, bucket_type(new), 1, ca->mi.bucket_size); - dev_usage->buckets_alloc += + u->buckets_alloc += (int) new.owned_by_allocator - (int) old.owned_by_allocator; - dev_usage->buckets_ec += - (int) new.stripe - (int) old.stripe; - dev_usage->buckets_unavailable += + u->buckets_unavailable += is_unavailable_bucket(new) - is_unavailable_bucket(old); - dev_usage->sectors[old.data_type] -= old.dirty_sectors; - dev_usage->sectors[new.data_type] += new.dirty_sectors; - dev_usage->sectors[BCH_DATA_CACHED] += + u->buckets_ec += (int) new.stripe - (int) old.stripe; + u->sectors_ec += bucket_stripe_sectors(new) - + bucket_stripe_sectors(old); + + u->sectors[old.data_type] -= old.dirty_sectors; + u->sectors[new.data_type] += new.dirty_sectors; + u->sectors[BCH_DATA_CACHED] += (int) new.cached_sectors - (int) old.cached_sectors; - dev_usage->sectors_fragmented += + u->sectors_fragmented += is_fragmented_bucket(new, ca) - is_fragmented_bucket(old, ca); preempt_enable(); @@ -1993,8 +2000,6 @@ int bch2_dev_buckets_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets) int ret = -ENOMEM; unsigned i; - lockdep_assert_held(&c->state_lock); - memset(&free, 0, sizeof(free)); memset(&free_inc, 0, sizeof(free_inc)); memset(&alloc_heap, 0, sizeof(alloc_heap)); @@ -2021,6 +2026,7 @@ int bch2_dev_buckets_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets) bch2_copygc_stop(ca); if (resize) { + down_write(&c->gc_lock); down_write(&ca->bucket_lock); percpu_down_write(&c->mark_lock); } @@ -2043,8 +2049,10 @@ int bch2_dev_buckets_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets) swap(ca->buckets_nouse, buckets_nouse); - if (resize) + if (resize) { percpu_up_write(&c->mark_lock); + up_write(&c->gc_lock); + } spin_lock(&c->freelist_lock); for (i = 0; i < RESERVE_NR; i++) { diff --git a/libbcachefs/buckets_types.h b/libbcachefs/buckets_types.h index 59e92a6d..53f22726 100644 --- a/libbcachefs/buckets_types.h +++ b/libbcachefs/buckets_types.h @@ -53,12 +53,14 @@ struct bucket_array { struct bch_dev_usage { u64 buckets[BCH_DATA_NR]; u64 buckets_alloc; - u64 buckets_ec; u64 buckets_unavailable; /* _compressed_ sectors: */ u64 sectors[BCH_DATA_NR]; u64 sectors_fragmented; + + u64 buckets_ec; + u64 sectors_ec; }; struct bch_fs_usage { diff --git a/libbcachefs/chardev.c b/libbcachefs/chardev.c index 5028d0dc..3af52194 100644 --- a/libbcachefs/chardev.c +++ b/libbcachefs/chardev.c @@ -470,9 +470,12 @@ static long bch2_ioctl_dev_usage(struct bch_fs *c, src = bch2_dev_usage_read(c, ca); - arg.state = ca->mi.state; - arg.bucket_size = ca->mi.bucket_size; - arg.nr_buckets = ca->mi.nbuckets - ca->mi.first_bucket; + arg.state = ca->mi.state; + arg.bucket_size = ca->mi.bucket_size; + arg.nr_buckets = ca->mi.nbuckets - ca->mi.first_bucket; + arg.available_buckets = arg.nr_buckets - src.buckets_unavailable; + arg.ec_buckets = src.buckets_ec; + arg.ec_sectors = src.sectors_ec; for (i = 0; i < BCH_DATA_NR; i++) { arg.buckets[i] = src.buckets[i]; diff --git a/libbcachefs/debug.c b/libbcachefs/debug.c index 4e0d14e3..aa10591a 100644 --- a/libbcachefs/debug.c +++ b/libbcachefs/debug.c @@ -97,10 +97,10 @@ void __bch2_btree_verify(struct bch_fs *c, struct btree *b) console_lock(); printk(KERN_ERR "*** in memory:\n"); - bch2_dump_bset(b, inmemory, 0); + bch2_dump_bset(c, b, inmemory, 0); printk(KERN_ERR "*** read back in:\n"); - bch2_dump_bset(v, sorted, 0); + bch2_dump_bset(c, v, sorted, 0); while (offset < b->written) { if (!offset ) { @@ -117,7 +117,7 @@ void __bch2_btree_verify(struct bch_fs *c, struct btree *b) } printk(KERN_ERR "*** on disk block %u:\n", offset); - bch2_dump_bset(b, i, offset); + bch2_dump_bset(c, b, i, offset); offset += sectors; } diff --git a/libbcachefs/sysfs.c b/libbcachefs/sysfs.c index 15c5dc1d..c169d282 100644 --- a/libbcachefs/sysfs.c +++ b/libbcachefs/sysfs.c @@ -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, @@ -835,6 +846,7 @@ static ssize_t show_dev_alloc_debug(struct bch_dev *ca, char *buf) " meta: %llu\n" " user: %llu\n" " cached: %llu\n" + " erasure coded: %llu\n" " fragmented: %llu\n" " copygc threshold: %llu\n" "freelist_wait: %s\n" @@ -861,6 +873,7 @@ static ssize_t show_dev_alloc_debug(struct bch_dev *ca, char *buf) stats.sectors[BCH_DATA_BTREE], stats.sectors[BCH_DATA_USER], stats.sectors[BCH_DATA_CACHED], + stats.sectors_ec, stats.sectors_fragmented, ca->copygc_threshold, c->freelist_wait.list.first ? "waiting" : "empty",