Re: [RFC PATCH] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume()

"Chen Cheng" <[email protected]> Sat, 11 Jul 2026 11:53:46 +0800
Newsgroups dev.linux.lists.dm-devel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-raid
Message-ID <[email protected]>
在 2026/7/5 21:10, yu kuai 写道:
> Hi,
> 
> 在 2026/6/24 18:13, Chen Cheng 写道:
>> From: Chen Cheng <[email protected]>
>>
>> Save token as mddev-scoped in mddev->noio_flags cause PF_MEMALLOC_NOIO
>> leak into task A, while task B restores a token that it never saved.
>>
>> scenario:
>>
>> task A                          mddev                         task B
>> ======                          =======                       ============
>> write suspend_lo
>> mddev_suspend()
>>                                   suspended == 0
>>                                   drain active_io
>>                                   suspended = 1
>> A: noio_A = memalloc_noio_save()
>> A returns with PF_MEMALLOC_NOIO set
>>
>>                                                                 write suspend_hi
>>                                                                 mddev_suspend()
>>                                   suspended == 1
>>                                   suspended = 2
>>                                                                 B returns
>>                                                                 (no save)
>>
>> mddev_resume()
>>                                   suspended = 1
>>                                   not last resume
>> A returns
>> A still has PF_MEMALLOC_NOIO   <-- leaked
>>
>>                                                                 mddev_resume()
>>                                   suspended = 0
>>                                                                 memalloc_noio_restore(noio_A)
>>                                                                 (restores A's token in B)
>>
>> Fixed by:
>>     - return each caller's noio_flags from mddev_suspend()
>>     - pass that token back into mddev_resume()
>>     - update the suspend-and-lock helpers to carry the token
>>     - store the token in struct raid_set for dm-raid paths where suspend
>>       and resume are paired across callbacks
>>
>> Validation:
>> repeatedly updates the array's suspend_lo and suspend_hi sysfs from many
>> concurrent userspace workers. That makes multiple tasks to call
>> mddev_suspend()/mddev_resume() concurrently.
>>
>> Each worker:
>>     - reads its initial /proc/self/stat flags and verifies that PF_MEMALLOC_NOIO is not already
>>       set
>>     - writes 0 to either suspend_lo or suspend_hi
>>     - immediately reads its own task flags again
>>     - reports success if flags & PF_MEMALLOC_NOIO is true after the write returns
>>
>> Link: https://github.com/chencheng-fnnas/reproducer/blob/main/repro-md-noio-token-leak.sh
>>
>> Fixes: 78f57ef9d50a ("md: use memalloc scope APIs in mddev_suspend()/mddev_resume()")
>>
>> Signed-off-by: Chen Cheng <[email protected]>
>> ---
>>    drivers/md/dm-raid.c       |  7 ++--
>>    drivers/md/md-autodetect.c |  5 ++-
>>    drivers/md/md-bitmap.c     | 12 +++---
>>    drivers/md/md.c            | 85 ++++++++++++++++++++++----------------
>>    drivers/md/md.h            | 23 ++++++-----
>>    drivers/md/raid5-cache.c   | 11 +++--
>>    drivers/md/raid5.c         | 25 ++++++-----
>>    7 files changed, 97 insertions(+), 71 deletions(-)
>>
>> diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
>> index 8f5a5e1342a9..d89207e3722a 100644
>> --- a/drivers/md/dm-raid.c
>> +++ b/drivers/md/dm-raid.c
>> @@ -239,10 +239,11 @@ struct raid_set {
>>    	int raid_disks;
>>    	int delta_disks;
>>    	int data_offset;
>>    	int raid10_copies;
>>    	int requested_bitmap_chunk_sectors;
>> +	unsigned int suspend_noio_flags;
>>    
>>    	struct mddev md;
>>    	struct raid_type *raid_type;
>>    
>>    	sector_t array_sectors;
>> @@ -3251,11 +3252,11 @@ static int raid_ctr(struct dm_target *ti, unsigned int argc, char **argv)
>>    	/* Start raid set read-only and assumed clean to change in raid_resume() */
>>    	rs->md.ro = MD_RDONLY;
>>    	rs->md.in_sync = 1;
>>    
>>    	/* Has to be held on running the array */
>> -	mddev_suspend_and_lock_nointr(&rs->md);
>> +	mddev_suspend_and_lock_nointr(&rs->md, &rs->suspend_noio_flags);
>>    
>>    	/* Keep array frozen until resume. */
>>    	md_frozen_sync_thread(&rs->md);
>>    
>>    	r = md_run(&rs->md);
>> @@ -3863,11 +3864,11 @@ static void raid_postsuspend(struct dm_target *ti)
>>    		/*
>>    		 * sync_thread must be stopped during suspend, and writes have
>>    		 * to be stopped before suspending to avoid deadlocks.
>>    		 */
>>    		md_stop_writes(&rs->md);
>> -		mddev_suspend(&rs->md, false);
>> +		mddev_suspend(&rs->md, false, &rs->suspend_noio_flags);
>>    		rs->md.ro = MD_RDONLY;
>>    	}
>>    	clear_bit(MD_DM_SUSPENDING, &mddev->flags);
>>    
>>    }
>> @@ -4141,11 +4142,11 @@ static void raid_resume(struct dm_target *ti)
>>    						       lockdep_is_held(&mddev->reconfig_mutex)));
>>    		clear_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags);
>>    		mddev->ro = MD_RDWR;
>>    		mddev->in_sync = 0;
>>    		md_unfrozen_sync_thread(mddev);
>> -		mddev_unlock_and_resume(mddev);
>> +		mddev_unlock_and_resume(mddev, rs->suspend_noio_flags);
>>    	}
>>    }
> 
> For mdraid, changes looks fine. However, for dm-raid, for example:
> 
> dmsetup suspend ...
> 
> In this case, array will be suspended while task returned to user. Take a look at
> the commit to introduce memalloc_noio_save(), it's supposed to be called for mdraid
> arrays to allocate memory while array is suspended, I don't see why dm-raid need it.
> So I'll suggest just bypass the memalloc_noio_save() for dm-raid where mddev_suspend()
> is not paired with mddev_resume() in the same task context.
> 

Hi Kuai.

I check the commit you mentioned., and I truelly misused 
memalloc_noio_save() in dmraid side, really thanks for point-out.

thanks,
cheng.

>>    
>>    static struct target_type raid_target = {
>>    	.name = "raid",
>> diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c
>> index 4b80165afd23..58e062cd0580 100644
>> --- a/drivers/md/md-autodetect.c
>> +++ b/drivers/md/md-autodetect.c
>> @@ -126,10 +126,11 @@ static void __init md_setup_drive(struct md_setup_args *args)
>>    	dev_t devices[MD_SB_DISKS + 1], mdev;
>>    	struct mdu_array_info_s ainfo = { };
>>    	struct mddev *mddev;
>>    	int err = 0, i;
>>    	char name[16];
>> +	unsigned int noio_flags;
>>    
>>    	if (args->partitioned) {
>>    		mdev = MKDEV(mdp_major, args->minor << MdpMinorShift);
>>    		sprintf(name, "md_d%d", args->minor);
>>    	} else {
>> @@ -173,11 +174,11 @@ static void __init md_setup_drive(struct md_setup_args *args)
>>    	if (IS_ERR(mddev)) {
>>    		pr_err("md: md_alloc failed - cannot start array %s\n", name);
>>    		return;
>>    	}
>>    
>> -	err = mddev_suspend_and_lock(mddev);
>> +	err = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (err) {
>>    		pr_err("md: failed to lock array %s\n", name);
>>    		goto out_mddev_put;
>>    	}
>>    
>> @@ -219,11 +220,11 @@ static void __init md_setup_drive(struct md_setup_args *args)
>>    	if (!err)
>>    		err = do_md_run(mddev);
>>    	if (err)
>>    		pr_warn("md: starting %s failed\n", name);
>>    out_unlock:
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    out_mddev_put:
>>    	mddev_put(mddev);
>>    }
>>    
>>    static int __init raid_setup(char *str)
>> diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
>> index 028b9ca8ce52..74b7f569a3f4 100644
>> --- a/drivers/md/md-bitmap.c
>> +++ b/drivers/md/md-bitmap.c
>> @@ -2620,13 +2620,14 @@ location_show(struct mddev *mddev, char *page)
>>    }
>>    
>>    static ssize_t
>>    location_store(struct mddev *mddev, const char *buf, size_t len)
>>    {
>> +	unsigned int noio_flags;
>>    	int rv;
>>    
>> -	rv = mddev_suspend_and_lock(mddev);
>> +	rv = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (rv)
>>    		return rv;
>>    
>>    	if (mddev->pers) {
>>    		if (mddev->recovery || mddev->sync_thread) {
>> @@ -2711,11 +2712,11 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
>>    		set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
>>    		md_wakeup_thread(mddev->thread);
>>    	}
>>    	rv = 0;
>>    out:
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    	if (rv)
>>    		return rv;
>>    	return len;
>>    
>>    merge_err:
>> @@ -2831,17 +2832,18 @@ backlog_store(struct mddev *mddev, const char *buf, size_t len)
>>    {
>>    	unsigned long backlog;
>>    	unsigned long old_mwb = mddev->bitmap_info.max_write_behind;
>>    	struct md_rdev *rdev;
>>    	bool has_write_mostly = false;
>> +	unsigned int noio_flags;
>>    	int rv = kstrtoul(buf, 10, &backlog);
>>    	if (rv)
>>    		return rv;
>>    	if (backlog > COUNTER_MAX)
>>    		return -EINVAL;
>>    
>> -	rv = mddev_suspend_and_lock(mddev);
>> +	rv = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (rv)
>>    		return rv;
>>    
>>    	/*
>>    	 * Without write mostly device, it doesn't make sense to set
>> @@ -2854,11 +2856,11 @@ backlog_store(struct mddev *mddev, const char *buf, size_t len)
>>    		}
>>    	}
>>    	if (!has_write_mostly) {
>>    		pr_warn_ratelimited("%s: can't set backlog, no write mostly device available\n",
>>    				    mdname(mddev));
>> -		mddev_unlock(mddev);
>> +		mddev_unlock_and_resume(mddev, noio_flags);
>>    		return -EINVAL;
>>    	}
>>    
>>    	mddev->bitmap_info.max_write_behind = backlog;
>>    	if (!backlog && mddev->serial_info_pool) {
>> @@ -2871,11 +2873,11 @@ backlog_store(struct mddev *mddev, const char *buf, size_t len)
>>    			mddev_create_serial_pool(mddev, rdev);
>>    	}
>>    	if (old_mwb != backlog)
>>    		bitmap_update_sb(mddev->bitmap);
>>    
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    	return len;
>>    }
>>    
>>    static struct md_sysfs_entry bitmap_backlog =
>>    __ATTR(backlog, S_IRUGO|S_IWUSR, backlog_show, backlog_store);
>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>> index 1377c407614c..86d938dee50a 100644
>> --- a/drivers/md/md.c
>> +++ b/drivers/md/md.c
>> @@ -459,11 +459,12 @@ static void md_submit_bio(struct bio *bio)
>>    
>>    /*
>>     * Make sure no new requests are submitted to the device, and any requests that
>>     * have been submitted are completely handled.
>>     */
>> -int mddev_suspend(struct mddev *mddev, bool interruptible)
>> +int mddev_suspend(struct mddev *mddev, bool interruptible,
>> +		  unsigned int *noio_flags)
>>    {
>>    	int err = 0;
>>    
>>    	/*
>>    	 * hold reconfig_mutex to wait for normal io will deadlock, because
>> @@ -478,10 +479,11 @@ int mddev_suspend(struct mddev *mddev, bool interruptible)
>>    		mutex_lock(&mddev->suspend_mutex);
>>    	if (err)
>>    		return err;
>>    
>>    	if (mddev->suspended) {
>> +		*noio_flags = memalloc_noio_save();
>>    		WRITE_ONCE(mddev->suspended, mddev->suspended + 1);
>>    		mutex_unlock(&mddev->suspend_mutex);
>>    		return 0;
>>    	}
>>    
>> @@ -515,31 +517,30 @@ int mddev_suspend(struct mddev *mddev, bool interruptible)
>>    	 * prevent deadlock.
>>    	 */
>>    	WRITE_ONCE(mddev->suspended, mddev->suspended + 1);
>>    
>>    	/* restrict memory reclaim I/O during raid array is suspend */
>> -	mddev->noio_flag = memalloc_noio_save();
>> +	*noio_flags = memalloc_noio_save();
>>    
>>    	mutex_unlock(&mddev->suspend_mutex);
>>    	return 0;
>>    }
>>    EXPORT_SYMBOL_GPL(mddev_suspend);
>>    
>> -static void __mddev_resume(struct mddev *mddev, bool recovery_needed)
>> +static void __mddev_resume(struct mddev *mddev, bool recovery_needed,
>> +			   unsigned int noio_flags)
>>    {
>>    	lockdep_assert_not_held(&mddev->reconfig_mutex);
>>    
>>    	mutex_lock(&mddev->suspend_mutex);
>> +	memalloc_noio_restore(noio_flags);
>>    	WRITE_ONCE(mddev->suspended, mddev->suspended - 1);
>>    	if (mddev->suspended) {
>>    		mutex_unlock(&mddev->suspend_mutex);
>>    		return;
>>    	}
>>    
>> -	/* entred the memalloc scope from mddev_suspend() */
>> -	memalloc_noio_restore(mddev->noio_flag);
>> -
>>    	percpu_ref_resurrect(&mddev->active_io);
>>    	wake_up(&mddev->sb_wait);
>>    
>>    	if (recovery_needed)
>>    		set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
>> @@ -547,13 +548,13 @@ static void __mddev_resume(struct mddev *mddev, bool recovery_needed)
>>    	md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
>>    
>>    	mutex_unlock(&mddev->suspend_mutex);
>>    }
>>    
>> -void mddev_resume(struct mddev *mddev)
>> +void mddev_resume(struct mddev *mddev, unsigned int noio_flags)
>>    {
>> -	return __mddev_resume(mddev, true);
>> +	return __mddev_resume(mddev, true, noio_flags);
>>    }
>>    EXPORT_SYMBOL_GPL(mddev_resume);
>>    
>>    /* sync bdev before setting device to readonly or stopping raid*/
>>    static int mddev_set_closing_and_sync_blockdev(struct mddev *mddev, int opener_num)
>> @@ -3737,10 +3738,11 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr,
>>    {
>>    	struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr);
>>    	struct md_rdev *rdev = container_of(kobj, struct md_rdev, kobj);
>>    	struct kernfs_node *kn = NULL;
>>    	bool suspend = false;
>> +	unsigned int noio_flags = 0;
>>    	ssize_t rv;
>>    	struct mddev *mddev = READ_ONCE(rdev->mddev);
>>    
>>    	if (!entry->store)
>>    		return -EIO;
>> @@ -3756,17 +3758,17 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr,
>>    		    cmd_match(page, "writemostly") ||
>>    		    cmd_match(page, "-writemostly"))
>>    			suspend = true;
>>    	}
>>    
>> -	rv = suspend ? mddev_suspend_and_lock(mddev) : mddev_lock(mddev);
>> +	rv = suspend ? mddev_suspend_and_lock(mddev, &noio_flags) : mddev_lock(mddev);
>>    	if (!rv) {
>>    		if (rdev->mddev == NULL)
>>    			rv = -ENODEV;
>>    		else
>>    			rv = entry->store(rdev, page, length);
>> -		suspend ? mddev_unlock_and_resume(mddev) : mddev_unlock(mddev);
>> +		suspend ? mddev_unlock_and_resume(mddev, noio_flags) : mddev_unlock(mddev);
>>    	}
>>    
>>    	if (kn)
>>    		sysfs_unbreak_active_protection(kn);
>>    
>> @@ -4049,15 +4051,16 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
>>    	size_t slen = len;
>>    	struct md_personality *pers, *oldpers;
>>    	long level;
>>    	void *priv, *oldpriv;
>>    	struct md_rdev *rdev;
>> +	unsigned int noio_flags;
>>    
>>    	if (slen == 0 || slen >= sizeof(clevel))
>>    		return -EINVAL;
>>    
>> -	rv = mddev_suspend_and_lock(mddev);
>> +	rv = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (rv)
>>    		return rv;
>>    
>>    	if (mddev->pers == NULL) {
>>    		memcpy(mddev->clevel, buf, slen);
>> @@ -4231,11 +4234,11 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
>>    		md_update_sb(mddev, 1);
>>    	sysfs_notify_dirent_safe(mddev->sysfs_level);
>>    	md_new_event();
>>    	rv = len;
>>    out_unlock:
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    	return rv;
>>    }
>>    
>>    static struct md_sysfs_entry md_level =
>>    __ATTR(level, S_IRUGO|S_IWUSR, level_show, level_store);
>> @@ -4410,17 +4413,18 @@ static int update_raid_disks(struct mddev *mddev, int raid_disks);
>>    
>>    static ssize_t
>>    raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
>>    {
>>    	unsigned int n;
>> +	unsigned int noio_flags;
>>    	int err;
>>    
>>    	err = kstrtouint(buf, 10, &n);
>>    	if (err < 0)
>>    		return err;
>>    
>> -	err = mddev_suspend_and_lock(mddev);
>> +	err = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (err)
>>    		return err;
>>    	if (mddev->pers) {
>>    		if (n != mddev->raid_disks)
>>    			err = update_raid_disks(mddev, n);
>> @@ -4442,11 +4446,11 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
>>    		mddev->raid_disks = n;
>>    		mddev->reshape_backwards = (mddev->delta_disks < 0);
>>    	} else
>>    		mddev->raid_disks = n;
>>    out_unlock:
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    	return err ? err : len;
>>    }
>>    static struct md_sysfs_entry md_raid_disks =
>>    __ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store);
>>    
>> @@ -4822,10 +4826,11 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len)
>>    	char *e;
>>    	int major = simple_strtoul(buf, &e, 10);
>>    	int minor;
>>    	dev_t dev;
>>    	struct md_rdev *rdev;
>> +	unsigned int noio_flags;
>>    	int err;
>>    
>>    	if (!*buf || *e != ':' || !e[1] || e[1] == '\n')
>>    		return -EINVAL;
>>    	minor = simple_strtoul(e+1, &e, 10);
>> @@ -4834,11 +4839,11 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len)
>>    	dev = MKDEV(major, minor);
>>    	if (major != MAJOR(dev) ||
>>    	    minor != MINOR(dev))
>>    		return -EOVERFLOW;
>>    
>> -	err = mddev_suspend_and_lock(mddev);
>> +	err = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (err)
>>    		return err;
>>    	if (mddev->persistent) {
>>    		rdev = md_import_device(dev, mddev->major_version,
>>    					mddev->minor_version);
>> @@ -4855,18 +4860,18 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len)
>>    		rdev = md_import_device(dev, -2, -1);
>>    	else
>>    		rdev = md_import_device(dev, -1, -1);
>>    
>>    	if (IS_ERR(rdev)) {
>> -		mddev_unlock_and_resume(mddev);
>> +		mddev_unlock_and_resume(mddev, noio_flags);
>>    		return PTR_ERR(rdev);
>>    	}
>>    	err = bind_rdev_to_array(rdev, mddev);
>>     out:
>>    	if (err)
>>    		export_rdev(rdev);
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    	if (!err)
>>    		md_new_event();
>>    	return err ? err : len;
>>    }
>>    
>> @@ -5257,28 +5262,29 @@ static int mddev_start_reshape(struct mddev *mddev)
>>    static ssize_t
>>    action_store(struct mddev *mddev, const char *page, size_t len)
>>    {
>>    	int ret;
>>    	enum sync_action action;
>> +	unsigned int noio_flags = 0;
>>    
>>    	if (!mddev->pers || !mddev->pers->sync_request)
>>    		return -EINVAL;
>>    
>>    	action = md_sync_action_by_name(page);
>>    retry:
>>    	if (work_busy(&mddev->sync_work))
>>    		flush_work(&mddev->sync_work);
>>    
>>    	ret = (action == ACTION_RESHAPE) ?
>> -		mddev_suspend_and_lock(mddev) :
>> +		mddev_suspend_and_lock(mddev, &noio_flags) :
>>    		mddev_lock(mddev);
>>    	if (ret)
>>    		return ret;
>>    
>>    	if (work_busy(&mddev->sync_work)) {
>>    		if (action == ACTION_RESHAPE)
>> -			mddev_unlock_and_resume(mddev);
>> +			mddev_unlock_and_resume(mddev, noio_flags);
>>    		else
>>    			mddev_unlock(mddev);
>>    		goto retry;
>>    	}
>>    
>> @@ -5349,11 +5355,11 @@ action_store(struct mddev *mddev, const char *page, size_t len)
>>    	sysfs_notify_dirent_safe(mddev->sysfs_action);
>>    	ret = len;
>>    
>>    out:
>>    	if (action == ACTION_RESHAPE)
>> -		mddev_unlock_and_resume(mddev);
>> +		mddev_unlock_and_resume(mddev, noio_flags);
>>    	else
>>    		mddev_unlock(mddev);
>>    	return ret;
>>    }
>>    
>> @@ -5640,24 +5646,25 @@ suspend_lo_show(struct mddev *mddev, char *page)
>>    
>>    static ssize_t
>>    suspend_lo_store(struct mddev *mddev, const char *buf, size_t len)
>>    {
>>    	unsigned long long new;
>> +	unsigned int noio_flags;
>>    	int err;
>>    
>>    	err = kstrtoull(buf, 10, &new);
>>    	if (err < 0)
>>    		return err;
>>    	if (new != (sector_t)new)
>>    		return -EINVAL;
>>    
>> -	err = mddev_suspend(mddev, true);
>> +	err = mddev_suspend(mddev, true, &noio_flags);
>>    	if (err)
>>    		return err;
>>    
>>    	WRITE_ONCE(mddev->suspend_lo, new);
>> -	mddev_resume(mddev);
>> +	mddev_resume(mddev, noio_flags);
>>    
>>    	return len;
>>    }
>>    static struct md_sysfs_entry md_suspend_lo =
>>    __ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store);
>> @@ -5671,24 +5678,25 @@ suspend_hi_show(struct mddev *mddev, char *page)
>>    
>>    static ssize_t
>>    suspend_hi_store(struct mddev *mddev, const char *buf, size_t len)
>>    {
>>    	unsigned long long new;
>> +	unsigned int noio_flags;
>>    	int err;
>>    
>>    	err = kstrtoull(buf, 10, &new);
>>    	if (err < 0)
>>    		return err;
>>    	if (new != (sector_t)new)
>>    		return -EINVAL;
>>    
>> -	err = mddev_suspend(mddev, true);
>> +	err = mddev_suspend(mddev, true, &noio_flags);
>>    	if (err)
>>    		return err;
>>    
>>    	WRITE_ONCE(mddev->suspend_hi, new);
>> -	mddev_resume(mddev);
>> +	mddev_resume(mddev, noio_flags);
>>    
>>    	return len;
>>    }
>>    static struct md_sysfs_entry md_suspend_hi =
>>    __ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store);
>> @@ -5928,19 +5936,20 @@ static ssize_t serialize_policy_show(struct mddev *mddev, char *page)
>>    static ssize_t
>>    serialize_policy_store(struct mddev *mddev, const char *buf, size_t len)
>>    {
>>    	int err;
>>    	bool value;
>> +	unsigned int noio_flags;
>>    
>>    	err = kstrtobool(buf, &value);
>>    	if (err)
>>    		return err;
>>    
>>    	if (value == test_bit(MD_SERIALIZE_POLICY, &mddev->flags))
>>    		return len;
>>    
>> -	err = mddev_suspend_and_lock(mddev);
>> +	err = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (err)
>>    		return err;
>>    	if (mddev->pers == NULL || (mddev->pers->head.id != ID_RAID1)) {
>>    		pr_err("md: serialize_policy is only effective for raid1\n");
>>    		err = -EINVAL;
>> @@ -5953,11 +5962,11 @@ serialize_policy_store(struct mddev *mddev, const char *buf, size_t len)
>>    	} else {
>>    		mddev_destroy_serial_pool(mddev, NULL);
>>    		clear_bit(MD_SERIALIZE_POLICY, &mddev->flags);
>>    	}
>>    unlock:
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    	return err ?: len;
>>    }
>>    
>>    static struct md_sysfs_entry md_serialize_policy =
>>    __ATTR(serialize_policy, S_IRUGO | S_IWUSR, serialize_policy_show,
>> @@ -6263,21 +6272,22 @@ EXPORT_SYMBOL_GPL(mddev_stack_new_rdev);
>>    
>>    /* update the optimal I/O size after a reshape */
>>    void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes)
>>    {
>>    	struct queue_limits lim;
>> +	unsigned int noio_flags;
>>    
>>    	if (mddev_is_dm(mddev))
>>    		return;
>>    
>>    	/* don't bother updating io_opt if we can't suspend the array */
>> -	if (mddev_suspend(mddev, false) < 0)
>> +	if (mddev_suspend(mddev, false, &noio_flags) < 0)
>>    		return;
>>    	lim = queue_limits_start_update(mddev->gendisk->queue);
>>    	lim.io_opt = lim.io_min * nr_stripes;
>>    	queue_limits_commit_update(mddev->gendisk->queue, &lim);
>> -	mddev_resume(mddev);
>> +	mddev_resume(mddev, noio_flags);
>>    }
>>    EXPORT_SYMBOL_GPL(mddev_update_io_opt);
>>    
>>    static void mddev_delayed_delete(struct work_struct *ws)
>>    {
>> @@ -7255,10 +7265,11 @@ static void autorun_array(struct mddev *mddev)
>>     */
>>    static void autorun_devices(int part)
>>    {
>>    	struct md_rdev *rdev0, *rdev, *tmp;
>>    	struct mddev *mddev;
>> +	unsigned int noio_flags;
>>    
>>    	pr_info("md: autorun ...\n");
>>    	while (!list_empty(&pending_raid_disks)) {
>>    		int unit;
>>    		dev_t dev;
>> @@ -7295,27 +7306,27 @@ static void autorun_devices(int part)
>>    
>>    		mddev = md_alloc(dev, NULL);
>>    		if (IS_ERR(mddev))
>>    			break;
>>    
>> -		if (mddev_suspend_and_lock(mddev))
>> +		if (mddev_suspend_and_lock(mddev, &noio_flags))
>>    			pr_warn("md: %s locked, cannot run\n", mdname(mddev));
>>    		else if (mddev->raid_disks || mddev->major_version
>>    			 || !list_empty(&mddev->disks)) {
>>    			pr_warn("md: %s already running, cannot run %pg\n",
>>    				mdname(mddev), rdev0->bdev);
>> -			mddev_unlock_and_resume(mddev);
>> +			mddev_unlock_and_resume(mddev, noio_flags);
>>    		} else {
>>    			pr_debug("md: created %s\n", mdname(mddev));
>>    			mddev->persistent = 1;
>>    			rdev_for_each_list(rdev, tmp, &candidates) {
>>    				list_del_init(&rdev->same_set);
>>    				if (bind_rdev_to_array(rdev, mddev))
>>    					export_rdev(rdev);
>>    			}
>>    			autorun_array(mddev);
>> -			mddev_unlock_and_resume(mddev);
>> +			mddev_unlock_and_resume(mddev, noio_flags);
>>    		}
>>    		/* on success, candidates will be empty, on error
>>    		 * it won't...
>>    		 */
>>    		rdev_for_each_list(rdev, tmp, &candidates) {
>> @@ -8329,10 +8340,11 @@ static int __md_set_array_info(struct mddev *mddev, void __user *argp)
>>    
>>    static int md_ioctl(struct block_device *bdev, blk_mode_t mode,
>>    			unsigned int cmd, unsigned long arg)
>>    {
>>    	int err = 0;
>> +	unsigned int noio_flags = 0;
>>    	void __user *argp = (void __user *)arg;
>>    	struct mddev *mddev = NULL;
>>    
>>    	err = md_ioctl_valid(cmd);
>>    	if (err)
>> @@ -8380,11 +8392,11 @@ static int md_ioctl(struct block_device *bdev, blk_mode_t mode,
>>    	}
>>    
>>    	if (!md_is_rdwr(mddev))
>>    		flush_work(&mddev->sync_work);
>>    
>> -	err = md_ioctl_need_suspend(cmd) ? mddev_suspend_and_lock(mddev) :
>> +	err = md_ioctl_need_suspend(cmd) ? mddev_suspend_and_lock(mddev, &noio_flags) :
>>    					   mddev_lock(mddev);
>>    	if (err) {
>>    		pr_debug("md: ioctl lock interrupted, reason %d, cmd %d\n",
>>    			 err, cmd);
>>    		goto out;
>> @@ -8511,11 +8523,11 @@ static int md_ioctl(struct block_device *bdev, blk_mode_t mode,
>>    unlock:
>>    	if (mddev->hold_active == UNTIL_IOCTL &&
>>    	    err != -EINVAL)
>>    		mddev->hold_active = 0;
>>    
>> -	md_ioctl_need_suspend(cmd) ? mddev_unlock_and_resume(mddev) :
>> +	md_ioctl_need_suspend(cmd) ? mddev_unlock_and_resume(mddev, noio_flags) :
>>    				     mddev_unlock(mddev);
>>    
>>    out:
>>    	if (cmd == STOP_ARRAY_RO || (err && cmd == STOP_ARRAY))
>>    		clear_bit(MD_CLOSING, &mddev->flags);
>> @@ -10180,20 +10192,21 @@ static bool md_choose_sync_action(struct mddev *mddev, int *spares)
>>    static void md_start_sync(struct work_struct *ws)
>>    {
>>    	struct mddev *mddev = container_of(ws, struct mddev, sync_work);
>>    	int spares = 0;
>>    	bool suspend = false;
>> +	unsigned int noio_flags = 0;
>>    	char *name;
>>    
>>    	/*
>>    	 * If reshape is still in progress, spares won't be added or removed
>>    	 * from conf until reshape is done.
>>    	 */
>>    	if (mddev->reshape_position == MaxSector &&
>>    	    md_spares_need_change(mddev)) {
>>    		suspend = true;
>> -		mddev_suspend(mddev, false);
>> +		mddev_suspend(mddev, false, &noio_flags);
>>    	}
>>    
>>    	mddev_lock_nointr(mddev);
>>    	if (!md_is_rdwr(mddev)) {
>>    		/*
>> @@ -10237,11 +10250,11 @@ static void md_start_sync(struct work_struct *ws)
>>    	 * not set it again. Otherwise, we may cause issue like this one:
>>    	 *     https://bugzilla.kernel.org/show_bug.cgi?id=218200
>>    	 * Therefore, use __mddev_resume(mddev, false).
>>    	 */
>>    	if (suspend)
>> -		__mddev_resume(mddev, false);
>> +		__mddev_resume(mddev, false, noio_flags);
>>    	md_wakeup_thread(mddev->sync_thread);
>>    	sysfs_notify_dirent_safe(mddev->sysfs_action);
>>    	md_new_event();
>>    	return;
>>    
>> @@ -10257,11 +10270,11 @@ static void md_start_sync(struct work_struct *ws)
>>    	 * not set it again. Otherwise, we may cause issue like this one:
>>    	 *     https://bugzilla.kernel.org/show_bug.cgi?id=218200
>>    	 * Therefore, use __mddev_resume(mddev, false).
>>    	 */
>>    	if (suspend)
>> -		__mddev_resume(mddev, false);
>> +		__mddev_resume(mddev, false, noio_flags);
>>    
>>    	wake_up(&resync_wait);
>>    	if (test_and_clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery) &&
>>    	    mddev->sysfs_action)
>>    		sysfs_notify_dirent_safe(mddev->sysfs_action);
>> diff --git a/drivers/md/md.h b/drivers/md/md.h
>> index d8daf0f75cbb..3337cd21eb30 100644
>> --- a/drivers/md/md.h
>> +++ b/drivers/md/md.h
>> @@ -619,11 +619,10 @@ struct mddev {
>>    	mempool_t *serial_info_pool;
>>    	void (*sync_super)(struct mddev *mddev, struct md_rdev *rdev);
>>    	struct md_cluster_info		*cluster_info;
>>    	struct md_cluster_operations *cluster_ops;
>>    	unsigned int			good_device_nr;	/* good device num within cluster raid */
>> -	unsigned int			noio_flag; /* for memalloc scope API */
>>    
>>    	/*
>>    	 * Temporarily store rdev that will be finally removed when
>>    	 * reconfig_mutex is unlocked, protected by reconfig_mutex.
>>    	 */
>> @@ -953,12 +952,13 @@ extern void md_stop(struct mddev *mddev);
>>    extern void md_stop_writes(struct mddev *mddev);
>>    extern int md_rdev_init(struct md_rdev *rdev);
>>    extern void md_rdev_clear(struct md_rdev *rdev);
>>    
>>    extern bool md_handle_request(struct mddev *mddev, struct bio *bio);
>> -extern int mddev_suspend(struct mddev *mddev, bool interruptible);
>> -extern void mddev_resume(struct mddev *mddev);
>> +extern int mddev_suspend(struct mddev *mddev, bool interruptible,
>> +			 unsigned int *noio_flags);
>> +extern void mddev_resume(struct mddev *mddev, unsigned int noio_flags);
>>    extern void md_idle_sync_thread(struct mddev *mddev);
>>    extern void md_frozen_sync_thread(struct mddev *mddev);
>>    extern void md_unfrozen_sync_thread(struct mddev *mddev);
>>    
>>    extern void md_update_sb(struct mddev *mddev, int force);
>> @@ -999,35 +999,38 @@ static inline void mddev_check_write_zeroes(struct mddev *mddev, struct bio *bio
>>    	if (bio_op(bio) == REQ_OP_WRITE_ZEROES &&
>>    	    !bio->bi_bdev->bd_disk->queue->limits.max_write_zeroes_sectors)
>>    		mddev->gendisk->queue->limits.max_write_zeroes_sectors = 0;
>>    }
>>    
>> -static inline int mddev_suspend_and_lock(struct mddev *mddev)
>> +static inline int mddev_suspend_and_lock(struct mddev *mddev,
>> +					 unsigned int *noio_flags)
>>    {
>>    	int ret;
>>    
>> -	ret = mddev_suspend(mddev, true);
>> +	ret = mddev_suspend(mddev, true, noio_flags);
>>    	if (ret)
>>    		return ret;
>>    
>>    	ret = mddev_lock(mddev);
>>    	if (ret)
>> -		mddev_resume(mddev);
>> +		mddev_resume(mddev, *noio_flags);
>>    
>>    	return ret;
>>    }
>>    
>> -static inline void mddev_suspend_and_lock_nointr(struct mddev *mddev)
>> +static inline void mddev_suspend_and_lock_nointr(struct mddev *mddev,
>> +						 unsigned int *noio_flags)
>>    {
>> -	mddev_suspend(mddev, false);
>> +	mddev_suspend(mddev, false, noio_flags);
>>    	mddev_lock_nointr(mddev);
>>    }
>>    
>> -static inline void mddev_unlock_and_resume(struct mddev *mddev)
>> +static inline void mddev_unlock_and_resume(struct mddev *mddev,
>> +					   unsigned int noio_flags)
>>    {
>>    	mddev_unlock(mddev);
>> -	mddev_resume(mddev);
>> +	mddev_resume(mddev, noio_flags);
>>    }
>>    
>>    struct mdu_array_info_s;
>>    struct mdu_disk_info_s;
>>    
>> diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
>> index 7b7546bfa21f..6f8e3a624456 100644
>> --- a/drivers/md/raid5-cache.c
>> +++ b/drivers/md/raid5-cache.c
>> @@ -693,13 +693,15 @@ static void r5c_disable_writeback_async(struct work_struct *work)
>>    		   !READ_ONCE(conf->log) ||
>>    		   !test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags));
>>    
>>    	log = READ_ONCE(conf->log);
>>    	if (log) {
>> -		mddev_suspend(mddev, false);
>> +		unsigned int noio_flags;
>> +
>> +		mddev_suspend(mddev, false, &noio_flags);
>>    		log->r5c_journal_mode = R5C_JOURNAL_MODE_WRITE_THROUGH;
>> -		mddev_resume(mddev);
>> +		mddev_resume(mddev, noio_flags);
>>    	}
>>    }
>>    
>>    static void r5l_submit_current_io(struct r5l_log *log)
>>    {
>> @@ -2603,10 +2605,11 @@ EXPORT_SYMBOL(r5c_journal_mode_set);
>>    static ssize_t r5c_journal_mode_store(struct mddev *mddev,
>>    				      const char *page, size_t length)
>>    {
>>    	int mode = ARRAY_SIZE(r5c_journal_mode_str);
>>    	size_t len = length;
>> +	unsigned int noio_flags;
>>    	int ret;
>>    
>>    	if (len < 2)
>>    		return -EINVAL;
>>    
>> @@ -2615,15 +2618,15 @@ static ssize_t r5c_journal_mode_store(struct mddev *mddev,
>>    
>>    	while (mode--)
>>    		if (strlen(r5c_journal_mode_str[mode]) == len &&
>>    		    !strncmp(page, r5c_journal_mode_str[mode], len))
>>    			break;
>> -	ret = mddev_suspend_and_lock(mddev);
>> +	ret = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (ret)
>>    		return ret;
>>    	ret = r5c_journal_mode_set(mddev, mode);
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    	return ret ?: length;
>>    }
>>    
>>    struct md_sysfs_entry
>>    r5c_journal_mode = __ATTR(journal_mode, 0644,
>> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
>> index 65ae7d8930fc..6062c4b62cc8 100644
>> --- a/drivers/md/raid5.c
>> +++ b/drivers/md/raid5.c
>> @@ -6992,11 +6992,11 @@ raid5_show_stripe_size(struct mddev  *mddev, char *page)
>>    #if PAGE_SIZE != DEFAULT_STRIPE_SIZE
>>    static ssize_t
>>    raid5_store_stripe_size(struct mddev  *mddev, const char *page, size_t len)
>>    {
>>    	struct r5conf *conf;
>> -	unsigned long new;
>> +	unsigned long new, noio_flags;
>>    	int err;
>>    	int size;
>>    
>>    	if (len >= PAGE_SIZE)
>>    		return -EINVAL;
>> @@ -7011,11 +7011,11 @@ raid5_store_stripe_size(struct mddev  *mddev, const char *page, size_t len)
>>    	if (new % DEFAULT_STRIPE_SIZE != 0 ||
>>    			new > PAGE_SIZE || new == 0 ||
>>    			new != roundup_pow_of_two(new))
>>    		return -EINVAL;
>>    
>> -	err = mddev_suspend_and_lock(mddev);
>> +	err = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (err)
>>    		return err;
>>    
>>    	conf = mddev->private;
>>    	if (!conf) {
>> @@ -7049,11 +7049,11 @@ raid5_store_stripe_size(struct mddev  *mddev, const char *page, size_t len)
>>    		err = -ENOMEM;
>>    	}
>>    	mutex_unlock(&conf->cache_size_mutex);
>>    
>>    out_unlock:
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    	return err ?: len;
>>    }
>>    
>>    static struct md_sysfs_entry
>>    raid5_stripe_size = __ATTR(stripe_size, 0644,
>> @@ -7127,19 +7127,20 @@ raid5_show_skip_copy(struct mddev *mddev, char *page)
>>    static ssize_t
>>    raid5_store_skip_copy(struct mddev *mddev, const char *page, size_t len)
>>    {
>>    	struct r5conf *conf;
>>    	unsigned long new;
>> +	unsigned int noio_flags;
>>    	int err;
>>    
>>    	if (len >= PAGE_SIZE)
>>    		return -EINVAL;
>>    	if (kstrtoul(page, 10, &new))
>>    		return -EINVAL;
>>    	new = !!new;
>>    
>> -	err = mddev_suspend_and_lock(mddev);
>> +	err = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (err)
>>    		return err;
>>    	conf = mddev->private;
>>    	if (!conf)
>>    		err = -ENODEV;
>> @@ -7152,11 +7153,11 @@ raid5_store_skip_copy(struct mddev *mddev, const char *page, size_t len)
>>    			lim.features |= BLK_FEAT_STABLE_WRITES;
>>    		else
>>    			lim.features &= ~BLK_FEAT_STABLE_WRITES;
>>    		err = queue_limits_commit_update(q, &lim);
>>    	}
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    	return err ?: len;
>>    }
>>    
>>    static struct md_sysfs_entry
>>    raid5_skip_copy = __ATTR(skip_copy, S_IRUGO | S_IWUSR,
>> @@ -7195,10 +7196,11 @@ static int alloc_thread_groups(struct r5conf *conf, int cnt,
>>    static ssize_t
>>    raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len)
>>    {
>>    	struct r5conf *conf;
>>    	unsigned int new;
>> +	unsigned int noio_flags;
>>    	int err;
>>    	struct r5worker_group *new_groups, *old_groups;
>>    	int group_cnt;
>>    
>>    	if (len >= PAGE_SIZE)
>> @@ -7207,16 +7209,16 @@ raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len)
>>    		return -EINVAL;
>>    	/* 8192 should be big enough */
>>    	if (new > 8192)
>>    		return -EINVAL;
>>    
>> -	err = mddev_suspend_and_lock(mddev);
>> +	err = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (err)
>>    		return err;
>>    	conf = mddev->private;
>>    	if (!conf) {
>> -		mddev_unlock_and_resume(mddev);
>> +		mddev_unlock_and_resume(mddev, noio_flags);
>>    		return -ENODEV;
>>    	}
>>    	raid5_quiesce(mddev, true);
>>    
>>    	if (new != conf->worker_cnt_per_group) {
>> @@ -7237,11 +7239,11 @@ raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len)
>>    			kfree(old_groups);
>>    		}
>>    	}
>>    
>>    	raid5_quiesce(mddev, false);
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    
>>    	return err ?: len;
>>    }
>>    
>>    static struct md_sysfs_entry
>> @@ -8940,18 +8942,19 @@ static void *raid6_takeover(struct mddev *mddev)
>>    }
>>    
>>    static int raid5_change_consistency_policy(struct mddev *mddev, const char *buf)
>>    {
>>    	struct r5conf *conf;
>> +	unsigned int noio_flags;
>>    	int err;
>>    
>> -	err = mddev_suspend_and_lock(mddev);
>> +	err = mddev_suspend_and_lock(mddev, &noio_flags);
>>    	if (err)
>>    		return err;
>>    	conf = mddev->private;
>>    	if (!conf) {
>> -		mddev_unlock_and_resume(mddev);
>> +		mddev_unlock_and_resume(mddev, noio_flags);
>>    		return -ENODEV;
>>    	}
>>    
>>    	if (strncmp(buf, "ppl", 3) == 0) {
>>    		/* ppl only works with RAID 5 */
>> @@ -8990,11 +8993,11 @@ static int raid5_change_consistency_policy(struct mddev *mddev, const char *buf)
>>    	}
>>    
>>    	if (!err)
>>    		md_update_sb(mddev, 1);
>>    
>> -	mddev_unlock_and_resume(mddev);
>> +	mddev_unlock_and_resume(mddev, noio_flags);
>>    
>>    	return err;
>>    }
>>    
>>    static int raid5_start(struct mddev *mddev)
>