Re: [RFC PATCH] md,dm: caller-owned memalloc_noio token in mddev_suspend()/resume()
"Chen Cheng" <[email protected]> Sat, 11 Jul 2026 11:53:46 +0800
| Newsgroups | dev.linux.lists.dm-devel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-raid |
|---|---|
| Message-ID | <[email protected]> |
在 2026/7/5 21:10, yu kuai 写道: > Hi, > > 在 2026/6/24 18:13, Chen Cheng 写道: >> From: Chen Cheng <[email protected]> >> >> Save token as mddev-scoped in mddev->noio_flags cause PF_MEMALLOC_NOIO >> leak into task A, while task B restores a token that it never saved. >> >> scenario: >> >> task A mddev task B >> ====== ======= ============ >> write suspend_lo >> mddev_suspend() >> suspended == 0 >> drain active_io >> suspended = 1 >> A: noio_A = memalloc_noio_save() >> A returns with PF_MEMALLOC_NOIO set >> >> write suspend_hi >> mddev_suspend() >> suspended == 1 >> suspended = 2 >> B returns >> (no save) >> >> mddev_resume() >> suspended = 1 >> not last resume >> A returns >> A still has PF_MEMALLOC_NOIO <-- leaked >> >> mddev_resume() >> suspended = 0 >> memalloc_noio_restore(noio_A) >> (restores A's token in B) >> >> Fixed by: >> - return each caller's noio_flags from mddev_suspend() >> - pass that token back into mddev_resume() >> - update the suspend-and-lock helpers to carry the token >> - store the token in struct raid_set for dm-raid paths where suspend >> and resume are paired across callbacks >> >> Validation: >> repeatedly updates the array's suspend_lo and suspend_hi sysfs from many >> concurrent userspace workers. That makes multiple tasks to call >> mddev_suspend()/mddev_resume() concurrently. >> >> Each worker: >> - reads its initial /proc/self/stat flags and verifies that PF_MEMALLOC_NOIO is not already >> set >> - writes 0 to either suspend_lo or suspend_hi >> - immediately reads its own task flags again >> - reports success if flags & PF_MEMALLOC_NOIO is true after the write returns >> >> Link: https://github.com/chencheng-fnnas/reproducer/blob/main/repro-md-noio-token-leak.sh >> >> Fixes: 78f57ef9d50a ("md: use memalloc scope APIs in mddev_suspend()/mddev_resume()") >> >> Signed-off-by: Chen Cheng <[email protected]> >> --- >> drivers/md/dm-raid.c | 7 ++-- >> drivers/md/md-autodetect.c | 5 ++- >> drivers/md/md-bitmap.c | 12 +++--- >> drivers/md/md.c | 85 ++++++++++++++++++++++---------------- >> drivers/md/md.h | 23 ++++++----- >> drivers/md/raid5-cache.c | 11 +++-- >> drivers/md/raid5.c | 25 ++++++----- >> 7 files changed, 97 insertions(+), 71 deletions(-) >> >> diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c >> index 8f5a5e1342a9..d89207e3722a 100644 >> --- a/drivers/md/dm-raid.c >> +++ b/drivers/md/dm-raid.c >> @@ -239,10 +239,11 @@ struct raid_set { >> int raid_disks; >> int delta_disks; >> int data_offset; >> int raid10_copies; >> int requested_bitmap_chunk_sectors; >> + unsigned int suspend_noio_flags; >> >> struct mddev md; >> struct raid_type *raid_type; >> >> sector_t array_sectors; >> @@ -3251,11 +3252,11 @@ static int raid_ctr(struct dm_target *ti, unsigned int argc, char **argv) >> /* Start raid set read-only and assumed clean to change in raid_resume() */ >> rs->md.ro = MD_RDONLY; >> rs->md.in_sync = 1; >> >> /* Has to be held on running the array */ >> - mddev_suspend_and_lock_nointr(&rs->md); >> + mddev_suspend_and_lock_nointr(&rs->md, &rs->suspend_noio_flags); >> >> /* Keep array frozen until resume. */ >> md_frozen_sync_thread(&rs->md); >> >> r = md_run(&rs->md); >> @@ -3863,11 +3864,11 @@ static void raid_postsuspend(struct dm_target *ti) >> /* >> * sync_thread must be stopped during suspend, and writes have >> * to be stopped before suspending to avoid deadlocks. >> */ >> md_stop_writes(&rs->md); >> - mddev_suspend(&rs->md, false); >> + mddev_suspend(&rs->md, false, &rs->suspend_noio_flags); >> rs->md.ro = MD_RDONLY; >> } >> clear_bit(MD_DM_SUSPENDING, &mddev->flags); >> >> } >> @@ -4141,11 +4142,11 @@ static void raid_resume(struct dm_target *ti) >> lockdep_is_held(&mddev->reconfig_mutex))); >> clear_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags); >> mddev->ro = MD_RDWR; >> mddev->in_sync = 0; >> md_unfrozen_sync_thread(mddev); >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, rs->suspend_noio_flags); >> } >> } > > For mdraid, changes looks fine. However, for dm-raid, for example: > > dmsetup suspend ... > > In this case, array will be suspended while task returned to user. Take a look at > the commit to introduce memalloc_noio_save(), it's supposed to be called for mdraid > arrays to allocate memory while array is suspended, I don't see why dm-raid need it. > So I'll suggest just bypass the memalloc_noio_save() for dm-raid where mddev_suspend() > is not paired with mddev_resume() in the same task context. > Hi Kuai. I check the commit you mentioned., and I truelly misused memalloc_noio_save() in dmraid side, really thanks for point-out. thanks, cheng. >> >> static struct target_type raid_target = { >> .name = "raid", >> diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c >> index 4b80165afd23..58e062cd0580 100644 >> --- a/drivers/md/md-autodetect.c >> +++ b/drivers/md/md-autodetect.c >> @@ -126,10 +126,11 @@ static void __init md_setup_drive(struct md_setup_args *args) >> dev_t devices[MD_SB_DISKS + 1], mdev; >> struct mdu_array_info_s ainfo = { }; >> struct mddev *mddev; >> int err = 0, i; >> char name[16]; >> + unsigned int noio_flags; >> >> if (args->partitioned) { >> mdev = MKDEV(mdp_major, args->minor << MdpMinorShift); >> sprintf(name, "md_d%d", args->minor); >> } else { >> @@ -173,11 +174,11 @@ static void __init md_setup_drive(struct md_setup_args *args) >> if (IS_ERR(mddev)) { >> pr_err("md: md_alloc failed - cannot start array %s\n", name); >> return; >> } >> >> - err = mddev_suspend_and_lock(mddev); >> + err = mddev_suspend_and_lock(mddev, &noio_flags); >> if (err) { >> pr_err("md: failed to lock array %s\n", name); >> goto out_mddev_put; >> } >> >> @@ -219,11 +220,11 @@ static void __init md_setup_drive(struct md_setup_args *args) >> if (!err) >> err = do_md_run(mddev); >> if (err) >> pr_warn("md: starting %s failed\n", name); >> out_unlock: >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> out_mddev_put: >> mddev_put(mddev); >> } >> >> static int __init raid_setup(char *str) >> diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c >> index 028b9ca8ce52..74b7f569a3f4 100644 >> --- a/drivers/md/md-bitmap.c >> +++ b/drivers/md/md-bitmap.c >> @@ -2620,13 +2620,14 @@ location_show(struct mddev *mddev, char *page) >> } >> >> static ssize_t >> location_store(struct mddev *mddev, const char *buf, size_t len) >> { >> + unsigned int noio_flags; >> int rv; >> >> - rv = mddev_suspend_and_lock(mddev); >> + rv = mddev_suspend_and_lock(mddev, &noio_flags); >> if (rv) >> return rv; >> >> if (mddev->pers) { >> if (mddev->recovery || mddev->sync_thread) { >> @@ -2711,11 +2712,11 @@ location_store(struct mddev *mddev, const char *buf, size_t len) >> set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags); >> md_wakeup_thread(mddev->thread); >> } >> rv = 0; >> out: >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> if (rv) >> return rv; >> return len; >> >> merge_err: >> @@ -2831,17 +2832,18 @@ backlog_store(struct mddev *mddev, const char *buf, size_t len) >> { >> unsigned long backlog; >> unsigned long old_mwb = mddev->bitmap_info.max_write_behind; >> struct md_rdev *rdev; >> bool has_write_mostly = false; >> + unsigned int noio_flags; >> int rv = kstrtoul(buf, 10, &backlog); >> if (rv) >> return rv; >> if (backlog > COUNTER_MAX) >> return -EINVAL; >> >> - rv = mddev_suspend_and_lock(mddev); >> + rv = mddev_suspend_and_lock(mddev, &noio_flags); >> if (rv) >> return rv; >> >> /* >> * Without write mostly device, it doesn't make sense to set >> @@ -2854,11 +2856,11 @@ backlog_store(struct mddev *mddev, const char *buf, size_t len) >> } >> } >> if (!has_write_mostly) { >> pr_warn_ratelimited("%s: can't set backlog, no write mostly device available\n", >> mdname(mddev)); >> - mddev_unlock(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return -EINVAL; >> } >> >> mddev->bitmap_info.max_write_behind = backlog; >> if (!backlog && mddev->serial_info_pool) { >> @@ -2871,11 +2873,11 @@ backlog_store(struct mddev *mddev, const char *buf, size_t len) >> mddev_create_serial_pool(mddev, rdev); >> } >> if (old_mwb != backlog) >> bitmap_update_sb(mddev->bitmap); >> >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return len; >> } >> >> static struct md_sysfs_entry bitmap_backlog = >> __ATTR(backlog, S_IRUGO|S_IWUSR, backlog_show, backlog_store); >> diff --git a/drivers/md/md.c b/drivers/md/md.c >> index 1377c407614c..86d938dee50a 100644 >> --- a/drivers/md/md.c >> +++ b/drivers/md/md.c >> @@ -459,11 +459,12 @@ static void md_submit_bio(struct bio *bio) >> >> /* >> * Make sure no new requests are submitted to the device, and any requests that >> * have been submitted are completely handled. >> */ >> -int mddev_suspend(struct mddev *mddev, bool interruptible) >> +int mddev_suspend(struct mddev *mddev, bool interruptible, >> + unsigned int *noio_flags) >> { >> int err = 0; >> >> /* >> * hold reconfig_mutex to wait for normal io will deadlock, because >> @@ -478,10 +479,11 @@ int mddev_suspend(struct mddev *mddev, bool interruptible) >> mutex_lock(&mddev->suspend_mutex); >> if (err) >> return err; >> >> if (mddev->suspended) { >> + *noio_flags = memalloc_noio_save(); >> WRITE_ONCE(mddev->suspended, mddev->suspended + 1); >> mutex_unlock(&mddev->suspend_mutex); >> return 0; >> } >> >> @@ -515,31 +517,30 @@ int mddev_suspend(struct mddev *mddev, bool interruptible) >> * prevent deadlock. >> */ >> WRITE_ONCE(mddev->suspended, mddev->suspended + 1); >> >> /* restrict memory reclaim I/O during raid array is suspend */ >> - mddev->noio_flag = memalloc_noio_save(); >> + *noio_flags = memalloc_noio_save(); >> >> mutex_unlock(&mddev->suspend_mutex); >> return 0; >> } >> EXPORT_SYMBOL_GPL(mddev_suspend); >> >> -static void __mddev_resume(struct mddev *mddev, bool recovery_needed) >> +static void __mddev_resume(struct mddev *mddev, bool recovery_needed, >> + unsigned int noio_flags) >> { >> lockdep_assert_not_held(&mddev->reconfig_mutex); >> >> mutex_lock(&mddev->suspend_mutex); >> + memalloc_noio_restore(noio_flags); >> WRITE_ONCE(mddev->suspended, mddev->suspended - 1); >> if (mddev->suspended) { >> mutex_unlock(&mddev->suspend_mutex); >> return; >> } >> >> - /* entred the memalloc scope from mddev_suspend() */ >> - memalloc_noio_restore(mddev->noio_flag); >> - >> percpu_ref_resurrect(&mddev->active_io); >> wake_up(&mddev->sb_wait); >> >> if (recovery_needed) >> set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); >> @@ -547,13 +548,13 @@ static void __mddev_resume(struct mddev *mddev, bool recovery_needed) >> md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */ >> >> mutex_unlock(&mddev->suspend_mutex); >> } >> >> -void mddev_resume(struct mddev *mddev) >> +void mddev_resume(struct mddev *mddev, unsigned int noio_flags) >> { >> - return __mddev_resume(mddev, true); >> + return __mddev_resume(mddev, true, noio_flags); >> } >> EXPORT_SYMBOL_GPL(mddev_resume); >> >> /* sync bdev before setting device to readonly or stopping raid*/ >> static int mddev_set_closing_and_sync_blockdev(struct mddev *mddev, int opener_num) >> @@ -3737,10 +3738,11 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr, >> { >> struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr); >> struct md_rdev *rdev = container_of(kobj, struct md_rdev, kobj); >> struct kernfs_node *kn = NULL; >> bool suspend = false; >> + unsigned int noio_flags = 0; >> ssize_t rv; >> struct mddev *mddev = READ_ONCE(rdev->mddev); >> >> if (!entry->store) >> return -EIO; >> @@ -3756,17 +3758,17 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr, >> cmd_match(page, "writemostly") || >> cmd_match(page, "-writemostly")) >> suspend = true; >> } >> >> - rv = suspend ? mddev_suspend_and_lock(mddev) : mddev_lock(mddev); >> + rv = suspend ? mddev_suspend_and_lock(mddev, &noio_flags) : mddev_lock(mddev); >> if (!rv) { >> if (rdev->mddev == NULL) >> rv = -ENODEV; >> else >> rv = entry->store(rdev, page, length); >> - suspend ? mddev_unlock_and_resume(mddev) : mddev_unlock(mddev); >> + suspend ? mddev_unlock_and_resume(mddev, noio_flags) : mddev_unlock(mddev); >> } >> >> if (kn) >> sysfs_unbreak_active_protection(kn); >> >> @@ -4049,15 +4051,16 @@ level_store(struct mddev *mddev, const char *buf, size_t len) >> size_t slen = len; >> struct md_personality *pers, *oldpers; >> long level; >> void *priv, *oldpriv; >> struct md_rdev *rdev; >> + unsigned int noio_flags; >> >> if (slen == 0 || slen >= sizeof(clevel)) >> return -EINVAL; >> >> - rv = mddev_suspend_and_lock(mddev); >> + rv = mddev_suspend_and_lock(mddev, &noio_flags); >> if (rv) >> return rv; >> >> if (mddev->pers == NULL) { >> memcpy(mddev->clevel, buf, slen); >> @@ -4231,11 +4234,11 @@ level_store(struct mddev *mddev, const char *buf, size_t len) >> md_update_sb(mddev, 1); >> sysfs_notify_dirent_safe(mddev->sysfs_level); >> md_new_event(); >> rv = len; >> out_unlock: >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return rv; >> } >> >> static struct md_sysfs_entry md_level = >> __ATTR(level, S_IRUGO|S_IWUSR, level_show, level_store); >> @@ -4410,17 +4413,18 @@ static int update_raid_disks(struct mddev *mddev, int raid_disks); >> >> static ssize_t >> raid_disks_store(struct mddev *mddev, const char *buf, size_t len) >> { >> unsigned int n; >> + unsigned int noio_flags; >> int err; >> >> err = kstrtouint(buf, 10, &n); >> if (err < 0) >> return err; >> >> - err = mddev_suspend_and_lock(mddev); >> + err = mddev_suspend_and_lock(mddev, &noio_flags); >> if (err) >> return err; >> if (mddev->pers) { >> if (n != mddev->raid_disks) >> err = update_raid_disks(mddev, n); >> @@ -4442,11 +4446,11 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len) >> mddev->raid_disks = n; >> mddev->reshape_backwards = (mddev->delta_disks < 0); >> } else >> mddev->raid_disks = n; >> out_unlock: >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return err ? err : len; >> } >> static struct md_sysfs_entry md_raid_disks = >> __ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store); >> >> @@ -4822,10 +4826,11 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len) >> char *e; >> int major = simple_strtoul(buf, &e, 10); >> int minor; >> dev_t dev; >> struct md_rdev *rdev; >> + unsigned int noio_flags; >> int err; >> >> if (!*buf || *e != ':' || !e[1] || e[1] == '\n') >> return -EINVAL; >> minor = simple_strtoul(e+1, &e, 10); >> @@ -4834,11 +4839,11 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len) >> dev = MKDEV(major, minor); >> if (major != MAJOR(dev) || >> minor != MINOR(dev)) >> return -EOVERFLOW; >> >> - err = mddev_suspend_and_lock(mddev); >> + err = mddev_suspend_and_lock(mddev, &noio_flags); >> if (err) >> return err; >> if (mddev->persistent) { >> rdev = md_import_device(dev, mddev->major_version, >> mddev->minor_version); >> @@ -4855,18 +4860,18 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len) >> rdev = md_import_device(dev, -2, -1); >> else >> rdev = md_import_device(dev, -1, -1); >> >> if (IS_ERR(rdev)) { >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return PTR_ERR(rdev); >> } >> err = bind_rdev_to_array(rdev, mddev); >> out: >> if (err) >> export_rdev(rdev); >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> if (!err) >> md_new_event(); >> return err ? err : len; >> } >> >> @@ -5257,28 +5262,29 @@ static int mddev_start_reshape(struct mddev *mddev) >> static ssize_t >> action_store(struct mddev *mddev, const char *page, size_t len) >> { >> int ret; >> enum sync_action action; >> + unsigned int noio_flags = 0; >> >> if (!mddev->pers || !mddev->pers->sync_request) >> return -EINVAL; >> >> action = md_sync_action_by_name(page); >> retry: >> if (work_busy(&mddev->sync_work)) >> flush_work(&mddev->sync_work); >> >> ret = (action == ACTION_RESHAPE) ? >> - mddev_suspend_and_lock(mddev) : >> + mddev_suspend_and_lock(mddev, &noio_flags) : >> mddev_lock(mddev); >> if (ret) >> return ret; >> >> if (work_busy(&mddev->sync_work)) { >> if (action == ACTION_RESHAPE) >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> else >> mddev_unlock(mddev); >> goto retry; >> } >> >> @@ -5349,11 +5355,11 @@ action_store(struct mddev *mddev, const char *page, size_t len) >> sysfs_notify_dirent_safe(mddev->sysfs_action); >> ret = len; >> >> out: >> if (action == ACTION_RESHAPE) >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> else >> mddev_unlock(mddev); >> return ret; >> } >> >> @@ -5640,24 +5646,25 @@ suspend_lo_show(struct mddev *mddev, char *page) >> >> static ssize_t >> suspend_lo_store(struct mddev *mddev, const char *buf, size_t len) >> { >> unsigned long long new; >> + unsigned int noio_flags; >> int err; >> >> err = kstrtoull(buf, 10, &new); >> if (err < 0) >> return err; >> if (new != (sector_t)new) >> return -EINVAL; >> >> - err = mddev_suspend(mddev, true); >> + err = mddev_suspend(mddev, true, &noio_flags); >> if (err) >> return err; >> >> WRITE_ONCE(mddev->suspend_lo, new); >> - mddev_resume(mddev); >> + mddev_resume(mddev, noio_flags); >> >> return len; >> } >> static struct md_sysfs_entry md_suspend_lo = >> __ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store); >> @@ -5671,24 +5678,25 @@ suspend_hi_show(struct mddev *mddev, char *page) >> >> static ssize_t >> suspend_hi_store(struct mddev *mddev, const char *buf, size_t len) >> { >> unsigned long long new; >> + unsigned int noio_flags; >> int err; >> >> err = kstrtoull(buf, 10, &new); >> if (err < 0) >> return err; >> if (new != (sector_t)new) >> return -EINVAL; >> >> - err = mddev_suspend(mddev, true); >> + err = mddev_suspend(mddev, true, &noio_flags); >> if (err) >> return err; >> >> WRITE_ONCE(mddev->suspend_hi, new); >> - mddev_resume(mddev); >> + mddev_resume(mddev, noio_flags); >> >> return len; >> } >> static struct md_sysfs_entry md_suspend_hi = >> __ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store); >> @@ -5928,19 +5936,20 @@ static ssize_t serialize_policy_show(struct mddev *mddev, char *page) >> static ssize_t >> serialize_policy_store(struct mddev *mddev, const char *buf, size_t len) >> { >> int err; >> bool value; >> + unsigned int noio_flags; >> >> err = kstrtobool(buf, &value); >> if (err) >> return err; >> >> if (value == test_bit(MD_SERIALIZE_POLICY, &mddev->flags)) >> return len; >> >> - err = mddev_suspend_and_lock(mddev); >> + err = mddev_suspend_and_lock(mddev, &noio_flags); >> if (err) >> return err; >> if (mddev->pers == NULL || (mddev->pers->head.id != ID_RAID1)) { >> pr_err("md: serialize_policy is only effective for raid1\n"); >> err = -EINVAL; >> @@ -5953,11 +5962,11 @@ serialize_policy_store(struct mddev *mddev, const char *buf, size_t len) >> } else { >> mddev_destroy_serial_pool(mddev, NULL); >> clear_bit(MD_SERIALIZE_POLICY, &mddev->flags); >> } >> unlock: >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return err ?: len; >> } >> >> static struct md_sysfs_entry md_serialize_policy = >> __ATTR(serialize_policy, S_IRUGO | S_IWUSR, serialize_policy_show, >> @@ -6263,21 +6272,22 @@ EXPORT_SYMBOL_GPL(mddev_stack_new_rdev); >> >> /* update the optimal I/O size after a reshape */ >> void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes) >> { >> struct queue_limits lim; >> + unsigned int noio_flags; >> >> if (mddev_is_dm(mddev)) >> return; >> >> /* don't bother updating io_opt if we can't suspend the array */ >> - if (mddev_suspend(mddev, false) < 0) >> + if (mddev_suspend(mddev, false, &noio_flags) < 0) >> return; >> lim = queue_limits_start_update(mddev->gendisk->queue); >> lim.io_opt = lim.io_min * nr_stripes; >> queue_limits_commit_update(mddev->gendisk->queue, &lim); >> - mddev_resume(mddev); >> + mddev_resume(mddev, noio_flags); >> } >> EXPORT_SYMBOL_GPL(mddev_update_io_opt); >> >> static void mddev_delayed_delete(struct work_struct *ws) >> { >> @@ -7255,10 +7265,11 @@ static void autorun_array(struct mddev *mddev) >> */ >> static void autorun_devices(int part) >> { >> struct md_rdev *rdev0, *rdev, *tmp; >> struct mddev *mddev; >> + unsigned int noio_flags; >> >> pr_info("md: autorun ...\n"); >> while (!list_empty(&pending_raid_disks)) { >> int unit; >> dev_t dev; >> @@ -7295,27 +7306,27 @@ static void autorun_devices(int part) >> >> mddev = md_alloc(dev, NULL); >> if (IS_ERR(mddev)) >> break; >> >> - if (mddev_suspend_and_lock(mddev)) >> + if (mddev_suspend_and_lock(mddev, &noio_flags)) >> pr_warn("md: %s locked, cannot run\n", mdname(mddev)); >> else if (mddev->raid_disks || mddev->major_version >> || !list_empty(&mddev->disks)) { >> pr_warn("md: %s already running, cannot run %pg\n", >> mdname(mddev), rdev0->bdev); >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> } else { >> pr_debug("md: created %s\n", mdname(mddev)); >> mddev->persistent = 1; >> rdev_for_each_list(rdev, tmp, &candidates) { >> list_del_init(&rdev->same_set); >> if (bind_rdev_to_array(rdev, mddev)) >> export_rdev(rdev); >> } >> autorun_array(mddev); >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> } >> /* on success, candidates will be empty, on error >> * it won't... >> */ >> rdev_for_each_list(rdev, tmp, &candidates) { >> @@ -8329,10 +8340,11 @@ static int __md_set_array_info(struct mddev *mddev, void __user *argp) >> >> static int md_ioctl(struct block_device *bdev, blk_mode_t mode, >> unsigned int cmd, unsigned long arg) >> { >> int err = 0; >> + unsigned int noio_flags = 0; >> void __user *argp = (void __user *)arg; >> struct mddev *mddev = NULL; >> >> err = md_ioctl_valid(cmd); >> if (err) >> @@ -8380,11 +8392,11 @@ static int md_ioctl(struct block_device *bdev, blk_mode_t mode, >> } >> >> if (!md_is_rdwr(mddev)) >> flush_work(&mddev->sync_work); >> >> - err = md_ioctl_need_suspend(cmd) ? mddev_suspend_and_lock(mddev) : >> + err = md_ioctl_need_suspend(cmd) ? mddev_suspend_and_lock(mddev, &noio_flags) : >> mddev_lock(mddev); >> if (err) { >> pr_debug("md: ioctl lock interrupted, reason %d, cmd %d\n", >> err, cmd); >> goto out; >> @@ -8511,11 +8523,11 @@ static int md_ioctl(struct block_device *bdev, blk_mode_t mode, >> unlock: >> if (mddev->hold_active == UNTIL_IOCTL && >> err != -EINVAL) >> mddev->hold_active = 0; >> >> - md_ioctl_need_suspend(cmd) ? mddev_unlock_and_resume(mddev) : >> + md_ioctl_need_suspend(cmd) ? mddev_unlock_and_resume(mddev, noio_flags) : >> mddev_unlock(mddev); >> >> out: >> if (cmd == STOP_ARRAY_RO || (err && cmd == STOP_ARRAY)) >> clear_bit(MD_CLOSING, &mddev->flags); >> @@ -10180,20 +10192,21 @@ static bool md_choose_sync_action(struct mddev *mddev, int *spares) >> static void md_start_sync(struct work_struct *ws) >> { >> struct mddev *mddev = container_of(ws, struct mddev, sync_work); >> int spares = 0; >> bool suspend = false; >> + unsigned int noio_flags = 0; >> char *name; >> >> /* >> * If reshape is still in progress, spares won't be added or removed >> * from conf until reshape is done. >> */ >> if (mddev->reshape_position == MaxSector && >> md_spares_need_change(mddev)) { >> suspend = true; >> - mddev_suspend(mddev, false); >> + mddev_suspend(mddev, false, &noio_flags); >> } >> >> mddev_lock_nointr(mddev); >> if (!md_is_rdwr(mddev)) { >> /* >> @@ -10237,11 +10250,11 @@ static void md_start_sync(struct work_struct *ws) >> * not set it again. Otherwise, we may cause issue like this one: >> * https://bugzilla.kernel.org/show_bug.cgi?id=218200 >> * Therefore, use __mddev_resume(mddev, false). >> */ >> if (suspend) >> - __mddev_resume(mddev, false); >> + __mddev_resume(mddev, false, noio_flags); >> md_wakeup_thread(mddev->sync_thread); >> sysfs_notify_dirent_safe(mddev->sysfs_action); >> md_new_event(); >> return; >> >> @@ -10257,11 +10270,11 @@ static void md_start_sync(struct work_struct *ws) >> * not set it again. Otherwise, we may cause issue like this one: >> * https://bugzilla.kernel.org/show_bug.cgi?id=218200 >> * Therefore, use __mddev_resume(mddev, false). >> */ >> if (suspend) >> - __mddev_resume(mddev, false); >> + __mddev_resume(mddev, false, noio_flags); >> >> wake_up(&resync_wait); >> if (test_and_clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery) && >> mddev->sysfs_action) >> sysfs_notify_dirent_safe(mddev->sysfs_action); >> diff --git a/drivers/md/md.h b/drivers/md/md.h >> index d8daf0f75cbb..3337cd21eb30 100644 >> --- a/drivers/md/md.h >> +++ b/drivers/md/md.h >> @@ -619,11 +619,10 @@ struct mddev { >> mempool_t *serial_info_pool; >> void (*sync_super)(struct mddev *mddev, struct md_rdev *rdev); >> struct md_cluster_info *cluster_info; >> struct md_cluster_operations *cluster_ops; >> unsigned int good_device_nr; /* good device num within cluster raid */ >> - unsigned int noio_flag; /* for memalloc scope API */ >> >> /* >> * Temporarily store rdev that will be finally removed when >> * reconfig_mutex is unlocked, protected by reconfig_mutex. >> */ >> @@ -953,12 +952,13 @@ extern void md_stop(struct mddev *mddev); >> extern void md_stop_writes(struct mddev *mddev); >> extern int md_rdev_init(struct md_rdev *rdev); >> extern void md_rdev_clear(struct md_rdev *rdev); >> >> extern bool md_handle_request(struct mddev *mddev, struct bio *bio); >> -extern int mddev_suspend(struct mddev *mddev, bool interruptible); >> -extern void mddev_resume(struct mddev *mddev); >> +extern int mddev_suspend(struct mddev *mddev, bool interruptible, >> + unsigned int *noio_flags); >> +extern void mddev_resume(struct mddev *mddev, unsigned int noio_flags); >> extern void md_idle_sync_thread(struct mddev *mddev); >> extern void md_frozen_sync_thread(struct mddev *mddev); >> extern void md_unfrozen_sync_thread(struct mddev *mddev); >> >> extern void md_update_sb(struct mddev *mddev, int force); >> @@ -999,35 +999,38 @@ static inline void mddev_check_write_zeroes(struct mddev *mddev, struct bio *bio >> if (bio_op(bio) == REQ_OP_WRITE_ZEROES && >> !bio->bi_bdev->bd_disk->queue->limits.max_write_zeroes_sectors) >> mddev->gendisk->queue->limits.max_write_zeroes_sectors = 0; >> } >> >> -static inline int mddev_suspend_and_lock(struct mddev *mddev) >> +static inline int mddev_suspend_and_lock(struct mddev *mddev, >> + unsigned int *noio_flags) >> { >> int ret; >> >> - ret = mddev_suspend(mddev, true); >> + ret = mddev_suspend(mddev, true, noio_flags); >> if (ret) >> return ret; >> >> ret = mddev_lock(mddev); >> if (ret) >> - mddev_resume(mddev); >> + mddev_resume(mddev, *noio_flags); >> >> return ret; >> } >> >> -static inline void mddev_suspend_and_lock_nointr(struct mddev *mddev) >> +static inline void mddev_suspend_and_lock_nointr(struct mddev *mddev, >> + unsigned int *noio_flags) >> { >> - mddev_suspend(mddev, false); >> + mddev_suspend(mddev, false, noio_flags); >> mddev_lock_nointr(mddev); >> } >> >> -static inline void mddev_unlock_and_resume(struct mddev *mddev) >> +static inline void mddev_unlock_and_resume(struct mddev *mddev, >> + unsigned int noio_flags) >> { >> mddev_unlock(mddev); >> - mddev_resume(mddev); >> + mddev_resume(mddev, noio_flags); >> } >> >> struct mdu_array_info_s; >> struct mdu_disk_info_s; >> >> diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c >> index 7b7546bfa21f..6f8e3a624456 100644 >> --- a/drivers/md/raid5-cache.c >> +++ b/drivers/md/raid5-cache.c >> @@ -693,13 +693,15 @@ static void r5c_disable_writeback_async(struct work_struct *work) >> !READ_ONCE(conf->log) || >> !test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags)); >> >> log = READ_ONCE(conf->log); >> if (log) { >> - mddev_suspend(mddev, false); >> + unsigned int noio_flags; >> + >> + mddev_suspend(mddev, false, &noio_flags); >> log->r5c_journal_mode = R5C_JOURNAL_MODE_WRITE_THROUGH; >> - mddev_resume(mddev); >> + mddev_resume(mddev, noio_flags); >> } >> } >> >> static void r5l_submit_current_io(struct r5l_log *log) >> { >> @@ -2603,10 +2605,11 @@ EXPORT_SYMBOL(r5c_journal_mode_set); >> static ssize_t r5c_journal_mode_store(struct mddev *mddev, >> const char *page, size_t length) >> { >> int mode = ARRAY_SIZE(r5c_journal_mode_str); >> size_t len = length; >> + unsigned int noio_flags; >> int ret; >> >> if (len < 2) >> return -EINVAL; >> >> @@ -2615,15 +2618,15 @@ static ssize_t r5c_journal_mode_store(struct mddev *mddev, >> >> while (mode--) >> if (strlen(r5c_journal_mode_str[mode]) == len && >> !strncmp(page, r5c_journal_mode_str[mode], len)) >> break; >> - ret = mddev_suspend_and_lock(mddev); >> + ret = mddev_suspend_and_lock(mddev, &noio_flags); >> if (ret) >> return ret; >> ret = r5c_journal_mode_set(mddev, mode); >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return ret ?: length; >> } >> >> struct md_sysfs_entry >> r5c_journal_mode = __ATTR(journal_mode, 0644, >> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c >> index 65ae7d8930fc..6062c4b62cc8 100644 >> --- a/drivers/md/raid5.c >> +++ b/drivers/md/raid5.c >> @@ -6992,11 +6992,11 @@ raid5_show_stripe_size(struct mddev *mddev, char *page) >> #if PAGE_SIZE != DEFAULT_STRIPE_SIZE >> static ssize_t >> raid5_store_stripe_size(struct mddev *mddev, const char *page, size_t len) >> { >> struct r5conf *conf; >> - unsigned long new; >> + unsigned long new, noio_flags; >> int err; >> int size; >> >> if (len >= PAGE_SIZE) >> return -EINVAL; >> @@ -7011,11 +7011,11 @@ raid5_store_stripe_size(struct mddev *mddev, const char *page, size_t len) >> if (new % DEFAULT_STRIPE_SIZE != 0 || >> new > PAGE_SIZE || new == 0 || >> new != roundup_pow_of_two(new)) >> return -EINVAL; >> >> - err = mddev_suspend_and_lock(mddev); >> + err = mddev_suspend_and_lock(mddev, &noio_flags); >> if (err) >> return err; >> >> conf = mddev->private; >> if (!conf) { >> @@ -7049,11 +7049,11 @@ raid5_store_stripe_size(struct mddev *mddev, const char *page, size_t len) >> err = -ENOMEM; >> } >> mutex_unlock(&conf->cache_size_mutex); >> >> out_unlock: >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return err ?: len; >> } >> >> static struct md_sysfs_entry >> raid5_stripe_size = __ATTR(stripe_size, 0644, >> @@ -7127,19 +7127,20 @@ raid5_show_skip_copy(struct mddev *mddev, char *page) >> static ssize_t >> raid5_store_skip_copy(struct mddev *mddev, const char *page, size_t len) >> { >> struct r5conf *conf; >> unsigned long new; >> + unsigned int noio_flags; >> int err; >> >> if (len >= PAGE_SIZE) >> return -EINVAL; >> if (kstrtoul(page, 10, &new)) >> return -EINVAL; >> new = !!new; >> >> - err = mddev_suspend_and_lock(mddev); >> + err = mddev_suspend_and_lock(mddev, &noio_flags); >> if (err) >> return err; >> conf = mddev->private; >> if (!conf) >> err = -ENODEV; >> @@ -7152,11 +7153,11 @@ raid5_store_skip_copy(struct mddev *mddev, const char *page, size_t len) >> lim.features |= BLK_FEAT_STABLE_WRITES; >> else >> lim.features &= ~BLK_FEAT_STABLE_WRITES; >> err = queue_limits_commit_update(q, &lim); >> } >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return err ?: len; >> } >> >> static struct md_sysfs_entry >> raid5_skip_copy = __ATTR(skip_copy, S_IRUGO | S_IWUSR, >> @@ -7195,10 +7196,11 @@ static int alloc_thread_groups(struct r5conf *conf, int cnt, >> static ssize_t >> raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len) >> { >> struct r5conf *conf; >> unsigned int new; >> + unsigned int noio_flags; >> int err; >> struct r5worker_group *new_groups, *old_groups; >> int group_cnt; >> >> if (len >= PAGE_SIZE) >> @@ -7207,16 +7209,16 @@ raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len) >> return -EINVAL; >> /* 8192 should be big enough */ >> if (new > 8192) >> return -EINVAL; >> >> - err = mddev_suspend_and_lock(mddev); >> + err = mddev_suspend_and_lock(mddev, &noio_flags); >> if (err) >> return err; >> conf = mddev->private; >> if (!conf) { >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return -ENODEV; >> } >> raid5_quiesce(mddev, true); >> >> if (new != conf->worker_cnt_per_group) { >> @@ -7237,11 +7239,11 @@ raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len) >> kfree(old_groups); >> } >> } >> >> raid5_quiesce(mddev, false); >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> >> return err ?: len; >> } >> >> static struct md_sysfs_entry >> @@ -8940,18 +8942,19 @@ static void *raid6_takeover(struct mddev *mddev) >> } >> >> static int raid5_change_consistency_policy(struct mddev *mddev, const char *buf) >> { >> struct r5conf *conf; >> + unsigned int noio_flags; >> int err; >> >> - err = mddev_suspend_and_lock(mddev); >> + err = mddev_suspend_and_lock(mddev, &noio_flags); >> if (err) >> return err; >> conf = mddev->private; >> if (!conf) { >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> return -ENODEV; >> } >> >> if (strncmp(buf, "ppl", 3) == 0) { >> /* ppl only works with RAID 5 */ >> @@ -8990,11 +8993,11 @@ static int raid5_change_consistency_policy(struct mddev *mddev, const char *buf) >> } >> >> if (!err) >> md_update_sb(mddev, 1); >> >> - mddev_unlock_and_resume(mddev); >> + mddev_unlock_and_resume(mddev, noio_flags); >> >> return err; >> } >> >> static int raid5_start(struct mddev *mddev) >