33#include <linux/cgroup.h>
44#include <linux/cpumask.h>
55#include <linux/gfp.h>
6- #include <linux/kstrtox.h>
76#include <linux/sched/rt.h>
87#include <linux/version.h>
98#include <asm/io.h>
@@ -143,26 +142,51 @@ static struct kstep_shm_rt_entity shm_rt_entity(struct task_struct *p, u32 task,
143142 return out ;
144143}
145144
146- // The cgroup's group entities, one record per test CPU, appended to `ents` -- the level the pick
147- // happens at, which nothing else reports: a task's own counters are kept in its group's queue, so
148- // four equal tasks split two cgroups' halves without anything in the task table saying why. The
149- // cpu controller's css is taken from the cgroup rather than the walk's, which is cgroup->self.
150- // Called under RCU; the entities are stable because the isolated CPUs are held for the whole
151- // command.
152- static void shm_group_entities (u32 cgroup , struct cgroup * cgrp , struct kstep_shm_entity * ents , u32 * nents ) {
153- #ifdef CONFIG_FAIR_GROUP_SCHED
145+ // The cgroup's task group, the cpu controller's state; NULL where the controller is off. The css
146+ // is taken from the cgroup rather than the walk's, which is cgroup->self. Under RCU.
147+ static struct task_group * shm_tg (struct cgroup * cgrp ) {
148+ #ifdef CONFIG_CGROUP_SCHED
154149 struct cgroup_subsys_state * css = rcu_dereference (cgrp -> subsys [cpu_cgrp_id ]);
155- struct task_group * tg ;
156150
157151 if (!css )
158- return ;
152+ return NULL ;
159153// css_tg() is private to core.c before 6.12, when sched_ext moved it into sched.h
160154#if LINUX_VERSION_CODE >= KERNEL_VERSION (6 , 12 , 0 )
161- tg = css_tg (css );
155+ return css_tg (css );
162156#else
163- tg = container_of (css , struct task_group , css );
157+ return container_of (css , struct task_group , css );
164158#endif
165- if (!tg -> parent ) // the root task group owns no entity: its cfs_rq is the runqueue's own
159+ #else
160+ return NULL ;
161+ #endif
162+ }
163+
164+ #ifdef CONFIG_CPUSETS
165+ // The head of struct cpuset, private to kernel/cgroup/cpuset.c (cpuset-internal.h on newer
166+ // kernels) and the same on every kernel kSTEP builds: enough to read the mask that
167+ // cpuset.cpus.effective shows without opening the file, which under emulation costs more than
168+ // the rest of an update together.
169+ struct kstep_cpuset_head {
170+ struct cgroup_subsys_state css ;
171+ unsigned long flags ;
172+ cpumask_var_t cpus_allowed ;
173+ nodemask_t mems_allowed ;
174+ cpumask_var_t effective_cpus ;
175+ };
176+ #if __has_include (< kernel /cgroup /cpuset - internal .h > )
177+ #include <kernel/cgroup/cpuset-internal.h>
178+ static_assert (offsetof(struct kstep_cpuset_head , effective_cpus ) == offsetof(struct cpuset , effective_cpus ));
179+ #endif
180+ #endif
181+
182+ // The cgroup's group entities, one record per test CPU, appended to `ents` -- the level the pick
183+ // happens at, which nothing else reports: a task's own counters are kept in its group's queue, so
184+ // four equal tasks split two cgroups' halves without anything in the task table saying why.
185+ // Called under RCU; the entities are stable because the isolated CPUs are held for the whole
186+ // command.
187+ static void shm_group_entities (u32 cgroup , struct task_group * tg , struct kstep_shm_entity * ents , u32 * nents ) {
188+ #ifdef CONFIG_FAIR_GROUP_SCHED
189+ if (!tg || !tg -> parent ) // the root task group owns no entity: its cfs_rq is the runqueue's own
166190 return ;
167191 for_each_test_cpu (cpu ) {
168192 struct sched_entity * se = tg -> se [cpu ];
@@ -181,12 +205,9 @@ static void shm_group_entities(u32 cgroup, struct cgroup *cgrp, struct kstep_shm
181205#endif
182206}
183207
184- // The cgroups: the root and every live cgroup under it, in tree order, collected into `out`.
185- // The tree walk needs RCU and the two control files sleep, so the walk takes the paths first and
186- // the files are read after it. Reading the files is how the kernel's own interface reports these
187- // values -- the cpu controller's weight could come from the task_group, but a cpuset's effective
188- // mask lives in struct cpuset, which is private to kernel/cgroup/cpuset.c. A cgroup with no cpu
189- // controller (the root among them, which has no cpu.weight) keeps weight 0.
208+ // The cgroups: the root and every live cgroup under it, in tree order, collected into `out` with
209+ // what their cpu.weight and cpuset.cpus.effective files show, read off the controllers' state. A
210+ // cgroup without the controller (the root among them, which has no cpu.weight) keeps 0.
190211static u32 shm_collect_cgroups (struct kstep_shm_cgroup * out , struct kstep_shm_entity * ents , u32 * nents ) {
191212 struct cgroup_subsys_state * pos ;
192213 struct cgroup * root ;
@@ -198,34 +219,33 @@ static u32 shm_collect_cgroups(struct kstep_shm_cgroup *out, struct kstep_shm_en
198219 return 0 ;
199220 rcu_read_lock ();
200221 css_for_each_descendant_pre (pos , & root -> self ) {
222+ struct task_group * tg = shm_tg (pos -> cgroup );
223+
201224 if (n == KSTEP_SHM_CGROUPS )
202225 break ;
203226 if (!(pos -> flags & CSS_ONLINE ))
204227 continue ;
205228 memset (& out [n ], 0 , sizeof (out [n ]));
206229 if (cgroup_path (pos -> cgroup , out [n ].path , sizeof (out [n ].path )) < 0 )
207230 continue ; // a path that does not fit is one no kSTEP driver made
208- shm_group_entities (n , pos -> cgroup , ents , nents );
231+ #ifdef CONFIG_FAIR_GROUP_SCHED
232+ if (tg && tg -> parent ) // cpu.weight's own conversion of the shares
233+ out [n ].weight = clamp_t (u64 , DIV_ROUND_CLOSEST_ULL (scale_load_down (tg -> shares ) * CGROUP_WEIGHT_DFL , 1024 ),
234+ CGROUP_WEIGHT_MIN , CGROUP_WEIGHT_MAX );
235+ #endif
236+ #ifdef CONFIG_CPUSETS
237+ {
238+ struct cgroup_subsys_state * cs = rcu_dereference (pos -> cgroup -> subsys [cpuset_cgrp_id ]);
239+
240+ if (cs )
241+ out [n ].cpus = cpumask_bits (container_of (cs , struct kstep_cpuset_head , css )-> effective_cpus )[0 ];
242+ }
243+ #endif
244+ shm_group_entities (n , tg , ents , nents );
209245 n ++ ;
210246 }
211247 rcu_read_unlock ();
212248 cgroup_put (root );
213-
214- for (u32 i = 0 ; i < n ; i ++ ) {
215- const char * name = out [i ].path [1 ] ? out [i ].path : "" ; // the root's files live at CGROUP_ROOT
216- char buf [64 ];
217- cpumask_var_t mask ;
218-
219- if (kstep_cgroup_read (name , "cpu.weight" , buf , sizeof (buf )) > 0 &&
220- kstrtouint (buf , 10 , & out [i ].weight ))
221- out [i ].weight = 0 ; // a value that does not parse is one this kernel does not report
222- if (kstep_cgroup_read (name , "cpuset.cpus.effective" , buf , sizeof (buf )) > 0 &&
223- zalloc_cpumask_var (& mask , GFP_KERNEL )) {
224- if (!cpulist_parse (buf , mask ))
225- out [i ].cpus = cpumask_bits (mask )[0 ];
226- free_cpumask_var (mask );
227- }
228- }
229249 return n ;
230250}
231251
0 commit comments