diff options
-rw-r--r-- | fs/bcachefs/checksum.h | 11 | ||||
-rw-r--r-- | fs/bcachefs/data_update.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/ec.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/io_write.c | 4 | ||||
-rw-r--r-- | fs/bcachefs/migrate.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/rebalance.c | 166 | ||||
-rw-r--r-- | fs/bcachefs/rebalance.h | 10 | ||||
-rw-r--r-- | fs/bcachefs/sb-downgrade.c | 3 | ||||
-rw-r--r-- | fs/bcachefs/sb-errors_format.h | 4 |
9 files changed, 184 insertions, 20 deletions
diff --git a/fs/bcachefs/checksum.h b/fs/bcachefs/checksum.h index 10bfadcde80a..362846d5bb87 100644 --- a/fs/bcachefs/checksum.h +++ b/fs/bcachefs/checksum.h @@ -143,6 +143,17 @@ static inline enum bch_csum_type bch2_data_checksum_type(struct bch_fs *c, return bch2_csum_opt_to_type(opts.data_checksum, true); } +static inline enum bch_csum_type bch2_data_checksum_type_rb(struct bch_fs *c, + struct bch_extent_rebalance opts) +{ + if (c->sb.encryption_type) + return c->opts.wide_macs + ? BCH_CSUM_chacha20_poly1305_128 + : BCH_CSUM_chacha20_poly1305_80; + + return bch2_csum_opt_to_type(opts.data_checksum, true); +} + static inline enum bch_csum_type bch2_meta_checksum_type(struct bch_fs *c) { if (c->sb.encryption_type) diff --git a/fs/bcachefs/data_update.c b/fs/bcachefs/data_update.c index 894ba0944fea..dbdf64b40829 100644 --- a/fs/bcachefs/data_update.c +++ b/fs/bcachefs/data_update.c @@ -438,7 +438,7 @@ restart_drop_extra_replicas: bch2_insert_snapshot_whiteouts(trans, m->btree_id, k.k->p, insert->k.p) ?: bch2_inum_snapshot_opts_get(trans, k.k->p.inode, k.k->p.snapshot, &opts) ?: - bch2_bkey_set_needs_rebalance(c, &opts, insert, + bch2_bkey_set_needs_rebalance(trans, NULL, &opts, insert, SET_NEEDS_REBALANCE_foreground, m->op.opts.change_cookie) ?: bch2_trans_update(trans, &iter, insert, diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c index 89a95b6c4e51..103719a76c81 100644 --- a/fs/bcachefs/ec.c +++ b/fs/bcachefs/ec.c @@ -1134,7 +1134,7 @@ static int ec_stripe_update_extent(struct btree_trans *trans, ret = bch2_extent_get_io_opts_one(trans, &opts, &iter, bkey_i_to_s_c(n), SET_NEEDS_REBALANCE_other) ?: - bch2_bkey_set_needs_rebalance(trans->c, &opts, n, + bch2_bkey_set_needs_rebalance(trans, NULL, &opts, n, SET_NEEDS_REBALANCE_other, 0) ?: bch2_trans_update(trans, &iter, n, 0); out: diff --git a/fs/bcachefs/io_write.c b/fs/bcachefs/io_write.c index 6a5da02ce266..ccbca802db0b 100644 --- a/fs/bcachefs/io_write.c +++ b/fs/bcachefs/io_write.c @@ -365,7 +365,7 @@ int bch2_extent_update(struct btree_trans *trans, min(k->k.p.offset << 9, new_i_size), i_sectors_delta, &inode) ?: (bch2_inode_opts_get_inode(c, &inode, &opts), - bch2_bkey_set_needs_rebalance(c, &opts, k, + bch2_bkey_set_needs_rebalance(trans, NULL, &opts, k, SET_NEEDS_REBALANCE_foreground, change_cookie)) ?: bch2_trans_update(trans, iter, k, 0) ?: @@ -1271,7 +1271,7 @@ static int bch2_nocow_write_convert_one_unwritten(struct btree_trans *trans, return bch2_extent_update_i_size_sectors(trans, iter, min(new->k.p.offset << 9, new_i_size), 0, &inode) ?: (bch2_inode_opts_get_inode(c, &inode, &opts), - bch2_bkey_set_needs_rebalance(c, &opts, new, + bch2_bkey_set_needs_rebalance(trans, NULL, &opts, new, SET_NEEDS_REBALANCE_foreground, op->opts.change_cookie)) ?: bch2_trans_update(trans, iter, new, diff --git a/fs/bcachefs/migrate.c b/fs/bcachefs/migrate.c index 139a6587a64e..9b172af4f8c8 100644 --- a/fs/bcachefs/migrate.c +++ b/fs/bcachefs/migrate.c @@ -84,7 +84,7 @@ static int bch2_dev_usrdata_drop_key(struct btree_trans *trans, struct bch_inode_opts opts; ret = bch2_extent_get_apply_io_opts_one(trans, &opts, iter, k, ctx) ?: - bch2_bkey_set_needs_rebalance(c, &opts, n, ctx, 0) ?: + bch2_bkey_set_needs_rebalance(trans, NULL, &opts, n, ctx, 0) ?: drop_dev_ptrs(c, bkey_i_to_s(n), dev_idx, flags, err, false); if (ret) return ret; diff --git a/fs/bcachefs/rebalance.c b/fs/bcachefs/rebalance.c index f2d0040d043c..e850b7d872e2 100644 --- a/fs/bcachefs/rebalance.c +++ b/fs/bcachefs/rebalance.c @@ -25,6 +25,8 @@ #include <linux/kthread.h> #include <linux/sched/cputime.h> +#define REBALANCE_WORK_SCAN_OFFSET (U64_MAX - 1) + /* bch_extent_rebalance: */ static const struct bch_extent_rebalance *bch2_bkey_ptrs_rebalance_opts(struct bkey_ptrs_c ptrs) @@ -237,6 +239,112 @@ bch2_bkey_needs_rebalance(struct bch_fs *c, struct bkey_s_c k, return r; } +static int check_rebalance_scan_cookie(struct btree_trans *trans, u64 inum, bool *v) +{ + if (v && *v) + return 1; + + /* + * If opts need to be propagated to the extent, a scan cookie should be + * present: + */ + CLASS(btree_iter, iter)(trans, BTREE_ID_rebalance_work, + SPOS(inum, REBALANCE_WORK_SCAN_OFFSET, U32_MAX), + 0); + struct bkey_s_c k = bch2_btree_iter_peek_slot(&iter); + int ret = bkey_err(k); + if (ret) + return ret; + + ret = k.k->type == KEY_TYPE_cookie; + if (v) + *v = ret; + return ret; +} + +static int new_needs_rb_allowed(struct btree_trans *trans, + struct per_snapshot_io_opts *s, + struct bkey_s_c k, + enum set_needs_rebalance_ctx ctx, + unsigned opt_change_cookie, + const struct bch_extent_rebalance *old, + const struct bch_extent_rebalance *new, + unsigned new_need_rb) +{ + struct bch_fs *c = trans->c; + /* + * New need_rb - pointers that don't match the current io path options - + * are only allowed in certain situations: + * + * Propagating new options: from bch2_set_rebalance_needs_scan + * + * Foreground writes: background_compression and background_target are + * allowed + * + * Foreground writes: we may have raced with an option change: + * opt_change_cookie checks for this + * + * XXX: foreground writes should still match compression, + * foreground_target - figure out how to check for this + */ + if (ctx == SET_NEEDS_REBALANCE_opt_change || + ctx == SET_NEEDS_REBALANCE_opt_change_indirect) + return 0; + + if (ctx == SET_NEEDS_REBALANCE_foreground) { + new_need_rb &= ~(BIT(BCH_REBALANCE_background_compression)| + BIT(BCH_REBALANCE_background_target)); + if (!new_need_rb) + return 0; + + if (opt_change_cookie != atomic_read(&c->opt_change_cookie)) + return 0; + } + + /* + * Either the extent data or the extent io options (from + * bch_extent_rebalance) should match the io_opts from the + * inode/filesystem, unless + * + * - There's a scan pending to propagate new options + * - It's an indirect extent: it may be referenced by inodes + * with inconsistent options + * + * For efficiency (so that we can cache checking for scan + * cookies), only check option consistency when we're called + * with snapshot_io_opts - don't bother when we're called from + * move_data_phys() -> get_io_opts_one() + * + * Note that we can cache the existence of a cookie, but not the + * non-existence, to avoid spurious false positives. + */ + int ret = check_rebalance_scan_cookie(trans, 0, s ? &s->fs_scan_cookie : NULL) ?: + check_rebalance_scan_cookie(trans, k.k->p.inode, s ? &s->inum_scan_cookie : NULL); + if (ret < 0) + return ret; + if (ret) + return 0; + + CLASS(printbuf, buf)(); + + prt_printf(&buf, "extent with incorrect/missing rebalance opts:\n"); + bch2_bkey_val_to_text(&buf, c, k); + + const struct bch_extent_rebalance _old = {}; + if (!old) + old = &_old; + +#define x(_name) \ + if (new_need_rb & BIT(BCH_REBALANCE_##_name)) \ + prt_printf(&buf, "\n" #_name " %u != %u", old->_name, new->_name); + BCH_REBALANCE_OPTS() +#undef x + + fsck_err(trans, extent_io_opts_not_set, "%s", buf.buf); +fsck_err: + return ret; +} + static inline bool bkey_should_have_rb_opts(struct bkey_s_c k, struct bch_extent_rebalance new) { @@ -248,7 +356,8 @@ static inline bool bkey_should_have_rb_opts(struct bkey_s_c k, return new.need_rb; } -int bch2_bkey_set_needs_rebalance(struct bch_fs *c, +int bch2_bkey_set_needs_rebalance(struct btree_trans *trans, + struct per_snapshot_io_opts *snapshot_io_opts, struct bch_inode_opts *opts, struct bkey_i *_k, enum set_needs_rebalance_ctx ctx, @@ -257,6 +366,7 @@ int bch2_bkey_set_needs_rebalance(struct bch_fs *c, if (!bkey_extent_is_direct_data(&_k->k)) return 0; + struct bch_fs *c = trans->c; struct bkey_s k = bkey_i_to_s(_k); struct bch_extent_rebalance *old = (struct bch_extent_rebalance *) bch2_bkey_rebalance_opts(k.s_c); @@ -273,6 +383,16 @@ int bch2_bkey_set_needs_rebalance(struct bch_fs *c, (should_have_rb ? !memcmp(old, &new, sizeof(new)) : !old)) return 0; + unsigned new_need_rb = new.need_rb & ~(old ? old->need_rb : 0); + + if (unlikely(new_need_rb)) { + int ret = new_needs_rb_allowed(trans, snapshot_io_opts, + k.s_c, ctx, opt_change_cookie, + old, &new, new_need_rb); + if (ret) + return ret; + } + if (should_have_rb) { if (!old) { old = bkey_val_end(k); @@ -287,6 +407,7 @@ int bch2_bkey_set_needs_rebalance(struct bch_fs *c, } static int bch2_get_update_rebalance_opts(struct btree_trans *trans, + struct per_snapshot_io_opts *snapshot_io_opts, struct bch_inode_opts *io_opts, struct btree_iter *iter, struct bkey_s_c k, @@ -324,7 +445,7 @@ static int bch2_get_update_rebalance_opts(struct btree_trans *trans, /* On successfull transaction commit, @k was invalidated: */ - return bch2_bkey_set_needs_rebalance(c, io_opts, n, ctx, 0) ?: + return bch2_bkey_set_needs_rebalance(trans, snapshot_io_opts, io_opts, n, ctx, 0) ?: bch2_trans_update(trans, iter, n, BTREE_UPDATE_internal_snapshot_node) ?: bch2_trans_commit(trans, NULL, NULL, 0) ?: bch_err_throw(c, transaction_restart_commit); @@ -367,7 +488,8 @@ static struct bch_inode_opts *bch2_extent_get_io_opts(struct btree_trans *trans, darray_push(&io_opts->d, e); })); - io_opts->cur_inum = extent_pos.inode; + io_opts->cur_inum = extent_pos.inode; + io_opts->inum_scan_cookie = false; } ret = ret ?: trans_was_restarted(trans, restart_count); @@ -395,7 +517,8 @@ struct bch_inode_opts *bch2_extent_get_apply_io_opts(struct btree_trans *trans, if (IS_ERR(opts) || btree_iter_path(trans, extent_iter)->level) return opts; - int ret = bch2_get_update_rebalance_opts(trans, opts, extent_iter, extent_k, ctx); + int ret = bch2_get_update_rebalance_opts(trans, snapshot_io_opts, opts, + extent_iter, extent_k, ctx); return ret ? ERR_PTR(ret) : opts; } @@ -439,11 +562,9 @@ int bch2_extent_get_apply_io_opts_one(struct btree_trans *trans, if (ret || btree_iter_path(trans, extent_iter)->level) return ret; - return bch2_get_update_rebalance_opts(trans, io_opts, extent_iter, extent_k, ctx); + return bch2_get_update_rebalance_opts(trans, NULL, io_opts, extent_iter, extent_k, ctx); } -#define REBALANCE_WORK_SCAN_OFFSET (U64_MAX - 1) - static const char * const bch2_rebalance_state_strs[] = { #define x(t) #t, BCH_REBALANCE_STATES() @@ -691,6 +812,7 @@ out: static int do_rebalance_scan_indirect(struct btree_trans *trans, struct bkey_s_c_reflink_p p, + struct per_snapshot_io_opts *snapshot_io_opts, struct bch_inode_opts *opts) { u64 idx = REFLINK_P_IDX(p.v) - le32_to_cpu(p.v->front_pad); @@ -703,7 +825,7 @@ static int do_rebalance_scan_indirect(struct btree_trans *trans, BTREE_ITER_not_extents, k, ({ if (bpos_ge(bkey_start_pos(k.k), POS(0, end))) break; - bch2_get_update_rebalance_opts(trans, opts, &iter, k, + bch2_get_update_rebalance_opts(trans, snapshot_io_opts, opts, &iter, k, SET_NEEDS_REBALANCE_opt_change_indirect); })); if (ret) @@ -751,7 +873,8 @@ static int do_rebalance_scan(struct moving_context *ctxt, (inum && k.k->type == KEY_TYPE_reflink_p && REFLINK_P_MAY_UPDATE_OPTIONS(bkey_s_c_to_reflink_p(k).v) - ? do_rebalance_scan_indirect(trans, bkey_s_c_to_reflink_p(k), opts) + ? do_rebalance_scan_indirect(trans, bkey_s_c_to_reflink_p(k), + snapshot_io_opts, opts) : 0); })); if (ret) @@ -1050,6 +1173,7 @@ int bch2_fs_rebalance_init(struct bch_fs *c) static int check_rebalance_work_one(struct btree_trans *trans, struct btree_iter *extent_iter, struct btree_iter *rebalance_iter, + struct per_snapshot_io_opts *snapshot_io_opts, struct bkey_buf *last_flushed) { struct bch_fs *c = trans->c; @@ -1119,6 +1243,21 @@ static int check_rebalance_work_one(struct btree_trans *trans, return ret; } + struct bch_inode_opts *opts = bch2_extent_get_apply_io_opts(trans, + snapshot_io_opts, extent_iter->pos, extent_iter, extent_k, + SET_NEEDS_REBALANCE_other); + ret = PTR_ERR_OR_ZERO(opts); + if (ret == -BCH_ERR_transaction_restart_commit) { + /* + * If get_apply_io_opts() did work, just advance and check the + * next key; it may have updated the rebalance_work btree so + * we'd need a write buffer flush to check what it just did. + */ + ret = 0; + } + if (ret) + return ret; + if (cmp <= 0) bch2_btree_iter_advance(extent_iter); if (cmp >= 0) @@ -1131,10 +1270,14 @@ int bch2_check_rebalance_work(struct bch_fs *c) { CLASS(btree_trans, trans)(c); CLASS(btree_iter, extent_iter)(trans, BTREE_ID_reflink, POS_MIN, + BTREE_ITER_not_extents| BTREE_ITER_prefetch); CLASS(btree_iter, rebalance_iter)(trans, BTREE_ID_rebalance_work, POS_MIN, BTREE_ITER_prefetch); + struct per_snapshot_io_opts snapshot_io_opts; + per_snapshot_io_opts_init(&snapshot_io_opts, c); + struct bkey_buf last_flushed; bch2_bkey_buf_init(&last_flushed); bkey_init(&last_flushed.k->k); @@ -1148,12 +1291,15 @@ int bch2_check_rebalance_work(struct bch_fs *c) bch2_trans_begin(trans); - ret = check_rebalance_work_one(trans, &extent_iter, &rebalance_iter, &last_flushed); + ret = check_rebalance_work_one(trans, &extent_iter, &rebalance_iter, + &snapshot_io_opts, &last_flushed) ?: + bch2_trans_commit(trans, NULL, NULL, 0); if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) ret = 0; } + per_snapshot_io_opts_exit(&snapshot_io_opts); bch2_bkey_buf_exit(&last_flushed, c); return ret < 0 ? ret : 0; } diff --git a/fs/bcachefs/rebalance.h b/fs/bcachefs/rebalance.h index ae576d8af609..767de863fb53 100644 --- a/fs/bcachefs/rebalance.h +++ b/fs/bcachefs/rebalance.h @@ -56,9 +56,6 @@ enum set_needs_rebalance_ctx { SET_NEEDS_REBALANCE_other, }; -int bch2_bkey_set_needs_rebalance(struct bch_fs *, struct bch_inode_opts *, - struct bkey_i *, enum set_needs_rebalance_ctx, u32); - /* Inodes in different snapshots may have different IO options: */ struct snapshot_io_opts_entry { u32 snapshot; @@ -67,6 +64,9 @@ struct snapshot_io_opts_entry { struct per_snapshot_io_opts { u64 cur_inum; + bool fs_scan_cookie; + bool inum_scan_cookie; + struct bch_inode_opts fs_io_opts; DARRAY(struct snapshot_io_opts_entry) d; }; @@ -82,6 +82,10 @@ static inline void per_snapshot_io_opts_exit(struct per_snapshot_io_opts *io_opt darray_exit(&io_opts->d); } +int bch2_bkey_set_needs_rebalance(struct btree_trans *, + struct per_snapshot_io_opts *, struct bch_inode_opts *, + struct bkey_i *, enum set_needs_rebalance_ctx, u32); + struct bch_inode_opts *bch2_extent_get_apply_io_opts(struct btree_trans *, struct per_snapshot_io_opts *, struct bpos, struct btree_iter *, struct bkey_s_c, diff --git a/fs/bcachefs/sb-downgrade.c b/fs/bcachefs/sb-downgrade.c index 0d47e83c28a6..66b7f19f0437 100644 --- a/fs/bcachefs/sb-downgrade.c +++ b/fs/bcachefs/sb-downgrade.c @@ -109,7 +109,8 @@ BIT_ULL(BCH_RECOVERY_PASS_check_allocations), \ BCH_FSCK_ERR_accounting_mismatch) \ x(rebalance_v2, \ - BIT_ULL(BCH_RECOVERY_PASS_check_rebalance_work)) + BIT_ULL(BCH_RECOVERY_PASS_check_rebalance_work), \ + BCH_FSCK_ERR_extent_io_opts_not_set) #define DOWNGRADE_TABLE() \ x(bucket_stripe_sectors, \ diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h index 77e3fc92e39b..04c1da1e664a 100644 --- a/fs/bcachefs/sb-errors_format.h +++ b/fs/bcachefs/sb-errors_format.h @@ -339,7 +339,9 @@ enum bch_fsck_flags { x(dirent_stray_data_after_cf_name, 305, 0) \ x(rebalance_work_incorrectly_set, 309, FSCK_AUTOFIX) \ x(rebalance_work_incorrectly_unset, 310, FSCK_AUTOFIX) \ - x(MAX, 328, 0) + x(extent_io_opts_not_set, 328, FSCK_AUTOFIX) \ + x(extent_io_opts_unneeded, 329, FSCK_AUTOFIX) \ + x(MAX, 330, 0) enum bch_sb_error_id { #define x(t, n, ...) BCH_FSCK_ERR_##t = n, |