uid_cputime.c revision 9d901e05f401bb6a17fc436f372c69264c56d016
1/* 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); 33static struct proc_dir_entry *parent; 34 35struct uid_entry { 36 uid_t uid; 37 cputime_t utime; 38 cputime_t stime; 39 cputime_t active_utime; 40 cputime_t active_stime; 41 struct hlist_node hash; 42}; 43 44static struct uid_entry *find_uid_entry(uid_t uid) 45{ 46 struct uid_entry *uid_entry; 47 hash_for_each_possible(hash_table, uid_entry, hash, uid) { 48 if (uid_entry->uid == uid) 49 return uid_entry; 50 } 51 return NULL; 52} 53 54static struct uid_entry *find_or_register_uid(uid_t uid) 55{ 56 struct uid_entry *uid_entry; 57 58 uid_entry = find_uid_entry(uid); 59 if (uid_entry) 60 return uid_entry; 61 62 uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC); 63 if (!uid_entry) 64 return NULL; 65 66 uid_entry->uid = uid; 67 68 hash_add(hash_table, &uid_entry->hash, uid); 69 70 return uid_entry; 71} 72 73static int uid_stat_show(struct seq_file *m, void *v) 74{ 75 struct uid_entry *uid_entry; 76 struct task_struct *task; 77 cputime_t utime; 78 cputime_t stime; 79 unsigned long bkt; 80 81 mutex_lock(&uid_lock); 82 83 hash_for_each(hash_table, bkt, uid_entry, hash) { 84 uid_entry->active_stime = 0; 85 uid_entry->active_utime = 0; 86 } 87 88 read_lock(&tasklist_lock); 89 for_each_process(task) { 90 uid_entry = find_or_register_uid(from_kuid_munged( 91 current_user_ns(), task_uid(task))); 92 if (!uid_entry) { 93 read_unlock(&tasklist_lock); 94 mutex_unlock(&uid_lock); 95 pr_err("%s: failed to find the uid_entry for uid %d\n", 96 __func__, from_kuid_munged(current_user_ns(), 97 task_uid(task))); 98 return -ENOMEM; 99 } 100 task_cputime_adjusted(task, &utime, &stime); 101 uid_entry->active_utime += utime; 102 uid_entry->active_stime += stime; 103 } 104 read_unlock(&tasklist_lock); 105 106 hash_for_each(hash_table, bkt, uid_entry, hash) { 107 cputime_t total_utime = uid_entry->utime + 108 uid_entry->active_utime; 109 cputime_t total_stime = uid_entry->stime + 110 uid_entry->active_stime; 111 seq_printf(m, "%d: %u %u\n", uid_entry->uid, 112 cputime_to_usecs(total_utime), 113 cputime_to_usecs(total_stime)); 114 } 115 116 mutex_unlock(&uid_lock); 117 return 0; 118} 119 120static int uid_stat_open(struct inode *inode, struct file *file) 121{ 122 return single_open(file, uid_stat_show, PDE_DATA(inode)); 123} 124 125static const struct file_operations uid_stat_fops = { 126 .open = uid_stat_open, 127 .read = seq_read, 128 .llseek = seq_lseek, 129 .release = single_release, 130}; 131 132static int uid_remove_open(struct inode *inode, struct file *file) 133{ 134 return single_open(file, NULL, NULL); 135} 136 137static ssize_t uid_remove_write(struct file *file, 138 const char __user *buffer, size_t count, loff_t *ppos) 139{ 140 struct uid_entry *uid_entry; 141 struct hlist_node *tmp; 142 char uids[128]; 143 char *start_uid, *end_uid = NULL; 144 long int uid_start = 0, uid_end = 0; 145 146 if (count >= sizeof(uids)) 147 count = sizeof(uids) - 1; 148 149 if (copy_from_user(uids, buffer, count)) 150 return -EFAULT; 151 152 uids[count] = '\0'; 153 end_uid = uids; 154 start_uid = strsep(&end_uid, "-"); 155 156 if (!start_uid || !end_uid) 157 return -EINVAL; 158 159 if (kstrtol(start_uid, 10, &uid_start) != 0 || 160 kstrtol(end_uid, 10, &uid_end) != 0) { 161 return -EINVAL; 162 } 163 164 mutex_lock(&uid_lock); 165 166 for (; uid_start <= uid_end; uid_start++) { 167 hash_for_each_possible_safe(hash_table, uid_entry, tmp, 168 hash, uid_start) { 169 hash_del(&uid_entry->hash); 170 kfree(uid_entry); 171 } 172 } 173 174 mutex_unlock(&uid_lock); 175 return count; 176} 177 178static const struct file_operations uid_remove_fops = { 179 .open = uid_remove_open, 180 .release = single_release, 181 .write = uid_remove_write, 182}; 183 184static int process_notifier(struct notifier_block *self, 185 unsigned long cmd, void *v) 186{ 187 struct task_struct *task = v; 188 struct uid_entry *uid_entry; 189 cputime_t utime, stime; 190 uid_t uid; 191 192 if (!task) 193 return NOTIFY_OK; 194 195 mutex_lock(&uid_lock); 196 uid = from_kuid_munged(current_user_ns(), task_uid(task)); 197 uid_entry = find_or_register_uid(uid); 198 if (!uid_entry) { 199 pr_err("%s: failed to find uid %d\n", __func__, uid); 200 goto exit; 201 } 202 203 task_cputime_adjusted(task, &utime, &stime); 204 uid_entry->utime += utime; 205 uid_entry->stime += stime; 206 207exit: 208 mutex_unlock(&uid_lock); 209 return NOTIFY_OK; 210} 211 212static struct notifier_block process_notifier_block = { 213 .notifier_call = process_notifier, 214}; 215 216static int __init proc_uid_cputime_init(void) 217{ 218 hash_init(hash_table); 219 220 parent = proc_mkdir("uid_cputime", NULL); 221 if (!parent) { 222 pr_err("%s: failed to create proc entry\n", __func__); 223 return -ENOMEM; 224 } 225 226 proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops, 227 NULL); 228 229 proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops, 230 NULL); 231 232 profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); 233 234 return 0; 235} 236 237early_initcall(proc_uid_cputime_init); 238