Skip to content

Commit 4c02e63

Browse files
author
Kent Overstreet
committed
bcachefs: Check for extents that point to same space
In backpointer repair, if we get a missing backpointer - but there's already a backpointer that points to an existing extent - we've got multiple extents that point to the same space and need to decide which to keep. Signed-off-by: Kent Overstreet <[email protected]>
1 parent a292be3 commit 4c02e63

File tree

2 files changed

+168
-8
lines changed

2 files changed

+168
-8
lines changed

fs/bcachefs/backpointers.c

Lines changed: 166 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "btree_update.h"
99
#include "btree_update_interior.h"
1010
#include "btree_write_buffer.h"
11+
#include "checksum.h"
1112
#include "error.h"
1213

1314
#include <linux/mm.h>
@@ -418,28 +419,113 @@ struct extents_to_bp_state {
418419
struct bkey_buf last_flushed;
419420
};
420421

422+
static int drop_dev_and_update(struct btree_trans *trans, enum btree_id btree,
423+
struct bkey_s_c extent, unsigned dev)
424+
{
425+
struct bkey_i *n = bch2_bkey_make_mut_noupdate(trans, extent);
426+
int ret = PTR_ERR_OR_ZERO(n);
427+
if (ret)
428+
return ret;
429+
430+
bch2_bkey_drop_device(bkey_i_to_s(n), dev);
431+
return bch2_btree_insert_trans(trans, btree, n, 0);
432+
}
433+
434+
static int check_extent_checksum(struct btree_trans *trans,
435+
enum btree_id btree, struct bkey_s_c extent,
436+
enum btree_id o_btree, struct bkey_s_c extent2, unsigned dev)
437+
{
438+
struct bch_fs *c = trans->c;
439+
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(extent);
440+
const union bch_extent_entry *entry;
441+
struct extent_ptr_decoded p;
442+
struct printbuf buf = PRINTBUF;
443+
void *data_buf = NULL;
444+
struct bio *bio = NULL;
445+
size_t bytes;
446+
int ret = 0;
447+
448+
if (bkey_is_btree_ptr(extent.k))
449+
return false;
450+
451+
bkey_for_each_ptr_decode(extent.k, ptrs, p, entry)
452+
if (p.ptr.dev == dev)
453+
goto found;
454+
BUG();
455+
found:
456+
if (!p.crc.csum_type)
457+
return false;
458+
459+
bytes = p.crc.compressed_size << 9;
460+
461+
struct bch_dev *ca = bch_dev_bkey_exists(c, dev);
462+
if (!bch2_dev_get_ioref(ca, READ))
463+
return false;
464+
465+
data_buf = kvmalloc(bytes, GFP_KERNEL);
466+
if (!data_buf) {
467+
ret = -ENOMEM;
468+
goto err;
469+
}
470+
471+
bio = bio_alloc(ca->disk_sb.bdev, 1, REQ_OP_READ, GFP_KERNEL);
472+
bio->bi_iter.bi_sector = p.ptr.offset;
473+
bch2_bio_map(bio, data_buf, bytes);
474+
ret = submit_bio_wait(bio);
475+
if (ret)
476+
goto err;
477+
478+
prt_str(&buf, "extents pointing to same space, but first extent checksum bad:");
479+
prt_printf(&buf, "\n %s ", bch2_btree_id_str(btree));
480+
bch2_bkey_val_to_text(&buf, c, extent);
481+
prt_printf(&buf, "\n %s ", bch2_btree_id_str(o_btree));
482+
bch2_bkey_val_to_text(&buf, c, extent2);
483+
484+
struct nonce nonce = extent_nonce(extent.k->version, p.crc);
485+
struct bch_csum csum = bch2_checksum(c, p.crc.csum_type, nonce, data_buf, bytes);
486+
if (fsck_err_on(bch2_crc_cmp(csum, p.crc.csum),
487+
c, dup_backpointer_to_bad_csum_extent,
488+
"%s", buf.buf))
489+
ret = drop_dev_and_update(trans, btree, extent, dev) ?: 1;
490+
fsck_err:
491+
err:
492+
if (bio)
493+
bio_put(bio);
494+
kvfree(data_buf);
495+
percpu_ref_put(&ca->io_ref);
496+
printbuf_exit(&buf);
497+
return ret;
498+
}
499+
421500
static int check_bp_exists(struct btree_trans *trans,
422501
struct extents_to_bp_state *s,
423502
struct bpos bucket,
424503
struct bch_backpointer bp,
425504
struct bkey_s_c orig_k)
426505
{
427506
struct bch_fs *c = trans->c;
428-
struct btree_iter bp_iter = { NULL };
507+
struct btree_iter bp_iter = {};
508+
struct btree_iter other_extent_iter = {};
429509
struct printbuf buf = PRINTBUF;
430510
struct bkey_s_c bp_k;
431511
struct bkey_buf tmp;
432512
int ret;
433513

434514
bch2_bkey_buf_init(&tmp);
435515

516+
if (!bch2_dev_bucket_exists(c, bucket)) {
517+
prt_str(&buf, "extent for nonexistent device:bucket ");
518+
bch2_bpos_to_text(&buf, bucket);
519+
prt_str(&buf, "\n ");
520+
bch2_bkey_val_to_text(&buf, c, orig_k);
521+
bch_err(c, "%s", buf.buf);
522+
return -BCH_ERR_fsck_repair_unimplemented;
523+
}
524+
436525
if (bpos_lt(bucket, s->bucket_start) ||
437526
bpos_gt(bucket, s->bucket_end))
438527
return 0;
439528

440-
if (!bch2_dev_bucket_exists(c, bucket))
441-
goto missing;
442-
443529
bp_k = bch2_bkey_get_iter(trans, &bp_iter, BTREE_ID_backpointers,
444530
bucket_pos_to_bp(c, bucket, bp.bucket_offset),
445531
0);
@@ -465,21 +551,94 @@ static int check_bp_exists(struct btree_trans *trans,
465551
ret = -BCH_ERR_transaction_restart_write_buffer_flush;
466552
goto out;
467553
}
468-
goto missing;
554+
555+
goto check_existing_bp;
469556
}
470557
out:
471558
err:
472559
fsck_err:
560+
bch2_trans_iter_exit(trans, &other_extent_iter);
473561
bch2_trans_iter_exit(trans, &bp_iter);
474562
bch2_bkey_buf_exit(&tmp, c);
475563
printbuf_exit(&buf);
476564
return ret;
565+
check_existing_bp:
566+
/* Do we have a backpointer for a different extent? */
567+
if (bp_k.k->type != KEY_TYPE_backpointer)
568+
goto missing;
569+
570+
struct bch_backpointer other_bp = *bkey_s_c_to_backpointer(bp_k).v;
571+
572+
struct bkey_s_c other_extent =
573+
bch2_backpointer_get_key(trans, &other_extent_iter, bp_k.k->p, other_bp, 0);
574+
ret = bkey_err(other_extent);
575+
if (ret == -BCH_ERR_backpointer_to_overwritten_btree_node)
576+
ret = 0;
577+
if (ret)
578+
goto err;
579+
580+
if (!other_extent.k)
581+
goto missing;
582+
583+
if (bch2_extents_match(orig_k, other_extent)) {
584+
printbuf_reset(&buf);
585+
prt_printf(&buf, "duplicate versions of same extent, deleting smaller\n ");
586+
bch2_bkey_val_to_text(&buf, c, orig_k);
587+
prt_str(&buf, "\n ");
588+
bch2_bkey_val_to_text(&buf, c, other_extent);
589+
bch_err(c, "%s", buf.buf);
590+
591+
if (other_extent.k->size <= orig_k.k->size) {
592+
ret = drop_dev_and_update(trans, other_bp.btree_id, other_extent, bucket.inode);
593+
if (ret)
594+
goto err;
595+
goto out;
596+
} else {
597+
ret = drop_dev_and_update(trans, bp.btree_id, orig_k, bucket.inode);
598+
if (ret)
599+
goto err;
600+
goto missing;
601+
}
602+
}
603+
604+
ret = check_extent_checksum(trans, other_bp.btree_id, other_extent, bp.btree_id, orig_k, bucket.inode);
605+
if (ret < 0)
606+
goto err;
607+
if (ret) {
608+
ret = 0;
609+
goto missing;
610+
}
611+
612+
ret = check_extent_checksum(trans, bp.btree_id, orig_k, other_bp.btree_id, other_extent, bucket.inode);
613+
if (ret < 0)
614+
goto err;
615+
if (ret) {
616+
ret = 0;
617+
goto out;
618+
}
619+
620+
printbuf_reset(&buf);
621+
prt_printf(&buf, "duplicate extents pointing to same space on dev %llu\n ", bucket.inode);
622+
bch2_bkey_val_to_text(&buf, c, orig_k);
623+
prt_str(&buf, "\n ");
624+
bch2_bkey_val_to_text(&buf, c, other_extent);
625+
bch_err(c, "%s", buf.buf);
626+
ret = -BCH_ERR_fsck_repair_unimplemented;
627+
goto err;
477628
missing:
629+
printbuf_reset(&buf);
478630
prt_printf(&buf, "missing backpointer for btree=%s l=%u ",
479631
bch2_btree_id_str(bp.btree_id), bp.level);
480632
bch2_bkey_val_to_text(&buf, c, orig_k);
481-
prt_printf(&buf, "\nbp pos ");
482-
bch2_bpos_to_text(&buf, bp_iter.pos);
633+
prt_printf(&buf, "\n got: ");
634+
bch2_bkey_val_to_text(&buf, c, bp_k);
635+
636+
struct bkey_i_backpointer n_bp_k;
637+
bkey_backpointer_init(&n_bp_k.k_i);
638+
n_bp_k.k.p = bucket_pos_to_bp(trans->c, bucket, bp.bucket_offset);
639+
n_bp_k.v = bp;
640+
prt_printf(&buf, "\n want: ");
641+
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&n_bp_k.k_i));
483642

484643
if (fsck_err(c, ptr_to_missing_backpointer, "%s", buf.buf))
485644
ret = bch2_bucket_backpointer_mod(trans, bucket, bp, orig_k, true);

fs/bcachefs/sb-errors_types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@
269269
x(btree_node_topology_empty_interior_node, 261) \
270270
x(btree_ptr_v2_min_key_bad, 262) \
271271
x(btree_root_unreadable_and_scan_found_nothing, 263) \
272-
x(snapshot_node_missing, 264)
272+
x(snapshot_node_missing, 264) \
273+
x(dup_backpointer_to_bad_csum_extent, 265)
273274

274275
enum bch_sb_error_id {
275276
#define x(t, n) BCH_FSCK_ERR_##t = n,

0 commit comments

Comments
 (0)