uid_cputime.c 5.8 KB

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