Linux Btrfs filesystem development
 help / color / mirror / Atom feed
* [PATCH] btrfs: fix a race which can lead to unreported write failure
@ 2023-09-25  4:59 Qu Wenruo
  2023-09-25  8:57 ` Christoph Hellwig
  0 siblings, 1 reply; 5+ messages in thread
From: Qu Wenruo @ 2023-09-25  4:59 UTC (permalink / raw)
  To: linux-btrfs; +Cc: hch

[RACE]
For write back to chunks with multiple mirrors, there is a race that due
to when the bioc->error is checked, we may falsely consider a write is
successful:

               Thread A                |            Thread B
---------------------------------------+----------------------------------
 btrfs_orig_write_end_io()             | btrfs_clone_write_end_io()
 |  this bio failed                    | |  this bio failed
 |                                     | |
 |- atommic_inc(&bioc->error);         | |
 |- atomic_read(&bioc->error)          | |
 |  So far we only hit one error,      | |
 |  thus can still consider the write  | |
 |  succeeded                          | |
 `- bio->bi_status = BLK_STS_OK;       | |
                                       | `- atomic_inc(&bioc->error);

This can lead to data loss, especially for metadata which by default
goes with duplication.

[FIX]
Instead of only relying on btrfs_orig_write_end_io() to determine if the
bio is successful, also check the error inside the
btrfs_clone_write_end_io().

If any call site found we have exceed the tolerance, mark the original
bio as failed.

Yes, we still have races between atomic_inc() and atomic_read() in all
the endio threads.

But we have ensured the last thread calling atomic_read() would have a
correct view to do the final call, thus fixing the problem.

Fixes: c3a62baf21ad ("btrfs: use chained bios when cloning")
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/bio.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index 42f1f87f1872..4eef135b57af 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -417,8 +417,6 @@ static void btrfs_orig_write_end_io(struct bio *bio)
 	 */
 	if (atomic_read(&bioc->error) > bioc->max_errors)
 		bio->bi_status = BLK_STS_IOERR;
-	else
-		bio->bi_status = BLK_STS_OK;
 
 	btrfs_orig_bbio_end_io(bbio);
 	btrfs_put_bioc(bioc);
@@ -435,6 +433,9 @@ static void btrfs_clone_write_end_io(struct bio *bio)
 		stripe->physical = bio->bi_iter.bi_sector << SECTOR_SHIFT;
 	}
 
+	if (atomic_read(&stripe->bioc->error) >= stripe->bioc->max_errors)
+		stripe->bioc->orig_bio->bi_status = BLK_STS_IOERR;
+
 	/* Pass on control to the original bio this one was cloned from */
 	bio_endio(stripe->bioc->orig_bio);
 	bio_put(bio);
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-09-25  9:40 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-25  4:59 [PATCH] btrfs: fix a race which can lead to unreported write failure Qu Wenruo
2023-09-25  8:57 ` Christoph Hellwig
2023-09-25  9:17   ` Qu Wenruo
2023-09-25  9:20     ` Christoph Hellwig
2023-09-25  9:39       ` Qu Wenruo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox