162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* Copyright (c) 2021, Microsoft Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/inetdevice.h> 562306a36Sopenharmony_ci#include <linux/etherdevice.h> 662306a36Sopenharmony_ci#include <linux/ethtool.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <net/mana/mana.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistatic const struct { 1162306a36Sopenharmony_ci char name[ETH_GSTRING_LEN]; 1262306a36Sopenharmony_ci u16 offset; 1362306a36Sopenharmony_ci} mana_eth_stats[] = { 1462306a36Sopenharmony_ci {"stop_queue", offsetof(struct mana_ethtool_stats, stop_queue)}, 1562306a36Sopenharmony_ci {"wake_queue", offsetof(struct mana_ethtool_stats, wake_queue)}, 1662306a36Sopenharmony_ci {"hc_tx_bytes", offsetof(struct mana_ethtool_stats, hc_tx_bytes)}, 1762306a36Sopenharmony_ci {"hc_tx_ucast_pkts", offsetof(struct mana_ethtool_stats, 1862306a36Sopenharmony_ci hc_tx_ucast_pkts)}, 1962306a36Sopenharmony_ci {"hc_tx_ucast_bytes", offsetof(struct mana_ethtool_stats, 2062306a36Sopenharmony_ci hc_tx_ucast_bytes)}, 2162306a36Sopenharmony_ci {"hc_tx_bcast_pkts", offsetof(struct mana_ethtool_stats, 2262306a36Sopenharmony_ci hc_tx_bcast_pkts)}, 2362306a36Sopenharmony_ci {"hc_tx_bcast_bytes", offsetof(struct mana_ethtool_stats, 2462306a36Sopenharmony_ci hc_tx_bcast_bytes)}, 2562306a36Sopenharmony_ci {"hc_tx_mcast_pkts", offsetof(struct mana_ethtool_stats, 2662306a36Sopenharmony_ci hc_tx_mcast_pkts)}, 2762306a36Sopenharmony_ci {"hc_tx_mcast_bytes", offsetof(struct mana_ethtool_stats, 2862306a36Sopenharmony_ci hc_tx_mcast_bytes)}, 2962306a36Sopenharmony_ci {"tx_cq_err", offsetof(struct mana_ethtool_stats, tx_cqe_err)}, 3062306a36Sopenharmony_ci {"tx_cqe_unknown_type", offsetof(struct mana_ethtool_stats, 3162306a36Sopenharmony_ci tx_cqe_unknown_type)}, 3262306a36Sopenharmony_ci {"rx_coalesced_err", offsetof(struct mana_ethtool_stats, 3362306a36Sopenharmony_ci rx_coalesced_err)}, 3462306a36Sopenharmony_ci {"rx_cqe_unknown_type", offsetof(struct mana_ethtool_stats, 3562306a36Sopenharmony_ci rx_cqe_unknown_type)}, 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int mana_get_sset_count(struct net_device *ndev, int stringset) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct mana_port_context *apc = netdev_priv(ndev); 4162306a36Sopenharmony_ci unsigned int num_queues = apc->num_queues; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (stringset != ETH_SS_STATS) 4462306a36Sopenharmony_ci return -EINVAL; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return ARRAY_SIZE(mana_eth_stats) + num_queues * 4762306a36Sopenharmony_ci (MANA_STATS_RX_COUNT + MANA_STATS_TX_COUNT); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void mana_get_strings(struct net_device *ndev, u32 stringset, u8 *data) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct mana_port_context *apc = netdev_priv(ndev); 5362306a36Sopenharmony_ci unsigned int num_queues = apc->num_queues; 5462306a36Sopenharmony_ci u8 *p = data; 5562306a36Sopenharmony_ci int i; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (stringset != ETH_SS_STATS) 5862306a36Sopenharmony_ci return; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mana_eth_stats); i++) { 6162306a36Sopenharmony_ci memcpy(p, mana_eth_stats[i].name, ETH_GSTRING_LEN); 6262306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci for (i = 0; i < num_queues; i++) { 6662306a36Sopenharmony_ci sprintf(p, "rx_%d_packets", i); 6762306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 6862306a36Sopenharmony_ci sprintf(p, "rx_%d_bytes", i); 6962306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 7062306a36Sopenharmony_ci sprintf(p, "rx_%d_xdp_drop", i); 7162306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 7262306a36Sopenharmony_ci sprintf(p, "rx_%d_xdp_tx", i); 7362306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 7462306a36Sopenharmony_ci sprintf(p, "rx_%d_xdp_redirect", i); 7562306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci for (i = 0; i < num_queues; i++) { 7962306a36Sopenharmony_ci sprintf(p, "tx_%d_packets", i); 8062306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 8162306a36Sopenharmony_ci sprintf(p, "tx_%d_bytes", i); 8262306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 8362306a36Sopenharmony_ci sprintf(p, "tx_%d_xdp_xmit", i); 8462306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 8562306a36Sopenharmony_ci sprintf(p, "tx_%d_tso_packets", i); 8662306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 8762306a36Sopenharmony_ci sprintf(p, "tx_%d_tso_bytes", i); 8862306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 8962306a36Sopenharmony_ci sprintf(p, "tx_%d_tso_inner_packets", i); 9062306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 9162306a36Sopenharmony_ci sprintf(p, "tx_%d_tso_inner_bytes", i); 9262306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 9362306a36Sopenharmony_ci sprintf(p, "tx_%d_long_pkt_fmt", i); 9462306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 9562306a36Sopenharmony_ci sprintf(p, "tx_%d_short_pkt_fmt", i); 9662306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 9762306a36Sopenharmony_ci sprintf(p, "tx_%d_csum_partial", i); 9862306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 9962306a36Sopenharmony_ci sprintf(p, "tx_%d_mana_map_err", i); 10062306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void mana_get_ethtool_stats(struct net_device *ndev, 10562306a36Sopenharmony_ci struct ethtool_stats *e_stats, u64 *data) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct mana_port_context *apc = netdev_priv(ndev); 10862306a36Sopenharmony_ci unsigned int num_queues = apc->num_queues; 10962306a36Sopenharmony_ci void *eth_stats = &apc->eth_stats; 11062306a36Sopenharmony_ci struct mana_stats_rx *rx_stats; 11162306a36Sopenharmony_ci struct mana_stats_tx *tx_stats; 11262306a36Sopenharmony_ci unsigned int start; 11362306a36Sopenharmony_ci u64 packets, bytes; 11462306a36Sopenharmony_ci u64 xdp_redirect; 11562306a36Sopenharmony_ci u64 xdp_xmit; 11662306a36Sopenharmony_ci u64 xdp_drop; 11762306a36Sopenharmony_ci u64 xdp_tx; 11862306a36Sopenharmony_ci u64 tso_packets; 11962306a36Sopenharmony_ci u64 tso_bytes; 12062306a36Sopenharmony_ci u64 tso_inner_packets; 12162306a36Sopenharmony_ci u64 tso_inner_bytes; 12262306a36Sopenharmony_ci u64 long_pkt_fmt; 12362306a36Sopenharmony_ci u64 short_pkt_fmt; 12462306a36Sopenharmony_ci u64 csum_partial; 12562306a36Sopenharmony_ci u64 mana_map_err; 12662306a36Sopenharmony_ci int q, i = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (!apc->port_is_up) 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci /* we call mana function to update stats from GDMA */ 13162306a36Sopenharmony_ci mana_query_gf_stats(apc); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (q = 0; q < ARRAY_SIZE(mana_eth_stats); q++) 13462306a36Sopenharmony_ci data[i++] = *(u64 *)(eth_stats + mana_eth_stats[q].offset); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci for (q = 0; q < num_queues; q++) { 13762306a36Sopenharmony_ci rx_stats = &apc->rxqs[q]->stats; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci do { 14062306a36Sopenharmony_ci start = u64_stats_fetch_begin(&rx_stats->syncp); 14162306a36Sopenharmony_ci packets = rx_stats->packets; 14262306a36Sopenharmony_ci bytes = rx_stats->bytes; 14362306a36Sopenharmony_ci xdp_drop = rx_stats->xdp_drop; 14462306a36Sopenharmony_ci xdp_tx = rx_stats->xdp_tx; 14562306a36Sopenharmony_ci xdp_redirect = rx_stats->xdp_redirect; 14662306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci data[i++] = packets; 14962306a36Sopenharmony_ci data[i++] = bytes; 15062306a36Sopenharmony_ci data[i++] = xdp_drop; 15162306a36Sopenharmony_ci data[i++] = xdp_tx; 15262306a36Sopenharmony_ci data[i++] = xdp_redirect; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci for (q = 0; q < num_queues; q++) { 15662306a36Sopenharmony_ci tx_stats = &apc->tx_qp[q].txq.stats; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci do { 15962306a36Sopenharmony_ci start = u64_stats_fetch_begin(&tx_stats->syncp); 16062306a36Sopenharmony_ci packets = tx_stats->packets; 16162306a36Sopenharmony_ci bytes = tx_stats->bytes; 16262306a36Sopenharmony_ci xdp_xmit = tx_stats->xdp_xmit; 16362306a36Sopenharmony_ci tso_packets = tx_stats->tso_packets; 16462306a36Sopenharmony_ci tso_bytes = tx_stats->tso_bytes; 16562306a36Sopenharmony_ci tso_inner_packets = tx_stats->tso_inner_packets; 16662306a36Sopenharmony_ci tso_inner_bytes = tx_stats->tso_inner_bytes; 16762306a36Sopenharmony_ci long_pkt_fmt = tx_stats->long_pkt_fmt; 16862306a36Sopenharmony_ci short_pkt_fmt = tx_stats->short_pkt_fmt; 16962306a36Sopenharmony_ci csum_partial = tx_stats->csum_partial; 17062306a36Sopenharmony_ci mana_map_err = tx_stats->mana_map_err; 17162306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci data[i++] = packets; 17462306a36Sopenharmony_ci data[i++] = bytes; 17562306a36Sopenharmony_ci data[i++] = xdp_xmit; 17662306a36Sopenharmony_ci data[i++] = tso_packets; 17762306a36Sopenharmony_ci data[i++] = tso_bytes; 17862306a36Sopenharmony_ci data[i++] = tso_inner_packets; 17962306a36Sopenharmony_ci data[i++] = tso_inner_bytes; 18062306a36Sopenharmony_ci data[i++] = long_pkt_fmt; 18162306a36Sopenharmony_ci data[i++] = short_pkt_fmt; 18262306a36Sopenharmony_ci data[i++] = csum_partial; 18362306a36Sopenharmony_ci data[i++] = mana_map_err; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int mana_get_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *cmd, 18862306a36Sopenharmony_ci u32 *rules) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct mana_port_context *apc = netdev_priv(ndev); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci switch (cmd->cmd) { 19362306a36Sopenharmony_ci case ETHTOOL_GRXRINGS: 19462306a36Sopenharmony_ci cmd->data = apc->num_queues; 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return -EOPNOTSUPP; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic u32 mana_get_rxfh_key_size(struct net_device *ndev) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci return MANA_HASH_KEY_SIZE; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic u32 mana_rss_indir_size(struct net_device *ndev) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci return MANA_INDIRECT_TABLE_SIZE; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int mana_get_rxfh(struct net_device *ndev, u32 *indir, u8 *key, 21262306a36Sopenharmony_ci u8 *hfunc) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct mana_port_context *apc = netdev_priv(ndev); 21562306a36Sopenharmony_ci int i; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (hfunc) 21862306a36Sopenharmony_ci *hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (indir) { 22162306a36Sopenharmony_ci for (i = 0; i < MANA_INDIRECT_TABLE_SIZE; i++) 22262306a36Sopenharmony_ci indir[i] = apc->indir_table[i]; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (key) 22662306a36Sopenharmony_ci memcpy(key, apc->hashkey, MANA_HASH_KEY_SIZE); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int mana_set_rxfh(struct net_device *ndev, const u32 *indir, 23262306a36Sopenharmony_ci const u8 *key, const u8 hfunc) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct mana_port_context *apc = netdev_priv(ndev); 23562306a36Sopenharmony_ci bool update_hash = false, update_table = false; 23662306a36Sopenharmony_ci u32 save_table[MANA_INDIRECT_TABLE_SIZE]; 23762306a36Sopenharmony_ci u8 save_key[MANA_HASH_KEY_SIZE]; 23862306a36Sopenharmony_ci int i, err; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!apc->port_is_up) 24162306a36Sopenharmony_ci return -EOPNOTSUPP; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) 24462306a36Sopenharmony_ci return -EOPNOTSUPP; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (indir) { 24762306a36Sopenharmony_ci for (i = 0; i < MANA_INDIRECT_TABLE_SIZE; i++) 24862306a36Sopenharmony_ci if (indir[i] >= apc->num_queues) 24962306a36Sopenharmony_ci return -EINVAL; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci update_table = true; 25262306a36Sopenharmony_ci for (i = 0; i < MANA_INDIRECT_TABLE_SIZE; i++) { 25362306a36Sopenharmony_ci save_table[i] = apc->indir_table[i]; 25462306a36Sopenharmony_ci apc->indir_table[i] = indir[i]; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (key) { 25962306a36Sopenharmony_ci update_hash = true; 26062306a36Sopenharmony_ci memcpy(save_key, apc->hashkey, MANA_HASH_KEY_SIZE); 26162306a36Sopenharmony_ci memcpy(apc->hashkey, key, MANA_HASH_KEY_SIZE); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci err = mana_config_rss(apc, TRI_STATE_TRUE, update_hash, update_table); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (err) { /* recover to original values */ 26762306a36Sopenharmony_ci if (update_table) { 26862306a36Sopenharmony_ci for (i = 0; i < MANA_INDIRECT_TABLE_SIZE; i++) 26962306a36Sopenharmony_ci apc->indir_table[i] = save_table[i]; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (update_hash) 27362306a36Sopenharmony_ci memcpy(apc->hashkey, save_key, MANA_HASH_KEY_SIZE); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci mana_config_rss(apc, TRI_STATE_TRUE, update_hash, update_table); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return err; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void mana_get_channels(struct net_device *ndev, 28262306a36Sopenharmony_ci struct ethtool_channels *channel) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct mana_port_context *apc = netdev_priv(ndev); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci channel->max_combined = apc->max_queues; 28762306a36Sopenharmony_ci channel->combined_count = apc->num_queues; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int mana_set_channels(struct net_device *ndev, 29162306a36Sopenharmony_ci struct ethtool_channels *channels) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct mana_port_context *apc = netdev_priv(ndev); 29462306a36Sopenharmony_ci unsigned int new_count = channels->combined_count; 29562306a36Sopenharmony_ci unsigned int old_count = apc->num_queues; 29662306a36Sopenharmony_ci int err, err2; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci err = mana_detach(ndev, false); 29962306a36Sopenharmony_ci if (err) { 30062306a36Sopenharmony_ci netdev_err(ndev, "mana_detach failed: %d\n", err); 30162306a36Sopenharmony_ci return err; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci apc->num_queues = new_count; 30562306a36Sopenharmony_ci err = mana_attach(ndev); 30662306a36Sopenharmony_ci if (!err) 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci netdev_err(ndev, "mana_attach failed: %d\n", err); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Try to roll it back to the old configuration. */ 31262306a36Sopenharmony_ci apc->num_queues = old_count; 31362306a36Sopenharmony_ci err2 = mana_attach(ndev); 31462306a36Sopenharmony_ci if (err2) 31562306a36Sopenharmony_ci netdev_err(ndev, "mana re-attach failed: %d\n", err2); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return err; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ciconst struct ethtool_ops mana_ethtool_ops = { 32162306a36Sopenharmony_ci .get_ethtool_stats = mana_get_ethtool_stats, 32262306a36Sopenharmony_ci .get_sset_count = mana_get_sset_count, 32362306a36Sopenharmony_ci .get_strings = mana_get_strings, 32462306a36Sopenharmony_ci .get_rxnfc = mana_get_rxnfc, 32562306a36Sopenharmony_ci .get_rxfh_key_size = mana_get_rxfh_key_size, 32662306a36Sopenharmony_ci .get_rxfh_indir_size = mana_rss_indir_size, 32762306a36Sopenharmony_ci .get_rxfh = mana_get_rxfh, 32862306a36Sopenharmony_ci .set_rxfh = mana_set_rxfh, 32962306a36Sopenharmony_ci .get_channels = mana_get_channels, 33062306a36Sopenharmony_ci .set_channels = mana_set_channels, 33162306a36Sopenharmony_ci}; 332