netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2.6] neighbour cache statistics like rt_stat
@ 2004-09-27  9:36 Harald Welte
  2004-09-27 16:23 ` Harald Welte
  0 siblings, 1 reply; 5+ messages in thread
From: Harald Welte @ 2004-09-27  9:36 UTC (permalink / raw)
  To: netdev


[-- Attachment #1.1: Type: text/plain, Size: 1462 bytes --]

The patch below (on top of davem's recent 6-patch set and 4-patch-set up
to diff4) adds rt_stat like statistics to the neighbour cache core.

It helped a lot during debugging/testing, and I think it is generally
useful for the mainline kernel if you want to do runtime analysis of
neighbour cahce performance / problems.

Some comments/questions/ideas

1) old statistics used to be unsigned long, new ones are int.  I propose
   to convert all of them to int.
2) I'd also like to add gc statistics on non-forced-gc but normal
   garbage collection.
3) Do we need a seperate 'miss' counter, or is the 'allocs' counter
   sufficient?
4) Don't we want to put them in a /proc/net/neigh directory ?
5) Since there's now rt_stat, ct_stat, neighbour statistics, and they
   all use the same clumsy format that is difficult to expand, and all
   use copy+paste userspace tools: Would you accept a patch that changes
   the format once again to display a template as first line, giving the
   names of the individual fields?  I would then work on a generic
   userspace tool (too bad that the name netstat is already used) that
   would work with all of those _stat files, including upcoming new
   ones.

-- 
- Harald Welte <laforge@gnumonks.org>               http://www.gnumonks.org/
============================================================================
Programming is like sex: One mistake and you have to support it your lifetime

[-- Attachment #1.2: laforge-neigh_statistics.patch --]
[-- Type: text/plain, Size: 8493 bytes --]


Add rtstat-like per-cpu statistics to the neighbour cache core.

This will add 
	/proc/net/arp_cache_stat for ipv4
	/proc/net/ndisc_cache_stat for ipv6
	/proc/net/clip_arp_cache_stat for atm/clip 
	/proc/net/decnet_neigh_stat for decnet

The format is similar to rt_stat, one line per cpu where each line is:
	Field 1:	Total number of entries in table
	Field 2:	Total number of hash bucket grows
	Field 3:	Total number of cache hits
	Field 4:	Total number of neigh_allocs (should equal misses)
	Field 5:	Total number of failed neighbour resolves
	Field 6:	?
	Field 7:	?
	Field 8:	Total number of forced garbage collector runs
	Field 9:	Total number of forced GC runs that missed their goal

Signed-off-by: Harald Welte <laforge@gnumonks.org>


Index: linux-2.6.9-rc2-bk9-neigh1/include/net/neighbour.h
===================================================================
--- linux-2.6.9-rc2-bk9-neigh1.orig/include/net/neighbour.h	2004-09-26 12:45:38.000000000 +0200
+++ linux-2.6.9-rc2-bk9-neigh1/include/net/neighbour.h	2004-09-26 23:31:13.000000000 +0200
@@ -7,6 +7,11 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
+ *
+ * 	Changes:
+ *
+ *	Harald Welte:		<laforge@gnumonks.org>
+ *		- Add neighbour cache statistics like rtstat
  */
 
 /* The following flags & states are exported to user space,
@@ -92,10 +97,21 @@
 {
 	unsigned long allocs;
 	unsigned long res_failed;
+
+	unsigned int hits;		/* number of cache hits */
+
 	unsigned long rcv_probes_mcast;
 	unsigned long rcv_probes_ucast;
+
+	unsigned int hash_grows;	/* total number of hash resizes */
+
+	unsigned int forced_gc_runs;	/* total number of GC runs */
+	unsigned int forced_gc_goal_miss;/* total number of gc goal misses */
 };
 
+#define NEIGH_CACHE_STAT_INC(tbl, field)				\
+		(per_cpu_ptr((tbl)->stats, smp_processor_id())->field++)
+
 struct neighbour
 {
 	struct neighbour	*next;
@@ -172,12 +188,15 @@
 	unsigned long		last_rand;
 	struct neigh_parms	*parms_list;
 	kmem_cache_t		*kmem_cachep;
-	struct neigh_statistics	stats;
+	struct neigh_statistics	*stats;
 	struct neighbour	**hash_buckets;
 	unsigned int		hash_mask;
 	__u32			hash_rnd;
 	unsigned int		hash_chain_gc;
 	struct pneigh_entry	**phash_buckets;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry	*pde;
+#endif
 };
 
 /* flags for neigh_update() */
Index: linux-2.6.9-rc2-bk9-neigh1/net/core/neighbour.c
===================================================================
--- linux-2.6.9-rc2-bk9-neigh1.orig/net/core/neighbour.c	2004-09-26 12:37:18.000000000 +0200
+++ linux-2.6.9-rc2-bk9-neigh1/net/core/neighbour.c	2004-09-27 10:54:35.934120048 +0200
@@ -21,6 +21,7 @@
 #include <linux/socket.h>
 #include <linux/sched.h>
 #include <linux/netdevice.h>
+#include <linux/proc_fs.h>
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
 #endif
@@ -59,6 +60,7 @@
 
 static int neigh_glbl_allocs;
 static struct neigh_table *neigh_tables;
+static struct file_operations neigh_stat_seq_fops;
 
 /*
    Neighbour hash table buckets are protected with rwlock tbl->lock.
@@ -115,6 +117,8 @@
 	int shrunk = 0, num_incomplete = 0, reap_incomplete = 0;
 	int i;
 
+	NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
+
 	write_lock_bh(&tbl->lock);
 rescan:
 	for (i = 0; i <= tbl->hash_mask; i++) {
@@ -161,6 +165,7 @@
 	if (reap_incomplete == 0 &&
 	    shrunk < goal &&
 	    (shrunk + num_incomplete) >= goal) {
+		NEIGH_CACHE_STAT_INC(tbl, forced_gc_goal_miss);
 		reap_incomplete = 1;
 		goto rescan;
 	}
@@ -299,7 +304,8 @@
 	init_timer(&n->timer);
 	n->timer.function = neigh_timer_handler;
 	n->timer.data	  = (unsigned long)n;
-	tbl->stats.allocs++;
+
+	NEIGH_CACHE_STAT_INC(tbl, allocs);
 	neigh_glbl_allocs++;
 	tbl->entries++;
 	n->tbl		  = tbl;
@@ -341,6 +347,8 @@
 	struct neighbour **new_hash, **old_hash;
 	unsigned int i, new_hash_mask, old_entries;
 
+	NEIGH_CACHE_STAT_INC(tbl, hash_grows);
+
 	BUG_ON(new_entries & (new_entries - 1));
 	new_hash = neigh_hash_alloc(new_entries);
 	if (!new_hash)
@@ -380,6 +388,7 @@
 	for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
 		if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
 			neigh_hold(n);
+			NEIGH_CACHE_STAT_INC(tbl, hits);
 			break;
 		}
 	}
@@ -397,6 +406,7 @@
 	for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
 		if (!memcmp(n->primary_key, pkey, key_len)) {
 			neigh_hold(n);
+			NEIGH_CACHE_STAT_INC(tbl, hits);
 			break;
 		}
 	}
@@ -787,7 +797,7 @@
 
 		neigh->nud_state = NUD_FAILED;
 		notify = 1;
-		neigh->tbl->stats.res_failed++;
+		NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
 		NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
 
 		/* It is very thin place. report_unreachable is very complicated
@@ -1336,6 +1346,30 @@
 	if (!tbl->kmem_cachep)
 		panic("cannot create neighbour cache");
 
+	tbl->stats = alloc_percpu(struct neigh_statistics);
+	if (!tbl->stats)
+		panic("cannot create neighbour cache statistics");
+	
+#ifdef CONFIG_PROC_FS
+#define NC_STAT_SUFFIX "_stat"
+	{
+	char *proc_stat_name;
+	proc_stat_name = kmalloc(strlen(tbl->id) + 
+				 strlen(NC_STAT_SUFFIX) + 1, GFP_KERNEL);
+	if (!proc_stat_name)
+		panic("cannot allocate neighbour cache proc name buffer");
+	strcpy(proc_stat_name, tbl->id);
+	strcat(proc_stat_name, NC_STAT_SUFFIX);
+
+	/* FIXME: move this to seperate directory and add _stat postfix */
+	tbl->pde = create_proc_entry(proc_stat_name, 0, proc_net);
+	if (!tbl->pde) 
+		panic("cannot create neighbour proc dir entry");
+	tbl->pde->proc_fops = &neigh_stat_seq_fops;
+	tbl->pde->data = tbl;
+	}
+#endif
+
 	tbl->hash_mask = 0x1f;
 	tbl->hash_buckets = neigh_hash_alloc(tbl->hash_mask + 1);
 
@@ -1882,6 +1916,94 @@
 }
 EXPORT_SYMBOL(neigh_seq_stop);
 
+/* statistics via seq_file */
+
+static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct proc_dir_entry *pde = seq->private;
+	struct neigh_table *tbl = pde->data;
+	int cpu;
+	
+	for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu;
+		return per_cpu_ptr(tbl->stats, cpu);
+	}
+	return NULL;
+}
+
+static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct proc_dir_entry *pde = seq->private;
+	struct neigh_table *tbl = pde->data;
+	int cpu;
+
+	for (cpu = *pos + 1; cpu < NR_CPUS; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu;
+		return per_cpu_ptr(tbl->stats, cpu);
+	}
+	return NULL;
+}
+
+static void neigh_stat_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int neigh_stat_seq_show(struct seq_file *seq, void *v)
+{
+	struct proc_dir_entry *pde = seq->private;
+	struct neigh_table *tbl = pde->data;
+	struct neigh_statistics *st = v;
+
+	seq_printf(seq, "%08x  %08x %08x %08lx %08lx  %08lx %08lx "
+			"%08x %08x\n",
+		   tbl->entries,
+
+		   st->hash_grows,
+		   st->hits,
+		   st->allocs,
+		   st->res_failed,
+
+		   st->rcv_probes_mcast,
+		   st->rcv_probes_ucast,
+
+		   st->forced_gc_runs,
+		   st->forced_gc_goal_miss
+		   );
+
+	return 0;
+}
+
+static struct seq_operations neigh_stat_seq_ops = {
+	.start	= neigh_stat_seq_start,
+	.next	= neigh_stat_seq_next,
+	.stop	= neigh_stat_seq_stop,
+	.show	= neigh_stat_seq_show,
+};
+
+static int neigh_stat_seq_open(struct inode *inode, struct file *file)
+{
+	int ret = seq_open(file, &neigh_stat_seq_ops);
+
+	if (!ret) {
+		struct seq_file *sf = file->private_data;
+		sf->private = PDE(inode);
+	}
+	return ret;
+};
+
+static struct file_operations neigh_stat_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open 	 = neigh_stat_seq_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release,
+};
+
 #endif /* CONFIG_PROC_FS */
 
 #ifdef CONFIG_ARPD
Index: linux-2.6.9-rc2-bk9-neigh1/net/ipv6/ndisc.c
===================================================================
--- linux-2.6.9-rc2-bk9-neigh1.orig/net/ipv6/ndisc.c	2004-09-26 12:29:03.000000000 +0200
+++ linux-2.6.9-rc2-bk9-neigh1/net/ipv6/ndisc.c	2004-09-26 23:30:56.000000000 +0200
@@ -802,9 +802,9 @@
 	}
 
 	if (inc)
-		nd_tbl.stats.rcv_probes_mcast++;
+		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
 	else
-		nd_tbl.stats.rcv_probes_ucast++;
+		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
 
 	/* 
 	 *	update / create cache entry

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2.6] neighbour cache statistics like rt_stat
  2004-09-27  9:36 [PATCH 2.6] neighbour cache statistics like rt_stat Harald Welte
@ 2004-09-27 16:23 ` Harald Welte
  2004-09-27 19:21   ` David S. Miller
  0 siblings, 1 reply; 5+ messages in thread
From: Harald Welte @ 2004-09-27 16:23 UTC (permalink / raw)
  To: netdev


[-- Attachment #1.1: Type: text/plain, Size: 589 bytes --]

On Mon, Sep 27, 2004 at 11:36:13AM +0200, Harald Welte wrote:
> The patch below (on top of davem's recent 6-patch set and 4-patch-set up
> to diff4) adds rt_stat like statistics to the neighbour cache core.

I've now updated the patch.

Changes:
	- more statistics (lookups, destroy, periodic_gc_runs)
	- add template line to top of file

-- 
- Harald Welte <laforge@gnumonks.org>               http://www.gnumonks.org/
============================================================================
Programming is like sex: One mistake and you have to support it your lifetime

[-- Attachment #1.2: laforge-neigh_statistics.patch --]
[-- Type: text/plain, Size: 10444 bytes --]


Add rtstat-like per-cpu statistics to the neighbour cache core.

This will add 
	/proc/net/arp_cache_stat for ipv4
	/proc/net/ndisc_cache_stat for ipv6
	/proc/net/clip_arp_cache_stat for atm/clip 
	/proc/net/decnet_neigh_stat for decnet

The format is similar to rt_stat, one line per cpu where each line is:
	Field 0:	Total number of entries in table
	Field 1:	Total number of neighbour allocations in this tbl
	Field 2:	Total number of neighbour destroys in this tbl
	Field 3:	Total number of hash bucket grows
	Field 4:	Total number of failed neighbour resolves
	Field 5:	Total number of cache lookups
	Field 6:	Total number of cache hits
	Field 7:	Total number of received multicast solicitations
	Field 8:	Total number of received unicast solicitations
	Field 9:	Total number of periodic garbage collector runs
	Field 10:	Total number of forced garbage collector runs
	Field 11:	Total number of forced GC runs that missed their goal

Signed-off-by: Harald Welte <laforge@gnumonks.org>


Index: linux-2.6.9-rc2-bk9-neigh1/include/net/neighbour.h
===================================================================
--- linux-2.6.9-rc2-bk9-neigh1.orig/include/net/neighbour.h	2004-09-26 12:45:38.000000000 +0200
+++ linux-2.6.9-rc2-bk9-neigh1/include/net/neighbour.h	2004-09-27 13:44:49.000000000 +0200
@@ -7,6 +7,11 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
+ *
+ * 	Changes:
+ *
+ *	Harald Welte:		<laforge@gnumonks.org>
+ *		- Add neighbour cache statistics like rtstat
  */
 
 /* The following flags & states are exported to user space,
@@ -90,12 +95,26 @@
 
 struct neigh_statistics
 {
-	unsigned long allocs;
-	unsigned long res_failed;
-	unsigned long rcv_probes_mcast;
-	unsigned long rcv_probes_ucast;
+	unsigned int allocs;		/* number of allocated neighs */
+	unsigned int destroys;		/* number of destroyed neighs */
+	unsigned int hash_grows;	/* number of hash resizes */
+
+	unsigned int res_failed;	/* nomber of failed resolutions */
+
+	unsigned int lookups;		/* number of lookups */
+	unsigned int hits;		/* number of hits (among lookups) */
+
+	unsigned int rcv_probes_mcast;	/* number of received mcast ipv6 */
+	unsigned int rcv_probes_ucast; /* number of received ucast ipv6 */
+
+	unsigned int periodic_gc_runs;	/* number of periodic GC runs */
+	unsigned int forced_gc_runs;	/* number of forced GC runs */
+	unsigned int forced_gc_goal_miss;/* number of gc goal misses */
 };
 
+#define NEIGH_CACHE_STAT_INC(tbl, field)				\
+		(per_cpu_ptr((tbl)->stats, smp_processor_id())->field++)
+
 struct neighbour
 {
 	struct neighbour	*next;
@@ -172,12 +191,15 @@
 	unsigned long		last_rand;
 	struct neigh_parms	*parms_list;
 	kmem_cache_t		*kmem_cachep;
-	struct neigh_statistics	stats;
+	struct neigh_statistics	*stats;
 	struct neighbour	**hash_buckets;
 	unsigned int		hash_mask;
 	__u32			hash_rnd;
 	unsigned int		hash_chain_gc;
 	struct pneigh_entry	**phash_buckets;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry	*pde;
+#endif
 };
 
 /* flags for neigh_update() */
Index: linux-2.6.9-rc2-bk9-neigh1/net/core/neighbour.c
===================================================================
--- linux-2.6.9-rc2-bk9-neigh1.orig/net/core/neighbour.c	2004-09-26 12:37:18.000000000 +0200
+++ linux-2.6.9-rc2-bk9-neigh1/net/core/neighbour.c	2004-09-27 17:24:41.990753504 +0200
@@ -12,6 +12,7 @@
  *
  *	Fixes:
  *	Vitaly E. Lavrov	releasing NULL neighbor in neigh_add.
+ *	Harald Welte		Add neighbour cache statistics like rtstat
  */
 
 #include <linux/config.h>
@@ -21,6 +22,7 @@
 #include <linux/socket.h>
 #include <linux/sched.h>
 #include <linux/netdevice.h>
+#include <linux/proc_fs.h>
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
 #endif
@@ -59,6 +61,7 @@
 
 static int neigh_glbl_allocs;
 static struct neigh_table *neigh_tables;
+static struct file_operations neigh_stat_seq_fops;
 
 /*
    Neighbour hash table buckets are protected with rwlock tbl->lock.
@@ -115,6 +118,8 @@
 	int shrunk = 0, num_incomplete = 0, reap_incomplete = 0;
 	int i;
 
+	NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
+
 	write_lock_bh(&tbl->lock);
 rescan:
 	for (i = 0; i <= tbl->hash_mask; i++) {
@@ -161,6 +166,7 @@
 	if (reap_incomplete == 0 &&
 	    shrunk < goal &&
 	    (shrunk + num_incomplete) >= goal) {
+		NEIGH_CACHE_STAT_INC(tbl, forced_gc_goal_miss);
 		reap_incomplete = 1;
 		goto rescan;
 	}
@@ -299,7 +305,8 @@
 	init_timer(&n->timer);
 	n->timer.function = neigh_timer_handler;
 	n->timer.data	  = (unsigned long)n;
-	tbl->stats.allocs++;
+
+	NEIGH_CACHE_STAT_INC(tbl, allocs);
 	neigh_glbl_allocs++;
 	tbl->entries++;
 	n->tbl		  = tbl;
@@ -341,6 +348,8 @@
 	struct neighbour **new_hash, **old_hash;
 	unsigned int i, new_hash_mask, old_entries;
 
+	NEIGH_CACHE_STAT_INC(tbl, hash_grows);
+
 	BUG_ON(new_entries & (new_entries - 1));
 	new_hash = neigh_hash_alloc(new_entries);
 	if (!new_hash)
@@ -375,11 +384,14 @@
 	struct neighbour *n;
 	int key_len = tbl->key_len;
 	u32 hash_val = tbl->hash(pkey, dev) & tbl->hash_mask;
+	
+	NEIGH_CACHE_STAT_INC(tbl, lookups);
 
 	read_lock_bh(&tbl->lock);
 	for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
 		if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
 			neigh_hold(n);
+			NEIGH_CACHE_STAT_INC(tbl, hits);
 			break;
 		}
 	}
@@ -393,10 +405,13 @@
 	int key_len = tbl->key_len;
 	u32 hash_val = tbl->hash(pkey, NULL) & tbl->hash_mask;
 
+	NEIGH_CACHE_STAT_INC(tbl, lookups);
+
 	read_lock_bh(&tbl->lock);
 	for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
 		if (!memcmp(n->primary_key, pkey, key_len)) {
 			neigh_hold(n);
+			NEIGH_CACHE_STAT_INC(tbl, hits);
 			break;
 		}
 	}
@@ -581,6 +596,8 @@
 {
 	struct hh_cache *hh;
 
+	NEIGH_CACHE_STAT_INC(neigh->tbl, destroys);
+
 	if (!neigh->dead) {
 		printk(KERN_WARNING
 		       "Destroying alive neighbour %p\n", neigh);
@@ -656,6 +673,8 @@
 	struct neighbour *n, **np;
 	unsigned long expire, now = jiffies;
 
+	NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs);
+
 	write_lock(&tbl->lock);
 
 	/*
@@ -787,7 +806,7 @@
 
 		neigh->nud_state = NUD_FAILED;
 		notify = 1;
-		neigh->tbl->stats.res_failed++;
+		NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
 		NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
 
 		/* It is very thin place. report_unreachable is very complicated
@@ -1336,6 +1355,29 @@
 	if (!tbl->kmem_cachep)
 		panic("cannot create neighbour cache");
 
+	tbl->stats = alloc_percpu(struct neigh_statistics);
+	if (!tbl->stats)
+		panic("cannot create neighbour cache statistics");
+	
+#ifdef CONFIG_PROC_FS
+#define NC_STAT_SUFFIX "_stat"
+	{
+	char *proc_stat_name;
+	proc_stat_name = kmalloc(strlen(tbl->id) + 
+				 strlen(NC_STAT_SUFFIX) + 1, GFP_KERNEL);
+	if (!proc_stat_name)
+		panic("cannot allocate neighbour cache proc name buffer");
+	strcpy(proc_stat_name, tbl->id);
+	strcat(proc_stat_name, NC_STAT_SUFFIX);
+
+	tbl->pde = create_proc_entry(proc_stat_name, 0, proc_net);
+	if (!tbl->pde) 
+		panic("cannot create neighbour proc dir entry");
+	tbl->pde->proc_fops = &neigh_stat_seq_fops;
+	tbl->pde->data = tbl;
+	}
+#endif
+
 	tbl->hash_mask = 0x1f;
 	tbl->hash_buckets = neigh_hash_alloc(tbl->hash_mask + 1);
 
@@ -1882,6 +1924,107 @@
 }
 EXPORT_SYMBOL(neigh_seq_stop);
 
+/* statistics via seq_file */
+
+static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct proc_dir_entry *pde = seq->private;
+	struct neigh_table *tbl = pde->data;
+	int cpu;
+
+	if (*pos == 0)
+		return SEQ_START_TOKEN;
+	
+	for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu+1;
+		return per_cpu_ptr(tbl->stats, cpu);
+	}
+	return NULL;
+}
+
+static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct proc_dir_entry *pde = seq->private;
+	struct neigh_table *tbl = pde->data;
+	int cpu;
+
+	for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu+1;
+		return per_cpu_ptr(tbl->stats, cpu);
+	}
+	return NULL;
+}
+
+static void neigh_stat_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int neigh_stat_seq_show(struct seq_file *seq, void *v)
+{
+	struct proc_dir_entry *pde = seq->private;
+	struct neigh_table *tbl = pde->data;
+	struct neigh_statistics *st = v;
+
+	if (v == SEQ_START_TOKEN) {
+		seq_printf(seq, "entries  allocs destroys hash_grows  lookups hits  res_failed  rcv_probes_mcast rcv_probes_ucast  periodic_gc_runs forced_gc_runs forced_gc_goal_miss\n");
+		return 0;
+	}
+
+	seq_printf(seq, "%08x  %08x %08x %08x  %08x %08x  %08x  %08x %08x  "
+			"%08x %08x %08x\n",
+		   tbl->entries,
+
+		   st->allocs,
+		   st->destroys,
+		   st->hash_grows,
+
+		   st->lookups,
+		   st->hits,
+
+		   st->res_failed,
+
+		   st->rcv_probes_mcast,
+		   st->rcv_probes_ucast,
+
+		   st->periodic_gc_runs,
+		   st->forced_gc_runs,
+		   st->forced_gc_goal_miss
+		   );
+
+	return 0;
+}
+
+static struct seq_operations neigh_stat_seq_ops = {
+	.start	= neigh_stat_seq_start,
+	.next	= neigh_stat_seq_next,
+	.stop	= neigh_stat_seq_stop,
+	.show	= neigh_stat_seq_show,
+};
+
+static int neigh_stat_seq_open(struct inode *inode, struct file *file)
+{
+	int ret = seq_open(file, &neigh_stat_seq_ops);
+
+	if (!ret) {
+		struct seq_file *sf = file->private_data;
+		sf->private = PDE(inode);
+	}
+	return ret;
+};
+
+static struct file_operations neigh_stat_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open 	 = neigh_stat_seq_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release,
+};
+
 #endif /* CONFIG_PROC_FS */
 
 #ifdef CONFIG_ARPD
Index: linux-2.6.9-rc2-bk9-neigh1/net/ipv6/ndisc.c
===================================================================
--- linux-2.6.9-rc2-bk9-neigh1.orig/net/ipv6/ndisc.c	2004-09-26 12:29:03.000000000 +0200
+++ linux-2.6.9-rc2-bk9-neigh1/net/ipv6/ndisc.c	2004-09-26 23:30:56.000000000 +0200
@@ -802,9 +802,9 @@
 	}
 
 	if (inc)
-		nd_tbl.stats.rcv_probes_mcast++;
+		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
 	else
-		nd_tbl.stats.rcv_probes_ucast++;
+		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
 
 	/* 
 	 *	update / create cache entry

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2.6] neighbour cache statistics like rt_stat
  2004-09-27 16:23 ` Harald Welte
