blob: 2141124a6c1291e2ace75a465c5c3b803364531c [file] [log] [blame]
Mike Chan5a0b3542009-01-07 11:40:42 -08001/* 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/slab.h>
24#include <linux/spinlock.h>
25#include <linux/stat.h>
26#include <linux/uid_stat.h>
Mike Chane24fb0b2010-05-28 14:32:19 -070027#include <net/activity_stats.h>
Mike Chan5a0b3542009-01-07 11:40:42 -080028
29static DEFINE_SPINLOCK(uid_lock);
30static LIST_HEAD(uid_list);
31static struct proc_dir_entry *parent;
32
33struct uid_stat {
34 struct list_head link;
35 uid_t uid;
36 atomic_t tcp_rcv;
37 atomic_t tcp_snd;
38};
39
40static struct uid_stat *find_uid_stat(uid_t uid) {
41 unsigned long flags;
42 struct uid_stat *entry;
43
44 spin_lock_irqsave(&uid_lock, flags);
45 list_for_each_entry(entry, &uid_list, link) {
46 if (entry->uid == uid) {
47 spin_unlock_irqrestore(&uid_lock, flags);
48 return entry;
49 }
50 }
51 spin_unlock_irqrestore(&uid_lock, flags);
52 return NULL;
53}
54
55static int tcp_snd_read_proc(char *page, char **start, off_t off,
56 int count, int *eof, void *data)
57{
58 int len;
59 unsigned int bytes;
60 char *p = page;
61 struct uid_stat *uid_entry = (struct uid_stat *) data;
62 if (!data)
63 return 0;
64
65 bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN);
66 p += sprintf(p, "%u\n", bytes);
67 len = (p - page) - off;
68 *eof = (len <= count) ? 1 : 0;
69 *start = page + off;
70 return len;
71}
72
73static int tcp_rcv_read_proc(char *page, char **start, off_t off,
74 int count, int *eof, void *data)
75{
76 int len;
77 unsigned int bytes;
78 char *p = page;
79 struct uid_stat *uid_entry = (struct uid_stat *) data;
80 if (!data)
81 return 0;
82
83 bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN);
84 p += sprintf(p, "%u\n", bytes);
85 len = (p - page) - off;
86 *eof = (len <= count) ? 1 : 0;
87 *start = page + off;
88 return len;
89}
90
91/* Create a new entry for tracking the specified uid. */
92static struct uid_stat *create_stat(uid_t uid) {
93 unsigned long flags;
94 char uid_s[32];
95 struct uid_stat *new_uid;
96 struct proc_dir_entry *entry;
97
98 /* Create the uid stat struct and append it to the list. */
99 if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL)
100 return NULL;
101
102 new_uid->uid = uid;
103 /* Counters start at INT_MIN, so we can track 4GB of network traffic. */
104 atomic_set(&new_uid->tcp_rcv, INT_MIN);
105 atomic_set(&new_uid->tcp_snd, INT_MIN);
106
107 spin_lock_irqsave(&uid_lock, flags);
108 list_add_tail(&new_uid->link, &uid_list);
109 spin_unlock_irqrestore(&uid_lock, flags);
110
111 sprintf(uid_s, "%d", uid);
112 entry = proc_mkdir(uid_s, parent);
113
114 /* Keep reference to uid_stat so we know what uid to read stats from. */
115 create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc,
116 (void *) new_uid);
117
118 create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc,
119 (void *) new_uid);
120
121 return new_uid;
122}
123
124int uid_stat_tcp_snd(uid_t uid, int size) {
125 struct uid_stat *entry;
Mike Chane24fb0b2010-05-28 14:32:19 -0700126 activity_stats_update();
Mike Chan5a0b3542009-01-07 11:40:42 -0800127 if ((entry = find_uid_stat(uid)) == NULL &&
128 ((entry = create_stat(uid)) == NULL)) {
129 return -1;
130 }
131 atomic_add(size, &entry->tcp_snd);
132 return 0;
133}
134
135int uid_stat_tcp_rcv(uid_t uid, int size) {
136 struct uid_stat *entry;
Mike Chane24fb0b2010-05-28 14:32:19 -0700137 activity_stats_update();
Mike Chan5a0b3542009-01-07 11:40:42 -0800138 if ((entry = find_uid_stat(uid)) == NULL &&
139 ((entry = create_stat(uid)) == NULL)) {
140 return -1;
141 }
142 atomic_add(size, &entry->tcp_rcv);
143 return 0;
144}
145
146static int __init uid_stat_init(void)
147{
148 parent = proc_mkdir("uid_stat", NULL);
149 if (!parent) {
150 pr_err("uid_stat: failed to create proc entry\n");
151 return -1;
152 }
153 return 0;
154}
155
156__initcall(uid_stat_init);