GNU Linux-libre 5.17.9-gnu
[releases.git] / kernel / sched / stats.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * /proc/schedstat implementation
4  */
5 #include "sched.h"
6
7 void __update_stats_wait_start(struct rq *rq, struct task_struct *p,
8                                struct sched_statistics *stats)
9 {
10         u64 wait_start, prev_wait_start;
11
12         wait_start = rq_clock(rq);
13         prev_wait_start = schedstat_val(stats->wait_start);
14
15         if (p && likely(wait_start > prev_wait_start))
16                 wait_start -= prev_wait_start;
17
18         __schedstat_set(stats->wait_start, wait_start);
19 }
20
21 void __update_stats_wait_end(struct rq *rq, struct task_struct *p,
22                              struct sched_statistics *stats)
23 {
24         u64 delta = rq_clock(rq) - schedstat_val(stats->wait_start);
25
26         if (p) {
27                 if (task_on_rq_migrating(p)) {
28                         /*
29                          * Preserve migrating task's wait time so wait_start
30                          * time stamp can be adjusted to accumulate wait time
31                          * prior to migration.
32                          */
33                         __schedstat_set(stats->wait_start, delta);
34
35                         return;
36                 }
37
38                 trace_sched_stat_wait(p, delta);
39         }
40
41         __schedstat_set(stats->wait_max,
42                         max(schedstat_val(stats->wait_max), delta));
43         __schedstat_inc(stats->wait_count);
44         __schedstat_add(stats->wait_sum, delta);
45         __schedstat_set(stats->wait_start, 0);
46 }
47
48 void __update_stats_enqueue_sleeper(struct rq *rq, struct task_struct *p,
49                                     struct sched_statistics *stats)
50 {
51         u64 sleep_start, block_start;
52
53         sleep_start = schedstat_val(stats->sleep_start);
54         block_start = schedstat_val(stats->block_start);
55
56         if (sleep_start) {
57                 u64 delta = rq_clock(rq) - sleep_start;
58
59                 if ((s64)delta < 0)
60                         delta = 0;
61
62                 if (unlikely(delta > schedstat_val(stats->sleep_max)))
63                         __schedstat_set(stats->sleep_max, delta);
64
65                 __schedstat_set(stats->sleep_start, 0);
66                 __schedstat_add(stats->sum_sleep_runtime, delta);
67
68                 if (p) {
69                         account_scheduler_latency(p, delta >> 10, 1);
70                         trace_sched_stat_sleep(p, delta);
71                 }
72         }
73
74         if (block_start) {
75                 u64 delta = rq_clock(rq) - block_start;
76
77                 if ((s64)delta < 0)
78                         delta = 0;
79
80                 if (unlikely(delta > schedstat_val(stats->block_max)))
81                         __schedstat_set(stats->block_max, delta);
82
83                 __schedstat_set(stats->block_start, 0);
84                 __schedstat_add(stats->sum_sleep_runtime, delta);
85                 __schedstat_add(stats->sum_block_runtime, delta);
86
87                 if (p) {
88                         if (p->in_iowait) {
89                                 __schedstat_add(stats->iowait_sum, delta);
90                                 __schedstat_inc(stats->iowait_count);
91                                 trace_sched_stat_iowait(p, delta);
92                         }
93
94                         trace_sched_stat_blocked(p, delta);
95
96                         /*
97                          * Blocking time is in units of nanosecs, so shift by
98                          * 20 to get a milliseconds-range estimation of the
99                          * amount of time that the task spent sleeping:
100                          */
101                         if (unlikely(prof_on == SLEEP_PROFILING)) {
102                                 profile_hits(SLEEP_PROFILING,
103                                              (void *)get_wchan(p),
104                                              delta >> 20);
105                         }
106                         account_scheduler_latency(p, delta >> 10, 0);
107                 }
108         }
109 }
110
111 /*
112  * Current schedstat API version.
113  *
114  * Bump this up when changing the output format or the meaning of an existing
115  * format, so that tools can adapt (or abort)
116  */
117 #define SCHEDSTAT_VERSION 15
118
119 static int show_schedstat(struct seq_file *seq, void *v)
120 {
121         int cpu;
122
123         if (v == (void *)1) {
124                 seq_printf(seq, "version %d\n", SCHEDSTAT_VERSION);
125                 seq_printf(seq, "timestamp %lu\n", jiffies);
126         } else {
127                 struct rq *rq;
128 #ifdef CONFIG_SMP
129                 struct sched_domain *sd;
130                 int dcount = 0;
131 #endif
132                 cpu = (unsigned long)(v - 2);
133                 rq = cpu_rq(cpu);
134
135                 /* runqueue-specific stats */
136                 seq_printf(seq,
137                     "cpu%d %u 0 %u %u %u %u %llu %llu %lu",
138                     cpu, rq->yld_count,
139                     rq->sched_count, rq->sched_goidle,
140                     rq->ttwu_count, rq->ttwu_local,
141                     rq->rq_cpu_time,
142                     rq->rq_sched_info.run_delay, rq->rq_sched_info.pcount);
143
144                 seq_printf(seq, "\n");
145
146 #ifdef CONFIG_SMP
147                 /* domain-specific stats */
148                 rcu_read_lock();
149                 for_each_domain(cpu, sd) {
150                         enum cpu_idle_type itype;
151
152                         seq_printf(seq, "domain%d %*pb", dcount++,
153                                    cpumask_pr_args(sched_domain_span(sd)));
154                         for (itype = CPU_IDLE; itype < CPU_MAX_IDLE_TYPES;
155                                         itype++) {
156                                 seq_printf(seq, " %u %u %u %u %u %u %u %u",
157                                     sd->lb_count[itype],
158                                     sd->lb_balanced[itype],
159                                     sd->lb_failed[itype],
160                                     sd->lb_imbalance[itype],
161                                     sd->lb_gained[itype],
162                                     sd->lb_hot_gained[itype],
163                                     sd->lb_nobusyq[itype],
164                                     sd->lb_nobusyg[itype]);
165                         }
166                         seq_printf(seq,
167                                    " %u %u %u %u %u %u %u %u %u %u %u %u\n",
168                             sd->alb_count, sd->alb_failed, sd->alb_pushed,
169                             sd->sbe_count, sd->sbe_balanced, sd->sbe_pushed,
170                             sd->sbf_count, sd->sbf_balanced, sd->sbf_pushed,
171                             sd->ttwu_wake_remote, sd->ttwu_move_affine,
172                             sd->ttwu_move_balance);
173                 }
174                 rcu_read_unlock();
175 #endif
176         }
177         return 0;
178 }
179
180 /*
181  * This iterator needs some explanation.
182  * It returns 1 for the header position.
183  * This means 2 is cpu 0.
184  * In a hotplugged system some CPUs, including cpu 0, may be missing so we have
185  * to use cpumask_* to iterate over the CPUs.
186  */
187 static void *schedstat_start(struct seq_file *file, loff_t *offset)
188 {
189         unsigned long n = *offset;
190
191         if (n == 0)
192                 return (void *) 1;
193
194         n--;
195
196         if (n > 0)
197                 n = cpumask_next(n - 1, cpu_online_mask);
198         else
199                 n = cpumask_first(cpu_online_mask);
200
201         *offset = n + 1;
202
203         if (n < nr_cpu_ids)
204                 return (void *)(unsigned long)(n + 2);
205
206         return NULL;
207 }
208
209 static void *schedstat_next(struct seq_file *file, void *data, loff_t *offset)
210 {
211         (*offset)++;
212
213         return schedstat_start(file, offset);
214 }
215
216 static void schedstat_stop(struct seq_file *file, void *data)
217 {
218 }
219
220 static const struct seq_operations schedstat_sops = {
221         .start = schedstat_start,
222         .next  = schedstat_next,
223         .stop  = schedstat_stop,
224         .show  = show_schedstat,
225 };
226
227 static int __init proc_schedstat_init(void)
228 {
229         proc_create_seq("schedstat", 0, NULL, &schedstat_sops);
230         return 0;
231 }
232 subsys_initcall(proc_schedstat_init);