From b422ff58ba8eedcfef3b67b66468660f07b0cfc1 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 12 Apr 2021 11:48:36 -0400 Subject: [PATCH] Update bcachefs sources to a8b3ce7599 fixup! bcachefs: Eliminate more PAGE_SIZE uses --- .bcachefs_revision | 2 +- include/linux/string.h | 1 + include/trace/events/bcachefs.h | 5 + libbcachefs/alloc_background.c | 20 +- libbcachefs/bset.h | 2 +- libbcachefs/btree_cache.c | 41 +- libbcachefs/btree_gc.c | 4 +- libbcachefs/btree_io.c | 71 ++- libbcachefs/btree_io.h | 4 +- libbcachefs/btree_iter.c | 8 +- libbcachefs/btree_key_cache.c | 14 +- libbcachefs/btree_types.h | 4 + libbcachefs/btree_update_interior.c | 11 +- libbcachefs/btree_update_interior.h | 4 +- libbcachefs/btree_update_leaf.c | 2 + libbcachefs/buckets.c | 2 +- libbcachefs/debug.c | 4 +- libbcachefs/dirent.c | 18 +- libbcachefs/fsck.c | 945 +++++++++++----------------- libbcachefs/fsck.h | 1 - libbcachefs/recovery.c | 40 +- libbcachefs/super-io.c | 40 +- libbcachefs/super.c | 3 +- libbcachefs/super_types.h | 2 +- libbcachefs/util.c | 2 +- 25 files changed, 567 insertions(+), 683 deletions(-) diff --git a/.bcachefs_revision b/.bcachefs_revision index 7b12d288..f3004007 100644 --- a/.bcachefs_revision +++ b/.bcachefs_revision @@ -1 +1 @@ -6a3927a96b2f362deccc7ee36e20e03f193a9e00 +a8b3ce75990057fc2e3a1c64310668e1ac9ed0f5 diff --git a/include/linux/string.h b/include/linux/string.h index 4806e2c5..b5e00a09 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -11,5 +11,6 @@ extern void memzero_explicit(void *, size_t); int match_string(const char * const *, size_t, const char *); #define kstrndup(s, n, gfp) strndup(s, n) +#define kstrdup(s, gfp) strdup(s) #endif /* _LINUX_STRING_H_ */ diff --git a/include/trace/events/bcachefs.h b/include/trace/events/bcachefs.h index d4cb7a29..5ab05693 100644 --- a/include/trace/events/bcachefs.h +++ b/include/trace/events/bcachefs.h @@ -716,6 +716,11 @@ DEFINE_EVENT(transaction_restart, trans_restart_iter_upgrade, TP_ARGS(ip) ); +DEFINE_EVENT(transaction_restart, trans_restart_relock, + TP_PROTO(unsigned long ip), + TP_ARGS(ip) +); + DEFINE_EVENT(transaction_restart, trans_restart_traverse, TP_PROTO(unsigned long ip), TP_ARGS(ip) diff --git a/libbcachefs/alloc_background.c b/libbcachefs/alloc_background.c index 48971fcf..b2821e63 100644 --- a/libbcachefs/alloc_background.c +++ b/libbcachefs/alloc_background.c @@ -1104,7 +1104,8 @@ static int bch2_allocator_thread(void *arg) pr_debug("free_inc now empty"); - do { + while (1) { + cond_resched(); /* * Find some buckets that we can invalidate, either * they're completely unused, or only contain clean data @@ -1127,22 +1128,21 @@ static int bch2_allocator_thread(void *arg) wake_up_process(c->gc_thread); } + if (nr) + break; + /* * If we found any buckets, we have to invalidate them * before we scan for more - but if we didn't find very * many we may want to wait on more buckets being * available so we don't spin: */ - if (!nr || - (nr < ALLOC_SCAN_BATCH(ca) && - !fifo_empty(&ca->free[RESERVE_NONE]))) { - ret = wait_buckets_available(c, ca); - if (ret) { - up_read(&c->gc_lock); - goto stop; - } + ret = wait_buckets_available(c, ca); + if (ret) { + up_read(&c->gc_lock); + goto stop; } - } while (!nr); + } up_read(&c->gc_lock); diff --git a/libbcachefs/bset.h b/libbcachefs/bset.h index 506da4e0..e42f866c 100644 --- a/libbcachefs/bset.h +++ b/libbcachefs/bset.h @@ -188,7 +188,7 @@ static inline enum bset_aux_tree_type bset_aux_tree_type(const struct bset_tree * gets to the second cacheline. */ -#define BSET_CACHELINE 128 +#define BSET_CACHELINE 256 static inline size_t btree_keys_cachelines(const struct btree *b) { diff --git a/libbcachefs/btree_cache.c b/libbcachefs/btree_cache.c index 1abc50f1..9f963179 100644 --- a/libbcachefs/btree_cache.c +++ b/libbcachefs/btree_cache.c @@ -214,7 +214,7 @@ static int __btree_node_reclaim(struct bch_fs *c, struct btree *b, bool flush) if (bch2_verify_btree_ondisk) bch2_btree_node_write(c, b, SIX_LOCK_intent); else - __bch2_btree_node_write(c, b, SIX_LOCK_read); + __bch2_btree_node_write(c, b); /* wait for any in flight btree write */ btree_node_wait_on_io(b); @@ -666,13 +666,9 @@ static noinline struct btree *bch2_btree_node_fill(struct bch_fs *c, return NULL; } - /* - * Unlock before doing IO: - * - * XXX: ideally should be dropping all btree node locks here - */ - if (iter && btree_node_read_locked(iter, level + 1)) - btree_node_unlock(iter, level + 1); + /* Unlock before doing IO: */ + if (iter && sync) + bch2_trans_unlock(iter->trans); bch2_btree_node_read(c, b, sync); @@ -683,6 +679,16 @@ static noinline struct btree *bch2_btree_node_fill(struct bch_fs *c, return NULL; } + /* + * XXX: this will probably always fail because btree_iter_relock() + * currently fails for iterators that aren't pointed at a valid btree + * node + */ + if (iter && !bch2_trans_relock(iter->trans)) { + six_unlock_intent(&b->c.lock); + return ERR_PTR(-EINTR); + } + if (lock_type == SIX_LOCK_read) six_lock_downgrade(&b->c.lock); @@ -789,9 +795,22 @@ lock_node: } } - /* XXX: waiting on IO with btree locks held: */ - wait_on_bit_io(&b->flags, BTREE_NODE_read_in_flight, - TASK_UNINTERRUPTIBLE); + if (unlikely(btree_node_read_in_flight(b))) { + six_unlock_type(&b->c.lock, lock_type); + bch2_trans_unlock(iter->trans); + + wait_on_bit_io(&b->flags, BTREE_NODE_read_in_flight, + TASK_UNINTERRUPTIBLE); + + /* + * XXX: check if this always fails - btree_iter_relock() + * currently fails for iterators that aren't pointed at a valid + * btree node + */ + if (iter && !bch2_trans_relock(iter->trans)) + return ERR_PTR(-EINTR); + goto retry; + } prefetch(b->aux_data); diff --git a/libbcachefs/btree_gc.c b/libbcachefs/btree_gc.c index 268e0072..751920a5 100644 --- a/libbcachefs/btree_gc.c +++ b/libbcachefs/btree_gc.c @@ -1158,8 +1158,6 @@ static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id btree_id) bch2_bkey_buf_reassemble(&sk, c, k); bch2_extent_normalize(c, bkey_i_to_s(sk.k)); - bch2_btree_iter_set_pos(iter, bkey_start_pos(&sk.k->k)); - bch2_trans_update(&trans, iter, sk.k, 0); ret = bch2_trans_commit(&trans, NULL, NULL, @@ -1206,7 +1204,7 @@ int bch2_gc_gens(struct bch_fs *c) } for (i = 0; i < BTREE_ID_NR; i++) - if (btree_node_type_needs_gc(i)) { + if ((1 << i) & BTREE_ID_HAS_PTRS) { ret = bch2_gc_btree_gens(c, i); if (ret) { bch_err(c, "error recalculating oldest_gen: %i", ret); diff --git a/libbcachefs/btree_io.c b/libbcachefs/btree_io.c index ec1290fa..4a2f5726 100644 --- a/libbcachefs/btree_io.c +++ b/libbcachefs/btree_io.c @@ -241,7 +241,6 @@ bool bch2_compact_whiteouts(struct bch_fs *c, struct btree *b, } static void btree_node_sort(struct bch_fs *c, struct btree *b, - struct btree_iter *iter, unsigned start_idx, unsigned end_idx, bool filter_whiteouts) @@ -377,8 +376,7 @@ void bch2_btree_sort_into(struct bch_fs *c, * We're about to add another bset to the btree node, so if there's currently * too many bsets - sort some of them together: */ -static bool btree_node_compact(struct bch_fs *c, struct btree *b, - struct btree_iter *iter) +static bool btree_node_compact(struct bch_fs *c, struct btree *b) { unsigned unwritten_idx; bool ret = false; @@ -390,13 +388,13 @@ static bool btree_node_compact(struct bch_fs *c, struct btree *b, break; if (b->nsets - unwritten_idx > 1) { - btree_node_sort(c, b, iter, unwritten_idx, + btree_node_sort(c, b, unwritten_idx, b->nsets, false); ret = true; } if (unwritten_idx > 1) { - btree_node_sort(c, b, iter, 0, unwritten_idx, false); + btree_node_sort(c, b, 0, unwritten_idx, false); ret = true; } @@ -426,12 +424,30 @@ void bch2_btree_init_next(struct bch_fs *c, struct btree *b, struct btree_iter *iter) { struct btree_node_entry *bne; - bool did_sort; + bool reinit_iter = false; EBUG_ON(!(b->c.lock.state.seq & 1)); EBUG_ON(iter && iter->l[b->c.level].b != b); + BUG_ON(bset_written(b, bset(b, &b->set[1]))); - did_sort = btree_node_compact(c, b, iter); + if (b->nsets == MAX_BSETS) { + unsigned log_u64s[] = { + ilog2(bset_u64s(&b->set[0])), + ilog2(bset_u64s(&b->set[1])), + ilog2(bset_u64s(&b->set[2])), + }; + + if (log_u64s[1] >= (log_u64s[0] + log_u64s[2]) / 2) { + bch2_btree_node_write(c, b, SIX_LOCK_write); + reinit_iter = true; + } + } + + if (b->nsets == MAX_BSETS && + btree_node_compact(c, b)) + reinit_iter = true; + + BUG_ON(b->nsets >= MAX_BSETS); bne = want_new_bset(c, b); if (bne) @@ -439,7 +455,7 @@ void bch2_btree_init_next(struct bch_fs *c, struct btree *b, bch2_btree_build_aux_trees(b); - if (iter && did_sort) + if (iter && reinit_iter) bch2_btree_iter_reinit_node(iter, b); } @@ -1321,15 +1337,20 @@ static int validate_bset_for_write(struct bch_fs *c, struct btree *b, return ret; } -void __bch2_btree_node_write(struct bch_fs *c, struct btree *b, - enum six_lock_type lock_type_held) +static void btree_write_submit(struct work_struct *work) +{ + struct btree_write_bio *wbio = container_of(work, struct btree_write_bio, work); + + bch2_submit_wbio_replicas(&wbio->wbio, wbio->wbio.c, BCH_DATA_btree, &wbio->key); +} + +void __bch2_btree_node_write(struct bch_fs *c, struct btree *b) { struct btree_write_bio *wbio; struct bset_tree *t; struct bset *i; struct btree_node *bn = NULL; struct btree_node_entry *bne = NULL; - struct bkey_buf k; struct bch_extent_ptr *ptr; struct sort_iter sort_iter; struct nonce nonce; @@ -1340,8 +1361,6 @@ void __bch2_btree_node_write(struct bch_fs *c, struct btree *b, bool validate_before_checksum = false; void *data; - bch2_bkey_buf_init(&k); - if (test_bit(BCH_FS_HOLD_BTREE_WRITES, &c->flags)) return; @@ -1518,6 +1537,7 @@ void __bch2_btree_node_write(struct bch_fs *c, struct btree *b, wbio_init(&wbio->wbio.bio); wbio->data = data; wbio->bytes = bytes; + wbio->wbio.c = c; wbio->wbio.used_mempool = used_mempool; wbio->wbio.bio.bi_opf = REQ_OP_WRITE|REQ_META; wbio->wbio.bio.bi_end_io = btree_node_write_endio; @@ -1540,9 +1560,9 @@ void __bch2_btree_node_write(struct bch_fs *c, struct btree *b, * just make all btree node writes FUA to keep things sane. */ - bch2_bkey_buf_copy(&k, c, &b->key); + bkey_copy(&wbio->key, &b->key); - bkey_for_each_ptr(bch2_bkey_ptrs(bkey_i_to_s(k.k)), ptr) + bkey_for_each_ptr(bch2_bkey_ptrs(bkey_i_to_s(&wbio->key)), ptr) ptr->offset += b->written; b->written += sectors_to_write; @@ -1550,9 +1570,8 @@ void __bch2_btree_node_write(struct bch_fs *c, struct btree *b, atomic64_inc(&c->btree_writes_nr); atomic64_add(sectors_to_write, &c->btree_writes_sectors); - /* XXX: submitting IO with btree locks held: */ - bch2_submit_wbio_replicas(&wbio->wbio, c, BCH_DATA_btree, k.k); - bch2_bkey_buf_exit(&k, c); + INIT_WORK(&wbio->work, btree_write_submit); + schedule_work(&wbio->work); return; err: set_btree_node_noevict(b); @@ -1592,7 +1611,7 @@ bool bch2_btree_post_write_cleanup(struct bch_fs *c, struct btree *b) * single bset: */ if (b->nsets > 1) { - btree_node_sort(c, b, NULL, 0, b->nsets, true); + btree_node_sort(c, b, 0, b->nsets, true); invalidated_iter = true; } else { invalidated_iter = bch2_drop_whiteouts(b, COMPACT_ALL); @@ -1622,13 +1641,12 @@ bool bch2_btree_post_write_cleanup(struct bch_fs *c, struct btree *b) * Use this one if the node is intent locked: */ void bch2_btree_node_write(struct bch_fs *c, struct btree *b, - enum six_lock_type lock_type_held) + enum six_lock_type lock_type_held) { - BUG_ON(lock_type_held == SIX_LOCK_write); - if (lock_type_held == SIX_LOCK_intent || - six_lock_tryupgrade(&b->c.lock)) { - __bch2_btree_node_write(c, b, SIX_LOCK_intent); + (lock_type_held == SIX_LOCK_read && + six_lock_tryupgrade(&b->c.lock))) { + __bch2_btree_node_write(c, b); /* don't cycle lock unnecessarily: */ if (btree_node_just_written(b) && @@ -1640,7 +1658,10 @@ void bch2_btree_node_write(struct bch_fs *c, struct btree *b, if (lock_type_held == SIX_LOCK_read) six_lock_downgrade(&b->c.lock); } else { - __bch2_btree_node_write(c, b, SIX_LOCK_read); + __bch2_btree_node_write(c, b); + if (lock_type_held == SIX_LOCK_write && + btree_node_just_written(b)) + bch2_btree_post_write_cleanup(c, b); } } diff --git a/libbcachefs/btree_io.h b/libbcachefs/btree_io.h index 9c14cd30..c8a8b05a 100644 --- a/libbcachefs/btree_io.h +++ b/libbcachefs/btree_io.h @@ -42,6 +42,7 @@ struct btree_read_bio { struct btree_write_bio { struct work_struct work; + __BKEY_PADDED(key, BKEY_BTREE_PTR_VAL_U64s_MAX); void *data; unsigned bytes; struct bch_write_bio wbio; @@ -144,8 +145,7 @@ void bch2_btree_complete_write(struct bch_fs *, struct btree *, struct btree_write *); void bch2_btree_write_error_work(struct work_struct *); -void __bch2_btree_node_write(struct bch_fs *, struct btree *, - enum six_lock_type); +void __bch2_btree_node_write(struct bch_fs *, struct btree *); bool bch2_btree_post_write_cleanup(struct bch_fs *, struct btree *); void bch2_btree_node_write(struct bch_fs *, struct btree *, diff --git a/libbcachefs/btree_iter.c b/libbcachefs/btree_iter.c index 425c9ad7..e9baa078 100644 --- a/libbcachefs/btree_iter.c +++ b/libbcachefs/btree_iter.c @@ -465,8 +465,10 @@ bool bch2_trans_relock(struct btree_trans *trans) trans_for_each_iter(trans, iter) if (btree_iter_keep(trans, iter) && - !bch2_btree_iter_relock(iter, true)) + !bch2_btree_iter_relock(iter, true)) { + trace_trans_restart_relock(trans->ip); return false; + } return true; } @@ -1631,7 +1633,9 @@ static inline struct bkey_s_c __btree_iter_peek(struct btree_iter *iter, bool wi * iter->pos should be mononotically increasing, and always be equal to * the key we just returned - except extents can straddle iter->pos: */ - if (bkey_cmp(bkey_start_pos(k.k), iter->pos) > 0) + if (!(iter->flags & BTREE_ITER_IS_EXTENTS)) + iter->pos = k.k->p; + else if (bkey_cmp(bkey_start_pos(k.k), iter->pos) > 0) iter->pos = bkey_start_pos(k.k); bch2_btree_iter_verify_entry_exit(iter); diff --git a/libbcachefs/btree_key_cache.c b/libbcachefs/btree_key_cache.c index 0d3c0a40..53191c99 100644 --- a/libbcachefs/btree_key_cache.c +++ b/libbcachefs/btree_key_cache.c @@ -689,20 +689,16 @@ int bch2_fs_btree_key_cache_init(struct btree_key_cache *c) { int ret; - c->shrink.seeks = 1; - c->shrink.count_objects = bch2_btree_key_cache_count; - c->shrink.scan_objects = bch2_btree_key_cache_scan; - - ret = register_shrinker(&c->shrink); - if (ret) - return ret; - ret = rhashtable_init(&c->table, &bch2_btree_key_cache_params); if (ret) return ret; c->table_init_done = true; - return 0; + + c->shrink.seeks = 1; + c->shrink.count_objects = bch2_btree_key_cache_count; + c->shrink.scan_objects = bch2_btree_key_cache_scan; + return register_shrinker(&c->shrink); } void bch2_btree_key_cache_to_text(struct printbuf *out, struct btree_key_cache *c) diff --git a/libbcachefs/btree_types.h b/libbcachefs/btree_types.h index 39e93da1..f942ccf6 100644 --- a/libbcachefs/btree_types.h +++ b/libbcachefs/btree_types.h @@ -614,6 +614,10 @@ static inline bool btree_iter_is_extents(struct btree_iter *iter) (1U << BTREE_ID_dirents)| \ (1U << BTREE_ID_xattrs)) +#define BTREE_ID_HAS_PTRS \ + ((1U << BTREE_ID_extents)| \ + (1U << BTREE_ID_reflink)) + static inline bool btree_type_has_snapshots(enum btree_id id) { return (1 << id) & BTREE_ID_HAS_SNAPSHOTS; diff --git a/libbcachefs/btree_update_interior.c b/libbcachefs/btree_update_interior.c index 00144707..07c92534 100644 --- a/libbcachefs/btree_update_interior.c +++ b/libbcachefs/btree_update_interior.c @@ -974,20 +974,25 @@ retry: * closure argument */ if (flags & BTREE_INSERT_NOUNLOCK) { + trace_trans_restart_journal_preres_get(trans->ip); ret = -EINTR; goto err; } bch2_trans_unlock(trans); - if (flags & BTREE_INSERT_JOURNAL_RECLAIM) - goto err; + if (flags & BTREE_INSERT_JOURNAL_RECLAIM) { + bch2_btree_update_free(as); + return ERR_PTR(ret); + } ret = bch2_journal_preres_get(&c->journal, &as->journal_preres, BTREE_UPDATE_JOURNAL_RES, journal_flags); - if (ret) + if (ret) { + trace_trans_restart_journal_preres_get(trans->ip); goto err; + } if (!bch2_trans_relock(trans)) { ret = -EINTR; diff --git a/libbcachefs/btree_update_interior.h b/libbcachefs/btree_update_interior.h index f2925b0d..7eef3dbb 100644 --- a/libbcachefs/btree_update_interior.h +++ b/libbcachefs/btree_update_interior.h @@ -256,13 +256,15 @@ static inline size_t bch_btree_keys_u64s_remaining(struct bch_fs *c, return remaining; } +#define BTREE_WRITE_SET_U64s_BITS 9 + static inline unsigned btree_write_set_buffer(struct btree *b) { /* * Could buffer up larger amounts of keys for btrees with larger keys, * pending benchmarking: */ - return 4 << 10; + return 8 << BTREE_WRITE_SET_U64s_BITS; } static inline struct btree_node_entry *want_new_bset(struct bch_fs *c, diff --git a/libbcachefs/btree_update_leaf.c b/libbcachefs/btree_update_leaf.c index e258cf89..f5160d4f 100644 --- a/libbcachefs/btree_update_leaf.c +++ b/libbcachefs/btree_update_leaf.c @@ -639,6 +639,8 @@ static int journal_reclaim_wait_done(struct bch_fs *c) if (ret) return ret; + journal_reclaim_kick(&c->journal); + if (mutex_trylock(&c->journal.reclaim_lock)) { ret = bch2_journal_reclaim(&c->journal); mutex_unlock(&c->journal.reclaim_lock); diff --git a/libbcachefs/buckets.c b/libbcachefs/buckets.c index 31f7617e..f0c3bbc7 100644 --- a/libbcachefs/buckets.c +++ b/libbcachefs/buckets.c @@ -832,7 +832,7 @@ static int mark_stripe_bucket(struct bch_fs *c, struct bkey_s_c k, if (g->stripe && g->stripe != k.k->p.offset) { bch2_fs_inconsistent(c, "bucket %u:%zu gen %u: multiple stripes using same bucket\n%s", - ptr->dev, PTR_BUCKET_NR(ca, ptr), new.gen, + ptr->dev, PTR_BUCKET_NR(ca, ptr), g->mark.gen, (bch2_bkey_val_to_text(&PBUF(buf), c, k), buf)); return -EINVAL; } diff --git a/libbcachefs/debug.c b/libbcachefs/debug.c index acf60038..90364b55 100644 --- a/libbcachefs/debug.c +++ b/libbcachefs/debug.c @@ -150,7 +150,7 @@ struct dump_iter { struct bch_fs *c; enum btree_id id; - char buf[PAGE_SIZE]; + char buf[1 << 12]; size_t bytes; /* what's currently in buf */ char __user *ubuf; /* destination user buffer */ @@ -230,7 +230,7 @@ static ssize_t bch2_read_btree(struct file *file, char __user *buf, while (k.k && !(err = bkey_err(k))) { bch2_bkey_val_to_text(&PBUF(i->buf), i->c, k); i->bytes = strlen(i->buf); - BUG_ON(i->bytes >= PAGE_SIZE); + BUG_ON(i->bytes >= sizeof(i->buf)); i->buf[i->bytes] = '\n'; i->bytes++; diff --git a/libbcachefs/dirent.c b/libbcachefs/dirent.c index cf4ce2e7..ec466614 100644 --- a/libbcachefs/dirent.c +++ b/libbcachefs/dirent.c @@ -84,16 +84,24 @@ const char *bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k) if (!len) return "empty name"; - /* - * older versions of bcachefs were buggy and creating dirent - * keys that were bigger than necessary: - */ - if (bkey_val_u64s(k.k) > dirent_val_u64s(len + 7)) + if (bkey_val_u64s(k.k) > dirent_val_u64s(len)) return "value too big"; if (len > BCH_NAME_MAX) return "dirent name too big"; + if (len == 1 && !memcmp(d.v->d_name, ".", 1)) + return "invalid name"; + + if (len == 2 && !memcmp(d.v->d_name, "..", 2)) + return "invalid name"; + + if (memchr(d.v->d_name, '/', len)) + return "invalid name"; + + if (le64_to_cpu(d.v->d_inum) == d.k->p.inode) + return "dirent points to own directory"; + return NULL; } diff --git a/libbcachefs/fsck.c b/libbcachefs/fsck.c index acf128f0..21802ed5 100644 --- a/libbcachefs/fsck.c +++ b/libbcachefs/fsck.c @@ -38,6 +38,49 @@ static s64 bch2_count_inode_sectors(struct btree_trans *trans, u64 inum) return ret ?: sectors; } +static int lookup_inode(struct btree_trans *trans, u64 inode_nr, + struct bch_inode_unpacked *inode, + u32 *snapshot) +{ + struct btree_iter *iter; + struct bkey_s_c k; + int ret; + + iter = bch2_trans_get_iter(trans, BTREE_ID_inodes, + POS(0, inode_nr), 0); + k = bch2_btree_iter_peek_slot(iter); + ret = bkey_err(k); + if (ret) + goto err; + + if (snapshot) + *snapshot = iter->pos.snapshot; + ret = k.k->type == KEY_TYPE_inode + ? bch2_inode_unpack(bkey_s_c_to_inode(k), inode) + : -ENOENT; +err: + bch2_trans_iter_put(trans, iter); + return ret; +} + +static int write_inode(struct btree_trans *trans, + struct bch_inode_unpacked *inode, + u32 snapshot) +{ + struct btree_iter *inode_iter = + bch2_trans_get_iter(trans, BTREE_ID_inodes, + SPOS(0, inode->bi_inum, snapshot), + BTREE_ITER_INTENT); + int ret = __bch2_trans_do(trans, NULL, NULL, + BTREE_INSERT_NOFAIL| + BTREE_INSERT_LAZY_RW, + bch2_inode_write(trans, inode_iter, inode)); + bch2_trans_iter_put(trans, inode_iter); + if (ret) + bch_err(trans->c, "error in fsck: error %i updating inode", ret); + return ret; +} + static int __remove_dirent(struct btree_trans *trans, struct bkey_s_c_dirent dirent) { @@ -58,7 +101,7 @@ static int __remove_dirent(struct btree_trans *trans, buf[name.len] = '\0'; name.name = buf; - ret = __bch2_inode_find_by_inum_trans(trans, dir_inum, &dir_inode, 0); + ret = lookup_inode(trans, dir_inum, &dir_inode, NULL); if (ret && ret != -EINTR) bch_err(c, "remove_dirent: err %i looking up directory inode", ret); if (ret) @@ -111,6 +154,7 @@ struct inode_walker { bool first_this_inode; bool have_inode; u64 cur_inum; + u32 snapshot; struct bch_inode_unpacked inode; }; @@ -126,8 +170,7 @@ static int walk_inode(struct btree_trans *trans, struct inode_walker *w, u64 inum) { if (inum != w->cur_inum) { - int ret = __bch2_inode_find_by_inum_trans(trans, inum, - &w->inode, 0); + int ret = lookup_inode(trans, inum, &w->inode, &w->snapshot); if (ret && ret != -ENOENT) return ret; @@ -142,42 +185,10 @@ static int walk_inode(struct btree_trans *trans, return 0; } -struct hash_check { - struct bch_hash_info info; - - /* start of current chain of hash collisions: */ - struct btree_iter *chain; - - /* next offset in current chain of hash collisions: */ - u64 chain_end; -}; - -static void hash_check_init(struct hash_check *h) -{ - h->chain = NULL; - h->chain_end = 0; -} - -static void hash_stop_chain(struct btree_trans *trans, - struct hash_check *h) -{ - if (h->chain) - bch2_trans_iter_free(trans, h->chain); - h->chain = NULL; -} - -static void hash_check_set_inode(struct btree_trans *trans, - struct hash_check *h, - const struct bch_inode_unpacked *bi) -{ - h->info = bch2_hash_info_init(trans->c, bi); - hash_stop_chain(trans, h); -} - -static int hash_redo_key(const struct bch_hash_desc desc, - struct btree_trans *trans, struct hash_check *h, - struct btree_iter *k_iter, struct bkey_s_c k, - u64 hashed) +static int hash_redo_key(struct btree_trans *trans, + const struct bch_hash_desc desc, + struct bch_hash_info *hash_info, + struct btree_iter *k_iter, struct bkey_s_c k) { struct bkey_i delete; struct bkey_i *tmp; @@ -192,7 +203,7 @@ static int hash_redo_key(const struct bch_hash_desc desc, delete.k.p = k_iter->pos; bch2_trans_update(trans, k_iter, &delete, 0); - return bch2_hash_set(trans, desc, &h->info, k_iter->pos.inode, + return bch2_hash_set(trans, desc, hash_info, k_iter->pos.inode, tmp, 0); } @@ -216,201 +227,217 @@ retry: return ret; } -static int hash_check_duplicates(struct btree_trans *trans, - const struct bch_hash_desc desc, struct hash_check *h, - struct btree_iter *k_iter, struct bkey_s_c k) +static int hash_check_key(struct btree_trans *trans, + const struct bch_hash_desc desc, + struct bch_hash_info *hash_info, + struct btree_iter *k_iter, struct bkey_s_c hash_k) { struct bch_fs *c = trans->c; - struct btree_iter *iter; - struct bkey_s_c k2; + struct btree_iter *iter = NULL; char buf[200]; + struct bkey_s_c k; + u64 hash; int ret = 0; - if (!bkey_cmp(h->chain->pos, k_iter->pos)) + if (hash_k.k->type != desc.key_type) return 0; - iter = bch2_trans_copy_iter(trans, h->chain); + hash = desc.hash_bkey(hash_info, hash_k); - for_each_btree_key_continue(iter, 0, k2, ret) { - if (bkey_cmp(k2.k->p, k.k->p) >= 0) + if (likely(hash == hash_k.k->p.offset)) + return 0; + + if (hash_k.k->p.offset < hash) + goto bad_hash; + + for_each_btree_key(trans, iter, desc.btree_id, POS(hash_k.k->p.inode, hash), + BTREE_ITER_SLOTS, k, ret) { + if (!bkey_cmp(k.k->p, hash_k.k->p)) break; - if (fsck_err_on(k2.k->type == desc.key_type && - !desc.cmp_bkey(k, k2), c, + if (fsck_err_on(k.k->type == desc.key_type && + !desc.cmp_bkey(k, hash_k), c, "duplicate hash table keys:\n%s", (bch2_bkey_val_to_text(&PBUF(buf), c, - k), buf))) { - ret = fsck_hash_delete_at(trans, desc, &h->info, k_iter); + hash_k), buf))) { + ret = fsck_hash_delete_at(trans, desc, hash_info, k_iter); if (ret) return ret; ret = 1; break; } + + if (bkey_deleted(k.k)) { + bch2_trans_iter_free(trans, iter); + goto bad_hash; + } + } -fsck_err: bch2_trans_iter_free(trans, iter); return ret; -} - -static void hash_set_chain_start(struct btree_trans *trans, - const struct bch_hash_desc desc, - struct hash_check *h, - struct btree_iter *k_iter, struct bkey_s_c k) -{ - bool hole = (k.k->type != KEY_TYPE_hash_whiteout && - k.k->type != desc.key_type); - - if (hole || k.k->p.offset > h->chain_end + 1) - hash_stop_chain(trans, h); - - if (!hole) { - if (!h->chain) - h->chain = bch2_trans_copy_iter(trans, k_iter); - - h->chain_end = k.k->p.offset; - } -} - -static bool key_has_correct_hash(struct btree_trans *trans, - const struct bch_hash_desc desc, - struct hash_check *h, - struct btree_iter *k_iter, struct bkey_s_c k) -{ - u64 hash; - - hash_set_chain_start(trans, desc, h, k_iter, k); - - if (k.k->type != desc.key_type) - return true; - - hash = desc.hash_bkey(&h->info, k); - - return hash >= h->chain->pos.offset && - hash <= k.k->p.offset; -} - -static int hash_check_key(struct btree_trans *trans, - const struct bch_hash_desc desc, struct hash_check *h, - struct btree_iter *k_iter, struct bkey_s_c k) -{ - struct bch_fs *c = trans->c; - char buf[200]; - u64 hashed; - int ret = 0; - - hash_set_chain_start(trans, desc, h, k_iter, k); - - if (k.k->type != desc.key_type) +bad_hash: + if (fsck_err(c, "hash table key at wrong offset: btree %u inode %llu offset %llu, " + "hashed to %llu should be at %llu\n%s", + desc.btree_id, hash_k.k->p.inode, hash_k.k->p.offset, + hash, iter->pos.offset, + (bch2_bkey_val_to_text(&PBUF(buf), c, hash_k), buf)) == FSCK_ERR_IGNORE) return 0; - hashed = desc.hash_bkey(&h->info, k); - - if (fsck_err_on(hashed < h->chain->pos.offset || - hashed > k.k->p.offset, c, - "hash table key at wrong offset: btree %u, %llu, " - "hashed to %llu chain starts at %llu\n%s", - desc.btree_id, k.k->p.offset, - hashed, h->chain->pos.offset, - (bch2_bkey_val_to_text(&PBUF(buf), c, k), buf))) { - ret = __bch2_trans_do(trans, NULL, NULL, - BTREE_INSERT_NOFAIL|BTREE_INSERT_LAZY_RW, - hash_redo_key(desc, trans, h, k_iter, k, hashed)); - if (ret) { - bch_err(c, "hash_redo_key err %i", ret); - return ret; - } - return -EINTR; + ret = __bch2_trans_do(trans, NULL, NULL, + BTREE_INSERT_NOFAIL|BTREE_INSERT_LAZY_RW, + hash_redo_key(trans, desc, hash_info, k_iter, hash_k)); + if (ret) { + bch_err(c, "hash_redo_key err %i", ret); + return ret; } - - ret = hash_check_duplicates(trans, desc, h, k_iter, k); + return -EINTR; fsck_err: return ret; } -static int check_dirent_hash(struct btree_trans *trans, struct hash_check *h, - struct btree_iter *iter, struct bkey_s_c *k) +static int check_inode(struct btree_trans *trans, + struct btree_iter *iter, + struct bkey_s_c_inode inode) { struct bch_fs *c = trans->c; - struct bkey_i_dirent *d = NULL; - int ret = -EINVAL; - char buf[200]; - unsigned len; - u64 hash; + struct bch_inode_unpacked u; + bool do_update = false; + int ret = 0; - if (key_has_correct_hash(trans, bch2_dirent_hash_desc, h, iter, *k)) - return 0; + ret = bch2_inode_unpack(inode, &u); - len = bch2_dirent_name_bytes(bkey_s_c_to_dirent(*k)); - BUG_ON(!len); + if (bch2_fs_inconsistent_on(ret, c, + "error unpacking inode %llu in fsck", + inode.k->p.inode)) + return ret; - memcpy(buf, bkey_s_c_to_dirent(*k).v->d_name, len); - buf[len] = '\0'; + if (u.bi_flags & BCH_INODE_UNLINKED && + (!c->sb.clean || + fsck_err(c, "filesystem marked clean, but inode %llu unlinked", + u.bi_inum))) { + bch_verbose(c, "deleting inode %llu", u.bi_inum); - d = kmalloc(bkey_bytes(k->k), GFP_KERNEL); - if (!d) { - bch_err(c, "memory allocation failure"); - return -ENOMEM; + bch2_trans_unlock(trans); + bch2_fs_lazy_rw(c); + + ret = bch2_inode_rm(c, u.bi_inum, false); + if (ret) + bch_err(c, "error in fsck: error %i while deleting inode", ret); + return ret; } - bkey_reassemble(&d->k_i, *k); + if (u.bi_flags & BCH_INODE_I_SIZE_DIRTY && + (!c->sb.clean || + fsck_err(c, "filesystem marked clean, but inode %llu has i_size dirty", + u.bi_inum))) { + bch_verbose(c, "truncating inode %llu", u.bi_inum); - do { - --len; - if (!len) - goto err_redo; + bch2_trans_unlock(trans); + bch2_fs_lazy_rw(c); - d->k.u64s = BKEY_U64s + dirent_val_u64s(len); + /* + * XXX: need to truncate partial blocks too here - or ideally + * just switch units to bytes and that issue goes away + */ + ret = bch2_btree_delete_range_trans(trans, BTREE_ID_extents, + POS(u.bi_inum, round_up(u.bi_size, block_bytes(c))), + POS(u.bi_inum, U64_MAX), + NULL); + if (ret) { + bch_err(c, "error in fsck: error %i truncating inode", ret); + return ret; + } - BUG_ON(bkey_val_bytes(&d->k) < - offsetof(struct bch_dirent, d_name) + len); + /* + * We truncated without our normal sector accounting hook, just + * make sure we recalculate it: + */ + u.bi_flags |= BCH_INODE_I_SECTORS_DIRTY; - memset(d->v.d_name + len, 0, - bkey_val_bytes(&d->k) - - offsetof(struct bch_dirent, d_name) - len); + u.bi_flags &= ~BCH_INODE_I_SIZE_DIRTY; + do_update = true; + } - hash = bch2_dirent_hash_desc.hash_bkey(&h->info, - bkey_i_to_s_c(&d->k_i)); - } while (hash < h->chain->pos.offset || - hash > k->k->p.offset); + if (u.bi_flags & BCH_INODE_I_SECTORS_DIRTY && + (!c->sb.clean || + fsck_err(c, "filesystem marked clean, but inode %llu has i_sectors dirty", + u.bi_inum))) { + s64 sectors; + + bch_verbose(c, "recounting sectors for inode %llu", + u.bi_inum); + + sectors = bch2_count_inode_sectors(trans, u.bi_inum); + if (sectors < 0) { + bch_err(c, "error in fsck: error %i recounting inode sectors", + (int) sectors); + return sectors; + } + + u.bi_sectors = sectors; + u.bi_flags &= ~BCH_INODE_I_SECTORS_DIRTY; + do_update = true; + } + + if (!S_ISDIR(u.bi_mode) && + u.bi_nlink && + !(u.bi_flags & BCH_INODE_BACKPTR_UNTRUSTED) && + (fsck_err_on(c->sb.version >= bcachefs_metadata_version_inode_backpointers, c, + "inode missing BCH_INODE_BACKPTR_UNTRUSTED flags") || + c->opts.version_upgrade)) { + u.bi_flags |= BCH_INODE_BACKPTR_UNTRUSTED; + do_update = true; + } + + if (do_update) { + struct bkey_inode_buf p; + + bch2_inode_pack(c, &p, &u); + p.inode.k.p = iter->pos; - if (fsck_err(c, "dirent with junk at end, was %s (%zu) now %s (%u)", - buf, strlen(buf), d->v.d_name, len)) { ret = __bch2_trans_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL| BTREE_INSERT_LAZY_RW, - (bch2_trans_update(trans, iter, &d->k_i, 0), 0)); + (bch2_trans_update(trans, iter, &p.inode.k_i, 0), 0)); if (ret) - goto err; - - *k = bch2_btree_iter_peek(iter); - - BUG_ON(k->k->type != KEY_TYPE_dirent); + bch_err(c, "error in fsck: error %i " + "updating inode", ret); } -err: fsck_err: - kfree(d); return ret; -err_redo: - hash = bch2_dirent_hash_desc.hash_bkey(&h->info, *k); +} - if (fsck_err(c, "cannot fix dirent by removing trailing garbage %s (%zu)\n" - "hash table key at wrong offset: btree %u, offset %llu, " - "hashed to %llu chain starts at %llu\n%s", - buf, strlen(buf), BTREE_ID_dirents, - k->k->p.offset, hash, h->chain->pos.offset, - (bch2_bkey_val_to_text(&PBUF(buf), c, - *k), buf))) { - ret = __bch2_trans_do(trans, NULL, NULL, - BTREE_INSERT_NOFAIL|BTREE_INSERT_LAZY_RW, - hash_redo_key(bch2_dirent_hash_desc, trans, - h, iter, *k, hash)); - if (ret) - bch_err(c, "hash_redo_key err %i", ret); - else - ret = 1; +noinline_for_stack +static int check_inodes(struct bch_fs *c, bool full) +{ + struct btree_trans trans; + struct btree_iter *iter; + struct bkey_s_c k; + struct bkey_s_c_inode inode; + int ret; + + bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0); + + for_each_btree_key(&trans, iter, BTREE_ID_inodes, POS_MIN, 0, k, ret) { + if (k.k->type != KEY_TYPE_inode) + continue; + + inode = bkey_s_c_to_inode(k); + + if (full || + (inode.v->bi_flags & (BCH_INODE_I_SIZE_DIRTY| + BCH_INODE_I_SECTORS_DIRTY| + BCH_INODE_UNLINKED))) { + ret = check_inode(&trans, iter, inode); + if (ret) + break; + } } + bch2_trans_iter_put(&trans, iter); - goto err; + BUG_ON(ret == -EINTR); + + return bch2_trans_exit(&trans) ?: ret; } static int fix_overlapping_extent(struct btree_trans *trans, @@ -448,6 +475,35 @@ static int fix_overlapping_extent(struct btree_trans *trans, BTREE_INSERT_LAZY_RW); } +static int inode_backpointer_exists(struct btree_trans *trans, + struct bch_inode_unpacked *inode) +{ + struct btree_iter *iter; + struct bkey_s_c k; + int ret; + + iter = bch2_trans_get_iter(trans, BTREE_ID_dirents, + POS(inode->bi_dir, inode->bi_dir_offset), 0); + k = bch2_btree_iter_peek_slot(iter); + ret = bkey_err(k); + if (ret) + goto out; + if (k.k->type != KEY_TYPE_dirent) + goto out; + + ret = le64_to_cpu(bkey_s_c_to_dirent(k).v->d_inum) == inode->bi_inum; +out: + bch2_trans_iter_free(trans, iter); + return ret; +} + +static bool inode_backpointer_matches(struct bkey_s_c_dirent d, + struct bch_inode_unpacked *inode) +{ + return d.k->p.inode == inode->bi_dir && + d.k->p.offset == inode->bi_dir_offset; +} + /* * Walk extents: verify that extents have a corresponding S_ISREG inode, and * that i_size an i_sectors are consistent @@ -482,18 +538,9 @@ retry: "inode %llu has incorrect i_sectors: got %llu, should be %llu", w.inode.bi_inum, w.inode.bi_sectors, i_sectors)) { - struct btree_iter *inode_iter = - bch2_trans_get_iter(&trans, BTREE_ID_inodes, - POS(0, w.cur_inum), - BTREE_ITER_INTENT); - w.inode.bi_sectors = i_sectors; - ret = __bch2_trans_do(&trans, NULL, NULL, - BTREE_INSERT_NOFAIL| - BTREE_INSERT_LAZY_RW, - bch2_inode_write(&trans, inode_iter, &w.inode)); - bch2_trans_iter_put(&trans, inode_iter); + ret = write_inode(&trans, &w.inode, w.snapshot); if (ret) break; } @@ -565,20 +612,18 @@ noinline_for_stack static int check_dirents(struct bch_fs *c) { struct inode_walker w = inode_walker_init(); - struct hash_check h; + struct bch_hash_info hash_info; struct btree_trans trans; struct btree_iter *iter; struct bkey_s_c k; - unsigned name_len; char buf[200]; + unsigned nr_subdirs = 0; int ret = 0; bch_verbose(c, "checking dirents"); bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0); - hash_check_init(&h); - iter = bch2_trans_get_iter(&trans, BTREE_ID_dirents, POS(BCACHEFS_ROOT_INO, 0), 0); retry: @@ -586,13 +631,29 @@ retry: !(ret = bkey_err(k))) { struct bkey_s_c_dirent d; struct bch_inode_unpacked target; + u32 target_snapshot; bool have_target; + bool backpointer_exists = true; u64 d_inum; + if (w.have_inode && + w.cur_inum != k.k->p.inode && + fsck_err_on(w.inode.bi_nlink != nr_subdirs, c, + "directory %llu with wrong i_nlink: got %u, should be %u", + w.inode.bi_inum, w.inode.bi_nlink, nr_subdirs)) { + w.inode.bi_nlink = nr_subdirs; + ret = write_inode(&trans, &w.inode, w.snapshot); + if (ret) + break; + } + ret = walk_inode(&trans, &w, k.k->p.inode); if (ret) break; + if (w.first_this_inode) + nr_subdirs = 0; + if (fsck_err_on(!w.have_inode, c, "dirent in nonexisting directory:\n%s", (bch2_bkey_val_to_text(&PBUF(buf), c, @@ -605,60 +666,31 @@ retry: ret = bch2_btree_delete_at(&trans, iter, 0); if (ret) goto err; - continue; + goto next; } - if (w.first_this_inode && w.have_inode) - hash_check_set_inode(&trans, &h, &w.inode); + if (!w.have_inode) + goto next; - ret = check_dirent_hash(&trans, &h, iter, &k); + if (w.first_this_inode) + hash_info = bch2_hash_info_init(c, &w.inode); + + ret = hash_check_key(&trans, bch2_dirent_hash_desc, + &hash_info, iter, k); if (ret > 0) { ret = 0; - continue; + goto next; } if (ret) goto fsck_err; - if (ret) - goto fsck_err; - if (k.k->type != KEY_TYPE_dirent) - continue; + goto next; d = bkey_s_c_to_dirent(k); d_inum = le64_to_cpu(d.v->d_inum); - name_len = bch2_dirent_name_bytes(d); - - if (fsck_err_on(!name_len, c, "empty dirent") || - fsck_err_on(name_len == 1 && - !memcmp(d.v->d_name, ".", 1), c, - ". dirent") || - fsck_err_on(name_len == 2 && - !memcmp(d.v->d_name, "..", 2), c, - ".. dirent") || - fsck_err_on(name_len == 2 && - !memcmp(d.v->d_name, "..", 2), c, - ".. dirent") || - fsck_err_on(memchr(d.v->d_name, '/', name_len), c, - "dirent name has invalid chars")) { - ret = remove_dirent(&trans, d); - if (ret) - goto err; - continue; - } - - if (fsck_err_on(d_inum == d.k->p.inode, c, - "dirent points to own directory:\n%s", - (bch2_bkey_val_to_text(&PBUF(buf), c, - k), buf))) { - ret = remove_dirent(&trans, d); - if (ret) - goto err; - continue; - } - - ret = __bch2_inode_find_by_inum_trans(&trans, d_inum, &target, 0); + ret = lookup_inode(&trans, d_inum, &target, &target_snapshot); if (ret && ret != -ENOENT) break; @@ -672,45 +704,66 @@ retry: ret = remove_dirent(&trans, d); if (ret) goto err; - continue; + goto next; } - if (!target.bi_nlink && - !(target.bi_flags & BCH_INODE_BACKPTR_UNTRUSTED) && - (target.bi_dir != k.k->p.inode || - target.bi_dir_offset != k.k->p.offset) && - (fsck_err_on(c->sb.version >= bcachefs_metadata_version_inode_backpointers, c, - "inode %llu has wrong backpointer:\n" - "got %llu:%llu\n" - "should be %llu:%llu", - d_inum, - target.bi_dir, - target.bi_dir_offset, - k.k->p.inode, - k.k->p.offset) || - c->opts.version_upgrade)) { - struct bkey_inode_buf p; + if (!have_target) + goto next; - target.bi_dir = k.k->p.inode; - target.bi_dir_offset = k.k->p.offset; - bch2_trans_unlock(&trans); - - bch2_inode_pack(c, &p, &target); - - ret = bch2_btree_insert(c, BTREE_ID_inodes, - &p.inode.k_i, NULL, NULL, - BTREE_INSERT_NOFAIL| - BTREE_INSERT_LAZY_RW); - if (ret) { - bch_err(c, "error in fsck: error %i updating inode", ret); + if (!inode_backpointer_matches(d, &target)) { + ret = inode_backpointer_exists(&trans, &target); + if (ret < 0) + goto err; + + backpointer_exists = ret; + ret = 0; + } + + if (fsck_err_on(S_ISDIR(target.bi_mode) && + !inode_backpointer_matches(d, &target) && + backpointer_exists, c, + "directory %llu with multiple links", + target.bi_inum)) { + ret = remove_dirent(&trans, d); + if (ret) goto err; - } continue; } - if (fsck_err_on(have_target && - d.v->d_type != - mode_to_type(target.bi_mode), c, + if (!inode_backpointer_matches(d, &target) && + (S_ISDIR(target.bi_mode) || !target.bi_nlink)) { + if (backpointer_exists) { + if (!fsck_err(c, "inode %llu has multiple links but i_nlink 0", + d_inum)) + goto check_type; + + target.bi_nlink++; + target.bi_flags |= BCH_INODE_BACKPTR_UNTRUSTED; + } else { + if (c->sb.version >= bcachefs_metadata_version_inode_backpointers && + !(target.bi_flags & BCH_INODE_BACKPTR_UNTRUSTED) && + !fsck_err(c, "inode %llu has wrong backpointer:\n" + "got %llu:%llu\n" + "should be %llu:%llu", + d_inum, + target.bi_dir, + target.bi_dir_offset, + k.k->p.inode, + k.k->p.offset)) + goto check_type; + + target.bi_dir = k.k->p.inode; + target.bi_dir_offset = k.k->p.offset; + target.bi_flags &= ~BCH_INODE_BACKPTR_UNTRUSTED; + } + + ret = write_inode(&trans, &target, target_snapshot); + if (ret) + goto err; + continue; + } +check_type: + if (fsck_err_on(d.v->d_type != mode_to_type(target.bi_mode), c, "incorrect d_type: should be %u:\n%s", mode_to_type(target.bi_mode), (bch2_bkey_val_to_text(&PBUF(buf), c, @@ -736,16 +789,15 @@ retry: } + nr_subdirs += d.v->d_type == DT_DIR; +next: bch2_btree_iter_advance(iter); } - - hash_stop_chain(&trans, &h); err: fsck_err: if (ret == -EINTR) goto retry; - bch2_trans_iter_put(&trans, h.chain); bch2_trans_iter_put(&trans, iter); return bch2_trans_exit(&trans) ?: ret; } @@ -757,7 +809,7 @@ noinline_for_stack static int check_xattrs(struct bch_fs *c) { struct inode_walker w = inode_walker_init(); - struct hash_check h; + struct bch_hash_info hash_info; struct btree_trans trans; struct btree_iter *iter; struct bkey_s_c k; @@ -765,8 +817,6 @@ static int check_xattrs(struct bch_fs *c) bch_verbose(c, "checking xattrs"); - hash_check_init(&h); - bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0); iter = bch2_trans_get_iter(&trans, BTREE_ID_xattrs, @@ -788,10 +838,10 @@ retry: } if (w.first_this_inode && w.have_inode) - hash_check_set_inode(&trans, &h, &w.inode); + hash_info = bch2_hash_info_init(c, &w.inode); ret = hash_check_key(&trans, bch2_xattr_hash_desc, - &h, iter, k); + &hash_info, iter, k); if (ret) break; @@ -801,7 +851,6 @@ fsck_err: if (ret == -EINTR) goto retry; - bch2_trans_iter_put(&trans, h.chain); bch2_trans_iter_put(&trans, iter); return bch2_trans_exit(&trans) ?: ret; } @@ -1088,14 +1137,12 @@ fsck_err: struct nlink { u32 count; - u32 dir_count; }; typedef GENRADIX(struct nlink) nlink_table; static void inc_link(struct bch_fs *c, nlink_table *links, - u64 range_start, u64 *range_end, - u64 inum, bool dir) + u64 range_start, u64 *range_end, u64 inum) { struct nlink *link; @@ -1114,10 +1161,7 @@ static void inc_link(struct bch_fs *c, nlink_table *links, return; } - if (dir) - link->dir_count++; - else - link->count++; + link->count++; } noinline_for_stack @@ -1128,26 +1172,18 @@ static int bch2_gc_walk_dirents(struct bch_fs *c, nlink_table *links, struct btree_iter *iter; struct bkey_s_c k; struct bkey_s_c_dirent d; - u64 d_inum; int ret; bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0); - inc_link(c, links, range_start, range_end, BCACHEFS_ROOT_INO, false); - for_each_btree_key(&trans, iter, BTREE_ID_dirents, POS_MIN, 0, k, ret) { switch (k.k->type) { case KEY_TYPE_dirent: d = bkey_s_c_to_dirent(k); - d_inum = le64_to_cpu(d.v->d_inum); - if (d.v->d_type == DT_DIR) + if (d.v->d_type != DT_DIR) inc_link(c, links, range_start, range_end, - d.k->p.inode, true); - - inc_link(c, links, range_start, range_end, - d_inum, false); - + le64_to_cpu(d.v->d_inum)); break; } @@ -1162,204 +1198,52 @@ static int bch2_gc_walk_dirents(struct bch_fs *c, nlink_table *links, return ret; } -static int check_inode_nlink(struct bch_fs *c, +static int check_inode_nlink(struct btree_trans *trans, struct bch_inode_unpacked *lostfound_inode, - struct bch_inode_unpacked *u, - struct nlink *link, - bool *do_update) -{ - u32 i_nlink = bch2_inode_nlink_get(u); - u32 real_i_nlink = - link->count * nlink_bias(u->bi_mode) + - link->dir_count; - int ret = 0; - - /* - * These should have been caught/fixed by earlier passes, we don't - * repair them here: - */ - if (S_ISDIR(u->bi_mode) && link->count > 1) { - need_fsck_err(c, "directory %llu with multiple hardlinks: %u", - u->bi_inum, link->count); - return 0; - } - - if (S_ISDIR(u->bi_mode) && !link->count) { - need_fsck_err(c, "unreachable directory found (inum %llu)", - u->bi_inum); - return 0; - } - - if (!S_ISDIR(u->bi_mode) && link->dir_count) { - need_fsck_err(c, "non directory with subdirectories (inum %llu)", - u->bi_inum); - return 0; - } - - if (!link->count && - !(u->bi_flags & BCH_INODE_UNLINKED) && - (c->sb.features & (1 << BCH_FEATURE_atomic_nlink))) { - if (fsck_err(c, "unreachable inode %llu not marked as unlinked (type %u)", - u->bi_inum, mode_to_type(u->bi_mode)) == - FSCK_ERR_IGNORE) - return 0; - - ret = reattach_inode(c, lostfound_inode, u->bi_inum); - if (ret) - return ret; - - link->count = 1; - real_i_nlink = nlink_bias(u->bi_mode) + link->dir_count; - goto set_i_nlink; - } - - if (i_nlink < link->count) { - if (fsck_err(c, "inode %llu i_link too small (%u < %u, type %i)", - u->bi_inum, i_nlink, link->count, - mode_to_type(u->bi_mode)) == FSCK_ERR_IGNORE) - return 0; - goto set_i_nlink; - } - - if (i_nlink != real_i_nlink && - c->sb.clean) { - if (fsck_err(c, "filesystem marked clean, " - "but inode %llu has wrong i_nlink " - "(type %u i_nlink %u, should be %u)", - u->bi_inum, mode_to_type(u->bi_mode), - i_nlink, real_i_nlink) == FSCK_ERR_IGNORE) - return 0; - goto set_i_nlink; - } - - if (i_nlink != real_i_nlink && - (c->sb.features & (1 << BCH_FEATURE_atomic_nlink))) { - if (fsck_err(c, "inode %llu has wrong i_nlink " - "(type %u i_nlink %u, should be %u)", - u->bi_inum, mode_to_type(u->bi_mode), - i_nlink, real_i_nlink) == FSCK_ERR_IGNORE) - return 0; - goto set_i_nlink; - } - - if (real_i_nlink && i_nlink != real_i_nlink) - bch_verbose(c, "setting inode %llu nlink from %u to %u", - u->bi_inum, i_nlink, real_i_nlink); -set_i_nlink: - if (i_nlink != real_i_nlink) { - bch2_inode_nlink_set(u, real_i_nlink); - *do_update = true; - } -fsck_err: - return ret; -} - -static int check_inode(struct btree_trans *trans, - struct bch_inode_unpacked *lostfound_inode, - struct btree_iter *iter, - struct bkey_s_c_inode inode, - struct nlink *link) + struct btree_iter *iter, + struct bkey_s_c_inode inode, + unsigned nlink) { struct bch_fs *c = trans->c; struct bch_inode_unpacked u; - bool do_update = false; int ret = 0; + /* + * Backpointer and directory structure checks are sufficient for + * directories, since they can't have hardlinks: + */ + if (S_ISDIR(le16_to_cpu(inode.v->bi_mode))) + return 0; + ret = bch2_inode_unpack(inode, &u); - bch2_trans_unlock(trans); - + /* Should never happen, checked by bch2_inode_invalid: */ if (bch2_fs_inconsistent_on(ret, c, "error unpacking inode %llu in fsck", inode.k->p.inode)) return ret; - if (link) { - ret = check_inode_nlink(c, lostfound_inode, &u, link, - &do_update); + /* Improved directory structure pass will catch this: */ + if (fsck_err_on(!nlink, c, + "unreachable inode %llu not marked as unlinked (type %u)", + u.bi_inum, mode_to_type(u.bi_mode))) { + ret = reattach_inode(c, lostfound_inode, u.bi_inum); if (ret) return ret; + + nlink = 1; } - if (u.bi_flags & BCH_INODE_UNLINKED && - (!c->sb.clean || - fsck_err(c, "filesystem marked clean, but inode %llu unlinked", - u.bi_inum))) { - bch_verbose(c, "deleting inode %llu", u.bi_inum); - - bch2_fs_lazy_rw(c); - - ret = bch2_inode_rm(c, u.bi_inum, false); - if (ret) - bch_err(c, "error in fsck: error %i while deleting inode", ret); - return ret; - } - - if (u.bi_flags & BCH_INODE_I_SIZE_DIRTY && - (!c->sb.clean || - fsck_err(c, "filesystem marked clean, but inode %llu has i_size dirty", - u.bi_inum))) { - bch_verbose(c, "truncating inode %llu", u.bi_inum); - - bch2_fs_lazy_rw(c); - - /* - * XXX: need to truncate partial blocks too here - or ideally - * just switch units to bytes and that issue goes away - */ - ret = bch2_btree_delete_range_trans(trans, BTREE_ID_extents, - POS(u.bi_inum, round_up(u.bi_size, block_bytes(c))), - POS(u.bi_inum, U64_MAX), - NULL); - if (ret) { - bch_err(c, "error in fsck: error %i truncating inode", ret); - return ret; - } - - /* - * We truncated without our normal sector accounting hook, just - * make sure we recalculate it: - */ - u.bi_flags |= BCH_INODE_I_SECTORS_DIRTY; - - u.bi_flags &= ~BCH_INODE_I_SIZE_DIRTY; - do_update = true; - } - - if (u.bi_flags & BCH_INODE_I_SECTORS_DIRTY && - (!c->sb.clean || - fsck_err(c, "filesystem marked clean, but inode %llu has i_sectors dirty", - u.bi_inum))) { - s64 sectors; - - bch_verbose(c, "recounting sectors for inode %llu", - u.bi_inum); - - sectors = bch2_count_inode_sectors(trans, u.bi_inum); - if (sectors < 0) { - bch_err(c, "error in fsck: error %i recounting inode sectors", - (int) sectors); - return sectors; - } - - u.bi_sectors = sectors; - u.bi_flags &= ~BCH_INODE_I_SECTORS_DIRTY; - do_update = true; - } - - if (!S_ISDIR(u.bi_mode) && - u.bi_nlink && - !(u.bi_flags & BCH_INODE_BACKPTR_UNTRUSTED) && - (fsck_err_on(c->sb.version >= bcachefs_metadata_version_inode_backpointers, c, - "inode missing BCH_INODE_BACKPTR_UNTRUSTED flags") || - c->opts.version_upgrade)) { - u.bi_flags |= BCH_INODE_BACKPTR_UNTRUSTED; - do_update = true; - } - - if (do_update) { + if (fsck_err_on(bch2_inode_nlink_get(&u) != nlink, c, + "inode %llu has wrong i_nlink (type %u i_nlink %u, should be %u)", + u.bi_inum, mode_to_type(u.bi_mode), + bch2_inode_nlink_get(&u), nlink)) { struct bkey_inode_buf p; + if (nlink > 1) + u.bi_flags |= BCH_INODE_BACKPTR_UNTRUSTED; + + bch2_inode_nlink_set(&u, nlink); bch2_inode_pack(c, &p, &u); p.inode.k.p = iter->pos; @@ -1368,8 +1252,7 @@ static int check_inode(struct btree_trans *trans, BTREE_INSERT_LAZY_RW, (bch2_trans_update(trans, iter, &p.inode.k_i, 0), 0)); if (ret) - bch_err(c, "error in fsck: error %i " - "updating inode", ret); + bch_err(c, "error in fsck: error %i updating inode", ret); } fsck_err: return ret; @@ -1384,70 +1267,37 @@ static int bch2_gc_walk_inodes(struct bch_fs *c, struct btree_trans trans; struct btree_iter *iter; struct bkey_s_c k; - struct nlink *link, zero_links = { 0, 0 }; - struct genradix_iter nlinks_iter; - int ret = 0, ret2 = 0; - u64 nlinks_pos; + struct nlink *link; + int ret = 0; bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0); - iter = bch2_trans_get_iter(&trans, BTREE_ID_inodes, - POS(0, range_start), 0); - nlinks_iter = genradix_iter_init(links, 0); - - while ((k = bch2_btree_iter_peek(iter)).k && - !(ret2 = bkey_err(k)) && - iter->pos.offset < range_end) { -peek_nlinks: link = genradix_iter_peek(&nlinks_iter, links); - - if (!link && (!k.k || iter->pos.offset >= range_end)) + for_each_btree_key(&trans, iter, BTREE_ID_inodes, + POS(0, range_start), 0, k, ret) { + if (!k.k || k.k->p.offset >= range_end) break; - nlinks_pos = range_start + nlinks_iter.pos; + if (k.k->type != KEY_TYPE_inode) + continue; - if (link && nlinks_pos < iter->pos.offset) { - /* Should have been caught by dirents pass: */ - need_fsck_err_on(link->count, c, - "missing inode %llu (nlink %u)", - nlinks_pos, link->count); - genradix_iter_advance(&nlinks_iter, links); - goto peek_nlinks; - } + link = genradix_ptr(links, k.k->p.offset - range_start); + ret = check_inode_nlink(&trans, lostfound_inode, iter, + bkey_s_c_to_inode(k), link ? link->count : 0); + if (ret) + break; - if (!link || nlinks_pos > iter->pos.offset) - link = &zero_links; - - if (k.k && k.k->type == KEY_TYPE_inode) { - ret = check_inode(&trans, lostfound_inode, iter, - bkey_s_c_to_inode(k), link); - BUG_ON(ret == -EINTR); - if (ret) - break; - } else { - /* Should have been caught by dirents pass: */ - need_fsck_err_on(link->count, c, - "missing inode %llu (nlink %u)", - nlinks_pos, link->count); - } - - if (nlinks_pos == iter->pos.offset) - genradix_iter_advance(&nlinks_iter, links); - - bch2_btree_iter_advance(iter); - bch2_trans_cond_resched(&trans); } -fsck_err: bch2_trans_iter_put(&trans, iter); bch2_trans_exit(&trans); - if (ret2) - bch_err(c, "error in fsck: btree error %i while walking inodes", ret2); + if (ret) + bch_err(c, "error in fsck: btree error %i while walking inodes", ret); - return ret ?: ret2; + return ret; } noinline_for_stack -static int check_inode_nlinks(struct bch_fs *c, +static int check_nlinks(struct bch_fs *c, struct bch_inode_unpacked *lostfound_inode) { nlink_table links; @@ -1490,52 +1340,17 @@ int bch2_fsck_full(struct bch_fs *c) { struct bch_inode_unpacked root_inode, lostfound_inode; - return check_extents(c) ?: + return check_inodes(c, true) ?: + check_extents(c) ?: check_dirents(c) ?: check_xattrs(c) ?: check_root(c, &root_inode) ?: check_lostfound(c, &root_inode, &lostfound_inode) ?: check_directory_structure(c, &lostfound_inode) ?: - check_inode_nlinks(c, &lostfound_inode); -} - -int bch2_fsck_inode_nlink(struct bch_fs *c) -{ - struct bch_inode_unpacked root_inode, lostfound_inode; - - return check_root(c, &root_inode) ?: - check_lostfound(c, &root_inode, &lostfound_inode) ?: - check_inode_nlinks(c, &lostfound_inode); + check_nlinks(c, &lostfound_inode); } int bch2_fsck_walk_inodes_only(struct bch_fs *c) { - struct btree_trans trans; - struct btree_iter *iter; - struct bkey_s_c k; - struct bkey_s_c_inode inode; - int ret; - - bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0); - - for_each_btree_key(&trans, iter, BTREE_ID_inodes, POS_MIN, 0, k, ret) { - if (k.k->type != KEY_TYPE_inode) - continue; - - inode = bkey_s_c_to_inode(k); - - if (inode.v->bi_flags & - (BCH_INODE_I_SIZE_DIRTY| - BCH_INODE_I_SECTORS_DIRTY| - BCH_INODE_UNLINKED)) { - ret = check_inode(&trans, NULL, iter, inode, NULL); - if (ret) - break; - } - } - bch2_trans_iter_put(&trans, iter); - - BUG_ON(ret == -EINTR); - - return bch2_trans_exit(&trans) ?: ret; + return check_inodes(c, false); } diff --git a/libbcachefs/fsck.h b/libbcachefs/fsck.h index 9e4af02b..264f2706 100644 --- a/libbcachefs/fsck.h +++ b/libbcachefs/fsck.h @@ -3,7 +3,6 @@ #define _BCACHEFS_FSCK_H int bch2_fsck_full(struct bch_fs *); -int bch2_fsck_inode_nlink(struct bch_fs *); int bch2_fsck_walk_inodes_only(struct bch_fs *); #endif /* _BCACHEFS_FSCK_H */ diff --git a/libbcachefs/recovery.c b/libbcachefs/recovery.c index 86593e92..b5cc0e64 100644 --- a/libbcachefs/recovery.c +++ b/libbcachefs/recovery.c @@ -1005,6 +1005,13 @@ int bch2_fs_recovery(struct bch_fs *c) } + if (!c->sb.clean && + !(c->sb.features & (1 << BCH_FEATURE_atomic_nlink))) { + bch_info(c, "BCH_FEATURE_atomic_nlink not set and filesystem dirty, fsck required"); + c->opts.fsck = true; + c->opts.fix_errors = FSCK_OPT_YES; + } + if (!(c->sb.features & (1ULL << BCH_FEATURE_alloc_v2))) { bch_info(c, "alloc_v2 feature bit not set, fsck required"); c->opts.fsck = true; @@ -1017,6 +1024,13 @@ int bch2_fs_recovery(struct bch_fs *c) set_bit(BCH_FS_REBUILD_REPLICAS, &c->flags); } + if (c->sb.version < bcachefs_metadata_version_inode_backpointers) { + bch_info(c, "version prior to inode backpointers, upgrade and fsck required"); + c->opts.version_upgrade = true; + c->opts.fsck = true; + c->opts.fix_errors = FSCK_OPT_YES; + } + ret = bch2_blacklist_table_initialize(c); if (ret) { bch_err(c, "error initializing blacklist table"); @@ -1179,25 +1193,6 @@ use_clean: bch_verbose(c, "alloc write done"); } - if (!c->sb.clean) { - if (!(c->sb.features & (1 << BCH_FEATURE_atomic_nlink))) { - bch_info(c, "checking inode link counts"); - err = "error in recovery"; - ret = bch2_fsck_inode_nlink(c); - if (ret) - goto err; - bch_verbose(c, "check inodes done"); - - } else { - bch_verbose(c, "checking for deleted inodes"); - err = "error in recovery"; - ret = bch2_fsck_walk_inodes_only(c); - if (ret) - goto err; - bch_verbose(c, "check inodes done"); - } - } - if (c->opts.fsck) { bch_info(c, "starting fsck"); err = "error in fsck"; @@ -1205,6 +1200,13 @@ use_clean: if (ret) goto err; bch_verbose(c, "fsck done"); + } else if (!c->sb.clean) { + bch_verbose(c, "checking for deleted inodes"); + err = "error in recovery"; + ret = bch2_fsck_walk_inodes_only(c); + if (ret) + goto err; + bch_verbose(c, "check inodes done"); } if (enabled_qtypes(c)) { diff --git a/libbcachefs/super-io.c b/libbcachefs/super-io.c index 17936974..de8d49e3 100644 --- a/libbcachefs/super-io.c +++ b/libbcachefs/super-io.c @@ -50,8 +50,7 @@ static struct bch_sb_field *__bch2_sb_field_resize(struct bch_sb_handle *sb, unsigned old_u64s = f ? le32_to_cpu(f->u64s) : 0; unsigned sb_u64s = le32_to_cpu(sb->sb->u64s) + u64s - old_u64s; - BUG_ON(get_order(__vstruct_bytes(struct bch_sb, sb_u64s)) > - sb->page_order); + BUG_ON(__vstruct_bytes(struct bch_sb, sb_u64s) > sb->buffer_size); if (!f && !u64s) { /* nothing to do: */ @@ -101,18 +100,23 @@ void bch2_free_super(struct bch_sb_handle *sb) if (!IS_ERR_OR_NULL(sb->bdev)) blkdev_put(sb->bdev, sb->mode); - free_pages((unsigned long) sb->sb, sb->page_order); + kfree(sb->sb); memset(sb, 0, sizeof(*sb)); } int bch2_sb_realloc(struct bch_sb_handle *sb, unsigned u64s) { size_t new_bytes = __vstruct_bytes(struct bch_sb, u64s); - unsigned order = get_order(new_bytes); + size_t new_buffer_size; struct bch_sb *new_sb; struct bio *bio; - if (sb->sb && sb->page_order >= order) + if (sb->bdev) + new_bytes = max_t(size_t, new_bytes, bdev_logical_block_size(sb->bdev)); + + new_buffer_size = roundup_pow_of_two(new_bytes); + + if (sb->sb && sb->buffer_size >= new_buffer_size) return 0; if (sb->have_layout) { @@ -127,14 +131,15 @@ int bch2_sb_realloc(struct bch_sb_handle *sb, unsigned u64s) } } - if (sb->page_order >= order && sb->sb) + if (sb->buffer_size >= new_buffer_size && sb->sb) return 0; if (dynamic_fault("bcachefs:add:super_realloc")) return -ENOMEM; if (sb->have_bio) { - bio = bio_kmalloc(GFP_KERNEL, 1 << order); + bio = bio_kmalloc(GFP_KERNEL, + DIV_ROUND_UP(new_buffer_size, PAGE_SIZE)); if (!bio) return -ENOMEM; @@ -143,17 +148,12 @@ int bch2_sb_realloc(struct bch_sb_handle *sb, unsigned u64s) sb->bio = bio; } - new_sb = (void *) __get_free_pages(GFP_NOFS|__GFP_ZERO, order); + new_sb = krealloc(sb->sb, new_buffer_size, GFP_NOFS|__GFP_ZERO); if (!new_sb) return -ENOMEM; - if (sb->sb) - memcpy(new_sb, sb->sb, PAGE_SIZE << sb->page_order); - - free_pages((unsigned long) sb->sb, sb->page_order); sb->sb = new_sb; - - sb->page_order = order; + sb->buffer_size = new_buffer_size; return 0; } @@ -475,7 +475,7 @@ reread: bio_set_dev(sb->bio, sb->bdev); sb->bio->bi_iter.bi_sector = offset; bio_set_op_attrs(sb->bio, REQ_OP_READ, REQ_SYNC|REQ_META); - bch2_bio_map(sb->bio, sb->sb, PAGE_SIZE << sb->page_order); + bch2_bio_map(sb->bio, sb->sb, sb->buffer_size); if (submit_bio_wait(sb->bio)) return "IO error"; @@ -492,7 +492,7 @@ reread: if (bytes > 512 << sb->sb->layout.sb_max_size_bits) return "Bad superblock: too big"; - if (get_order(bytes) > sb->page_order) { + if (bytes > sb->buffer_size) { if (bch2_sb_realloc(sb, le32_to_cpu(sb->sb->u64s))) return "cannot allocate memory"; goto reread; @@ -698,8 +698,12 @@ int bch2_write_super(struct bch_fs *c) const char *err; struct bch_devs_mask sb_written; bool wrote, can_mount_without_written, can_mount_with_written; + unsigned degraded_flags = BCH_FORCE_IF_DEGRADED; int ret = 0; + if (c->opts.very_degraded) + degraded_flags |= BCH_FORCE_IF_LOST; + lockdep_assert_held(&c->sb_lock); closure_init_stack(cl); @@ -770,13 +774,13 @@ int bch2_write_super(struct bch_fs *c) nr_wrote = dev_mask_nr(&sb_written); can_mount_with_written = - bch2_have_enough_devs(c, sb_written, BCH_FORCE_IF_DEGRADED, false); + bch2_have_enough_devs(c, sb_written, degraded_flags, false); for (i = 0; i < ARRAY_SIZE(sb_written.d); i++) sb_written.d[i] = ~sb_written.d[i]; can_mount_without_written = - bch2_have_enough_devs(c, sb_written, BCH_FORCE_IF_DEGRADED, false); + bch2_have_enough_devs(c, sb_written, degraded_flags, false); /* * If we would be able to mount _without_ the devices we successfully diff --git a/libbcachefs/super.c b/libbcachefs/super.c index 670e9cdc..b2a2614b 100644 --- a/libbcachefs/super.c +++ b/libbcachefs/super.c @@ -516,8 +516,7 @@ static void __bch2_fs_free(struct bch_fs *c) if (c->wq) destroy_workqueue(c->wq); - free_pages((unsigned long) c->disk_sb.sb, - c->disk_sb.page_order); + bch2_free_super(&c->disk_sb); kvpfree(c, sizeof(*c)); module_put(THIS_MODULE); } diff --git a/libbcachefs/super_types.h b/libbcachefs/super_types.h index 069973a3..96023f37 100644 --- a/libbcachefs/super_types.h +++ b/libbcachefs/super_types.h @@ -6,7 +6,7 @@ struct bch_sb_handle { struct bch_sb *sb; struct block_device *bdev; struct bio *bio; - unsigned page_order; + size_t buffer_size; fmode_t mode; unsigned have_layout:1; unsigned have_bio:1; diff --git a/libbcachefs/util.c b/libbcachefs/util.c index 2709163e..e3ad26e2 100644 --- a/libbcachefs/util.c +++ b/libbcachefs/util.c @@ -154,7 +154,7 @@ void bch2_flags_to_text(struct printbuf *out, u64 bch2_read_flag_list(char *opt, const char * const list[]) { u64 ret = 0; - char *p, *s, *d = kstrndup(opt, PAGE_SIZE - 1, GFP_KERNEL); + char *p, *s, *d = kstrdup(opt, GFP_KERNEL); if (!d) return -ENOMEM;