162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright 2013 Cisco Systems, Inc. All rights reserved. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/netdevice.h> 562306a36Sopenharmony_ci#include <linux/ethtool.h> 662306a36Sopenharmony_ci#include <linux/net_tstamp.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "enic_res.h" 962306a36Sopenharmony_ci#include "enic.h" 1062306a36Sopenharmony_ci#include "enic_dev.h" 1162306a36Sopenharmony_ci#include "enic_clsf.h" 1262306a36Sopenharmony_ci#include "vnic_rss.h" 1362306a36Sopenharmony_ci#include "vnic_stats.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct enic_stat { 1662306a36Sopenharmony_ci char name[ETH_GSTRING_LEN]; 1762306a36Sopenharmony_ci unsigned int index; 1862306a36Sopenharmony_ci}; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define ENIC_TX_STAT(stat) { \ 2162306a36Sopenharmony_ci .name = #stat, \ 2262306a36Sopenharmony_ci .index = offsetof(struct vnic_tx_stats, stat) / sizeof(u64) \ 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define ENIC_RX_STAT(stat) { \ 2662306a36Sopenharmony_ci .name = #stat, \ 2762306a36Sopenharmony_ci .index = offsetof(struct vnic_rx_stats, stat) / sizeof(u64) \ 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define ENIC_GEN_STAT(stat) { \ 3162306a36Sopenharmony_ci .name = #stat, \ 3262306a36Sopenharmony_ci .index = offsetof(struct vnic_gen_stats, stat) / sizeof(u64)\ 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic const struct enic_stat enic_tx_stats[] = { 3662306a36Sopenharmony_ci ENIC_TX_STAT(tx_frames_ok), 3762306a36Sopenharmony_ci ENIC_TX_STAT(tx_unicast_frames_ok), 3862306a36Sopenharmony_ci ENIC_TX_STAT(tx_multicast_frames_ok), 3962306a36Sopenharmony_ci ENIC_TX_STAT(tx_broadcast_frames_ok), 4062306a36Sopenharmony_ci ENIC_TX_STAT(tx_bytes_ok), 4162306a36Sopenharmony_ci ENIC_TX_STAT(tx_unicast_bytes_ok), 4262306a36Sopenharmony_ci ENIC_TX_STAT(tx_multicast_bytes_ok), 4362306a36Sopenharmony_ci ENIC_TX_STAT(tx_broadcast_bytes_ok), 4462306a36Sopenharmony_ci ENIC_TX_STAT(tx_drops), 4562306a36Sopenharmony_ci ENIC_TX_STAT(tx_errors), 4662306a36Sopenharmony_ci ENIC_TX_STAT(tx_tso), 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic const struct enic_stat enic_rx_stats[] = { 5062306a36Sopenharmony_ci ENIC_RX_STAT(rx_frames_ok), 5162306a36Sopenharmony_ci ENIC_RX_STAT(rx_frames_total), 5262306a36Sopenharmony_ci ENIC_RX_STAT(rx_unicast_frames_ok), 5362306a36Sopenharmony_ci ENIC_RX_STAT(rx_multicast_frames_ok), 5462306a36Sopenharmony_ci ENIC_RX_STAT(rx_broadcast_frames_ok), 5562306a36Sopenharmony_ci ENIC_RX_STAT(rx_bytes_ok), 5662306a36Sopenharmony_ci ENIC_RX_STAT(rx_unicast_bytes_ok), 5762306a36Sopenharmony_ci ENIC_RX_STAT(rx_multicast_bytes_ok), 5862306a36Sopenharmony_ci ENIC_RX_STAT(rx_broadcast_bytes_ok), 5962306a36Sopenharmony_ci ENIC_RX_STAT(rx_drop), 6062306a36Sopenharmony_ci ENIC_RX_STAT(rx_no_bufs), 6162306a36Sopenharmony_ci ENIC_RX_STAT(rx_errors), 6262306a36Sopenharmony_ci ENIC_RX_STAT(rx_rss), 6362306a36Sopenharmony_ci ENIC_RX_STAT(rx_crc_errors), 6462306a36Sopenharmony_ci ENIC_RX_STAT(rx_frames_64), 6562306a36Sopenharmony_ci ENIC_RX_STAT(rx_frames_127), 6662306a36Sopenharmony_ci ENIC_RX_STAT(rx_frames_255), 6762306a36Sopenharmony_ci ENIC_RX_STAT(rx_frames_511), 6862306a36Sopenharmony_ci ENIC_RX_STAT(rx_frames_1023), 6962306a36Sopenharmony_ci ENIC_RX_STAT(rx_frames_1518), 7062306a36Sopenharmony_ci ENIC_RX_STAT(rx_frames_to_max), 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct enic_stat enic_gen_stats[] = { 7462306a36Sopenharmony_ci ENIC_GEN_STAT(dma_map_error), 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats); 7862306a36Sopenharmony_cistatic const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats); 7962306a36Sopenharmony_cistatic const unsigned int enic_n_gen_stats = ARRAY_SIZE(enic_gen_stats); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void enic_intr_coal_set_rx(struct enic *enic, u32 timer) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int i; 8462306a36Sopenharmony_ci int intr; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) { 8762306a36Sopenharmony_ci intr = enic_msix_rq_intr(enic, i); 8862306a36Sopenharmony_ci vnic_intr_coalescing_timer_set(&enic->intr[intr], timer); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int enic_get_ksettings(struct net_device *netdev, 9362306a36Sopenharmony_ci struct ethtool_link_ksettings *ecmd) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 9662306a36Sopenharmony_ci struct ethtool_link_settings *base = &ecmd->base; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, 9962306a36Sopenharmony_ci 10000baseT_Full); 10062306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, FIBRE); 10162306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, advertising, 10262306a36Sopenharmony_ci 10000baseT_Full); 10362306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, advertising, FIBRE); 10462306a36Sopenharmony_ci base->port = PORT_FIBRE; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (netif_carrier_ok(netdev)) { 10762306a36Sopenharmony_ci base->speed = vnic_dev_port_speed(enic->vdev); 10862306a36Sopenharmony_ci base->duplex = DUPLEX_FULL; 10962306a36Sopenharmony_ci } else { 11062306a36Sopenharmony_ci base->speed = SPEED_UNKNOWN; 11162306a36Sopenharmony_ci base->duplex = DUPLEX_UNKNOWN; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci base->autoneg = AUTONEG_DISABLE; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void enic_get_drvinfo(struct net_device *netdev, 12062306a36Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 12362306a36Sopenharmony_ci struct vnic_devcmd_fw_info *fw_info; 12462306a36Sopenharmony_ci int err; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci err = enic_dev_fw_info(enic, &fw_info); 12762306a36Sopenharmony_ci /* return only when dma_alloc_coherent fails in vnic_dev_fw_info 12862306a36Sopenharmony_ci * For other failures, like devcmd failure, we return previously 12962306a36Sopenharmony_ci * recorded info. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci if (err == -ENOMEM) 13262306a36Sopenharmony_ci return; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); 13562306a36Sopenharmony_ci strscpy(drvinfo->fw_version, fw_info->fw_version, 13662306a36Sopenharmony_ci sizeof(drvinfo->fw_version)); 13762306a36Sopenharmony_ci strscpy(drvinfo->bus_info, pci_name(enic->pdev), 13862306a36Sopenharmony_ci sizeof(drvinfo->bus_info)); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void enic_get_strings(struct net_device *netdev, u32 stringset, 14262306a36Sopenharmony_ci u8 *data) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci unsigned int i; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci switch (stringset) { 14762306a36Sopenharmony_ci case ETH_SS_STATS: 14862306a36Sopenharmony_ci for (i = 0; i < enic_n_tx_stats; i++) { 14962306a36Sopenharmony_ci memcpy(data, enic_tx_stats[i].name, ETH_GSTRING_LEN); 15062306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci for (i = 0; i < enic_n_rx_stats; i++) { 15362306a36Sopenharmony_ci memcpy(data, enic_rx_stats[i].name, ETH_GSTRING_LEN); 15462306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci for (i = 0; i < enic_n_gen_stats; i++) { 15762306a36Sopenharmony_ci memcpy(data, enic_gen_stats[i].name, ETH_GSTRING_LEN); 15862306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void enic_get_ringparam(struct net_device *netdev, 16562306a36Sopenharmony_ci struct ethtool_ringparam *ring, 16662306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 16762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 17062306a36Sopenharmony_ci struct vnic_enet_config *c = &enic->config; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci ring->rx_max_pending = ENIC_MAX_RQ_DESCS; 17362306a36Sopenharmony_ci ring->rx_pending = c->rq_desc_count; 17462306a36Sopenharmony_ci ring->tx_max_pending = ENIC_MAX_WQ_DESCS; 17562306a36Sopenharmony_ci ring->tx_pending = c->wq_desc_count; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int enic_set_ringparam(struct net_device *netdev, 17962306a36Sopenharmony_ci struct ethtool_ringparam *ring, 18062306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 18162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 18462306a36Sopenharmony_ci struct vnic_enet_config *c = &enic->config; 18562306a36Sopenharmony_ci int running = netif_running(netdev); 18662306a36Sopenharmony_ci unsigned int rx_pending; 18762306a36Sopenharmony_ci unsigned int tx_pending; 18862306a36Sopenharmony_ci int err = 0; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (ring->rx_mini_max_pending || ring->rx_mini_pending) { 19162306a36Sopenharmony_ci netdev_info(netdev, 19262306a36Sopenharmony_ci "modifying mini ring params is not supported"); 19362306a36Sopenharmony_ci return -EINVAL; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci if (ring->rx_jumbo_max_pending || ring->rx_jumbo_pending) { 19662306a36Sopenharmony_ci netdev_info(netdev, 19762306a36Sopenharmony_ci "modifying jumbo ring params is not supported"); 19862306a36Sopenharmony_ci return -EINVAL; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci rx_pending = c->rq_desc_count; 20162306a36Sopenharmony_ci tx_pending = c->wq_desc_count; 20262306a36Sopenharmony_ci if (ring->rx_pending > ENIC_MAX_RQ_DESCS || 20362306a36Sopenharmony_ci ring->rx_pending < ENIC_MIN_RQ_DESCS) { 20462306a36Sopenharmony_ci netdev_info(netdev, "rx pending (%u) not in range [%u,%u]", 20562306a36Sopenharmony_ci ring->rx_pending, ENIC_MIN_RQ_DESCS, 20662306a36Sopenharmony_ci ENIC_MAX_RQ_DESCS); 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci if (ring->tx_pending > ENIC_MAX_WQ_DESCS || 21062306a36Sopenharmony_ci ring->tx_pending < ENIC_MIN_WQ_DESCS) { 21162306a36Sopenharmony_ci netdev_info(netdev, "tx pending (%u) not in range [%u,%u]", 21262306a36Sopenharmony_ci ring->tx_pending, ENIC_MIN_WQ_DESCS, 21362306a36Sopenharmony_ci ENIC_MAX_WQ_DESCS); 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci if (running) 21762306a36Sopenharmony_ci dev_close(netdev); 21862306a36Sopenharmony_ci c->rq_desc_count = 21962306a36Sopenharmony_ci ring->rx_pending & 0xffffffe0; /* must be aligned to groups of 32 */ 22062306a36Sopenharmony_ci c->wq_desc_count = 22162306a36Sopenharmony_ci ring->tx_pending & 0xffffffe0; /* must be aligned to groups of 32 */ 22262306a36Sopenharmony_ci enic_free_vnic_resources(enic); 22362306a36Sopenharmony_ci err = enic_alloc_vnic_resources(enic); 22462306a36Sopenharmony_ci if (err) { 22562306a36Sopenharmony_ci netdev_err(netdev, 22662306a36Sopenharmony_ci "Failed to alloc vNIC resources, aborting\n"); 22762306a36Sopenharmony_ci enic_free_vnic_resources(enic); 22862306a36Sopenharmony_ci goto err_out; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci enic_init_vnic_resources(enic); 23162306a36Sopenharmony_ci if (running) { 23262306a36Sopenharmony_ci err = dev_open(netdev, NULL); 23362306a36Sopenharmony_ci if (err) 23462306a36Sopenharmony_ci goto err_out; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_cierr_out: 23862306a36Sopenharmony_ci c->rq_desc_count = rx_pending; 23962306a36Sopenharmony_ci c->wq_desc_count = tx_pending; 24062306a36Sopenharmony_ci return err; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int enic_get_sset_count(struct net_device *netdev, int sset) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci switch (sset) { 24662306a36Sopenharmony_ci case ETH_SS_STATS: 24762306a36Sopenharmony_ci return enic_n_tx_stats + enic_n_rx_stats + enic_n_gen_stats; 24862306a36Sopenharmony_ci default: 24962306a36Sopenharmony_ci return -EOPNOTSUPP; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void enic_get_ethtool_stats(struct net_device *netdev, 25462306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 25762306a36Sopenharmony_ci struct vnic_stats *vstats; 25862306a36Sopenharmony_ci unsigned int i; 25962306a36Sopenharmony_ci int err; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci err = enic_dev_stats_dump(enic, &vstats); 26262306a36Sopenharmony_ci /* return only when dma_alloc_coherent fails in vnic_dev_stats_dump 26362306a36Sopenharmony_ci * For other failures, like devcmd failure, we return previously 26462306a36Sopenharmony_ci * recorded stats. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci if (err == -ENOMEM) 26762306a36Sopenharmony_ci return; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci for (i = 0; i < enic_n_tx_stats; i++) 27062306a36Sopenharmony_ci *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].index]; 27162306a36Sopenharmony_ci for (i = 0; i < enic_n_rx_stats; i++) 27262306a36Sopenharmony_ci *(data++) = ((u64 *)&vstats->rx)[enic_rx_stats[i].index]; 27362306a36Sopenharmony_ci for (i = 0; i < enic_n_gen_stats; i++) 27462306a36Sopenharmony_ci *(data++) = ((u64 *)&enic->gen_stats)[enic_gen_stats[i].index]; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic u32 enic_get_msglevel(struct net_device *netdev) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 28062306a36Sopenharmony_ci return enic->msg_enable; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic void enic_set_msglevel(struct net_device *netdev, u32 value) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 28662306a36Sopenharmony_ci enic->msg_enable = value; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int enic_get_coalesce(struct net_device *netdev, 29062306a36Sopenharmony_ci struct ethtool_coalesce *ecmd, 29162306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 29262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 29562306a36Sopenharmony_ci struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX) 29862306a36Sopenharmony_ci ecmd->tx_coalesce_usecs = enic->tx_coalesce_usecs; 29962306a36Sopenharmony_ci ecmd->rx_coalesce_usecs = enic->rx_coalesce_usecs; 30062306a36Sopenharmony_ci if (rxcoal->use_adaptive_rx_coalesce) 30162306a36Sopenharmony_ci ecmd->use_adaptive_rx_coalesce = 1; 30262306a36Sopenharmony_ci ecmd->rx_coalesce_usecs_low = rxcoal->small_pkt_range_start; 30362306a36Sopenharmony_ci ecmd->rx_coalesce_usecs_high = rxcoal->range_end; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int enic_coalesce_valid(struct enic *enic, 30962306a36Sopenharmony_ci struct ethtool_coalesce *ec) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci u32 coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev); 31262306a36Sopenharmony_ci u32 rx_coalesce_usecs_high = min_t(u32, coalesce_usecs_max, 31362306a36Sopenharmony_ci ec->rx_coalesce_usecs_high); 31462306a36Sopenharmony_ci u32 rx_coalesce_usecs_low = min_t(u32, coalesce_usecs_max, 31562306a36Sopenharmony_ci ec->rx_coalesce_usecs_low); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if ((vnic_dev_get_intr_mode(enic->vdev) != VNIC_DEV_INTR_MODE_MSIX) && 31862306a36Sopenharmony_ci ec->tx_coalesce_usecs) 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if ((ec->tx_coalesce_usecs > coalesce_usecs_max) || 32262306a36Sopenharmony_ci (ec->rx_coalesce_usecs > coalesce_usecs_max) || 32362306a36Sopenharmony_ci (ec->rx_coalesce_usecs_low > coalesce_usecs_max) || 32462306a36Sopenharmony_ci (ec->rx_coalesce_usecs_high > coalesce_usecs_max)) 32562306a36Sopenharmony_ci netdev_info(enic->netdev, "ethtool_set_coalesce: adaptor supports max coalesce value of %d. Setting max value.\n", 32662306a36Sopenharmony_ci coalesce_usecs_max); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (ec->rx_coalesce_usecs_high && 32962306a36Sopenharmony_ci (rx_coalesce_usecs_high < 33062306a36Sopenharmony_ci rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF)) 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int enic_set_coalesce(struct net_device *netdev, 33762306a36Sopenharmony_ci struct ethtool_coalesce *ecmd, 33862306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 33962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 34262306a36Sopenharmony_ci u32 tx_coalesce_usecs; 34362306a36Sopenharmony_ci u32 rx_coalesce_usecs; 34462306a36Sopenharmony_ci u32 rx_coalesce_usecs_low; 34562306a36Sopenharmony_ci u32 rx_coalesce_usecs_high; 34662306a36Sopenharmony_ci u32 coalesce_usecs_max; 34762306a36Sopenharmony_ci unsigned int i, intr; 34862306a36Sopenharmony_ci int ret; 34962306a36Sopenharmony_ci struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci ret = enic_coalesce_valid(enic, ecmd); 35262306a36Sopenharmony_ci if (ret) 35362306a36Sopenharmony_ci return ret; 35462306a36Sopenharmony_ci coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev); 35562306a36Sopenharmony_ci tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs, 35662306a36Sopenharmony_ci coalesce_usecs_max); 35762306a36Sopenharmony_ci rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs, 35862306a36Sopenharmony_ci coalesce_usecs_max); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci rx_coalesce_usecs_low = min_t(u32, ecmd->rx_coalesce_usecs_low, 36162306a36Sopenharmony_ci coalesce_usecs_max); 36262306a36Sopenharmony_ci rx_coalesce_usecs_high = min_t(u32, ecmd->rx_coalesce_usecs_high, 36362306a36Sopenharmony_ci coalesce_usecs_max); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX) { 36662306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) { 36762306a36Sopenharmony_ci intr = enic_msix_wq_intr(enic, i); 36862306a36Sopenharmony_ci vnic_intr_coalescing_timer_set(&enic->intr[intr], 36962306a36Sopenharmony_ci tx_coalesce_usecs); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci enic->tx_coalesce_usecs = tx_coalesce_usecs; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci rxcoal->use_adaptive_rx_coalesce = !!ecmd->use_adaptive_rx_coalesce; 37462306a36Sopenharmony_ci if (!rxcoal->use_adaptive_rx_coalesce) 37562306a36Sopenharmony_ci enic_intr_coal_set_rx(enic, rx_coalesce_usecs); 37662306a36Sopenharmony_ci if (ecmd->rx_coalesce_usecs_high) { 37762306a36Sopenharmony_ci rxcoal->range_end = rx_coalesce_usecs_high; 37862306a36Sopenharmony_ci rxcoal->small_pkt_range_start = rx_coalesce_usecs_low; 37962306a36Sopenharmony_ci rxcoal->large_pkt_range_start = rx_coalesce_usecs_low + 38062306a36Sopenharmony_ci ENIC_AIC_LARGE_PKT_DIFF; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci enic->rx_coalesce_usecs = rx_coalesce_usecs; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int enic_grxclsrlall(struct enic *enic, struct ethtool_rxnfc *cmd, 38962306a36Sopenharmony_ci u32 *rule_locs) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci int j, ret = 0, cnt = 0; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci cmd->data = enic->rfs_h.max - enic->rfs_h.free; 39462306a36Sopenharmony_ci for (j = 0; j < (1 << ENIC_RFS_FLW_BITSHIFT); j++) { 39562306a36Sopenharmony_ci struct hlist_head *hhead; 39662306a36Sopenharmony_ci struct hlist_node *tmp; 39762306a36Sopenharmony_ci struct enic_rfs_fltr_node *n; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci hhead = &enic->rfs_h.ht_head[j]; 40062306a36Sopenharmony_ci hlist_for_each_entry_safe(n, tmp, hhead, node) { 40162306a36Sopenharmony_ci if (cnt == cmd->rule_cnt) 40262306a36Sopenharmony_ci return -EMSGSIZE; 40362306a36Sopenharmony_ci rule_locs[cnt] = n->fltr_id; 40462306a36Sopenharmony_ci cnt++; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci cmd->rule_cnt = cnt; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return ret; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int enic_grxclsrule(struct enic *enic, struct ethtool_rxnfc *cmd) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct ethtool_rx_flow_spec *fsp = 41562306a36Sopenharmony_ci (struct ethtool_rx_flow_spec *)&cmd->fs; 41662306a36Sopenharmony_ci struct enic_rfs_fltr_node *n; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci n = htbl_fltr_search(enic, (u16)fsp->location); 41962306a36Sopenharmony_ci if (!n) 42062306a36Sopenharmony_ci return -EINVAL; 42162306a36Sopenharmony_ci switch (n->keys.basic.ip_proto) { 42262306a36Sopenharmony_ci case IPPROTO_TCP: 42362306a36Sopenharmony_ci fsp->flow_type = TCP_V4_FLOW; 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case IPPROTO_UDP: 42662306a36Sopenharmony_ci fsp->flow_type = UDP_V4_FLOW; 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci default: 42962306a36Sopenharmony_ci return -EINVAL; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci fsp->h_u.tcp_ip4_spec.ip4src = flow_get_u32_src(&n->keys); 43362306a36Sopenharmony_ci fsp->m_u.tcp_ip4_spec.ip4src = (__u32)~0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci fsp->h_u.tcp_ip4_spec.ip4dst = flow_get_u32_dst(&n->keys); 43662306a36Sopenharmony_ci fsp->m_u.tcp_ip4_spec.ip4dst = (__u32)~0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci fsp->h_u.tcp_ip4_spec.psrc = n->keys.ports.src; 43962306a36Sopenharmony_ci fsp->m_u.tcp_ip4_spec.psrc = (__u16)~0; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci fsp->h_u.tcp_ip4_spec.pdst = n->keys.ports.dst; 44262306a36Sopenharmony_ci fsp->m_u.tcp_ip4_spec.pdst = (__u16)~0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci fsp->ring_cookie = n->rq_id; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int enic_get_rx_flow_hash(struct enic *enic, struct ethtool_rxnfc *cmd) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci u8 rss_hash_type = 0; 45262306a36Sopenharmony_ci cmd->data = 0; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 45562306a36Sopenharmony_ci (void)vnic_dev_capable_rss_hash_type(enic->vdev, &rss_hash_type); 45662306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 45762306a36Sopenharmony_ci switch (cmd->flow_type) { 45862306a36Sopenharmony_ci case TCP_V6_FLOW: 45962306a36Sopenharmony_ci case TCP_V4_FLOW: 46062306a36Sopenharmony_ci cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3 | 46162306a36Sopenharmony_ci RXH_IP_SRC | RXH_IP_DST; 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci case UDP_V6_FLOW: 46462306a36Sopenharmony_ci cmd->data |= RXH_IP_SRC | RXH_IP_DST; 46562306a36Sopenharmony_ci if (rss_hash_type & NIC_CFG_RSS_HASH_TYPE_UDP_IPV6) 46662306a36Sopenharmony_ci cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case UDP_V4_FLOW: 46962306a36Sopenharmony_ci cmd->data |= RXH_IP_SRC | RXH_IP_DST; 47062306a36Sopenharmony_ci if (rss_hash_type & NIC_CFG_RSS_HASH_TYPE_UDP_IPV4) 47162306a36Sopenharmony_ci cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case SCTP_V4_FLOW: 47462306a36Sopenharmony_ci case AH_ESP_V4_FLOW: 47562306a36Sopenharmony_ci case AH_V4_FLOW: 47662306a36Sopenharmony_ci case ESP_V4_FLOW: 47762306a36Sopenharmony_ci case SCTP_V6_FLOW: 47862306a36Sopenharmony_ci case AH_ESP_V6_FLOW: 47962306a36Sopenharmony_ci case AH_V6_FLOW: 48062306a36Sopenharmony_ci case ESP_V6_FLOW: 48162306a36Sopenharmony_ci case IPV4_FLOW: 48262306a36Sopenharmony_ci case IPV6_FLOW: 48362306a36Sopenharmony_ci cmd->data |= RXH_IP_SRC | RXH_IP_DST; 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci default: 48662306a36Sopenharmony_ci return -EINVAL; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int enic_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 49362306a36Sopenharmony_ci u32 *rule_locs) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct enic *enic = netdev_priv(dev); 49662306a36Sopenharmony_ci int ret = 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci switch (cmd->cmd) { 49962306a36Sopenharmony_ci case ETHTOOL_GRXRINGS: 50062306a36Sopenharmony_ci cmd->data = enic->rq_count; 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLCNT: 50362306a36Sopenharmony_ci spin_lock_bh(&enic->rfs_h.lock); 50462306a36Sopenharmony_ci cmd->rule_cnt = enic->rfs_h.max - enic->rfs_h.free; 50562306a36Sopenharmony_ci cmd->data = enic->rfs_h.max; 50662306a36Sopenharmony_ci spin_unlock_bh(&enic->rfs_h.lock); 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLALL: 50962306a36Sopenharmony_ci spin_lock_bh(&enic->rfs_h.lock); 51062306a36Sopenharmony_ci ret = enic_grxclsrlall(enic, cmd, rule_locs); 51162306a36Sopenharmony_ci spin_unlock_bh(&enic->rfs_h.lock); 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci case ETHTOOL_GRXCLSRULE: 51462306a36Sopenharmony_ci spin_lock_bh(&enic->rfs_h.lock); 51562306a36Sopenharmony_ci ret = enic_grxclsrule(enic, cmd); 51662306a36Sopenharmony_ci spin_unlock_bh(&enic->rfs_h.lock); 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci case ETHTOOL_GRXFH: 51962306a36Sopenharmony_ci ret = enic_get_rx_flow_hash(enic, cmd); 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci default: 52262306a36Sopenharmony_ci ret = -EOPNOTSUPP; 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return ret; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int enic_get_tunable(struct net_device *dev, 53062306a36Sopenharmony_ci const struct ethtool_tunable *tuna, void *data) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct enic *enic = netdev_priv(dev); 53362306a36Sopenharmony_ci int ret = 0; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci switch (tuna->id) { 53662306a36Sopenharmony_ci case ETHTOOL_RX_COPYBREAK: 53762306a36Sopenharmony_ci *(u32 *)data = enic->rx_copybreak; 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci default: 54062306a36Sopenharmony_ci ret = -EINVAL; 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return ret; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int enic_set_tunable(struct net_device *dev, 54862306a36Sopenharmony_ci const struct ethtool_tunable *tuna, 54962306a36Sopenharmony_ci const void *data) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct enic *enic = netdev_priv(dev); 55262306a36Sopenharmony_ci int ret = 0; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci switch (tuna->id) { 55562306a36Sopenharmony_ci case ETHTOOL_RX_COPYBREAK: 55662306a36Sopenharmony_ci enic->rx_copybreak = *(u32 *)data; 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci default: 55962306a36Sopenharmony_ci ret = -EINVAL; 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci return ret; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic u32 enic_get_rxfh_key_size(struct net_device *netdev) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci return ENIC_RSS_LEN; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int enic_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey, 57262306a36Sopenharmony_ci u8 *hfunc) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (hkey) 57762306a36Sopenharmony_ci memcpy(hkey, enic->rss_key, ENIC_RSS_LEN); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (hfunc) 58062306a36Sopenharmony_ci *hfunc = ETH_RSS_HASH_TOP; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return 0; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int enic_set_rxfh(struct net_device *netdev, const u32 *indir, 58662306a36Sopenharmony_ci const u8 *hkey, const u8 hfunc) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if ((hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) || 59162306a36Sopenharmony_ci indir) 59262306a36Sopenharmony_ci return -EINVAL; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (hkey) 59562306a36Sopenharmony_ci memcpy(enic->rss_key, hkey, ENIC_RSS_LEN); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return __enic_set_rsskey(enic); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int enic_get_ts_info(struct net_device *netdev, 60162306a36Sopenharmony_ci struct ethtool_ts_info *info) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 60462306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 60562306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic const struct ethtool_ops enic_ethtool_ops = { 61162306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 61262306a36Sopenharmony_ci ETHTOOL_COALESCE_USE_ADAPTIVE_RX | 61362306a36Sopenharmony_ci ETHTOOL_COALESCE_RX_USECS_LOW | 61462306a36Sopenharmony_ci ETHTOOL_COALESCE_RX_USECS_HIGH, 61562306a36Sopenharmony_ci .get_drvinfo = enic_get_drvinfo, 61662306a36Sopenharmony_ci .get_msglevel = enic_get_msglevel, 61762306a36Sopenharmony_ci .set_msglevel = enic_set_msglevel, 61862306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 61962306a36Sopenharmony_ci .get_strings = enic_get_strings, 62062306a36Sopenharmony_ci .get_ringparam = enic_get_ringparam, 62162306a36Sopenharmony_ci .set_ringparam = enic_set_ringparam, 62262306a36Sopenharmony_ci .get_sset_count = enic_get_sset_count, 62362306a36Sopenharmony_ci .get_ethtool_stats = enic_get_ethtool_stats, 62462306a36Sopenharmony_ci .get_coalesce = enic_get_coalesce, 62562306a36Sopenharmony_ci .set_coalesce = enic_set_coalesce, 62662306a36Sopenharmony_ci .get_rxnfc = enic_get_rxnfc, 62762306a36Sopenharmony_ci .get_tunable = enic_get_tunable, 62862306a36Sopenharmony_ci .set_tunable = enic_set_tunable, 62962306a36Sopenharmony_ci .get_rxfh_key_size = enic_get_rxfh_key_size, 63062306a36Sopenharmony_ci .get_rxfh = enic_get_rxfh, 63162306a36Sopenharmony_ci .set_rxfh = enic_set_rxfh, 63262306a36Sopenharmony_ci .get_link_ksettings = enic_get_ksettings, 63362306a36Sopenharmony_ci .get_ts_info = enic_get_ts_info, 63462306a36Sopenharmony_ci}; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_civoid enic_set_ethtool_ops(struct net_device *netdev) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci netdev->ethtool_ops = &enic_ethtool_ops; 63962306a36Sopenharmony_ci} 640