[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: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									9e762a4a89
								
							
						
					
					
						commit
						1af5a8c4a1
					
				| @ -150,6 +150,7 @@ struct fib_result_nl { | ||||
| #endif /* CONFIG_IP_ROUTE_MULTIPATH_WRANDOM */ | ||||
| 
 | ||||
| struct fib_table { | ||||
| 	struct hlist_node tb_hlist; | ||||
| 	u32		tb_id; | ||||
| 	unsigned	tb_stamp; | ||||
| 	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 | ||||
| } | ||||
| 
 | ||||
| #else /* CONFIG_IP_MULTIPLE_TABLES */ | ||||
| #define ip_fib_local_table (fib_tables[RT_TABLE_LOCAL]) | ||||
| #define ip_fib_main_table (fib_tables[RT_TABLE_MAIN]) | ||||
| #define ip_fib_local_table fib_get_table(RT_TABLE_LOCAL) | ||||
| #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 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); | ||||
| 
 | ||||
| #endif /* CONFIG_IP_MULTIPLE_TABLES */ | ||||
|  | ||||
| @ -37,6 +37,7 @@ | ||||
| #include <linux/skbuff.h> | ||||
| #include <linux/netlink.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/list.h> | ||||
| 
 | ||||
| #include <net/ip.h> | ||||
| #include <net/protocol.h> | ||||
| @ -51,48 +52,67 @@ | ||||
| 
 | ||||
| #ifndef CONFIG_IP_MULTIPLE_TABLES | ||||
| 
 | ||||
| #define RT_TABLE_MIN RT_TABLE_MAIN | ||||
| 
 | ||||
| struct fib_table *ip_fib_local_table; | ||||
| struct fib_table *ip_fib_main_table; | ||||
| 
 | ||||
| #define FIB_TABLE_HASHSZ 1 | ||||
| static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ]; | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| #define RT_TABLE_MIN 1 | ||||
| #define FIB_TABLE_HASHSZ 256 | ||||
| static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ]; | ||||
| 
 | ||||
| struct fib_table *fib_tables[RT_TABLE_MAX+1]; | ||||
| 
 | ||||
| struct fib_table *__fib_new_table(u32 id) | ||||
| struct fib_table *fib_new_table(u32 id) | ||||
| { | ||||
| 	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); | ||||
| 	if (!tb) | ||||
| 		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; | ||||
| } | ||||
| 
 | ||||
| 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 */ | ||||
| 
 | ||||
| 
 | ||||
| static void fib_flush(void) | ||||
| { | ||||
| 	int flushed = 0; | ||||
| #ifdef CONFIG_IP_MULTIPLE_TABLES | ||||
| 	struct fib_table *tb; | ||||
| 	u32 id; | ||||
| 	struct hlist_node *node; | ||||
| 	unsigned int h; | ||||
| 
 | ||||
| 	for (id = RT_TABLE_MAX; id>0; id--) { | ||||
| 		if ((tb = fib_get_table(id))==NULL) | ||||
| 			continue; | ||||
| 		flushed += tb->tb_flush(tb); | ||||
| 	for (h = 0; h < FIB_TABLE_HASHSZ; h++) { | ||||
| 		hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist) | ||||
| 			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) | ||||
| 		rt_cache_flush(-1); | ||||
| @ -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) | ||||
| { | ||||
| 	u32 t; | ||||
| 	u32 s_t; | ||||
| 	unsigned int h, s_h; | ||||
| 	unsigned int e = 0, s_e; | ||||
| 	struct fib_table *tb; | ||||
| 	struct hlist_node *node; | ||||
| 	int dumped = 0; | ||||
| 
 | ||||
| 	if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) && | ||||
| 	    ((struct rtmsg*)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED) | ||||
| 		return ip_rt_dump(skb, cb); | ||||
| 
 | ||||
| 	s_t = cb->args[0]; | ||||
| 	if (s_t == 0) | ||||
| 		s_t = cb->args[0] = RT_TABLE_MIN; | ||||
| 	s_h = cb->args[0]; | ||||
| 	s_e = cb->args[1]; | ||||
| 
 | ||||
