* [PATCH v2 1/2] net/gve: advertise Rx scatter in GQI queue format
2026-10-19 20:20 ` [PATCH v2 0/2] net/gve: Rx scatter for GQI Ryan Wolfe
@ 2026-10-19 20:20 ` Ryan Wolfe
2026-10-19 20:20 ` [PATCH v2 2/2] net/gve: refuse an MTU that needs Rx scatter when it is off Ryan Wolfe
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Ryan Wolfe @ 2026-10-19 20:20 UTC (permalink / raw)
To: dev; +Cc: joshwash, stephen, stable
Since jumbo frame support for GQI, a frame that does not fit in one Rx
buffer arrives as several descriptors with GVE_RXF_PKT_CONT set on all
but the last, and gve_rx_burst() chains them into one multi-segment
mbuf. The driver never reports RTE_ETH_RX_OFFLOAD_SCATTER, though, so
an application that enables multi-segment receive only when the port
claims it treats every packet as one segment. VPP is one: it masks its
requested Rx offloads with rx_offload_capa, so on a GQI gVNIC it never
sets its multi-segment flag, reads only the first buffer of a jumbo
frame and drops it as malformed. With 4 KB buffers that caps the usable
MTU near 4000 bytes on a VPC whose MTU is 8896.
Report SCATTER for GQI only. The DQO Rx path is not touched by this
patch.
Fixes: 496d4d2c8b54 ("net/gve: support jumbo frame for GQI")
Cc: stable@dpdk.org
Signed-off-by: Ryan Wolfe <rnwolfe@google.com>
---
.mailmap | 1 +
doc/guides/nics/features/gve.ini | 1 +
drivers/net/gve/gve_ethdev.c | 5 +++++
3 files changed, 7 insertions(+)
diff --git a/.mailmap b/.mailmap
index 6d9e262cfb..87c544947c 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1477,6 +1477,7 @@ Ruifeng Wang <ruifeng.wang@arm.com>
Rupesh Chiluka <rchiluka@marvell.com>
Rushil Gupta <rushilg@google.com>
Ryan E Hall <ryan.e.hall@intel.com>
+Ryan Wolfe <rnwolfe@google.com>
Sabyasachi Sengupta <sabyasg@hpe.com>
Sachin Saxena <sachin.saxena@nxp.com> <sachin.saxena@oss.nxp.com>
Sagar Abhang <sabhang@brocade.com>
diff --git a/doc/guides/nics/features/gve.ini b/doc/guides/nics/features/gve.ini
index 9f3835edf6..5566511b6b 100644
--- a/doc/guides/nics/features/gve.ini
+++ b/doc/guides/nics/features/gve.ini
@@ -7,6 +7,7 @@
Speed capabilities = Y
Queue start/stop = Y
MTU update = Y
+Scattered Rx = P
TSO = Y
RSS hash = Y
RSS key update = Y
diff --git a/drivers/net/gve/gve_ethdev.c b/drivers/net/gve/gve_ethdev.c
index 3c8d1a76e4..92a5a7679f 100644
--- a/drivers/net/gve/gve_ethdev.c
+++ b/drivers/net/gve/gve_ethdev.c
@@ -897,6 +897,11 @@ gve_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
dev_info->min_mtu = RTE_ETHER_MIN_MTU;
dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_RSS_HASH;
+ /* GQI receives a frame larger than one Rx buffer as a chain of
+ * buffers (gve_rx_burst follows GVE_RXF_PKT_CONT).
+ */
+ if (gve_is_gqi(priv))
+ dev_info->rx_offload_capa |= RTE_ETH_RX_OFFLOAD_SCATTER;
if (priv->clk_read_type != GVE_DEV_CLK_UNSUPPORTED &&
priv->nic_ts_report_mz != NULL)
dev_info->rx_offload_capa |= RTE_ETH_RX_OFFLOAD_TIMESTAMP;
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 2/2] net/gve: refuse an MTU that needs Rx scatter when it is off
2026-10-19 20:20 ` [PATCH v2 0/2] net/gve: Rx scatter for GQI Ryan Wolfe
2026-10-19 20:20 ` [PATCH v2 1/2] net/gve: advertise Rx scatter in GQI queue format Ryan Wolfe
@ 2026-10-19 20:20 ` Ryan Wolfe
2026-10-20 15:08 ` [PATCH v2 0/2] net/gve: Rx scatter for GQI Stephen Hemminger
2026-10-21 18:25 ` [PATCH] net/gve: check the Rx scatter MTU at device start Ryan Wolfe
3 siblings, 0 replies; 8+ messages in thread
From: Ryan Wolfe @ 2026-10-19 20:20 UTC (permalink / raw)
To: dev; +Cc: joshwash, stephen, stable
GQI chains a frame larger than one Rx buffer into a multi-segment mbuf
whether or not the application enabled RTE_ETH_RX_OFFLOAD_SCATTER, so
an application that did not ask for scatter can still receive chained
mbufs once the MTU exceeds the Rx buffer.
Refuse such a configuration on GQI. Rx queue setup fails when the
current MTU plus the Ethernet header and the GVE_RX_PAD bytes ahead of
the frame does not fit in the queue's Rx buffer, and an MTU change
fails on the same test against each configured Rx queue. The buffer
length comes from one helper shared with queue setup, so the check
matches the size the device is given. DQO is unchanged.
Fixes: 496d4d2c8b54 ("net/gve: support jumbo frame for GQI")
Cc: stable@dpdk.org
Suggested-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Ryan Wolfe <rnwolfe@google.com>
---
drivers/net/gve/gve_ethdev.c | 18 ++++++++++++++++++
drivers/net/gve/gve_ethdev.h | 9 +++++++++
drivers/net/gve/gve_rx.c | 31 +++++++++++++++++++++++++------
3 files changed, 52 insertions(+), 6 deletions(-)
diff --git a/drivers/net/gve/gve_ethdev.c b/drivers/net/gve/gve_ethdev.c
index 92a5a7679f..e47ec0c55b 100644
--- a/drivers/net/gve/gve_ethdev.c
+++ b/drivers/net/gve/gve_ethdev.c
@@ -1031,6 +1031,24 @@ gve_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
return -EBUSY;
}
+ /* GQI chains a frame larger than one Rx buffer into a multi-segment
+ * mbuf. No Rx offload is per-queue here, so check the port offloads.
+ */
+ if (gve_is_gqi(priv) &&
+ !(dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_SCATTER)) {
+ for (uint16_t i = 0; i < dev->data->nb_rx_queues; i++) {
+ struct gve_rx_queue *rxq = dev->data->rx_queues[i];
+
+ if (rxq != NULL &&
+ gve_rx_gqi_needs_scatter(mtu, rxq->rx_buf_len)) {
+ PMD_DRV_LOG(ERR,
+ "MTU %u needs Rx scatter with %u byte Rx buffers",
+ mtu, rxq->rx_buf_len);
+ return -EINVAL;
+ }
+ }
+ }
+
err = process_priv->ctrl_ops->set_mtu(priv, mtu);
if (err) {
PMD_DRV_LOG(ERR, "Failed to set mtu as %u err = %d", mtu, err);
diff --git a/drivers/net/gve/gve_ethdev.h b/drivers/net/gve/gve_ethdev.h
index 19eac9ec0b..f4b72a9159 100644
--- a/drivers/net/gve/gve_ethdev.h
+++ b/drivers/net/gve/gve_ethdev.h
@@ -454,6 +454,15 @@ gve_is_gqi(struct gve_priv *priv)
priv->queue_format == GVE_GQI_QPL_FORMAT;
}
+/* GQI places GVE_RX_PAD bytes ahead of the frame in its first Rx buffer
+ * and chains the rest of a larger frame into further buffers.
+ */
+static inline bool
+gve_rx_gqi_needs_scatter(uint16_t mtu, uint16_t rx_buf_len)
+{
+ return (uint32_t)mtu + RTE_ETHER_HDR_LEN + GVE_RX_PAD > rx_buf_len;
+}
+
static inline bool
gve_get_admin_queue_ok(struct gve_priv *priv)
{
diff --git a/drivers/net/gve/gve_rx.c b/drivers/net/gve/gve_rx.c
index 09f9d8d480..4a455b3067 100644
--- a/drivers/net/gve/gve_rx.c
+++ b/drivers/net/gve/gve_rx.c
@@ -297,6 +297,16 @@ gve_rx_queue_release(struct rte_eth_dev *dev, uint16_t qid)
dev->data->rx_queues[qid] = NULL;
}
+static uint16_t
+gve_rx_buf_len_gqi(struct rte_mempool *pool)
+{
+ uint32_t mbuf_len =
+ rte_pktmbuf_data_room_size(pool) - RTE_PKTMBUF_HEADROOM;
+
+ return RTE_MIN((uint16_t)GVE_RX_MAX_BUF_SIZE_GQI,
+ RTE_ALIGN_FLOOR(mbuf_len, GVE_RX_BUF_ALIGN_GQI));
+}
+
int
gve_rx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_id,
uint16_t nb_desc, unsigned int socket_id,
@@ -306,7 +316,8 @@ gve_rx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_id,
const struct rte_memzone *mz;
struct gve_rx_queue *rxq;
uint16_t free_thresh;
- uint32_t mbuf_len;
+ uint16_t rx_buf_len;
+ uint64_t offloads;
int err = 0;
/* Ring size is required to be a power of two. */
@@ -316,6 +327,18 @@ gve_rx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_id,
return -EINVAL;
}
+ /* A frame larger than one Rx buffer is chained into a multi-segment
+ * mbuf, which the application must have asked for.
+ */
+ offloads = dev->data->dev_conf.rxmode.offloads | conf->offloads;
+ rx_buf_len = gve_rx_buf_len_gqi(pool);
+ if (!(offloads & RTE_ETH_RX_OFFLOAD_SCATTER) &&
+ gve_rx_gqi_needs_scatter(dev->data->mtu, rx_buf_len)) {
+ PMD_DRV_LOG(ERR, "MTU %u needs Rx scatter with %u byte Rx buffers",
+ dev->data->mtu, rx_buf_len);
+ return -EINVAL;
+ }
+
/* Free memory if needed. */
if (dev->data->rx_queues[queue_id]) {
gve_rx_queue_release(dev, queue_id);
@@ -351,11 +374,7 @@ gve_rx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_id,
rxq->hw = hw;
rxq->ntfy_addr = &hw->db_bar[rte_be_to_cpu_32(hw->irq_dbs[rxq->ntfy_id].id)];
- mbuf_len =
- rte_pktmbuf_data_room_size(rxq->mpool) - RTE_PKTMBUF_HEADROOM;
- rxq->rx_buf_len =
- RTE_MIN((uint16_t)GVE_RX_MAX_BUF_SIZE_GQI,
- RTE_ALIGN_FLOOR(mbuf_len, GVE_RX_BUF_ALIGN_GQI));
+ rxq->rx_buf_len = rx_buf_len;
/* Allocate software ring */
rxq->sw_ring = rte_zmalloc_socket("gve rx sw ring", sizeof(struct rte_mbuf *) * nb_desc,
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v2 0/2] net/gve: Rx scatter for GQI
2026-10-19 20:20 ` [PATCH v2 0/2] net/gve: Rx scatter for GQI Ryan Wolfe
2026-10-19 20:20 ` [PATCH v2 1/2] net/gve: advertise Rx scatter in GQI queue format Ryan Wolfe
2026-10-19 20:20 ` [PATCH v2 2/2] net/gve: refuse an MTU that needs Rx scatter when it is off Ryan Wolfe
@ 2026-10-20 15:08 ` Stephen Hemminger
2026-10-21 18:25 ` [PATCH] net/gve: check the Rx scatter MTU at device start Ryan Wolfe
3 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2026-10-20 15:08 UTC (permalink / raw)
To: Ryan Wolfe; +Cc: dev, joshwash, stable
On Mon, 19 Oct 2026 16:20:02 -0400
Ryan Wolfe <rnwolfe@google.com> wrote:
> GQI receives a frame larger than one Rx buffer as a chain of mbufs, but
> the driver doesn't report RTE_ETH_RX_OFFLOAD_SCATTER, and it chains
> whether or not the application enabled it. 1/2 reports SCATTER for GQI
> so an application that gates multi-segment receive on the capability,
> such as VPP, can turn it on. 2/2 refuses an Rx queue setup or an MTU
> change that would need scatter when the application hasn't enabled it.
>
> Both patches change only the GQI queue format. DQO has a related
> problem that this series leaves alone: its Rx path ignores
> end_of_packet, so with Rx buffers smaller than the frame each buffer
> is delivered as its own packet. DQO should either chain those buffers
> or refuse that configuration too; that is left for a follow-up.
>
> One more gap 2/2 doesn't close: the check reads dev->data->mtu, but gve
> never pushes that value to the device. rte_eth_dev_configure() sets it
> without calling mtu_set, so an application that never calls
> rte_eth_dev_set_mtu() passes the check at 1500 while the device keeps
> the MTU from its device descriptor, and on a jumbo VPC it can still get
> chained frames. Applying dev->data->mtu at configure or start would fix
> that but changes behavior and needs hardware to test, so it is left for
> the same follow-up.
>
> v2:
> - add 2/2 to refuse MTUs that need scatter when it isn't enabled
> (Stephen)
> - correct the DQO statement in 1/2
Applied to next-net
AI review had a followup suggestion:
Subject: Re: [PATCH v2 0/2] net/gve: Rx scatter for GQI
Applies to main; both commits build with -Dwerror=true.
Info
[PATCH v2 2/2] net/gve: refuse an MTU that needs Rx scatter when it
is off
1. rte_eth_dev_configure() sets dev->data->mtu directly from
rxmode.mtu without calling mtu_set, and keeps already set up Rx
queues when the queue count is unchanged. So stop, reconfigure with
a larger MTU (or without SCATTER), and start without redoing queue
setup passes both checks. Moving the mtu_set loop into a helper and
calling it from gve_dev_start() as well covers that path:
static int
gve_rx_check_scatter(struct rte_eth_dev *dev, uint16_t mtu)
{
struct gve_priv *priv = dev->data->dev_private;
uint16_t i;
if (!gve_is_gqi(priv) ||
(dev->data->dev_conf.rxmode.offloads &
RTE_ETH_RX_OFFLOAD_SCATTER))
return 0;
for (i = 0; i < dev->data->nb_rx_queues; i++) {
struct gve_rx_queue *q = dev->data->rx_queues[i];
if (q != NULL &&
gve_rx_gqi_needs_scatter(mtu, q->rx_buf_len)) {
PMD_DRV_LOG(ERR,
"MTU %u needs Rx scatter", mtu);
return -EINVAL;
}
}
return 0;
}
Not required for this series; can be a follow up.
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH] net/gve: check the Rx scatter MTU at device start
2026-10-19 20:20 ` [PATCH v2 0/2] net/gve: Rx scatter for GQI Ryan Wolfe
` (2 preceding siblings ...)
2026-10-20 15:08 ` [PATCH v2 0/2] net/gve: Rx scatter for GQI Stephen Hemminger
@ 2026-10-21 18:25 ` Ryan Wolfe
2026-10-22 17:00 ` Stephen Hemminger
3 siblings, 1 reply; 8+ messages in thread
From: Ryan Wolfe @ 2026-10-21 18:25 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, joshwash, stable
rte_eth_dev_configure() sets the MTU from rxmode.mtu without calling
mtu_set, and keeps Rx queues that are already set up when the queue
count doesn't change. Stopping the port and reconfiguring it with a
larger MTU, or without RTE_ETH_RX_OFFLOAD_SCATTER, therefore skipped
both the MTU change check and the Rx queue setup check, and GQI went
on to chain large frames for an application that hadn't enabled
scatter.
Move the check into gve_rx_check_scatter() and call it from
gve_dev_start() as well as gve_dev_mtu_set(), so starting such a
configuration fails with an error log instead.
Fixes: cffdca824388 ("net/gve: refuse an MTU that needs Rx scatter when it is off")
Cc: stable@dpdk.org
Reported-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Ryan Wolfe <rnwolfe@google.com>
---
Built with -Dwerror=true (gcc and clang); not yet run on gVNIC.
drivers/net/gve/gve_ethdev.c | 53 ++++++++++++++++++++++++------------
1 file changed, 36 insertions(+), 17 deletions(-)
diff --git a/drivers/net/gve/gve_ethdev.c b/drivers/net/gve/gve_ethdev.c
index 7506c46d9a..269950fde0 100644
--- a/drivers/net/gve/gve_ethdev.c
+++ b/drivers/net/gve/gve_ethdev.c
@@ -459,6 +459,32 @@ gve_start_queues(struct rte_eth_dev *dev)
return ret;
}
+/* GQI chains a frame larger than one Rx buffer into a multi-segment
+ * mbuf. No Rx offload is per-queue here, so check the port offloads.
+ */
+static int
+gve_rx_check_scatter(struct rte_eth_dev *dev, uint16_t mtu)
+{
+ struct gve_priv *priv = dev->data->dev_private;
+ uint16_t i;
+
+ if (!gve_is_gqi(priv) ||
+ (dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_SCATTER))
+ return 0;
+
+ for (i = 0; i < dev->data->nb_rx_queues; i++) {
+ struct gve_rx_queue *rxq = dev->data->rx_queues[i];
+
+ if (rxq != NULL && gve_rx_gqi_needs_scatter(mtu, rxq->rx_buf_len)) {
+ PMD_DRV_LOG(ERR, "MTU %u needs Rx scatter with %u byte Rx buffers",
+ mtu, rxq->rx_buf_len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static int
gve_dev_start(struct rte_eth_dev *dev)
{
@@ -471,6 +497,13 @@ gve_dev_start(struct rte_eth_dev *dev)
return -EPERM;
}
+ /* rte_eth_dev_configure() sets the MTU and the Rx offloads without
+ * calling mtu_set, and keeps Rx queues already set up, so check again.
+ */
+ ret = gve_rx_check_scatter(dev, dev->data->mtu);
+ if (ret != 0)
+ return ret;
+
ret = gve_start_queues(dev);
if (ret != 0) {
PMD_DRV_LOG(ERR, "Failed to start queues");
@@ -1031,23 +1064,9 @@ gve_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
return -EBUSY;
}
- /* GQI chains a frame larger than one Rx buffer into a multi-segment
- * mbuf. No Rx offload is per-queue here, so check the port offloads.
- */
- if (gve_is_gqi(priv) &&
- !(dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_SCATTER)) {
- for (uint16_t i = 0; i < dev->data->nb_rx_queues; i++) {
- struct gve_rx_queue *rxq = dev->data->rx_queues[i];
-
- if (rxq != NULL &&
- gve_rx_gqi_needs_scatter(mtu, rxq->rx_buf_len)) {
- PMD_DRV_LOG(ERR,
- "MTU %u needs Rx scatter with %u byte Rx buffers",
- mtu, rxq->rx_buf_len);
- return -EINVAL;
- }
- }
- }
+ err = gve_rx_check_scatter(dev, mtu);
+ if (err != 0)
+ return err;
err = process_priv->ctrl_ops->set_mtu(priv, mtu);
if (err) {
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH] net/gve: check the Rx scatter MTU at device start
2026-10-21 18:25 ` [PATCH] net/gve: check the Rx scatter MTU at device start Ryan Wolfe
@ 2026-10-22 17:00 ` Stephen Hemminger
0 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2026-10-22 17:00 UTC (permalink / raw)
To: Ryan Wolfe; +Cc: dev, joshwash, stable
On Wed, 21 Oct 2026 14:25:22 -0400
Ryan Wolfe <rnwolfe@google.com> wrote:
> rte_eth_dev_configure() sets the MTU from rxmode.mtu without calling
> mtu_set, and keeps Rx queues that are already set up when the queue
> count doesn't change. Stopping the port and reconfiguring it with a
> larger MTU, or without RTE_ETH_RX_OFFLOAD_SCATTER, therefore skipped
> both the MTU change check and the Rx queue setup check, and GQI went
> on to chain large frames for an application that hadn't enabled
> scatter.
>
> Move the check into gve_rx_check_scatter() and call it from
> gve_dev_start() as well as gve_dev_mtu_set(), so starting such a
> configuration fails with an error log instead.
>
> Fixes: cffdca824388 ("net/gve: refuse an MTU that needs Rx scatter when it is off")
> Cc: stable@dpdk.org
>
> Reported-by: Stephen Hemminger <stephen@networkplumber.org>
> Signed-off-by: Ryan Wolfe <rnwolfe@google.com>
> ---
AI found a flaw in this logic
Review: [PATCH] net/gve: check the Rx scatter MTU at device start
Patchwork: 171827
Applied on top of cffdca824388 (next-net-for-main); builds clean with
-Dwerror=true for net/gve.
Warning
gve_dev_start() checks dev->data->mtu, but gve never gives that MTU to
the device. The only caller of ctrl_ops->set_mtu() is gve_dev_mtu_set(),
and rte_eth_dev_configure() just assigns dev->data->mtu from
rxmode.mtu. After configure the gVNIC keeps whatever MTU it had (its
default, or the last mtu_set), so the start check validates a number
the hardware is not using. With rxmode.mtu left at 0 (1500) on a
device running a larger MTU, the check passes and GQI still chains
frames up to the device MTU without scatter.
Program the MTU at start, after the check, so the two agree:
ret = gve_rx_check_scatter(dev, dev->data->mtu);
if (ret != 0)
return ret;
ret = process_priv->ctrl_ops->set_mtu(priv, dev->data->mtu);
if (ret != 0) {
PMD_DRV_LOG(ERR, "Failed to set MTU %u: %d",
dev->data->mtu, ret);
return ret;
}
(move "priv = dev->data->dev_private;" to the top of the function.)
This also fixes rxmode.mtu being ignored by gve, which is the larger
bug here.
"Not yet run on gVNIC". Please test on GQI before v2: testpmd, port
stop, port config mtu / reconfigure without scatter, port start must
fail; and with the set_mtu change, confirm a 1500 MTU config drops
jumbo frames.
Info
Commit message: ethdev keeps already set up Rx queues below the new
count whether or not the count changes (eth_dev_rx_queue_config only
releases queues >= nb_queues). Drop "when the queue count doesn't
change".
cffdca824388 is only in next-net-for-main, not in any release. This
can be squashed into it when merging; if kept separate, Cc: stable is
not needed since the Fixes target already carries it.
^ permalink raw reply [flat|nested] 8+ messages in thread