Re: [PATCH v5 1/3] md: suspend array when sync_action=reshape

"Chen Cheng" <[email protected]>
Newsgroups gmane.linux.raid
Message-ID <[email protected]>
在 2026/6/22 20:25, [email protected] 写道:
> 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 <[email protected]>
> 
> 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.
> 
> [ ... ]
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.