]> rtime.felk.cvut.cz Git - linux-imx.git/blob - drivers/misc/iface_stat.c
mx53: CPU HW idle state set to WAIT_CLOCKED
[linux-imx.git] / drivers / misc / iface_stat.c
1 /* drivers/misc/iface_stat.c
2  *
3  * Copyright (C) 2011 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 <linux/err.h>
17 #include <linux/init.h>
18 #include <linux/kernel.h>
19 #include <linux/in.h>
20 #include <linux/list.h>
21 #include <linux/proc_fs.h>
22 #include <linux/slab.h>
23 #include <linux/stat.h>
24 #include <linux/netdevice.h>
25 #include <linux/inetdevice.h>
26 #include <linux/rtnetlink.h>
27 #include <linux/iface_stat.h>
28
29 static LIST_HEAD(iface_list);
30 static struct proc_dir_entry *iface_stat_procdir;
31
32 struct iface_stat {
33         struct list_head if_link;
34         char *iface_name;
35         unsigned long tx_bytes;
36         unsigned long rx_bytes;
37         unsigned long tx_packets;
38         unsigned long rx_packets;
39         bool active;
40 };
41
42 static int read_proc_entry(char *page, char **start, off_t off,
43                 int count, int *eof, void *data)
44 {
45         int len;
46         unsigned long value;
47         char *p = page;
48         unsigned long *iface_entry = (unsigned long *) data;
49         if (!data)
50                 return 0;
51
52         value = (unsigned long) (*iface_entry);
53         p += sprintf(p, "%lu\n", value);
54         len = (p - page) - off;
55         *eof = (len <= count) ? 1 : 0;
56         *start = page + off;
57         return len;
58 }
59
60 static int read_proc_bool_entry(char *page, char **start, off_t off,
61                 int count, int *eof, void *data)
62 {
63         int len;
64         bool value;
65         char *p = page;
66         unsigned long *iface_entry = (unsigned long *) data;
67         if (!data)
68                 return 0;
69
70         value = (bool) (*iface_entry);
71         p += sprintf(p, "%u\n", value ? 1 : 0);
72         len = (p - page) - off;
73         *eof = (len <= count) ? 1 : 0;
74         *start = page + off;
75         return len;
76 }
77
78 /* Find the entry for tracking the specified interface. */
79 static struct iface_stat *get_iface_stat(const char *ifname)
80 {
81         struct iface_stat *iface_entry;
82         if (!ifname)
83                 return NULL;
84
85         list_for_each_entry(iface_entry, &iface_list, if_link) {
86                 if (!strcmp(iface_entry->iface_name, ifname))
87                         return iface_entry;
88         }
89         return NULL;
90 }
91
92 /*
93  * Create a new entry for tracking the specified interface.
94  * Do nothing if the entry already exists.
95  * Called when an interface is configured with a valid IP address.
96  */
97 void create_iface_stat(const struct in_device *in_dev)
98 {
99         struct iface_stat *new_iface;
100         struct proc_dir_entry *proc_entry;
101         const struct net_device *dev;
102         const char *ifname;
103         struct iface_stat *entry;
104         __be32 ipaddr = 0;
105         struct in_ifaddr *ifa = NULL;
106
107         ASSERT_RTNL(); /* No need for separate locking */
108
109         dev = in_dev->dev;
110         if (!dev) {
111                 pr_err("iface_stat: This should never happen.\n");
112                 return;
113         }
114
115         ifname = dev->name;
116         for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
117                 if (!strcmp(dev->name, ifa->ifa_label))
118                         break;
119
120         if (ifa)
121                 ipaddr = ifa->ifa_local;
122         else {
123                 pr_err("iface_stat: Interface not found.\n");
124                 return;
125         }
126
127         entry = get_iface_stat(dev->name);
128         if (entry != NULL) {
129                 pr_debug("iface_stat: Already monitoring device %s\n", ifname);
130                 if (ipv4_is_loopback(ipaddr)) {
131                         entry->active = false;
132                         pr_debug("iface_stat: Disabling monitor for "
133                                         "loopback device %s\n", ifname);
134                 } else {
135                         entry->active = true;
136                         pr_debug("iface_stat: Re-enabling monitor for "
137                                         "device %s with ip %pI4\n",
138                                         ifname, &ipaddr);
139                 }
140                 return;
141         } else if (ipv4_is_loopback(ipaddr)) {
142                 pr_debug("iface_stat: Ignoring monitor for "
143                                 "loopback device %s with ip %pI4\n",
144                                 ifname, &ipaddr);
145                 return;
146         }
147
148         /* Create a new entry for tracking the specified interface. */
149         new_iface = kmalloc(sizeof(struct iface_stat), GFP_KERNEL);
150         if (new_iface == NULL)
151                 return;
152
153         new_iface->iface_name = kmalloc((strlen(ifname)+1)*sizeof(char),
154                                                 GFP_KERNEL);
155         if (new_iface->iface_name == NULL) {
156                 kfree(new_iface);
157                 return;
158         }
159
160         strcpy(new_iface->iface_name, ifname);
161         /* Counters start at 0, so we can track 4GB of network traffic. */
162         new_iface->tx_bytes = 0;
163         new_iface->rx_bytes = 0;
164         new_iface->rx_packets = 0;
165         new_iface->tx_packets = 0;
166         new_iface->active = true;
167
168         /* Append the newly created iface stat struct to the list. */
169         list_add_tail(&new_iface->if_link, &iface_list);
170         proc_entry = proc_mkdir(ifname, iface_stat_procdir);
171
172         /* Keep reference to iface_stat so we know where to read stats from. */
173         create_proc_read_entry("tx_bytes", S_IRUGO, proc_entry,
174                         read_proc_entry, &new_iface->tx_bytes);
175
176         create_proc_read_entry("rx_bytes", S_IRUGO, proc_entry,
177                         read_proc_entry, &new_iface->rx_bytes);
178
179         create_proc_read_entry("tx_packets", S_IRUGO, proc_entry,
180                         read_proc_entry, &new_iface->tx_packets);
181
182         create_proc_read_entry("rx_packets", S_IRUGO, proc_entry,
183                         read_proc_entry, &new_iface->rx_packets);
184
185         create_proc_read_entry("active", S_IRUGO, proc_entry,
186                         read_proc_bool_entry, &new_iface->active);
187
188         pr_debug("iface_stat: Now monitoring device %s with ip %pI4\n",
189                         ifname, &ipaddr);
190 }
191
192 /*
193  * Update stats for the specified interface. Do nothing if the entry
194  * does not exist (when a device was never configured with an IP address).
195  * Called when an device is being unregistered.
196  */
197 void iface_stat_update(struct net_device *dev)
198 {
199         const struct net_device_stats *stats = dev_get_stats(dev);
200         struct iface_stat *entry;
201
202         ASSERT_RTNL();
203
204         entry = get_iface_stat(dev->name);
205         if (entry == NULL) {
206                 pr_debug("iface_stat: dev %s monitor not found\n", dev->name);
207                 return;
208         }
209
210         if (entry->active) { /* FIXME: Support for more than 4GB */
211                 entry->tx_bytes += stats->tx_bytes;
212                 entry->tx_packets += stats->tx_packets;
213                 entry->rx_bytes += stats->rx_bytes;
214                 entry->rx_packets += stats->rx_packets;
215                 entry->active = false;
216                 pr_debug("iface_stat: Updating stats for "
217                                "dev %s which went down\n", dev->name);
218         } else
219                 pr_debug("iface_stat: Didn't update stats for "
220                                 "dev %s which went down\n", dev->name);
221 }
222
223 static int __init iface_stat_init(void)
224 {
225         iface_stat_procdir = proc_mkdir("iface_stat", NULL);
226         if (!iface_stat_procdir) {
227                 pr_err("iface_stat: failed to create proc entry\n");
228                 return -1;
229         }
230
231         return 0;
232 }
233
234 device_initcall(iface_stat_init);