The DQ queue format expects that any MTU-sized packet or segment will only cross at most 10 data descriptors.
In the non-TSO case, this means that a given packet simply can have at most 10 descriptors. In the TSO case, things are a bit more complex. For large TSO packets, mbufs must be parsed and split into tso_segsz-sized (MSS) segments. For any such MSS segment, the number of descriptors that would be used to transmit the segment must be counted. The following restrictions apply when counting descriptors: 1) Every TSO segment (including the very first) will be prepended by a _separate_ data descriptor holding only header data, 2) The hardware can send at most up to 16K bytes in a single data descriptor, and 3) The start of every mbuf counts as a separator between data descriptors -- data is not assumed to be coalesced or copied The value of nb_mtu_seg_max is set to GVE_TX_MAX_DATA_DESCS-1 to ensure that the hidden extra prepended descriptor added to the beginning of each segment in the TSO case is accounted for. Fixes: 403c671a46b6 ("net/gve: support TSO in DQO RDA") Cc: tathagat.d...@gmail.com Cc: sta...@dpdk.org Signed-off-by: Joshua Washington <joshw...@google.com> Reviewed-by: Ankit Garg <nkt...@google.com> --- drivers/net/gve/gve_ethdev.c | 2 +- drivers/net/gve/gve_tx_dqo.c | 64 +++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/net/gve/gve_ethdev.c b/drivers/net/gve/gve_ethdev.c index 81325ba98c..ef1c543aac 100644 --- a/drivers/net/gve/gve_ethdev.c +++ b/drivers/net/gve/gve_ethdev.c @@ -603,7 +603,7 @@ gve_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) .nb_max = priv->max_tx_desc_cnt, .nb_min = priv->min_tx_desc_cnt, .nb_align = 1, - .nb_mtu_seg_max = GVE_TX_MAX_DATA_DESCS, + .nb_mtu_seg_max = GVE_TX_MAX_DATA_DESCS - 1, }; dev_info->flow_type_rss_offloads = GVE_RTE_RSS_OFFLOAD_ALL; diff --git a/drivers/net/gve/gve_tx_dqo.c b/drivers/net/gve/gve_tx_dqo.c index 27f98cdeb3..3befbbcacb 100644 --- a/drivers/net/gve/gve_tx_dqo.c +++ b/drivers/net/gve/gve_tx_dqo.c @@ -80,6 +80,68 @@ gve_tx_clean_descs_dqo(struct gve_tx_queue *txq, uint16_t nb_descs) { gve_tx_clean_dqo(txq); } +/* GVE expects at most 10 data descriptors per mtu-sized segment. Beyond this, + * the hardware will assume the driver is malicious and stop transmitting + * packets altogether. Validate that a packet can be sent to avoid sending + * posting descriptors for an invalid packet. + */ +static inline bool +gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t nb_descs, bool is_tso) +{ + if (!is_tso) + 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; + + /* Headers will be split into their own buffer. */ + num_descs = 1; + seg_offset = 0; + mbuf_len = tx_pkt->data_len - headlen; + + while (tx_pkt) { + if (!mbuf_len) + goto next_mbuf; + + int seg_remain = tso_segsz - seg_offset; + if (num_descs == GVE_TX_MAX_DATA_DESCS && seg_remain) + return false; + + if (seg_remain < mbuf_len) { + seg_offset = mbuf_len % tso_segsz; + /* The MSS is bound from above by 9728B, so a + * single TSO segment in the middle of an mbuf + * will be part of at most two descriptors, and + * 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; + + /* For each TSO segment, HW will prepend + * headers. The remaining bytes of this mbuf + * will be the start of the payload of the next + * TSO segment. In addition, if the final + * segment in this mbuf is divided between two + * descriptors, both must be counted. + */ + num_descs = 1 + !!(seg_offset) + + (mbuf_remain < seg_offset && mbuf_remain); + } else { + seg_offset += mbuf_len; + num_descs++; + } + +next_mbuf: + tx_pkt = tx_pkt->next; + if (tx_pkt) + mbuf_len = tx_pkt->data_len; + } + + + return true; +} + static uint16_t gve_tx_pkt_nb_data_descs(struct rte_mbuf *tx_pkt) { @@ -166,7 +228,7 @@ gve_tx_burst_dqo(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts) } /* Drop packet if it doesn't adhere to hardware limits. */ - if (!tso && nb_descs > GVE_TX_MAX_DATA_DESCS) { + if (!gve_tx_validate_descs(tx_pkt, nb_descs, tso)) { txq->stats.too_many_descs++; break; } -- 2.50.0.727.gbf7dc18ff4-goog