Instead of submitting the bio fragment with the highest LBA first, submit the bio fragment with the lowest LBA first. If plugging is active, append requests at the end of the list with plugged requests instead of at the start. This approach prevents write errors when submitting large bios to host-managed zoned block devices. Cc: Christoph Hellwig <hch@xxxxxx> Cc: Ming Lei <ming.lei@xxxxxxxxxx> Cc: Damien Le Moal <damien.lemoal@xxxxxxxxxxxxxxxxxx> Cc: Johannes Thumshirn <johannes.thumshirn@xxxxxxx> Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx> --- block/blk-merge.c | 9 +++++---- block/blk-mq.c | 7 +++++-- include/linux/blk-mq.h | 6 ++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/block/blk-merge.c b/block/blk-merge.c index 2e07f6bd96be..6031021d7ac0 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -344,8 +344,8 @@ static struct bio *bio_split_rw(struct bio *bio, const struct queue_limits *lim, * @nr_segs: returns the number of segments in the returned bio * * Check if @bio needs splitting based on the queue limits, and if so split off - * a bio fitting the limits from the beginning of @bio and return it. @bio is - * shortened to the remainder and re-submitted. + * a bio fitting the limits from the beginning of @bio and submit it. @bio is + * shortened to the remainder and returned. * * The split bio is allocated from @q->bio_split, which is provided by the * block layer. @@ -380,8 +380,9 @@ struct bio *__bio_split_to_limits(struct bio *bio, blkcg_bio_issue_init(split); bio_chain(split, bio); trace_block_split(split, bio->bi_iter.bi_sector); - submit_bio_noacct(bio); - return split; + submit_bio_noacct(split); + *nr_segs = bio_nr_segments(lim, bio); + return bio; } return bio; } diff --git a/block/blk-mq.c b/block/blk-mq.c index cc32ad0cd548..9b0f9f3fdba0 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1300,7 +1300,7 @@ static inline unsigned short blk_plug_max_rq_count(struct blk_plug *plug) static void blk_add_rq_to_plug(struct blk_plug *plug, struct request *rq) { - struct request *last = rq_list_peek(&plug->mq_list); + struct request *last = rq_list_peek(&plug->mq_list), **last_p; if (!plug->rq_count) { trace_block_plug(rq->q); @@ -1317,7 +1317,10 @@ static void blk_add_rq_to_plug(struct blk_plug *plug, struct request *rq) if (!plug->has_elevator && (rq->rq_flags & RQF_ELV)) plug->has_elevator = true; rq->rq_next = NULL; - rq_list_add(&plug->mq_list, rq); + last_p = &plug->mq_list; + while (*last_p) + last_p = &(*last_p)->rq_next; + rq_list_add_tail(&last_p, rq); plug->rq_count++; } diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index dd5ce1137f04..5e01791967c0 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -228,6 +228,12 @@ static inline unsigned short req_get_ioprio(struct request *req) *(listptr) = rq; \ } while (0) +#define rq_list_add_tail(lastpptr, rq) do { \ + (rq)->rq_next = NULL; \ + **(lastpptr) = rq; \ + *(lastpptr) = &rq->rq_next; \ +} while (0) + #define rq_list_pop(listptr) \ ({ \ struct request *__req = NULL; \