@ 2004-09-27 19:21   ` David S. Miller
  2004-09-27 22:20     ` Harald Welte
  0 siblings, 1 reply; 5+ messages in thread
From: David S. Miller @ 2004-09-27 19:21 UTC (permalink / raw)
  To: Harald Welte; +Cc: netdev

On Mon, 27 Sep 2004 18:23:34 +0200
Harald Welte <laforge@gnumonks.org> wrote:

> On Mon, Sep 27, 2004 at 11:36:13AM +0200, Harald Welte wrote:
> > The patch below (on top of davem's recent 6-patch set and 4-patch-set up
> > to diff4) adds rt_stat like statistics to the neighbour cache core.
> 
> I've now updated the patch.
> 
> Changes:
> 	- more statistics (lookups, destroy, periodic_gc_runs)
> 	- add template line to top of file

I'm generally very happy with these changes, but two things:

1) Please update the patch since I replaced "diff4" with
   a patch simply removing the INCOMPLETE checks from neigh_forced_gc()

2) I'd recommend using unsigned long for all the statistics.

Thanks.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2.6] neighbour cache statistics like rt_stat
  2004-09-27 19:21   ` David S. Miller
@ 2004-09-27 22:20     ` Harald Welte
  2004-09-28  5:17       ` David S. Miller
  0 siblings, 1 reply; 5+ messages in thread
From: Harald Welte @ 2004-09-27 22:20 UTC (permalink / raw)
  To: David S. Miller; +Cc: netdev


[-- Attachment #1.1: Type: text/plain, Size: 562 bytes --]

Hi Dave!

On Mon, Sep 27, 2004 at 12:21:36PM -0700, David S. Miller wrote:

> 1) Please update the patch since I replaced "diff4" with
>    a patch simply removing the INCOMPLETE checks from neigh_forced_gc()
> 
> 2) I'd recommend using unsigned long for all the statistics.

both updated in attached version.

-- 
- Harald Welte <laforge@gnumonks.org>               http://www.gnumonks.org/
============================================================================
Programming is like sex: One mistake and you have to support it your lifetime

[-- Attachment #1.2: laforge-neigh_statistics.patch --]
[-- Type: text/plain, Size: 10055 bytes --]


Add rtstat-like per-cpu statistics to the neighbour cache core.

This will add 
	/proc/net/arp_cache_stat for ipv4
	/proc/net/ndisc_cache_stat for ipv6
	/proc/net/clip_arp_cache_stat for atm/clip 
	/proc/net/decnet_neigh_stat for decnet

The format is similar to rt_stat, one line per cpu where each line is:
	Field 0:	Total number of entries in table
	Field 1:	Total number of neighbour allocations in this tbl
	Field 2:	Total number of neighbour destroys in this tbl
	Field 3:	Total number of hash bucket grows
	Field 4:	Total number of failed neighbour resolves
	Field 5:	Total number of cache lookups
	Field 6:	Total number of cache hits
	Field 7:	Total number of received multicast solicitations
	Field 8:	Total number of received unicast solicitations
	Field 9:	Total number of periodic garbage collector runs
	Field 10:	Total number of forced garbage collector runs

Signed-off-by: Harald Welte <laforge@gnumonks.org>


Index: linux-2.6.9-rc2-bk9-neigh1/include/net/neighbour.h
===================================================================
--- linux-2.6.9-rc2-bk9-neigh1.orig/include/net/neighbour.h	2004-09-26 12:45:38.000000000 +0200
+++ linux-2.6.9-rc2-bk9-neigh1/include/net/neighbour.h	2004-09-28 00:07:55.544774240 +0200
@@ -7,6 +7,11 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
+ *
+ * 	Changes:
+ *
+ *	Harald Welte:		<laforge@gnumonks.org>
+ *		- Add neighbour cache statistics like rtstat
  */
 
 /* The following flags & states are exported to user space,
@@ -90,12 +95,25 @@
 
 struct neigh_statistics
 {
-	unsigned long allocs;
-	unsigned long res_failed;
-	unsigned long rcv_probes_mcast;
-	unsigned long rcv_probes_ucast;
+	unsigned long allocs;		/* number of allocated neighs */
+	unsigned long destroys;		/* number of destroyed neighs */
+	unsigned long hash_grows;	/* number of hash resizes */
+
+	unsigned long res_failed;	/* nomber of failed resolutions */
+
+	unsigned long lookups;		/* number of lookups */
+	unsigned long hits;		/* number of hits (among lookups) */
+
+	unsigned long rcv_probes_mcast;	/* number of received mcast ipv6 */
+	unsigned long rcv_probes_ucast; /* number of received ucast ipv6 */
+
+	unsigned long periodic_gc_runs;	/* number of periodic GC runs */
+	unsigned long forced_gc_runs;	/* number of forced GC runs */
 };
 
+#define NEIGH_CACHE_STAT_INC(tbl, field)				\
+		(per_cpu_ptr((tbl)->stats, smp_processor_id())->field++)
+
 struct neighbour
 {
 	struct neighbour	*next;
@@ -172,12 +190,15 @@
 	unsigned long		last_rand;
 	struct neigh_parms	*parms_list;
 	kmem_cache_t		*kmem_cachep;
-	struct neigh_statistics	stats;
+	struct neigh_statistics	*stats;
 	struct neighbour	**hash_buckets;
 	unsigned int		hash_mask;
 	__u32			hash_rnd;
 	unsigned int		hash_chain_gc;
 	struct pneigh_entry	**phash_buckets;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry	*pde;
+#endif
 };
 
 /* flags for neigh_update() */
Index: linux-2.6.9-rc2-bk9-neigh1/net/core/neighbour.c
===================================================================
--- linux-2.6.9-rc2-bk9-neigh1.orig/net/core/neighbour.c	2004-09-28 00:05:19.555488216 +0200
+++ linux-2.6.9-rc2-bk9-neigh1/net/core/neighbour.c	2004-09-28 00:15:51.372437440 +0200
@@ -12,6 +12,7 @@
  *
  *	Fixes:
  *	Vitaly E. Lavrov	releasing NULL neighbor in neigh_add.
+ *	Harald Welte		Add neighbour cache statistics like rtstat
  */
 
 #include <linux/config.h>
@@ -21,6 +22,7 @@
 #include <linux/socket.h>
 #include <linux/sched.h>
 #include <linux/netdevice.h>
+#include <linux/proc_fs.h>
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
 #endif
@@ -59,6 +61,7 @@
 
 static int neigh_glbl_allocs;
 static struct neigh_table *neigh_tables;
+static struct file_operations neigh_stat_seq_fops;
 
 /*
    Neighbour hash table buckets are protected with rwlock tbl->lock.
@@ -116,6 +119,8 @@
 	int shrunk = 0;
 	int i;
 
+	NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
+
 	write_lock_bh(&tbl->lock);
 	for (i = 0; i <= tbl->hash_mask; i++) {
 		struct neighbour *n, **np;
@@ -273,7 +278,8 @@
 	init_timer(&n->timer);
 	n->timer.function = neigh_timer_handler;
 	n->timer.data	  = (unsigned long)n;
-	tbl->stats.allocs++;
+
+	NEIGH_CACHE_STAT_INC(tbl, allocs);
 	neigh_glbl_allocs++;
 	tbl->entries++;
 	n->tbl		  = tbl;
@@ -315,6 +321,8 @@
 	struct neighbour **new_hash, **old_hash;
 	unsigned int i, new_hash_mask, old_entries;
 
+	NEIGH_CACHE_STAT_INC(tbl, hash_grows);
+
 	BUG_ON(new_entries & (new_entries - 1));
 	new_hash = neigh_hash_alloc(new_entries);
 	if (!new_hash)
@@ -349,11 +357,14 @@
 	struct neighbour *n;
 	int key_len = tbl->key_len;
 	u32 hash_val = tbl->hash(pkey, dev) & tbl->hash_mask;
+	
+	NEIGH_CACHE_STAT_INC(tbl, lookups);
 
 	read_lock_bh(&tbl->lock);
 	for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
 		if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
 			neigh_hold(n);
+			NEIGH_CACHE_STAT_INC(tbl, hits);
 			break;
 		}
 	}
@@ -367,10 +378,13 @@
 	int key_len = tbl->key_len;
 	u32 hash_val = tbl->hash(pkey, NULL) & tbl->hash_mask;
 
+	NEIGH_CACHE_STAT_INC(tbl, lookups);
+
 	read_lock_bh(&tbl->lock);
 	for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
 		if (!memcmp(n->primary_key, pkey, key_len)) {
 			neigh_hold(n);
+			NEIGH_CACHE_STAT_INC(tbl, hits);
 			break;
 		}
 	}
@@ -555,6 +569,8 @@
 {
 	struct hh_cache *hh;
 
+	NEIGH_CACHE_STAT_INC(neigh->tbl, destroys);
+
 	if (!neigh->dead) {
 		printk(KERN_WARNING
 		       "Destroying alive neighbour %p\n", neigh);
@@ -630,6 +646,8 @@
 	struct neighbour *n, **np;
 	unsigned long expire, now = jiffies;
 
+	NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs);
+
 	write_lock(&tbl->lock);
 
 	/*
@@ -761,7 +779,7 @@
 
 		neigh->nud_state = NUD_FAILED;
 		notify = 1;
-		neigh->tbl->stats.res_failed++;
+		NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
 		NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
 
 		/* It is very thin place. report_unreachable is very complicated
@@ -1310,6 +1328,29 @@
 	if (!tbl->kmem_cachep)
 		panic("cannot create neighbour cache");
 
+	tbl->stats = alloc_percpu(struct neigh_statistics);
+	if (!tbl->stats)
+		panic("cannot create neighbour cache statistics");
+	
+#ifdef CONFIG_PROC_FS
+#define NC_STAT_SUFFIX "_stat"
+	{
+	char *proc_stat_name;
+	proc_stat_name = kmalloc(strlen(tbl->id) + 
+				 strlen(NC_STAT_SUFFIX) + 1, GFP_KERNEL);
+	if (!proc_stat_name)
+		panic("cannot allocate neighbour cache proc name buffer");
+	strcpy(proc_stat_name, tbl->id);
+	strcat(proc_stat_name, NC_STAT_SUFFIX);
+
+	tbl->pde = create_proc_entry(proc_stat_name, 0, proc_net);
+	if (!tbl->pde) 
+		panic("cannot create neighbour proc dir entry");
+	tbl->pde->proc_fops = &neigh_stat_seq_fops;
+	tbl->pde->data = tbl;
+	}
+#endif
+
 	tbl->hash_mask = 0x1f;
 	tbl->hash_buckets = neigh_hash_alloc(tbl->hash_mask + 1);
 
@@ -1856,6 +1897,106 @@
 }
 EXPORT_SYMBOL(neigh_seq_stop);
 
+/* statistics via seq_file */
+
+static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct proc_dir_entry *pde = seq->private;
+	struct neigh_table *tbl = pde->data;
+	int cpu;
+
+	if (*pos == 0)
+		return SEQ_START_TOKEN;
+	
+	for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu+1;
+		return per_cpu_ptr(tbl->stats, cpu);
+	}
+	return NULL;
+}
+
+static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct proc_dir_entry *pde = seq->private;
+	struct neigh_table *tbl = pde->data;
+	int cpu;
+
+	for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
+		if (!cpu_possible(cpu))
+			continue;
+		*pos = cpu+1;
+		return per_cpu_ptr(tbl->stats, cpu);
+	}
+	return NULL;
+}
+
+static void neigh_stat_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int neigh_stat_seq_show(struct seq_file *seq, void *v)
+{
+	struct proc_dir_entry *pde = seq->private;
+	struct neigh_table *tbl = pde->data;
+	struct neigh_statistics *st = v;
+
+	if (v == SEQ_START_TOKEN) {
+		seq_printf(seq, "entries  allocs destroys hash_grows  lookups hits  res_failed  rcv_probes_mcast rcv_probes_ucast  periodic_gc_runs forced_gc_runs forced_gc_goal_miss\n");
+		return 0;
+	}
+
+	seq_printf(seq, "%08x  %08lx %08lx %08lx  %08lx %08lx  %08lx  "
+			"%08lx %08lx  %08lx %08lx\n",
+		   tbl->entries,
+
+		   st->allocs,
+		   st->destroys,
+		   st->hash_grows,
+
+		   st->lookups,
+		   st->hits,
+
+		   st->res_failed,
+
+		   st->rcv_probes_mcast,
+		   st->rcv_probes_ucast,
+
+		   st->periodic_gc_runs,
+		   st->forced_gc_runs
+		   );
+
+	return 0;
+}
+
+static struct seq_operations neigh_stat_seq_ops = {
+	.start	= neigh_stat_seq_start,
+	.next	= neigh_stat_seq_next,
+	.stop	= neigh_stat_seq_stop,
+	.show	= neigh_stat_seq_show,
+};
+
+static int neigh_stat_seq_open(struct inode *inode, struct file *file)
+{
+	int ret = seq_open(file, &neigh_stat_seq_ops);
+
+	if (!ret) {
+		struct seq_file *sf = file->private_data;
+		sf->private = PDE(inode);
+	}
+	return ret;
+};
+
+static struct file_operations neigh_stat_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open 	 = neigh_stat_seq_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release,
+};
+
 #endif /* CONFIG_PROC_FS */
 
 #ifdef CONFIG_ARPD
Index: linux-2.6.9-rc2-bk9-neigh1/net/ipv6/ndisc.c
===================================================================
--- linux-2.6.9-rc2-bk9-neigh1.orig/net/ipv6/ndisc.c	2004-09-26 12:29:03.000000000 +0200
+++ linux-2.6.9-rc2-bk9-neigh1/net/ipv6/ndisc.c	2004-09-28 00:05:29.917912888 +0200
@@ -802,9 +802,9 @@
 	}
 
 	if (inc)
-		nd_tbl.stats.rcv_probes_mcast++;
+		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
 	else
-		nd_tbl.stats.rcv_probes_ucast++;
+		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
 
 	/* 
 	 *	update / create cache entry

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2.6] neighbour cache statistics like rt_stat
  2004-09-27 22:20     ` Harald Welte
@ 2004-09-28  5:17       ` David S. Miller
  0 siblings, 0 replies; 5+ messages in thread
From: David S. Miller @ 2004-09-28  5:17 UTC (permalink / raw)
  To: Harald Welte; +Cc: netdev

On Tue, 28 Sep 2004 00:20:55 +0200
Harald Welte <laforge@gnumonks.org> wrote:

> On Mon, Sep 27, 2004 at 12:21:36PM -0700, David S. Miller wrote:
> 
> > 1) Please update the patch since I replaced "diff4" with
> >    a patch simply removing the INCOMPLETE checks from neigh_forced_gc()
> > 
> > 2) I'd recommend using unsigned long for all the statistics.
> 
> both updated in attached version.

Applied, thanks Harald.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2004-09-28  5:17 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-09-27  9:36 [PATCH 2.6] neighbour cache statistics like rt_stat Harald Welte
2004-09-27 16:23 ` Harald Welte
2004-09-27 19:21   ` David S. Miller
2004-09-27 22:20     ` Harald Welte
2004-09-28  5:17       ` David S. Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).