[5.10,13/25] block: only update parent bi_status when bio fail
diff mbox series

Message ID 20210415144413.582250269@linuxfoundation.org
State New, archived
Headers show
  • Untitled series #495371
Related show

Commit Message

Greg Kroah-Hartman April 15, 2021, 2:48 p.m. UTC
From: Yufen Yu <yuyufen@huawei.com>

[ Upstream commit 3edf5346e4f2ce2fa0c94651a90a8dda169565ee ]

For multiple split bios, if one of the bio is fail, the whole
should return error to application. But we found there is a race
between bio_integrity_verify_fn and bio complete, which return
io success to application after one of the bio fail. The race as

split bio(READ)          kworker

blk_update_request //split error=0
      queue_work(kintegrityd_wq, &bip->bip_work);

                         bio_endio //split bio
                             if (!parent->bi_status)

                               <interrupt entry>
                                 blk_update_request //parent error=7
                                    bio->bi_status = 7 //parent bio
                               <interrupt exit>

                               parent->bi_status = 0
                        parent->bi_end_io() // return bi_status=0

The bio has been split as two: split and parent. When split
bio completed, it depends on kworker to do endio, while
bio_integrity_verify_fn have been interrupted by parent bio
complete irq handler. Then, parent bio->bi_status which have
been set in irq handler will overwrite by kworker.

In fact, even without the above race, we also need to conside
the concurrency beteen mulitple split bio complete and update
the same parent bi_status. Normally, multiple split bios will
be issued to the same hctx and complete from the same irq
vector. But if we have updated queue map between multiple split
bios, these bios may complete on different hw queue and different
irq vector. Then the concurrency update parent bi_status may
cause the final status error.

Suggested-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Yufen Yu <yuyufen@huawei.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20210331115359.1125679-1-yuyufen@huawei.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
 block/bio.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff mbox series

diff --git a/block/bio.c b/block/bio.c
index fa01bef35bb1..9c931df2d986 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -313,7 +313,7 @@  static struct bio *__bio_chain_endio(struct bio *bio)
 	struct bio *parent = bio->bi_private;
-	if (!parent->bi_status)
+	if (bio->bi_status && !parent->bi_status)
 		parent->bi_status = bio->bi_status;
 	return parent;