| // 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; |
| } |