From 7296004bd2ceb1284fb557399f0134361f8e34af Mon Sep 17 00:00:00 2001 From: Eric Chanudet Date: Mon, 8 Jun 2026 11:53:51 -0400 Subject: [PATCH] cgroup/dmem: accept only one region per limit write Accept only one "region value" pair entry for the dmem.max, dmem.min, dmem.low files. This changes the UAPI that otherwise accepted multiple lines for setting multiple entries in one write. No existing user is known to rely on writing multiple regions in a single write. Processing multiple regions in dmemcg_limit_write() could quietly change first limits before failing on a later one and returning an error to the writer, with no indication some changes occurred. Acked-by: Tejun Heo Signed-off-by: Eric Chanudet Reviewed-by: Maxime Ripard Reviewed-by: Thadeu Lima de Souza Cascardo Tested-by: Thadeu Lima de Souza Cascardo Reviewed-by: Natalie Vock Link: https://patch.msgid.link/20260608-cgroup-dmem-write-single-region-v2-1-b0cd6c4ccf1b@redhat.com Signed-off-by: Maarten Lankhorst --- kernel/cgroup/dmem.c | 71 ++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 45 deletions(-) diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c index 6430c7ce1e03..39930c59cb76 100644 --- a/kernel/cgroup/dmem.c +++ b/kernel/cgroup/dmem.c @@ -734,57 +734,38 @@ static ssize_t dmemcg_limit_write(struct kernfs_open_file *of, void (*apply)(struct dmem_cgroup_pool_state *, u64)) { struct dmemcg_state *dmemcs = css_to_dmemcs(of_css(of)); - int err = 0; + struct dmem_cgroup_pool_state *pool; + struct dmem_cgroup_region *region; + char *region_name; + u64 new_limit; + int err; - while (buf && !err) { - struct dmem_cgroup_pool_state *pool = NULL; - char *options, *region_name; - struct dmem_cgroup_region *region; - u64 new_limit; + buf = strstrip(buf); + region_name = strsep(&buf, " \t"); + if (!buf || !region_name[0]) + return -EINVAL; - options = buf; - buf = strchr(buf, '\n'); - if (buf) - *buf++ = '\0'; + rcu_read_lock(); + region = dmemcg_get_region_by_name(region_name); + rcu_read_unlock(); + if (!region) + return -EINVAL; - options = strstrip(options); + err = dmemcg_parse_limit(buf, &new_limit); + if (err < 0) + goto out_put; - /* eat empty lines */ - if (!options[0]) - continue; - - region_name = strsep(&options, " \t"); - if (!region_name[0]) - continue; - - if (!options || !*options) - return -EINVAL; - - rcu_read_lock(); - region = dmemcg_get_region_by_name(region_name); - rcu_read_unlock(); - - if (!region) - return -EINVAL; - - err = dmemcg_parse_limit(options, &new_limit); - if (err < 0) - goto out_put; - - pool = get_cg_pool_unlocked(dmemcs, region); - if (IS_ERR(pool)) { - err = PTR_ERR(pool); - goto out_put; - } - - /* And commit */ - apply(pool, new_limit); - dmemcg_pool_put(pool); - -out_put: - kref_put(®ion->ref, dmemcg_free_region); + pool = get_cg_pool_unlocked(dmemcs, region); + if (IS_ERR(pool)) { + err = PTR_ERR(pool); + goto out_put; } + apply(pool, new_limit); + dmemcg_pool_put(pool); + +out_put: + kref_put(®ion->ref, dmemcg_free_region); return err ?: nbytes; }