diff options
| author | Tejun Heo <tj@kernel.org> | 2026-05-05 03:51:18 +0300 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2026-05-15 20:24:22 +0300 |
| commit | 44fabf05634ce9e90b3fb179ea962995b7bbaa09 (patch) | |
| tree | ad5d5b33088a5f2d6b8965ea86e0c0a4ac4cc557 | |
| parent | 3360a5c16d87933fb74b530f5e016eb3dfffee5d (diff) | |
| download | linux-44fabf05634ce9e90b3fb179ea962995b7bbaa09.tar.xz | |
cgroup: Annotate unlocked nr_populated_* accesses with READ_ONCE/WRITE_ONCE
cgroup_update_populated() updates nr_populated_csets,
nr_populated_domain_children, and nr_populated_threaded_children under
css_set_lock, but cgroup_has_tasks(), cgroup_is_populated(), and
cgroup_can_be_thread_root() read them without holding it. Use
READ_ONCE/WRITE_ONCE.
Signed-off-by: Tejun Heo <tj@kernel.org>
| -rw-r--r-- | include/linux/cgroup.h | 21 | ||||
| -rw-r--r-- | kernel/cgroup/cgroup.c | 11 |
2 files changed, 24 insertions, 8 deletions
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index ceb87507667e..9f8bef8f3a60 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -639,16 +639,29 @@ static inline bool task_under_cgroup_hierarchy(struct task_struct *task, return cgroup_is_descendant(cset->dfl_cgrp, ancestor); } +/* + * Populated counters: writes happen under css_set_lock. The accessors below + * may read unlocked. What an unpopulated result means depends on context: + * + * - No lock held. Just a snapshot. May race with concurrent updates and is + * useful only as a hint. + * + * - cgroup_mutex held. Migration into the cgroup is blocked, so an observed + * !populated stays !populated until cgroup_mutex is dropped. + * + * - CSS_DYING set. The css can no longer be repopulated, so !populated is + * sticky once observed. + */ static inline bool cgroup_has_tasks(struct cgroup *cgrp) { - return cgrp->nr_populated_csets; + return READ_ONCE(cgrp->nr_populated_csets); } -/* no synchronization, the result can only be used as a hint */ static inline bool cgroup_is_populated(struct cgroup *cgrp) { - return cgrp->nr_populated_csets + cgrp->nr_populated_domain_children + - cgrp->nr_populated_threaded_children; + return READ_ONCE(cgrp->nr_populated_csets) + + READ_ONCE(cgrp->nr_populated_domain_children) + + READ_ONCE(cgrp->nr_populated_threaded_children); } /* returns ino associated with a cgroup */ diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 7a94c2ea1036..d1395784871a 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -404,7 +404,7 @@ static bool cgroup_can_be_thread_root(struct cgroup *cgrp) return false; /* can only have either domain or threaded children */ - if (cgrp->nr_populated_domain_children) + if (READ_ONCE(cgrp->nr_populated_domain_children)) return false; /* and no domain controllers can be enabled */ @@ -783,12 +783,15 @@ static void cgroup_update_populated(struct cgroup *cgrp, bool populated) bool was_populated = cgroup_is_populated(cgrp); if (!child) { - cgrp->nr_populated_csets += adj; + WRITE_ONCE(cgrp->nr_populated_csets, + cgrp->nr_populated_csets + adj); } else { if (cgroup_is_threaded(child)) - cgrp->nr_populated_threaded_children += adj; + WRITE_ONCE(cgrp->nr_populated_threaded_children, + cgrp->nr_populated_threaded_children + adj); else - cgrp->nr_populated_domain_children += adj; + WRITE_ONCE(cgrp->nr_populated_domain_children, + cgrp->nr_populated_domain_children + adj); } if (was_populated == cgroup_is_populated(cgrp)) |
