From: Jihong Min <hurryman2212@gmail.com>
To: Christian Marangi <ansuelsmth@gmail.com>,
Antoine Tenart <atenart@kernel.org>,
Herbert Xu <herbert@gondor.apana.org.au>,
"David S . Miller" <davem@davemloft.net>,
Lorenzo Bianconi <lorenzo@kernel.org>,
Andrew Lunn <andrew+netdev@lunn.ch>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>,
Steffen Klassert <steffen.klassert@secunet.com>
Cc: linux-kernel@vger.kernel.org, linux-crypto@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, netdev@vger.kernel.org,
Jihong Min <hurryman2212@gmail.com>
Subject: [PATCH 1/3] xfrm: extend ESP offload infrastructure for packet engines
Date: Sat, 23 May 2026 21:15:20 +0900 [thread overview]
Message-ID: <20260523121522.3023992-2-hurryman2212@gmail.com> (raw)
In-Reply-To: <20260523121522.3023992-1-hurryman2212@gmail.com>
Some ESP offload engines operate on whole ESP packets rather than the
generic software trailer layout. They can generate outbound ESP padding,
next-header and ICV bytes in hardware, and inbound decapsulation can
return an already-trimmed packet with the recovered next-header value.
Add a netdev offload callback for drivers to opt into hardware-generated
ESP TX trailers, carry the reserved ESP TX tail length in xfrm_offload,
and let ESP input skip software trailer removal when hardware has already
done it.
This keeps the default ESP offload behavior unchanged for existing devices
while providing the infrastructure needed by packet-mode ESP engines.
Assisted-by: Codex:gpt-5.5
Signed-off-by: Jihong Min <hurryman2212@gmail.com>
---
include/linux/netdevice.h | 3 +++
include/net/xfrm.h | 8 +++++++-
net/ipv4/esp4.c | 6 +++++-
net/ipv4/esp4_offload.c | 29 ++++++++++++++++++++++++++++-
net/ipv6/esp6.c | 6 +++++-
net/ipv6/esp6_offload.c | 29 ++++++++++++++++++++++++++++-
6 files changed, 76 insertions(+), 5 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 0e1e581efc5a..b6ff04c3df78 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1043,6 +1043,9 @@ struct xfrmdev_ops {
struct xfrm_state *x);
bool (*xdo_dev_offload_ok) (struct sk_buff *skb,
struct xfrm_state *x);
+ /* Return true when the device generates the ESP trailer/ICV itself. */
+ bool (*xdo_dev_esp_tx_hw_trailer)(struct sk_buff *skb,
+ struct xfrm_state *x);
void (*xdo_dev_state_advance_esn) (struct xfrm_state *x);
void (*xdo_dev_state_update_stats) (struct xfrm_state *x);
int (*xdo_dev_policy_add) (struct xfrm_policy *x, struct netlink_ext_ack *extack);
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 10d3edde6b2f..160069901e0a 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1141,7 +1141,7 @@ struct xfrm_offload {
#define CRYPTO_FALLBACK 8
#define XFRM_GSO_SEGMENT 16
#define XFRM_GRO 32
-/* 64 is free */
+#define XFRM_ESP_NO_TRAILER 64
#define XFRM_DEV_RESUME 128
#define XFRM_XMIT 256
@@ -1158,6 +1158,12 @@ struct xfrm_offload {
/* Used to keep whole l2 header for transport mode GRO */
__u16 orig_mac_len;
+ /*
+ * ESP packet engines can reserve tailroom in the generic ESP path and
+ * generate padding, next-header and ICV bytes during device TX.
+ */
+ __u16 esp_tx_tailen;
+
__u8 proto;
__u8 inner_ipproto;
};
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 6a5febbdbee4..f21c8f2e60f7 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -720,7 +720,11 @@ int esp_input_done2(struct sk_buff *skb, int err)
if (unlikely(err))
goto out;
- err = esp_remove_trailer(skb);
+ /* Hardware ESP decapsulation can already remove pad/trailer/ICV. */
+ if (xo && (xo->flags & XFRM_ESP_NO_TRAILER))
+ err = xo->proto;
+ else
+ err = esp_remove_trailer(skb);
if (unlikely(err < 0))
goto out;
diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c
index abd77162f5e7..f00fff98b69f 100644
--- a/net/ipv4/esp4_offload.c
+++ b/net/ipv4/esp4_offload.c
@@ -270,8 +270,10 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
struct xfrm_offload *xo;
struct ip_esp_hdr *esph;
struct crypto_aead *aead;
+ struct sk_buff *trailer;
struct esp_info esp;
bool hw_offload = true;
+ bool hw_trailer = false;
__u32 seq;
int encap_type = 0;
@@ -281,6 +283,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
if (!xo)
return -EINVAL;
+ xo->esp_tx_tailen = 0;
if ((!(features & NETIF_F_HW_ESP) &&
!(skb->dev->gso_partial_features & NETIF_F_HW_ESP)) ||
@@ -303,13 +306,37 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize);
esp.plen = esp.clen - skb->len - esp.tfclen;
esp.tailen = esp.tfclen + esp.plen + alen;
+ if (esp.tailen > U16_MAX)
+ return -EINVAL;
esp.esph = ip_esp_hdr(skb);
if (x->encap)
encap_type = x->encap->encap_type;
- if (!hw_offload || !skb_is_gso(skb) || (hw_offload && encap_type == UDP_ENCAP_ESPINUDP)) {
+ if (hw_offload && !skb_is_gso(skb) && !encap_type && x->xso.dev &&
+ x->xso.dev->xfrmdev_ops &&
+ x->xso.dev->xfrmdev_ops->xdo_dev_esp_tx_hw_trailer)
+ hw_trailer =
+ x->xso.dev->xfrmdev_ops->xdo_dev_esp_tx_hw_trailer(skb, x);
+
+ if (hw_trailer) {
+ int esph_offset;
+
+ /*
+ * The device packet engine will write ESP padding, next-header
+ * and ICV bytes. Keep skb->len unchanged here, but make sure the
+ * later DMA writer owns enough linear tailroom.
+ */
+ esph_offset = (unsigned char *)esp.esph - skb_transport_header(skb);
+ esp.nfrags = skb_cow_data(skb, esp.tailen, &trailer);
+ if (esp.nfrags < 0)
+ return esp.nfrags;
+ esp.esph = (struct ip_esp_hdr *)(skb_transport_header(skb) +
+ esph_offset);
+ xo->esp_tx_tailen = esp.tailen;
+ } else if (!hw_offload || !skb_is_gso(skb) ||
+ (hw_offload && encap_type == UDP_ENCAP_ESPINUDP)) {
esp.nfrags = esp_output_head(x, skb, &esp);
if (esp.nfrags < 0)
return esp.nfrags;
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 9c06c5a1419d..730588f8eaba 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -751,7 +751,11 @@ int esp6_input_done2(struct sk_buff *skb, int err)
if (unlikely(err))
goto out;
- err = esp_remove_trailer(skb);
+ /* Hardware ESP decapsulation can already remove pad/trailer/ICV. */
+ if (xo && (xo->flags & XFRM_ESP_NO_TRAILER))
+ err = xo->proto;
+ else
+ err = esp_remove_trailer(skb);
if (unlikely(err < 0))
goto out;
diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c
index 22895521a57d..d124493da40b 100644
--- a/net/ipv6/esp6_offload.c
+++ b/net/ipv6/esp6_offload.c
@@ -308,8 +308,10 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features
int blksize;
struct xfrm_offload *xo;
struct crypto_aead *aead;
+ struct sk_buff *trailer;
struct esp_info esp;
bool hw_offload = true;
+ bool hw_trailer = false;
__u32 seq;
esp.inplace = true;
@@ -318,6 +320,7 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features
if (!xo)
return -EINVAL;
+ xo->esp_tx_tailen = 0;
if (!(features & NETIF_F_HW_ESP) || x->xso.dev != skb->dev) {
xo->flags |= CRYPTO_FALLBACK;
@@ -338,8 +341,32 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features
esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize);
esp.plen = esp.clen - skb->len - esp.tfclen;
esp.tailen = esp.tfclen + esp.plen + alen;
+ if (esp.tailen > U16_MAX)
+ return -EINVAL;
- if (!hw_offload || !skb_is_gso(skb)) {
+ if (hw_offload && !skb_is_gso(skb) && !x->encap && x->xso.dev &&
+ x->xso.dev->xfrmdev_ops &&
+ x->xso.dev->xfrmdev_ops->xdo_dev_esp_tx_hw_trailer)
+ hw_trailer =
+ x->xso.dev->xfrmdev_ops->xdo_dev_esp_tx_hw_trailer(skb, x);
+
+ if (hw_trailer) {
+ int esph_offset;
+
+ /*
+ * The device packet engine will write ESP padding, next-header
+ * and ICV bytes. Keep skb->len unchanged here, but make sure the
+ * later DMA writer owns enough linear tailroom.
+ */
+ esp.esph = ip_esp_hdr(skb);
+ esph_offset = (unsigned char *)esp.esph - skb_transport_header(skb);
+ esp.nfrags = skb_cow_data(skb, esp.tailen, &trailer);
+ if (esp.nfrags < 0)
+ return esp.nfrags;
+ esp.esph = (struct ip_esp_hdr *)(skb_transport_header(skb) +
+ esph_offset);
+ xo->esp_tx_tailen = esp.tailen;
+ } else if (!hw_offload || !skb_is_gso(skb)) {
esp.nfrags = esp6_output_head(x, skb, &esp);
if (esp.nfrags < 0)
return esp.nfrags;
--
2.53.0
next prev parent reply other threads:[~2026-05-23 12:15 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-23 12:15 [PATCH 0/3] Add packet-mode ESP offload for Airoha/EIP93 Jihong Min
2026-05-23 12:15 ` Jihong Min [this message]
2026-05-23 12:15 ` [PATCH 2/3] crypto: inside-secure: add EIP93 ESP packet backend Jihong Min
2026-05-27 10:08 ` Simon Horman
2026-05-23 12:15 ` [PATCH 3/3] net: airoha: add EIP93-backed ESP XFRM offload Jihong Min
2026-05-23 12:24 ` [PATCH 0/3] Add packet-mode ESP offload for Airoha/EIP93 Jihong Min
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260523121522.3023992-2-hurryman2212@gmail.com \
--to=hurryman2212@gmail.com \
--cc=andrew+netdev@lunn.ch \
--cc=ansuelsmth@gmail.com \
--cc=atenart@kernel.org \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=herbert@gondor.apana.org.au \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=lorenzo@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=steffen.klassert@secunet.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox