From: Joshua Washington 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 --- 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 #include +#include #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