|
@@ -43,7 +43,7 @@
|
|
* 2 of the License, or (at your option) any later version.
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
*/
|
|
|
|
|
|
-#define VERSION "0.402"
|
|
|
|
|
|
+#define VERSION "0.403"
|
|
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/config.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/uaccess.h>
|
|
@@ -164,7 +164,6 @@ static struct node *resize(struct trie *t, struct tnode *tn);
|
|
static struct tnode *inflate(struct trie *t, struct tnode *tn);
|
|
static struct tnode *inflate(struct trie *t, struct tnode *tn);
|
|
static struct tnode *halve(struct trie *t, struct tnode *tn);
|
|
static struct tnode *halve(struct trie *t, struct tnode *tn);
|
|
static void tnode_free(struct tnode *tn);
|
|
static void tnode_free(struct tnode *tn);
|
|
-static void trie_dump_seq(struct seq_file *seq, struct trie *t);
|
|
|
|
|
|
|
|
static kmem_cache_t *fn_alias_kmem __read_mostly;
|
|
static kmem_cache_t *fn_alias_kmem __read_mostly;
|
|
static struct trie *trie_local = NULL, *trie_main = NULL;
|
|
static struct trie *trie_local = NULL, *trie_main = NULL;
|
|
@@ -1971,558 +1970,525 @@ struct fib_table * __init fib_hash_init(int id)
|
|
return tb;
|
|
return tb;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Trie dump functions */
|
|
|
|
|
|
+#ifdef CONFIG_PROC_FS
|
|
|
|
+/* Depth first Trie walk iterator */
|
|
|
|
+struct fib_trie_iter {
|
|
|
|
+ struct tnode *tnode;
|
|
|
|
+ struct trie *trie;
|
|
|
|
+ unsigned index;
|
|
|
|
+ unsigned depth;
|
|
|
|
+};
|
|
|
|
|
|
-static void putspace_seq(struct seq_file *seq, int n)
|
|
|
|
|
|
+static struct node *fib_trie_get_next(struct fib_trie_iter *iter)
|
|
{
|
|
{
|
|
- while (n--)
|
|
|
|
- seq_printf(seq, " ");
|
|
|
|
-}
|
|
|
|
|
|
+ struct tnode *tn = iter->tnode;
|
|
|
|
+ unsigned cindex = iter->index;
|
|
|
|
+ struct tnode *p;
|
|
|
|
|
|
-static void printbin_seq(struct seq_file *seq, unsigned int v, int bits)
|
|
|
|
-{
|
|
|
|
- while (bits--)
|
|
|
|
- seq_printf(seq, "%s", (v & (1<<bits))?"1":"0");
|
|
|
|
-}
|
|
|
|
|
|
+ pr_debug("get_next iter={node=%p index=%d depth=%d}\n",
|
|
|
|
+ iter->tnode, iter->index, iter->depth);
|
|
|
|
+rescan:
|
|
|
|
+ while (cindex < (1<<tn->bits)) {
|
|
|
|
+ struct node *n = tnode_get_child(tn, cindex);
|
|
|
|
|
|
-static void printnode_seq(struct seq_file *seq, int indent, struct node *n,
|
|
|
|
- int pend, int cindex, int bits)
|
|
|
|
-{
|
|
|
|
- putspace_seq(seq, indent);
|
|
|
|
- if (IS_LEAF(n))
|
|
|
|
- seq_printf(seq, "|");
|
|
|
|
- else
|
|
|
|
- seq_printf(seq, "+");
|
|
|
|
- if (bits) {
|
|
|
|
- seq_printf(seq, "%d/", cindex);
|
|
|
|
- printbin_seq(seq, cindex, bits);
|
|
|
|
- seq_printf(seq, ": ");
|
|
|
|
- } else
|
|
|
|
- seq_printf(seq, "<root>: ");
|
|
|
|
- seq_printf(seq, "%s:%p ", IS_LEAF(n)?"Leaf":"Internal node", n);
|
|
|
|
|
|
+ if (n) {
|
|
|
|
+ if (IS_LEAF(n)) {
|
|
|
|
+ iter->tnode = tn;
|
|
|
|
+ iter->index = cindex + 1;
|
|
|
|
+ } else {
|
|
|
|
+ /* push down one level */
|
|
|
|
+ iter->tnode = (struct tnode *) n;
|
|
|
|
+ iter->index = 0;
|
|
|
|
+ ++iter->depth;
|
|
|
|
+ }
|
|
|
|
+ return n;
|
|
|
|
+ }
|
|
|
|
|
|
- if (IS_LEAF(n)) {
|
|
|
|
- struct leaf *l = (struct leaf *)n;
|
|
|
|
- struct fib_alias *fa;
|
|
|
|
- int i;
|
|
|
|
|
|
+ ++cindex;
|
|
|
|
+ }
|
|
|
|
|
|
- seq_printf(seq, "key=%d.%d.%d.%d\n",
|
|
|
|
- n->key >> 24, (n->key >> 16) % 256, (n->key >> 8) % 256, n->key % 256);
|
|
|
|
-
|
|
|
|
- for (i = 32; i >= 0; i--)
|
|
|
|
- if (find_leaf_info(&l->list, i)) {
|
|
|
|
- struct list_head *fa_head = get_fa_head(l, i);
|
|
|
|
-
|
|
|
|
- if (!fa_head)
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- if (list_empty(fa_head))
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- putspace_seq(seq, indent+2);
|
|
|
|
- seq_printf(seq, "{/%d...dumping}\n", i);
|
|
|
|
-
|
|
|
|
- list_for_each_entry_rcu(fa, fa_head, fa_list) {
|
|
|
|
- putspace_seq(seq, indent+2);
|
|
|
|
- if (fa->fa_info == NULL) {
|
|
|
|
- seq_printf(seq, "Error fa_info=NULL\n");
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- if (fa->fa_info->fib_nh == NULL) {
|
|
|
|
- seq_printf(seq, "Error _fib_nh=NULL\n");
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- seq_printf(seq, "{type=%d scope=%d TOS=%d}\n",
|
|
|
|
- fa->fa_type,
|
|
|
|
- fa->fa_scope,
|
|
|
|
- fa->fa_tos);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- struct tnode *tn = (struct tnode *)n;
|
|
|
|
- int plen = ((struct tnode *)n)->pos;
|
|
|
|
- t_key prf = MASK_PFX(n->key, plen);
|
|
|
|
-
|
|
|
|
- seq_printf(seq, "key=%d.%d.%d.%d/%d\n",
|
|
|
|
- prf >> 24, (prf >> 16) % 256, (prf >> 8) % 256, prf % 256, plen);
|
|
|
|
-
|
|
|
|
- putspace_seq(seq, indent); seq_printf(seq, "| ");
|
|
|
|
- seq_printf(seq, "{key prefix=%08x/", tn->key & TKEY_GET_MASK(0, tn->pos));
|
|
|
|
- printbin_seq(seq, tkey_extract_bits(tn->key, 0, tn->pos), tn->pos);
|
|
|
|
- seq_printf(seq, "}\n");
|
|
|
|
- putspace_seq(seq, indent); seq_printf(seq, "| ");
|
|
|
|
- seq_printf(seq, "{pos=%d", tn->pos);
|
|
|
|
- seq_printf(seq, " (skip=%d bits)", tn->pos - pend);
|
|
|
|
- seq_printf(seq, " bits=%d (%u children)}\n", tn->bits, (1 << tn->bits));
|
|
|
|
- putspace_seq(seq, indent); seq_printf(seq, "| ");
|
|
|
|
- seq_printf(seq, "{empty=%d full=%d}\n", tn->empty_children, tn->full_children);
|
|
|
|
|
|
+ /* Current node exhausted, pop back up */
|
|
|
|
+ p = NODE_PARENT(tn);
|
|
|
|
+ if (p) {
|
|
|
|
+ cindex = tkey_extract_bits(tn->key, p->pos, p->bits)+1;
|
|
|
|
+ tn = p;
|
|
|
|
+ --iter->depth;
|
|
|
|
+ goto rescan;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /* got root? */
|
|
|
|
+ return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
-static void trie_dump_seq(struct seq_file *seq, struct trie *t)
|
|
|
|
|
|
+static struct node *fib_trie_get_first(struct fib_trie_iter *iter,
|
|
|
|
+ struct trie *t)
|
|
{
|
|
{
|
|
- struct node *n;
|
|
|
|
- int cindex = 0;
|
|
|
|
- int indent = 1;
|
|
|
|
- int pend = 0;
|
|
|
|
- int depth = 0;
|
|
|
|
- struct tnode *tn;
|
|
|
|
-
|
|
|
|
- rcu_read_lock();
|
|
|
|
- n = rcu_dereference(t->trie);
|
|
|
|
- seq_printf(seq, "------ trie_dump of t=%p ------\n", t);
|
|
|
|
|
|
+ struct node *n = rcu_dereference(t->trie);
|
|
|
|
|
|
- if (!n) {
|
|
|
|
- seq_printf(seq, "------ trie is empty\n");
|
|
|
|
-
|
|
|
|
- rcu_read_unlock();
|
|
|
|
- return;
|
|
|
|
|
|
+ if (n && IS_TNODE(n)) {
|
|
|
|
+ iter->tnode = (struct tnode *) n;
|
|
|
|
+ iter->trie = t;
|
|
|
|
+ iter->index = 0;
|
|
|
|
+ iter->depth = 0;
|
|
|
|
+ return n;
|
|
}
|
|
}
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
|
|
- printnode_seq(seq, indent, n, pend, cindex, 0);
|
|
|
|
-
|
|
|
|
- if (!IS_TNODE(n)) {
|
|
|
|
- rcu_read_unlock();
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tn = (struct tnode *)n;
|
|
|
|
- pend = tn->pos+tn->bits;
|
|
|
|
- putspace_seq(seq, indent); seq_printf(seq, "\\--\n");
|
|
|
|
- indent += 3;
|
|
|
|
- depth++;
|
|
|
|
-
|
|
|
|
- while (tn && cindex < (1 << tn->bits)) {
|
|
|
|
- struct node *child = rcu_dereference(tn->child[cindex]);
|
|
|
|
- if (!child)
|
|
|
|
- cindex++;
|
|
|
|
- else {
|
|
|
|
- /* Got a child */
|
|
|
|
- printnode_seq(seq, indent, child, pend,
|
|
|
|
- cindex, tn->bits);
|
|
|
|
-
|
|
|
|
- if (IS_LEAF(child))
|
|
|
|
- cindex++;
|
|
|
|
-
|
|
|
|
- else {
|
|
|
|
- /*
|
|
|
|
- * New tnode. Decend one level
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- depth++;
|
|
|
|
- n = child;
|
|
|
|
- tn = (struct tnode *)n;
|
|
|
|
- pend = tn->pos+tn->bits;
|
|
|
|
- putspace_seq(seq, indent);
|
|
|
|
- seq_printf(seq, "\\--\n");
|
|
|
|
- indent += 3;
|
|
|
|
- cindex = 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Test if we are done
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- while (cindex >= (1 << tn->bits)) {
|
|
|
|
- /*
|
|
|
|
- * Move upwards and test for root
|
|
|
|
- * pop off all traversed nodes
|
|
|
|
- */
|
|
|
|
|
|
+static void trie_collect_stats(struct trie *t, struct trie_stat *s)
|
|
|
|
+{
|
|
|
|
+ struct node *n;
|
|
|
|
+ struct fib_trie_iter iter;
|
|
|
|
|
|
- if (NODE_PARENT(tn) == NULL) {
|
|
|
|
- tn = NULL;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
|
|
+ memset(s, 0, sizeof(*s));
|
|
|
|
|
|
- cindex = tkey_extract_bits(tn->key, NODE_PARENT(tn)->pos, NODE_PARENT(tn)->bits);
|
|
|
|
- cindex++;
|
|
|
|
- tn = NODE_PARENT(tn);
|
|
|
|
- pend = tn->pos + tn->bits;
|
|
|
|
- indent -= 3;
|
|
|
|
- depth--;
|
|
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ for (n = fib_trie_get_first(&iter, t); n;
|
|
|
|
+ n = fib_trie_get_next(&iter)) {
|
|
|
|
+ if (IS_LEAF(n)) {
|
|
|
|
+ s->leaves++;
|
|
|
|
+ s->totdepth += iter.depth;
|
|
|
|
+ if (iter.depth > s->maxdepth)
|
|
|
|
+ s->maxdepth = iter.depth;
|
|
|
|
+ } else {
|
|
|
|
+ const struct tnode *tn = (const struct tnode *) n;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ s->tnodes++;
|
|
|
|
+ s->nodesizes[tn->bits]++;
|
|
|
|
+ for (i = 0; i < (1<<tn->bits); i++)
|
|
|
|
+ if (!tn->child[i])
|
|
|
|
+ s->nullpointers++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
}
|
|
}
|
|
|
|
|
|
-static struct trie_stat *trie_stat_new(void)
|
|
|
|
|
|
+/*
|
|
|
|
+ * This outputs /proc/net/fib_triestats
|
|
|
|
+ */
|
|
|
|
+static void trie_show_stats(struct seq_file *seq, struct trie_stat *stat)
|
|
{
|
|
{
|
|
- struct trie_stat *s;
|
|
|
|
- int i;
|
|
|
|
|
|
+ unsigned i, max, pointers, bytes, avdepth;
|
|
|
|
|
|
- s = kmalloc(sizeof(struct trie_stat), GFP_KERNEL);
|
|
|
|
- if (!s)
|
|
|
|
- return NULL;
|
|
|
|
|
|
+ if (stat->leaves)
|
|
|
|
+ avdepth = stat->totdepth*100 / stat->leaves;
|
|
|
|
+ else
|
|
|
|
+ avdepth = 0;
|
|
|
|
|
|
- s->totdepth = 0;
|
|
|
|
- s->maxdepth = 0;
|
|
|
|
- s->tnodes = 0;
|
|
|
|
- s->leaves = 0;
|
|
|
|
- s->nullpointers = 0;
|
|
|
|
|
|
+ seq_printf(seq, "\tAver depth: %d.%02d\n", avdepth / 100, avdepth % 100 );
|
|
|
|
+ seq_printf(seq, "\tMax depth: %u\n", stat->maxdepth);
|
|
|
|
|
|
- for (i = 0; i < MAX_CHILDS; i++)
|
|
|
|
- s->nodesizes[i] = 0;
|
|
|
|
|
|
+ seq_printf(seq, "\tLeaves: %u\n", stat->leaves);
|
|
|
|
|
|
- return s;
|
|
|
|
-}
|
|
|
|
|
|
+ bytes = sizeof(struct leaf) * stat->leaves;
|
|
|
|
+ seq_printf(seq, "\tInternal nodes: %d\n\t", stat->tnodes);
|
|
|
|
+ bytes += sizeof(struct tnode) * stat->tnodes;
|
|
|
|
|
|
-static struct trie_stat *trie_collect_stats(struct trie *t)
|
|
|
|
-{
|
|
|
|
- struct node *n;
|
|
|
|
- struct trie_stat *s = trie_stat_new();
|
|
|
|
- int cindex = 0;
|
|
|
|
- int pend = 0;
|
|
|
|
- int depth = 0;
|
|
|
|
|
|
+ max = MAX_CHILDS-1;
|
|
|
|
+ while (max >= 0 && stat->nodesizes[max] == 0)
|
|
|
|
+ max--;
|
|
|
|
|
|
- if (!s)
|
|
|
|
- return NULL;
|
|
|
|
|
|
+ pointers = 0;
|
|
|
|
+ for (i = 1; i <= max; i++)
|
|
|
|
+ if (stat->nodesizes[i] != 0) {
|
|
|
|
+ seq_printf(seq, " %d: %d", i, stat->nodesizes[i]);
|
|
|
|
+ pointers += (1<<i) * stat->nodesizes[i];
|
|
|
|
+ }
|
|
|
|
+ seq_putc(seq, '\n');
|
|
|
|
+ seq_printf(seq, "\tPointers: %d\n", pointers);
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
|
- n = rcu_dereference(t->trie);
|
|
|
|
|
|
+ bytes += sizeof(struct node *) * pointers;
|
|
|
|
+ seq_printf(seq, "Null ptrs: %d\n", stat->nullpointers);
|
|
|
|
+ seq_printf(seq, "Total size: %d kB\n", (bytes + 1023) / 1024);
|
|
|
|
|
|
- if (!n)
|
|
|
|
- return s;
|
|
|
|
|
|
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
|
|
|
+ seq_printf(seq, "Counters:\n---------\n");
|
|
|
|
+ seq_printf(seq,"gets = %d\n", t->stats.gets);
|
|
|
|
+ seq_printf(seq,"backtracks = %d\n", t->stats.backtrack);
|
|
|
|
+ seq_printf(seq,"semantic match passed = %d\n", t->stats.semantic_match_passed);
|
|
|
|
+ seq_printf(seq,"semantic match miss = %d\n", t->stats.semantic_match_miss);
|
|
|
|
+ seq_printf(seq,"null node hit= %d\n", t->stats.null_node_hit);
|
|
|
|
+ seq_printf(seq,"skipped node resize = %d\n", t->stats.resize_node_skipped);
|
|
|
|
+#ifdef CLEAR_STATS
|
|
|
|
+ memset(&(t->stats), 0, sizeof(t->stats));
|
|
|
|
+#endif
|
|
|
|
+#endif /* CONFIG_IP_FIB_TRIE_STATS */
|
|
|
|
+}
|
|
|
|
|
|
- if (IS_TNODE(n)) {
|
|
|
|
- struct tnode *tn = (struct tnode *)n;
|
|
|
|
- pend = tn->pos+tn->bits;
|
|
|
|
- s->nodesizes[tn->bits]++;
|
|
|
|
- depth++;
|
|
|
|
-
|
|
|
|
- while (tn && cindex < (1 << tn->bits)) {
|
|
|
|
- struct node *ch = rcu_dereference(tn->child[cindex]);
|
|
|
|
- if (ch) {
|
|
|
|
-
|
|
|
|
- /* Got a child */
|
|
|
|
-
|
|
|
|
- if (IS_LEAF(tn->child[cindex])) {
|
|
|
|
- cindex++;
|
|
|
|
-
|
|
|
|
- /* stats */
|
|
|
|
- if (depth > s->maxdepth)
|
|
|
|
- s->maxdepth = depth;
|
|
|
|
- s->totdepth += depth;
|
|
|
|
- s->leaves++;
|
|
|
|
- } else {
|
|
|
|
- /*
|
|
|
|
- * New tnode. Decend one level
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- s->tnodes++;
|
|
|
|
- s->nodesizes[tn->bits]++;
|
|
|
|
- depth++;
|
|
|
|
-
|
|
|
|
- n = ch;
|
|
|
|
- tn = (struct tnode *)n;
|
|
|
|
- pend = tn->pos+tn->bits;
|
|
|
|
-
|
|
|
|
- cindex = 0;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- cindex++;
|
|
|
|
- s->nullpointers++;
|
|
|
|
- }
|
|
|
|
|
|
+static int fib_triestat_seq_show(struct seq_file *seq, void *v)
|
|
|
|
+{
|
|
|
|
+ struct trie_stat *stat;
|
|
|
|
|
|
- /*
|
|
|
|
- * Test if we are done
|
|
|
|
- */
|
|
|
|
|
|
+ stat = kmalloc(sizeof(*stat), GFP_KERNEL);
|
|
|
|
+ if (!stat)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
- while (cindex >= (1 << tn->bits)) {
|
|
|
|
- /*
|
|
|
|
- * Move upwards and test for root
|
|
|
|
- * pop off all traversed nodes
|
|
|
|
- */
|
|
|
|
|
|
+ seq_printf(seq, "Basic info: size of leaf: %Zd bytes, size of tnode: %Zd bytes.\n",
|
|
|
|
+ sizeof(struct leaf), sizeof(struct tnode));
|
|
|
|
|
|
- if (NODE_PARENT(tn) == NULL) {
|
|
|
|
- tn = NULL;
|
|
|
|
- n = NULL;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
|
|
+ if (trie_local) {
|
|
|
|
+ seq_printf(seq, "Local:\n");
|
|
|
|
+ trie_collect_stats(trie_local, stat);
|
|
|
|
+ trie_show_stats(seq, stat);
|
|
|
|
+ }
|
|
|
|
|
|
- cindex = tkey_extract_bits(tn->key, NODE_PARENT(tn)->pos, NODE_PARENT(tn)->bits);
|
|
|
|
- tn = NODE_PARENT(tn);
|
|
|
|
- cindex++;
|
|
|
|
- n = (struct node *)tn;
|
|
|
|
- pend = tn->pos+tn->bits;
|
|
|
|
- depth--;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ if (trie_main) {
|
|
|
|
+ seq_printf(seq, "Main:\n");
|
|
|
|
+ trie_collect_stats(trie_main, stat);
|
|
|
|
+ trie_show_stats(seq, stat);
|
|
}
|
|
}
|
|
|
|
+ kfree(stat);
|
|
|
|
|
|
- rcu_read_unlock();
|
|
|
|
- return s;
|
|
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_PROC_FS
|
|
|
|
-
|
|
|
|
-static struct fib_alias *fib_triestat_get_first(struct seq_file *seq)
|
|
|
|
|
|
+static int fib_triestat_seq_open(struct inode *inode, struct file *file)
|
|
{
|
|
{
|
|
- return NULL;
|
|
|
|
|
|
+ return single_open(file, fib_triestat_seq_show, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
-static struct fib_alias *fib_triestat_get_next(struct seq_file *seq)
|
|
|
|
|
|
+static struct file_operations fib_triestat_fops = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .open = fib_triestat_seq_open,
|
|
|
|
+ .read = seq_read,
|
|
|
|
+ .llseek = seq_lseek,
|
|
|
|
+ .release = single_release,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static struct node *fib_trie_get_idx(struct fib_trie_iter *iter,
|
|
|
|
+ loff_t pos)
|
|
{
|
|
{
|
|
|
|
+ loff_t idx = 0;
|
|
|
|
+ struct node *n;
|
|
|
|
+
|
|
|
|
+ for (n = fib_trie_get_first(iter, trie_local);
|
|
|
|
+ n; ++idx, n = fib_trie_get_next(iter)) {
|
|
|
|
+ if (pos == idx)
|
|
|
|
+ return n;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (n = fib_trie_get_first(iter, trie_main);
|
|
|
|
+ n; ++idx, n = fib_trie_get_next(iter)) {
|
|
|
|
+ if (pos == idx)
|
|
|
|
+ return n;
|
|
|
|
+ }
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
-static void *fib_triestat_seq_start(struct seq_file *seq, loff_t *pos)
|
|
|
|
|
|
+static void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos)
|
|
{
|
|
{
|
|
- if (!ip_fib_main_table)
|
|
|
|
- return NULL;
|
|
|
|
-
|
|
|
|
- if (*pos)
|
|
|
|
- return fib_triestat_get_next(seq);
|
|
|
|
- else
|
|
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ if (*pos == 0)
|
|
return SEQ_START_TOKEN;
|
|
return SEQ_START_TOKEN;
|
|
|
|
+ return fib_trie_get_idx(seq->private, *pos - 1);
|
|
}
|
|
}
|
|
|
|
|
|
-static void *fib_triestat_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
|
|
|
|
+static void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
{
|
|
{
|
|
|
|
+ struct fib_trie_iter *iter = seq->private;
|
|
|
|
+ void *l = v;
|
|
|
|
+
|
|
++*pos;
|
|
++*pos;
|
|
if (v == SEQ_START_TOKEN)
|
|
if (v == SEQ_START_TOKEN)
|
|
- return fib_triestat_get_first(seq);
|
|
|
|
- else
|
|
|
|
- return fib_triestat_get_next(seq);
|
|
|
|
-}
|
|
|
|
|
|
+ return fib_trie_get_idx(iter, 0);
|
|
|
|
|
|
-static void fib_triestat_seq_stop(struct seq_file *seq, void *v)
|
|
|
|
-{
|
|
|
|
|
|
+ v = fib_trie_get_next(iter);
|
|
|
|
+ BUG_ON(v == l);
|
|
|
|
+ if (v)
|
|
|
|
+ return v;
|
|
|
|
|
|
-}
|
|
|
|
|
|
+ /* continue scan in next trie */
|
|
|
|
+ if (iter->trie == trie_local)
|
|
|
|
+ return fib_trie_get_first(iter, trie_main);
|
|
|
|
|
|
-/*
|
|
|
|
- * This outputs /proc/net/fib_triestats
|
|
|
|
- *
|
|
|
|
- * It always works in backward compatibility mode.
|
|
|
|
- * The format of the file is not supposed to be changed.
|
|
|
|
- */
|
|
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
|
|
-static void collect_and_show(struct trie *t, struct seq_file *seq)
|
|
|
|
|
|
+static void fib_trie_seq_stop(struct seq_file *seq, void *v)
|
|
{
|
|
{
|
|
- int bytes = 0; /* How many bytes are used, a ref is 4 bytes */
|
|
|
|
- int i, max, pointers;
|
|
|
|
- struct trie_stat *stat;
|
|
|
|
- int avdepth;
|
|
|
|
-
|
|
|
|
- stat = trie_collect_stats(t);
|
|
|
|
-
|
|
|
|
- bytes = 0;
|
|
|
|
- seq_printf(seq, "trie=%p\n", t);
|
|
|
|
-
|
|
|
|
- if (stat) {
|
|
|
|
- if (stat->leaves)
|
|
|
|
- avdepth = stat->totdepth*100 / stat->leaves;
|
|
|
|
- else
|
|
|
|
- avdepth = 0;
|
|
|
|
- seq_printf(seq, "Aver depth: %d.%02d\n", avdepth / 100, avdepth % 100);
|
|
|
|
- seq_printf(seq, "Max depth: %4d\n", stat->maxdepth);
|
|
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+}
|
|
|
|
|
|
- seq_printf(seq, "Leaves: %d\n", stat->leaves);
|
|
|
|
- bytes += sizeof(struct leaf) * stat->leaves;
|
|
|
|
- seq_printf(seq, "Internal nodes: %d\n", stat->tnodes);
|
|
|
|
- bytes += sizeof(struct tnode) * stat->tnodes;
|
|
|
|
|
|
+static void seq_indent(struct seq_file *seq, int n)
|
|
|
|
+{
|
|
|
|
+ while (n-- > 0) seq_puts(seq, " ");
|
|
|
|
+}
|
|
|
|
|
|
- max = MAX_CHILDS-1;
|
|
|
|
|
|
+static inline const char *rtn_scope(enum rt_scope_t s)
|
|
|
|
+{
|
|
|
|
+ static char buf[32];
|
|
|
|
|
|
- while (max >= 0 && stat->nodesizes[max] == 0)
|
|
|
|
- max--;
|
|
|
|
- pointers = 0;
|
|
|
|
|
|
+ switch(s) {
|
|
|
|
+ case RT_SCOPE_UNIVERSE: return "universe";
|
|
|
|
+ case RT_SCOPE_SITE: return "site";
|
|
|
|
+ case RT_SCOPE_LINK: return "link";
|
|
|
|
+ case RT_SCOPE_HOST: return "host";
|
|
|
|
+ case RT_SCOPE_NOWHERE: return "nowhere";
|
|
|
|
+ default:
|
|
|
|
+ snprintf(buf, sizeof(buf), "scope=%d", s);
|
|
|
|
+ return buf;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
- for (i = 1; i <= max; i++)
|
|
|
|
- if (stat->nodesizes[i] != 0) {
|
|
|
|
- seq_printf(seq, " %d: %d", i, stat->nodesizes[i]);
|
|
|
|
- pointers += (1<<i) * stat->nodesizes[i];
|
|
|
|
- }
|
|
|
|
- seq_printf(seq, "\n");
|
|
|
|
- seq_printf(seq, "Pointers: %d\n", pointers);
|
|
|
|
- bytes += sizeof(struct node *) * pointers;
|
|
|
|
- seq_printf(seq, "Null ptrs: %d\n", stat->nullpointers);
|
|
|
|
- seq_printf(seq, "Total size: %d kB\n", bytes / 1024);
|
|
|
|
|
|
+static const char *rtn_type_names[__RTN_MAX] = {
|
|
|
|
+ [RTN_UNSPEC] = "UNSPEC",
|
|
|
|
+ [RTN_UNICAST] = "UNICAST",
|
|
|
|
+ [RTN_LOCAL] = "LOCAL",
|
|
|
|
+ [RTN_BROADCAST] = "BROADCAST",
|
|
|
|
+ [RTN_ANYCAST] = "ANYCAST",
|
|
|
|
+ [RTN_MULTICAST] = "MULTICAST",
|
|
|
|
+ [RTN_BLACKHOLE] = "BLACKHOLE",
|
|
|
|
+ [RTN_UNREACHABLE] = "UNREACHABLE",
|
|
|
|
+ [RTN_PROHIBIT] = "PROHIBIT",
|
|
|
|
+ [RTN_THROW] = "THROW",
|
|
|
|
+ [RTN_NAT] = "NAT",
|
|
|
|
+ [RTN_XRESOLVE] = "XRESOLVE",
|
|
|
|
+};
|
|
|
|
|
|
- kfree(stat);
|
|
|
|
- }
|
|
|
|
|
|
+static inline const char *rtn_type(unsigned t)
|
|
|
|
+{
|
|
|
|
+ static char buf[32];
|
|
|
|
|
|
-#ifdef CONFIG_IP_FIB_TRIE_STATS
|
|
|
|
- seq_printf(seq, "Counters:\n---------\n");
|
|
|
|
- seq_printf(seq,"gets = %d\n", t->stats.gets);
|
|
|
|
- seq_printf(seq,"backtracks = %d\n", t->stats.backtrack);
|
|
|
|
- seq_printf(seq,"semantic match passed = %d\n", t->stats.semantic_match_passed);
|
|
|
|
- seq_printf(seq,"semantic match miss = %d\n", t->stats.semantic_match_miss);
|
|
|
|
- seq_printf(seq,"null node hit= %d\n", t->stats.null_node_hit);
|
|
|
|
- seq_printf(seq,"skipped node resize = %d\n", t->stats.resize_node_skipped);
|
|
|
|
-#ifdef CLEAR_STATS
|
|
|
|
- memset(&(t->stats), 0, sizeof(t->stats));
|
|
|
|
-#endif
|
|
|
|
-#endif /* CONFIG_IP_FIB_TRIE_STATS */
|
|
|
|
|
|
+ if (t < __RTN_MAX && rtn_type_names[t])
|
|
|
|
+ return rtn_type_names[t];
|
|
|
|
+ snprintf(buf, sizeof(buf), "type %d", t);
|
|
|
|
+ return buf;
|
|
}
|
|
}
|
|
|
|
|
|
-static int fib_triestat_seq_show(struct seq_file *seq, void *v)
|
|
|
|
|
|
+/* Pretty print the trie */
|
|
|
|
+static int fib_trie_seq_show(struct seq_file *seq, void *v)
|
|
{
|
|
{
|
|
- char bf[128];
|
|
|
|
|
|
+ const struct fib_trie_iter *iter = seq->private;
|
|
|
|
+ struct node *n = v;
|
|
|
|
|
|
- if (v == SEQ_START_TOKEN) {
|
|
|
|
- seq_printf(seq, "Basic info: size of leaf: %Zd bytes, size of tnode: %Zd bytes.\n",
|
|
|
|
- sizeof(struct leaf), sizeof(struct tnode));
|
|
|
|
- if (trie_local)
|
|
|
|
- collect_and_show(trie_local, seq);
|
|
|
|
|
|
+ if (v == SEQ_START_TOKEN)
|
|
|
|
+ return 0;
|
|
|
|
|
|
- if (trie_main)
|
|
|
|
- collect_and_show(trie_main, seq);
|
|
|
|
- } else {
|
|
|
|
- snprintf(bf, sizeof(bf), "*\t%08X\t%08X", 200, 400);
|
|
|
|
|
|
+ if (IS_TNODE(n)) {
|
|
|
|
+ struct tnode *tn = (struct tnode *) n;
|
|
|
|
+ t_key prf = ntohl(MASK_PFX(tn->key, tn->pos));
|
|
|
|
|
|
- seq_printf(seq, "%-127s\n", bf);
|
|
|
|
|
|
+ if (!NODE_PARENT(n)) {
|
|
|
|
+ if (iter->trie == trie_local)
|
|
|
|
+ seq_puts(seq, "<local>:\n");
|
|
|
|
+ else
|
|
|
|
+ seq_puts(seq, "<main>:\n");
|
|
|
|
+ } else {
|
|
|
|
+ seq_indent(seq, iter->depth-1);
|
|
|
|
+ seq_printf(seq, " +-- %d.%d.%d.%d/%d\n",
|
|
|
|
+ NIPQUAD(prf), tn->pos);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ struct leaf *l = (struct leaf *) n;
|
|
|
|
+ int i;
|
|
|
|
+ u32 val = ntohl(l->key);
|
|
|
|
+
|
|
|
|
+ seq_indent(seq, iter->depth);
|
|
|
|
+ seq_printf(seq, " |-- %d.%d.%d.%d\n", NIPQUAD(val));
|
|
|
|
+ for (i = 32; i >= 0; i--) {
|
|
|
|
+ struct leaf_info *li = find_leaf_info(&l->list, i);
|
|
|
|
+ if (li) {
|
|
|
|
+ struct fib_alias *fa;
|
|
|
|
+ list_for_each_entry_rcu(fa, &li->falh, fa_list) {
|
|
|
|
+ seq_indent(seq, iter->depth+1);
|
|
|
|
+ seq_printf(seq, " /%d %s %s", i,
|
|
|
|
+ rtn_scope(fa->fa_scope),
|
|
|
|
+ rtn_type(fa->fa_type));
|
|
|
|
+ if (fa->fa_tos)
|
|
|
|
+ seq_printf(seq, "tos =%d\n",
|
|
|
|
+ fa->fa_tos);
|
|
|
|
+ seq_putc(seq, '\n');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static struct seq_operations fib_triestat_seq_ops = {
|
|
|
|
- .start = fib_triestat_seq_start,
|
|
|
|
- .next = fib_triestat_seq_next,
|
|
|
|
- .stop = fib_triestat_seq_stop,
|
|
|
|
- .show = fib_triestat_seq_show,
|
|
|
|
|
|
+static struct seq_operations fib_trie_seq_ops = {
|
|
|
|
+ .start = fib_trie_seq_start,
|
|
|
|
+ .next = fib_trie_seq_next,
|
|
|
|
+ .stop = fib_trie_seq_stop,
|
|
|
|
+ .show = fib_trie_seq_show,
|
|
};
|
|
};
|
|
|
|
|
|
-static int fib_triestat_seq_open(struct inode *inode, struct file *file)
|
|
|
|
|
|
+static int fib_trie_seq_open(struct inode *inode, struct file *file)
|
|
{
|
|
{
|
|
struct seq_file *seq;
|
|
struct seq_file *seq;
|
|
int rc = -ENOMEM;
|
|
int rc = -ENOMEM;
|
|
|
|
+ struct fib_trie_iter *s = kmalloc(sizeof(*s), GFP_KERNEL);
|
|
|
|
|
|
- rc = seq_open(file, &fib_triestat_seq_ops);
|
|
|
|
|
|
+ if (!s)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ rc = seq_open(file, &fib_trie_seq_ops);
|
|
if (rc)
|
|
if (rc)
|
|
goto out_kfree;
|
|
goto out_kfree;
|
|
|
|
|
|
- seq = file->private_data;
|
|
|
|
|
|
+ seq = file->private_data;
|
|
|
|
+ seq->private = s;
|
|
|
|
+ memset(s, 0, sizeof(*s));
|
|
out:
|
|
out:
|
|
return rc;
|
|
return rc;
|
|
out_kfree:
|
|
out_kfree:
|
|
|
|
+ kfree(s);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
-static struct file_operations fib_triestat_seq_fops = {
|
|
|
|
- .owner = THIS_MODULE,
|
|
|
|
- .open = fib_triestat_seq_open,
|
|
|
|
- .read = seq_read,
|
|
|
|
- .llseek = seq_lseek,
|
|
|
|
|
|
+static struct file_operations fib_trie_fops = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .open = fib_trie_seq_open,
|
|
|
|
+ .read = seq_read,
|
|
|
|
+ .llseek = seq_lseek,
|
|
.release = seq_release_private,
|
|
.release = seq_release_private,
|
|
};
|
|
};
|
|
|
|
|
|
-int __init fib_stat_proc_init(void)
|
|
|
|
-{
|
|
|
|
- if (!proc_net_fops_create("fib_triestat", S_IRUGO, &fib_triestat_seq_fops))
|
|
|
|
- return -ENOMEM;
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void __init fib_stat_proc_exit(void)
|
|
|
|
|
|
+static unsigned fib_flag_trans(int type, u32 mask, const struct fib_info *fi)
|
|
{
|
|
{
|
|
- proc_net_remove("fib_triestat");
|
|
|
|
-}
|
|
|
|
|
|
+ static unsigned type2flags[RTN_MAX + 1] = {
|
|
|
|
+ [7] = RTF_REJECT, [8] = RTF_REJECT,
|
|
|
|
+ };
|
|
|
|
+ unsigned flags = type2flags[type];
|
|
|
|
|
|
-static struct fib_alias *fib_trie_get_first(struct seq_file *seq)
|
|
|
|
-{
|
|
|
|
- return NULL;
|
|
|
|
|
|
+ if (fi && fi->fib_nh->nh_gw)
|
|
|
|
+ flags |= RTF_GATEWAY;
|
|
|
|
+ if (mask == 0xFFFFFFFF)
|
|
|
|
+ flags |= RTF_HOST;
|
|
|
|
+ flags |= RTF_UP;
|
|
|
|
+ return flags;
|
|
}
|
|
}
|
|
|
|
|
|
-static struct fib_alias *fib_trie_get_next(struct seq_file *seq)
|
|
|
|
|
|
+/*
|
|
|
|
+ * This outputs /proc/net/route.
|
|
|
|
+ * The format of the file is not supposed to be changed
|
|
|
|
+ * and needs to be same as fib_hash output to avoid breaking
|
|
|
|
+ * legacy utilities
|
|
|
|
+ */
|
|
|
|
+static int fib_route_seq_show(struct seq_file *seq, void *v)
|
|
{
|
|
{
|
|
- return NULL;
|
|
|
|
-}
|
|
|
|
|
|
+ struct leaf *l = v;
|
|
|
|
+ int i;
|
|
|
|
+ char bf[128];
|
|
|
|
|
|
-static void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos)
|
|
|
|
-{
|
|
|
|
- if (!ip_fib_main_table)
|
|
|
|
- return NULL;
|
|
|
|
|
|
+ if (v == SEQ_START_TOKEN) {
|
|
|
|
+ seq_printf(seq, "%-127s\n", "Iface\tDestination\tGateway "
|
|
|
|
+ "\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU"
|
|
|
|
+ "\tWindow\tIRTT");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
|
|
- if (*pos)
|
|
|
|
- return fib_trie_get_next(seq);
|
|
|
|
- else
|
|
|
|
- return SEQ_START_TOKEN;
|
|
|
|
-}
|
|
|
|
|
|
+ if (IS_TNODE(l))
|
|
|
|
+ return 0;
|
|
|
|
|
|
-static void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
|
|
-{
|
|
|
|
- ++*pos;
|
|
|
|
- if (v == SEQ_START_TOKEN)
|
|
|
|
- return fib_trie_get_first(seq);
|
|
|
|
- else
|
|
|
|
- return fib_trie_get_next(seq);
|
|
|
|
|
|
+ for (i=32; i>=0; i--) {
|
|
|
|
+ struct leaf_info *li = find_leaf_info(&l->list, i);
|
|
|
|
+ struct fib_alias *fa;
|
|
|
|
+ u32 mask, prefix;
|
|
|
|
|
|
-}
|
|
|
|
|
|
+ if (!li)
|
|
|
|
+ continue;
|
|
|
|
|
|
-static void fib_trie_seq_stop(struct seq_file *seq, void *v)
|
|
|
|
-{
|
|
|
|
-}
|
|
|
|
|
|
+ mask = inet_make_mask(li->plen);
|
|
|
|
+ prefix = htonl(l->key);
|
|
|
|
|
|
-/*
|
|
|
|
- * This outputs /proc/net/fib_trie.
|
|
|
|
- *
|
|
|
|
- * It always works in backward compatibility mode.
|
|
|
|
- * The format of the file is not supposed to be changed.
|
|
|
|
- */
|
|
|
|
|
|
+ list_for_each_entry_rcu(fa, &li->falh, fa_list) {
|
|
|
|
+ const struct fib_info *fi = rcu_dereference(fa->fa_info);
|
|
|
|
+ unsigned flags = fib_flag_trans(fa->fa_type, mask, fi);
|
|
|
|
|
|
-static int fib_trie_seq_show(struct seq_file *seq, void *v)
|
|
|
|
-{
|
|
|
|
- char bf[128];
|
|
|
|
|
|
+ if (fa->fa_type == RTN_BROADCAST
|
|
|
|
+ || fa->fa_type == RTN_MULTICAST)
|
|
|
|
+ continue;
|
|
|
|
|
|
- if (v == SEQ_START_TOKEN) {
|
|
|
|
- if (trie_local)
|
|
|
|
- trie_dump_seq(seq, trie_local);
|
|
|
|
|
|
+ if (fi)
|
|
|
|
+ snprintf(bf, sizeof(bf),
|
|
|
|
+ "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
|
|
|
|
+ fi->fib_dev ? fi->fib_dev->name : "*",
|
|
|
|
+ prefix,
|
|
|
|
+ fi->fib_nh->nh_gw, flags, 0, 0,
|
|
|
|
+ fi->fib_priority,
|
|
|
|
+ mask,
|
|
|
|
+ (fi->fib_advmss ? fi->fib_advmss + 40 : 0),
|
|
|
|
+ fi->fib_window,
|
|
|
|
+ fi->fib_rtt >> 3);
|
|
|
|
+ else
|
|
|
|
+ snprintf(bf, sizeof(bf),
|
|
|
|
+ "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
|
|
|
|
+ prefix, 0, flags, 0, 0, 0,
|
|
|
|
+ mask, 0, 0, 0);
|
|
|
|
|
|
- if (trie_main)
|
|
|
|
- trie_dump_seq(seq, trie_main);
|
|
|
|
- } else {
|
|
|
|
- snprintf(bf, sizeof(bf),
|
|
|
|
- "*\t%08X\t%08X", 200, 400);
|
|
|
|
- seq_printf(seq, "%-127s\n", bf);
|
|
|
|
|
|
+ seq_printf(seq, "%-127s\n", bf);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static struct seq_operations fib_trie_seq_ops = {
|
|
|
|
- .start = fib_trie_seq_start,
|
|
|
|
- .next = fib_trie_seq_next,
|
|
|
|
- .stop = fib_trie_seq_stop,
|
|
|
|
- .show = fib_trie_seq_show,
|
|
|
|
|
|
+static struct seq_operations fib_route_seq_ops = {
|
|
|
|
+ .start = fib_trie_seq_start,
|
|
|
|
+ .next = fib_trie_seq_next,
|
|
|
|
+ .stop = fib_trie_seq_stop,
|
|
|
|
+ .show = fib_route_seq_show,
|
|
};
|
|
};
|
|
|
|
|
|
-static int fib_trie_seq_open(struct inode *inode, struct file *file)
|
|
|
|
|
|
+static int fib_route_seq_open(struct inode *inode, struct file *file)
|
|
{
|
|
{
|
|
struct seq_file *seq;
|
|
struct seq_file *seq;
|
|
int rc = -ENOMEM;
|
|
int rc = -ENOMEM;
|
|
|
|
+ struct fib_trie_iter *s = kmalloc(sizeof(*s), GFP_KERNEL);
|
|
|
|
|
|
- rc = seq_open(file, &fib_trie_seq_ops);
|
|
|
|
|
|
+ if (!s)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ rc = seq_open(file, &fib_route_seq_ops);
|
|
if (rc)
|
|
if (rc)
|
|
goto out_kfree;
|
|
goto out_kfree;
|
|
|
|
|
|
- seq = file->private_data;
|
|
|
|
|
|
+ seq = file->private_data;
|
|
|
|
+ seq->private = s;
|
|
|
|
+ memset(s, 0, sizeof(*s));
|
|
out:
|
|
out:
|
|
return rc;
|
|
return rc;
|
|
out_kfree:
|
|
out_kfree:
|
|
|
|
+ kfree(s);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
-static struct file_operations fib_trie_seq_fops = {
|
|
|
|
- .owner = THIS_MODULE,
|
|
|
|
- .open = fib_trie_seq_open,
|
|
|
|
- .read = seq_read,
|
|
|
|
- .llseek = seq_lseek,
|
|
|
|
- .release= seq_release_private,
|
|
|
|
|
|
+static struct file_operations fib_route_fops = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .open = fib_route_seq_open,
|
|
|
|
+ .read = seq_read,
|
|
|
|
+ .llseek = seq_lseek,
|
|
|
|
+ .release = seq_release_private,
|
|
};
|
|
};
|
|
|
|
|
|
int __init fib_proc_init(void)
|
|
int __init fib_proc_init(void)
|
|
{
|
|
{
|
|
- if (!proc_net_fops_create("fib_trie", S_IRUGO, &fib_trie_seq_fops))
|
|
|
|
- return -ENOMEM;
|
|
|
|
|
|
+ if (!proc_net_fops_create("fib_trie", S_IRUGO, &fib_trie_fops))
|
|
|
|
+ goto out1;
|
|
|
|
+
|
|
|
|
+ if (!proc_net_fops_create("fib_triestat", S_IRUGO, &fib_triestat_fops))
|
|
|
|
+ goto out2;
|
|
|
|
+
|
|
|
|
+ if (!proc_net_fops_create("route", S_IRUGO, &fib_route_fops))
|
|
|
|
+ goto out3;
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
|
|
+
|
|
|
|
+out3:
|
|
|
|
+ proc_net_remove("fib_triestat");
|
|
|
|
+out2:
|
|
|
|
+ proc_net_remove("fib_trie");
|
|
|
|
+out1:
|
|
|
|
+ return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
|
|
void __init fib_proc_exit(void)
|
|
void __init fib_proc_exit(void)
|
|
{
|
|
{
|
|
proc_net_remove("fib_trie");
|
|
proc_net_remove("fib_trie");
|
|
|
|
+ proc_net_remove("fib_triestat");
|
|
|
|
+ proc_net_remove("route");
|
|
}
|
|
}
|
|
|
|
|
|
#endif /* CONFIG_PROC_FS */
|
|
#endif /* CONFIG_PROC_FS */
|