diff mbox series

[net-next,2/3] net: ipv6: seg6_iptunnel: mitigate 2-realloc issue

Message ID 20241025133727.27742-3-justin.iurman@uliege.be (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series Mitigate the two-reallocations issue for iptunnels | expand

Commit Message

Justin Iurman Oct. 25, 2024, 1:37 p.m. UTC
This patch mitigates the two-reallocations issue with seg6_iptunnel by
providing the dst_entry (in the cache) to the first call to
skb_cow_head(). As a result, the very first iteration would still
trigger two reallocations (i.e., empty cache), while next iterations
would only trigger a single reallocation.

Performance tests before/after applying this patch, which clearly shows
the improvement:
- before: https://ibb.co/3Cg4sNH
- after: https://ibb.co/8rQ350r

Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
---
 net/ipv6/seg6_iptunnel.c | 103 ++++++++++++++++++++++-----------------
 1 file changed, 58 insertions(+), 45 deletions(-)

Comments

kernel test robot Oct. 26, 2024, 8:38 a.m. UTC | #1
Hi Justin,

kernel test robot noticed the following build errors:

[auto build test ERROR on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Justin-Iurman/net-ipv6-ioam6_iptunnel-mitigate-2-realloc-issue/20241025-214849
base:   net-next/main
patch link:    https://lore.kernel.org/r/20241025133727.27742-3-justin.iurman%40uliege.be
patch subject: [PATCH net-next 2/3] net: ipv6: seg6_iptunnel: mitigate 2-realloc issue
config: arc-randconfig-001-20241026 (https://download.01.org/0day-ci/archive/20241026/202410261651.MiKpheOT-lkp@intel.com/config)
compiler: arc-elf-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241026/202410261651.MiKpheOT-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410261651.MiKpheOT-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   net/ipv6/seg6_iptunnel.c: In function 'seg6_do_srh_encap':
>> net/ipv6/seg6_iptunnel.c:130:16: error: implicit declaration of function '__seg6_do_srh_encap'; did you mean 'seg6_do_srh_encap'? [-Werror=implicit-function-declaration]
     130 |         return __seg6_do_srh_encap(skb, osrh, proto, NULL);
         |                ^~~~~~~~~~~~~~~~~~~
         |                seg6_do_srh_encap
   net/ipv6/seg6_iptunnel.c: At top level:
>> net/ipv6/seg6_iptunnel.c:134:5: warning: no previous prototype for '__seg6_do_srh_encap' [-Wmissing-prototypes]
     134 | int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
         |     ^~~~~~~~~~~~~~~~~~~
   net/ipv6/seg6_iptunnel.c: In function 'seg6_do_srh_inline':
>> net/ipv6/seg6_iptunnel.c:330:16: error: implicit declaration of function '__seg6_do_srh_inline'; did you mean 'seg6_do_srh_inline'? [-Werror=implicit-function-declaration]
     330 |         return __seg6_do_srh_inline(skb, osrh, NULL);
         |                ^~~~~~~~~~~~~~~~~~~~
         |                seg6_do_srh_inline
   net/ipv6/seg6_iptunnel.c: At top level:
>> net/ipv6/seg6_iptunnel.c:334:5: warning: no previous prototype for '__seg6_do_srh_inline' [-Wmissing-prototypes]
     334 | int __seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
         |     ^~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +130 net/ipv6/seg6_iptunnel.c

   126	
   127	/* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */
   128	int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
   129	{
 > 130		return __seg6_do_srh_encap(skb, osrh, proto, NULL);
   131	}
   132	EXPORT_SYMBOL_GPL(seg6_do_srh_encap);
   133	
 > 134	int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
   135				int proto, struct dst_entry *dst)
   136	{
   137		struct net *net = dev_net(skb_dst(skb)->dev);
   138		struct ipv6hdr *hdr, *inner_hdr;
   139		struct ipv6_sr_hdr *isrh;
   140		int hdrlen, tot_len, err;
   141		__be32 flowlabel;
   142	
   143		hdrlen = (osrh->hdrlen + 1) << 3;
   144		tot_len = hdrlen + sizeof(*hdr);
   145	
   146		err = skb_cow_head(skb, tot_len + (!dst ? skb->mac_len
   147							: LL_RESERVED_SPACE(dst->dev)));
   148		if (unlikely(err))
   149			return err;
   150	
   151		inner_hdr = ipv6_hdr(skb);
   152		flowlabel = seg6_make_flowlabel(net, skb, inner_hdr);
   153	
   154		skb_push(skb, tot_len);
   155		skb_reset_network_header(skb);
   156		skb_mac_header_rebuild(skb);
   157		hdr = ipv6_hdr(skb);
   158	
   159		/* inherit tc, flowlabel and hlim
   160		 * hlim will be decremented in ip6_forward() afterwards and
   161		 * decapsulation will overwrite inner hlim with outer hlim
   162		 */
   163	
   164		if (skb->protocol == htons(ETH_P_IPV6)) {
   165			ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)),
   166				     flowlabel);
   167			hdr->hop_limit = inner_hdr->hop_limit;
   168		} else {
   169			ip6_flow_hdr(hdr, 0, flowlabel);
   170			hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb));
   171	
   172			memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
   173	
   174			/* the control block has been erased, so we have to set the
   175			 * iif once again.
   176			 * We read the receiving interface index directly from the
   177			 * skb->skb_iif as it is done in the IPv4 receiving path (i.e.:
   178			 * ip_rcv_core(...)).
   179			 */
   180			IP6CB(skb)->iif = skb->skb_iif;
   181		}
   182	
   183		hdr->nexthdr = NEXTHDR_ROUTING;
   184	
   185		isrh = (void *)hdr + sizeof(*hdr);
   186		memcpy(isrh, osrh, hdrlen);
   187	
   188		isrh->nexthdr = proto;
   189	
   190		hdr->daddr = isrh->segments[isrh->first_segment];
   191		set_tun_src(net, skb_dst(skb)->dev, &hdr->daddr, &hdr->saddr);
   192
