diff options
Diffstat (limited to 'net/ipv4/route.c')
| -rw-r--r-- | net/ipv4/route.c | 50 | 
1 files changed, 38 insertions, 12 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index b2ba5581d2a..d9b40248b97 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -146,7 +146,6 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst);  static void		 ipv4_link_failure(struct sk_buff *skb);  static void		 ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu);  static int rt_garbage_collect(struct dst_ops *ops); -static void rt_emergency_hash_rebuild(struct net *net);  static struct dst_ops ipv4_dst_ops = { @@ -780,11 +779,30 @@ static void rt_do_flush(int process_context)  #define FRACT_BITS 3  #define ONE (1UL << FRACT_BITS) +/* + * Given a hash chain and an item in this hash chain, + * find if a previous entry has the same hash_inputs + * (but differs on tos, mark or oif) + * Returns 0 if an alias is found. + * Returns ONE if rth has no alias before itself. + */ +static int has_noalias(const struct rtable *head, const struct rtable *rth) +{ +	const struct rtable *aux = head; + +	while (aux != rth) { +		if (compare_hash_inputs(&aux->fl, &rth->fl)) +			return 0; +		aux = aux->u.dst.rt_next; +	} +	return ONE; +} +  static void rt_check_expire(void)  {  	static unsigned int rover;  	unsigned int i = rover, goal; -	struct rtable *rth, *aux, **rthp; +	struct rtable *rth, **rthp;  	unsigned long samples = 0;  	unsigned long sum = 0, sum2 = 0;  	unsigned long delta; @@ -835,15 +853,7 @@ nofree:  					 * attributes don't unfairly skew  					 * the length computation  					 */ -					for (aux = rt_hash_table[i].chain;;) { -						if (aux == rth) { -							length += ONE; -							break; -						} -						if (compare_hash_inputs(&aux->fl, &rth->fl)) -							break; -						aux = aux->u.dst.rt_next; -					} +					length += has_noalias(rt_hash_table[i].chain, rth);  					continue;  				}  			} else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout)) @@ -1073,6 +1083,21 @@ work_done:  out:	return 0;  } +/* + * Returns number of entries in a hash chain that have different hash_inputs + */ +static int slow_chain_length(const struct rtable *head) +{ +	int length = 0; +	const struct rtable *rth = head; + +	while (rth) { +		length += has_noalias(head, rth); +		rth = rth->u.dst.rt_next; +	} +	return length >> FRACT_BITS; +} +  static int rt_intern_hash(unsigned hash, struct rtable *rt,  			  struct rtable **rp, struct sk_buff *skb)  { @@ -1185,7 +1210,8 @@ restart:  			rt_free(cand);  		}  	} else { -		if (chain_length > rt_chain_length_max) { +		if (chain_length > rt_chain_length_max && +		    slow_chain_length(rt_hash_table[hash].chain) > rt_chain_length_max) {  			struct net *net = dev_net(rt->u.dst.dev);  			int num = ++net->ipv4.current_rt_cache_rebuild_count;  			if (!rt_caching(dev_net(rt->u.dst.dev))) {  |