if (!(c->sb.btrees_lost_data & b)) {
prt_printf(msg, "flagging btree ");
bch2_btree_id_to_text(msg, btree);
prt_printf(msg, " lost data\n");
ext->btrees_lost_data |= cpu_to_le64(b);
}
/* Once we have runtime self healing for topology errors we won't need this: */
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_topology, 0) ?: ret;
/* Btree node accounting will be off: */
__set_bit_le64(BCH_FSCK_ERR_accounting_mismatch, ext->errors_silent);
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_allocations, 0) ?: ret;
#ifdef CONFIG_BCACHEFS_DEBUG /* * These are much more minor, and don't need to be corrected right away, * but in debug mode we want the next fsck run to be clean:
*/
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_lrus, 0) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_backpointers_to_extents, 0) ?: ret; #endif
switch (btree) { case BTREE_ID_alloc:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret;
__set_bit_le64(BCH_FSCK_ERR_alloc_key_data_type_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_gen_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_dirty_sectors_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_cached_sectors_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_stripe_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_stripe_redundancy_wrong, ext->errors_silent); goto out; case BTREE_ID_backpointers:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_btree_backpointers, 0) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_extents_to_backpointers, 0) ?: ret; goto out; case BTREE_ID_need_discard:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret; goto out; case BTREE_ID_freespace:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret; goto out; case BTREE_ID_bucket_gens:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret; goto out; case BTREE_ID_lru:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret; goto out; case BTREE_ID_accounting:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_allocations, 0) ?: ret; goto out; case BTREE_ID_snapshots:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_reconstruct_snapshots, 0) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_topology, 0) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_scan_for_btree_nodes, 0) ?: ret; goto out; default:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_topology, 0) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_scan_for_btree_nodes, 0) ?: ret; goto out;
}
out:
bch2_write_super(c);
mutex_unlock(&c->sb_lock);
for (unsigned i = 0; i < btree_id_nr_alive(c); i++) if (btree_id_is_alloc(i))
kill_btree(c, i);
}
/* * Btree node pointers have a field to stack a pointer to the in memory btree * node; we need to zero out this field when reading in btree nodes, or when * reading in keys from the journal:
*/ staticvoid zero_out_btree_mem_ptr(struct journal_keys *keys)
{
darray_for_each(*keys, i) if (i->k->k.type == KEY_TYPE_btree_ptr_v2)
bkey_i_to_btree_ptr_v2(i->k)->v.mem_ptr = 0;
}
/* * BTREE_UPDATE_key_cache_reclaim disables key cache lookup/update to * keep the key cache coherent with the underlying btree. Nothing * besides the allocator is doing updates yet so we don't need key cache * coherency for non-alloc btrees, and key cache fills for snapshots * btrees use BTREE_ITER_filter_snapshots, which isn't available until * the snapshots recovery pass runs.
*/ if (!k->level && k->btree_id == BTREE_ID_alloc)
iter_flags |= BTREE_ITER_cached; else
update_flags |= BTREE_UPDATE_key_cache_reclaim;
bch2_trans_node_iter_init(trans, &iter, k->btree_id, k->k->k.p,
BTREE_MAX_DEPTH, k->level,
iter_flags);
ret = bch2_btree_iter_traverse(trans, &iter); if (ret) goto out;
if (!(c->recovery.passes_complete & (BIT_ULL(BCH_RECOVERY_PASS_scan_for_btree_nodes)|
BIT_ULL(BCH_RECOVERY_PASS_check_topology)))) {
bch_err(c, "have key in journal replay for btree depth that does not exist, confused\n%s",
buf.buf);
ret = -EINVAL;
}
if (!k->allocated) {
bch_notice(c, "dropping key in journal replay for depth that does not exist because we're recovering from scan\n%s",
buf.buf);
k->overwritten = true; goto out;
}
/* * Map 0 to U64_MAX, so that keys with journal_seq === 0 come last * * journal_seq == 0 means that the key comes from early repair, and * should be inserted last so as to avoid overflowing the journal
*/ return cmp_int(l->journal_seq - 1, r->journal_seq - 1);
}
/* * First, attempt to replay keys in sorted order. This is more * efficient - better locality of btree access - but some might fail if * that would cause a journal deadlock.
*/
darray_for_each(*keys, k) {
cond_resched();
/* * k->allocated means the key wasn't read in from the journal, * rather it was from early repair code
*/ if (k->allocated)
immediate_flush = true;
/* Skip fastpath if we're low on space in the journal */
ret = c->journal.watermark ? -1 :
commit_do(trans, NULL, NULL,
BCH_TRANS_COMMIT_no_enospc|
BCH_TRANS_COMMIT_journal_reclaim|
BCH_TRANS_COMMIT_skip_accounting_apply|
(!k->allocated ? BCH_TRANS_COMMIT_no_journal_res : 0),
bch2_journal_replay_key(trans, k));
BUG_ON(!ret && !k->overwritten && k->k->k.type != KEY_TYPE_accounting); if (ret) {
ret = darray_push(&keys_sorted, k); if (ret) goto err;
}
}
bch2_trans_unlock_long(trans); /* * Now, replay any remaining keys in the order in which they appear in * the journal, unpinning those journal entries as we go:
*/
sort_nonatomic(keys_sorted.data, keys_sorted.nr, sizeof(keys_sorted.data[0]),
journal_sort_seq_cmp, NULL);
/* * We need to put our btree_trans before calling flush_all_pins(), since * that will use a btree_trans internally
*/
bch2_trans_put(trans);
trans = NULL;
if (!c->opts.retain_recovery_info &&
c->recovery.pass_done >= BCH_RECOVERY_PASS_journal_replay)
bch2_journal_keys_put_initial(c);
/* if we did any repair, flush it immediately */ if (immediate_flush) {
bch2_journal_flush_all_pins(&c->journal);
ret = bch2_journal_meta(&c->journal);
}
if (keys->nr)
bch2_journal_log_msg(c, "journal replay finished");
err: if (trans)
bch2_trans_put(trans);
darray_exit(&keys_sorted);
bch_err_fn(c, ret); return ret;
}
/* journal replay early: */
staticint journal_replay_entry_early(struct bch_fs *c, struct jset_entry *entry)
{ int ret = 0;
switch (entry->type) { case BCH_JSET_ENTRY_btree_root: {
if (unlikely(!entry->u64s)) return 0;
if (fsck_err_on(entry->btree_id >= BTREE_ID_NR_MAX,
c, invalid_btree_id, "invalid btree id %u (max %u)",
entry->btree_id, BTREE_ID_NR_MAX)) return 0;
while (entry->btree_id >= c->btree_roots_extra.nr + BTREE_ID_NR) {
ret = darray_push(&c->btree_roots_extra, (struct btree_root) { NULL }); if (ret) return ret;
}
prt_str(&buf, "Now allowing incompatible features up to ");
bch2_version_to_text(&buf, new_version);
prt_str(&buf, ", previously allowed up to ");
bch2_version_to_text(&buf, c->sb.version_incompat_allowed);
prt_newline(&buf);
if (ret)
bch2_sb_upgrade(c, new_version,
c->opts.version_upgrade == BCH_VERSION_UPGRADE_incompatible);
return ret;
}
int bch2_fs_recovery(struct bch_fs *c)
{ struct bch_sb_field_clean *clean = NULL; struct jset *last_journal_entry = NULL;
u64 last_seq = 0, blacklist_seq, journal_seq; int ret = 0;
if (c->sb.clean) {
clean = bch2_read_superblock_clean(c);
ret = PTR_ERR_OR_ZERO(clean); if (ret) goto err;
bch_info(c, "recovering from clean shutdown, journal seq %llu",
le64_to_cpu(clean->journal_seq));
} else {
bch_info(c, "recovering from unclean shutdown");
}
if (!(c->sb.features & (1ULL << BCH_FEATURE_new_extent_overwrite))) {
bch_err(c, "feature new_extent_overwrite not set, filesystem no longer supported");
ret = -EINVAL; goto err;
}
if (!c->sb.clean &&
!(c->sb.features & (1ULL << BCH_FEATURE_extents_above_btree_updates))) {
bch_err(c, "filesystem needs recovery from older version; run fsck from older bcachefs-tools to fix");
ret = -EINVAL; goto err;
}
if (go_rw_in_recovery(c)) { /* * start workqueues/kworkers early - kthread creation checks for * pending signals, which is _very_ annoying
*/
ret = bch2_fs_init_rw(c); if (ret) goto err;
}
if (write_sb)
bch2_write_super(c);
mutex_unlock(&c->sb_lock);
if (c->sb.clean)
set_bit(BCH_FS_clean_recovery, &c->flags); if (c->opts.fsck)
set_bit(BCH_FS_in_fsck, &c->flags);
set_bit(BCH_FS_in_recovery, &c->flags);
ret = bch2_blacklist_table_initialize(c); if (ret) {
bch_err(c, "error initializing blacklist table"); goto err;
}
bch_verbose(c, "starting journal read");
ret = bch2_journal_read(c, &last_seq, &blacklist_seq, &journal_seq); if (ret) goto err;
/* * note: cmd_list_journal needs the blacklist table fully up to date so * it can asterisk ignored journal entries:
*/ if (c->opts.read_journal_only) goto out;
if (mustfix_fsck_err_on(c->sb.clean &&
last_journal_entry &&
!journal_entry_empty(last_journal_entry), c,
clean_but_journal_not_empty, "filesystem marked clean but journal not empty")) {
c->sb.compat &= ~(1ULL << BCH_COMPAT_alloc_info);
SET_BCH_SB_CLEAN(c->disk_sb.sb, false);
c->sb.clean = false;
}
if (!last_journal_entry) {
fsck_err_on(!c->sb.clean, c,
dirty_but_no_journal_entries, "no journal entries found"); if (clean) goto use_clean;
genradix_for_each_reverse(&c->journal_entries, iter, i) if (*i) {
last_journal_entry = &(*i)->j;
(*i)->ignore_blacklisted = false;
(*i)->ignore_not_dirty= false; /* * This was probably a NO_FLUSH entry, * so last_seq was garbage - but we know * we're only using a single journal * entry, set it here:
*/
(*i)->j.last_seq = (*i)->j.seq; break;
}
}
ret = bch2_journal_keys_sort(c); if (ret) goto err;
if (c->sb.clean && last_journal_entry) {
ret = bch2_verify_superblock_clean(c, &clean,
last_journal_entry); if (ret) goto err;
}
} else {
use_clean: if (!clean) {
bch_err(c, "no superblock clean section found");
ret = bch_err_throw(c, fsck_repair_impossible); goto err;
ret = journal_replay_early(c, clean); if (ret) goto err;
ret = bch2_fs_resize_on_mount(c); if (ret) {
up_write(&c->state_lock); goto err;
}
if (c->sb.features & BIT_ULL(BCH_FEATURE_small_image)) {
bch_info(c, "filesystem is an unresized image file, mounting ro");
c->opts.read_only = true;
}
if (!c->opts.read_only &&
(c->sb.features & BIT_ULL(BCH_FEATURE_no_alloc_info))) {
bch_info(c, "mounting a filesystem with no alloc info read-write; will recreate");
bch2_reconstruct_alloc(c);
} elseif (c->opts.reconstruct_alloc) {
bch2_journal_log_msg(c, "dropping alloc info");
bch_info(c, "dropping and reconstructing all alloc info");
bch2_reconstruct_alloc(c);
}
if (c->sb.features & BIT_ULL(BCH_FEATURE_no_alloc_info)) { /* We can't go RW to fix errors without alloc info */ if (c->opts.fix_errors == FSCK_FIX_yes ||
c->opts.fix_errors == FSCK_FIX_ask)
c->opts.fix_errors = FSCK_FIX_no; if (c->opts.errors == BCH_ON_ERROR_fix_safe)
c->opts.errors = BCH_ON_ERROR_continue;
}
/* * After an unclean shutdown, skip then next few journal sequence * numbers as they may have been referenced by btree writes that * happened before their corresponding journal writes - those btree * writes need to be ignored, by skipping and blacklisting the next few * journal sequence numbers:
*/ if (!c->sb.clean)
journal_seq += JOURNAL_BUF_NR * 4;
if (blacklist_seq != journal_seq) {
ret = bch2_journal_log_msg(c, "blacklisting entries %llu-%llu",
blacklist_seq, journal_seq) ?:
bch2_journal_seq_blacklist_add(c,
blacklist_seq, journal_seq); if (ret) {
bch_err_msg(c, ret, "error creating new journal seq blacklist entry"); goto err;
}
}
ret = bch2_journal_log_msg(c, "starting journal at entry %llu, replaying %llu-%llu",
journal_seq, last_seq, blacklist_seq - 1) ?:
bch2_fs_journal_start(&c->journal, last_seq, journal_seq); if (ret) goto err;
/* * Skip past versions that might have possibly been used (as nonces), * but hadn't had their pointers written:
*/ if (c->sb.encryption_type && !c->sb.clean)
atomic64_add(1 << 16, &c->key_version);
ret = read_btree_roots(c); if (ret) goto err;
set_bit(BCH_FS_btree_running, &c->flags);
ret = bch2_sb_set_upgrade_extra(c); if (ret) goto err;
ret = bch2_run_recovery_passes(c, 0); if (ret) goto err;
/* * Normally set by the appropriate recovery pass: when cleared, this * indicates we're in early recovery and btree updates should be done by * being applied to the journal replay keys. _Must_ be cleared before * multithreaded use:
*/
set_bit(BCH_FS_may_go_rw, &c->flags);
clear_bit(BCH_FS_in_fsck, &c->flags);
/* in case we don't run journal replay, i.e. norecovery mode */
set_bit(BCH_FS_accounting_replay_done, &c->flags);
bch2_async_btree_node_rewrites_flush(c);
/* fsync if we fixed errors */ if (test_bit(BCH_FS_errors_fixed, &c->flags)) {
bch2_journal_flush_all_pins(&c->journal);
bch2_journal_meta(&c->journal);
}
/* If we fixed errors, verify that fs is actually clean now: */ if (IS_ENABLED(CONFIG_BCACHEFS_DEBUG) &&
test_bit(BCH_FS_errors_fixed, &c->flags) &&
!test_bit(BCH_FS_errors_not_fixed, &c->flags) &&
!test_bit(BCH_FS_error, &c->flags)) {
bch2_flush_fsck_errs(c);
bch_info(c, "Fixed errors, running fsck a second time to verify fs is clean");
clear_bit(BCH_FS_errors_fixed, &c->flags);
ret = bch2_run_recovery_passes(c,
BCH_RECOVERY_PASS_check_alloc_info); if (ret) goto err;
if (test_bit(BCH_FS_errors_fixed, &c->flags) ||
test_bit(BCH_FS_errors_not_fixed, &c->flags)) {
bch_err(c, "Second fsck run was not clean");
set_bit(BCH_FS_errors_not_fixed, &c->flags);
}
set_bit(BCH_FS_errors_fixed, &c->flags);
}
if (enabled_qtypes(c)) {
bch_verbose(c, "reading quotas");
ret = bch2_fs_quota_read(c); if (ret) goto err;
bch_verbose(c, "quotas done");
}
for (unsigned i = 0; i < BTREE_ID_NR; i++)
bch2_btree_root_alloc_fake(c, i, 0);
ret = bch2_fs_journal_alloc(c); if (ret) goto err;
/* * journal_res_get() will crash if called before this has * set up the journal.pin FIFO and journal.cur pointer:
*/
ret = bch2_fs_journal_start(&c->journal, 1, 1); if (ret) goto err;
ret = bch2_fs_read_write_early(c); if (ret) goto err;
for_each_member_device(c, ca) {
ret = bch2_dev_usage_init(ca, false); if (ret) {
bch2_dev_put(ca); goto err;
}
}
/* * Write out the superblock and journal buckets, now that we can do * btree updates
*/
bch_verbose(c, "marking superblocks");
ret = bch2_trans_mark_dev_sbs(c);
bch_err_msg(c, ret, "marking superblocks"); if (ret) goto err;
ret = bch2_fs_freespace_init(c); if (ret) goto err;
ret = bch2_initialize_subvolumes(c); if (ret) goto err;
bch_verbose(c, "reading snapshots table");
ret = bch2_snapshots_read(c); if (ret) goto err;
bch_verbose(c, "reading snapshots done");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.