Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : #include "cgroup-internal.h" 3 : : 4 : : #include <linux/sched/cputime.h> 5 : : 6 : : static DEFINE_SPINLOCK(cgroup_rstat_lock); 7 : : static DEFINE_PER_CPU(raw_spinlock_t, cgroup_rstat_cpu_lock); 8 : : 9 : : static void cgroup_base_stat_flush(struct cgroup *cgrp, int cpu); 10 : : 11 : : static struct cgroup_rstat_cpu *cgroup_rstat_cpu(struct cgroup *cgrp, int cpu) 12 : : { 13 : 3 : return per_cpu_ptr(cgrp->rstat_cpu, cpu); 14 : : } 15 : : 16 : : /** 17 : : * cgroup_rstat_updated - keep track of updated rstat_cpu 18 : : * @cgrp: target cgroup 19 : : * @cpu: cpu on which rstat_cpu was updated 20 : : * 21 : : * @cgrp's rstat_cpu on @cpu was updated. Put it on the parent's matching 22 : : * rstat_cpu->updated_children list. See the comment on top of 23 : : * cgroup_rstat_cpu definition for details. 24 : : */ 25 : 3 : void cgroup_rstat_updated(struct cgroup *cgrp, int cpu) 26 : : { 27 : 3 : raw_spinlock_t *cpu_lock = per_cpu_ptr(&cgroup_rstat_cpu_lock, cpu); 28 : : struct cgroup *parent; 29 : : unsigned long flags; 30 : : 31 : : /* nothing to do for root */ 32 : 3 : if (!cgroup_parent(cgrp)) 33 : : return; 34 : : 35 : : /* 36 : : * Speculative already-on-list test. This may race leading to 37 : : * temporary inaccuracies, which is fine. 38 : : * 39 : : * Because @parent's updated_children is terminated with @parent 40 : : * instead of NULL, we can tell whether @cgrp is on the list by 41 : : * testing the next pointer for NULL. 42 : : */ 43 : 3 : if (cgroup_rstat_cpu(cgrp, cpu)->updated_next) 44 : : return; 45 : : 46 : 3 : raw_spin_lock_irqsave(cpu_lock, flags); 47 : : 48 : : /* put @cgrp and all ancestors on the corresponding updated lists */ 49 : 3 : for (parent = cgroup_parent(cgrp); parent; 50 : : cgrp = parent, parent = cgroup_parent(cgrp)) { 51 : : struct cgroup_rstat_cpu *rstatc = cgroup_rstat_cpu(cgrp, cpu); 52 : : struct cgroup_rstat_cpu *prstatc = cgroup_rstat_cpu(parent, cpu); 53 : : 54 : : /* 55 : : * Both additions and removals are bottom-up. If a cgroup 56 : : * is already in the tree, all ancestors are. 57 : : */ 58 : 3 : if (rstatc->updated_next) 59 : : break; 60 : : 61 : 3 : rstatc->updated_next = prstatc->updated_children; 62 : 3 : prstatc->updated_children = cgrp; 63 : : } 64 : : 65 : 3 : raw_spin_unlock_irqrestore(cpu_lock, flags); 66 : : } 67 : : EXPORT_SYMBOL_GPL(cgroup_rstat_updated); 68 : : 69 : : /** 70 : : * cgroup_rstat_cpu_pop_updated - iterate and dismantle rstat_cpu updated tree 71 : : * @pos: current position 72 : : * @root: root of the tree to traversal 73 : : * @cpu: target cpu 74 : : * 75 : : * Walks the udpated rstat_cpu tree on @cpu from @root. %NULL @pos starts 76 : : * the traversal and %NULL return indicates the end. During traversal, 77 : : * each returned cgroup is unlinked from the tree. Must be called with the 78 : : * matching cgroup_rstat_cpu_lock held. 79 : : * 80 : : * The only ordering guarantee is that, for a parent and a child pair 81 : : * covered by a given traversal, if a child is visited, its parent is 82 : : * guaranteed to be visited afterwards. 83 : : */ 84 : 3 : static struct cgroup *cgroup_rstat_cpu_pop_updated(struct cgroup *pos, 85 : : struct cgroup *root, int cpu) 86 : : { 87 : : struct cgroup_rstat_cpu *rstatc; 88 : : 89 : 3 : if (pos == root) 90 : : return NULL; 91 : : 92 : : /* 93 : : * We're gonna walk down to the first leaf and visit/remove it. We 94 : : * can pick whatever unvisited node as the starting point. 95 : : */ 96 : 3 : if (!pos) 97 : : pos = root; 98 : : else 99 : : pos = cgroup_parent(pos); 100 : : 101 : : /* walk down to the first leaf */ 102 : : while (true) { 103 : : rstatc = cgroup_rstat_cpu(pos, cpu); 104 : 3 : if (rstatc->updated_children == pos) 105 : : break; 106 : : pos = rstatc->updated_children; 107 : : } 108 : : 109 : : /* 110 : : * Unlink @pos from the tree. As the updated_children list is 111 : : * singly linked, we have to walk it to find the removal point. 112 : : * However, due to the way we traverse, @pos will be the first 113 : : * child in most cases. The only exception is @root. 114 : : */ 115 : 3 : if (rstatc->updated_next) { 116 : : struct cgroup *parent = cgroup_parent(pos); 117 : : struct cgroup_rstat_cpu *prstatc = cgroup_rstat_cpu(parent, cpu); 118 : : struct cgroup_rstat_cpu *nrstatc; 119 : : struct cgroup **nextp; 120 : : 121 : 3 : nextp = &prstatc->updated_children; 122 : : while (true) { 123 : 3 : nrstatc = cgroup_rstat_cpu(*nextp, cpu); 124 : 3 : if (*nextp == pos) 125 : : break; 126 : : 127 : 3 : WARN_ON_ONCE(*nextp == parent); 128 : 3 : nextp = &nrstatc->updated_next; 129 : 3 : } 130 : : 131 : 3 : *nextp = rstatc->updated_next; 132 : 3 : rstatc->updated_next = NULL; 133 : : 134 : 3 : return pos; 135 : : } 136 : : 137 : : /* only happens for @root */ 138 : : return NULL; 139 : : } 140 : : 141 : : /* see cgroup_rstat_flush() */ 142 : 3 : static void cgroup_rstat_flush_locked(struct cgroup *cgrp, bool may_sleep) 143 : : __releases(&cgroup_rstat_lock) __acquires(&cgroup_rstat_lock) 144 : : { 145 : : int cpu; 146 : : 147 : : lockdep_assert_held(&cgroup_rstat_lock); 148 : : 149 : 3 : for_each_possible_cpu(cpu) { 150 : 3 : raw_spinlock_t *cpu_lock = per_cpu_ptr(&cgroup_rstat_cpu_lock, 151 : : cpu); 152 : : struct cgroup *pos = NULL; 153 : : 154 : 3 : raw_spin_lock(cpu_lock); 155 : 3 : while ((pos = cgroup_rstat_cpu_pop_updated(pos, cgrp, cpu))) { 156 : : struct cgroup_subsys_state *css; 157 : : 158 : 3 : cgroup_base_stat_flush(pos, cpu); 159 : : 160 : : rcu_read_lock(); 161 : 3 : list_for_each_entry_rcu(css, &pos->rstat_css_list, 162 : : rstat_css_node) 163 : 0 : css->ss->css_rstat_flush(css, cpu); 164 : : rcu_read_unlock(); 165 : : } 166 : : raw_spin_unlock(cpu_lock); 167 : : 168 : : /* if @may_sleep, play nice and yield if necessary */ 169 : 3 : if (may_sleep && (need_resched() || 170 : : spin_needbreak(&cgroup_rstat_lock))) { 171 : : spin_unlock_irq(&cgroup_rstat_lock); 172 : 1 : if (!cond_resched()) 173 : 0 : cpu_relax(); 174 : : spin_lock_irq(&cgroup_rstat_lock); 175 : : } 176 : : } 177 : 3 : } 178 : : 179 : : /** 180 : : * cgroup_rstat_flush - flush stats in @cgrp's subtree 181 : : * @cgrp: target cgroup 182 : : * 183 : : * Collect all per-cpu stats in @cgrp's subtree into the global counters 184 : : * and propagate them upwards. After this function returns, all cgroups in 185 : : * the subtree have up-to-date ->stat. 186 : : * 187 : : * This also gets all cgroups in the subtree including @cgrp off the 188 : : * ->updated_children lists. 189 : : * 190 : : * This function may block. 191 : : */ 192 : 3 : void cgroup_rstat_flush(struct cgroup *cgrp) 193 : : { 194 : 3 : might_sleep(); 195 : : 196 : : spin_lock_irq(&cgroup_rstat_lock); 197 : 3 : cgroup_rstat_flush_locked(cgrp, true); 198 : : spin_unlock_irq(&cgroup_rstat_lock); 199 : 3 : } 200 : : 201 : : /** 202 : : * cgroup_rstat_flush_irqsafe - irqsafe version of cgroup_rstat_flush() 203 : : * @cgrp: target cgroup 204 : : * 205 : : * This function can be called from any context. 206 : : */ 207 : 0 : void cgroup_rstat_flush_irqsafe(struct cgroup *cgrp) 208 : : { 209 : : unsigned long flags; 210 : : 211 : 0 : spin_lock_irqsave(&cgroup_rstat_lock, flags); 212 : 0 : cgroup_rstat_flush_locked(cgrp, false); 213 : : spin_unlock_irqrestore(&cgroup_rstat_lock, flags); 214 : 0 : } 215 : : 216 : : /** 217 : : * cgroup_rstat_flush_begin - flush stats in @cgrp's subtree and hold 218 : : * @cgrp: target cgroup 219 : : * 220 : : * Flush stats in @cgrp's subtree and prevent further flushes. Must be 221 : : * paired with cgroup_rstat_flush_release(). 222 : : * 223 : : * This function may block. 224 : : */ 225 : 0 : void cgroup_rstat_flush_hold(struct cgroup *cgrp) 226 : : __acquires(&cgroup_rstat_lock) 227 : : { 228 : 0 : might_sleep(); 229 : : spin_lock_irq(&cgroup_rstat_lock); 230 : 0 : cgroup_rstat_flush_locked(cgrp, true); 231 : 0 : } 232 : : 233 : : /** 234 : : * cgroup_rstat_flush_release - release cgroup_rstat_flush_hold() 235 : : */ 236 : 0 : void cgroup_rstat_flush_release(void) 237 : : __releases(&cgroup_rstat_lock) 238 : : { 239 : : spin_unlock_irq(&cgroup_rstat_lock); 240 : 0 : } 241 : : 242 : 3 : int cgroup_rstat_init(struct cgroup *cgrp) 243 : : { 244 : : int cpu; 245 : : 246 : : /* the root cgrp has rstat_cpu preallocated */ 247 : 3 : if (!cgrp->rstat_cpu) { 248 : 3 : cgrp->rstat_cpu = alloc_percpu(struct cgroup_rstat_cpu); 249 : 3 : if (!cgrp->rstat_cpu) 250 : : return -ENOMEM; 251 : : } 252 : : 253 : : /* ->updated_children list is self terminated */ 254 : 3 : for_each_possible_cpu(cpu) { 255 : : struct cgroup_rstat_cpu *rstatc = cgroup_rstat_cpu(cgrp, cpu); 256 : : 257 : 3 : rstatc->updated_children = cgrp; 258 : : u64_stats_init(&rstatc->bsync); 259 : : } 260 : : 261 : : return 0; 262 : : } 263 : : 264 : 3 : void cgroup_rstat_exit(struct cgroup *cgrp) 265 : : { 266 : : int cpu; 267 : : 268 : 3 : cgroup_rstat_flush(cgrp); 269 : : 270 : : /* sanity check */ 271 : 3 : for_each_possible_cpu(cpu) { 272 : : struct cgroup_rstat_cpu *rstatc = cgroup_rstat_cpu(cgrp, cpu); 273 : : 274 : 3 : if (WARN_ON_ONCE(rstatc->updated_children != cgrp) || 275 : 3 : WARN_ON_ONCE(rstatc->updated_next)) 276 : 3 : return; 277 : : } 278 : : 279 : 3 : free_percpu(cgrp->rstat_cpu); 280 : 3 : cgrp->rstat_cpu = NULL; 281 : : } 282 : : 283 : 3 : void __init cgroup_rstat_boot(void) 284 : : { 285 : : int cpu; 286 : : 287 : 3 : for_each_possible_cpu(cpu) 288 : 3 : raw_spin_lock_init(per_cpu_ptr(&cgroup_rstat_cpu_lock, cpu)); 289 : : 290 : 3 : BUG_ON(cgroup_rstat_init(&cgrp_dfl_root.cgrp)); 291 : 3 : } 292 : : 293 : : /* 294 : : * Functions for cgroup basic resource statistics implemented on top of 295 : : * rstat. 296 : : */ 297 : : static void cgroup_base_stat_accumulate(struct cgroup_base_stat *dst_bstat, 298 : : struct cgroup_base_stat *src_bstat) 299 : : { 300 : 3 : dst_bstat->cputime.utime += src_bstat->cputime.utime; 301 : 3 : dst_bstat->cputime.stime += src_bstat->cputime.stime; 302 : 3 : dst_bstat->cputime.sum_exec_runtime += src_bstat->cputime.sum_exec_runtime; 303 : : } 304 : : 305 : 3 : static void cgroup_base_stat_flush(struct cgroup *cgrp, int cpu) 306 : : { 307 : : struct cgroup *parent = cgroup_parent(cgrp); 308 : : struct cgroup_rstat_cpu *rstatc = cgroup_rstat_cpu(cgrp, cpu); 309 : : struct task_cputime *last_cputime = &rstatc->last_bstat.cputime; 310 : : struct task_cputime cputime; 311 : : struct cgroup_base_stat delta; 312 : : unsigned seq; 313 : : 314 : : /* fetch the current per-cpu values */ 315 : : do { 316 : : seq = __u64_stats_fetch_begin(&rstatc->bsync); 317 : 3 : cputime = rstatc->bstat.cputime; 318 : 3 : } while (__u64_stats_fetch_retry(&rstatc->bsync, seq)); 319 : : 320 : : /* calculate the delta to propgate */ 321 : 3 : delta.cputime.utime = cputime.utime - last_cputime->utime; 322 : 3 : delta.cputime.stime = cputime.stime - last_cputime->stime; 323 : 3 : delta.cputime.sum_exec_runtime = cputime.sum_exec_runtime - 324 : 3 : last_cputime->sum_exec_runtime; 325 : 3 : *last_cputime = cputime; 326 : : 327 : : /* transfer the pending stat into delta */ 328 : : cgroup_base_stat_accumulate(&delta, &cgrp->pending_bstat); 329 : 3 : memset(&cgrp->pending_bstat, 0, sizeof(cgrp->pending_bstat)); 330 : : 331 : : /* propagate delta into the global stat and the parent's pending */ 332 : : cgroup_base_stat_accumulate(&cgrp->bstat, &delta); 333 : 3 : if (parent) 334 : : cgroup_base_stat_accumulate(&parent->pending_bstat, &delta); 335 : 3 : } 336 : : 337 : : static struct cgroup_rstat_cpu * 338 : : cgroup_base_stat_cputime_account_begin(struct cgroup *cgrp) 339 : : { 340 : : struct cgroup_rstat_cpu *rstatc; 341 : : 342 : 3 : rstatc = get_cpu_ptr(cgrp->rstat_cpu); 343 : : u64_stats_update_begin(&rstatc->bsync); 344 : : return rstatc; 345 : : } 346 : : 347 : 3 : static void cgroup_base_stat_cputime_account_end(struct cgroup *cgrp, 348 : : struct cgroup_rstat_cpu *rstatc) 349 : : { 350 : : u64_stats_update_end(&rstatc->bsync); 351 : 3 : cgroup_rstat_updated(cgrp, smp_processor_id()); 352 : 3 : put_cpu_ptr(rstatc); 353 : 3 : } 354 : : 355 : 3 : void __cgroup_account_cputime(struct cgroup *cgrp, u64 delta_exec) 356 : : { 357 : : struct cgroup_rstat_cpu *rstatc; 358 : : 359 : : rstatc = cgroup_base_stat_cputime_account_begin(cgrp); 360 : 3 : rstatc->bstat.cputime.sum_exec_runtime += delta_exec; 361 : 3 : cgroup_base_stat_cputime_account_end(cgrp, rstatc); 362 : 3 : } 363 : : 364 : 3 : void __cgroup_account_cputime_field(struct cgroup *cgrp, 365 : : enum cpu_usage_stat index, u64 delta_exec) 366 : : { 367 : : struct cgroup_rstat_cpu *rstatc; 368 : : 369 : : rstatc = cgroup_base_stat_cputime_account_begin(cgrp); 370 : : 371 : 3 : switch (index) { 372 : : case CPUTIME_USER: 373 : : case CPUTIME_NICE: 374 : 3 : rstatc->bstat.cputime.utime += delta_exec; 375 : 3 : break; 376 : : case CPUTIME_SYSTEM: 377 : : case CPUTIME_IRQ: 378 : : case CPUTIME_SOFTIRQ: 379 : 3 : rstatc->bstat.cputime.stime += delta_exec; 380 : 3 : break; 381 : : default: 382 : : break; 383 : : } 384 : : 385 : 3 : cgroup_base_stat_cputime_account_end(cgrp, rstatc); 386 : 3 : } 387 : : 388 : 0 : void cgroup_base_stat_cputime_show(struct seq_file *seq) 389 : : { 390 : 0 : struct cgroup *cgrp = seq_css(seq)->cgroup; 391 : : u64 usage, utime, stime; 392 : : 393 : 0 : if (!cgroup_parent(cgrp)) 394 : 0 : return; 395 : : 396 : 0 : cgroup_rstat_flush_hold(cgrp); 397 : 0 : usage = cgrp->bstat.cputime.sum_exec_runtime; 398 : 0 : cputime_adjust(&cgrp->bstat.cputime, &cgrp->prev_cputime, &utime, &stime); 399 : : cgroup_rstat_flush_release(); 400 : : 401 : 0 : do_div(usage, NSEC_PER_USEC); 402 : 0 : do_div(utime, NSEC_PER_USEC); 403 : 0 : do_div(stime, NSEC_PER_USEC); 404 : : 405 : 0 : seq_printf(seq, "usage_usec %llu\n" 406 : : "user_usec %llu\n" 407 : : "system_usec %llu\n", 408 : : usage, utime, stime); 409 : : }