167 lines
5.7 KiB
Diff
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
|
|
|