]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/recovery.c
Update bcachefs sources to 021e62a098 bcachefs: Fix error in filesystem initialization
[bcachefs-tools-debian] / libbcachefs / recovery.c
index a94f25cca679d3b22e385056d0c6b45a16fa8432..0b3521c9cc19ef3ba285805a05de94bcc8bfd717 100644 (file)
 
 #define QSTR(n) { { { .len = strlen(n) } }, .name = n }
 
+/* for -o reconstruct_alloc: */
+static void drop_alloc_keys(struct journal_keys *keys)
+{
+       size_t src, dst;
+
+       for (src = 0, dst = 0; src < keys->nr; src++)
+               if (keys->d[src].btree_id != BTREE_ID_ALLOC)
+                       keys->d[dst++] = keys->d[src];
+
+       keys->nr = dst;
+}
+
 /* iterate over keys read from the journal: */
 
 static struct journal_key *journal_key_search(struct journal_keys *journal_keys,
@@ -188,7 +200,7 @@ void bch2_btree_and_journal_iter_init_node_iter(struct btree_and_journal_iter *i
        iter->b = b;
        bch2_btree_node_iter_init_from_start(&iter->node_iter, iter->b);
        bch2_journal_iter_init(&iter->journal, journal_keys,
-                              b->btree_id, b->level, b->data->min_key);
+                              b->c.btree_id, b->c.level, b->data->min_key);
 }
 
 /* Walk btree, overlaying keys from the journal: */
