]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bcachefs: Don't self-heal if a data update is already rewriting
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 20 Jan 2025 01:34:57 +0000 (20:34 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sat, 15 Mar 2025 01:02:12 +0000 (21:02 -0400)
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/io_read.c

index 15494aba4547b1981317046df0e77bc2f42d3656..bb5d1de25aa19645a1a2ab19bba762b0d469c0b7 100644 (file)
@@ -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;