net: activity_stats: Add statistics for network transmission activity
Mike Chan [Fri, 28 May 2010 21:32:19 +0000 (14:32 -0700)]
When enabled, tracks the frequency of network transmissions
(inbound and outbound) and buckets them accordingly.
Buckets are determined by time between network activity.

Each bucket represents the number of network transmisions that were
N sec or longer apart. Where N is defined as 1 << bucket index.

This network pattern tracking is particularly useful for wireless
networks (ie: 3G) where batching network activity closely together
is more power efficient than far apart.

New file: /proc/net/stat/activity

output:

Min Bucket(sec) Count
              1 7
              2 0
              4 1
              8 0
             16 0
             32 2
             64 1
            128 0

Change-Id: I4c4cd8627b872a55f326b1715c51bc3bdd6e8d92
Signed-off-by: Mike Chan <mike@android.com>

drivers/misc/uid_stat.c
include/net/activity_stats.h [new file with mode: 0644]
net/Kconfig
net/Makefile
net/activity_stats.c [new file with mode: 0644]

index e6760b5..2141124 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/spinlock.h>
 #include <linux/stat.h>
 #include <linux/uid_stat.h>
+#include <net/activity_stats.h>
 
 static DEFINE_SPINLOCK(uid_lock);
 static LIST_HEAD(uid_list);
@@ -122,6 +123,7 @@ static struct uid_stat *create_stat(uid_t uid) {
 
 int uid_stat_tcp_snd(uid_t uid, int size) {
        struct uid_stat *entry;
+       activity_stats_update();
        if ((entry = find_uid_stat(uid)) == NULL &&
                ((entry = create_stat(uid)) == NULL)) {
                        return -1;
@@ -132,6 +134,7 @@ int uid_stat_tcp_snd(uid_t uid, int size) {
 
 int uid_stat_tcp_rcv(uid_t uid, int size) {
        struct uid_stat *entry;
+       activity_stats_update();
        if ((entry = find_uid_stat(uid)) == NULL &&
                ((entry = create_stat(uid)) == NULL)) {
                        return -1;
diff --git a/include/net/activity_stats.h b/include/net/activity_stats.h
new file mode 100644 (file)
index 0000000..10e4c15
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 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.
+ *
+ * Author: Mike Chan (mike@android.com)
+ */
+
+#ifndef __activity_stats_h
+#define __activity_stats_h
+
+#ifdef CONFIG_NET_ACTIVITY_STATS
+void activity_stats_update(void);
+#else
+#define activity_stats_update(void) {}
+#endif
+
+#endif /* _NET_ACTIVITY_STATS_H */
index 6ab695a..e569181 100644 (file)
@@ -85,6 +85,14 @@ config ANDROID_PARANOID_NETWORK
        help
                none
 
+config NET_ACTIVITY_STATS
+       bool "Network activity statistics tracking"
+       default y
+       help
+        Network activity statistics are useful for tracking wireless
+        modem activity on 2G, 3G, 4G wireless networks. Counts number of
+        transmissions and groups them in specified time buckets.
+
 config NETWORK_SECMARK
        bool "Security Marking"
        help
index ad432fa..6865dab 100644 (file)
@@ -70,3 +70,4 @@ obj-$(CONFIG_CEPH_LIB)                += ceph/
 obj-$(CONFIG_BATMAN_ADV)       += batman-adv/
 obj-$(CONFIG_NFC)              += nfc/
 obj-$(CONFIG_OPENVSWITCH)      += openvswitch/
+obj-$(CONFIG_NET_ACTIVITY_STATS)               += activity_stats.o
diff --git a/net/activity_stats.c b/net/activity_stats.c
new file mode 100644 (file)
index 0000000..8a3e934
--- /dev/null
@@ -0,0 +1,115 @@
+/* net/activity_stats.c
+ *
+ * Copyright (C) 2010 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.
+ *
+ * Author: Mike Chan (mike@android.com)
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/suspend.h>
+#include <net/net_namespace.h>
+
+/*
+ * Track transmission rates in buckets (power of 2).
+ * 1,2,4,8...512 seconds.
+ *
+ * Buckets represent the count of network transmissions at least
+ * N seconds apart, where N is 1 << bucket index.
+ */
+#define BUCKET_MAX 10
+
+/* Track network activity frequency */
+static unsigned long activity_stats[BUCKET_MAX];
+static ktime_t last_transmit;
+static ktime_t suspend_time;
+static DEFINE_SPINLOCK(activity_lock);
+
+void activity_stats_update(void)
+{
+       int i;
+       unsigned long flags;
+       ktime_t now;
+       s64 delta;
+
+       spin_lock_irqsave(&activity_lock, flags);
+       now = ktime_get();
+       delta = ktime_to_ns(ktime_sub(now, last_transmit));
+
+       for (i = BUCKET_MAX - 1; i >= 0; i--) {
+               /*
+                * Check if the time delta between network activity is within the
+                * minimum bucket range.
+                */
+               if (delta < (1000000000ULL << i))
+                       continue;
+
+               activity_stats[i]++;
+               last_transmit = now;
+               break;
+       }
+       spin_unlock_irqrestore(&activity_lock, flags);
+}
+
+static int activity_stats_read_proc(char *page, char **start, off_t off,
+                                       int count, int *eof, void *data)
+{
+       int i;
+       int len;
+       char *p = page;
+
+       /* Only print if offset is 0, or we have enough buffer space */
+       if (off || count < (30 * BUCKET_MAX + 22))
+               return -ENOMEM;
+
+       len = snprintf(p, count, "Min Bucket(sec) Count\n");
+       count -= len;
+       p += len;
+
+       for (i = 0; i < BUCKET_MAX; i++) {
+               len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]);
+               count -= len;
+               p += len;
+       }
+       *eof = 1;
+
+       return p - page;
+}
+
+static int activity_stats_notifier(struct notifier_block *nb,
+                                       unsigned long event, void *dummy)
+{
+       switch (event) {
+               case PM_SUSPEND_PREPARE:
+                       suspend_time = ktime_get_real();
+                       break;
+
+               case PM_POST_SUSPEND:
+                       suspend_time = ktime_sub(ktime_get_real(), suspend_time);
+                       last_transmit = ktime_sub(last_transmit, suspend_time);
+       }
+
+       return 0;
+}
+
+static struct notifier_block activity_stats_notifier_block = {
+       .notifier_call = activity_stats_notifier,
+};
+
+static int  __init activity_stats_init(void)
+{
+       create_proc_read_entry("activity", S_IRUGO,
+                       init_net.proc_net_stat, activity_stats_read_proc, NULL);
+       return register_pm_notifier(&activity_stats_notifier_block);
+}
+
+subsys_initcall(activity_stats_init);
+