octeon_ep_vf: add ethtool support
authorShinas Rasheed <srasheed@marvell.com>
Sat, 23 Dec 2023 13:39:59 +0000 (05:39 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 2 Jan 2024 14:19:54 +0000 (14:19 +0000)
Add support for the following ethtool commands:

ethtool -i|--driver devname
ethtool devname
ethtool -S|--statistics devname

Signed-off-by: Shinas Rasheed <srasheed@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/marvell/octeon_ep_vf/Makefile
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_ethtool.c [new file with mode: 0644]
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.c

index 694eb9b46e99f9c927dcd6f5d62a80a50c69000d..4a5f9fcb0b4074c73cf6f35ad51f50e4de0d59fc 100644 (file)
@@ -6,4 +6,5 @@
 obj-$(CONFIG_OCTEON_EP_VF) += octeon_ep_vf.o
 
 octeon_ep_vf-y := octep_vf_main.o octep_vf_cn9k.o octep_vf_cnxk.o \
-                 octep_vf_tx.o octep_vf_rx.o octep_vf_mbox.o
+                 octep_vf_tx.o octep_vf_rx.o octep_vf_mbox.o \
+                 octep_vf_ethtool.o
diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_ethtool.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_ethtool.c
new file mode 100644 (file)
index 0000000..25e6f64
--- /dev/null
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell Octeon EP (EndPoint) VF Ethernet Driver
+ *
+ * Copyright (C) 2020 Marvell.
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+
+#include "octep_vf_config.h"
+#include "octep_vf_main.h"
+
+static const char octep_vf_gstrings_global_stats[][ETH_GSTRING_LEN] = {
+       "rx_packets",
+       "tx_packets",
+       "rx_bytes",
+       "tx_bytes",
+       "rx_alloc_errors",
+       "tx_busy_errors",
+       "rx_dropped",
+       "tx_dropped",
+       "tx_hw_pkts",
+       "tx_hw_octs",
+       "tx_hw_bcast",
+       "tx_hw_mcast",
+       "rx_hw_pkts",
+       "rx_hw_bytes",
+       "rx_hw_bcast",
+       "rx_hw_mcast",
+       "rx_dropped_pkts_fifo_full",
+       "rx_dropped_bytes_fifo_full",
+       "rx_err_pkts",
+};
+
+#define OCTEP_VF_GLOBAL_STATS_CNT (sizeof(octep_vf_gstrings_global_stats) / ETH_GSTRING_LEN)
+
+static const char octep_vf_gstrings_tx_q_stats[][ETH_GSTRING_LEN] = {
+       "tx_packets_posted[Q-%u]",
+       "tx_packets_completed[Q-%u]",
+       "tx_bytes[Q-%u]",
+       "tx_busy[Q-%u]",
+};
+
+#define OCTEP_VF_TX_Q_STATS_CNT (sizeof(octep_vf_gstrings_tx_q_stats) / ETH_GSTRING_LEN)
+
+static const char octep_vf_gstrings_rx_q_stats[][ETH_GSTRING_LEN] = {
+       "rx_packets[Q-%u]",
+       "rx_bytes[Q-%u]",
+       "rx_alloc_errors[Q-%u]",
+};
+
+#define OCTEP_VF_RX_Q_STATS_CNT (sizeof(octep_vf_gstrings_rx_q_stats) / ETH_GSTRING_LEN)
+
+static void octep_vf_get_drvinfo(struct net_device *netdev,
+                                struct ethtool_drvinfo *info)
+{
+       struct octep_vf_device *oct = netdev_priv(netdev);
+
+       strscpy(info->driver, OCTEP_VF_DRV_NAME, sizeof(info->driver));
+       strscpy(info->bus_info, pci_name(oct->pdev), sizeof(info->bus_info));
+}
+
+static void octep_vf_get_strings(struct net_device *netdev,
+                                u32 stringset, u8 *data)
+{
+       struct octep_vf_device *oct = netdev_priv(netdev);
+       u16 num_queues = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf);
+       char *strings = (char *)data;
+       int i, j;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < OCTEP_VF_GLOBAL_STATS_CNT; i++) {
+                       snprintf(strings, ETH_GSTRING_LEN,
+                                octep_vf_gstrings_global_stats[i]);
+                       strings += ETH_GSTRING_LEN;
+               }
+
+               for (i = 0; i < num_queues; i++) {
+                       for (j = 0; j < OCTEP_VF_TX_Q_STATS_CNT; j++) {
+                               snprintf(strings, ETH_GSTRING_LEN,
+                                        octep_vf_gstrings_tx_q_stats[j], i);
+                               strings += ETH_GSTRING_LEN;
+                       }
+               }
+
+               for (i = 0; i < num_queues; i++) {
+                       for (j = 0; j < OCTEP_VF_RX_Q_STATS_CNT; j++) {
+                               snprintf(strings, ETH_GSTRING_LEN,
+                                        octep_vf_gstrings_rx_q_stats[j], i);
+                               strings += ETH_GSTRING_LEN;
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static int octep_vf_get_sset_count(struct net_device *netdev, int sset)
+{
+       struct octep_vf_device *oct = netdev_priv(netdev);
+       u16 num_queues = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf);
+
+       switch (sset) {
+       case ETH_SS_STATS:
+               return OCTEP_VF_GLOBAL_STATS_CNT + (num_queues *
+                      (OCTEP_VF_TX_Q_STATS_CNT + OCTEP_VF_RX_Q_STATS_CNT));
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void octep_vf_get_ethtool_stats(struct net_device *netdev,
+                                      struct ethtool_stats *stats, u64 *data)
+{
+       struct octep_vf_device *oct = netdev_priv(netdev);
+       struct octep_vf_iface_tx_stats *iface_tx_stats;
+       struct octep_vf_iface_rx_stats *iface_rx_stats;
+       u64 rx_alloc_errors, tx_busy_errors;
+       u64 rx_packets, rx_bytes;
+       u64 tx_packets, tx_bytes;
+       int q, i;
+
+       rx_packets = 0;
+       rx_bytes = 0;
+       tx_packets = 0;
+       tx_bytes = 0;
+       rx_alloc_errors = 0;
+       tx_busy_errors = 0;
+       tx_packets = 0;
+       tx_bytes = 0;
+       rx_packets = 0;
+       rx_bytes = 0;
+
+       octep_vf_get_if_stats(oct);
+       iface_tx_stats = &oct->iface_tx_stats;
+       iface_rx_stats = &oct->iface_rx_stats;
+
+       for (q = 0; q < oct->num_oqs; q++) {
+               struct octep_vf_iq *iq = oct->iq[q];
+               struct octep_vf_oq *oq = oct->oq[q];
+
+               tx_packets += iq->stats.instr_completed;
+               tx_bytes += iq->stats.bytes_sent;
+               tx_busy_errors += iq->stats.tx_busy;
+
+               rx_packets += oq->stats.packets;
+               rx_bytes += oq->stats.bytes;
+               rx_alloc_errors += oq->stats.alloc_failures;
+       }
+       i = 0;
+       data[i++] = rx_packets;
+       data[i++] = tx_packets;
+       data[i++] = rx_bytes;
+       data[i++] = tx_bytes;
+       data[i++] = rx_alloc_errors;
+       data[i++] = tx_busy_errors;
+       data[i++] = iface_rx_stats->dropped_pkts_fifo_full +
+                   iface_rx_stats->err_pkts;
+       data[i++] = iface_tx_stats->dropped;
+       data[i++] = iface_tx_stats->pkts;
+       data[i++] = iface_tx_stats->octs;
+       data[i++] = iface_tx_stats->bcst;
+       data[i++] = iface_tx_stats->mcst;
+       data[i++] = iface_rx_stats->pkts;
+       data[i++] = iface_rx_stats->octets;
+       data[i++] = iface_rx_stats->mcast_pkts;
+       data[i++] = iface_rx_stats->bcast_pkts;
+       data[i++] = iface_rx_stats->dropped_pkts_fifo_full;
+       data[i++] = iface_rx_stats->dropped_octets_fifo_full;
+       data[i++] = iface_rx_stats->err_pkts;
+
+       /* Per Tx Queue stats */
+       for (q = 0; q < oct->num_iqs; q++) {
+               struct octep_vf_iq *iq = oct->iq[q];
+
+               data[i++] = iq->stats.instr_posted;
+               data[i++] = iq->stats.instr_completed;
+               data[i++] = iq->stats.bytes_sent;
+               data[i++] = iq->stats.tx_busy;
+       }
+
+       /* Per Rx Queue stats */
+       for (q = 0; q < oct->num_oqs; q++) {
+               struct octep_vf_oq *oq = oct->oq[q];
+
+               data[i++] = oq->stats.packets;
+               data[i++] = oq->stats.bytes;
+               data[i++] = oq->stats.alloc_failures;
+       }
+}
+
+#define OCTEP_VF_SET_ETHTOOL_LINK_MODES_BITMAP(octep_vf_speeds, ksettings, name) \
+{ \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_T)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseT_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_R)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseR_FEC); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_CR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseCR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_KR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseKR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_LR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseLR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_SR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseSR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_25GBASE_CR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 25000baseCR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_25GBASE_KR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 25000baseKR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_25GBASE_SR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 25000baseSR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_40GBASE_CR4)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 40000baseCR4_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_40GBASE_KR4)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 40000baseKR4_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_40GBASE_LR4)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 40000baseLR4_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_40GBASE_SR4)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 40000baseSR4_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_CR2)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseCR2_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_KR2)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseKR2_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_SR2)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseSR2_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_CR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseCR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_KR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseKR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_LR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseLR_ER_FR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_SR)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseSR_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_100GBASE_CR4)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 100000baseCR4_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_100GBASE_KR4)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 100000baseKR4_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_100GBASE_LR4)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 100000baseLR4_ER4_Full); \
+       if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_100GBASE_SR4)) \
+               ethtool_link_ksettings_add_link_mode(ksettings, name, 100000baseSR4_Full); \
+}
+
+static int octep_vf_get_link_ksettings(struct net_device *netdev,
+                                      struct ethtool_link_ksettings *cmd)
+{
+       struct octep_vf_device *oct = netdev_priv(netdev);
+       struct octep_vf_iface_link_info *link_info;
+       u32 advertised_modes, supported_modes;
+
+       ethtool_link_ksettings_zero_link_mode(cmd, supported);
+       ethtool_link_ksettings_zero_link_mode(cmd, advertising);
+
+       octep_vf_get_link_info(oct);
+
+       advertised_modes = oct->link_info.advertised_modes;
+       supported_modes = oct->link_info.supported_modes;
+       link_info = &oct->link_info;
+
+       OCTEP_VF_SET_ETHTOOL_LINK_MODES_BITMAP(supported_modes, cmd, supported);
+       OCTEP_VF_SET_ETHTOOL_LINK_MODES_BITMAP(advertised_modes, cmd, advertising);
+
+       if (link_info->autoneg) {
+               if (link_info->autoneg & OCTEP_VF_LINK_MODE_AUTONEG_SUPPORTED)
+                       ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
+               if (link_info->autoneg & OCTEP_VF_LINK_MODE_AUTONEG_ADVERTISED) {
+                       ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
+                       cmd->base.autoneg = AUTONEG_ENABLE;
+               } else {
+                       cmd->base.autoneg = AUTONEG_DISABLE;
+               }
+       } else {
+               cmd->base.autoneg = AUTONEG_DISABLE;
+       }
+
+       cmd->base.port = PORT_FIBRE;
+       ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
+       ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE);
+
+       if (netif_carrier_ok(netdev)) {
+               cmd->base.speed = link_info->speed;
+               cmd->base.duplex = DUPLEX_FULL;
+       } else {
+               cmd->base.speed = SPEED_UNKNOWN;
+               cmd->base.duplex = DUPLEX_UNKNOWN;
+       }
+       return 0;
+}
+
+static const struct ethtool_ops octep_vf_ethtool_ops = {
+       .get_drvinfo = octep_vf_get_drvinfo,
+       .get_link = ethtool_op_get_link,
+       .get_strings = octep_vf_get_strings,
+       .get_sset_count = octep_vf_get_sset_count,
+       .get_ethtool_stats = octep_vf_get_ethtool_stats,
+       .get_link_ksettings = octep_vf_get_link_ksettings,
+};
+
+void octep_vf_set_ethtool_ops(struct net_device *netdev)
+{
+       netdev->ethtool_ops = &octep_vf_ethtool_ops;
+}
index 9ce60e86ba205c8767de211577d061cd880c8a67..773168772f6a1862d56e311c741495db32563a85 100644 (file)
@@ -1081,6 +1081,7 @@ static int octep_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        INIT_WORK(&octep_vf_dev->tx_timeout_task, octep_vf_tx_timeout_task);
 
        netdev->netdev_ops = &octep_vf_netdev_ops;
