The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v1] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume()
@ 2026-07-11  6:48 Chen Cheng
  2026-07-11 18:08 ` yu kuai
  0 siblings, 1 reply; 2+ messages in thread
From: Chen Cheng @ 2026-07-11  6:48 UTC (permalink / raw)
  To: linux-raid, yukuai; +Cc: chencheng, linux-kernel

From: Chen Cheng <chencheng@fnnas.com>

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:
  for md-raid side:
	  - 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
  for dm-raid side:
	  - just bypass because: dm-raid call suspend() or resume() will return to
		userland, so is not paired in the same task context.

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 <chencheng@fnnas.com>
---

rfc -> v1:
        - dm-raid side just bypass because suspend and resume not in the
          same task context..

---
 drivers/md/dm-raid.c       |  6 +--
 drivers/md/md-autodetect.c |  5 ++-
 drivers/md/md-bitmap.c     | 12 ++---
 drivers/md/md.c            | 90 +++++++++++++++++++++++---------------
 drivers/md/md.h            | 23 +++++-----
 drivers/md/raid5-cache.c   | 11 +++--
 drivers/md/raid5.c         | 24 +++++-----
 7 files changed, 101 insertions(+), 70 deletions(-)

diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 8f5a5e1342a9..ce0f562c5da3 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -3251,11 +3251,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, NULL);
 
 	/* Keep array frozen until resume. */
 	md_frozen_sync_thread(&rs->md);
 
 	r = md_run(&rs->md);
@@ -3863,11 +3863,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, NULL);
 		rs->md.ro = MD_RDONLY;
 	}
 	clear_bit(MD_DM_SUSPENDING, &mddev->flags);
 
 }
@@ -4141,11 +4141,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, 0);
 	}
 }
 
 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..04e10769d5c6 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
@@ -477,11 +478,16 @@ int mddev_suspend(struct mddev *mddev, bool interruptible)
 	else
 		mutex_lock(&mddev->suspend_mutex);
 	if (err)
 		return err;
 
+	if (noio_flags)
+		*noio_flags = 0;
+
 	if (mddev->suspended) {
+		if (noio_flags && !mddev_is_dm(mddev))
+			*noio_flags = memalloc_noio_save();
 		WRITE_ONCE(mddev->suspended, mddev->suspended + 1);
 		mutex_unlock(&mddev->suspend_mutex);
 		return 0;
 	}
 
@@ -515,31 +521,31 @@ 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();
+	if (noio_flags && !mddev_is_dm(mddev))
+		*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 +553,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 +3743,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 +3763,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 +4056,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 +4239,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 +4418,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 +4451,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 +4831,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 +4844,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 +4865,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 +5267,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 +5360,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 +5651,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 +5683,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 +5941,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 +5967,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 +6277,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 +7270,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 +7311,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 +8345,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 +8397,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 +8528,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 +10197,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 +10255,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 +10275,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..7d92deea34f2 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 ? *noio_flags : 0);
 
 	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..482872ae7198 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6993,10 +6993,11 @@ raid5_show_stripe_size(struct mddev  *mddev, char *page)
 static ssize_t
 raid5_store_stripe_size(struct mddev  *mddev, const char *page, size_t len)
 {
 	struct r5conf *conf;
 	unsigned long new;
+	unsigned int noio_flags;
 	int err;
 	int size;
 
 	if (len >= PAGE_SIZE)
 		return -EINVAL;
@@ -7011,11 +7012,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 +7050,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 +7128,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 +7154,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 +7197,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 +7210,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 +7240,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 +8943,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 +8994,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)
-- 
2.54.0

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH v1] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume()
  2026-07-11  6:48 [PATCH v1] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume() Chen Cheng
@ 2026-07-11 18:08 ` yu kuai
  0 siblings, 0 replies; 2+ messages in thread
From: yu kuai @ 2026-07-11 18:08 UTC (permalink / raw)
  To: Chen Cheng, linux-raid; +Cc: linux-kernel, yu kuai

Hi,

