Add support for the following ethtool commands:

ethtool -s|--change devname [msglvl N] [msglevel type on|off]
ethtool -S|--statistics devname
ethtool -i|--driver devname
ethtool -l|--show-channels devname
ethtool -L|--set-channels devname
ethtool -g|--show-ring devname
ethtool --reset devname

Signed-off-by: Catherine Sullivan <csu...@google.com>
Signed-off-by: Sagi Shahar <sa...@google.com>
Signed-off-by: Jon Olson <jonol...@google.com>
Acked-by: Willem de Bruijn <will...@google.com>
Reviewed-by: Luigi Rizzo <lri...@google.com>
---
 drivers/net/ethernet/google/gve/Makefile      |   2 +-
 drivers/net/ethernet/google/gve/gve.h         |   4 +
 drivers/net/ethernet/google/gve/gve_ethtool.c | 226 ++++++++++++++++++
 drivers/net/ethernet/google/gve/gve_main.c    |  39 +++
 4 files changed, 270 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/google/gve/gve_ethtool.c

diff --git a/drivers/net/ethernet/google/gve/Makefile 
b/drivers/net/ethernet/google/gve/Makefile
index a1890c93705b..3354ce40eb97 100644
--- a/drivers/net/ethernet/google/gve/Makefile
+++ b/drivers/net/ethernet/google/gve/Makefile
@@ -1,4 +1,4 @@
 # Makefile for the Google virtual Ethernet (gve) driver
 
 obj-$(CONFIG_GVE) += gve.o
-gve-objs := gve_main.o gve_tx.o gve_rx.o gve_adminq.o
+gve-objs := gve_main.o gve_tx.o gve_rx.o gve_ethtool.o gve_adminq.o
diff --git a/drivers/net/ethernet/google/gve/gve.h 
b/drivers/net/ethernet/google/gve/gve.h
index b7cc23b06284..c765f718dc4a 100644
--- a/drivers/net/ethernet/google/gve/gve.h
+++ b/drivers/net/ethernet/google/gve/gve.h
@@ -449,4 +449,8 @@ 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);
+/* exported by ethtool.c */
+extern const struct ethtool_ops gve_ethtool_ops;
+/* needed by ethtool */
+extern const char gve_version_str[];
 #endif /* _GVE_H_ */
diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c 
b/drivers/net/ethernet/google/gve/gve_ethtool.c
new file mode 100644
index 000000000000..8e6863cb0b47
--- /dev/null
+++ b/drivers/net/ethernet/google/gve/gve_ethtool.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Google virtual Ethernet (gve) driver
+ *
+ * Copyright (C) 2015-2019 Google, Inc.
+ */
+
+#include <linux/rtnetlink.h>
+#include "gve.h"
+
+static void gve_get_drvinfo(struct net_device *netdev,
+                           struct ethtool_drvinfo *info)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+
+       strlcpy(info->driver, "gve", sizeof(info->driver));
+       strlcpy(info->version, gve_version_str, sizeof(info->version));
+       strlcpy(info->bus_info, pci_name(priv->pdev), sizeof(info->bus_info));
+}
+
+static void gve_set_msglevel(struct net_device *netdev, u32 value)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+
+       priv->msg_enable = value;
+}
+
+static u32 gve_get_msglevel(struct net_device *netdev)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+
+       return priv->msg_enable;
+}
+
+static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = {
+       "rx_packets", "tx_packets", "rx_bytes", "tx_bytes",
+       "rx_dropped", "tx_dropped", "tx_timeouts",
+};
+
+#define GVE_MAIN_STATS_LEN  ARRAY_SIZE(gve_gstrings_main_stats)
+#define NUM_GVE_TX_CNTS        5
+#define NUM_GVE_RX_CNTS        2
+
+static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+       char *s = (char *)data;
+       int i;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       memcpy(s, *gve_gstrings_main_stats,
+              sizeof(gve_gstrings_main_stats));
+       s += sizeof(gve_gstrings_main_stats);
+       for (i = 0; i < priv->rx_cfg.num_queues; i++) {
+               snprintf(s, ETH_GSTRING_LEN, "rx_desc_cnt[%u]", i);
+               s += ETH_GSTRING_LEN;
+               snprintf(s, ETH_GSTRING_LEN, "rx_desc_fill_cnt[%u]", i);
+               s += ETH_GSTRING_LEN;
+       }
+       for (i = 0; i < priv->tx_cfg.num_queues; i++) {
+               snprintf(s, ETH_GSTRING_LEN, "tx_req[%u]", i);
+               s += ETH_GSTRING_LEN;
+               snprintf(s, ETH_GSTRING_LEN, "tx_done[%u]", i);
+               s += ETH_GSTRING_LEN;
+               snprintf(s, ETH_GSTRING_LEN, "tx_wake[%u]", i);
+               s += ETH_GSTRING_LEN;
+               snprintf(s, ETH_GSTRING_LEN, "tx_stop[%u]", i);
+               s += ETH_GSTRING_LEN;
+               snprintf(s, ETH_GSTRING_LEN, "tx_event_counter[%u]", i);
+               s += ETH_GSTRING_LEN;
+       }
+}
+
+static int gve_get_sset_count(struct net_device *netdev, int sset)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+
+       if (!netif_carrier_ok(netdev))
+               return 0;
+
+       switch (sset) {
+       case ETH_SS_STATS:
+               return GVE_MAIN_STATS_LEN +
+                      (priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS) +
+                      (priv->tx_cfg.num_queues * NUM_GVE_TX_CNTS);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void
+gve_get_ethtool_stats(struct net_device *netdev,
+                     struct ethtool_stats *stats, u64 *data)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+       u64 rx_pkts, rx_bytes, tx_pkts, tx_bytes;
+       int ring;
+       int i;
+
+       ASSERT_RTNL();
+
+       if (!netif_carrier_ok(netdev))
+               return;
+
+       for (rx_pkts = 0, rx_bytes = 0, ring = 0;
+            ring < priv->rx_cfg.num_queues; ring++) {
+               rx_pkts += priv->rx[ring].rpackets;
+               rx_bytes += priv->rx[ring].rbytes;
+       }
+       for (tx_pkts = 0, tx_bytes = 0, ring = 0;
+            ring < priv->tx_cfg.num_queues; ring++) {
+               tx_pkts += priv->tx[ring].pkt_done;
+               tx_bytes += priv->tx[ring].bytes_done;
+       }
+       memset(data, 0, GVE_MAIN_STATS_LEN * sizeof(*data));
+
+       i = 0;
+       data[i++] = rx_pkts;
+       data[i++] = tx_pkts;
+       data[i++] = rx_bytes;
+       data[i++] = tx_bytes;
+       /* Skip rx_dropped and tx_dropped */
+       i += 2;
+       data[i++] = priv->tx_timeo_cnt;
+       i = GVE_MAIN_STATS_LEN;
+
+       /* walk RX rings */
+       for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) {
+               struct gve_rx_ring *rx = &priv->rx[ring];
+
+               data[i++] = rx->desc.cnt;
+               data[i++] = rx->desc.fill_cnt;
+       }
+       /* walk TX rings */
+       for (ring = 0; ring < priv->tx_cfg.num_queues; ring++) {
+               struct gve_tx_ring *tx = &priv->tx[ring];
+
+               data[i++] = tx->req;
+               data[i++] = tx->done;
+               data[i++] = tx->wake_queue;
+               data[i++] = tx->stop_queue;
+               data[i++] = be32_to_cpu(gve_tx_load_event_counter(priv, tx));
+       }
+}
+
+void gve_get_channels(struct net_device *netdev, struct ethtool_channels *cmd)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+
+       cmd->max_rx = priv->rx_cfg.max_queues;
+       cmd->max_tx = priv->tx_cfg.max_queues;
+       cmd->max_other = 0;
+       cmd->max_combined = 0;
+       cmd->rx_count = priv->rx_cfg.num_queues;
+       cmd->tx_count = priv->tx_cfg.num_queues;
+       cmd->other_count = 0;
+       cmd->combined_count = 0;
+}
+
+int gve_set_channels(struct net_device *netdev, struct ethtool_channels *cmd)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+       struct gve_queue_config new_tx_cfg = priv->tx_cfg;
+       struct gve_queue_config new_rx_cfg = priv->rx_cfg;
+       struct ethtool_channels old_settings;
+       int new_tx = cmd->tx_count;
+       int new_rx = cmd->rx_count;
+
+       gve_get_channels(netdev, &old_settings);
+
+       /* Changing combined is not allowed allowed */
+       if (cmd->combined_count != old_settings.combined_count)
+               return -EINVAL;
+
+       if (!new_rx || !new_tx)
+               return -EINVAL;
+
+       if (!netif_carrier_ok(netdev)) {
+               priv->tx_cfg.num_queues = new_tx;
+               priv->rx_cfg.num_queues = new_rx;
+               return 0;
+       }
+
+       new_tx_cfg.num_queues = new_tx;
+       new_rx_cfg.num_queues = new_rx;
+
+       return gve_adjust_queues(priv, new_rx_cfg, new_tx_cfg);
+}
+
+void gve_get_ringparam(struct net_device *netdev,
+                      struct ethtool_ringparam *cmd)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+
+       cmd->rx_max_pending = priv->rx_desc_cnt;
+       cmd->tx_max_pending = priv->tx_desc_cnt;
+       cmd->rx_pending = priv->rx_desc_cnt;
+       cmd->tx_pending = priv->tx_desc_cnt;
+}
+
+int gve_user_reset(struct net_device *netdev, u32 *flags)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+
+       if (*flags == ETH_RESET_ALL) {
+               *flags = 0;
+               return gve_reset(priv, true);
+       }
+
+       return -EOPNOTSUPP;
+}
+
+const struct ethtool_ops gve_ethtool_ops = {
+       .get_drvinfo = gve_get_drvinfo,
+       .get_strings = gve_get_strings,
+       .get_sset_count = gve_get_sset_count,
+       .get_ethtool_stats = gve_get_ethtool_stats,
+       .set_msglevel = gve_set_msglevel,
+       .get_msglevel = gve_get_msglevel,
+       .set_channels = gve_set_channels,
+       .get_channels = gve_get_channels,
+       .get_link = ethtool_op_get_link,
+       .get_ringparam = gve_get_ringparam,
+       .reset = gve_user_reset,
+};
diff --git a/drivers/net/ethernet/google/gve/gve_main.c 
b/drivers/net/ethernet/google/gve/gve_main.c
index 00b7b606ebe7..0216be27583c 100644
--- a/drivers/net/ethernet/google/gve/gve_main.c
+++ b/drivers/net/ethernet/google/gve/gve_main.c
@@ -737,6 +737,44 @@ static int gve_close(struct net_device *dev)
        return gve_reset_recovery(priv, false);
 }
 
