Re: [PATCH] md/raid10: honor REQ_NOWAIT in wait_blocked_dev()

[email protected] Wed, 22 Jul 2026 07:53:25 +0000
Newsgroups org.kernel.vger.linux-raid
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Dropping the barrier in `wait_blocked_dev()` allows concurrent reshape to invalidate the physical mapping, leading to data corruption.
- [High] Pre-existing memory leak of `r10_bio` in `raid10_write_request()` when returning early due to `REQ_NOWAIT`.
- [High] `wait_blocked_dev()` incorrectly checks the first `conf->copies` physical disks for blocked state, rather than the dynamically calculated target disks.
- [High] Allocation of `r10_bio` unconditionally sleeps, violating `REQ_NOWAIT` semantics.
- [High] TOCTOU Use-After-Free of `md_rdev` due to missing `rcu_read_lock()` in `raid10_write_request()` and `wait_blocked_dev()`.
--

--- Patch [1]: [PATCH] md/raid10: honor REQ_NOWAIT in wait_blocked_dev() ---
commit 4ef20896ea5200b8191d2a0afa7c08883eacb0e6
Author: liyouhong <[email protected]>

md/raid10: honor REQ_NOWAIT in wait_blocked_dev()

The patch modifies wait_blocked_dev() in the raid10 driver to honor the
REQ_NOWAIT flag. It returns false immediately if a blocked device is
encountered during a nowait write request, instead of sleeping. The write
path then releases the barrier, frees the bio, and returns a wouldblock error.

> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 0a3cfdd3f5df8..2ccca42e07029 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -1293,7 +1293,8 @@ static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio,
>  	}
>  }
>  
> -static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
> +static bool wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio,
> +			     bool nowait)
>  {

[Severity: High]
This isn't a bug introduced by this patch, but how does wait_blocked_dev()
identify the correct mapped target disks?

The loop examines conf->mirrors[i] instead of using the dynamically
calculated target disk indices:

wait_blocked_dev() {
    ...
    for (i = 0; i < conf->copies; i++) {
        struct md_rdev *rdev, *rrdev;
        rdev = conf->mirrors[i].rdev;
    ...
}

Might this loop hardcode the check for the first conf->copies physical
disks, ignoring the actual target disks stored in r10_bio->devs[i].devnum
by raid10_find_phys()? This could risk bypassing blocked checks for disks
that the IO will actually touch.

>  	struct r10conf *conf = mddev->private;
>  	struct md_rdev *blocked_rdev;
[ ... ]
> @@ -1323,6 +1324,8 @@ static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
>  				set_bit(BlockedBadBlocks, &rdev->flags);
>  
>  			if (rdev_blocked(rdev)) {
> +				if (nowait)
> +					return false;
>  				blocked_rdev = rdev;
>  				atomic_inc(&rdev->nr_pending);

[Severity: High]
This is a pre-existing issue, but is there a use-after-free risk for
md_rdev here?

Because atomic_inc(&rdev->nr_pending) is executed without holding
rcu_read_lock(), could a concurrent raid10_remove_disk() check the
pending count, find it zero, and call md_kick_rdev_from_array(),
freeing the device before the increment occurs?

A similar pattern happens in raid10_write_request() where
atomic_inc(&rdev->nr_pending) is also called without rcu_read_lock()
protection.

>  				break;
>  			}
[ ... ]
> @@ -1347,6 +1352,7 @@ static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
>  		wait_barrier(conf, false);

[Severity: Critical]
This isn't a bug introduced by this patch, but can dropping the barrier
allow a concurrent reshape to invalidate the physical mapping?

When a blocked device is found, the synchronization barrier is dropped:

wait_blocked_dev() {
    ...
    allow_barrier(conf);
    md_wait_for_blocked_rdev(blocked_rdev, mddev);
    wait_barrier(conf, false);
    ...
}

Since raid10_find_phys() was already called before wait_blocked_dev(),
could a waiting reshape thread freeze the array, move data, and advance
reshape_progress while the barrier is dropped? If that happens,
raid10_write_request() might use a stale physical mapping in
r10_bio->devs to submit the IO after the barrier is re-acquired.

>  		goto retry_wait;
>  	}
> +	return true;
>  }
>  
>  static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
> @@ -1421,7 +1427,12 @@ static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
>  	r10_bio->read_slot = -1; /* make sure repl_bio gets freed */
>  	raid10_find_phys(conf, r10_bio);
>  
> -	wait_blocked_dev(mddev, r10_bio);
> +	if (!wait_blocked_dev(mddev, r10_bio, bio->bi_opf & REQ_NOWAIT)) {

[Severity: High]
This is a pre-existing issue, but are we leaking the r10_bio structure
on early returns in raid10_write_request()?

When REQ_NOWAIT is set and the IO overlaps with a clustered resync or
reshape, the function correctly bails out early, for example:

raid10_write_request() {
    ...
    if (bio->bi_opf & REQ_NOWAIT) {
        allow_barrier(conf);
        bio_wouldblock_error(bio);
        return false;
    }
    ...
}

If the function returns false without calling free_r10bio(r10_bio), will
this permanently leak the slab memory allocated for r10_bio?

[Severity: High]
This isn't a bug introduced by this patch, but would the initial allocation
of r10_bio violate REQ_NOWAIT semantics?

Before reaching the newly added nowait logic, the caller (__make_request)
unconditionally allocates r10_bio using GFP_NOIO:

__make_request() {
    ...
    r10_bio = mempool_alloc(&conf->r10bio_pool, GFP_NOIO);
    ...
}

Since GFP_NOIO allows direct reclaim, could this allocation sleep if the
mempool is exhausted, regardless of whether bio->bi_opf & REQ_NOWAIT
is set?

> +		allow_barrier(conf);
> +		free_r10bio(r10_bio);
> +		bio_wouldblock_error(bio);
> +		return false;
> +	}
>  
>  	max_sectors = r10_bio->sectors;

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=1