[LEDE-DEV] [PATCH] kernel: Backport net struct reduction from 4.16.

Rosen Penev rosenp at gmail.com
Fri Mar 23 11:19:07 PDT 2018


This patch series shrinks the networking structs in order to reduce cache misses. This has performance benefits in routing.

Full details here: https://www.netdevconf.org/2.2/slides/miller-datastructurebloat-keynote.pdf

I'm keeping this for kernel 4.14 as it's easier to port.

I have tested this on mvebu for several days with no issues to report. mvebu is powerful enough for gigabit routing though.

Signed-off-by: Rosen Penev <rosenp at gmail.com>
---
 .../400-net-dst-rt_next-is-unused.patch            |  28 ++
 ...-Move-dn_next-into-decnet-route-structure.patch | 161 +++++++++++
 ...t6_next-from-dst_entry-into-ipv6-route-st.patch | 304 ++++++++++++++++++++
 ...-Create-and-use-new-helper-xfrm_dst_child.patch | 197 +++++++++++++
 ...e-and-use-new-helpers-for-dst-child-acces.patch | 142 ++++++++++
 ...rm-Move-child-route-linkage-into-xfrm_dst.patch | 216 ++++++++++++++
 ...6-ipv6-Move-dst-from-into-struct-rt6_info.patch | 211 ++++++++++++++
 ...7-xfrm-Move-dst-path-into-struct-xfrm_dst.patch | 310 +++++++++++++++++++++
 ...ge-dst_entry-layout-to-avoid-useless-padd.patch |  96 +++++++
 ...top-using-dst-next-in-bundle-construction.patch | 197 +++++++++++++
 .../backport-4.14/410-net-Remove-dst-next.patch    |  44 +++
 ...jecting-with-source-address-failed-policy.patch |   4 +-
 12 files changed, 1907 insertions(+), 3 deletions(-)
 create mode 100644 target/linux/generic/backport-4.14/400-net-dst-rt_next-is-unused.patch
 create mode 100644 target/linux/generic/backport-4.14/401-decnet-Move-dn_next-into-decnet-route-structure.patch
 create mode 100644 target/linux/generic/backport-4.14/402-ipv6-Move-rt6_next-from-dst_entry-into-ipv6-route-st.patch
 create mode 100644 target/linux/generic/backport-4.14/403-net-Create-and-use-new-helper-xfrm_dst_child.patch
 create mode 100644 target/linux/generic/backport-4.14/404-ipsec-Create-and-use-new-helpers-for-dst-child-acces.patch
 create mode 100644 target/linux/generic/backport-4.14/405-xfrm-Move-child-route-linkage-into-xfrm_dst.patch
 create mode 100644 target/linux/generic/backport-4.14/406-ipv6-Move-dst-from-into-struct-rt6_info.patch
 create mode 100644 target/linux/generic/backport-4.14/407-xfrm-Move-dst-path-into-struct-xfrm_dst.patch
 create mode 100644 target/linux/generic/backport-4.14/408-net-Rearrange-dst_entry-layout-to-avoid-useless-padd.patch
 create mode 100644 target/linux/generic/backport-4.14/409-xfrm-Stop-using-dst-next-in-bundle-construction.patch
 create mode 100644 target/linux/generic/backport-4.14/410-net-Remove-dst-next.patch

