[PATCH v2 01/14] net/enetc: add KEEP_CRC offload support for ENETC4
Gagandeep Singh
g.singh at nxp.com
Fri Aug 7 05:48:16 CEST 2026
The legacy ENETC (LS1028A) driver already supports the
RTE_ETH_RX_OFFLOAD_KEEP_CRC offload, but ENETC4 (i.MX95 NETC) did not.
Add KEEP_CRC support for ENETC4 (both PF and VF):
- Advertise RTE_ETH_RX_OFFLOAD_KEEP_CRC in the supported Rx offloads
for the ENETC4 PF and VF. The VF reuses the PF Rx queue setup, so the
HW configuration and datapath handling apply to both.
- Configure the per-ring RBaMR[CRC] bit in the Rx queue setup so that
HW preserves the Ethernet FCS in the receive buffer when the offload
is requested (0 = FCS removed, 1 = FCS preserved).
- Follow the DPDK convention used by the legacy ENETC and ixgbe drivers:
set crc_len to RTE_ETHER_CRC_LEN when KEEP_CRC is enabled and have the
datapath subtract crc_len from pkt_len/data_len. ENETC and ENETC4 share
the same datapath (enetc_rxtx.c), so the semantics stay consistent.
- Handle the scatter-gather boundary case where the 4-byte FCS straddles
the last two segments: drop the trailing segment, decrement nb_segs and
trim the carry-over from its predecessor.
Update the enetc4 feature matrix to list CRC offload.
Signed-off-by: Gagandeep Singh <g.singh at nxp.com>
---
doc/guides/nics/features/enetc4.ini | 1 +
doc/guides/rel_notes/release_26_11.rst | 6 ++++
drivers/net/enetc/base/enetc4_hw.h | 4 +++
drivers/net/enetc/enetc4_ethdev.c | 31 ++++++++++++++++++--
drivers/net/enetc/enetc4_vf.c | 1 +
drivers/net/enetc/enetc_rxtx.c | 39 +++++++++++++++++++++++---
6 files changed, 75 insertions(+), 7 deletions(-)
diff --git a/doc/guides/nics/features/enetc4.ini b/doc/guides/nics/features/enetc4.ini
index 698140e30b..91b18d979e 100644
--- a/doc/guides/nics/features/enetc4.ini
+++ b/doc/guides/nics/features/enetc4.ini
@@ -16,6 +16,7 @@ Packet type parsing = Y
Basic stats = Y
L3 checksum offload = Y
L4 checksum offload = Y
+CRC offload = Y
Queue start/stop = Y
Scattered Rx = Y
Linux = Y
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index c8cc86295d..3678dd6894 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -56,6 +56,12 @@ New Features
=======================================================
+* **Updated NXP ENETC4 PMD.**
+
+ Updated the NXP ENETC4 poll mode driver for i.MX95:
+
+ * Added KEEP_CRC Rx offload support for the ENETC4 PMD to preserve the Ethernet FCS.
+
Removed Items
-------------
diff --git a/drivers/net/enetc/base/enetc4_hw.h b/drivers/net/enetc/base/enetc4_hw.h
index 9685e7e1e5..0105549857 100644
--- a/drivers/net/enetc/base/enetc4_hw.h
+++ b/drivers/net/enetc/base/enetc4_hw.h
@@ -68,6 +68,10 @@ struct enetc_msg_swbd {
#define PM_CMD_CFG_TX_EN BIT(0)
#define PM_CMD_CFG_RX_EN BIT(1)
+/* RBaMR[CRC]: 0 = FCS removed, 1 = FCS preserved (KEEP_CRC) */
+#define ENETC4_RBMR_CRC BIT(8)
+
+
/* i.MX95 supports jumbo frame, but it is recommended to set the max frame
* size to 2000 bytes.
*/
diff --git a/drivers/net/enetc/enetc4_ethdev.c b/drivers/net/enetc/enetc4_ethdev.c
index 2ddd63dafb..1a8056580c 100644
--- a/drivers/net/enetc/enetc4_ethdev.c
+++ b/drivers/net/enetc/enetc4_ethdev.c
@@ -11,6 +11,19 @@
#include "enetc_logs.h"
#include "enetc.h"
+/* Supported Rx offloads */
+static uint64_t dev_rx_offloads_sup =
+ RTE_ETH_RX_OFFLOAD_IPV4_CKSUM |
+ RTE_ETH_RX_OFFLOAD_UDP_CKSUM |
+ RTE_ETH_RX_OFFLOAD_TCP_CKSUM |
+ RTE_ETH_RX_OFFLOAD_KEEP_CRC;
+
+/* Supported Tx offloads */
+static uint64_t dev_tx_offloads_sup =
+ RTE_ETH_TX_OFFLOAD_IPV4_CKSUM |
+ RTE_ETH_TX_OFFLOAD_UDP_CKSUM |
+ RTE_ETH_TX_OFFLOAD_TCP_CKSUM;
+
#define ENETC4_TXQ_PRIORITIES "enetc4_txq_prior"
#define ENETC4_NC_MEMORY "nc"
@@ -536,6 +549,8 @@ enetc4_rx_queue_setup(struct rte_eth_dev *dev,
struct enetc_eth_adapter *adapter =
ENETC_DEV_PRIVATE(data->dev_private);
uint64_t rx_offloads = data->dev_conf.rxmode.offloads;
+ uint32_t rx_enable;
+ bool keep_crc;
PMD_INIT_FUNC_TRACE();
if (nb_rx_desc > MAX_BD_COUNT)
@@ -549,6 +564,9 @@ enetc4_rx_queue_setup(struct rte_eth_dev *dev,
}
rx_ring->index = rx_queue_id;
+ keep_crc = !!(rx_offloads & RTE_ETH_RX_OFFLOAD_KEEP_CRC);
+ rx_ring->crc_len = (uint8_t)(keep_crc ? RTE_ETHER_CRC_LEN : 0);
+
err = enetc4_alloc_rxbdr(rx_ring, nb_rx_desc);
if (err)
goto fail;
@@ -562,19 +580,25 @@ enetc4_rx_queue_setup(struct rte_eth_dev *dev,
data->rx_queues[rx_queue_id] = rx_ring;
rx_ring->rx_deferred_start = rx_conf->rx_deferred_start;
+ if (keep_crc)
+ rx_enable |= ENETC4_RBMR_CRC;
+ else
+ rx_enable &= ~ENETC4_RBMR_CRC;
+
if (!rx_conf->rx_deferred_start) {
/* enable ring */
+ rx_enable |= ENETC_RBMR_EN;
enetc4_rxbdr_wr(&adapter->hw.hw, rx_ring->index, ENETC_RBMR,
- ENETC_RBMR_EN);
+ rx_enable);
dev->data->rx_queue_state[rx_ring->index] =
RTE_ETH_QUEUE_STATE_STARTED;
} else {
+ enetc4_rxbdr_wr(&adapter->hw.hw, rx_ring->index, ENETC_RBMR,
+ rx_enable);
dev->data->rx_queue_state[rx_ring->index] =
RTE_ETH_QUEUE_STATE_STOPPED;
}
- rx_ring->crc_len = (uint8_t)((rx_offloads & RTE_ETH_RX_OFFLOAD_KEEP_CRC) ?
- RTE_ETHER_CRC_LEN : 0);
return 0;
fail:
rte_free(rx_ring);
@@ -582,6 +606,7 @@ enetc4_rx_queue_setup(struct rte_eth_dev *dev,
return err;
}
+
void
enetc4_rx_queue_release(struct rte_eth_dev *dev, uint16_t qid)
{
diff --git a/drivers/net/enetc/enetc4_vf.c b/drivers/net/enetc/enetc4_vf.c
index ef5f1e6d66..83a1e4931f 100644
--- a/drivers/net/enetc/enetc4_vf.c
+++ b/drivers/net/enetc/enetc4_vf.c
@@ -52,6 +52,7 @@ static uint64_t dev_rx_offloads_sup =
RTE_ETH_RX_OFFLOAD_UDP_CKSUM |
RTE_ETH_RX_OFFLOAD_TCP_CKSUM |
RTE_ETH_RX_OFFLOAD_VLAN_FILTER |
+ RTE_ETH_RX_OFFLOAD_KEEP_CRC |
RTE_ETH_RX_OFFLOAD_SCATTER;
/* Supported Tx offloads */
diff --git a/drivers/net/enetc/enetc_rxtx.c b/drivers/net/enetc/enetc_rxtx.c
index 8678bafece..e3bef607dd 100644
--- a/drivers/net/enetc/enetc_rxtx.c
+++ b/drivers/net/enetc/enetc_rxtx.c
@@ -530,6 +530,28 @@ enetc_clean_rx_ring(struct enetc_bdr *rx_ring,
return rx_frm_cnt;
}
+/*
+ * Trim the Ethernet FCS from a received cluster when HW CRC strip is
+ * disabled. pkt_len is reduced by crc_len. If the FCS straddles the last
+ * two segments (last seg holds fewer bytes than crc_len), drop the trailing
+ * segment and trim the carry-over from its predecessor. prev_seg is the
+ * segment preceding last_seg in the chain (the caller already tracks it).
+ */
+static inline void
+enetc_rx_crc_trim(struct rte_mbuf *first_seg, struct rte_mbuf *prev_seg,
+ struct rte_mbuf *last_seg, uint16_t crc_len)
+{
+ first_seg->pkt_len -= crc_len;
+ if (likely(last_seg->data_len > crc_len)) {
+ last_seg->data_len -= crc_len;
+ } else if (prev_seg != NULL) {
+ first_seg->nb_segs--;
+ prev_seg->data_len -= crc_len - last_seg->data_len;
+ prev_seg->next = NULL;
+ rte_pktmbuf_free_seg(last_seg);
+ }
+}
+
static int
enetc_clean_rx_ring_nc(struct enetc_bdr *rx_ring,
struct rte_mbuf **rx_pkts,
@@ -539,7 +561,7 @@ enetc_clean_rx_ring_nc(struct enetc_bdr *rx_ring,
int cleaned_cnt, i;
struct enetc_swbd *rx_swbd;
union enetc_rx_bd *rxbd, rxbd_temp;
- struct rte_mbuf *first_seg, *cur_seg;
+ struct rte_mbuf *first_seg = NULL, *cur_seg = NULL;
uint32_t bd_status;
uint8_t *data;
uint32_t j;
@@ -572,6 +594,7 @@ enetc_clean_rx_ring_nc(struct enetc_bdr *rx_ring,
if (!first_seg) {
first_seg = seg;
cur_seg = seg;
+ prev_seg = NULL;
first_seg->pkt_len = data_len;
enetc_dev_rx_parse(first_seg, rxbd_temp.r.parse_summary);
first_seg->hash.rss = rxbd_temp.r.rss_hash;
@@ -579,6 +602,7 @@ enetc_clean_rx_ring_nc(struct enetc_bdr *rx_ring,
first_seg->pkt_len += data_len;
first_seg->nb_segs++;
cur_seg->next = seg;
+ prev_seg = cur_seg;
cur_seg = seg;
}
@@ -590,7 +614,9 @@ enetc_clean_rx_ring_nc(struct enetc_bdr *rx_ring,
if (bd_status & ENETC_RXBD_LSTATUS_F) {
seg->next = NULL;
- first_seg->pkt_len -= rx_ring->crc_len;
+ if (rx_ring->crc_len)
+ enetc_rx_crc_trim(first_seg, prev_seg, seg,
+ rx_ring->crc_len);
rx_pkts[rx_frm_cnt] = first_seg;
rx_frm_cnt++;
first_seg = NULL;
@@ -760,7 +786,7 @@ enetc_clean_rx_ring_cacheable(struct enetc_bdr *rx_ring,
int cleaned_cnt, i;
struct enetc_swbd *rx_swbd;
union enetc_rx_bd *rxbd;
- struct rte_mbuf *first_seg, *cur_seg;
+ struct rte_mbuf *first_seg = NULL, *cur_seg = NULL;
uint32_t bd_status;
uint8_t *data;
uint32_t j;
@@ -815,6 +841,7 @@ enetc_clean_rx_ring_cacheable(struct enetc_bdr *rx_ring,
if (!first_seg) {
first_seg = seg;
cur_seg = seg;
+ prev_seg = NULL;
first_seg->pkt_len = data_len;
enetc_dev_rx_parse(first_seg,
rxbd->r.parse_summary);
@@ -823,6 +850,7 @@ enetc_clean_rx_ring_cacheable(struct enetc_bdr *rx_ring,
first_seg->pkt_len += data_len;
first_seg->nb_segs++;
cur_seg->next = seg;
+ prev_seg = cur_seg;
cur_seg = seg;
}
@@ -838,7 +866,10 @@ enetc_clean_rx_ring_cacheable(struct enetc_bdr *rx_ring,
if (bd_status & ENETC_RXBD_LSTATUS_F) {
seg->next = NULL;
- first_seg->pkt_len -= rx_ring->crc_len;
+ if (rx_ring->crc_len)
+ enetc_rx_crc_trim(first_seg, prev_seg, seg,
+ rx_ring->crc_len);
+
rx_pkts[rx_frm_cnt] = first_seg;
rx_frm_cnt++;
first_seg = NULL;
--
2.25.1
More information about the dev
mailing list