blob: f4574e969d31f17ef61c388e90e015766fc70675 [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
Connor O'Brien6e7b83d2018-01-31 18:11:57 -080032static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */
Connor O'Brienfcb3db12018-02-06 13:30:27 -080033static DEFINE_SPINLOCK(uid_lock); /* uid_hash_table */
34
Connor O'Brien605e2ec2018-10-05 19:20:54 -070035struct concurrent_times {
36 atomic64_t active[NR_CPUS];
37 atomic64_t policy[NR_CPUS];
38};
39
Connor O'Brienfcb3db12018-02-06 13:30:27 -080040struct uid_entry {
41 uid_t uid;
42 unsigned int max_state;
43 struct hlist_node hash;
44 struct rcu_head rcu;
Connor O'Brien605e2ec2018-10-05 19:20:54 -070045 struct concurrent_times *concurrent_times;
Connor O'Brienfcb3db12018-02-06 13:30:27 -080046 u64 time_in_state[0];
47};
Connor O'Brien6e7b83d2018-01-31 18:11:57 -080048
49/**
50 * struct cpu_freqs - per-cpu frequency information
51 * @offset: start of these freqs' stats in task time_in_state array
52 * @max_state: number of entries in freq_table
53 * @last_index: index in freq_table of last frequency switched to
54 * @freq_table: list of available frequencies
55 */
56struct cpu_freqs {
57 unsigned int offset;
58 unsigned int max_state;
59 unsigned int last_index;
60 unsigned int freq_table[0];
61};
62
63static struct cpu_freqs *all_freqs[NR_CPUS];
64
65static unsigned int next_offset;
66
Connor O'Brienf17f4fb2018-01-22 18:28:08 -080067
68/* Caller must hold rcu_read_lock() */
69static struct uid_entry *find_uid_entry_rcu(uid_t uid)
70{
71 struct uid_entry *uid_entry;
72
73 hash_for_each_possible_rcu(uid_hash_table, uid_entry, hash, uid) {
74 if (uid_entry->uid == uid)
75 return uid_entry;
76 }
77 return NULL;
78}
79
Connor O'Brienfcb3db12018-02-06 13:30:27 -080080/* Caller must hold uid lock */
81static struct uid_entry *find_uid_entry_locked(uid_t uid)
82{
83 struct uid_entry *uid_entry;
84
85 hash_for_each_possible(uid_hash_table, uid_entry, hash, uid) {
86 if (uid_entry->uid == uid)
87 return uid_entry;
88 }
89 return NULL;
90}
91
92/* Caller must hold uid lock */
93static struct uid_entry *find_or_register_uid_locked(uid_t uid)
94{
95 struct uid_entry *uid_entry, *temp;
Connor O'Brien605e2ec2018-10-05 19:20:54 -070096 struct concurrent_times *times;
Connor O'Brienfcb3db12018-02-06 13:30:27 -080097 unsigned int max_state = READ_ONCE(next_offset);
98 size_t alloc_size = sizeof(*uid_entry) + max_state *
99 sizeof(uid_entry->time_in_state[0]);
100
101 uid_entry = find_uid_entry_locked(uid);
102 if (uid_entry) {
103 if (uid_entry->max_state == max_state)
104 return uid_entry;
105 /* uid_entry->time_in_state is too small to track all freqs, so
106 * expand it.
107 */
108 temp = __krealloc(uid_entry, alloc_size, GFP_ATOMIC);
109 if (!temp)
110 return uid_entry;
111 temp->max_state = max_state;
112 memset(temp->time_in_state + uid_entry->max_state, 0,
113 (max_state - uid_entry->max_state) *
114 sizeof(uid_entry->time_in_state[0]));
115 if (temp != uid_entry) {
116 hlist_replace_rcu(&uid_entry->hash, &temp->hash);
117 kfree_rcu(uid_entry, rcu);
118 }
119 return temp;
120 }
121
122 uid_entry = kzalloc(alloc_size, GFP_ATOMIC);
123 if (!uid_entry)
124 return NULL;
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700125 times = kzalloc(sizeof(*times), GFP_ATOMIC);
126 if (!times) {
127 kfree(uid_entry);
128 return NULL;
129 }
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800130
131 uid_entry->uid = uid;
132 uid_entry->max_state = max_state;
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700133 uid_entry->concurrent_times = times;
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800134
135 hash_add_rcu(uid_hash_table, &uid_entry->hash, uid);
136
137 return uid_entry;
138}
139
Connor O'Brienf17f4fb2018-01-22 18:28:08 -0800140static int single_uid_time_in_state_show(struct seq_file *m, void *ptr)
141{
142 struct uid_entry *uid_entry;
143 unsigned int i;
Connor O'Brienf17f4fb2018-01-22 18:28:08 -0800144 uid_t uid = from_kuid_munged(current_user_ns(), *(kuid_t *)m->private);
145
146 if (uid == overflowuid)
147 return -EINVAL;
148
149 rcu_read_lock();
150
151 uid_entry = find_uid_entry_rcu(uid);
152 if (!uid_entry) {
153 rcu_read_unlock();
154 return 0;
155 }
156
157 for (i = 0; i < uid_entry->max_state; ++i) {
Connor O'Brien3f33df22019-03-01 15:40:30 -0800158 u64 time = cputime_to_clock_t(uid_entry->time_in_state[i]);
Connor O'Brienf17f4fb2018-01-22 18:28:08 -0800159 seq_write(m, &time, sizeof(time));
160 }
161
162 rcu_read_unlock();
163
164 return 0;
165}
166
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800167static void *uid_seq_start(struct seq_file *seq, loff_t *pos)
168{
169 if (*pos >= HASH_SIZE(uid_hash_table))
170 return NULL;
171
172 return &uid_hash_table[*pos];
173}
174
175static void *uid_seq_next(struct seq_file *seq, void *v, loff_t *pos)
176{
Connor O'Brienc68876d2019-02-20 12:17:48 -0800177 do {
178 (*pos)++;
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800179
Connor O'Brienc68876d2019-02-20 12:17:48 -0800180 if (*pos >= HASH_SIZE(uid_hash_table))
181 return NULL;
182 } while (hlist_empty(&uid_hash_table[*pos]));
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800183
184 return &uid_hash_table[*pos];
185}
186
187static void uid_seq_stop(struct seq_file *seq, void *v) { }
188
189static int uid_time_in_state_seq_show(struct seq_file *m, void *v)
190{
191 struct uid_entry *uid_entry;
192 struct cpu_freqs *freqs, *last_freqs = NULL;
193 int i, cpu;
194
195 if (v == uid_hash_table) {
196 seq_puts(m, "uid:");
197 for_each_possible_cpu(cpu) {
198 freqs = all_freqs[cpu];
199 if (!freqs || freqs == last_freqs)
200 continue;
201 last_freqs = freqs;
Connor O'Brien7cd95612018-04-03 16:05:37 -0700202 for (i = 0; i < freqs->max_state; i++) {
Connor O'Brienc68876d2019-02-20 12:17:48 -0800203 seq_put_decimal_ull(m, " ",
204 freqs->freq_table[i]);
Connor O'Brien7cd95612018-04-03 16:05:37 -0700205 }
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800206 }
207 seq_putc(m, '\n');
208 }
209
210 rcu_read_lock();
211
212 hlist_for_each_entry_rcu(uid_entry, (struct hlist_head *)v, hash) {
Connor O'Brienc68876d2019-02-20 12:17:48 -0800213 if (uid_entry->max_state) {
214 seq_put_decimal_ull(m, "", uid_entry->uid);
215 seq_putc(m, ':');
216 }
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800217 for (i = 0; i < uid_entry->max_state; ++i) {
Connor O'Brien3f33df22019-03-01 15:40:30 -0800218 u64 time =
219 cputime_to_clock_t(uid_entry->time_in_state[i]);
Connor O'Brienc68876d2019-02-20 12:17:48 -0800220 seq_put_decimal_ull(m, " ", time);
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800221 }
222 if (uid_entry->max_state)
223 seq_putc(m, '\n');
224 }
225
226 rcu_read_unlock();
227 return 0;
228}
229
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700230static int concurrent_time_seq_show(struct seq_file *m, void *v,
231 atomic64_t *(*get_times)(struct concurrent_times *))
232{
233 struct uid_entry *uid_entry;
234 int i, num_possible_cpus = num_possible_cpus();
235
236 rcu_read_lock();
237
238 hlist_for_each_entry_rcu(uid_entry, (struct hlist_head *)v, hash) {
239 atomic64_t *times = get_times(uid_entry->concurrent_times);
240
241 seq_put_decimal_ull(m, "", (u64)uid_entry->uid);
242 seq_putc(m, ':');
243
244 for (i = 0; i < num_possible_cpus; ++i) {
245 u64 time = cputime_to_clock_t(atomic64_read(&times[i]));
246
247 seq_put_decimal_ull(m, " ", time);
248 }
249 seq_putc(m, '\n');
250 }
251
252 rcu_read_unlock();
253
254 return 0;
255}
256
257static inline atomic64_t *get_active_times(struct concurrent_times *times)
258{
259 return times->active;
260}
261
262static int concurrent_active_time_seq_show(struct seq_file *m, void *v)
263{
264 if (v == uid_hash_table) {
265 seq_put_decimal_ull(m, "cpus: ", num_possible_cpus());
266 seq_putc(m, '\n');
267 }
268
269 return concurrent_time_seq_show(m, v, get_active_times);
270}
271
272static inline atomic64_t *get_policy_times(struct concurrent_times *times)
273{
274 return times->policy;
275}
276
277static int concurrent_policy_time_seq_show(struct seq_file *m, void *v)
278{
279 int i;
280 struct cpu_freqs *freqs, *last_freqs = NULL;
281
282 if (v == uid_hash_table) {
283 int cnt = 0;
284
285 for_each_possible_cpu(i) {
286 freqs = all_freqs[i];
287 if (!freqs)
288 continue;
289 if (freqs != last_freqs) {
290 if (last_freqs) {
291 seq_put_decimal_ull(m, ": ", cnt);
292 seq_putc(m, ' ');
293 cnt = 0;
294 }
295 seq_put_decimal_ull(m, "policy", i);
296
297 last_freqs = freqs;
298 }
299 cnt++;
300 }
301 if (last_freqs) {
302 seq_put_decimal_ull(m, ": ", cnt);
303 seq_putc(m, '\n');
304 }
305 }
306
307 return concurrent_time_seq_show(m, v, get_policy_times);
308}
309
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800310void cpufreq_task_times_init(struct task_struct *p)
311{
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800312 unsigned long flags;
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800313
314 spin_lock_irqsave(&task_time_in_state_lock, flags);
315 p->time_in_state = NULL;
316 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
317 p->max_state = 0;
Sultan Alsawaf47bbcd62018-06-03 10:47:51 -0700318}
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800319
Sultan Alsawaf47bbcd62018-06-03 10:47:51 -0700320void cpufreq_task_times_alloc(struct task_struct *p)
321{
322 void *temp;
323 unsigned long flags;
324 unsigned int max_state = READ_ONCE(next_offset);
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800325
326 /* We use one array to avoid multiple allocs per task */
327 temp = kcalloc(max_state, sizeof(p->time_in_state[0]), GFP_ATOMIC);
328 if (!temp)
329 return;
330
331 spin_lock_irqsave(&task_time_in_state_lock, flags);
332 p->time_in_state = temp;
333 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
334 p->max_state = max_state;
335}
336
337/* Caller must hold task_time_in_state_lock */
338static int cpufreq_task_times_realloc_locked(struct task_struct *p)
339{
340 void *temp;
341 unsigned int max_state = READ_ONCE(next_offset);
342
343 temp = krealloc(p->time_in_state, max_state * sizeof(u64), GFP_ATOMIC);
344 if (!temp)
345 return -ENOMEM;
346 p->time_in_state = temp;
347 memset(p->time_in_state + p->max_state, 0,
348 (max_state - p->max_state) * sizeof(u64));
349 p->max_state = max_state;
350 return 0;
351}
352
353void cpufreq_task_times_exit(struct task_struct *p)
354{
355 unsigned long flags;
356 void *temp;
357
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800358 if (!p->time_in_state)
359 return;
360
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800361 spin_lock_irqsave(&task_time_in_state_lock, flags);
362 temp = p->time_in_state;
363 p->time_in_state = NULL;
364 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
365 kfree(temp);
366}
367
368int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns,
369 struct pid *pid, struct task_struct *p)
370{
371 unsigned int cpu, i;
372 cputime_t cputime;
373 unsigned long flags;
374 struct cpu_freqs *freqs;
375 struct cpu_freqs *last_freqs = NULL;
376
377 spin_lock_irqsave(&task_time_in_state_lock, flags);
378 for_each_possible_cpu(cpu) {
379 freqs = all_freqs[cpu];
380 if (!freqs || freqs == last_freqs)
381 continue;
382 last_freqs = freqs;
383
384 seq_printf(m, "cpu%u\n", cpu);
385 for (i = 0; i < freqs->max_state; i++) {
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800386 cputime = 0;
387 if (freqs->offset + i < p->max_state &&
388 p->time_in_state)
389 cputime = p->time_in_state[freqs->offset + i];
390 seq_printf(m, "%u %lu\n", freqs->freq_table[i],
391 (unsigned long)cputime_to_clock_t(cputime));
392 }
393 }
394 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
395 return 0;
396}
397
398void cpufreq_acct_update_power(struct task_struct *p, cputime_t cputime)
399{
400 unsigned long flags;
401 unsigned int state;
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700402 unsigned int active_cpu_cnt = 0;
403 unsigned int policy_cpu_cnt = 0;
404 unsigned int policy_first_cpu;
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800405 struct uid_entry *uid_entry;
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800406 struct cpu_freqs *freqs = all_freqs[task_cpu(p)];
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700407 struct cpufreq_policy *policy;
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800408 uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700409 int cpu = 0;
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800410
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700411 if (!freqs || is_idle_task(p) || p->flags & PF_EXITING)
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800412 return;
413
414 state = freqs->offset + READ_ONCE(freqs->last_index);
415
416 spin_lock_irqsave(&task_time_in_state_lock, flags);
417 if ((state < p->max_state || !cpufreq_task_times_realloc_locked(p)) &&
418 p->time_in_state)
419 p->time_in_state[state] += cputime;
420 spin_unlock_irqrestore(&task_time_in_state_lock, flags);
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800421
422 spin_lock_irqsave(&uid_lock, flags);
423 uid_entry = find_or_register_uid_locked(uid);
424 if (uid_entry && state < uid_entry->max_state)
425 uid_entry->time_in_state[state] += cputime;
426 spin_unlock_irqrestore(&uid_lock, flags);
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700427
428 rcu_read_lock();
429 uid_entry = find_uid_entry_rcu(uid);
430 if (!uid_entry) {
431 rcu_read_unlock();
432 return;
433 }
434
435 for_each_possible_cpu(cpu)
436 if (!idle_cpu(cpu))
437 ++active_cpu_cnt;
438
439 atomic64_add(cputime,
440 &uid_entry->concurrent_times->active[active_cpu_cnt - 1]);
441
442 policy = cpufreq_cpu_get(task_cpu(p));
443 if (!policy) {
444 /*
445 * This CPU may have just come up and not have a cpufreq policy
446 * yet.
447 */
448 rcu_read_unlock();
449 return;
450 }
451
452 for_each_cpu(cpu, policy->related_cpus)
453 if (!idle_cpu(cpu))
454 ++policy_cpu_cnt;
455
456 policy_first_cpu = cpumask_first(policy->related_cpus);
457 cpufreq_cpu_put(policy);
458
459 atomic64_add(cputime,
460 &uid_entry->concurrent_times->policy[policy_first_cpu +
461 policy_cpu_cnt - 1]);
462 rcu_read_unlock();
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800463}
464
Connor O'Brien3f33df22019-03-01 15:40:30 -0800465static int cpufreq_times_get_index(struct cpu_freqs *freqs, unsigned int freq)
466{
467 int index;
468 for (index = 0; index < freqs->max_state; ++index) {
469 if (freqs->freq_table[index] == freq)
470 return index;
471 }
472 return -1;
473}
474
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800475void cpufreq_times_create_policy(struct cpufreq_policy *policy)
476{
Connor O'Brien3f33df22019-03-01 15:40:30 -0800477 int cpu, index = 0;
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800478 unsigned int count = 0;
479 struct cpufreq_frequency_table *pos, *table;
480 struct cpu_freqs *freqs;
481 void *tmp;
482
483 if (all_freqs[policy->cpu])
484 return;
485
486 table = policy->freq_table;
487 if (!table)
488 return;
489
Connor O'Brien3f33df22019-03-01 15:40:30 -0800490 cpufreq_for_each_valid_entry(pos, table)
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800491 count++;
492
493 tmp = kzalloc(sizeof(*freqs) + sizeof(freqs->freq_table[0]) * count,
494 GFP_KERNEL);
495 if (!tmp)
496 return;
497
498 freqs = tmp;
499 freqs->max_state = count;
500
Connor O'Brien3f33df22019-03-01 15:40:30 -0800501 cpufreq_for_each_valid_entry(pos, table)
502 freqs->freq_table[index++] = pos->frequency;
503
504 index = cpufreq_times_get_index(freqs, policy->cur);
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800505 if (index >= 0)
506 WRITE_ONCE(freqs->last_index, index);
507
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800508 freqs->offset = next_offset;
509 WRITE_ONCE(next_offset, freqs->offset + count);
510 for_each_cpu(cpu, policy->related_cpus)
511 all_freqs[cpu] = freqs;
512}
513
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700514static void uid_entry_reclaim(struct rcu_head *rcu)
515{
516 struct uid_entry *uid_entry = container_of(rcu, struct uid_entry, rcu);
517
518 kfree(uid_entry->concurrent_times);
519 kfree(uid_entry);
520}
521
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800522void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end)
523{
524 struct uid_entry *uid_entry;
525 struct hlist_node *tmp;
526 unsigned long flags;
527
528 spin_lock_irqsave(&uid_lock, flags);
529
530 for (; uid_start <= uid_end; uid_start++) {
531 hash_for_each_possible_safe(uid_hash_table, uid_entry, tmp,
532 hash, uid_start) {
533 if (uid_start == uid_entry->uid) {
534 hash_del_rcu(&uid_entry->hash);
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700535 call_rcu(&uid_entry->rcu, uid_entry_reclaim);
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800536 }
537 }
538 }
539
540 spin_unlock_irqrestore(&uid_lock, flags);
541}
542
Connor O'Brien2f0bc4b2019-02-08 12:30:41 -0800543void cpufreq_times_record_transition(struct cpufreq_policy *policy,
544 unsigned int new_freq)
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800545{
546 int index;
Connor O'Brien2f0bc4b2019-02-08 12:30:41 -0800547 struct cpu_freqs *freqs = all_freqs[policy->cpu];
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800548 if (!freqs)
549 return;
550
Connor O'Brien3f33df22019-03-01 15:40:30 -0800551 index = cpufreq_times_get_index(freqs, new_freq);
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800552 if (index >= 0)
553 WRITE_ONCE(freqs->last_index, index);
Connor O'Brien6e7b83d2018-01-31 18:11:57 -0800554}
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800555
556static const struct seq_operations uid_time_in_state_seq_ops = {
557 .start = uid_seq_start,
558 .next = uid_seq_next,
559 .stop = uid_seq_stop,
560 .show = uid_time_in_state_seq_show,
561};
562
563static int uid_time_in_state_open(struct inode *inode, struct file *file)
564{
565 return seq_open(file, &uid_time_in_state_seq_ops);
566}
567
Connor O'Brienf17f4fb2018-01-22 18:28:08 -0800568int single_uid_time_in_state_open(struct inode *inode, struct file *file)
569{
570 return single_open(file, single_uid_time_in_state_show,
571 &(inode->i_uid));
572}
573
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800574static const struct file_operations uid_time_in_state_fops = {
575 .open = uid_time_in_state_open,
576 .read = seq_read,
577 .llseek = seq_lseek,
578 .release = seq_release,
579};
580
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700581static const struct seq_operations concurrent_active_time_seq_ops = {
582 .start = uid_seq_start,
583 .next = uid_seq_next,
584 .stop = uid_seq_stop,
585 .show = concurrent_active_time_seq_show,
586};
587
588static int concurrent_active_time_open(struct inode *inode, struct file *file)
589{
590 return seq_open(file, &concurrent_active_time_seq_ops);
591}
592
593static const struct file_operations concurrent_active_time_fops = {
594 .open = concurrent_active_time_open,
595 .read = seq_read,
596 .llseek = seq_lseek,
597 .release = seq_release,
598};
599
600static const struct seq_operations concurrent_policy_time_seq_ops = {
601 .start = uid_seq_start,
602 .next = uid_seq_next,
603 .stop = uid_seq_stop,
604 .show = concurrent_policy_time_seq_show,
605};
606
607static int concurrent_policy_time_open(struct inode *inode, struct file *file)
608{
609 return seq_open(file, &concurrent_policy_time_seq_ops);
610}
611
612static const struct file_operations concurrent_policy_time_fops = {
613 .open = concurrent_policy_time_open,
614 .read = seq_read,
615 .llseek = seq_lseek,
616 .release = seq_release,
617};
618
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800619static int __init cpufreq_times_init(void)
620{
621 proc_create_data("uid_time_in_state", 0444, NULL,
622 &uid_time_in_state_fops, NULL);
623
Connor O'Brien605e2ec2018-10-05 19:20:54 -0700624 proc_create_data("uid_concurrent_active_time", 0444, NULL,
625 &concurrent_active_time_fops, NULL);
626
627 proc_create_data("uid_concurrent_policy_time", 0444, NULL,
628 &concurrent_policy_time_fops, NULL);
629
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800630 return 0;
631}
632
633early_initcall(cpufreq_times_init);