blob: 98de3ce62e3bbae58d3bd367b2d0be82658ff628 [file] [log] [blame]
Jin Qiana1ad2292015-09-07 14:55:10 +05301/* drivers/misc/uid_cputime.c
2 *
3 * Copyright (C) 2014 - 2015 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/atomic.h>
17#include <linux/err.h>
18#include <linux/hashtable.h>
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <linux/list.h>
22#include <linux/proc_fs.h>
23#include <linux/profile.h>
24#include <linux/sched.h>
25#include <linux/seq_file.h>
26#include <linux/slab.h>
27#include <linux/uaccess.h>
28
29#define UID_HASH_BITS 10
30DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
31
32static DEFINE_MUTEX(uid_lock);
33static struct proc_dir_entry *parent;
34
35struct uid_entry {
36 uid_t uid;
37 cputime_t utime;
38 cputime_t stime;
39 cputime_t active_utime;
40 cputime_t active_stime;
Ruchi Kandoif49d0f72015-04-17 16:52:54 -070041 unsigned long long active_power;
42 unsigned long long power;
Jin Qiana1ad2292015-09-07 14:55:10 +053043 struct hlist_node hash;
44};
45
46static struct uid_entry *find_uid_entry(uid_t uid)
47{
48 struct uid_entry *uid_entry;
Srinivasarao Pe168fec2015-09-24 13:08:20 +053049 struct hlist_node *node;
50 hash_for_each_possible(hash_table, uid_entry, node, hash, uid) {
Jin Qiana1ad2292015-09-07 14:55:10 +053051 if (uid_entry->uid == uid)
52 return uid_entry;
53 }
54 return NULL;
55}
56
57static struct uid_entry *find_or_register_uid(uid_t uid)
58{
59 struct uid_entry *uid_entry;
60
61 uid_entry = find_uid_entry(uid);
62 if (uid_entry)
63 return uid_entry;
64
65 uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
66 if (!uid_entry)
67 return NULL;
68
69 uid_entry->uid = uid;
70
71 hash_add(hash_table, &uid_entry->hash, uid);
72
73 return uid_entry;
74}
75
76static int uid_stat_show(struct seq_file *m, void *v)
77{
78 struct uid_entry *uid_entry;
Ruchi Kandoie4dd5832015-07-31 10:17:54 -070079 struct task_struct *task, *temp;
Jin Qiana1ad2292015-09-07 14:55:10 +053080 unsigned long bkt;
Srinivasarao Pe168fec2015-09-24 13:08:20 +053081 struct hlist_node *node;
Jin Qiana1ad2292015-09-07 14:55:10 +053082
83 mutex_lock(&uid_lock);
84
Srinivasarao Pe168fec2015-09-24 13:08:20 +053085 hash_for_each(hash_table, bkt, node, uid_entry, hash) {
Jin Qiana1ad2292015-09-07 14:55:10 +053086 uid_entry->active_stime = 0;
87 uid_entry->active_utime = 0;
Ruchi Kandoif49d0f72015-04-17 16:52:54 -070088 uid_entry->active_power = 0;
Jin Qiana1ad2292015-09-07 14:55:10 +053089 }
90
91 read_lock(&tasklist_lock);
Ruchi Kandoie4dd5832015-07-31 10:17:54 -070092 do_each_thread(temp, task) {
Jin Qiana1ad2292015-09-07 14:55:10 +053093 uid_entry = find_or_register_uid(task_uid(task));
94 if (!uid_entry) {
95 read_unlock(&tasklist_lock);
96 mutex_unlock(&uid_lock);
97 pr_err("%s: failed to find the uid_entry for uid %d\n",
98 __func__, task_uid(task));
99 return -ENOMEM;
100 }
Ruchi Kandoi1fee5762015-06-26 14:19:21 -0700101 /* if this task is exiting, we have already accounted for the
102 * time and power. */
103 if (task->cpu_power == ULLONG_MAX)
104 continue;
Srinivasarao Pe168fec2015-09-24 13:08:20 +0530105 uid_entry->active_utime += task->utime;
106 uid_entry->active_stime += task->stime;
Ruchi Kandoif49d0f72015-04-17 16:52:54 -0700107 uid_entry->active_power += task->cpu_power;
Ruchi Kandoie4dd5832015-07-31 10:17:54 -0700108 } while_each_thread(temp, task);
Jin Qiana1ad2292015-09-07 14:55:10 +0530109 read_unlock(&tasklist_lock);
110
Srinivasarao Pe168fec2015-09-24 13:08:20 +0530111 hash_for_each(hash_table, bkt, node, uid_entry, hash) {
Jin Qiana1ad2292015-09-07 14:55:10 +0530112 cputime_t total_utime = uid_entry->utime +
113 uid_entry->active_utime;
114 cputime_t total_stime = uid_entry->stime +
115 uid_entry->active_stime;
Ruchi Kandoif49d0f72015-04-17 16:52:54 -0700116 unsigned long long total_power = uid_entry->power +
117 uid_entry->active_power;
Jin Qianf4b41b72015-07-13 18:16:55 -0700118 seq_printf(m, "%d: %llu %llu %llu\n", uid_entry->uid,
119 (unsigned long long)jiffies_to_msecs(
120 cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
121 (unsigned long long)jiffies_to_msecs(
122 cputime_to_jiffies(total_stime)) * USEC_PER_MSEC,
123 total_power);
Jin Qiana1ad2292015-09-07 14:55:10 +0530124 }
125
126 mutex_unlock(&uid_lock);
127 return 0;
128}
129
130static int uid_stat_open(struct inode *inode, struct file *file)
131{
Srinivasarao Pe168fec2015-09-24 13:08:20 +0530132 return single_open(file, uid_stat_show, PDE(inode)->data);
Jin Qiana1ad2292015-09-07 14:55:10 +0530133}
134
135static const struct file_operations uid_stat_fops = {
136 .open = uid_stat_open,
137 .read = seq_read,
138 .llseek = seq_lseek,
139 .release = single_release,
140};
141
142static int uid_remove_open(struct inode *inode, struct file *file)
143{
144 return single_open(file, NULL, NULL);
145}
146
147static ssize_t uid_remove_write(struct file *file,
148 const char __user *buffer, size_t count, loff_t *ppos)
149{
150 struct uid_entry *uid_entry;
151 struct hlist_node *tmp;
152 char uids[128];
153 char *start_uid, *end_uid = NULL;
154 long int uid_start = 0, uid_end = 0;
Srinivasarao Pe168fec2015-09-24 13:08:20 +0530155 struct hlist_node *node;
Jin Qiana1ad2292015-09-07 14:55:10 +0530156
157 if (count >= sizeof(uids))
158 count = sizeof(uids) - 1;
159
160 if (copy_from_user(uids, buffer, count))
161 return -EFAULT;
162
163 uids[count] = '\0';
164 end_uid = uids;
165 start_uid = strsep(&end_uid, "-");
166
167 if (!start_uid || !end_uid)
168 return -EINVAL;
169
170 if (kstrtol(start_uid, 10, &uid_start) != 0 ||
171 kstrtol(end_uid, 10, &uid_end) != 0) {
172 return -EINVAL;
173 }
174
175 mutex_lock(&uid_lock);
176
177 for (; uid_start <= uid_end; uid_start++) {
Srinivasarao Pe168fec2015-09-24 13:08:20 +0530178 hash_for_each_possible_safe(hash_table, uid_entry, node,
179 tmp, hash, uid_start) {
Jin Qiana1ad2292015-09-07 14:55:10 +0530180 hash_del(&uid_entry->hash);
181 kfree(uid_entry);
182 }
183 }
184
185 mutex_unlock(&uid_lock);
186 return count;
187}
188
189static const struct file_operations uid_remove_fops = {
190 .open = uid_remove_open,
191 .release = single_release,
192 .write = uid_remove_write,
193};
194
195static int process_notifier(struct notifier_block *self,
196 unsigned long cmd, void *v)
197{
198 struct task_struct *task = v;
199 struct uid_entry *uid_entry;
Jin Qiana1ad2292015-09-07 14:55:10 +0530200 uid_t uid;
201
202 if (!task)
203 return NOTIFY_OK;
204
205 mutex_lock(&uid_lock);
206 uid = task_uid(task);
207 uid_entry = find_or_register_uid(uid);
208 if (!uid_entry) {
209 pr_err("%s: failed to find uid %d\n", __func__, uid);
210 goto exit;
211 }
212
Srinivasarao Pe168fec2015-09-24 13:08:20 +0530213 uid_entry->utime += task->utime;
214 uid_entry->stime += task->stime;
Ruchi Kandoif49d0f72015-04-17 16:52:54 -0700215 uid_entry->power += task->cpu_power;
Ruchi Kandoi1fee5762015-06-26 14:19:21 -0700216 task->cpu_power = ULLONG_MAX;
Jin Qiana1ad2292015-09-07 14:55:10 +0530217
218exit:
219 mutex_unlock(&uid_lock);
220 return NOTIFY_OK;
221}
222
223static struct notifier_block process_notifier_block = {
224 .notifier_call = process_notifier,
225};
226
227static int __init proc_uid_cputime_init(void)
228{
229 hash_init(hash_table);
230
231 parent = proc_mkdir("uid_cputime", NULL);
232 if (!parent) {
233 pr_err("%s: failed to create proc entry\n", __func__);
234 return -ENOMEM;
235 }
236
237 proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops,
238 NULL);
239
Jin Qian78a59752015-05-11 17:57:52 -0700240 proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops,
Jin Qiana1ad2292015-09-07 14:55:10 +0530241 NULL);
242
243 profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
244
245 return 0;
246}
247
248early_initcall(proc_uid_cputime_init);