|
@@ -129,6 +129,7 @@ static int ip_rt_mtu_expires __read_mostly = 10 * 60 * HZ;
|
|
static int ip_rt_min_pmtu __read_mostly = 512 + 20 + 20;
|
|
static int ip_rt_min_pmtu __read_mostly = 512 + 20 + 20;
|
|
static int ip_rt_min_advmss __read_mostly = 256;
|
|
static int ip_rt_min_advmss __read_mostly = 256;
|
|
|
|
|
|
|
|
+static int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT;
|
|
/*
|
|
/*
|
|
* Interface to generic destination cache.
|
|
* Interface to generic destination cache.
|
|
*/
|
|
*/
|
|
@@ -755,7 +756,7 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow
|
|
struct fib_nh *nh = &FIB_RES_NH(res);
|
|
struct fib_nh *nh = &FIB_RES_NH(res);
|
|
|
|
|
|
update_or_create_fnhe(nh, fl4->daddr, new_gw,
|
|
update_or_create_fnhe(nh, fl4->daddr, new_gw,
|
|
- 0, 0);
|
|
|
|
|
|
+ 0, jiffies + ip_rt_gc_timeout);
|
|
}
|
|
}
|
|
if (kill_route)
|
|
if (kill_route)
|
|
rt->dst.obsolete = DST_OBSOLETE_KILL;
|
|
rt->dst.obsolete = DST_OBSOLETE_KILL;
|
|
@@ -1556,6 +1557,36 @@ static void ip_handle_martian_source(struct net_device *dev,
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void ip_del_fnhe(struct fib_nh *nh, __be32 daddr)
|
|
|
|
+{
|
|
|
|
+ struct fnhe_hash_bucket *hash;
|
|
|
|
+ struct fib_nh_exception *fnhe, __rcu **fnhe_p;
|
|
|
|
+ u32 hval = fnhe_hashfun(daddr);
|
|
|
|
+
|
|
|
|
+ spin_lock_bh(&fnhe_lock);
|
|
|
|
+
|
|
|
|
+ hash = rcu_dereference_protected(nh->nh_exceptions,
|
|
|
|
+ lockdep_is_held(&fnhe_lock));
|
|
|
|
+ hash += hval;
|
|
|
|
+
|
|
|
|
+ fnhe_p = &hash->chain;
|
|
|
|
+ fnhe = rcu_dereference_protected(*fnhe_p, lockdep_is_held(&fnhe_lock));
|
|
|
|
+ while (fnhe) {
|
|
|
|
+ if (fnhe->fnhe_daddr == daddr) {
|
|
|
|
+ rcu_assign_pointer(*fnhe_p, rcu_dereference_protected(
|
|
|
|
+ fnhe->fnhe_next, lockdep_is_held(&fnhe_lock)));
|
|
|
|
+ fnhe_flush_routes(fnhe);
|
|
|
|
+ kfree_rcu(fnhe, rcu);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ fnhe_p = &fnhe->fnhe_next;
|
|
|
|
+ fnhe = rcu_dereference_protected(fnhe->fnhe_next,
|
|
|
|
+ lockdep_is_held(&fnhe_lock));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ spin_unlock_bh(&fnhe_lock);
|
|
|
|
+}
|
|
|
|
+
|
|
/* called in rcu_read_lock() section */
|
|
/* called in rcu_read_lock() section */
|
|
static int __mkroute_input(struct sk_buff *skb,
|
|
static int __mkroute_input(struct sk_buff *skb,
|
|
const struct fib_result *res,
|
|
const struct fib_result *res,
|
|
@@ -1609,11 +1640,20 @@ static int __mkroute_input(struct sk_buff *skb,
|
|
|
|
|
|
fnhe = find_exception(&FIB_RES_NH(*res), daddr);
|
|
fnhe = find_exception(&FIB_RES_NH(*res), daddr);
|
|
if (do_cache) {
|
|
if (do_cache) {
|
|
- if (fnhe)
|
|
|
|
|
|
+ if (fnhe) {
|
|
rth = rcu_dereference(fnhe->fnhe_rth_input);
|
|
rth = rcu_dereference(fnhe->fnhe_rth_input);
|
|
- else
|
|
|
|
- rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);
|
|
|
|
|
|
+ if (rth && rth->dst.expires &&
|
|
|
|
+ time_after(jiffies, rth->dst.expires)) {
|
|
|
|
+ ip_del_fnhe(&FIB_RES_NH(*res), daddr);
|
|
|
|
+ fnhe = NULL;
|
|
|
|
+ } else {
|
|
|
|
+ goto rt_cache;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);
|
|
|
|
|
|
|
|
+rt_cache:
|
|
if (rt_cache_valid(rth)) {
|
|
if (rt_cache_valid(rth)) {
|
|
skb_dst_set_noref(skb, &rth->dst);
|
|
skb_dst_set_noref(skb, &rth->dst);
|
|
goto out;
|
|
goto out;
|
|
@@ -2014,19 +2054,29 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
|
|
struct fib_nh *nh = &FIB_RES_NH(*res);
|
|
struct fib_nh *nh = &FIB_RES_NH(*res);
|
|
|
|
|
|
fnhe = find_exception(nh, fl4->daddr);
|
|
fnhe = find_exception(nh, fl4->daddr);
|
|
- if (fnhe)
|
|
|
|
|
|
+ if (fnhe) {
|
|
prth = &fnhe->fnhe_rth_output;
|
|
prth = &fnhe->fnhe_rth_output;
|
|
- else {
|
|
|
|
- if (unlikely(fl4->flowi4_flags &
|
|
|
|
- FLOWI_FLAG_KNOWN_NH &&
|
|
|
|
- !(nh->nh_gw &&
|
|
|
|
- nh->nh_scope == RT_SCOPE_LINK))) {
|
|
|
|
- do_cache = false;
|
|
|
|
- goto add;
|
|
|
|
|
|
+ rth = rcu_dereference(*prth);
|
|
|
|
+ if (rth && rth->dst.expires &&
|
|
|
|
+ time_after(jiffies, rth->dst.expires)) {
|
|
|
|
+ ip_del_fnhe(nh, fl4->daddr);
|
|
|
|
+ fnhe = NULL;
|
|
|
|
+ } else {
|
|
|
|
+ goto rt_cache;
|
|
}
|
|
}
|
|
- prth = raw_cpu_ptr(nh->nh_pcpu_rth_output);
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (unlikely(fl4->flowi4_flags &
|
|
|
|
+ FLOWI_FLAG_KNOWN_NH &&
|
|
|
|
+ !(nh->nh_gw &&
|
|
|
|
+ nh->nh_scope == RT_SCOPE_LINK))) {
|
|
|
|
+ do_cache = false;
|
|
|
|
+ goto add;
|
|
|
|
+ }
|
|
|
|
+ prth = raw_cpu_ptr(nh->nh_pcpu_rth_output);
|
|
rth = rcu_dereference(*prth);
|
|
rth = rcu_dereference(*prth);
|
|
|
|
+
|
|
|
|
+rt_cache:
|
|
if (rt_cache_valid(rth)) {
|
|
if (rt_cache_valid(rth)) {
|
|
dst_hold(&rth->dst);
|
|
dst_hold(&rth->dst);
|
|
return rth;
|
|
return rth;
|
|
@@ -2569,7 +2619,6 @@ void ip_rt_multicast_event(struct in_device *in_dev)
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
#ifdef CONFIG_SYSCTL
|
|
-static int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT;
|
|
|
|
static int ip_rt_gc_interval __read_mostly = 60 * HZ;
|
|
static int ip_rt_gc_interval __read_mostly = 60 * HZ;
|
|
static int ip_rt_gc_min_interval __read_mostly = HZ / 2;
|
|
static int ip_rt_gc_min_interval __read_mostly = HZ / 2;
|
|
static int ip_rt_gc_elasticity __read_mostly = 8;
|
|
static int ip_rt_gc_elasticity __read_mostly = 8;
|