| 	for (t=s_t; t<=RT_TABLE_MAX; t++) { | ||||
| 		if (t < s_t) continue; | ||||
| 		if (t > s_t) | ||||
| 			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); | ||||
| 		if ((tb = fib_get_table(t))==NULL) | ||||
| 			continue; | ||||
| 		if (tb->tb_dump(tb, skb, cb) < 0)  | ||||
| 			break; | ||||
| 	for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) { | ||||
| 		e = 0; | ||||
| 		hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist) { | ||||
| 			if (e < s_e) | ||||
| 				goto next; | ||||
| 			if (dumped) | ||||
| 				memset(&cb->args[2], 0, sizeof(cb->args) - | ||||
| 				                 2 * sizeof(cb->args[0])); | ||||
| 			if (tb->tb_dump(tb, skb, cb) < 0) | ||||
| 				goto out; | ||||
| 			dumped = 1; | ||||
| next: | ||||
| 			e++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	cb->args[0] = t; | ||||
| out: | ||||
| 	cb->args[1] = e; | ||||
| 	cb->args[0] = h; | ||||
| 
 | ||||
| 	return skb->len; | ||||
| } | ||||
| @ -654,9 +682,15 @@ static struct notifier_block fib_netdev_notifier = { | ||||
| 
 | ||||
| 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 | ||||
| 	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); | ||||
| 	hlist_add_head_rcu(&ip_fib_main_table->tb_hlist, &fib_table_hash[0]); | ||||
| #else | ||||
| 	fib4_rules_init(); | ||||
| #endif | ||||
|  | ||||
| @ -684,7 +684,7 @@ fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb, | ||||
| 	struct fib_node *f; | ||||
| 	int i, s_i; | ||||
| 
 | ||||
| 	s_i = cb->args[3]; | ||||
| 	s_i = cb->args[4]; | ||||
| 	i = 0; | ||||
| 	hlist_for_each_entry(f, node, head, fn_hash) { | ||||
| 		struct fib_alias *fa; | ||||
| @ -704,14 +704,14 @@ fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb, | ||||
| 					  fa->fa_tos, | ||||
| 					  fa->fa_info, | ||||
| 					  NLM_F_MULTI) < 0) { | ||||
| 				cb->args[3] = i; | ||||
| 				cb->args[4] = i; | ||||
| 				return -1; | ||||
| 			} | ||||
| 		next: | ||||
| 			i++; | ||||
| 		} | ||||
| 	} | ||||
| 	cb->args[3] = i; | ||||
| 	cb->args[4] = i; | ||||
| 	return skb->len; | ||||
| } | ||||
| 
 | ||||
| @ -722,21 +722,21 @@ fn_hash_dump_zone(struct sk_buff *skb, struct netlink_callback *cb, | ||||
| { | ||||
| 	int h, s_h; | ||||
| 
 | ||||
| 	s_h = cb->args[2]; | ||||
| 	s_h = cb->args[3]; | ||||
| 	for (h=0; h < fz->fz_divisor; h++) { | ||||
| 		if (h < s_h) continue; | ||||
| 		if (h > s_h) | ||||
| 			memset(&cb->args[3], 0, | ||||
| 			       sizeof(cb->args) - 3*sizeof(cb->args[0])); | ||||
| 			memset(&cb->args[4], 0, | ||||
| 			       sizeof(cb->args) - 4*sizeof(cb->args[0])); | ||||
| 		if (fz->fz_hash == NULL || | ||||
| 		    hlist_empty(&fz->fz_hash[h])) | ||||
| 			continue; | ||||
| 		if (fn_hash_dump_bucket(skb, cb, tb, fz, &fz->fz_hash[h])<0) { | ||||
| 			cb->args[2] = h; | ||||
| 			cb->args[3] = h; | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 	cb->args[2] = h; | ||||
| 	cb->args[3] = h; | ||||
| 	return skb->len; | ||||
| } | ||||
| 
 | ||||
| @ -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_hash *table = (struct fn_hash*)tb->tb_data; | ||||
| 
 | ||||
| 	s_m = cb->args[1]; | ||||
| 	s_m = cb->args[2]; | ||||
| 	read_lock(&fib_hash_lock); | ||||
| 	for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) { | ||||
| 		if (m < s_m) continue; | ||||
| 		if (m > s_m) | ||||
| 			memset(&cb->args[2], 0, | ||||
| 			       sizeof(cb->args) - 2*sizeof(cb->args[0])); | ||||
| 			memset(&cb->args[3], 0, | ||||
| 			       sizeof(cb->args) - 3*sizeof(cb->args[0])); | ||||
| 		if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) { | ||||
| 			cb->args[1] = m; | ||||
| 			cb->args[2] = m; | ||||
| 			read_unlock(&fib_hash_lock); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 	read_unlock(&fib_hash_lock); | ||||
| 	cb->args[1] = m; | ||||
| 	cb->args[2] = m; | ||||
| 	return skb->len; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -172,8 +172,8 @@ static struct fib_table *fib_empty_table(void) | ||||
| 	u32 id; | ||||
| 
 | ||||
| 	for (id = 1; id <= RT_TABLE_MAX; id++) | ||||
| 		if (fib_tables[id] == NULL) | ||||
| 			return __fib_new_table(id); | ||||
| 		if (fib_get_table(id) == NULL) | ||||
| 			return fib_new_table(id); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
| 	s_i = cb->args[3]; | ||||
| 	s_i = cb->args[4]; | ||||
| 	i = 0; | ||||
| 
 | ||||
| 	/* 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 | ||||
| 				  plen, | ||||
| 				  fa->fa_tos, | ||||
| 				  fa->fa_info, 0) < 0) { | ||||
| 			cb->args[3] = i; | ||||
| 			cb->args[4] = i; | ||||
| 			return -1; | ||||
| 		} | ||||
| 		i++; | ||||
| 	} | ||||
| 	cb->args[3] = i; | ||||
| 	cb->args[4] = i; | ||||
| 	return skb->len; | ||||
| } | ||||
| 
 | ||||
| @ -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 leaf *l = NULL; | ||||
| 
 | ||||
| 	s_h = cb->args[2]; | ||||
| 	s_h = cb->args[3]; | ||||
| 
 | ||||
| 	for (h = 0; (l = nextleaf(t, l)) != NULL; h++) { | ||||
| 		if (h < s_h) | ||||
| 			continue; | ||||
| 		if (h > s_h) | ||||
| 			memset(&cb->args[3], 0, | ||||
| 			       sizeof(cb->args) - 3*sizeof(cb->args[0])); | ||||
| 			memset(&cb->args[4], 0, | ||||
| 			       sizeof(cb->args) - 4*sizeof(cb->args[0])); | ||||
| 
 | ||||
| 		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 | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (fn_trie_dump_fa(l->key, plen, fa_head, tb, skb, cb)<0) { | ||||
| 			cb->args[2] = h; | ||||
| 			cb->args[3] = h; | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 	cb->args[2] = h; | ||||
| 	cb->args[3] = h; | ||||
| 	return skb->len; | ||||
| } | ||||
| 
 | ||||
| @ -1917,23 +1917,23 @@ static int fn_trie_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin | ||||
| 	int m, s_m; | ||||
| 	struct trie *t = (struct trie *) tb->tb_data; | ||||
| 
 | ||||
| 	s_m = cb->args[1]; | ||||
| 	s_m = cb->args[2]; | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 	for (m = 0; m <= 32; m++) { | ||||
| 		if (m < s_m) | ||||
| 			continue; | ||||
| 		if (m > s_m) | ||||
| 			memset(&cb->args[2], 0, | ||||
| 				sizeof(cb->args) - 2*sizeof(cb->args[0])); | ||||
| 			memset(&cb->args[3], 0, | ||||
| 				sizeof(cb->args) - 3*sizeof(cb->args[0])); | ||||
| 
 | ||||
| 		if (fn_trie_dump_plen(t, 32-m, tb, skb, cb)<0) { | ||||
| 			cb->args[1] = m; | ||||
| 			cb->args[2] = m; | ||||
| 			goto out; | ||||
| 		} | ||||
| 	} | ||||
| 	rcu_read_unlock(); | ||||
| 	cb->args[1] = m; | ||||
| 	cb->args[2] = m; | ||||
| 	return skb->len; | ||||
| out: | ||||
| 	rcu_read_unlock(); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user