summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2022-10-09 03:32:17 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2023-01-06 19:47:52 -0500
commitcf50219450a73bff095f3088147bdd92faccc65a (patch)
treef8843266ef79d4a6abc89d90eee12a7c997a5799
parentda2061be7468e77e093fcfecaca42858c28f4e6c (diff)
bcachefs: Handle dropping pointers in data_update path
Cached pointers are generally dropped, not moved: this led to an assertion firing in the data update path when there were no new replicas being written. This path adds a data_options field for pointers to be dropped, and tweaks move_extent() to check if we're only dropping pointers, not writing new ones, before kicking off a data update operation. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r--fs/bcachefs/data_update.c23
-rw-r--r--fs/bcachefs/data_update.h2
-rw-r--r--fs/bcachefs/move.c65
3 files changed, 84 insertions, 6 deletions
diff --git a/fs/bcachefs/data_update.c b/fs/bcachefs/data_update.c
index 6cda97bfeb17..08b33c845af3 100644
--- a/fs/bcachefs/data_update.c
+++ b/fs/bcachefs/data_update.c
@@ -328,8 +328,9 @@ int bch2_data_update_init(struct bch_fs *c, struct data_update *m,
i = 0;
bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
- if (p.ptr.cached)
- m->data_opts.rewrite_ptrs &= ~(1U << i);
+ if (((1U << i) & m->data_opts.rewrite_ptrs) &&
+ p.ptr.cached)
+ BUG();
if (!((1U << i) & m->data_opts.rewrite_ptrs))
bch2_dev_list_add_dev(&m->op.devs_have, p.ptr.dev);
@@ -365,5 +366,23 @@ int bch2_data_update_init(struct bch_fs *c, struct data_update *m,
m->op.nr_replicas = m->op.nr_replicas_required =
hweight32(m->data_opts.rewrite_ptrs) + m->data_opts.extra_replicas;
+
+ BUG_ON(!m->op.nr_replicas);
return 0;
}
+
+void bch2_data_update_opts_normalize(struct bkey_s_c k, struct data_update_opts *opts)
+{
+ struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+ const struct bch_extent_ptr *ptr;
+ unsigned i = 0;
+
+ bkey_for_each_ptr(ptrs, ptr) {
+ if ((opts->rewrite_ptrs & (1U << i)) && ptr->cached) {
+ opts->kill_ptrs |= 1U << i;
+ opts->rewrite_ptrs ^= 1U << i;
+ }
+
+ i++;
+ }
+}
diff --git a/fs/bcachefs/data_update.h b/fs/bcachefs/data_update.h
index ee38bd655af1..5d8690795959 100644
--- a/fs/bcachefs/data_update.h
+++ b/fs/bcachefs/data_update.h
@@ -10,6 +10,7 @@ struct moving_context;
struct data_update_opts {
unsigned rewrite_ptrs;
+ unsigned kill_ptrs;
u16 target;
u8 extra_replicas;
unsigned btree_insert_flags;
@@ -35,5 +36,6 @@ int bch2_data_update_init(struct bch_fs *, struct data_update *,
struct write_point_specifier,
struct bch_io_opts, struct data_update_opts,
enum btree_id, struct bkey_s_c);
+void bch2_data_update_opts_normalize(struct bkey_s_c, struct data_update_opts *);
#endif /* _BCACHEFS_DATA_UPDATE_H */
diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c
index 3b01579bd138..8f9c817eed7b 100644
--- a/fs/bcachefs/move.c
+++ b/fs/bcachefs/move.c
@@ -189,7 +189,52 @@ void bch_move_stats_init(struct bch_move_stats *stats, char *name)
scnprintf(stats->name, sizeof(stats->name), "%s", name);
}
+static int bch2_extent_drop_ptrs(struct btree_trans *trans,
+ struct btree_iter *iter,
+ struct bkey_s_c k,
+ struct data_update_opts data_opts)
+{
+ struct bch_fs *c = trans->c;
+ struct bkey_i *n;
+ int ret;
+
+ n = bch2_trans_kmalloc(trans, bkey_bytes(k.k));
+ ret = PTR_ERR_OR_ZERO(n);
+ if (ret)
+ return ret;
+
+ bkey_reassemble(n, k);
+
+ while (data_opts.kill_ptrs) {
+ unsigned i = 0, drop = __fls(data_opts.kill_ptrs);
+ struct bch_extent_ptr *ptr;
+
+ bch2_bkey_drop_ptrs(bkey_i_to_s(n), ptr, i++ == drop);
+ data_opts.kill_ptrs ^= 1U << drop;
+ }
+
+ /*
+ * If the new extent no longer has any pointers, bch2_extent_normalize()
+ * will do the appropriate thing with it (turning it into a
+ * KEY_TYPE_error key, or just a discard if it was a cached extent)
+ */
+ bch2_extent_normalize(c, bkey_i_to_s(n));
+
+ /*
+ * Since we're not inserting through an extent iterator
+ * (BTREE_ITER_ALL_SNAPSHOTS iterators aren't extent iterators),
+ * we aren't using the extent overwrite path to delete, we're
+ * just using the normal key deletion path:
+ */
+ if (bkey_deleted(&n->k))
+ n->k.size = 0;
+
+ return bch2_trans_update(trans, iter, n, BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) ?:
+ bch2_trans_commit(trans, NULL, NULL, BTREE_INSERT_NOFAIL);
+}
+
static int bch2_move_extent(struct btree_trans *trans,
+ struct btree_iter *iter,
struct moving_context *ctxt,
struct bch_io_opts io_opts,
enum btree_id btree_id,
@@ -204,6 +249,15 @@ static int bch2_move_extent(struct btree_trans *trans,
unsigned sectors = k.k->size, pages;
int ret = -ENOMEM;
+ bch2_data_update_opts_normalize(k, &data_opts);
+
+ if (!data_opts.rewrite_ptrs &&
+ !data_opts.extra_replicas) {
+ if (data_opts.kill_ptrs)
+ return bch2_extent_drop_ptrs(trans, iter, k, data_opts);
+ return 0;
+ }
+
if (!percpu_ref_tryget_live(&c->writes))
return -EROFS;
@@ -446,7 +500,7 @@ static int __bch2_move_data(struct moving_context *ctxt,
bch2_bkey_buf_reassemble(&sk, c, k);
k = bkey_i_to_s_c(sk.k);
- ret2 = bch2_move_extent(&trans, ctxt, io_opts,
+ ret2 = bch2_move_extent(&trans, &iter, ctxt, io_opts,
btree_id, k, data_opts);
if (ret2) {
if (bch2_err_matches(ret2, BCH_ERR_transaction_restart))
@@ -598,11 +652,12 @@ int __bch2_evacuate_bucket(struct moving_context *ctxt,
bch2_bkey_buf_reassemble(&sk, c, k);
k = bkey_i_to_s_c(sk.k);
- bch2_trans_iter_exit(&trans, &iter);
ret = move_get_io_opts(&trans, &io_opts, k, &cur_inum);
- if (ret)
+ if (ret) {
+ bch2_trans_iter_exit(&trans, &iter);
continue;
+ }
data_opts = _data_opts;
data_opts.target = io_opts.background_target;
@@ -614,8 +669,10 @@ int __bch2_evacuate_bucket(struct moving_context *ctxt,
i++;
}
- ret = bch2_move_extent(&trans, ctxt, io_opts,
+ ret = bch2_move_extent(&trans, &iter, ctxt, io_opts,
bp.btree_id, k, data_opts);
+ bch2_trans_iter_exit(&trans, &iter);
+
if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
continue;
if (ret == -ENOMEM) {