[IPV4] fib_trie: apply fixes from fib_hash

Update fib_trie with some fib_hash fixes:
- check for duplicate alternative routes for prefix+tos+priority when
replacing route
- properly insert by matching tos together with priority
- fix alias walking to use list_for_each_entry_continue for insertion
and deletion when fa_head is not NULL
- copy state from fa to new_fa on replace (not a problem for now)
- additionally, avoid replacement without error if new route is same,
as Joonwoo Park suggests.

Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Julian Anastasov 2008-01-28 21:18:06 -08:00 committed by David S. Miller
parent c18865f392
commit 936f6f8e1b

View file

@ -1205,20 +1205,45 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
* and we need to allocate a new one of those as well. * and we need to allocate a new one of those as well.
*/ */
if (fa && fa->fa_info->fib_priority == fi->fib_priority) { if (fa && fa->fa_tos == tos &&
struct fib_alias *fa_orig; fa->fa_info->fib_priority == fi->fib_priority) {
struct fib_alias *fa_first, *fa_match;
err = -EEXIST; err = -EEXIST;
if (cfg->fc_nlflags & NLM_F_EXCL) if (cfg->fc_nlflags & NLM_F_EXCL)
goto out; goto out;
/* We have 2 goals:
* 1. Find exact match for type, scope, fib_info to avoid
* duplicate routes
* 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
*/
fa_match = NULL;
fa_first = fa;
fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
list_for_each_entry_continue(fa, fa_head, fa_list) {
if (fa->fa_tos != tos)
break;
if (fa->fa_info->fib_priority != fi->fib_priority)
break;
if (fa->fa_type == cfg->fc_type &&
fa->fa_scope == cfg->fc_scope &&
fa->fa_info == fi) {
fa_match = fa;
break;
}
}
if (cfg->fc_nlflags & NLM_F_REPLACE) { if (cfg->fc_nlflags & NLM_F_REPLACE) {
struct fib_info *fi_drop; struct fib_info *fi_drop;
u8 state; u8 state;
if (fi->fib_treeref > 1) fa = fa_first;
if (fa_match) {
if (fa == fa_match)
err = 0;
goto out; goto out;
}
err = -ENOBUFS; err = -ENOBUFS;
new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
if (new_fa == NULL) if (new_fa == NULL)
@ -1230,7 +1255,7 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
new_fa->fa_type = cfg->fc_type; new_fa->fa_type = cfg->fc_type;
new_fa->fa_scope = cfg->fc_scope; new_fa->fa_scope = cfg->fc_scope;
state = fa->fa_state; state = fa->fa_state;
new_fa->fa_state &= ~FA_S_ACCESSED; new_fa->fa_state = state & ~FA_S_ACCESSED;
list_replace_rcu(&fa->fa_list, &new_fa->fa_list); list_replace_rcu(&fa->fa_list, &new_fa->fa_list);
alias_free_mem_rcu(fa); alias_free_mem_rcu(fa);
@ -1247,20 +1272,11 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
* uses the same scope, type, and nexthop * uses the same scope, type, and nexthop
* information. * information.
*/ */
fa_orig = fa; if (fa_match)
list_for_each_entry(fa, fa_orig->fa_list.prev, fa_list) { goto out;
if (fa->fa_tos != tos)
break;
if (fa->fa_info->fib_priority != fi->fib_priority)
break;
if (fa->fa_type == cfg->fc_type &&
fa->fa_scope == cfg->fc_scope &&
fa->fa_info == fi)
goto out;
}
if (!(cfg->fc_nlflags & NLM_F_APPEND)) if (!(cfg->fc_nlflags & NLM_F_APPEND))
fa = fa_orig; fa = fa_first;
} }
err = -ENOENT; err = -ENOENT;
if (!(cfg->fc_nlflags & NLM_F_CREATE)) if (!(cfg->fc_nlflags & NLM_F_CREATE))
@ -1600,9 +1616,8 @@ static int fn_trie_delete(struct fib_table *tb, struct fib_config *cfg)
pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t); pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t);
fa_to_delete = NULL; fa_to_delete = NULL;
fa_head = fa->fa_list.prev; fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
list_for_each_entry_continue(fa, fa_head, fa_list) {
list_for_each_entry(fa, fa_head, fa_list) {
struct fib_info *fi = fa->fa_info; struct fib_info *fi = fa->fa_info;
if (fa->fa_tos != tos) if (fa->fa_tos != tos)