|
@@ -1199,23 +1199,31 @@ restart:
|
|
|
fnhe->fnhe_stamp = jiffies;
|
|
|
}
|
|
|
|
|
|
+static inline void rt_free(struct rtable *rt)
|
|
|
+{
|
|
|
+ call_rcu(&rt->dst.rcu_head, dst_rcu_free);
|
|
|
+}
|
|
|
+
|
|
|
static void rt_cache_route(struct fib_nh *nh, struct rtable *rt)
|
|
|
{
|
|
|
- struct rtable *orig, *prev, **p = &nh->nh_rth_output;
|
|
|
+ struct rtable *orig, *prev, **p = (struct rtable **)&nh->nh_rth_output;
|
|
|
|
|
|
if (rt_is_input_route(rt))
|
|
|
- p = &nh->nh_rth_input;
|
|
|
+ p = (struct rtable **)&nh->nh_rth_input;
|
|
|
|
|
|
orig = *p;
|
|
|
|
|
|
- rt->dst.flags |= DST_RCU_FREE;
|
|
|
- dst_hold(&rt->dst);
|
|
|
prev = cmpxchg(p, orig, rt);
|
|
|
if (prev == orig) {
|
|
|
if (orig)
|
|
|
- dst_release(&orig->dst);
|
|
|
+ rt_free(orig);
|
|
|
} else {
|
|
|
- dst_release(&rt->dst);
|
|
|
+ /* Routes we intend to cache in the FIB nexthop have
|
|
|
+ * the DST_NOCACHE bit clear. However, if we are
|
|
|
+ * unsuccessful at storing this route into the cache
|
|
|
+ * we really need to set it.
|
|
|
+ */
|
|
|
+ rt->dst.flags |= DST_NOCACHE;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1412,7 +1420,7 @@ static int __mkroute_input(struct sk_buff *skb,
|
|
|
do_cache = false;
|
|
|
if (res->fi) {
|
|
|
if (!itag) {
|
|
|
- rth = FIB_RES_NH(*res).nh_rth_input;
|
|
|
+ rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);
|
|
|
if (rt_cache_valid(rth)) {
|
|
|
skb_dst_set_noref(skb, &rth->dst);
|
|
|
goto out;
|
|
@@ -1574,7 +1582,7 @@ local_input:
|
|
|
do_cache = false;
|
|
|
if (res.fi) {
|
|
|
if (!itag) {
|
|
|
- rth = FIB_RES_NH(res).nh_rth_input;
|
|
|
+ rth = rcu_dereference(FIB_RES_NH(res).nh_rth_input);
|
|
|
if (rt_cache_valid(rth)) {
|
|
|
skb_dst_set_noref(skb, &rth->dst);
|
|
|
err = 0;
|
|
@@ -1742,7 +1750,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
|
|
|
if (fi) {
|
|
|
fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr);
|
|
|
if (!fnhe) {
|
|
|
- rth = FIB_RES_NH(*res).nh_rth_output;
|
|
|
+ rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_output);
|
|
|
if (rt_cache_valid(rth)) {
|
|
|
dst_hold(&rth->dst);
|
|
|
return rth;
|