blob: 7b746acf416c9e9034a5b6d39fd1e8c63d387375 [file] [log] [blame]
jinqian69014222015-03-11 10:44:50 -07001/* 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);
Jin Qian012f2002017-01-10 16:10:35 -080033static struct proc_dir_entry *cpu_parent;
34static struct proc_dir_entry *io_parent;
35static struct proc_dir_entry *proc_parent;
36
37struct io_stats {
38 u64 read_bytes;
39 u64 write_bytes;
40 u64 rchar;
41 u64 wchar;
42};
43
44#define UID_STATE_FOREGROUND 0
45#define UID_STATE_BACKGROUND 1
46#define UID_STATE_BUCKET_SIZE 2
47
48#define UID_STATE_TOTAL_CURR 2
49#define UID_STATE_TOTAL_LAST 3
50#define UID_STATE_SIZE 4
jinqian69014222015-03-11 10:44:50 -070051
52struct uid_entry {
53 uid_t uid;
54 cputime_t utime;
55 cputime_t stime;
56 cputime_t active_utime;
57 cputime_t active_stime;
Jin Qian012f2002017-01-10 16:10:35 -080058 int state;
59 struct io_stats io[UID_STATE_SIZE];
jinqian69014222015-03-11 10:44:50 -070060 struct hlist_node hash;
61};
62
63static struct uid_entry *find_uid_entry(uid_t uid)
64{
65 struct uid_entry *uid_entry;
66 hash_for_each_possible(hash_table, uid_entry, hash, uid) {
67 if (uid_entry->uid == uid)
68 return uid_entry;
69 }
70 return NULL;
71}
72
73static struct uid_entry *find_or_register_uid(uid_t uid)
74{
75 struct uid_entry *uid_entry;
76
77 uid_entry = find_uid_entry(uid);
78 if (uid_entry)
79 return uid_entry;
80
81 uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
82 if (!uid_entry)
83 return NULL;
84
85 uid_entry->uid = uid;
86
87 hash_add(hash_table, &uid_entry->hash, uid);
88
89 return uid_entry;
90}
91
Jin Qian012f2002017-01-10 16:10:35 -080092static int uid_cputime_show(struct seq_file *m, void *v)
jinqian69014222015-03-11 10:44:50 -070093{
94 struct uid_entry *uid_entry;
Ruchi Kandoi0a733772015-07-31 10:17:54 -070095 struct task_struct *task, *temp;
jinqian69014222015-03-11 10:44:50 -070096 cputime_t utime;
97 cputime_t stime;
98 unsigned long bkt;
99
100 mutex_lock(&uid_lock);
101
102 hash_for_each(hash_table, bkt, uid_entry, hash) {
103 uid_entry->active_stime = 0;
104 uid_entry->active_utime = 0;
105 }
106
107 read_lock(&tasklist_lock);
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700108 do_each_thread(temp, task) {
Amit Pundir48a99062015-04-15 00:40:21 +0530109 uid_entry = find_or_register_uid(from_kuid_munged(
110 current_user_ns(), task_uid(task)));
jinqian69014222015-03-11 10:44:50 -0700111 if (!uid_entry) {
112 read_unlock(&tasklist_lock);
113 mutex_unlock(&uid_lock);
114 pr_err("%s: failed to find the uid_entry for uid %d\n",
Amit Pundir48a99062015-04-15 00:40:21 +0530115 __func__, from_kuid_munged(current_user_ns(),
116 task_uid(task)));
jinqian69014222015-03-11 10:44:50 -0700117 return -ENOMEM;
118 }
119 task_cputime_adjusted(task, &utime, &stime);
120 uid_entry->active_utime += utime;
121 uid_entry->active_stime += stime;
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700122 } while_each_thread(temp, task);
jinqian69014222015-03-11 10:44:50 -0700123 read_unlock(&tasklist_lock);
124
125 hash_for_each(hash_table, bkt, uid_entry, hash) {
126 cputime_t total_utime = uid_entry->utime +
127 uid_entry->active_utime;
128 cputime_t total_stime = uid_entry->stime +
129 uid_entry->active_stime;
Amit Pundire4395b22015-12-14 11:56:35 +0530130 seq_printf(m, "%d: %llu %llu\n", uid_entry->uid,
Jin Qianbe7074f2015-07-13 18:16:55 -0700131 (unsigned long long)jiffies_to_msecs(
132 cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
133 (unsigned long long)jiffies_to_msecs(
Amit Pundire4395b22015-12-14 11:56:35 +0530134 cputime_to_jiffies(total_stime)) * USEC_PER_MSEC);
jinqian69014222015-03-11 10:44:50 -0700135 }
136
137 mutex_unlock(&uid_lock);
138 return 0;
139}
140
Jin Qian012f2002017-01-10 16:10:35 -0800141static int uid_cputime_open(struct inode *inode, struct file *file)
jinqian69014222015-03-11 10:44:50 -0700142{
Jin Qian012f2002017-01-10 16:10:35 -0800143 return single_open(file, uid_cputime_show, PDE_DATA(inode));
jinqian69014222015-03-11 10:44:50 -0700144}
145
Jin Qian012f2002017-01-10 16:10:35 -0800146static const struct file_operations uid_cputime_fops = {
147 .open = uid_cputime_open,
jinqian69014222015-03-11 10:44:50 -0700148 .read = seq_read,
149 .llseek = seq_lseek,
150 .release = single_release,
151};
152
153static int uid_remove_open(struct inode *inode, struct file *file)
154{
155 return single_open(file, NULL, NULL);
156}
157
158static ssize_t uid_remove_write(struct file *file,
159 const char __user *buffer, size_t count, loff_t *ppos)
160{
161 struct uid_entry *uid_entry;
162 struct hlist_node *tmp;
163 char uids[128];
164 char *start_uid, *end_uid = NULL;
165 long int uid_start = 0, uid_end = 0;
166
167 if (count >= sizeof(uids))
168 count = sizeof(uids) - 1;
169
170 if (copy_from_user(uids, buffer, count))
171 return -EFAULT;
172
173 uids[count] = '\0';
174 end_uid = uids;
175 start_uid = strsep(&end_uid, "-");
176
177 if (!start_uid || !end_uid)
178 return -EINVAL;
179
180 if (kstrtol(start_uid, 10, &uid_start) != 0 ||
181 kstrtol(end_uid, 10, &uid_end) != 0) {
182 return -EINVAL;
183 }
jinqian69014222015-03-11 10:44:50 -0700184 mutex_lock(&uid_lock);
185
186 for (; uid_start <= uid_end; uid_start++) {
187 hash_for_each_possible_safe(hash_table, uid_entry, tmp,
Ruchi Kandoi17f35ea2015-10-23 17:49:11 -0700188 hash, (uid_t)uid_start) {
189 if (uid_start == uid_entry->uid) {
190 hash_del(&uid_entry->hash);
191 kfree(uid_entry);
192 }
jinqian69014222015-03-11 10:44:50 -0700193 }
194 }
195
196 mutex_unlock(&uid_lock);
197 return count;
198}
199
200static const struct file_operations uid_remove_fops = {
201 .open = uid_remove_open,
202 .release = single_release,
203 .write = uid_remove_write,
204};
205
Jin Qian5c837b92017-02-28 15:09:42 -0800206static u64 compute_write_bytes(struct task_struct *task)
207{
208 if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
209 return 0;
210
211 return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
212}
213
Jin Qian012f2002017-01-10 16:10:35 -0800214static void add_uid_io_curr_stats(struct uid_entry *uid_entry,
215 struct task_struct *task)
216{
217 struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
218
219 io_curr->read_bytes += task->ioac.read_bytes;
Jin Qian5c837b92017-02-28 15:09:42 -0800220 io_curr->write_bytes += compute_write_bytes(task);
Jin Qian012f2002017-01-10 16:10:35 -0800221 io_curr->rchar += task->ioac.rchar;
222 io_curr->wchar += task->ioac.wchar;
223}
224
225static void clean_uid_io_last_stats(struct uid_entry *uid_entry,
226 struct task_struct *task)
227{
228 struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
229
230 io_last->read_bytes -= task->ioac.read_bytes;
Jin Qian5c837b92017-02-28 15:09:42 -0800231 io_last->write_bytes -= compute_write_bytes(task);
Jin Qian012f2002017-01-10 16:10:35 -0800232 io_last->rchar -= task->ioac.rchar;
233 io_last->wchar -= task->ioac.wchar;
234}
235
236static void update_io_stats_locked(void)
237{
238 struct uid_entry *uid_entry;
239 struct task_struct *task, *temp;
240 struct io_stats *io_bucket, *io_curr, *io_last;
241 unsigned long bkt;
242
243 BUG_ON(!mutex_is_locked(&uid_lock));
244
245 hash_for_each(hash_table, bkt, uid_entry, hash)
246 memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
247 sizeof(struct io_stats));
248
249 read_lock(&tasklist_lock);
250 do_each_thread(temp, task) {
251 uid_entry = find_or_register_uid(from_kuid_munged(
252 current_user_ns(), task_uid(task)));
253 if (!uid_entry)
254 continue;
255 add_uid_io_curr_stats(uid_entry, task);
256 } while_each_thread(temp, task);
257 read_unlock(&tasklist_lock);
258
259 hash_for_each(hash_table, bkt, uid_entry, hash) {
260 io_bucket = &uid_entry->io[uid_entry->state];
261 io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
262 io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
263
264 io_bucket->read_bytes +=
265 io_curr->read_bytes - io_last->read_bytes;
266 io_bucket->write_bytes +=
267 io_curr->write_bytes - io_last->write_bytes;
268 io_bucket->rchar += io_curr->rchar - io_last->rchar;
269 io_bucket->wchar += io_curr->wchar - io_last->wchar;
270
271 io_last->read_bytes = io_curr->read_bytes;
272 io_last->write_bytes = io_curr->write_bytes;
273 io_last->rchar = io_curr->rchar;
274 io_last->wchar = io_curr->wchar;
275 }
276}
277
278static int uid_io_show(struct seq_file *m, void *v)
279{
280 struct uid_entry *uid_entry;
281 unsigned long bkt;
282
283 mutex_lock(&uid_lock);
284
285 update_io_stats_locked();
286
287 hash_for_each(hash_table, bkt, uid_entry, hash) {
288 seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu\n",
289 uid_entry->uid,
290 uid_entry->io[UID_STATE_FOREGROUND].rchar,
291 uid_entry->io[UID_STATE_FOREGROUND].wchar,
292 uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
293 uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
294 uid_entry->io[UID_STATE_BACKGROUND].rchar,
295 uid_entry->io[UID_STATE_BACKGROUND].wchar,
296 uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
297 uid_entry->io[UID_STATE_BACKGROUND].write_bytes);
298 }
299
300 mutex_unlock(&uid_lock);
301
302 return 0;
303}
304
305static int uid_io_open(struct inode *inode, struct file *file)
306{
307 return single_open(file, uid_io_show, PDE_DATA(inode));
308}
309
310static const struct file_operations uid_io_fops = {
311 .open = uid_io_open,
312 .read = seq_read,
313 .llseek = seq_lseek,
314 .release = single_release,
315};
316
317static int uid_procstat_open(struct inode *inode, struct file *file)
318{
319 return single_open(file, NULL, NULL);
320}
321
322static ssize_t uid_procstat_write(struct file *file,
323 const char __user *buffer, size_t count, loff_t *ppos)
324{
325 struct uid_entry *uid_entry;
326 uid_t uid;
327 int argc, state;
328 char input[128];
329
330 if (count >= sizeof(input))
331 return -EINVAL;
332
333 if (copy_from_user(input, buffer, count))
334 return -EFAULT;
335
336 input[count] = '\0';
337
338 argc = sscanf(input, "%u %d", &uid, &state);
339 if (argc != 2)
340 return -EINVAL;
341
342 if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
343 return -EINVAL;
344
345 mutex_lock(&uid_lock);
346
347 uid_entry = find_or_register_uid(uid);
Jin Qian8782d8f2017-01-17 17:26:07 -0800348 if (!uid_entry) {
Jin Qian012f2002017-01-10 16:10:35 -0800349 mutex_unlock(&uid_lock);
350 return -EINVAL;
351 }
352
Jin Qian8782d8f2017-01-17 17:26:07 -0800353 if (uid_entry->state == state) {
354 mutex_unlock(&uid_lock);
355 return count;
356 }
357
Jin Qian012f2002017-01-10 16:10:35 -0800358 update_io_stats_locked();
359
360 uid_entry->state = state;
361
362 mutex_unlock(&uid_lock);
363
364 return count;
365}
366
367static const struct file_operations uid_procstat_fops = {
368 .open = uid_procstat_open,
369 .release = single_release,
370 .write = uid_procstat_write,
371};
372
jinqian69014222015-03-11 10:44:50 -0700373static int process_notifier(struct notifier_block *self,
374 unsigned long cmd, void *v)
375{
376 struct task_struct *task = v;
377 struct uid_entry *uid_entry;
378 cputime_t utime, stime;
379 uid_t uid;
380
381 if (!task)
382 return NOTIFY_OK;
383
384 mutex_lock(&uid_lock);
Amit Pundir48a99062015-04-15 00:40:21 +0530385 uid = from_kuid_munged(current_user_ns(), task_uid(task));
jinqian69014222015-03-11 10:44:50 -0700386 uid_entry = find_or_register_uid(uid);
387 if (!uid_entry) {
388 pr_err("%s: failed to find uid %d\n", __func__, uid);
389 goto exit;
390 }
391
392 task_cputime_adjusted(task, &utime, &stime);
393 uid_entry->utime += utime;
394 uid_entry->stime += stime;
395
Jin Qian012f2002017-01-10 16:10:35 -0800396 update_io_stats_locked();
397 clean_uid_io_last_stats(uid_entry, task);
398
jinqian69014222015-03-11 10:44:50 -0700399exit:
400 mutex_unlock(&uid_lock);
401 return NOTIFY_OK;
402}
403
404static struct notifier_block process_notifier_block = {
405 .notifier_call = process_notifier,
406};
407
Jin Qian012f2002017-01-10 16:10:35 -0800408static int __init proc_uid_sys_stats_init(void)
jinqian69014222015-03-11 10:44:50 -0700409{
410 hash_init(hash_table);
411
Jin Qian012f2002017-01-10 16:10:35 -0800412 cpu_parent = proc_mkdir("uid_cputime", NULL);
413 if (!cpu_parent) {
414 pr_err("%s: failed to create uid_cputime proc entry\n",
415 __func__);
416 goto err;
jinqian69014222015-03-11 10:44:50 -0700417 }
418
Jin Qian012f2002017-01-10 16:10:35 -0800419 proc_create_data("remove_uid_range", 0222, cpu_parent,
420 &uid_remove_fops, NULL);
421 proc_create_data("show_uid_stat", 0444, cpu_parent,
422 &uid_cputime_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700423
Jin Qian012f2002017-01-10 16:10:35 -0800424 io_parent = proc_mkdir("uid_io", NULL);
425 if (!io_parent) {
426 pr_err("%s: failed to create uid_io proc entry\n",
427 __func__);
428 goto err;
429 }
430
431 proc_create_data("stats", 0444, io_parent,
432 &uid_io_fops, NULL);
433
434 proc_parent = proc_mkdir("uid_procstat", NULL);
435 if (!proc_parent) {
436 pr_err("%s: failed to create uid_procstat proc entry\n",
437 __func__);
438 goto err;
439 }
440
441 proc_create_data("set", 0222, proc_parent,
442 &uid_procstat_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700443
444 profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
445
446 return 0;
Jin Qian012f2002017-01-10 16:10:35 -0800447
448err:
449 remove_proc_subtree("uid_cputime", NULL);
450 remove_proc_subtree("uid_io", NULL);
451 remove_proc_subtree("uid_procstat", NULL);
452 return -ENOMEM;
jinqian69014222015-03-11 10:44:50 -0700453}
454
Jin Qian012f2002017-01-10 16:10:35 -0800455early_initcall(proc_uid_sys_stats_init);