#include <net/ip_fib.h>
#include "fib_lookup.h"
-#undef CONFIG_IP_FIB_TRIE_STATS
#define MAX_STAT_DEPTH 32
#define KEYLENGTH (8*sizeof(t_key))
#define IS_LEAF(n) (n->parent & T_LEAF)
struct node {
- t_key key;
unsigned long parent;
+ t_key key;
};
struct leaf {
- t_key key;
unsigned long parent;
+ t_key key;
struct hlist_head list;
struct rcu_head rcu;
};
};
struct tnode {
- t_key key;
unsigned long parent;
- unsigned short pos:5; /* 2log(KEYLENGTH) bits needed */
- unsigned short bits:5; /* 2log(KEYLENGTH) bits needed */
- unsigned short full_children; /* KEYLENGTH bits needed */
- unsigned short empty_children; /* KEYLENGTH bits needed */
+ t_key key;
+ unsigned char pos; /* 2log(KEYLENGTH) bits needed */
+ unsigned char bits; /* 2log(KEYLENGTH) bits needed */
+ unsigned int full_children; /* KEYLENGTH bits needed */
+ unsigned int empty_children; /* KEYLENGTH bits needed */
struct rcu_head rcu;
struct node *child[0];
};
struct trie_use_stats stats;
#endif
int size;
- unsigned int revision;
};
static void put_child(struct trie *t, struct tnode *tn, int i, struct node *n);
WARN_ON(tn && tn->pos+tn->bits > 32);
}
-static int halve_threshold = 25;
-static int inflate_threshold = 50;
-static int halve_threshold_root = 8;
-static int inflate_threshold_root = 15;
+static const int halve_threshold = 25;
+static const int inflate_threshold = 50;
+static const int halve_threshold_root = 8;
+static const int inflate_threshold_root = 15;
static void __alias_free_mem(struct rcu_head *head)
call_rcu(&leaf->rcu, __leaf_info_free_rcu);
}
-static struct tnode *tnode_alloc(unsigned int size)
+static struct tnode *tnode_alloc(size_t size)
{
struct page *pages;
if (size <= PAGE_SIZE)
- return kcalloc(size, 1, GFP_KERNEL);
+ return kzalloc(size, GFP_KERNEL);
pages = alloc_pages(GFP_KERNEL|__GFP_ZERO, get_order(size));
if (!pages)
static void __tnode_free_rcu(struct rcu_head *head)
{
struct tnode *tn = container_of(head, struct tnode, rcu);
- unsigned int size = sizeof(struct tnode) +
- (1 << tn->bits) * sizeof(struct node *);
+ size_t size = sizeof(struct tnode) +
+ (sizeof(struct node *) << tn->bits);
if (size <= PAGE_SIZE)
kfree(tn);
static struct tnode* tnode_new(t_key key, int pos, int bits)
{
- int nchildren = 1<<bits;
- int sz = sizeof(struct tnode) + nchildren * sizeof(struct node *);
+ size_t sz = sizeof(struct tnode) + (sizeof(struct node *) << bits);
struct tnode *tn = tnode_alloc(sz);
if (tn) {
- memset(tn, 0, sz);
tn->parent = T_TNODE;
tn->pos = pos;
tn->bits = bits;
tn->empty_children = 1<<bits;
}
- pr_debug("AT %p s=%u %u\n", tn, (unsigned int) sizeof(struct tnode),
- (unsigned int) (sizeof(struct node) * 1<<bits));
+ pr_debug("AT %p s=%u %lu\n", tn, (unsigned int) sizeof(struct tnode),
+ (unsigned long) (sizeof(struct node) << bits));
return tn;
}
static struct tnode *inflate(struct trie *t, struct tnode *tn)
{
- struct tnode *inode;
struct tnode *oldtnode = tn;
int olen = tnode_child_length(tn);
int i;
}
for (i = 0; i < olen; i++) {
+ struct tnode *inode;
struct node *node = tnode_get_child(oldtnode, i);
struct tnode *left, *right;
int size, j;
}
}
-static void trie_init(struct trie *t)
-{
- if (!t)
- return;
-
- t->size = 0;
- rcu_assign_pointer(t->trie, NULL);
- t->revision = 0;
-#ifdef CONFIG_IP_FIB_TRIE_STATS
- memset(&t->stats, 0, sizeof(struct trie_use_stats));
-#endif
-}
-
/* readside must use rcu_read_lock currently dump routines
via get_fa_head and dump */
/* only used from updater-side */
-static struct list_head *
-fib_insert_node(struct trie *t, int *err, u32 key, int plen)
+static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
{
int pos, newpos;
struct tnode *tp = NULL, *tn = NULL;
/* Case 1: n is a leaf. Compare prefixes */
if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) {
- struct leaf *l = (struct leaf *) n;
-
+ l = (struct leaf *) n;
li = leaf_info_new(plen);
- if (!li) {
- *err = -ENOMEM;
- goto err;
- }
+ if (!li)
+ return NULL;
fa_head = &li->falh;
insert_leaf_info(&l->list, li);
t->size++;
l = leaf_new();
- if (!l) {
- *err = -ENOMEM;
- goto err;
- }
+ if (!l)
+ return NULL;
l->key = key;
li = leaf_info_new(plen);
if (!li) {
tnode_free((struct tnode *) l);
- *err = -ENOMEM;
- goto err;
+ return NULL;
}
fa_head = &li->falh;
if (!tn) {
free_leaf_info(li);
tnode_free((struct tnode *) l);
- *err = -ENOMEM;
- goto err;
+ return NULL;
}
node_set_parent((struct node *)tn, tp);
rcu_assign_pointer(t->trie, trie_rebalance(t, tp));
done:
- t->revision++;
-err:
return fa_head;
}
*/
if (!fa_head) {
- err = 0;
- fa_head = fib_insert_node(t, &err, key, plen);
- if (err)
+ fa_head = fib_insert_node(t, key, plen);
+ if (unlikely(!fa_head)) {
+ err = -ENOMEM;
goto out_free_new_fa;
+ }
}
list_add_tail_rcu(&new_fa->fa_list,
* Remove the leaf and rebalance the tree
*/
- t->revision++;
t->size--;
tp = node_parent(n);
struct leaf *ll = NULL, *l = NULL;
int found = 0, h;
- t->revision++;
-
for (h = 0; (l = nextleaf(t, l)) != NULL; h++) {
found += trie_flush_leaf(t, l);
return found;
}
-static int trie_last_dflt = -1;
-
static void
fn_trie_select_default(struct fib_table *tb, const struct flowi *flp, struct fib_result *res)
{
if (next_fi != res->fi)
break;
} else if (!fib_detect_death(fi, order, &last_resort,
- &last_idx, trie_last_dflt)) {
+ &last_idx, tb->tb_default)) {
fib_result_assign(res, fi);
- trie_last_dflt = order;
+ tb->tb_default = order;
goto out;
}
fi = next_fi;
order++;
}
if (order <= 0 || fi == NULL) {
- trie_last_dflt = -1;
+ tb->tb_default = -1;
goto out;
}
- if (!fib_detect_death(fi, order, &last_resort, &last_idx, trie_last_dflt)) {
+ if (!fib_detect_death(fi, order, &last_resort, &last_idx,
+ tb->tb_default)) {
fib_result_assign(res, fi);
- trie_last_dflt = order;
+ tb->tb_default = order;
goto out;
}
if (last_idx >= 0)
fib_result_assign(res, last_resort);
- trie_last_dflt = last_idx;
- out:;
+ tb->tb_default = last_idx;
+out:
rcu_read_unlock();
}
/* Fix more generic FIB names for init later */
-#ifdef CONFIG_IP_MULTIPLE_TABLES
-struct fib_table * fib_hash_init(u32 id)
-#else
-struct fib_table * __init fib_hash_init(u32 id)
-#endif
+struct fib_table *fib_hash_init(u32 id)
{
struct fib_table *tb;
struct trie *t;
return NULL;
tb->tb_id = id;
+ tb->tb_default = -1;
tb->tb_lookup = fn_trie_lookup;
tb->tb_insert = fn_trie_insert;
tb->tb_delete = fn_trie_delete;
tb->tb_flush = fn_trie_flush;
tb->tb_select_default = fn_trie_select_default;
tb->tb_dump = fn_trie_dump;
- memset(tb->tb_data, 0, sizeof(struct trie));
t = (struct trie *) tb->tb_data;
-
- trie_init(t);
+ memset(t, 0, sizeof(*t));
if (id == RT_TABLE_LOCAL)
printk(KERN_INFO "IPv4 FIB: Using LC-trie version %s\n", VERSION);
#ifdef CONFIG_PROC_FS
/* Depth first Trie walk iterator */
struct fib_trie_iter {
+ struct seq_net_private p;
struct trie *trie_local, *trie_main;
struct tnode *tnode;
struct trie *trie;
else
avdepth = 0;
- seq_printf(seq, "\tAver depth: %d.%02d\n", avdepth / 100, avdepth % 100 );
+ seq_printf(seq, "\tAver depth: %u.%02d\n", avdepth / 100, avdepth % 100 );
seq_printf(seq, "\tMax depth: %u\n", stat->maxdepth);
seq_printf(seq, "\tLeaves: %u\n", stat->leaves);
bytes = sizeof(struct leaf) * stat->leaves;
- seq_printf(seq, "\tInternal nodes: %d\n\t", stat->tnodes);
+ seq_printf(seq, "\tInternal nodes: %u\n\t", stat->tnodes);
bytes += sizeof(struct tnode) * stat->tnodes;
max = MAX_STAT_DEPTH;
pointers = 0;
for (i = 1; i <= max; i++)
if (stat->nodesizes[i] != 0) {
- seq_printf(seq, " %d: %d", i, stat->nodesizes[i]);
+ seq_printf(seq, " %u: %u", i, stat->nodesizes[i]);
pointers += (1<<i) * stat->nodesizes[i];
}
seq_putc(seq, '\n');
- seq_printf(seq, "\tPointers: %d\n", pointers);
+ seq_printf(seq, "\tPointers: %u\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 + 1023) / 1024);
+ seq_printf(seq, "Null ptrs: %u\n", stat->nullpointers);
+ seq_printf(seq, "Total size: %u kB\n", (bytes + 1023) / 1024);
+}
#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 */
+static void trie_show_usage(struct seq_file *seq,
+ const struct trie_use_stats *stats)
+{
+ seq_printf(seq, "\nCounters:\n---------\n");
+ seq_printf(seq,"gets = %u\n", stats->gets);
+ seq_printf(seq,"backtracks = %u\n", stats->backtrack);
+ seq_printf(seq,"semantic match passed = %u\n", stats->semantic_match_passed);
+ seq_printf(seq,"semantic match miss = %u\n", stats->semantic_match_miss);
+ seq_printf(seq,"null node hit= %u\n", stats->null_node_hit);
+ seq_printf(seq,"skipped node resize = %u\n\n", stats->resize_node_skipped);
}
+#endif /* CONFIG_IP_FIB_TRIE_STATS */
+
static int fib_triestat_seq_show(struct seq_file *seq, void *v)
{
+ struct net *net = (struct net *)seq->private;
struct trie *trie_local, *trie_main;
struct trie_stat *stat;
struct fib_table *tb;
trie_local = NULL;
- tb = fib_get_table(RT_TABLE_LOCAL);
+ tb = fib_get_table(net, RT_TABLE_LOCAL);
if (tb)
trie_local = (struct trie *) tb->tb_data;
trie_main = NULL;
- tb = fib_get_table(RT_TABLE_MAIN);
+ tb = fib_get_table(net, RT_TABLE_MAIN);
if (tb)
trie_main = (struct trie *) tb->tb_data;
seq_printf(seq, "Local:\n");
trie_collect_stats(trie_local, stat);
trie_show_stats(seq, stat);
+#ifdef CONFIG_IP_FIB_TRIE_STATS
+ trie_show_usage(seq, &trie_local->stats);
+#endif
}
if (trie_main) {
seq_printf(seq, "Main:\n");
trie_collect_stats(trie_main, stat);
trie_show_stats(seq, stat);
+#ifdef CONFIG_IP_FIB_TRIE_STATS
+ trie_show_usage(seq, &trie_main->stats);
+#endif
}
kfree(stat);
static int fib_triestat_seq_open(struct inode *inode, struct file *file)
{
- return single_open(file, fib_triestat_seq_show, NULL);
+ int err;
+ struct net *net;
+
+ net = get_proc_net(inode);
+ if (net == NULL)
+ return -ENXIO;
+ err = single_open(file, fib_triestat_seq_show, net);
+ if (err < 0) {
+ put_net(net);
+ return err;
+ }
+ return 0;
+}
+
+static int fib_triestat_seq_release(struct inode *ino, struct file *f)
+{
+ struct seq_file *seq = f->private_data;
+ put_net(seq->private);
+ return single_release(ino, f);
}
static const struct file_operations fib_triestat_fops = {
.open = fib_triestat_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = single_release,
+ .release = fib_triestat_seq_release,
};
static struct node *fib_trie_get_idx(struct fib_trie_iter *iter,
}
static void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos)
+ __acquires(RCU)
{
struct fib_trie_iter *iter = seq->private;
struct fib_table *tb;
if (!iter->trie_local) {
- tb = fib_get_table(RT_TABLE_LOCAL);
+ tb = fib_get_table(iter->p.net, RT_TABLE_LOCAL);
if (tb)
iter->trie_local = (struct trie *) tb->tb_data;
}
if (!iter->trie_main) {
- tb = fib_get_table(RT_TABLE_MAIN);
+ tb = fib_get_table(iter->p.net, RT_TABLE_MAIN);
if (tb)
iter->trie_main = (struct trie *) tb->tb_data;
}
}
static void fib_trie_seq_stop(struct seq_file *seq, void *v)
+ __releases(RCU)
{
rcu_read_unlock();
}
while (n-- > 0) seq_puts(seq, " ");
}
-static inline const char *rtn_scope(enum rt_scope_t s)
+static inline const char *rtn_scope(char *buf, size_t len, enum rt_scope_t s)
{
- static char buf[32];
-
switch (s) {
case RT_SCOPE_UNIVERSE: return "universe";
case RT_SCOPE_SITE: return "site";
case RT_SCOPE_HOST: return "host";
case RT_SCOPE_NOWHERE: return "nowhere";
default:
- snprintf(buf, sizeof(buf), "scope=%d", s);
+ snprintf(buf, len, "scope=%d", s);
return buf;
}
}
[RTN_XRESOLVE] = "XRESOLVE",
};
-static inline const char *rtn_type(unsigned t)
+static inline const char *rtn_type(char *buf, size_t len, unsigned t)
{
- static char buf[32];
-
if (t < __RTN_MAX && rtn_type_names[t])
return rtn_type_names[t];
- snprintf(buf, sizeof(buf), "type %d", t);
+ snprintf(buf, len, "type %u", t);
return buf;
}
seq_printf(seq, " |-- %d.%d.%d.%d\n", NIPQUAD(val));
for (i = 32; i >= 0; i--) {
struct leaf_info *li = find_leaf_info(l, i);
+
if (li) {
struct fib_alias *fa;
+
list_for_each_entry_rcu(fa, &li->falh, fa_list) {
+ char buf1[32], buf2[32];
+
seq_indent(seq, iter->depth+1);
seq_printf(seq, " /%d %s %s", i,
- rtn_scope(fa->fa_scope),
- rtn_type(fa->fa_type));
+ rtn_scope(buf1, sizeof(buf1),
+ fa->fa_scope),
+ rtn_type(buf2, sizeof(buf2),
+ fa->fa_type));
if (fa->fa_tos)
seq_printf(seq, "tos =%d\n",
fa->fa_tos);
static int fib_trie_seq_open(struct inode *inode, struct file *file)
{
- return seq_open_private(file, &fib_trie_seq_ops,
- sizeof(struct fib_trie_iter));
+ return seq_open_net(inode, file, &fib_trie_seq_ops,
+ sizeof(struct fib_trie_iter));
}
static const struct file_operations fib_trie_fops = {
.open = fib_trie_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
static unsigned fib_flag_trans(int type, __be32 mask, const struct fib_info *fi)
static int fib_route_seq_open(struct inode *inode, struct file *file)
{
- return seq_open_private(file, &fib_route_seq_ops,
- sizeof(struct fib_trie_iter));
+ return seq_open_net(inode, file, &fib_route_seq_ops,
+ sizeof(struct fib_trie_iter));
}
static const struct file_operations fib_route_fops = {
.open = fib_route_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
-int __init fib_proc_init(void)
+int __net_init fib_proc_init(struct net *net)
{
- if (!proc_net_fops_create(&init_net, "fib_trie", S_IRUGO, &fib_trie_fops))
+ if (!proc_net_fops_create(net, "fib_trie", S_IRUGO, &fib_trie_fops))
goto out1;
- if (!proc_net_fops_create(&init_net, "fib_triestat", S_IRUGO, &fib_triestat_fops))
+ if (!proc_net_fops_create(net, "fib_triestat", S_IRUGO,
+ &fib_triestat_fops))
goto out2;
- if (!proc_net_fops_create(&init_net, "route", S_IRUGO, &fib_route_fops))
+ if (!proc_net_fops_create(net, "route", S_IRUGO, &fib_route_fops))
goto out3;
return 0;
out3:
- proc_net_remove(&init_net, "fib_triestat");
+ proc_net_remove(net, "fib_triestat");
out2:
- proc_net_remove(&init_net, "fib_trie");
+ proc_net_remove(net, "fib_trie");
out1:
return -ENOMEM;
}
-void __init fib_proc_exit(void)
+void __net_exit fib_proc_exit(struct net *net)
{
- proc_net_remove(&init_net, "fib_trie");
- proc_net_remove(&init_net, "fib_triestat");
- proc_net_remove(&init_net, "route");
+ proc_net_remove(net, "fib_trie");
+ proc_net_remove(net, "fib_triestat");
+ proc_net_remove(net, "route");
}
#endif /* CONFIG_PROC_FS */