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

Commit 2970f2a8 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-metrics-consolidate'



David Ahern says:

====================
net: Consolidate metrics handling for ipv4 and ipv6

As part of the IPv6 fib info refactoring, the intent was to make metrics
handling for ipv6 identical to ipv4. One oversight in ip6_dst_destroy
led to confusion and a couple of incomplete attempts at finding and
fixing the resulting memory leak which was ultimately resolved by
ce7ea4af ("ipv6: fix memory leak on dst->_metrics").

Refactor metrics hanlding make the code really identical for v4 and v6,
and add a few test cases.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d26d4b19 a0e11da7
Loading
Loading
Loading
Loading
+29 −2
Original line number Diff line number Diff line
@@ -420,8 +420,35 @@ static inline unsigned int ip_skb_dst_mtu(struct sock *sk,
	return min(READ_ONCE(skb_dst(skb)->dev->mtu), IP_MAX_MTU);
}

int ip_metrics_convert(struct net *net, struct nlattr *fc_mx, int fc_mx_len,
		       u32 *metrics);
struct dst_metrics *ip_fib_metrics_init(struct net *net, struct nlattr *fc_mx,
					int fc_mx_len);
static inline void ip_fib_metrics_put(struct dst_metrics *fib_metrics)
{
	if (fib_metrics != &dst_default_metrics &&
	    refcount_dec_and_test(&fib_metrics->refcnt))
		kfree(fib_metrics);
}

/* ipv4 and ipv6 both use refcounted metrics if it is not the default */
static inline
void ip_dst_init_metrics(struct dst_entry *dst, struct dst_metrics *fib_metrics)
{
	dst_init_metrics(dst, fib_metrics->metrics, true);

	if (fib_metrics != &dst_default_metrics) {
		dst->_metrics |= DST_METRICS_REFCOUNTED;
		refcount_inc(&fib_metrics->refcnt);
	}
}

static inline
void ip_dst_metrics_put(struct dst_entry *dst)
{
	struct dst_metrics *p = (struct dst_metrics *)DST_METRICS_PTR(dst);

	if (p != &dst_default_metrics && refcount_dec_and_test(&p->refcnt))
		kfree(p);
}

u32 ip_idents_reserve(u32 hash, int segs);
void __ip_select_ident(struct net *net, struct iphdr *iph, int segs);
+9 −24
Original line number Diff line number Diff line
@@ -208,7 +208,6 @@ static void rt_fibinfo_free_cpus(struct rtable __rcu * __percpu *rtp)
static void free_fib_info_rcu(struct rcu_head *head)
{
	struct fib_info *fi = container_of(head, struct fib_info, rcu);
	struct dst_metrics *m;

	change_nexthops(fi) {
		if (nexthop_nh->nh_dev)
@@ -219,9 +218,8 @@ static void free_fib_info_rcu(struct rcu_head *head)
		rt_fibinfo_free(&nexthop_nh->nh_rth_input);
	} endfor_nexthops(fi);

	m = fi->fib_metrics;
	if (m != &dst_default_metrics && refcount_dec_and_test(&m->refcnt))
		kfree(m);
	ip_fib_metrics_put(fi->fib_metrics);

	kfree(fi);
}

@@ -1020,13 +1018,6 @@ static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc)
	return true;
}

static int
fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg)
{
	return ip_metrics_convert(fi->fib_net, cfg->fc_mx, cfg->fc_mx_len,
				  fi->fib_metrics->metrics);
}

struct fib_info *fib_create_info(struct fib_config *cfg,
				 struct netlink_ext_ack *extack)
{
@@ -1084,16 +1075,14 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
	fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
	if (!fi)
		goto failure;
	if (cfg->fc_mx) {
		fi->fib_metrics = kzalloc(sizeof(*fi->fib_metrics), GFP_KERNEL);
		if (unlikely(!fi->fib_metrics)) {
	fi->fib_metrics = ip_fib_metrics_init(fi->fib_net, cfg->fc_mx,
					      cfg->fc_mx_len);
	if (unlikely(IS_ERR(fi->fib_metrics))) {
		err = PTR_ERR(fi->fib_metrics);
		kfree(fi);
		return ERR_PTR(err);
	}
		refcount_set(&fi->fib_metrics->refcnt, 1);
	} else {
		fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics;
	}

	fib_info_cnt++;
	fi->fib_net = net;
	fi->fib_protocol = cfg->fc_protocol;
@@ -1112,10 +1101,6 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
			goto failure;
	} endfor_nexthops(fi)

