mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC v2 00/17] bio cleanups
@ 2025-12-21  2:52 Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 01/17] xfs: don't clobber bi_status in xfs_zone_alloc_and_submit Andreas Gruenbacher
                   ` (16 more replies)
  0 siblings, 17 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Hello,

we are not quite careful enough about setting bio->bi_status in all
places (see BACKGROUND below).  This patch queue tries to fix this by
systematically eliminating the direct assignments to bi_status sprinkled
all throughout the code.  Please comment.

The patches are currently still based on v6.18.

GIT tree:
https://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2.git/log/?h=bio-cleanups

The first version of this patch queue is avaliable here:
https://lore.kernel.org/linux-block/20251208121020.1780402-1-agruenba@redhat.com/

With these changes, only a few direct assignments to bio->bi_status
remain, in BTRFS and in MD, and SOME OF THOSE MAY BE UNSAFE.  Could the
maintainers of those subsystems please have a look?

Once the remaining direct assignments to bi_status are gone, we may want
to think about "write protecting" bi_status to prevent unintended new
direct assignments from creeping back in.


BACKGROUND

'struct bio' objects start out with their bi_status field initialized to
BLK_STS_OK (0).  When the bio completes, that field needs to be left
unchanged in case of success, and set to a BLK_STS_* error code
otherwise.

This is important when bios are chained (bio_chain()) because then,
multiple execution contexts will race updating the same bi_status field.
When an execution context resets bi_status to BLK_STS_OK (0) during bio
completion, this could hide the error code of the adjacent bio in the
chain.

When more than a single bio fails in a chain, we know that the resulting
bi_status will not be BLK_STS_OK, but we don't know which of the status
error codes will survive.


CRYPTO FALLBACK (SATYA TANGIRALA?)

Related to chained bios but unrelated to setting bio->bi_status,
blk_crypto_fallback_encrypt_bio() in block/blk-crypto-fallback.c swaps
out bi_private and bi_end_io and reuses the same bio for downcalls, then
restores those fields in blk_crypto_fallback_decrypt_endio() before
calling bio_endio() again on the same bio.  This will at the very least
break with chained bios because it will mess up __bi_remaining.


Thanks,
Andreas

Andreas Gruenbacher (17):
  xfs: don't clobber bi_status in xfs_zone_alloc_and_submit
  bio: rename bio_chain arguments
  bio: use bio_io_error more often
  bio: add bio_set_status
  bio: use bio_set_status for BLK_STS_* status codes
  bio: do not check bio->bi_status before assigning to it
  block: consecutive blk_status_t error codes
  block: fix blk_status_to_{errno,str} inconsistency
  block: turn blk_errors array into a macro
  block: optimize blk_status <=> errno conversion
  bio: bio_set_status from non-zero errno
  bio: do not check bio->bi_status before assigning to it (part 2)
  xfs: use bio_set_status in xfs_zone_alloc_and_submit
  bio: switch to bio_set_status in submit_bio_noacct
  bio: never set bi_status to BLK_STS_OK during completion
  bio: never set bi_status to BLK_STS_OK during completion (part 2)
  bio: add bio_endio_status

 block/bio-integrity-auto.c       |   3 +-
 block/bio.c                      |  25 +++----
 block/blk-core.c                 | 123 ++++++++++++++++---------------
 block/blk-crypto-fallback.c      |  22 +++---
 block/blk-crypto-internal.h      |   2 +-
 block/blk-crypto.c               |   4 +-
 block/blk-merge.c                |   6 +-
 block/blk-mq.c                   |  10 +--
 block/fops.c                     |   6 +-
 block/t10-pi.c                   |   2 +-
 drivers/block/aoe/aoecmd.c       |   8 +-
 drivers/block/aoe/aoedev.c       |   2 +-
 drivers/block/drbd/drbd_int.h    |   3 +-
 drivers/block/drbd/drbd_req.c    |   7 +-
 drivers/block/ps3vram.c          |   3 +-
 drivers/block/zram/zram_drv.c    |   4 +-
 drivers/md/bcache/bcache.h       |   3 +-
 drivers/md/bcache/request.c      |   8 +-
 drivers/md/dm-cache-target.c     |   9 ++-
 drivers/md/dm-ebs-target.c       |   2 +-
 drivers/md/dm-flakey.c           |   2 +-
 drivers/md/dm-integrity.c        |  34 ++++-----
 drivers/md/dm-mpath.c            |   6 +-
 drivers/md/dm-pcache/dm_pcache.c |   3 +-
 drivers/md/dm-raid1.c            |   7 +-
 drivers/md/dm-thin.c             |   7 +-
 drivers/md/dm-vdo/data-vio.c     |   3 +-
 drivers/md/dm-verity-target.c    |   2 +-
 drivers/md/dm-writecache.c       |   7 +-
 drivers/md/dm-zoned-target.c     |   3 +-
 drivers/md/dm.c                  |   4 +-
 drivers/md/md.c                  |   8 +-
 drivers/md/raid1-10.c            |   3 +-
 drivers/md/raid1.c               |   2 +-
 drivers/md/raid10.c              |  20 +++--
 drivers/md/raid5.c               |   4 +-
 drivers/nvdimm/btt.c             |   4 +-
 drivers/nvdimm/pmem.c            |   4 +-
 fs/btrfs/bio.c                   |   8 +-
 fs/btrfs/direct-io.c             |   2 +-
 fs/btrfs/raid56.c                |   6 +-
 fs/crypto/bio.c                  |   2 +-
 fs/erofs/fileio.c                |   4 +-
 fs/erofs/fscache.c               |   8 +-
 fs/f2fs/data.c                   |   6 +-
 fs/f2fs/segment.c                |   3 +-
 fs/iomap/ioend.c                 |   3 +-
 fs/verity/verify.c               |   2 +-
 fs/xfs/xfs_aops.c                |   3 +-
 fs/xfs/xfs_zone_alloc.c          |   4 +-
 include/linux/bio.h              |  28 ++++++-
 include/linux/blk_types.h        |   2 +-
 include/linux/blkdev.h           |  18 ++++-
 53 files changed, 236 insertions(+), 238 deletions(-)

-- 
2.52.0


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

* [RFC v2 01/17] xfs: don't clobber bi_status in xfs_zone_alloc_and_submit
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21 15:39   ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 02/17] bio: rename bio_chain arguments Andreas Gruenbacher
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Function xfs_zone_alloc_and_submit() sets bio->bi_status and then it
calls bio_io_error(), which overwrites that value again.  Fix that by
completing the bio separately after setting bio->bi_status.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/xfs/xfs_zone_alloc.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c
index ef7a931ebde5..bd6f3ef095cb 100644
--- a/fs/xfs/xfs_zone_alloc.c
+++ b/fs/xfs/xfs_zone_alloc.c
@@ -897,6 +897,9 @@ xfs_zone_alloc_and_submit(
 
 out_split_error:
 	ioend->io_bio.bi_status = errno_to_blk_status(PTR_ERR(split));
+	bio_endio(&ioend->io_bio);
+	return;
+
 out_error:
 	bio_io_error(&ioend->io_bio);
 }
-- 
2.52.0


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

* [RFC v2 02/17] bio: rename bio_chain arguments
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 01/17] xfs: don't clobber bi_status in xfs_zone_alloc_and_submit Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 03/17] bio: use bio_io_error more often Andreas Gruenbacher
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel, Christoph Hellwig

Use the same argument names in bio_chain() as in bio_chain_and_submit()
to be consistent.  Slightly improve the function description.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 block/bio.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index b3a79285c278..3f408e1ba13f 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -327,22 +327,22 @@ static void bio_chain_endio(struct bio *bio)
 
 /**
  * bio_chain - chain bio completions
- * @bio: the target bio
- * @parent: the parent bio of @bio
+ * @prev: the bio to chain
+ * @new: the bio to chain to
  *
- * The caller won't have a bi_end_io called when @bio completes - instead,
- * @parent's bi_end_io won't be called until both @parent and @bio have
- * completed; the chained bio will also be freed when it completes.
+ * The caller won't have a bi_end_io called when @prev completes.  Instead,
+ * @new's bi_end_io will be called once both @new and @prev have completed.
+ * Like an unchained bio, @prev will be put when it completes.
  *
- * The caller must not set bi_private or bi_end_io in @bio.
+ * The caller must not set bi_private or bi_end_io in @prev.
  */
-void bio_chain(struct bio *bio, struct bio *parent)
+void bio_chain(struct bio *prev, struct bio *new)
 {
-	BUG_ON(bio->bi_private || bio->bi_end_io);
+	BUG_ON(prev->bi_private || prev->bi_end_io);
 
-	bio->bi_private = parent;
-	bio->bi_end_io	= bio_chain_endio;
-	bio_inc_remaining(parent);
+	prev->bi_private = new;
+	prev->bi_end_io	= bio_chain_endio;
+	bio_inc_remaining(new);
 }
 EXPORT_SYMBOL(bio_chain);
 
-- 
2.52.0


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

* [RFC v2 03/17] bio: use bio_io_error more often
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 01/17] xfs: don't clobber bi_status in xfs_zone_alloc_and_submit Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 02/17] bio: rename bio_chain arguments Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 04/17] bio: add bio_set_status Andreas Gruenbacher
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel, Christoph Hellwig

Instead of setting bio->bi_status to BLK_STS_IOERR and calling
bio_endio(bio), use the shorthand bio_io_error(bio).

Created with Coccinelle using the following semantic patch:

@@
struct bio *bio;
@@
- bio->bi_status = BLK_STS_IOERR;
- bio_endio(bio);
+ bio_io_error(bio);

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 block/fops.c                  | 3 +--
 drivers/block/drbd/drbd_int.h | 3 +--
 drivers/md/bcache/bcache.h    | 3 +--
 drivers/md/bcache/request.c   | 6 ++----
 drivers/md/dm-mpath.c         | 3 +--
 drivers/md/dm-writecache.c    | 3 +--
 fs/f2fs/segment.c             | 3 +--
 7 files changed, 8 insertions(+), 16 deletions(-)

diff --git a/block/fops.c b/block/fops.c
index 5e3db9fead77..b4f911273289 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -221,8 +221,7 @@ static ssize_t __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
 
 		ret = blkdev_iov_iter_get_pages(bio, iter, bdev);
 		if (unlikely(ret)) {
-			bio->bi_status = BLK_STS_IOERR;
-			bio_endio(bio);
+			bio_io_error(bio);
 			break;
 		}
 		if (iocb->ki_flags & IOCB_NOWAIT) {
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index f6d6276974ee..32639e8ea72a 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -1491,8 +1491,7 @@ static inline void drbd_submit_bio_noacct(struct drbd_device *device,
 	__release(local);
 	if (!bio->bi_bdev) {
 		drbd_err(device, "drbd_submit_bio_noacct: bio->bi_bdev == NULL\n");
-		bio->bi_status = BLK_STS_IOERR;
-		bio_endio(bio);
+		bio_io_error(bio);
 		return;
 	}
 
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 1d33e40d26ea..7ad7c778d8ff 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -940,8 +940,7 @@ static inline void closure_bio_submit(struct cache_set *c,
 {
 	closure_get(cl);
 	if (unlikely(test_bit(CACHE_SET_IO_DISABLE, &c->flags))) {
-		bio->bi_status = BLK_STS_IOERR;
-		bio_endio(bio);
+		bio_io_error(bio);
 		return;
 	}
 	submit_bio_noacct(bio);
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index af345dc6fde1..acccecd061ea 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -1192,8 +1192,7 @@ void cached_dev_submit_bio(struct bio *bio)
 
 	if (unlikely((d->c && test_bit(CACHE_SET_IO_DISABLE, &d->c->flags)) ||
 		     dc->io_disable)) {
-		bio->bi_status = BLK_STS_IOERR;
-		bio_endio(bio);
+		bio_io_error(bio);
 		return;
 	}
 
@@ -1296,8 +1295,7 @@ void flash_dev_submit_bio(struct bio *bio)
 	struct bcache_device *d = bio->bi_bdev->bd_disk->private_data;
 
 	if (unlikely(d->c && test_bit(CACHE_SET_IO_DISABLE, &d->c->flags))) {
-		bio->bi_status = BLK_STS_IOERR;
-		bio_endio(bio);
+		bio_io_error(bio);
 		return;
 	}
 
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index aaf4a0a4b0eb..1424ef75d088 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -719,8 +719,7 @@ static void process_queued_bios(struct work_struct *work)
 		r = __multipath_map_bio(m, bio, mpio);
 		switch (r) {
 		case DM_MAPIO_KILL:
-			bio->bi_status = BLK_STS_IOERR;
-			bio_endio(bio);
+			bio_io_error(bio);
 			break;
 		case DM_MAPIO_REQUEUE:
 			bio->bi_status = BLK_STS_DM_REQUEUE;
diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c
index d8de4a3076a1..4f70e2673e4b 100644
--- a/drivers/md/dm-writecache.c
+++ b/drivers/md/dm-writecache.c
@@ -1877,8 +1877,7 @@ static void __writecache_writeback_pmem(struct dm_writecache *wc, struct writeba
 		if (WC_MODE_FUA(wc))
 			bio->bi_opf |= REQ_FUA;
 		if (writecache_has_error(wc)) {
-			bio->bi_status = BLK_STS_IOERR;
-			bio_endio(bio);
+			bio_io_error(bio);
 		} else if (unlikely(!bio_sectors(bio))) {
 			bio->bi_status = BLK_STS_OK;
 			bio_endio(bio);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index b45eace879d7..6444d572f0c7 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -4090,8 +4090,7 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
 	if (fio->bio && *(fio->bio)) {
 		struct bio *bio = *(fio->bio);
 
-		bio->bi_status = BLK_STS_IOERR;
-		bio_endio(bio);
+		bio_io_error(bio);
 		*(fio->bio) = NULL;
 	}
 	return err;
-- 
2.52.0


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

* [RFC v2 04/17] bio: add bio_set_status
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (2 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 03/17] bio: use bio_io_error more often Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 05/17] bio: use bio_set_status for BLK_STS_* status codes Andreas Gruenbacher
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Add a bio_set_status(bio, status) helper that sets bio->bi_status to
status if status != BLK_STS_OK.  Replace instances of this pattern in
the code with a call to the new helper.

The WRITE_ONCE() in bio_set_status() ensures that the compiler won't
reorder things in a weird way, but tearing is not an issue because
bi_status is a single-byte field.

Created with Coccinelle using the following semantic patch and option
'--disable-iso unlikely'.

(Coccinelle occasionally likes to add unnecessary curly braces
in if statements; those were removed by hand.)

@@
expression status;
struct bio *bio;
@@
- if (status)
-       bio->bi_status = status;
+ bio_set_status(bio, status);

@@
expression status;
struct bio *bio;
@@
- if (unlikely(status))
-       bio->bi_status = status;
+ if (unlikely(status))
+	bio_set_status(bio, status);

@@
expression status;
struct bio *bio;
@@
if (status) {
<...
-       bio->bi_status = status;
+       bio_set_status(bio, status);
...>
}

@@
expression status;
struct bio *bio;
@@
if (status != BLK_STS_OK) {
<...
-       bio->bi_status = status;
+       bio_set_status(bio, status);
...>
}

@@
expression status;
struct bio *bio;
statement S;
@@
if (status)
-       bio->bi_status = status;
+       bio_set_status(bio, status);
else
	S

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 block/blk-crypto-fallback.c  |  4 ++--
 block/blk-mq.c               |  4 ++--
 block/t10-pi.c               |  2 +-
 drivers/md/dm-cache-target.c |  3 ++-
 drivers/md/dm.c              |  3 +--
 drivers/nvdimm/pmem.c        |  2 +-
 include/linux/bio.h          | 16 ++++++++++++++++
 7 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c
index 86b27f96051a..a9cb5647879c 100644
--- a/block/blk-crypto-fallback.c
+++ b/block/blk-crypto-fallback.c
@@ -292,7 +292,7 @@ static bool blk_crypto_fallback_encrypt_bio(struct bio **bio_ptr)
 	blk_st = blk_crypto_get_keyslot(blk_crypto_fallback_profile,
 					bc->bc_key, &slot);
 	if (blk_st != BLK_STS_OK) {
-		src_bio->bi_status = blk_st;
+		bio_set_status(src_bio, blk_st);
 		goto out_put_enc_bio;
 	}
 
@@ -395,7 +395,7 @@ static void blk_crypto_fallback_decrypt_bio(struct work_struct *work)
 	blk_st = blk_crypto_get_keyslot(blk_crypto_fallback_profile,
 					bc->bc_key, &slot);
 	if (blk_st != BLK_STS_OK) {
-		bio->bi_status = blk_st;
+		bio_set_status(bio, blk_st);
 		goto out_no_keyslot;
 	}
 
diff --git a/block/blk-mq.c b/block/blk-mq.c
index d626d32f6e57..9d1883d653c4 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -970,7 +970,7 @@ bool blk_update_request(struct request *req, blk_status_t error,
 		unsigned bio_bytes = min(bio->bi_iter.bi_size, nr_bytes);
 
 		if (unlikely(error))
-			bio->bi_status = error;
+			bio_set_status(bio, error);
 
 		if (bio_bytes == bio->bi_iter.bi_size) {
 			req->bio = bio->bi_next;
@@ -3206,7 +3206,7 @@ void blk_mq_submit_bio(struct bio *bio)
 
 	ret = blk_crypto_rq_get_keyslot(rq);
 	if (ret != BLK_STS_OK) {
-		bio->bi_status = ret;
+		bio_set_status(bio, ret);
 		bio_endio(bio);
 		blk_mq_free_request(rq);
 		return;
diff --git a/block/t10-pi.c b/block/t10-pi.c
index 0c4ed9702146..968cf1fe45f0 100644
--- a/block/t10-pi.c
+++ b/block/t10-pi.c
@@ -440,7 +440,7 @@ void blk_integrity_verify_iter(struct bio *bio, struct bvec_iter *saved_iter)
 		kunmap_local(kaddr);
 
 		if (ret) {
-			bio->bi_status = ret;
+			bio_set_status(bio, ret);
 			return;
 		}
 	}
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index a10d75a562db..03816f0dcb58 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -1190,7 +1190,8 @@ static void mg_complete(struct dm_cache_migration *mg, bool success)
 			if (success)
 				force_set_dirty(cache, cblock);
 			else if (mg->k.input)
-				mg->overwrite_bio->bi_status = mg->k.input;
+				bio_set_status(mg->overwrite_bio,
+					       mg->k.input);
 			else
 				mg->overwrite_bio->bi_status = BLK_STS_IOERR;
 			bio_endio(mg->overwrite_bio);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 6c83ab940af7..cbc64377fa96 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -983,8 +983,7 @@ static void __dm_io_complete(struct dm_io *io, bool first_stage)
 		queue_io(md, bio);
 	} else {
 		/* done with normal IO or empty flush */
-		if (io_error)
-			bio->bi_status = io_error;
+		bio_set_status(bio, io_error);
 		bio_endio(bio);
 	}
 }
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 05785ff21a8b..4fdcbe56a3bc 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -222,7 +222,7 @@ static void pmem_submit_bio(struct bio *bio)
 			rc = pmem_do_read(pmem, bvec.bv_page, bvec.bv_offset,
 				iter.bi_sector, bvec.bv_len);
 		if (rc) {
-			bio->bi_status = rc;
+			bio_set_status(bio, rc);
 			break;
 		}
 	}
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 16c1c85613b7..a1324ce61ebd 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -376,6 +376,22 @@ void submit_bio(struct bio *bio);
 
 extern void bio_endio(struct bio *);
 
+/**
+ * bio_set_status - set the status of bio
+ * @bio: bio
+ * @status: a BLK_STS_* status code
+ *
+ * Set the status of @bio to @status unless @status is BLK_STS_OK (0).  In bio
+ * chains, this function may be called repeatedly on the same bio.  In that
+ * case, it can override a previous error, but it will never revert from an
+ * error back to BLK_STS_OK.
+ */
+static inline void bio_set_status(struct bio *bio, blk_status_t status)
+{
+	if (status != BLK_STS_OK)
+		WRITE_ONCE(bio->bi_status, status);
+}
+
 static inline void bio_io_error(struct bio *bio)
 {
 	bio->bi_status = BLK_STS_IOERR;
-- 
2.52.0


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

* [RFC v2 05/17] bio: use bio_set_status for BLK_STS_* status codes
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (3 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 04/17] bio: add bio_set_status Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 06/17] bio: do not check bio->bi_status before assigning to it Andreas Gruenbacher
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Use bio_set_status(bio, status) in cases where status is a blk_status_t
constant other than BLK_STS_OK.  In these cases, the compiler knows that
status != BLK_STS_OK, so it can optimize out the check in
bio_set_status() and effectively end up with identical code.

Created with Coccinelle using the following semantic patch:

@@
struct bio *bio;
constant C != 0 && != BLK_STS_OK;
@@
- bio->bi_status = C;
+ bio_set_status(bio, C);

@@
struct bio bio;
constant C != 0 && != BLK_STS_OK;
@@
- bio.bi_status = C;
+ bio_set_status(&bio, C);

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 block/bio-integrity-auto.c    |  2 +-
 block/blk-core.c              |  2 +-
 block/blk-crypto-fallback.c   | 16 ++++++++--------
 block/blk-crypto-internal.h   |  2 +-
 block/blk-crypto.c            |  4 ++--
 block/blk-mq.c                |  4 ++--
 drivers/block/aoe/aoecmd.c    |  8 ++++----
 drivers/block/aoe/aoedev.c    |  2 +-
 drivers/block/drbd/drbd_req.c |  4 ++--
 drivers/block/zram/zram_drv.c |  4 ++--
 drivers/md/bcache/request.c   |  2 +-
 drivers/md/dm-cache-target.c  |  5 +++--
 drivers/md/dm-integrity.c     | 12 ++++++------
 drivers/md/dm-mpath.c         |  2 +-
 drivers/md/dm-raid1.c         |  6 +++---
 drivers/md/dm-thin.c          |  2 +-
 drivers/md/dm-writecache.c    |  4 ++--
 drivers/md/md.c               |  2 +-
 drivers/md/raid1-10.c         |  2 +-
 drivers/md/raid1.c            |  2 +-
 drivers/md/raid10.c           | 10 +++++-----
 drivers/md/raid5.c            |  4 ++--
 drivers/nvdimm/btt.c          |  2 +-
 fs/btrfs/bio.c                |  6 +++---
 fs/f2fs/data.c                |  6 +++---
 fs/verity/verify.c            |  2 +-
 include/linux/bio.h           |  4 ++--
 27 files changed, 61 insertions(+), 60 deletions(-)

diff --git a/block/bio-integrity-auto.c b/block/bio-integrity-auto.c
index 687952f63bbb..736d53a7f699 100644
--- a/block/bio-integrity-auto.c
+++ b/block/bio-integrity-auto.c
@@ -190,7 +190,7 @@ bool bio_integrity_prep(struct bio *bio)
 err_free_buf:
 	kfree(buf);
 err_end_io:
-	bio->bi_status = BLK_STS_RESOURCE;
+	bio_set_status(bio, BLK_STS_RESOURCE);
 	bio_endio(bio);
 	return false;
 }
diff --git a/block/blk-core.c b/block/blk-core.c
index 14ae73eebe0d..4f7b7cf50d23 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -640,7 +640,7 @@ static void __submit_bio(struct bio *bio)
 	
 		if ((bio->bi_opf & REQ_POLLED) &&
 		    !(disk->queue->limits.features & BLK_FEAT_POLL)) {
-			bio->bi_status = BLK_STS_NOTSUPP;
+			bio_set_status(bio, BLK_STS_NOTSUPP);
 			bio_endio(bio);
 		} else {
 			disk->fops->submit_bio(bio);
diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c
index a9cb5647879c..8a2631b1e7e1 100644
--- a/block/blk-crypto-fallback.c
+++ b/block/blk-crypto-fallback.c
@@ -281,7 +281,7 @@ static bool blk_crypto_fallback_encrypt_bio(struct bio **bio_ptr)
 	/* Allocate bounce bio for encryption */
 	enc_bio = blk_crypto_fallback_clone_bio(src_bio);
 	if (!enc_bio) {
-		src_bio->bi_status = BLK_STS_RESOURCE;
+		bio_set_status(src_bio, BLK_STS_RESOURCE);
 		return false;
 	}
 
@@ -298,7 +298,7 @@ static bool blk_crypto_fallback_encrypt_bio(struct bio **bio_ptr)
 
 	/* and then allocate an skcipher_request for it */
 	if (!blk_crypto_fallback_alloc_cipher_req(slot, &ciph_req, &wait)) {
-		src_bio->bi_status = BLK_STS_RESOURCE;
+		bio_set_status(src_bio, BLK_STS_RESOURCE);
 		goto out_release_keyslot;
 	}
 
@@ -319,7 +319,7 @@ static bool blk_crypto_fallback_encrypt_bio(struct bio **bio_ptr)
 		enc_bvec->bv_page = ciphertext_page;
 
 		if (!ciphertext_page) {
-			src_bio->bi_status = BLK_STS_RESOURCE;
+			bio_set_status(src_bio, BLK_STS_RESOURCE);
 			goto out_free_bounce_pages;
 		}
 
@@ -334,7 +334,7 @@ static bool blk_crypto_fallback_encrypt_bio(struct bio **bio_ptr)
 			if (crypto_wait_req(crypto_skcipher_encrypt(ciph_req),
 					    &wait)) {
 				i++;
-				src_bio->bi_status = BLK_STS_IOERR;
+				bio_set_status(src_bio, BLK_STS_IOERR);
 				goto out_free_bounce_pages;
 			}
 			bio_crypt_dun_increment(curr_dun, 1);
@@ -401,7 +401,7 @@ static void blk_crypto_fallback_decrypt_bio(struct work_struct *work)
 
 	/* and then allocate an skcipher_request for it */
 	if (!blk_crypto_fallback_alloc_cipher_req(slot, &ciph_req, &wait)) {
-		bio->bi_status = BLK_STS_RESOURCE;
+		bio_set_status(bio, BLK_STS_RESOURCE);
 		goto out;
 	}
 
@@ -421,7 +421,7 @@ static void blk_crypto_fallback_decrypt_bio(struct work_struct *work)
 			blk_crypto_dun_to_iv(curr_dun, &iv);
 			if (crypto_wait_req(crypto_skcipher_decrypt(ciph_req),
 					    &wait)) {
-				bio->bi_status = BLK_STS_IOERR;
+				bio_set_status(bio, BLK_STS_IOERR);
 				goto out;
 			}
 			bio_crypt_dun_increment(curr_dun, 1);
@@ -492,13 +492,13 @@ bool blk_crypto_fallback_bio_prep(struct bio **bio_ptr)
 
 	if (WARN_ON_ONCE(!tfms_inited[bc->bc_key->crypto_cfg.crypto_mode])) {
 		/* User didn't call blk_crypto_start_using_key() first */
-		bio->bi_status = BLK_STS_IOERR;
+		bio_set_status(bio, BLK_STS_IOERR);
 		return false;
 	}
 
 	if (!__blk_crypto_cfg_supported(blk_crypto_fallback_profile,
 					&bc->bc_key->crypto_cfg)) {
-		bio->bi_status = BLK_STS_NOTSUPP;
+		bio_set_status(bio, BLK_STS_NOTSUPP);
 		return false;
 	}
 
diff --git a/block/blk-crypto-internal.h b/block/blk-crypto-internal.h
index ccf6dff6ff6b..fc606352a31a 100644
--- a/block/blk-crypto-internal.h
+++ b/block/blk-crypto-internal.h
@@ -235,7 +235,7 @@ blk_crypto_fallback_start_using_mode(enum blk_crypto_mode_num mode_num)
 static inline bool blk_crypto_fallback_bio_prep(struct bio **bio_ptr)
 {
 	pr_warn_once("crypto API fallback disabled; failing request.\n");
-	(*bio_ptr)->bi_status = BLK_STS_NOTSUPP;
+	bio_set_status((*bio_ptr), BLK_STS_NOTSUPP);
 	return false;
 }
 
diff --git a/block/blk-crypto.c b/block/blk-crypto.c
index 3e7bf1974cbd..cbba31cd4e58 100644
--- a/block/blk-crypto.c
+++ b/block/blk-crypto.c
@@ -287,12 +287,12 @@ bool __blk_crypto_bio_prep(struct bio **bio_ptr)
 
 	/* Error if bio has no data. */
 	if (WARN_ON_ONCE(!bio_has_data(bio))) {
-		bio->bi_status = BLK_STS_IOERR;
+		bio_set_status(bio, BLK_STS_IOERR);
 		goto fail;
 	}
 
 	if (!bio_crypt_check_alignment(bio)) {
-		bio->bi_status = BLK_STS_INVAL;
+		bio_set_status(bio, BLK_STS_INVAL);
 		goto fail;
 	}
 
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 9d1883d653c4..8fb35a258b4c 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -980,7 +980,7 @@ bool blk_update_request(struct request *req, blk_status_t error,
 			 * as the BIO fragments may end up not being written
 			 * sequentially.
 			 */
-			bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(bio, BLK_STS_IOERR);
 		}
 
 		/* Completion has already been traced */
@@ -3165,7 +3165,7 @@ void blk_mq_submit_bio(struct bio *bio)
 	}
 
 	if ((bio->bi_opf & REQ_POLLED) && !blk_mq_can_poll(q)) {
-		bio->bi_status = BLK_STS_NOTSUPP;
+		bio_set_status(bio, BLK_STS_NOTSUPP);
 		bio_endio(bio);
 		goto queue_exit;
 	}
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index a9affb7c264d..a5eb4912fb3e 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -1112,7 +1112,7 @@ ktiocomplete(struct frame *f)
 			ahout->cmdstat, ahin->cmdstat,
 			d->aoemajor, d->aoeminor);
 noskb:		if (buf)
-			buf->bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(buf->bio, BLK_STS_IOERR);
 		goto out;
 	}
 
@@ -1125,7 +1125,7 @@ noskb:		if (buf)
 				"aoe: runt data size in read from",
 				(long) d->aoemajor, d->aoeminor,
 			       skb->len, n);
-			buf->bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(buf->bio, BLK_STS_IOERR);
 			break;
 		}
 		if (n > f->iter.bi_size) {
@@ -1133,7 +1133,7 @@ noskb:		if (buf)
 				"aoe: too-large data size in read from",
 				(long) d->aoemajor, d->aoeminor,
 				n, f->iter.bi_size);
-			buf->bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(buf->bio, BLK_STS_IOERR);
 			break;
 		}
 		bvcpy(skb, f->buf->bio, f->iter, n);
@@ -1637,7 +1637,7 @@ aoe_failbuf(struct aoedev *d, struct buf *buf)
 	if (buf == NULL)
 		return;
 	buf->iter.bi_size = 0;
-	buf->bio->bi_status = BLK_STS_IOERR;
+	bio_set_status(buf->bio, BLK_STS_IOERR);
 	if (buf->nframesout == 0)
 		aoe_end_buf(d, buf);
 }
diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c
index 3a240755045b..11320258d0ce 100644
--- a/drivers/block/aoe/aoedev.c
+++ b/drivers/block/aoe/aoedev.c
@@ -170,7 +170,7 @@ aoe_failip(struct aoedev *d)
 
 	req = blk_mq_rq_to_pdu(rq);
 	while ((bio = d->ip.nxbio)) {
-		bio->bi_status = BLK_STS_IOERR;
+		bio_set_status(bio, BLK_STS_IOERR);
 		d->ip.nxbio = bio->bi_next;
 		req->nr_bios--;
 	}
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index d15826f6ee81..baa08d56494d 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -1143,7 +1143,7 @@ static void drbd_process_discard_or_zeroes_req(struct drbd_request *req, int fla
 	int err = drbd_issue_discard_or_zero_out(req->device,
 				req->i.sector, req->i.size >> 9, flags);
 	if (err)
-		req->private_bio->bi_status = BLK_STS_IOERR;
+		bio_set_status(req->private_bio, BLK_STS_IOERR);
 	bio_endio(req->private_bio);
 }
 
@@ -1211,7 +1211,7 @@ drbd_request_prepare(struct drbd_device *device, struct bio *bio)
 		/* only pass the error to the upper layers.
 		 * if user cannot handle io errors, that's not our business. */
 		drbd_err(device, "could not kmalloc() req\n");
-		bio->bi_status = BLK_STS_RESOURCE;
+		bio_set_status(bio, BLK_STS_RESOURCE);
 		bio_endio(bio);
 		return ERR_PTR(-ENOMEM);
 	}
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index a43074657531..975757f01bb8 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -2341,7 +2341,7 @@ static void zram_bio_read(struct zram *zram, struct bio *bio)
 
 		if (zram_bvec_read(zram, &bv, index, offset, bio) < 0) {
 			atomic64_inc(&zram->stats.failed_reads);
-			bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(bio, BLK_STS_IOERR);
 			break;
 		}
 		flush_dcache_page(bv.bv_page);
@@ -2372,7 +2372,7 @@ static void zram_bio_write(struct zram *zram, struct bio *bio)
 
 		if (zram_bvec_write(zram, &bv, index, offset, bio) < 0) {
 			atomic64_inc(&zram->stats.failed_writes);
-			bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(bio, BLK_STS_IOERR);
 			break;
 		}
 
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index acccecd061ea..3ea67a8e252a 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -1120,7 +1120,7 @@ static void detached_dev_do_request(struct bcache_device *d, struct bio *bio,
 	 */
 	ddip = kzalloc(sizeof(struct detached_dev_io_private), GFP_NOIO);
 	if (!ddip) {
-		bio->bi_status = BLK_STS_RESOURCE;
+		bio_set_status(bio, BLK_STS_RESOURCE);
 		bio->bi_end_io(bio);
 		return;
 	}
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 03816f0dcb58..d1dbd4ddaadb 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -1193,7 +1193,8 @@ static void mg_complete(struct dm_cache_migration *mg, bool success)
 				bio_set_status(mg->overwrite_bio,
 					       mg->k.input);
 			else
-				mg->overwrite_bio->bi_status = BLK_STS_IOERR;
+				bio_set_status(mg->overwrite_bio,
+					       BLK_STS_IOERR);
 			bio_endio(mg->overwrite_bio);
 		} else {
 			if (success)
@@ -1855,7 +1856,7 @@ static void requeue_deferred_bios(struct cache *cache)
 	bio_list_merge_init(&bios, &cache->deferred_bios);
 
 	while ((bio = bio_list_pop(&bios))) {
-		bio->bi_status = BLK_STS_DM_REQUEUE;
+		bio_set_status(bio, BLK_STS_DM_REQUEUE);
 		bio_endio(bio);
 		cond_resched();
 	}
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index 170bf67a2edd..e380ed7b2a7b 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -2513,7 +2513,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
 	sector_t recalc_sector;
 
 	if (unlikely(bio_integrity(bio))) {
-		bio->bi_status = BLK_STS_NOTSUPP;
+		bio_set_status(bio, BLK_STS_NOTSUPP);
 		bio_endio(bio);
 		return DM_MAPIO_SUBMITTED;
 	}
@@ -2535,7 +2535,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
 			if (dio->payload_len > x_size) {
 				unsigned sectors = ((x_size - extra_size) / ic->tuple_size) << ic->sb->log2_sectors_per_block;
 				if (WARN_ON(!sectors || sectors >= bio_sectors(bio))) {
-					bio->bi_status = BLK_STS_NOTSUPP;
+					bio_set_status(bio, BLK_STS_NOTSUPP);
 					bio_endio(bio);
 					return DM_MAPIO_SUBMITTED;
 				}
@@ -2616,7 +2616,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
 	ret = bio_integrity_add_page(bio, virt_to_page(dio->integrity_payload),
 					dio->payload_len, offset_in_page(dio->integrity_payload));
 	if (unlikely(ret != dio->payload_len)) {
-		bio->bi_status = BLK_STS_RESOURCE;
+		bio_set_status(bio, BLK_STS_RESOURCE);
 		bio_endio(bio);
 		return DM_MAPIO_SUBMITTED;
 	}
@@ -2670,7 +2670,7 @@ static void dm_integrity_inline_recheck(struct work_struct *w)
 		r = bio_integrity_add_page(outgoing_bio, virt_to_page(dio->integrity_payload), ic->tuple_size, 0);
 		if (unlikely(r != ic->tuple_size)) {
 			bio_put(outgoing_bio);
-			bio->bi_status = BLK_STS_RESOURCE;
+			bio_set_status(bio, BLK_STS_RESOURCE);
 			bio_endio(bio);
 			return;
 		}
@@ -2694,7 +2694,7 @@ static void dm_integrity_inline_recheck(struct work_struct *w)
 			dm_audit_log_bio(DM_MSG_PREFIX, "integrity-checksum",
 				bio, dio->bio_details.bi_iter.bi_sector, 0);
 
-			bio->bi_status = BLK_STS_PROTECTION;
+			bio_set_status(bio, BLK_STS_PROTECTION);
 			bio_endio(bio);
 			return;
 		}
@@ -2784,7 +2784,7 @@ static void integrity_bio_wait(struct work_struct *w)
 		int r = dm_integrity_map_inline(dio, false);
 		switch (r) {
 			case DM_MAPIO_KILL:
-				bio->bi_status = BLK_STS_IOERR;
+				bio_set_status(bio, BLK_STS_IOERR);
 				fallthrough;
 			case DM_MAPIO_REMAPPED:
 				submit_bio_noacct(bio);
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 1424ef75d088..761e5e79d4a7 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -722,7 +722,7 @@ static void process_queued_bios(struct work_struct *work)
 			bio_io_error(bio);
 			break;
 		case DM_MAPIO_REQUEUE:
-			bio->bi_status = BLK_STS_DM_REQUEUE;
+			bio_set_status(bio, BLK_STS_DM_REQUEUE);
 			bio_endio(bio);
 			break;
 		case DM_MAPIO_REMAPPED:
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index 268f734ca9c3..c54995847db0 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -491,9 +491,9 @@ static void hold_bio(struct mirror_set *ms, struct bio *bio)
 		 * If device is suspended, complete the bio.
 		 */
 		if (dm_noflush_suspending(ms->ti))
-			bio->bi_status = BLK_STS_DM_REQUEUE;
+			bio_set_status(bio, BLK_STS_DM_REQUEUE);
 		else
-			bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(bio, BLK_STS_IOERR);
 
 		bio_endio(bio);
 		return;
@@ -627,7 +627,7 @@ static void write_callback(unsigned long error, void *context)
 	 * degrade the array.
 	 */
 	if (bio_op(bio) == REQ_OP_DISCARD) {
-		bio->bi_status = BLK_STS_NOTSUPP;
+		bio_set_status(bio, BLK_STS_NOTSUPP);
 		bio_endio(bio);
 		return;
 	}
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index c84149ba4e38..00ede45a3d27 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -2731,7 +2731,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
 	thin_hook_bio(tc, bio);
 
 	if (tc->requeue_mode) {
-		bio->bi_status = BLK_STS_DM_REQUEUE;
+		bio_set_status(bio, BLK_STS_DM_REQUEUE);
 		bio_endio(bio);
 		return DM_MAPIO_SUBMITTED;
 	}
diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c
index 4f70e2673e4b..f615ff46cc41 100644
--- a/drivers/md/dm-writecache.c
+++ b/drivers/md/dm-writecache.c
@@ -1265,7 +1265,7 @@ static void bio_copy_block(struct dm_writecache *wc, struct bio *bio, void *data
 			flush_dcache_page(bio_page(bio));
 			if (unlikely(r)) {
 				writecache_error(wc, r, "hardware memory error when reading data: %d", r);
-				bio->bi_status = BLK_STS_IOERR;
+				bio_set_status(bio, BLK_STS_IOERR);
 			}
 		} else {
 			flush_dcache_page(bio_page(bio));
@@ -1312,7 +1312,7 @@ static int writecache_flush_thread(void *data)
 			writecache_flush(wc);
 			wc_unlock(wc);
 			if (writecache_has_error(wc))
-				bio->bi_status = BLK_STS_IOERR;
+				bio_set_status(bio, BLK_STS_IOERR);
 			bio_endio(bio);
 		}
 	}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 41c476b40c7a..cbca792fa380 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -447,7 +447,7 @@ static void md_submit_bio(struct bio *bio)
 
 	if (mddev->ro == MD_RDONLY && unlikely(rw == WRITE)) {
 		if (bio_sectors(bio) != 0)
-			bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(bio, BLK_STS_IOERR);
 		bio_endio(bio);
 		return;
 	}
diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
index 521625756128..504730aba9df 100644
--- a/drivers/md/raid1-10.c
+++ b/drivers/md/raid1-10.c
@@ -104,7 +104,7 @@ static void md_bio_reset_resync_pages(struct bio *bio, struct resync_pages *rp,
 		int len = min_t(int, size, PAGE_SIZE);
 
 		if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
-			bio->bi_status = BLK_STS_RESOURCE;
+			bio_set_status(bio, BLK_STS_RESOURCE);
 			bio_endio(bio);
 			return;
 		}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 592a40233004..923b6a762858 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -301,7 +301,7 @@ static void call_bio_endio(struct r1bio *r1_bio)
 	struct bio *bio = r1_bio->master_bio;
 
 	if (!test_bit(R1BIO_Uptodate, &r1_bio->state))
-		bio->bi_status = BLK_STS_IOERR;
+		bio_set_status(bio, BLK_STS_IOERR);
 
 	bio_endio(bio);
 }
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 14dcd5142eb4..59b900672a7a 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -324,7 +324,7 @@ static void raid_end_bio_io(struct r10bio *r10_bio)
 
 	if (!test_and_set_bit(R10BIO_Returned, &r10_bio->state)) {
 		if (!test_bit(R10BIO_Uptodate, &r10_bio->state))
-			bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(bio, BLK_STS_IOERR);
 		bio_endio(bio);
 	}
 
@@ -3600,7 +3600,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 				r10_bio->devs[i].repl_bio->bi_end_io = NULL;
 
 			bio = r10_bio->devs[i].bio;
-			bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(bio, BLK_STS_IOERR);
 			rdev = conf->mirrors[d].rdev;
 			if (rdev == NULL || test_bit(Faulty, &rdev->flags))
 				continue;
@@ -3637,7 +3637,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 
 			/* Need to set up for writing to the replacement */
 			bio = r10_bio->devs[i].repl_bio;
-			bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(bio, BLK_STS_IOERR);
 
 			sector = r10_bio->devs[i].addr;
 			bio->bi_next = biolist;
@@ -3683,7 +3683,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 			struct resync_pages *rp = get_resync_pages(bio);
 			page = resync_fetch_page(rp, page_idx);
 			if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
-				bio->bi_status = BLK_STS_RESOURCE;
+				bio_set_status(bio, BLK_STS_RESOURCE);
 				bio_endio(bio);
 				goto giveup;
 			}
@@ -4865,7 +4865,7 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
 			len = PAGE_SIZE;
 		for (bio = blist; bio ; bio = bio->bi_next) {
 			if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
-				bio->bi_status = BLK_STS_RESOURCE;
+				bio_set_status(bio, BLK_STS_RESOURCE);
 				bio_endio(bio);
 				return sectors_done;
 			}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 24b32a0c95b4..fa84fadac2fd 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5974,7 +5974,7 @@ static enum stripe_result make_stripe_request(struct mddev *mddev,
 	sh = raid5_get_active_stripe(conf, ctx, new_sector, flags);
 	if (unlikely(!sh)) {
 		/* cannot get stripe, just give-up */
-		bi->bi_status = BLK_STS_IOERR;
+		bio_set_status(bi, BLK_STS_IOERR);
 		return STRIPE_FAIL;
 	}
 
@@ -6037,7 +6037,7 @@ static enum stripe_result make_stripe_request(struct mddev *mddev,
 	raid5_release_stripe(sh);
 out:
 	if (ret == STRIPE_SCHEDULE_AND_RETRY && reshape_interrupted(mddev)) {
-		bi->bi_status = BLK_STS_RESOURCE;
+		bio_set_status(bi, BLK_STS_RESOURCE);
 		ret = STRIPE_WAIT_RESHAPE;
 		pr_err_ratelimited("dm-raid456: io across reshape position while reshape can't make progress");
 	}
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index a933db961ed7..3da30dcbf26c 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -1456,7 +1456,7 @@ static void btt_submit_bio(struct bio *bio)
 				len % btt->sector_size) {
 			dev_err_ratelimited(&btt->nd_btt->dev,
 				"unaligned bio segment (len: %d)\n", len);
-			bio->bi_status = BLK_STS_IOERR;
+			bio_set_status(bio, BLK_STS_IOERR);
 			break;
 		}
 
diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index 21df48e6c4fa..519635e2833a 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -174,7 +174,7 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
 		mirror = next_repair_mirror(fbio, mirror);
 		if (mirror == fbio->bbio->mirror_num) {
 			btrfs_debug(fs_info, "no mirror left");
-			fbio->bbio->bio.bi_status = BLK_STS_IOERR;
+			bio_set_status(&fbio->bbio->bio, BLK_STS_IOERR);
 			goto done;
 		}
 
@@ -225,7 +225,7 @@ static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
 	num_copies = btrfs_num_copies(fs_info, logical, sectorsize);
 	if (num_copies == 1) {
 		btrfs_debug(fs_info, "no copy to repair from");
-		failed_bbio->bio.bi_status = BLK_STS_IOERR;
+		bio_set_status(&failed_bbio->bio, BLK_STS_IOERR);
 		return fbio;
 	}
 
@@ -382,7 +382,7 @@ static void btrfs_orig_write_end_io(struct bio *bio)
 	 * threshold.
 	 */
 	if (atomic_read(&bioc->error) > bioc->max_errors)
-		bio->bi_status = BLK_STS_IOERR;
+		bio_set_status(bio, BLK_STS_IOERR);
 	else
 		bio->bi_status = BLK_STS_OK;
 
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 775aa4f63aa3..3b031d0391d3 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -188,7 +188,7 @@ static void f2fs_verify_bio(struct work_struct *work)
 
 			if (!f2fs_is_compressed_page(folio) &&
 			    !fsverity_verify_page(&folio->page)) {
-				bio->bi_status = BLK_STS_IOERR;
+				bio_set_status(bio, BLK_STS_IOERR);
 				break;
 			}
 		}
@@ -286,7 +286,7 @@ static void f2fs_read_end_io(struct bio *bio)
 	ctx = bio->bi_private;
 
 	if (time_to_inject(sbi, FAULT_READ_IO))
-		bio->bi_status = BLK_STS_IOERR;
+		bio_set_status(bio, BLK_STS_IOERR);
 
 	if (bio->bi_status != BLK_STS_OK) {
 		f2fs_finish_read_bio(bio, intask);
@@ -323,7 +323,7 @@ static void f2fs_write_end_io(struct bio *bio)
 	sbi = bio->bi_private;
 
 	if (time_to_inject(sbi, FAULT_WRITE_IO))
-		bio->bi_status = BLK_STS_IOERR;
+		bio_set_status(bio, BLK_STS_IOERR);
 
 	bio_for_each_folio_all(fi, bio) {
 		struct folio *folio = fi.folio;
diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index 86067c8b40cf..17a168744037 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -432,7 +432,7 @@ void fsverity_verify_bio(struct bio *bio)
 
 ioerr:
 	fsverity_clear_pending_blocks(&ctx);
-	bio->bi_status = BLK_STS_IOERR;
+	bio_set_status(bio, BLK_STS_IOERR);
 }
 EXPORT_SYMBOL_GPL(fsverity_verify_bio);
 #endif /* CONFIG_BLOCK */
diff --git a/include/linux/bio.h b/include/linux/bio.h
index a1324ce61ebd..cf0f5aba7bee 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -394,14 +394,14 @@ static inline void bio_set_status(struct bio *bio, blk_status_t status)
 
 static inline void bio_io_error(struct bio *bio)
 {
-	bio->bi_status = BLK_STS_IOERR;
+	bio_set_status(bio, BLK_STS_IOERR);
 	bio_endio(bio);
 }
 
 static inline void bio_wouldblock_error(struct bio *bio)
 {
 	bio_set_flag(bio, BIO_QUIET);
-	bio->bi_status = BLK_STS_AGAIN;
+	bio_set_status(bio, BLK_STS_AGAIN);
 	bio_endio(bio);
 }
 
-- 
2.52.0


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

* [RFC v2 06/17] bio: do not check bio->bi_status before assigning to it
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (4 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 05/17] bio: use bio_set_status for BLK_STS_* status codes Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 07/17] block: consecutive blk_status_t error codes Andreas Gruenbacher
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

In a few places, we check if to->bi_status is 0 before setting it.  This
creates the impression of preserving the first error code that occurs,
but that is not actually the case because bio->bi_status is not updated
atomically.  (And even updating bio->bi_status atomically wouldn't
preserve the first error in bio chains longer than two.)  We don't
actually need to preserve the first error code, though; and all that
matters is that when an error occurs, one of the error codes is
preserved.

Checking bio->bi_status before assigning to it might also have been
meant as an optimization.  We are on an error path and the potential
benefit is miniscule, though.

So, don't check bio->bi_status before assigning to it.

Created with Coccinelle using the following semantic patch:

@@
expression status;
struct bio *bio;
@@
- if (status && !bio->bi_status)
-       bio->bi_status = status;
+ bio_set_status(bio, status);

@@
expression status;
struct bio *bio;
@@
- if (status != BLK_STS_OK && bio->bi_status == BLK_STS_OK)
-       bio->bi_status = status;
+ bio_set_status(bio, status);

@@
expression status;
struct bio *bio;
@@
- if (unlikely(status) && !bio->bi_status)
-       bio->bi_status = status;
+ bio_set_status(bio, status);

@@
expression status;
struct bio bio;
@@
- if (status && !bio.bi_status)
-       bio.bi_status = status;
+ bio_set_status(&bio, status);
---
 block/bio.c                  | 3 +--
 block/fops.c                 | 3 +--
 drivers/md/dm-integrity.c    | 3 +--
 drivers/md/dm-zoned-target.c | 3 +--
 drivers/md/md.c              | 6 ++----
 5 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index 3f408e1ba13f..5389321872f0 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -314,8 +314,7 @@ static struct bio *__bio_chain_endio(struct bio *bio)
 {
 	struct bio *parent = bio->bi_private;
 
-	if (bio->bi_status && !parent->bi_status)
-		parent->bi_status = bio->bi_status;
+	bio_set_status(parent, bio->bi_status);
 	bio_put(bio);
 	return parent;
 }
diff --git a/block/fops.c b/block/fops.c
index b4f911273289..a4a6972cbfbf 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -135,8 +135,7 @@ static void blkdev_bio_end_io(struct bio *bio)
 	bool should_dirty = dio->flags & DIO_SHOULD_DIRTY;
 	bool is_sync = dio->flags & DIO_IS_SYNC;
 
-	if (bio->bi_status && !dio->bio.bi_status)
-		dio->bio.bi_status = bio->bi_status;
+	bio_set_status(&dio->bio, bio->bi_status);
 
 	if (bio_integrity(bio))
 		bio_integrity_unmap_user(bio);
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index e380ed7b2a7b..6d04b067060d 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -1616,8 +1616,7 @@ static void dec_in_flight(struct dm_integrity_io *dio)
 			schedule_autocommit(ic);
 
 		bio = dm_bio_from_per_bio_data(dio, sizeof(struct dm_integrity_io));
-		if (unlikely(dio->bi_status) && !bio->bi_status)
-			bio->bi_status = dio->bi_status;
+		bio_set_status(bio, dio->bi_status);
 		if (likely(!bio->bi_status) && unlikely(bio_sectors(bio) != dio->range.n_sectors)) {
 			dio->range.logical_sector += dio->range.n_sectors;
 			bio_advance(bio, dio->range.n_sectors << SECTOR_SHIFT);
diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c
index 9da329078ea4..efb960eca8ea 100644
--- a/drivers/md/dm-zoned-target.c
+++ b/drivers/md/dm-zoned-target.c
@@ -77,8 +77,7 @@ static inline void dmz_bio_endio(struct bio *bio, blk_status_t status)
 	struct dmz_bioctx *bioctx =
 		dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
 
-	if (status != BLK_STS_OK && bio->bi_status == BLK_STS_OK)
-		bio->bi_status = status;
+	bio_set_status(bio, status);
 	if (bioctx->dev && bio->bi_status != BLK_STS_OK)
 		bioctx->dev->flags |= DMZ_CHECK_BDEV;
 
diff --git a/drivers/md/md.c b/drivers/md/md.c
index cbca792fa380..5afc6d63aa7b 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9084,8 +9084,7 @@ static void md_end_clone_io(struct bio *bio)
 	if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
 		md_bitmap_end(mddev, md_io_clone);
 
-	if (bio->bi_status && !orig_bio->bi_status)
-		orig_bio->bi_status = bio->bi_status;
+	bio_set_status(orig_bio, bio->bi_status);
 
 	if (md_io_clone->start_time)
 		bio_end_io_acct(orig_bio, md_io_clone->start_time);
@@ -9136,8 +9135,7 @@ void md_free_cloned_bio(struct bio *bio)
 	if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
 		md_bitmap_end(mddev, md_io_clone);
 
-	if (bio->bi_status && !orig_bio->bi_status)
-		orig_bio->bi_status = bio->bi_status;
+	bio_set_status(orig_bio, bio->bi_status);
 
 	if (md_io_clone->start_time)
 		bio_end_io_acct(orig_bio, md_io_clone->start_time);
-- 
2.52.0


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

* [RFC v2 07/17] block: consecutive blk_status_t error codes
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (5 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 06/17] bio: do not check bio->bi_status before assigning to it Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 08/17] block: fix blk_status_to_{errno,str} inconsistency Andreas Gruenbacher
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Since commit 9da3d1e912f3 ("block: Add core atomic write support"),
there is a gap in the blk_status_t codes and block status code 18 is
unused.  This causes errno_to_blk_status() and blk_status_to_str() to
return incorrect values for that code.  Fix by making the blk_status_t
codes consecutive again.

Fixes: 9da3d1e912f3 ("block: Add core atomic write support")
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 include/linux/blk_types.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 44c30183ecc3..044a7cec1d70 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -168,7 +168,7 @@ typedef u16 blk_short_t;
 /*
  * Invalid size or alignment.
  */
-#define BLK_STS_INVAL	((__force blk_status_t)19)
+#define BLK_STS_INVAL	((__force blk_status_t)18)
 
 /**
  * blk_path_error - returns true if error may be path related
-- 
2.52.0


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

* [RFC v2 08/17] block: fix blk_status_to_{errno,str} inconsistency
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (6 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 07/17] block: consecutive blk_status_t error codes Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 09/17] block: turn blk_errors array into a macro Andreas Gruenbacher
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Change blk_status_to_str() to be consistent with blk_status_to_errno()
and return "I/O" for undefined status codes.

With that, the fallback case in blk_errors can be removed with no change
in behavior.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 block/blk-core.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/block/blk-core.c b/block/blk-core.c
index 4f7b7cf50d23..47ce458b4f34 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -161,9 +161,6 @@ static const struct {
 	[BLK_STS_DURATION_LIMIT]	= { -ETIME, "duration limit exceeded" },
 
 	[BLK_STS_INVAL]		= { -EINVAL,	"invalid" },
-
-	/* everything else not covered above: */
-	[BLK_STS_IOERR]		= { -EIO,	"I/O" },
 };
 
 blk_status_t errno_to_blk_status(int errno)
@@ -194,7 +191,7 @@ const char *blk_status_to_str(blk_status_t status)
 	int idx = (__force int)status;
 
 	if (WARN_ON_ONCE(idx >= ARRAY_SIZE(blk_errors)))
-		return "<null>";
+		return "I/O";
 	return blk_errors[idx].name;
 }
 EXPORT_SYMBOL_GPL(blk_status_to_str);
-- 
2.52.0


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

* [RFC v2 09/17] block: turn blk_errors array into a macro
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (7 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 08/17] block: fix blk_status_to_{errno,str} inconsistency Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 10/17] block: optimize blk_status <=> errno conversion Andreas Gruenbacher
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Get rid of the static blk_errors array and replace it with a macro for
generating the appropriate switch statements in errno_to_blk_status(),
blk_status_to_errno(), and blk_status_to_str().

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 block/blk-core.c | 97 ++++++++++++++++++++++++++----------------------
 1 file changed, 52 insertions(+), 45 deletions(-)

diff --git a/block/blk-core.c b/block/blk-core.c
index 47ce458b4f34..9b3100d171b7 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -132,70 +132,77 @@ inline const char *blk_op_str(enum req_op op)
 }
 EXPORT_SYMBOL_GPL(blk_op_str);
 
-static const struct {
-	int		errno;
-	const char	*name;
-} blk_errors[] = {
-	[BLK_STS_OK]		= { 0,		"" },
-	[BLK_STS_NOTSUPP]	= { -EOPNOTSUPP, "operation not supported" },
-	[BLK_STS_TIMEOUT]	= { -ETIMEDOUT,	"timeout" },
-	[BLK_STS_NOSPC]		= { -ENOSPC,	"critical space allocation" },
-	[BLK_STS_TRANSPORT]	= { -ENOLINK,	"recoverable transport" },
-	[BLK_STS_TARGET]	= { -EREMOTEIO,	"critical target" },
-	[BLK_STS_RESV_CONFLICT]	= { -EBADE,	"reservation conflict" },
-	[BLK_STS_MEDIUM]	= { -ENODATA,	"critical medium" },
-	[BLK_STS_PROTECTION]	= { -EILSEQ,	"protection" },
-	[BLK_STS_RESOURCE]	= { -ENOMEM,	"kernel resource" },
-	[BLK_STS_DEV_RESOURCE]	= { -EBUSY,	"device resource" },
-	[BLK_STS_AGAIN]		= { -EAGAIN,	"nonblocking retry" },
-	[BLK_STS_OFFLINE]	= { -ENODEV,	"device offline" },
-
-	/* device mapper special case, should not leak out: */
-	[BLK_STS_DM_REQUEUE]	= { -EREMCHG, "dm internal retry" },
-
-	/* zone device specific errors */
-	[BLK_STS_ZONE_OPEN_RESOURCE]	= { -ETOOMANYREFS, "open zones exceeded" },
-	[BLK_STS_ZONE_ACTIVE_RESOURCE]	= { -EOVERFLOW, "active zones exceeded" },
-
-	/* Command duration limit device-side timeout */
-	[BLK_STS_DURATION_LIMIT]	= { -ETIME, "duration limit exceeded" },
-
-	[BLK_STS_INVAL]		= { -EINVAL,	"invalid" },
-};
+#define blk_errors(_)									\
+	_(BLK_STS_OK,			0,		"")				\
+	_(BLK_STS_NOTSUPP,		-EOPNOTSUPP,	"operation not supported")	\
+	_(BLK_STS_TIMEOUT,		-ETIMEDOUT,	"timeout")			\
+	_(BLK_STS_NOSPC,		-ENOSPC,	"critical space allocation")	\
+	_(BLK_STS_TRANSPORT,		-ENOLINK,	"recoverable transport")	\
+	_(BLK_STS_TARGET,		-EREMOTEIO,	"critical target")		\
+	_(BLK_STS_RESV_CONFLICT,	-EBADE,		"reservation conflict")	\
+	_(BLK_STS_MEDIUM,		-ENODATA,	"critical medium")		\
+	_(BLK_STS_PROTECTION,		-EILSEQ,	"protection")			\
+	_(BLK_STS_RESOURCE,		-ENOMEM,	"kernel resource")		\
+	_(BLK_STS_DEV_RESOURCE,		-EBUSY,		"device resource")		\
+	_(BLK_STS_AGAIN,		-EAGAIN,	"nonblocking retry")		\
+	_(BLK_STS_OFFLINE,		-ENODEV,	"device offline")		\
+											\
+	/* device mapper special case, should not leak out: */				\
+	_(BLK_STS_DM_REQUEUE,		-EREMCHG,	"dm internal retry")		\
+											\
+	/* zone device specific errors */						\
+	_(BLK_STS_ZONE_OPEN_RESOURCE,	-ETOOMANYREFS,	"open zones exceeded")		\
+	_(BLK_STS_ZONE_ACTIVE_RESOURCE,	-EOVERFLOW,	"active zones exceeded" )	\
+											\
+	/* Command duration limit device-side timeout */				\
+	_(BLK_STS_DURATION_LIMIT,	-ETIME,		"duration limit exceeded")	\
+											\
+	_(BLK_STS_INVAL,		-EINVAL,	"invalid")
 
 blk_status_t errno_to_blk_status(int errno)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(blk_errors); i++) {
-		if (blk_errors[i].errno == errno)
-			return (__force blk_status_t)i;
+	switch(errno) {
+#define _(_status, _errno, _name)		\
+	case _errno:				\
+		return _status;
+	blk_errors(_)
+#undef _
+	default:
+		return BLK_STS_IOERR;
 	}
-
-	return BLK_STS_IOERR;
 }
 EXPORT_SYMBOL_GPL(errno_to_blk_status);
 
 int blk_status_to_errno(blk_status_t status)
 {
-	int idx = (__force int)status;
-
-	if (WARN_ON_ONCE(idx >= ARRAY_SIZE(blk_errors)))
+	switch(status) {
+#define _(_status, _errno, _name)		\
+	case _status:				\
+		return _errno;
+	blk_errors(_)
+#undef _
+	default:
 		return -EIO;
-	return blk_errors[idx].errno;
+	}
 }
 EXPORT_SYMBOL_GPL(blk_status_to_errno);
 
 const char *blk_status_to_str(blk_status_t status)
 {
-	int idx = (__force int)status;
-
-	if (WARN_ON_ONCE(idx >= ARRAY_SIZE(blk_errors)))
+	switch(status) {
+#define _(_status, _errno, _name)		\
+	case _status:				\
+		return _name;
+	blk_errors(_)
+#undef _
+	default:
 		return "I/O";
-	return blk_errors[idx].name;
+	}
 }
 EXPORT_SYMBOL_GPL(blk_status_to_str);
 
+#undef blk_errors
+
 /**
  * blk_sync_queue - cancel any pending callbacks on a queue
  * @q: the queue
-- 
2.52.0


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

* [RFC v2 10/17] block: optimize blk_status <=> errno conversion
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (8 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 09/17] block: turn blk_errors array into a macro Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 11/17] bio: bio_set_status from non-zero errno Andreas Gruenbacher
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Handle the errno_to_blk_status(0) case inline so that the compiler can
optimize when it knows that errno is not 0.  Likewise for
blk_status_to_errno(BLK_STATUS_OK).

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 block/blk-core.c       | 11 ++++++-----
 include/linux/blkdev.h | 18 ++++++++++++++++--
 2 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/block/blk-core.c b/block/blk-core.c
index 9b3100d171b7..381bdf66045b 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -133,7 +133,6 @@ inline const char *blk_op_str(enum req_op op)
 EXPORT_SYMBOL_GPL(blk_op_str);
 
 #define blk_errors(_)									\
-	_(BLK_STS_OK,			0,		"")				\
 	_(BLK_STS_NOTSUPP,		-EOPNOTSUPP,	"operation not supported")	\
 	_(BLK_STS_TIMEOUT,		-ETIMEDOUT,	"timeout")			\
 	_(BLK_STS_NOSPC,		-ENOSPC,	"critical space allocation")	\
@@ -159,7 +158,7 @@ EXPORT_SYMBOL_GPL(blk_op_str);
 											\
 	_(BLK_STS_INVAL,		-EINVAL,	"invalid")
 
-blk_status_t errno_to_blk_status(int errno)
+blk_status_t __errno_to_blk_status(int errno)
 {
 	switch(errno) {
 #define _(_status, _errno, _name)		\
@@ -171,9 +170,9 @@ blk_status_t errno_to_blk_status(int errno)
 		return BLK_STS_IOERR;
 	}
 }
-EXPORT_SYMBOL_GPL(errno_to_blk_status);
+EXPORT_SYMBOL_GPL(__errno_to_blk_status);
 
-int blk_status_to_errno(blk_status_t status)
+int __blk_status_to_errno(blk_status_t status)
 {
 	switch(status) {
 #define _(_status, _errno, _name)		\
@@ -185,11 +184,13 @@ int blk_status_to_errno(blk_status_t status)
 		return -EIO;
 	}
 }
-EXPORT_SYMBOL_GPL(blk_status_to_errno);
+EXPORT_SYMBOL_GPL(__blk_status_to_errno);
 
 const char *blk_status_to_str(blk_status_t status)
 {
 	switch(status) {
+	case BLK_STS_OK:
+		return "";
 #define _(_status, _errno, _name)		\
 	case _status:				\
 		return _name;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 70b671a9a7f7..a2451fbd53a8 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1011,8 +1011,22 @@ extern void blk_sync_queue(struct request_queue *q);
 /* Helper to convert REQ_OP_XXX to its string format XXX */
 extern const char *blk_op_str(enum req_op op);
 
-int blk_status_to_errno(blk_status_t status);
-blk_status_t errno_to_blk_status(int errno);
+int __blk_status_to_errno(blk_status_t status);
+static inline int blk_status_to_errno(blk_status_t status)
+{
+	if (status == BLK_STS_OK)
+		return 0;
+	return __blk_status_to_errno(status);
+}
+
+blk_status_t __errno_to_blk_status(int errno);
+static inline blk_status_t errno_to_blk_status(int errno)
+{
+	if (errno == 0)
+		return BLK_STS_OK;
+	return __errno_to_blk_status(errno);
+}
+
 const char *blk_status_to_str(blk_status_t status);
 
 /* only poll the hardware once, don't continue until a completion was found */
-- 
2.52.0


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

* [RFC v2 11/17] bio: bio_set_status from non-zero errno
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (9 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 10/17] block: optimize blk_status <=> errno conversion Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 12/17] bio: do not check bio->bi_status before assigning to it (part 2) Andreas Gruenbacher
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

In these instances, the compiler knows that errno is not 0 and so it can
optimize 'bio_set_status(bio, errno_to_blk_status(errno))' as
'bio->bi_status = __errno_to_blk_status(errno)'.

Created with Coccinelle using the following semantic patch:

@@
struct bio *bio;
expression errno;
@@
- if (errno)
-       bio->bi_status = errno_to_blk_status(errno);
+ if (errno)
+	bio_set_status(bio, errno_to_blk_status(errno));

@@
struct bio *bio;
expression errno;
@@
- if (unlikely(errno))
-       bio->bi_status = errno_to_blk_status(errno);
+ if (unlikely(errno))
+	bio_set_status(bio, errno_to_blk_status(errno));

@@
expression status;
struct bio *bio;
@@
if (IS_ERR(status)) {
<...
-       bio->bi_status = errno_to_blk_status(PTR_ERR(status));
+       bio_set_status(bio, errno_to_blk_status(PTR_ERR(status)));
...>
}

@@
expression errno;
struct bio *bio;
@@
if (!errno)
	return;
-bio->bi_status = errno_to_blk_status(errno);
+bio_set_status(bio, errno_to_blk_status(errno));

@@
expression errno;
struct bio bio;
@@
if (errno)
-       bio.bi_status = errno_to_blk_status(errno);
+       bio_set_status(&bio, errno_to_blk_status(errno));

@@
expression errno;
struct bio bio;
@@
if (IS_ERR_VALUE(errno))
-       bio.bi_status = errno_to_blk_status(errno);
+       bio_set_status(&bio, errno_to_blk_status(errno));

@@
expression errno;
struct bio *bio;
@@
if (errno) {
<...
-       bio->bi_status = errno_to_blk_status(errno);
+       bio_set_status(bio, errno_to_blk_status(errno));
...>
}

@@
expression errno;
struct iomap_ioend *ioend;
struct bio bio;
@@
if (errno) {
<...
-       ioend->io_bio.bi_status = errno_to_blk_status(errno);
+       bio_set_status(&ioend->io_bio, errno_to_blk_status(errno));
...>
}

@@
expression errno;
struct bio *bio;
@@
if (errno < 0) {
<...
-       bio->bi_status = errno_to_blk_status(errno);
+       bio_set_status(bio, errno_to_blk_status(errno));
...>
}

@@
expression errno;
struct bio *bio;
@@
if (unlikely(errno < 0)) {
<...
-       bio->bi_status = errno_to_blk_status(errno);
+       bio_set_status(bio, errno_to_blk_status(errno));
...>
}

@@
expression errno;
struct bio *bio;
@@
if (unlikely(errno != 0)) {
<...
-       bio->bi_status = errno_to_blk_status(errno);
+       bio_set_status(bio, errno_to_blk_status(errno));
...>
}

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 block/blk-merge.c             | 4 ++--
 drivers/block/drbd/drbd_req.c | 2 +-
 drivers/md/dm-ebs-target.c    | 2 +-
 drivers/md/dm-integrity.c     | 6 +++---
 drivers/md/raid10.c           | 6 ++++--
 drivers/nvdimm/btt.c          | 2 +-
 drivers/nvdimm/pmem.c         | 2 +-
 fs/btrfs/bio.c                | 2 +-
 fs/btrfs/direct-io.c          | 2 +-
 fs/btrfs/raid56.c             | 4 ++--
 fs/crypto/bio.c               | 2 +-
 fs/erofs/fscache.c            | 8 +++++---
 fs/iomap/ioend.c              | 2 +-
 fs/xfs/xfs_aops.c             | 2 +-
 14 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/block/blk-merge.c b/block/blk-merge.c
index 37864c5d287e..27ea5ffb8f77 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -122,7 +122,7 @@ struct bio *bio_submit_split_bioset(struct bio *bio, unsigned int split_sectors,
 	struct bio *split = bio_split(bio, split_sectors, GFP_NOIO, bs);
 
 	if (IS_ERR(split)) {
-		bio->bi_status = errno_to_blk_status(PTR_ERR(split));
+		bio_set_status(bio, errno_to_blk_status(PTR_ERR(split)));
 		bio_endio(bio);
 		return NULL;
 	}
@@ -143,7 +143,7 @@ EXPORT_SYMBOL_GPL(bio_submit_split_bioset);
 static struct bio *bio_submit_split(struct bio *bio, int split_sectors)
 {
 	if (unlikely(split_sectors < 0)) {
-		bio->bi_status = errno_to_blk_status(split_sectors);
+		bio_set_status(bio, errno_to_blk_status(split_sectors));
 		bio_endio(bio);
 		return NULL;
 	}
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index baa08d56494d..983b2ff5eb6b 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -177,7 +177,7 @@ void complete_master_bio(struct drbd_device *device,
 		struct bio_and_error *m)
 {
 	if (unlikely(m->error))
-		m->bio->bi_status = errno_to_blk_status(m->error);
+		bio_set_status(m->bio, errno_to_blk_status(m->error));
 	bio_endio(m->bio);
 	dec_ap_bio(device);
 }
diff --git a/drivers/md/dm-ebs-target.c b/drivers/md/dm-ebs-target.c
index 6abb31ca9662..2c97c36df23a 100644
--- a/drivers/md/dm-ebs-target.c
+++ b/drivers/md/dm-ebs-target.c
@@ -217,7 +217,7 @@ static void __ebs_process_bios(struct work_struct *ws)
 		}
 
 		if (r < 0)
-			bio->bi_status = errno_to_blk_status(r);
+			bio_set_status(bio, errno_to_blk_status(r));
 	}
 
 	/*
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index 6d04b067060d..c5c7c167b45d 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -2593,7 +2593,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
 
 	bip = bio_integrity_alloc(bio, GFP_NOIO, 1);
 	if (IS_ERR(bip)) {
-		bio->bi_status = errno_to_blk_status(PTR_ERR(bip));
+		bio_set_status(bio, errno_to_blk_status(PTR_ERR(bip)));
 		bio_endio(bio);
 		return DM_MAPIO_SUBMITTED;
 	}
@@ -2661,7 +2661,7 @@ static void dm_integrity_inline_recheck(struct work_struct *w)
 		bip = bio_integrity_alloc(outgoing_bio, GFP_NOIO, 1);
 		if (IS_ERR(bip)) {
 			bio_put(outgoing_bio);
-			bio->bi_status = errno_to_blk_status(PTR_ERR(bip));
+			bio_set_status(bio, errno_to_blk_status(PTR_ERR(bip)));
 			bio_endio(bio);
 			return;
 		}
@@ -2679,7 +2679,7 @@ static void dm_integrity_inline_recheck(struct work_struct *w)
 		r = submit_bio_wait(outgoing_bio);
 		if (unlikely(r != 0)) {
 			bio_put(outgoing_bio);
-			bio->bi_status = errno_to_blk_status(r);
+			bio_set_status(bio, errno_to_blk_status(r));
 			bio_endio(bio);
 			return;
 		}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 59b900672a7a..3e15e190f103 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1665,7 +1665,8 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
 		split_size = stripe_size - remainder;
 		split = bio_split(bio, split_size, GFP_NOIO, &conf->bio_split);
 		if (IS_ERR(split)) {
-			bio->bi_status = errno_to_blk_status(PTR_ERR(split));
+			bio_set_status(bio,
+				       errno_to_blk_status(PTR_ERR(split)));
 			bio_endio(bio);
 			return 0;
 		}
@@ -1682,7 +1683,8 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
 		split_size = bio_sectors(bio) - remainder;
 		split = bio_split(bio, split_size, GFP_NOIO, &conf->bio_split);
 		if (IS_ERR(split)) {
-			bio->bi_status = errno_to_blk_status(PTR_ERR(split));
+			bio_set_status(bio,
+				       errno_to_blk_status(PTR_ERR(split)));
 			bio_endio(bio);
 			return 0;
 		}
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 3da30dcbf26c..3b9a7d7b9694 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -1468,7 +1468,7 @@ static void btt_submit_bio(struct bio *bio)
 					(op_is_write(bio_op(bio))) ? "WRITE" :
 					"READ",
 					(unsigned long long) iter.bi_sector, len);
-			bio->bi_status = errno_to_blk_status(err);
+			bio_set_status(bio, errno_to_blk_status(err));
 			break;
 		}
 	}
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 4fdcbe56a3bc..6b64a217d180 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -233,7 +233,7 @@ static void pmem_submit_bio(struct bio *bio)
 		ret = nvdimm_flush(nd_region, bio);
 
 	if (ret)
-		bio->bi_status = errno_to_blk_status(ret);
+		bio_set_status(bio, errno_to_blk_status(ret));
 
 	bio_endio(bio);
 }
diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index 519635e2833a..1947f1cb7d08 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -540,7 +540,7 @@ static void run_one_async_start(struct btrfs_work *work)
 
 	ret = btrfs_bio_csum(async->bbio);
 	if (ret)
-		async->bbio->bio.bi_status = errno_to_blk_status(ret);
+		bio_set_status(&async->bbio->bio, errno_to_blk_status(ret));
 }
 
 /*
diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c
index 802d4dbe5b38..04facbbe6994 100644
--- a/fs/btrfs/direct-io.c
+++ b/fs/btrfs/direct-io.c
@@ -738,7 +738,7 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio,
 			btrfs_finish_ordered_extent(dio_data->ordered, NULL,
 						    file_offset, dip->bytes,
 						    !ret);
-			bio->bi_status = errno_to_blk_status(ret);
+			bio_set_status(bio, errno_to_blk_status(ret));
 			iomap_dio_bio_end_io(bio);
 			return;
 		}
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 0135dceb7baa..b60ab0bb08ad 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1750,7 +1750,7 @@ void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc)
 
 	rbio = alloc_rbio(fs_info, bioc);
 	if (IS_ERR(rbio)) {
-		bio->bi_status = errno_to_blk_status(PTR_ERR(rbio));
+		bio_set_status(bio, errno_to_blk_status(PTR_ERR(rbio)));
 		bio_endio(bio);
 		return;
 	}
@@ -2148,7 +2148,7 @@ void raid56_parity_recover(struct bio *bio, struct btrfs_io_context *bioc,
 
 	rbio = alloc_rbio(fs_info, bioc);
 	if (IS_ERR(rbio)) {
-		bio->bi_status = errno_to_blk_status(PTR_ERR(rbio));
+		bio_set_status(bio, errno_to_blk_status(PTR_ERR(rbio)));
 		bio_endio(bio);
 		return;
 	}
diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c
index 5f5599020e94..21a3a1c170a2 100644
--- a/fs/crypto/bio.c
+++ b/fs/crypto/bio.c
@@ -39,7 +39,7 @@ bool fscrypt_decrypt_bio(struct bio *bio)
 							   fi.offset);
 
 		if (err) {
-			bio->bi_status = errno_to_blk_status(err);
+			bio_set_status(bio, errno_to_blk_status(err));
 			return false;
 		}
 	}
diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c
index 362acf828279..9d84bf24e501 100644
--- a/fs/erofs/fscache.c
+++ b/fs/erofs/fscache.c
@@ -183,8 +183,10 @@ static void erofs_fscache_bio_endio(void *priv, ssize_t transferred_or_error)
 {
 	struct erofs_fscache_bio *io = priv;
 
-	if (IS_ERR_VALUE(transferred_or_error))
-		io->bio.bi_status = errno_to_blk_status(transferred_or_error);
+	if (IS_ERR_VALUE(transferred_or_error)) {
+		bio_set_status(&io->bio,
+			       errno_to_blk_status(transferred_or_error));
+	}
 	io->bio.bi_end_io(&io->bio);
 	BUILD_BUG_ON(offsetof(struct erofs_fscache_bio, io) != 0);
 	erofs_fscache_io_put(&io->io);
@@ -215,7 +217,7 @@ void erofs_fscache_submit_bio(struct bio *bio)
 	erofs_fscache_io_put(&io->io);
 	if (!ret)
 		return;
-	bio->bi_status = errno_to_blk_status(ret);
+	bio_set_status(bio, errno_to_blk_status(ret));
 	bio->bi_end_io(bio);
 }
 
diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
index b49fa75eab26..1ebe6730d013 100644
--- a/fs/iomap/ioend.c
+++ b/fs/iomap/ioend.c
@@ -87,7 +87,7 @@ int iomap_ioend_writeback_submit(struct iomap_writepage_ctx *wpc, int error)
 		error = -EIO;
 
 	if (error) {
-		ioend->io_bio.bi_status = errno_to_blk_status(error);
+		bio_set_status(&ioend->io_bio, errno_to_blk_status(error));
 		bio_endio(&ioend->io_bio);
 		return error;
 	}
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index a26f79815533..bd2b828164cb 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -653,7 +653,7 @@ xfs_zoned_writeback_submit(
 
 	ioend->io_bio.bi_end_io = xfs_end_bio;
 	if (error) {
-		ioend->io_bio.bi_status = errno_to_blk_status(error);
+		bio_set_status(&ioend->io_bio, errno_to_blk_status(error));
 		bio_endio(&ioend->io_bio);
 		return error;
 	}
-- 
2.52.0


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

* [RFC v2 12/17] bio: do not check bio->bi_status before assigning to it (part 2)
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (10 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 11/17] bio: bio_set_status from non-zero errno Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 13/17] xfs: use bio_set_status in xfs_zone_alloc_and_submit Andreas Gruenbacher
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Don't check bio->bi_status before assigning to it.  These are the cases
that involve an errno instead of a block status.

See commit "do not check bio->bi_status before assigning to it" for a
rationale.

Created with Coccinelle using the following semantic patch and option
'--disable-iso unlikely':

@@
expression errno;
struct bio *bio;
@@
-if (unlikely(errno) && !bio->bi_status)
-	bio->bi_status = errno_to_blk_status(errno);
+if (unlikely(errno))
+	bio_set_status(bio, errno_to_blk_status(errno));

@@
expression errno;
struct bio *bio;
@@
-if (errno && !bio->bi_status)
-	bio->bi_status = errno_to_blk_status(errno);
+bio_set_status(bio, errno_to_blk_status(errno));

@@
expression errno;
struct bio bio;
@@
-if (errno < 0 && !bio.bi_status)
-	bio.bi_status = errno_to_blk_status(errno);
+if (errno < 0)
+	bio_set_status(&bio, errno_to_blk_status(errno));

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 drivers/md/dm-integrity.c | 4 ++--
 drivers/md/dm-thin.c      | 3 +--
 fs/erofs/fileio.c         | 4 ++--
 3 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index c5c7c167b45d..4a6e27c0d510 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -1580,8 +1580,8 @@ static void do_endio(struct dm_integrity_c *ic, struct bio *bio)
 	int r;
 
 	r = dm_integrity_failed(ic);
-	if (unlikely(r) && !bio->bi_status)
-		bio->bi_status = errno_to_blk_status(r);
+	if (unlikely(r))
+		bio_set_status(bio, errno_to_blk_status(r));
 	if (unlikely(ic->synchronous_mode) && bio_op(bio) == REQ_OP_WRITE) {
 		unsigned long flags;
 
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 00ede45a3d27..42261bbe4771 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -422,8 +422,7 @@ static void end_discard(struct discard_op *op, int r)
 	 * Even if r is set, there could be sub discards in flight that we
 	 * need to wait for.
 	 */
-	if (r && !op->parent_bio->bi_status)
-		op->parent_bio->bi_status = errno_to_blk_status(r);
+	bio_set_status(op->parent_bio, errno_to_blk_status(r));
 	bio_endio(op->parent_bio);
 }
 
diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
index b7b3432a9882..7ed32cb9e670 100644
--- a/fs/erofs/fileio.c
+++ b/fs/erofs/fileio.c
@@ -32,8 +32,8 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
 		ret = 0;
 	}
 	if (rq->bio.bi_end_io) {
-		if (ret < 0 && !rq->bio.bi_status)
-			rq->bio.bi_status = errno_to_blk_status(ret);
+		if (ret < 0)
+			bio_set_status(&rq->bio, errno_to_blk_status(ret));
 		rq->bio.bi_end_io(&rq->bio);
 	} else {
 		bio_for_each_folio_all(fi, &rq->bio) {
-- 
2.52.0


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

* [RFC v2 13/17] xfs: use bio_set_status in xfs_zone_alloc_and_submit
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (11 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 12/17] bio: do not check bio->bi_status before assigning to it (part 2) Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 14/17] bio: switch to bio_set_status in submit_bio_noacct Andreas Gruenbacher
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

At label out_split_error in xfs_zone_alloc_and_submit(), IS_ERR(split)
is true, so bio_set_status() compiles down to a simple assignment.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/xfs/xfs_zone_alloc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c
index bd6f3ef095cb..2b069cffac00 100644
--- a/fs/xfs/xfs_zone_alloc.c
+++ b/fs/xfs/xfs_zone_alloc.c
@@ -896,7 +896,7 @@ xfs_zone_alloc_and_submit(
 	return;
 
 out_split_error:
-	ioend->io_bio.bi_status = errno_to_blk_status(PTR_ERR(split));
+	bio_set_status(&ioend->io_bio, errno_to_blk_status(PTR_ERR(split)));
 	bio_endio(&ioend->io_bio);
 	return;
 
-- 
2.52.0


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

* [RFC v2 14/17] bio: switch to bio_set_status in submit_bio_noacct
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (12 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 13/17] xfs: use bio_set_status in xfs_zone_alloc_and_submit Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 15/17] bio: never set bi_status to BLK_STS_OK during completion Andreas Gruenbacher
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

In submit_bio_noacct(), call bio_endio() and return directly when
successful.  That way, bio_set_status(bio, status) will only be called
for actual errors and the compiler can optimize out the 'status !=
BLK_STS_OK' check inside bio_set_status().

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 block/blk-core.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/block/blk-core.c b/block/blk-core.c
index 381bdf66045b..acf0a82a90ce 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -821,8 +821,8 @@ void submit_bio_noacct(struct bio *bio)
 		if (!bdev_write_cache(bdev)) {
 			bio->bi_opf &= ~(REQ_PREFLUSH | REQ_FUA);
 			if (!bio_sectors(bio)) {
-				status = BLK_STS_OK;
-				goto end_io;
+				bio_endio(bio);
+				return;
 			}
 		}
 	}
@@ -887,7 +887,7 @@ void submit_bio_noacct(struct bio *bio)
 not_supported:
 	status = BLK_STS_NOTSUPP;
 end_io:
-	bio->bi_status = status;
+	bio_set_status(bio, status);
 	bio_endio(bio);
 }
 EXPORT_SYMBOL(submit_bio_noacct);
-- 
2.52.0


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

* [RFC v2 15/17] bio: never set bi_status to BLK_STS_OK during completion
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (13 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 14/17] bio: switch to bio_set_status in submit_bio_noacct Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 16/17] bio: never set bi_status to BLK_STS_OK during completion (part 2) Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 17/17] bio: add bio_endio_status Andreas Gruenbacher
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

In a few places in the code, we don't know if a bio has failed or
completed successfully, so use bio_set_status() instead of setting
bi_status directly.  That way, bi_status will never be set to
BI_STS_OK (0) during completion.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 block/blk-crypto-fallback.c   | 2 +-
 drivers/block/ps3vram.c       | 2 +-
 drivers/md/dm-flakey.c        | 2 +-
 drivers/md/dm-verity-target.c | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c
index 8a2631b1e7e1..7014f646e236 100644
--- a/block/blk-crypto-fallback.c
+++ b/block/blk-crypto-fallback.c
@@ -150,7 +150,7 @@ static void blk_crypto_fallback_encrypt_endio(struct bio *enc_bio)
 		mempool_free(enc_bio->bi_io_vec[i].bv_page,
 			     blk_crypto_bounce_page_pool);
 
-	src_bio->bi_status = enc_bio->bi_status;
+	bio_set_status(src_bio, enc_bio->bi_status);
 
 	bio_uninit(enc_bio);
 	kfree(enc_bio);
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index bdcf083b45e2..06844674c998 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -573,7 +573,7 @@ static struct bio *ps3vram_do_bio(struct ps3_system_bus_device *dev,
 	next = bio_list_peek(&priv->list);
 	spin_unlock_irq(&priv->lock);
 
-	bio->bi_status = error;
+	bio_set_status(bio, error);
 	bio_endio(bio);
 	return next;
 }
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index 08925aca838c..8dde47beb387 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -420,7 +420,7 @@ static void clone_free(struct bio *clone)
 static void clone_endio(struct bio *clone)
 {
 	struct bio *bio = clone->bi_private;
-	bio->bi_status = clone->bi_status;
+	bio_set_status(bio, clone->bi_status);
 	clone_free(clone);
 	bio_endio(bio);
 }
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 66a00a8ccb39..4793bcf546ad 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -544,7 +544,7 @@ static void verity_finish_io(struct dm_verity_io *io, blk_status_t status)
 	struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
 
 	bio->bi_end_io = io->orig_bi_end_io;
-	bio->bi_status = status;
+	bio_set_status(bio, status);
 
 	if (!static_branch_unlikely(&use_bh_wq_enabled) || !io->in_bh)
 		verity_fec_finish_io(io);
-- 
2.52.0


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

* [RFC v2 16/17] bio: never set bi_status to BLK_STS_OK during completion (part 2)
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (14 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 15/17] bio: never set bi_status to BLK_STS_OK during completion Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  2025-12-21  2:52 ` [RFC v2 17/17] bio: add bio_endio_status Andreas Gruenbacher
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

