diff mbox series

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

Message ID 20180323181907.18342-1-rosenp@gmail.com
State Rejected
Delegated to: Mathias Kresin
Headers show
Series [LEDE-DEV] kernel: Backport net struct reduction from 4.16. | expand

Commit Message

Rosen Penev March 23, 2018, 6:19 p.m. UTC
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@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

Comments

Mathias Kresin March 23, 2018, 9:53 p.m. UTC | #1
23.03.2018 19:19, Rosen Penev:
> 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.

I gave it a try on lantiq, as I benchmarked the flow offloading impact 
anyway.

Doing my usual iperf3 test, I wasn't able to notice any improvement with 
the patches applied. The differences are rather within the measuring 
tolerance:

=HEAD=

net to client	95.2 Mbits/sec routing
client to net	109 Mbits/sec  routing

net to client	85.6 Mbits/sec NAT
client to net	96.2 Mbits/sec NAT


=k4.16 net struct reduction=

net to client	92.4 Mbits/sec routing
client to net	107 Mbits/sec  routing

net to client	83.7 Mbits/sec NAT
client to net	93.4 Mbits/sec NAT


=FLOW_OFFLOADING=

net to client	129 Mbits/sec routing
client to net	144 Mbits/sec routing

net to client	124 Mbits/sec NAT
client to net	144 Mbits/sec NAT


=FLOW_OFFLOADING + k4.16 net struct reduction=

net to client	130 Mbits/sec routing
client to net	140 Mbits/sec routing

net to client	126 Mbits/sec NAT
client to net	140 Mbits/sec NAT


As long as nobody proves that there is a real performance gain, I don't 
see a reason to backport the datastruct debloat.

Mathias
Florian Fainelli March 23, 2018, 10:58 p.m. UTC | #2
On 03/23/2018 02:53 PM, Mathias Kresin wrote:
> As long as nobody proves that there is a real performance gain, I don't
> see a reason to backport the datastruct debloat.

Agreed, it will also make it a tad harder to merge stable kernels with
these changes applied.

Rosen, if you are willing to start working on improving cache footprint,
then really start using tools like perf, pahole and whatnot to select
what the lowest hanging fruit is...
Rosen Penev March 24, 2018, 4:24 p.m. UTC | #3
On Fri, Mar 23, 2018 at 3:58 PM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> On 03/23/2018 02:53 PM, Mathias Kresin wrote:
>> As long as nobody proves that there is a real performance gain, I don't
>> see a reason to backport the datastruct debloat.
Well, it's supposed to make a difference in routing.
>
> Agreed, it will also make it a tad harder to merge stable kernels with
> these changes applied.
>
> Rosen, if you are willing to start working on improving cache footprint,
> then really start using tools like perf, pahole and whatnot to select
> what the lowest hanging fruit is...
Well, back porting other people's work is easier :).

Since the MIPS platforms are still on 4.9, this is probably pointless anyway.
> --
> Florian
diff mbox series

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@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@davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet@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@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@davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet@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@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@davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet@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@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@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@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@davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet@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@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@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@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@davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet@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@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@davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet@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@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@davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet@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@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@davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet@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@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@davemloft.net>
+Reviewed-by: Eric Dumazet <edumazet@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@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@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);