Linux RAID subsystem development
 help / color / mirror / Atom feed
From: "Chen Cheng" <chencheng@fnnas.com>
To: <sashiko-reviews@lists.linux.dev>
Cc: <yukuai@fygo.io>, <linux-raid@vger.kernel.org>
Subject: Re: [PATCH v5 1/3] md: suspend array when sync_action=reshape
Date: Tue, 23 Jun 2026 18:20:34 +0800	[thread overview]
Message-ID: <44048e05-f7fa-499e-8bb4-bef56da87e86@fnnas.com> (raw)
In-Reply-To: <20260622122554.E43991F000E9@smtp.kernel.org>

在 2026/6/22 20:25, sashiko-bot@kernel.org 写道:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> 
> New issues:
> - [High] Unmatched mddev_resume() on mddev_lock() failure causes mddev->suspended underflow.
> - [High] The retry loop bypasses array re-suspension but leaves the suspended flag set, causing unprotected reshape and double-resume.
> - [High] Suspending the array before calling flush_work() creates a circular memory reclaim deadlock.
> 
> Pre-existing issues:
> - [High] Cross-task corruption of PF_MEMALLOC_NOIO flags when mddev_suspend() and mddev_resume() are interleaved.
> --
> 
> commit ec573079e939aa92bfb3fd89247846ae57883ef2
> Author: Chen Cheng <chencheng@fnnas.com>
> 
> md: suspend array when sync_action=reshape
> 
> This patch suspends the md array before starting a reshape operation to
> prevent new requests from allocating objects from the old pool during
> the transition. The array is then resumed on exit.
> 
>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>> index 096bb64e87bd5..e139f36e30b92 100644
>> --- a/drivers/md/md.c
>> +++ b/drivers/md/md.c
>> @@ -5258,26 +5258,37 @@ static ssize_t
>>   action_store(struct mddev *mddev, const char *page, size_t len)
>>   {
>>   	int ret;
>> +	bool suspended = false;
>>   	enum sync_action action;
>>   
>>   	if (!mddev->pers || !mddev->pers->sync_request)
>>   		return -EINVAL;
>>   
>> +	action = md_sync_action_by_name(page);
>> +	if (action == ACTION_RESHAPE) {
>> +		ret = mddev_suspend(mddev, true);
> 
> [Severity: High]
> This is a pre-existing issue, but does calling mddev_suspend() and
> mddev_resume() from concurrent contexts risk corrupting the PF_MEMALLOC_NOIO
> task flags?
> 
> When mddev_suspend() is called, it stores the task-specific NOIO flag into
> the global mddev structure:
> 
> drivers/md/md.c:mddev_suspend() {
>      ...
>      mddev->noio_flag = memalloc_noio_save();
>      ...
> }
> 
> If two tasks concurrently enter mddev_suspend(), the second task increments
> the suspend counter without overwriting mddev->noio_flag. If the first task
> then resumes (e.g., on a lock failure) it decrements the counter but returns
> early because the array is still suspended:
> 
> drivers/md/md.c:__mddev_resume() {
>      if (mddev->suspended) {
>          mutex_unlock(&mddev->suspend_mutex);
>          return;
>      }
>      ...
>      memalloc_noio_restore(mddev->noio_flag);
> }
> 
> This prevents the first task from restoring its NOIO flag. When the second
> task later resumes and drops the counter to 0, it incorrectly restores the
> first task's flags onto its own context. Could this leave the first task
> stuck in NOIO mode indefinitely?
> 
>> +		if (ret)
>> +			return ret;
>> +		suspended = true;
>> +	}
>>   retry:
>>   	if (work_busy(&mddev->sync_work))
>>   		flush_work(&mddev->sync_work);
> 

Looks like a independent problem, I need to sort out the flow.

> [Severity: High]
> Can holding the array suspended while synchronously waiting for the worker
> thread expose the worker's memory allocations to a circular deadlock?
> 
> If md_start_sync() allocates memory with GFP_KERNEL in the kworker thread
> (for example, when calling md_register_thread()), it can trigger direct
> memory reclaim. Because the worker thread did not transition the array to
> suspended itself, it does not inherit the PF_MEMALLOC_NOIO flag.
> 
> Consequently, memory reclaim can issue I/O to the md device. Since the array
> is now suspended, md_handle_request() will block this I/O indefinitely. The
> worker thread hangs waiting for I/O, and action_store() hangs here in
> flush_work() waiting for the worker thread, permanently deadlocking the
> array. Is it safe to suspend the array before flushing the work?
> 
>>   
>>   	ret = mddev_lock(mddev);
>> -	if (ret)
>> +	if (ret) {
>> +		mddev_resume(mddev);
>>   		return ret;
>> +	}
> 

Seems suspend_and_lock() after flush_work() more reasonable.

> [Severity: High]
> Does this unconditional mddev_resume() risk underflowing the mddev->suspended
> counter if the array was not actually suspended?
> 
> If action_store() is called with an action other than ACTION_RESHAPE, the
> array is not suspended. If the subsequent mddev_lock() fails (e.g., if it is
> interrupted by a signal), the error path unconditionally calls mddev_resume().
> 
> Since the array was not suspended, __mddev_resume() decrements
> mddev->suspended from 0 to -1. Because mddev->suspended is a signed integer
> and -1 evaluates to true, subsequent calls to mddev_suspend() will see
> mddev->suspended != 0, increment it back to 0, and return immediately without
> actually pausing I/O. Could this permanently break the suspension mechanism
> for the array?
> 
>>   
>>   	if (work_busy(&mddev->sync_work)) {
>> -		mddev_unlock(mddev);
>> +		if (suspended)
>> +			mddev_unlock_and_resume(mddev);
>> +		else
>> +			mddev_unlock(mddev);
>>   		goto retry;
>>   	}
> 

sure, my mistake.

> [Severity: High]
> Will the retry loop bypass array re-suspension while leaving the local
> suspended flag set to true?
> 
> If work_busy(&mddev->sync_work) is true, the code unlocks and resumes the
> array, then jumps back to the retry label. However, the retry label is placed
> after the mddev_suspend() block.
> 
> The code will not re-suspend the array, but the local 'suspended' boolean
> remains true. As a result, the reshape operation proceeds on an active,
> unsuspended array. Furthermore, when the function exits, it will call
> mddev_unlock_and_resume() again because 'suspended' is true, leading to a
> double-resume and another mddev->suspended underflow.
> 
> [ ... ]
>

  reply	other threads:[~2026-06-23 10:20 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-22 12:13 [PATCH v5 0/3] md/raid10: fix r10bio width mismatches across reshape Chen Cheng
2026-06-22 12:13 ` [PATCH v5 1/3] md: suspend array when sync_action=reshape Chen Cheng
2026-06-22 12:25   ` sashiko-bot
2026-06-23 10:20     ` Chen Cheng [this message]
2026-06-22 12:13 ` [PATCH v5 2/3] md/raid10: resize r10bio_pool for reshape Chen Cheng
2026-06-22 12:42   ` sashiko-bot
2026-06-22 12:13 ` [PATCH v5 3/3] md/raid10: free r10bio before ending master_bio in raid_end_bio_io() Chen Cheng
2026-06-22 12:29   ` sashiko-bot
2026-06-23 10:02     ` 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=44048e05-f7fa-499e-8bb4-bef56da87e86@fnnas.com \
    --to=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