951 lines · 21.4 KB · C
RawDownload
// 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 failed for: %s\n", buf.buf);		printbuf_exit(&buf);	}out:	mutex_unlock(&c->verify_lock);	bch2_btree_node_io_unlock(b);} void bch2_btree_node_ondisk_to_text(struct printbuf *out, struct bch_fs *c,				    const struct btree *b){	struct btree_node *n_ondisk = NULL;	struct extent_ptr_decoded pick;	struct bch_dev *ca;	struct bio *bio = NULL;	unsigned offset = 0;	int ret; 	if (bch2_bkey_pick_read_device(c, bkey_i_to_s_c(&b->key), NULL, &pick) <= 0) {		prt_printf(out, "error getting device to read from: invalid device\n");		return;	} 	ca = bch2_dev_get_ioref(c, pick.ptr.dev, READ);	if (!ca) {		prt_printf(out, "error getting device to read from: not online\n");		return;	} 	n_ondisk = kvmalloc(btree_buf_bytes(b), GFP_KERNEL);	if (!n_ondisk) {		prt_printf(out, "memory allocation failure\n");		goto out;	} 	bio = bio_alloc_bioset(ca->disk_sb.bdev,			       buf_pages(n_ondisk, 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_ondisk, btree_buf_bytes(b)); 	ret = submit_bio_wait(bio);	if (ret) {		prt_printf(out, "IO error reading btree node: %s\n", bch2_err_str(ret));		goto out;	} 	while (offset < btree_sectors(c)) {		struct bset *i;		struct nonce nonce;		struct bch_csum csum;		struct bkey_packed *k;		unsigned sectors; 		if (!offset) {			i = &n_ondisk->keys; 			if (!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i))) {				prt_printf(out, "unknown checksum type at offset %u: %llu\n",					   offset, BSET_CSUM_TYPE(i));				goto out;			} 			nonce = btree_nonce(i, offset << 9);			csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, n_ondisk); 			if (bch2_crc_cmp(csum, n_ondisk->csum)) {				prt_printf(out, "invalid checksum\n");				goto out;			} 			bset_encrypt(c, i, offset << 9); 			sectors = vstruct_sectors(n_ondisk, c->block_bits);		} else {			struct btree_node_entry *bne = (void *) n_ondisk + (offset << 9); 			i = &bne->keys; 			if (i->seq != n_ondisk->keys.seq)				break; 			if (!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i))) {				prt_printf(out, "unknown checksum type at offset %u: %llu\n",					   offset, BSET_CSUM_TYPE(i));				goto out;			} 			nonce = btree_nonce(i, offset << 9);			csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne); 			if (bch2_crc_cmp(csum, bne->csum)) {				prt_printf(out, "invalid checksum");				goto out;			} 			bset_encrypt(c, i, offset << 9); 			sectors = vstruct_sectors(bne, c->block_bits);		} 		prt_printf(out, "  offset %u version %u, journal seq %llu\n",			   offset,			   le16_to_cpu(i->version),			   le64_to_cpu(i->journal_seq));		offset += sectors; 		printbuf_indent_add(out, 4); 		for (k = i->start; k != vstruct_last(i); k = bkey_p_next(k)) {			struct bkey u; 			bch2_bkey_val_to_text(out, c, bkey_disassemble(b, k, &u));			prt_newline(out);		} 		printbuf_indent_sub(out, 4);	}out:	if (bio)		bio_put(bio);	kvfree(n_ondisk);	percpu_ref_put(&ca->io_ref);} #ifdef CONFIG_DEBUG_FS /* XXX: bch_fs refcounting */ struct dump_iter {	struct bch_fs		*c;	enum btree_id		id;	struct bpos		from;	struct bpos		prev_node;	u64			iter; 	struct printbuf		buf; 	char __user		*ubuf;	/* destination user buffer */	size_t			size;	/* size of requested read */	ssize_t			ret;	/* bytes read so far */}; static ssize_t flush_buf(struct dump_iter *i){	if (i->buf.pos) {		size_t bytes = min_t(size_t, i->buf.pos, i->size);		int copied = bytes - copy_to_user(i->ubuf, i->buf.buf, bytes); 		i->ret	 += copied;		i->ubuf	 += copied;		i->size	 -= copied;		i->buf.pos -= copied;		memmove(i->buf.buf, i->buf.buf + copied, i->buf.pos); 		if (copied != bytes)			return -EFAULT;	} 	return i->size ? 0 : i->ret;} static int bch2_dump_open(struct inode *inode, struct file *file){	struct btree_debug *bd = inode->i_private;	struct dump_iter *i; 	i = kzalloc(sizeof(struct dump_iter), GFP_KERNEL);	if (!i)		return -ENOMEM; 	file->private_data = i;	i->from = POS_MIN;	i->iter	= 0;	i->c	= container_of(bd, struct bch_fs, btree_debug[bd->id]);	i->id	= bd->id;	i->buf	= PRINTBUF; 	return 0;} static int bch2_dump_release(struct inode *inode, struct file *file){	struct dump_iter *i = file->private_data; 	printbuf_exit(&i->buf);	kfree(i);	return 0;} static ssize_t bch2_read_btree(struct file *file, char __user *buf,			       size_t size, loff_t *ppos){	struct dump_iter *i = file->private_data; 	i->ubuf = buf;	i->size	= size;	i->ret	= 0; 	return flush_buf(i) ?:		bch2_trans_run(i->c,			for_each_btree_key(trans, iter, i->id, i->from,					   BTREE_ITER_prefetch|					   BTREE_ITER_all_snapshots, k, ({				bch2_bkey_val_to_text(&i->buf, i->c, k);				prt_newline(&i->buf);				bch2_trans_unlock(trans);				i->from = bpos_successor(iter.pos);				flush_buf(i);			}))) ?:		i->ret;} static const struct file_operations btree_debug_ops = {	.owner		= THIS_MODULE,	.open		= bch2_dump_open,	.release	= bch2_dump_release,	.read		= bch2_read_btree,}; static ssize_t bch2_read_btree_formats(struct file *file, char __user *buf,				       size_t size, loff_t *ppos){	struct dump_iter *i = file->private_data; 	i->ubuf = buf;	i->size	= size;	i->ret	= 0; 	ssize_t ret = flush_buf(i);	if (ret)		return ret; 	if (bpos_eq(SPOS_MAX, i->from))		return i->ret; 	return bch2_trans_run(i->c,		for_each_btree_node(trans, iter, i->id, i->from, 0, b, ({			bch2_btree_node_to_text(&i->buf, i->c, b);			i->from = !bpos_eq(SPOS_MAX, b->key.k.p)				? bpos_successor(b->key.k.p)				: b->key.k.p; 			drop_locks_do(trans, flush_buf(i));		}))) ?: i->ret;} static const struct file_operations btree_format_debug_ops = {	.owner		= THIS_MODULE,	.open		= bch2_dump_open,	.release	= bch2_dump_release,	.read		= bch2_read_btree_formats,}; static ssize_t bch2_read_bfloat_failed(struct file *file, char __user *buf,				       size_t size, loff_t *ppos){	struct dump_iter *i = file->private_data; 	i->ubuf = buf;	i->size	= size;	i->ret	= 0; 	return flush_buf(i) ?:		bch2_trans_run(i->c,			for_each_btree_key(trans, iter, i->id, i->from,					   BTREE_ITER_prefetch|					   BTREE_ITER_all_snapshots, k, ({				struct btree_path_level *l =					&btree_iter_path(trans, &iter)->l[0];				struct bkey_packed *_k =					bch2_btree_node_iter_peek(&l->iter, l->b); 				if (bpos_gt(l->b->key.k.p, i->prev_node)) {					bch2_btree_node_to_text(&i->buf, i->c, l->b);					i->prev_node = l->b->key.k.p;				} 				bch2_bfloat_to_text(&i->buf, l->b, _k);				bch2_trans_unlock(trans);				i->from = bpos_successor(iter.pos);				flush_buf(i);			}))) ?:		i->ret;} static const struct file_operations bfloat_failed_debug_ops = {	.owner		= THIS_MODULE,	.open		= bch2_dump_open,	.release	= bch2_dump_release,	.read		= bch2_read_bfloat_failed,}; static void bch2_cached_btree_node_to_text(struct printbuf *out, struct bch_fs *c,					   struct btree *b){	if (!out->nr_tabstops)		printbuf_tabstop_push(out, 32); 	prt_printf(out, "%px btree=%s l=%u\n", b, bch2_btree_id_str(b->c.btree_id), b->c.level); 	printbuf_indent_add(out, 2); 	bch2_bkey_val_to_text(out, c, bkey_i_to_s_c(&b->key));	prt_newline(out); 	prt_printf(out, "flags:\t");	prt_bitflags(out, bch2_btree_node_flags, b->flags);	prt_newline(out); 	prt_printf(out, "pcpu read locks:\t%u\n",	b->c.lock.readers != NULL);	prt_printf(out, "written:\t%u\n",		b->written);	prt_printf(out, "writes blocked:\t%u\n",	!list_empty_careful(&b->write_blocked));	prt_printf(out, "will make reachable:\t%lx\n",	b->will_make_reachable); 	prt_printf(out, "journal pin %px:\t%llu\n",		   &b->writes[0].journal, b->writes[0].journal.seq);	prt_printf(out, "journal pin %px:\t%llu\n",		   &b->writes[1].journal, b->writes[1].journal.seq); 	printbuf_indent_sub(out, 2);} static ssize_t bch2_cached_btree_nodes_read(struct file *file, char __user *buf,					    size_t size, loff_t *ppos)

Lines 1–500 of 951.