221 lines
6.3 KiB
C
221 lines
6.3 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
|
||
|
|
||
|
#include <linux/ethtool.h>
|
||
|
#include <linux/netdevice.h>
|
||
|
#include <linux/pci.h>
|
||
|
|
||
|
#include "fbnic.h"
|
||
|
#include "fbnic_netdev.h"
|
||
|
#include "fbnic_tlv.h"
|
||
|
|
||
|
struct fbnic_stat {
|
||
|
u8 string[ETH_GSTRING_LEN];
|
||
|
unsigned int size;
|
||
|
unsigned int offset;
|
||
|
};
|
||
|
|
||
|
#define FBNIC_STAT_FIELDS(type, name, stat) { \
|
||
|
.string = name, \
|
||
|
.size = sizeof_field(struct type, stat), \
|
||
|
.offset = offsetof(struct type, stat), \
|
||
|
}
|
||
|
|
||
|
/* Hardware statistics not captured in rtnl_link_stats */
|
||
|
#define FBNIC_HW_STAT(name, stat) \
|
||
|
FBNIC_STAT_FIELDS(fbnic_hw_stats, name, stat)
|
||
|
|
||
|
static const struct fbnic_stat fbnic_gstrings_hw_stats[] = {
|
||
|
/* RPC */
|
||
|
FBNIC_HW_STAT("rpc_unkn_etype", rpc.unkn_etype),
|
||
|
FBNIC_HW_STAT("rpc_unkn_ext_hdr", rpc.unkn_ext_hdr),
|
||
|
FBNIC_HW_STAT("rpc_ipv4_frag", rpc.ipv4_frag),
|
||
|
FBNIC_HW_STAT("rpc_ipv6_frag", rpc.ipv6_frag),
|
||
|
FBNIC_HW_STAT("rpc_ipv4_esp", rpc.ipv4_esp),
|
||
|
FBNIC_HW_STAT("rpc_ipv6_esp", rpc.ipv6_esp),
|
||
|
FBNIC_HW_STAT("rpc_tcp_opt_err", rpc.tcp_opt_err),
|
||
|
FBNIC_HW_STAT("rpc_out_of_hdr_err", rpc.out_of_hdr_err),
|
||
|
};
|
||
|
|
||
|
#define FBNIC_HW_FIXED_STATS_LEN ARRAY_SIZE(fbnic_gstrings_hw_stats)
|
||
|
#define FBNIC_HW_STATS_LEN FBNIC_HW_FIXED_STATS_LEN
|
||
|
|
||
|
static int
|
||
|
fbnic_get_ts_info(struct net_device *netdev,
|
||
|
struct kernel_ethtool_ts_info *tsinfo)
|
||
|
{
|
||
|
struct fbnic_net *fbn = netdev_priv(netdev);
|
||
|
|
||
|
tsinfo->phc_index = ptp_clock_index(fbn->fbd->ptp);
|
||
|
|
||
|
tsinfo->so_timestamping =
|
||
|
SOF_TIMESTAMPING_TX_SOFTWARE |
|
||
|
SOF_TIMESTAMPING_TX_HARDWARE |
|
||
|
SOF_TIMESTAMPING_RX_HARDWARE |
|
||
|
SOF_TIMESTAMPING_RAW_HARDWARE;
|
||
|
|
||
|
tsinfo->tx_types =
|
||
|
BIT(HWTSTAMP_TX_OFF) |
|
||
|
BIT(HWTSTAMP_TX_ON);
|
||
|
|
||
|
tsinfo->rx_filters =
|
||
|
BIT(HWTSTAMP_FILTER_NONE) |
|
||
|
BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
|
||
|
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
|
||
|
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
|
||
|
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) |
|
||
|
BIT(HWTSTAMP_FILTER_ALL);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
fbnic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
|
||
|
{
|
||
|
struct fbnic_net *fbn = netdev_priv(netdev);
|
||
|
struct fbnic_dev *fbd = fbn->fbd;
|
||
|
|
||
|
fbnic_get_fw_ver_commit_str(fbd, drvinfo->fw_version,
|
||
|
sizeof(drvinfo->fw_version));
|
||
|
}
|
||
|
|
||
|
static void fbnic_set_counter(u64 *stat, struct fbnic_stat_counter *counter)
|
||
|
{
|
||
|
if (counter->reported)
|
||
|
*stat = counter->value;
|
||
|
}
|
||
|
|
||
|
static void fbnic_get_strings(struct net_device *dev, u32 sset, u8 *data)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
switch (sset) {
|
||
|
case ETH_SS_STATS:
|
||
|
for (i = 0; i < FBNIC_HW_STATS_LEN; i++)
|
||
|
ethtool_puts(&data, fbnic_gstrings_hw_stats[i].string);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int fbnic_get_sset_count(struct net_device *dev, int sset)
|
||
|
{
|
||
|
switch (sset) {
|
||
|
case ETH_SS_STATS:
|
||
|
return FBNIC_HW_STATS_LEN;
|
||
|
default:
|
||
|
return -EOPNOTSUPP;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void fbnic_get_ethtool_stats(struct net_device *dev,
|
||
|
struct ethtool_stats *stats, u64 *data)
|
||
|
{
|
||
|
struct fbnic_net *fbn = netdev_priv(dev);
|
||
|
const struct fbnic_stat *stat;
|
||
|
int i;
|
||
|
|
||
|
fbnic_get_hw_stats(fbn->fbd);
|
||
|
|
||
|
for (i = 0; i < FBNIC_HW_STATS_LEN; i++) {
|
||
|
stat = &fbnic_gstrings_hw_stats[i];
|
||
|
data[i] = *(u64 *)((u8 *)&fbn->fbd->hw_stats + stat->offset);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
fbnic_get_eth_mac_stats(struct net_device *netdev,
|
||
|
struct ethtool_eth_mac_stats *eth_mac_stats)
|
||
|
{
|
||
|
struct fbnic_net *fbn = netdev_priv(netdev);
|
||
|
struct fbnic_mac_stats *mac_stats;
|
||
|
struct fbnic_dev *fbd = fbn->fbd;
|
||
|
const struct fbnic_mac *mac;
|
||
|
|
||
|
mac_stats = &fbd->hw_stats.mac;
|
||
|
mac = fbd->mac;
|
||
|
|
||
|
mac->get_eth_mac_stats(fbd, false, &mac_stats->eth_mac);
|
||
|
|
||
|
fbnic_set_counter(ð_mac_stats->FramesTransmittedOK,
|
||
|
&mac_stats->eth_mac.FramesTransmittedOK);
|
||
|
fbnic_set_counter(ð_mac_stats->FramesReceivedOK,
|
||
|
&mac_stats->eth_mac.FramesReceivedOK);
|
||
|
fbnic_set_counter(ð_mac_stats->FrameCheckSequenceErrors,
|
||
|
&mac_stats->eth_mac.FrameCheckSequenceErrors);
|
||
|
fbnic_set_counter(ð_mac_stats->AlignmentErrors,
|
||
|
&mac_stats->eth_mac.AlignmentErrors);
|
||
|
fbnic_set_counter(ð_mac_stats->OctetsTransmittedOK,
|
||
|
&mac_stats->eth_mac.OctetsTransmittedOK);
|
||
|
fbnic_set_counter(ð_mac_stats->FramesLostDueToIntMACXmitError,
|
||
|
&mac_stats->eth_mac.FramesLostDueToIntMACXmitError);
|
||
|
fbnic_set_counter(ð_mac_stats->OctetsReceivedOK,
|
||
|
&mac_stats->eth_mac.OctetsReceivedOK);
|
||
|
fbnic_set_counter(ð_mac_stats->FramesLostDueToIntMACRcvError,
|
||
|
&mac_stats->eth_mac.FramesLostDueToIntMACRcvError);
|
||
|
fbnic_set_counter(ð_mac_stats->MulticastFramesXmittedOK,
|
||
|
&mac_stats->eth_mac.MulticastFramesXmittedOK);
|
||
|
fbnic_set_counter(ð_mac_stats->BroadcastFramesXmittedOK,
|
||
|
&mac_stats->eth_mac.BroadcastFramesXmittedOK);
|
||
|
fbnic_set_counter(ð_mac_stats->MulticastFramesReceivedOK,
|
||
|
&mac_stats->eth_mac.MulticastFramesReceivedOK);
|
||
|
fbnic_set_counter(ð_mac_stats->BroadcastFramesReceivedOK,
|
||
|
&mac_stats->eth_mac.BroadcastFramesReceivedOK);
|
||
|
fbnic_set_counter(ð_mac_stats->FrameTooLongErrors,
|
||
|
&mac_stats->eth_mac.FrameTooLongErrors);
|
||
|
}
|
||
|
|
||
|
static void fbnic_get_ts_stats(struct net_device *netdev,
|
||
|
struct ethtool_ts_stats *ts_stats)
|
||
|
{
|
||
|
struct fbnic_net *fbn = netdev_priv(netdev);
|
||
|
u64 ts_packets, ts_lost;
|
||
|
struct fbnic_ring *ring;
|
||
|
unsigned int start;
|
||
|
int i;
|
||
|
|
||
|
ts_stats->pkts = fbn->tx_stats.ts_packets;
|
||
|
ts_stats->lost = fbn->tx_stats.ts_lost;
|
||
|
for (i = 0; i < fbn->num_tx_queues; i++) {
|
||
|
ring = fbn->tx[i];
|
||
|
do {
|
||
|
start = u64_stats_fetch_begin(&ring->stats.syncp);
|
||
|
ts_packets = ring->stats.ts_packets;
|
||
|
ts_lost = ring->stats.ts_lost;
|
||
|
} while (u64_stats_fetch_retry(&ring->stats.syncp, start));
|
||
|
ts_stats->pkts += ts_packets;
|
||
|
ts_stats->lost += ts_lost;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void fbnic_get_regs(struct net_device *netdev,
|
||
|
struct ethtool_regs *regs, void *data)
|
||
|
{
|
||
|
struct fbnic_net *fbn = netdev_priv(netdev);
|
||
|
|
||
|
fbnic_csr_get_regs(fbn->fbd, data, ®s->version);
|
||
|
}
|
||
|
|
||
|
static int fbnic_get_regs_len(struct net_device *netdev)
|
||
|
{
|
||
|
struct fbnic_net *fbn = netdev_priv(netdev);
|
||
|
|
||
|
return fbnic_csr_regs_len(fbn->fbd) * sizeof(u32);
|
||
|
}
|
||
|
|
||
|
static const struct ethtool_ops fbnic_ethtool_ops = {
|
||
|
.get_drvinfo = fbnic_get_drvinfo,
|
||
|
.get_regs_len = fbnic_get_regs_len,
|
||
|
.get_regs = fbnic_get_regs,
|
||
|
.get_strings = fbnic_get_strings,
|
||
|
.get_ethtool_stats = fbnic_get_ethtool_stats,
|
||
|
.get_sset_count = fbnic_get_sset_count,
|
||
|
.get_ts_info = fbnic_get_ts_info,
|
||
|
.get_ts_stats = fbnic_get_ts_stats,
|
||
|
.get_eth_mac_stats = fbnic_get_eth_mac_stats,
|
||
|
};
|
||
|
|
||
|
void fbnic_set_ethtool_ops(struct net_device *dev)
|
||
|
{
|
||
|
dev->ethtool_ops = &fbnic_ethtool_ops;
|
||
|
}
|