Linux RAID subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Chen Cheng" <chencheng@fnnas.com>
Cc: linux-raid@vger.kernel.org, yukuai@fygo.io
Subject: Re: [RFC PATCH 2/5] md/raid1: do not move nonrot reads onto a rot disk
Date: Tue, 18 Aug 2026 07:26:22 +0000	[thread overview]
Message-ID: <20260818072623.1DDB71F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260818070646.1029149-3-chencheng@fnnas.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Sequential and distance-based heuristics are broken for rotational disks when non-rotational disks fail because `has_nonrot` does not check readability.
- [High] Sequential reads on rotational disks will thrash in mixed arrays because idle sequential disks fail the early-return check and are artificially penalized.
--

--- Patch [2]: [RFC PATCH 2/5] md/raid1: do not move nonrot reads onto a rot disk ---
commit bee044f7822d405084d8b841ba855a8f7a81b6a4
Author: Chen Cheng <chencheng@fnnas.com>

md/raid1: do not move nonrot reads onto a rot disk

This patch updates the read balancing logic in RAID1 to prefer non-rotational
disks over rotational ones in mixed arrays. It modifies choose_best_rdev() to
ensure that non-rotational disks are favored during sequential I/O and
round-robin selection, aiming to prevent rotational disks from slowing down
faster NVMe or SSD drives.

> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 319b24bcab5b7..36520e48826f2 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c

[ ... ]

> @@ -829,10 +849,12 @@ static int choose_best_rdev(struct r1conf *conf, struct r1bio *r1_bio)
>  		pending = atomic_read(&rdev->nr_pending);
>  		dist = abs(r1_bio->sector -
>  			   READ_ONCE(conf->mirrors[disk].head_position));
> +		nonrot = test_bit(Nonrot, &rdev->flags);
>  
>  		/* Don't change to another disk for sequential reads */
>  		if (is_sequential(conf, disk, r1_bio)) {
> -			if (!should_choose_next(conf, disk) && !pending)
> +			if (!should_choose_next(conf, disk) && !pending &&
> +			    (nonrot || !has_nonrot))
>  				return disk;

[Severity: High]
Does has_nonrot check if the non-rotational disks are actually readable?

Since has_nonrot is initialized as READ_ONCE(conf->nonrot_disks) at the top
of the function, it appears to evaluate to true if non-rotational disks are
configured in the array, even if they have failed, are rebuilding, or have
bad blocks.

If the non-rotational disks are unreadable, and we are evaluating a
sequential read on a rotational disk, (nonrot || !has_nonrot) will evaluate
to false. This causes the logic to bypass the early return for the
sequential disk.


[Severity: High]
Will this logic also cause sequential reads to thrash between idle rotational
disks in a healthy mixed array?

If the array has non-rotational disks (has_nonrot == true) that are currently
busy, but multiple rotational disks are idle:

1. A sequential read for HDD1 is evaluated.
2. The early return is bypassed because (nonrot || !has_nonrot) is false.
3. HDD1's pending count is artificially incremented to 1 via pending++.
4. HDD2 is evaluated, and since it is idle (pending == 0), it wins the
   min_pending contest against HDD1.

>  			/*
> @@ -844,17 +866,18 @@ static int choose_best_rdev(struct r1conf *conf, struct r1bio *r1_bio)
>  			 * If there is no other idle disk, this disk
>  			 * will be chosen.
>  			 */
> -			ctl.sequential_disk = disk;
> +			if (ctl.sequential_disk < 0 ||
> +			    (nonrot && !ctl.sequential_nonrot)) {
> +				ctl.sequential_disk = disk;
> +				ctl.sequential_nonrot = nonrot;
> +			}
>  		}
>  
> -		if (ctl.min_pending > pending ||
> -		    (has_nonrot && ctl.min_pending == pending &&
> -		     ctl.min_pending_disk >= 0 &&
> -		     raid1_rr_pos(disk, rr_start, conf->raid_disks) <
> -		     raid1_rr_pos(ctl.min_pending_disk, rr_start,
> -				  conf->raid_disks))) {
> +		if (is_better_disk(pending, disk, nonrot, &ctl,
> +				   rr_start, conf->raid_disks)) {
>  			ctl.min_pending = pending;
>  			ctl.min_pending_disk = disk;
> +			ctl.min_pending_nonrot = nonrot;
>  		}
>  
>  		if (ctl.closest_dist > dist) {
> @@ -867,7 +890,8 @@ static int choose_best_rdev(struct r1conf *conf, struct r1bio *r1_bio)
>  	 * sequential IO size exceeds optimal iosize, however, there is no other
>  	 * idle disk, so choose the sequential disk.
>  	 */
> -	if (ctl.sequential_disk != -1 && ctl.min_pending != 0)
> +	if (ctl.sequential_disk != -1 && ctl.min_pending != 0 &&
> +	    (ctl.sequential_nonrot || !has_nonrot))
>  		return ctl.sequential_disk;

[Severity: High]
Is it intentional that the sequential disk fallback is bypassed here?

Because HDD2 was idle (ctl.min_pending == 0), this sequential fallback
condition fails. The function will then fall through and return
ctl.min_pending_disk (HDD2), moving the I/O away from the sequential disk
(HDD1) and causing the read to ping-pong between the rotational disks.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260818070646.1029149-1-chencheng@fnnas.com?part=2

  reply	other threads:[~2026-08-18  7:26 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-18  7:06 [RFC PATCH 0/5] md/raid1: improve choose_best_rdev read balance Chen Cheng
2026-08-18  7:06 ` [RFC PATCH 1/5] md/raid1: balance reads across non-rotational disks Chen Cheng
2026-08-18  7:16   ` sashiko-bot
2026-08-18  7:06 ` [RFC PATCH 2/5] md/raid1: do not move nonrot reads onto a rot disk Chen Cheng
2026-08-18  7:26   ` sashiko-bot [this message]
2026-08-18  7:06 ` [RFC PATCH 3/5] md/raid1: do not send random reads to " Chen Cheng
2026-08-18  7:06 ` [RFC PATCH 4/5] md/raid1: use rot policy when no nonrot disk is readable Chen Cheng
2026-08-18  7:34   ` sashiko-bot
2026-08-18  7:06 ` [RFC PATCH 5/5] md/raid1: clarify choose_best_rdev comments Chen Cheng

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=20260818072623.1DDB71F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=chencheng@fnnas.com \
    --cc=linux-raid@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=yukuai@fygo.io \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox