Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93531c67 authored by David Ahern's avatar David Ahern Committed by David S. Miller
Browse files

net/ipv6: separate handling of FIB entries from dst based routes



Last step before flipping the data type for FIB entries:
- use fib6_info_alloc to create FIB entries in ip6_route_info_create
  and addrconf_dst_alloc
- use fib6_info_release in place of dst_release, ip6_rt_put and
  rt6_release
- remove the dst_hold before calling __ip6_ins_rt or ip6_del_rt
- when purging routes, drop per-cpu routes
- replace inc and dec of rt6i_ref with fib6_info_hold and fib6_info_release
- use rt->from since it points to the FIB entry
- drop references to exception bucket, fib6_metrics and per-cpu from
  dst entries (those are relevant for fib entries only)

Signed-off-by: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a64efe14
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -314,9 +314,7 @@ static inline u32 rt6_get_cookie(const struct rt6_info *rt)

	if (rt->rt6i_flags & RTF_PCPU ||
	    (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from))
		rt = rt->from;

	rt6_get_cookie_safe(rt, &cookie);
		rt6_get_cookie_safe(rt->from, &cookie);

	return cookie;
}
+1 −2
Original line number Diff line number Diff line
@@ -114,8 +114,7 @@ static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt,
				      unsigned int prefs,
				      struct in6_addr *saddr)
{
	struct inet6_dev *idev =
			rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
	struct inet6_dev *idev = rt ? rt->rt6i_idev : NULL;
	int err = 0;

	if (rt && rt->rt6i_prefsrc.plen)
+8 −10
Original line number Diff line number Diff line
@@ -916,7 +916,6 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
		pr_warn("Freeing alive inet6 address %p\n", ifp);
		return;
	}
	ip6_rt_put(ifp->rt);

	kfree_rcu(ifp, rcu);
}
@@ -1102,8 +1101,8 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
	inet6addr_notifier_call_chain(NETDEV_UP, ifa);
out:
	if (unlikely(err < 0)) {
		if (rt)
			ip6_rt_put(rt);
		fib6_info_release(rt);

		if (ifa) {
			if (ifa->idev)
				in6_dev_put(ifa->idev);
@@ -1191,7 +1190,7 @@ cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, bool del_r
		else {
			if (!(rt->rt6i_flags & RTF_EXPIRES))
				fib6_set_expires(rt, expires);
			ip6_rt_put(rt);
			fib6_info_release(rt);
		}
	}
}
@@ -2375,8 +2374,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
			continue;
		if ((rt->rt6i_flags & noflags) != 0)
			continue;
		if (!dst_hold_safe(&rt->dst))
			rt = NULL;
		fib6_info_hold(rt);
		break;
	}
out:
@@ -2687,7 +2685,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
			addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
					      dev, expires, flags, GFP_ATOMIC);
		}
		ip6_rt_put(rt);
		fib6_info_release(rt);
	}

	/* Try to figure out our local address for this prefix */
@@ -3361,7 +3359,7 @@ static int fixup_permanent_addr(struct net *net,
		ifp->rt = rt;
		spin_unlock(&ifp->lock);

		ip6_rt_put(prev);
		fib6_info_release(prev);
	}

	if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) {
@@ -5636,8 +5634,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
				ip6_del_rt(net, rt);
		}
		if (ifp->rt) {
			if (dst_hold_safe(&ifp->rt->dst))
			ip6_del_rt(net, ifp->rt);
			ifp->rt = NULL;
		}
		rt_genid_bump_ipv6(net);
		break;
+3 −4
Original line number Diff line number Diff line
@@ -213,7 +213,7 @@ static void aca_put(struct ifacaddr6 *ac)
{
	if (refcount_dec_and_test(&ac->aca_refcnt)) {
		in6_dev_put(ac->aca_idev);
		dst_release(&ac->aca_rt->dst);
		fib6_info_release(ac->aca_rt);
		kfree(ac);
	}
}
@@ -231,6 +231,7 @@ static struct ifacaddr6 *aca_alloc(struct rt6_info *rt,
	aca->aca_addr = *addr;
	in6_dev_hold(idev);
	aca->aca_idev = idev;
	fib6_info_hold(rt);
	aca->aca_rt = rt;
	aca->aca_users = 1;
	/* aca_tstamp should be updated upon changes */
@@ -274,7 +275,7 @@ int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr)
	}
	aca = aca_alloc(rt, addr);
	if (!aca) {
		ip6_rt_put(rt);
		fib6_info_release(rt);
		err = -ENOMEM;
		goto out;
	}
@@ -330,7 +331,6 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr)
	write_unlock_bh(&idev->lock);
	addrconf_leave_solict(idev, &aca->aca_addr);

	dst_hold(&aca->aca_rt->dst);
	ip6_del_rt(dev_net(idev->dev), aca->aca_rt);

	aca_put(aca);
@@ -358,7 +358,6 @@ void ipv6_ac_destroy_dev(struct inet6_dev *idev)

		addrconf_leave_solict(idev, &aca->aca_addr);

		dst_hold(&aca->aca_rt->dst);
		ip6_del_rt(dev_net(idev->dev), aca->aca_rt);

		aca_put(aca);
+36 −19
Original line number Diff line number Diff line
@@ -170,6 +170,7 @@ struct rt6_info *fib6_info_alloc(gfp_t gfp_flags)
void fib6_info_destroy(struct rt6_info *f6i)
{
	struct rt6_exception_bucket *bucket;
	struct dst_metrics *m;

	WARN_ON(f6i->rt6i_node);

@@ -201,6 +202,10 @@ void fib6_info_destroy(struct rt6_info *f6i)
	if (f6i->fib6_nh.nh_dev)
		dev_put(f6i->fib6_nh.nh_dev);

	m = f6i->fib6_metrics;
	if (m != &dst_default_metrics && refcount_dec_and_test(&m->refcnt))
		kfree(m);

	kfree(f6i);
}
EXPORT_SYMBOL_GPL(fib6_info_destroy);
@@ -714,7 +719,7 @@ static struct fib6_node *fib6_add_1(struct net *net,
			/* clean up an intermediate node */
			if (!(fn->fn_flags & RTN_RTINFO)) {
				RCU_INIT_POINTER(fn->leaf, NULL);
				rt6_release(leaf);
				fib6_info_release(leaf);
			/* remove null_entry in the root node */
			} else if (fn->fn_flags & RTN_TL_ROOT &&
				   rcu_access_pointer(fn->leaf) ==
@@ -898,12 +903,32 @@ static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn,
			if (!(fn->fn_flags & RTN_RTINFO) && leaf == rt) {
				new_leaf = fib6_find_prefix(net, table, fn);
				atomic_inc(&new_leaf->rt6i_ref);

				rcu_assign_pointer(fn->leaf, new_leaf);
				rt6_release(rt);
				fib6_info_release(rt);
			}
			fn = rcu_dereference_protected(fn->parent,
				    lockdep_is_held(&table->tb6_lock));
		}

		if (rt->rt6i_pcpu) {
			int cpu;

			/* release the reference to this fib entry from
			 * all of its cached pcpu routes
			 */
			for_each_possible_cpu(cpu) {
				struct rt6_info **ppcpu_rt;
				struct rt6_info *pcpu_rt;

				ppcpu_rt = per_cpu_ptr(rt->rt6i_pcpu, cpu);
				pcpu_rt = *ppcpu_rt;
				if (pcpu_rt) {
					fib6_info_release(pcpu_rt->from);
					pcpu_rt->from = NULL;
				}
			}
		}
	}
}

@@ -1099,7 +1124,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
		fib6_purge_rt(iter, fn, info->nl_net);
		if (rcu_access_pointer(fn->rr_ptr) == iter)
			fn->rr_ptr = NULL;
		rt6_release(iter);
		fib6_info_release(iter);

		if (nsiblings) {
			/* Replacing an ECMP route, remove all siblings */
@@ -1115,7 +1140,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
					fib6_purge_rt(iter, fn, info->nl_net);
					if (rcu_access_pointer(fn->rr_ptr) == iter)
						fn->rr_ptr = NULL;
					rt6_release(iter);
					fib6_info_release(iter);
					nsiblings--;
					info->nl_net->ipv6.rt6_stats->fib_rt_entries--;
				} else {
@@ -1183,9 +1208,6 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
	int replace_required = 0;
	int sernum = fib6_new_sernum(info->nl_net);

	if (WARN_ON_ONCE(!atomic_read(&rt->dst.__refcnt)))
		return -EINVAL;

	if (info->nlh) {
		if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
			allow_create = 0;
@@ -1300,7 +1322,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
			if (pn_leaf == rt) {
				pn_leaf = NULL;
				RCU_INIT_POINTER(pn->leaf, NULL);
				atomic_dec(&rt->rt6i_ref);
				fib6_info_release(rt);
			}
			if (!pn_leaf && !(pn->fn_flags & RTN_RTINFO)) {
				pn_leaf = fib6_find_prefix(info->nl_net, table,
@@ -1312,7 +1334,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
					    info->nl_net->ipv6.fib6_null_entry;
				}
#endif
				atomic_inc(&pn_leaf->rt6i_ref);
				fib6_info_hold(pn_leaf);
				rcu_assign_pointer(pn->leaf, pn_leaf);
			}
		}
@@ -1334,10 +1356,6 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
	     (fn->fn_flags & RTN_TL_ROOT &&
	      !rcu_access_pointer(fn->leaf))))
		fib6_repair_tree(info->nl_net, table, fn);
	/* Always release dst as dst->__refcnt is guaranteed
	 * to be taken before entering this function
	 */
	dst_release_immediate(&rt->dst);
	return err;
}

@@ -1637,7 +1655,7 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
				new_fn_leaf = net->ipv6.fib6_null_entry;
			}
#endif
			atomic_inc(&new_fn_leaf->rt6i_ref);
			fib6_info_hold(new_fn_leaf);
			rcu_assign_pointer(fn->leaf, new_fn_leaf);
			return pn;
		}
@@ -1693,7 +1711,7 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
			return pn;

		RCU_INIT_POINTER(pn->leaf, NULL);
		rt6_release(pn_leaf);
		fib6_info_release(pn_leaf);
		fn = pn;
	}
}
@@ -1763,7 +1781,7 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
	call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, NULL);
	if (!info->skip_notify)
		inet6_rt_notify(RTM_DELROUTE, rt, info, 0);
	rt6_release(rt);
	fib6_info_release(rt);
}

/* Need to own table->tb6_lock */
@@ -2261,9 +2279,8 @@ static int ipv6_route_seq_show(struct seq_file *seq, void *v)

	dev = rt->fib6_nh.nh_dev;
	seq_printf(seq, " %08x %08x %08x %08x %8s\n",
		   rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
		   rt->dst.__use, rt->rt6i_flags,
		   dev ? dev->name : "");
		   rt->rt6i_metric, atomic_read(&rt->rt6i_ref), 0,
		   rt->rt6i_flags, dev ? dev->name : "");
	iter->w.leaf = NULL;
	return 0;
}
Loading