summaryrefslogtreecommitdiff
path: root/fs/bcachefs
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2020-10-16 21:36:26 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2020-11-05 12:45:13 -0500
commit7fe3e9eec8d2c65c4172b50be0964dffe88c69d4 (patch)
treed10e8eef479a53ed5c202e78fb6c630578ea454c /fs/bcachefs
parent8fa4a45ebea5909e1c713c101ba12293fddd4d02 (diff)
bcachefs: Improvements to writing alloc info
Now that we've got transactional alloc info updates (and have for awhile), we don't need to write it out on shutdown, and we don't need to write it out on startup except when GC found errors - this is a big improvement to mount/unmount performance. This patch also fixes a few bugs where we weren't writing out alloc info (on new filesystems, and new devices) and should have been. Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Diffstat (limited to 'fs/bcachefs')
-rw-r--r--fs/bcachefs/alloc_background.c68
-rw-r--r--fs/bcachefs/alloc_background.h3
-rw-r--r--fs/bcachefs/btree_gc.c5
-rw-r--r--fs/bcachefs/ec.c4
-rw-r--r--fs/bcachefs/ec.h2
-rw-r--r--fs/bcachefs/recovery.c38
-rw-r--r--fs/bcachefs/super.c21
7 files changed, 71 insertions, 70 deletions
diff --git a/fs/bcachefs/alloc_background.c b/fs/bcachefs/alloc_background.c
index f85d97da37b5..cb2fa60dbce9 100644
--- a/fs/bcachefs/alloc_background.c
+++ b/fs/bcachefs/alloc_background.c
@@ -271,12 +271,6 @@ int bch2_alloc_read(struct bch_fs *c, struct journal_keys *journal_keys)
return 0;
}
-enum alloc_write_ret {
- ALLOC_WROTE,
- ALLOC_NOWROTE,
- ALLOC_END,
-};
-
static int bch2_alloc_write_key(struct btree_trans *trans,
struct btree_iter *iter,
unsigned flags)
@@ -306,26 +300,17 @@ retry:
old_u = bch2_alloc_unpack(k);
- if (iter->pos.inode >= c->sb.nr_devices ||
- !c->devs[iter->pos.inode])
- return ALLOC_END;
-
percpu_down_read(&c->mark_lock);
ca = bch_dev_bkey_exists(c, iter->pos.inode);
ba = bucket_array(ca);
- if (iter->pos.offset >= ba->nbuckets) {
- percpu_up_read(&c->mark_lock);
- return ALLOC_END;
- }
-
g = &ba->b[iter->pos.offset];
m = READ_ONCE(g->mark);
new_u = alloc_mem_to_key(g, m);
percpu_up_read(&c->mark_lock);
if (!bkey_alloc_unpacked_cmp(old_u, new_u))
- return ALLOC_NOWROTE;
+ return 0;
a = bkey_alloc_init(&alloc_key.k);
a->k.p = iter->pos;
@@ -343,50 +328,55 @@ err:
return ret;
}
-int bch2_alloc_write(struct bch_fs *c, unsigned flags, bool *wrote)
+int bch2_dev_alloc_write(struct bch_fs *c, struct bch_dev *ca, unsigned flags)
{
struct btree_trans trans;
struct btree_iter *iter;
- struct bch_dev *ca;
- unsigned i;
+ u64 first_bucket, nbuckets;
int ret = 0;
+ percpu_down_read(&c->mark_lock);
+ first_bucket = bucket_array(ca)->first_bucket;
+ nbuckets = bucket_array(ca)->nbuckets;
+ percpu_up_read(&c->mark_lock);
+
BUG_ON(BKEY_ALLOC_VAL_U64s_MAX > 8);
bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
- iter = bch2_trans_get_iter(&trans, BTREE_ID_ALLOC, POS_MIN,
+ iter = bch2_trans_get_iter(&trans, BTREE_ID_ALLOC,
+ POS(ca->dev_idx, first_bucket),
BTREE_ITER_SLOTS|BTREE_ITER_INTENT);
- for_each_rw_member(ca, c, i) {
- unsigned first_bucket;
+ while (iter->pos.offset < nbuckets) {
+ bch2_trans_cond_resched(&trans);
- percpu_down_read(&c->mark_lock);
- first_bucket = bucket_array(ca)->first_bucket;
- percpu_up_read(&c->mark_lock);
+ ret = bch2_alloc_write_key(&trans, iter, flags);
+ if (ret)
+ break;
+ bch2_btree_iter_next_slot(iter);
+ }
- bch2_btree_iter_set_pos(iter, POS(i, first_bucket));
+ bch2_trans_exit(&trans);
- while (1) {
- bch2_trans_cond_resched(&trans);
+ return ret;
+}
- ret = bch2_alloc_write_key(&trans, iter, flags);
- if (ret < 0 || ret == ALLOC_END)
- break;
- if (ret == ALLOC_WROTE)
- *wrote = true;
- bch2_btree_iter_next_slot(iter);
- }
+int bch2_alloc_write(struct bch_fs *c, unsigned flags)
+{
+ struct bch_dev *ca;
+ unsigned i;
+ int ret = 0;
- if (ret < 0) {
+ for_each_rw_member(ca, c, i) {
+ bch2_dev_alloc_write(c, ca, flags);
+ if (ret) {
percpu_ref_put(&ca->io_ref);
break;
}
}
- bch2_trans_exit(&trans);
-
- return ret < 0 ? ret : 0;
+ return ret;
}
/* Bucket IO clocks: */
diff --git a/fs/bcachefs/alloc_background.h b/fs/bcachefs/alloc_background.h
index ade0cec8c736..870714ff16a7 100644
--- a/fs/bcachefs/alloc_background.h
+++ b/fs/bcachefs/alloc_background.h
@@ -95,7 +95,8 @@ void bch2_dev_allocator_quiesce(struct bch_fs *, struct bch_dev *);
void bch2_dev_allocator_stop(struct bch_dev *);
int bch2_dev_allocator_start(struct bch_dev *);
-int bch2_alloc_write(struct bch_fs *, unsigned, bool *);
+int bch2_dev_alloc_write(struct bch_fs *, struct bch_dev *, unsigned);
+int bch2_alloc_write(struct bch_fs *, unsigned);
void bch2_fs_allocator_background_init(struct bch_fs *);
#endif /* _BCACHEFS_ALLOC_BACKGROUND_H */
diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c
index c6f1acb97803..e8c1e752a25d 100644
--- a/fs/bcachefs/btree_gc.c
+++ b/fs/bcachefs/btree_gc.c
@@ -570,6 +570,7 @@ static int bch2_gc_done(struct bch_fs *c,
fsck_err(c, _msg ": got %llu, should be %llu" \
, ##__VA_ARGS__, dst->_f, src->_f); \
dst->_f = src->_f; \
+ ret = 1; \
}
#define copy_stripe_field(_f, _msg, ...) \
if (dst->_f != src->_f) { \
@@ -580,6 +581,7 @@ static int bch2_gc_done(struct bch_fs *c,
dst->_f, src->_f); \
dst->_f = src->_f; \
dst->dirty = true; \
+ ret = 1; \
}
#define copy_bucket_field(_f) \
if (dst->b[b].mark._f != src->b[b].mark._f) { \
@@ -590,6 +592,7 @@ static int bch2_gc_done(struct bch_fs *c,
bch2_data_types[dst->b[b].mark.data_type],\
dst->b[b].mark._f, src->b[b].mark._f); \
dst->b[b]._mark._f = src->b[b].mark._f; \
+ ret = 1; \
}
#define copy_dev_field(_f, _msg, ...) \
copy_field(_f, "dev %u has wrong " _msg, i, ##__VA_ARGS__)
@@ -1396,7 +1399,7 @@ static int bch2_gc_thread(void *arg)
#else
ret = bch2_gc_gens(c);
#endif
- if (ret)
+ if (ret < 0)
bch_err(c, "btree gc failed: %i", ret);
debug_check_no_locks_held();
diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c
index 3e7ee41f4d67..eac750ad2240 100644
--- a/fs/bcachefs/ec.c
+++ b/fs/bcachefs/ec.c
@@ -1448,7 +1448,7 @@ static int __bch2_stripe_write_key(struct btree_trans *trans,
return 0;
}
-int bch2_stripes_write(struct bch_fs *c, unsigned flags, bool *wrote)
+int bch2_stripes_write(struct bch_fs *c, unsigned flags)
{
struct btree_trans trans;
struct btree_iter *iter;
@@ -1476,8 +1476,6 @@ int bch2_stripes_write(struct bch_fs *c, unsigned flags, bool *wrote)
if (ret)
break;
-
- *wrote = true;
}
bch2_trans_exit(&trans);
diff --git a/fs/bcachefs/ec.h b/fs/bcachefs/ec.h
index f8fc3d616cd7..6db16cf768da 100644
--- a/fs/bcachefs/ec.h
+++ b/fs/bcachefs/ec.h
@@ -156,7 +156,7 @@ void bch2_ec_flush_new_stripes(struct bch_fs *);
struct journal_keys;
int bch2_stripes_read(struct bch_fs *, struct journal_keys *);
-int bch2_stripes_write(struct bch_fs *, unsigned, bool *);
+int bch2_stripes_write(struct bch_fs *, unsigned);
int bch2_ec_mem_alloc(struct bch_fs *, bool);
diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c
index 6e829bf0a31f..d70fa968db50 100644
--- a/fs/bcachefs/recovery.c
+++ b/fs/bcachefs/recovery.c
@@ -845,9 +845,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];
@@ -961,7 +963,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)
@@ -1090,8 +1092,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");
}
@@ -1101,8 +1105,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");
}
@@ -1126,7 +1132,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
@@ -1134,8 +1140,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;
@@ -1281,6 +1287,20 @@ 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;
@@ -1289,7 +1309,7 @@ int bch2_fs_initialize(struct bch_fs *c)
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;
diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
index c4799a58ff2c..945adad94e16 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/super.c
@@ -169,9 +169,7 @@ struct bch_fs *bch2_uuid_to_fs(uuid_le uuid)
static void __bch2_fs_read_only(struct bch_fs *c)
{
struct bch_dev *ca;
- bool wrote = false;
unsigned i, clean_passes = 0;
- int ret;
bch2_rebalance_stop(c);
bch2_copygc_stop(c);
@@ -190,20 +188,6 @@ static void __bch2_fs_read_only(struct bch_fs *c)
if (!test_bit(BCH_FS_ALLOCATOR_RUNNING, &c->flags))
goto nowrote_alloc;
- bch_verbose(c, "writing alloc info");
- /*
- * This should normally just be writing the bucket read/write clocks:
- */
- ret = bch2_stripes_write(c, BTREE_INSERT_NOCHECK_RW, &wrote) ?:
- bch2_alloc_write(c, BTREE_INSERT_NOCHECK_RW, &wrote);
- bch_verbose(c, "writing alloc info complete");
-
- if (ret && !test_bit(BCH_FS_EMERGENCY_RO, &c->flags))
- bch2_fs_inconsistent(c, "error writing out alloc info %i", ret);
-
- if (ret)
- goto nowrote_alloc;
-
bch_verbose(c, "flushing journal and stopping allocators");
bch2_journal_flush_all_pins(&c->journal);
@@ -1663,6 +1647,11 @@ have_slot:
bch2_write_super(c);
mutex_unlock(&c->sb_lock);
+ err = "alloc write failed";
+ ret = bch2_dev_alloc_write(c, ca, 0);
+ if (ret)
+ goto err;
+
if (ca->mi.state == BCH_MEMBER_STATE_RW) {
err = __bch2_dev_read_write(c, ca);
if (err)