@ -1120,6 +1120,9 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
break ;
if ( fa - > fa_info - > fib_priority ! = fi - > fib_priority )
break ;
/* duplicate entry from another table */
if ( WARN_ON ( fa - > tb_id ! = tb - > tb_id ) )
continue ;
if ( fa - > fa_type = = cfg - > fc_type & &
fa - > fa_info = = fi ) {
fa_match = fa ;
@ -1197,6 +1200,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
new_fa - > fa_type = cfg - > fc_type ;
new_fa - > fa_state = 0 ;
new_fa - > fa_slen = slen ;
new_fa - > tb_id = tb - > tb_id ;
/* (Optionally) offload fib entry to switch hardware. */
err = netdev_switch_fib_ipv4_add ( key , plen , fi , tos ,
@ -1217,7 +1221,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
tb - > tb_num_default + + ;
rt_cache_flush ( cfg - > fc_nlinfo . nl_net ) ;
rtmsg_fib ( RTM_NEWROUTE , htonl ( key ) , new_fa , plen , tb - > tb_id ,
rtmsg_fib ( RTM_NEWROUTE , htonl ( key ) , new_fa , plen , new_fa - > tb_id ,
& cfg - > fc_nlinfo , 0 ) ;
succeeded :
return 0 ;
@ -1483,6 +1487,9 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
if ( ( fa - > fa_slen ! = slen ) | | ( fa - > fa_tos ! = tos ) )
break ;
if ( fa - > tb_id ! = tb - > tb_id )
continue ;
if ( ( ! cfg - > fc_type | | fa - > fa_type = = cfg - > fc_type ) & &
( cfg - > fc_scope = = RT_SCOPE_NOWHERE | |
fa - > fa_info - > fib_scope = = cfg - > fc_scope ) & &
@ -1576,6 +1583,120 @@ found:
return n ;
}
static void fib_trie_free ( struct fib_table * tb )
{
struct trie * t = ( struct trie * ) tb - > tb_data ;
struct key_vector * pn = t - > kv ;
unsigned long cindex = 1 ;
struct hlist_node * tmp ;
struct fib_alias * fa ;
/* walk trie in reverse order and free everything */
for ( ; ; ) {
struct key_vector * n ;
if ( ! ( cindex - - ) ) {
t_key pkey = pn - > key ;
if ( IS_TRIE ( pn ) )
break ;
n = pn ;
pn = node_parent ( pn ) ;
/* drop emptied tnode */
put_child_root ( pn , n - > key , NULL ) ;
node_free ( n ) ;
cindex = get_index ( pkey , pn ) ;
continue ;
}
/* grab the next available node */
n = get_child ( pn , cindex ) ;
if ( ! n )
continue ;
if ( IS_TNODE ( n ) ) {
/* record pn and cindex for leaf walking */
pn = n ;
cindex = 1ul < < n - > bits ;
continue ;
}
hlist_for_each_entry_safe ( fa , tmp , & n - > leaf , fa_list ) {
hlist_del_rcu ( & fa - > fa_list ) ;
alias_free_mem_rcu ( fa ) ;
}
put_child_root ( pn , n - > key , NULL ) ;
node_free ( n ) ;
}
# ifdef CONFIG_IP_FIB_TRIE_STATS
free_percpu ( t - > stats ) ;
# endif
kfree ( tb ) ;
}
struct fib_table * fib_trie_unmerge ( struct fib_table * oldtb )
{
struct trie * ot = ( struct trie * ) oldtb - > tb_data ;
struct key_vector * l , * tp = ot - > kv ;
struct fib_table * local_tb ;
struct fib_alias * fa ;
struct trie * lt ;
t_key key = 0 ;
if ( oldtb - > tb_data = = oldtb - > __data )
return oldtb ;
local_tb = fib_trie_table ( RT_TABLE_LOCAL , NULL ) ;
if ( ! local_tb )
return NULL ;
lt = ( struct trie * ) local_tb - > tb_data ;
while ( ( l = leaf_walk_rcu ( & tp , key ) ) ! = NULL ) {
struct key_vector * local_l = NULL , * local_tp ;
hlist_for_each_entry_rcu ( fa , & l - > leaf , fa_list ) {
struct fib_alias * new_fa ;
if ( local_tb - > tb_id ! = fa - > tb_id )
continue ;
/* clone fa for new local table */
new_fa = kmem_cache_alloc ( fn_alias_kmem , GFP_KERNEL ) ;
if ( ! new_fa )
goto out ;
memcpy ( new_fa , fa , sizeof ( * fa ) ) ;
/* insert clone into table */
if ( ! local_l )
local_l = fib_find_node ( lt , & local_tp , l - > key ) ;
if ( fib_insert_alias ( lt , local_tp , local_l , new_fa ,
NULL , l - > key ) )
goto out ;
}
/* stop loop if key wrapped back to 0 */
key = l - > key + 1 ;
if ( key < l - > key )
break ;
}
return local_tb ;
out :
fib_trie_free ( local_tb ) ;
return NULL ;
}
/* Caller must hold RTNL */
void fib_table_flush_external ( struct fib_table * tb )
{
@ -1587,6 +1708,7 @@ void fib_table_flush_external(struct fib_table *tb)
/* walk trie in reverse order */
for ( ; ; ) {
unsigned char slen = 0 ;
struct key_vector * n ;
if ( ! ( cindex - - ) ) {
@ -1596,8 +1718,8 @@ void fib_table_flush_external(struct fib_table *tb)
if ( IS_TRIE ( pn ) )
break ;
/* no need to resize like in flush below */
pn = node_parent ( pn ) ;
/* resize completed node */
pn = resize ( t , pn ) ;
cindex = get_index ( pkey , pn ) ;
continue ;
@ -1619,6 +1741,18 @@ void fib_table_flush_external(struct fib_table *tb)
hlist_for_each_entry_safe ( fa , tmp , & n - > leaf , fa_list ) {
struct fib_info * fi = fa - > fa_info ;
/* if alias was cloned to local then we just
* need to remove the local copy from main
*/
if ( tb - > tb_id ! = fa - > tb_id ) {
hlist_del_rcu ( & fa - > fa_list ) ;
alias_free_mem_rcu ( fa ) ;
continue ;
}
/* record local slen */
slen = fa - > fa_slen ;
if ( ! fi | | ! ( fi - > fib_flags & RTNH_F_EXTERNAL ) )
continue ;
@ -1627,6 +1761,16 @@ void fib_table_flush_external(struct fib_table *tb)
fi , fa - > fa_tos ,
fa - > fa_type , tb - > tb_id ) ;
}
/* update leaf slen */
n - > slen = slen ;
if ( hlist_empty ( & n - > leaf ) ) {
put_child_root ( pn , n - > key , NULL ) ;
node_free ( n ) ;
} else {
leaf_pull_suffix ( pn , n ) ;
}
}
}
@ -1711,6 +1855,7 @@ static void __trie_free_rcu(struct rcu_head *head)
# ifdef CONFIG_IP_FIB_TRIE_STATS
struct trie * t = ( struct trie * ) tb - > tb_data ;
if ( tb - > tb_data = = tb - > __data )
free_percpu ( t - > stats ) ;
# endif /* CONFIG_IP_FIB_TRIE_STATS */
kfree ( tb ) ;
@ -1738,6 +1883,11 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb,
continue ;
}
if ( tb - > tb_id ! = fa - > tb_id ) {
i + + ;
continue ;
}
if ( fib_dump_info ( skb , NETLINK_CB ( cb - > skb ) . portid ,
cb - > nlh - > nlmsg_seq ,
RTM_NEWROUTE ,
@ -1804,18 +1954,26 @@ void __init fib_trie_init(void)
0 , SLAB_PANIC , NULL ) ;
}
struct fib_table * fib_trie_table ( u32 id )
struct fib_table * fib_trie_table ( u32 id , struct fib_table * alias )
{
struct fib_table * tb ;
struct trie * t ;
size_t sz = sizeof ( * tb ) ;
tb = kzalloc ( sizeof ( * tb ) + sizeof ( struct trie ) , GFP_KERNEL ) ;
if ( ! alias )
sz + = sizeof ( struct trie ) ;
tb = kzalloc ( sz , GFP_KERNEL ) ;
if ( tb = = NULL )
return NULL ;
tb - > tb_id = id ;
tb - > tb_default = - 1 ;
tb - > tb_num_default = 0 ;
tb - > tb_data = ( alias ? alias - > __data : tb - > __data ) ;
if ( alias )
return tb ;
t = ( struct trie * ) tb - > tb_data ;
t - > kv [ 0 ] . pos = KEYLENGTH ;