diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2025-01-19 20:34:57 -0500 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2025-02-07 14:49:49 -0500 |
commit | fe53ea84d0548faa9a66badcddd90bc4148c60f5 (patch) | |
tree | ba49625e5065ece61ae6b0bb29b4f10e658aa12e | |
parent | d1a34af1237066612d07898abf1f2c2868ca2ed5 (diff) |
bcachefs: Don't self-heal if a data update is already rewriting
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r-- | fs/bcachefs/io_read.c | 68 |
1 files changed, 48 insertions, 20 deletions
diff --git a/fs/bcachefs/io_read.c b/fs/bcachefs/io_read.c index b0495f7a0782..9353fda974b3 100644 --- a/fs/bcachefs/io_read.c +++ b/fs/bcachefs/io_read.c @@ -97,6 +97,26 @@ static inline bool have_io_error(struct bch_io_failures *failed) return failed && failed->nr; } +static bool ptr_being_rewritten(struct bch_read_bio *orig, + unsigned dev, + unsigned flags) +{ + if (!(flags & BCH_READ_data_update)) + return false; + + struct data_update *u = container_of(orig, struct data_update, rbio); + struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(u->k.k)); + unsigned i = 0; + bkey_for_each_ptr(ptrs, ptr) { + if (ptr->dev == dev && + u->data_opts.rewrite_ptrs & BIT(i)) + return true; + i++; + } + + return false; +} + static inline int should_promote(struct bch_fs *c, struct bkey_s_c k, struct bpos pos, struct bch_io_opts opts, @@ -173,30 +193,13 @@ static struct bch_read_bio *__promote_alloc(struct btree_trans *trans, struct bpos pos, struct extent_ptr_decoded *pick, unsigned sectors, + unsigned flags, struct bch_read_bio *orig, struct bch_io_failures *failed) { struct bch_fs *c = trans->c; int ret; - if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_promote)) - return ERR_PTR(-BCH_ERR_nopromote_no_writes); - - struct promote_op *op = kzalloc(sizeof(*op), GFP_KERNEL); - if (!op) { - ret = -BCH_ERR_nopromote_enomem; - goto err_put; - } - - op->start_time = local_clock(); - op->pos = pos; - - if (rhashtable_lookup_insert_fast(&c->promote_table, &op->hash, - bch_promote_params)) { - ret = -BCH_ERR_nopromote_in_flight; - goto err; - } - struct data_update_opts update_opts = { .write_flags = BCH_WRITE_alloc_nowait }; if (!have_io_error(failed)) { @@ -210,10 +213,32 @@ static struct bch_read_bio *__promote_alloc(struct btree_trans *trans, struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k); unsigned ptr_bit = 1; bkey_for_each_ptr(ptrs, ptr) { - if (bch2_dev_io_failures(failed, ptr->dev)) + if (bch2_dev_io_failures(failed, ptr->dev) && + !ptr_being_rewritten(orig, ptr->dev, flags)) update_opts.rewrite_ptrs |= ptr_bit; ptr_bit <<= 1; } + + if (!update_opts.rewrite_ptrs) + return NULL; + } + + if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_promote)) + return ERR_PTR(-BCH_ERR_nopromote_no_writes); + + struct promote_op *op = kzalloc(sizeof(*op), GFP_KERNEL); + if (!op) { + ret = -BCH_ERR_nopromote_enomem; + goto err_put; + } + + op->start_time = local_clock(); + op->pos = pos; + + if (rhashtable_lookup_insert_fast(&c->promote_table, &op->hash, + bch_promote_params)) { + ret = -BCH_ERR_nopromote_in_flight; + goto err; } ret = bch2_data_update_init(trans, NULL, NULL, &op->write, @@ -283,7 +308,10 @@ static struct bch_read_bio *promote_alloc(struct btree_trans *trans, k.k->type == KEY_TYPE_reflink_v ? BTREE_ID_reflink : BTREE_ID_extents, - k, pos, pick, sectors, orig, failed); + k, pos, pick, sectors, flags, orig, failed); + if (!promote) + return NULL; + ret = PTR_ERR_OR_ZERO(promote); if (ret) goto nopromote; |