blob: d4c139156e6fa8e431bd4b024399c4e157cf66ee [file] [log] [blame]
Connor O'Brien6e7b83d2018-01-31 18:11:57 -08001/* drivers/cpufreq/cpufreq_times.c
2 *
3 * Copyright (C) 2018 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/cpufreq.h>
17#include <linux/cpufreq_times.h>
18#include <linux/cputime.h>
Connor O'Brienfcb3db12018-02-06 13:30:27 -080019#include <linux/hashtable.h>
20#include <linux/init.h>
21#include <linux/proc_fs.h>
Connor O'Brien6e7b83d2018-01-31 18:11:57 -080022#include <linux/sched.h>
23#include <linux/seq_file.h>
24#include <linux/slab.h>
25#include <linux/spinlock.h>
26#include <linux/threads.h>
27
Connor O'Brienfcb3db12018-02-06 13:30:27 -080028#define UID_HASH_BITS 10
29
30static DECLARE_HASHTABLE(uid_hash_table, UID_HASH_BITS);
31
Tengfei Fanbfea4ae2019-04-18 13:55:38 +080032/* task->time_in_state */
33static __cacheline_aligned_in_smp DEFINE_SPINLOCK(task_time_in_state_lock);
34/* uid_hash_table */
35static __cacheline_aligned_in_smp DEFINE_SPINLOCK(uid_lock);
Connor O'Brienfcb3db12018-02-06 13:30:27 -080036
Connor O'Brien605e2ec2018-10-05 19:20:54 -070037struct concurrent_times {
38 atomic64_t active[NR_CPUS];
39 atomic64_t policy[NR_CPUS];
40};
41
Connor O'Brienfcb3db12018-02-06 13:30:27 -080042struct uid_entry {
43 uid_t uid;
44 unsigned int max_state;
45 struct hlist_node hash;
46 struct rcu_head rcu;
Connor O'Brien605e2ec2018-10-05 19:20:54 -070047 struct concurrent_times *concurrent_times;
Connor O'Brienfcb3db12018-02-06 13:30:27 -080048 u64 time_in_state[0];
49};
Connor O'Brien6e7b83d2018-01-31 18:11:57 -080050
51/**
52 * struct cpu_freqs - per-cpu frequency information
53 * @offset: start of these freqs' stats in task time_in_state array
54 * @max_state: number of entries in freq_table
55 * @last_index: index in freq_table of last frequency switched to
56 * @freq_table: list of available frequencies
57 */
58struct cpu_freqs {
59 unsigned int offset;
60 unsigned int max_state;
61 unsigned int last_index;
62 unsigned int freq_table[0];
63};
64
65static struct cpu_freqs *all_freqs[NR_CPUS];
66
67static unsigned int next_offset;
68
Connor O'Brienf17f4fb2018-01-22 18:28:08 -080069
70/* Caller must hold rcu_read_lock() */
71static struct uid_entry *find_uid_entry_rcu(uid_t uid)
72{
73 struct uid_entry *uid_entry;
74
75 hash_for_each_possible_rcu(uid_hash_table, uid_entry, hash, uid) {
76 if (uid_entry->uid == uid)
77 return uid_entry;
78 }
79 return NULL;
80}
81
Connor O'Brienfcb3db12018-02-06 13:30:27 -080082/* Caller must hold uid lock */
83static struct uid_entry *find_uid_entry_locked(uid_t uid)
84{
85 struct uid_entry *uid_entry;
86
87 hash_for_each_possible(uid_hash_table, uid_entry, hash, uid) {
88 if (uid_entry->uid == uid)
89 return uid_entry;
90 }
91 return NULL;
92}
93
94/* Caller must hold uid lock */
95static struct uid_entry *find_or_register_uid_locked(uid_t uid)
96{
97 struct uid_entry *uid_entry, *temp;
Connor O'Brien605e2ec2018-10-05 19:20:54 -070098 struct concurrent_times *times;
Connor O'Brienfcb3db12018-02-06 13:30:27 -080099 unsigned int max_state = READ_ONCE(next_offset);
100 size_t alloc_size = sizeof(*uid_entry) + max_state *
101 sizeof(uid_entry->time_in_state[0]);
102
103 uid_entry = find_uid_entry_locked(uid);
104 if (uid_entry) {
105 if (uid_entry->max_state == max_state)
106 return uid_entry;
107 /* uid_entry->time_in_state is too small to track all freqs, so
108 * expand it.
109 */
110 temp = __krealloc(uid_entry, alloc_size, GFP_ATOMIC);
111 if (!temp)
112 return uid_entry;
113 temp->max_state = max_state;
114 memset(temp->time_in_state + uid_entry->max_state, 0,
115 (max_state - uid_entry->max_state) *
116 sizeof(uid_entry->time_in_state[0]));
117 if (temp != uid_entry) {
118 hlist_replace_rcu(&uid_entry->hash, &temp->hash);
119 kfree_rcu(uid_entry, rcu);
120 }
121 return temp;
122 }
123
124 uid_entry = kzalloc(alloc_size, GFP_ATOMIC);
125 if (!uid_entry)
126 return NULL;
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700127 times = kzalloc(sizeof(*times), GFP_ATOMIC);
128 if (!times) {
129 kfree(uid_entry);
130 return NULL;
131 }
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800132
133 uid_entry->uid = uid;
134 uid_entry->max_state = max_state;
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700135 uid_entry->concurrent_times = times;
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800136
137 hash_add_rcu(uid_hash_table, &uid_entry->hash, uid);
138
139 return uid_entry;
140}
141
Connor O'Brienf17f4fb2018-01-22 18:28:08 -0800142static int single_uid_time_in_state_show(struct seq_file *m, void *ptr)
143{
144 struct uid_entry *uid_entry;
145 unsigned int i;
Connor O'Brienf17f4fb2018-01-22 18:28:08 -0800146 uid_t uid = from_kuid_munged(current_user_ns(), *(kuid_t *)m->private);
147
148 if (uid == overflowuid)
149 return -EINVAL;
150
151 rcu_read_lock();
152
153 uid_entry = find_uid_entry_rcu(uid);
154 if (!uid_entry) {
155 rcu_read_unlock();
156 return 0;
157 }
158
159 for (i = 0; i < uid_entry->max_state; ++i) {
Connor O'Brien3f33df22019-03-01 15:40:30 -0800160 u64 time = cputime_to_clock_t(uid_entry->time_in_state[i]);
Connor O'Brienf17f4fb2018-01-22 18:28:08 -0800161 seq_write(m, &time, sizeof(time));
162 }
163
164 rcu_read_unlock();
165
166 return 0;
167}
168
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800169static void *uid_seq_start(struct seq_file *seq, loff_t *pos)
170{
171 if (*pos >= HASH_SIZE(uid_hash_table))
172 return NULL;
173
174 return &uid_hash_table[*pos];
175}
176
177static void *uid_seq_next(struct seq_file *seq, void *v, loff_t *pos)
178{
Connor O'Brienc68876d2019-02-20 12:17:48 -0800179 do {
180 (*pos)++;
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800181
Connor O'Brienc68876d2019-02-20 12:17:48 -0800182 if (*pos >= HASH_SIZE(uid_hash_table))
183 return NULL;
184 } while (hlist_empty(&uid_hash_table[*pos]));
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800185
186 return &uid_hash_table[*pos];
187}
188
189static void uid_seq_stop(struct seq_file *seq, void *v) { }
190
191static int uid_time_in_state_seq_show(struct seq_file *m, void *v)
192{
193 struct uid_entry *uid_entry;
194 struct cpu_freqs *freqs, *last_freqs = NULL;
195 int i, cpu;
196
197 if (v == uid_hash_table) {
198 seq_puts(m, "uid:");
199 for_each_possible_cpu(cpu) {
200 freqs = all_freqs[cpu];
201 if (!freqs || freqs == last_freqs)
202 continue;
203 last_freqs = freqs;
Connor O'Brien7cd95612018-04-03 16:05:37 -0700204 for (i = 0; i < freqs->max_state; i++) {
Connor O'Brienc68876d2019-02-20 12:17:48 -0800205 seq_put_decimal_ull(m, " ",
206 freqs->freq_table[i]);
Connor O'Brien7cd95612018-04-03 16:05:37 -0700207 }
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800208 }
209 seq_putc(m, '\n');
210 }
211
212 rcu_read_lock();
213
214 hlist_for_each_entry_rcu(uid_entry, (struct hlist_head *)v, hash) {
Connor O'Brienc68876d2019-02-20 12:17:48 -0800215 if (uid_entry->max_state) {
216 seq_put_decimal_ull(m, "", uid_entry->uid);
217 seq_putc(m, ':');
218 }
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800219 for (i = 0; i < uid_entry->max_state; ++i) {
Connor O'Brien3f33df22019-03-01 15:40:30 -0800220 u64 time =
221 cputime_to_clock_t(uid_entry->time_in_state[i]);
Connor O'Brienc68876d2019-02-20 12:17:48 -0800222 seq_put_decimal_ull(m, " ", time);
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800223 }
224 if (uid_entry->max_state)
225 seq_putc(m, '\n');
226 }
227
228 rcu_read_unlock();
229 return 0;
230}
231
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700232static int concurrent_time_seq_show(struct seq_file *m, void *v,
233 atomic64_t *(*get_times)(struct concurrent_times *))
234{
235 struct uid_entry *uid_entry;
236 int i, num_possible_cpus = num_possible_cpus();
237
238 rcu_read_lock();
239
240 hlist_for_each_entry_rcu(uid_entry, (struct hlist_head *)v, hash) {
241 atomic64_t *times = get_times(uid_entry->concurrent_times);
242
243 seq_put_decimal_ull(m, "", (u64)uid_entry->uid);
244 seq_putc(m, ':');
245
246 for (i = 0; i < num_possible_cpus; ++i) {
247 u64 time = cputime_to_clock_t(atomic64_read(&times[i]));
248
249 seq_put_decimal_ull(m, " ", time);
250 }
251 seq_putc(m, '\n');
252 }
253
254 rcu_read_unlock();
255
256 return 0;
257}
258
259static inline atomic64_t *get_active_times(struct concurrent_times *times)
260{
261 return times->active;
262}
263
264static int concurrent_active_time_seq_show(struct seq_file *m, void *v)
265{
266 if (v == uid_hash_table) {
267 seq_put_decimal_ull(m, "cpus: ", num_possible_cpus());
268 seq_putc(m, '\n');
269 }
270
271 return concurrent_time_seq_show(m, v, get_active_times);
272}
273
274static inline atomic64_t *get_policy_times(struct concurrent_times *times)
275{
276 return times->policy;
277}
278
279static int concurrent_policy_time_seq_show(struct seq_file *m, void *v)
280{
281 int i;
282 struct cpu_freqs *freqs, *last_freqs = NULL;
283
284 if (v == uid_hash_table) {
285 int cnt = 0;
286
287 for_each_possible_cpu(i) {
288 freqs = all_freqs[i];
289 if (!freqs)
290 continue;
291 if (freqs != last_freqs) {
292 if (last_freqs) {
293 seq_put_decimal_ull(m, ": ", cnt);
294 seq_putc(m, ' ');
295 cnt = 0;
296 }
297 seq_put_decimal_ull(m, "policy", i);
298
299 last_freqs = freqs;
300 }
301 cnt++;
302 }
303 if (last_freqs) {
304 seq_put_decimal_ull(m, ": ", cnt);
305 seq_putc(m, '\n');
306 }
307 }
308
309 return concurrent_time_seq_show(m, v, get_policy_times);
310}
311
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800312void cpufreq_task_times_init(struct task_struct *p)
313{
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800314 unsigned long flags;
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800315
316 spin_lock_irqsave(&task_time_in_state_lock, flags);
317 p->time_in_state = NULL;
318 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
319 p->max_state = 0;
Sultan Alsawaf47bbcd62018-06-03 10:47:51 -0700320}
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800321
Sultan Alsawaf47bbcd62018-06-03 10:47:51 -0700322void cpufreq_task_times_alloc(struct task_struct *p)
323{
324 void *temp;
325 unsigned long flags;
326 unsigned int max_state = READ_ONCE(next_offset);
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800327
328 /* We use one array to avoid multiple allocs per task */
329 temp = kcalloc(max_state, sizeof(p->time_in_state[0]), GFP_ATOMIC);
330 if (!temp)
331 return;
332
333 spin_lock_irqsave(&task_time_in_state_lock, flags);
334 p->time_in_state = temp;
335 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
336 p->max_state = max_state;
337}
338
339/* Caller must hold task_time_in_state_lock */
340static int cpufreq_task_times_realloc_locked(struct task_struct *p)
341{
342 void *temp;
343 unsigned int max_state = READ_ONCE(next_offset);
344
345 temp = krealloc(p->time_in_state, max_state * sizeof(u64), GFP_ATOMIC);
346 if (!temp)
347 return -ENOMEM;
348 p->time_in_state = temp;
349 memset(p->time_in_state + p->max_state, 0,
350 (max_state - p->max_state) * sizeof(u64));
351 p->max_state = max_state;
352 return 0;
353}
354
355void cpufreq_task_times_exit(struct task_struct *p)
356{
357 unsigned long flags;
358 void *temp;
359
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800360 if (!p->time_in_state)
361 return;
362
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800363 spin_lock_irqsave(&task_time_in_state_lock, flags);
364 temp = p->time_in_state;
365 p->time_in_state = NULL;
366 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
367 kfree(temp);
368}
369
370int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns,
371 struct pid *pid, struct task_struct *p)
372{
373 unsigned int cpu, i;
374 cputime_t cputime;
375 unsigned long flags;
376 struct cpu_freqs *freqs;
377 struct cpu_freqs *last_freqs = NULL;
378
379 spin_lock_irqsave(&task_time_in_state_lock, flags);
380 for_each_possible_cpu(cpu) {
381 freqs = all_freqs[cpu];
382 if (!freqs || freqs == last_freqs)
383 continue;
384 last_freqs = freqs;
385
386 seq_printf(m, "cpu%u\n", cpu);
387 for (i = 0; i < freqs->max_state; i++) {
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800388 cputime = 0;
389 if (freqs->offset + i < p->max_state &&
390 p->time_in_state)
391 cputime = p->time_in_state[freqs->offset + i];
392 seq_printf(m, "%u %lu\n", freqs->freq_table[i],
393 (unsigned long)cputime_to_clock_t(cputime));
394 }
395 }
396 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
397 return 0;
398}
399
400void cpufreq_acct_update_power(struct task_struct *p, cputime_t cputime)
401{
402 unsigned long flags;
403 unsigned int state;
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700404 unsigned int active_cpu_cnt = 0;
405 unsigned int policy_cpu_cnt = 0;
406 unsigned int policy_first_cpu;
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800407 struct uid_entry *uid_entry;
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800408 struct cpu_freqs *freqs = all_freqs[task_cpu(p)];
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700409 struct cpufreq_policy *policy;
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800410 uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700411 int cpu = 0;
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800412
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700413 if (!freqs || is_idle_task(p) || p->flags & PF_EXITING)
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800414 return;
415
416 state = freqs->offset + READ_ONCE(freqs->last_index);
417
418 spin_lock_irqsave(&task_time_in_state_lock, flags);
419 if ((state < p->max_state || !cpufreq_task_times_realloc_locked(p)) &&
420 p->time_in_state)
421 p->time_in_state[state] += cputime;
422 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800423
424 spin_lock_irqsave(&uid_lock, flags);
425 uid_entry = find_or_register_uid_locked(uid);
426 if (uid_entry && state < uid_entry->max_state)
427 uid_entry->time_in_state[state] += cputime;
428 spin_unlock_irqrestore(&uid_lock, flags);
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700429
430 rcu_read_lock();
431 uid_entry = find_uid_entry_rcu(uid);
432 if (!uid_entry) {
433 rcu_read_unlock();
434 return;
435 }
436
437 for_each_possible_cpu(cpu)
438 if (!idle_cpu(cpu))
439 ++active_cpu_cnt;
440
441 atomic64_add(cputime,
442 &uid_entry->concurrent_times->active[active_cpu_cnt - 1]);
443
444 policy = cpufreq_cpu_get(task_cpu(p));
445 if (!policy) {
446 /*
447 * This CPU may have just come up and not have a cpufreq policy
448 * yet.
449 */
450 rcu_read_unlock();
451 return;
452 }
453
454 for_each_cpu(cpu, policy->related_cpus)
455 if (!idle_cpu(cpu))
456 ++policy_cpu_cnt;
457
458 policy_first_cpu = cpumask_first(policy->related_cpus);
459 cpufreq_cpu_put(policy);
460
461 atomic64_add(cputime,
462 &uid_entry->concurrent_times->policy[policy_first_cpu +
463 policy_cpu_cnt - 1]);
464 rcu_read_unlock();
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800465}
466
Connor O'Brien3f33df22019-03-01 15:40:30 -0800467static int cpufreq_times_get_index(struct cpu_freqs *freqs, unsigned int freq)
468{
469 int index;
470 for (index = 0; index < freqs->max_state; ++index) {
471 if (freqs->freq_table[index] == freq)
472 return index;
473 }
474 return -1;
475}
476
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800477void cpufreq_times_create_policy(struct cpufreq_policy *policy)
478{
Connor O'Brien3f33df22019-03-01 15:40:30 -0800479 int cpu, index = 0;
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800480 unsigned int count = 0;
481 struct cpufreq_frequency_table *pos, *table;
482 struct cpu_freqs *freqs;
483 void *tmp;
484
485 if (all_freqs[policy->cpu])
486 return;
487
488 table = policy->freq_table;
489 if (!table)
490 return;
491
Connor O'Brien3f33df22019-03-01 15:40:30 -0800492 cpufreq_for_each_valid_entry(pos, table)
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800493 count++;
494
495 tmp = kzalloc(sizeof(*freqs) + sizeof(freqs->freq_table[0]) * count,
496 GFP_KERNEL);
497 if (!tmp)
498 return;
499
500 freqs = tmp;
501 freqs->max_state = count;
502
Connor O'Brien3f33df22019-03-01 15:40:30 -0800503 cpufreq_for_each_valid_entry(pos, table)
504 freqs->freq_table[index++] = pos->frequency;
505
506 index = cpufreq_times_get_index(freqs, policy->cur);
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800507 if (index >= 0)
508 WRITE_ONCE(freqs->last_index, index);
509
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800510 freqs->offset = next_offset;
511 WRITE_ONCE(next_offset, freqs->offset + count);
512 for_each_cpu(cpu, policy->related_cpus)
513 all_freqs[cpu] = freqs;
514}
515
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700516static void uid_entry_reclaim(struct rcu_head *rcu)
517{
518 struct uid_entry *uid_entry = container_of(rcu, struct uid_entry, rcu);
519
520 kfree(uid_entry->concurrent_times);
521 kfree(uid_entry);
522}
523
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800524void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end)
525{
526 struct uid_entry *uid_entry;
527 struct hlist_node *tmp;
528 unsigned long flags;
529
530 spin_lock_irqsave(&uid_lock, flags);
531
532 for (; uid_start <= uid_end; uid_start++) {
533 hash_for_each_possible_safe(uid_hash_table, uid_entry, tmp,
534 hash, uid_start) {
535 if (uid_start == uid_entry->uid) {
536 hash_del_rcu(&uid_entry->hash);
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700537 call_rcu(&uid_entry->rcu, uid_entry_reclaim);
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800538 }
539 }
540 }
541
542 spin_unlock_irqrestore(&uid_lock, flags);
543}
544
Connor O'Brien2f0bc4b2019-02-08 12:30:41 -0800545void cpufreq_times_record_transition(struct cpufreq_policy *policy,
546 unsigned int new_freq)
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800547{
548 int index;
Connor O'Brien2f0bc4b2019-02-08 12:30:41 -0800549 struct cpu_freqs *freqs = all_freqs[policy->cpu];
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800550 if (!freqs)
551 return;
552
Connor O'Brien3f33df22019-03-01 15:40:30 -0800553 index = cpufreq_times_get_index(freqs, new_freq);
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800554 if (index >= 0)
555 WRITE_ONCE(freqs->last_index, index);
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800556}
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800557
558static const struct seq_operations uid_time_in_state_seq_ops = {
559 .start = uid_seq_start,
560 .next = uid_seq_next,
561 .stop = uid_seq_stop,
562 .show = uid_time_in_state_seq_show,
563};
564
565static int uid_time_in_state_open(struct inode *inode, struct file *file)
566{
567 return seq_open(file, &uid_time_in_state_seq_ops);
568}
569
Connor O'Brienf17f4fb2018-01-22 18:28:08 -0800570int single_uid_time_in_state_open(struct inode *inode, struct file *file)
571{
572 return single_open(file, single_uid_time_in_state_show,
573 &(inode->i_uid));
574}
575
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800576static const struct file_operations uid_time_in_state_fops = {
577 .open = uid_time_in_state_open,
578 .read = seq_read,
579 .llseek = seq_lseek,
580 .release = seq_release,
581};
582
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700583static const struct seq_operations concurrent_active_time_seq_ops = {
584 .start = uid_seq_start,
585 .next = uid_seq_next,
586 .stop = uid_seq_stop,
587 .show = concurrent_active_time_seq_show,
588};
589
590static int concurrent_active_time_open(struct inode *inode, struct file *file)
591{
592 return seq_open(file, &concurrent_active_time_seq_ops);
593}
594
595static const struct file_operations concurrent_active_time_fops = {
596 .open = concurrent_active_time_open,
597 .read = seq_read,
598 .llseek = seq_lseek,
599 .release = seq_release,
600};
601
602static const struct seq_operations concurrent_policy_time_seq_ops = {
603 .start = uid_seq_start,
604 .next = uid_seq_next,
605 .stop = uid_seq_stop,
606 .show = concurrent_policy_time_seq_show,
607};
608
609static int concurrent_policy_time_open(struct inode *inode, struct file *file)
610{
611 return seq_open(file, &concurrent_policy_time_seq_ops);
612}
613
614static const struct file_operations concurrent_policy_time_fops = {
615 .open = concurrent_policy_time_open,
616 .read = seq_read,
617 .llseek = seq_lseek,
618 .release = seq_release,
619};
620
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800621static int __init cpufreq_times_init(void)
622{
623 proc_create_data("uid_time_in_state", 0444, NULL,
624 &uid_time_in_state_fops, NULL);
625
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700626 proc_create_data("uid_concurrent_active_time", 0444, NULL,
627 &concurrent_active_time_fops, NULL);
628
629 proc_create_data("uid_concurrent_policy_time", 0444, NULL,
630 &concurrent_policy_time_fops, NULL);
631
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800632 return 0;
633}
634
635early_initcall(cpufreq_times_init);