* [RFC PATCH] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume()
@ 2026-06-24 10:13 Chen Cheng
2026-07-05 13:10 ` yu kuai
0 siblings, 1 reply; 3+ messages in thread
From: Chen Cheng @ 2026-06-24 10:13 UTC (permalink / raw)
To: linux-raid, yukuai, dm-devel; +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:
- 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 <chencheng@fnnas.com>
---
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);
}
}
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)
--
2.54.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [RFC PATCH] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume()
2026-06-24 10:13 [RFC PATCH] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume() Chen Cheng
@ 2026-07-05 13:10 ` yu kuai
2026-07-11 3:53 ` Chen Cheng
0 siblings, 1 reply; 3+ messages in thread
From: yu kuai @ 2026-07-05 13:10 UTC (permalink / raw)
To: Chen Cheng, linux-raid, dm-devel, yukuai; +Cc: linux-kernel
Hi,
在 2026/6/24 18:13, 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:
> - 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 <chencheng@fnnas.com>
> ---
> 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.
>
> 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)
--
Thanks,
Kuai
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [RFC PATCH] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume()
2026-07-05 13:10 ` yu kuai
@ 2026-07-11 3:53 ` Chen Cheng
0 siblings, 0 replies; 3+ messages in thread
From: Chen Cheng @ 2026-07-11 3:53 UTC (permalink / raw)
To: yukuai, linux-raid, dm-devel; +Cc: linux-kernel
在 2026/7/5 21:10, yu kuai 写道:
> Hi,
>
> 在 2026/6/24 18:13, 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:
>> - 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 <chencheng@fnnas.com>
>> ---
>> 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)
>
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-11 3:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-24 10:13 [RFC PATCH] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume() Chen Cheng
2026-07-05 13:10 ` yu kuai
2026-07-11 3:53 ` Chen Cheng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox