blob: 4988e323cf02b0784aaff86512c93f4e78153a0c [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;
Jin Qian62eb9f92017-03-02 13:39:43 -080042 u64 fsync;
Jin Qian012f2002017-01-10 16:10:35 -080043};
44
45#define UID_STATE_FOREGROUND 0
46#define UID_STATE_BACKGROUND 1
47#define UID_STATE_BUCKET_SIZE 2
48
49#define UID_STATE_TOTAL_CURR 2
50#define UID_STATE_TOTAL_LAST 3
51#define UID_STATE_SIZE 4
jinqian69014222015-03-11 10:44:50 -070052
53struct uid_entry {
54 uid_t uid;
55 cputime_t utime;
56 cputime_t stime;
57 cputime_t active_utime;
58 cputime_t active_stime;
Jin Qian012f2002017-01-10 16:10:35 -080059 int state;
60 struct io_stats io[UID_STATE_SIZE];
jinqian69014222015-03-11 10:44:50 -070061 struct hlist_node hash;
62};
63
64static struct uid_entry *find_uid_entry(uid_t uid)
65{
66 struct uid_entry *uid_entry;
67 hash_for_each_possible(hash_table, uid_entry, hash, uid) {
68 if (uid_entry->uid == uid)
69 return uid_entry;
70 }
71 return NULL;
72}
73
74static struct uid_entry *find_or_register_uid(uid_t uid)
75{
76 struct uid_entry *uid_entry;
77
78 uid_entry = find_uid_entry(uid);
79 if (uid_entry)
80 return uid_entry;
81
82 uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
83 if (!uid_entry)
84 return NULL;
85
86 uid_entry->uid = uid;
87
88 hash_add(hash_table, &uid_entry->hash, uid);
89
90 return uid_entry;
91}
92
Jin Qian012f2002017-01-10 16:10:35 -080093static int uid_cputime_show(struct seq_file *m, void *v)
jinqian69014222015-03-11 10:44:50 -070094{
95 struct uid_entry *uid_entry;
Ruchi Kandoi0a733772015-07-31 10:17:54 -070096 struct task_struct *task, *temp;
jinqian69014222015-03-11 10:44:50 -070097 cputime_t utime;
98 cputime_t stime;
99 unsigned long bkt;
100
101 mutex_lock(&uid_lock);
102
103 hash_for_each(hash_table, bkt, uid_entry, hash) {
104 uid_entry->active_stime = 0;
105 uid_entry->active_utime = 0;
106 }
107
108 read_lock(&tasklist_lock);
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700109 do_each_thread(temp, task) {
Amit Pundir48a99062015-04-15 00:40:21 +0530110 uid_entry = find_or_register_uid(from_kuid_munged(
111 current_user_ns(), task_uid(task)));
jinqian69014222015-03-11 10:44:50 -0700112 if (!uid_entry) {
113 read_unlock(&tasklist_lock);
114 mutex_unlock(&uid_lock);
115 pr_err("%s: failed to find the uid_entry for uid %d\n",
Amit Pundir48a99062015-04-15 00:40:21 +0530116 __func__, from_kuid_munged(current_user_ns(),
117 task_uid(task)));
jinqian69014222015-03-11 10:44:50 -0700118 return -ENOMEM;
119 }
120 task_cputime_adjusted(task, &utime, &stime);
121 uid_entry->active_utime += utime;
122 uid_entry->active_stime += stime;
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700123 } while_each_thread(temp, task);
jinqian69014222015-03-11 10:44:50 -0700124 read_unlock(&tasklist_lock);
125
126 hash_for_each(hash_table, bkt, uid_entry, hash) {
127 cputime_t total_utime = uid_entry->utime +
128 uid_entry->active_utime;
129 cputime_t total_stime = uid_entry->stime +
130 uid_entry->active_stime;
Amit Pundire4395b22015-12-14 11:56:35 +0530131 seq_printf(m, "%d: %llu %llu\n", uid_entry->uid,
Jin Qianbe7074f2015-07-13 18:16:55 -0700132 (unsigned long long)jiffies_to_msecs(
133 cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
134 (unsigned long long)jiffies_to_msecs(
Amit Pundire4395b22015-12-14 11:56:35 +0530135 cputime_to_jiffies(total_stime)) * USEC_PER_MSEC);
jinqian69014222015-03-11 10:44:50 -0700136 }
137
138 mutex_unlock(&uid_lock);
139 return 0;
140}
141
Jin Qian012f2002017-01-10 16:10:35 -0800142static int uid_cputime_open(struct inode *inode, struct file *file)
jinqian69014222015-03-11 10:44:50 -0700143{
Jin Qian012f2002017-01-10 16:10:35 -0800144 return single_open(file, uid_cputime_show, PDE_DATA(inode));
jinqian69014222015-03-11 10:44:50 -0700145}
146
Jin Qian012f2002017-01-10 16:10:35 -0800147static const struct file_operations uid_cputime_fops = {
148 .open = uid_cputime_open,
jinqian69014222015-03-11 10:44:50 -0700149 .read = seq_read,
150 .llseek = seq_lseek,
151 .release = single_release,
152};
153
154static int uid_remove_open(struct inode *inode, struct file *file)
155{
156 return single_open(file, NULL, NULL);
157}
158
159static ssize_t uid_remove_write(struct file *file,
160 const char __user *buffer, size_t count, loff_t *ppos)
161{
162 struct uid_entry *uid_entry;
163 struct hlist_node *tmp;
164 char uids[128];
165 char *start_uid, *end_uid = NULL;
166 long int uid_start = 0, uid_end = 0;
167
168 if (count >= sizeof(uids))
169 count = sizeof(uids) - 1;
170
171 if (copy_from_user(uids, buffer, count))
172 return -EFAULT;
173
174 uids[count] = '\0';
175 end_uid = uids;
176 start_uid = strsep(&end_uid, "-");
177
178 if (!start_uid || !end_uid)
179 return -EINVAL;
180
181 if (kstrtol(start_uid, 10, &uid_start) != 0 ||
182 kstrtol(end_uid, 10, &uid_end) != 0) {
183 return -EINVAL;
184 }
jinqian69014222015-03-11 10:44:50 -0700185 mutex_lock(&uid_lock);
186
187 for (; uid_start <= uid_end; uid_start++) {
188 hash_for_each_possible_safe(hash_table, uid_entry, tmp,
Ruchi Kandoi17f35ea2015-10-23 17:49:11 -0700189 hash, (uid_t)uid_start) {
190 if (uid_start == uid_entry->uid) {
191 hash_del(&uid_entry->hash);
192 kfree(uid_entry);
193 }
jinqian69014222015-03-11 10:44:50 -0700194 }
195 }
196
197 mutex_unlock(&uid_lock);
198 return count;
199}
200
201static const struct file_operations uid_remove_fops = {
202 .open = uid_remove_open,
203 .release = single_release,
204 .write = uid_remove_write,
205};
206
Jin Qian5c837b92017-02-28 15:09:42 -0800207static u64 compute_write_bytes(struct task_struct *task)
208{
209 if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
210 return 0;
211
212 return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
213}
214
Jin Qian012f2002017-01-10 16:10:35 -0800215static void add_uid_io_curr_stats(struct uid_entry *uid_entry,
216 struct task_struct *task)
217{
218 struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
219
220 io_curr->read_bytes += task->ioac.read_bytes;
Jin Qian5c837b92017-02-28 15:09:42 -0800221 io_curr->write_bytes += compute_write_bytes(task);
Jin Qian012f2002017-01-10 16:10:35 -0800222 io_curr->rchar += task->ioac.rchar;
223 io_curr->wchar += task->ioac.wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800224 io_curr->fsync += task->ioac.syscfs;
Jin Qian012f2002017-01-10 16:10:35 -0800225}
226
227static void clean_uid_io_last_stats(struct uid_entry *uid_entry,
228 struct task_struct *task)
229{
230 struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
231
232 io_last->read_bytes -= task->ioac.read_bytes;
Jin Qian5c837b92017-02-28 15:09:42 -0800233 io_last->write_bytes -= compute_write_bytes(task);
Jin Qian012f2002017-01-10 16:10:35 -0800234 io_last->rchar -= task->ioac.rchar;
235 io_last->wchar -= task->ioac.wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800236 io_last->fsync -= task->ioac.syscfs;
Jin Qian012f2002017-01-10 16:10:35 -0800237}
238
239static void update_io_stats_locked(void)
240{
241 struct uid_entry *uid_entry;
242 struct task_struct *task, *temp;
243 struct io_stats *io_bucket, *io_curr, *io_last;
244 unsigned long bkt;
245
246 BUG_ON(!mutex_is_locked(&uid_lock));
247
248 hash_for_each(hash_table, bkt, uid_entry, hash)
249 memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
250 sizeof(struct io_stats));
251
252 read_lock(&tasklist_lock);
253 do_each_thread(temp, task) {
254 uid_entry = find_or_register_uid(from_kuid_munged(
255 current_user_ns(), task_uid(task)));
256 if (!uid_entry)
257 continue;
258 add_uid_io_curr_stats(uid_entry, task);
259 } while_each_thread(temp, task);
260 read_unlock(&tasklist_lock);
261
262 hash_for_each(hash_table, bkt, uid_entry, hash) {
263 io_bucket = &uid_entry->io[uid_entry->state];
264 io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
265 io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
266
267 io_bucket->read_bytes +=
268 io_curr->read_bytes - io_last->read_bytes;
269 io_bucket->write_bytes +=
270 io_curr->write_bytes - io_last->write_bytes;
271 io_bucket->rchar += io_curr->rchar - io_last->rchar;
272 io_bucket->wchar += io_curr->wchar - io_last->wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800273 io_bucket->fsync += io_curr->fsync - io_last->fsync;
Jin Qian012f2002017-01-10 16:10:35 -0800274
275 io_last->read_bytes = io_curr->read_bytes;
276 io_last->write_bytes = io_curr->write_bytes;
277 io_last->rchar = io_curr->rchar;
278 io_last->wchar = io_curr->wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -0800279 io_last->fsync = io_curr->fsync;
Jin Qian012f2002017-01-10 16:10:35 -0800280 }
281}
282
283static int uid_io_show(struct seq_file *m, void *v)
284{
285 struct uid_entry *uid_entry;
286 unsigned long bkt;
287
288 mutex_lock(&uid_lock);
289
290 update_io_stats_locked();
291
292 hash_for_each(hash_table, bkt, uid_entry, hash) {
Jin Qian62eb9f92017-03-02 13:39:43 -0800293 seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
Jin Qian012f2002017-01-10 16:10:35 -0800294 uid_entry->uid,
295 uid_entry->io[UID_STATE_FOREGROUND].rchar,
296 uid_entry->io[UID_STATE_FOREGROUND].wchar,
297 uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
298 uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
299 uid_entry->io[UID_STATE_BACKGROUND].rchar,
300 uid_entry->io[UID_STATE_BACKGROUND].wchar,
301 uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
Jin Qian62eb9f92017-03-02 13:39:43 -0800302 uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
303 uid_entry->io[UID_STATE_FOREGROUND].fsync,
304 uid_entry->io[UID_STATE_BACKGROUND].fsync);
Jin Qian012f2002017-01-10 16:10:35 -0800305 }
306
307 mutex_unlock(&uid_lock);
308
309 return 0;
310}
311
312static int uid_io_open(struct inode *inode, struct file *file)
313{
314 return single_open(file, uid_io_show, PDE_DATA(inode));
315}
316
317static const struct file_operations uid_io_fops = {
318 .open = uid_io_open,
319 .read = seq_read,
320 .llseek = seq_lseek,
321 .release = single_release,
322};
323
324static int uid_procstat_open(struct inode *inode, struct file *file)
325{
326 return single_open(file, NULL, NULL);
327}
328
329static ssize_t uid_procstat_write(struct file *file,
330 const char __user *buffer, size_t count, loff_t *ppos)
331{
332 struct uid_entry *uid_entry;
333 uid_t uid;
334 int argc, state;
335 char input[128];
336
337 if (count >= sizeof(input))
338 return -EINVAL;
339
340 if (copy_from_user(input, buffer, count))
341 return -EFAULT;
342
343 input[count] = '\0';
344
345 argc = sscanf(input, "%u %d", &uid, &state);
346 if (argc != 2)
347 return -EINVAL;
348
349 if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
350 return -EINVAL;
351
352 mutex_lock(&uid_lock);
353
354 uid_entry = find_or_register_uid(uid);
Jin Qian8782d8f2017-01-17 17:26:07 -0800355 if (!uid_entry) {
Jin Qian012f2002017-01-10 16:10:35 -0800356 mutex_unlock(&uid_lock);
357 return -EINVAL;
358 }
359
Jin Qian8782d8f2017-01-17 17:26:07 -0800360 if (uid_entry->state == state) {
361 mutex_unlock(&uid_lock);
362 return count;
363 }
364
Jin Qian012f2002017-01-10 16:10:35 -0800365 update_io_stats_locked();
366
367 uid_entry->state = state;
368
369 mutex_unlock(&uid_lock);
370
371 return count;
372}
373
374static const struct file_operations uid_procstat_fops = {
375 .open = uid_procstat_open,
376 .release = single_release,
377 .write = uid_procstat_write,
378};
379
jinqian69014222015-03-11 10:44:50 -0700380static int process_notifier(struct notifier_block *self,
381 unsigned long cmd, void *v)
382{
383 struct task_struct *task = v;
384 struct uid_entry *uid_entry;
385 cputime_t utime, stime;
386 uid_t uid;
387
388 if (!task)
389 return NOTIFY_OK;
390
391 mutex_lock(&uid_lock);
Amit Pundir48a99062015-04-15 00:40:21 +0530392 uid = from_kuid_munged(current_user_ns(), task_uid(task));
jinqian69014222015-03-11 10:44:50 -0700393 uid_entry = find_or_register_uid(uid);
394 if (!uid_entry) {
395 pr_err("%s: failed to find uid %d\n", __func__, uid);
396 goto exit;
397 }
398
399 task_cputime_adjusted(task, &utime, &stime);
400 uid_entry->utime += utime;
401 uid_entry->stime += stime;
402
Jin Qian012f2002017-01-10 16:10:35 -0800403 update_io_stats_locked();
404 clean_uid_io_last_stats(uid_entry, task);
405
jinqian69014222015-03-11 10:44:50 -0700406exit:
407 mutex_unlock(&uid_lock);
408 return NOTIFY_OK;
409}
410
411static struct notifier_block process_notifier_block = {
412 .notifier_call = process_notifier,
413};
414
Jin Qian012f2002017-01-10 16:10:35 -0800415static int __init proc_uid_sys_stats_init(void)
jinqian69014222015-03-11 10:44:50 -0700416{
417 hash_init(hash_table);
418
Jin Qian012f2002017-01-10 16:10:35 -0800419 cpu_parent = proc_mkdir("uid_cputime", NULL);
420 if (!cpu_parent) {
421 pr_err("%s: failed to create uid_cputime proc entry\n",
422 __func__);
423 goto err;
jinqian69014222015-03-11 10:44:50 -0700424 }
425
Jin Qian012f2002017-01-10 16:10:35 -0800426 proc_create_data("remove_uid_range", 0222, cpu_parent,
427 &uid_remove_fops, NULL);
428 proc_create_data("show_uid_stat", 0444, cpu_parent,
429 &uid_cputime_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700430
Jin Qian012f2002017-01-10 16:10:35 -0800431 io_parent = proc_mkdir("uid_io", NULL);
432 if (!io_parent) {
433 pr_err("%s: failed to create uid_io proc entry\n",
434 __func__);
435 goto err;
436 }
437
438 proc_create_data("stats", 0444, io_parent,
439 &uid_io_fops, NULL);
440
441 proc_parent = proc_mkdir("uid_procstat", NULL);
442 if (!proc_parent) {
443 pr_err("%s: failed to create uid_procstat proc entry\n",
444 __func__);
445 goto err;
446 }
447
448 proc_create_data("set", 0222, proc_parent,
449 &uid_procstat_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700450
451 profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
452
453 return 0;
Jin Qian012f2002017-01-10 16:10:35 -0800454
455err:
456 remove_proc_subtree("uid_cputime", NULL);
457 remove_proc_subtree("uid_io", NULL);
458 remove_proc_subtree("uid_procstat", NULL);
459 return -ENOMEM;
jinqian69014222015-03-11 10:44:50 -0700460}
461
Jin Qian012f2002017-01-10 16:10:35 -0800462early_initcall(proc_uid_sys_stats_init);