In these two places, it seems that errno can be 0.  This would assign
BLK_STS_OK (0) to bio->bi_status, which is not allowed.  Fix that by
using bio_set_status() instead.

Created with Coccinelle using the following semantic patch.

(Coccinelle occasionally likes to add unnecessary curly braces
in if statements; those were removed by hand.)

@@
struct bio *bio;
expression errno;
@@
- bio->bi_status = errno_to_blk_status(errno);
+ bio_set_status(bio, errno_to_blk_status(errno));

@@
struct bio bio;
expression errno;
@@
- bio.bi_status = errno_to_blk_status(errno);
+ bio_set_status(&bio, errno_to_blk_status(errno));

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 drivers/md/dm-pcache/dm_pcache.c | 2 +-
 drivers/md/dm-vdo/data-vio.c     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/md/dm-pcache/dm_pcache.c b/drivers/md/dm-pcache/dm_pcache.c
index e5f5936fa6f0..086ae9b06bfb 100644
--- a/drivers/md/dm-pcache/dm_pcache.c
+++ b/drivers/md/dm-pcache/dm_pcache.c
@@ -74,7 +74,7 @@ static void end_req(struct kref *ref)
 		pcache_req_get(pcache_req);
 		defer_req(pcache_req);
 	} else {
-		bio->bi_status = errno_to_blk_status(ret);
+		bio_set_status(bio, errno_to_blk_status(ret));
 		bio_endio(bio);
 
 		if (atomic_dec_and_test(&pcache->inflight_reqs))
diff --git a/drivers/md/dm-vdo/data-vio.c b/drivers/md/dm-vdo/data-vio.c
index 262e11581f2d..11becc4138c4 100644
--- a/drivers/md/dm-vdo/data-vio.c
+++ b/drivers/md/dm-vdo/data-vio.c
@@ -287,7 +287,7 @@ static void acknowledge_data_vio(struct data_vio *data_vio)
 	if (data_vio->is_partial)
 		vdo_count_bios(&vdo->stats.bios_acknowledged_partial, bio);
 
-	bio->bi_status = errno_to_blk_status(error);
+	bio_set_status(bio, errno_to_blk_status(error));
 	bio_endio(bio);
 }
 