	err = fib_convert_metrics(fi, cfg);
	if (err)
		goto failure;

	if (cfg->fc_mp) {
#ifdef CONFIG_IP_ROUTE_MULTIPATH
		err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg, extack);
+27 −3
Original line number Diff line number Diff line
@@ -5,8 +5,8 @@
#include <net/net_namespace.h>
#include <net/tcp.h>

int ip_metrics_convert(struct net *net, struct nlattr *fc_mx, int fc_mx_len,
		       u32 *metrics)
static int ip_metrics_convert(struct net *net, struct nlattr *fc_mx,
			      int fc_mx_len, u32 *metrics)
{
	bool ecn_ca = false;
	struct nlattr *nla;
@@ -52,4 +52,28 @@ int ip_metrics_convert(struct net *net, struct nlattr *fc_mx, int fc_mx_len,

	return 0;
}
EXPORT_SYMBOL_GPL(ip_metrics_convert);

struct dst_metrics *ip_fib_metrics_init(struct net *net, struct nlattr *fc_mx,
					int fc_mx_len)
{
	struct dst_metrics *fib_metrics;
	int err;

	if (!fc_mx)
		return (struct dst_metrics *)&dst_default_metrics;

	fib_metrics = kzalloc(sizeof(*fib_metrics), GFP_KERNEL);
	if (unlikely(!fib_metrics))
		return ERR_PTR(-ENOMEM);

	err = ip_metrics_convert(net, fc_mx, fc_mx_len, fib_metrics->metrics);
	if (!err) {
		refcount_set(&fib_metrics->refcnt, 1);
	} else {
		kfree(fib_metrics);
		fib_metrics = ERR_PTR(err);
	}

	return fib_metrics;
}
EXPORT_SYMBOL_GPL(ip_fib_metrics_init);
+3 −9
Original line number Diff line number Diff line
@@ -1476,12 +1476,9 @@ void rt_del_uncached_list(struct rtable *rt)

static void ipv4_dst_destroy(struct dst_entry *dst)
{
	struct dst_metrics *p = (struct dst_metrics *)DST_METRICS_PTR(dst);
	struct rtable *rt = (struct rtable *)dst;

	if (p != &dst_default_metrics && refcount_dec_and_test(&p->refcnt))
		kfree(p);

	ip_dst_metrics_put(dst);
	rt_del_uncached_list(rt);
}

@@ -1528,11 +1525,8 @@ static void rt_set_nexthop(struct rtable *rt, __be32 daddr,
			rt->rt_gateway = nh->nh_gw;
			rt->rt_uses_gateway = 1;
		}
		dst_init_metrics(&rt->dst, fi->fib_metrics->metrics, true);
		if (fi->fib_metrics != &dst_default_metrics) {
			rt->dst._metrics |= DST_METRICS_REFCOUNTED;
			refcount_inc(&fi->fib_metrics->refcnt);
		}
		ip_dst_init_metrics(&rt->dst, fi->fib_metrics);

#ifdef CONFIG_IP_ROUTE_CLASSID
		rt->dst.tclassid = nh->nh_tclassid;
#endif
+2 −6
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <linux/list.h>
#include <linux/slab.h>

#include <net/ip.h>
#include <net/ipv6.h>
#include <net/ndisc.h>
#include <net/addrconf.h>
@@ -160,8 +161,6 @@ struct fib6_info *fib6_info_alloc(gfp_t gfp_flags)
	}

	INIT_LIST_HEAD(&f6i->fib6_siblings);
	f6i->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;

	atomic_inc(&f6i->fib6_ref);

	return f6i;
@@ -171,7 +170,6 @@ void fib6_info_destroy_rcu(struct rcu_head *head)
{
	struct fib6_info *f6i = container_of(head, struct fib6_info, rcu);
	struct rt6_exception_bucket *bucket;
	struct dst_metrics *m;

	WARN_ON(f6i->fib6_node);

@@ -203,9 +201,7 @@ void fib6_info_destroy_rcu(struct rcu_head *head)
	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);
	ip_fib_metrics_put(f6i->fib6_metrics);

	kfree(f6i);
}
Loading