On Mon, Aug 24, 2026 at 10:21:53AM +0000, Anurag Mandal wrote:
> Added an AVX2 context descriptor path for tunneled
> outer IPv4 and UDP checksum offloads.
> 
> Signed-off-by: Anurag Mandal <[email protected]>
> ---
>  doc/guides/rel_notes/release_26_11.rst    |   5 +
>  drivers/net/intel/ice/ice_dcf_ethdev.c    |   4 +-
>  drivers/net/intel/ice/ice_ethdev.h        |   1 +
>  drivers/net/intel/ice/ice_rxtx.c          |  31 +++++-
>  drivers/net/intel/ice/ice_rxtx.h          |   8 ++
>  drivers/net/intel/ice/ice_rxtx_vec_avx2.c | 119 ++++++++++++++++++++++
>  6 files changed, 163 insertions(+), 5 deletions(-)
> 

I asked AI to take a look at this patch and review it by comparison to the
existing iavf driver. Here's the output, most of which seems relevant.  [It
also is flagging an alignment change that could be fixed in iavf driver,
but that is a separate, minor issue]

Please review feedback below for a new revision. Ideally, I'd like to keep
the ice implementation as aligned as possible to the iavf one, so we can
merge those code paths in future.

/Bruce

Review: net/ice: add AVX2 context descriptor Tx path
Errors
ice_tx_queue_start does not set use_ctx, so runtime-added queues get the wrong 
value.

ice_set_tx_function iterates all existing queues and sets use_ctx. However,
ice_tx_queue_start (called via rte_eth_tx_queue_start) does not set
use_ctx. A queue added at runtime after device start will have use_ctx =
false while the device burst function (ice_xmit_pkts_vec_avx2_ctx_offload)
treats every queue as using context descriptors. When that queue is stopped
or released, ci_txq_release_all_mbufs(txq, txq->use_ctx) with use_ctx =
false iterates physical descriptor indices into sw_ring_vec, which is sized
at nb_tx_desc / 2 — producing an out-of-bounds read on the sw_ring_vec
array.


iavf avoids this by checking txq->use_ctx in the Tx burst path itself (it's
set at configure time from the per-queue IAVF_TX_OFFLOAD_CTX flag). The ice
implementation sets it only once globally.

Warnings
Outer IPv6 tunneling (without checksum offload) skips the context descriptor 
path.

req_features.ctx_desc is set only when RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM
or RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM is configured. An application that
only uses outer-IPv6 tunneling (no outer cksum offload needed) will not
select ICE_TX_AVX2_CTX_OFFLOAD, and the hardware will receive no context
descriptor with the tunnel type or outer IP type — even though
ice_txd_tunneling_ctx would encode them correctly. iavf triggers use_ctx on
any per-packet RTE_MBUF_F_TX_TUNNEL_MASK flag at the burst entry,
regardless of device-level offload configuration. Whether ice hardware
needs the context descriptor for outer IPv6 tunnels at all is
hardware-dependent and should be documented or validated.

No validation that tx_rs_thresh is adequate for 2-descriptor-per-packet mode.

ice_tx_vec_queue_default enforces tx_rs_thresh >= ICE_VPMD_TX_BURST (32).
In ctx mode, each batch handles tx_rs_thresh >> 1 packets. With
tx_rs_thresh = 32, that's 16 packets per burst. No check ensures this is
above a meaningful minimum. This is a weak warning — the current minimum
(16 packets) is still functional — but it's worth a comment.

Implementation Comparison (new ctx path vs iavf)
Aspect                  ice (ice_ctx_vtx1)              iavf (ctx_vtx1)
256-bit store           _mm256_store_si256 (aligned)    _mm256_storeu_si256 
(unaligned)
Context desc high word  CI_TX_DESC_DTYPE_CTX only       DTYPE_CONTEXT + 
optional IL2TAG2/LLDP bits
VLAN QinQ support       Not handled in ctx path         Handled in ctx 
descriptor
ctx_desc triggering     Device-level offload flags      Per-packet ol_flags at 
burst entry
use_ctx set per-queue   Only in ice_set_tx_function (device start)      Also 
maintained per-queue in setup

