11f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan/* net/activity_stats.c
21f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan *
31f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * Copyright (C) 2010 Google, Inc.
41f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan *
51f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * This software is licensed under the terms of the GNU General Public
61f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * License version 2, as published by the Free Software Foundation, and
71f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * may be copied, distributed, and modified under those terms.
81f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan *
91f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * This program is distributed in the hope that it will be useful,
101f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * GNU General Public License for more details.
131f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan *
141f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * Author: Mike Chan (mike@android.com)
151f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan */
161f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
171f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan#include <linux/proc_fs.h>
184af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg#include <linux/seq_file.h>
191f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan#include <linux/suspend.h>
201f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan#include <net/net_namespace.h>
211f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
221f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan/*
231f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * Track transmission rates in buckets (power of 2).
241f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * 1,2,4,8...512 seconds.
251f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan *
261f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * Buckets represent the count of network transmissions at least
271f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan * N seconds apart, where N is 1 << bucket index.
281f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan */
291f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan#define BUCKET_MAX 10
301f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
311f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan/* Track network activity frequency */
321f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chanstatic unsigned long activity_stats[BUCKET_MAX];
331f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chanstatic ktime_t last_transmit;
341f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chanstatic ktime_t suspend_time;
351f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chanstatic DEFINE_SPINLOCK(activity_lock);
361f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
371f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chanvoid activity_stats_update(void)
381f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan{
391f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	int i;
401f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	unsigned long flags;
411f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	ktime_t now;
421f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	s64 delta;
431f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
441f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	spin_lock_irqsave(&activity_lock, flags);
451f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	now = ktime_get();
461f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	delta = ktime_to_ns(ktime_sub(now, last_transmit));
471f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
481f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	for (i = BUCKET_MAX - 1; i >= 0; i--) {
491f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		/*
501f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		 * Check if the time delta between network activity is within the
511f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		 * minimum bucket range.
521f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		 */
531f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		if (delta < (1000000000ULL << i))
541f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan			continue;
551f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
561f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		activity_stats[i]++;
571f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		last_transmit = now;
581f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		break;
591f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	}
601f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	spin_unlock_irqrestore(&activity_lock, flags);
611f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan}
621f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
634af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevågstatic int activity_stats_show(struct seq_file *m, void *v)
641f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan{
651f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	int i;
664af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg	int ret;
671f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
684af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg	seq_printf(m, "Min Bucket(sec) Count\n");
691f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
701f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	for (i = 0; i < BUCKET_MAX; i++) {
714af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg		ret = seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]);
724af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg		if (ret)
734af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg			return ret;
741f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	}
751f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
764af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg	return 0;
771f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan}
781f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
791f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chanstatic int activity_stats_notifier(struct notifier_block *nb,
801f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan					unsigned long event, void *dummy)
811f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan{
821f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	switch (event) {
831f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		case PM_SUSPEND_PREPARE:
841f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan			suspend_time = ktime_get_real();
851f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan			break;
861f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
871f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan		case PM_POST_SUSPEND:
881f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan			suspend_time = ktime_sub(ktime_get_real(), suspend_time);
891f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan			last_transmit = ktime_sub(last_transmit, suspend_time);
901f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	}
911f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
921f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	return 0;
931f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan}
941f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
954af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevågstatic int activity_stats_open(struct inode *inode, struct file *file)
964af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg{
974af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg	return single_open(file, activity_stats_show, PDE_DATA(inode));
984af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg}
994af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg
1004af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevågstatic const struct file_operations activity_stats_fops = {
1014af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg	.open		= activity_stats_open,
1024af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg	.read		= seq_read,
1034af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg	.llseek		= seq_lseek,
1044af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg	.release	= seq_release,
1054af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg};
1064af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg
1071f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chanstatic struct notifier_block activity_stats_notifier_block = {
1081f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	.notifier_call = activity_stats_notifier,
1091f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan};
1101f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
1111f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chanstatic int  __init activity_stats_init(void)
1121f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan{
1134af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg	proc_create("activity", S_IRUGO,
1144af1c50c2b8d5cb96ee803cc8c8d969708130509Arve Hjønnevåg		    init_net.proc_net_stat, &activity_stats_fops);
1151f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan	return register_pm_notifier(&activity_stats_notifier_block);
1161f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan}
1171f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
1181f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chansubsys_initcall(activity_stats_init);
1191f65785d2b92ccad4ebab8f0b39c9e232d76946fMike Chan
120