blob: 345f2292fa093cc2befd7085a01167be0f0cf061 [file] [log] [blame]
Artem Borisovf0f62932018-01-13 18:03:40 +03001/* drivers/misc/uid_sys_stats.c
jinqian69014222015-03-11 10:44:50 -07002 *
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>
Connor O'Brienfcb3db12018-02-06 13:30:27 -080017#include <linux/cpufreq_times.h>
jinqian69014222015-03-11 10:44:50 -070018#include <linux/err.h>
19#include <linux/hashtable.h>
20#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/list.h>
Amit Pundir0ea205a2017-08-17 18:52:04 +053023#include <linux/mm.h>
jinqian69014222015-03-11 10:44:50 -070024#include <linux/proc_fs.h>
25#include <linux/profile.h>
Wei Wang7f1ad082017-03-13 12:22:21 -070026#include <linux/rtmutex.h>
jinqian69014222015-03-11 10:44:50 -070027#include <linux/sched.h>
28#include <linux/seq_file.h>
29#include <linux/slab.h>
30#include <linux/uaccess.h>
31
Wei Wang7f1ad082017-03-13 12:22:21 -070032
jinqian69014222015-03-11 10:44:50 -070033#define UID_HASH_BITS 10
34DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
35
Wei Wang7f1ad082017-03-13 12:22:21 -070036static DEFINE_RT_MUTEX(uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -080037static struct proc_dir_entry *cpu_parent;
38static struct proc_dir_entry *io_parent;
39static struct proc_dir_entry *proc_parent;
40
41struct io_stats {
42 u64 read_bytes;
43 u64 write_bytes;
44 u64 rchar;
45 u64 wchar;
Jin Qian62eb9f92017-03-02 13:39:43 -080046 u64 fsync;
Jin Qian012f2002017-01-10 16:10:35 -080047};
48
49#define UID_STATE_FOREGROUND 0
50#define UID_STATE_BACKGROUND 1
51#define UID_STATE_BUCKET_SIZE 2
52
53#define UID_STATE_TOTAL_CURR 2
54#define UID_STATE_TOTAL_LAST 3
Jin Qiancd5e19c2017-05-22 12:08:06 -070055#define UID_STATE_DEAD_TASKS 4
56#define UID_STATE_SIZE 5
jinqian69014222015-03-11 10:44:50 -070057
Yang Jin6e8f1e72017-07-26 12:52:22 -070058#define MAX_TASK_COMM_LEN 256
59
60struct task_entry {
61 char comm[MAX_TASK_COMM_LEN];
62 pid_t pid;
63 struct io_stats io[UID_STATE_SIZE];
64 struct hlist_node hash;
65};
66
jinqian69014222015-03-11 10:44:50 -070067struct uid_entry {
68 uid_t uid;
69 cputime_t utime;
70 cputime_t stime;
71 cputime_t active_utime;
72 cputime_t active_stime;
Jin Qian012f2002017-01-10 16:10:35 -080073 int state;
74 struct io_stats io[UID_STATE_SIZE];
jinqian69014222015-03-11 10:44:50 -070075 struct hlist_node hash;
Yang Jin6e8f1e72017-07-26 12:52:22 -070076#ifdef CONFIG_UID_SYS_STATS_DEBUG
77 DECLARE_HASHTABLE(task_entries, UID_HASH_BITS);
78#endif
jinqian69014222015-03-11 10:44:50 -070079};
80
Yang Jin6e8f1e72017-07-26 12:52:22 -070081static u64 compute_write_bytes(struct task_struct *task)
82{
83 if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
84 return 0;
85
86 return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
87}
88
89static void compute_io_bucket_stats(struct io_stats *io_bucket,
90 struct io_stats *io_curr,
91 struct io_stats *io_last,
92 struct io_stats *io_dead)
93{
94 /* tasks could switch to another uid group, but its io_last in the
95 * previous uid group could still be positive.
96 * therefore before each update, do an overflow check first
97 */
98 int64_t delta;
99
100 delta = io_curr->read_bytes + io_dead->read_bytes -
101 io_last->read_bytes;
102 io_bucket->read_bytes += delta > 0 ? delta : 0;
103 delta = io_curr->write_bytes + io_dead->write_bytes -
104 io_last->write_bytes;
105 io_bucket->write_bytes += delta > 0 ? delta : 0;
106 delta = io_curr->rchar + io_dead->rchar - io_last->rchar;
107 io_bucket->rchar += delta > 0 ? delta : 0;
108 delta = io_curr->wchar + io_dead->wchar - io_last->wchar;
109 io_bucket->wchar += delta > 0 ? delta : 0;
110 delta = io_curr->fsync + io_dead->fsync - io_last->fsync;
111 io_bucket->fsync += delta > 0 ? delta : 0;
112
113 io_last->read_bytes = io_curr->read_bytes;
114 io_last->write_bytes = io_curr->write_bytes;
115 io_last->rchar = io_curr->rchar;
116 io_last->wchar = io_curr->wchar;
117 io_last->fsync = io_curr->fsync;
118
119 memset(io_dead, 0, sizeof(struct io_stats));
120}
121
122#ifdef CONFIG_UID_SYS_STATS_DEBUG
123static void get_full_task_comm(struct task_entry *task_entry,
124 struct task_struct *task)
125{
126 int i = 0, offset = 0, len = 0;
127 /* save one byte for terminating null character */
128 int unused_len = MAX_TASK_COMM_LEN - TASK_COMM_LEN - 1;
129 char buf[unused_len];
130 struct mm_struct *mm = task->mm;
131
132 /* fill the first TASK_COMM_LEN bytes with thread name */
Dmitry Shmidt856452b2018-03-07 14:11:03 -0800133 __get_task_comm(task_entry->comm, TASK_COMM_LEN, task);
Yang Jin6e8f1e72017-07-26 12:52:22 -0700134 i = strlen(task_entry->comm);
135 while (i < TASK_COMM_LEN)
136 task_entry->comm[i++] = ' ';
137
138 /* next the executable file name */
139 if (mm) {
140 down_read(&mm->mmap_sem);
141 if (mm->exe_file) {
142 char *pathname = d_path(&mm->exe_file->f_path, buf,
143 unused_len);
144
145 if (!IS_ERR(pathname)) {
146 len = strlcpy(task_entry->comm + i, pathname,
147 unused_len);
148 i += len;
149 task_entry->comm[i++] = ' ';
150 unused_len--;
151 }
152 }
153 up_read(&mm->mmap_sem);
154 }
155 unused_len -= len;
156
157 /* fill the rest with command line argument
158 * replace each null or new line character
159 * between args in argv with whitespace */
160 len = get_cmdline(task, buf, unused_len);
161 while (offset < len) {
162 if (buf[offset] != '\0' && buf[offset] != '\n')
163 task_entry->comm[i++] = buf[offset];
164 else
165 task_entry->comm[i++] = ' ';
166 offset++;
167 }
168
169 /* get rid of trailing whitespaces in case when arg is memset to
170 * zero before being reset in userspace
171 */
172 while (task_entry->comm[i-1] == ' ')
173 i--;
174 task_entry->comm[i] = '\0';
175}
176
177static struct task_entry *find_task_entry(struct uid_entry *uid_entry,
178 struct task_struct *task)
179{
180 struct task_entry *task_entry;
181
182 hash_for_each_possible(uid_entry->task_entries, task_entry, hash,
183 task->pid) {
184 if (task->pid == task_entry->pid) {
185 /* if thread name changed, update the entire command */
186 int len = strnchr(task_entry->comm, ' ', TASK_COMM_LEN)
187 - task_entry->comm;
188
189 if (strncmp(task_entry->comm, task->comm, len))
190 get_full_task_comm(task_entry, task);
191 return task_entry;
192 }
193 }
194 return NULL;
195}
196
197static struct task_entry *find_or_register_task(struct uid_entry *uid_entry,
198 struct task_struct *task)
199{
200 struct task_entry *task_entry;
201 pid_t pid = task->pid;
202
203 task_entry = find_task_entry(uid_entry, task);
204 if (task_entry)
205 return task_entry;
206
207 task_entry = kzalloc(sizeof(struct task_entry), GFP_ATOMIC);
208 if (!task_entry)
209 return NULL;
210
211 get_full_task_comm(task_entry, task);
212
213 task_entry->pid = pid;
214 hash_add(uid_entry->task_entries, &task_entry->hash, (unsigned int)pid);
215
216 return task_entry;
217}
218
219static void remove_uid_tasks(struct uid_entry *uid_entry)
220{
221 struct task_entry *task_entry;
222 unsigned long bkt_task;
223 struct hlist_node *tmp_task;
224
225 hash_for_each_safe(uid_entry->task_entries, bkt_task,
226 tmp_task, task_entry, hash) {
227 hash_del(&task_entry->hash);
228 kfree(task_entry);
229 }
230}
231
232static void set_io_uid_tasks_zero(struct uid_entry *uid_entry)
233{
234 struct task_entry *task_entry;
235 unsigned long bkt_task;
236
237 hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
238 memset(&task_entry->io[UID_STATE_TOTAL_CURR], 0,
239 sizeof(struct io_stats));
240 }
241}
242
243static void add_uid_tasks_io_stats(struct uid_entry *uid_entry,
244 struct task_struct *task, int slot)
245{
246 struct task_entry *task_entry = find_or_register_task(uid_entry, task);
247 struct io_stats *task_io_slot = &task_entry->io[slot];
248
249 task_io_slot->read_bytes += task->ioac.read_bytes;
250 task_io_slot->write_bytes += compute_write_bytes(task);
251 task_io_slot->rchar += task->ioac.rchar;
252 task_io_slot->wchar += task->ioac.wchar;
253 task_io_slot->fsync += task->ioac.syscfs;
254}
255
256static void compute_io_uid_tasks(struct uid_entry *uid_entry)
257{
258 struct task_entry *task_entry;
259 unsigned long bkt_task;
260
261 hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
262 compute_io_bucket_stats(&task_entry->io[uid_entry->state],
263 &task_entry->io[UID_STATE_TOTAL_CURR],
264 &task_entry->io[UID_STATE_TOTAL_LAST],
265 &task_entry->io[UID_STATE_DEAD_TASKS]);
266 }
267}
268
269static void show_io_uid_tasks(struct seq_file *m, struct uid_entry *uid_entry)
270{
271 struct task_entry *task_entry;
272 unsigned long bkt_task;
273
274 hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
275 /* Separated by comma because space exists in task comm */
276 seq_printf(m, "task,%s,%lu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n",
277 task_entry->comm,
278 (unsigned long)task_entry->pid,
279 task_entry->io[UID_STATE_FOREGROUND].rchar,
280 task_entry->io[UID_STATE_FOREGROUND].wchar,
281 task_entry->io[UID_STATE_FOREGROUND].read_bytes,
282 task_entry->io[UID_STATE_FOREGROUND].write_bytes,
283 task_entry->io[UID_STATE_BACKGROUND].rchar,
284 task_entry->io[UID_STATE_BACKGROUND].wchar,
285 task_entry->io[UID_STATE_BACKGROUND].read_bytes,
286 task_entry->io[UID_STATE_BACKGROUND].write_bytes,
287 task_entry->io[UID_STATE_FOREGROUND].fsync,
288 task_entry->io[UID_STATE_BACKGROUND].fsync);
289 }
290}
291#else
292static void remove_uid_tasks(struct uid_entry *uid_entry) {};
293static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) {};
294static void add_uid_tasks_io_stats(struct uid_entry *uid_entry,
295 struct task_struct *task, int slot) {};
296static void compute_io_uid_tasks(struct uid_entry *uid_entry) {};
297static void show_io_uid_tasks(struct seq_file *m,
298 struct uid_entry *uid_entry) {}
299#endif
300
jinqian69014222015-03-11 10:44:50 -0700301static struct uid_entry *find_uid_entry(uid_t uid)
302{
303 struct uid_entry *uid_entry;
304 hash_for_each_possible(hash_table, uid_entry, hash, uid) {
305 if (uid_entry->uid == uid)
306 return uid_entry;
307 }
308 return NULL;
309}
310
311static struct uid_entry *find_or_register_uid(uid_t uid)
312{
313 struct uid_entry *uid_entry;
314
315 uid_entry = find_uid_entry(uid);
316 if (uid_entry)
317 return uid_entry;
318
319 uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
320 if (!uid_entry)
321 return NULL;
322
323 uid_entry->uid = uid;
Yang Jin6e8f1e72017-07-26 12:52:22 -0700324#ifdef CONFIG_UID_SYS_STATS_DEBUG
325 hash_init(uid_entry->task_entries);
326#endif
jinqian69014222015-03-11 10:44:50 -0700327 hash_add(hash_table, &uid_entry->hash, uid);
328
329 return uid_entry;
330}
331
Jin Qian012f2002017-01-10 16:10:35 -0800332static int uid_cputime_show(struct seq_file *m, void *v)
jinqian69014222015-03-11 10:44:50 -0700333{
Ganesh Mahendran2b3da4a2017-05-25 15:20:29 +0800334 struct uid_entry *uid_entry = NULL;
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700335 struct task_struct *task, *temp;
Ganesh Mahendrane6ef73f2017-04-25 18:07:43 +0800336 struct user_namespace *user_ns = current_user_ns();
jinqian69014222015-03-11 10:44:50 -0700337 cputime_t utime;
338 cputime_t stime;
339 unsigned long bkt;
Ganesh Mahendrane6ef73f2017-04-25 18:07:43 +0800340 uid_t uid;
jinqian69014222015-03-11 10:44:50 -0700341
Wei Wang7f1ad082017-03-13 12:22:21 -0700342 rt_mutex_lock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700343
344 hash_for_each(hash_table, bkt, uid_entry, hash) {
345 uid_entry->active_stime = 0;
346 uid_entry->active_utime = 0;
347 }
348
Pavankumar Kondeti71184082017-06-21 09:22:45 +0530349 rcu_read_lock();
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700350 do_each_thread(temp, task) {
Ganesh Mahendrane6ef73f2017-04-25 18:07:43 +0800351 uid = from_kuid_munged(user_ns, task_uid(task));
Ganesh Mahendran2b3da4a2017-05-25 15:20:29 +0800352 if (!uid_entry || uid_entry->uid != uid)
353 uid_entry = find_or_register_uid(uid);
jinqian69014222015-03-11 10:44:50 -0700354 if (!uid_entry) {
Pavankumar Kondeti71184082017-06-21 09:22:45 +0530355 rcu_read_unlock();
Wei Wang7f1ad082017-03-13 12:22:21 -0700356 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700357 pr_err("%s: failed to find the uid_entry for uid %d\n",
Ganesh Mahendrane6ef73f2017-04-25 18:07:43 +0800358 __func__, uid);
jinqian69014222015-03-11 10:44:50 -0700359 return -ENOMEM;
360 }
361 task_cputime_adjusted(task, &utime, &stime);
362 uid_entry->active_utime += utime;
363 uid_entry->active_stime += stime;
Ruchi Kandoi0a733772015-07-31 10:17:54 -0700364 } while_each_thread(temp, task);
Pavankumar Kondeti71184082017-06-21 09:22:45 +0530365 rcu_read_unlock();
jinqian69014222015-03-11 10:44:50 -0700366
367 hash_for_each(hash_table, bkt, uid_entry, hash) {
368 cputime_t total_utime = uid_entry->utime +
369 uid_entry->active_utime;
370 cputime_t total_stime = uid_entry->stime +
371 uid_entry->active_stime;
Amit Pundire4395b22015-12-14 11:56:35 +0530372 seq_printf(m, "%d: %llu %llu\n", uid_entry->uid,
Jin Qianbe7074f2015-07-13 18:16:55 -0700373 (unsigned long long)jiffies_to_msecs(
374 cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
375 (unsigned long long)jiffies_to_msecs(
Amit Pundire4395b22015-12-14 11:56:35 +0530376 cputime_to_jiffies(total_stime)) * USEC_PER_MSEC);
jinqian69014222015-03-11 10:44:50 -0700377 }
378
Wei Wang7f1ad082017-03-13 12:22:21 -0700379 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700380 return 0;
381}
382
Jin Qian012f2002017-01-10 16:10:35 -0800383static int uid_cputime_open(struct inode *inode, struct file *file)
jinqian69014222015-03-11 10:44:50 -0700384{
Jin Qian012f2002017-01-10 16:10:35 -0800385 return single_open(file, uid_cputime_show, PDE_DATA(inode));
jinqian69014222015-03-11 10:44:50 -0700386}
387
Jin Qian012f2002017-01-10 16:10:35 -0800388static const struct file_operations uid_cputime_fops = {
389 .open = uid_cputime_open,
jinqian69014222015-03-11 10:44:50 -0700390 .read = seq_read,
391 .llseek = seq_lseek,
392 .release = single_release,
393};
394
395static int uid_remove_open(struct inode *inode, struct file *file)
396{
397 return single_open(file, NULL, NULL);
398}
399
400static ssize_t uid_remove_write(struct file *file,
401 const char __user *buffer, size_t count, loff_t *ppos)
402{
403 struct uid_entry *uid_entry;
404 struct hlist_node *tmp;
405 char uids[128];
406 char *start_uid, *end_uid = NULL;
407 long int uid_start = 0, uid_end = 0;
408
409 if (count >= sizeof(uids))
410 count = sizeof(uids) - 1;
411
412 if (copy_from_user(uids, buffer, count))
413 return -EFAULT;
414
415 uids[count] = '\0';
416 end_uid = uids;
417 start_uid = strsep(&end_uid, "-");
418
419 if (!start_uid || !end_uid)
420 return -EINVAL;
421
422 if (kstrtol(start_uid, 10, &uid_start) != 0 ||
423 kstrtol(end_uid, 10, &uid_end) != 0) {
424 return -EINVAL;
425 }
Connor O'Brienfcb3db12018-02-06 13:30:27 -0800426
427 /* Also remove uids from /proc/uid_time_in_state */
428 cpufreq_task_times_remove_uids(uid_start, uid_end);
429
Wei Wang7f1ad082017-03-13 12:22:21 -0700430 rt_mutex_lock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700431
432 for (; uid_start <= uid_end; uid_start++) {
433 hash_for_each_possible_safe(hash_table, uid_entry, tmp,
Ruchi Kandoi17f35ea2015-10-23 17:49:11 -0700434 hash, (uid_t)uid_start) {
435 if (uid_start == uid_entry->uid) {
Yang Jin6e8f1e72017-07-26 12:52:22 -0700436 remove_uid_tasks(uid_entry);
Ruchi Kandoi17f35ea2015-10-23 17:49:11 -0700437 hash_del(&uid_entry->hash);
438 kfree(uid_entry);
439 }
jinqian69014222015-03-11 10:44:50 -0700440 }
441 }
442
Wei Wang7f1ad082017-03-13 12:22:21 -0700443 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700444 return count;
445}
446
447static const struct file_operations uid_remove_fops = {
448 .open = uid_remove_open,
449 .release = single_release,
450 .write = uid_remove_write,
451};
452
Jin Qian5c837b92017-02-28 15:09:42 -0800453
Jin Qiancd5e19c2017-05-22 12:08:06 -0700454static void add_uid_io_stats(struct uid_entry *uid_entry,
455 struct task_struct *task, int slot)
Jin Qian012f2002017-01-10 16:10:35 -0800456{
Jin Qiancd5e19c2017-05-22 12:08:06 -0700457 struct io_stats *io_slot = &uid_entry->io[slot];
Jin Qian012f2002017-01-10 16:10:35 -0800458
Jin Qiancd5e19c2017-05-22 12:08:06 -0700459 io_slot->read_bytes += task->ioac.read_bytes;
460 io_slot->write_bytes += compute_write_bytes(task);
461 io_slot->rchar += task->ioac.rchar;
462 io_slot->wchar += task->ioac.wchar;
463 io_slot->fsync += task->ioac.syscfs;
Jin Qian012f2002017-01-10 16:10:35 -0800464
Yang Jin6e8f1e72017-07-26 12:52:22 -0700465 add_uid_tasks_io_stats(uid_entry, task, slot);
Jin Qian012f2002017-01-10 16:10:35 -0800466}
467
Jin Qian67f252c2017-04-13 17:07:58 -0700468static void update_io_stats_all_locked(void)
Jin Qian012f2002017-01-10 16:10:35 -0800469{
Ganesh Mahendran2b3da4a2017-05-25 15:20:29 +0800470 struct uid_entry *uid_entry = NULL;
Jin Qian012f2002017-01-10 16:10:35 -0800471 struct task_struct *task, *temp;
Jin Qian67f252c2017-04-13 17:07:58 -0700472 struct user_namespace *user_ns = current_user_ns();
Jin Qian012f2002017-01-10 16:10:35 -0800473 unsigned long bkt;
Jin Qian67f252c2017-04-13 17:07:58 -0700474 uid_t uid;
Jin Qian012f2002017-01-10 16:10:35 -0800475
Yang Jin6e8f1e72017-07-26 12:52:22 -0700476 hash_for_each(hash_table, bkt, uid_entry, hash) {
Jin Qian012f2002017-01-10 16:10:35 -0800477 memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
478 sizeof(struct io_stats));
Yang Jin6e8f1e72017-07-26 12:52:22 -0700479 set_io_uid_tasks_zero(uid_entry);
480 }
Jin Qian012f2002017-01-10 16:10:35 -0800481
Jin Qian67f252c2017-04-13 17:07:58 -0700482 rcu_read_lock();
Jin Qian012f2002017-01-10 16:10:35 -0800483 do_each_thread(temp, task) {
Jin Qian67f252c2017-04-13 17:07:58 -0700484 uid = from_kuid_munged(user_ns, task_uid(task));
Ganesh Mahendran2b3da4a2017-05-25 15:20:29 +0800485 if (!uid_entry || uid_entry->uid != uid)
486 uid_entry = find_or_register_uid(uid);
Jin Qian012f2002017-01-10 16:10:35 -0800487 if (!uid_entry)
488 continue;
Jin Qiancd5e19c2017-05-22 12:08:06 -0700489 add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
Jin Qian012f2002017-01-10 16:10:35 -0800490 } while_each_thread(temp, task);
Jin Qian67f252c2017-04-13 17:07:58 -0700491 rcu_read_unlock();
Jin Qian012f2002017-01-10 16:10:35 -0800492
493 hash_for_each(hash_table, bkt, uid_entry, hash) {
Yang Jin6e8f1e72017-07-26 12:52:22 -0700494 compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
Jin Qiancd5e19c2017-05-22 12:08:06 -0700495 &uid_entry->io[UID_STATE_TOTAL_CURR],
496 &uid_entry->io[UID_STATE_TOTAL_LAST],
497 &uid_entry->io[UID_STATE_DEAD_TASKS]);
Yang Jin6e8f1e72017-07-26 12:52:22 -0700498 compute_io_uid_tasks(uid_entry);
Jin Qian012f2002017-01-10 16:10:35 -0800499 }
500}
501
Jin Qiancd5e19c2017-05-22 12:08:06 -0700502static void update_io_stats_uid_locked(struct uid_entry *uid_entry)
Jin Qian67f252c2017-04-13 17:07:58 -0700503{
Jin Qian67f252c2017-04-13 17:07:58 -0700504 struct task_struct *task, *temp;
Jin Qian67f252c2017-04-13 17:07:58 -0700505 struct user_namespace *user_ns = current_user_ns();
506
Jin Qian67f252c2017-04-13 17:07:58 -0700507 memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
508 sizeof(struct io_stats));
Yang Jin6e8f1e72017-07-26 12:52:22 -0700509 set_io_uid_tasks_zero(uid_entry);
Jin Qian67f252c2017-04-13 17:07:58 -0700510
511 rcu_read_lock();
512 do_each_thread(temp, task) {
Jin Qiancd5e19c2017-05-22 12:08:06 -0700513 if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid)
Jin Qian67f252c2017-04-13 17:07:58 -0700514 continue;
Jin Qiancd5e19c2017-05-22 12:08:06 -0700515 add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
Jin Qian67f252c2017-04-13 17:07:58 -0700516 } while_each_thread(temp, task);
517 rcu_read_unlock();
518
Yang Jin6e8f1e72017-07-26 12:52:22 -0700519 compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
Jin Qiancd5e19c2017-05-22 12:08:06 -0700520 &uid_entry->io[UID_STATE_TOTAL_CURR],
521 &uid_entry->io[UID_STATE_TOTAL_LAST],
522 &uid_entry->io[UID_STATE_DEAD_TASKS]);
Yang Jin6e8f1e72017-07-26 12:52:22 -0700523 compute_io_uid_tasks(uid_entry);
Jin Qian67f252c2017-04-13 17:07:58 -0700524}
525
Yang Jin6e8f1e72017-07-26 12:52:22 -0700526
Jin Qian012f2002017-01-10 16:10:35 -0800527static int uid_io_show(struct seq_file *m, void *v)
528{
529 struct uid_entry *uid_entry;
530 unsigned long bkt;
531
Wei Wang7f1ad082017-03-13 12:22:21 -0700532 rt_mutex_lock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800533
Jin Qian67f252c2017-04-13 17:07:58 -0700534 update_io_stats_all_locked();
Jin Qian012f2002017-01-10 16:10:35 -0800535
536 hash_for_each(hash_table, bkt, uid_entry, hash) {
Jin Qian62eb9f92017-03-02 13:39:43 -0800537 seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
Yang Jin6e8f1e72017-07-26 12:52:22 -0700538 uid_entry->uid,
539 uid_entry->io[UID_STATE_FOREGROUND].rchar,
540 uid_entry->io[UID_STATE_FOREGROUND].wchar,
541 uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
542 uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
543 uid_entry->io[UID_STATE_BACKGROUND].rchar,
544 uid_entry->io[UID_STATE_BACKGROUND].wchar,
545 uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
546 uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
547 uid_entry->io[UID_STATE_FOREGROUND].fsync,
548 uid_entry->io[UID_STATE_BACKGROUND].fsync);
549
550 show_io_uid_tasks(m, uid_entry);
Jin Qian012f2002017-01-10 16:10:35 -0800551 }
552
Wei Wang7f1ad082017-03-13 12:22:21 -0700553 rt_mutex_unlock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800554 return 0;
555}
556
557static int uid_io_open(struct inode *inode, struct file *file)
558{
559 return single_open(file, uid_io_show, PDE_DATA(inode));
560}
561
562static const struct file_operations uid_io_fops = {
563 .open = uid_io_open,
564 .read = seq_read,
565 .llseek = seq_lseek,
566 .release = single_release,
567};
568
569static int uid_procstat_open(struct inode *inode, struct file *file)
570{
571 return single_open(file, NULL, NULL);
572}
573
574static ssize_t uid_procstat_write(struct file *file,
575 const char __user *buffer, size_t count, loff_t *ppos)
576{
577 struct uid_entry *uid_entry;
578 uid_t uid;
579 int argc, state;
580 char input[128];
581
582 if (count >= sizeof(input))
583 return -EINVAL;
584
585 if (copy_from_user(input, buffer, count))
586 return -EFAULT;
587
588 input[count] = '\0';
589
590 argc = sscanf(input, "%u %d", &uid, &state);
591 if (argc != 2)
592 return -EINVAL;
593
594 if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
595 return -EINVAL;
596
Wei Wang7f1ad082017-03-13 12:22:21 -0700597 rt_mutex_lock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800598
599 uid_entry = find_or_register_uid(uid);
Jin Qian8782d8f2017-01-17 17:26:07 -0800600 if (!uid_entry) {
Wei Wang7f1ad082017-03-13 12:22:21 -0700601 rt_mutex_unlock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800602 return -EINVAL;
603 }
604
Jin Qian8782d8f2017-01-17 17:26:07 -0800605 if (uid_entry->state == state) {
Wei Wang7f1ad082017-03-13 12:22:21 -0700606 rt_mutex_unlock(&uid_lock);
Jin Qian8782d8f2017-01-17 17:26:07 -0800607 return count;
608 }
609
Jin Qiancd5e19c2017-05-22 12:08:06 -0700610 update_io_stats_uid_locked(uid_entry);
Jin Qian012f2002017-01-10 16:10:35 -0800611
612 uid_entry->state = state;
613
Wei Wang7f1ad082017-03-13 12:22:21 -0700614 rt_mutex_unlock(&uid_lock);
Jin Qian012f2002017-01-10 16:10:35 -0800615
616 return count;
617}
618
619static const struct file_operations uid_procstat_fops = {
620 .open = uid_procstat_open,
621 .release = single_release,
622 .write = uid_procstat_write,
623};
624
jinqian69014222015-03-11 10:44:50 -0700625static int process_notifier(struct notifier_block *self,
626 unsigned long cmd, void *v)
627{
628 struct task_struct *task = v;
629 struct uid_entry *uid_entry;
630 cputime_t utime, stime;
631 uid_t uid;
632
633 if (!task)
634 return NOTIFY_OK;
635
Wei Wang7f1ad082017-03-13 12:22:21 -0700636 rt_mutex_lock(&uid_lock);
Amit Pundir48a99062015-04-15 00:40:21 +0530637 uid = from_kuid_munged(current_user_ns(), task_uid(task));
jinqian69014222015-03-11 10:44:50 -0700638 uid_entry = find_or_register_uid(uid);
639 if (!uid_entry) {
640 pr_err("%s: failed to find uid %d\n", __func__, uid);
641 goto exit;
642 }
643
644 task_cputime_adjusted(task, &utime, &stime);
645 uid_entry->utime += utime;
646 uid_entry->stime += stime;
647
Jin Qiancd5e19c2017-05-22 12:08:06 -0700648 add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS);
Jin Qian012f2002017-01-10 16:10:35 -0800649
jinqian69014222015-03-11 10:44:50 -0700650exit:
Wei Wang7f1ad082017-03-13 12:22:21 -0700651 rt_mutex_unlock(&uid_lock);
jinqian69014222015-03-11 10:44:50 -0700652 return NOTIFY_OK;
653}
654
655static struct notifier_block process_notifier_block = {
656 .notifier_call = process_notifier,
657};
658
Jin Qian012f2002017-01-10 16:10:35 -0800659static int __init proc_uid_sys_stats_init(void)
jinqian69014222015-03-11 10:44:50 -0700660{
661 hash_init(hash_table);
662
Jin Qian012f2002017-01-10 16:10:35 -0800663 cpu_parent = proc_mkdir("uid_cputime", NULL);
664 if (!cpu_parent) {
665 pr_err("%s: failed to create uid_cputime proc entry\n",
666 __func__);
667 goto err;
jinqian69014222015-03-11 10:44:50 -0700668 }
669
Jin Qian012f2002017-01-10 16:10:35 -0800670 proc_create_data("remove_uid_range", 0222, cpu_parent,
671 &uid_remove_fops, NULL);
672 proc_create_data("show_uid_stat", 0444, cpu_parent,
673 &uid_cputime_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700674
Jin Qian012f2002017-01-10 16:10:35 -0800675 io_parent = proc_mkdir("uid_io", NULL);
676 if (!io_parent) {
677 pr_err("%s: failed to create uid_io proc entry\n",
678 __func__);
679 goto err;
680 }
681
682 proc_create_data("stats", 0444, io_parent,
683 &uid_io_fops, NULL);
684
685 proc_parent = proc_mkdir("uid_procstat", NULL);
686 if (!proc_parent) {
687 pr_err("%s: failed to create uid_procstat proc entry\n",
688 __func__);
689 goto err;
690 }
691
692 proc_create_data("set", 0222, proc_parent,
693 &uid_procstat_fops, NULL);
jinqian69014222015-03-11 10:44:50 -0700694
695 profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
696
697 return 0;
Jin Qian012f2002017-01-10 16:10:35 -0800698
699err:
700 remove_proc_subtree("uid_cputime", NULL);
701 remove_proc_subtree("uid_io", NULL);
702 remove_proc_subtree("uid_procstat", NULL);
703 return -ENOMEM;
jinqian69014222015-03-11 10:44:50 -0700704}
705
Jin Qian012f2002017-01-10 16:10:35 -0800706early_initcall(proc_uid_sys_stats_init);