blob: 127a052ed141e0fd3aa1c675bd9d51b7411816b7 [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>
Wei Wang7f1ad082017-03-13 12:22:21 -070024#include <linux/rtmutex.h>
jinqian69014222015-03-11 10:44:50 -070025#include <linux/sched.h>
26#include <linux/seq_file.h>
27#include <linux/slab.h>
28#include <linux/uaccess.h>
29
Wei Wang7f1ad082017-03-13 12:22:21 -070030
jinqian69014222015-03-11 10:44:50 -070031#define UID_HASH_BITS 10
32DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
33
Wei Wang7f1ad082017-03-13 12:22:21 -070034static DEFINE_RT_MUTEX(uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -080035static struct proc_dir_entry *cpu_parent;
36static struct proc_dir_entry *io_parent;
37static struct proc_dir_entry *proc_parent;
38
39struct io_stats {
40 u64 read_bytes;
41 u64 write_bytes;
42 u64 rchar;
43 u64 wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -080044 u64 fsync;
Jin Qian012f2002017-01-10 16:10:35 -080045};
46
47#define UID_STATE_FOREGROUND 0
48#define UID_STATE_BACKGROUND 1
49#define UID_STATE_BUCKET_SIZE 2
50
51#define UID_STATE_TOTAL_CURR 2
52#define UID_STATE_TOTAL_LAST 3
53#define UID_STATE_SIZE 4
jinqian69014222015-03-11 10:44:50 -070054
55struct uid_entry {
56 uid_t uid;
57 cputime_t utime;
58 cputime_t stime;
59 cputime_t active_utime;
60 cputime_t active_stime;
Jin Qian012f2002017-01-10 16:10:35 -080061 int state;
62 struct io_stats io[UID_STATE_SIZE];
jinqian69014222015-03-11 10:44:50 -070063 struct hlist_node hash;
64};
65
66static struct uid_entry *find_uid_entry(uid_t uid)
67{
68 struct uid_entry *uid_entry;
69 hash_for_each_possible(hash_table, uid_entry, hash, uid) {
70 if (uid_entry->uid == uid)
71 return uid_entry;
72 }
73 return NULL;
74}
75
76static struct uid_entry *find_or_register_uid(uid_t uid)
77{
78 struct uid_entry *uid_entry;
79
80 uid_entry = find_uid_entry(uid);
81 if (uid_entry)
82 return uid_entry;
83
84 uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
85 if (!uid_entry)
86 return NULL;
87
88 uid_entry->uid = uid;
89
90 hash_add(hash_table, &uid_entry->hash, uid);
91
92 return uid_entry;
93}
94
Jin Qian012f2002017-01-10 16:10:35 -080095static int uid_cputime_show(struct seq_file *m, void *v)
jinqian69014222015-03-11 10:44:50 -070096{
97 struct uid_entry *uid_entry;
Ruchi Kandoi0a733772015-07-31 10:17:54 -070098 struct task_struct *task, *temp;
Ganesh Mahendrane6ef73f2017-04-25 18:07:43 +080099 struct user_namespace *user_ns = current_user_ns();
jinqian69014222015-03-11 10:44:50 -0700100 cputime_t utime;
101 cputime_t stime;
102 unsigned long bkt;
Ganesh Mahendrane6ef73f2017-04-25 18:07:43 +0800103 uid_t uid;
jinqian69014222015-03-11 10:44:50 -0700104
Wei Wang7f1ad082017-03-13 12:22:21 -0700105 rt_mutex_lock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700106
107 hash_for_each(hash_table, bkt, uid_entry, hash) {
108 uid_entry->active_stime = 0;
109 uid_entry->active_utime = 0;
110 }
111
112 read_lock(&tasklist_lock);
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700113 do_each_thread(temp, task) {
Ganesh Mahendrane6ef73f2017-04-25 18:07:43 +0800114 uid = from_kuid_munged(user_ns, task_uid(task));
115 uid_entry = find_or_register_uid(uid);
jinqian69014222015-03-11 10:44:50 -0700116 if (!uid_entry) {
117 read_unlock(&tasklist_lock);
Wei Wang7f1ad082017-03-13 12:22:21 -0700118 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700119 pr_err("%s: failed to find the uid_entry for uid %d\n",
Ganesh Mahendrane6ef73f2017-04-25 18:07:43 +0800120 __func__, uid);
jinqian69014222015-03-11 10:44:50 -0700121 return -ENOMEM;
122 }
123 task_cputime_adjusted(task, &utime, &stime);
124 uid_entry->active_utime += utime;
125 uid_entry->active_stime += stime;
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700126 } while_each_thread(temp, task);
jinqian69014222015-03-11 10:44:50 -0700127 read_unlock(&tasklist_lock);
128
129 hash_for_each(hash_table, bkt, uid_entry, hash) {
130 cputime_t total_utime = uid_entry->utime +
131 uid_entry->active_utime;
132 cputime_t total_stime = uid_entry->stime +
133 uid_entry->active_stime;
Amit Pundire4395b22015-12-14 11:56:35 +0530134 seq_printf(m, "%d: %llu %llu\n", uid_entry->uid,
Jin Qianbe7074f2015-07-13 18:16:55 -0700135 (unsigned long long)jiffies_to_msecs(
136 cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
137 (unsigned long long)jiffies_to_msecs(
Amit Pundire4395b22015-12-14 11:56:35 +0530138 cputime_to_jiffies(total_stime)) * USEC_PER_MSEC);
jinqian69014222015-03-11 10:44:50 -0700139 }
140
Wei Wang7f1ad082017-03-13 12:22:21 -0700141 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700142 return 0;
143}
144
Jin Qian012f2002017-01-10 16:10:35 -0800145static int uid_cputime_open(struct inode *inode, struct file *file)
jinqian69014222015-03-11 10:44:50 -0700146{
Jin Qian012f2002017-01-10 16:10:35 -0800147 return single_open(file, uid_cputime_show, PDE_DATA(inode));
jinqian69014222015-03-11 10:44:50 -0700148}
149
Jin Qian012f2002017-01-10 16:10:35 -0800150static const struct file_operations uid_cputime_fops = {
151 .open = uid_cputime_open,
jinqian69014222015-03-11 10:44:50 -0700152 .read = seq_read,
153 .llseek = seq_lseek,
154 .release = single_release,
155};
156
157static int uid_remove_open(struct inode *inode, struct file *file)
158{
159 return single_open(file, NULL, NULL);
160}
161
162static ssize_t uid_remove_write(struct file *file,
163 const char __user *buffer, size_t count, loff_t *ppos)
164{
165 struct uid_entry *uid_entry;
166 struct hlist_node *tmp;
167 char uids[128];
168 char *start_uid, *end_uid = NULL;
169 long int uid_start = 0, uid_end = 0;
170
171 if (count >= sizeof(uids))
172 count = sizeof(uids) - 1;
173
174 if (copy_from_user(uids, buffer, count))
175 return -EFAULT;
176
177 uids[count] = '\0';
178 end_uid = uids;
179 start_uid = strsep(&end_uid, "-");
180
181 if (!start_uid || !end_uid)
182 return -EINVAL;
183
184 if (kstrtol(start_uid, 10, &uid_start) != 0 ||
185 kstrtol(end_uid, 10, &uid_end) != 0) {
186 return -EINVAL;
187 }
Wei Wang7f1ad082017-03-13 12:22:21 -0700188 rt_mutex_lock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700189
190 for (; uid_start <= uid_end; uid_start++) {
191 hash_for_each_possible_safe(hash_table, uid_entry, tmp,
Ruchi Kandoi17f35ea2015-10-23 17:49:11 -0700192 hash, (uid_t)uid_start) {
193 if (uid_start == uid_entry->uid) {
194 hash_del(&uid_entry->hash);
195 kfree(uid_entry);
196 }
jinqian69014222015-03-11 10:44:50 -0700197 }
198 }
199
Wei Wang7f1ad082017-03-13 12:22:21 -0700200 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700201 return count;
202}
203
204static const struct file_operations uid_remove_fops = {
205 .open = uid_remove_open,
206 .release = single_release,
207 .write = uid_remove_write,
208};
209
Jin Qian5c837b92017-02-28 15:09:42 -0800210static u64 compute_write_bytes(struct task_struct *task)
211{
212 if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
213 return 0;
214
215 return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
216}
217
Jin Qian012f2002017-01-10 16:10:35 -0800218static void add_uid_io_curr_stats(struct uid_entry *uid_entry,
219 struct task_struct *task)
220{
221 struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
222
223 io_curr->read_bytes += task->ioac.read_bytes;
Jin Qian5c837b92017-02-28 15:09:42 -0800224 io_curr->write_bytes += compute_write_bytes(task);
Jin Qian012f2002017-01-10 16:10:35 -0800225 io_curr->rchar += task->ioac.rchar;
226 io_curr->wchar += task->ioac.wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800227 io_curr->fsync += task->ioac.syscfs;
Jin Qian012f2002017-01-10 16:10:35 -0800228}
229
230static void clean_uid_io_last_stats(struct uid_entry *uid_entry,
231 struct task_struct *task)
232{
233 struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
234
235 io_last->read_bytes -= task->ioac.read_bytes;
Jin Qian5c837b92017-02-28 15:09:42 -0800236 io_last->write_bytes -= compute_write_bytes(task);
Jin Qian012f2002017-01-10 16:10:35 -0800237 io_last->rchar -= task->ioac.rchar;
238 io_last->wchar -= task->ioac.wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800239 io_last->fsync -= task->ioac.syscfs;
Jin Qian012f2002017-01-10 16:10:35 -0800240}
241
Jin Qian67f252c2017-04-13 17:07:58 -0700242static void update_io_stats_all_locked(void)
Jin Qian012f2002017-01-10 16:10:35 -0800243{
244 struct uid_entry *uid_entry;
245 struct task_struct *task, *temp;
246 struct io_stats *io_bucket, *io_curr, *io_last;
Jin Qian67f252c2017-04-13 17:07:58 -0700247 struct user_namespace *user_ns = current_user_ns();
Jin Qian012f2002017-01-10 16:10:35 -0800248 unsigned long bkt;
Jin Qian67f252c2017-04-13 17:07:58 -0700249 uid_t uid;
Jin Qian012f2002017-01-10 16:10:35 -0800250
251 hash_for_each(hash_table, bkt, uid_entry, hash)
252 memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
253 sizeof(struct io_stats));
254
Jin Qian67f252c2017-04-13 17:07:58 -0700255 rcu_read_lock();
Jin Qian012f2002017-01-10 16:10:35 -0800256 do_each_thread(temp, task) {
Jin Qian67f252c2017-04-13 17:07:58 -0700257 uid = from_kuid_munged(user_ns, task_uid(task));
258 uid_entry = find_or_register_uid(uid);
Jin Qian012f2002017-01-10 16:10:35 -0800259 if (!uid_entry)
260 continue;
261 add_uid_io_curr_stats(uid_entry, task);
262 } while_each_thread(temp, task);
Jin Qian67f252c2017-04-13 17:07:58 -0700263 rcu_read_unlock();
Jin Qian012f2002017-01-10 16:10:35 -0800264
265 hash_for_each(hash_table, bkt, uid_entry, hash) {
266 io_bucket = &uid_entry->io[uid_entry->state];
267 io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
268 io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
269
270 io_bucket->read_bytes +=
271 io_curr->read_bytes - io_last->read_bytes;
272 io_bucket->write_bytes +=
273 io_curr->write_bytes - io_last->write_bytes;
274 io_bucket->rchar += io_curr->rchar - io_last->rchar;
275 io_bucket->wchar += io_curr->wchar - io_last->wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800276 io_bucket->fsync += io_curr->fsync - io_last->fsync;
Jin Qian012f2002017-01-10 16:10:35 -0800277
278 io_last->read_bytes = io_curr->read_bytes;
279 io_last->write_bytes = io_curr->write_bytes;
280 io_last->rchar = io_curr->rchar;
281 io_last->wchar = io_curr->wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800282 io_last->fsync = io_curr->fsync;
Jin Qian012f2002017-01-10 16:10:35 -0800283 }
284}
285
Jin Qian67f252c2017-04-13 17:07:58 -0700286static void update_io_stats_uid_locked(uid_t target_uid)
287{
288 struct uid_entry *uid_entry;
289 struct task_struct *task, *temp;
290 struct io_stats *io_bucket, *io_curr, *io_last;
291 struct user_namespace *user_ns = current_user_ns();
292
293 uid_entry = find_or_register_uid(target_uid);
294 if (!uid_entry)
295 return;
296
297 memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
298 sizeof(struct io_stats));
299
300 rcu_read_lock();
301 do_each_thread(temp, task) {
302 if (from_kuid_munged(user_ns, task_uid(task)) != target_uid)
303 continue;
304 add_uid_io_curr_stats(uid_entry, task);
305 } while_each_thread(temp, task);
306 rcu_read_unlock();
307
308 io_bucket = &uid_entry->io[uid_entry->state];
309 io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
310 io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
311
312 io_bucket->read_bytes +=
313 io_curr->read_bytes - io_last->read_bytes;
314 io_bucket->write_bytes +=
315 io_curr->write_bytes - io_last->write_bytes;
316 io_bucket->rchar += io_curr->rchar - io_last->rchar;
317 io_bucket->wchar += io_curr->wchar - io_last->wchar;
318 io_bucket->fsync += io_curr->fsync - io_last->fsync;
319
320 io_last->read_bytes = io_curr->read_bytes;
321 io_last->write_bytes = io_curr->write_bytes;
322 io_last->rchar = io_curr->rchar;
323 io_last->wchar = io_curr->wchar;
324 io_last->fsync = io_curr->fsync;
325}
326
Jin Qian012f2002017-01-10 16:10:35 -0800327static int uid_io_show(struct seq_file *m, void *v)
328{
329 struct uid_entry *uid_entry;
330 unsigned long bkt;
331
Wei Wang7f1ad082017-03-13 12:22:21 -0700332 rt_mutex_lock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800333
Jin Qian67f252c2017-04-13 17:07:58 -0700334 update_io_stats_all_locked();
Jin Qian012f2002017-01-10 16:10:35 -0800335
336 hash_for_each(hash_table, bkt, uid_entry, hash) {
Jin Qian62eb9f92017-03-02 13:39:43 -0800337 seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
Jin Qian012f2002017-01-10 16:10:35 -0800338 uid_entry->uid,
339 uid_entry->io[UID_STATE_FOREGROUND].rchar,
340 uid_entry->io[UID_STATE_FOREGROUND].wchar,
341 uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
342 uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
343 uid_entry->io[UID_STATE_BACKGROUND].rchar,
344 uid_entry->io[UID_STATE_BACKGROUND].wchar,
345 uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
Jin Qian62eb9f92017-03-02 13:39:43 -0800346 uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
347 uid_entry->io[UID_STATE_FOREGROUND].fsync,
348 uid_entry->io[UID_STATE_BACKGROUND].fsync);
Jin Qian012f2002017-01-10 16:10:35 -0800349 }
350
Wei Wang7f1ad082017-03-13 12:22:21 -0700351 rt_mutex_unlock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800352
353 return 0;
354}
355
356static int uid_io_open(struct inode *inode, struct file *file)
357{
358 return single_open(file, uid_io_show, PDE_DATA(inode));
359}
360
361static const struct file_operations uid_io_fops = {
362 .open = uid_io_open,
363 .read = seq_read,
364 .llseek = seq_lseek,
365 .release = single_release,
366};
367
368static int uid_procstat_open(struct inode *inode, struct file *file)
369{
370 return single_open(file, NULL, NULL);
371}
372
373static ssize_t uid_procstat_write(struct file *file,
374 const char __user *buffer, size_t count, loff_t *ppos)
375{
376 struct uid_entry *uid_entry;
377 uid_t uid;
378 int argc, state;
379 char input[128];
380
381 if (count >= sizeof(input))
382 return -EINVAL;
383
384 if (copy_from_user(input, buffer, count))
385 return -EFAULT;
386
387 input[count] = '\0';
388
389 argc = sscanf(input, "%u %d", &uid, &state);
390 if (argc != 2)
391 return -EINVAL;
392
393 if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
394 return -EINVAL;
395
Wei Wang7f1ad082017-03-13 12:22:21 -0700396 rt_mutex_lock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800397
398 uid_entry = find_or_register_uid(uid);
Jin Qian8782d8f2017-01-17 17:26:07 -0800399 if (!uid_entry) {
Wei Wang7f1ad082017-03-13 12:22:21 -0700400 rt_mutex_unlock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800401 return -EINVAL;
402 }
403
Jin Qian8782d8f2017-01-17 17:26:07 -0800404 if (uid_entry->state == state) {
Wei Wang7f1ad082017-03-13 12:22:21 -0700405 rt_mutex_unlock(&uid_lock);
Jin Qian8782d8f2017-01-17 17:26:07 -0800406 return count;
407 }
408
Jin Qian67f252c2017-04-13 17:07:58 -0700409 update_io_stats_uid_locked(uid);
Jin Qian012f2002017-01-10 16:10:35 -0800410
411 uid_entry->state = state;
412
Wei Wang7f1ad082017-03-13 12:22:21 -0700413 rt_mutex_unlock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800414
415 return count;
416}
417
418static const struct file_operations uid_procstat_fops = {
419 .open = uid_procstat_open,
420 .release = single_release,
421 .write = uid_procstat_write,
422};
423
jinqian69014222015-03-11 10:44:50 -0700424static int process_notifier(struct notifier_block *self,
425 unsigned long cmd, void *v)
426{
427 struct task_struct *task = v;
428 struct uid_entry *uid_entry;
429 cputime_t utime, stime;
430 uid_t uid;
431
432 if (!task)
433 return NOTIFY_OK;
434
Wei Wang7f1ad082017-03-13 12:22:21 -0700435 rt_mutex_lock(&uid_lock);
Amit Pundir48a99062015-04-15 00:40:21 +0530436 uid = from_kuid_munged(current_user_ns(), task_uid(task));
jinqian69014222015-03-11 10:44:50 -0700437 uid_entry = find_or_register_uid(uid);
438 if (!uid_entry) {
439 pr_err("%s: failed to find uid %d\n", __func__, uid);
440 goto exit;
441 }
442
443 task_cputime_adjusted(task, &utime, &stime);
444 uid_entry->utime += utime;
445 uid_entry->stime += stime;
446
Jin Qian67f252c2017-04-13 17:07:58 -0700447 update_io_stats_uid_locked(uid);
Jin Qian012f2002017-01-10 16:10:35 -0800448 clean_uid_io_last_stats(uid_entry, task);
449
jinqian69014222015-03-11 10:44:50 -0700450exit:
Wei Wang7f1ad082017-03-13 12:22:21 -0700451 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700452 return NOTIFY_OK;
453}
454
455static struct notifier_block process_notifier_block = {
456 .notifier_call = process_notifier,
457};
458
Jin Qian012f2002017-01-10 16:10:35 -0800459static int __init proc_uid_sys_stats_init(void)
jinqian69014222015-03-11 10:44:50 -0700460{
461 hash_init(hash_table);
462
Jin Qian012f2002017-01-10 16:10:35 -0800463 cpu_parent = proc_mkdir("uid_cputime", NULL);
464 if (!cpu_parent) {
465 pr_err("%s: failed to create uid_cputime proc entry\n",
466 __func__);
467 goto err;
jinqian69014222015-03-11 10:44:50 -0700468 }
469
Jin Qian012f2002017-01-10 16:10:35 -0800470 proc_create_data("remove_uid_range", 0222, cpu_parent,
471 &uid_remove_fops, NULL);
472 proc_create_data("show_uid_stat", 0444, cpu_parent,
473 &uid_cputime_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700474
Jin Qian012f2002017-01-10 16:10:35 -0800475 io_parent = proc_mkdir("uid_io", NULL);
476 if (!io_parent) {
477 pr_err("%s: failed to create uid_io proc entry\n",
478 __func__);
479 goto err;
480 }
481
482 proc_create_data("stats", 0444, io_parent,
483 &uid_io_fops, NULL);
484
485 proc_parent = proc_mkdir("uid_procstat", NULL);
486 if (!proc_parent) {
487 pr_err("%s: failed to create uid_procstat proc entry\n",
488 __func__);
489 goto err;
490 }
491
492 proc_create_data("set", 0222, proc_parent,
493 &uid_procstat_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700494
495 profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
496
497 return 0;
Jin Qian012f2002017-01-10 16:10:35 -0800498
499err:
500 remove_proc_subtree("uid_cputime", NULL);
501 remove_proc_subtree("uid_io", NULL);
502 remove_proc_subtree("uid_procstat", NULL);
503 return -ENOMEM;
jinqian69014222015-03-11 10:44:50 -0700504}
505
Jin Qian012f2002017-01-10 16:10:35 -0800506early_initcall(proc_uid_sys_stats_init);