]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2e76c24d LZ |
2 | /* |
3 | * CPU accounting code for task groups. | |
4 | * | |
5 | * Based on the work by Paul Menage ([email protected]) and Balbir Singh | |
6 | * ([email protected]). | |
7 | */ | |
325ea10c | 8 | #include "sched.h" |
2e76c24d | 9 | |
97fb7a0a | 10 | /* Time spent by the tasks of the CPU accounting group executing in ... */ |
d1712796 LZ |
11 | enum cpuacct_stat_index { |
12 | CPUACCT_STAT_USER, /* ... user mode */ | |
13 | CPUACCT_STAT_SYSTEM, /* ... kernel mode */ | |
14 | ||
15 | CPUACCT_STAT_NSTATS, | |
16 | }; | |
17 | ||
9acacc2a ZL |
18 | static const char * const cpuacct_stat_desc[] = { |
19 | [CPUACCT_STAT_USER] = "user", | |
20 | [CPUACCT_STAT_SYSTEM] = "system", | |
d740037f DY |
21 | }; |
22 | ||
23 | struct cpuacct_usage { | |
9acacc2a | 24 | u64 usages[CPUACCT_STAT_NSTATS]; |
d740037f DY |
25 | }; |
26 | ||
97fb7a0a | 27 | /* track CPU usage of a group of tasks and its child groups */ |
d1712796 | 28 | struct cpuacct { |
97fb7a0a IM |
29 | struct cgroup_subsys_state css; |
30 | /* cpuusage holds pointer to a u64-type object on every CPU */ | |
31 | struct cpuacct_usage __percpu *cpuusage; | |
32 | struct kernel_cpustat __percpu *cpustat; | |
d1712796 LZ |
33 | }; |
34 | ||
a7c6d554 TH |
35 | static inline struct cpuacct *css_ca(struct cgroup_subsys_state *css) |
36 | { | |
37 | return css ? container_of(css, struct cpuacct, css) : NULL; | |
38 | } | |
39 | ||
97fb7a0a | 40 | /* Return CPU accounting group to which this task belongs */ |
d1712796 LZ |
41 | static inline struct cpuacct *task_ca(struct task_struct *tsk) |
42 | { | |
073219e9 | 43 | return css_ca(task_css(tsk, cpuacct_cgrp_id)); |
d1712796 LZ |
44 | } |
45 | ||
d1712796 LZ |
46 | static inline struct cpuacct *parent_ca(struct cpuacct *ca) |
47 | { | |
5c9d535b | 48 | return css_ca(ca->css.parent); |
d1712796 LZ |
49 | } |
50 | ||
d740037f | 51 | static DEFINE_PER_CPU(struct cpuacct_usage, root_cpuacct_cpuusage); |
14c6d3c8 LZ |
52 | static struct cpuacct root_cpuacct = { |
53 | .cpustat = &kernel_cpustat, | |
54 | .cpuusage = &root_cpuacct_cpuusage, | |
55 | }; | |
2e76c24d | 56 | |
97fb7a0a | 57 | /* Create a new CPU accounting group */ |
eb95419b TH |
58 | static struct cgroup_subsys_state * |
59 | cpuacct_css_alloc(struct cgroup_subsys_state *parent_css) | |
2e76c24d LZ |
60 | { |
61 | struct cpuacct *ca; | |
62 | ||
eb95419b | 63 | if (!parent_css) |
2e76c24d LZ |
64 | return &root_cpuacct.css; |
65 | ||
66 | ca = kzalloc(sizeof(*ca), GFP_KERNEL); | |
67 | if (!ca) | |
68 | goto out; | |
69 | ||
d740037f | 70 | ca->cpuusage = alloc_percpu(struct cpuacct_usage); |
2e76c24d LZ |
71 | if (!ca->cpuusage) |
72 | goto out_free_ca; | |
73 | ||
74 | ca->cpustat = alloc_percpu(struct kernel_cpustat); | |
75 | if (!ca->cpustat) | |
76 | goto out_free_cpuusage; | |
77 | ||
78 | return &ca->css; | |
79 | ||
80 | out_free_cpuusage: | |
81 | free_percpu(ca->cpuusage); | |
82 | out_free_ca: | |
83 | kfree(ca); | |
84 | out: | |
85 | return ERR_PTR(-ENOMEM); | |
86 | } | |
87 | ||
97fb7a0a | 88 | /* Destroy an existing CPU accounting group */ |
eb95419b | 89 | static void cpuacct_css_free(struct cgroup_subsys_state *css) |
2e76c24d | 90 | { |
eb95419b | 91 | struct cpuacct *ca = css_ca(css); |
2e76c24d LZ |
92 | |
93 | free_percpu(ca->cpustat); | |
94 | free_percpu(ca->cpuusage); | |
95 | kfree(ca); | |
96 | } | |
97 | ||
d740037f | 98 | static u64 cpuacct_cpuusage_read(struct cpuacct *ca, int cpu, |
9acacc2a | 99 | enum cpuacct_stat_index index) |
2e76c24d | 100 | { |
d740037f | 101 | struct cpuacct_usage *cpuusage = per_cpu_ptr(ca->cpuusage, cpu); |
2e76c24d LZ |
102 | u64 data; |
103 | ||
d740037f | 104 | /* |
9acacc2a | 105 | * We allow index == CPUACCT_STAT_NSTATS here to read |
d740037f DY |
106 | * the sum of suages. |
107 | */ | |
9acacc2a | 108 | BUG_ON(index > CPUACCT_STAT_NSTATS); |
d740037f | 109 | |
2e76c24d LZ |
110 | #ifndef CONFIG_64BIT |
111 | /* | |
112 | * Take rq->lock to make 64-bit read safe on 32-bit platforms. | |
113 | */ | |
114 | raw_spin_lock_irq(&cpu_rq(cpu)->lock); | |
d740037f DY |
115 | #endif |
116 | ||
9acacc2a | 117 | if (index == CPUACCT_STAT_NSTATS) { |
d740037f DY |
118 | int i = 0; |
119 | ||
120 | data = 0; | |
9acacc2a | 121 | for (i = 0; i < CPUACCT_STAT_NSTATS; i++) |
d740037f DY |
122 | data += cpuusage->usages[i]; |
123 | } else { | |
124 | data = cpuusage->usages[index]; | |
125 | } | |
126 | ||
127 | #ifndef CONFIG_64BIT | |
2e76c24d | 128 | raw_spin_unlock_irq(&cpu_rq(cpu)->lock); |
2e76c24d LZ |
129 | #endif |
130 | ||
131 | return data; | |
132 | } | |
133 | ||
134 | static void cpuacct_cpuusage_write(struct cpuacct *ca, int cpu, u64 val) | |
135 | { | |
d740037f DY |
136 | struct cpuacct_usage *cpuusage = per_cpu_ptr(ca->cpuusage, cpu); |
137 | int i; | |
2e76c24d LZ |
138 | |
139 | #ifndef CONFIG_64BIT | |
140 | /* | |
141 | * Take rq->lock to make 64-bit write safe on 32-bit platforms. | |
142 | */ | |
143 | raw_spin_lock_irq(&cpu_rq(cpu)->lock); | |
d740037f DY |
144 | #endif |
145 | ||
9acacc2a | 146 | for (i = 0; i < CPUACCT_STAT_NSTATS; i++) |
d740037f DY |
147 | cpuusage->usages[i] = val; |
148 | ||
149 | #ifndef CONFIG_64BIT | |
2e76c24d | 150 | raw_spin_unlock_irq(&cpu_rq(cpu)->lock); |
2e76c24d LZ |
151 | #endif |
152 | } | |
153 | ||
97fb7a0a | 154 | /* Return total CPU usage (in nanoseconds) of a group */ |
d740037f | 155 | static u64 __cpuusage_read(struct cgroup_subsys_state *css, |
9acacc2a | 156 | enum cpuacct_stat_index index) |
2e76c24d | 157 | { |
182446d0 | 158 | struct cpuacct *ca = css_ca(css); |
2e76c24d LZ |
159 | u64 totalcpuusage = 0; |
160 | int i; | |
161 | ||
5ca3726a | 162 | for_each_possible_cpu(i) |
d740037f | 163 | totalcpuusage += cpuacct_cpuusage_read(ca, i, index); |
2e76c24d LZ |
164 | |
165 | return totalcpuusage; | |
166 | } | |
167 | ||
d740037f DY |
168 | static u64 cpuusage_user_read(struct cgroup_subsys_state *css, |
169 | struct cftype *cft) | |
170 | { | |
9acacc2a | 171 | return __cpuusage_read(css, CPUACCT_STAT_USER); |
d740037f DY |
172 | } |
173 | ||
174 | static u64 cpuusage_sys_read(struct cgroup_subsys_state *css, | |
175 | struct cftype *cft) | |
176 | { | |
9acacc2a | 177 | return __cpuusage_read(css, CPUACCT_STAT_SYSTEM); |
d740037f DY |
178 | } |
179 | ||
180 | static u64 cpuusage_read(struct cgroup_subsys_state *css, struct cftype *cft) | |
181 | { | |
9acacc2a | 182 | return __cpuusage_read(css, CPUACCT_STAT_NSTATS); |
d740037f DY |
183 | } |
184 | ||
182446d0 | 185 | static int cpuusage_write(struct cgroup_subsys_state *css, struct cftype *cft, |
1a736b77 | 186 | u64 val) |
2e76c24d | 187 | { |
182446d0 | 188 | struct cpuacct *ca = css_ca(css); |
d740037f | 189 | int cpu; |
2e76c24d | 190 | |
1a736b77 DY |
191 | /* |
192 | * Only allow '0' here to do a reset. | |
193 | */ | |
d740037f DY |
194 | if (val) |
195 | return -EINVAL; | |
2e76c24d | 196 | |
d740037f DY |
197 | for_each_possible_cpu(cpu) |
198 | cpuacct_cpuusage_write(ca, cpu, 0); | |
2e76c24d | 199 | |
d740037f | 200 | return 0; |
2e76c24d LZ |
201 | } |
202 | ||
d740037f | 203 | static int __cpuacct_percpu_seq_show(struct seq_file *m, |
9acacc2a | 204 | enum cpuacct_stat_index index) |
2e76c24d | 205 | { |
2da8ca82 | 206 | struct cpuacct *ca = css_ca(seq_css(m)); |
2e76c24d LZ |
207 | u64 percpu; |
208 | int i; | |
209 | ||
5ca3726a | 210 | for_each_possible_cpu(i) { |
d740037f | 211 | percpu = cpuacct_cpuusage_read(ca, i, index); |
2e76c24d LZ |
212 | seq_printf(m, "%llu ", (unsigned long long) percpu); |
213 | } | |
214 | seq_printf(m, "\n"); | |
215 | return 0; | |
216 | } | |
217 | ||
d740037f DY |
218 | static int cpuacct_percpu_user_seq_show(struct seq_file *m, void *V) |
219 | { | |
9acacc2a | 220 | return __cpuacct_percpu_seq_show(m, CPUACCT_STAT_USER); |
d740037f DY |
221 | } |
222 | ||
223 | static int cpuacct_percpu_sys_seq_show(struct seq_file *m, void *V) | |
224 | { | |
9acacc2a | 225 | return __cpuacct_percpu_seq_show(m, CPUACCT_STAT_SYSTEM); |
d740037f DY |
226 | } |
227 | ||
228 | static int cpuacct_percpu_seq_show(struct seq_file *m, void *V) | |
229 | { | |
9acacc2a | 230 | return __cpuacct_percpu_seq_show(m, CPUACCT_STAT_NSTATS); |
d740037f DY |
231 | } |
232 | ||
277a13e4 ZL |
233 | static int cpuacct_all_seq_show(struct seq_file *m, void *V) |
234 | { | |
235 | struct cpuacct *ca = css_ca(seq_css(m)); | |
236 | int index; | |
237 | int cpu; | |
238 | ||
239 | seq_puts(m, "cpu"); | |
240 | for (index = 0; index < CPUACCT_STAT_NSTATS; index++) | |
241 | seq_printf(m, " %s", cpuacct_stat_desc[index]); | |
242 | seq_puts(m, "\n"); | |
243 | ||
244 | for_each_possible_cpu(cpu) { | |
245 | struct cpuacct_usage *cpuusage = per_cpu_ptr(ca->cpuusage, cpu); | |
246 | ||
247 | seq_printf(m, "%d", cpu); | |
248 | ||
249 | for (index = 0; index < CPUACCT_STAT_NSTATS; index++) { | |
250 | #ifndef CONFIG_64BIT | |
251 | /* | |
252 | * Take rq->lock to make 64-bit read safe on 32-bit | |
253 | * platforms. | |
254 | */ | |
255 | raw_spin_lock_irq(&cpu_rq(cpu)->lock); | |
256 | #endif | |
257 | ||
258 | seq_printf(m, " %llu", cpuusage->usages[index]); | |
259 | ||
260 | #ifndef CONFIG_64BIT | |
261 | raw_spin_unlock_irq(&cpu_rq(cpu)->lock); | |
262 | #endif | |
263 | } | |
264 | seq_puts(m, "\n"); | |
265 | } | |
266 | return 0; | |
267 | } | |
268 | ||
2da8ca82 | 269 | static int cpuacct_stats_show(struct seq_file *sf, void *v) |
2e76c24d | 270 | { |
2da8ca82 | 271 | struct cpuacct *ca = css_ca(seq_css(sf)); |
8e546bfa | 272 | s64 val[CPUACCT_STAT_NSTATS]; |
2e76c24d | 273 | int cpu; |
8e546bfa | 274 | int stat; |
2e76c24d | 275 | |
8e546bfa | 276 | memset(val, 0, sizeof(val)); |
5ca3726a | 277 | for_each_possible_cpu(cpu) { |
8e546bfa | 278 | u64 *cpustat = per_cpu_ptr(ca->cpustat, cpu)->cpustat; |
2e76c24d | 279 | |
8e546bfa ZL |
280 | val[CPUACCT_STAT_USER] += cpustat[CPUTIME_USER]; |
281 | val[CPUACCT_STAT_USER] += cpustat[CPUTIME_NICE]; | |
282 | val[CPUACCT_STAT_SYSTEM] += cpustat[CPUTIME_SYSTEM]; | |
283 | val[CPUACCT_STAT_SYSTEM] += cpustat[CPUTIME_IRQ]; | |
284 | val[CPUACCT_STAT_SYSTEM] += cpustat[CPUTIME_SOFTIRQ]; | |
2e76c24d LZ |
285 | } |
286 | ||
8e546bfa ZL |
287 | for (stat = 0; stat < CPUACCT_STAT_NSTATS; stat++) { |
288 | seq_printf(sf, "%s %lld\n", | |
289 | cpuacct_stat_desc[stat], | |
7fb1327e | 290 | (long long)nsec_to_clock_t(val[stat])); |
8e546bfa | 291 | } |
2e76c24d LZ |
292 | |
293 | return 0; | |
294 | } | |
295 | ||
296 | static struct cftype files[] = { | |
297 | { | |
298 | .name = "usage", | |
299 | .read_u64 = cpuusage_read, | |
300 | .write_u64 = cpuusage_write, | |
301 | }, | |
d740037f DY |
302 | { |
303 | .name = "usage_user", | |
304 | .read_u64 = cpuusage_user_read, | |
305 | }, | |
306 | { | |
307 | .name = "usage_sys", | |
308 | .read_u64 = cpuusage_sys_read, | |
309 | }, | |
2e76c24d LZ |
310 | { |
311 | .name = "usage_percpu", | |
2da8ca82 | 312 | .seq_show = cpuacct_percpu_seq_show, |
2e76c24d | 313 | }, |
d740037f DY |
314 | { |
315 | .name = "usage_percpu_user", | |
316 | .seq_show = cpuacct_percpu_user_seq_show, | |
317 | }, | |
318 | { | |
319 | .name = "usage_percpu_sys", | |
320 | .seq_show = cpuacct_percpu_sys_seq_show, | |
321 | }, | |
277a13e4 ZL |
322 | { |
323 | .name = "usage_all", | |
324 | .seq_show = cpuacct_all_seq_show, | |
325 | }, | |
2e76c24d LZ |
326 | { |
327 | .name = "stat", | |
2da8ca82 | 328 | .seq_show = cpuacct_stats_show, |
2e76c24d LZ |
329 | }, |
330 | { } /* terminate */ | |
331 | }; | |
332 | ||
333 | /* | |
334 | * charge this task's execution time to its accounting group. | |
335 | * | |
336 | * called with rq->lock held. | |
337 | */ | |
338 | void cpuacct_charge(struct task_struct *tsk, u64 cputime) | |
339 | { | |
340 | struct cpuacct *ca; | |
9acacc2a | 341 | int index = CPUACCT_STAT_SYSTEM; |
bd928830 | 342 | struct pt_regs *regs = task_pt_regs(tsk); |
d740037f | 343 | |
bd928830 | 344 | if (regs && user_mode(regs)) |
9acacc2a | 345 | index = CPUACCT_STAT_USER; |
2e76c24d LZ |
346 | |
347 | rcu_read_lock(); | |
d740037f | 348 | |
73e6aafd | 349 | for (ca = task_ca(tsk); ca; ca = parent_ca(ca)) |
d740037f DY |
350 | this_cpu_ptr(ca->cpuusage)->usages[index] += cputime; |
351 | ||
2e76c24d LZ |
352 | rcu_read_unlock(); |
353 | } | |
354 | ||
1966aaf7 LZ |
355 | /* |
356 | * Add user/system time to cpuacct. | |
357 | * | |
358 | * Note: it's the caller that updates the account of the root cgroup. | |
359 | */ | |
73e6aafd | 360 | void cpuacct_account_field(struct task_struct *tsk, int index, u64 val) |
1966aaf7 | 361 | { |
1966aaf7 LZ |
362 | struct cpuacct *ca; |
363 | ||
1966aaf7 | 364 | rcu_read_lock(); |
73e6aafd ZL |
365 | for (ca = task_ca(tsk); ca != &root_cpuacct; ca = parent_ca(ca)) |
366 | this_cpu_ptr(ca->cpustat)->cpustat[index] += val; | |
1966aaf7 LZ |
367 | rcu_read_unlock(); |
368 | } | |
369 | ||
073219e9 | 370 | struct cgroup_subsys cpuacct_cgrp_subsys = { |
621e2de0 LZ |
371 | .css_alloc = cpuacct_css_alloc, |
372 | .css_free = cpuacct_css_free, | |
5577964e | 373 | .legacy_cftypes = files, |
b38e42e9 | 374 | .early_init = true, |
2e76c24d | 375 | }; |