On Mon, Oct 12, 2026 at 3:50 AM Tathagat Priyadarshi
<[email protected]> wrote:
>
> The GVNIC DQO hardware restricts each packet (or MTU segment in TSO)
> to at most 10 data descriptors (GVE_TX_MAX_DATA_DESCS), beyond which
> the hardware drops traffic. In addition to this 10-descriptor limit,
> the hardware imposes strict packet sizing and header constraints.
>
> Enforce these hardware packet and TSO validation constraints in DQO
> RDA mode within gve_tx_validate_descs() matching the upstream Linux
> kernel driver:
> - Define GVE_TX_MAX_TSO_MSS_DQO (9728 bytes) to avoid 14-bit MSS
>   field overflow in struct gve_tx_tso_context_desc_dqo.
> - Define GVE_TX_MIN_PKT_SIZE_DQO (17 bytes) and GVE_TX_MAX_PKT_SIZE_DQO
>   (9728 bytes) to reject runts (which cause hardware DMA parse errors)
>   and oversized non-TSO packets.
> - In gve_tx_validate_descs():
>   * Reject zero-length mbufs (nb_descs == 0 or pkt_len == 0).
>   * Enforce packet size in [17, 9728] for non-TSO packets alongside
>     the 10-descriptor limit (nb_descs <= GVE_TX_MAX_DATA_DESCS).
>   * Reject split transport headers across mbuf segments
>     (data_len < headlen).
>   * Reject TSO packets missing an L4 header (l4_len == 0).
>   * Reject TSO packets without payload (pkt_len <= headlen).
>   * Enforce MSS limits [88, 9728] and header length limit <= 255.
>
> Fixes: 403c671a46b6 ("net/gve: support TSO in DQO RDA")
> Cc: [email protected]
>
> Acked-by: Joshua Washington <[email protected]>
> Signed-off-by: Tathagat Priyadarshi <[email protected]>
> ---
>  drivers/net/gve/base/gve_desc_dqo.h |  3 ++
>  drivers/net/gve/gve_tx_dqo.c        | 44 ++++++++++++++++++++++++-----
>  2 files changed, 40 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/gve/base/gve_desc_dqo.h 
> b/drivers/net/gve/base/gve_desc_dqo.h
> index c1534959c2..1740f69d73 100644
> --- a/drivers/net/gve/base/gve_desc_dqo.h
> +++ b/drivers/net/gve/base/gve_desc_dqo.h
> @@ -12,6 +12,9 @@
>
>  #define GVE_TX_MAX_HDR_SIZE_DQO 255
>  #define GVE_TX_MIN_TSO_MSS_DQO 88
> +#define GVE_TX_MAX_TSO_MSS_DQO 9728
> +#define GVE_TX_MIN_PKT_SIZE_DQO 17
> +#define GVE_TX_MAX_PKT_SIZE_DQO 9728

GVE_TX_MAX_TSO_MSS_DQO and GVE_TX_MAX_PKT_SIZE_DQO have the same
semantic meaning: they represent the maximum wire packet allowed on
the NIC. Let's opt to use a single value here.

>
>  /* Basic TX descriptor (DTYPE 0x0C) */
>  struct gve_tx_pkt_desc_dqo {
> diff --git a/drivers/net/gve/gve_tx_dqo.c b/drivers/net/gve/gve_tx_dqo.c
> index 9210b02eb1..03ac3bd5dd 100644
> --- a/drivers/net/gve/gve_tx_dqo.c
> +++ b/drivers/net/gve/gve_tx_dqo.c
> @@ -134,12 +134,43 @@ gve_tx_clean_descs_dqo(struct gve_tx_queue *txq, 
> uint16_t nb_descs) {
>  static inline bool
>  gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t nb_descs, bool 
> is_tso)
>  {
> -       if (!is_tso)
> +       int tso_segsz, seg_remain, mbuf_remain;
> +       int num_descs, seg_offset, mbuf_len;
> +       int headlen;
> +
> +       /* Reject zero-length packet */
> +       if (unlikely(nb_descs == 0 || tx_pkt->pkt_len == 0))
> +               return false;
> +
> +       if (!is_tso) {
> +               /* Reject packets that violate hardware length constraints */
> +               if (unlikely(tx_pkt->pkt_len < GVE_TX_MIN_PKT_SIZE_DQO ||
> +                            tx_pkt->pkt_len > GVE_TX_MAX_PKT_SIZE_DQO))
> +                       return false;
>                 return nb_descs <= GVE_TX_MAX_DATA_DESCS;
> +       }
>
> -       int tso_segsz = tx_pkt->tso_segsz;
> -       int num_descs, seg_offset, mbuf_len;
> -       int headlen = tx_pkt->l2_len + tx_pkt->l3_len + tx_pkt->l4_len;
> +       headlen = tx_pkt->l2_len + tx_pkt->l3_len + tx_pkt->l4_len;
> +
> +       /* Reject split transport header */
> +       if (unlikely(tx_pkt->data_len < headlen))
> +               return false;
> +
> +       /* Reject missing TCP header on TSO */
> +       if (unlikely(tx_pkt->l4_len == 0))
> +               return false;
> +
> +       /* Reject TSO packet without payload */
> +       if (unlikely(tx_pkt->pkt_len <= (uint32_t)headlen))
> +               return false;
> +
> +       /* Enforce GVE hardware MSS and header limits */
> +       if (unlikely(tx_pkt->tso_segsz < GVE_TX_MIN_TSO_MSS_DQO ||
> +                    tx_pkt->tso_segsz > GVE_TX_MAX_TSO_MSS_DQO ||
> +                    headlen > GVE_TX_MAX_HDR_SIZE_DQO))
> +               return false;
> +
> +       tso_segsz = tx_pkt->tso_segsz;
>
>         /* Headers will be split into their own buffer. */
>         num_descs = 1;
> @@ -150,7 +181,7 @@ gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t 
> nb_descs, bool is_tso)
>                 if (!mbuf_len)
>                         goto next_mbuf;
>
> -               int seg_remain = tso_segsz - seg_offset;
> +               seg_remain = tso_segsz - seg_offset;

Thanks for fixing the forward declarations but generally, declarations
should happen in the smallest scope possible. Since seg_remain and
mbuf_remain aren't used outside of this loop and branch, respectively,
they should be declared at those scopes. It might also be appropriate
to just declare both at the start of the loop.

>                 if (num_descs == GVE_TX_MAX_DATA_DESCS && seg_remain)
>                         return false;
>
> @@ -162,7 +193,7 @@ gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t 
> nb_descs, bool is_tso)
>                          * is not at risk of defying this limitation.
>                          * Thus, such segments are ignored.
>                          */
> -                       int mbuf_remain = tx_pkt->data_len % 
> GVE_TX_MAX_BUF_SIZE_DQO;
> +                       mbuf_remain = tx_pkt->data_len % 
> GVE_TX_MAX_BUF_SIZE_DQO;
>
>                         /* For each TSO segment, HW will prepend
>                          * headers. The remaining bytes of this mbuf
> @@ -184,7 +215,6 @@ gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t 
> nb_descs, bool is_tso)
>                         mbuf_len = tx_pkt->data_len;
>         }
>
> -
>         return true;
>  }
>
> --
> 2.43.0
>

Thanks for sending this!

Reply via email to