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 <[email protected]>
---
 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


Reply via email to