kernel test robot Oct. 26, 2024, 10 a.m. UTC | #2
Hi Justin,

kernel test robot noticed the following build errors:

[auto build test ERROR on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Justin-Iurman/net-ipv6-ioam6_iptunnel-mitigate-2-realloc-issue/20241025-214849
base:   net-next/main
patch link:    https://lore.kernel.org/r/20241025133727.27742-3-justin.iurman%40uliege.be
patch subject: [PATCH net-next 2/3] net: ipv6: seg6_iptunnel: mitigate 2-realloc issue
config: i386-buildonly-randconfig-004-20241026 (https://download.01.org/0day-ci/archive/20241026/202410261713.GIaQEsJC-lkp@intel.com/config)
compiler: clang version 19.1.2 (https://github.com/llvm/llvm-project 7ba7d8e2f7b6445b60679da826210cdde29eaf8b)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241026/202410261713.GIaQEsJC-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410261713.GIaQEsJC-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   In file included from net/ipv6/seg6_iptunnel.c:10:
   In file included from include/linux/skbuff.h:17:
   In file included from include/linux/bvec.h:10:
   In file included from include/linux/highmem.h:8:
   In file included from include/linux/cacheflush.h:5:
   In file included from arch/x86/include/asm/cacheflush.h:5:
   In file included from include/linux/mm.h:2213:
   include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
>> net/ipv6/seg6_iptunnel.c:130:9: error: call to undeclared function '__seg6_do_srh_encap'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     130 |         return __seg6_do_srh_encap(skb, osrh, proto, NULL);
         |                ^
   net/ipv6/seg6_iptunnel.c:130:9: note: did you mean 'seg6_do_srh_encap'?
   net/ipv6/seg6_iptunnel.c:128:5: note: 'seg6_do_srh_encap' declared here
     128 | int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
         |     ^
     129 | {
     130 |         return __seg6_do_srh_encap(skb, osrh, proto, NULL);
         |                ~~~~~~~~~~~~~~~~~~~
         |                seg6_do_srh_encap
>> net/ipv6/seg6_iptunnel.c:134:5: warning: no previous prototype for function '__seg6_do_srh_encap' [-Wmissing-prototypes]
     134 | int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
         |     ^
   net/ipv6/seg6_iptunnel.c:134:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     134 | int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
         | ^
         | static 
>> net/ipv6/seg6_iptunnel.c:330:9: error: call to undeclared function '__seg6_do_srh_inline'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     330 |         return __seg6_do_srh_inline(skb, osrh, NULL);
         |                ^
   net/ipv6/seg6_iptunnel.c:330:9: note: did you mean 'seg6_do_srh_inline'?
   net/ipv6/seg6_iptunnel.c:328:5: note: 'seg6_do_srh_inline' declared here
     328 | int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
         |     ^
     329 | {
     330 |         return __seg6_do_srh_inline(skb, osrh, NULL);
         |                ~~~~~~~~~~~~~~~~~~~~
         |                seg6_do_srh_inline
>> net/ipv6/seg6_iptunnel.c:334:5: warning: no previous prototype for function '__seg6_do_srh_inline' [-Wmissing-prototypes]
     334 | int __seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
         |     ^
   net/ipv6/seg6_iptunnel.c:334:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     334 | int __seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
         | ^
         | static 
   3 warnings and 2 errors generated.


vim +/__seg6_do_srh_encap +130 net/ipv6/seg6_iptunnel.c

   126	
   127	/* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */
   128	int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
   129	{
 > 130		return __seg6_do_srh_encap(skb, osrh, proto, NULL);
   131	}
   132	EXPORT_SYMBOL_GPL(seg6_do_srh_encap);
   133	
 > 134	int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
   135				int proto, struct dst_entry *dst)
   136	{
   137		struct net *net = dev_net(skb_dst(skb)->dev);
   138		struct ipv6hdr *hdr, *inner_hdr;
   139		struct ipv6_sr_hdr *isrh;
   140		int hdrlen, tot_len, err;
   141		__be32 flowlabel;
   142	
   143		hdrlen = (osrh->hdrlen + 1) << 3;
   144		tot_len = hdrlen + sizeof(*hdr);
   145	
   146		err = skb_cow_head(skb, tot_len + (!dst ? skb->mac_len
   147							: LL_RESERVED_SPACE(dst->dev)));
   148		if (unlikely(err))
   149			return err;
   150	
   151		inner_hdr = ipv6_hdr(skb);
   152		flowlabel = seg6_make_flowlabel(net, skb, inner_hdr);
   153	
   154		skb_push(skb, tot_len);
   155		skb_reset_network_header(skb);
   156		skb_mac_header_rebuild(skb);
   157		hdr = ipv6_hdr(skb);
   158	
   159		/* inherit tc, flowlabel and hlim
   160		 * hlim will be decremented in ip6_forward() afterwards and
   161		 * decapsulation will overwrite inner hlim with outer hlim
   162		 */
   163	
   164		if (skb->protocol == htons(ETH_P_IPV6)) {
   165			ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)),
   166				     flowlabel);
   167			hdr->hop_limit = inner_hdr->hop_limit;
   168		} else {
   169			ip6_flow_hdr(hdr, 0, flowlabel);
   170			hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb));
   171	
   172			memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
   173	
   174			/* the control block has been erased, so we have to set the
   175			 * iif once again.
   176			 * We read the receiving interface index directly from the
   177			 * skb->skb_iif as it is done in the IPv4 receiving path (i.e.:
   178			 * ip_rcv_core(...)).
   179			 */
   180			IP6CB(skb)->iif = skb->skb_iif;
   181		}
   182	
   183		hdr->nexthdr = NEXTHDR_ROUTING;
   184	
   185		isrh = (void *)hdr + sizeof(*hdr);
   186		memcpy(isrh, osrh, hdrlen);
   187	
   188		isrh->nexthdr = proto;
   189	
   190		hdr->daddr = isrh->segments[isrh->first_segment];
   191		set_tun_src(net, skb_dst(skb)->dev, &hdr->daddr, &hdr->saddr);
   192
