uid_cputime.c 6.4 KB

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