在 2026/7/11 14:48, Chen Cheng 写道:
> From: Chen Cheng <chencheng@fnnas.com>
>
> 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:
>    for md-raid side:
> 	  - 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
>    for dm-raid side:
> 	  - just bypass because: dm-raid call suspend() or resume() will return to
> 		userland, so is not paired in the same task context.
>
> 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 <chencheng@fnnas.com>
> ---
>
> rfc -> v1:
>          - dm-raid side just bypass because suspend and resume not in the
>            same task context..
>
> ---
>   drivers/md/dm-raid.c       |  6 +--
>   drivers/md/md-autodetect.c |  5 ++-
>   drivers/md/md-bitmap.c     | 12 ++---
>   drivers/md/md.c            | 90 +++++++++++++++++++++++---------------
>   drivers/md/md.h            | 23 +++++-----
>   drivers/md/raid5-cache.c   | 11 +++--
>   drivers/md/raid5.c         | 24 +++++-----
>   7 files changed, 101 insertions(+), 70 deletions(-)
>
> diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
> index 8f5a5e1342a9..ce0f562c5da3 100644
> --- a/drivers/md/dm-raid.c
> +++ b/drivers/md/dm-raid.c
> @@ -3251,11 +3251,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, NULL);
>   
>   	/* Keep array frozen until resume. */
>   	md_frozen_sync_thread(&rs->md);
>   
>   	r = md_run(&rs->md);
> @@ -3863,11 +3863,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, NULL);
>   		rs->md.ro = MD_RDONLY;
>   	}
>   	clear_bit(MD_DM_SUSPENDING, &mddev->flags);
>   
>   }
> @@ -4141,11 +4141,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, 0);

Personally I don't like the coding style here.

Perhaps you can just move memalloc_noio_save() out of mddev_suspend(), so dm raid doesn't
need to change at all. And for mdraid, you can just call flags = memalloc_noio_save()
directly in mdraid, where: 1) after array suspend succeed; 2) before allocating memory,
means you don't have to do this for all the places to suspend array;

>   	}
>   }
>   
>   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..04e10769d5c6 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
> @@ -477,11 +478,16 @@ int mddev_suspend(struct mddev *mddev, bool interruptible)
>   	else
>   		mutex_lock(&mddev->suspend_mutex);
>   	if (err)
>   		return err;
>   
> +	if (noio_flags)
> +		*noio_flags = 0;
> +
>   	if (mddev->suspended) {
> +		if (noio_flags && !mddev_is_dm(mddev))
> +			*noio_flags = memalloc_noio_save();
>   		WRITE_ONCE(mddev->suspended, mddev->suspended + 1);
>   		mutex_unlock(&mddev->suspend_mutex);
>   		return 0;
>   	}
>   
> @@ -515,31 +521,31 @@ 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();
> +	if (noio_flags && !mddev_is_dm(mddev))
> +		*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 +553,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 +3743,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 +3763,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 +4056,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 +4239,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 +4418,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 +4451,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 +4831,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 +4844,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 +4865,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 +5267,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 +5360,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 +5651,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 +5683,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 +5941,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 +5967,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 +6277,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 +7270,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 +7311,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 +8345,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 +8397,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 +8528,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 +10197,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 +10255,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 +10275,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..7d92deea34f2 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 ? *noio_flags : 0);
>   
>   	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..482872ae7198 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -6993,10 +6993,11 @@ raid5_show_stripe_size(struct mddev  *mddev, char *page)
>   static ssize_t
>   raid5_store_stripe_size(struct mddev  *mddev, const char *page, size_t len)
>   {
>   	struct r5conf *conf;
>   	unsigned long new;
> +	unsigned int noio_flags;
>   	int err;
>   	int size;
>   
>   	if (len >= PAGE_SIZE)
>   		return -EINVAL;
> @@ -7011,11 +7012,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 +7050,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 +7128,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 +7154,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 +7197,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 +7210,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 +7240,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 +8943,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 +8994,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)

-- 
Thanks,
Kuai

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-07-11 18:08 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-11  6:48 [PATCH v1] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume() Chen Cheng
2026-07-11 18:08 ` yu kuai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox