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