blob: 33fc2b9597cb3cf43cc0413b3c830715cd52eaaf [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;
jinqian69014222015-03-11 10:44:50 -070099 cputime_t utime;
100 cputime_t stime;
101 unsigned long bkt;
102
Wei Wang7f1ad082017-03-13 12:22:21 -0700103 rt_mutex_lock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700104
105 hash_for_each(hash_table, bkt, uid_entry, hash) {
106 uid_entry->active_stime = 0;
107 uid_entry->active_utime = 0;
108 }
109
110 read_lock(&tasklist_lock);
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700111 do_each_thread(temp, task) {
Amit Pundir48a99062015-04-15 00:40:21 +0530112 uid_entry = find_or_register_uid(from_kuid_munged(
113 current_user_ns(), task_uid(task)));
jinqian69014222015-03-11 10:44:50 -0700114 if (!uid_entry) {
115 read_unlock(&tasklist_lock);
Wei Wang7f1ad082017-03-13 12:22:21 -0700116 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700117 pr_err("%s: failed to find the uid_entry for uid %d\n",
Amit Pundir48a99062015-04-15 00:40:21 +0530118 __func__, from_kuid_munged(current_user_ns(),
119 task_uid(task)));
jinqian69014222015-03-11 10:44:50 -0700120 return -ENOMEM;
121 }
122 task_cputime_adjusted(task, &utime, &stime);
123 uid_entry->active_utime += utime;
124 uid_entry->active_stime += stime;
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700125 } while_each_thread(temp, task);
jinqian69014222015-03-11 10:44:50 -0700126 read_unlock(&tasklist_lock);
127
128 hash_for_each(hash_table, bkt, uid_entry, hash) {
129 cputime_t total_utime = uid_entry->utime +
130 uid_entry->active_utime;
131 cputime_t total_stime = uid_entry->stime +
132 uid_entry->active_stime;
Amit Pundire4395b22015-12-14 11:56:35 +0530133 seq_printf(m, "%d: %llu %llu\n", uid_entry->uid,
Jin Qianbe7074f2015-07-13 18:16:55 -0700134 (unsigned long long)jiffies_to_msecs(
135 cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
136 (unsigned long long)jiffies_to_msecs(
Amit Pundire4395b22015-12-14 11:56:35 +0530137 cputime_to_jiffies(total_stime)) * USEC_PER_MSEC);
jinqian69014222015-03-11 10:44:50 -0700138 }
139
Wei Wang7f1ad082017-03-13 12:22:21 -0700140 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700141 return 0;
142}
143
Jin Qian012f2002017-01-10 16:10:35 -0800144static int uid_cputime_open(struct inode *inode, struct file *file)
jinqian69014222015-03-11 10:44:50 -0700145{
Jin Qian012f2002017-01-10 16:10:35 -0800146 return single_open(file, uid_cputime_show, PDE_DATA(inode));
jinqian69014222015-03-11 10:44:50 -0700147}
148
Jin Qian012f2002017-01-10 16:10:35 -0800149static const struct file_operations uid_cputime_fops = {
150 .open = uid_cputime_open,
jinqian69014222015-03-11 10:44:50 -0700151 .read = seq_read,
152 .llseek = seq_lseek,
153 .release = single_release,
154};
155
156static int uid_remove_open(struct inode *inode, struct file *file)
157{
158 return single_open(file, NULL, NULL);
159}
160
161static ssize_t uid_remove_write(struct file *file,
162 const char __user *buffer, size_t count, loff_t *ppos)
163{
164 struct uid_entry *uid_entry;
165 struct hlist_node *tmp;
166 char uids[128];
167 char *start_uid, *end_uid = NULL;
168 long int uid_start = 0, uid_end = 0;
169
170 if (count >= sizeof(uids))
171 count = sizeof(uids) - 1;
172
173 if (copy_from_user(uids, buffer, count))
174 return -EFAULT;
175
176 uids[count] = '\0';
177 end_uid = uids;
178 start_uid = strsep(&end_uid, "-");
179
180 if (!start_uid || !end_uid)
181 return -EINVAL;
182
183 if (kstrtol(start_uid, 10, &uid_start) != 0 ||
184 kstrtol(end_uid, 10, &uid_end) != 0) {
185 return -EINVAL;
186 }
Wei Wang7f1ad082017-03-13 12:22:21 -0700187 rt_mutex_lock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700188
189 for (; uid_start <= uid_end; uid_start++) {
190 hash_for_each_possible_safe(hash_table, uid_entry, tmp,
Ruchi Kandoi17f35ea2015-10-23 17:49:11 -0700191 hash, (uid_t)uid_start) {
192 if (uid_start == uid_entry->uid) {
193 hash_del(&uid_entry->hash);
194 kfree(uid_entry);
195 }
jinqian69014222015-03-11 10:44:50 -0700196 }
197 }
198
Wei Wang7f1ad082017-03-13 12:22:21 -0700199 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700200 return count;
201}
202
203static const struct file_operations uid_remove_fops = {
204 .open = uid_remove_open,
205 .release = single_release,
206 .write = uid_remove_write,
207};
208
Jin Qian5c837b92017-02-28 15:09:42 -0800209static u64 compute_write_bytes(struct task_struct *task)
210{
211 if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
212 return 0;
213
214 return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
215}
216
Jin Qian012f2002017-01-10 16:10:35 -0800217static void add_uid_io_curr_stats(struct uid_entry *uid_entry,
218 struct task_struct *task)
219{
220 struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
221
222 io_curr->read_bytes += task->ioac.read_bytes;
Jin Qian5c837b92017-02-28 15:09:42 -0800223 io_curr->write_bytes += compute_write_bytes(task);
Jin Qian012f2002017-01-10 16:10:35 -0800224 io_curr->rchar += task->ioac.rchar;
225 io_curr->wchar += task->ioac.wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800226 io_curr->fsync += task->ioac.syscfs;
Jin Qian012f2002017-01-10 16:10:35 -0800227}
228
229static void clean_uid_io_last_stats(struct uid_entry *uid_entry,
230 struct task_struct *task)
231{
232 struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
233
234 io_last->read_bytes -= task->ioac.read_bytes;
Jin Qian5c837b92017-02-28 15:09:42 -0800235 io_last->write_bytes -= compute_write_bytes(task);
Jin Qian012f2002017-01-10 16:10:35 -0800236 io_last->rchar -= task->ioac.rchar;
237 io_last->wchar -= task->ioac.wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800238 io_last->fsync -= task->ioac.syscfs;
Jin Qian012f2002017-01-10 16:10:35 -0800239}
240
241static void update_io_stats_locked(void)
242{
243 struct uid_entry *uid_entry;
244 struct task_struct *task, *temp;
245 struct io_stats *io_bucket, *io_curr, *io_last;
246 unsigned long bkt;
247
Wei Wang7f1ad082017-03-13 12:22:21 -0700248 BUG_ON(!rt_mutex_is_locked(&uid_lock));
Jin Qian012f2002017-01-10 16:10:35 -0800249
250 hash_for_each(hash_table, bkt, uid_entry, hash)
251 memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
252 sizeof(struct io_stats));
253
254 read_lock(&tasklist_lock);
255 do_each_thread(temp, task) {
256 uid_entry = find_or_register_uid(from_kuid_munged(
257 current_user_ns(), task_uid(task)));
258 if (!uid_entry)
259 continue;
260 add_uid_io_curr_stats(uid_entry, task);
261 } while_each_thread(temp, task);
262 read_unlock(&tasklist_lock);
263
264 hash_for_each(hash_table, bkt, uid_entry, hash) {
265 io_bucket = &uid_entry->io[uid_entry->state];
266 io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
267 io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
268
269 io_bucket->read_bytes +=
270 io_curr->read_bytes - io_last->read_bytes;
271 io_bucket->write_bytes +=
272 io_curr->write_bytes - io_last->write_bytes;
273 io_bucket->rchar += io_curr->rchar - io_last->rchar;
274 io_bucket->wchar += io_curr->wchar - io_last->wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800275 io_bucket->fsync += io_curr->fsync - io_last->fsync;
Jin Qian012f2002017-01-10 16:10:35 -0800276
277 io_last->read_bytes = io_curr->read_bytes;
278 io_last->write_bytes = io_curr->write_bytes;
279 io_last->rchar = io_curr->rchar;
280 io_last->wchar = io_curr->wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800281 io_last->fsync = io_curr->fsync;
Jin Qian012f2002017-01-10 16:10:35 -0800282 }
283}
284
285static int uid_io_show(struct seq_file *m, void *v)
286{
287 struct uid_entry *uid_entry;
288 unsigned long bkt;
289
Wei Wang7f1ad082017-03-13 12:22:21 -0700290 rt_mutex_lock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800291
292 update_io_stats_locked();
293
294 hash_for_each(hash_table, bkt, uid_entry, hash) {
Jin Qian62eb9f92017-03-02 13:39:43 -0800295 seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
Jin Qian012f2002017-01-10 16:10:35 -0800296 uid_entry->uid,
297 uid_entry->io[UID_STATE_FOREGROUND].rchar,
298 uid_entry->io[UID_STATE_FOREGROUND].wchar,
299 uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
300 uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
301 uid_entry->io[UID_STATE_BACKGROUND].rchar,
302 uid_entry->io[UID_STATE_BACKGROUND].wchar,
303 uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
Jin Qian62eb9f92017-03-02 13:39:43 -0800304 uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
305 uid_entry->io[UID_STATE_FOREGROUND].fsync,
306 uid_entry->io[UID_STATE_BACKGROUND].fsync);
Jin Qian012f2002017-01-10 16:10:35 -0800307 }
308
Wei Wang7f1ad082017-03-13 12:22:21 -0700309 rt_mutex_unlock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800310
311 return 0;
312}
313
314static int uid_io_open(struct inode *inode, struct file *file)
315{
316 return single_open(file, uid_io_show, PDE_DATA(inode));
317}
318
319static const struct file_operations uid_io_fops = {
320 .open = uid_io_open,
321 .read = seq_read,
322 .llseek = seq_lseek,
323 .release = single_release,
324};
325
326static int uid_procstat_open(struct inode *inode, struct file *file)
327{
328 return single_open(file, NULL, NULL);
329}
330
331static ssize_t uid_procstat_write(struct file *file,
332 const char __user *buffer, size_t count, loff_t *ppos)
333{
334 struct uid_entry *uid_entry;
335 uid_t uid;
336 int argc, state;
337 char input[128];
338
339 if (count >= sizeof(input))
340 return -EINVAL;
341
342 if (copy_from_user(input, buffer, count))
343 return -EFAULT;
344
345 input[count] = '\0';
346
347 argc = sscanf(input, "%u %d", &uid, &state);
348 if (argc != 2)
349 return -EINVAL;
350
351 if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
352 return -EINVAL;
353
Wei Wang7f1ad082017-03-13 12:22:21 -0700354 rt_mutex_lock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800355
356 uid_entry = find_or_register_uid(uid);
Jin Qian8782d8f2017-01-17 17:26:07 -0800357 if (!uid_entry) {
Wei Wang7f1ad082017-03-13 12:22:21 -0700358 rt_mutex_unlock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800359 return -EINVAL;
360 }
361
Jin Qian8782d8f2017-01-17 17:26:07 -0800362 if (uid_entry->state == state) {
Wei Wang7f1ad082017-03-13 12:22:21 -0700363 rt_mutex_unlock(&uid_lock);
Jin Qian8782d8f2017-01-17 17:26:07 -0800364 return count;
365 }
366
Jin Qian012f2002017-01-10 16:10:35 -0800367 update_io_stats_locked();
368
369 uid_entry->state = state;
370
Wei Wang7f1ad082017-03-13 12:22:21 -0700371 rt_mutex_unlock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800372
373 return count;
374}
375
376static const struct file_operations uid_procstat_fops = {
377 .open = uid_procstat_open,
378 .release = single_release,
379 .write = uid_procstat_write,
380};
381
jinqian69014222015-03-11 10:44:50 -0700382static int process_notifier(struct notifier_block *self,
383 unsigned long cmd, void *v)
384{
385 struct task_struct *task = v;
386 struct uid_entry *uid_entry;
387 cputime_t utime, stime;
388 uid_t uid;
389
390 if (!task)
391 return NOTIFY_OK;
392
Wei Wang7f1ad082017-03-13 12:22:21 -0700393 rt_mutex_lock(&uid_lock);
Amit Pundir48a99062015-04-15 00:40:21 +0530394 uid = from_kuid_munged(current_user_ns(), task_uid(task));
jinqian69014222015-03-11 10:44:50 -0700395 uid_entry = find_or_register_uid(uid);
396 if (!uid_entry) {
397 pr_err("%s: failed to find uid %d\n", __func__, uid);
398 goto exit;
399 }
400
401 task_cputime_adjusted(task, &utime, &stime);
402 uid_entry->utime += utime;
403 uid_entry->stime += stime;
404
Jin Qian012f2002017-01-10 16:10:35 -0800405 update_io_stats_locked();
406 clean_uid_io_last_stats(uid_entry, task);
407
jinqian69014222015-03-11 10:44:50 -0700408exit:
Wei Wang7f1ad082017-03-13 12:22:21 -0700409 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700410 return NOTIFY_OK;
411}
412
413static struct notifier_block process_notifier_block = {
414 .notifier_call = process_notifier,
415};
416
Jin Qian012f2002017-01-10 16:10:35 -0800417static int __init proc_uid_sys_stats_init(void)
jinqian69014222015-03-11 10:44:50 -0700418{
419 hash_init(hash_table);
420
Jin Qian012f2002017-01-10 16:10:35 -0800421 cpu_parent = proc_mkdir("uid_cputime", NULL);
422 if (!cpu_parent) {
423 pr_err("%s: failed to create uid_cputime proc entry\n",
424 __func__);
425 goto err;
jinqian69014222015-03-11 10:44:50 -0700426 }
427
Jin Qian012f2002017-01-10 16:10:35 -0800428 proc_create_data("remove_uid_range", 0222, cpu_parent,
429 &uid_remove_fops, NULL);
430 proc_create_data("show_uid_stat", 0444, cpu_parent,
431 &uid_cputime_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700432
Jin Qian012f2002017-01-10 16:10:35 -0800433 io_parent = proc_mkdir("uid_io", NULL);
434 if (!io_parent) {
435 pr_err("%s: failed to create uid_io proc entry\n",
436 __func__);
437 goto err;
438 }
439
440 proc_create_data("stats", 0444, io_parent,
441 &uid_io_fops, NULL);
442
443 proc_parent = proc_mkdir("uid_procstat", NULL);
444 if (!proc_parent) {
445 pr_err("%s: failed to create uid_procstat proc entry\n",
446 __func__);
447 goto err;
448 }
449
450 proc_create_data("set", 0222, proc_parent,
451 &uid_procstat_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700452
453 profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
454
455 return 0;
Jin Qian012f2002017-01-10 16:10:35 -0800456
457err:
458 remove_proc_subtree("uid_cputime", NULL);
459 remove_proc_subtree("uid_io", NULL);
460 remove_proc_subtree("uid_procstat", NULL);
461 return -ENOMEM;
jinqian69014222015-03-11 10:44:50 -0700462}
463
Jin Qian012f2002017-01-10 16:10:35 -0800464early_initcall(proc_uid_sys_stats_init);