|
@@ -594,11 +594,25 @@ static inline u32 fnhe_hashfun(__be32 daddr)
|
|
|
return hval & (FNHE_HASH_SIZE - 1);
|
|
|
}
|
|
|
|
|
|
+static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnhe)
|
|
|
+{
|
|
|
+ rt->rt_pmtu = fnhe->fnhe_pmtu;
|
|
|
+ rt->dst.expires = fnhe->fnhe_expires;
|
|
|
+
|
|
|
+ if (fnhe->fnhe_gw) {
|
|
|
+ rt->rt_flags |= RTCF_REDIRECTED;
|
|
|
+ rt->rt_gateway = fnhe->fnhe_gw;
|
|
|
+ rt->rt_uses_gateway = 1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
|
|
|
u32 pmtu, unsigned long expires)
|
|
|
{
|
|
|
struct fnhe_hash_bucket *hash;
|
|
|
struct fib_nh_exception *fnhe;
|
|
|
+ struct rtable *rt;
|
|
|
+ unsigned int i;
|
|
|
int depth;
|
|
|
u32 hval = fnhe_hashfun(daddr);
|
|
|
|
|
@@ -627,8 +641,12 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
|
|
|
fnhe->fnhe_gw = gw;
|
|
|
if (pmtu) {
|
|
|
fnhe->fnhe_pmtu = pmtu;
|
|
|
- fnhe->fnhe_expires = expires;
|
|
|
+ fnhe->fnhe_expires = max(1UL, expires);
|
|
|
}
|
|
|
+ /* Update all cached dsts too */
|
|
|
+ rt = rcu_dereference(fnhe->fnhe_rth);
|
|
|
+ if (rt)
|
|
|
+ fill_route_from_fnhe(rt, fnhe);
|
|
|
} else {
|
|
|
if (depth > FNHE_RECLAIM_DEPTH)
|
|
|
fnhe = fnhe_oldest(hash);
|
|
@@ -644,6 +662,18 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
|
|
|
fnhe->fnhe_gw = gw;
|
|
|
fnhe->fnhe_pmtu = pmtu;
|
|
|
fnhe->fnhe_expires = expires;
|
|
|
+
|
|
|
+ /* Exception created; mark the cached routes for the nexthop
|
|
|
+ * stale, so anyone caching it rechecks if this exception
|
|
|
+ * applies to them.
|
|
|
+ */
|
|
|
+ for_each_possible_cpu(i) {
|
|
|
+ struct rtable __rcu **prt;
|
|
|
+ prt = per_cpu_ptr(nh->nh_pcpu_rth_output, i);
|
|
|
+ rt = rcu_dereference(*prt);
|
|
|
+ if (rt)
|
|
|
+ rt->dst.obsolete = DST_OBSOLETE_KILL;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
fnhe->fnhe_stamp = jiffies;
|
|
@@ -917,13 +947,6 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
|
|
|
if (mtu < ip_rt_min_pmtu)
|
|
|
mtu = ip_rt_min_pmtu;
|
|
|
|
|
|
- if (!rt->rt_pmtu) {
|
|
|
- dst->obsolete = DST_OBSOLETE_KILL;
|
|
|
- } else {
|
|
|
- rt->rt_pmtu = mtu;
|
|
|
- dst->expires = max(1UL, jiffies + ip_rt_mtu_expires);
|
|
|
- }
|
|
|
-
|
|
|
rcu_read_lock();
|
|
|
if (fib_lookup(dev_net(dst->dev), fl4, &res) == 0) {
|
|
|
struct fib_nh *nh = &FIB_RES_NH(res);
|
|
@@ -1063,11 +1086,11 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
|
|
|
* DST_OBSOLETE_FORCE_CHK which forces validation calls down
|
|
|
* into this function always.
|
|
|
*
|
|
|
- * When a PMTU/redirect information update invalidates a
|
|
|
- * route, this is indicated by setting obsolete to
|
|
|
- * DST_OBSOLETE_KILL.
|
|
|
+ * When a PMTU/redirect information update invalidates a route,
|
|
|
+ * this is indicated by setting obsolete to DST_OBSOLETE_KILL or
|
|
|
+ * DST_OBSOLETE_DEAD by dst_free().
|
|
|
*/
|
|
|
- if (dst->obsolete == DST_OBSOLETE_KILL || rt_is_expired(rt))
|
|
|
+ if (dst->obsolete != DST_OBSOLETE_FORCE_CHK || rt_is_expired(rt))
|
|
|
return NULL;
|
|
|
return dst;
|
|
|
}
|
|
@@ -1215,20 +1238,8 @@ static bool rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe,
|
|
|
fnhe->fnhe_pmtu = 0;
|
|
|
fnhe->fnhe_expires = 0;
|
|
|
}
|
|
|
- if (fnhe->fnhe_pmtu) {
|
|
|
- unsigned long expires = fnhe->fnhe_expires;
|
|
|
- unsigned long diff = expires - jiffies;
|
|
|
-
|
|
|
- if (time_before(jiffies, expires)) {
|
|
|
- rt->rt_pmtu = fnhe->fnhe_pmtu;
|
|
|
- dst_set_expires(&rt->dst, diff);
|
|
|
- }
|
|
|
- }
|
|
|
- if (fnhe->fnhe_gw) {
|
|
|
- rt->rt_flags |= RTCF_REDIRECTED;
|
|
|
- rt->rt_gateway = fnhe->fnhe_gw;
|
|
|
- rt->rt_uses_gateway = 1;
|
|
|
- } else if (!rt->rt_gateway)
|
|
|
+ fill_route_from_fnhe(rt, fnhe);
|
|
|
+ if (!rt->rt_gateway)
|
|
|
rt->rt_gateway = daddr;
|
|
|
|
|
|
rcu_assign_pointer(fnhe->fnhe_rth, rt);
|