162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Linux driver for VMware's vmxnet3 ethernet NIC. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2008-2022, VMware, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 762306a36Sopenharmony_ci * under the terms of the GNU General Public License as published by the 862306a36Sopenharmony_ci * Free Software Foundation; version 2 of the License and no later version. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 1162306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1262306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 1362306a36Sopenharmony_ci * NON INFRINGEMENT. See the GNU General Public License for more 1462306a36Sopenharmony_ci * details. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 1762306a36Sopenharmony_ci * along with this program; if not, write to the Free Software 1862306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * The full GNU General Public License is included in this distribution in 2162306a36Sopenharmony_ci * the file called "COPYING". 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Maintained by: pv-drivers@vmware.com 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "vmxnet3_int.h" 2962306a36Sopenharmony_ci#include <net/vxlan.h> 3062306a36Sopenharmony_ci#include <net/geneve.h> 3162306a36Sopenharmony_ci#include "vmxnet3_xdp.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define VXLAN_UDP_PORT 8472 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct vmxnet3_stat_desc { 3662306a36Sopenharmony_ci char desc[ETH_GSTRING_LEN]; 3762306a36Sopenharmony_ci int offset; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* per tq stats maintained by the device */ 4262306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc 4362306a36Sopenharmony_civmxnet3_tq_dev_stats[] = { 4462306a36Sopenharmony_ci /* description, offset */ 4562306a36Sopenharmony_ci { "Tx Queue#", 0 }, 4662306a36Sopenharmony_ci { " TSO pkts tx", offsetof(struct UPT1_TxStats, TSOPktsTxOK) }, 4762306a36Sopenharmony_ci { " TSO bytes tx", offsetof(struct UPT1_TxStats, TSOBytesTxOK) }, 4862306a36Sopenharmony_ci { " ucast pkts tx", offsetof(struct UPT1_TxStats, ucastPktsTxOK) }, 4962306a36Sopenharmony_ci { " ucast bytes tx", offsetof(struct UPT1_TxStats, ucastBytesTxOK) }, 5062306a36Sopenharmony_ci { " mcast pkts tx", offsetof(struct UPT1_TxStats, mcastPktsTxOK) }, 5162306a36Sopenharmony_ci { " mcast bytes tx", offsetof(struct UPT1_TxStats, mcastBytesTxOK) }, 5262306a36Sopenharmony_ci { " bcast pkts tx", offsetof(struct UPT1_TxStats, bcastPktsTxOK) }, 5362306a36Sopenharmony_ci { " bcast bytes tx", offsetof(struct UPT1_TxStats, bcastBytesTxOK) }, 5462306a36Sopenharmony_ci { " pkts tx err", offsetof(struct UPT1_TxStats, pktsTxError) }, 5562306a36Sopenharmony_ci { " pkts tx discard", offsetof(struct UPT1_TxStats, pktsTxDiscard) }, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* per tq stats maintained by the driver */ 5962306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc 6062306a36Sopenharmony_civmxnet3_tq_driver_stats[] = { 6162306a36Sopenharmony_ci /* description, offset */ 6262306a36Sopenharmony_ci {" drv dropped tx total", offsetof(struct vmxnet3_tq_driver_stats, 6362306a36Sopenharmony_ci drop_total) }, 6462306a36Sopenharmony_ci { " too many frags", offsetof(struct vmxnet3_tq_driver_stats, 6562306a36Sopenharmony_ci drop_too_many_frags) }, 6662306a36Sopenharmony_ci { " giant hdr", offsetof(struct vmxnet3_tq_driver_stats, 6762306a36Sopenharmony_ci drop_oversized_hdr) }, 6862306a36Sopenharmony_ci { " hdr err", offsetof(struct vmxnet3_tq_driver_stats, 6962306a36Sopenharmony_ci drop_hdr_inspect_err) }, 7062306a36Sopenharmony_ci { " tso", offsetof(struct vmxnet3_tq_driver_stats, 7162306a36Sopenharmony_ci drop_tso) }, 7262306a36Sopenharmony_ci { " ring full", offsetof(struct vmxnet3_tq_driver_stats, 7362306a36Sopenharmony_ci tx_ring_full) }, 7462306a36Sopenharmony_ci { " pkts linearized", offsetof(struct vmxnet3_tq_driver_stats, 7562306a36Sopenharmony_ci linearized) }, 7662306a36Sopenharmony_ci { " hdr cloned", offsetof(struct vmxnet3_tq_driver_stats, 7762306a36Sopenharmony_ci copy_skb_header) }, 7862306a36Sopenharmony_ci { " giant hdr", offsetof(struct vmxnet3_tq_driver_stats, 7962306a36Sopenharmony_ci oversized_hdr) }, 8062306a36Sopenharmony_ci { " xdp xmit", offsetof(struct vmxnet3_tq_driver_stats, 8162306a36Sopenharmony_ci xdp_xmit) }, 8262306a36Sopenharmony_ci { " xdp xmit err", offsetof(struct vmxnet3_tq_driver_stats, 8362306a36Sopenharmony_ci xdp_xmit_err) }, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* per rq stats maintained by the device */ 8762306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc 8862306a36Sopenharmony_civmxnet3_rq_dev_stats[] = { 8962306a36Sopenharmony_ci { "Rx Queue#", 0 }, 9062306a36Sopenharmony_ci { " LRO pkts rx", offsetof(struct UPT1_RxStats, LROPktsRxOK) }, 9162306a36Sopenharmony_ci { " LRO byte rx", offsetof(struct UPT1_RxStats, LROBytesRxOK) }, 9262306a36Sopenharmony_ci { " ucast pkts rx", offsetof(struct UPT1_RxStats, ucastPktsRxOK) }, 9362306a36Sopenharmony_ci { " ucast bytes rx", offsetof(struct UPT1_RxStats, ucastBytesRxOK) }, 9462306a36Sopenharmony_ci { " mcast pkts rx", offsetof(struct UPT1_RxStats, mcastPktsRxOK) }, 9562306a36Sopenharmony_ci { " mcast bytes rx", offsetof(struct UPT1_RxStats, mcastBytesRxOK) }, 9662306a36Sopenharmony_ci { " bcast pkts rx", offsetof(struct UPT1_RxStats, bcastPktsRxOK) }, 9762306a36Sopenharmony_ci { " bcast bytes rx", offsetof(struct UPT1_RxStats, bcastBytesRxOK) }, 9862306a36Sopenharmony_ci { " pkts rx OOB", offsetof(struct UPT1_RxStats, pktsRxOutOfBuf) }, 9962306a36Sopenharmony_ci { " pkts rx err", offsetof(struct UPT1_RxStats, pktsRxError) }, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* per rq stats maintained by the driver */ 10362306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc 10462306a36Sopenharmony_civmxnet3_rq_driver_stats[] = { 10562306a36Sopenharmony_ci /* description, offset */ 10662306a36Sopenharmony_ci { " drv dropped rx total", offsetof(struct vmxnet3_rq_driver_stats, 10762306a36Sopenharmony_ci drop_total) }, 10862306a36Sopenharmony_ci { " err", offsetof(struct vmxnet3_rq_driver_stats, 10962306a36Sopenharmony_ci drop_err) }, 11062306a36Sopenharmony_ci { " fcs", offsetof(struct vmxnet3_rq_driver_stats, 11162306a36Sopenharmony_ci drop_fcs) }, 11262306a36Sopenharmony_ci { " rx buf alloc fail", offsetof(struct vmxnet3_rq_driver_stats, 11362306a36Sopenharmony_ci rx_buf_alloc_failure) }, 11462306a36Sopenharmony_ci { " xdp packets", offsetof(struct vmxnet3_rq_driver_stats, 11562306a36Sopenharmony_ci xdp_packets) }, 11662306a36Sopenharmony_ci { " xdp tx", offsetof(struct vmxnet3_rq_driver_stats, 11762306a36Sopenharmony_ci xdp_tx) }, 11862306a36Sopenharmony_ci { " xdp redirects", offsetof(struct vmxnet3_rq_driver_stats, 11962306a36Sopenharmony_ci xdp_redirects) }, 12062306a36Sopenharmony_ci { " xdp drops", offsetof(struct vmxnet3_rq_driver_stats, 12162306a36Sopenharmony_ci xdp_drops) }, 12262306a36Sopenharmony_ci { " xdp aborted", offsetof(struct vmxnet3_rq_driver_stats, 12362306a36Sopenharmony_ci xdp_aborted) }, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* global stats maintained by the driver */ 12762306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc 12862306a36Sopenharmony_civmxnet3_global_stats[] = { 12962306a36Sopenharmony_ci /* description, offset */ 13062306a36Sopenharmony_ci { "tx timeout count", offsetof(struct vmxnet3_adapter, 13162306a36Sopenharmony_ci tx_timeout_count) } 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid 13662306a36Sopenharmony_civmxnet3_get_stats64(struct net_device *netdev, 13762306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct vmxnet3_adapter *adapter; 14062306a36Sopenharmony_ci struct vmxnet3_tq_driver_stats *drvTxStats; 14162306a36Sopenharmony_ci struct vmxnet3_rq_driver_stats *drvRxStats; 14262306a36Sopenharmony_ci struct UPT1_TxStats *devTxStats; 14362306a36Sopenharmony_ci struct UPT1_RxStats *devRxStats; 14462306a36Sopenharmony_ci unsigned long flags; 14562306a36Sopenharmony_ci int i; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci adapter = netdev_priv(netdev); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Collect the dev stats into the shared area */ 15062306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 15162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS); 15262306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 15562306a36Sopenharmony_ci devTxStats = &adapter->tqd_start[i].stats; 15662306a36Sopenharmony_ci drvTxStats = &adapter->tx_queue[i].stats; 15762306a36Sopenharmony_ci stats->tx_packets += devTxStats->ucastPktsTxOK + 15862306a36Sopenharmony_ci devTxStats->mcastPktsTxOK + 15962306a36Sopenharmony_ci devTxStats->bcastPktsTxOK; 16062306a36Sopenharmony_ci stats->tx_bytes += devTxStats->ucastBytesTxOK + 16162306a36Sopenharmony_ci devTxStats->mcastBytesTxOK + 16262306a36Sopenharmony_ci devTxStats->bcastBytesTxOK; 16362306a36Sopenharmony_ci stats->tx_errors += devTxStats->pktsTxError; 16462306a36Sopenharmony_ci stats->tx_dropped += drvTxStats->drop_total; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 16862306a36Sopenharmony_ci devRxStats = &adapter->rqd_start[i].stats; 16962306a36Sopenharmony_ci drvRxStats = &adapter->rx_queue[i].stats; 17062306a36Sopenharmony_ci stats->rx_packets += devRxStats->ucastPktsRxOK + 17162306a36Sopenharmony_ci devRxStats->mcastPktsRxOK + 17262306a36Sopenharmony_ci devRxStats->bcastPktsRxOK; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci stats->rx_bytes += devRxStats->ucastBytesRxOK + 17562306a36Sopenharmony_ci devRxStats->mcastBytesRxOK + 17662306a36Sopenharmony_ci devRxStats->bcastBytesRxOK; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci stats->rx_errors += devRxStats->pktsRxError; 17962306a36Sopenharmony_ci stats->rx_dropped += drvRxStats->drop_total; 18062306a36Sopenharmony_ci stats->multicast += devRxStats->mcastPktsRxOK; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int 18562306a36Sopenharmony_civmxnet3_get_sset_count(struct net_device *netdev, int sset) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 18862306a36Sopenharmony_ci switch (sset) { 18962306a36Sopenharmony_ci case ETH_SS_STATS: 19062306a36Sopenharmony_ci return (ARRAY_SIZE(vmxnet3_tq_dev_stats) + 19162306a36Sopenharmony_ci ARRAY_SIZE(vmxnet3_tq_driver_stats)) * 19262306a36Sopenharmony_ci adapter->num_tx_queues + 19362306a36Sopenharmony_ci (ARRAY_SIZE(vmxnet3_rq_dev_stats) + 19462306a36Sopenharmony_ci ARRAY_SIZE(vmxnet3_rq_driver_stats)) * 19562306a36Sopenharmony_ci adapter->num_rx_queues + 19662306a36Sopenharmony_ci ARRAY_SIZE(vmxnet3_global_stats); 19762306a36Sopenharmony_ci default: 19862306a36Sopenharmony_ci return -EOPNOTSUPP; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with 20462306a36Sopenharmony_ci * the version 2 of the vmxnet3 support for ethtool(8) --register-dump. 20562306a36Sopenharmony_ci * Therefore, if any registers are added, removed or modified, then a version 20662306a36Sopenharmony_ci * bump and a corresponding change in the vmxnet3 support for ethtool(8) 20762306a36Sopenharmony_ci * --register-dump would be required. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_cistatic int 21062306a36Sopenharmony_civmxnet3_get_regs_len(struct net_device *netdev) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return ((9 /* BAR1 registers */ + 21562306a36Sopenharmony_ci (1 + adapter->intr.num_intrs) + 21662306a36Sopenharmony_ci (1 + adapter->num_tx_queues * 17 /* Tx queue registers */) + 21762306a36Sopenharmony_ci (1 + adapter->num_rx_queues * 23 /* Rx queue registers */)) * 21862306a36Sopenharmony_ci sizeof(u32)); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void 22362306a36Sopenharmony_civmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci strscpy(drvinfo->driver, vmxnet3_driver_name, sizeof(drvinfo->driver)); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci strscpy(drvinfo->version, VMXNET3_DRIVER_VERSION_REPORT, 23062306a36Sopenharmony_ci sizeof(drvinfo->version)); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci strscpy(drvinfo->bus_info, pci_name(adapter->pdev), 23362306a36Sopenharmony_ci sizeof(drvinfo->bus_info)); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void 23862306a36Sopenharmony_civmxnet3_get_strings(struct net_device *netdev, u32 stringset, u8 *buf) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 24162306a36Sopenharmony_ci int i, j; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (stringset != ETH_SS_STATS) 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci for (j = 0; j < adapter->num_tx_queues; j++) { 24762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++) 24862306a36Sopenharmony_ci ethtool_sprintf(&buf, vmxnet3_tq_dev_stats[i].desc); 24962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++) 25062306a36Sopenharmony_ci ethtool_sprintf(&buf, vmxnet3_tq_driver_stats[i].desc); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (j = 0; j < adapter->num_rx_queues; j++) { 25462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) 25562306a36Sopenharmony_ci ethtool_sprintf(&buf, vmxnet3_rq_dev_stats[i].desc); 25662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++) 25762306a36Sopenharmony_ci ethtool_sprintf(&buf, vmxnet3_rq_driver_stats[i].desc); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++) 26162306a36Sopenharmony_ci ethtool_sprintf(&buf, vmxnet3_global_stats[i].desc); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cinetdev_features_t vmxnet3_fix_features(struct net_device *netdev, 26562306a36Sopenharmony_ci netdev_features_t features) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* If Rx checksum is disabled, then LRO should also be disabled */ 27062306a36Sopenharmony_ci if (!(features & NETIF_F_RXCSUM)) 27162306a36Sopenharmony_ci features &= ~NETIF_F_LRO; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* If XDP is enabled, then LRO should not be enabled */ 27462306a36Sopenharmony_ci if (vmxnet3_xdp_enabled(adapter) && (features & NETIF_F_LRO)) { 27562306a36Sopenharmony_ci netdev_err(netdev, "LRO is not supported with XDP"); 27662306a36Sopenharmony_ci features &= ~NETIF_F_LRO; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return features; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cinetdev_features_t vmxnet3_features_check(struct sk_buff *skb, 28362306a36Sopenharmony_ci struct net_device *netdev, 28462306a36Sopenharmony_ci netdev_features_t features) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Validate if the tunneled packet is being offloaded by the device */ 28962306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_4(adapter) && 29062306a36Sopenharmony_ci skb->encapsulation && skb->ip_summed == CHECKSUM_PARTIAL) { 29162306a36Sopenharmony_ci u8 l4_proto = 0; 29262306a36Sopenharmony_ci u16 port; 29362306a36Sopenharmony_ci struct udphdr *udph; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci switch (vlan_get_protocol(skb)) { 29662306a36Sopenharmony_ci case htons(ETH_P_IP): 29762306a36Sopenharmony_ci l4_proto = ip_hdr(skb)->protocol; 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci case htons(ETH_P_IPV6): 30062306a36Sopenharmony_ci l4_proto = ipv6_hdr(skb)->nexthdr; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci default: 30362306a36Sopenharmony_ci return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci switch (l4_proto) { 30762306a36Sopenharmony_ci case IPPROTO_UDP: 30862306a36Sopenharmony_ci udph = udp_hdr(skb); 30962306a36Sopenharmony_ci port = be16_to_cpu(udph->dest); 31062306a36Sopenharmony_ci /* Check if offloaded port is supported */ 31162306a36Sopenharmony_ci if (port != GENEVE_UDP_PORT && 31262306a36Sopenharmony_ci port != IANA_VXLAN_UDP_PORT && 31362306a36Sopenharmony_ci port != VXLAN_UDP_PORT) { 31462306a36Sopenharmony_ci return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci default: 31862306a36Sopenharmony_ci return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci return features; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void vmxnet3_enable_encap_offloads(struct net_device *netdev, netdev_features_t features) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_4(adapter)) { 32962306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_SG | NETIF_F_RXCSUM | 33062306a36Sopenharmony_ci NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX | 33162306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_TSO | NETIF_F_TSO6 | 33262306a36Sopenharmony_ci NETIF_F_LRO; 33362306a36Sopenharmony_ci if (features & NETIF_F_GSO_UDP_TUNNEL) 33462306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL; 33562306a36Sopenharmony_ci if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM) 33662306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 33962306a36Sopenharmony_ci unsigned long flags; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 34262306a36Sopenharmony_ci VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD)) { 34362306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 34662306a36Sopenharmony_ci VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD)) { 34762306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 35062306a36Sopenharmony_ci VMXNET3_CAP_GENEVE_TSO)) { 35162306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_GENEVE_TSO; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 35462306a36Sopenharmony_ci VMXNET3_CAP_VXLAN_TSO)) { 35562306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_VXLAN_TSO; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 35862306a36Sopenharmony_ci VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD)) { 35962306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 36262306a36Sopenharmony_ci VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD)) { 36362306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DCR, adapter->dev_caps[0]); 36762306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 36862306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_DCR0_REG); 36962306a36Sopenharmony_ci adapter->dev_caps[0] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 37062306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD)) && 37362306a36Sopenharmony_ci !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD)) && 37462306a36Sopenharmony_ci !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_GENEVE_TSO)) && 37562306a36Sopenharmony_ci !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_VXLAN_TSO))) { 37662306a36Sopenharmony_ci netdev->hw_enc_features &= ~NETIF_F_GSO_UDP_TUNNEL; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci if (!(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD)) && 37962306a36Sopenharmony_ci !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD))) { 38062306a36Sopenharmony_ci netdev->hw_enc_features &= ~NETIF_F_GSO_UDP_TUNNEL_CSUM; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void vmxnet3_disable_encap_offloads(struct net_device *netdev) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_4(adapter)) { 39062306a36Sopenharmony_ci netdev->hw_enc_features &= ~(NETIF_F_SG | NETIF_F_RXCSUM | 39162306a36Sopenharmony_ci NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX | 39262306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_TSO | NETIF_F_TSO6 | 39362306a36Sopenharmony_ci NETIF_F_LRO | NETIF_F_GSO_UDP_TUNNEL | 39462306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL_CSUM); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 39762306a36Sopenharmony_ci unsigned long flags; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD | 40062306a36Sopenharmony_ci 1UL << VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD | 40162306a36Sopenharmony_ci 1UL << VMXNET3_CAP_GENEVE_TSO | 40262306a36Sopenharmony_ci 1UL << VMXNET3_CAP_VXLAN_TSO | 40362306a36Sopenharmony_ci 1UL << VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD | 40462306a36Sopenharmony_ci 1UL << VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DCR, adapter->dev_caps[0]); 40762306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 40862306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_DCR0_REG); 40962306a36Sopenharmony_ci adapter->dev_caps[0] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 41062306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciint vmxnet3_set_features(struct net_device *netdev, netdev_features_t features) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 41762306a36Sopenharmony_ci unsigned long flags; 41862306a36Sopenharmony_ci netdev_features_t changed = features ^ netdev->features; 41962306a36Sopenharmony_ci netdev_features_t tun_offload_mask = NETIF_F_GSO_UDP_TUNNEL | 42062306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL_CSUM; 42162306a36Sopenharmony_ci u8 udp_tun_enabled = (netdev->features & tun_offload_mask) != 0; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (changed & (NETIF_F_RXCSUM | NETIF_F_LRO | 42462306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX | tun_offload_mask)) { 42562306a36Sopenharmony_ci if (features & NETIF_F_RXCSUM) 42662306a36Sopenharmony_ci adapter->shared->devRead.misc.uptFeatures |= 42762306a36Sopenharmony_ci UPT1_F_RXCSUM; 42862306a36Sopenharmony_ci else 42962306a36Sopenharmony_ci adapter->shared->devRead.misc.uptFeatures &= 43062306a36Sopenharmony_ci ~UPT1_F_RXCSUM; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* update hardware LRO capability accordingly */ 43362306a36Sopenharmony_ci if (features & NETIF_F_LRO) 43462306a36Sopenharmony_ci adapter->shared->devRead.misc.uptFeatures |= 43562306a36Sopenharmony_ci UPT1_F_LRO; 43662306a36Sopenharmony_ci else 43762306a36Sopenharmony_ci adapter->shared->devRead.misc.uptFeatures &= 43862306a36Sopenharmony_ci ~UPT1_F_LRO; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 44162306a36Sopenharmony_ci adapter->shared->devRead.misc.uptFeatures |= 44262306a36Sopenharmony_ci UPT1_F_RXVLAN; 44362306a36Sopenharmony_ci else 44462306a36Sopenharmony_ci adapter->shared->devRead.misc.uptFeatures &= 44562306a36Sopenharmony_ci ~UPT1_F_RXVLAN; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if ((features & tun_offload_mask) != 0) { 44862306a36Sopenharmony_ci vmxnet3_enable_encap_offloads(netdev, features); 44962306a36Sopenharmony_ci adapter->shared->devRead.misc.uptFeatures |= 45062306a36Sopenharmony_ci UPT1_F_RXINNEROFLD; 45162306a36Sopenharmony_ci } else if ((features & tun_offload_mask) == 0 && 45262306a36Sopenharmony_ci udp_tun_enabled) { 45362306a36Sopenharmony_ci vmxnet3_disable_encap_offloads(netdev); 45462306a36Sopenharmony_ci adapter->shared->devRead.misc.uptFeatures &= 45562306a36Sopenharmony_ci ~UPT1_F_RXINNEROFLD; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 45962306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 46062306a36Sopenharmony_ci VMXNET3_CMD_UPDATE_FEATURE); 46162306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic void 46762306a36Sopenharmony_civmxnet3_get_ethtool_stats(struct net_device *netdev, 46862306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *buf) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 47162306a36Sopenharmony_ci unsigned long flags; 47262306a36Sopenharmony_ci u8 *base; 47362306a36Sopenharmony_ci int i; 47462306a36Sopenharmony_ci int j = 0; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 47762306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS); 47862306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* this does assume each counter is 64-bit wide */ 48162306a36Sopenharmony_ci for (j = 0; j < adapter->num_tx_queues; j++) { 48262306a36Sopenharmony_ci base = (u8 *)&adapter->tqd_start[j].stats; 48362306a36Sopenharmony_ci *buf++ = (u64)j; 48462306a36Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++) 48562306a36Sopenharmony_ci *buf++ = *(u64 *)(base + 48662306a36Sopenharmony_ci vmxnet3_tq_dev_stats[i].offset); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci base = (u8 *)&adapter->tx_queue[j].stats; 48962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++) 49062306a36Sopenharmony_ci *buf++ = *(u64 *)(base + 49162306a36Sopenharmony_ci vmxnet3_tq_driver_stats[i].offset); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci for (j = 0; j < adapter->num_rx_queues; j++) { 49562306a36Sopenharmony_ci base = (u8 *)&adapter->rqd_start[j].stats; 49662306a36Sopenharmony_ci *buf++ = (u64) j; 49762306a36Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) 49862306a36Sopenharmony_ci *buf++ = *(u64 *)(base + 49962306a36Sopenharmony_ci vmxnet3_rq_dev_stats[i].offset); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci base = (u8 *)&adapter->rx_queue[j].stats; 50262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++) 50362306a36Sopenharmony_ci *buf++ = *(u64 *)(base + 50462306a36Sopenharmony_ci vmxnet3_rq_driver_stats[i].offset); 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci base = (u8 *)adapter; 50862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++) 50962306a36Sopenharmony_ci *buf++ = *(u64 *)(base + vmxnet3_global_stats[i].offset); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with 51462306a36Sopenharmony_ci * the version 2 of the vmxnet3 support for ethtool(8) --register-dump. 51562306a36Sopenharmony_ci * Therefore, if any registers are added, removed or modified, then a version 51662306a36Sopenharmony_ci * bump and a corresponding change in the vmxnet3 support for ethtool(8) 51762306a36Sopenharmony_ci * --register-dump would be required. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_cistatic void 52062306a36Sopenharmony_civmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 52362306a36Sopenharmony_ci u32 *buf = p; 52462306a36Sopenharmony_ci int i = 0, j = 0; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci memset(p, 0, vmxnet3_get_regs_len(netdev)); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci regs->version = 2; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Update vmxnet3_get_regs_len if we want to dump more registers */ 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS); 53362306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS); 53462306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAL); 53562306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAH); 53662306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 53762306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACL); 53862306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACH); 53962306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ICR); 54062306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ECR); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci buf[j++] = adapter->intr.num_intrs; 54362306a36Sopenharmony_ci for (i = 0; i < adapter->intr.num_intrs; i++) { 54462306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_IMR 54562306a36Sopenharmony_ci + i * VMXNET3_REG_ALIGN); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci buf[j++] = adapter->num_tx_queues; 54962306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 55062306a36Sopenharmony_ci struct vmxnet3_tx_queue *tq = &adapter->tx_queue[i]; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR0_REG(adapter, adapter->tx_prod_offset + 55362306a36Sopenharmony_ci i * VMXNET3_REG_ALIGN); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_LO(tq->tx_ring.basePA); 55662306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_HI(tq->tx_ring.basePA); 55762306a36Sopenharmony_ci buf[j++] = tq->tx_ring.size; 55862306a36Sopenharmony_ci buf[j++] = tq->tx_ring.next2fill; 55962306a36Sopenharmony_ci buf[j++] = tq->tx_ring.next2comp; 56062306a36Sopenharmony_ci buf[j++] = tq->tx_ring.gen; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_LO(tq->data_ring.basePA); 56362306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_HI(tq->data_ring.basePA); 56462306a36Sopenharmony_ci buf[j++] = tq->data_ring.size; 56562306a36Sopenharmony_ci buf[j++] = tq->txdata_desc_size; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_LO(tq->comp_ring.basePA); 56862306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_HI(tq->comp_ring.basePA); 56962306a36Sopenharmony_ci buf[j++] = tq->comp_ring.size; 57062306a36Sopenharmony_ci buf[j++] = tq->comp_ring.next2proc; 57162306a36Sopenharmony_ci buf[j++] = tq->comp_ring.gen; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci buf[j++] = tq->stopped; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci buf[j++] = adapter->num_rx_queues; 57762306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 57862306a36Sopenharmony_ci struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR0_REG(adapter, adapter->rx_prod_offset + 58162306a36Sopenharmony_ci i * VMXNET3_REG_ALIGN); 58262306a36Sopenharmony_ci buf[j++] = VMXNET3_READ_BAR0_REG(adapter, adapter->rx_prod2_offset + 58362306a36Sopenharmony_ci i * VMXNET3_REG_ALIGN); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[0].basePA); 58662306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[0].basePA); 58762306a36Sopenharmony_ci buf[j++] = rq->rx_ring[0].size; 58862306a36Sopenharmony_ci buf[j++] = rq->rx_ring[0].next2fill; 58962306a36Sopenharmony_ci buf[j++] = rq->rx_ring[0].next2comp; 59062306a36Sopenharmony_ci buf[j++] = rq->rx_ring[0].gen; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[1].basePA); 59362306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[1].basePA); 59462306a36Sopenharmony_ci buf[j++] = rq->rx_ring[1].size; 59562306a36Sopenharmony_ci buf[j++] = rq->rx_ring[1].next2fill; 59662306a36Sopenharmony_ci buf[j++] = rq->rx_ring[1].next2comp; 59762306a36Sopenharmony_ci buf[j++] = rq->rx_ring[1].gen; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_LO(rq->data_ring.basePA); 60062306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_HI(rq->data_ring.basePA); 60162306a36Sopenharmony_ci buf[j++] = rq->rx_ring[0].size; 60262306a36Sopenharmony_ci buf[j++] = rq->data_ring.desc_size; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_LO(rq->comp_ring.basePA); 60562306a36Sopenharmony_ci buf[j++] = VMXNET3_GET_ADDR_HI(rq->comp_ring.basePA); 60662306a36Sopenharmony_ci buf[j++] = rq->comp_ring.size; 60762306a36Sopenharmony_ci buf[j++] = rq->comp_ring.next2proc; 60862306a36Sopenharmony_ci buf[j++] = rq->comp_ring.gen; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void 61462306a36Sopenharmony_civmxnet3_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci wol->supported = WAKE_UCAST | WAKE_ARP | WAKE_MAGIC; 61962306a36Sopenharmony_ci wol->wolopts = adapter->wol; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int 62462306a36Sopenharmony_civmxnet3_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (wol->wolopts & (WAKE_PHY | WAKE_MCAST | WAKE_BCAST | 62962306a36Sopenharmony_ci WAKE_MAGICSECURE)) { 63062306a36Sopenharmony_ci return -EOPNOTSUPP; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci adapter->wol = wol->wolopts; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int 64262306a36Sopenharmony_civmxnet3_get_link_ksettings(struct net_device *netdev, 64362306a36Sopenharmony_ci struct ethtool_link_ksettings *ecmd) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ecmd, supported); 64862306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, 10000baseT_Full); 64962306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, 1000baseT_Full); 65062306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, TP); 65162306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ecmd, advertising); 65262306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, advertising, TP); 65362306a36Sopenharmony_ci ecmd->base.port = PORT_TP; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (adapter->link_speed) { 65662306a36Sopenharmony_ci ecmd->base.speed = adapter->link_speed; 65762306a36Sopenharmony_ci ecmd->base.duplex = DUPLEX_FULL; 65862306a36Sopenharmony_ci } else { 65962306a36Sopenharmony_ci ecmd->base.speed = SPEED_UNKNOWN; 66062306a36Sopenharmony_ci ecmd->base.duplex = DUPLEX_UNKNOWN; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic void 66662306a36Sopenharmony_civmxnet3_get_ringparam(struct net_device *netdev, 66762306a36Sopenharmony_ci struct ethtool_ringparam *param, 66862306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_param, 66962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci param->rx_max_pending = VMXNET3_RX_RING_MAX_SIZE; 67462306a36Sopenharmony_ci param->tx_max_pending = VMXNET3_TX_RING_MAX_SIZE; 67562306a36Sopenharmony_ci param->rx_mini_max_pending = VMXNET3_VERSION_GE_3(adapter) ? 67662306a36Sopenharmony_ci VMXNET3_RXDATA_DESC_MAX_SIZE : 0; 67762306a36Sopenharmony_ci param->rx_jumbo_max_pending = VMXNET3_RX_RING2_MAX_SIZE; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci param->rx_pending = adapter->rx_ring_size; 68062306a36Sopenharmony_ci param->tx_pending = adapter->tx_ring_size; 68162306a36Sopenharmony_ci param->rx_mini_pending = VMXNET3_VERSION_GE_3(adapter) ? 68262306a36Sopenharmony_ci adapter->rxdata_desc_size : 0; 68362306a36Sopenharmony_ci param->rx_jumbo_pending = adapter->rx_ring2_size; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic int 68762306a36Sopenharmony_civmxnet3_set_ringparam(struct net_device *netdev, 68862306a36Sopenharmony_ci struct ethtool_ringparam *param, 68962306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_param, 69062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 69362306a36Sopenharmony_ci u32 new_tx_ring_size, new_rx_ring_size, new_rx_ring2_size; 69462306a36Sopenharmony_ci u16 new_rxdata_desc_size; 69562306a36Sopenharmony_ci u32 sz; 69662306a36Sopenharmony_ci int err = 0; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (param->tx_pending == 0 || param->tx_pending > 69962306a36Sopenharmony_ci VMXNET3_TX_RING_MAX_SIZE) 70062306a36Sopenharmony_ci return -EINVAL; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (param->rx_pending == 0 || param->rx_pending > 70362306a36Sopenharmony_ci VMXNET3_RX_RING_MAX_SIZE) 70462306a36Sopenharmony_ci return -EINVAL; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (param->rx_jumbo_pending == 0 || 70762306a36Sopenharmony_ci param->rx_jumbo_pending > VMXNET3_RX_RING2_MAX_SIZE) 70862306a36Sopenharmony_ci return -EINVAL; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* if adapter not yet initialized, do nothing */ 71162306a36Sopenharmony_ci if (adapter->rx_buf_per_pkt == 0) { 71262306a36Sopenharmony_ci netdev_err(netdev, "adapter not completely initialized, " 71362306a36Sopenharmony_ci "ring size cannot be changed yet\n"); 71462306a36Sopenharmony_ci return -EOPNOTSUPP; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_3(adapter)) { 71862306a36Sopenharmony_ci if (param->rx_mini_pending > VMXNET3_RXDATA_DESC_MAX_SIZE) 71962306a36Sopenharmony_ci return -EINVAL; 72062306a36Sopenharmony_ci } else if (param->rx_mini_pending != 0) { 72162306a36Sopenharmony_ci return -EINVAL; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* round it up to a multiple of VMXNET3_RING_SIZE_ALIGN */ 72562306a36Sopenharmony_ci new_tx_ring_size = (param->tx_pending + VMXNET3_RING_SIZE_MASK) & 72662306a36Sopenharmony_ci ~VMXNET3_RING_SIZE_MASK; 72762306a36Sopenharmony_ci new_tx_ring_size = min_t(u32, new_tx_ring_size, 72862306a36Sopenharmony_ci VMXNET3_TX_RING_MAX_SIZE); 72962306a36Sopenharmony_ci if (new_tx_ring_size > VMXNET3_TX_RING_MAX_SIZE || (new_tx_ring_size % 73062306a36Sopenharmony_ci VMXNET3_RING_SIZE_ALIGN) != 0) 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* ring0 has to be a multiple of 73462306a36Sopenharmony_ci * rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_ci sz = adapter->rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN; 73762306a36Sopenharmony_ci new_rx_ring_size = (param->rx_pending + sz - 1) / sz * sz; 73862306a36Sopenharmony_ci new_rx_ring_size = min_t(u32, new_rx_ring_size, 73962306a36Sopenharmony_ci VMXNET3_RX_RING_MAX_SIZE / sz * sz); 74062306a36Sopenharmony_ci if (new_rx_ring_size > VMXNET3_RX_RING_MAX_SIZE || (new_rx_ring_size % 74162306a36Sopenharmony_ci sz) != 0) 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* ring2 has to be a multiple of VMXNET3_RING_SIZE_ALIGN */ 74562306a36Sopenharmony_ci new_rx_ring2_size = (param->rx_jumbo_pending + VMXNET3_RING_SIZE_MASK) & 74662306a36Sopenharmony_ci ~VMXNET3_RING_SIZE_MASK; 74762306a36Sopenharmony_ci new_rx_ring2_size = min_t(u32, new_rx_ring2_size, 74862306a36Sopenharmony_ci VMXNET3_RX_RING2_MAX_SIZE); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* For v7 and later, keep ring size power of 2 for UPT */ 75162306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 75262306a36Sopenharmony_ci new_tx_ring_size = rounddown_pow_of_two(new_tx_ring_size); 75362306a36Sopenharmony_ci new_rx_ring_size = rounddown_pow_of_two(new_rx_ring_size); 75462306a36Sopenharmony_ci new_rx_ring2_size = rounddown_pow_of_two(new_rx_ring2_size); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* rx data ring buffer size has to be a multiple of 75862306a36Sopenharmony_ci * VMXNET3_RXDATA_DESC_SIZE_ALIGN 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_ci new_rxdata_desc_size = 76162306a36Sopenharmony_ci (param->rx_mini_pending + VMXNET3_RXDATA_DESC_SIZE_MASK) & 76262306a36Sopenharmony_ci ~VMXNET3_RXDATA_DESC_SIZE_MASK; 76362306a36Sopenharmony_ci new_rxdata_desc_size = min_t(u16, new_rxdata_desc_size, 76462306a36Sopenharmony_ci VMXNET3_RXDATA_DESC_MAX_SIZE); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (new_tx_ring_size == adapter->tx_ring_size && 76762306a36Sopenharmony_ci new_rx_ring_size == adapter->rx_ring_size && 76862306a36Sopenharmony_ci new_rx_ring2_size == adapter->rx_ring2_size && 76962306a36Sopenharmony_ci new_rxdata_desc_size == adapter->rxdata_desc_size) { 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* 77462306a36Sopenharmony_ci * Reset_work may be in the middle of resetting the device, wait for its 77562306a36Sopenharmony_ci * completion. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_ci while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) 77862306a36Sopenharmony_ci usleep_range(1000, 2000); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (netif_running(netdev)) { 78162306a36Sopenharmony_ci vmxnet3_quiesce_dev(adapter); 78262306a36Sopenharmony_ci vmxnet3_reset_dev(adapter); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* recreate the rx queue and the tx queue based on the 78562306a36Sopenharmony_ci * new sizes */ 78662306a36Sopenharmony_ci vmxnet3_tq_destroy_all(adapter); 78762306a36Sopenharmony_ci vmxnet3_rq_destroy_all(adapter); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci err = vmxnet3_create_queues(adapter, new_tx_ring_size, 79062306a36Sopenharmony_ci new_rx_ring_size, new_rx_ring2_size, 79162306a36Sopenharmony_ci adapter->txdata_desc_size, 79262306a36Sopenharmony_ci new_rxdata_desc_size); 79362306a36Sopenharmony_ci if (err) { 79462306a36Sopenharmony_ci /* failed, most likely because of OOM, try default 79562306a36Sopenharmony_ci * size */ 79662306a36Sopenharmony_ci netdev_err(netdev, "failed to apply new sizes, " 79762306a36Sopenharmony_ci "try the default ones\n"); 79862306a36Sopenharmony_ci new_rx_ring_size = VMXNET3_DEF_RX_RING_SIZE; 79962306a36Sopenharmony_ci new_rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE; 80062306a36Sopenharmony_ci new_tx_ring_size = VMXNET3_DEF_TX_RING_SIZE; 80162306a36Sopenharmony_ci new_rxdata_desc_size = VMXNET3_VERSION_GE_3(adapter) ? 80262306a36Sopenharmony_ci VMXNET3_DEF_RXDATA_DESC_SIZE : 0; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci err = vmxnet3_create_queues(adapter, 80562306a36Sopenharmony_ci new_tx_ring_size, 80662306a36Sopenharmony_ci new_rx_ring_size, 80762306a36Sopenharmony_ci new_rx_ring2_size, 80862306a36Sopenharmony_ci adapter->txdata_desc_size, 80962306a36Sopenharmony_ci new_rxdata_desc_size); 81062306a36Sopenharmony_ci if (err) { 81162306a36Sopenharmony_ci netdev_err(netdev, "failed to create queues " 81262306a36Sopenharmony_ci "with default sizes. Closing it\n"); 81362306a36Sopenharmony_ci goto out; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci err = vmxnet3_activate_dev(adapter); 81862306a36Sopenharmony_ci if (err) 81962306a36Sopenharmony_ci netdev_err(netdev, "failed to re-activate, error %d." 82062306a36Sopenharmony_ci " Closing it\n", err); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci adapter->tx_ring_size = new_tx_ring_size; 82362306a36Sopenharmony_ci adapter->rx_ring_size = new_rx_ring_size; 82462306a36Sopenharmony_ci adapter->rx_ring2_size = new_rx_ring2_size; 82562306a36Sopenharmony_ci adapter->rxdata_desc_size = new_rxdata_desc_size; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ciout: 82862306a36Sopenharmony_ci clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); 82962306a36Sopenharmony_ci if (err) 83062306a36Sopenharmony_ci vmxnet3_force_close(adapter); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return err; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic int 83662306a36Sopenharmony_civmxnet3_get_rss_hash_opts(struct vmxnet3_adapter *adapter, 83762306a36Sopenharmony_ci struct ethtool_rxnfc *info) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci enum Vmxnet3_RSSField rss_fields; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (netif_running(adapter->netdev)) { 84262306a36Sopenharmony_ci unsigned long flags; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 84762306a36Sopenharmony_ci VMXNET3_CMD_GET_RSS_FIELDS); 84862306a36Sopenharmony_ci rss_fields = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 84962306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 85062306a36Sopenharmony_ci } else { 85162306a36Sopenharmony_ci rss_fields = adapter->rss_fields; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci info->data = 0; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* Report default options for RSS on vmxnet3 */ 85762306a36Sopenharmony_ci switch (info->flow_type) { 85862306a36Sopenharmony_ci case TCP_V4_FLOW: 85962306a36Sopenharmony_ci case TCP_V6_FLOW: 86062306a36Sopenharmony_ci info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3 | 86162306a36Sopenharmony_ci RXH_IP_SRC | RXH_IP_DST; 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci case UDP_V4_FLOW: 86462306a36Sopenharmony_ci if (rss_fields & VMXNET3_RSS_FIELDS_UDPIP4) 86562306a36Sopenharmony_ci info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 86662306a36Sopenharmony_ci info->data |= RXH_IP_SRC | RXH_IP_DST; 86762306a36Sopenharmony_ci break; 86862306a36Sopenharmony_ci case AH_ESP_V4_FLOW: 86962306a36Sopenharmony_ci case AH_V4_FLOW: 87062306a36Sopenharmony_ci case ESP_V4_FLOW: 87162306a36Sopenharmony_ci if (rss_fields & VMXNET3_RSS_FIELDS_ESPIP4) 87262306a36Sopenharmony_ci info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 87362306a36Sopenharmony_ci fallthrough; 87462306a36Sopenharmony_ci case SCTP_V4_FLOW: 87562306a36Sopenharmony_ci case IPV4_FLOW: 87662306a36Sopenharmony_ci info->data |= RXH_IP_SRC | RXH_IP_DST; 87762306a36Sopenharmony_ci break; 87862306a36Sopenharmony_ci case UDP_V6_FLOW: 87962306a36Sopenharmony_ci if (rss_fields & VMXNET3_RSS_FIELDS_UDPIP6) 88062306a36Sopenharmony_ci info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 88162306a36Sopenharmony_ci info->data |= RXH_IP_SRC | RXH_IP_DST; 88262306a36Sopenharmony_ci break; 88362306a36Sopenharmony_ci case AH_ESP_V6_FLOW: 88462306a36Sopenharmony_ci case AH_V6_FLOW: 88562306a36Sopenharmony_ci case ESP_V6_FLOW: 88662306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_6(adapter) && 88762306a36Sopenharmony_ci (rss_fields & VMXNET3_RSS_FIELDS_ESPIP6)) 88862306a36Sopenharmony_ci info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 88962306a36Sopenharmony_ci fallthrough; 89062306a36Sopenharmony_ci case SCTP_V6_FLOW: 89162306a36Sopenharmony_ci case IPV6_FLOW: 89262306a36Sopenharmony_ci info->data |= RXH_IP_SRC | RXH_IP_DST; 89362306a36Sopenharmony_ci break; 89462306a36Sopenharmony_ci default: 89562306a36Sopenharmony_ci return -EINVAL; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic int 90262306a36Sopenharmony_civmxnet3_set_rss_hash_opt(struct net_device *netdev, 90362306a36Sopenharmony_ci struct vmxnet3_adapter *adapter, 90462306a36Sopenharmony_ci struct ethtool_rxnfc *nfc) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci enum Vmxnet3_RSSField rss_fields = adapter->rss_fields; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* RSS does not support anything other than hashing 90962306a36Sopenharmony_ci * to queues on src and dst IPs and ports 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_ci if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | 91262306a36Sopenharmony_ci RXH_L4_B_0_1 | RXH_L4_B_2_3)) 91362306a36Sopenharmony_ci return -EINVAL; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci switch (nfc->flow_type) { 91662306a36Sopenharmony_ci case TCP_V4_FLOW: 91762306a36Sopenharmony_ci case TCP_V6_FLOW: 91862306a36Sopenharmony_ci if (!(nfc->data & RXH_IP_SRC) || 91962306a36Sopenharmony_ci !(nfc->data & RXH_IP_DST) || 92062306a36Sopenharmony_ci !(nfc->data & RXH_L4_B_0_1) || 92162306a36Sopenharmony_ci !(nfc->data & RXH_L4_B_2_3)) 92262306a36Sopenharmony_ci return -EINVAL; 92362306a36Sopenharmony_ci break; 92462306a36Sopenharmony_ci case UDP_V4_FLOW: 92562306a36Sopenharmony_ci if (!(nfc->data & RXH_IP_SRC) || 92662306a36Sopenharmony_ci !(nfc->data & RXH_IP_DST)) 92762306a36Sopenharmony_ci return -EINVAL; 92862306a36Sopenharmony_ci switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { 92962306a36Sopenharmony_ci case 0: 93062306a36Sopenharmony_ci rss_fields &= ~VMXNET3_RSS_FIELDS_UDPIP4; 93162306a36Sopenharmony_ci break; 93262306a36Sopenharmony_ci case (RXH_L4_B_0_1 | RXH_L4_B_2_3): 93362306a36Sopenharmony_ci rss_fields |= VMXNET3_RSS_FIELDS_UDPIP4; 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci default: 93662306a36Sopenharmony_ci return -EINVAL; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci break; 93962306a36Sopenharmony_ci case UDP_V6_FLOW: 94062306a36Sopenharmony_ci if (!(nfc->data & RXH_IP_SRC) || 94162306a36Sopenharmony_ci !(nfc->data & RXH_IP_DST)) 94262306a36Sopenharmony_ci return -EINVAL; 94362306a36Sopenharmony_ci switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { 94462306a36Sopenharmony_ci case 0: 94562306a36Sopenharmony_ci rss_fields &= ~VMXNET3_RSS_FIELDS_UDPIP6; 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci case (RXH_L4_B_0_1 | RXH_L4_B_2_3): 94862306a36Sopenharmony_ci rss_fields |= VMXNET3_RSS_FIELDS_UDPIP6; 94962306a36Sopenharmony_ci break; 95062306a36Sopenharmony_ci default: 95162306a36Sopenharmony_ci return -EINVAL; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci break; 95462306a36Sopenharmony_ci case ESP_V4_FLOW: 95562306a36Sopenharmony_ci case AH_V4_FLOW: 95662306a36Sopenharmony_ci case AH_ESP_V4_FLOW: 95762306a36Sopenharmony_ci if (!(nfc->data & RXH_IP_SRC) || 95862306a36Sopenharmony_ci !(nfc->data & RXH_IP_DST)) 95962306a36Sopenharmony_ci return -EINVAL; 96062306a36Sopenharmony_ci switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { 96162306a36Sopenharmony_ci case 0: 96262306a36Sopenharmony_ci rss_fields &= ~VMXNET3_RSS_FIELDS_ESPIP4; 96362306a36Sopenharmony_ci break; 96462306a36Sopenharmony_ci case (RXH_L4_B_0_1 | RXH_L4_B_2_3): 96562306a36Sopenharmony_ci rss_fields |= VMXNET3_RSS_FIELDS_ESPIP4; 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci default: 96862306a36Sopenharmony_ci return -EINVAL; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci break; 97162306a36Sopenharmony_ci case ESP_V6_FLOW: 97262306a36Sopenharmony_ci case AH_V6_FLOW: 97362306a36Sopenharmony_ci case AH_ESP_V6_FLOW: 97462306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_6(adapter)) 97562306a36Sopenharmony_ci return -EOPNOTSUPP; 97662306a36Sopenharmony_ci if (!(nfc->data & RXH_IP_SRC) || 97762306a36Sopenharmony_ci !(nfc->data & RXH_IP_DST)) 97862306a36Sopenharmony_ci return -EINVAL; 97962306a36Sopenharmony_ci switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { 98062306a36Sopenharmony_ci case 0: 98162306a36Sopenharmony_ci rss_fields &= ~VMXNET3_RSS_FIELDS_ESPIP6; 98262306a36Sopenharmony_ci break; 98362306a36Sopenharmony_ci case (RXH_L4_B_0_1 | RXH_L4_B_2_3): 98462306a36Sopenharmony_ci rss_fields |= VMXNET3_RSS_FIELDS_ESPIP6; 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci default: 98762306a36Sopenharmony_ci return -EINVAL; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci case SCTP_V4_FLOW: 99162306a36Sopenharmony_ci case SCTP_V6_FLOW: 99262306a36Sopenharmony_ci if (!(nfc->data & RXH_IP_SRC) || 99362306a36Sopenharmony_ci !(nfc->data & RXH_IP_DST) || 99462306a36Sopenharmony_ci (nfc->data & RXH_L4_B_0_1) || 99562306a36Sopenharmony_ci (nfc->data & RXH_L4_B_2_3)) 99662306a36Sopenharmony_ci return -EINVAL; 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci default: 99962306a36Sopenharmony_ci return -EINVAL; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* if we changed something we need to update flags */ 100362306a36Sopenharmony_ci if (rss_fields != adapter->rss_fields) { 100462306a36Sopenharmony_ci adapter->default_rss_fields = false; 100562306a36Sopenharmony_ci if (netif_running(netdev)) { 100662306a36Sopenharmony_ci struct Vmxnet3_DriverShared *shared = adapter->shared; 100762306a36Sopenharmony_ci union Vmxnet3_CmdInfo *cmdInfo = &shared->cu.cmdInfo; 100862306a36Sopenharmony_ci unsigned long flags; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 101162306a36Sopenharmony_ci if ((rss_fields & VMXNET3_RSS_FIELDS_UDPIP4 || 101262306a36Sopenharmony_ci rss_fields & VMXNET3_RSS_FIELDS_UDPIP6) && 101362306a36Sopenharmony_ci vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 101462306a36Sopenharmony_ci VMXNET3_CAP_UDP_RSS)) { 101562306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_UDP_RSS; 101662306a36Sopenharmony_ci } else { 101762306a36Sopenharmony_ci adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_UDP_RSS); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci if ((rss_fields & VMXNET3_RSS_FIELDS_ESPIP4) && 102062306a36Sopenharmony_ci vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 102162306a36Sopenharmony_ci VMXNET3_CAP_ESP_RSS_IPV4)) { 102262306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_ESP_RSS_IPV4; 102362306a36Sopenharmony_ci } else { 102462306a36Sopenharmony_ci adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_ESP_RSS_IPV4); 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci if ((rss_fields & VMXNET3_RSS_FIELDS_ESPIP6) && 102762306a36Sopenharmony_ci vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 102862306a36Sopenharmony_ci VMXNET3_CAP_ESP_RSS_IPV6)) { 102962306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_ESP_RSS_IPV6; 103062306a36Sopenharmony_ci } else { 103162306a36Sopenharmony_ci adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_ESP_RSS_IPV6); 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DCR, 103562306a36Sopenharmony_ci adapter->dev_caps[0]); 103662306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 103762306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 103862306a36Sopenharmony_ci VMXNET3_CMD_GET_DCR0_REG); 103962306a36Sopenharmony_ci adapter->dev_caps[0] = VMXNET3_READ_BAR1_REG(adapter, 104062306a36Sopenharmony_ci VMXNET3_REG_CMD); 104162306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 104462306a36Sopenharmony_ci cmdInfo->setRssFields = rss_fields; 104562306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 104662306a36Sopenharmony_ci VMXNET3_CMD_SET_RSS_FIELDS); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* Not all requested RSS may get applied, so get and 104962306a36Sopenharmony_ci * cache what was actually applied. 105062306a36Sopenharmony_ci */ 105162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 105262306a36Sopenharmony_ci VMXNET3_CMD_GET_RSS_FIELDS); 105362306a36Sopenharmony_ci adapter->rss_fields = 105462306a36Sopenharmony_ci VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 105562306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 105662306a36Sopenharmony_ci } else { 105762306a36Sopenharmony_ci /* When the device is activated, we will try to apply 105862306a36Sopenharmony_ci * these rules and cache the applied value later. 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ci adapter->rss_fields = rss_fields; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci return 0; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic int 106762306a36Sopenharmony_civmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info, 106862306a36Sopenharmony_ci u32 *rules) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 107162306a36Sopenharmony_ci int err = 0; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci switch (info->cmd) { 107462306a36Sopenharmony_ci case ETHTOOL_GRXRINGS: 107562306a36Sopenharmony_ci info->data = adapter->num_rx_queues; 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci case ETHTOOL_GRXFH: 107862306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_4(adapter)) { 107962306a36Sopenharmony_ci err = -EOPNOTSUPP; 108062306a36Sopenharmony_ci break; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci#ifdef VMXNET3_RSS 108362306a36Sopenharmony_ci if (!adapter->rss) { 108462306a36Sopenharmony_ci err = -EOPNOTSUPP; 108562306a36Sopenharmony_ci break; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci#endif 108862306a36Sopenharmony_ci err = vmxnet3_get_rss_hash_opts(adapter, info); 108962306a36Sopenharmony_ci break; 109062306a36Sopenharmony_ci default: 109162306a36Sopenharmony_ci err = -EOPNOTSUPP; 109262306a36Sopenharmony_ci break; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci return err; 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_cistatic int 109962306a36Sopenharmony_civmxnet3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 110262306a36Sopenharmony_ci int err = 0; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_4(adapter)) { 110562306a36Sopenharmony_ci err = -EOPNOTSUPP; 110662306a36Sopenharmony_ci goto done; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci#ifdef VMXNET3_RSS 110962306a36Sopenharmony_ci if (!adapter->rss) { 111062306a36Sopenharmony_ci err = -EOPNOTSUPP; 111162306a36Sopenharmony_ci goto done; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci#endif 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci switch (info->cmd) { 111662306a36Sopenharmony_ci case ETHTOOL_SRXFH: 111762306a36Sopenharmony_ci err = vmxnet3_set_rss_hash_opt(netdev, adapter, info); 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci default: 112062306a36Sopenharmony_ci err = -EOPNOTSUPP; 112162306a36Sopenharmony_ci break; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cidone: 112562306a36Sopenharmony_ci return err; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci#ifdef VMXNET3_RSS 112962306a36Sopenharmony_cistatic u32 113062306a36Sopenharmony_civmxnet3_get_rss_indir_size(struct net_device *netdev) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 113362306a36Sopenharmony_ci struct UPT1_RSSConf *rssConf = adapter->rss_conf; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci return rssConf->indTableSize; 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistatic int 113962306a36Sopenharmony_civmxnet3_get_rss(struct net_device *netdev, u32 *p, u8 *key, u8 *hfunc) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 114262306a36Sopenharmony_ci struct UPT1_RSSConf *rssConf = adapter->rss_conf; 114362306a36Sopenharmony_ci unsigned int n = rssConf->indTableSize; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci if (hfunc) 114662306a36Sopenharmony_ci *hfunc = ETH_RSS_HASH_TOP; 114762306a36Sopenharmony_ci if (!p) 114862306a36Sopenharmony_ci return 0; 114962306a36Sopenharmony_ci if (n > UPT1_RSS_MAX_IND_TABLE_SIZE) 115062306a36Sopenharmony_ci return 0; 115162306a36Sopenharmony_ci while (n--) 115262306a36Sopenharmony_ci p[n] = rssConf->indTable[n]; 115362306a36Sopenharmony_ci return 0; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic int 115862306a36Sopenharmony_civmxnet3_set_rss(struct net_device *netdev, const u32 *p, const u8 *key, 115962306a36Sopenharmony_ci const u8 hfunc) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci unsigned int i; 116262306a36Sopenharmony_ci unsigned long flags; 116362306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 116462306a36Sopenharmony_ci struct UPT1_RSSConf *rssConf = adapter->rss_conf; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* We do not allow change in unsupported parameters */ 116762306a36Sopenharmony_ci if (key || 116862306a36Sopenharmony_ci (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) 116962306a36Sopenharmony_ci return -EOPNOTSUPP; 117062306a36Sopenharmony_ci if (!p) 117162306a36Sopenharmony_ci return 0; 117262306a36Sopenharmony_ci for (i = 0; i < rssConf->indTableSize; i++) 117362306a36Sopenharmony_ci rssConf->indTable[i] = p[i]; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 117662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 117762306a36Sopenharmony_ci VMXNET3_CMD_UPDATE_RSSIDT); 117862306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci return 0; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci#endif 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic int vmxnet3_get_coalesce(struct net_device *netdev, 118662306a36Sopenharmony_ci struct ethtool_coalesce *ec, 118762306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 118862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_3(adapter)) 119362306a36Sopenharmony_ci return -EOPNOTSUPP; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci switch (adapter->coal_conf->coalMode) { 119662306a36Sopenharmony_ci case VMXNET3_COALESCE_DISABLED: 119762306a36Sopenharmony_ci /* struct ethtool_coalesce is already initialized to 0 */ 119862306a36Sopenharmony_ci break; 119962306a36Sopenharmony_ci case VMXNET3_COALESCE_ADAPT: 120062306a36Sopenharmony_ci ec->use_adaptive_rx_coalesce = true; 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci case VMXNET3_COALESCE_STATIC: 120362306a36Sopenharmony_ci ec->tx_max_coalesced_frames = 120462306a36Sopenharmony_ci adapter->coal_conf->coalPara.coalStatic.tx_comp_depth; 120562306a36Sopenharmony_ci ec->rx_max_coalesced_frames = 120662306a36Sopenharmony_ci adapter->coal_conf->coalPara.coalStatic.rx_depth; 120762306a36Sopenharmony_ci break; 120862306a36Sopenharmony_ci case VMXNET3_COALESCE_RBC: { 120962306a36Sopenharmony_ci u32 rbc_rate; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci rbc_rate = adapter->coal_conf->coalPara.coalRbc.rbc_rate; 121262306a36Sopenharmony_ci ec->rx_coalesce_usecs = VMXNET3_COAL_RBC_USECS(rbc_rate); 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci break; 121562306a36Sopenharmony_ci default: 121662306a36Sopenharmony_ci return -EOPNOTSUPP; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic int vmxnet3_set_coalesce(struct net_device *netdev, 122362306a36Sopenharmony_ci struct ethtool_coalesce *ec, 122462306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 122562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 122862306a36Sopenharmony_ci struct Vmxnet3_DriverShared *shared = adapter->shared; 122962306a36Sopenharmony_ci union Vmxnet3_CmdInfo *cmdInfo = &shared->cu.cmdInfo; 123062306a36Sopenharmony_ci unsigned long flags; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_3(adapter)) 123362306a36Sopenharmony_ci return -EOPNOTSUPP; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if ((ec->rx_coalesce_usecs == 0) && 123662306a36Sopenharmony_ci (ec->use_adaptive_rx_coalesce == 0) && 123762306a36Sopenharmony_ci (ec->tx_max_coalesced_frames == 0) && 123862306a36Sopenharmony_ci (ec->rx_max_coalesced_frames == 0)) { 123962306a36Sopenharmony_ci memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf)); 124062306a36Sopenharmony_ci adapter->coal_conf->coalMode = VMXNET3_COALESCE_DISABLED; 124162306a36Sopenharmony_ci goto done; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (ec->rx_coalesce_usecs != 0) { 124562306a36Sopenharmony_ci u32 rbc_rate; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci if ((ec->use_adaptive_rx_coalesce != 0) || 124862306a36Sopenharmony_ci (ec->tx_max_coalesced_frames != 0) || 124962306a36Sopenharmony_ci (ec->rx_max_coalesced_frames != 0)) { 125062306a36Sopenharmony_ci return -EINVAL; 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci rbc_rate = VMXNET3_COAL_RBC_RATE(ec->rx_coalesce_usecs); 125462306a36Sopenharmony_ci if (rbc_rate < VMXNET3_COAL_RBC_MIN_RATE || 125562306a36Sopenharmony_ci rbc_rate > VMXNET3_COAL_RBC_MAX_RATE) { 125662306a36Sopenharmony_ci return -EINVAL; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf)); 126062306a36Sopenharmony_ci adapter->coal_conf->coalMode = VMXNET3_COALESCE_RBC; 126162306a36Sopenharmony_ci adapter->coal_conf->coalPara.coalRbc.rbc_rate = rbc_rate; 126262306a36Sopenharmony_ci goto done; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (ec->use_adaptive_rx_coalesce != 0) { 126662306a36Sopenharmony_ci if (ec->tx_max_coalesced_frames != 0 || 126762306a36Sopenharmony_ci ec->rx_max_coalesced_frames != 0) { 126862306a36Sopenharmony_ci return -EINVAL; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf)); 127162306a36Sopenharmony_ci adapter->coal_conf->coalMode = VMXNET3_COALESCE_ADAPT; 127262306a36Sopenharmony_ci goto done; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci if ((ec->tx_max_coalesced_frames != 0) || 127662306a36Sopenharmony_ci (ec->rx_max_coalesced_frames != 0)) { 127762306a36Sopenharmony_ci if ((ec->tx_max_coalesced_frames > 127862306a36Sopenharmony_ci VMXNET3_COAL_STATIC_MAX_DEPTH) || 127962306a36Sopenharmony_ci (ec->rx_max_coalesced_frames > 128062306a36Sopenharmony_ci VMXNET3_COAL_STATIC_MAX_DEPTH)) { 128162306a36Sopenharmony_ci return -EINVAL; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf)); 128562306a36Sopenharmony_ci adapter->coal_conf->coalMode = VMXNET3_COALESCE_STATIC; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci adapter->coal_conf->coalPara.coalStatic.tx_comp_depth = 128862306a36Sopenharmony_ci (ec->tx_max_coalesced_frames ? 128962306a36Sopenharmony_ci ec->tx_max_coalesced_frames : 129062306a36Sopenharmony_ci VMXNET3_COAL_STATIC_DEFAULT_DEPTH); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci adapter->coal_conf->coalPara.coalStatic.rx_depth = 129362306a36Sopenharmony_ci (ec->rx_max_coalesced_frames ? 129462306a36Sopenharmony_ci ec->rx_max_coalesced_frames : 129562306a36Sopenharmony_ci VMXNET3_COAL_STATIC_DEFAULT_DEPTH); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci adapter->coal_conf->coalPara.coalStatic.tx_depth = 129862306a36Sopenharmony_ci VMXNET3_COAL_STATIC_DEFAULT_DEPTH; 129962306a36Sopenharmony_ci goto done; 130062306a36Sopenharmony_ci } 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cidone: 130362306a36Sopenharmony_ci adapter->default_coal_mode = false; 130462306a36Sopenharmony_ci if (netif_running(netdev)) { 130562306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 130662306a36Sopenharmony_ci cmdInfo->varConf.confVer = 1; 130762306a36Sopenharmony_ci cmdInfo->varConf.confLen = 130862306a36Sopenharmony_ci cpu_to_le32(sizeof(*adapter->coal_conf)); 130962306a36Sopenharmony_ci cmdInfo->varConf.confPA = cpu_to_le64(adapter->coal_conf_pa); 131062306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 131162306a36Sopenharmony_ci VMXNET3_CMD_SET_COALESCE); 131262306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci return 0; 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic void vmxnet3_get_channels(struct net_device *netdev, 131962306a36Sopenharmony_ci struct ethtool_channels *ec) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PCI_MSI) && adapter->intr.type == VMXNET3_IT_MSIX) { 132462306a36Sopenharmony_ci if (adapter->share_intr == VMXNET3_INTR_BUDDYSHARE) { 132562306a36Sopenharmony_ci ec->combined_count = adapter->num_tx_queues; 132662306a36Sopenharmony_ci } else { 132762306a36Sopenharmony_ci ec->rx_count = adapter->num_rx_queues; 132862306a36Sopenharmony_ci ec->tx_count = 132962306a36Sopenharmony_ci adapter->share_intr == VMXNET3_INTR_TXSHARE ? 133062306a36Sopenharmony_ci 1 : adapter->num_tx_queues; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci } else { 133362306a36Sopenharmony_ci ec->combined_count = 1; 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci ec->other_count = 1; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* Number of interrupts cannot be changed on the fly */ 133962306a36Sopenharmony_ci /* Just set maximums to actual values */ 134062306a36Sopenharmony_ci ec->max_rx = ec->rx_count; 134162306a36Sopenharmony_ci ec->max_tx = ec->tx_count; 134262306a36Sopenharmony_ci ec->max_combined = ec->combined_count; 134362306a36Sopenharmony_ci ec->max_other = ec->other_count; 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic const struct ethtool_ops vmxnet3_ethtool_ops = { 134762306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | 134862306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES | 134962306a36Sopenharmony_ci ETHTOOL_COALESCE_USE_ADAPTIVE_RX, 135062306a36Sopenharmony_ci .get_drvinfo = vmxnet3_get_drvinfo, 135162306a36Sopenharmony_ci .get_regs_len = vmxnet3_get_regs_len, 135262306a36Sopenharmony_ci .get_regs = vmxnet3_get_regs, 135362306a36Sopenharmony_ci .get_wol = vmxnet3_get_wol, 135462306a36Sopenharmony_ci .set_wol = vmxnet3_set_wol, 135562306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 135662306a36Sopenharmony_ci .get_coalesce = vmxnet3_get_coalesce, 135762306a36Sopenharmony_ci .set_coalesce = vmxnet3_set_coalesce, 135862306a36Sopenharmony_ci .get_strings = vmxnet3_get_strings, 135962306a36Sopenharmony_ci .get_sset_count = vmxnet3_get_sset_count, 136062306a36Sopenharmony_ci .get_ethtool_stats = vmxnet3_get_ethtool_stats, 136162306a36Sopenharmony_ci .get_ringparam = vmxnet3_get_ringparam, 136262306a36Sopenharmony_ci .set_ringparam = vmxnet3_set_ringparam, 136362306a36Sopenharmony_ci .get_rxnfc = vmxnet3_get_rxnfc, 136462306a36Sopenharmony_ci .set_rxnfc = vmxnet3_set_rxnfc, 136562306a36Sopenharmony_ci#ifdef VMXNET3_RSS 136662306a36Sopenharmony_ci .get_rxfh_indir_size = vmxnet3_get_rss_indir_size, 136762306a36Sopenharmony_ci .get_rxfh = vmxnet3_get_rss, 136862306a36Sopenharmony_ci .set_rxfh = vmxnet3_set_rss, 136962306a36Sopenharmony_ci#endif 137062306a36Sopenharmony_ci .get_link_ksettings = vmxnet3_get_link_ksettings, 137162306a36Sopenharmony_ci .get_channels = vmxnet3_get_channels, 137262306a36Sopenharmony_ci}; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_civoid vmxnet3_set_ethtool_ops(struct net_device *netdev) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci netdev->ethtool_ops = &vmxnet3_ethtool_ops; 137762306a36Sopenharmony_ci} 1378