@@ -206,11 +218,11 @@ static int bch2_btree_and_journal_walk_recurse(struct bch_fs *c, struct btree *b
        bch2_btree_and_journal_iter_init_node_iter(&iter, journal_keys, b);
 
        while ((k = bch2_btree_and_journal_iter_peek(&iter)).k) {
-               ret = key_fn(c, btree_id, b->level, k);
+               ret = key_fn(c, btree_id, b->c.level, k);
                if (ret)
                        break;
 
-               if (b->level) {
+               if (b->c.level) {
                        struct btree *child;
                        BKEY_PADDED(k) tmp;
 
@@ -219,9 +231,9 @@ static int bch2_btree_and_journal_walk_recurse(struct bch_fs *c, struct btree *b
 
                        bch2_btree_and_journal_iter_advance(&iter);
 
-                       if (b->level > 0) {
+                       if (b->c.level > 0) {
                                child = bch2_btree_node_get_noiter(c, &tmp.k,
-                                                       b->btree_id, b->level - 1);
+                                                       b->c.btree_id, b->c.level - 1);
                                ret = PTR_ERR_OR_ZERO(child);
                                if (ret)
                                        break;
@@ -229,7 +241,7 @@ static int bch2_btree_and_journal_walk_recurse(struct bch_fs *c, struct btree *b
                                ret   = (node_fn ? node_fn(c, b) : 0) ?:
                                        bch2_btree_and_journal_walk_recurse(c, child,
                                                journal_keys, btree_id, node_fn, key_fn);
-                               six_unlock_read(&child->lock);
+                               six_unlock_read(&child->c.lock);
 
                                if (ret)
                                        break;
@@ -253,12 +265,12 @@ int bch2_btree_and_journal_walk(struct bch_fs *c, struct journal_keys *journal_k
        if (btree_node_fake(b))
                return 0;
 
-       six_lock_read(&b->lock);
+       six_lock_read(&b->c.lock, NULL, NULL);
        ret   = (node_fn ? node_fn(c, b) : 0) ?:
                bch2_btree_and_journal_walk_recurse(c, b, journal_keys, btree_id,
                                                    node_fn, key_fn) ?:
-               key_fn(c, btree_id, b->level + 1, bkey_i_to_s_c(&b->key));
-       six_unlock_read(&b->lock);
+               key_fn(c, btree_id, b->c.level + 1, bkey_i_to_s_c(&b->key));
+       six_unlock_read(&b->c.lock);
 
        return ret;
 }
@@ -292,17 +304,6 @@ static int journal_sort_key_cmp(const void *_l, const void *_r)
                cmp_int(l->journal_offset, r->journal_offset);
 }
 
-static int journal_sort_seq_cmp(const void *_l, const void *_r)
-{
-       const struct journal_key *l = _l;
-       const struct journal_key *r = _r;
-
-       return  cmp_int(r->level,       l->level) ?:
-               cmp_int(l->journal_seq, r->journal_seq) ?:
-               cmp_int(l->btree_id,    r->btree_id) ?:
-               bkey_cmp(l->k->k.p,     r->k->k.p);
-}
-
 void bch2_journal_keys_free(struct journal_keys *keys)
 {
        kvfree(keys->d);
@@ -319,20 +320,30 @@ static struct journal_keys journal_keys_sort(struct list_head *journal_entries)
        struct journal_key *src, *dst;
        size_t nr_keys = 0;
 
-       list_for_each_entry(p, journal_entries, list)
+       if (list_empty(journal_entries))
+               return keys;
+
+       keys.journal_seq_base =
+               le64_to_cpu(list_last_entry(journal_entries,
+                               struct journal_replay, list)->j.last_seq);
+
+       list_for_each_entry(p, journal_entries, list) {
+               if (le64_to_cpu(p->j.seq) < keys.journal_seq_base)
+                       continue;
+
                for_each_jset_key(k, _n, entry, &p->j)
                        nr_keys++;
+       }
 
-       keys.journal_seq_base =
-               le64_to_cpu(list_first_entry(journal_entries,
-                                            struct journal_replay,
-                                            list)->j.seq);
 
        keys.d = kvmalloc(sizeof(keys.d[0]) * nr_keys, GFP_KERNEL);
        if (!keys.d)
                goto err;
 
-       list_for_each_entry(p, journal_entries, list)
+       list_for_each_entry(p, journal_entries, list) {
+               if (le64_to_cpu(p->j.seq) < keys.journal_seq_base)
+                       continue;
+
                for_each_jset_key(k, _n, entry, &p->j)
                        keys.d[keys.nr++] = (struct journal_key) {
                                .btree_id       = entry->btree_id,
@@ -342,6 +353,7 @@ static struct journal_keys journal_keys_sort(struct list_head *journal_entries)
                                        keys.journal_seq_base,
                                .journal_offset = k->_data - p->j._data,
                        };
+       }
 
        sort(keys.d, keys.nr, sizeof(keys.d[0]), journal_sort_key_cmp, NULL);
 
@@ -442,11 +454,19 @@ retry:
                 * regular keys
                 */
                __bch2_btree_iter_set_pos(split_iter, split->k.p, false);
-               bch2_trans_update(&trans, split_iter, split, !remark
-                                 ? BTREE_TRIGGER_NORUN
-                                 : BTREE_TRIGGER_NOOVERWRITES);
+               bch2_trans_update(&trans, split_iter, split,
+                                 BTREE_TRIGGER_NORUN);
+               bch2_trans_iter_put(&trans, split_iter);
 
                bch2_btree_iter_set_pos(iter, split->k.p);
+
+               if (remark) {
+                       ret = bch2_trans_mark_key(&trans, bkey_i_to_s_c(split),
+                                                 0, split->k.size,
+                                                 BTREE_TRIGGER_INSERT);
+                       if (ret)
+                               goto err;
+               }
        } while (bkey_cmp(iter->pos, k->k.p) < 0);
 
        if (remark) {
@@ -462,6 +482,8 @@ retry:
                                BTREE_INSERT_LAZY_RW|
                                BTREE_INSERT_JOURNAL_REPLAY);
 err:
+       bch2_trans_iter_put(&trans, iter);
+
        if (ret == -EINTR)
                goto retry;
 
@@ -507,11 +529,48 @@ static int bch2_journal_replay_key(struct bch_fs *c, enum btree_id id,
                             __bch2_journal_replay_key(&trans, id, level, k));
 }
 
+static int __bch2_alloc_replay_key(struct btree_trans *trans, struct bkey_i *k)
+{
+       struct btree_iter *iter;
+       int ret;
+
+       iter = bch2_trans_get_iter(trans, BTREE_ID_ALLOC, k->k.p,
+                                  BTREE_ITER_CACHED|
+                                  BTREE_ITER_CACHED_NOFILL|
+                                  BTREE_ITER_INTENT);
+       ret =   PTR_ERR_OR_ZERO(iter) ?:
+               bch2_trans_update(trans, iter, k, BTREE_TRIGGER_NORUN);
+       bch2_trans_iter_put(trans, iter);
+       return ret;
+}
+
+static int bch2_alloc_replay_key(struct bch_fs *c, struct bkey_i *k)
+{
+       return bch2_trans_do(c, NULL, NULL,
+                            BTREE_INSERT_NOFAIL|
+                            BTREE_INSERT_USE_RESERVE|
+                            BTREE_INSERT_LAZY_RW|
+                            BTREE_INSERT_JOURNAL_REPLAY,
+                       __bch2_alloc_replay_key(&trans, k));
+}
+
+static int journal_sort_seq_cmp(const void *_l, const void *_r)
+{
+       const struct journal_key *l = _l;
+       const struct journal_key *r = _r;
+
+       return  cmp_int(r->level,       l->level) ?:
+               cmp_int(l->journal_seq, r->journal_seq) ?:
+               cmp_int(l->btree_id,    r->btree_id) ?:
+               bkey_cmp(l->k->k.p,     r->k->k.p);
+}
+
 static int bch2_journal_replay(struct bch_fs *c,
                               struct journal_keys keys)
 {
        struct journal *j = &c->journal;
        struct journal_key *i;
+       u64 seq;
        int ret;
 
        sort(keys.d, keys.nr, sizeof(keys.d[0]), journal_sort_seq_cmp, NULL);
@@ -519,26 +578,64 @@ static int bch2_journal_replay(struct bch_fs *c,
        if (keys.nr)
                replay_now_at(j, keys.journal_seq_base);
 
+       seq = j->replay_journal_seq;
+
+       /*
+        * First replay updates to the alloc btree - these will only update the
+        * btree key cache:
+        */
        for_each_journal_key(keys, i) {
-               if (!i->level)
-                       replay_now_at(j, keys.journal_seq_base + i->journal_seq);
+               cond_resched();
 
-               if (i->level)
-                       ret = bch2_journal_replay_key(c, i->btree_id, i->level, i->k);
-               if (i->btree_id == BTREE_ID_ALLOC)
+               if (!i->level && i->btree_id == BTREE_ID_ALLOC) {
+                       j->replay_journal_seq = keys.journal_seq_base + i->journal_seq;
                        ret = bch2_alloc_replay_key(c, i->k);
-               else if (i->k->k.size)
-                       ret = bch2_extent_replay_key(c, i->btree_id, i->k);
-               else
-                       ret = bch2_journal_replay_key(c, i->btree_id, i->level, i->k);
+                       if (ret)
+                               goto err;
+               }
+       }
 
-               if (ret) {
-                       bch_err(c, "journal replay: error %d while replaying key",
-                               ret);
-                       return ret;
+       /*
+        * Next replay updates to interior btree nodes:
+        */
+       for_each_journal_key(keys, i) {
+               cond_resched();
+
+               if (i->level) {
+                       j->replay_journal_seq = keys.journal_seq_base + i->journal_seq;
+                       ret = bch2_journal_replay_key(c, i->btree_id, i->level, i->k);
+                       if (ret)
+                               goto err;
                }
+       }
 
+       /*
+        * Now that the btree is in a consistent state, we can start journal
+        * reclaim (which will be flushing entries from the btree key cache back
+        * to the btree:
+        */
+       set_bit(BCH_FS_BTREE_INTERIOR_REPLAY_DONE, &c->flags);
+       set_bit(JOURNAL_RECLAIM_STARTED, &j->flags);
+       journal_reclaim_kick(j);
+
+       j->replay_journal_seq = seq;
+
+       /*
+        * Now replay leaf node updates:
+        */
+       for_each_journal_key(keys, i) {
                cond_resched();
+
+               if (i->level || i->btree_id == BTREE_ID_ALLOC)
+                       continue;
+
+               replay_now_at(j, keys.journal_seq_base + i->journal_seq);
+
+               ret = i->k->k.size
+                       ? bch2_extent_replay_key(c, i->btree_id, i->k)
+                       : bch2_journal_replay_key(c, i->btree_id, i->level, i->k);
+               if (ret)
+                       goto err;
        }
 
        replay_now_at(j, j->replay_journal_seq_end);
@@ -547,6 +644,9 @@ static int bch2_journal_replay(struct bch_fs *c,
        bch2_journal_set_replay_done(j);
        bch2_journal_flush_all_pins(j);
        return bch2_journal_error(j);
+err:
+       bch_err(c, "journal replay: error %d while replaying key", ret);
+       return ret;
 }
 
 static bool journal_empty(struct list_head *journal)
@@ -568,6 +668,9 @@ verify_journal_entries_not_blacklisted_or_missing(struct bch_fs *c,
        int ret = 0;
 
        list_for_each_entry(i, journal, list) {
+               if (le64_to_cpu(i->j.seq) < start_seq)
+                       continue;
+
                fsck_err_on(seq != le64_to_cpu(i->j.seq), c,
                        "journal entries %llu-%llu missing! (replaying %llu-%llu)",
                        seq, le64_to_cpu(i->j.seq) - 1,
@@ -758,9 +861,11 @@ static int verify_superblock_clean(struct bch_fs *c,
        }
 
        mustfix_fsck_err_on(j->read_clock != clean->read_clock, c,
-                       "superblock read clock doesn't match journal after clean shutdown");
+                       "superblock read clock %u doesn't match journal %u after clean shutdown",
+                       clean->read_clock, j->read_clock);
        mustfix_fsck_err_on(j->write_clock != clean->write_clock, c,
-                       "superblock read clock doesn't match journal after clean shutdown");
+                       "superblock write clock %u doesn't match journal %u after clean shutdown",
+                       clean->write_clock, j->write_clock);
 
        for (i = 0; i < BTREE_ID_NR; i++) {
                char buf1[200], buf2[200];
@@ -841,7 +946,6 @@ static int read_btree_roots(struct bch_fs *c)
                        continue;
                }
 
-
                if (r->error) {
                        __fsck_err(c, i == BTREE_ID_ALLOC
                                   ? FSCK_CAN_IGNORE : 0,
@@ -874,7 +978,7 @@ int bch2_fs_recovery(struct bch_fs *c)
        const char *err = "cannot allocate memory";
        struct bch_sb_field_clean *clean = NULL;
        u64 journal_seq;
-       bool wrote = false, write_sb = false;
+       bool write_sb = false, need_write_alloc = false;
        int ret;
 
        if (c->sb.clean)
@@ -887,7 +991,8 @@ int bch2_fs_recovery(struct bch_fs *c)
                bch_info(c, "recovering from clean shutdown, journal seq %llu",
                         le64_to_cpu(clean->journal_seq));
 
-       if (!c->replicas.entries) {
+       if (!c->replicas.entries ||
+           c->opts.rebuild_replicas) {
                bch_info(c, "building replicas info");
                set_bit(BCH_FS_REBUILD_REPLICAS, &c->flags);
        }
@@ -937,6 +1042,11 @@ int bch2_fs_recovery(struct bch_fs *c)
                goto err;
        }
 
+       if (c->opts.reconstruct_alloc) {
+               c->sb.compat &= ~(1ULL << BCH_COMPAT_FEAT_ALLOC_INFO);
+               drop_alloc_keys(&c->journal_keys);
+       }
+
        ret = journal_replay_early(c, clean, &c->journal_entries);
        if (ret)
                goto err;
@@ -951,6 +1061,11 @@ int bch2_fs_recovery(struct bch_fs *c)
                }
 
                journal_seq += 4;
+
+               /*
+                * The superblock needs to be written before we do any btree
+                * node writes: it will be in the read_write() path
+                */
        }
 
        ret = bch2_blacklist_table_initialize(c);
@@ -997,8 +1112,10 @@ int bch2_fs_recovery(struct bch_fs *c)
                bch_info(c, "starting metadata mark and sweep");
                err = "error in mark and sweep";
                ret = bch2_gc(c, &c->journal_keys, true, true);
-               if (ret)
+               if (ret < 0)
                        goto err;
+               if (ret)
+                       need_write_alloc = true;
                bch_verbose(c, "mark and sweep done");
        }
 
@@ -1008,8 +1125,10 @@ int bch2_fs_recovery(struct bch_fs *c)
                bch_info(c, "starting mark and sweep");
                err = "error in mark and sweep";
                ret = bch2_gc(c, &c->journal_keys, true, false);
-               if (ret)
+               if (ret < 0)
                        goto err;
+               if (ret)
+                       need_write_alloc = true;
                bch_verbose(c, "mark and sweep done");
        }
 
@@ -1033,7 +1152,7 @@ int bch2_fs_recovery(struct bch_fs *c)
                goto err;
        bch_verbose(c, "journal replay done");
 
-       if (!c->opts.nochanges) {
+       if (need_write_alloc && !c->opts.nochanges) {
                /*
                 * note that even when filesystem was clean there might be work
                 * to do here, if we ran gc (because of fsck) which recalculated
@@ -1041,8 +1160,8 @@ int bch2_fs_recovery(struct bch_fs *c)
                 */
                bch_verbose(c, "writing allocation info");
                err = "error writing out alloc info";
-               ret = bch2_stripes_write(c, BTREE_INSERT_LAZY_RW, &wrote) ?:
-                       bch2_alloc_write(c, BTREE_INSERT_LAZY_RW, &wrote);
+               ret = bch2_stripes_write(c, BTREE_INSERT_LAZY_RW) ?:
+                       bch2_alloc_write(c, BTREE_INSERT_LAZY_RW);
                if (ret) {
                        bch_err(c, "error writing alloc info");
                        goto err;
@@ -1169,6 +1288,9 @@ int bch2_fs_initialize(struct bch_fs *c)
        for (i = 0; i < BTREE_ID_NR; i++)
                bch2_btree_root_alloc(c, i);
 
+       set_bit(BCH_FS_BTREE_INTERIOR_REPLAY_DONE, &c->flags);
+       set_bit(JOURNAL_RECLAIM_STARTED, &c->journal.flags);
+
        err = "unable to allocate journal buckets";
        for_each_online_member(ca, c, i) {
                ret = bch2_dev_journal_alloc(ca);
@@ -1185,15 +1307,29 @@ int bch2_fs_initialize(struct bch_fs *c)
        bch2_fs_journal_start(&c->journal, 1, &journal);
        bch2_journal_set_replay_done(&c->journal);
 
+       err = "error going read-write";
+       ret = bch2_fs_read_write_early(c);
+       if (ret)
+               goto err;
+
+       /*
+        * Write out the superblock and journal buckets, now that we can do
+        * btree updates
+        */
+       err = "error writing alloc info";
+       ret = bch2_alloc_write(c, 0);
+       if (ret)
+               goto err;
+
        bch2_inode_init(c, &root_inode, 0, 0,
                        S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO, 0, NULL);
        root_inode.bi_inum = BCACHEFS_ROOT_INO;
-       bch2_inode_pack(&packed_inode, &root_inode);
+       bch2_inode_pack(c, &packed_inode, &root_inode);
 
        err = "error creating root directory";
        ret = bch2_btree_insert(c, BTREE_ID_INODES,
                                &packed_inode.inode.k_i,
-                               NULL, NULL, BTREE_INSERT_LAZY_RW);
+                               NULL, NULL, 0);
        if (ret)
                goto err;