+       octep_vf_set_ethtool_ops(netdev);
        netif_carrier_off(netdev);
 
        if (octep_vf_setup_mbox(octep_vf_dev)) {
index b3150f977a3027c939d698bd82337bbee0113108..599e1c7fc651c4ec93a662d6358b9999c46785cb 100644 (file)
@@ -331,6 +331,7 @@ void octep_vf_device_setup_cn93(struct octep_vf_device *oct);
 void octep_vf_device_setup_cnxk(struct octep_vf_device *oct);
 int octep_vf_iq_process_completions(struct octep_vf_iq *iq, u16 budget);
 int octep_vf_oq_process_rx(struct octep_vf_oq *oq, int budget);
+void octep_vf_set_ethtool_ops(struct net_device *netdev);
 int octep_vf_get_link_info(struct octep_vf_device *oct);
 int octep_vf_get_if_stats(struct octep_vf_device *oct);
 void octep_vf_mbox_work(struct work_struct *work);
index 2eab21e43048a1149745874bfddbc053ccb2b6af..594bee15bd363c29e4780894601d70c861c9838e 100644 (file)
@@ -114,7 +114,7 @@ static int __octep_vf_mbox_send_cmd(struct octep_vf_device *oct,
 {
        struct octep_vf_mbox *mbox = oct->mbox;
        u64 reg_val = 0ull;
-       int count;
+       int count = 0;
 
        if (!mbox)
                return OCTEP_PFVF_MBOX_CMD_STATUS_NOT_SETUP;