diff mbox series

Patch

diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
index 098632adc9b5..7789a31db355 100644
--- a/net/ipv6/seg6_iptunnel.c
+++ b/net/ipv6/seg6_iptunnel.c
@@ -127,8 +127,14 @@  static __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb,
 /* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */
 int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
 {
-	struct dst_entry *dst = skb_dst(skb);
-	struct net *net = dev_net(dst->dev);
+	return __seg6_do_srh_encap(skb, osrh, proto, NULL);
+}
+EXPORT_SYMBOL_GPL(seg6_do_srh_encap);
+
+int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
+			int proto, struct dst_entry *dst)
+{
+	struct net *net = dev_net(skb_dst(skb)->dev);
 	struct ipv6hdr *hdr, *inner_hdr;
 	struct ipv6_sr_hdr *isrh;
 	int hdrlen, tot_len, err;
@@ -137,7 +143,8 @@  int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
 	hdrlen = (osrh->hdrlen + 1) << 3;
 	tot_len = hdrlen + sizeof(*hdr);
 
-	err = skb_cow_head(skb, tot_len + skb->mac_len);
+	err = skb_cow_head(skb, tot_len + (!dst ? skb->mac_len
+						: LL_RESERVED_SPACE(dst->dev)));
 	if (unlikely(err))
 		return err;
 
@@ -181,7 +188,7 @@  int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
 	isrh->nexthdr = proto;
 
 	hdr->daddr = isrh->segments[isrh->first_segment];
-	set_tun_src(net, dst->dev, &hdr->daddr, &hdr->saddr);
+	set_tun_src(net, skb_dst(skb)->dev, &hdr->daddr, &hdr->saddr);
 
 #ifdef CONFIG_IPV6_SEG6_HMAC
 	if (sr_has_hmac(isrh)) {
@@ -197,15 +204,14 @@  int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(seg6_do_srh_encap);
 
 /* encapsulate an IPv6 packet within an outer IPv6 header with reduced SRH */
 static int seg6_do_srh_encap_red(struct sk_buff *skb,
-				 struct ipv6_sr_hdr *osrh, int proto)
+				 struct ipv6_sr_hdr *osrh, int proto,
+				 struct dst_entry *dst)
 {
 	__u8 first_seg = osrh->first_segment;
-	struct dst_entry *dst = skb_dst(skb);
-	struct net *net = dev_net(dst->dev);
+	struct net *net = dev_net(skb_dst(skb)->dev);
 	struct ipv6hdr *hdr, *inner_hdr;
 	int hdrlen = ipv6_optlen(osrh);
 	int red_tlv_offset, tlv_offset;
@@ -230,7 +236,8 @@  static int seg6_do_srh_encap_red(struct sk_buff *skb,
 
 	tot_len = red_hdrlen + sizeof(struct ipv6hdr);
 
-	err = skb_cow_head(skb, tot_len + skb->mac_len);
+	err = skb_cow_head(skb, tot_len + (!dst ? skb->mac_len
+						: LL_RESERVED_SPACE(dst->dev)));
 	if (unlikely(err))
 		return err;
 
@@ -263,7 +270,7 @@  static int seg6_do_srh_encap_red(struct sk_buff *skb,
 	if (skip_srh) {
 		hdr->nexthdr = proto;
 
-		set_tun_src(net, dst->dev, &hdr->daddr, &hdr->saddr);
+		set_tun_src(net, skb_dst(skb)->dev, &hdr->daddr, &hdr->saddr);
 		goto out;
 	}
 
@@ -299,7 +306,7 @@  static int seg6_do_srh_encap_red(struct sk_buff *skb,
 
 srcaddr:
 	isrh->nexthdr = proto;
-	set_tun_src(net, dst->dev, &hdr->daddr, &hdr->saddr);
+	set_tun_src(net, skb_dst(skb)->dev, &hdr->daddr, &hdr->saddr);
 
 #ifdef CONFIG_IPV6_SEG6_HMAC
 	if (unlikely(!skip_srh && sr_has_hmac(isrh))) {
@@ -319,6 +326,13 @@  static int seg6_do_srh_encap_red(struct sk_buff *skb,
 
 /* insert an SRH within an IPv6 packet, just after the IPv6 header */
 int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
+{
+	return __seg6_do_srh_inline(skb, osrh, NULL);
+}
+EXPORT_SYMBOL_GPL(seg6_do_srh_inline);
+
+int __seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
+			 struct dst_entry *dst)
 {
 	struct ipv6hdr *hdr, *oldhdr;
 	struct ipv6_sr_hdr *isrh;
@@ -326,7 +340,8 @@  int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
 
 	hdrlen = (osrh->hdrlen + 1) << 3;
 
-	err = skb_cow_head(skb, hdrlen + skb->mac_len);
+	err = skb_cow_head(skb, hdrlen + (!dst ? skb->mac_len
+					       : LL_RESERVED_SPACE(dst->dev)));
 	if (unlikely(err))
 		return err;
 
@@ -369,22 +384,20 @@  int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(seg6_do_srh_inline);
 
-static int seg6_do_srh(struct sk_buff *skb)
+static int seg6_do_srh(struct sk_buff *skb, struct dst_entry *dst)
 {
-	struct dst_entry *dst = skb_dst(skb);
 	struct seg6_iptunnel_encap *tinfo;
 	int proto, err = 0;
 
-	tinfo = seg6_encap_lwtunnel(dst->lwtstate);
+	tinfo = seg6_encap_lwtunnel(skb_dst(skb)->lwtstate);
 
 	switch (tinfo->mode) {
 	case SEG6_IPTUN_MODE_INLINE:
 		if (skb->protocol != htons(ETH_P_IPV6))
 			return -EINVAL;
 
-		err = seg6_do_srh_inline(skb, tinfo->srh);
+		err = __seg6_do_srh_inline(skb, tinfo->srh, dst);
 		if (err)
 			return err;
 		break;
@@ -402,9 +415,9 @@  static int seg6_do_srh(struct sk_buff *skb)
 			return -EINVAL;
 
 		if (tinfo->mode == SEG6_IPTUN_MODE_ENCAP)
-			err = seg6_do_srh_encap(skb, tinfo->srh, proto);
+			err = __seg6_do_srh_encap(skb, tinfo->srh, proto, dst);
 		else
-			err = seg6_do_srh_encap_red(skb, tinfo->srh, proto);
+			err = seg6_do_srh_encap_red(skb, tinfo->srh, proto, dst);
 
 		if (err)
 			return err;
@@ -425,11 +438,11 @@  static int seg6_do_srh(struct sk_buff *skb)
 		skb_push(skb, skb->mac_len);
 
 		if (tinfo->mode == SEG6_IPTUN_MODE_L2ENCAP)
-			err = seg6_do_srh_encap(skb, tinfo->srh,
-						IPPROTO_ETHERNET);
+			err = __seg6_do_srh_encap(skb, tinfo->srh,
+						  IPPROTO_ETHERNET, dst);
 		else
 			err = seg6_do_srh_encap_red(skb, tinfo->srh,
-						    IPPROTO_ETHERNET);
+						    IPPROTO_ETHERNET, dst);
 
 		if (err)
 			return err;
@@ -453,36 +466,37 @@  static int seg6_input_finish(struct net *net, struct sock *sk,
 static int seg6_input_core(struct net *net, struct sock *sk,
 			   struct sk_buff *skb)
 {
-	struct dst_entry *orig_dst = skb_dst(skb);
-	struct dst_entry *dst = NULL;
+	struct dst_entry *dst;
 	struct seg6_lwt *slwt;
 	int err;
 
-	err = seg6_do_srh(skb);
-	if (unlikely(err))
-		goto drop;
-
-	slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate);
+	slwt = seg6_lwt_lwtunnel(skb_dst(skb)->lwtstate);
 
 	local_bh_disable();
 	dst = dst_cache_get(&slwt->cache);
+	local_bh_enable();
+
+	err = seg6_do_srh(skb, dst);
+	if (unlikely(err))
+		goto drop;
 
 	if (!dst) {
 		ip6_route_input(skb);
 		dst = skb_dst(skb);
 		if (!dst->error) {
+			local_bh_disable();
 			dst_cache_set_ip6(&slwt->cache, dst,
 					  &ipv6_hdr(skb)->saddr);
+			local_bh_enable();
 		}
+
+		err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
+		if (unlikely(err))
+			goto drop;
 	} else {
 		skb_dst_drop(skb);
 		skb_dst_set(skb, dst);
 	}
-	local_bh_enable();
-
-	err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
-	if (unlikely(err))
-		goto drop;
 
 	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
 		return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
@@ -523,21 +537,20 @@  static int seg6_input(struct sk_buff *skb)
 static int seg6_output_core(struct net *net, struct sock *sk,
 			    struct sk_buff *skb)
 {
-	struct dst_entry *orig_dst = skb_dst(skb);
-	struct dst_entry *dst = NULL;
+	struct dst_entry *dst;
 	struct seg6_lwt *slwt;
 	int err;
 
-	err = seg6_do_srh(skb);
-	if (unlikely(err))
-		goto drop;
-
-	slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate);
+	slwt = seg6_lwt_lwtunnel(skb_dst(skb)->lwtstate);
 
 	local_bh_disable();
 	dst = dst_cache_get(&slwt->cache);
 	local_bh_enable();
 
+	err = seg6_do_srh(skb, dst);
+	if (unlikely(err))
+		goto drop;
+
 	if (unlikely(!dst)) {
 		struct ipv6hdr *hdr = ipv6_hdr(skb);
 		struct flowi6 fl6;
@@ -559,15 +572,15 @@  static int seg6_output_core(struct net *net, struct sock *sk,
 		local_bh_disable();
 		dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr);
 		local_bh_enable();
+
+		err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
+		if (unlikely(err))
+			goto drop;
 	}
 
 	skb_dst_drop(skb);
 	skb_dst_set(skb, dst);
 
-	err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
-	if (unlikely(err))
-		goto drop;
-
 	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
 		return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb,
 			       NULL, skb_dst(skb)->dev, dst_output);