From: colyli@kernel.org
To: linux-raid@vger.kernel.org
Cc: linux-block@vger.kernel.org, yukuai3@huawei.com,
Coly Li <colyli@kernel.org>
Subject: [PATCH 2/2] md: split bio by io_opt size in md_submit_bio()
Date: Sun, 17 Aug 2025 23:26:45 +0800 [thread overview]
Message-ID: <20250817152645.7115-2-colyli@kernel.org> (raw)
In-Reply-To: <20250817152645.7115-1-colyli@kernel.org>
From: Coly Li <colyli@kernel.org>
Currently in md_submit_bio() the incoming request bio is split by
bio_split_to_limits() which makes sure the bio won't exceed
max_hw_sectors of a specific raid level before senting into its
.make_request method.
For raid level 4/5/6 such split method might be problematic and hurt
large read/write perforamnce. Because limits.max_hw_sectors are not
always aligned to limits.io_opt size, the split bio won't be full
stripes covered on all data disks, and will introduce extra read-in I/O.
Even the bio's bi_sector is aligned to limits.io_opt size and large
enough, the resulted split bio is not size-friendly to corresponding
raid456 level.
This patch introduces bio_split_by_io_opt() to solve the above issue,
1, If the incoming bio is not limits.io_opt aligned, split the non-
aligned head part. Then the next one will be aligned.
2, If the imcoming bio is limits.io_opt aligned, and split is necessary,
then try to split a by multiple of limits.io_opt but not exceed
limits.max_hw_sectors.
Then for large bio, the sligned split part will be full-stripes covered
to all data disks, no extra read-in I/Os when rmw_level is 0. And for
rmw_level > 0 condistions, the limits.io_opt aligned bios are welcomed
for performace as well.
This patch only tests on 8 disks raid5 array with 64KiB chunk size.
By this patch, 64KiB chunk size for a 8 disks raid5 array, sequential
write performance increases from 900MiB/s to 1.1GiB/s by fio bs=10M.
If fio bs=488K (exact limits.io_opt size) the peak sequential write
throughput can reach 1.51GiB/s.
Signed-off-by: Coly Li <colyli@kernel.org>
---
drivers/md/md.c | 51 +++++++++++++++++++++++++++++++++++++++++++++-
drivers/md/raid5.c | 6 +++++-
2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index ac85ec73a409..d0d4d05150fe 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -426,6 +426,55 @@ bool md_handle_request(struct mddev *mddev, struct bio *bio)
}
EXPORT_SYMBOL(md_handle_request);
+/**
+ * For raid456 read/write request, if bio LBA isn't aligned tot io_opt,
+ * split the non io_opt aligned header, to make the second part's LBA be
+ * aligned to io_opt. Otherwise still call bio_split_to_limits() to
+ * handle bio split with queue limits.
+ */
+static struct bio *bio_split_by_io_opt(struct bio *bio)
+{
+ sector_t io_opt_sectors, start, offset;
+ struct queue_limits lim;
+ struct mddev *mddev;
+ struct bio *split;
+ int level;
+
+ mddev = bio->bi_bdev->bd_disk->private_data;
+ level = mddev->level;
+
+ /* Only handle read456 read/write requests */
+ if (level == 1 || level == 10 || level == 0 || level == LEVEL_LINEAR ||
+ (bio_op(bio) != REQ_OP_READ && bio_op(bio) != REQ_OP_WRITE))
+ return bio_split_to_limits(bio);
+
+ /* In case raid456 chunk size is too large */
+ lim = mddev->gendisk->queue->limits;
+ io_opt_sectors = lim.io_opt >> SECTOR_SHIFT;
+ if (unlikely(io_opt_sectors > lim.max_hw_sectors))
+ return bio_split_to_limits(bio);
+
+ /* Small request, no need to split */
+ if (bio_sectors(bio) <= io_opt_sectors)
+ return bio;
+
+ /* Only split the non-io-opt aligned header part */
+ start = bio->bi_iter.bi_sector;
+ offset = sector_div(start, io_opt_sectors);
+ if (offset == 0)
+ return bio_split_to_limits(bio);
+
+ split = bio_split(bio, (io_opt_sectors - offset), GFP_NOIO,
+ &bio->bi_bdev->bd_disk->bio_split);
+ if (!split)
+ return bio_split_to_limits(bio);
+
+ split->bi_opf |= REQ_NOMERGE;
+ bio_chain(split, bio);
+ submit_bio_noacct(bio);
+ return split;
+}
+
static void md_submit_bio(struct bio *bio)
{
const int rw = bio_data_dir(bio);
@@ -441,7 +490,7 @@ static void md_submit_bio(struct bio *bio)
return;
}
- bio = bio_split_to_limits(bio);
+ bio = bio_split_by_io_opt(bio);
if (!bio)
return;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 989acd8abd98..985fabeeead5 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -7759,9 +7759,13 @@ static int raid5_set_limits(struct mddev *mddev)
/*
* Requests require having a bitmap for each stripe.
- * Limit the max sectors based on this.
+ * Limit the max sectors based on this. And being
+ * aligned to lim.io_opt for better I/O performance.
*/
lim.max_hw_sectors = RAID5_MAX_REQ_STRIPES << RAID5_STRIPE_SHIFT(conf);
+ if (lim.max_hw_sectors > lim.io_opt >> SECTOR_SHIFT)
+ lim.max_hw_sectors = rounddown(lim.max_hw_sectors,
+ lim.io_opt >> SECTOR_SHIFT);
/* No restrictions on the number of segments in the request */
lim.max_segments = USHRT_MAX;
--
2.47.2
next prev parent reply other threads:[~2025-08-17 15:26 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-17 15:26 [PATCH 1/2] block: ignore underlying non-stack devices io_opt colyli
2025-08-17 15:26 ` colyli [this message]
2025-08-18 1:38 ` [PATCH 2/2] md: split bio by io_opt size in md_submit_bio() Yu Kuai
2025-08-18 8:01 ` Christoph Hellwig
2025-08-18 9:51 ` John Garry
[not found] ` <6DA25F37-26B3-4912-90A3-346CFD9A6EEA@coly.li>
2025-08-18 12:20 ` John Garry
2025-08-18 15:36 ` Coly Li
2025-08-17 18:37 ` [PATCH 1/2] block: ignore underlying non-stack devices io_opt Paul Menzel
2025-08-18 1:14 ` Yu Kuai
2025-08-18 2:51 ` Damien Le Moal
2025-08-18 2:57 ` Yu Kuai
2025-08-18 3:18 ` Damien Le Moal
2025-08-18 3:40 ` Yu Kuai
2025-08-18 5:56 ` Christoph Hellwig
2025-08-18 6:14 ` Yu Kuai
2025-08-18 6:18 ` Christoph Hellwig
2025-08-18 6:31 ` Yu Kuai
2025-08-18 8:00 ` Christoph Hellwig
2025-08-18 8:10 ` Yu Kuai
2025-08-18 8:14 ` Christoph Hellwig
2025-08-18 8:57 ` Yu Kuai
2025-08-18 9:08 ` Christoph Hellwig
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250817152645.7115-2-colyli@kernel.org \
--to=colyli@kernel.org \
--cc=linux-block@vger.kernel.org \
--cc=linux-raid@vger.kernel.org \
--cc=yukuai3@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.