]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bcachefs: Data move can read from poisoned extents
authorKent Overstreet <kent.overstreet@linux.dev>
Tue, 11 Mar 2025 14:02:40 +0000 (10:02 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 22 May 2025 00:13:21 +0000 (20:13 -0400)
Now, if an extent is poisoned we can move it even if there was a
checksum error. We'll have to give it a new checksum, but the poison bit
means that userspace will still see the appropriate error when they try
to read it.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/extents.c
fs/bcachefs/io_read.c
fs/bcachefs/move.c

index e597fb9c98236c6058483edf4dd7f8e5ff8d4141..a369f978ffe6c34232430a2dbd562ea2e1f8f36b 100644 (file)
@@ -136,12 +136,8 @@ int bch2_bkey_pick_read_device(struct bch_fs *c, struct bkey_s_c k,
        if (k.k->type == KEY_TYPE_error)
                return -BCH_ERR_key_type_error;
 
-       struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
-
-       if (bch2_bkey_extent_ptrs_flags(ptrs) & BIT_ULL(BCH_EXTENT_FLAG_poisoned))
-               return -BCH_ERR_extent_poisoned;
-
        rcu_read_lock();
+       struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
        const union bch_extent_entry *entry;
        struct extent_ptr_decoded p;
        u64 pick_latency;
index 3f111f9183458ae5eb979f673171e15b09fae24a..751a9679d7e5c3cee57292161d6b5e28fcc52324 100644 (file)
@@ -1053,6 +1053,10 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
                             bvec_iter_sectors(iter));
                goto out_read_done;
        }
+
+       if ((bch2_bkey_extent_flags(k) & BIT_ULL(BCH_EXTENT_FLAG_poisoned)) &&
+           !orig->data_update)
+               return -BCH_ERR_extent_poisoned;
 retry_pick:
        ret = bch2_bkey_pick_read_device(c, k, failed, &pick, dev);
 
index dfdbb9259985b6060b7dac1a1ab70b7a191452ce..fe2fa665150bebde130747d744f959a1cb3c1442 100644 (file)
@@ -126,26 +126,40 @@ static void move_write_done(struct bch_write_op *op)
 
 static void move_write(struct moving_io *io)
 {
+       struct bch_fs *c = io->write.op.c;
        struct moving_context *ctxt = io->write.ctxt;
+       struct bch_read_bio *rbio = &io->write.rbio;
 
        if (ctxt->stats) {
-               if (io->write.rbio.bio.bi_status)
+               if (rbio->bio.bi_status)
                        atomic64_add(io->write.rbio.bvec_iter.bi_size >> 9,
                                     &ctxt->stats->sectors_error_uncorrected);
-               else if (io->write.rbio.saw_error)
+               else if (rbio->saw_error)
                        atomic64_add(io->write.rbio.bvec_iter.bi_size >> 9,
                                     &ctxt->stats->sectors_error_corrected);
        }
 
-       if (unlikely(io->write.rbio.ret ||
-                    io->write.rbio.bio.bi_status ||
-                    io->write.data_opts.scrub)) {
+       /*
+        * If the extent has been bitrotted, we're going to have to give it a
+        * new checksum in order to move it - but the poison bit will ensure
+        * that userspace still gets the appropriate error.
+        */
+       if (unlikely(rbio->ret == -BCH_ERR_data_read_csum_err &&
+                    (bch2_bkey_extent_flags(bkey_i_to_s_c(io->write.k.k)) & BIT_ULL(BCH_EXTENT_FLAG_poisoned)))) {
+               struct bch_extent_crc_unpacked crc = rbio->pick.crc;
+               struct nonce nonce = extent_nonce(rbio->version, crc);
+
+               rbio->pick.crc.csum     = bch2_checksum_bio(c, rbio->pick.crc.csum_type,
+                                                           nonce, &rbio->bio);
+               rbio->ret               = 0;
+       }
+
+       if (unlikely(rbio->ret || io->write.data_opts.scrub)) {
                move_free(io);
                return;
        }
 
        if (trace_io_move_write_enabled()) {
-               struct bch_fs *c = io->write.op.c;
                struct printbuf buf = PRINTBUF;
 
                bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(io->write.k.k));