diff --git a/target/linux/generic/backport-4.14/400-net-dst-rt_next-is-unused.patch b/target/linux/generic/backport-4.14/400-net-dst-rt_next-is-unused.patch
new file mode 100644
index 0000000000..7355d0af91
--- /dev/null
+++ b/target/linux/generic/backport-4.14/400-net-dst-rt_next-is-unused.patch
@@ -0,0 +1,28 @@
+From bf1b3d8dada83c08d7bb101665a30404bcc855c9 Mon Sep 17 00:00:00 2001
+From: David Miller <davem at davemloft.net>
+Date: Tue, 28 Nov 2017 15:39:59 -0500
+Subject: [PATCH 01/11] net: dst->rt_next is unused.
+
+Delete it.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet at google.com>
+---
+ include/net/dst.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/include/net/dst.h b/include/net/dst.h
+index 694c2e6ae618..fe67595b0846 100644
+--- a/include/net/dst.h
++++ b/include/net/dst.h
+@@ -101,7 +101,6 @@ struct dst_entry {
+ 	struct lwtunnel_state   *lwtstate;
+ 	union {
+ 		struct dst_entry	*next;
+-		struct rtable __rcu	*rt_next;
+ 		struct rt6_info		*rt6_next;
+ 		struct dn_route __rcu	*dn_next;
+ 	};
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/401-decnet-Move-dn_next-into-decnet-route-structure.patch b/target/linux/generic/backport-4.14/401-decnet-Move-dn_next-into-decnet-route-structure.patch
new file mode 100644
index 0000000000..58c30b43a1
--- /dev/null
+++ b/target/linux/generic/backport-4.14/401-decnet-Move-dn_next-into-decnet-route-structure.patch
@@ -0,0 +1,161 @@
+From afd979df91ffb73588a9aa73a1fe0afa4537067f Mon Sep 17 00:00:00 2001
+From: David Miller <davem at davemloft.net>
+Date: Tue, 28 Nov 2017 15:40:08 -0500
+Subject: [PATCH 02/11] decnet: Move dn_next into decnet route structure.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet at google.com>
+---
+ include/net/dn_route.h |  1 +
+ include/net/dst.h      |  1 -
+ net/decnet/dn_route.c  | 34 ++++++++++++++++++----------------
+ 3 files changed, 19 insertions(+), 17 deletions(-)
+
+diff --git a/include/net/dn_route.h b/include/net/dn_route.h
+index 55df9939bca2..342d2503cba5 100644
+--- a/include/net/dn_route.h
++++ b/include/net/dn_route.h
+@@ -69,6 +69,7 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev,
+  */
+ struct dn_route {
+ 	struct dst_entry dst;
++	struct dn_route __rcu *dn_next;
+ 
+ 	struct neighbour *n;
+ 
+diff --git a/include/net/dst.h b/include/net/dst.h
+index fe67595b0846..9fda03dcc527 100644
+--- a/include/net/dst.h
++++ b/include/net/dst.h
+@@ -102,7 +102,6 @@ struct dst_entry {
+ 	union {
+ 		struct dst_entry	*next;
+ 		struct rt6_info		*rt6_next;
+-		struct dn_route __rcu	*dn_next;
+ 	};
+ };
+ 
+diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
+index 0bd3afd01dd2..79744425d333 100644
+--- a/net/decnet/dn_route.c
++++ b/net/decnet/dn_route.c
+@@ -199,11 +199,11 @@ static void dn_dst_check_expire(unsigned long dummy)
+ 						lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
+ 			if (atomic_read(&rt->dst.__refcnt) > 1 ||
+ 			    (now - rt->dst.lastuse) < expire) {
+-				rtp = &rt->dst.dn_next;
++				rtp = &rt->dn_next;
+ 				continue;
+ 			}
+-			*rtp = rt->dst.dn_next;
+-			rt->dst.dn_next = NULL;
++			*rtp = rt->dn_next;
++			rt->dn_next = NULL;
+ 			dst_dev_put(&rt->dst);
+ 			dst_release(&rt->dst);
+ 		}
+@@ -233,11 +233,11 @@ static int dn_dst_gc(struct dst_ops *ops)
+ 						lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
+ 			if (atomic_read(&rt->dst.__refcnt) > 1 ||
+ 			    (now - rt->dst.lastuse) < expire) {
+-				rtp = &rt->dst.dn_next;
++				rtp = &rt->dn_next;
+ 				continue;
+ 			}
+-			*rtp = rt->dst.dn_next;
+-			rt->dst.dn_next = NULL;
++			*rtp = rt->dn_next;
++			rt->dn_next = NULL;
+ 			dst_dev_put(&rt->dst);
+ 			dst_release(&rt->dst);
+ 			break;
+@@ -333,8 +333,8 @@ static int dn_insert_route(struct dn_route *rt, unsigned int hash, struct dn_rou
+ 						lockdep_is_held(&dn_rt_hash_table[hash].lock))) != NULL) {
+ 		if (compare_keys(&rth->fld, &rt->fld)) {
+ 			/* Put it first */
+-			*rthp = rth->dst.dn_next;
+-			rcu_assign_pointer(rth->dst.dn_next,
++			*rthp = rth->dn_next;
++			rcu_assign_pointer(rth->dn_next,
+ 					   dn_rt_hash_table[hash].chain);
+ 			rcu_assign_pointer(dn_rt_hash_table[hash].chain, rth);
+ 
+@@ -345,10 +345,10 @@ static int dn_insert_route(struct dn_route *rt, unsigned int hash, struct dn_rou
+ 			*rp = rth;
+ 			return 0;
+ 		}
+-		rthp = &rth->dst.dn_next;
++		rthp = &rth->dn_next;
+ 	}
+ 
+-	rcu_assign_pointer(rt->dst.dn_next, dn_rt_hash_table[hash].chain);
++	rcu_assign_pointer(rt->dn_next, dn_rt_hash_table[hash].chain);
+ 	rcu_assign_pointer(dn_rt_hash_table[hash].chain, rt);
+ 
+ 	dst_use(&rt->dst, now);
+@@ -369,8 +369,8 @@ static void dn_run_flush(unsigned long dummy)
+ 			goto nothing_to_declare;
+ 
+ 		for(; rt; rt = next) {
+-			next = rcu_dereference_raw(rt->dst.dn_next);
+-			RCU_INIT_POINTER(rt->dst.dn_next, NULL);
++			next = rcu_dereference_raw(rt->dn_next);
++			RCU_INIT_POINTER(rt->dn_next, NULL);
+ 			dst_dev_put(&rt->dst);
+ 			dst_release(&rt->dst);
+ 		}
+@@ -1183,6 +1183,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *o
+ 	if (rt == NULL)
+ 		goto e_nobufs;
+ 
++	rt->dn_next = NULL;
+ 	memset(&rt->fld, 0, sizeof(rt->fld));
+ 	rt->fld.saddr        = oldflp->saddr;
+ 	rt->fld.daddr        = oldflp->daddr;
+@@ -1252,7 +1253,7 @@ static int __dn_route_output_key(struct dst_entry **pprt, const struct flowidn *
+ 	if (!(flags & MSG_TRYHARD)) {
+ 		rcu_read_lock_bh();
+ 		for (rt = rcu_dereference_bh(dn_rt_hash_table[hash].chain); rt;
+-			rt = rcu_dereference_bh(rt->dst.dn_next)) {
++			rt = rcu_dereference_bh(rt->dn_next)) {
+ 			if ((flp->daddr == rt->fld.daddr) &&
+ 			    (flp->saddr == rt->fld.saddr) &&
+ 			    (flp->flowidn_mark == rt->fld.flowidn_mark) &&
+@@ -1448,6 +1449,7 @@ static int dn_route_input_slow(struct sk_buff *skb)
+ 	if (rt == NULL)
+ 		goto e_nobufs;
+ 
++	rt->dn_next = NULL;
+ 	memset(&rt->fld, 0, sizeof(rt->fld));
+ 	rt->rt_saddr      = fld.saddr;
+ 	rt->rt_daddr      = fld.daddr;
+@@ -1529,7 +1531,7 @@ static int dn_route_input(struct sk_buff *skb)
+ 
+ 	rcu_read_lock();
+ 	for(rt = rcu_dereference(dn_rt_hash_table[hash].chain); rt != NULL;
+-	    rt = rcu_dereference(rt->dst.dn_next)) {
++	    rt = rcu_dereference(rt->dn_next)) {
+ 		if ((rt->fld.saddr == cb->src) &&
+ 		    (rt->fld.daddr == cb->dst) &&
+ 		    (rt->fld.flowidn_oif == 0) &&
+@@ -1749,7 +1751,7 @@ int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb)
+ 		rcu_read_lock_bh();
+ 		for(rt = rcu_dereference_bh(dn_rt_hash_table[h].chain), idx = 0;
+ 			rt;
+-			rt = rcu_dereference_bh(rt->dst.dn_next), idx++) {
++			rt = rcu_dereference_bh(rt->dn_next), idx++) {
+ 			if (idx < s_idx)
+ 				continue;
+ 			skb_dst_set(skb, dst_clone(&rt->dst));
+@@ -1795,7 +1797,7 @@ static struct dn_route *dn_rt_cache_get_next(struct seq_file *seq, struct dn_rou
+ {
+ 	struct dn_rt_cache_iter_state *s = seq->private;
+ 
+-	rt = rcu_dereference_bh(rt->dst.dn_next);
++	rt = rcu_dereference_bh(rt->dn_next);
+ 	while (!rt) {
+ 		rcu_read_unlock_bh();
+ 		if (--s->bucket < 0)
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/402-ipv6-Move-rt6_next-from-dst_entry-into-ipv6-route-st.patch b/target/linux/generic/backport-4.14/402-ipv6-Move-rt6_next-from-dst_entry-into-ipv6-route-st.patch
new file mode 100644
index 0000000000..76d5a32222
--- /dev/null
+++ b/target/linux/generic/backport-4.14/402-ipv6-Move-rt6_next-from-dst_entry-into-ipv6-route-st.patch
@@ -0,0 +1,304 @@
+From 826c34f94d78a3f1d6230672ecaeacb2db8b0640 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp at gmail.com>
+Date: Mon, 19 Mar 2018 15:45:41 -0700
+Subject: [PATCH 03/11] ipv6: Move rt6_next from dst_entry into ipv6 route
+ structure.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet at google.com>
+---
+ include/net/dst.h     |  1 -
+ include/net/ip6_fib.h |  1 +
+ net/ipv6/addrconf.c   |  2 +-
+ net/ipv6/ip6_fib.c    | 34 +++++++++++++++++-----------------
+ net/ipv6/route.c      | 20 ++++++++++----------
+ 5 files changed, 29 insertions(+), 29 deletions(-)
+
+diff --git a/include/net/dst.h b/include/net/dst.h
+index 9fda03dcc527..90ea98549d83 100644
+--- a/include/net/dst.h
++++ b/include/net/dst.h
+@@ -101,7 +101,6 @@ struct dst_entry {
+ 	struct lwtunnel_state   *lwtstate;
+ 	union {
+ 		struct dst_entry	*next;
+-		struct rt6_info		*rt6_next;
+ 	};
+ };
+ 
+diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
+index d060d711a624..f35a71ea4c82 100644
+--- a/include/net/ip6_fib.h
++++ b/include/net/ip6_fib.h
+@@ -100,6 +100,7 @@ struct fib6_table;
+ 
+ struct rt6_info {
+ 	struct dst_entry		dst;
++	struct rt6_info			*rt6_next;
+ 
+ 	/*
+ 	 * Tail elements of dst_entry (__refcnt etc.)
+diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
+index 6a76e41e6d51..f03367cd9700 100644
+--- a/net/ipv6/addrconf.c
++++ b/net/ipv6/addrconf.c
+@@ -2328,7 +2328,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
+ 		goto out;
+ 
+ 	noflags |= RTF_CACHE;
+-	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
++	for (rt = fn->leaf; rt; rt = rt->rt6_next) {
+ 		if (rt->dst.dev->ifindex != dev->ifindex)
+ 			continue;
+ 		if ((rt->rt6i_flags & flags) != flags)
+diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
+index e5308d7cbd75..5d71585a5755 100644
+--- a/net/ipv6/ip6_fib.c
++++ b/net/ipv6/ip6_fib.c
+@@ -372,7 +372,7 @@ static int fib6_node_dump(struct fib6_walker *w)
+ {
+ 	struct rt6_info *rt;
+ 
+-	for (rt = w->leaf; rt; rt = rt->dst.rt6_next)
++	for (rt = w->leaf; rt; rt = rt->rt6_next)
+ 		fib6_rt_dump(rt, w->args);
+ 	w->leaf = NULL;
+ 	return 0;
+@@ -421,7 +421,7 @@ static int fib6_dump_node(struct fib6_walker *w)
+ 	int res;
+ 	struct rt6_info *rt;
+ 
+-	for (rt = w->leaf; rt; rt = rt->dst.rt6_next) {
++	for (rt = w->leaf; rt; rt = rt->rt6_next) {
+ 		res = rt6_dump_route(rt, w->args);
+ 		if (res < 0) {
+ 			/* Frame is full, suspend walking */
+@@ -874,7 +874,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
+ 
+ 	ins = &fn->leaf;
+ 
+-	for (iter = fn->leaf; iter; iter = iter->dst.rt6_next) {
++	for (iter = fn->leaf; iter; iter = iter->rt6_next) {
+ 		/*
+ 		 *	Search for duplicates
+ 		 */
+@@ -930,7 +930,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
+ 			break;
+ 
+ next_iter:
+-		ins = &iter->dst.rt6_next;
++		ins = &iter->rt6_next;
+ 	}
+ 
+ 	if (fallback_ins && !found) {
+@@ -958,7 +958,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
+ 					      &sibling->rt6i_siblings);
+ 				break;
+ 			}
+-			sibling = sibling->dst.rt6_next;
++			sibling = sibling->rt6_next;
+ 		}
+ 		/* For each sibling in the list, increment the counter of
+ 		 * siblings. BUG() if counters does not match, list of siblings
+@@ -987,7 +987,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
+ 		if (err)
+ 			return err;
+ 
+-		rt->dst.rt6_next = iter;
++		rt->rt6_next = iter;
+ 		*ins = rt;
+ 		rcu_assign_pointer(rt->rt6i_node, fn);
+ 		atomic_inc(&rt->rt6i_ref);
+@@ -1018,7 +1018,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
+ 
+ 		*ins = rt;
+ 		rcu_assign_pointer(rt->rt6i_node, fn);
+-		rt->dst.rt6_next = iter->dst.rt6_next;
++		rt->rt6_next = iter->rt6_next;
+ 		atomic_inc(&rt->rt6i_ref);
+ 		call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_REPLACE,
+ 					  rt);
+@@ -1037,13 +1037,13 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
+ 
+ 		if (nsiblings) {
+ 			/* Replacing an ECMP route, remove all siblings */
+-			ins = &rt->dst.rt6_next;
++			ins = &rt->rt6_next;
+ 			iter = *ins;
+ 			while (iter) {
+ 				if (iter->rt6i_metric > rt->rt6i_metric)
+ 					break;
+ 				if (rt6_qualify_for_ecmp(iter)) {
+-					*ins = iter->dst.rt6_next;
++					*ins = iter->rt6_next;
+ 					iter->rt6i_node = NULL;
+ 					fib6_purge_rt(iter, fn, info->nl_net);
+ 					if (fn->rr_ptr == iter)
+@@ -1051,7 +1051,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
+ 					rt6_release(iter);
+ 					nsiblings--;
+ 				} else {
+-					ins = &iter->dst.rt6_next;
++					ins = &iter->rt6_next;
+ 				}
+ 				iter = *ins;
+ 			}
+@@ -1536,7 +1536,7 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
+ 	RT6_TRACE("fib6_del_route\n");
+ 
+ 	/* Unlink it */
+-	*rtp = rt->dst.rt6_next;
++	*rtp = rt->rt6_next;
+ 	rt->rt6i_node = NULL;
+ 	net->ipv6.rt6_stats->fib_rt_entries--;
+ 	net->ipv6.rt6_stats->fib_discarded_routes++;
+@@ -1561,14 +1561,14 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
+ 	FOR_WALKERS(net, w) {
+ 		if (w->state == FWS_C && w->leaf == rt) {
+ 			RT6_TRACE("walker %p adjusted by delroute\n", w);
+-			w->leaf = rt->dst.rt6_next;
++			w->leaf = rt->rt6_next;
+ 			if (!w->leaf)
+ 				w->state = FWS_U;
+ 		}
+ 	}
+ 	read_unlock(&net->ipv6.fib6_walker_lock);
+ 
+-	rt->dst.rt6_next = NULL;
++	rt->rt6_next = NULL;
+ 
+ 	/* If it was last route, expunge its radix tree node */
+ 	if (!fn->leaf) {
+@@ -1620,7 +1620,7 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info)
+ 	 *	Walk the leaf entries looking for ourself
+ 	 */
+ 
+-	for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->dst.rt6_next) {
++	for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->rt6_next) {
+ 		if (*rtp == rt) {
+ 			fib6_del_route(fn, rtp, info);
+ 			return 0;
+@@ -1770,7 +1770,7 @@ static int fib6_clean_node(struct fib6_walker *w)
+ 		return 0;
+ 	}
+ 
+-	for (rt = w->leaf; rt; rt = rt->dst.rt6_next) {
++	for (rt = w->leaf; rt; rt = rt->rt6_next) {
+ 		res = c->func(rt, c->arg);
+ 		if (res < 0) {
+ 			w->leaf = rt;
+@@ -2134,7 +2134,7 @@ static int ipv6_route_yield(struct fib6_walker *w)
+ 		return 1;
+ 
+ 	do {
+-		iter->w.leaf = iter->w.leaf->dst.rt6_next;
++		iter->w.leaf = iter->w.leaf->rt6_next;
+ 		iter->skip--;
+ 		if (!iter->skip && iter->w.leaf)
+ 			return 1;
+@@ -2199,7 +2199,7 @@ static void *ipv6_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+ 	if (!v)
+ 		goto iter_table;
+ 
+-	n = ((struct rt6_info *)v)->dst.rt6_next;
++	n = ((struct rt6_info *)v)->rt6_next;
+ 	if (n) {
+ 		++*pos;
+ 		return n;
+diff --git a/net/ipv6/route.c b/net/ipv6/route.c
+index a4a865c8a23c..c1b6148ebba1 100644
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -493,7 +493,7 @@ static inline struct rt6_info *rt6_device_match(struct net *net,
+ 	if (!oif && ipv6_addr_any(saddr))
+ 		goto out;
+ 
+-	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
++	for (sprt = rt; sprt; sprt = sprt->rt6_next) {
+ 		struct net_device *dev = sprt->dst.dev;
+ 
+ 		if (oif) {
+@@ -711,7 +711,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
+ 
+ 	match = NULL;
+ 	cont = NULL;
+-	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
++	for (rt = rr_head; rt; rt = rt->rt6_next) {
+ 		if (rt->rt6i_metric != metric) {
+ 			cont = rt;
+ 			break;
+@@ -720,7 +720,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
+ 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
+ 	}
+ 
+-	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
++	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->rt6_next) {
+ 		if (rt->rt6i_metric != metric) {
+ 			cont = rt;
+ 			break;
+@@ -732,7 +732,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
+ 	if (match || !cont)
+ 		return match;
+ 
+-	for (rt = cont; rt; rt = rt->dst.rt6_next)
++	for (rt = cont; rt; rt = rt->rt6_next)
+ 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
+ 
+ 	return match;
+@@ -752,7 +752,7 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
+ 			     &do_rr);
+ 
+ 	if (do_rr) {
+-		struct rt6_info *next = rt0->dst.rt6_next;
++		struct rt6_info *next = rt0->rt6_next;
+ 
+ 		/* no entries matched; do round-robin */
+ 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
+@@ -1587,7 +1587,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
+ 	read_lock_bh(&table->tb6_lock);
+ 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
+ restart:
+-	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
++	for (rt = fn->leaf; rt; rt = rt->rt6_next) {
+ 		if (rt6_check_expired(rt))
+ 			continue;
+ 		if (rt->dst.error)
+@@ -2307,7 +2307,7 @@ static int ip6_route_del(struct fib6_config *cfg,
+ 			 &cfg->fc_src, cfg->fc_src_len);
+ 
+ 	if (fn) {
+-		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
++		for (rt = fn->leaf; rt; rt = rt->rt6_next) {
+ 			if ((rt->rt6i_flags & RTF_CACHE) &&
+ 			    !(cfg->fc_flags & RTF_CACHE))
+ 				continue;
+@@ -2517,7 +2517,7 @@ static struct rt6_info *rt6_get_route_info(struct net *net,
+ 	if (!fn)
+ 		goto out;
+ 
+-	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
++	for (rt = fn->leaf; rt; rt = rt->rt6_next) {
+ 		if (rt->dst.dev->ifindex != ifindex)
+ 			continue;
+ 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
+@@ -2575,7 +2575,7 @@ struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_dev
+ 		return NULL;
+ 
+ 	read_lock_bh(&table->tb6_lock);
+-	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
++	for (rt = table->tb6_root.leaf; rt; rt = rt->rt6_next) {
+ 		if (dev == rt->dst.dev &&
+ 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
+ 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
+@@ -2622,7 +2622,7 @@ static void __rt6_purge_dflt_routers(struct fib6_table *table)
+ 
+ restart:
+ 	read_lock_bh(&table->tb6_lock);
+-	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
++	for (rt = table->tb6_root.leaf; rt; rt = rt->rt6_next) {
+ 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
+ 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
+ 			dst_hold(&rt->dst);
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/403-net-Create-and-use-new-helper-xfrm_dst_child.patch b/target/linux/generic/backport-4.14/403-net-Create-and-use-new-helper-xfrm_dst_child.patch
new file mode 100644
index 0000000000..ab94384eb9
--- /dev/null
+++ b/target/linux/generic/backport-4.14/403-net-Create-and-use-new-helper-xfrm_dst_child.patch
@@ -0,0 +1,197 @@
+From 7f635fe85dd9d3bbd8e0de42eeec820ad1894c60 Mon Sep 17 00:00:00 2001
+From: David Miller <davem at davemloft.net>
+Date: Tue, 28 Nov 2017 15:40:22 -0500
+Subject: [PATCH 04/11] net: Create and use new helper xfrm_dst_child().
+
+Only IPSEC routes have a non-NULL dst->child pointer.  And IPSEC
+routes are identified by a non-NULL dst->xfrm pointer.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ include/net/xfrm.h           |  9 +++++++++
+ net/core/dst.c               |  8 +++++---
+ net/ipv4/xfrm4_mode_tunnel.c |  2 +-
+ net/ipv6/xfrm6_mode_tunnel.c |  2 +-
+ net/ipv6/xfrm6_policy.c      |  2 +-
+ net/xfrm/xfrm_output.c       |  2 +-
+ net/xfrm/xfrm_policy.c       | 14 +++++++-------
+ security/selinux/xfrm.c      |  2 +-
+ 8 files changed, 26 insertions(+), 15 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index db99efb2d1d0..ba329f691831 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -994,6 +994,15 @@ struct xfrm_dst {
+ 	u32 path_cookie;
+ };
+ 
++static inline struct dst_entry *xfrm_dst_child(const struct dst_entry *dst)
++{
++#ifdef CONFIG_XFRM
++	if (dst->xfrm)
++		return dst->child;
++#endif
++	return NULL;
++}
++
+ #ifdef CONFIG_XFRM
+ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
+ {
+diff --git a/net/core/dst.c b/net/core/dst.c
+index a6c47da7d0f8..d1d1cdaefbf8 100644
+--- a/net/core/dst.c
++++ b/net/core/dst.c
+@@ -116,12 +116,14 @@ EXPORT_SYMBOL(dst_alloc);
+ 
+ struct dst_entry *dst_destroy(struct dst_entry * dst)
+ {
+-	struct dst_entry *child;
++	struct dst_entry *child = NULL;
+ 
+ 	smp_rmb();
+ 
+-	child = dst->child;
+-
++#ifdef CONFIG_XFRM
++	if (dst->xfrm)
++		child = dst->child;
++#endif
+ 	if (!(dst->flags & DST_NOCOUNT))
+ 		dst_entries_add(dst->ops, -1);
+ 
+diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
+index e6265e2c274e..7d885a44dc9d 100644
+--- a/net/ipv4/xfrm4_mode_tunnel.c
++++ b/net/ipv4/xfrm4_mode_tunnel.c
+@@ -62,7 +62,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
+ 	top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
+ 		0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
+ 
+-	top_iph->ttl = ip4_dst_hoplimit(dst->child);
++	top_iph->ttl = ip4_dst_hoplimit(xfrm_dst_child(dst));
+ 
+ 	top_iph->saddr = x->props.saddr.a4;
+ 	top_iph->daddr = x->id.daddr.a4;
+diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
+index 02556e356f87..e66b94f46532 100644
+--- a/net/ipv6/xfrm6_mode_tunnel.c
++++ b/net/ipv6/xfrm6_mode_tunnel.c
+@@ -59,7 +59,7 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
+ 	if (x->props.flags & XFRM_STATE_NOECN)
+ 		dsfield &= ~INET_ECN_MASK;
+ 	ipv6_change_dsfield(top_iph, 0, dsfield);
+-	top_iph->hop_limit = ip6_dst_hoplimit(dst->child);
++	top_iph->hop_limit = ip6_dst_hoplimit(xfrm_dst_child(dst));
+ 	top_iph->saddr = *(struct in6_addr *)&x->props.saddr;
+ 	top_iph->daddr = *(struct in6_addr *)&x->id.daddr;
+ 	return 0;
+diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
+index 17e95a0386b3..e38d0b27fa2c 100644
+--- a/net/ipv6/xfrm6_policy.c
++++ b/net/ipv6/xfrm6_policy.c
+@@ -264,7 +264,7 @@ static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
+ 			in6_dev_put(xdst->u.rt6.rt6i_idev);
+ 			xdst->u.rt6.rt6i_idev = loopback_idev;
+ 			in6_dev_hold(loopback_idev);
+-			xdst = (struct xfrm_dst *)xdst->u.dst.child;
++			xdst = (struct xfrm_dst *)xfrm_dst_child(&xdst->u.dst);
+ 		} while (xdst->u.dst.xfrm);
+ 
+ 		__in6_dev_put(loopback_idev);
+diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
+index 73ad8c8ef344..23468672a767 100644
+--- a/net/xfrm/xfrm_output.c
++++ b/net/xfrm/xfrm_output.c
+@@ -44,7 +44,7 @@ static int xfrm_skb_check_space(struct sk_buff *skb)
+ 
+ static struct dst_entry *skb_dst_pop(struct sk_buff *skb)
+ {
+-	struct dst_entry *child = dst_clone(skb_dst(skb)->child);
++	struct dst_entry *child = dst_clone(xfrm_dst_child(skb_dst(skb)));
+ 
+ 	skb_dst_drop(skb);
+ 	return child;
+diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
+index 7d17c207fc8a..10ee664e6a89 100644
+--- a/net/xfrm/xfrm_policy.c
++++ b/net/xfrm/xfrm_policy.c
+@@ -1642,7 +1642,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+ 	xfrm_init_path((struct xfrm_dst *)dst0, dst, nfheader_len);
+ 	xfrm_init_pmtu(dst_prev);
+ 
+-	for (dst_prev = dst0; dst_prev != dst; dst_prev = dst_prev->child) {
++	for (dst_prev = dst0; dst_prev != dst; dst_prev = xfrm_dst_child(dst_prev)) {
+ 		struct xfrm_dst *xdst = (struct xfrm_dst *)dst_prev;
+ 
+ 		err = xfrm_fill_dst(xdst, dev, fl);
+@@ -1808,7 +1808,7 @@ static bool xfrm_xdst_can_reuse(struct xfrm_dst *xdst,
+ 	for (i = 0; i < num; i++) {
+ 		if (!dst || dst->xfrm != xfrm[i])
+ 			return false;
+-		dst = dst->child;
++		dst = xfrm_dst_child(dst);
+ 	}
+ 
+ 	return xfrm_bundle_ok(xdst);
+@@ -2590,7 +2590,7 @@ static int stale_bundle(struct dst_entry *dst)
+ 
+ void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
+ {
+-	while ((dst = dst->child) && dst->xfrm && dst->dev == dev) {
++	while ((dst = xfrm_dst_child(dst)) && dst->xfrm && dst->dev == dev) {
+ 		dst->dev = dev_net(dev)->loopback_dev;
+ 		dev_hold(dst->dev);
+ 		dev_put(dev);
+@@ -2620,7 +2620,7 @@ static void xfrm_init_pmtu(struct dst_entry *dst)
+ 		struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
+ 		u32 pmtu, route_mtu_cached;
+ 
+-		pmtu = dst_mtu(dst->child);
++		pmtu = dst_mtu(xfrm_dst_child(dst));
+ 		xdst->child_mtu_cached = pmtu;
+ 
+ 		pmtu = xfrm_state_mtu(dst->xfrm, pmtu);
+@@ -2665,7 +2665,7 @@ static int xfrm_bundle_ok(struct xfrm_dst *first)
+ 		    xdst->policy_genid != atomic_read(&xdst->pols[0]->genid))
+ 			return 0;
+ 
+-		mtu = dst_mtu(dst->child);
++		mtu = dst_mtu(xfrm_dst_child(dst));
+ 		if (xdst->child_mtu_cached != mtu) {
+ 			last = xdst;
+ 			xdst->child_mtu_cached = mtu;
+@@ -2679,7 +2679,7 @@ static int xfrm_bundle_ok(struct xfrm_dst *first)
+ 			xdst->route_mtu_cached = mtu;
+ 		}
+ 
+-		dst = dst->child;
++		dst = xfrm_dst_child(dst);
+ 	} while (dst->xfrm);
+ 
+ 	if (likely(!last))
+@@ -2721,7 +2721,7 @@ static const void *xfrm_get_dst_nexthop(const struct dst_entry *dst,
+ {
+ 	const struct dst_entry *path = dst->path;
+ 
+-	for (; dst != path; dst = dst->child) {
++	for (; dst != path; dst = xfrm_dst_child(dst)) {
+ 		const struct xfrm_state *xfrm = dst->xfrm;
+ 
+ 		if (xfrm->props.mode == XFRM_MODE_TRANSPORT)
+diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
+index 56e354fcdfc6..928188902901 100644
+--- a/security/selinux/xfrm.c
++++ b/security/selinux/xfrm.c
+@@ -452,7 +452,7 @@ int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
+ 	if (dst) {
+ 		struct dst_entry *iter;
+ 
+-		for (iter = dst; iter != NULL; iter = iter->child) {
++		for (iter = dst; iter != NULL; iter = xfrm_dst_child(iter)) {
+ 			struct xfrm_state *x = iter->xfrm;
+ 
+ 			if (x && selinux_authorizable_xfrm(x))
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/404-ipsec-Create-and-use-new-helpers-for-dst-child-acces.patch b/target/linux/generic/backport-4.14/404-ipsec-Create-and-use-new-helpers-for-dst-child-acces.patch
new file mode 100644
index 0000000000..7a56288c5a
--- /dev/null
+++ b/target/linux/generic/backport-4.14/404-ipsec-Create-and-use-new-helpers-for-dst-child-acces.patch
@@ -0,0 +1,142 @@
+From 855c6661c65e5680c21303a8984b918ac8bd96b5 Mon Sep 17 00:00:00 2001
+From: David Miller <davem at davemloft.net>
+Date: Tue, 28 Nov 2017 15:40:28 -0500
+Subject: [PATCH 05/11] ipsec: Create and use new helpers for dst child access.
+
+This will make a future change moving the dst->child pointer less
+invasive.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet at google.com>
+---
+ include/net/xfrm.h     |  5 +++++
+ net/xfrm/xfrm_policy.c | 47 +++++++++++++++++++++++------------------------
+ 2 files changed, 28 insertions(+), 24 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index ba329f691831..56c8f461e904 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -1004,6 +1004,11 @@ static inline struct dst_entry *xfrm_dst_child(const struct dst_entry *dst)
+ }
+ 
+ #ifdef CONFIG_XFRM
++static inline void xfrm_dst_set_child(struct xfrm_dst *xdst, struct dst_entry *child)
++{
++	xdst->u.dst.child = child;
++}
++
+ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
+ {
+ 	xfrm_pols_put(xdst->pols, xdst->num_pols);
+diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
+index 10ee664e6a89..98d45fb616fb 100644
+--- a/net/xfrm/xfrm_policy.c
++++ b/net/xfrm/xfrm_policy.c
+@@ -1552,8 +1552,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+ 	unsigned long now = jiffies;
+ 	struct net_device *dev;
+ 	struct xfrm_mode *inner_mode;
+-	struct dst_entry *dst_prev = NULL;
+-	struct dst_entry *dst0 = NULL;
++	struct xfrm_dst *xdst_prev = NULL;
++	struct xfrm_dst *xdst0 = NULL;
+ 	int i = 0;
+ 	int err;
+ 	int header_len = 0;
+@@ -1579,13 +1579,13 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+ 			goto put_states;
+ 		}
+ 
+-		if (!dst_prev)
+-			dst0 = dst1;
++		if (!xdst_prev)
++			xdst0 = xdst;
+ 		else
+ 			/* Ref count is taken during xfrm_alloc_dst()
+ 			 * No need to do dst_clone() on dst1
+ 			 */
+-			dst_prev->child = dst1;
++			xfrm_dst_set_child(xdst_prev, &xdst->u.dst);
+ 
+ 		if (xfrm[i]->sel.family == AF_UNSPEC) {
+ 			inner_mode = xfrm_ip2inner_mode(xfrm[i],
+@@ -1622,8 +1622,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+ 		dst1->input = dst_discard;
+ 		dst1->output = inner_mode->afinfo->output;
+ 
+-		dst1->next = dst_prev;
+-		dst_prev = dst1;
++		dst1->next = &xdst_prev->u.dst;
++		xdst_prev = xdst;
+ 
+ 		header_len += xfrm[i]->props.header_len;
+ 		if (xfrm[i]->type->flags & XFRM_TYPE_NON_FRAGMENT)
+@@ -1631,40 +1631,39 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+ 		trailer_len += xfrm[i]->props.trailer_len;
+ 	}
+ 
+-	dst_prev->child = dst;
+-	dst0->path = dst;
++	xfrm_dst_set_child(xdst_prev, dst);
++	xdst0->u.dst.path = dst;
+ 
+ 	err = -ENODEV;
+ 	dev = dst->dev;
+ 	if (!dev)
+ 		goto free_dst;
+ 
+-	xfrm_init_path((struct xfrm_dst *)dst0, dst, nfheader_len);
+-	xfrm_init_pmtu(dst_prev);
++	xfrm_init_path(xdst0, dst, nfheader_len);
++	xfrm_init_pmtu(&xdst_prev->u.dst);
+ 
+-	for (dst_prev = dst0; dst_prev != dst; dst_prev = xfrm_dst_child(dst_prev)) {
+-		struct xfrm_dst *xdst = (struct xfrm_dst *)dst_prev;
+-
+-		err = xfrm_fill_dst(xdst, dev, fl);
++	for (xdst_prev = xdst0; xdst_prev != (struct xfrm_dst *)dst;
++	     xdst_prev = (struct xfrm_dst *) xfrm_dst_child(&xdst_prev->u.dst)) {
++		err = xfrm_fill_dst(xdst_prev, dev, fl);
+ 		if (err)
+ 			goto free_dst;
+ 
+-		dst_prev->header_len = header_len;
+-		dst_prev->trailer_len = trailer_len;
+-		header_len -= xdst->u.dst.xfrm->props.header_len;
+-		trailer_len -= xdst->u.dst.xfrm->props.trailer_len;
++		xdst_prev->u.dst.header_len = header_len;
++		xdst_prev->u.dst.trailer_len = trailer_len;
++		header_len -= xdst_prev->u.dst.xfrm->props.header_len;
++		trailer_len -= xdst_prev->u.dst.xfrm->props.trailer_len;
+ 	}
+ 
+ out:
+-	return dst0;
++	return &xdst0->u.dst;
+ 
+ put_states:
+ 	for (; i < nx; i++)
+ 		xfrm_state_put(xfrm[i]);
+ free_dst:
+-	if (dst0)
+-		dst_release_immediate(dst0);
+-	dst0 = ERR_PTR(err);
++	if (xdst0)
++		dst_release_immediate(&xdst0->u.dst);
++	xdst0 = ERR_PTR(err);
+ 	goto out;
+ }
+ 
+@@ -2020,7 +2019,7 @@ static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,
+ 	dst1->output = xdst_queue_output;
+ 
+ 	dst_hold(dst);
+-	dst1->child = dst;
++	xfrm_dst_set_child(xdst, dst);
+ 	dst1->path = dst;
+ 
+ 	xfrm_init_path((struct xfrm_dst *)dst1, dst, 0);
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/405-xfrm-Move-child-route-linkage-into-xfrm_dst.patch b/target/linux/generic/backport-4.14/405-xfrm-Move-child-route-linkage-into-xfrm_dst.patch
new file mode 100644
index 0000000000..a691b67bb8
--- /dev/null
+++ b/target/linux/generic/backport-4.14/405-xfrm-Move-child-route-linkage-into-xfrm_dst.patch
@@ -0,0 +1,216 @@
+From 89de70e2c4dba7f1911a91817349099eee14389e Mon Sep 17 00:00:00 2001
+From: David Miller <davem at davemloft.net>
+Date: Tue, 28 Nov 2017 15:45:44 -0500
+Subject: [PATCH 06/11] xfrm: Move child route linkage into xfrm_dst.
+
+XFRM bundle child chains look like this:
+
+	xdst1 --> xdst2 --> xdst3 --> path_dst
+
+All of xdstN are xfrm_dst objects and xdst->u.dst.xfrm is non-NULL.
+The final child pointer in the chain, here called 'path_dst', is some
+other kind of route such as an ipv4 or ipv6 one.
+
+The xfrm output path pops routes, one at a time, via the child
+pointer, until we hit one which has a dst->xfrm pointer which
+is NULL.
+
+We can easily preserve the above mechanisms with child sitting
+only in the xfrm_dst structure.  All children in the chain
+before we break out of the xfrm_output() loop have dst->xfrm
+non-NULL and are therefore xfrm_dst objects.
+
+Since we break out of the loop when we find dst->xfrm NULL, we
+will not try to dereference 'dst' as if it were an xfrm_dst.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ include/net/dst.h         |  3 +--
+ include/net/xfrm.h        | 15 ++++++++++-----
+ net/core/dst.c            |  9 ++++++---
+ net/core/pktgen.c         | 12 ++++++------
+ net/netfilter/xt_policy.c |  3 ++-
+ net/xfrm/xfrm_device.c    |  2 +-
+ 6 files changed, 26 insertions(+), 18 deletions(-)
+
+diff --git a/include/net/dst.h b/include/net/dst.h
+index 90ea98549d83..fdd99f90d3c5 100644
+--- a/include/net/dst.h
++++ b/include/net/dst.h
+@@ -35,7 +35,6 @@ struct sk_buff;
+ struct dst_entry {
+ 	struct net_device       *dev;
+ 	struct rcu_head		rcu_head;
+-	struct dst_entry	*child;
+ 	struct  dst_ops	        *ops;
+ 	unsigned long		_metrics;
+ 	unsigned long           expires;
+@@ -89,7 +88,7 @@ struct dst_entry {
+ 	 * Align __refcnt to a 64 bytes alignment
+ 	 * (L1_CACHE_SIZE would be too much)
+ 	 */
+-	long			__pad_to_align_refcnt[2];
++	long			__pad_to_align_refcnt[3];
+ #endif
+ 	/*
+ 	 * __refcnt wants to be on a different cache line from
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index 56c8f461e904..e76770544d11 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -968,7 +968,7 @@ static inline bool xfrm_sec_ctx_match(struct xfrm_sec_ctx *s1, struct xfrm_sec_c
+ 
+ /* A struct encoding bundle of transformations to apply to some set of flow.
+  *
+- * dst->child points to the next element of bundle.
++ * xdst->child points to the next element of bundle.
+  * dst->xfrm  points to an instanse of transformer.
+  *
+  * Due to unfortunate limitations of current routing cache, which we
+@@ -984,6 +984,7 @@ struct xfrm_dst {
+ 		struct rt6_info		rt6;
+ 	} u;
+ 	struct dst_entry *route;
++	struct dst_entry *child;
+ 	struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
+ 	int num_pols, num_xfrms;
+ 	u32 xfrm_genid;
+@@ -997,8 +998,10 @@ struct xfrm_dst {
+ static inline struct dst_entry *xfrm_dst_child(const struct dst_entry *dst)
+ {
+ #ifdef CONFIG_XFRM
+-	if (dst->xfrm)
+-		return dst->child;
++	if (dst->xfrm) {
++		struct xfrm_dst *xdst = (struct xfrm_dst *) dst;
++		return xdst->child;
++	}
+ #endif
+ 	return NULL;
+ }
+@@ -1006,7 +1009,7 @@ static inline struct dst_entry *xfrm_dst_child(const struct dst_entry *dst)
+ #ifdef CONFIG_XFRM
+ static inline void xfrm_dst_set_child(struct xfrm_dst *xdst, struct dst_entry *child)
+ {
+-	xdst->u.dst.child = child;
++	xdst->child = child;
+ }
+ 
+ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
+@@ -1883,12 +1886,14 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);
+ static inline bool xfrm_dst_offload_ok(struct dst_entry *dst)
+ {
+ 	struct xfrm_state *x = dst->xfrm;
++	struct xfrm_dst *xdst;
+ 
+ 	if (!x || !x->type_offload)
+ 		return false;
+ 
++	xdst = (struct xfrm_dst *) dst;
+ 	if (x->xso.offload_handle && (x->xso.dev == dst->path->dev) &&
+-	    !dst->child->xfrm)
++	    !xdst->child->xfrm)
+ 		return true;
+ 
+ 	return false;
+diff --git a/net/core/dst.c b/net/core/dst.c
+index d1d1cdaefbf8..1de656b77ed2 100644
+--- a/net/core/dst.c
++++ b/net/core/dst.c
+@@ -21,6 +21,7 @@
+ #include <linux/sched.h>
+ #include <linux/prefetch.h>
+ #include <net/lwtunnel.h>
++#include <net/xfrm.h>
+ 
+ #include <net/dst.h>
+ #include <net/dst_metadata.h>
+@@ -62,7 +63,6 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops,
+ 	      struct net_device *dev, int initial_ref, int initial_obsolete,
+ 	      unsigned short flags)
+ {
+-	dst->child = NULL;
+ 	dst->dev = dev;
+ 	if (dev)
+ 		dev_hold(dev);
+@@ -121,8 +121,11 @@ struct dst_entry *dst_destroy(struct dst_entry * dst)
+ 	smp_rmb();
+ 
+ #ifdef CONFIG_XFRM
+-	if (dst->xfrm)
+-		child = dst->child;
++	if (dst->xfrm) {
++		struct xfrm_dst *xdst = (struct xfrm_dst *) dst;
++
++		child = xdst->child;
++	}
+ #endif
+ 	if (!(dst->flags & DST_NOCOUNT))
+ 		dst_entries_add(dst->ops, -1);
+diff --git a/net/core/pktgen.c b/net/core/pktgen.c
+index 6e1e10ff433a..099b0a2f6bb2 100644
+--- a/net/core/pktgen.c
++++ b/net/core/pktgen.c
+@@ -399,7 +399,7 @@ struct pktgen_dev {
+ 	__u8	ipsmode;		/* IPSEC mode (config) */
+ 	__u8	ipsproto;		/* IPSEC type (config) */
+ 	__u32	spi;
+-	struct dst_entry dst;
++	struct xfrm_dst xdst;
+ 	struct dst_ops dstops;
+ #endif
+ 	char result[512];
+@@ -2609,7 +2609,7 @@ static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
+ 	 * supports both transport/tunnel mode + ESP/AH type.
+ 	 */
+ 	if ((x->props.mode == XFRM_MODE_TUNNEL) && (pkt_dev->spi != 0))
+-		skb->_skb_refdst = (unsigned long)&pkt_dev->dst | SKB_DST_NOREF;
++		skb->_skb_refdst = (unsigned long)&pkt_dev->xdst.u.dst | SKB_DST_NOREF;
+ 
+ 	rcu_read_lock_bh();
+ 	err = x->outer_mode->output(x, skb);
+@@ -3734,10 +3734,10 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
+ 	 * performance under such circumstance.
+ 	 */
+ 	pkt_dev->dstops.family = AF_INET;
+-	pkt_dev->dst.dev = pkt_dev->odev;
+-	dst_init_metrics(&pkt_dev->dst, pktgen_dst_metrics, false);
+-	pkt_dev->dst.child = &pkt_dev->dst;
+-	pkt_dev->dst.ops = &pkt_dev->dstops;
++	pkt_dev->xdst.u.dst.dev = pkt_dev->odev;
++	dst_init_metrics(&pkt_dev->xdst.u.dst, pktgen_dst_metrics, false);
++	pkt_dev->xdst.child = &pkt_dev->xdst.u.dst;
++	pkt_dev->xdst.u.dst.ops = &pkt_dev->dstops;
+ #endif
+ 
+ 	return add_dev_to_thread(t, pkt_dev);
+diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c
+index 2b4ab189bba7..5639fb03bdd9 100644
+--- a/net/netfilter/xt_policy.c
++++ b/net/netfilter/xt_policy.c
+@@ -93,7 +93,8 @@ match_policy_out(const struct sk_buff *skb, const struct xt_policy_info *info,
+ 	if (dst->xfrm == NULL)
+ 		return -1;
+ 
+-	for (i = 0; dst && dst->xfrm; dst = dst->child, i++) {
++	for (i = 0; dst && dst->xfrm;
++	     dst = ((struct xfrm_dst *)dst)->child, i++) {
+ 		pos = strict ? i : 0;
+ 		if (pos >= info->len)
+ 			return 0;
+diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
+index 30e5746085b8..c5851ddddd2a 100644
+--- a/net/xfrm/xfrm_device.c
++++ b/net/xfrm/xfrm_device.c
+@@ -121,7 +121,7 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
+ 		return false;
+ 
+ 	if ((x->xso.offload_handle && (dev == dst->path->dev)) &&
+-	     !dst->child->xfrm && x->type->get_mtu) {
++	     !xdst->child->xfrm && x->type->get_mtu) {
+ 		mtu = x->type->get_mtu(x, xdst->child_mtu_cached);
+ 
+ 		if (skb->len <= mtu)
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/406-ipv6-Move-dst-from-into-struct-rt6_info.patch b/target/linux/generic/backport-4.14/406-ipv6-Move-dst-from-into-struct-rt6_info.patch
new file mode 100644
index 0000000000..4add309411
--- /dev/null
+++ b/target/linux/generic/backport-4.14/406-ipv6-Move-dst-from-into-struct-rt6_info.patch
@@ -0,0 +1,211 @@
+From cd598e3bd215a60a5cbf98b6af967157dfa52a49 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp at gmail.com>
+Date: Mon, 19 Mar 2018 16:08:56 -0700
+Subject: [PATCH 07/11] ipv6: Move dst->from into struct rt6_info.
+
+Any time we clone or copy a core ipv6 route in the ipv6 routing
+tables, we have the copy/clone's ->from point to the base route.
+
+This is used to handle route expiration properly.
+
+Only ipv6 uses this mechanism, and only ipv6 code references
+it.  So it is safe to move it into rt6_info.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet at google.com>
+---
+ include/net/dst.h     |  3 +--
+ include/net/ip6_fib.h |  8 ++++----
+ net/core/dst.c        |  1 -
+ net/ipv6/route.c      | 35 +++++++++++++++++------------------
+ 4 files changed, 22 insertions(+), 25 deletions(-)
+
+diff --git a/include/net/dst.h b/include/net/dst.h
+index fdd99f90d3c5..a43f37837c36 100644
+--- a/include/net/dst.h
++++ b/include/net/dst.h
+@@ -39,7 +39,6 @@ struct dst_entry {
+ 	unsigned long		_metrics;
+ 	unsigned long           expires;
+ 	struct dst_entry	*path;
+-	struct dst_entry	*from;
+ #ifdef CONFIG_XFRM
+ 	struct xfrm_state	*xfrm;
+ #else
+@@ -88,7 +87,7 @@ struct dst_entry {
+ 	 * Align __refcnt to a 64 bytes alignment
+ 	 * (L1_CACHE_SIZE would be too much)
+ 	 */
+-	long			__pad_to_align_refcnt[3];
++	long			__pad_to_align_refcnt[4];
+ #endif
+ 	/*
+ 	 * __refcnt wants to be on a different cache line from
+diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
+index f35a71ea4c82..cdf3e1f1ca65 100644
+--- a/include/net/ip6_fib.h
++++ b/include/net/ip6_fib.h
+@@ -101,6 +101,7 @@ struct fib6_table;
+ struct rt6_info {
+ 	struct dst_entry		dst;
+ 	struct rt6_info			*rt6_next;
++	struct rt6_info			*from;
+ 
+ 	/*
+ 	 * Tail elements of dst_entry (__refcnt etc.)
+@@ -164,8 +165,7 @@ static inline void rt6_update_expires(struct rt6_info *rt0, int timeout)
+ {
+ 	struct rt6_info *rt;
+ 
+-	for (rt = rt0; rt && !(rt->rt6i_flags & RTF_EXPIRES);
+-	     rt = (struct rt6_info *)rt->dst.from);
++	for (rt = rt0; rt && !(rt->rt6i_flags & RTF_EXPIRES); rt = rt->from);
+ 	if (rt && rt != rt0)
+ 		rt0->dst.expires = rt->dst.expires;
+ 
+@@ -201,8 +201,8 @@ static inline u32 rt6_get_cookie(const struct rt6_info *rt)
+ 	u32 cookie = 0;
+ 
+ 	if (rt->rt6i_flags & RTF_PCPU ||
+-	    (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->dst.from))
+-		rt = (struct rt6_info *)(rt->dst.from);
++	    (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from))
++		rt = rt->from;
+ 
+ 	rt6_get_cookie_safe(rt, &cookie);
+ 
+diff --git a/net/core/dst.c b/net/core/dst.c
+index 1de656b77ed2..d17d51b9ac9a 100644
+--- a/net/core/dst.c
++++ b/net/core/dst.c
+@@ -70,7 +70,6 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops,
+ 	dst_init_metrics(dst, dst_default_metrics.metrics, true);
+ 	dst->expires = 0UL;
+ 	dst->path = dst;
+-	dst->from = NULL;
+ #ifdef CONFIG_XFRM
+ 	dst->xfrm = NULL;
+ #endif
+diff --git a/net/ipv6/route.c b/net/ipv6/route.c
+index c1b6148ebba1..4e8b24d5f9c6 100644
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -180,7 +180,7 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
+ 
+ static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt)
+ {
+-	return dst_metrics_write_ptr(rt->dst.from);
++	return dst_metrics_write_ptr(&rt->from->dst);
+ }
+ 
+ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
+@@ -392,7 +392,7 @@ EXPORT_SYMBOL(ip6_dst_alloc);
+ static void ip6_dst_destroy(struct dst_entry *dst)
+ {
+ 	struct rt6_info *rt = (struct rt6_info *)dst;
+-	struct dst_entry *from = dst->from;
++	struct rt6_info *from = rt->from;
+ 	struct inet6_dev *idev;
+ 
+ 	dst_destroy_metrics_generic(dst);
+@@ -405,8 +405,8 @@ static void ip6_dst_destroy(struct dst_entry *dst)
+ 		in6_dev_put(idev);
+ 	}
+ 
+-	dst->from = NULL;
+-	dst_release(from);
++	rt->from = NULL;
++	dst_release(&from->dst);
+ }
+ 
+ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
+@@ -439,9 +439,9 @@ static bool rt6_check_expired(const struct rt6_info *rt)
+ 	if (rt->rt6i_flags & RTF_EXPIRES) {
+ 		if (time_after(jiffies, rt->dst.expires))
+ 			return true;
+-	} else if (rt->dst.from) {
++	} else if (rt->from) {
+ 		return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
+-		       rt6_check_expired((struct rt6_info *)rt->dst.from);
++		       rt6_check_expired(rt->from);
+ 	}
+ 	return false;
+ }
+@@ -990,7 +990,7 @@ static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
+ 	 */
+ 
+ 	if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU))
+-		ort = (struct rt6_info *)ort->dst.from;
++		ort = ort->from;
+ 
+ 	rcu_read_lock();
+ 	dev = ip6_rt_get_dev_rcu(ort);
+@@ -1357,9 +1357,9 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori
+ 
+ static void rt6_dst_from_metrics_check(struct rt6_info *rt)
+ {
+-	if (rt->dst.from &&
+-	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
+-		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
++	if (rt->from &&
++	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(&rt->from->dst))
++		dst_init_metrics(&rt->dst, dst_metrics_ptr(&rt->from->dst), true);;
+ }
+ 
+ static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
+@@ -1379,7 +1379,7 @@ static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
+ {
+ 	if (!__rt6_check_expired(rt) &&
+ 	    rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
+-	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
++	    rt6_check(rt->from, cookie))
+ 		return &rt->dst;
+ 	else
+ 		return NULL;
+@@ -1399,7 +1399,7 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
+ 	rt6_dst_from_metrics_check(rt);
+ 
+ 	if (rt->rt6i_flags & RTF_PCPU ||
+-	    (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->dst.from))
++	    (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from))
+ 		return rt6_dst_from_check(rt, cookie);
+ 	else
+ 		return rt6_check(rt, cookie);
+@@ -2466,11 +2466,11 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
+ 
+ static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
+ {
+-	BUG_ON(from->dst.from);
++	BUG_ON(from->from);
+ 
+ 	rt->rt6i_flags &= ~RTF_EXPIRES;
+ 	dst_hold(&from->dst);
+-	rt->dst.from = &from->dst;
++	rt->from = from;
+ 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
+ }
+ 
+@@ -2930,7 +2930,7 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
+ 		if (rt->rt6i_flags & RTF_CACHE) {
+ 			/* For RTF_CACHE with rt6i_pmtu == 0
+ 			 * (i.e. a redirected route),
+-			 * the metrics of its rt->dst.from has already
++			 * the metrics of its rt->from has already
+ 			 * been updated.
+ 			 */
+ 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
+@@ -3724,9 +3724,8 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
+ 		goto errout;
+ 	}
+ 
+-	if (fibmatch && rt->dst.from) {
+-		struct rt6_info *ort = container_of(rt->dst.from,
+-						    struct rt6_info, dst);
++	if (fibmatch && rt->from) {
++		struct rt6_info *ort = container_of(&rt->from->dst, struct rt6_info, dst);
+ 
+ 		dst_hold(&ort->dst);
+ 		ip6_rt_put(rt);
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/407-xfrm-Move-dst-path-into-struct-xfrm_dst.patch b/target/linux/generic/backport-4.14/407-xfrm-Move-dst-path-into-struct-xfrm_dst.patch
new file mode 100644
index 0000000000..9c6dbbe1bc
--- /dev/null
+++ b/target/linux/generic/backport-4.14/407-xfrm-Move-dst-path-into-struct-xfrm_dst.patch
@@ -0,0 +1,310 @@
+From aa6688e6c4509eebacbc7ad9de3b5e7775b55a21 Mon Sep 17 00:00:00 2001
+From: David Miller <davem at davemloft.net>
+Date: Tue, 28 Nov 2017 15:40:46 -0500
+Subject: [PATCH 08/11] xfrm: Move dst->path into struct xfrm_dst
+
+The first member of an IPSEC route bundle chain sets it's dst->path to
+the underlying ipv4/ipv6 route that carries the bundle.
+
+Stated another way, if one were to follow the xfrm_dst->child chain of
+the bundle, the final non-NULL pointer would be the path and point to
+either an ipv4 or an ipv6 route.
+
+This is largely used to make sure that PMTU events propagate down to
+the correct ipv4 or ipv6 route.
+
+When we don't have the top of an IPSEC bundle 'dst->path == dst'.
+
+Move it down into xfrm_dst and key off of dst->xfrm.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet at google.com>
+---
+ include/net/dst.h       |  3 +--
+ include/net/xfrm.h      | 15 ++++++++++++++-
+ net/bridge/br_nf_core.c |  1 -
+ net/core/dst.c          |  1 -
+ net/ipv4/route.c        |  2 +-
+ net/ipv6/ip6_output.c   |  6 +++---
+ net/ipv6/route.c        |  6 ------
+ net/xfrm/xfrm_device.c  |  2 +-
+ net/xfrm/xfrm_policy.c  | 28 ++++++++++++++--------------
+ 9 files changed, 34 insertions(+), 30 deletions(-)
+
+diff --git a/include/net/dst.h b/include/net/dst.h
+index a43f37837c36..05404fba2a5a 100644
+--- a/include/net/dst.h
++++ b/include/net/dst.h
+@@ -38,7 +38,6 @@ struct dst_entry {
+ 	struct  dst_ops	        *ops;
+ 	unsigned long		_metrics;
+ 	unsigned long           expires;
+-	struct dst_entry	*path;
+ #ifdef CONFIG_XFRM
+ 	struct xfrm_state	*xfrm;
+ #else
+@@ -87,7 +86,7 @@ struct dst_entry {
+ 	 * Align __refcnt to a 64 bytes alignment
+ 	 * (L1_CACHE_SIZE would be too much)
+ 	 */
+-	long			__pad_to_align_refcnt[4];
++	long			__pad_to_align_refcnt[5];
+ #endif
+ 	/*
+ 	 * __refcnt wants to be on a different cache line from
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index e76770544d11..47ec8b68c208 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -985,6 +985,7 @@ struct xfrm_dst {
+ 	} u;
+ 	struct dst_entry *route;
+ 	struct dst_entry *child;
++	struct dst_entry *path;
+ 	struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
+ 	int num_pols, num_xfrms;
+ 	u32 xfrm_genid;
+@@ -995,6 +996,18 @@ struct xfrm_dst {
+ 	u32 path_cookie;
+ };
+ 
++static inline struct dst_entry *xfrm_dst_path(const struct dst_entry *dst)
++{
++#ifdef CONFIG_XFRM
++	if (dst->xfrm) {
++		const struct xfrm_dst *xdst = (const struct xfrm_dst *) dst;
++
++		return xdst->path;
++	}
++#endif
++	return (struct dst_entry *) dst;
++}
++
+ static inline struct dst_entry *xfrm_dst_child(const struct dst_entry *dst)
+ {
+ #ifdef CONFIG_XFRM
+@@ -1892,7 +1905,7 @@ static inline bool xfrm_dst_offload_ok(struct dst_entry *dst)
+ 		return false;
+ 
+ 	xdst = (struct xfrm_dst *) dst;
+-	if (x->xso.offload_handle && (x->xso.dev == dst->path->dev) &&
++	if (x->xso.offload_handle && (x->xso.dev == xfrm_dst_path(dst)->dev) &&
+ 	    !xdst->child->xfrm)
+ 		return true;
+ 
+diff --git a/net/bridge/br_nf_core.c b/net/bridge/br_nf_core.c
+index 20cbb727df4d..8e2d7cfa4e16 100644
+--- a/net/bridge/br_nf_core.c
++++ b/net/bridge/br_nf_core.c
+@@ -78,7 +78,6 @@ void br_netfilter_rtable_init(struct net_bridge *br)
+ 
+ 	atomic_set(&rt->dst.__refcnt, 1);
+ 	rt->dst.dev = br->dev;
+-	rt->dst.path = &rt->dst;
+ 	dst_init_metrics(&rt->dst, br_dst_default_metrics, true);
+ 	rt->dst.flags	= DST_NOXFRM | DST_FAKE_RTABLE;
+ 	rt->dst.ops = &fake_dst_ops;
+diff --git a/net/core/dst.c b/net/core/dst.c
+index d17d51b9ac9a..6ef9285319e9 100644
+--- a/net/core/dst.c
++++ b/net/core/dst.c
+@@ -69,7 +69,6 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops,
+ 	dst->ops = ops;
+ 	dst_init_metrics(dst, dst_default_metrics.metrics, true);
+ 	dst->expires = 0UL;
+-	dst->path = dst;
+ #ifdef CONFIG_XFRM
+ 	dst->xfrm = NULL;
+ #endif
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 9ff06c5051ae..fdd12d6a536b 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -1109,7 +1109,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
+ 		new = true;
+ 	}
+ 
+-	__ip_rt_update_pmtu((struct rtable *) rt->dst.path, &fl4, mtu);
++	__ip_rt_update_pmtu((struct rtable *) xfrm_dst_path(&rt->dst), &fl4, mtu);
+ 
+ 	if (!dst_check(&rt->dst, 0)) {
+ 		if (new)
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 3763dc01e374..a9ac19ff8683 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -1185,10 +1185,10 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
+ 	v6_cork->tclass = ipc6->tclass;
+ 	if (rt->dst.flags & DST_XFRM_TUNNEL)
+ 		mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ?
+-		      READ_ONCE(rt->dst.dev->mtu) : dst_mtu(&rt->dst);
++		      READ_ONCE(rt->dst.dev->mtu) : dst_mtu(xfrm_dst_path(&rt->dst));
+ 	else
+ 		mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ?
+-		      READ_ONCE(rt->dst.dev->mtu) : dst_mtu(rt->dst.path);
++		      READ_ONCE(rt->dst.dev->mtu) : dst_mtu(xfrm_dst_path(&rt->dst));
+ 	if (np->frag_size < mtu) {
+ 		if (np->frag_size)
+ 			mtu = np->frag_size;
+@@ -1196,7 +1196,7 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
+ 	if (mtu < IPV6_MIN_MTU)
+ 		return -EINVAL;
+ 	cork->base.fragsize = mtu;
+-	if (dst_allfrag(rt->dst.path))
++	if (dst_allfrag(xfrm_dst_path(&rt->dst)))
+ 		cork->base.flags |= IPCORK_ALLFRAG;
+ 	cork->base.length = 0;
+ 
+diff --git a/net/ipv6/route.c b/net/ipv6/route.c
+index 4e8b24d5f9c6..b6487ccd2a08 100644
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -4002,8 +4002,6 @@ static int __net_init ip6_route_net_init(struct net *net)
+ 					   GFP_KERNEL);
+ 	if (!net->ipv6.ip6_null_entry)
+ 		goto out_ip6_dst_entries;
+-	net->ipv6.ip6_null_entry->dst.path =
+-		(struct dst_entry *)net->ipv6.ip6_null_entry;
+ 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
+ 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
+ 			 ip6_template_metrics, true);
+@@ -4015,8 +4013,6 @@ static int __net_init ip6_route_net_init(struct net *net)
+ 					       GFP_KERNEL);
+ 	if (!net->ipv6.ip6_prohibit_entry)
+ 		goto out_ip6_null_entry;
+-	net->ipv6.ip6_prohibit_entry->dst.path =
+-		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
+ 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
+ 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
+ 			 ip6_template_metrics, true);
+@@ -4026,8 +4022,6 @@ static int __net_init ip6_route_net_init(struct net *net)
+ 					       GFP_KERNEL);
+ 	if (!net->ipv6.ip6_blk_hole_entry)
+ 		goto out_ip6_prohibit_entry;
+-	net->ipv6.ip6_blk_hole_entry->dst.path =
+-		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
+ 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
+ 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
+ 			 ip6_template_metrics, true);
+diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
+index c5851ddddd2a..c61a7d46b412 100644
+--- a/net/xfrm/xfrm_device.c
++++ b/net/xfrm/xfrm_device.c
+@@ -120,7 +120,7 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
+ 	if (!x->type_offload || x->encap)
+ 		return false;
+ 
+-	if ((x->xso.offload_handle && (dev == dst->path->dev)) &&
++	if ((x->xso.offload_handle && (dev == xfrm_dst_path(dst)->dev)) &&
+ 	     !xdst->child->xfrm && x->type->get_mtu) {
+ 		mtu = x->type->get_mtu(x, xdst->child_mtu_cached);
+ 
+diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
+index 98d45fb616fb..e0cd3a8e7d3e 100644
+--- a/net/xfrm/xfrm_policy.c
++++ b/net/xfrm/xfrm_policy.c
+@@ -1632,7 +1632,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+ 	}
+ 
+ 	xfrm_dst_set_child(xdst_prev, dst);
+-	xdst0->u.dst.path = dst;
++	xdst0->path = dst;
+ 
+ 	err = -ENODEV;
+ 	dev = dst->dev;
+@@ -1887,8 +1887,8 @@ static void xfrm_policy_queue_process(unsigned long arg)
+ 	xfrm_decode_session(skb, &fl, dst->ops->family);
+ 	spin_unlock(&pq->hold_queue.lock);
+ 
+-	dst_hold(dst->path);
+-	dst = xfrm_lookup(net, dst->path, &fl, sk, 0);
++	dst_hold(xfrm_dst_path(dst));
++	dst = xfrm_lookup(net, xfrm_dst_path(dst), &fl, sk, 0);
+ 	if (IS_ERR(dst))
+ 		goto purge_queue;
+ 
+@@ -1917,8 +1917,8 @@ static void xfrm_policy_queue_process(unsigned long arg)
+ 		skb = __skb_dequeue(&list);
+ 
+ 		xfrm_decode_session(skb, &fl, skb_dst(skb)->ops->family);
+-		dst_hold(skb_dst(skb)->path);
+-		dst = xfrm_lookup(net, skb_dst(skb)->path, &fl, skb->sk, 0);
++		dst_hold(xfrm_dst_path(skb_dst(skb)));
++		dst = xfrm_lookup(net, xfrm_dst_path(skb_dst(skb)), &fl, skb->sk, 0);
+ 		if (IS_ERR(dst)) {
+ 			kfree_skb(skb);
+ 			continue;
+@@ -2020,7 +2020,7 @@ static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,
+ 
+ 	dst_hold(dst);
+ 	xfrm_dst_set_child(xdst, dst);
+-	dst1->path = dst;
++	xdst->path = dst;
+ 
+ 	xfrm_init_path((struct xfrm_dst *)dst1, dst, 0);
+ 
+@@ -2644,7 +2644,7 @@ static int xfrm_bundle_ok(struct xfrm_dst *first)
+ 	struct xfrm_dst *last;
+ 	u32 mtu;
+ 
+-	if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) ||
++	if (!dst_check(xfrm_dst_path(dst), ((struct xfrm_dst *)dst)->path_cookie) ||
+ 	    (dst->dev && !netif_running(dst->dev)))
+ 		return 0;
+ 
+@@ -2705,22 +2705,20 @@ static int xfrm_bundle_ok(struct xfrm_dst *first)
+ 
+ static unsigned int xfrm_default_advmss(const struct dst_entry *dst)
+ {
+-	return dst_metric_advmss(dst->path);
++	return dst_metric_advmss(xfrm_dst_path(dst));
+ }
+ 
+ static unsigned int xfrm_mtu(const struct dst_entry *dst)
+ {
+ 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
+ 
+-	return mtu ? : dst_mtu(dst->path);
++	return mtu ? : dst_mtu(xfrm_dst_path(dst));
+ }
+ 
+ static const void *xfrm_get_dst_nexthop(const struct dst_entry *dst,
+ 					const void *daddr)
+ {
+-	const struct dst_entry *path = dst->path;
+-
+-	for (; dst != path; dst = xfrm_dst_child(dst)) {
++	while (dst->xfrm) {
+ 		const struct xfrm_state *xfrm = dst->xfrm;
+ 
+ 		if (xfrm->props.mode == XFRM_MODE_TRANSPORT)
+@@ -2729,6 +2727,8 @@ static const void *xfrm_get_dst_nexthop(const struct dst_entry *dst,
+ 			daddr = xfrm->coaddr;
+ 		else if (!(xfrm->type->flags & XFRM_TYPE_LOCAL_COADDR))
+ 			daddr = &xfrm->id.daddr;
++
++		dst = xfrm_dst_child(dst);
+ 	}
+ 	return daddr;
+ }
+@@ -2737,7 +2737,7 @@ static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst,
+ 					   struct sk_buff *skb,
+ 					   const void *daddr)
+ {
+-	const struct dst_entry *path = dst->path;
++	const struct dst_entry *path = xfrm_dst_path(dst);
+ 
+ 	if (!skb)
+ 		daddr = xfrm_get_dst_nexthop(dst, daddr);
+@@ -2746,7 +2746,7 @@ static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst,
+ 
+ static void xfrm_confirm_neigh(const struct dst_entry *dst, const void *daddr)
+ {
+-	const struct dst_entry *path = dst->path;
++	const struct dst_entry *path = xfrm_dst_path(dst);
+ 
+ 	daddr = xfrm_get_dst_nexthop(dst, daddr);
+ 	path->ops->confirm_neigh(path, daddr);
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/408-net-Rearrange-dst_entry-layout-to-avoid-useless-padd.patch b/target/linux/generic/backport-4.14/408-net-Rearrange-dst_entry-layout-to-avoid-useless-padd.patch
new file mode 100644
index 0000000000..cdd30273e6
--- /dev/null
+++ b/target/linux/generic/backport-4.14/408-net-Rearrange-dst_entry-layout-to-avoid-useless-padd.patch
@@ -0,0 +1,96 @@
+From 42f6089f469fb2e1fbe093eab06260381feabdff Mon Sep 17 00:00:00 2001
+From: David Miller <davem at davemloft.net>
+Date: Tue, 28 Nov 2017 15:40:53 -0500
+Subject: [PATCH 09/11] net: Rearrange dst_entry layout to avoid useless
+ padding.
+
+We have padding to try and align the refcount on a separate cache
+line.  But after several simplifications the padding has increased
+substantially.
+
+So now it's easy to change the layout to get rid of the padding
+entirely.
+
+We group the write-heavy __refcnt and __use with less often used
+items such as the rcu_head and the error code.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet at google.com>
+---
+ include/net/dst.h | 31 ++++++++++++-------------------
+ 1 file changed, 12 insertions(+), 19 deletions(-)
+
+diff --git a/include/net/dst.h b/include/net/dst.h
+index 05404fba2a5a..5c54c2e97f1b 100644
+--- a/include/net/dst.h
++++ b/include/net/dst.h
+@@ -34,7 +34,6 @@ struct sk_buff;
+ 
+ struct dst_entry {
+ 	struct net_device       *dev;
+-	struct rcu_head		rcu_head;
+ 	struct  dst_ops	        *ops;
+ 	unsigned long		_metrics;
+ 	unsigned long           expires;
+@@ -56,8 +55,6 @@ struct dst_entry {
+ #define DST_XFRM_QUEUE		0x0040
+ #define DST_METADATA		0x0080
+ 
+-	short			error;
+-
+ 	/* A non-zero value of dst->obsolete forces by-hand validation
+ 	 * of the route entry.  Positive values are set by the generic
+ 	 * dst layer to indicate that the entry has been forcefully
+@@ -73,29 +70,25 @@ struct dst_entry {
+ #define DST_OBSOLETE_KILL	-2
+ 	unsigned short		header_len;	/* more space at head required */
+ 	unsigned short		trailer_len;	/* space to reserve at tail */
+-	unsigned short		__pad3;
+-
+-#ifdef CONFIG_IP_ROUTE_CLASSID
+-	__u32			tclassid;
+-#else
+-	__u32			__pad2;
+-#endif
+ 
+-#ifdef CONFIG_64BIT
+-	/*
+-	 * Align __refcnt to a 64 bytes alignment
+-	 * (L1_CACHE_SIZE would be too much)
+-	 */
+-	long			__pad_to_align_refcnt[5];
+-#endif
+ 	/*
+ 	 * __refcnt wants to be on a different cache line from
+ 	 * input/output/ops or performance tanks badly
+ 	 */
+-	atomic_t		__refcnt;	/* client references	*/
++#ifdef CONFIG_64BIT
++	atomic_t		__refcnt;	/* 64-bit offset 64 */
++#endif
+ 	int			__use;
+ 	unsigned long		lastuse;
+ 	struct lwtunnel_state   *lwtstate;
++	struct rcu_head		rcu_head;
++	short			error;
++	short			__pad;
++	__u32			tclassid;
++#ifndef CONFIG_64BIT
++	atomic_t		__refcnt;	/* 32-bit offset 64 */
++#endif
++
+ 	union {
+ 		struct dst_entry	*next;
+ 	};
+@@ -244,7 +237,7 @@ static inline void dst_hold(struct dst_entry *dst)
+ {
+ 	/*
+ 	 * If your kernel compilation stops here, please check
+-	 * __pad_to_align_refcnt declaration in struct dst_entry
++	 * the placement of __refcnt in struct dst_entry
+ 	 */
+ 	BUILD_BUG_ON(offsetof(struct dst_entry, __refcnt) & 63);
+ 	WARN_ON(atomic_inc_not_zero(&dst->__refcnt) == 0);
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/409-xfrm-Stop-using-dst-next-in-bundle-construction.patch b/target/linux/generic/backport-4.14/409-xfrm-Stop-using-dst-next-in-bundle-construction.patch
new file mode 100644
index 0000000000..620895ff45
--- /dev/null
+++ b/target/linux/generic/backport-4.14/409-xfrm-Stop-using-dst-next-in-bundle-construction.patch
@@ -0,0 +1,197 @@
+From e22f80e4d546bd11ffe6886c6a79203964a86d6b Mon Sep 17 00:00:00 2001
+From: David Miller <davem at davemloft.net>
+Date: Tue, 28 Nov 2017 15:41:01 -0500
+Subject: [PATCH 10/11] xfrm: Stop using dst->next in bundle construction.
+
+While building ipsec bundles, blocks of xfrm dsts are linked together
+using dst->next from bottom to the top.
+
+The only thing this is used for is initializing the pmtu values of the
+xfrm stack, and for updating the mtu values at xfrm_bundle_ok() time.
+
+The bundle pmtu entries must be processed in this order so that pmtu
+values lower in the stack of routes can propagate up to the higher
+ones.
+
+Avoid using dst->next by simply maintaining an array of dst pointers
+as we already do for the xfrm_state objects when building the bundle.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet at google.com>
+---
+ net/xfrm/xfrm_policy.c | 56 ++++++++++++++++++++++++++++----------------------
+ 1 file changed, 32 insertions(+), 24 deletions(-)
+
+diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
+index e0cd3a8e7d3e..62ddc4b643c9 100644
+--- a/net/xfrm/xfrm_policy.c
++++ b/net/xfrm/xfrm_policy.c
+@@ -54,7 +54,7 @@ static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
+ static struct kmem_cache *xfrm_dst_cache __read_mostly;
+ static __read_mostly seqcount_t xfrm_policy_hash_generation;
+ 
+-static void xfrm_init_pmtu(struct dst_entry *dst);
++static void xfrm_init_pmtu(struct xfrm_dst **bundle, int nr);
+ static int stale_bundle(struct dst_entry *dst);
+ static int xfrm_bundle_ok(struct xfrm_dst *xdst);
+ static void xfrm_policy_queue_process(unsigned long arg);
+@@ -1544,7 +1544,9 @@ static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
+  */
+ 
+ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+-					    struct xfrm_state **xfrm, int nx,
++					    struct xfrm_state **xfrm,
++					    struct xfrm_dst **bundle,
++					    int nx,
+ 					    const struct flowi *fl,
+ 					    struct dst_entry *dst)
+ {
+@@ -1579,6 +1581,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+ 			goto put_states;
+ 		}
+ 
++		bundle[i] = xdst;
+ 		if (!xdst_prev)
+ 			xdst0 = xdst;
+ 		else
+@@ -1622,7 +1625,6 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+ 		dst1->input = dst_discard;
+ 		dst1->output = inner_mode->afinfo->output;
+ 
+-		dst1->next = &xdst_prev->u.dst;
+ 		xdst_prev = xdst;
+ 
+ 		header_len += xfrm[i]->props.header_len;
+@@ -1640,7 +1642,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+ 		goto free_dst;
+ 
+ 	xfrm_init_path(xdst0, dst, nfheader_len);
+-	xfrm_init_pmtu(&xdst_prev->u.dst);
++	xfrm_init_pmtu(bundle, nx);
+ 
+ 	for (xdst_prev = xdst0; xdst_prev != (struct xfrm_dst *)dst;
+ 	     xdst_prev = (struct xfrm_dst *) xfrm_dst_child(&xdst_prev->u.dst)) {
+@@ -1820,6 +1822,7 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
+ {
+ 	struct net *net = xp_net(pols[0]);
+ 	struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
++	struct xfrm_dst *bundle[XFRM_MAX_DEPTH];
+ 	struct xfrm_dst *xdst, *old;
+ 	struct dst_entry *dst;
+ 	int err;
+@@ -1847,7 +1850,7 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
+ 
+ 	old = xdst;
+ 
+-	dst = xfrm_bundle_create(pols[0], xfrm, err, fl, dst_orig);
++	dst = xfrm_bundle_create(pols[0], xfrm, bundle, err, fl, dst_orig);
+ 	if (IS_ERR(dst)) {
+ 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLEGENERROR);
+ 		return ERR_CAST(dst);
+@@ -2613,12 +2616,14 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)
+ 	return dst;
+ }
+ 
+-static void xfrm_init_pmtu(struct dst_entry *dst)
++static void xfrm_init_pmtu(struct xfrm_dst **bundle, int nr)
+ {
+-	do {
+-		struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
++	while (nr--) {
++		struct xfrm_dst *xdst = bundle[nr];
+ 		u32 pmtu, route_mtu_cached;
++		struct dst_entry *dst;
+ 
++		dst = &xdst->u.dst;
+ 		pmtu = dst_mtu(xfrm_dst_child(dst));
+ 		xdst->child_mtu_cached = pmtu;
+ 
+@@ -2631,7 +2636,7 @@ static void xfrm_init_pmtu(struct dst_entry *dst)
+ 			pmtu = route_mtu_cached;
+ 
+ 		dst_metric_set(dst, RTAX_MTU, pmtu);
+-	} while ((dst = dst->next));
++	}
+ }
+ 
+ /* Check that the bundle accepts the flow and its components are
+@@ -2640,8 +2645,10 @@ static void xfrm_init_pmtu(struct dst_entry *dst)
+ 
+ static int xfrm_bundle_ok(struct xfrm_dst *first)
+ {
++	struct xfrm_dst *bundle[XFRM_MAX_DEPTH];
+ 	struct dst_entry *dst = &first->u.dst;
+-	struct xfrm_dst *last;
++	struct xfrm_dst *xdst;
++	int start_from, nr;
+ 	u32 mtu;
+ 
+ 	if (!dst_check(xfrm_dst_path(dst), ((struct xfrm_dst *)dst)->path_cookie) ||
+@@ -2651,8 +2658,7 @@ static int xfrm_bundle_ok(struct xfrm_dst *first)
+ 	if (dst->flags & DST_XFRM_QUEUE)
+ 		return 1;
+ 
+-	last = NULL;
+-
++	start_from = nr = 0;
+ 	do {
+ 		struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
+ 
+@@ -2664,9 +2670,11 @@ static int xfrm_bundle_ok(struct xfrm_dst *first)
+ 		    xdst->policy_genid != atomic_read(&xdst->pols[0]->genid))
+ 			return 0;
+ 
++		bundle[nr++] = xdst;
++
+ 		mtu = dst_mtu(xfrm_dst_child(dst));
+ 		if (xdst->child_mtu_cached != mtu) {
+-			last = xdst;
++			start_from = nr;
+ 			xdst->child_mtu_cached = mtu;
+ 		}
+ 
+@@ -2674,30 +2682,30 @@ static int xfrm_bundle_ok(struct xfrm_dst *first)
+ 			return 0;
+ 		mtu = dst_mtu(xdst->route);
+ 		if (xdst->route_mtu_cached != mtu) {
+-			last = xdst;
++			start_from = nr;
+ 			xdst->route_mtu_cached = mtu;
+ 		}
+ 
+ 		dst = xfrm_dst_child(dst);
+ 	} while (dst->xfrm);
+ 
+-	if (likely(!last))
++	if (likely(!start_from))
+ 		return 1;
+ 
+-	mtu = last->child_mtu_cached;
+-	for (;;) {
+-		dst = &last->u.dst;
++	xdst = bundle[start_from - 1];
++	mtu = xdst->child_mtu_cached;
++	while (start_from--) {
++		dst = &xdst->u.dst;
+ 
+ 		mtu = xfrm_state_mtu(dst->xfrm, mtu);
+-		if (mtu > last->route_mtu_cached)
+-			mtu = last->route_mtu_cached;
++		if (mtu > xdst->route_mtu_cached)
++			mtu = xdst->route_mtu_cached;
+ 		dst_metric_set(dst, RTAX_MTU, mtu);
+-
+-		if (last == first)
++		if (!start_from)
+ 			break;
+ 
+-		last = (struct xfrm_dst *)last->u.dst.next;
+-		last->child_mtu_cached = mtu;
++		xdst = bundle[start_from - 1];
++		xdst->child_mtu_cached = mtu;
+ 	}
+ 
+ 	return 1;
+-- 
+2.16.2
+
diff --git a/target/linux/generic/backport-4.14/410-net-Remove-dst-next.patch b/target/linux/generic/backport-4.14/410-net-Remove-dst-next.patch
new file mode 100644
index 0000000000..f854fef7f6
--- /dev/null
+++ b/target/linux/generic/backport-4.14/410-net-Remove-dst-next.patch
@@ -0,0 +1,44 @@
+From a7c83c86e21b6a0f8083e7397861433c472c3c9b Mon Sep 17 00:00:00 2001
+From: David Miller <davem at davemloft.net>
+Date: Tue, 28 Nov 2017 15:41:07 -0500
+Subject: [PATCH 11/11] net: Remove dst->next
+
+There are no more users.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet at google.com>
+---
+ include/net/dst.h | 4 ----
+ net/core/dst.c    | 1 -
+ 2 files changed, 5 deletions(-)
+
+diff --git a/include/net/dst.h b/include/net/dst.h
+index 5c54c2e97f1b..b039f394c1af 100644
+--- a/include/net/dst.h
++++ b/include/net/dst.h
+@@ -88,10 +88,6 @@ struct dst_entry {
+ #ifndef CONFIG_64BIT
+ 	atomic_t		__refcnt;	/* 32-bit offset 64 */
+ #endif
+-
+-	union {
+-		struct dst_entry	*next;
+-	};
+ };
+ 
+ struct dst_metrics {
+diff --git a/net/core/dst.c b/net/core/dst.c
+index 6ef9285319e9..20ded1e491a5 100644
+--- a/net/core/dst.c
++++ b/net/core/dst.c
+@@ -86,7 +86,6 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops,
+ 	dst->__use = 0;
+ 	dst->lastuse = jiffies;
+ 	dst->flags = flags;
+-	dst->next = NULL;
+ 	if (!(flags & DST_NOCOUNT))
+ 		dst_entries_add(ops, 1);
+ }
+-- 
+2.16.2
+
diff --git a/target/linux/generic/pending-4.14/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/target/linux/generic/pending-4.14/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch
index 39346886ef..3d42366e7a 100644
--- a/target/linux/generic/pending-4.14/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch
+++ b/target/linux/generic/pending-4.14/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch
@@ -200,7 +200,7 @@ Signed-off-by: Jonas Gorski <jogo at openwrt.org>
  		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
  		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
  #endif
-@@ -4032,6 +4071,17 @@ static int __net_init ip6_route_net_init
+@@ -4032,6 +4069,15 @@ static int __net_init ip6_route_net_init
  	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
  	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
  			 ip6_template_metrics, true);
@@ -210,8 +210,6 @@ Signed-off-by: Jonas Gorski <jogo at openwrt.org>
 +			sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL);
 +	if (!net->ipv6.ip6_policy_failed_entry)
 +		goto out_ip6_blk_hole_entry;
-+	net->ipv6.ip6_policy_failed_entry->dst.path =
-+		(struct dst_entry *)net->ipv6.ip6_policy_failed_entry;
 +	net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops;
 +	dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst,
 +			 ip6_template_metrics, true);
-- 
2.16.2




More information about the Lede-dev mailing list