Commit c23a9e08 authored by Kent Overstreet's avatar Kent Overstreet

bcachefs: Improve jset_validate()

Previously, jset_validate() was formatting the initial part of an error
string for every entry it validating - expensive.

This moves that code to journal_entry_err_msg(), which is now only
called if there's an actual error.
Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 3f3bc66e
......@@ -186,30 +186,57 @@ static void journal_entry_null_range(void *start, void *end)
#define JOURNAL_ENTRY_NONE 6
#define JOURNAL_ENTRY_BAD 7
#define journal_entry_err(c, msg, ...) \
static void journal_entry_err_msg(struct printbuf *out,
struct jset *jset,
struct jset_entry *entry)
{
prt_str(out, "invalid journal entry ");
if (entry)
prt_printf(out, "%s ", bch2_jset_entry_types[entry->type]);
if (!jset)
prt_printf(out, "in superblock");
else if (!entry)
prt_printf(out, "at seq %llu", le64_to_cpu(jset->seq));
else
prt_printf(out, "at offset %zi/%u seq %llu",
(u64 *) entry - jset->_data,
le32_to_cpu(jset->u64s),
le64_to_cpu(jset->seq));
prt_str(out, ": ");
}
#define journal_entry_err(c, jset, entry, msg, ...) \
({ \
struct printbuf buf = PRINTBUF; \
\
journal_entry_err_msg(&buf, jset, entry); \
prt_printf(&buf, msg, ##__VA_ARGS__); \
\
switch (write) { \
case READ: \
mustfix_fsck_err(c, msg, ##__VA_ARGS__); \
mustfix_fsck_err(c, "%s", buf.buf); \
break; \
case WRITE: \
bch_err(c, "corrupt metadata before write:\n" \
msg, ##__VA_ARGS__); \
bch_err(c, "corrupt metadata before write: %s\n", buf.buf);\
if (bch2_fs_inconsistent(c)) { \
ret = -BCH_ERR_fsck_errors_not_fixed; \
goto fsck_err; \
} \
break; \
} \
\
printbuf_exit(&buf); \
true; \
})
#define journal_entry_err_on(cond, c, msg, ...) \
((cond) ? journal_entry_err(c, msg, ##__VA_ARGS__) : false)
#define journal_entry_err_on(cond, c, jset, entry, msg, ...) \
((cond) ? journal_entry_err(c, jset, entry, msg, ##__VA_ARGS__) : false)
#define FSCK_DELETED_KEY 5
static int journal_validate_key(struct bch_fs *c, const char *where,
static int journal_validate_key(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned level, enum btree_id btree_id,
struct bkey_i *k,
......@@ -219,33 +246,24 @@ static int journal_validate_key(struct bch_fs *c, const char *where,
struct printbuf buf = PRINTBUF;
int ret = 0;
if (journal_entry_err_on(!k->k.u64s, c,
"invalid key in %s at %s offset %zi/%u: k->u64s 0",
bch2_jset_entry_types[entry->type], where,
(u64 *) k - entry->_data,
le16_to_cpu(entry->u64s))) {
if (journal_entry_err_on(!k->k.u64s, c, jset, entry, "k->u64s 0")) {
entry->u64s = cpu_to_le16((u64 *) k - entry->_data);
journal_entry_null_range(vstruct_next(entry), next);
return FSCK_DELETED_KEY;
}
if (journal_entry_err_on((void *) bkey_next(k) >
(void *) vstruct_next(entry), c,
"invalid key in %s at %s offset %zi/%u: extends past end of journal entry",
bch2_jset_entry_types[entry->type], where,
(u64 *) k - entry->_data,
le16_to_cpu(entry->u64s))) {
(void *) vstruct_next(entry),
c, jset, entry,
"extends past end of journal entry")) {
entry->u64s = cpu_to_le16((u64 *) k - entry->_data);
journal_entry_null_range(vstruct_next(entry), next);
return FSCK_DELETED_KEY;
}
if (journal_entry_err_on(k->k.format != KEY_FORMAT_CURRENT, c,
"invalid key in %s at %s offset %zi/%u: bad format %u",
bch2_jset_entry_types[entry->type], where,
(u64 *) k - entry->_data,
le16_to_cpu(entry->u64s),
k->k.format)) {
if (journal_entry_err_on(k->k.format != KEY_FORMAT_CURRENT,
c, jset, entry,
"bad format %u", k->k.format)) {
le16_add_cpu(&entry->u64s, -((u16) k->k.u64s));
memmove(k, bkey_next(k), next - (void *) bkey_next(k));
journal_entry_null_range(vstruct_next(entry), next);
......@@ -259,10 +277,11 @@ static int journal_validate_key(struct bch_fs *c, const char *where,
if (bch2_bkey_invalid(c, bkey_i_to_s_c(k),
__btree_node_type(level, btree_id), write, &buf)) {
printbuf_reset(&buf);
prt_printf(&buf, "invalid key in %s at %s offset %zi/%u:",
bch2_jset_entry_types[entry->type], where,
(u64 *) k - entry->_data,
le16_to_cpu(entry->u64s));
prt_printf(&buf, "invalid journal entry %s at offset %zi/%u seq %llu:",
bch2_jset_entry_types[entry->type],
(u64 *) entry - jset->_data,
le32_to_cpu(jset->u64s),
le64_to_cpu(jset->seq));
prt_newline(&buf);
printbuf_indent_add(&buf, 2);
......@@ -290,14 +309,14 @@ static int journal_validate_key(struct bch_fs *c, const char *where,
}
static int journal_entry_btree_keys_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
struct bkey_i *k = entry->start;
while (k != vstruct_last(entry)) {
int ret = journal_validate_key(c, where, entry,
int ret = journal_validate_key(c, jset, entry,
entry->level,
entry->btree_id,
k, version, big_endian, write);
......@@ -328,7 +347,7 @@ static void journal_entry_btree_keys_to_text(struct printbuf *out, struct bch_fs
}
static int journal_entry_btree_root_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
......@@ -336,7 +355,8 @@ static int journal_entry_btree_root_validate(struct bch_fs *c,
int ret = 0;
if (journal_entry_err_on(!entry->u64s ||
le16_to_cpu(entry->u64s) != k->k.u64s, c,
le16_to_cpu(entry->u64s) != k->k.u64s,
c, jset, entry,
"invalid btree root journal entry: wrong number of keys")) {
void *next = vstruct_next(entry);
/*
......@@ -349,7 +369,7 @@ static int journal_entry_btree_root_validate(struct bch_fs *c,
return 0;
}
return journal_validate_key(c, where, entry, 1, entry->btree_id, k,
return journal_validate_key(c, jset, entry, 1, entry->btree_id, k,
version, big_endian, write);
fsck_err:
return ret;
......@@ -362,7 +382,7 @@ static void journal_entry_btree_root_to_text(struct printbuf *out, struct bch_fs
}
static int journal_entry_prio_ptrs_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
......@@ -376,13 +396,14 @@ static void journal_entry_prio_ptrs_to_text(struct printbuf *out, struct bch_fs
}
static int journal_entry_blacklist_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
int ret = 0;
if (journal_entry_err_on(le16_to_cpu(entry->u64s) != 1, c,
if (journal_entry_err_on(le16_to_cpu(entry->u64s) != 1,
c, jset, entry,
"invalid journal seq blacklist entry: bad size")) {
journal_entry_null_range(entry, vstruct_next(entry));
}
......@@ -400,14 +421,15 @@ static void journal_entry_blacklist_to_text(struct printbuf *out, struct bch_fs
}
static int journal_entry_blacklist_v2_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
struct jset_entry_blacklist_v2 *bl_entry;
int ret = 0;
if (journal_entry_err_on(le16_to_cpu(entry->u64s) != 2, c,
if (journal_entry_err_on(le16_to_cpu(entry->u64s) != 2,
c, jset, entry,
"invalid journal seq blacklist entry: bad size")) {
journal_entry_null_range(entry, vstruct_next(entry));
goto out;
......@@ -416,7 +438,8 @@ static int journal_entry_blacklist_v2_validate(struct bch_fs *c,
bl_entry = container_of(entry, struct jset_entry_blacklist_v2, entry);
if (journal_entry_err_on(le64_to_cpu(bl_entry->start) >
le64_to_cpu(bl_entry->end), c,
le64_to_cpu(bl_entry->end),
c, jset, entry,
"invalid journal seq blacklist entry: start > end")) {
journal_entry_null_range(entry, vstruct_next(entry));
}
......@@ -437,7 +460,7 @@ static void journal_entry_blacklist_v2_to_text(struct printbuf *out, struct bch_
}
static int journal_entry_usage_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
......@@ -447,7 +470,7 @@ static int journal_entry_usage_validate(struct bch_fs *c,
int ret = 0;
if (journal_entry_err_on(bytes < sizeof(*u),
c,
c, jset, entry,
"invalid journal entry usage: bad size")) {
journal_entry_null_range(entry, vstruct_next(entry));
return ret;
......@@ -469,7 +492,7 @@ static void journal_entry_usage_to_text(struct printbuf *out, struct bch_fs *c,
}
static int journal_entry_data_usage_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
......@@ -480,7 +503,7 @@ static int journal_entry_data_usage_validate(struct bch_fs *c,
if (journal_entry_err_on(bytes < sizeof(*u) ||
bytes < sizeof(*u) + u->r.nr_devs,
c,
c, jset, entry,
"invalid journal entry usage: bad size")) {
journal_entry_null_range(entry, vstruct_next(entry));
return ret;
......@@ -501,7 +524,7 @@ static void journal_entry_data_usage_to_text(struct printbuf *out, struct bch_fs
}
static int journal_entry_clock_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
......@@ -511,13 +534,13 @@ static int journal_entry_clock_validate(struct bch_fs *c,
int ret = 0;
if (journal_entry_err_on(bytes != sizeof(*clock),
c, "invalid journal entry clock: bad size")) {
c, jset, entry, "bad size")) {
journal_entry_null_range(entry, vstruct_next(entry));
return ret;
}
if (journal_entry_err_on(clock->rw > 1,
c, "invalid journal entry clock: bad rw")) {
c, jset, entry, "bad rw")) {
journal_entry_null_range(entry, vstruct_next(entry));
return ret;
}
......@@ -536,7 +559,7 @@ static void journal_entry_clock_to_text(struct printbuf *out, struct bch_fs *c,
}
static int journal_entry_dev_usage_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
......@@ -548,7 +571,7 @@ static int journal_entry_dev_usage_validate(struct bch_fs *c,
int ret = 0;
if (journal_entry_err_on(bytes < expected,
c, "invalid journal entry dev usage: bad size (%u < %u)",
c, jset, entry, "bad size (%u < %u)",
bytes, expected)) {
journal_entry_null_range(entry, vstruct_next(entry));
return ret;
......@@ -557,13 +580,13 @@ static int journal_entry_dev_usage_validate(struct bch_fs *c,
dev = le32_to_cpu(u->dev);
if (journal_entry_err_on(!bch2_dev_exists2(c, dev),
c, "invalid journal entry dev usage: bad dev")) {
c, jset, entry, "bad dev")) {
journal_entry_null_range(entry, vstruct_next(entry));
return ret;
}
if (journal_entry_err_on(u->pad,
c, "invalid journal entry dev usage: bad pad")) {
c, jset, entry, "bad pad")) {
journal_entry_null_range(entry, vstruct_next(entry));
return ret;
}
......@@ -596,7 +619,7 @@ static void journal_entry_dev_usage_to_text(struct printbuf *out, struct bch_fs
}
static int journal_entry_log_validate(struct bch_fs *c,
const char *where,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
......@@ -612,11 +635,12 @@ static void journal_entry_log_to_text(struct printbuf *out, struct bch_fs *c,
prt_printf(out, "%.*s", bytes, l->d);
}
static int journal_entry_overwrite_validate(struct bch_fs *c, const char *where,
static int journal_entry_overwrite_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
return journal_entry_btree_keys_validate(c, where, entry, version, big_endian, write);
return journal_entry_btree_keys_validate(c, jset, entry, version, big_endian, write);
}
static void journal_entry_overwrite_to_text(struct printbuf *out, struct bch_fs *c,
......@@ -626,7 +650,7 @@ static void journal_entry_overwrite_to_text(struct printbuf *out, struct bch_fs
}
struct jset_entry_ops {
int (*validate)(struct bch_fs *, const char *,
int (*validate)(struct bch_fs *, struct jset *,
struct jset_entry *, unsigned, int, int);
void (*to_text)(struct printbuf *, struct bch_fs *, struct jset_entry *);
};
......@@ -641,12 +665,13 @@ static const struct jset_entry_ops bch2_jset_entry_ops[] = {
#undef x
};
int bch2_journal_entry_validate(struct bch_fs *c, const char *where,
int bch2_journal_entry_validate(struct bch_fs *c,
struct jset *jset,
struct jset_entry *entry,
unsigned version, int big_endian, int write)
{
return entry->type < BCH_JSET_ENTRY_NR
? bch2_jset_entry_ops[entry->type].validate(c, where, entry,
? bch2_jset_entry_ops[entry->type].validate(c, jset, entry,
version, big_endian, write)
: 0;
}
......@@ -665,24 +690,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,
int write)
{
char buf[100];
struct jset_entry *entry;
int ret = 0;
vstruct_for_each(jset, entry) {
scnprintf(buf, sizeof(buf), "jset %llu entry offset %zi/%u",
le64_to_cpu(jset->seq),
(u64 *) entry - jset->_data,
le32_to_cpu(jset->u64s));
if (journal_entry_err_on(vstruct_next(entry) >
vstruct_last(jset), c,
vstruct_last(jset), c, jset, entry,
"journal entry extends past end of jset")) {
jset->u64s = cpu_to_le32((u64 *) entry - jset->_data);
break;
}
ret = bch2_journal_entry_validate(c, buf, entry,
ret = bch2_journal_entry_validate(c, jset, entry,
le32_to_cpu(jset->version),
JSET_BIG_ENDIAN(jset), write);
if (ret)
......@@ -710,7 +729,8 @@ static int jset_validate(struct bch_fs *c,
version = le32_to_cpu(jset->version);
if (journal_entry_err_on((version != BCH_JSET_VERSION_OLD &&
version < bcachefs_metadata_version_min) ||
version >= bcachefs_metadata_version_max, c,
version >= bcachefs_metadata_version_max,
c, jset, NULL,
"%s sector %llu seq %llu: unknown journal entry version %u",
ca ? ca->name : c->name,
sector, le64_to_cpu(jset->seq),
......@@ -723,7 +743,8 @@ static int jset_validate(struct bch_fs *c,
sectors_read < bucket_sectors_left)
return JOURNAL_ENTRY_REREAD;
if (journal_entry_err_on(bytes > bucket_sectors_left << 9, c,
if (journal_entry_err_on(bytes > bucket_sectors_left << 9,
c, jset, NULL,
"%s sector %llu seq %llu: journal entry too big (%zu bytes)",
ca ? ca->name : c->name,
sector, le64_to_cpu(jset->seq), bytes)) {
......@@ -732,7 +753,8 @@ static int jset_validate(struct bch_fs *c,
-((bytes - (bucket_sectors_left << 9)) / 8));
}
if (journal_entry_err_on(!bch2_checksum_type_valid(c, JSET_CSUM_TYPE(jset)), c,
if (journal_entry_err_on(!bch2_checksum_type_valid(c, JSET_CSUM_TYPE(jset)),
c, jset, NULL,
"%s sector %llu seq %llu: journal entry with unknown csum type %llu",
ca ? ca->name : c->name,
sector, le64_to_cpu(jset->seq),
......@@ -745,7 +767,8 @@ static int jset_validate(struct bch_fs *c,
goto csum_done;
csum = csum_vstruct(c, JSET_CSUM_TYPE(jset), journal_nonce(jset), jset);
if (journal_entry_err_on(bch2_crc_cmp(csum, jset->csum), c,
if (journal_entry_err_on(bch2_crc_cmp(csum, jset->csum),
c, jset, NULL,
"%s sector %llu seq %llu: journal checksum bad",
ca ? ca->name : c->name,
sector, le64_to_cpu(jset->seq)))
......@@ -759,7 +782,8 @@ static int jset_validate(struct bch_fs *c,
csum_done:
/* last_seq is ignored when JSET_NO_FLUSH is true */
if (journal_entry_err_on(!JSET_NO_FLUSH(jset) &&
le64_to_cpu(jset->last_seq) > le64_to_cpu(jset->seq), c,
le64_to_cpu(jset->last_seq) > le64_to_cpu(jset->seq),
c, jset, NULL,
"invalid journal entry: last_seq > seq (%llu > %llu)",
le64_to_cpu(jset->last_seq),
le64_to_cpu(jset->seq))) {
......
......@@ -44,7 +44,7 @@ static inline struct jset_entry *__jset_entry_type_next(struct jset *jset,
for_each_jset_entry_type(entry, jset, BCH_JSET_ENTRY_btree_keys) \
vstruct_for_each_safe(entry, k, _n)
int bch2_journal_entry_validate(struct bch_fs *, const char *,
int bch2_journal_entry_validate(struct bch_fs *, struct jset *,
struct jset_entry *, unsigned, int, int);
void bch2_journal_entry_to_text(struct printbuf *, struct bch_fs *,
struct jset_entry *);
......
......@@ -1161,7 +1161,7 @@ int bch2_sb_clean_validate_late(struct bch_fs *c, struct bch_sb_field_clean *cle
for (entry = clean->start;
entry < (struct jset_entry *) vstruct_end(&clean->field);
entry = vstruct_next(entry)) {
ret = bch2_journal_entry_validate(c, "superblock", entry,
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment