// SPDX-License-Identifier: GPL-2.0
/*
* Assorted bcachefs debug code
*
* Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
* Copyright 2012 Google, Inc.
*/
#include "bcachefs.h"
#include "bkey_methods.h"
#include "btree_cache.h"
#include "btree_io.h"
#include "btree_iter.h"
#include "btree_locking.h"
#include "btree_update.h"
#include "btree_update_interior.h"
#include "buckets.h"
#include "debug.h"
#include "error.h"
#include "extents.h"
#include "fsck.h"
#include "inode.h"
#include "super.h"
#include <linux/console.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/seq_file.h>
static struct dentry *bch_debug;
static bool bch2_btree_verify_replica(struct bch_fs *c, struct btree *b,
struct extent_ptr_decoded pick)
{
struct btree *v = c->verify_data;
struct btree_node *n_ondisk = c->verify_ondisk;
struct btree_node *n_sorted = c->verify_data->data;
struct bset *sorted, *inmemory = &b->data->keys;
struct bio *bio;
bool failed = false, saw_error = false;
struct bch_dev *ca = bch2_dev_get_ioref(c, pick.ptr.dev, READ);
if (!ca)
return false;
bio = bio_alloc_bioset(ca->disk_sb.bdev,
buf_pages(n_sorted, btree_buf_bytes(b)),
REQ_OP_READ|REQ_META,
GFP_NOFS,
&c->btree_bio);
bio->bi_iter.bi_sector = pick.ptr.offset;
bch2_bio_map(bio, n_sorted, btree_buf_bytes(b));
submit_bio_wait(bio);
bio_put(bio);
percpu_ref_put(&ca->io_ref);
memcpy(n_ondisk, n_sorted, btree_buf_bytes(b));
v->written = 0;
if (bch2_btree_node_read_done(c, ca, v, false, &saw_error) || saw_error)
return false;
n_sorted = c->verify_data->data;
sorted = &n_sorted->keys;
if (inmemory->u64s != sorted->u64s ||
memcmp(inmemory->start,
sorted->start,
vstruct_end(inmemory) - (void *) inmemory->start)) {
unsigned offset = 0, sectors;
struct bset *i;
unsigned j;
console_lock();
printk(KERN_ERR "*** in memory:\n");
bch2_dump_bset(c, b, inmemory, 0);
printk(KERN_ERR "*** read back in:\n");
bch2_dump_bset(c, v, sorted, 0);
while (offset < v->written) {
if (!offset) {
i = &n_ondisk->keys;
sectors = vstruct_blocks(n_ondisk, c->block_bits) <<
c->block_bits;
} else {
struct btree_node_entry *bne =
(void *) n_ondisk + (offset << 9);
i = &bne->keys;
sectors = vstruct_blocks(bne, c->block_bits) <<
c->block_bits;
}
printk(KERN_ERR "*** on disk block %u:\n", offset);
bch2_dump_bset(c, b, i, offset);
offset += sectors;
}
for (j = 0; j < le16_to_cpu(inmemory->u64s); j++)
if (inmemory->_data[j] != sorted->_data[j])
break;
console_unlock();
bch_err(c, "verify failed at key %u", j);
failed = true;
}
if (v->written != b->written) {
bch_err(c, "written wrong: expected %u, got %u",
b->written, v->written);
failed = true;
}
return failed;
}
void __bch2_btree_verify(struct bch_fs *c, struct btree *b)
{
struct bkey_ptrs_c ptrs;
struct extent_ptr_decoded p;
const union bch_extent_entry *entry;
struct btree *v;
struct bset *inmemory = &b->data->keys;
struct bkey_packed *k;
bool failed = false;
if (c->opts.nochanges)
return;
bch2_btree_node_io_lock(b);
mutex_lock(&c->verify_lock);
if (!c->verify_ondisk) {
c->verify_ondisk = kvmalloc(btree_buf_bytes(b), GFP_KERNEL);
if (!c->verify_ondisk)
goto out;
}
if (!c->verify_data) {
c->verify_data = __bch2_btree_node_mem_alloc(c);
if (!c->verify_data)
goto out;
list_del_init(&c->verify_data->list);
}
BUG_ON(b->nsets != 1);
for (k = inmemory->start; k != vstruct_last(inmemory); k = bkey_p_next(k))
if (k->type == KEY_TYPE_btree_ptr_v2)
((struct bch_btree_ptr_v2 *) bkeyp_val(&b->format, k))->mem_ptr = 0;
v = c->verify_data;
bkey_copy(&v->key, &b->key);
v->c.level = b->c.level;
v->c.btree_id = b->c.btree_id;
bch2_btree_keys_init(v);
ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(&b->key));
bkey_for_each_ptr_decode(&b->key.k, ptrs, p, entry)
failed |= bch2_btree_verify_replica(c, b, p);
if (failed) {
struct printbuf buf = PRINTBUF;
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
bch2_fs_fatal_error(c, ": btree node verify fail