From 80e9a56e75f2077020b47436f5dfae5b1b19b9ce Mon Sep 17 00:00:00 2001 From: Wenwu Hou Date: Mon, 13 Apr 2026 10:38:12 +0800 Subject: [PATCH] anolis: memcg: fix deadlock in memory.wmark_min_adj ANBZ: #33093 The lock ordering in memory_wmark_min_adj_write is incorrect, which can lead to a deadlock with other paths. For example: memory_wmark_min_adj_write: kernfs_get_active -> cgroup_mutex cgroup_rmdir: cgroup_mutex -> wait for kernfs_node deactivation Fix this by avoiding nesting cgroup_mutex under kernfs active protection. See the comment on cgroup_kn_lock_live() for details. Fixes: 16c4840e40dc ("anolis: memcg: Introduce memory.wmark_min_adj") Signed-off-by: Wenwu Hou --- include/linux/cgroup.h | 2 ++ kernel/cgroup/cgroup-internal.h | 2 -- mm/memcontrol.c | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c2ad120281e8..8d326442cda3 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -575,6 +575,8 @@ static inline struct cftype *of_cft(struct kernfs_open_file *of) } struct cgroup_subsys_state *of_css(struct kernfs_open_file *of); +struct cgroup *cgroup_kn_lock_live(struct kernfs_node *kn, bool drain_offline); +void cgroup_kn_unlock(struct kernfs_node *kn); /* cft/css accessors for cftype->seq_*() operations */ static inline struct cftype *seq_cft(struct seq_file *seq) diff --git a/kernel/cgroup/cgroup-internal.h b/kernel/cgroup/cgroup-internal.h index 9d4a477bbd37..dcd116f04174 100644 --- a/kernel/cgroup/cgroup-internal.h +++ b/kernel/cgroup/cgroup-internal.h @@ -227,8 +227,6 @@ struct cgroup *kn_priv(struct kernfs_node *kn); struct cgroup_root *cgroup_root_from_kf(struct kernfs_root *kf_root); struct cgroup *task_cgroup_from_root(struct task_struct *task, struct cgroup_root *root); -struct cgroup *cgroup_kn_lock_live(struct kernfs_node *kn, bool drain_offline); -void cgroup_kn_unlock(struct kernfs_node *kn); int cgroup_path_ns_locked(struct cgroup *cgrp, char *buf, size_t buflen, struct cgroup_namespace *ns); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 55bbddf61cfc..e59d38163162 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -5277,12 +5277,14 @@ static ssize_t memory_wmark_scale_factor_write(struct kernfs_open_file *of, * wmark_min_adj: A -10, B -25, C 0, D 50, E -25, F 50 * wmark_min_eadj: A -10, B -10, C 0, D 50, E -10, F 50 */ -static void memcg_update_wmark_min_adj(struct mem_cgroup *memcg, int val) +static void memcg_update_wmark_min_adj_locked(struct mem_cgroup *memcg, + int val) { struct mem_cgroup *p; struct mem_cgroup *iter; - mutex_lock(&cgroup_mutex); + lockdep_assert_held(&cgroup_mutex); + memcg->wmark_min_adj = val; /* update hierarchical wmark_min_eadj, pre-order iteration */ for_each_mem_cgroup_tree(iter, memcg) { @@ -5294,7 +5296,6 @@ static void memcg_update_wmark_min_adj(struct mem_cgroup *memcg, int val) val = p->wmark_min_eadj; iter->wmark_min_eadj = val; } - mutex_unlock(&cgroup_mutex); } static int memory_wmark_min_adj_show(struct seq_file *m, void *v) @@ -5310,7 +5311,8 @@ static int memory_wmark_min_adj_show(struct seq_file *m, void *v) static ssize_t memory_wmark_min_adj_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { - struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); + struct cgroup *cgrp; + struct mem_cgroup *memcg; int ret, wmark_min_adj; buf = strstrip(buf); @@ -5321,8 +5323,14 @@ static ssize_t memory_wmark_min_adj_write(struct kernfs_open_file *of, if (wmark_min_adj < -25 || wmark_min_adj > 50) return -EINVAL; - memcg_update_wmark_min_adj(memcg, wmark_min_adj); + cgrp = cgroup_kn_lock_live(of->kn, false); + if (!cgrp) + return -ENODEV; + + memcg = mem_cgroup_from_css(of_css(of)); + memcg_update_wmark_min_adj_locked(memcg, wmark_min_adj); + cgroup_kn_unlock(of->kn); return nbytes; } -- Gitee