-- 
2.52.0


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

* [RFC v2 17/17] bio: add bio_endio_status
  2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
                   ` (15 preceding siblings ...)
  2025-12-21  2:52 ` [RFC v2 16/17] bio: never set bi_status to BLK_STS_OK during completion (part 2) Andreas Gruenbacher
@ 2025-12-21  2:52 ` Andreas Gruenbacher
  16 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21  2:52 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba,
	Satya Tangirala
  Cc: Andreas Gruenbacher, linux-block, linux-btrfs, linux-raid,
	dm-devel, linux-kernel

Add a bio_endio_status() helper as a shortcut for calling
bio_set_status() and bio_endio() in sequence. Use the new helper
throughout the code.

Created with Coccinelle using the following semantic patch:

@@
expression bio, status;
@@
- bio_set_status(bio, status);
- bio_endio(bio);
+ bio_endio_status(bio, status);

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 block/bio-integrity-auto.c       |  3 +--
 block/blk-core.c                 |  6 ++----
 block/blk-merge.c                |  6 ++----
 block/blk-mq.c                   |  6 ++----
 drivers/block/drbd/drbd_req.c    |  3 +--
 drivers/block/ps3vram.c          |  3 +--
 drivers/md/dm-cache-target.c     |  3 +--
 drivers/md/dm-integrity.c        | 25 +++++++++----------------
 drivers/md/dm-mpath.c            |  3 +--
 drivers/md/dm-pcache/dm_pcache.c |  3 +--
 drivers/md/dm-raid1.c            |  3 +--
 drivers/md/dm-thin.c             |  6 ++----
 drivers/md/dm-vdo/data-vio.c     |  3 +--
 drivers/md/dm.c                  |  3 +--
 drivers/md/raid1-10.c            |  3 +--
 drivers/md/raid10.c              | 16 ++++++----------
 fs/btrfs/raid56.c                |  6 ++----
 fs/iomap/ioend.c                 |  3 +--
 fs/xfs/xfs_aops.c                |  3 +--
 fs/xfs/xfs_zone_alloc.c          |  3 +--
 include/linux/bio.h              | 12 ++++++++----
 21 files changed, 46 insertions(+), 76 deletions(-)

diff --git a/block/bio-integrity-auto.c b/block/bio-integrity-auto.c
index 736d53a7f699..1185f6e15d86 100644
--- a/block/bio-integrity-auto.c
+++ b/block/bio-integrity-auto.c
@@ -190,8 +190,7 @@ bool bio_integrity_prep(struct bio *bio)
 err_free_buf:
 	kfree(buf);
 err_end_io:
-	bio_set_status(bio, BLK_STS_RESOURCE);
-	bio_endio(bio);
+	bio_endio_status(bio, BLK_STS_RESOURCE);
 	return false;
 }
 EXPORT_SYMBOL(bio_integrity_prep);
diff --git a/block/blk-core.c b/block/blk-core.c
index acf0a82a90ce..119ac3156eeb 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -645,8 +645,7 @@ static void __submit_bio(struct bio *bio)
 	
 		if ((bio->bi_opf & REQ_POLLED) &&
 		    !(disk->queue->limits.features & BLK_FEAT_POLL)) {
-			bio_set_status(bio, BLK_STS_NOTSUPP);
-			bio_endio(bio);
+			bio_endio_status(bio, BLK_STS_NOTSUPP);
 		} else {
 			disk->fops->submit_bio(bio);
 		}
@@ -887,8 +886,7 @@ void submit_bio_noacct(struct bio *bio)
 not_supported:
 	status = BLK_STS_NOTSUPP;
 end_io:
-	bio_set_status(bio, status);
-	bio_endio(bio);
+	bio_endio_status(bio, status);
 }
 EXPORT_SYMBOL(submit_bio_noacct);
 
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 27ea5ffb8f77..5ab948ed4c1a 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -122,8 +122,7 @@ struct bio *bio_submit_split_bioset(struct bio *bio, unsigned int split_sectors,
 	struct bio *split = bio_split(bio, split_sectors, GFP_NOIO, bs);
 
 	if (IS_ERR(split)) {
-		bio_set_status(bio, errno_to_blk_status(PTR_ERR(split)));
-		bio_endio(bio);
+		bio_endio_status(bio, errno_to_blk_status(PTR_ERR(split)));
 		return NULL;
 	}
 
@@ -143,8 +142,7 @@ EXPORT_SYMBOL_GPL(bio_submit_split_bioset);
 static struct bio *bio_submit_split(struct bio *bio, int split_sectors)
 {
 	if (unlikely(split_sectors < 0)) {
-		bio_set_status(bio, errno_to_blk_status(split_sectors));
-		bio_endio(bio);
+		bio_endio_status(bio, errno_to_blk_status(split_sectors));
 		return NULL;
 	}
 
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 8fb35a258b4c..3ad79017ceeb 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -3165,8 +3165,7 @@ void blk_mq_submit_bio(struct bio *bio)
 	}
 
 	if ((bio->bi_opf & REQ_POLLED) && !blk_mq_can_poll(q)) {
-		bio_set_status(bio, BLK_STS_NOTSUPP);
-		bio_endio(bio);
+		bio_endio_status(bio, BLK_STS_NOTSUPP);
 		goto queue_exit;
 	}
 
@@ -3206,8 +3205,7 @@ void blk_mq_submit_bio(struct bio *bio)
 
 	ret = blk_crypto_rq_get_keyslot(rq);
 	if (ret != BLK_STS_OK) {
-		bio_set_status(bio, ret);
-		bio_endio(bio);
+		bio_endio_status(bio, ret);
 		blk_mq_free_request(rq);
 		return;
 	}
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 983b2ff5eb6b..3d3476707638 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -1211,8 +1211,7 @@ drbd_request_prepare(struct drbd_device *device, struct bio *bio)
 		/* only pass the error to the upper layers.
 		 * if user cannot handle io errors, that's not our business. */
 		drbd_err(device, "could not kmalloc() req\n");
-		bio_set_status(bio, BLK_STS_RESOURCE);
-		bio_endio(bio);
+		bio_endio_status(bio, BLK_STS_RESOURCE);
 		return ERR_PTR(-ENOMEM);
 	}
 
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index 06844674c998..8b8bdfa50c97 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -573,8 +573,7 @@ static struct bio *ps3vram_do_bio(struct ps3_system_bus_device *dev,
 	next = bio_list_peek(&priv->list);
 	spin_unlock_irq(&priv->lock);
 
-	bio_set_status(bio, error);
-	bio_endio(bio);
+	bio_endio_status(bio, error);
 	return next;
 }
 
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index d1dbd4ddaadb..da1b1eb29bb8 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -1856,8 +1856,7 @@ static void requeue_deferred_bios(struct cache *cache)
 	bio_list_merge_init(&bios, &cache->deferred_bios);
 
 	while ((bio = bio_list_pop(&bios))) {
-		bio_set_status(bio, BLK_STS_DM_REQUEUE);
-		bio_endio(bio);
+		bio_endio_status(bio, BLK_STS_DM_REQUEUE);
 		cond_resched();
 	}
 }
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index 4a6e27c0d510..c21b0d866627 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -2512,8 +2512,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
 	sector_t recalc_sector;
 
 	if (unlikely(bio_integrity(bio))) {
-		bio_set_status(bio, BLK_STS_NOTSUPP);
-		bio_endio(bio);
+		bio_endio_status(bio, BLK_STS_NOTSUPP);
 		return DM_MAPIO_SUBMITTED;
 	}
 
@@ -2534,8 +2533,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
 			if (dio->payload_len > x_size) {
 				unsigned sectors = ((x_size - extra_size) / ic->tuple_size) << ic->sb->log2_sectors_per_block;
 				if (WARN_ON(!sectors || sectors >= bio_sectors(bio))) {
-					bio_set_status(bio, BLK_STS_NOTSUPP);
-					bio_endio(bio);
+					bio_endio_status(bio, BLK_STS_NOTSUPP);
 					return DM_MAPIO_SUBMITTED;
 				}
 				dm_accept_partial_bio(bio, sectors);
@@ -2593,8 +2591,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
 
 	bip = bio_integrity_alloc(bio, GFP_NOIO, 1);
 	if (IS_ERR(bip)) {
-		bio_set_status(bio, errno_to_blk_status(PTR_ERR(bip)));
-		bio_endio(bio);
+		bio_endio_status(bio, errno_to_blk_status(PTR_ERR(bip)));
 		return DM_MAPIO_SUBMITTED;
 	}
 
@@ -2615,8 +2612,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
 	ret = bio_integrity_add_page(bio, virt_to_page(dio->integrity_payload),
 					dio->payload_len, offset_in_page(dio->integrity_payload));
 	if (unlikely(ret != dio->payload_len)) {
-		bio_set_status(bio, BLK_STS_RESOURCE);
-		bio_endio(bio);
+		bio_endio_status(bio, BLK_STS_RESOURCE);
 		return DM_MAPIO_SUBMITTED;
 	}
 
@@ -2661,16 +2657,15 @@ static void dm_integrity_inline_recheck(struct work_struct *w)
 		bip = bio_integrity_alloc(outgoing_bio, GFP_NOIO, 1);
 		if (IS_ERR(bip)) {
 			bio_put(outgoing_bio);
-			bio_set_status(bio, errno_to_blk_status(PTR_ERR(bip)));
-			bio_endio(bio);
+			bio_endio_status(bio,
+					 errno_to_blk_status(PTR_ERR(bip)));
 			return;
 		}
 
 		r = bio_integrity_add_page(outgoing_bio, virt_to_page(dio->integrity_payload), ic->tuple_size, 0);
 		if (unlikely(r != ic->tuple_size)) {
 			bio_put(outgoing_bio);
-			bio_set_status(bio, BLK_STS_RESOURCE);
-			bio_endio(bio);
+			bio_endio_status(bio, BLK_STS_RESOURCE);
 			return;
 		}
 
@@ -2679,8 +2674,7 @@ static void dm_integrity_inline_recheck(struct work_struct *w)
 		r = submit_bio_wait(outgoing_bio);
 		if (unlikely(r != 0)) {
 			bio_put(outgoing_bio);
-			bio_set_status(bio, errno_to_blk_status(r));
-			bio_endio(bio);
+			bio_endio_status(bio, errno_to_blk_status(r));
 			return;
 		}
 		bio_put(outgoing_bio);
@@ -2693,8 +2687,7 @@ static void dm_integrity_inline_recheck(struct work_struct *w)
 			dm_audit_log_bio(DM_MSG_PREFIX, "integrity-checksum",
 				bio, dio->bio_details.bi_iter.bi_sector, 0);
 
-			bio_set_status(bio, BLK_STS_PROTECTION);
-			bio_endio(bio);
+			bio_endio_status(bio, BLK_STS_PROTECTION);
 			return;
 		}
 
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 761e5e79d4a7..1097ffb05b00 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -722,8 +722,7 @@ static void process_queued_bios(struct work_struct *work)
 			bio_io_error(bio);
 			break;
 		case DM_MAPIO_REQUEUE:
-			bio_set_status(bio, BLK_STS_DM_REQUEUE);
-			bio_endio(bio);
+			bio_endio_status(bio, BLK_STS_DM_REQUEUE);
 			break;
 		case DM_MAPIO_REMAPPED:
 			submit_bio_noacct(bio);
diff --git a/drivers/md/dm-pcache/dm_pcache.c b/drivers/md/dm-pcache/dm_pcache.c
index 086ae9b06bfb..925a25aa8699 100644
--- a/drivers/md/dm-pcache/dm_pcache.c
+++ b/drivers/md/dm-pcache/dm_pcache.c
@@ -74,8 +74,7 @@ static void end_req(struct kref *ref)
 		pcache_req_get(pcache_req);
 		defer_req(pcache_req);
 	} else {
-		bio_set_status(bio, errno_to_blk_status(ret));
-		bio_endio(bio);
+		bio_endio_status(bio, errno_to_blk_status(ret));
 
 		if (atomic_dec_and_test(&pcache->inflight_reqs))
 			wake_up(&pcache->inflight_wq);
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index c54995847db0..1f53b125b333 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -627,8 +627,7 @@ static void write_callback(unsigned long error, void *context)
 	 * degrade the array.
 	 */
 	if (bio_op(bio) == REQ_OP_DISCARD) {
-		bio_set_status(bio, BLK_STS_NOTSUPP);
-		bio_endio(bio);
+		bio_endio_status(bio, BLK_STS_NOTSUPP);
 		return;
 	}
 
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 42261bbe4771..7fe004a330c0 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -422,8 +422,7 @@ static void end_discard(struct discard_op *op, int r)
 	 * Even if r is set, there could be sub discards in flight that we
 	 * need to wait for.
 	 */
-	bio_set_status(op->parent_bio, errno_to_blk_status(r));
-	bio_endio(op->parent_bio);
+	bio_endio_status(op->parent_bio, errno_to_blk_status(r));
 }
 
 /*----------------------------------------------------------------*/
@@ -2730,8 +2729,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
 	thin_hook_bio(tc, bio);
 
 	if (tc->requeue_mode) {
-		bio_set_status(bio, BLK_STS_DM_REQUEUE);
-		bio_endio(bio);
+		bio_endio_status(bio, BLK_STS_DM_REQUEUE);
 		return DM_MAPIO_SUBMITTED;
 	}
 
diff --git a/drivers/md/dm-vdo/data-vio.c b/drivers/md/dm-vdo/data-vio.c
index 11becc4138c4..298957fa938a 100644
--- a/drivers/md/dm-vdo/data-vio.c
+++ b/drivers/md/dm-vdo/data-vio.c
@@ -287,8 +287,7 @@ static void acknowledge_data_vio(struct data_vio *data_vio)
 	if (data_vio->is_partial)
 		vdo_count_bios(&vdo->stats.bios_acknowledged_partial, bio);
 
-	bio_set_status(bio, errno_to_blk_status(error));
-	bio_endio(bio);
+	bio_endio_status(bio, errno_to_blk_status(error));
 }
 
 static void copy_to_bio(struct bio *bio, char *data_ptr)
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index cbc64377fa96..1743042db9f6 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -983,8 +983,7 @@ static void __dm_io_complete(struct dm_io *io, bool first_stage)
 		queue_io(md, bio);
 	} else {
 		/* done with normal IO or empty flush */
-		bio_set_status(bio, io_error);
-		bio_endio(bio);
+		bio_endio_status(bio, io_error);
 	}
 }
 
diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
index 504730aba9df..53903bb91408 100644
--- a/drivers/md/raid1-10.c
+++ b/drivers/md/raid1-10.c
@@ -104,8 +104,7 @@ static void md_bio_reset_resync_pages(struct bio *bio, struct resync_pages *rp,
 		int len = min_t(int, size, PAGE_SIZE);
 
 		if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
-			bio_set_status(bio, BLK_STS_RESOURCE);
-			bio_endio(bio);
+			bio_endio_status(bio, BLK_STS_RESOURCE);
 			return;
 		}
 
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 3e15e190f103..5bbef4954fd5 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1665,9 +1665,8 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
 		split_size = stripe_size - remainder;
 		split = bio_split(bio, split_size, GFP_NOIO, &conf->bio_split);
 		if (IS_ERR(split)) {
-			bio_set_status(bio,
-				       errno_to_blk_status(PTR_ERR(split)));
-			bio_endio(bio);
+			bio_endio_status(bio,
+					 errno_to_blk_status(PTR_ERR(split)));
 			return 0;
 		}
 
@@ -1683,9 +1682,8 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
 		split_size = bio_sectors(bio) - remainder;
 		split = bio_split(bio, split_size, GFP_NOIO, &conf->bio_split);
 		if (IS_ERR(split)) {
-			bio_set_status(bio,
-				       errno_to_blk_status(PTR_ERR(split)));
-			bio_endio(bio);
+			bio_endio_status(bio,
+					 errno_to_blk_status(PTR_ERR(split)));
 			return 0;
 		}
 
@@ -3685,8 +3683,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 			struct resync_pages *rp = get_resync_pages(bio);
 			page = resync_fetch_page(rp, page_idx);
 			if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
-				bio_set_status(bio, BLK_STS_RESOURCE);
-				bio_endio(bio);
+				bio_endio_status(bio, BLK_STS_RESOURCE);
 				goto giveup;
 			}
 		}
@@ -4867,8 +4864,7 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
 			len = PAGE_SIZE;
 		for (bio = blist; bio ; bio = bio->bi_next) {
 			if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
-				bio_set_status(bio, BLK_STS_RESOURCE);
-				bio_endio(bio);
+				bio_endio_status(bio, BLK_STS_RESOURCE);
 				return sectors_done;
 			}
 		}
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index b60ab0bb08ad..5bd9dd132314 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1750,8 +1750,7 @@ void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc)
 
 	rbio = alloc_rbio(fs_info, bioc);
 	if (IS_ERR(rbio)) {
-		bio_set_status(bio, errno_to_blk_status(PTR_ERR(rbio)));
-		bio_endio(bio);
+		bio_endio_status(bio, errno_to_blk_status(PTR_ERR(rbio)));
 		return;
 	}
 	rbio->operation = BTRFS_RBIO_WRITE;
@@ -2148,8 +2147,7 @@ void raid56_parity_recover(struct bio *bio, struct btrfs_io_context *bioc,
 
 	rbio = alloc_rbio(fs_info, bioc);
 	if (IS_ERR(rbio)) {
-		bio_set_status(bio, errno_to_blk_status(PTR_ERR(rbio)));
-		bio_endio(bio);
+		bio_endio_status(bio, errno_to_blk_status(PTR_ERR(rbio)));
 		return;
 	}
 
diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
index 1ebe6730d013..10963b7c0837 100644
--- a/fs/iomap/ioend.c
+++ b/fs/iomap/ioend.c
@@ -87,8 +87,7 @@ int iomap_ioend_writeback_submit(struct iomap_writepage_ctx *wpc, int error)
 		error = -EIO;
 
 	if (error) {
-		bio_set_status(&ioend->io_bio, errno_to_blk_status(error));
-		bio_endio(&ioend->io_bio);
+		bio_endio_status(&ioend->io_bio, errno_to_blk_status(error));
 		return error;
 	}
 
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index bd2b828164cb..c56560715ba4 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -653,8 +653,7 @@ xfs_zoned_writeback_submit(
 
 	ioend->io_bio.bi_end_io = xfs_end_bio;
 	if (error) {
-		bio_set_status(&ioend->io_bio, errno_to_blk_status(error));
-		bio_endio(&ioend->io_bio);
+		bio_endio_status(&ioend->io_bio, errno_to_blk_status(error));
 		return error;
 	}
 	xfs_zone_alloc_and_submit(ioend, &XFS_ZWPC(wpc)->open_zone);
diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c
index 2b069cffac00..e5f91580e6d9 100644
--- a/fs/xfs/xfs_zone_alloc.c
+++ b/fs/xfs/xfs_zone_alloc.c
@@ -896,8 +896,7 @@ xfs_zone_alloc_and_submit(
 	return;
 
 out_split_error:
-	bio_set_status(&ioend->io_bio, errno_to_blk_status(PTR_ERR(split)));
-	bio_endio(&ioend->io_bio);
+	bio_endio_status(&ioend->io_bio, errno_to_blk_status(PTR_ERR(split)));
 	return;
 
 out_error:
diff --git a/include/linux/bio.h b/include/linux/bio.h
index cf0f5aba7bee..94f0c7f58002 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -392,17 +392,21 @@ static inline void bio_set_status(struct bio *bio, blk_status_t status)
 		WRITE_ONCE(bio->bi_status, status);
 }
 
-static inline void bio_io_error(struct bio *bio)
+static inline void bio_endio_status(struct bio *bio, blk_status_t status)
 {
-	bio_set_status(bio, BLK_STS_IOERR);
+	bio_set_status(bio, status);
 	bio_endio(bio);
 }
 
+static inline void bio_io_error(struct bio *bio)
+{
+	bio_endio_status(bio, BLK_STS_IOERR);
+}
+
 static inline void bio_wouldblock_error(struct bio *bio)
 {
 	bio_set_flag(bio, BIO_QUIET);
-	bio_set_status(bio, BLK_STS_AGAIN);
-	bio_endio(bio);
+	bio_endio_status(bio, BLK_STS_AGAIN);
 }
 
 /*
-- 
2.52.0


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

* Re: [RFC v2 01/17] xfs: don't clobber bi_status in xfs_zone_alloc_and_submit
  2025-12-21  2:52 ` [RFC v2 01/17] xfs: don't clobber bi_status in xfs_zone_alloc_and_submit Andreas Gruenbacher