The use of _mm256_store_si256 (aligned) is consistent with the existing
non-ctx ice_vtx loop which also uses aligned stores, and is safe because
descriptor rings are cache-line aligned and tx_id is always even in ctx
mode.

> diff --git a/doc/guides/rel_notes/release_26_11.rst 
> b/doc/guides/rel_notes/release_26_11.rst
> index 907f9013ff..8ce1875843 100644
> --- a/doc/guides/rel_notes/release_26_11.rst
> +++ b/doc/guides/rel_notes/release_26_11.rst
> @@ -55,6 +55,11 @@ New Features
>       Also, make sure to start the actual text at the margin.
>       =======================================================
>  
> +* **Updated Intel ice driver.**
> +
> +  Added an AVX2 Tx path using context descriptors, allowing tunneled outer 
> IPv4
> +  and UDP checksum offloads without falling back to scalar Tx.
> +
>  * **Updated Intel iavf driver.**
>  
>    * Runtime Rx/Tx queue setup is now automatically disabled while a
> diff --git a/drivers/net/intel/ice/ice_dcf_ethdev.c 
> b/drivers/net/intel/ice/ice_dcf_ethdev.c
> index c78b290b0d..d1cdae6eb9 100644
> --- a/drivers/net/intel/ice/ice_dcf_ethdev.c
> +++ b/drivers/net/intel/ice/ice_dcf_ethdev.c
> @@ -498,7 +498,7 @@ ice_dcf_tx_queue_stop(struct rte_eth_dev *dev, uint16_t 
> tx_queue_id)
>       }
>  
>       txq = dev->data->tx_queues[tx_queue_id];
> -     ci_txq_release_all_mbufs(txq, false);
> +     ci_txq_release_all_mbufs(txq, txq->use_ctx);
>       reset_tx_queue(txq);
>       dev->data->tx_queue_state[tx_queue_id] = RTE_ETH_QUEUE_STATE_STOPPED;
>  
> @@ -648,7 +648,7 @@ ice_dcf_stop_queues(struct rte_eth_dev *dev)
>               txq = dev->data->tx_queues[i];
>               if (!txq)
>                       continue;
> -             ci_txq_release_all_mbufs(txq, false);
> +             ci_txq_release_all_mbufs(txq, txq->use_ctx);
>               reset_tx_queue(txq);
>               dev->data->tx_queue_state[i] = RTE_ETH_QUEUE_STATE_STOPPED;
>       }
> diff --git a/drivers/net/intel/ice/ice_ethdev.h 
> b/drivers/net/intel/ice/ice_ethdev.h
> index 7ee3ea8a70..0e74f8d776 100644
> --- a/drivers/net/intel/ice/ice_ethdev.h
> +++ b/drivers/net/intel/ice/ice_ethdev.h
> @@ -213,6 +213,7 @@ enum ice_tx_func_type {
>       ICE_TX_SIMPLE,
>       ICE_TX_AVX2,
>       ICE_TX_AVX2_OFFLOAD,
> +     ICE_TX_AVX2_CTX_OFFLOAD,
>       ICE_TX_AVX512,
>       ICE_TX_AVX512_OFFLOAD,
>       ICE_TX_NEON,
> diff --git a/drivers/net/intel/ice/ice_rxtx.c 
> b/drivers/net/intel/ice/ice_rxtx.c
> index c4b5454c53..5ec0b4d1fd 100644
> --- a/drivers/net/intel/ice/ice_rxtx.c
> +++ b/drivers/net/intel/ice/ice_rxtx.c
> @@ -1193,7 +1193,7 @@ ice_tx_queue_stop(struct rte_eth_dev *dev, uint16_t 
> tx_queue_id)
>               return -EINVAL;
>       }
>  
> -     ci_txq_release_all_mbufs(txq, false);
> +     ci_txq_release_all_mbufs(txq, txq->use_ctx);
>       ice_reset_tx_queue(txq);
>       dev->data->tx_queue_state[tx_queue_id] = RTE_ETH_QUEUE_STATE_STOPPED;
>  
> @@ -1256,7 +1256,7 @@ ice_fdir_tx_queue_stop(struct rte_eth_dev *dev, 
> uint16_t tx_queue_id)
>               return -EINVAL;
>       }
>  
> -     ci_txq_release_all_mbufs(txq, false);
> +     ci_txq_release_all_mbufs(txq, txq->use_ctx);
>       txq->qtx_tail = NULL;
>  
>       return 0;
> @@ -1744,7 +1744,7 @@ ice_tx_queue_release(void *txq)
>               return;
>       }
>  
> -     ci_txq_release_all_mbufs(q, false);
> +     ci_txq_release_all_mbufs(q, q->use_ctx);
>       rte_free(q->sw_ring);
>       rte_free(q->rs_last_id);
>       if (q->tsq) {
> @@ -3554,6 +3554,16 @@ static const struct ci_tx_path_info 
> ice_tx_path_infos[] = {
>               },
>               .pkt_prep = ice_prep_pkts
>       },
> +     [ICE_TX_AVX2_CTX_OFFLOAD] = {
> +             .pkt_burst = ice_xmit_pkts_vec_avx2_ctx_offload,
> +             .info = "Context Offload Vector AVX2",
> +             .features = {
> +                     .tx_offloads = ICE_TX_VECTOR_CTX_OFFLOAD_OFFLOADS,
> +                     .simd_width = RTE_VECT_SIMD_256,
> +                     .ctx_desc = true
> +             },
> +             .pkt_prep = ice_prep_pkts
> +     },
>  #ifdef CC_AVX512_SUPPORT
>       [ICE_TX_AVX512] = {
>               .pkt_burst = ice_xmit_pkts_vec_avx512,
> @@ -3755,11 +3765,17 @@ ice_set_tx_function(struct rte_eth_dev *dev)
>  {
>       struct ice_adapter *ad =
>               ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
> +     const struct ci_tx_path_features *selected_features;
> +     struct ci_tx_queue *txq;
>       int mbuf_check = ad->devargs.mbuf_check;
> +     int i;
>       struct ci_tx_path_features req_features = {
>               .tx_offloads = dev->data->dev_conf.txmode.offloads,
>               .simd_width = RTE_VECT_SIMD_DISABLED,
>       };
> +     req_features.ctx_desc = req_features.tx_offloads &
> +             (RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM |
> +              RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM);
>  
>       /* If the device has started the function has already been selected. */
>       if (dev->data->dev_started)
> @@ -3785,6 +3801,15 @@ ice_set_tx_function(struct rte_eth_dev *dev)
>       ad->tx_vec_allowed =
>               (ice_tx_path_infos[ad->tx_func_type].features.simd_width >= 
> RTE_VECT_SIMD_128);
>  #endif
> +     selected_features = &ice_tx_path_infos[ad->tx_func_type].features;
> +     for (i = 0; i < dev->data->nb_tx_queues; i++) {
> +             txq = dev->data->tx_queues[i];
> +             if (!txq)
> +                     continue;
> +             txq->use_ctx = selected_features->ctx_desc;
> +             txq->use_vec_entry = selected_features->simple_tx ||
> +                     selected_features->simd_width >= RTE_VECT_SIMD_128;
> +     }
>  
>       dev->tx_pkt_burst = mbuf_check ? ice_xmit_pkts_check :
>                                        
> ice_tx_path_infos[ad->tx_func_type].pkt_burst;
> diff --git a/drivers/net/intel/ice/ice_rxtx.h 
> b/drivers/net/intel/ice/ice_rxtx.h
> index 999b6b30d6..37e346fe39 100644
> --- a/drivers/net/intel/ice/ice_rxtx.h
> +++ b/drivers/net/intel/ice/ice_rxtx.h
> @@ -136,6 +136,11 @@
>       RTE_ETH_TX_OFFLOAD_TCP_CKSUM |          \
>       RTE_ETH_TX_OFFLOAD_SCTP_CKSUM)
>  
> +#define ICE_TX_VECTOR_CTX_OFFLOAD_OFFLOADS ( \
> +     ICE_TX_VECTOR_OFFLOAD_OFFLOADS |                \
> +     RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM |   \
> +     RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM)
> +
>  /* Max header size can be 2K - 64 bytes */
>  #define ICE_RX_HDR_BUF_SIZE    (2048 - 64)
>  
> @@ -284,6 +289,9 @@ uint16_t ice_xmit_pkts_vec_avx2(void *tx_queue, struct 
> rte_mbuf **tx_pkts,
>                               uint16_t nb_pkts);
>  uint16_t ice_xmit_pkts_vec_avx2_offload(void *tx_queue, struct rte_mbuf 
> **tx_pkts,
>                                       uint16_t nb_pkts);
> +uint16_t ice_xmit_pkts_vec_avx2_ctx_offload(void *tx_queue,
> +                                         struct rte_mbuf **tx_pkts,
> +                                         uint16_t nb_pkts);
>  uint16_t ice_recv_pkts_vec_avx512(void *rx_queue, struct rte_mbuf **rx_pkts,
>                                 uint16_t nb_pkts);
>  uint16_t ice_recv_pkts_vec_avx512_offload(void *rx_queue,
> diff --git a/drivers/net/intel/ice/ice_rxtx_vec_avx2.c 
> b/drivers/net/intel/ice/ice_rxtx_vec_avx2.c
> index b72f69a47b..88a3dfb1b6 100644
> --- a/drivers/net/intel/ice/ice_rxtx_vec_avx2.c
> +++ b/drivers/net/intel/ice/ice_rxtx_vec_avx2.c
> @@ -837,6 +837,125 @@ ice_vtx(volatile struct ci_tx_desc *txdp,
>       }
>  }
>  
> +static inline void
> +ice_ctx_vtx1(volatile struct ci_tx_desc *txdp, struct rte_mbuf *pkt,
> +          uint64_t flags, bool offload)
> +{
> +     uint64_t high_data_qw = CI_TX_DESC_DTYPE_DATA |
> +                     (flags << CI_TXD_QW1_CMD_S) |
> +                     ((uint64_t)pkt->data_len << CI_TXD_QW1_TX_BUF_SZ_S);
> +     const uint64_t low_ctx_qw = offload ? ice_txd_tunneling_ctx(pkt) : 0;
> +
> +     if (offload)
> +             ice_txd_enable_offload(pkt, &high_data_qw);
> +
> +     const __m256i ctx_data_desc = _mm256_set_epi64x(high_data_qw,
> +                     rte_pktmbuf_iova(pkt), CI_TX_DESC_DTYPE_CTX, 
> low_ctx_qw);
> +
> +     _mm256_store_si256(RTE_CAST_PTR(__m256i *, txdp), ctx_data_desc);
> +}
> +
> +static inline void
> +ice_ctx_vtx(volatile struct ci_tx_desc *txdp, struct rte_mbuf **pkt,
> +         uint16_t nb_pkts, uint64_t flags, bool offload)
> +{
> +     while (nb_pkts) {
> +             ice_ctx_vtx1(txdp, *pkt, flags, offload);
> +             txdp += 2;
> +             pkt++;
> +             nb_pkts--;
> +     }
> +}
> +
> +static inline uint16_t
> +ice_xmit_fixed_burst_vec_avx2_ctx(void *tx_queue, struct rte_mbuf **tx_pkts,
> +                               uint16_t nb_pkts, bool offload)
> +{
> +     struct ci_tx_queue *txq = tx_queue;
> +     volatile struct ci_tx_desc *txdp;
> +     struct ci_tx_entry_vec *txep;
> +     uint16_t n, nb_commit, nb_mbuf, tx_id;
> +     const uint64_t flags = CI_TX_DESC_CMD_DEFAULT;
> +     const uint64_t rs = CI_TX_DESC_CMD_RS | flags;
> +
> +     if (txq->nb_tx_free < txq->tx_free_thresh)
> +             ci_tx_free_bufs_vec(txq, ice_tx_desc_done, true);
> +
> +     nb_commit = (uint16_t)RTE_MIN(txq->nb_tx_free,
> +                     (uint32_t)nb_pkts * 2);
> +     nb_commit &= (uint16_t)~1;
> +     if (unlikely(nb_commit == 0))
> +             return 0;
> +
> +     nb_pkts = nb_commit >> 1;
> +     tx_id = txq->tx_tail;
> +     txdp = &txq->ci_tx_ring[tx_id];
> +     txep = &txq->sw_ring_vec[tx_id >> 1];
> +
> +     txq->nb_tx_free = (uint16_t)(txq->nb_tx_free - nb_commit);
> +     n = (uint16_t)(txq->nb_tx_desc - tx_id);
> +
> +     if (nb_commit >= n) {
> +             nb_mbuf = n >> 1;
> +             ci_tx_backlog_entry_vec(txep, tx_pkts, nb_mbuf);
> +
> +             ice_ctx_vtx(txdp, tx_pkts, nb_mbuf - 1, flags, offload);
> +             tx_pkts += nb_mbuf - 1;
> +             txdp += n - 2;
> +             ice_ctx_vtx1(txdp, *tx_pkts++, rs, offload);
> +
> +             nb_commit = (uint16_t)(nb_commit - n);
> +             txq->tx_next_rs = (uint16_t)(txq->tx_rs_thresh - 1);
> +             tx_id = 0;
> +             txdp = txq->ci_tx_ring;
> +             txep = txq->sw_ring_vec;
> +     }
> +
> +     nb_mbuf = nb_commit >> 1;
> +     ci_tx_backlog_entry_vec(txep, tx_pkts, nb_mbuf);
> +     ice_ctx_vtx(txdp, tx_pkts, nb_mbuf, flags, offload);
> +     tx_id = (uint16_t)(tx_id + nb_commit);
> +
> +     if (tx_id > txq->tx_next_rs) {
> +             txq->ci_tx_ring[txq->tx_next_rs].cmd_type_offset_bsz |=
> +                     rte_cpu_to_le_64((uint64_t)CI_TX_DESC_CMD_RS << 
> CI_TXD_QW1_CMD_S);
> +             txq->tx_next_rs = (uint16_t)(txq->tx_next_rs + 
> txq->tx_rs_thresh);
> +     }
> +
> +     txq->tx_tail = tx_id;
> +     ICE_PCI_REG_WC_WRITE(txq->qtx_tail, txq->tx_tail);
> +
> +     return nb_pkts;
> +}
> +
> +static inline uint16_t
> +ice_xmit_pkts_vec_avx2_ctx_common(void *tx_queue, struct rte_mbuf **tx_pkts,
> +                               uint16_t nb_pkts, bool offload)
> +{
> +     struct ci_tx_queue *txq = tx_queue;
> +     uint16_t nb_tx = 0;
> +
> +     while (nb_pkts) {
> +             const uint16_t num = RTE_MIN(nb_pkts, txq->tx_rs_thresh >> 1);
> +             const uint16_t ret = ice_xmit_fixed_burst_vec_avx2_ctx(tx_queue,
> +                             &tx_pkts[nb_tx], num, offload);
> +
> +             nb_tx += ret;
> +             nb_pkts -= ret;
> +             if (ret < num)
> +                     break;
> +     }
> +
> +     return nb_tx;
> +}
> +
> +uint16_t
> +ice_xmit_pkts_vec_avx2_ctx_offload(void *tx_queue,
> +             struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
> +{
> +     return ice_xmit_pkts_vec_avx2_ctx_common(tx_queue, tx_pkts, nb_pkts, 
> true);
> +}
> +
>  static __rte_always_inline uint16_t
>  ice_xmit_fixed_burst_vec_avx2(void *tx_queue, struct rte_mbuf **tx_pkts,
>                             uint16_t nb_pkts, bool offload)
> -- 
> 2.34.1
> 

Reply via email to