diff --git a/arch/arm/configs/htcleo_defconfig b/arch/arm/configs/htcleo_defconfig index c8fdf493..64a7c895 100644 --- a/arch/arm/configs/htcleo_defconfig +++ b/arch/arm/configs/htcleo_defconfig @@ -903,6 +903,7 @@ CONFIG_ANDROID_PMEM=y CONFIG_KERNEL_DEBUGGER_CORE=y # CONFIG_ISL29003 is not set CONFIG_UID_STAT=y +CONFIG_IFACE_STAT=y # CONFIG_WL127X_RFKILL is not set CONFIG_APANIC=y CONFIG_APANIC_PLABEL="kpanic" diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 11623b22..0906f6c9 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -261,6 +261,10 @@ config UID_STAT bool "UID based statistics tracking exported to /proc/uid_stat" default n +config IFACE_STAT + bool "Persistent interface statistics tracking exported to /proc/iface_stat" + default n + config WL127X_RFKILL tristate "Bluetooth power control driver for TI wl127x" depends on RFKILL diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 58deb976..3481842f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_HP_ILO) += hpilo.o obj-$(CONFIG_ISL29003) += isl29003.o obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o obj-$(CONFIG_UID_STAT) += uid_stat.o +obj-$(CONFIG_IFACE_STAT) += iface_stat.o obj-$(CONFIG_C2PORT) += c2port/ obj-y += eeprom/ obj-$(CONFIG_MSM_720P_CORE) += video_core/720p/ diff --git a/drivers/misc/iface_stat.c b/drivers/misc/iface_stat.c new file mode 100644 index 00000000..bd16449f --- /dev/null +++ b/drivers/misc/iface_stat.c @@ -0,0 +1,234 @@ +/* drivers/misc/iface_stat.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(iface_list); +static struct proc_dir_entry *iface_stat_procdir; + +struct iface_stat { + struct list_head if_link; + char *iface_name; + unsigned long tx_bytes; + unsigned long rx_bytes; + unsigned long tx_packets; + unsigned long rx_packets; + bool active; +}; + +static int read_proc_entry(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + unsigned long value; + char *p = page; + unsigned long *iface_entry = (unsigned long *) data; + if (!data) + return 0; + + value = (unsigned long) (*iface_entry); + p += sprintf(p, "%lu\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int read_proc_bool_entry(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + bool value; + char *p = page; + unsigned long *iface_entry = (unsigned long *) data; + if (!data) + return 0; + + value = (bool) (*iface_entry); + p += sprintf(p, "%u\n", value ? 1 : 0); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +/* Find the entry for tracking the specified interface. */ +static struct iface_stat *get_iface_stat(const char *ifname) +{ + struct iface_stat *iface_entry; + if (!ifname) + return NULL; + + list_for_each_entry(iface_entry, &iface_list, if_link) { + if (!strcmp(iface_entry->iface_name, ifname)) + return iface_entry; + } + return NULL; +} + +/* + * Create a new entry for tracking the specified interface. + * Do nothing if the entry already exists. + * Called when an interface is configured with a valid IP address. + */ +void create_iface_stat(const struct in_device *in_dev) +{ + struct iface_stat *new_iface; + struct proc_dir_entry *proc_entry; + const struct net_device *dev; + const char *ifname; + struct iface_stat *entry; + __be32 ipaddr = 0; + struct in_ifaddr *ifa = NULL; + + ASSERT_RTNL(); /* No need for separate locking */ + + dev = in_dev->dev; + if (!dev) { + pr_err("iface_stat: This should never happen.\n"); + return; + } + + ifname = dev->name; + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) + if (!strcmp(dev->name, ifa->ifa_label)) + break; + + if (ifa) + ipaddr = ifa->ifa_local; + else { + pr_err("iface_stat: Interface not found.\n"); + return; + } + + entry = get_iface_stat(dev->name); + if (entry != NULL) { + pr_debug("iface_stat: Already monitoring device %s\n", ifname); + if (ipv4_is_loopback(ipaddr)) { + entry->active = false; + pr_debug("iface_stat: Disabling monitor for " + "loopback device %s\n", ifname); + } else { + entry->active = true; + pr_debug("iface_stat: Re-enabling monitor for " + "device %s with ip %pI4\n", + ifname, &ipaddr); + } + return; + } else if (ipv4_is_loopback(ipaddr)) { + pr_debug("iface_stat: Ignoring monitor for " + "loopback device %s with ip %pI4\n", + ifname, &ipaddr); + return; + } + + /* Create a new entry for tracking the specified interface. */ + new_iface = kmalloc(sizeof(struct iface_stat), GFP_KERNEL); + if (new_iface == NULL) + return; + + new_iface->iface_name = kmalloc((strlen(ifname)+1)*sizeof(char), + GFP_KERNEL); + if (new_iface->iface_name == NULL) { + kfree(new_iface); + return; + } + + strcpy(new_iface->iface_name, ifname); + /* Counters start at 0, so we can track 4GB of network traffic. */ + new_iface->tx_bytes = 0; + new_iface->rx_bytes = 0; + new_iface->rx_packets = 0; + new_iface->tx_packets = 0; + new_iface->active = true; + + /* Append the newly created iface stat struct to the list. */ + list_add_tail(&new_iface->if_link, &iface_list); + proc_entry = proc_mkdir(ifname, iface_stat_procdir); + + /* Keep reference to iface_stat so we know where to read stats from. */ + create_proc_read_entry("tx_bytes", S_IRUGO, proc_entry, + read_proc_entry, &new_iface->tx_bytes); + + create_proc_read_entry("rx_bytes", S_IRUGO, proc_entry, + read_proc_entry, &new_iface->rx_bytes); + + create_proc_read_entry("tx_packets", S_IRUGO, proc_entry, + read_proc_entry, &new_iface->tx_packets); + + create_proc_read_entry("rx_packets", S_IRUGO, proc_entry, + read_proc_entry, &new_iface->rx_packets); + + create_proc_read_entry("active", S_IRUGO, proc_entry, + read_proc_bool_entry, &new_iface->active); + + pr_debug("iface_stat: Now monitoring device %s with ip %pI4\n", + ifname, &ipaddr); +} + +/* + * Update stats for the specified interface. Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called when an device is being unregistered. + */ +void iface_stat_update(struct net_device *dev) +{ + const struct net_device_stats *stats = dev_get_stats(dev); + struct iface_stat *entry; + + ASSERT_RTNL(); + + entry = get_iface_stat(dev->name); + if (entry == NULL) { + pr_debug("iface_stat: dev %s monitor not found\n", dev->name); + return; + } + + if (entry->active) { /* FIXME: Support for more than 4GB */ + entry->tx_bytes += stats->tx_bytes; + entry->tx_packets += stats->tx_packets; + entry->rx_bytes += stats->rx_bytes; + entry->rx_packets += stats->rx_packets; + entry->active = false; + pr_debug("iface_stat: Updating stats for " + "dev %s which went down\n", dev->name); + } else + pr_debug("iface_stat: Didn't update stats for " + "dev %s which went down\n", dev->name); +} + +static int __init iface_stat_init(void) +{ + iface_stat_procdir = proc_mkdir("iface_stat", NULL); + if (!iface_stat_procdir) { + pr_err("iface_stat: failed to create proc entry\n"); + return -1; + } + + return 0; +} + +device_initcall(iface_stat_init); diff --git a/include/linux/iface_stat.h b/include/linux/iface_stat.h new file mode 100644 index 00000000..e1e17303 --- /dev/null +++ b/include/linux/iface_stat.h @@ -0,0 +1,50 @@ +/* include/linux/iface_stat.h + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __IFACE_STAT_H +#define __IFACE_STAT_H + +/* + * Contains definitions for persistent data usage tracking per + * network interface. + */ + +#ifdef CONFIG_IFACE_STAT +/* + * Create a new entry for tracking the specified interface. + * Do nothing if the entry already exists. + * Called when an interface is configured with a valid IP address. + */ +void create_iface_stat(const struct in_device *in_dev); + +/* + * Update stats for the specified interface. + * Do nothing if the entry does not exist (when a device was never + * configured with an IP address). Called when an device is being + * unregistered. + */ +void iface_stat_update(struct net_device *dev); + +#else + +static inline void create_iface_stat(in_dev) +{ } + +static inline void iface_stat_update(dev) +{ } + +#endif + +#endif /* __IFACE_STAT_H */ diff --git a/net/core/dev.c b/net/core/dev.c index 74d0ccef..f36676f0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -127,6 +127,7 @@ #include #include #include +#include #include "net-sysfs.h" @@ -4662,6 +4663,9 @@ static void rollback_registered(struct net_device *dev) synchronize_net(); + /* Store stats for this device in persistent iface_stat */ + iface_stat_update(dev); + /* Shutdown queueing discipline. */ dev_shutdown(dev); diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index efff6a28..1560ad9b 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -54,6 +54,7 @@ #include #endif #include +#include #include #include @@ -373,6 +374,9 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid); blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); + /* Start persistent interface stat monitoring. Ignores if loopback. */ + create_iface_stat(in_dev); + return 0; }