diff options
Diffstat (limited to 'drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c')
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c | 209 |
1 files changed, 202 insertions, 7 deletions
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c index dc7ba8d5fc43..a1c2db69b198 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c @@ -2,6 +2,7 @@ /* Copyright (c) Meta Platforms, Inc. and affiliates. */ #include <linux/ethtool.h> +#include <linux/ethtool_netlink.h> #include <linux/netdevice.h> #include <linux/pci.h> #include <net/ipv6.h> @@ -111,6 +112,20 @@ static const struct fbnic_stat fbnic_gstrings_hw_q_stats[] = { FBNIC_HW_RXB_DEQUEUE_STATS_LEN * FBNIC_RXB_DEQUEUE_INDICES + \ FBNIC_HW_Q_STATS_LEN * FBNIC_MAX_QUEUES) +#define FBNIC_QUEUE_STAT(name, stat) \ + FBNIC_STAT_FIELDS(fbnic_ring, name, stat) + +static const struct fbnic_stat fbnic_gstrings_xdp_stats[] = { + FBNIC_QUEUE_STAT("xdp_tx_queue_%u_packets", stats.packets), + FBNIC_QUEUE_STAT("xdp_tx_queue_%u_bytes", stats.bytes), + FBNIC_QUEUE_STAT("xdp_tx_queue_%u_dropped", stats.dropped), +}; + +#define FBNIC_XDP_STATS_LEN ARRAY_SIZE(fbnic_gstrings_xdp_stats) + +#define FBNIC_STATS_LEN \ + (FBNIC_HW_STATS_LEN + FBNIC_XDP_STATS_LEN * FBNIC_MAX_XDPQS) + static void fbnic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { @@ -160,6 +175,7 @@ static void fbnic_clone_swap_cfg(struct fbnic_net *orig, swap(clone->num_rx_queues, orig->num_rx_queues); swap(clone->num_tx_queues, orig->num_tx_queues); swap(clone->num_napi, orig->num_napi); + swap(clone->hds_thresh, orig->hds_thresh); } static void fbnic_aggregate_vector_counters(struct fbnic_net *fbn, @@ -277,15 +293,21 @@ fbnic_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, ring->rx_mini_pending = fbn->hpq_size; ring->rx_jumbo_pending = fbn->ppq_size; ring->tx_pending = fbn->txq_size; + + kernel_ring->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_ENABLED; + kernel_ring->hds_thresh_max = FBNIC_HDS_THRESH_MAX; + kernel_ring->hds_thresh = fbn->hds_thresh; } static void fbnic_set_rings(struct fbnic_net *fbn, - struct ethtool_ringparam *ring) + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring) { fbn->rcq_size = ring->rx_pending; fbn->hpq_size = ring->rx_mini_pending; fbn->ppq_size = ring->rx_jumbo_pending; fbn->txq_size = ring->tx_pending; + fbn->hds_thresh = kernel_ring->hds_thresh; } static int @@ -316,8 +338,24 @@ fbnic_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, return -EINVAL; } + if (kernel_ring->tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_DISABLED) { + NL_SET_ERR_MSG_MOD(extack, "Cannot disable TCP data split"); + return -EINVAL; + } + + /* If an XDP program is attached, we should check for potential frame + * splitting. If the new HDS threshold can cause splitting, we should + * only allow if the attached XDP program can handle frags. + */ + if (fbnic_check_split_frames(fbn->xdp_prog, netdev->mtu, + kernel_ring->hds_thresh)) { + NL_SET_ERR_MSG_MOD(extack, + "Use higher HDS threshold or multi-buf capable program"); + return -EINVAL; + } + if (!netif_running(netdev)) { - fbnic_set_rings(fbn, ring); + fbnic_set_rings(fbn, ring, kernel_ring); return 0; } @@ -325,7 +363,7 @@ fbnic_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, if (!clone) return -ENOMEM; - fbnic_set_rings(clone, ring); + fbnic_set_rings(clone, ring, kernel_ring); err = fbnic_alloc_napi_vectors(clone); if (err) @@ -398,6 +436,16 @@ static void fbnic_get_rxb_dequeue_strings(u8 **data, unsigned int idx) ethtool_sprintf(data, stat->string, idx); } +static void fbnic_get_xdp_queue_strings(u8 **data, unsigned int idx) +{ + const struct fbnic_stat *stat; + int i; + + stat = fbnic_gstrings_xdp_stats; + for (i = 0; i < FBNIC_XDP_STATS_LEN; i++, stat++) + ethtool_sprintf(data, stat->string, idx); +} + static void fbnic_get_strings(struct net_device *dev, u32 sset, u8 *data) { const struct fbnic_stat *stat; @@ -423,6 +471,9 @@ static void fbnic_get_strings(struct net_device *dev, u32 sset, u8 *data) for (i = 0; i < FBNIC_HW_Q_STATS_LEN; i++, stat++) ethtool_sprintf(&data, stat->string, idx); } + + for (i = 0; i < FBNIC_MAX_XDPQS; i++) + fbnic_get_xdp_queue_strings(&data, i); break; } } @@ -440,6 +491,24 @@ static void fbnic_report_hw_stats(const struct fbnic_stat *stat, } } +static void fbnic_get_xdp_queue_stats(struct fbnic_ring *ring, u64 **data) +{ + const struct fbnic_stat *stat; + int i; + + if (!ring) { + *data += FBNIC_XDP_STATS_LEN; + return; + } + + stat = fbnic_gstrings_xdp_stats; + for (i = 0; i < FBNIC_XDP_STATS_LEN; i++, stat++, (*data)++) { + u8 *p = (u8 *)ring + stat->offset; + + **data = *(u64 *)p; + } +} + static void fbnic_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { @@ -449,7 +518,7 @@ static void fbnic_get_ethtool_stats(struct net_device *dev, fbnic_get_hw_stats(fbn->fbd); - spin_lock(&fbd->hw_stats_lock); + spin_lock(&fbd->hw_stats.lock); fbnic_report_hw_stats(fbnic_gstrings_hw_stats, &fbd->hw_stats, FBNIC_HW_FIXED_STATS_LEN, &data); @@ -486,14 +555,17 @@ static void fbnic_get_ethtool_stats(struct net_device *dev, fbnic_report_hw_stats(fbnic_gstrings_hw_q_stats, hw_q, FBNIC_HW_Q_STATS_LEN, &data); } - spin_unlock(&fbd->hw_stats_lock); + spin_unlock(&fbd->hw_stats.lock); + + for (i = 0; i < FBNIC_MAX_XDPQS; i++) + fbnic_get_xdp_queue_stats(fbn->tx[i + FBNIC_MAX_TXQS], &data); } static int fbnic_get_sset_count(struct net_device *dev, int sset) { switch (sset) { case ETH_SS_STATS: - return FBNIC_HW_STATS_LEN; + return FBNIC_STATS_LEN; default: return -EOPNOTSUPP; } @@ -1310,7 +1382,7 @@ fbnic_get_rss_hash_opts(struct net_device *netdev, #define FBNIC_L2_HASH_OPTIONS \ (RXH_L2DA | RXH_DISCARD) #define FBNIC_L3_HASH_OPTIONS \ - (FBNIC_L2_HASH_OPTIONS | RXH_IP_SRC | RXH_IP_DST) + (FBNIC_L2_HASH_OPTIONS | RXH_IP_SRC | RXH_IP_DST | RXH_IP6_FL) #define FBNIC_L4_HASH_OPTIONS \ (FBNIC_L3_HASH_OPTIONS | RXH_L4_B_0_1 | RXH_L4_B_2_3) @@ -1563,6 +1635,65 @@ static void fbnic_get_ts_stats(struct net_device *netdev, } } +static int +fbnic_get_module_eeprom_by_page(struct net_device *netdev, + const struct ethtool_module_eeprom *page_data, + struct netlink_ext_ack *extack) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_fw_completion *fw_cmpl; + struct fbnic_dev *fbd = fbn->fbd; + int err; + + if (page_data->i2c_address != 0x50) { + NL_SET_ERR_MSG_MOD(extack, + "Invalid i2c address. Only 0x50 is supported"); + return -EINVAL; + } + + fw_cmpl = __fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_QSFP_READ_RESP, + page_data->length); + if (!fw_cmpl) + return -ENOMEM; + + /* Initialize completion and queue it for FW to process */ + fw_cmpl->u.qsfp.length = page_data->length; + fw_cmpl->u.qsfp.offset = page_data->offset; + fw_cmpl->u.qsfp.page = page_data->page; + fw_cmpl->u.qsfp.bank = page_data->bank; + + err = fbnic_fw_xmit_qsfp_read_msg(fbd, fw_cmpl, page_data->page, + page_data->bank, page_data->offset, + page_data->length); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Failed to transmit EEPROM read request"); + goto exit_free; + } + + if (!wait_for_completion_timeout(&fw_cmpl->done, 2 * HZ)) { + err = -ETIMEDOUT; + NL_SET_ERR_MSG_MOD(extack, + "Timed out waiting for firmware response"); + goto exit_cleanup; + } + + if (fw_cmpl->result) { + err = fw_cmpl->result; + NL_SET_ERR_MSG_MOD(extack, "Failed to read EEPROM"); + goto exit_cleanup; + } + + memcpy(page_data->data, fw_cmpl->u.qsfp.data, page_data->length); + +exit_cleanup: + fbnic_mbx_clear_cmpl(fbd, fw_cmpl); +exit_free: + fbnic_fw_put_cmpl(fw_cmpl); + + return err ? : page_data->length; +} + static void fbnic_set_counter(u64 *stat, struct fbnic_stat_counter *counter) { if (counter->reported) @@ -1570,6 +1701,63 @@ static void fbnic_set_counter(u64 *stat, struct fbnic_stat_counter *counter) } static void +fbnic_get_pause_stats(struct net_device *netdev, + struct ethtool_pause_stats *pause_stats) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_mac_stats *mac_stats; + struct fbnic_dev *fbd = fbn->fbd; + + mac_stats = &fbd->hw_stats.mac; + + fbd->mac->get_pause_stats(fbd, false, &mac_stats->pause); + + pause_stats->tx_pause_frames = mac_stats->pause.tx_pause_frames.value; + pause_stats->rx_pause_frames = mac_stats->pause.rx_pause_frames.value; +} + +static void +fbnic_get_fec_stats(struct net_device *netdev, + struct ethtool_fec_stats *fec_stats, + struct ethtool_fec_hist *hist) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_phy_stats *phy_stats; + struct fbnic_dev *fbd = fbn->fbd; + + fbnic_get_hw_stats32(fbd); + phy_stats = &fbd->hw_stats.phy; + + spin_lock(&fbd->hw_stats.lock); + fec_stats->corrected_blocks.total = + phy_stats->fec.corrected_blocks.value; + fec_stats->uncorrectable_blocks.total = + phy_stats->fec.uncorrectable_blocks.value; + spin_unlock(&fbd->hw_stats.lock); +} + +static void +fbnic_get_eth_phy_stats(struct net_device *netdev, + struct ethtool_eth_phy_stats *eth_phy_stats) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_phy_stats *phy_stats; + struct fbnic_dev *fbd = fbn->fbd; + u64 total = 0; + int i; + + fbnic_get_hw_stats32(fbd); + phy_stats = &fbd->hw_stats.phy; + + spin_lock(&fbd->hw_stats.lock); + for (i = 0; i < FBNIC_PCS_MAX_LANES; i++) + total += phy_stats->pcs.SymbolErrorDuringCarrier.lanes[i].value; + + eth_phy_stats->SymbolErrorDuringCarrier = total; + spin_unlock(&fbd->hw_stats.lock); +} + +static void fbnic_get_eth_mac_stats(struct net_device *netdev, struct ethtool_eth_mac_stats *eth_mac_stats) { @@ -1676,8 +1864,11 @@ fbnic_get_rmon_stats(struct net_device *netdev, } static const struct ethtool_ops fbnic_ethtool_ops = { + .cap_link_lanes_supported = true, .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_RX_MAX_FRAMES, + .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT | + ETHTOOL_RING_USE_HDS_THRS, .rxfh_max_num_contexts = FBNIC_RPC_RSS_TBL_COUNT, .get_drvinfo = fbnic_get_drvinfo, .get_regs_len = fbnic_get_regs_len, @@ -1687,6 +1878,7 @@ static const struct ethtool_ops fbnic_ethtool_ops = { .set_coalesce = fbnic_set_coalesce, .get_ringparam = fbnic_get_ringparam, .set_ringparam = fbnic_set_ringparam, + .get_pause_stats = fbnic_get_pause_stats, .get_pauseparam = fbnic_phylink_get_pauseparam, .set_pauseparam = fbnic_phylink_set_pauseparam, .get_strings = fbnic_get_strings, @@ -1708,7 +1900,10 @@ static const struct ethtool_ops fbnic_ethtool_ops = { .get_ts_info = fbnic_get_ts_info, .get_ts_stats = fbnic_get_ts_stats, .get_link_ksettings = fbnic_phylink_ethtool_ksettings_get, + .get_fec_stats = fbnic_get_fec_stats, .get_fecparam = fbnic_phylink_get_fecparam, + .get_module_eeprom_by_page = fbnic_get_module_eeprom_by_page, + .get_eth_phy_stats = fbnic_get_eth_phy_stats, .get_eth_mac_stats = fbnic_get_eth_mac_stats, .get_eth_ctrl_stats = fbnic_get_eth_ctrl_stats, .get_rmon_stats = fbnic_get_rmon_stats, |