bcachefs: Plumb bkey_validate_context to journal_entry_validate

This lets us print the exact location in the journal if it was found in
the journal, or correctly print if it was found in the superblock.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2024-12-07 21:36:15 -05:00
parent 488249b3f6
commit e8d604148b
7 changed files with 100 additions and 87 deletions

View File

@ -213,16 +213,16 @@ BCH_BKEY_TYPES();
enum bch_validate_flags {
BCH_VALIDATE_write = BIT(0),
BCH_VALIDATE_commit = BIT(1),
BCH_VALIDATE_journal = BIT(2),
BCH_VALIDATE_silent = BIT(3),
BCH_VALIDATE_silent = BIT(2),
};
#define BKEY_VALIDATE_CONTEXTS() \
x(unknown) \
x(commit) \
x(superblock) \
x(journal) \
x(btree_root) \
x(btree_node)
x(btree_node) \
x(commit)
struct bkey_validate_context {
enum {
@ -230,10 +230,12 @@ struct bkey_validate_context {
BKEY_VALIDATE_CONTEXTS()
#undef x
} from:8;
enum bch_validate_flags flags:8;
u8 level;
enum btree_id btree;
bool root:1;
enum bch_validate_flags flags:8;
unsigned journal_offset;
u64 journal_seq;
};
#endif /* _BCACHEFS_BKEY_TYPES_H */

View File

@ -719,19 +719,29 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
goto fatal_err;
}
struct bkey_validate_context validate_context = { .from = BKEY_VALIDATE_commit };
if (!(flags & BCH_TRANS_COMMIT_no_journal_res))
validate_context.flags = BCH_VALIDATE_write|BCH_VALIDATE_commit;
for (struct jset_entry *i = trans->journal_entries;
i != (void *) ((u64 *) trans->journal_entries + trans->journal_entries_u64s);
i = vstruct_next(i)) {
ret = bch2_journal_entry_validate(c, NULL, i,
bcachefs_metadata_version_current,
CPU_BIG_ENDIAN, validate_context);
if (unlikely(ret)) {
bch2_trans_inconsistent(trans, "invalid journal entry on insert from %s\n",
trans->fn);
goto fatal_err;
}
}
trans_for_each_update(trans, i) {
enum bch_validate_flags invalid_flags = 0;
validate_context.level = i->level;
validate_context.btree = i->btree_id;
if (!(flags & BCH_TRANS_COMMIT_no_journal_res))
invalid_flags |= BCH_VALIDATE_write|BCH_VALIDATE_commit;
ret = bch2_bkey_validate(c, bkey_i_to_s_c(i->k),
(struct bkey_validate_context) {
.from = BKEY_VALIDATE_commit,
.level = i->level,
.btree = i->btree_id,
.flags = invalid_flags,
});
ret = bch2_bkey_validate(c, bkey_i_to_s_c(i->k), validate_context);
if (unlikely(ret)){
bch2_trans_inconsistent(trans, "invalid bkey on insert from %s -> %ps\n",
trans->fn, (void *) i->ip_allocated);
@ -740,24 +750,6 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
btree_insert_entry_checks(trans, i);
}
for (struct jset_entry *i = trans->journal_entries;
i != (void *) ((u64 *) trans->journal_entries + trans->journal_entries_u64s);
i = vstruct_next(i)) {
enum bch_validate_flags invalid_flags = 0;
if (!(flags & BCH_TRANS_COMMIT_no_journal_res))
invalid_flags |= BCH_VALIDATE_write|BCH_VALIDATE_commit;
ret = bch2_journal_entry_validate(c, NULL, i,
bcachefs_metadata_version_current,
CPU_BIG_ENDIAN, invalid_flags);
if (unlikely(ret)) {
bch2_trans_inconsistent(trans, "invalid journal entry on insert from %s\n",
trans->fn);
goto fatal_err;
}
}
if (likely(!(flags & BCH_TRANS_COMMIT_no_journal_res))) {
struct journal *j = &c->journal;
struct jset_entry *entry;

View File

@ -486,9 +486,14 @@ int __bch2_bkey_fsck_err(struct bch_fs *c,
fsck_flags |= fsck_flags_extra[err];
struct printbuf buf = PRINTBUF;
prt_printf(&buf, "invalid bkey in %s btree=",
prt_printf(&buf, "invalid bkey in %s",
bch2_bkey_validate_contexts[from.from]);
if (from.from == BKEY_VALIDATE_journal)
prt_printf(&buf, " journal seq=%llu offset=%u",
from.journal_seq, from.journal_offset);
prt_str(&buf, " btree=");
bch2_btree_id_to_text(&buf, from.btree);
prt_printf(&buf, " level=%u: ", from.level);

View File

@ -1238,6 +1238,12 @@ static int extent_ptr_validate(struct bch_fs *c,
{
int ret = 0;
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
bkey_for_each_ptr(ptrs, ptr2)
bkey_fsck_err_on(ptr != ptr2 && ptr->dev == ptr2->dev,
c, ptr_to_duplicate_device,
"multiple pointers to same device (%u)", ptr->dev);
/* bad pointers are repaired by check_fix_ptrs(): */
rcu_read_lock();
struct bch_dev *ca = bch2_dev_rcu_noerror(c, ptr->dev);
@ -1252,13 +1258,6 @@ static int extent_ptr_validate(struct bch_fs *c,
unsigned bucket_size = ca->mi.bucket_size;
rcu_read_unlock();
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
bkey_for_each_ptr(ptrs, ptr2)
bkey_fsck_err_on(ptr != ptr2 && ptr->dev == ptr2->dev,
c, ptr_to_duplicate_device,
"multiple pointers to same device (%u)", ptr->dev);
bkey_fsck_err_on(bucket >= nbuckets,
c, ptr_after_last_bucket,
"pointer past last bucket (%llu > %llu)", bucket, nbuckets);

View File

@ -301,7 +301,7 @@ static void journal_entry_err_msg(struct printbuf *out,
journal_entry_err_msg(&_buf, version, jset, entry); \
prt_printf(&_buf, msg, ##__VA_ARGS__); \
\
switch (flags & BCH_VALIDATE_write) { \
switch (from.flags & BCH_VALIDATE_write) { \
case READ: \
mustfix_fsck_err(c, _err, "%s", _buf.buf); \
break; \
@ -390,15 +390,12 @@ static int journal_entry_btree_keys_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
struct bkey_i *k = entry->start;
struct bkey_validate_context from = {
.from = BKEY_VALIDATE_journal,
.level = entry->level,
.btree = entry->btree_id,
.flags = flags|BCH_VALIDATE_journal,
};
from.level = entry->level;
from.btree = entry->btree_id;
while (k != vstruct_last(entry)) {
int ret = journal_validate_key(c, jset, entry, k, from, version, big_endian);
@ -435,11 +432,15 @@ static int journal_entry_btree_root_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
struct bkey_i *k = entry->start;
int ret = 0;
from.root = true;
from.level = entry->level + 1;
from.btree = entry->btree_id;
if (journal_entry_err_on(!entry->u64s ||
le16_to_cpu(entry->u64s) != k->k.u64s,
c, version, jset, entry,
@ -456,13 +457,6 @@ static int journal_entry_btree_root_validate(struct bch_fs *c,
return 0;
}
struct bkey_validate_context from = {
.from = BKEY_VALIDATE_journal,
.level = entry->level + 1,
.btree = entry->btree_id,
.root = true,
.flags = flags,
};
ret = journal_validate_key(c, jset, entry, k, from, version, big_endian);
if (ret == FSCK_DELETED_KEY)
ret = 0;
@ -480,7 +474,7 @@ static int journal_entry_prio_ptrs_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
/* obsolete, don't care: */
return 0;
@ -495,7 +489,7 @@ static int journal_entry_blacklist_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
int ret = 0;
@ -522,7 +516,7 @@ static int journal_entry_blacklist_v2_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
struct jset_entry_blacklist_v2 *bl_entry;
int ret = 0;
@ -564,7 +558,7 @@ static int journal_entry_usage_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
struct jset_entry_usage *u =
container_of(entry, struct jset_entry_usage, entry);
@ -598,7 +592,7 @@ static int journal_entry_data_usage_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
struct jset_entry_data_usage *u =
container_of(entry, struct jset_entry_data_usage, entry);
@ -642,7 +636,7 @@ static int journal_entry_clock_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
struct jset_entry_clock *clock =
container_of(entry, struct jset_entry_clock, entry);
@ -682,7 +676,7 @@ static int journal_entry_dev_usage_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
struct jset_entry_dev_usage *u =
container_of(entry, struct jset_entry_dev_usage, entry);
@ -739,7 +733,7 @@ static int journal_entry_log_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
return 0;
}
@ -756,10 +750,11 @@ static int journal_entry_overwrite_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
from.flags = 0;
return journal_entry_btree_keys_validate(c, jset, entry,
version, big_endian, READ);
version, big_endian, from);
}
static void journal_entry_overwrite_to_text(struct printbuf *out, struct bch_fs *c,
@ -772,10 +767,10 @@ static int journal_entry_write_buffer_keys_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
return journal_entry_btree_keys_validate(c, jset, entry,
version, big_endian, READ);
version, big_endian, from);
}
static void journal_entry_write_buffer_keys_to_text(struct printbuf *out, struct bch_fs *c,
@ -788,7 +783,7 @@ static int journal_entry_datetime_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
unsigned bytes = vstruct_bytes(entry);
unsigned expected = 16;
@ -818,7 +813,7 @@ static void journal_entry_datetime_to_text(struct printbuf *out, struct bch_fs *
struct jset_entry_ops {
int (*validate)(struct bch_fs *, struct jset *,
struct jset_entry *, unsigned, int,
enum bch_validate_flags);
struct bkey_validate_context);
void (*to_text)(struct printbuf *, struct bch_fs *, struct jset_entry *);
};
@ -836,11 +831,11 @@ int bch2_journal_entry_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian,
enum bch_validate_flags flags)
struct bkey_validate_context from)
{
return entry->type < BCH_JSET_ENTRY_NR
? bch2_jset_entry_ops[entry->type].validate(c, jset, entry,
version, big_endian, flags)
version, big_endian, from)
: 0;
}
@ -858,10 +853,18 @@ void bch2_journal_entry_to_text(struct printbuf *out, struct bch_fs *c,
static int jset_validate_entries(struct bch_fs *c, struct jset *jset,
enum bch_validate_flags flags)
{
struct bkey_validate_context from = {
.flags = flags,
.from = BKEY_VALIDATE_journal,
.journal_seq = le64_to_cpu(jset->seq),
};
unsigned version = le32_to_cpu(jset->version);
int ret = 0;
vstruct_for_each(jset, entry) {
from.journal_offset = (u64 *) entry - jset->_data;
if (journal_entry_err_on(vstruct_next(entry) > vstruct_last(jset),
c, version, jset, entry,
journal_entry_past_jset_end,
@ -870,8 +873,8 @@ static int jset_validate_entries(struct bch_fs *c, struct jset *jset,
break;
}
ret = bch2_journal_entry_validate(c, jset, entry,
version, JSET_BIG_ENDIAN(jset), flags);
ret = bch2_journal_entry_validate(c, jset, entry, version,
JSET_BIG_ENDIAN(jset), from);
if (ret)
break;
}
@ -884,13 +887,17 @@ static int jset_validate(struct bch_fs *c,
struct jset *jset, u64 sector,
enum bch_validate_flags flags)
{
unsigned version;
struct bkey_validate_context from = {
.flags = flags,
.from = BKEY_VALIDATE_journal,
.journal_seq = le64_to_cpu(jset->seq),
};
int ret = 0;
if (le64_to_cpu(jset->magic) != jset_magic(c))
return JOURNAL_ENTRY_NONE;
version = le32_to_cpu(jset->version);
unsigned version = le32_to_cpu(jset->version);
if (journal_entry_err_on(!bch2_version_compatible(version),
c, version, jset, NULL,
jset_unsupported_version,
@ -935,15 +942,16 @@ static int jset_validate_early(struct bch_fs *c,
unsigned bucket_sectors_left,
unsigned sectors_read)
{
size_t bytes = vstruct_bytes(jset);
unsigned version;
enum bch_validate_flags flags = BCH_VALIDATE_journal;
struct bkey_validate_context from = {
.from = BKEY_VALIDATE_journal,
.journal_seq = le64_to_cpu(jset->seq),
};
int ret = 0;
if (le64_to_cpu(jset->magic) != jset_magic(c))
return JOURNAL_ENTRY_NONE;
version = le32_to_cpu(jset->version);
unsigned version = le32_to_cpu(jset->version);
if (journal_entry_err_on(!bch2_version_compatible(version),
c, version, jset, NULL,
jset_unsupported_version,
@ -956,6 +964,7 @@ static int jset_validate_early(struct bch_fs *c,
return -EINVAL;
}
size_t bytes = vstruct_bytes(jset);
if (bytes > (sectors_read << 9) &&
sectors_read < bucket_sectors_left)
return JOURNAL_ENTRY_REREAD;
@ -1240,8 +1249,6 @@ int bch2_journal_read(struct bch_fs *c,
* those entries will be blacklisted:
*/
genradix_for_each_reverse(&c->journal_entries, radix_iter, _i) {
enum bch_validate_flags flags = BCH_VALIDATE_journal;
i = *_i;
if (journal_replay_ignore(i))
@ -1261,6 +1268,10 @@ int bch2_journal_read(struct bch_fs *c,
continue;
}
struct bkey_validate_context from = {
.from = BKEY_VALIDATE_journal,
.journal_seq = le64_to_cpu(i->j.seq),
};
if (journal_entry_err_on(le64_to_cpu(i->j.last_seq) > le64_to_cpu(i->j.seq),
c, le32_to_cpu(i->j.version), &i->j, NULL,
jset_last_seq_newer_than_seq,

View File

@ -63,7 +63,7 @@ static inline struct jset_entry *__jset_entry_type_next(struct jset *jset,
int bch2_journal_entry_validate(struct bch_fs *, struct jset *,
struct jset_entry *, unsigned, int,
enum bch_validate_flags);
struct bkey_validate_context);
void bch2_journal_entry_to_text(struct printbuf *, struct bch_fs *,
struct jset_entry *);

View File

@ -23,6 +23,10 @@
int bch2_sb_clean_validate_late(struct bch_fs *c, struct bch_sb_field_clean *clean,
int write)
{
struct bkey_validate_context from = {
.flags = write,
.from = BKEY_VALIDATE_superblock,
};
struct jset_entry *entry;
int ret;
@ -40,7 +44,7 @@ int bch2_sb_clean_validate_late(struct bch_fs *c, struct bch_sb_field_clean *cle
ret = bch2_journal_entry_validate(c, NULL, entry,
le16_to_cpu(c->disk_sb.sb->version),
BCH_SB_BIG_ENDIAN(c->disk_sb.sb),
write);
from);
if (ret)
return ret;
}