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 b2ba5581d2ae..d9b40248b97f 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); | |||
146 | static void ipv4_link_failure(struct sk_buff *skb); | 146 | static void ipv4_link_failure(struct sk_buff *skb); |
147 | static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu); | 147 | static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu); |
148 | static int rt_garbage_collect(struct dst_ops *ops); | 148 | static int rt_garbage_collect(struct dst_ops *ops); |
149 | static void rt_emergency_hash_rebuild(struct net *net); | ||
150 | 149 | ||
151 | 150 | ||
152 | static struct dst_ops ipv4_dst_ops = { | 151 | static struct dst_ops ipv4_dst_ops = { |
@@ -780,11 +779,30 @@ static void rt_do_flush(int process_context) | |||
780 | #define FRACT_BITS 3 | 779 | #define FRACT_BITS 3 |
781 | #define ONE (1UL << FRACT_BITS) | 780 | #define ONE (1UL << FRACT_BITS) |
782 | 781 | ||
782 | /* | ||
783 | * Given a hash chain and an item in this hash chain, | ||
784 | * find if a previous entry has the same hash_inputs | ||
785 | * (but differs on tos, mark or oif) | ||
786 | * Returns 0 if an alias is found. | ||
787 | * Returns ONE if rth has no alias before itself. | ||
788 | */ | ||
789 | static int has_noalias(const struct rtable *head, const struct rtable *rth) | ||
790 | { | ||
791 | const struct rtable *aux = head; | ||
792 | |||
793 | while (aux != rth) { | ||
794 | if (compare_hash_inputs(&aux->fl, &rth->fl)) | ||
795 | return 0; | ||
796 | aux = aux->u.dst.rt_next; | ||
797 | } | ||
798 | return ONE; | ||
799 | } | ||
800 | |||
783 | static void rt_check_expire(void) | 801 | static void rt_check_expire(void) |
784 | { | 802 | { |
785 | static unsigned int rover; | 803 | static unsigned int rover; |
786 | unsigned int i = rover, goal; | 804 | unsigned int i = rover, goal; |
787 | struct rtable *rth, *aux, **rthp; | 805 | struct rtable *rth, **rthp; |
788 | unsigned long samples = 0; | 806 | unsigned long samples = 0; |
789 | unsigned long sum = 0, sum2 = 0; | 807 | unsigned long sum = 0, sum2 = 0; |
790 | unsigned long delta; | 808 | unsigned long delta; |
@@ -835,15 +853,7 @@ nofree: | |||
835 | * attributes don't unfairly skew | 853 | * attributes don't unfairly skew |
836 | * the length computation | 854 | * the length computation |
837 | */ | 855 | */ |
838 | for (aux = rt_hash_table[i].chain;;) { | 856 | length += has_noalias(rt_hash_table[i].chain, rth); |
839 | if (aux == rth) { | ||
840 | length += ONE; | ||
841 | break; | ||
842 | } | ||
843 | if (compare_hash_inputs(&aux->fl, &rth->fl)) | ||
844 | break; | ||
845 | aux = aux->u.dst.rt_next; | ||
846 | } | ||
847 | continue; | 857 | continue; |
848 | } | 858 | } |
849 | } else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout)) | 859 | } else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout)) |
@@ -1073,6 +1083,21 @@ work_done: | |||
1073 | out: return 0; | 1083 | out: return 0; |
1074 | } | 1084 | } |
1075 | 1085 | ||
1086 | /* | ||
1087 | * Returns number of entries in a hash chain that have different hash_inputs | ||
1088 | */ | ||
1089 | static int slow_chain_length(const struct rtable *head) | ||
1090 | { | ||
1091 | int length = 0; | ||
1092 | const struct rtable *rth = head; | ||
1093 | |||
1094 | while (rth) { | ||
1095 | length += has_noalias(head, rth); | ||
1096 | rth = rth->u.dst.rt_next; | ||
1097 | } | ||
1098 | return length >> FRACT_BITS; | ||
1099 | } | ||
1100 | |||
1076 | static int rt_intern_hash(unsigned hash, struct rtable *rt, | 1101 | static int rt_intern_hash(unsigned hash, struct rtable *rt, |
1077 | struct rtable **rp, struct sk_buff *skb) | 1102 | struct rtable **rp, struct sk_buff *skb) |
1078 | { | 1103 | { |
@@ -1185,7 +1210,8 @@ restart: | |||
1185 | rt_free(cand); | 1210 | rt_free(cand); |
1186 | } | 1211 | } |
1187 | } else { | 1212 | } else { |
1188 | if (chain_length > rt_chain_length_max) { | 1213 | if (chain_length > rt_chain_length_max && |
1214 | slow_chain_length(rt_hash_table[hash].chain) > rt_chain_length_max) { | ||
1189 | struct net *net = dev_net(rt->u.dst.dev); | 1215 | struct net *net = dev_net(rt->u.dst.dev); |
1190 | int num = ++net->ipv4.current_rt_cache_rebuild_count; | 1216 | int num = ++net->ipv4.current_rt_cache_rebuild_count; |
1191 | if (!rt_caching(dev_net(rt->u.dst.dev))) { | 1217 | if (!rt_caching(dev_net(rt->u.dst.dev))) { |