Commit | Line | Data |
---|---|---|
1c6fdbd8 KO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Assorted bcachefs debug code | |
4 | * | |
5 | * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com> | |
6 | * Copyright 2012 Google, Inc. | |
7 | */ | |
8 | ||
9 | #include "bcachefs.h" | |
10 | #include "bkey_methods.h" | |
11 | #include "btree_cache.h" | |
12 | #include "btree_io.h" | |
13 | #include "btree_iter.h" | |
96d994b3 | 14 | #include "btree_locking.h" |
1c6fdbd8 | 15 | #include "btree_update.h" |
9fb3036f | 16 | #include "btree_update_interior.h" |
1c6fdbd8 KO |
17 | #include "buckets.h" |
18 | #include "debug.h" | |
19 | #include "error.h" | |
20 | #include "extents.h" | |
21 | #include "fsck.h" | |
22 | #include "inode.h" | |
1c6fdbd8 KO |
23 | #include "super.h" |
24 | ||
25 | #include <linux/console.h> | |
26 | #include <linux/debugfs.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/random.h> | |
29 | #include <linux/seq_file.h> | |
30 | ||
31 | static struct dentry *bch_debug; | |
32 | ||
6adaac0b KO |
33 | static bool bch2_btree_verify_replica(struct bch_fs *c, struct btree *b, |
34 | struct extent_ptr_decoded pick) | |
1c6fdbd8 KO |
35 | { |
36 | struct btree *v = c->verify_data; | |
6adaac0b KO |
37 | struct btree_node *n_ondisk = c->verify_ondisk; |
38 | struct btree_node *n_sorted = c->verify_data->data; | |
39 | struct bset *sorted, *inmemory = &b->data->keys; | |
1c6fdbd8 | 40 | struct bio *bio; |
494dcc57 | 41 | bool failed = false, saw_error = false; |
1c6fdbd8 | 42 | |
2c91ab72 | 43 | struct bch_dev *ca = bch2_dev_get_ioref(c, pick.ptr.dev, READ); |
91ffdecf | 44 | if (!ca) |
6adaac0b | 45 | return false; |
1c6fdbd8 KO |
46 | |
47 | bio = bio_alloc_bioset(ca->disk_sb.bdev, | |
ec4edd7b | 48 | buf_pages(n_sorted, btree_buf_bytes(b)), |
1c6fdbd8 | 49 | REQ_OP_READ|REQ_META, |
19c304be | 50 | GFP_NOFS, |
1c6fdbd8 KO |
51 | &c->btree_bio); |
52 | bio->bi_iter.bi_sector = pick.ptr.offset; | |
ec4edd7b | 53 | bch2_bio_map(bio, n_sorted, btree_buf_bytes(b)); |
1c6fdbd8 KO |
54 | |
55 | submit_bio_wait(bio); | |
56 | ||
57 | bio_put(bio); | |
58 | percpu_ref_put(&ca->io_ref); | |
59 | ||
ec4edd7b | 60 | memcpy(n_ondisk, n_sorted, btree_buf_bytes(b)); |
1c6fdbd8 | 61 | |
6adaac0b | 62 | v->written = 0; |
494dcc57 | 63 | if (bch2_btree_node_read_done(c, ca, v, false, &saw_error) || saw_error) |
6adaac0b | 64 | return false; |
1c6fdbd8 KO |
65 | |
66 | n_sorted = c->verify_data->data; | |
67 | sorted = &n_sorted->keys; | |
1c6fdbd8 KO |
68 | |
69 | if (inmemory->u64s != sorted->u64s || | |
70 | memcmp(inmemory->start, | |
71 | sorted->start, | |
72 | vstruct_end(inmemory) - (void *) inmemory->start)) { | |
73 | unsigned offset = 0, sectors; | |
74 | struct bset *i; | |
75 | unsigned j; | |
76 | ||
77 | console_lock(); | |
78 | ||
79 | printk(KERN_ERR "*** in memory:\n"); | |
a34782a0 | 80 | bch2_dump_bset(c, b, inmemory, 0); |
1c6fdbd8 KO |
81 | |
82 | printk(KERN_ERR "*** read back in:\n"); | |
a34782a0 | 83 | bch2_dump_bset(c, v, sorted, 0); |
1c6fdbd8 | 84 | |
6adaac0b KO |
85 | while (offset < v->written) { |
86 | if (!offset) { | |
1c6fdbd8 KO |
87 | i = &n_ondisk->keys; |
88 | sectors = vstruct_blocks(n_ondisk, c->block_bits) << | |
89 | c->block_bits; | |
90 | } else { | |
91 | struct btree_node_entry *bne = | |
92 | (void *) n_ondisk + (offset << 9); | |
93 | i = &bne->keys; | |
94 | ||
95 | sectors = vstruct_blocks(bne, c->block_bits) << | |
96 | c->block_bits; | |
97 | } | |
98 | ||
99 | printk(KERN_ERR "*** on disk block %u:\n", offset); | |
a34782a0 | 100 | bch2_dump_bset(c, b, i, offset); |
1c6fdbd8 KO |
101 | |
102 | offset += sectors; | |
103 | } | |
104 | ||
1c6fdbd8 KO |
105 | for (j = 0; j < le16_to_cpu(inmemory->u64s); j++) |
106 | if (inmemory->_data[j] != sorted->_data[j]) | |
107 | break; | |
108 | ||
1c6fdbd8 | 109 | console_unlock(); |
6adaac0b KO |
110 | bch_err(c, "verify failed at key %u", j); |
111 | ||
112 | failed = true; | |
113 | } | |
114 | ||
115 | if (v->written != b->written) { | |
116 | bch_err(c, "written wrong: expected %u, got %u", | |
117 | b->written, v->written); | |
118 | failed = true; | |
119 | } | |
120 | ||
121 | return failed; | |
122 | } | |
123 | ||
124 | void __bch2_btree_verify(struct bch_fs *c, struct btree *b) | |
125 | { | |
126 | struct bkey_ptrs_c ptrs; | |
127 | struct extent_ptr_decoded p; | |
128 | const union bch_extent_entry *entry; | |
129 | struct btree *v; | |
130 | struct bset *inmemory = &b->data->keys; | |
131 | struct bkey_packed *k; | |
132 | bool failed = false; | |
133 | ||
134 | if (c->opts.nochanges) | |
135 | return; | |
136 | ||
19d54324 | 137 | bch2_btree_node_io_lock(b); |
6adaac0b KO |
138 | mutex_lock(&c->verify_lock); |
139 | ||
140 | if (!c->verify_ondisk) { | |
cb6fc943 | 141 | c->verify_ondisk = kvmalloc(btree_buf_bytes(b), GFP_KERNEL); |
6adaac0b KO |
142 | if (!c->verify_ondisk) |
143 | goto out; | |
144 | } | |
145 | ||
146 | if (!c->verify_data) { | |
147 | c->verify_data = __bch2_btree_node_mem_alloc(c); | |
148 | if (!c->verify_data) | |
149 | goto out; | |
150 | ||
151 | list_del_init(&c->verify_data->list); | |
152 | } | |
153 | ||
154 | BUG_ON(b->nsets != 1); | |
155 | ||
ac2ccddc | 156 | for (k = inmemory->start; k != vstruct_last(inmemory); k = bkey_p_next(k)) |
96dea3d5 KO |
157 | if (k->type == KEY_TYPE_btree_ptr_v2) |
158 | ((struct bch_btree_ptr_v2 *) bkeyp_val(&b->format, k))->mem_ptr = 0; | |
6adaac0b KO |
159 | |
160 | v = c->verify_data; | |
161 | bkey_copy(&v->key, &b->key); | |
162 | v->c.level = b->c.level; | |
163 | v->c.btree_id = b->c.btree_id; | |
164 | bch2_btree_keys_init(v); | |
165 | ||
166 | ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(&b->key)); | |
167 | bkey_for_each_ptr_decode(&b->key.k, ptrs, p, entry) | |
168 | failed |= bch2_btree_verify_replica(c, b, p); | |
169 | ||
170 | if (failed) { | |
fa8e94fa | 171 | struct printbuf buf = PRINTBUF; |
6adaac0b | 172 | |
fa8e94fa | 173 | bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key)); |
3ed94062 | 174 | bch2_fs_fatal_error(c, ": btree node verify failed for: %s\n", buf.buf); |
fa8e94fa | 175 | printbuf_exit(&buf); |
1c6fdbd8 KO |
176 | } |
177 | out: | |
178 | mutex_unlock(&c->verify_lock); | |
19d54324 | 179 | bch2_btree_node_io_unlock(b); |
1c6fdbd8 KO |
180 | } |
181 | ||
b65499b7 KO |
182 | void bch2_btree_node_ondisk_to_text(struct printbuf *out, struct bch_fs *c, |
183 | const struct btree *b) | |
184 | { | |
185 | struct btree_node *n_ondisk = NULL; | |
186 | struct extent_ptr_decoded pick; | |
187 | struct bch_dev *ca; | |
188 | struct bio *bio = NULL; | |
189 | unsigned offset = 0; | |
190 | int ret; | |
191 | ||
192 | if (bch2_bkey_pick_read_device(c, bkey_i_to_s_c(&b->key), NULL, &pick) <= 0) { | |
193 | prt_printf(out, "error getting device to read from: invalid device\n"); | |
194 | return; | |
195 | } | |
196 | ||
2c91ab72 | 197 | ca = bch2_dev_get_ioref(c, pick.ptr.dev, READ); |
91ffdecf | 198 | if (!ca) { |
b65499b7 KO |
199 | prt_printf(out, "error getting device to read from: not online\n"); |
200 | return; | |
201 | } | |
202 | ||
cb6fc943 | 203 | n_ondisk = kvmalloc(btree_buf_bytes(b), GFP_KERNEL); |
b65499b7 KO |
204 | if (!n_ondisk) { |
205 | prt_printf(out, "memory allocation failure\n"); | |
206 | goto out; | |
207 | } | |
208 | ||
209 | bio = bio_alloc_bioset(ca->disk_sb.bdev, | |
ec4edd7b | 210 | buf_pages(n_ondisk, btree_buf_bytes(b)), |
b65499b7 | 211 | REQ_OP_READ|REQ_META, |
19c304be | 212 | GFP_NOFS, |
b65499b7 KO |
213 | &c->btree_bio); |
214 | bio->bi_iter.bi_sector = pick.ptr.offset; | |
ec4edd7b | 215 | bch2_bio_map(bio, n_ondisk, btree_buf_bytes(b)); |
b65499b7 KO |
216 | |
217 | ret = submit_bio_wait(bio); | |
218 | if (ret) { | |
219 | prt_printf(out, "IO error reading btree node: %s\n", bch2_err_str(ret)); | |
220 | goto out; | |
221 | } | |
222 | ||
223 | while (offset < btree_sectors(c)) { | |
224 | struct bset *i; | |
225 | struct nonce nonce; | |
226 | struct bch_csum csum; | |
227 | struct bkey_packed *k; | |
228 | unsigned sectors; | |
229 | ||
230 | if (!offset) { | |
231 | i = &n_ondisk->keys; | |
232 | ||
233 | if (!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i))) { | |
234 | prt_printf(out, "unknown checksum type at offset %u: %llu\n", | |
235 | offset, BSET_CSUM_TYPE(i)); | |
236 | goto out; | |
237 | } | |
238 | ||
239 | nonce = btree_nonce(i, offset << 9); | |
240 | csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, n_ondisk); | |
241 | ||
242 | if (bch2_crc_cmp(csum, n_ondisk->csum)) { | |
243 | prt_printf(out, "invalid checksum\n"); | |
244 | goto out; | |
245 | } | |
246 | ||
247 | bset_encrypt(c, i, offset << 9); | |
248 | ||
249 | sectors = vstruct_sectors(n_ondisk, c->block_bits); | |
250 | } else { | |
251 | struct btree_node_entry *bne = (void *) n_ondisk + (offset << 9); | |
252 | ||
253 | i = &bne->keys; | |
254 | ||
255 | if (i->seq != n_ondisk->keys.seq) | |
256 | break; | |
257 | ||
258 | if (!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i))) { | |
259 | prt_printf(out, "unknown checksum type at offset %u: %llu\n", | |
260 | offset, BSET_CSUM_TYPE(i)); | |
261 | goto out; | |
262 | } | |
263 | ||
264 | nonce = btree_nonce(i, offset << 9); | |
265 | csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne); | |
266 | ||
267 | if (bch2_crc_cmp(csum, bne->csum)) { | |
268 | prt_printf(out, "invalid checksum"); | |
269 | goto out; | |
270 | } | |
271 | ||
272 | bset_encrypt(c, i, offset << 9); | |
273 | ||
274 | sectors = vstruct_sectors(bne, c->block_bits); | |
275 | } | |
276 | ||
277 | prt_printf(out, " offset %u version %u, journal seq %llu\n", | |
278 | offset, | |
279 | le16_to_cpu(i->version), | |
280 | le64_to_cpu(i->journal_seq)); | |
281 | offset += sectors; | |
282 | ||
283 | printbuf_indent_add(out, 4); | |
284 | ||
285 | for (k = i->start; k != vstruct_last(i); k = bkey_p_next(k)) { | |
286 | struct bkey u; | |
287 | ||
288 | bch2_bkey_val_to_text(out, c, bkey_disassemble(b, k, &u)); | |
289 | prt_newline(out); | |
290 | } | |
291 | ||
292 | printbuf_indent_sub(out, 4); | |
293 | } | |
294 | out: | |
295 | if (bio) | |
296 | bio_put(bio); | |
cb6fc943 | 297 | kvfree(n_ondisk); |
b65499b7 KO |
298 | percpu_ref_put(&ca->io_ref); |
299 | } | |
300 | ||
1c6fdbd8 KO |
301 | #ifdef CONFIG_DEBUG_FS |
302 | ||
303 | /* XXX: bch_fs refcounting */ | |
304 | ||
305 | struct dump_iter { | |
75ef2c59 | 306 | struct bch_fs *c; |
1c6fdbd8 | 307 | enum btree_id id; |
75ef2c59 | 308 | struct bpos from; |
d7228ecc | 309 | struct bpos prev_node; |
75ef2c59 | 310 | u64 iter; |
1c6fdbd8 | 311 | |
fa8e94fa | 312 | struct printbuf buf; |
1c6fdbd8 KO |
313 | |
314 | char __user *ubuf; /* destination user buffer */ | |
315 | size_t size; /* size of requested read */ | |
316 | ssize_t ret; /* bytes read so far */ | |
317 | }; | |
318 | ||
9375fbc2 | 319 | static ssize_t flush_buf(struct dump_iter *i) |
1c6fdbd8 | 320 | { |
fa8e94fa KO |
321 | if (i->buf.pos) { |
322 | size_t bytes = min_t(size_t, i->buf.pos, i->size); | |
f7f6943a | 323 | int copied = bytes - copy_to_user(i->ubuf, i->buf.buf, bytes); |
1c6fdbd8 | 324 | |
f7f6943a KO |
325 | i->ret += copied; |
326 | i->ubuf += copied; | |
327 | i->size -= copied; | |
328 | i->buf.pos -= copied; | |
329 | memmove(i->buf.buf, i->buf.buf + copied, i->buf.pos); | |
1c6fdbd8 | 330 | |
f7f6943a KO |
331 | if (copied != bytes) |
332 | return -EFAULT; | |
1c6fdbd8 KO |
333 | } |
334 | ||
9375fbc2 | 335 | return i->size ? 0 : i->ret; |
1c6fdbd8 KO |
336 | } |
337 | ||
338 | static int bch2_dump_open(struct inode *inode, struct file *file) | |
339 | { | |
340 | struct btree_debug *bd = inode->i_private; | |
341 | struct dump_iter *i; | |
342 | ||
343 | i = kzalloc(sizeof(struct dump_iter), GFP_KERNEL); | |
344 | if (!i) | |
345 | return -ENOMEM; | |
346 | ||
347 | file->private_data = i; | |
348 | i->from = POS_MIN; | |
75ef2c59 | 349 | i->iter = 0; |
1c6fdbd8 KO |
350 | i->c = container_of(bd, struct bch_fs, btree_debug[bd->id]); |
351 | i->id = bd->id; | |
fa8e94fa | 352 | i->buf = PRINTBUF; |
1c6fdbd8 KO |
353 | |
354 | return 0; | |
355 | } | |
356 | ||
357 | static int bch2_dump_release(struct inode *inode, struct file *file) | |
358 | { | |
fa8e94fa KO |
359 | struct dump_iter *i = file->private_data; |
360 | ||
361 | printbuf_exit(&i->buf); | |
362 | kfree(i); | |
1c6fdbd8 KO |
363 | return 0; |
364 | } | |
365 | ||
366 | static ssize_t bch2_read_btree(struct file *file, char __user *buf, | |
367 | size_t size, loff_t *ppos) | |
368 | { | |
369 | struct dump_iter *i = file->private_data; | |
1c6fdbd8 KO |
370 | |
371 | i->ubuf = buf; | |
372 | i->size = size; | |
373 | i->ret = 0; | |
374 | ||
80eab7a7 KO |
375 | return flush_buf(i) ?: |
376 | bch2_trans_run(i->c, | |
377 | for_each_btree_key(trans, iter, i->id, i->from, | |
5dd8c60e KO |
378 | BTREE_ITER_prefetch| |
379 | BTREE_ITER_all_snapshots, k, ({ | |
80eab7a7 KO |
380 | bch2_bkey_val_to_text(&i->buf, i->c, k); |
381 | prt_newline(&i->buf); | |
382 | bch2_trans_unlock(trans); | |
383 | i->from = bpos_successor(iter.pos); | |
384 | flush_buf(i); | |
385 | }))) ?: | |
386 | i->ret; | |
1c6fdbd8 KO |
387 | } |
388 | ||
389 | static const struct file_operations btree_debug_ops = { | |
390 | .owner = THIS_MODULE, | |
391 | .open = bch2_dump_open, | |
392 | .release = bch2_dump_release, | |
393 | .read = bch2_read_btree, | |
394 | }; | |
395 | ||
396 | static ssize_t bch2_read_btree_formats(struct file *file, char __user *buf, | |
397 | size_t size, loff_t *ppos) | |
398 | { | |
399 | struct dump_iter *i = file->private_data; | |
1c6fdbd8 KO |
400 | |
401 | i->ubuf = buf; | |
402 | i->size = size; | |
403 | i->ret = 0; | |
404 | ||
968feb85 | 405 | ssize_t ret = flush_buf(i); |
9375fbc2 KO |
406 | if (ret) |
407 | return ret; | |
1c6fdbd8 | 408 | |
e88a75eb | 409 | if (bpos_eq(SPOS_MAX, i->from)) |
1c6fdbd8 KO |
410 | return i->ret; |
411 | ||
968feb85 KO |
412 | return bch2_trans_run(i->c, |
413 | for_each_btree_node(trans, iter, i->id, i->from, 0, b, ({ | |
414 | bch2_btree_node_to_text(&i->buf, i->c, b); | |
415 | i->from = !bpos_eq(SPOS_MAX, b->key.k.p) | |
416 | ? bpos_successor(b->key.k.p) | |
417 | : b->key.k.p; | |
424eb881 | 418 | |
968feb85 KO |
419 | drop_locks_do(trans, flush_buf(i)); |
420 | }))) ?: i->ret; | |
1c6fdbd8 KO |
421 | } |
422 | ||
423 | static const struct file_operations btree_format_debug_ops = { | |
424 | .owner = THIS_MODULE, | |
425 | .open = bch2_dump_open, | |
426 | .release = bch2_dump_release, | |
427 | .read = bch2_read_btree_formats, | |
428 | }; | |
429 | ||
430 | static ssize_t bch2_read_bfloat_failed(struct file *file, char __user *buf, | |
431 | size_t size, loff_t *ppos) | |
432 | { | |
433 | struct dump_iter *i = file->private_data; | |
1c6fdbd8 KO |
434 | |
435 | i->ubuf = buf; | |
436 | i->size = size; | |
437 | i->ret = 0; | |
438 | ||
80eab7a7 KO |
439 | return flush_buf(i) ?: |
440 | bch2_trans_run(i->c, | |
441 | for_each_btree_key(trans, iter, i->id, i->from, | |
5dd8c60e KO |
442 | BTREE_ITER_prefetch| |
443 | BTREE_ITER_all_snapshots, k, ({ | |
07f383c7 KO |
444 | struct btree_path_level *l = |
445 | &btree_iter_path(trans, &iter)->l[0]; | |
80eab7a7 KO |
446 | struct bkey_packed *_k = |
447 | bch2_btree_node_iter_peek(&l->iter, l->b); | |
448 | ||
449 | if (bpos_gt(l->b->key.k.p, i->prev_node)) { | |
450 | bch2_btree_node_to_text(&i->buf, i->c, l->b); | |
451 | i->prev_node = l->b->key.k.p; | |
452 | } | |
453 | ||
454 | bch2_bfloat_to_text(&i->buf, l->b, _k); | |
455 | bch2_trans_unlock(trans); | |
456 | i->from = bpos_successor(iter.pos); | |
457 | flush_buf(i); | |
458 | }))) ?: | |
459 | i->ret; | |
1c6fdbd8 KO |
460 | } |
461 | ||
462 | static const struct file_operations bfloat_failed_debug_ops = { | |
463 | .owner = THIS_MODULE, | |
464 | .open = bch2_dump_open, | |
465 | .release = bch2_dump_release, | |
466 | .read = bch2_read_bfloat_failed, | |
467 | }; | |
468 | ||
75ef2c59 KO |
469 | static void bch2_cached_btree_node_to_text(struct printbuf *out, struct bch_fs *c, |
470 | struct btree *b) | |
471 | { | |
401ec4db KO |
472 | if (!out->nr_tabstops) |
473 | printbuf_tabstop_push(out, 32); | |
75ef2c59 | 474 | |
7423330e | 475 | prt_printf(out, "%px btree=%s l=%u\n", b, bch2_btree_id_str(b->c.btree_id), b->c.level); |
75ef2c59 | 476 | |
401ec4db | 477 | printbuf_indent_add(out, 2); |
75ef2c59 KO |
478 | |
479 | bch2_bkey_val_to_text(out, c, bkey_i_to_s_c(&b->key)); | |
401ec4db KO |
480 | prt_newline(out); |
481 | ||
7423330e | 482 | prt_printf(out, "flags:\t"); |
401ec4db KO |
483 | prt_bitflags(out, bch2_btree_node_flags, b->flags); |
484 | prt_newline(out); | |
485 | ||
7423330e KO |
486 | prt_printf(out, "pcpu read locks:\t%u\n", b->c.lock.readers != NULL); |
487 | prt_printf(out, "written:\t%u\n", b->written); | |
488 | prt_printf(out, "writes blocked:\t%u\n", !list_empty_careful(&b->write_blocked)); | |
489 | prt_printf(out, "will make reachable:\t%lx\n", b->will_make_reachable); | |
401ec4db | 490 | |
7423330e KO |
491 | prt_printf(out, "journal pin %px:\t%llu\n", |
492 | &b->writes[0].journal, b->writes[0].journal.seq); | |
493 | prt_printf(out, "journal pin %px:\t%llu\n", | |
494 | &b->writes[1].journal, b->writes[1].journal.seq); | |
401ec4db KO |
495 | |
496 | printbuf_indent_sub(out, 2); | |
75ef2c59 KO |
497 | } |
498 | ||
499 | static ssize_t bch2_cached_btree_nodes_read(struct file *file, char __user *buf, | |
500 | size_t size, loff_t *ppos) | |
501 | { | |
502 | struct dump_iter *i = file->private_data; | |
503 | struct bch_fs *c = i->c; | |
504 | bool done = false; | |
9375fbc2 | 505 | ssize_t ret = 0; |
75ef2c59 KO |
506 | |
507 | i->ubuf = buf; | |
508 | i->size = size; | |
509 | i->ret = 0; | |
510 | ||
511 | do { | |
512 | struct bucket_table *tbl; | |
513 | struct rhash_head *pos; | |
514 | struct btree *b; | |
515 | ||
9375fbc2 KO |
516 | ret = flush_buf(i); |
517 | if (ret) | |
518 | return ret; | |
75ef2c59 KO |
519 | |
520 | rcu_read_lock(); | |
521 | i->buf.atomic++; | |
522 | tbl = rht_dereference_rcu(c->btree_cache.table.tbl, | |
523 | &c->btree_cache.table); | |
524 | if (i->iter < tbl->size) { | |
525 | rht_for_each_entry_rcu(b, pos, tbl, i->iter, hash) | |
526 | bch2_cached_btree_node_to_text(&i->buf, c, b); | |
3e3e02e6 | 527 | i->iter++; |
75ef2c59 KO |
528 | } else { |
529 | done = true; | |
530 | } | |
531 | --i->buf.atomic; | |
532 | rcu_read_unlock(); | |
533 | } while (!done); | |
534 | ||
535 | if (i->buf.allocation_failure) | |
9375fbc2 KO |
536 | ret = -ENOMEM; |
537 | ||
538 | if (!ret) | |
539 | ret = flush_buf(i); | |
75ef2c59 | 540 | |
9375fbc2 | 541 | return ret ?: i->ret; |
75ef2c59 KO |
542 | } |
543 | ||
544 | static const struct file_operations cached_btree_nodes_ops = { | |
545 | .owner = THIS_MODULE, | |
546 | .open = bch2_dump_open, | |
547 | .release = bch2_dump_release, | |
548 | .read = bch2_cached_btree_nodes_read, | |
549 | }; | |
550 | ||
1aaf5cb4 KO |
551 | typedef int (*list_cmp_fn)(const struct list_head *l, const struct list_head *r); |
552 | ||
553 | static void list_sort(struct list_head *head, list_cmp_fn cmp) | |
554 | { | |
555 | struct list_head *pos; | |
556 | ||
557 | list_for_each(pos, head) | |
558 | while (!list_is_last(pos, head) && | |
559 | cmp(pos, pos->next) > 0) { | |
560 | struct list_head *pos2, *next = pos->next; | |
561 | ||
562 | list_del(next); | |
563 | list_for_each(pos2, head) | |
564 | if (cmp(next, pos2) < 0) | |
565 | goto pos_found; | |
566 | BUG(); | |
567 | pos_found: | |
568 | list_add_tail(next, pos2); | |
569 | } | |
570 | } | |
571 | ||
572 | static int list_ptr_order_cmp(const struct list_head *l, const struct list_head *r) | |
573 | { | |
574 | return cmp_int(l, r); | |
575 | } | |
576 | ||
50b13bee KO |
577 | static ssize_t bch2_btree_transactions_read(struct file *file, char __user *buf, |
578 | size_t size, loff_t *ppos) | |
579 | { | |
580 | struct dump_iter *i = file->private_data; | |
581 | struct bch_fs *c = i->c; | |
582 | struct btree_trans *trans; | |
9375fbc2 | 583 | ssize_t ret = 0; |
50b13bee KO |
584 | |
585 | i->ubuf = buf; | |
586 | i->size = size; | |
587 | i->ret = 0; | |
a5b696ee KO |
588 | restart: |
589 | seqmutex_lock(&c->btree_trans_lock); | |
1aaf5cb4 | 590 | list_sort(&c->btree_trans_list, list_ptr_order_cmp); |
31403dca | 591 | |
1aaf5cb4 | 592 | list_for_each_entry(trans, &c->btree_trans_list, list) { |
67c56411 | 593 | if ((ulong) trans <= i->iter) |
50b13bee KO |
594 | continue; |
595 | ||
1aaf5cb4 KO |
596 | i->iter = (ulong) trans; |
597 | ||
de611ab6 KO |
598 | if (!closure_get_not_zero(&trans->ref)) |
599 | continue; | |
a5b696ee | 600 | |
de611ab6 | 601 | u32 seq = seqmutex_unlock(&c->btree_trans_lock); |
50b13bee KO |
602 | |
603 | bch2_btree_trans_to_text(&i->buf, trans); | |
604 | ||
7423330e | 605 | prt_printf(&i->buf, "backtrace:\n"); |
50b13bee | 606 | printbuf_indent_add(&i->buf, 2); |
1aaf5cb4 | 607 | bch2_prt_task_backtrace(&i->buf, trans->locking_wait.task, 0, GFP_KERNEL); |
50b13bee KO |
608 | printbuf_indent_sub(&i->buf, 2); |
609 | prt_newline(&i->buf); | |
610 | ||
a5b696ee KO |
611 | closure_put(&trans->ref); |
612 | ||
de611ab6 KO |
613 | ret = flush_buf(i); |
614 | if (ret) | |
615 | goto unlocked; | |
616 | ||
a5b696ee KO |
617 | if (!seqmutex_relock(&c->btree_trans_lock, seq)) |
618 | goto restart; | |
619 | } | |
620 | seqmutex_unlock(&c->btree_trans_lock); | |
621 | unlocked: | |
50b13bee | 622 | if (i->buf.allocation_failure) |
9375fbc2 KO |
623 | ret = -ENOMEM; |
624 | ||
625 | if (!ret) | |
626 | ret = flush_buf(i); | |
50b13bee | 627 | |
9375fbc2 | 628 | return ret ?: i->ret; |
50b13bee KO |
629 | } |
630 | ||
631 | static const struct file_operations btree_transactions_ops = { | |
632 | .owner = THIS_MODULE, | |
633 | .open = bch2_dump_open, | |
634 | .release = bch2_dump_release, | |
635 | .read = bch2_btree_transactions_read, | |
636 | }; | |
50b13bee | 637 | |
75ef2c59 KO |
638 | static ssize_t bch2_journal_pins_read(struct file *file, char __user *buf, |
639 | size_t size, loff_t *ppos) | |
640 | { | |
641 | struct dump_iter *i = file->private_data; | |
642 | struct bch_fs *c = i->c; | |
643 | bool done = false; | |
644 | int err; | |
645 | ||
646 | i->ubuf = buf; | |
647 | i->size = size; | |
648 | i->ret = 0; | |
649 | ||
9fb3036f | 650 | while (1) { |
75ef2c59 KO |
651 | err = flush_buf(i); |
652 | if (err) | |
653 | return err; | |
654 | ||
655 | if (!i->size) | |
656 | break; | |
657 | ||
9fb3036f KO |
658 | if (done) |
659 | break; | |
660 | ||
75ef2c59 KO |
661 | done = bch2_journal_seq_pins_to_text(&i->buf, &c->journal, &i->iter); |
662 | i->iter++; | |
9fb3036f | 663 | } |
75ef2c59 KO |
664 | |
665 | if (i->buf.allocation_failure) | |
666 | return -ENOMEM; | |
667 | ||
668 | return i->ret; | |
669 | } | |
670 | ||
671 | static const struct file_operations journal_pins_ops = { | |
672 | .owner = THIS_MODULE, | |
673 | .open = bch2_dump_open, | |
674 | .release = bch2_dump_release, | |
675 | .read = bch2_journal_pins_read, | |
676 | }; | |
677 | ||
9fb3036f KO |
678 | static ssize_t bch2_btree_updates_read(struct file *file, char __user *buf, |
679 | size_t size, loff_t *ppos) | |
680 | { | |
681 | struct dump_iter *i = file->private_data; | |
682 | struct bch_fs *c = i->c; | |
683 | int err; | |
684 | ||
685 | i->ubuf = buf; | |
686 | i->size = size; | |
687 | i->ret = 0; | |
688 | ||
689 | if (!i->iter) { | |
690 | bch2_btree_updates_to_text(&i->buf, c); | |
691 | i->iter++; | |
692 | } | |
693 | ||
694 | err = flush_buf(i); | |
695 | if (err) | |
696 | return err; | |
697 | ||
698 | if (i->buf.allocation_failure) | |
699 | return -ENOMEM; | |
700 | ||
701 | return i->ret; | |
702 | } | |
703 | ||
704 | static const struct file_operations btree_updates_ops = { | |
705 | .owner = THIS_MODULE, | |
706 | .open = bch2_dump_open, | |
707 | .release = bch2_dump_release, | |
708 | .read = bch2_btree_updates_read, | |
709 | }; | |
710 | ||
89056f24 | 711 | static int btree_transaction_stats_open(struct inode *inode, struct file *file) |
c807ca95 DH |
712 | { |
713 | struct bch_fs *c = inode->i_private; | |
714 | struct dump_iter *i; | |
715 | ||
716 | i = kzalloc(sizeof(struct dump_iter), GFP_KERNEL); | |
c807ca95 DH |
717 | if (!i) |
718 | return -ENOMEM; | |
719 | ||
89056f24 | 720 | i->iter = 1; |
c807ca95 DH |
721 | i->c = c; |
722 | i->buf = PRINTBUF; | |
723 | file->private_data = i; | |
724 | ||
725 | return 0; | |
726 | } | |
727 | ||
89056f24 | 728 | static int btree_transaction_stats_release(struct inode *inode, struct file *file) |
c807ca95 DH |
729 | { |
730 | struct dump_iter *i = file->private_data; | |
731 | ||
732 | printbuf_exit(&i->buf); | |
733 | kfree(i); | |
734 | ||
735 | return 0; | |
736 | } | |
737 | ||
89056f24 KO |
738 | static ssize_t btree_transaction_stats_read(struct file *file, char __user *buf, |
739 | size_t size, loff_t *ppos) | |
c807ca95 DH |
740 | { |
741 | struct dump_iter *i = file->private_data; | |
4aba7d45 | 742 | struct bch_fs *c = i->c; |
c807ca95 DH |
743 | int err; |
744 | ||
745 | i->ubuf = buf; | |
746 | i->size = size; | |
747 | i->ret = 0; | |
748 | ||
ff7dc365 | 749 | while (1) { |
4aba7d45 KO |
750 | struct btree_transaction_stats *s = &c->btree_transaction_stats[i->iter]; |
751 | ||
c807ca95 DH |
752 | err = flush_buf(i); |
753 | if (err) | |
754 | return err; | |
755 | ||
756 | if (!i->size) | |
757 | break; | |
758 | ||
307e3c13 KO |
759 | if (i->iter == ARRAY_SIZE(bch2_btree_transaction_fns) || |
760 | !bch2_btree_transaction_fns[i->iter]) | |
ff7dc365 KO |
761 | break; |
762 | ||
7423330e | 763 | prt_printf(&i->buf, "%s:\n", bch2_btree_transaction_fns[i->iter]); |
4aba7d45 | 764 | printbuf_indent_add(&i->buf, 2); |
5c0bb66a KO |
765 | |
766 | mutex_lock(&s->lock); | |
767 | ||
7423330e KO |
768 | prt_printf(&i->buf, "Max mem used: %u\n", s->max_mem); |
769 | prt_printf(&i->buf, "Transaction duration:\n"); | |
89056f24 KO |
770 | |
771 | printbuf_indent_add(&i->buf, 2); | |
772 | bch2_time_stats_to_text(&i->buf, &s->duration); | |
773 | printbuf_indent_sub(&i->buf, 2); | |
774 | ||
5c0bb66a | 775 | if (IS_ENABLED(CONFIG_BCACHEFS_LOCK_TIME_STATS)) { |
7423330e | 776 | prt_printf(&i->buf, "Lock hold times:\n"); |
5c0bb66a KO |
777 | |
778 | printbuf_indent_add(&i->buf, 2); | |
779 | bch2_time_stats_to_text(&i->buf, &s->lock_hold_times); | |
780 | printbuf_indent_sub(&i->buf, 2); | |
781 | } | |
782 | ||
783 | if (s->max_paths_text) { | |
7423330e | 784 | prt_printf(&i->buf, "Maximum allocated btree paths (%u):\n", s->nr_max_paths); |
5c0bb66a KO |
785 | |
786 | printbuf_indent_add(&i->buf, 2); | |
787 | prt_str_indented(&i->buf, s->max_paths_text); | |
788 | printbuf_indent_sub(&i->buf, 2); | |
789 | } | |
790 | ||
791 | mutex_unlock(&s->lock); | |
792 | ||
4aba7d45 | 793 | printbuf_indent_sub(&i->buf, 2); |
c807ca95 DH |
794 | prt_newline(&i->buf); |
795 | i->iter++; | |
796 | } | |
797 | ||
798 | if (i->buf.allocation_failure) | |
799 | return -ENOMEM; | |
800 | ||
801 | return i->ret; | |
802 | } | |
803 | ||
89056f24 KO |
804 | static const struct file_operations btree_transaction_stats_op = { |
805 | .owner = THIS_MODULE, | |
806 | .open = btree_transaction_stats_open, | |
807 | .release = btree_transaction_stats_release, | |
808 | .read = btree_transaction_stats_read, | |
c807ca95 DH |
809 | }; |
810 | ||
18e92841 KO |
811 | /* walk btree transactions until we find a deadlock and print it */ |
812 | static void btree_deadlock_to_text(struct printbuf *out, struct bch_fs *c) | |
96d994b3 | 813 | { |
96d994b3 | 814 | struct btree_trans *trans; |
67c56411 | 815 | ulong iter = 0; |
a5b696ee KO |
816 | restart: |
817 | seqmutex_lock(&c->btree_trans_lock); | |
67c56411 | 818 | list_sort(&c->btree_trans_list, list_ptr_order_cmp); |
31403dca | 819 | |
67c56411 KO |
820 | list_for_each_entry(trans, &c->btree_trans_list, list) { |
821 | if ((ulong) trans <= iter) | |
40a44873 KO |
822 | continue; |
823 | ||
67c56411 | 824 | iter = (ulong) trans; |
18e92841 | 825 | |
de611ab6 KO |
826 | if (!closure_get_not_zero(&trans->ref)) |
827 | continue; | |
a5b696ee | 828 | |
18e92841 | 829 | u32 seq = seqmutex_unlock(&c->btree_trans_lock); |
40a44873 | 830 | |
18e92841 | 831 | bool found = bch2_check_for_deadlock(trans, out) != 0; |
a5b696ee KO |
832 | |
833 | closure_put(&trans->ref); | |
834 | ||
18e92841 KO |
835 | if (found) |
836 | return; | |
837 | ||
a5b696ee KO |
838 | if (!seqmutex_relock(&c->btree_trans_lock, seq)) |
839 | goto restart; | |
40a44873 | 840 | } |
a5b696ee | 841 | seqmutex_unlock(&c->btree_trans_lock); |
18e92841 KO |
842 | } |
843 | ||
844 | static ssize_t bch2_btree_deadlock_read(struct file *file, char __user *buf, | |
845 | size_t size, loff_t *ppos) | |
846 | { | |
847 | struct dump_iter *i = file->private_data; | |
848 | struct bch_fs *c = i->c; | |
849 | ssize_t ret = 0; | |
850 | ||
851 | i->ubuf = buf; | |
852 | i->size = size; | |
853 | i->ret = 0; | |
854 | ||
855 | if (!i->iter) { | |
856 | btree_deadlock_to_text(&i->buf, c); | |
857 | i->iter++; | |
858 | } | |
859 | ||
96d994b3 KO |
860 | if (i->buf.allocation_failure) |
861 | ret = -ENOMEM; | |
862 | ||
863 | if (!ret) | |
864 | ret = flush_buf(i); | |
865 | ||
866 | return ret ?: i->ret; | |
867 | } | |
868 | ||
869 | static const struct file_operations btree_deadlock_ops = { | |
870 | .owner = THIS_MODULE, | |
871 | .open = bch2_dump_open, | |
872 | .release = bch2_dump_release, | |
873 | .read = bch2_btree_deadlock_read, | |
874 | }; | |
875 | ||
1c6fdbd8 KO |
876 | void bch2_fs_debug_exit(struct bch_fs *c) |
877 | { | |
75ef2c59 KO |
878 | if (!IS_ERR_OR_NULL(c->fs_debug_dir)) |
879 | debugfs_remove_recursive(c->fs_debug_dir); | |
1c6fdbd8 KO |
880 | } |
881 | ||
e60aa472 TB |
882 | static void bch2_fs_debug_btree_init(struct bch_fs *c, struct btree_debug *bd) |
883 | { | |
884 | struct dentry *d; | |
885 | ||
886 | d = debugfs_create_dir(bch2_btree_id_str(bd->id), c->btree_debug_dir); | |
887 | ||
888 | debugfs_create_file("keys", 0400, d, bd, &btree_debug_ops); | |
889 | ||
890 | debugfs_create_file("formats", 0400, d, bd, &btree_format_debug_ops); | |
891 | ||
892 | debugfs_create_file("bfloat-failed", 0400, d, bd, | |
893 | &bfloat_failed_debug_ops); | |
894 | } | |
895 | ||
1c6fdbd8 KO |
896 | void bch2_fs_debug_init(struct bch_fs *c) |
897 | { | |
898 | struct btree_debug *bd; | |
899 | char name[100]; | |
900 | ||
901 | if (IS_ERR_OR_NULL(bch_debug)) | |
902 | return; | |
903 | ||
904 | snprintf(name, sizeof(name), "%pU", c->sb.user_uuid.b); | |
75ef2c59 KO |
905 | c->fs_debug_dir = debugfs_create_dir(name, bch_debug); |
906 | if (IS_ERR_OR_NULL(c->fs_debug_dir)) | |
907 | return; | |
908 | ||
909 | debugfs_create_file("cached_btree_nodes", 0400, c->fs_debug_dir, | |
910 | c->btree_debug, &cached_btree_nodes_ops); | |
911 | ||
50b13bee KO |
912 | debugfs_create_file("btree_transactions", 0400, c->fs_debug_dir, |
913 | c->btree_debug, &btree_transactions_ops); | |
50b13bee | 914 | |
75ef2c59 KO |
915 | debugfs_create_file("journal_pins", 0400, c->fs_debug_dir, |
916 | c->btree_debug, &journal_pins_ops); | |
917 | ||
9fb3036f KO |
918 | debugfs_create_file("btree_updates", 0400, c->fs_debug_dir, |
919 | c->btree_debug, &btree_updates_ops); | |
920 | ||
5c0bb66a | 921 | debugfs_create_file("btree_transaction_stats", 0400, c->fs_debug_dir, |
89056f24 | 922 | c, &btree_transaction_stats_op); |
c807ca95 | 923 | |
96d994b3 KO |
924 | debugfs_create_file("btree_deadlock", 0400, c->fs_debug_dir, |
925 | c->btree_debug, &btree_deadlock_ops); | |
926 | ||
75ef2c59 KO |
927 | c->btree_debug_dir = debugfs_create_dir("btrees", c->fs_debug_dir); |
928 | if (IS_ERR_OR_NULL(c->btree_debug_dir)) | |
1c6fdbd8 KO |
929 | return; |
930 | ||
931 | for (bd = c->btree_debug; | |
932 | bd < c->btree_debug + ARRAY_SIZE(c->btree_debug); | |
933 | bd++) { | |
934 | bd->id = bd - c->btree_debug; | |
e60aa472 | 935 | bch2_fs_debug_btree_init(c, bd); |
1c6fdbd8 KO |
936 | } |
937 | } | |
938 | ||
939 | #endif | |
940 | ||
941 | void bch2_debug_exit(void) | |
942 | { | |
943 | if (!IS_ERR_OR_NULL(bch_debug)) | |
944 | debugfs_remove_recursive(bch_debug); | |
945 | } | |
946 | ||
947 | int __init bch2_debug_init(void) | |
948 | { | |
1c6fdbd8 | 949 | bch_debug = debugfs_create_dir("bcachefs", NULL); |
8a0dda6f | 950 | return 0; |
1c6fdbd8 | 951 | } |