Commit 1af5a8c4 authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

[IPV4]: Increase number of possible routing tables to 2^32

Increase the number of possible routing tables to 2^32 by replacing the
fixed sized array of pointers by a hash table and replacing iterations
over all possible table IDs by hash table walking.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9e762a4a
...@@ -150,6 +150,7 @@ struct fib_result_nl { ...@@ -150,6 +150,7 @@ struct fib_result_nl {
#endif /* CONFIG_IP_ROUTE_MULTIPATH_WRANDOM */ #endif /* CONFIG_IP_ROUTE_MULTIPATH_WRANDOM */
struct fib_table { struct fib_table {
struct hlist_node tb_hlist;
u32 tb_id; u32 tb_id;
unsigned tb_stamp; unsigned tb_stamp;
int (*tb_lookup)(struct fib_table *tb, const struct flowi *flp, struct fib_result *res); int (*tb_lookup)(struct fib_table *tb, const struct flowi *flp, struct fib_result *res);
...@@ -200,29 +201,13 @@ static inline void fib_select_default(const struct flowi *flp, struct fib_result ...@@ -200,29 +201,13 @@ static inline void fib_select_default(const struct flowi *flp, struct fib_result
} }
#else /* CONFIG_IP_MULTIPLE_TABLES */ #else /* CONFIG_IP_MULTIPLE_TABLES */
#define ip_fib_local_table (fib_tables[RT_TABLE_LOCAL]) #define ip_fib_local_table fib_get_table(RT_TABLE_LOCAL)
#define ip_fib_main_table (fib_tables[RT_TABLE_MAIN]) #define ip_fib_main_table fib_get_table(RT_TABLE_MAIN)
extern struct fib_table * fib_tables[RT_TABLE_MAX+1];
extern int fib_lookup(struct flowi *flp, struct fib_result *res); extern int fib_lookup(struct flowi *flp, struct fib_result *res);
extern struct fib_table *__fib_new_table(u32 id);
static inline struct fib_table *fib_get_table(u32 id)
{
if (id == 0)
id = RT_TABLE_MAIN;
return fib_tables[id];
}
static inline struct fib_table *fib_new_table(u32 id)
{
if (id == 0)
id = RT_TABLE_MAIN;
return fib_tables[id] ? : __fib_new_table(id);
}
extern struct fib_table *fib_new_table(u32 id);
extern struct fib_table *fib_get_table(u32 id);
extern void fib_select_default(const struct flowi *flp, struct fib_result *res); extern void fib_select_default(const struct flowi *flp, struct fib_result *res);
#endif /* CONFIG_IP_MULTIPLE_TABLES */ #endif /* CONFIG_IP_MULTIPLE_TABLES */
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/list.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/protocol.h> #include <net/protocol.h>
...@@ -51,48 +52,67 @@ ...@@ -51,48 +52,67 @@
#ifndef CONFIG_IP_MULTIPLE_TABLES #ifndef CONFIG_IP_MULTIPLE_TABLES
#define RT_TABLE_MIN RT_TABLE_MAIN
struct fib_table *ip_fib_local_table; struct fib_table *ip_fib_local_table;
struct fib_table *ip_fib_main_table; struct fib_table *ip_fib_main_table;
#else #define FIB_TABLE_HASHSZ 1
static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
#define RT_TABLE_MIN 1 #else
struct fib_table *fib_tables[RT_TABLE_MAX+1]; #define FIB_TABLE_HASHSZ 256
static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
struct fib_table *__fib_new_table(u32 id) struct fib_table *fib_new_table(u32 id)
{ {
struct fib_table *tb; struct fib_table *tb;
unsigned int h;
if (id == 0)
id = RT_TABLE_MAIN;
tb = fib_get_table(id);
if (tb)
return tb;
tb = fib_hash_init(id); tb = fib_hash_init(id);
if (!tb) if (!tb)
return NULL; return NULL;
fib_tables[id] = tb; h = id & (FIB_TABLE_HASHSZ - 1);
hlist_add_head_rcu(&tb->tb_hlist, &fib_table_hash[h]);
return tb; return tb;
} }
struct fib_table *fib_get_table(u32 id)
{
struct fib_table *tb;
struct hlist_node *node;
unsigned int h;
if (id == 0)
id = RT_TABLE_MAIN;
h = id & (FIB_TABLE_HASHSZ - 1);
rcu_read_lock();
hlist_for_each_entry_rcu(tb, node, &fib_table_hash[h], tb_hlist) {
if (tb->tb_id == id) {
rcu_read_unlock();
return tb;
}
}
rcu_read_unlock();
return NULL;
}
#endif /* CONFIG_IP_MULTIPLE_TABLES */ #endif /* CONFIG_IP_MULTIPLE_TABLES */
static void fib_flush(void) static void fib_flush(void)
{ {
int flushed = 0; int flushed = 0;
#ifdef CONFIG_IP_MULTIPLE_TABLES
struct fib_table *tb; struct fib_table *tb;
u32 id; struct hlist_node *node;
unsigned int h;
for (id = RT_TABLE_MAX; id>0; id--) { for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
if ((tb = fib_get_table(id))==NULL) hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist)
continue; flushed += tb->tb_flush(tb);
flushed += tb->tb_flush(tb);
} }
#else /* CONFIG_IP_MULTIPLE_TABLES */
flushed += ip_fib_main_table->tb_flush(ip_fib_main_table);
flushed += ip_fib_local_table->tb_flush(ip_fib_local_table);
#endif /* CONFIG_IP_MULTIPLE_TABLES */
if (flushed) if (flushed)
rt_cache_flush(-1); rt_cache_flush(-1);
...@@ -334,29 +354,37 @@ int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) ...@@ -334,29 +354,37 @@ int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
{ {
u32 t; unsigned int h, s_h;
u32 s_t; unsigned int e = 0, s_e;
struct fib_table *tb; struct fib_table *tb;
struct hlist_node *node;
int dumped = 0;
if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) && if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
((struct rtmsg*)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED) ((struct rtmsg*)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)
return ip_rt_dump(skb, cb); return ip_rt_dump(skb, cb);
s_t = cb->args[0]; s_h = cb->args[0];
if (s_t == 0) s_e = cb->args[1];
s_t = cb->args[0] = RT_TABLE_MIN;
for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
for (t=s_t; t<=RT_TABLE_MAX; t++) { e = 0;
if (t < s_t) continue; hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist) {
if (t > s_t) if (e < s_e)
memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); goto next;
if ((tb = fib_get_table(t))==NULL) if (dumped)
continue; memset(&cb->args[2], 0, sizeof(cb->args) -
if (tb->tb_dump(tb, skb, cb) < 0) 2 * sizeof(cb->args[0]));
break; if (tb->tb_dump(tb, skb, cb) < 0)
goto out;
dumped = 1;
next:
e++;
}
} }
out:
cb->args[0] = t; cb->args[1] = e;
cb->args[0] = h;
return skb->len; return skb->len;
} }
...@@ -654,9 +682,15 @@ static struct notifier_block fib_netdev_notifier = { ...@@ -654,9 +682,15 @@ static struct notifier_block fib_netdev_notifier = {
void __init ip_fib_init(void) void __init ip_fib_init(void)
{ {
unsigned int i;
for (i = 0; i < FIB_TABLE_HASHSZ; i++)
INIT_HLIST_HEAD(&fib_table_hash[i]);
#ifndef CONFIG_IP_MULTIPLE_TABLES #ifndef CONFIG_IP_MULTIPLE_TABLES
ip_fib_local_table = fib_hash_init(RT_TABLE_LOCAL); ip_fib_local_table = fib_hash_init(RT_TABLE_LOCAL);
hlist_add_head_rcu(&ip_fib_local_table->tb_hlist, &fib_table_hash[0]);
ip_fib_main_table = fib_hash_init(RT_TABLE_MAIN); ip_fib_main_table = fib_hash_init(RT_TABLE_MAIN);
hlist_add_head_rcu(&ip_fib_main_table->tb_hlist, &fib_table_hash[0]);
#else #else
fib4_rules_init(); fib4_rules_init();
#endif #endif
......
...@@ -684,7 +684,7 @@ fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -684,7 +684,7 @@ fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb,
struct fib_node *f; struct fib_node *f;
int i, s_i; int i, s_i;
s_i = cb->args[3]; s_i = cb->args[4];
i = 0; i = 0;
hlist_for_each_entry(f, node, head, fn_hash) { hlist_for_each_entry(f, node, head, fn_hash) {
struct fib_alias *fa; struct fib_alias *fa;
...@@ -704,14 +704,14 @@ fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -704,14 +704,14 @@ fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb,
fa->fa_tos, fa->fa_tos,
fa->fa_info, fa->fa_info,
NLM_F_MULTI) < 0) { NLM_F_MULTI) < 0) {
cb->args[3] = i; cb->args[4] = i;
return -1; return -1;
} }
next: next:
i++; i++;
} }
} }
cb->args[3] = i; cb->args[4] = i;
return skb->len; return skb->len;
} }
...@@ -722,21 +722,21 @@ fn_hash_dump_zone(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -722,21 +722,21 @@ fn_hash_dump_zone(struct sk_buff *skb, struct netlink_callback *cb,
{ {
int h, s_h; int h, s_h;
s_h = cb->args[2]; s_h = cb->args[3];
for (h=0; h < fz->fz_divisor; h++) { for (h=0; h < fz->fz_divisor; h++) {
if (h < s_h) continue; if (h < s_h) continue;
if (h > s_h) if (h > s_h)
memset(&cb->args[3], 0, memset(&cb->args[4], 0,
sizeof(cb->args) - 3*sizeof(cb->args[0])); sizeof(cb->args) - 4*sizeof(cb->args[0]));
if (fz->fz_hash == NULL || if (fz->fz_hash == NULL ||
hlist_empty(&fz->fz_hash[h])) hlist_empty(&fz->fz_hash[h]))
continue; continue;
if (fn_hash_dump_bucket(skb, cb, tb, fz, &fz->fz_hash[h])<0) { if (fn_hash_dump_bucket(skb, cb, tb, fz, &fz->fz_hash[h])<0) {
cb->args[2] = h; cb->args[3] = h;
return -1; return -1;
} }
} }
cb->args[2] = h; cb->args[3] = h;
return skb->len; return skb->len;
} }
...@@ -746,21 +746,21 @@ static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin ...@@ -746,21 +746,21 @@ static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin
struct fn_zone *fz; struct fn_zone *fz;
struct fn_hash *table = (struct fn_hash*)tb->tb_data; struct fn_hash *table = (struct fn_hash*)tb->tb_data;
s_m = cb->args[1]; s_m = cb->args[2];
read_lock(&fib_hash_lock); read_lock(&fib_hash_lock);
for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) { for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) {
if (m < s_m) continue; if (m < s_m) continue;
if (m > s_m) if (m > s_m)
memset(&cb->args[2], 0, memset(&cb->args[3], 0,
sizeof(cb->args) - 2*sizeof(cb->args[0])); sizeof(cb->args) - 3*sizeof(cb->args[0]));
if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) { if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) {
cb->args[1] = m; cb->args[2] = m;
read_unlock(&fib_hash_lock); read_unlock(&fib_hash_lock);
return -1; return -1;
} }
} }
read_unlock(&fib_hash_lock); read_unlock(&fib_hash_lock);
cb->args[1] = m; cb->args[2] = m;
return skb->len; return skb->len;
} }
......
...@@ -172,8 +172,8 @@ static struct fib_table *fib_empty_table(void) ...@@ -172,8 +172,8 @@ static struct fib_table *fib_empty_table(void)
u32 id; u32 id;
for (id = 1; id <= RT_TABLE_MAX; id++) for (id = 1; id <= RT_TABLE_MAX; id++)
if (fib_tables[id] == NULL) if (fib_get_table(id) == NULL)
return __fib_new_table(id); return fib_new_table(id);
return NULL; return NULL;
} }
......
...@@ -1848,7 +1848,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, struct fi ...@@ -1848,7 +1848,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, struct fi
u32 xkey = htonl(key); u32 xkey = htonl(key);
s_i = cb->args[3]; s_i = cb->args[4];
i = 0; i = 0;
/* rcu_read_lock is hold by caller */ /* rcu_read_lock is hold by caller */
...@@ -1870,12 +1870,12 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, struct fi ...@@ -1870,12 +1870,12 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, struct fi
plen, plen,
fa->fa_tos, fa->fa_tos,
fa->fa_info, 0) < 0) { fa->fa_info, 0) < 0) {
cb->args[3] = i; cb->args[4] = i;
return -1; return -1;
} }
i++; i++;
} }
cb->args[3] = i; cb->args[4] = i;
return skb->len; return skb->len;
} }
...@@ -1886,14 +1886,14 @@ static int fn_trie_dump_plen(struct trie *t, int plen, struct fib_table *tb, str ...@@ -1886,14 +1886,14 @@ static int fn_trie_dump_plen(struct trie *t, int plen, struct fib_table *tb, str
struct list_head *fa_head; struct list_head *fa_head;
struct leaf *l = NULL; struct leaf *l = NULL;
s_h = cb->args[2]; s_h = cb->args[3];
for (h = 0; (l = nextleaf(t, l)) != NULL; h++) { for (h = 0; (l = nextleaf(t, l)) != NULL; h++) {
if (h < s_h) if (h < s_h)
continue; continue;
if (h > s_h) if (h > s_h)
memset(&cb->args[3], 0, memset(&cb->args[4], 0,
sizeof(cb->args) - 3*sizeof(cb->args[0])); sizeof(cb->args) - 4*sizeof(cb->args[0]));
fa_head = get_fa_head(l, plen); fa_head = get_fa_head(l, plen);
...@@ -1904,11 +1904,11 @@ static int fn_trie_dump_plen(struct trie *t, int plen, struct fib_table *tb, str ...@@ -1904,11 +1904,11 @@ static int fn_trie_dump_plen(struct trie *t, int plen, struct fib_table *tb, str
continue; continue;
if (fn_trie_dump_fa(l->key, plen, fa_head, tb, skb, cb)<0) { if (fn_trie_dump_fa(l->key, plen, fa_head, tb, skb, cb)<0) {
cb->args[2] = h; cb->args[3] = h;
return -1; return -1;
} }
} }
cb->args[2] = h; cb->args[3] = h;
return skb->len; return skb->len;
} }
...@@ -1917,23 +1917,23 @@ static int fn_trie_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin ...@@ -1917,23 +1917,23 @@ static int fn_trie_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin
int m, s_m; int m, s_m;
struct trie *t = (struct trie *) tb->tb_data; struct trie *t = (struct trie *) tb->tb_data;
s_m = cb->args[1]; s_m = cb->args[2];
rcu_read_lock(); rcu_read_lock();
for (m = 0; m <= 32; m++) { for (m = 0; m <= 32; m++) {
if (m < s_m) if (m < s_m)
continue; continue;
if (m > s_m) if (m > s_m)
memset(&cb->args[2], 0, memset(&cb->args[3], 0,
sizeof(cb->args) - 2*sizeof(cb->args[0])); sizeof(cb->args) - 3*sizeof(cb->args[0]));
if (fn_trie_dump_plen(t, 32-m, tb, skb, cb)<0) { if (fn_trie_dump_plen(t, 32-m, tb, skb, cb)<0) {
cb->args[1] = m; cb->args[2] = m;
goto out; goto out;
} }
} }
rcu_read_unlock(); rcu_read_unlock();
cb->args[1] = m; cb->args[2] = m;
return skb->len; return skb->len;
out: out:
rcu_read_unlock(); rcu_read_unlock();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment