diff options
Diffstat (limited to 'net/ipv4/fib_trie.c')
| -rw-r--r-- | net/ipv4/fib_trie.c | 158 | 
1 files changed, 112 insertions, 46 deletions
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 31cef3602585..e3665bf7a7f3 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -719,6 +719,13 @@ static unsigned char update_suffix(struct key_vector *tn)  {  	unsigned char slen = tn->pos;  	unsigned long stride, i; +	unsigned char slen_max; + +	/* only vector 0 can have a suffix length greater than or equal to +	 * tn->pos + tn->bits, the second highest node will have a suffix +	 * length at most of tn->pos + tn->bits - 1 +	 */ +	slen_max = min_t(unsigned char, tn->pos + tn->bits - 1, tn->slen);  	/* search though the list of children looking for nodes that might  	 * have a suffix greater than the one we currently have.  This is @@ -736,12 +743,8 @@ static unsigned char update_suffix(struct key_vector *tn)  		slen = n->slen;  		i &= ~(stride - 1); -		/* if slen covers all but the last bit we can stop here -		 * there will be nothing longer than that since only node -		 * 0 and 1 << (bits - 1) could have that as their suffix -		 * length. -		 */ -		if ((slen + 1) >= (tn->pos + tn->bits)) +		/* stop searching if we have hit the maximum possible value */ +		if (slen >= slen_max)  			break;  	} @@ -913,39 +916,27 @@ static struct key_vector *resize(struct trie *t, struct key_vector *tn)  		return collapse(t, tn);  	/* update parent in case halve failed */ -	tp = node_parent(tn); - -	/* Return if at least one deflate was run */ -	if (max_work != MAX_WORK) -		return tp; - -	/* push the suffix length to the parent node */ -	if (tn->slen > tn->pos) { -		unsigned char slen = update_suffix(tn); - -		if (slen > tp->slen) -			tp->slen = slen; -	} - -	return tp; +	return node_parent(tn);  } -static void leaf_pull_suffix(struct key_vector *tp, struct key_vector *l) +static void node_pull_suffix(struct key_vector *tn, unsigned char slen)  { -	while ((tp->slen > tp->pos) && (tp->slen > l->slen)) { -		if (update_suffix(tp) > l->slen) +	unsigned char node_slen = tn->slen; + +	while ((node_slen > tn->pos) && (node_slen > slen)) { +		slen = update_suffix(tn); +		if (node_slen == slen)  			break; -		tp = node_parent(tp); + +		tn = node_parent(tn); +		node_slen = tn->slen;  	}  } -static void leaf_push_suffix(struct key_vector *tn, struct key_vector *l) +static void node_push_suffix(struct key_vector *tn, unsigned char slen)  { -	/* if this is a new leaf then tn will be NULL and we can sort -	 * out parent suffix lengths as a part of trie_rebalance -	 */ -	while (tn->slen < l->slen) { -		tn->slen = l->slen; +	while (tn->slen < slen) { +		tn->slen = slen;  		tn = node_parent(tn);  	}  } @@ -1066,6 +1057,7 @@ static int fib_insert_node(struct trie *t, struct key_vector *tp,  	}  	/* Case 3: n is NULL, and will just insert a new leaf */ +	node_push_suffix(tp, new->fa_slen);  	NODE_INIT_PARENT(l, tp);  	put_child_root(tp, key, l);  	trie_rebalance(t, tp); @@ -1107,7 +1099,7 @@ static int fib_insert_alias(struct trie *t, struct key_vector *tp,  	/* if we added to the tail node then we need to update slen */  	if (l->slen < new->fa_slen) {  		l->slen = new->fa_slen; -		leaf_push_suffix(tp, l); +		node_push_suffix(tp, new->fa_slen);  	}  	return 0; @@ -1499,6 +1491,8 @@ static void fib_remove_alias(struct trie *t, struct key_vector *tp,  	 * out parent suffix lengths as a part of trie_rebalance  	 */  	if (hlist_empty(&l->leaf)) { +		if (tp->slen == l->slen) +			node_pull_suffix(tp, tp->pos);  		put_child_root(tp, l->key, NULL);  		node_free(l);  		trie_rebalance(t, tp); @@ -1511,7 +1505,7 @@ static void fib_remove_alias(struct trie *t, struct key_vector *tp,  	/* update the trie with the latest suffix length */  	l->slen = fa->fa_slen; -	leaf_pull_suffix(tp, l); +	node_pull_suffix(tp, fa->fa_slen);  }  /* Caller must hold RTNL. */ @@ -1743,8 +1737,10 @@ struct fib_table *fib_trie_unmerge(struct fib_table *oldtb)  				local_l = fib_find_node(lt, &local_tp, l->key);  			if (fib_insert_alias(lt, local_tp, local_l, new_fa, -					     NULL, l->key)) +					     NULL, l->key)) { +				kmem_cache_free(fn_alias_kmem, new_fa);  				goto out; +			}  		}  		/* stop loop if key wrapped back to 0 */ @@ -1760,6 +1756,75 @@ out:  	return NULL;  } +/* Caller must hold RTNL */ +void fib_table_flush_external(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 */ +	for (;;) { +		unsigned char slen = 0; +		struct key_vector *n; + +		if (!(cindex--)) { +			t_key pkey = pn->key; + +			/* cannot resize the trie vector */ +			if (IS_TRIE(pn)) +				break; + +			/* update the suffix to address pulled leaves */ +			if (pn->slen > pn->pos) +				update_suffix(pn); + +			/* resize completed node */ +			pn = resize(t, pn); +			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) { +			/* 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; +		} + +		/* update leaf slen */ +		n->slen = slen; + +		if (hlist_empty(&n->leaf)) { +			put_child_root(pn, n->key, NULL); +			node_free(n); +		} +	} +} +  /* Caller must hold RTNL. */  int fib_table_flush(struct net *net, struct fib_table *tb)  { @@ -1782,6 +1847,10 @@ int fib_table_flush(struct net *net, struct fib_table *tb)  			if (IS_TRIE(pn))  				break; +			/* update the suffix to address pulled leaves */ +			if (pn->slen > pn->pos) +				update_suffix(pn); +  			/* resize completed node */  			pn = resize(t, pn);  			cindex = get_index(pkey, pn); @@ -2413,22 +2482,19 @@ static struct key_vector *fib_route_get_idx(struct fib_route_iter *iter,  	struct key_vector *l, **tp = &iter->tnode;  	t_key key; -	/* use cache location of next-to-find key */ +	/* use cached location of previously found key */  	if (iter->pos > 0 && pos >= iter->pos) { -		pos -= iter->pos;  		key = iter->key;  	} else { -		iter->pos = 0; +		iter->pos = 1;  		key = 0;  	} -	while ((l = leaf_walk_rcu(tp, key)) != NULL) { +	pos -= iter->pos; + +	while ((l = leaf_walk_rcu(tp, key)) && (pos-- > 0)) {  		key = l->key + 1;  		iter->pos++; - -		if (--pos <= 0) -			break; -  		l = NULL;  		/* handle unlikely case of a key wrap */ @@ -2437,7 +2503,7 @@ static struct key_vector *fib_route_get_idx(struct fib_route_iter *iter,  	}  	if (l) -		iter->key = key;	/* remember it */ +		iter->key = l->key;	/* remember it */  	else  		iter->pos = 0;		/* forget it */ @@ -2465,7 +2531,7 @@ static void *fib_route_seq_start(struct seq_file *seq, loff_t *pos)  		return fib_route_get_idx(iter, *pos);  	iter->pos = 0; -	iter->key = 0; +	iter->key = KEY_MAX;  	return SEQ_START_TOKEN;  } @@ -2474,7 +2540,7 @@ static void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)  {  	struct fib_route_iter *iter = seq->private;  	struct key_vector *l = NULL; -	t_key key = iter->key; +	t_key key = iter->key + 1;  	++*pos; @@ -2483,7 +2549,7 @@ static void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)  		l = leaf_walk_rcu(&iter->tnode, key);  	if (l) { -		iter->key = l->key + 1; +		iter->key = l->key;  		iter->pos++;  	} else {  		iter->pos = 0;  | 
