1/* drivers/misc/uid_stat.c 2 * 3 * Copyright (C) 2008 - 2009 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 <asm/atomic.h> 17 18#include <linux/err.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/seq_file.h> 24#include <linux/slab.h> 25#include <linux/spinlock.h> 26#include <linux/stat.h> 27#include <linux/uid_stat.h> 28#include <net/activity_stats.h> 29 30static DEFINE_SPINLOCK(uid_lock); 31static LIST_HEAD(uid_list); 32static struct proc_dir_entry *parent; 33 34struct uid_stat { 35 struct list_head link; 36 uid_t uid; 37 atomic_t tcp_rcv; 38 atomic_t tcp_snd; 39}; 40 41static struct uid_stat *find_uid_stat(uid_t uid) { 42 struct uid_stat *entry; 43 44 list_for_each_entry(entry, &uid_list, link) { 45 if (entry->uid == uid) { 46 return entry; 47 } 48 } 49 return NULL; 50} 51 52static int uid_stat_atomic_int_show(struct seq_file *m, void *v) 53{ 54 unsigned int bytes; 55 atomic_t *counter = m->private; 56 57 bytes = (unsigned int) (atomic_read(counter) + INT_MIN); 58 return seq_printf(m, "%u\n", bytes); 59} 60 61static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file) 62{ 63 return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode)); 64} 65 66static const struct file_operations uid_stat_read_atomic_int_fops = { 67 .open = uid_stat_read_atomic_int_open, 68 .read = seq_read, 69 .llseek = seq_lseek, 70 .release = seq_release, 71}; 72 73/* Create a new entry for tracking the specified uid. */ 74static struct uid_stat *create_stat(uid_t uid) { 75 struct uid_stat *new_uid; 76 /* Create the uid stat struct and append it to the list. */ 77 new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC); 78 if (!new_uid) 79 return NULL; 80 81 new_uid->uid = uid; 82 /* Counters start at INT_MIN, so we can track 4GB of network traffic. */ 83 atomic_set(&new_uid->tcp_rcv, INT_MIN); 84 atomic_set(&new_uid->tcp_snd, INT_MIN); 85 86 list_add_tail(&new_uid->link, &uid_list); 87 return new_uid; 88} 89 90static void create_stat_proc(struct uid_stat *new_uid) 91{ 92 char uid_s[32]; 93 struct proc_dir_entry *entry; 94 sprintf(uid_s, "%d", new_uid->uid); 95 entry = proc_mkdir(uid_s, parent); 96 97 /* Keep reference to uid_stat so we know what uid to read stats from. */ 98 proc_create_data("tcp_snd", S_IRUGO, entry, 99 &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd); 100 101 proc_create_data("tcp_rcv", S_IRUGO, entry, 102 &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv); 103} 104 105static struct uid_stat *find_or_create_uid_stat(uid_t uid) 106{ 107 struct uid_stat *entry; 108 unsigned long flags; 109 spin_lock_irqsave(&uid_lock, flags); 110 entry = find_uid_stat(uid); 111 if (entry) { 112 spin_unlock_irqrestore(&uid_lock, flags); 113 return entry; 114 } 115 entry = create_stat(uid); 116 spin_unlock_irqrestore(&uid_lock, flags); 117 if (entry) 118 create_stat_proc(entry); 119 return entry; 120} 121 122int uid_stat_tcp_snd(uid_t uid, int size) { 123 struct uid_stat *entry; 124 activity_stats_update(); 125 entry = find_or_create_uid_stat(uid); 126 if (!entry) 127 return -1; 128 atomic_add(size, &entry->tcp_snd); 129 return 0; 130} 131 132int uid_stat_tcp_rcv(uid_t uid, int size) { 133 struct uid_stat *entry; 134 activity_stats_update(); 135 entry = find_or_create_uid_stat(uid); 136 if (!entry) 137 return -1; 138 atomic_add(size, &entry->tcp_rcv); 139 return 0; 140} 141 142static int __init uid_stat_init(void) 143{ 144 parent = proc_mkdir("uid_stat", NULL); 145 if (!parent) { 146 pr_err("uid_stat: failed to create proc entry\n"); 147 return -1; 148 } 149 return 0; 150} 151 152__initcall(uid_stat_init); 153