kernel/files/2102-gve-Add-ethtool-support-for-changing-ring-size.patch
2026-08-21 12:35:59 +03:00

167 lines
5.7 KiB
Diff

From: Joshua Washington <joshwash@google.com>
Date: Fri, 5 Jun 2026 00:00:01 +0000
Subject: gve: Add ethtool support for changing ring size
Add ethtool -G (set_ringparam) support so the TX/RX ring sizes can
actually be changed within the bounds reported by the device in the
"modify ring" device option.
Requested ring sizes are validated against the device-provided minimums
and maximums and must be a power of two. Changing the ring size tears the
device down, applies the new descriptor counts, and brings it back up.
[Jonathan Wright: backported the upstream modifiable-ring-size support to
this kernel's gve, using the RHEL 10 / AlmaLinux 10 gve driver
(kernel 6.12.0-211.7.1.el10_2) as the reference. Adaptations: upstream's
gve_adjust_ring_sizes() reconfigures via gve_get_curr_alloc_cfgs() /
gve_adjust_config(), which do not exist in this tree (it predates the
gve_{tx,rx}_alloc_rings_cfg / queues_mem_alloc refactor). Instead
gve_adjust_ring_sizes() lives in gve_main.c and follows the existing
gve_adjust_queues() pattern used by this tree for ethtool -L: teardown
with gve_close(), set the new descriptor counts, then gve_open(), which
rebuilds the rings and re-sizes the QPLs from the new ring size via
gve_update_num_qpl_pages() (1106). It is declared in gve.h because
gve_close()/gve_open() are static to gve_main.c. Upstream's
gve_set_ringparam() also handles header-split/tcp_data_split; that
feature does not exist in this tree, so only the ring-size logic is
carried over.]
Signed-off-by: Jonathan Wright <jonathan@almalinux.org>
---
diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h
index a3c1caa..acac856 100644
--- a/drivers/net/ethernet/google/gve/gve.h
+++ b/drivers/net/ethernet/google/gve/gve.h
@@ -1074,6 +1074,9 @@ int gve_reset(struct gve_priv *priv, bool attempt_teardown);
int gve_adjust_queues(struct gve_priv *priv,
struct gve_queue_config new_rx_config,
struct gve_queue_config new_tx_config);
+int gve_adjust_ring_sizes(struct gve_priv *priv,
+ u16 new_tx_desc_cnt,
+ u16 new_rx_desc_cnt);
/* report stats handling */
void gve_handle_report_stats(struct gve_priv *priv);
/* exported by ethtool.c */
diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c
index fb7b477..a8822c9 100644
--- a/drivers/net/ethernet/google/gve/gve_ethtool.c
+++ b/drivers/net/ethernet/google/gve/gve_ethtool.c
@@ -6,6 +6,7 @@
#include <linux/ethtool.h>
#include <linux/rtnetlink.h>
+#include <linux/log2.h>
#include "gve.h"
#include "gve_adminq.h"
#include "gve_dqo.h"
@@ -482,6 +483,50 @@ static void gve_get_ringparam(struct net_device *netdev,
cmd->tx_pending = priv->tx_desc_cnt;
}
+static int gve_validate_req_ring_size(struct gve_priv *priv, u16 new_tx_desc_cnt,
+ u16 new_rx_desc_cnt)
+{
+ /* check for valid range */
+ if (new_tx_desc_cnt < priv->min_tx_desc_cnt ||
+ new_tx_desc_cnt > priv->max_tx_desc_cnt ||
+ new_rx_desc_cnt < priv->min_rx_desc_cnt ||
+ new_rx_desc_cnt > priv->max_rx_desc_cnt) {
+ dev_err(&priv->pdev->dev, "Requested descriptor count out of range\n");
+ return -EINVAL;
+ }
+
+ if (!is_power_of_2(new_tx_desc_cnt) || !is_power_of_2(new_rx_desc_cnt)) {
+ dev_err(&priv->pdev->dev, "Requested descriptor count has to be a power of 2\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int gve_set_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *cmd,
+ struct kernel_ethtool_ringparam *kernel_cmd,
+ struct netlink_ext_ack *extack)
+{
+ struct gve_priv *priv = netdev_priv(netdev);
+ u16 new_tx_cnt, new_rx_cnt;
+
+ if (cmd->tx_pending == priv->tx_desc_cnt && cmd->rx_pending == priv->rx_desc_cnt)
+ return 0;
+
+ if (!priv->modify_ring_size_enabled) {
+ dev_err(&priv->pdev->dev, "Modify ring size is not supported.\n");
+ return -EOPNOTSUPP;
+ }
+
+ new_tx_cnt = cmd->tx_pending;
+ new_rx_cnt = cmd->rx_pending;
+
+ if (gve_validate_req_ring_size(priv, new_tx_cnt, new_rx_cnt))
+ return -EINVAL;
+
+ return gve_adjust_ring_sizes(priv, new_tx_cnt, new_rx_cnt);
+}
+
static int gve_user_reset(struct net_device *netdev, u32 *flags)
{
struct gve_priv *priv = netdev_priv(netdev);
@@ -667,6 +712,7 @@ const struct ethtool_ops gve_ethtool_ops = {
.get_coalesce = gve_get_coalesce,
.set_coalesce = gve_set_coalesce,
.get_ringparam = gve_get_ringparam,
+ .set_ringparam = gve_set_ringparam,
.reset = gve_user_reset,
.get_tunable = gve_get_tunable,
.set_tunable = gve_set_tunable,
diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c
index a21df52..e939750 100644
--- a/drivers/net/ethernet/google/gve/gve_main.c
+++ b/drivers/net/ethernet/google/gve/gve_main.c
@@ -1780,6 +1780,44 @@ err:
return err;
}
+int gve_adjust_ring_sizes(struct gve_priv *priv,
+ u16 new_tx_desc_cnt,
+ u16 new_rx_desc_cnt)
+{
+ int err;
+
+ if (netif_carrier_ok(priv->dev)) {
+ /* To make this process as simple as possible we teardown the
+ * device, set the new ring sizes, and then bring the device
+ * up again.
+ */
+ err = gve_close(priv->dev);
+ /* we have already tried to reset in close,
+ * just fail at this point
+ */
+ if (err)
+ return err;
+ priv->tx_desc_cnt = new_tx_desc_cnt;
+ priv->rx_desc_cnt = new_rx_desc_cnt;
+
+ err = gve_open(priv->dev);
+ if (err)
+ goto err;
+
+ return 0;
+ }
+ /* Set the ring sizes for the next up. */
+ priv->tx_desc_cnt = new_tx_desc_cnt;
+ priv->rx_desc_cnt = new_rx_desc_cnt;
+
+ return 0;
+err:
+ netif_err(priv, drv, priv->dev,
+ "Adjust ring sizes failed! !!! DISABLING ALL QUEUES !!!\n");
+ gve_turndown(priv);
+ return err;
+}
+
static void gve_turndown(struct gve_priv *priv)
{
int idx;
--
2.43.0