> -----Original Message-----

> From: Richardson, Bruce <[email protected]>

> Sent: 24 August 2026 20:17

> To: Mandal, Anurag <[email protected]>

> Cc: [email protected]; Burakov, Anatoly <[email protected]>

> Subject: Re: [PATCH 3/4] net/ice: add AVX2 context descriptor Tx path

>

> 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]<mailto:[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.

>



Hi Bruce,

I have sent v2. Also, have following comments w.r.t. comparisons between ice 
and iavf.

1. Aligned vs unaligned store:- It is safe in ice because ctx mode keeps tx_id 
even, so txdp is always 32-byte aligned, and it matches the existing 
ice_vtx()/ice_vtx1(). iavf uses storeu plus an explicit "if unaligned, do one 
to align" prologue in ctx_vtx() — that prologue is dead code inherited from the 
non-ctx path, since iavf's ctx mode also guarantees even tx_id. If anything, 
iavf should adopt ice's form, not the reverse. Note the AVX-512 ice_ctx_vtx() 
does use _mm512_storeu_si512, correctly, since a 4-descriptor group is only 
32-byte aligned.



2. Context descriptor high word:- The extra bits iavf sets have no ice PF 
equivalent: SWTCH_UPLINK/LLDP is a VF-specific devarg, and IL2TAG2 is only 
needed for the L2TAG2 VLAN insertion mode that iavf negotiates over virtchnl. 
ice's vector path inserts VLAN via IL2TAG1 in the data descriptor in 
ice_txd_enable_offload(). ice also uses the common CI_TX_DESC_DTYPE_CTX rather 
than a driver-local DTYPE_CONTEXT, so it is ahead of iavf on the merge path 
here.



3. QinQ:- RTE_ETH_TX_OFFLOAD_QINQ_INSERT is deliberately absent from 
ICE_TX_VECTOR_CTX_OFFLOAD_OFFLOADS, so requesting QinQ falls back to scalar, 
which handles it. It is in ICE_TX_SCALAR_OFFLOADS and advertised in dev_info, 
so nothing is silently dropped.
Adding QinQ to the ctx path is a reasonable follow-up patch separately.



4. ctx_desc triggering — iavf does not inspect per-packet ol_flags at burst 
entry. iavf_xmit_pkts_vec_avx512_ctx_offload() → 
iavf_xmit_pkts_vec_avx512_ctx_cmn() just loops on tx_rs_thresh and writes a 
context descriptor for every packet unconditionally. Selection is entirely 
device-level in iavf_rxtx.c:3936: offload matching via 
IAVF_TX_VECTOR_CTX_OFFLOAD_OFFLOADS, plus req_features.ctx_desc = true for the 
LLDP devarg and the per-queue L2TAG2 VLAN case. ice uses the same mechanism; 
there is nothing to align.



5. use_ctx per-queue — iavf sets use_ctx in exactly one place, the “out:” block 
of “iavf_set_tx_function()”; it is never touched in queue setup. The only real 
inconsistency was on the ice side, where use_vec_entry was separately 
re-recorded in ice_tx_queue_start(), and that duplicate is removed in the 
revision I sent now.



Thanks,
Anurag




> > 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