+int gve_adjust_queues(struct gve_priv *priv,
+                     struct gve_queue_config new_rx_config,
+                     struct gve_queue_config new_tx_config)
+{
+       int err;
+
+       if (netif_carrier_ok(priv->dev)) {
+               /* To make this process as simple as possible we teardown the
+                * device, set the new configuration, 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_cfg = new_tx_config;
+               priv->rx_cfg = new_rx_config;
+
+               err = gve_open(priv->dev);
+               if (err)
+                       goto err;
+
+               return 0;
+       }
+       /* Set the config for the next up. */
+       priv->tx_cfg = new_tx_config;
+       priv->rx_cfg = new_rx_config;
+
+       return 0;
+err:
+       netif_err(priv, drv, priv->dev,
+                 "Adjust queues failed! !!! DISABLING ALL QUEUES !!!\n");
+       gve_turndown(priv);
+       return err;
+}
+
 static void gve_turndown(struct gve_priv *priv)
 {
        int idx;
@@ -1074,6 +1112,7 @@ static int gve_probe(struct pci_dev *pdev, const struct 
pci_device_id *ent)
        }
        SET_NETDEV_DEV(dev, &pdev->dev);
        pci_set_drvdata(pdev, dev);
+       dev->ethtool_ops = &gve_ethtool_ops;
        dev->netdev_ops = &gve_netdev_ops;
        /* advertise features */
        dev->hw_features = NETIF_F_HIGHDMA;
-- 
2.22.0.410.gd8fdbe21b5-goog

Reply via email to