@ 2025-12-21 15:39   ` Andreas Gruenbacher
  0 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2025-12-21 15:39 UTC (permalink / raw)
  To: Christoph Hellwig, Jens Axboe, Chris Mason, David Sterba
  Cc: linux-block, linux-btrfs, linux-raid, dm-devel, linux-kernel

On Sun, Dec 21, 2025 at 3:52 AM Andreas Gruenbacher <agruenba@redhat.com> wrote:
> Function xfs_zone_alloc_and_submit() sets bio->bi_status and then it
> calls bio_io_error(), which overwrites that value again.  Fix that by
> completing the bio separately after setting bio->bi_status.

By the way, this bug makes me wonder if we shouldn't just get rid of
bio_io_error() in favour of bio_endio_status(bio, BLK_STS_IOERR). The
latter would be a lot more obvious.

Andreas

> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
> ---
>  fs/xfs/xfs_zone_alloc.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c
> index ef7a931ebde5..bd6f3ef095cb 100644
> --- a/fs/xfs/xfs_zone_alloc.c
> +++ b/fs/xfs/xfs_zone_alloc.c
> @@ -897,6 +897,9 @@ xfs_zone_alloc_and_submit(
>
>  out_split_error:
>         ioend->io_bio.bi_status = errno_to_blk_status(PTR_ERR(split));
> +       bio_endio(&ioend->io_bio);
> +       return;
> +
>  out_error:
>         bio_io_error(&ioend->io_bio);
>  }
> --
> 2.52.0
>


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

end of thread, other threads:[~2025-12-21 15:40 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-21  2:52 [RFC v2 00/17] bio cleanups Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 01/17] xfs: don't clobber bi_status in xfs_zone_alloc_and_submit Andreas Gruenbacher
2025-12-21 15:39   ` Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 02/17] bio: rename bio_chain arguments Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 03/17] bio: use bio_io_error more often Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 04/17] bio: add bio_set_status Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 05/17] bio: use bio_set_status for BLK_STS_* status codes Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 06/17] bio: do not check bio->bi_status before assigning to it Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 07/17] block: consecutive blk_status_t error codes Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 08/17] block: fix blk_status_to_{errno,str} inconsistency Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 09/17] block: turn blk_errors array into a macro Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 10/17] block: optimize blk_status <=> errno conversion Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 11/17] bio: bio_set_status from non-zero errno Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 12/17] bio: do not check bio->bi_status before assigning to it (part 2) Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 13/17] xfs: use bio_set_status in xfs_zone_alloc_and_submit Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 14/17] bio: switch to bio_set_status in submit_bio_noacct Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 15/17] bio: never set bi_status to BLK_STS_OK during completion Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 16/17] bio: never set bi_status to BLK_STS_OK during completion (part 2) Andreas Gruenbacher
2025-12-21  2:52 ` [RFC v2 17/17] bio: add bio_endio_status Andreas Gruenbacher

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

all inboxes | Powered by JetHome®