1219820Sjeff/* 2272407Shselasky * Copyright (c) 2007, 2014 Mellanox Technologies. All rights reserved. 3219820Sjeff * 4219820Sjeff * This software is available to you under a choice of one of two 5219820Sjeff * licenses. You may choose to be licensed under the terms of the GNU 6219820Sjeff * General Public License (GPL) Version 2, available from the file 7219820Sjeff * COPYING in the main directory of this source tree, or the 8219820Sjeff * OpenIB.org BSD license below: 9219820Sjeff * 10219820Sjeff * Redistribution and use in source and binary forms, with or 11219820Sjeff * without modification, are permitted provided that the following 12219820Sjeff * conditions are met: 13219820Sjeff * 14219820Sjeff * - Redistributions of source code must retain the above 15219820Sjeff * copyright notice, this list of conditions and the following 16219820Sjeff * disclaimer. 17219820Sjeff * 18219820Sjeff * - Redistributions in binary form must reproduce the above 19219820Sjeff * copyright notice, this list of conditions and the following 20219820Sjeff * disclaimer in the documentation and/or other materials 21219820Sjeff * provided with the distribution. 22219820Sjeff * 23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30219820Sjeff * SOFTWARE. 31219820Sjeff * 32219820Sjeff */ 33219820Sjeff 34219820Sjeff#include <linux/kernel.h> 35219820Sjeff#include <linux/ethtool.h> 36219820Sjeff#include <linux/netdevice.h> 37272407Shselasky#include <linux/mlx4/driver.h> 38272407Shselasky#include <linux/in.h> 39272407Shselasky#include <net/ip.h> 40272407Shselasky#include <linux/bitmap.h> 41219820Sjeff 42219820Sjeff#include "mlx4_en.h" 43219820Sjeff#include "en_port.h" 44219820Sjeff 45272407Shselasky#define EN_ETHTOOL_QP_ATTACH (1ull << 63) 46219820Sjeff 47272407Shselaskyunion mlx4_ethtool_flow_union { 48272407Shselasky struct ethtool_tcpip4_spec tcp_ip4_spec; 49272407Shselasky struct ethtool_tcpip4_spec udp_ip4_spec; 50272407Shselasky struct ethtool_tcpip4_spec sctp_ip4_spec; 51272407Shselasky struct ethtool_ah_espip4_spec ah_ip4_spec; 52272407Shselasky struct ethtool_ah_espip4_spec esp_ip4_spec; 53272407Shselasky struct ethtool_usrip4_spec usr_ip4_spec; 54272407Shselasky struct ethhdr ether_spec; 55272407Shselasky __u8 hdata[52]; 56272407Shselasky}; 57219820Sjeff 58272407Shselaskystruct mlx4_ethtool_flow_ext { 59272407Shselasky __u8 padding[2]; 60272407Shselasky unsigned char h_dest[ETH_ALEN]; 61272407Shselasky __be16 vlan_etype; 62272407Shselasky __be16 vlan_tci; 63272407Shselasky __be32 data[2]; 64272407Shselasky}; 65219820Sjeff 66272407Shselaskystruct mlx4_ethtool_rx_flow_spec { 67272407Shselasky __u32 flow_type; 68272407Shselasky union mlx4_ethtool_flow_union h_u; 69272407Shselasky struct mlx4_ethtool_flow_ext h_ext; 70272407Shselasky union mlx4_ethtool_flow_union m_u; 71272407Shselasky struct mlx4_ethtool_flow_ext m_ext; 72272407Shselasky __u64 ring_cookie; 73272407Shselasky __u32 location; 74272407Shselasky}; 75219820Sjeff 76272407Shselaskystruct mlx4_ethtool_rxnfc { 77272407Shselasky __u32 cmd; 78272407Shselasky __u32 flow_type; 79272407Shselasky __u64 data; 80272407Shselasky struct mlx4_ethtool_rx_flow_spec fs; 81272407Shselasky __u32 rule_cnt; 82272407Shselasky __u32 rule_locs[0]; 83272407Shselasky}; 84272407Shselasky 85272407Shselasky#ifndef FLOW_MAC_EXT 86272407Shselasky#define FLOW_MAC_EXT 0x40000000 87272407Shselasky#endif 88272407Shselasky 89219820Sjeffstatic void 90219820Sjeffmlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) 91219820Sjeff{ 92219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 93219820Sjeff struct mlx4_en_dev *mdev = priv->mdev; 94219820Sjeff 95272407Shselasky strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); 96272407Shselasky strlcpy(drvinfo->version, DRV_VERSION " (" DRV_RELDATE ")", 97272407Shselasky sizeof(drvinfo->version)); 98272407Shselasky snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), 99272407Shselasky "%d.%d.%d", 100219820Sjeff (u16) (mdev->dev->caps.fw_ver >> 32), 101219820Sjeff (u16) ((mdev->dev->caps.fw_ver >> 16) & 0xffff), 102219820Sjeff (u16) (mdev->dev->caps.fw_ver & 0xffff)); 103272407Shselasky strlcpy(drvinfo->bus_info, pci_name(mdev->dev->pdev), 104272407Shselasky sizeof(drvinfo->bus_info)); 105219820Sjeff drvinfo->n_stats = 0; 106219820Sjeff drvinfo->regdump_len = 0; 107219820Sjeff drvinfo->eedump_len = 0; 108219820Sjeff} 109219820Sjeff 110272407Shselaskystatic const char main_strings[][ETH_GSTRING_LEN] = { 111272407Shselasky /* packet statistics */ 112272407Shselasky "rx_packets", 113272407Shselasky "rx_bytes", 114272407Shselasky "rx_multicast_packets", 115272407Shselasky "rx_broadcast_packets", 116272407Shselasky "rx_errors", 117272407Shselasky "rx_dropped", 118272407Shselasky "rx_length_errors", 119272407Shselasky "rx_over_errors", 120272407Shselasky "rx_crc_errors", 121272407Shselasky "rx_jabbers", 122272407Shselasky "rx_in_range_length_error", 123272407Shselasky "rx_out_range_length_error", 124272407Shselasky "rx_lt_64_bytes_packets", 125272407Shselasky "rx_127_bytes_packets", 126272407Shselasky "rx_255_bytes_packets", 127272407Shselasky "rx_511_bytes_packets", 128272407Shselasky "rx_1023_bytes_packets", 129272407Shselasky "rx_1518_bytes_packets", 130272407Shselasky "rx_1522_bytes_packets", 131272407Shselasky "rx_1548_bytes_packets", 132272407Shselasky "rx_gt_1548_bytes_packets", 133272407Shselasky "tx_packets", 134272407Shselasky "tx_bytes", 135272407Shselasky "tx_multicast_packets", 136272407Shselasky "tx_broadcast_packets", 137272407Shselasky "tx_errors", 138272407Shselasky "tx_dropped", 139272407Shselasky "tx_lt_64_bytes_packets", 140272407Shselasky "tx_127_bytes_packets", 141272407Shselasky "tx_255_bytes_packets", 142272407Shselasky "tx_511_bytes_packets", 143272407Shselasky "tx_1023_bytes_packets", 144272407Shselasky "tx_1518_bytes_packets", 145272407Shselasky "tx_1522_bytes_packets", 146272407Shselasky "tx_1548_bytes_packets", 147272407Shselasky "tx_gt_1548_bytes_packets", 148272407Shselasky "rx_prio_0_packets", "rx_prio_0_bytes", 149272407Shselasky "rx_prio_1_packets", "rx_prio_1_bytes", 150272407Shselasky "rx_prio_2_packets", "rx_prio_2_bytes", 151272407Shselasky "rx_prio_3_packets", "rx_prio_3_bytes", 152272407Shselasky "rx_prio_4_packets", "rx_prio_4_bytes", 153272407Shselasky "rx_prio_5_packets", "rx_prio_5_bytes", 154272407Shselasky "rx_prio_6_packets", "rx_prio_6_bytes", 155272407Shselasky "rx_prio_7_packets", "rx_prio_7_bytes", 156272407Shselasky "rx_novlan_packets", "rx_novlan_bytes", 157272407Shselasky "tx_prio_0_packets", "tx_prio_0_bytes", 158272407Shselasky "tx_prio_1_packets", "tx_prio_1_bytes", 159272407Shselasky "tx_prio_2_packets", "tx_prio_2_bytes", 160272407Shselasky "tx_prio_3_packets", "tx_prio_3_bytes", 161272407Shselasky "tx_prio_4_packets", "tx_prio_4_bytes", 162272407Shselasky "tx_prio_5_packets", "tx_prio_5_bytes", 163272407Shselasky "tx_prio_6_packets", "tx_prio_6_bytes", 164272407Shselasky "tx_prio_7_packets", "tx_prio_7_bytes", 165272407Shselasky "tx_novlan_packets", "tx_novlan_bytes", 166219820Sjeff 167272407Shselasky /* flow control statistics */ 168272407Shselasky "rx_pause_prio_0", "rx_pause_duration_prio_0", 169272407Shselasky "rx_pause_transition_prio_0", "tx_pause_prio_0", 170272407Shselasky "tx_pause_duration_prio_0", "tx_pause_transition_prio_0", 171272407Shselasky "rx_pause_prio_1", "rx_pause_duration_prio_1", 172272407Shselasky "rx_pause_transition_prio_1", "tx_pause_prio_1", 173272407Shselasky "tx_pause_duration_prio_1", "tx_pause_transition_prio_1", 174272407Shselasky "rx_pause_prio_2", "rx_pause_duration_prio_2", 175272407Shselasky "rx_pause_transition_prio_2", "tx_pause_prio_2", 176272407Shselasky "tx_pause_duration_prio_2", "tx_pause_transition_prio_2", 177272407Shselasky "rx_pause_prio_3", "rx_pause_duration_prio_3", 178272407Shselasky "rx_pause_transition_prio_3", "tx_pause_prio_3", 179272407Shselasky "tx_pause_duration_prio_3", "tx_pause_transition_prio_3", 180272407Shselasky "rx_pause_prio_4", "rx_pause_duration_prio_4", 181272407Shselasky "rx_pause_transition_prio_4", "tx_pause_prio_4", 182272407Shselasky "tx_pause_duration_prio_4", "tx_pause_transition_prio_4", 183272407Shselasky "rx_pause_prio_5", "rx_pause_duration_prio_5", 184272407Shselasky "rx_pause_transition_prio_5", "tx_pause_prio_5", 185272407Shselasky "tx_pause_duration_prio_5", "tx_pause_transition_prio_5", 186272407Shselasky "rx_pause_prio_6", "rx_pause_duration_prio_6", 187272407Shselasky "rx_pause_transition_prio_6", "tx_pause_prio_6", 188272407Shselasky "tx_pause_duration_prio_6", "tx_pause_transition_prio_6", 189272407Shselasky "rx_pause_prio_7", "rx_pause_duration_prio_7", 190272407Shselasky "rx_pause_transition_prio_7", "tx_pause_prio_7", 191272407Shselasky "tx_pause_duration_prio_7", "tx_pause_transition_prio_7", 192219820Sjeff 193272407Shselasky /* VF statistics */ 194272407Shselasky "rx_packets", 195272407Shselasky "rx_bytes", 196272407Shselasky "rx_multicast_packets", 197272407Shselasky "rx_broadcast_packets", 198272407Shselasky "rx_errors", 199272407Shselasky "rx_dropped", 200272407Shselasky "tx_packets", 201272407Shselasky "tx_bytes", 202272407Shselasky "tx_multicast_packets", 203272407Shselasky "tx_broadcast_packets", 204272407Shselasky "tx_errors", 205219820Sjeff 206272407Shselasky /* VPort statistics */ 207272407Shselasky "vport_rx_unicast_packets", 208272407Shselasky "vport_rx_unicast_bytes", 209272407Shselasky "vport_rx_multicast_packets", 210272407Shselasky "vport_rx_multicast_bytes", 211272407Shselasky "vport_rx_broadcast_packets", 212272407Shselasky "vport_rx_broadcast_bytes", 213272407Shselasky "vport_rx_dropped", 214272407Shselasky "vport_rx_errors", 215272407Shselasky "vport_tx_unicast_packets", 216272407Shselasky "vport_tx_unicast_bytes", 217272407Shselasky "vport_tx_multicast_packets", 218272407Shselasky "vport_tx_multicast_bytes", 219272407Shselasky "vport_tx_broadcast_packets", 220272407Shselasky "vport_tx_broadcast_bytes", 221272407Shselasky "vport_tx_errors", 222219820Sjeff 223219820Sjeff /* port statistics */ 224272407Shselasky "tx_tso_packets", 225272407Shselasky "tx_queue_stopped", "tx_wake_queue", "tx_timeout", "rx_alloc_failed", 226219820Sjeff "rx_csum_good", "rx_csum_none", "tx_chksum_offload", 227219820Sjeff}; 228219820Sjeff 229219820Sjeffstatic const char mlx4_en_test_names[][ETH_GSTRING_LEN]= { 230272407Shselasky "Interrupt Test", 231219820Sjeff "Link Test", 232219820Sjeff "Speed Test", 233219820Sjeff "Register Test", 234219820Sjeff "Loopback Test", 235219820Sjeff}; 236219820Sjeff 237219820Sjeffstatic u32 mlx4_en_get_msglevel(struct net_device *dev) 238219820Sjeff{ 239219820Sjeff return ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable; 240219820Sjeff} 241219820Sjeff 242219820Sjeffstatic void mlx4_en_set_msglevel(struct net_device *dev, u32 val) 243219820Sjeff{ 244219820Sjeff ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable = val; 245219820Sjeff} 246219820Sjeff 247219820Sjeffstatic void mlx4_en_get_wol(struct net_device *netdev, 248219820Sjeff struct ethtool_wolinfo *wol) 249219820Sjeff{ 250272407Shselasky struct mlx4_en_priv *priv = netdev_priv(netdev); 251272407Shselasky int err = 0; 252272407Shselasky u64 config = 0; 253272407Shselasky u64 mask; 254219820Sjeff 255272407Shselasky if ((priv->port < 1) || (priv->port > 2)) { 256272407Shselasky en_err(priv, "Failed to get WoL information\n"); 257272407Shselasky return; 258272407Shselasky } 259272407Shselasky 260272407Shselasky mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 : 261272407Shselasky MLX4_DEV_CAP_FLAG_WOL_PORT2; 262272407Shselasky 263272407Shselasky if (!(priv->mdev->dev->caps.flags & mask)) { 264272407Shselasky wol->supported = 0; 265272407Shselasky wol->wolopts = 0; 266272407Shselasky return; 267272407Shselasky } 268272407Shselasky 269272407Shselasky err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); 270272407Shselasky if (err) { 271272407Shselasky en_err(priv, "Failed to get WoL information\n"); 272272407Shselasky return; 273272407Shselasky } 274272407Shselasky 275272407Shselasky if (config & MLX4_EN_WOL_MAGIC) 276272407Shselasky wol->supported = WAKE_MAGIC; 277272407Shselasky else 278272407Shselasky wol->supported = 0; 279272407Shselasky 280272407Shselasky if (config & MLX4_EN_WOL_ENABLED) 281272407Shselasky wol->wolopts = WAKE_MAGIC; 282272407Shselasky else 283272407Shselasky wol->wolopts = 0; 284219820Sjeff} 285219820Sjeff 286272407Shselaskystatic int mlx4_en_set_wol(struct net_device *netdev, 287272407Shselasky struct ethtool_wolinfo *wol) 288219820Sjeff{ 289272407Shselasky struct mlx4_en_priv *priv = netdev_priv(netdev); 290272407Shselasky u64 config = 0; 291272407Shselasky int err = 0; 292272407Shselasky u64 mask; 293272407Shselasky 294272407Shselasky if ((priv->port < 1) || (priv->port > 2)) 295272407Shselasky return -EOPNOTSUPP; 296272407Shselasky 297272407Shselasky mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 : 298272407Shselasky MLX4_DEV_CAP_FLAG_WOL_PORT2; 299272407Shselasky 300272407Shselasky if (!(priv->mdev->dev->caps.flags & mask)) 301272407Shselasky return -EOPNOTSUPP; 302272407Shselasky 303272407Shselasky if (wol->supported & ~WAKE_MAGIC) 304272407Shselasky return -EINVAL; 305272407Shselasky 306272407Shselasky err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); 307272407Shselasky if (err) { 308272407Shselasky en_err(priv, "Failed to get WoL info, unable to modify\n"); 309272407Shselasky return err; 310272407Shselasky } 311272407Shselasky 312272407Shselasky if (wol->wolopts & WAKE_MAGIC) { 313272407Shselasky config |= MLX4_EN_WOL_DO_MODIFY | MLX4_EN_WOL_ENABLED | 314272407Shselasky MLX4_EN_WOL_MAGIC; 315272407Shselasky } else { 316272407Shselasky config &= ~(MLX4_EN_WOL_ENABLED | MLX4_EN_WOL_MAGIC); 317272407Shselasky config |= MLX4_EN_WOL_DO_MODIFY; 318272407Shselasky } 319272407Shselasky 320272407Shselasky err = mlx4_wol_write(priv->mdev->dev, config, priv->port); 321272407Shselasky if (err) 322272407Shselasky en_err(priv, "Failed to set WoL information\n"); 323272407Shselasky 324272407Shselasky return err; 325272407Shselasky} 326272407Shselasky 327272407Shselaskystruct bitmap_sim_iterator { 328272407Shselasky bool advance_array; 329272407Shselasky unsigned long *stats_bitmap; 330272407Shselasky unsigned int count; 331272407Shselasky unsigned int j; 332272407Shselasky}; 333272407Shselasky 334272407Shselaskystatic inline void bitmap_sim_iterator_init(struct bitmap_sim_iterator *h, 335272407Shselasky unsigned long *stats_bitmap, 336272407Shselasky int count) 337272407Shselasky{ 338272407Shselasky h->j = 0; 339272407Shselasky h->advance_array = !bitmap_empty(stats_bitmap, count); 340272407Shselasky h->count = h->advance_array ? bitmap_weight(stats_bitmap, count) 341272407Shselasky : count; 342272407Shselasky h->stats_bitmap = stats_bitmap; 343272407Shselasky} 344272407Shselasky 345272407Shselaskystatic inline int bitmap_sim_iterator_test(struct bitmap_sim_iterator *h) 346272407Shselasky{ 347272407Shselasky return !h->advance_array ? 1 : test_bit(h->j, h->stats_bitmap); 348272407Shselasky} 349272407Shselasky 350272407Shselaskystatic inline int bitmap_sim_iterator_inc(struct bitmap_sim_iterator *h) 351272407Shselasky{ 352272407Shselasky return h->j++; 353272407Shselasky} 354272407Shselasky 355272407Shselaskystatic inline unsigned int bitmap_sim_iterator_count( 356272407Shselasky struct bitmap_sim_iterator *h) 357272407Shselasky{ 358272407Shselasky return h->count; 359272407Shselasky} 360272407Shselasky 361272407Shselaskyint mlx4_en_get_sset_count(struct net_device *dev, int sset) 362272407Shselasky{ 363219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 364272407Shselasky struct bitmap_sim_iterator it; 365219820Sjeff 366272407Shselasky int num_of_stats = NUM_ALL_STATS - 367272407Shselasky ((priv->mdev->dev->caps.flags2 & 368272407Shselasky MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) ? 0 : NUM_FLOW_STATS); 369272407Shselasky 370272407Shselasky bitmap_sim_iterator_init(&it, priv->stats_bitmap, num_of_stats); 371272407Shselasky 372219820Sjeff switch (sset) { 373219820Sjeff case ETH_SS_STATS: 374272407Shselasky return bitmap_sim_iterator_count(&it) + 375272407Shselasky (priv->tx_ring_num * 2) + 376272407Shselasky#ifdef LL_EXTENDED_STATS 377272407Shselasky (priv->rx_ring_num * 5); 378272407Shselasky#else 379272407Shselasky (priv->rx_ring_num * 2); 380272407Shselasky#endif 381219820Sjeff case ETH_SS_TEST: 382272407Shselasky return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags 383272407Shselasky & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2; 384219820Sjeff default: 385219820Sjeff return -EOPNOTSUPP; 386219820Sjeff } 387219820Sjeff} 388219820Sjeff 389272407Shselaskyvoid mlx4_en_get_ethtool_stats(struct net_device *dev, 390272407Shselasky struct ethtool_stats *stats, u64 *data) 391219820Sjeff{ 392219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 393219820Sjeff int index = 0; 394219820Sjeff int i; 395272407Shselasky struct bitmap_sim_iterator it; 396219820Sjeff 397272407Shselasky int num_of_stats = NUM_ALL_STATS - 398272407Shselasky ((priv->mdev->dev->caps.flags2 & 399272407Shselasky MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) ? 0 : NUM_FLOW_STATS); 400272407Shselasky 401272407Shselasky bitmap_sim_iterator_init(&it, priv->stats_bitmap, num_of_stats); 402272407Shselasky 403272407Shselasky if (!data || !priv->port_up) 404272407Shselasky return; 405272407Shselasky 406219820Sjeff spin_lock_bh(&priv->stats_lock); 407219820Sjeff 408272407Shselasky for (i = 0; i < NUM_PKT_STATS; i++, 409272407Shselasky bitmap_sim_iterator_inc(&it)) 410272407Shselasky if (bitmap_sim_iterator_test(&it)) 411272407Shselasky data[index++] = 412272407Shselasky ((unsigned long *)&priv->pkstats)[i]; 413272407Shselasky for (i = 0; i < NUM_FLOW_STATS; i++, 414272407Shselasky bitmap_sim_iterator_inc(&it)) 415272407Shselasky if (priv->mdev->dev->caps.flags2 & 416272407Shselasky MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) 417272407Shselasky if (bitmap_sim_iterator_test(&it)) 418272407Shselasky data[index++] = 419272407Shselasky ((u64 *)&priv->flowstats)[i]; 420272407Shselasky for (i = 0; i < NUM_VF_STATS; i++, 421272407Shselasky bitmap_sim_iterator_inc(&it)) 422272407Shselasky if (bitmap_sim_iterator_test(&it)) 423272407Shselasky data[index++] = 424272407Shselasky ((unsigned long *)&priv->vf_stats)[i]; 425272407Shselasky for (i = 0; i < NUM_VPORT_STATS; i++, 426272407Shselasky bitmap_sim_iterator_inc(&it)) 427272407Shselasky if (bitmap_sim_iterator_test(&it)) 428272407Shselasky data[index++] = 429272407Shselasky ((unsigned long *)&priv->vport_stats)[i]; 430272407Shselasky for (i = 0; i < NUM_PORT_STATS; i++, 431272407Shselasky bitmap_sim_iterator_inc(&it)) 432272407Shselasky if (bitmap_sim_iterator_test(&it)) 433272407Shselasky data[index++] = 434272407Shselasky ((unsigned long *)&priv->port_stats)[i]; 435219820Sjeff 436219820Sjeff for (i = 0; i < priv->tx_ring_num; i++) { 437272407Shselasky data[index++] = priv->tx_ring[i]->packets; 438272407Shselasky data[index++] = priv->tx_ring[i]->bytes; 439219820Sjeff } 440219820Sjeff for (i = 0; i < priv->rx_ring_num; i++) { 441272407Shselasky data[index++] = priv->rx_ring[i]->packets; 442272407Shselasky data[index++] = priv->rx_ring[i]->bytes; 443272407Shselasky#ifdef LL_EXTENDED_STATS 444272407Shselasky data[index++] = priv->rx_ring[i]->yields; 445272407Shselasky data[index++] = priv->rx_ring[i]->misses; 446272407Shselasky data[index++] = priv->rx_ring[i]->cleaned; 447272407Shselasky#endif 448219820Sjeff } 449219820Sjeff spin_unlock_bh(&priv->stats_lock); 450219820Sjeff 451219820Sjeff} 452219820Sjeff 453272407Shselaskyvoid mlx4_en_restore_ethtool_stats(struct mlx4_en_priv *priv, u64 *data) 454272407Shselasky{ 455272407Shselasky int index = 0; 456272407Shselasky int i; 457272407Shselasky struct bitmap_sim_iterator it; 458272407Shselasky 459272407Shselasky int num_of_stats = NUM_ALL_STATS - 460272407Shselasky ((priv->mdev->dev->caps.flags2 & 461272407Shselasky MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) ? 0 : NUM_FLOW_STATS); 462272407Shselasky 463272407Shselasky bitmap_sim_iterator_init(&it, priv->stats_bitmap, num_of_stats); 464272407Shselasky 465272407Shselasky if (!data || !priv->port_up) 466272407Shselasky return; 467272407Shselasky 468272407Shselasky spin_lock_bh(&priv->stats_lock); 469272407Shselasky 470272407Shselasky for (i = 0; i < NUM_PKT_STATS; i++, 471272407Shselasky bitmap_sim_iterator_inc(&it)) 472272407Shselasky if (bitmap_sim_iterator_test(&it)) 473272407Shselasky ((unsigned long *)&priv->pkstats)[i] = 474272407Shselasky data[index++]; 475272407Shselasky for (i = 0; i < NUM_FLOW_STATS; i++, 476272407Shselasky bitmap_sim_iterator_inc(&it)) 477272407Shselasky if (priv->mdev->dev->caps.flags2 & 478272407Shselasky MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) 479272407Shselasky if (bitmap_sim_iterator_test(&it)) 480272407Shselasky ((u64 *)&priv->flowstats)[i] = 481272407Shselasky data[index++]; 482272407Shselasky for (i = 0; i < NUM_VF_STATS; i++, 483272407Shselasky bitmap_sim_iterator_inc(&it)) 484272407Shselasky if (bitmap_sim_iterator_test(&it)) 485272407Shselasky ((unsigned long *)&priv->vf_stats)[i] = 486272407Shselasky data[index++]; 487272407Shselasky for (i = 0; i < NUM_VPORT_STATS; i++, 488272407Shselasky bitmap_sim_iterator_inc(&it)) 489272407Shselasky if (bitmap_sim_iterator_test(&it)) 490272407Shselasky ((unsigned long *)&priv->vport_stats)[i] = 491272407Shselasky data[index++]; 492272407Shselasky for (i = 0; i < NUM_PORT_STATS; i++, 493272407Shselasky bitmap_sim_iterator_inc(&it)) 494272407Shselasky if (bitmap_sim_iterator_test(&it)) 495272407Shselasky ((unsigned long *)&priv->port_stats)[i] = 496272407Shselasky data[index++]; 497272407Shselasky 498272407Shselasky for (i = 0; i < priv->tx_ring_num; i++) { 499272407Shselasky priv->tx_ring[i]->packets = data[index++]; 500272407Shselasky priv->tx_ring[i]->bytes = data[index++]; 501272407Shselasky } 502272407Shselasky for (i = 0; i < priv->rx_ring_num; i++) { 503272407Shselasky priv->rx_ring[i]->packets = data[index++]; 504272407Shselasky priv->rx_ring[i]->bytes = data[index++]; 505272407Shselasky } 506272407Shselasky spin_unlock_bh(&priv->stats_lock); 507272407Shselasky} 508272407Shselasky 509219820Sjeffstatic void mlx4_en_self_test(struct net_device *dev, 510219820Sjeff struct ethtool_test *etest, u64 *buf) 511219820Sjeff{ 512219820Sjeff mlx4_en_ex_selftest(dev, &etest->flags, buf); 513219820Sjeff} 514219820Sjeff 515219820Sjeffstatic void mlx4_en_get_strings(struct net_device *dev, 516219820Sjeff uint32_t stringset, uint8_t *data) 517219820Sjeff{ 518219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 519219820Sjeff int index = 0; 520272407Shselasky int i, k; 521272407Shselasky struct bitmap_sim_iterator it; 522219820Sjeff 523272407Shselasky int num_of_stats = NUM_ALL_STATS - 524272407Shselasky ((priv->mdev->dev->caps.flags2 & 525272407Shselasky MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) ? 0 : NUM_FLOW_STATS); 526272407Shselasky 527272407Shselasky bitmap_sim_iterator_init(&it, priv->stats_bitmap, num_of_stats); 528272407Shselasky 529219820Sjeff switch (stringset) { 530219820Sjeff case ETH_SS_TEST: 531219820Sjeff for (i = 0; i < MLX4_EN_NUM_SELF_TEST - 2; i++) 532219820Sjeff strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); 533272407Shselasky if (priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) 534219820Sjeff for (; i < MLX4_EN_NUM_SELF_TEST; i++) 535219820Sjeff strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); 536219820Sjeff break; 537219820Sjeff 538219820Sjeff case ETH_SS_STATS: 539219820Sjeff /* Add main counters */ 540272407Shselasky for (i = 0; i < NUM_PKT_STATS; i++, 541272407Shselasky bitmap_sim_iterator_inc(&it)) 542272407Shselasky if (bitmap_sim_iterator_test(&it)) 543272407Shselasky strcpy(data + (index++) * ETH_GSTRING_LEN, 544272407Shselasky main_strings[i]); 545272407Shselasky 546272407Shselasky for (k = 0; k < NUM_FLOW_STATS; k++, 547272407Shselasky bitmap_sim_iterator_inc(&it)) 548272407Shselasky if (priv->mdev->dev->caps.flags2 & 549272407Shselasky MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) 550272407Shselasky if (bitmap_sim_iterator_test(&it)) 551272407Shselasky strcpy(data + (index++) * 552272407Shselasky ETH_GSTRING_LEN, 553272407Shselasky main_strings[i + k]); 554272407Shselasky 555272407Shselasky for (; (i + k) < num_of_stats; i++, 556272407Shselasky bitmap_sim_iterator_inc(&it)) 557272407Shselasky if (bitmap_sim_iterator_test(&it)) 558272407Shselasky strcpy(data + (index++) * ETH_GSTRING_LEN, 559272407Shselasky main_strings[i + k]); 560272407Shselasky 561219820Sjeff for (i = 0; i < priv->tx_ring_num; i++) { 562219820Sjeff sprintf(data + (index++) * ETH_GSTRING_LEN, 563219820Sjeff "tx%d_packets", i); 564219820Sjeff sprintf(data + (index++) * ETH_GSTRING_LEN, 565219820Sjeff "tx%d_bytes", i); 566219820Sjeff } 567219820Sjeff for (i = 0; i < priv->rx_ring_num; i++) { 568219820Sjeff sprintf(data + (index++) * ETH_GSTRING_LEN, 569219820Sjeff "rx%d_packets", i); 570219820Sjeff sprintf(data + (index++) * ETH_GSTRING_LEN, 571219820Sjeff "rx%d_bytes", i); 572272407Shselasky#ifdef LL_EXTENDED_STATS 573272407Shselasky sprintf(data + (index++) * ETH_GSTRING_LEN, 574272407Shselasky "rx%d_napi_yield", i); 575272407Shselasky sprintf(data + (index++) * ETH_GSTRING_LEN, 576272407Shselasky "rx%d_misses", i); 577272407Shselasky sprintf(data + (index++) * ETH_GSTRING_LEN, 578272407Shselasky "rx%d_cleaned", i); 579272407Shselasky#endif 580219820Sjeff } 581219820Sjeff break; 582219820Sjeff } 583219820Sjeff} 584219820Sjeff 585272407Shselaskystatic u32 mlx4_en_autoneg_get(struct net_device *dev) 586272407Shselasky{ 587272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 588272407Shselasky struct mlx4_en_dev *mdev = priv->mdev; 589272407Shselasky u32 autoneg = AUTONEG_DISABLE; 590272407Shselasky 591272407Shselasky if ((mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETH_BACKPL_AN_REP) && 592272407Shselasky priv->port_state.autoneg) { 593272407Shselasky autoneg = AUTONEG_ENABLE; 594272407Shselasky } 595272407Shselasky 596272407Shselasky return autoneg; 597272407Shselasky} 598272407Shselasky 599219820Sjeffstatic int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) 600219820Sjeff{ 601219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 602219820Sjeff int trans_type; 603219820Sjeff 604272407Shselasky /* SUPPORTED_1000baseT_Half isn't supported */ 605272407Shselasky cmd->supported = SUPPORTED_1000baseT_Full 606272407Shselasky |SUPPORTED_10000baseT_Full; 607219820Sjeff 608272407Shselasky cmd->advertising = ADVERTISED_1000baseT_Full 609272407Shselasky |ADVERTISED_10000baseT_Full; 610272407Shselasky 611272407Shselasky cmd->supported |= SUPPORTED_1000baseKX_Full 612272407Shselasky |SUPPORTED_10000baseKX4_Full 613272407Shselasky |SUPPORTED_10000baseKR_Full 614272407Shselasky |SUPPORTED_10000baseR_FEC 615272407Shselasky |SUPPORTED_40000baseKR4_Full 616272407Shselasky |SUPPORTED_40000baseCR4_Full 617272407Shselasky |SUPPORTED_40000baseSR4_Full 618272407Shselasky |SUPPORTED_40000baseLR4_Full; 619272407Shselasky 620272407Shselasky /* ADVERTISED_1000baseT_Half isn't advertised */ 621272407Shselasky cmd->advertising |= ADVERTISED_1000baseKX_Full 622272407Shselasky |ADVERTISED_10000baseKX4_Full 623272407Shselasky |ADVERTISED_10000baseKR_Full 624272407Shselasky |ADVERTISED_10000baseR_FEC 625272407Shselasky |ADVERTISED_40000baseKR4_Full 626272407Shselasky |ADVERTISED_40000baseCR4_Full 627272407Shselasky |ADVERTISED_40000baseSR4_Full 628272407Shselasky |ADVERTISED_40000baseLR4_Full; 629272407Shselasky 630219820Sjeff if (mlx4_en_QUERY_PORT(priv->mdev, priv->port)) 631219820Sjeff return -ENOMEM; 632219820Sjeff 633272407Shselasky cmd->autoneg = mlx4_en_autoneg_get(dev); 634272407Shselasky if (cmd->autoneg == AUTONEG_ENABLE) { 635272407Shselasky cmd->supported |= SUPPORTED_Autoneg; 636272407Shselasky cmd->advertising |= ADVERTISED_Autoneg; 637272407Shselasky } 638272407Shselasky 639219820Sjeff trans_type = priv->port_state.transciver; 640219820Sjeff if (netif_carrier_ok(dev)) { 641272407Shselasky ethtool_cmd_speed_set(cmd, priv->port_state.link_speed); 642219820Sjeff cmd->duplex = DUPLEX_FULL; 643219820Sjeff } else { 644272407Shselasky ethtool_cmd_speed_set(cmd, -1); 645219820Sjeff cmd->duplex = -1; 646219820Sjeff } 647219820Sjeff 648219820Sjeff if (trans_type > 0 && trans_type <= 0xC) { 649219820Sjeff cmd->port = PORT_FIBRE; 650219820Sjeff cmd->transceiver = XCVR_EXTERNAL; 651219820Sjeff cmd->supported |= SUPPORTED_FIBRE; 652219820Sjeff cmd->advertising |= ADVERTISED_FIBRE; 653219820Sjeff } else if (trans_type == 0x80 || trans_type == 0) { 654219820Sjeff cmd->port = PORT_TP; 655219820Sjeff cmd->transceiver = XCVR_INTERNAL; 656219820Sjeff cmd->supported |= SUPPORTED_TP; 657219820Sjeff cmd->advertising |= ADVERTISED_TP; 658219820Sjeff } else { 659219820Sjeff cmd->port = -1; 660219820Sjeff cmd->transceiver = -1; 661219820Sjeff } 662219820Sjeff return 0; 663219820Sjeff} 664219820Sjeff 665272407Shselaskystatic const char *mlx4_en_duplex_to_string(int duplex) 666272407Shselasky{ 667272407Shselasky switch (duplex) { 668272407Shselasky case DUPLEX_FULL: 669272407Shselasky return "FULL"; 670272407Shselasky case DUPLEX_HALF: 671272407Shselasky return "HALF"; 672272407Shselasky default: 673272407Shselasky break; 674272407Shselasky } 675272407Shselasky return "UNKNOWN"; 676272407Shselasky} 677272407Shselasky 678219820Sjeffstatic int mlx4_en_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) 679219820Sjeff{ 680272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 681272407Shselasky struct mlx4_en_port_state *port_state = &priv->port_state; 682219820Sjeff 683272407Shselasky if ((cmd->autoneg != port_state->autoneg) || 684272407Shselasky (ethtool_cmd_speed(cmd) != port_state->link_speed) || 685272407Shselasky (cmd->duplex != DUPLEX_FULL)) { 686272407Shselasky en_info(priv, "Changing port state properties (auto-negotiation" 687272407Shselasky " , speed/duplex) is not supported. Current:" 688272407Shselasky " auto-negotiation=%d speed/duplex=%d/%s\n", 689272407Shselasky port_state->autoneg, port_state->link_speed, 690272407Shselasky mlx4_en_duplex_to_string(DUPLEX_FULL)); 691272407Shselasky return -EOPNOTSUPP; 692272407Shselasky } 693272407Shselasky 694272407Shselasky /* User provided same port state properties that are currently set. 695272407Shselasky * Nothing to change 696272407Shselasky */ 697219820Sjeff return 0; 698219820Sjeff} 699219820Sjeff 700219820Sjeffstatic int mlx4_en_get_coalesce(struct net_device *dev, 701219820Sjeff struct ethtool_coalesce *coal) 702219820Sjeff{ 703219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 704219820Sjeff 705272407Shselasky coal->tx_coalesce_usecs = priv->tx_usecs; 706272407Shselasky coal->tx_max_coalesced_frames = priv->tx_frames; 707219820Sjeff coal->rx_coalesce_usecs = priv->rx_usecs; 708219820Sjeff coal->rx_max_coalesced_frames = priv->rx_frames; 709219820Sjeff 710219820Sjeff coal->pkt_rate_low = priv->pkt_rate_low; 711219820Sjeff coal->rx_coalesce_usecs_low = priv->rx_usecs_low; 712219820Sjeff coal->pkt_rate_high = priv->pkt_rate_high; 713219820Sjeff coal->rx_coalesce_usecs_high = priv->rx_usecs_high; 714219820Sjeff coal->rate_sample_interval = priv->sample_interval; 715219820Sjeff coal->use_adaptive_rx_coalesce = priv->adaptive_rx_coal; 716219820Sjeff return 0; 717219820Sjeff} 718219820Sjeff 719219820Sjeffstatic int mlx4_en_set_coalesce(struct net_device *dev, 720219820Sjeff struct ethtool_coalesce *coal) 721219820Sjeff{ 722219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 723219820Sjeff int err, i; 724219820Sjeff 725219820Sjeff priv->rx_frames = (coal->rx_max_coalesced_frames == 726219820Sjeff MLX4_EN_AUTO_CONF) ? 727219820Sjeff MLX4_EN_RX_COAL_TARGET / 728219820Sjeff priv->dev->mtu + 1 : 729219820Sjeff coal->rx_max_coalesced_frames; 730219820Sjeff priv->rx_usecs = (coal->rx_coalesce_usecs == 731219820Sjeff MLX4_EN_AUTO_CONF) ? 732219820Sjeff MLX4_EN_RX_COAL_TIME : 733219820Sjeff coal->rx_coalesce_usecs; 734219820Sjeff 735272407Shselasky /* Setting TX coalescing parameters */ 736272407Shselasky if (coal->tx_coalesce_usecs != priv->tx_usecs || 737272407Shselasky coal->tx_max_coalesced_frames != priv->tx_frames) { 738272407Shselasky priv->tx_usecs = coal->tx_coalesce_usecs; 739272407Shselasky priv->tx_frames = coal->tx_max_coalesced_frames; 740272407Shselasky if (priv->port_up) { 741272407Shselasky for (i = 0; i < priv->tx_ring_num; i++) { 742272407Shselasky priv->tx_cq[i]->moder_cnt = priv->tx_frames; 743272407Shselasky priv->tx_cq[i]->moder_time = priv->tx_usecs; 744272407Shselasky if (mlx4_en_set_cq_moder(priv, priv->tx_cq[i])) 745272407Shselasky en_warn(priv, "Failed changing moderation for TX cq %d\n", i); 746272407Shselasky } 747272407Shselasky } 748272407Shselasky } 749272407Shselasky 750219820Sjeff /* Set adaptive coalescing params */ 751219820Sjeff priv->pkt_rate_low = coal->pkt_rate_low; 752219820Sjeff priv->rx_usecs_low = coal->rx_coalesce_usecs_low; 753219820Sjeff priv->pkt_rate_high = coal->pkt_rate_high; 754219820Sjeff priv->rx_usecs_high = coal->rx_coalesce_usecs_high; 755219820Sjeff priv->sample_interval = coal->rate_sample_interval; 756219820Sjeff priv->adaptive_rx_coal = coal->use_adaptive_rx_coalesce; 757219820Sjeff if (priv->adaptive_rx_coal) 758219820Sjeff return 0; 759219820Sjeff 760272407Shselasky if (priv->port_up) { 761272407Shselasky for (i = 0; i < priv->rx_ring_num; i++) { 762272407Shselasky priv->rx_cq[i]->moder_cnt = priv->rx_frames; 763272407Shselasky priv->rx_cq[i]->moder_time = priv->rx_usecs; 764272407Shselasky priv->last_moder_time[i] = MLX4_EN_AUTO_CONF; 765272407Shselasky err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]); 766272407Shselasky if (err) 767272407Shselasky return err; 768272407Shselasky } 769219820Sjeff } 770272407Shselasky 771219820Sjeff return 0; 772219820Sjeff} 773219820Sjeff 774219820Sjeffstatic int mlx4_en_set_pauseparam(struct net_device *dev, 775219820Sjeff struct ethtool_pauseparam *pause) 776219820Sjeff{ 777219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 778219820Sjeff struct mlx4_en_dev *mdev = priv->mdev; 779219820Sjeff int err; 780219820Sjeff 781272407Shselasky if (pause->autoneg) 782272407Shselasky return -EOPNOTSUPP; 783272407Shselasky 784219820Sjeff priv->prof->tx_pause = pause->tx_pause != 0; 785219820Sjeff priv->prof->rx_pause = pause->rx_pause != 0; 786219820Sjeff err = mlx4_SET_PORT_general(mdev->dev, priv->port, 787272407Shselasky priv->rx_skb_size + ETH_FCS_LEN, 788219820Sjeff priv->prof->tx_pause, 789219820Sjeff priv->prof->tx_ppp, 790219820Sjeff priv->prof->rx_pause, 791219820Sjeff priv->prof->rx_ppp); 792219820Sjeff if (err) 793219820Sjeff en_err(priv, "Failed setting pause params\n"); 794219820Sjeff 795219820Sjeff return err; 796219820Sjeff} 797219820Sjeff 798219820Sjeffstatic void mlx4_en_get_pauseparam(struct net_device *dev, 799219820Sjeff struct ethtool_pauseparam *pause) 800219820Sjeff{ 801219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 802219820Sjeff 803219820Sjeff pause->tx_pause = priv->prof->tx_pause; 804219820Sjeff pause->rx_pause = priv->prof->rx_pause; 805272407Shselasky pause->autoneg = mlx4_en_autoneg_get(dev); 806219820Sjeff} 807219820Sjeff 808272407Shselasky/* rtnl lock must be taken before calling */ 809272407Shselaskyint mlx4_en_pre_config(struct mlx4_en_priv *priv) 810272407Shselasky{ 811272407Shselasky#ifdef CONFIG_RFS_ACCEL 812272407Shselasky struct cpu_rmap *rmap; 813272407Shselasky 814272407Shselasky if (!priv->dev->rx_cpu_rmap) 815272407Shselasky return 0; 816272407Shselasky 817272407Shselasky /* Disable RFS events 818272407Shselasky * Must have all RFS jobs flushed before freeing resources 819272407Shselasky */ 820272407Shselasky rmap = priv->dev->rx_cpu_rmap; 821272407Shselasky priv->dev->rx_cpu_rmap = NULL; 822272407Shselasky 823272407Shselasky rtnl_unlock(); 824272407Shselasky free_irq_cpu_rmap(rmap); 825272407Shselasky rtnl_lock(); 826272407Shselasky 827272407Shselasky if (priv->dev->rx_cpu_rmap) 828272407Shselasky return -EBUSY; /* another configuration completed while lock 829272407Shselasky * was free 830272407Shselasky */ 831272407Shselasky 832272407Shselasky /* Make sure all currently running filter_work are being processed 833272407Shselasky * Other work will return immediatly because of disable_rfs 834272407Shselasky */ 835272407Shselasky flush_workqueue(priv->mdev->workqueue); 836272407Shselasky 837272407Shselasky#endif 838272407Shselasky 839272407Shselasky return 0; 840272407Shselasky} 841272407Shselasky 842219820Sjeffstatic int mlx4_en_set_ringparam(struct net_device *dev, 843219820Sjeff struct ethtool_ringparam *param) 844219820Sjeff{ 845219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 846219820Sjeff struct mlx4_en_dev *mdev = priv->mdev; 847219820Sjeff u32 rx_size, tx_size; 848219820Sjeff int port_up = 0; 849219820Sjeff int err = 0; 850272407Shselasky int i, n_stats; 851272407Shselasky u64 *data = NULL; 852219820Sjeff 853272407Shselasky if (!priv->port_up) 854272407Shselasky return -ENOMEM; 855272407Shselasky 856219820Sjeff if (param->rx_jumbo_pending || param->rx_mini_pending) 857219820Sjeff return -EINVAL; 858219820Sjeff 859219820Sjeff rx_size = roundup_pow_of_two(param->rx_pending); 860219820Sjeff rx_size = max_t(u32, rx_size, MLX4_EN_MIN_RX_SIZE); 861219820Sjeff rx_size = min_t(u32, rx_size, MLX4_EN_MAX_RX_SIZE); 862219820Sjeff tx_size = roundup_pow_of_two(param->tx_pending); 863219820Sjeff tx_size = max_t(u32, tx_size, MLX4_EN_MIN_TX_SIZE); 864219820Sjeff tx_size = min_t(u32, tx_size, MLX4_EN_MAX_TX_SIZE); 865219820Sjeff 866272407Shselasky if (rx_size == (priv->port_up ? priv->rx_ring[0]->actual_size : 867272407Shselasky priv->rx_ring[0]->size) && 868272407Shselasky tx_size == priv->tx_ring[0]->size) 869219820Sjeff return 0; 870272407Shselasky err = mlx4_en_pre_config(priv); 871272407Shselasky if (err) 872272407Shselasky return err; 873219820Sjeff 874219820Sjeff mutex_lock(&mdev->state_lock); 875219820Sjeff if (priv->port_up) { 876219820Sjeff port_up = 1; 877219820Sjeff mlx4_en_stop_port(dev); 878219820Sjeff } 879219820Sjeff 880272407Shselasky /* Cache port statistics */ 881272407Shselasky n_stats = mlx4_en_get_sset_count(dev, ETH_SS_STATS); 882272407Shselasky if (n_stats > 0) { 883272407Shselasky data = kmalloc(n_stats * sizeof(u64), GFP_KERNEL); 884272407Shselasky if (data) 885272407Shselasky mlx4_en_get_ethtool_stats(dev, NULL, data); 886272407Shselasky } 887272407Shselasky 888219820Sjeff mlx4_en_free_resources(priv); 889219820Sjeff 890219820Sjeff priv->prof->tx_ring_size = tx_size; 891219820Sjeff priv->prof->rx_ring_size = rx_size; 892219820Sjeff 893219820Sjeff err = mlx4_en_alloc_resources(priv); 894219820Sjeff if (err) { 895219820Sjeff en_err(priv, "Failed reallocating port resources\n"); 896219820Sjeff goto out; 897219820Sjeff } 898272407Shselasky 899272407Shselasky /* Restore port statistics */ 900272407Shselasky if (n_stats > 0 && data) 901272407Shselasky mlx4_en_restore_ethtool_stats(priv, data); 902272407Shselasky 903219820Sjeff if (port_up) { 904219820Sjeff err = mlx4_en_start_port(dev); 905272407Shselasky if (err) { 906219820Sjeff en_err(priv, "Failed starting port\n"); 907272407Shselasky goto out; 908272407Shselasky } 909219820Sjeff 910272407Shselasky for (i = 0; i < priv->rx_ring_num; i++) { 911272407Shselasky priv->rx_cq[i]->moder_cnt = priv->rx_frames; 912272407Shselasky priv->rx_cq[i]->moder_time = priv->rx_usecs; 913272407Shselasky priv->last_moder_time[i] = MLX4_EN_AUTO_CONF; 914272407Shselasky err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]); 915272407Shselasky if (err) 916272407Shselasky goto out; 917272407Shselasky } 918257867Salfred } 919257867Salfred 920219820Sjeffout: 921272407Shselasky kfree(data); 922219820Sjeff mutex_unlock(&mdev->state_lock); 923219820Sjeff return err; 924219820Sjeff} 925219820Sjeff 926219820Sjeffstatic void mlx4_en_get_ringparam(struct net_device *dev, 927219820Sjeff struct ethtool_ringparam *param) 928219820Sjeff{ 929219820Sjeff struct mlx4_en_priv *priv = netdev_priv(dev); 930219820Sjeff 931272407Shselasky if (!priv->port_up) 932272407Shselasky return; 933272407Shselasky 934219820Sjeff memset(param, 0, sizeof(*param)); 935219820Sjeff param->rx_max_pending = MLX4_EN_MAX_RX_SIZE; 936219820Sjeff param->tx_max_pending = MLX4_EN_MAX_TX_SIZE; 937219820Sjeff param->rx_pending = priv->port_up ? 938272407Shselasky priv->rx_ring[0]->actual_size : priv->rx_ring[0]->size; 939272407Shselasky param->tx_pending = priv->tx_ring[0]->size; 940219820Sjeff} 941219820Sjeff 942272407Shselaskystatic u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev) 943272407Shselasky{ 944272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 945272407Shselasky 946272407Shselasky return priv->rx_ring_num; 947272407Shselasky} 948272407Shselasky 949272407Shselaskystatic int mlx4_en_get_rxfh_indir(struct net_device *dev, u32 *ring_index) 950272407Shselasky{ 951272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 952272407Shselasky struct mlx4_en_rss_map *rss_map = &priv->rss_map; 953272407Shselasky int rss_rings; 954272407Shselasky size_t n = priv->rx_ring_num; 955272407Shselasky int err = 0; 956272407Shselasky 957272407Shselasky rss_rings = priv->prof->rss_rings ?: priv->rx_ring_num; 958272407Shselasky rss_rings = 1 << ilog2(rss_rings); 959272407Shselasky 960272407Shselasky while (n--) { 961272407Shselasky ring_index[n] = rss_map->qps[n % rss_rings].qpn - 962272407Shselasky rss_map->base_qpn; 963272407Shselasky } 964272407Shselasky 965272407Shselasky return err; 966272407Shselasky} 967272407Shselasky 968272407Shselaskystatic int mlx4_en_set_rxfh_indir(struct net_device *dev, 969272407Shselasky const u32 *ring_index) 970272407Shselasky{ 971272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 972272407Shselasky struct mlx4_en_dev *mdev = priv->mdev; 973272407Shselasky int port_up = 0; 974272407Shselasky int err = 0; 975272407Shselasky int i; 976272407Shselasky int rss_rings = 0; 977272407Shselasky 978272407Shselasky /* Calculate RSS table size and make sure flows are spread evenly 979272407Shselasky * between rings 980272407Shselasky */ 981272407Shselasky for (i = 0; i < priv->rx_ring_num; i++) { 982272407Shselasky if (i > 0 && !ring_index[i] && !rss_rings) 983272407Shselasky rss_rings = i; 984272407Shselasky 985272407Shselasky if (ring_index[i] != (i % (rss_rings ?: priv->rx_ring_num))) 986272407Shselasky return -EINVAL; 987272407Shselasky } 988272407Shselasky 989272407Shselasky if (!rss_rings) 990272407Shselasky rss_rings = priv->rx_ring_num; 991272407Shselasky 992272407Shselasky /* RSS table size must be an order of 2 */ 993272407Shselasky if (!is_power_of_2(rss_rings)) 994272407Shselasky return -EINVAL; 995272407Shselasky 996272407Shselasky mutex_lock(&mdev->state_lock); 997272407Shselasky if (priv->port_up) { 998272407Shselasky port_up = 1; 999272407Shselasky mlx4_en_stop_port(dev); 1000272407Shselasky } 1001272407Shselasky 1002272407Shselasky priv->prof->rss_rings = rss_rings; 1003272407Shselasky 1004272407Shselasky if (port_up) { 1005272407Shselasky err = mlx4_en_start_port(dev); 1006272407Shselasky if (err) 1007272407Shselasky en_err(priv, "Failed starting port\n"); 1008272407Shselasky } 1009272407Shselasky 1010272407Shselasky mutex_unlock(&mdev->state_lock); 1011272407Shselasky return err; 1012272407Shselasky} 1013272407Shselasky 1014272407Shselasky#define all_zeros_or_all_ones(field) \ 1015272407Shselasky ((field) == 0 || (field) == (__force typeof(field))-1) 1016272407Shselasky 1017272407Shselaskystatic int mlx4_en_validate_flow(struct net_device *dev, 1018272407Shselasky struct mlx4_ethtool_rxnfc *cmd) 1019272407Shselasky{ 1020272407Shselasky struct ethtool_usrip4_spec *l3_mask; 1021272407Shselasky struct ethtool_tcpip4_spec *l4_mask; 1022272407Shselasky struct ethhdr *eth_mask; 1023272407Shselasky 1024272407Shselasky if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) 1025272407Shselasky return -EINVAL; 1026272407Shselasky 1027272407Shselasky if (cmd->fs.flow_type & FLOW_MAC_EXT) { 1028272407Shselasky /* dest mac mask must be ff:ff:ff:ff:ff:ff */ 1029272407Shselasky if (!is_broadcast_ether_addr(cmd->fs.m_ext.h_dest)) 1030272407Shselasky return -EINVAL; 1031272407Shselasky } 1032272407Shselasky 1033272407Shselasky switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { 1034272407Shselasky case TCP_V4_FLOW: 1035272407Shselasky case UDP_V4_FLOW: 1036272407Shselasky if (cmd->fs.m_u.tcp_ip4_spec.tos) 1037272407Shselasky return -EINVAL; 1038272407Shselasky l4_mask = &cmd->fs.m_u.tcp_ip4_spec; 1039272407Shselasky /* don't allow mask which isn't all 0 or 1 */ 1040272407Shselasky if (!all_zeros_or_all_ones(l4_mask->ip4src) || 1041272407Shselasky !all_zeros_or_all_ones(l4_mask->ip4dst) || 1042272407Shselasky !all_zeros_or_all_ones(l4_mask->psrc) || 1043272407Shselasky !all_zeros_or_all_ones(l4_mask->pdst)) 1044272407Shselasky return -EINVAL; 1045272407Shselasky break; 1046272407Shselasky case IP_USER_FLOW: 1047272407Shselasky l3_mask = &cmd->fs.m_u.usr_ip4_spec; 1048272407Shselasky if (l3_mask->l4_4_bytes || l3_mask->tos || l3_mask->proto || 1049272407Shselasky cmd->fs.h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4 || 1050272407Shselasky (!l3_mask->ip4src && !l3_mask->ip4dst) || 1051272407Shselasky !all_zeros_or_all_ones(l3_mask->ip4src) || 1052272407Shselasky !all_zeros_or_all_ones(l3_mask->ip4dst)) 1053272407Shselasky return -EINVAL; 1054272407Shselasky break; 1055272407Shselasky case ETHER_FLOW: 1056272407Shselasky eth_mask = &cmd->fs.m_u.ether_spec; 1057272407Shselasky /* source mac mask must not be set */ 1058272407Shselasky if (!is_zero_ether_addr(eth_mask->h_source)) 1059272407Shselasky return -EINVAL; 1060272407Shselasky 1061272407Shselasky /* dest mac mask must be ff:ff:ff:ff:ff:ff */ 1062272407Shselasky if (!is_broadcast_ether_addr(eth_mask->h_dest)) 1063272407Shselasky return -EINVAL; 1064272407Shselasky 1065272407Shselasky if (!all_zeros_or_all_ones(eth_mask->h_proto)) 1066272407Shselasky return -EINVAL; 1067272407Shselasky break; 1068272407Shselasky default: 1069272407Shselasky return -EINVAL; 1070272407Shselasky } 1071272407Shselasky 1072272407Shselasky if ((cmd->fs.flow_type & FLOW_EXT)) { 1073272407Shselasky if (cmd->fs.m_ext.vlan_etype || 1074272407Shselasky !(cmd->fs.m_ext.vlan_tci == 0 || 1075272407Shselasky cmd->fs.m_ext.vlan_tci == cpu_to_be16(0xfff))) 1076272407Shselasky return -EINVAL; 1077272407Shselasky if (cmd->fs.m_ext.vlan_tci) { 1078272407Shselasky if (be16_to_cpu(cmd->fs.h_ext.vlan_tci) < 1079272407Shselasky VLAN_MIN_VALUE || 1080272407Shselasky be16_to_cpu(cmd->fs.h_ext.vlan_tci) > 1081272407Shselasky VLAN_MAX_VALUE) 1082272407Shselasky return -EINVAL; 1083272407Shselasky } 1084272407Shselasky } 1085272407Shselasky 1086272407Shselasky return 0; 1087272407Shselasky} 1088272407Shselasky 1089272407Shselaskystatic int mlx4_en_ethtool_add_mac_rule(struct mlx4_ethtool_rxnfc *cmd, 1090272407Shselasky struct list_head *rule_list_h, 1091272407Shselasky struct mlx4_spec_list *spec_l2, 1092272407Shselasky unsigned char *mac) 1093272407Shselasky{ 1094272407Shselasky int err = 0; 1095272407Shselasky __be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16); 1096272407Shselasky 1097272407Shselasky spec_l2->id = MLX4_NET_TRANS_RULE_ID_ETH; 1098272407Shselasky memcpy(spec_l2->eth.dst_mac_msk, &mac_msk, ETH_ALEN); 1099272407Shselasky memcpy(spec_l2->eth.dst_mac, mac, ETH_ALEN); 1100272407Shselasky 1101272407Shselasky if ((cmd->fs.flow_type & FLOW_EXT) && cmd->fs.m_ext.vlan_tci) { 1102272407Shselasky spec_l2->eth.vlan_id = cmd->fs.h_ext.vlan_tci; 1103272407Shselasky spec_l2->eth.vlan_id_msk = cpu_to_be16(0xfff); 1104272407Shselasky } 1105272407Shselasky 1106272407Shselasky list_add_tail(&spec_l2->list, rule_list_h); 1107272407Shselasky 1108272407Shselasky return err; 1109272407Shselasky} 1110272407Shselasky 1111272407Shselaskystatic int mlx4_en_ethtool_add_mac_rule_by_ipv4(struct mlx4_en_priv *priv, 1112272407Shselasky struct mlx4_ethtool_rxnfc *cmd, 1113272407Shselasky struct list_head *rule_list_h, 1114272407Shselasky struct mlx4_spec_list *spec_l2, 1115272407Shselasky __be32 ipv4_dst) 1116272407Shselasky{ 1117272407Shselasky unsigned char mac[ETH_ALEN]; 1118272407Shselasky 1119272407Shselasky if (!ipv4_is_multicast(ipv4_dst)) { 1120272407Shselasky if (cmd->fs.flow_type & FLOW_MAC_EXT) 1121272407Shselasky memcpy(&mac, cmd->fs.h_ext.h_dest, ETH_ALEN); 1122272407Shselasky else 1123272407Shselasky memcpy(&mac, priv->dev->dev_addr, ETH_ALEN); 1124272407Shselasky } else { 1125272407Shselasky ip_eth_mc_map(ipv4_dst, mac); 1126272407Shselasky } 1127272407Shselasky 1128272407Shselasky return mlx4_en_ethtool_add_mac_rule(cmd, rule_list_h, spec_l2, &mac[0]); 1129272407Shselasky} 1130272407Shselasky 1131272407Shselaskystatic int add_ip_rule(struct mlx4_en_priv *priv, 1132272407Shselasky struct mlx4_ethtool_rxnfc *cmd, 1133272407Shselasky struct list_head *list_h) 1134272407Shselasky{ 1135272407Shselasky struct mlx4_spec_list *spec_l2 = NULL; 1136272407Shselasky struct mlx4_spec_list *spec_l3 = NULL; 1137272407Shselasky struct ethtool_usrip4_spec *l3_mask = &cmd->fs.m_u.usr_ip4_spec; 1138272407Shselasky 1139272407Shselasky spec_l3 = kzalloc(sizeof(*spec_l3), GFP_KERNEL); 1140272407Shselasky spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL); 1141272407Shselasky if (!spec_l2 || !spec_l3) { 1142272407Shselasky en_err(priv, "Fail to alloc ethtool rule.\n"); 1143272407Shselasky kfree(spec_l2); 1144272407Shselasky kfree(spec_l3); 1145272407Shselasky return -ENOMEM; 1146272407Shselasky } 1147272407Shselasky 1148272407Shselasky mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h, spec_l2, 1149272407Shselasky cmd->fs.h_u. 1150272407Shselasky usr_ip4_spec.ip4dst); 1151272407Shselasky spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4; 1152272407Shselasky spec_l3->ipv4.src_ip = cmd->fs.h_u.usr_ip4_spec.ip4src; 1153272407Shselasky if (l3_mask->ip4src) 1154272407Shselasky spec_l3->ipv4.src_ip_msk = MLX4_BE_WORD_MASK; 1155272407Shselasky spec_l3->ipv4.dst_ip = cmd->fs.h_u.usr_ip4_spec.ip4dst; 1156272407Shselasky if (l3_mask->ip4dst) 1157272407Shselasky spec_l3->ipv4.dst_ip_msk = MLX4_BE_WORD_MASK; 1158272407Shselasky list_add_tail(&spec_l3->list, list_h); 1159272407Shselasky 1160272407Shselasky return 0; 1161272407Shselasky} 1162272407Shselasky 1163272407Shselaskystatic int add_tcp_udp_rule(struct mlx4_en_priv *priv, 1164272407Shselasky struct mlx4_ethtool_rxnfc *cmd, 1165272407Shselasky struct list_head *list_h, int proto) 1166272407Shselasky{ 1167272407Shselasky struct mlx4_spec_list *spec_l2 = NULL; 1168272407Shselasky struct mlx4_spec_list *spec_l3 = NULL; 1169272407Shselasky struct mlx4_spec_list *spec_l4 = NULL; 1170272407Shselasky struct ethtool_tcpip4_spec *l4_mask = &cmd->fs.m_u.tcp_ip4_spec; 1171272407Shselasky 1172272407Shselasky spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL); 1173272407Shselasky spec_l3 = kzalloc(sizeof(*spec_l3), GFP_KERNEL); 1174272407Shselasky spec_l4 = kzalloc(sizeof(*spec_l4), GFP_KERNEL); 1175272407Shselasky if (!spec_l2 || !spec_l3 || !spec_l4) { 1176272407Shselasky en_err(priv, "Fail to alloc ethtool rule.\n"); 1177272407Shselasky kfree(spec_l2); 1178272407Shselasky kfree(spec_l3); 1179272407Shselasky kfree(spec_l4); 1180272407Shselasky return -ENOMEM; 1181272407Shselasky } 1182272407Shselasky 1183272407Shselasky spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4; 1184272407Shselasky 1185272407Shselasky if (proto == TCP_V4_FLOW) { 1186272407Shselasky mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h, 1187272407Shselasky spec_l2, 1188272407Shselasky cmd->fs.h_u. 1189272407Shselasky tcp_ip4_spec.ip4dst); 1190272407Shselasky spec_l4->id = MLX4_NET_TRANS_RULE_ID_TCP; 1191272407Shselasky spec_l3->ipv4.src_ip = cmd->fs.h_u.tcp_ip4_spec.ip4src; 1192272407Shselasky spec_l3->ipv4.dst_ip = cmd->fs.h_u.tcp_ip4_spec.ip4dst; 1193272407Shselasky spec_l4->tcp_udp.src_port = cmd->fs.h_u.tcp_ip4_spec.psrc; 1194272407Shselasky spec_l4->tcp_udp.dst_port = cmd->fs.h_u.tcp_ip4_spec.pdst; 1195272407Shselasky } else { 1196272407Shselasky mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h, 1197272407Shselasky spec_l2, 1198272407Shselasky cmd->fs.h_u. 1199272407Shselasky udp_ip4_spec.ip4dst); 1200272407Shselasky spec_l4->id = MLX4_NET_TRANS_RULE_ID_UDP; 1201272407Shselasky spec_l3->ipv4.src_ip = cmd->fs.h_u.udp_ip4_spec.ip4src; 1202272407Shselasky spec_l3->ipv4.dst_ip = cmd->fs.h_u.udp_ip4_spec.ip4dst; 1203272407Shselasky spec_l4->tcp_udp.src_port = cmd->fs.h_u.udp_ip4_spec.psrc; 1204272407Shselasky spec_l4->tcp_udp.dst_port = cmd->fs.h_u.udp_ip4_spec.pdst; 1205272407Shselasky } 1206272407Shselasky 1207272407Shselasky if (l4_mask->ip4src) 1208272407Shselasky spec_l3->ipv4.src_ip_msk = MLX4_BE_WORD_MASK; 1209272407Shselasky if (l4_mask->ip4dst) 1210272407Shselasky spec_l3->ipv4.dst_ip_msk = MLX4_BE_WORD_MASK; 1211272407Shselasky 1212272407Shselasky if (l4_mask->psrc) 1213272407Shselasky spec_l4->tcp_udp.src_port_msk = MLX4_BE_SHORT_MASK; 1214272407Shselasky if (l4_mask->pdst) 1215272407Shselasky spec_l4->tcp_udp.dst_port_msk = MLX4_BE_SHORT_MASK; 1216272407Shselasky 1217272407Shselasky list_add_tail(&spec_l3->list, list_h); 1218272407Shselasky list_add_tail(&spec_l4->list, list_h); 1219272407Shselasky 1220272407Shselasky return 0; 1221272407Shselasky} 1222272407Shselasky 1223272407Shselaskystatic int mlx4_en_ethtool_to_net_trans_rule(struct net_device *dev, 1224272407Shselasky struct mlx4_ethtool_rxnfc *cmd, 1225272407Shselasky struct list_head *rule_list_h) 1226272407Shselasky{ 1227272407Shselasky int err; 1228272407Shselasky struct ethhdr *eth_spec; 1229272407Shselasky struct mlx4_spec_list *spec_l2; 1230272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 1231272407Shselasky 1232272407Shselasky err = mlx4_en_validate_flow(dev, cmd); 1233272407Shselasky if (err) 1234272407Shselasky return err; 1235272407Shselasky 1236272407Shselasky switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { 1237272407Shselasky case ETHER_FLOW: 1238272407Shselasky spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL); 1239272407Shselasky if (!spec_l2) 1240272407Shselasky return -ENOMEM; 1241272407Shselasky 1242272407Shselasky eth_spec = &cmd->fs.h_u.ether_spec; 1243272407Shselasky mlx4_en_ethtool_add_mac_rule(cmd, rule_list_h, spec_l2, ð_spec->h_dest[0]); 1244272407Shselasky spec_l2->eth.ether_type = eth_spec->h_proto; 1245272407Shselasky if (eth_spec->h_proto) 1246272407Shselasky spec_l2->eth.ether_type_enable = 1; 1247272407Shselasky break; 1248272407Shselasky case IP_USER_FLOW: 1249272407Shselasky err = add_ip_rule(priv, cmd, rule_list_h); 1250272407Shselasky break; 1251272407Shselasky case TCP_V4_FLOW: 1252272407Shselasky err = add_tcp_udp_rule(priv, cmd, rule_list_h, TCP_V4_FLOW); 1253272407Shselasky break; 1254272407Shselasky case UDP_V4_FLOW: 1255272407Shselasky err = add_tcp_udp_rule(priv, cmd, rule_list_h, UDP_V4_FLOW); 1256272407Shselasky break; 1257272407Shselasky } 1258272407Shselasky 1259272407Shselasky return err; 1260272407Shselasky} 1261272407Shselasky 1262272407Shselaskystatic int mlx4_en_flow_replace(struct net_device *dev, 1263272407Shselasky struct mlx4_ethtool_rxnfc *cmd) 1264272407Shselasky{ 1265272407Shselasky int err; 1266272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 1267272407Shselasky struct mlx4_en_dev *mdev = priv->mdev; 1268272407Shselasky struct ethtool_flow_id *loc_rule; 1269272407Shselasky struct mlx4_spec_list *spec, *tmp_spec; 1270272407Shselasky u32 qpn; 1271272407Shselasky u64 reg_id; 1272272407Shselasky 1273272407Shselasky struct mlx4_net_trans_rule rule = { 1274272407Shselasky .queue_mode = MLX4_NET_TRANS_Q_FIFO, 1275272407Shselasky .exclusive = 0, 1276272407Shselasky .allow_loopback = 1, 1277272407Shselasky .promisc_mode = MLX4_FS_REGULAR, 1278272407Shselasky }; 1279272407Shselasky 1280272407Shselasky rule.port = priv->port; 1281272407Shselasky rule.priority = MLX4_DOMAIN_ETHTOOL | cmd->fs.location; 1282272407Shselasky INIT_LIST_HEAD(&rule.list); 1283272407Shselasky 1284272407Shselasky /* Allow direct QP attaches if the EN_ETHTOOL_QP_ATTACH flag is set */ 1285272407Shselasky if (cmd->fs.ring_cookie == RX_CLS_FLOW_DISC) 1286272407Shselasky qpn = priv->drop_qp.qpn; 1287272407Shselasky else if (cmd->fs.ring_cookie & EN_ETHTOOL_QP_ATTACH) { 1288272407Shselasky qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1); 1289272407Shselasky } else { 1290272407Shselasky if (cmd->fs.ring_cookie >= priv->rx_ring_num) { 1291272407Shselasky en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist.\n", 1292272407Shselasky cmd->fs.ring_cookie); 1293272407Shselasky return -EINVAL; 1294272407Shselasky } 1295272407Shselasky qpn = priv->rss_map.qps[cmd->fs.ring_cookie].qpn; 1296272407Shselasky if (!qpn) { 1297272407Shselasky en_warn(priv, "rxnfc: RX ring (%llu) is inactive.\n", 1298272407Shselasky cmd->fs.ring_cookie); 1299272407Shselasky return -EINVAL; 1300272407Shselasky } 1301272407Shselasky } 1302272407Shselasky rule.qpn = qpn; 1303272407Shselasky err = mlx4_en_ethtool_to_net_trans_rule(dev, cmd, &rule.list); 1304272407Shselasky if (err) 1305272407Shselasky goto out_free_list; 1306272407Shselasky 1307272407Shselasky mutex_lock(&mdev->state_lock); 1308272407Shselasky loc_rule = &priv->ethtool_rules[cmd->fs.location]; 1309272407Shselasky if (loc_rule->id) { 1310272407Shselasky err = mlx4_flow_detach(priv->mdev->dev, loc_rule->id); 1311272407Shselasky if (err) { 1312272407Shselasky en_err(priv, "Fail to detach network rule at location %d. registration id = %llx\n", 1313272407Shselasky cmd->fs.location, loc_rule->id); 1314272407Shselasky goto unlock; 1315272407Shselasky } 1316272407Shselasky loc_rule->id = 0; 1317272407Shselasky memset(&loc_rule->flow_spec, 0, 1318272407Shselasky sizeof(struct ethtool_rx_flow_spec)); 1319272407Shselasky list_del(&loc_rule->list); 1320272407Shselasky } 1321272407Shselasky err = mlx4_flow_attach(priv->mdev->dev, &rule, ®_id); 1322272407Shselasky if (err) { 1323272407Shselasky en_err(priv, "Fail to attach network rule at location %d.\n", 1324272407Shselasky cmd->fs.location); 1325272407Shselasky goto unlock; 1326272407Shselasky } 1327272407Shselasky loc_rule->id = reg_id; 1328272407Shselasky memcpy(&loc_rule->flow_spec, &cmd->fs, 1329272407Shselasky sizeof(struct ethtool_rx_flow_spec)); 1330272407Shselasky list_add_tail(&loc_rule->list, &priv->ethtool_list); 1331272407Shselasky 1332272407Shselaskyunlock: 1333272407Shselasky mutex_unlock(&mdev->state_lock); 1334272407Shselaskyout_free_list: 1335272407Shselasky list_for_each_entry_safe(spec, tmp_spec, &rule.list, list) { 1336272407Shselasky list_del(&spec->list); 1337272407Shselasky kfree(spec); 1338272407Shselasky } 1339272407Shselasky return err; 1340272407Shselasky} 1341272407Shselasky 1342272407Shselaskystatic int mlx4_en_flow_detach(struct net_device *dev, 1343272407Shselasky struct mlx4_ethtool_rxnfc *cmd) 1344272407Shselasky{ 1345272407Shselasky int err = 0; 1346272407Shselasky struct ethtool_flow_id *rule; 1347272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 1348272407Shselasky struct mlx4_en_dev *mdev = priv->mdev; 1349272407Shselasky 1350272407Shselasky if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) 1351272407Shselasky return -EINVAL; 1352272407Shselasky 1353272407Shselasky mutex_lock(&mdev->state_lock); 1354272407Shselasky rule = &priv->ethtool_rules[cmd->fs.location]; 1355272407Shselasky if (!rule->id) { 1356272407Shselasky err = -ENOENT; 1357272407Shselasky goto out; 1358272407Shselasky } 1359272407Shselasky 1360272407Shselasky err = mlx4_flow_detach(priv->mdev->dev, rule->id); 1361272407Shselasky if (err) { 1362272407Shselasky en_err(priv, "Fail to detach network rule at location %d. registration id = 0x%llx\n", 1363272407Shselasky cmd->fs.location, rule->id); 1364272407Shselasky goto out; 1365272407Shselasky } 1366272407Shselasky rule->id = 0; 1367272407Shselasky memset(&rule->flow_spec, 0, sizeof(struct ethtool_rx_flow_spec)); 1368272407Shselasky 1369272407Shselasky list_del(&rule->list); 1370272407Shselaskyout: 1371272407Shselasky mutex_unlock(&mdev->state_lock); 1372272407Shselasky return err; 1373272407Shselasky 1374272407Shselasky} 1375272407Shselasky 1376272407Shselaskystatic int mlx4_en_get_flow(struct net_device *dev, struct mlx4_ethtool_rxnfc *cmd, 1377272407Shselasky int loc) 1378272407Shselasky{ 1379272407Shselasky int err = 0; 1380272407Shselasky struct ethtool_flow_id *rule; 1381272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 1382272407Shselasky 1383272407Shselasky if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES) 1384272407Shselasky return -EINVAL; 1385272407Shselasky 1386272407Shselasky rule = &priv->ethtool_rules[loc]; 1387272407Shselasky if (rule->id) 1388272407Shselasky memcpy(&cmd->fs, &rule->flow_spec, 1389272407Shselasky sizeof(struct ethtool_rx_flow_spec)); 1390272407Shselasky else 1391272407Shselasky err = -ENOENT; 1392272407Shselasky 1393272407Shselasky return err; 1394272407Shselasky} 1395272407Shselasky 1396272407Shselaskystatic int mlx4_en_get_num_flows(struct mlx4_en_priv *priv) 1397272407Shselasky{ 1398272407Shselasky 1399272407Shselasky int i, res = 0; 1400272407Shselasky for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) { 1401272407Shselasky if (priv->ethtool_rules[i].id) 1402272407Shselasky res++; 1403272407Shselasky } 1404272407Shselasky return res; 1405272407Shselasky 1406272407Shselasky} 1407272407Shselasky 1408272407Shselaskystatic int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *c, 1409272407Shselasky u32 *rule_locs) 1410272407Shselasky{ 1411272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 1412272407Shselasky struct mlx4_en_dev *mdev = priv->mdev; 1413272407Shselasky int err = 0; 1414272407Shselasky int i = 0, priority = 0; 1415272407Shselasky struct mlx4_ethtool_rxnfc *cmd = (struct mlx4_ethtool_rxnfc *)c; 1416272407Shselasky 1417272407Shselasky if ((cmd->cmd == ETHTOOL_GRXCLSRLCNT || 1418272407Shselasky cmd->cmd == ETHTOOL_GRXCLSRULE || 1419272407Shselasky cmd->cmd == ETHTOOL_GRXCLSRLALL) && 1420272407Shselasky (mdev->dev->caps.steering_mode != 1421272407Shselasky MLX4_STEERING_MODE_DEVICE_MANAGED || !priv->port_up)) 1422272407Shselasky return -EINVAL; 1423272407Shselasky 1424272407Shselasky switch (cmd->cmd) { 1425272407Shselasky case ETHTOOL_GRXRINGS: 1426272407Shselasky cmd->data = priv->rx_ring_num; 1427272407Shselasky break; 1428272407Shselasky case ETHTOOL_GRXCLSRLCNT: 1429272407Shselasky cmd->rule_cnt = mlx4_en_get_num_flows(priv); 1430272407Shselasky break; 1431272407Shselasky case ETHTOOL_GRXCLSRULE: 1432272407Shselasky err = mlx4_en_get_flow(dev, cmd, cmd->fs.location); 1433272407Shselasky break; 1434272407Shselasky case ETHTOOL_GRXCLSRLALL: 1435272407Shselasky while ((!err || err == -ENOENT) && priority < cmd->rule_cnt) { 1436272407Shselasky err = mlx4_en_get_flow(dev, cmd, i); 1437272407Shselasky if (!err) 1438272407Shselasky rule_locs[priority++] = i; 1439272407Shselasky i++; 1440272407Shselasky } 1441272407Shselasky err = 0; 1442272407Shselasky break; 1443272407Shselasky default: 1444272407Shselasky err = -EOPNOTSUPP; 1445272407Shselasky break; 1446272407Shselasky } 1447272407Shselasky 1448272407Shselasky return err; 1449272407Shselasky} 1450272407Shselasky 1451272407Shselaskystatic int mlx4_en_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *c) 1452272407Shselasky{ 1453272407Shselasky int err = 0; 1454272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 1455272407Shselasky struct mlx4_en_dev *mdev = priv->mdev; 1456272407Shselasky struct mlx4_ethtool_rxnfc *cmd = (struct mlx4_ethtool_rxnfc *)c; 1457272407Shselasky 1458272407Shselasky if (mdev->dev->caps.steering_mode != 1459272407Shselasky MLX4_STEERING_MODE_DEVICE_MANAGED || !priv->port_up) 1460272407Shselasky return -EINVAL; 1461272407Shselasky 1462272407Shselasky switch (cmd->cmd) { 1463272407Shselasky case ETHTOOL_SRXCLSRLINS: 1464272407Shselasky err = mlx4_en_flow_replace(dev, cmd); 1465272407Shselasky break; 1466272407Shselasky case ETHTOOL_SRXCLSRLDEL: 1467272407Shselasky err = mlx4_en_flow_detach(dev, cmd); 1468272407Shselasky break; 1469272407Shselasky default: 1470272407Shselasky en_warn(priv, "Unsupported ethtool command. (%d)\n", cmd->cmd); 1471272407Shselasky return -EINVAL; 1472272407Shselasky } 1473272407Shselasky 1474272407Shselasky return err; 1475272407Shselasky} 1476272407Shselasky 1477272407Shselaskystatic void mlx4_en_get_channels(struct net_device *dev, 1478272407Shselasky struct ethtool_channels *channel) 1479272407Shselasky{ 1480272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 1481272407Shselasky 1482272407Shselasky memset(channel, 0, sizeof(*channel)); 1483272407Shselasky 1484272407Shselasky channel->max_rx = MAX_RX_RINGS; 1485272407Shselasky channel->max_tx = MLX4_EN_MAX_TX_RING_P_UP; 1486272407Shselasky 1487272407Shselasky channel->rx_count = priv->rx_ring_num; 1488272407Shselasky channel->tx_count = priv->tx_ring_num / MLX4_EN_NUM_UP; 1489272407Shselasky} 1490272407Shselasky 1491272407Shselaskystatic int mlx4_en_set_channels(struct net_device *dev, 1492272407Shselasky struct ethtool_channels *channel) 1493272407Shselasky{ 1494272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 1495272407Shselasky struct mlx4_en_dev *mdev = priv->mdev; 1496272407Shselasky int port_up = 0; 1497272407Shselasky int i; 1498272407Shselasky int err = 0; 1499272407Shselasky 1500272407Shselasky if (channel->other_count || channel->combined_count || 1501272407Shselasky channel->tx_count > MLX4_EN_MAX_TX_RING_P_UP || 1502272407Shselasky channel->rx_count > MAX_RX_RINGS || 1503272407Shselasky !channel->tx_count || !channel->rx_count) 1504272407Shselasky return -EINVAL; 1505272407Shselasky 1506272407Shselasky err = mlx4_en_pre_config(priv); 1507272407Shselasky if (err) 1508272407Shselasky return err; 1509272407Shselasky 1510272407Shselasky mutex_lock(&mdev->state_lock); 1511272407Shselasky if (priv->port_up) { 1512272407Shselasky port_up = 1; 1513272407Shselasky mlx4_en_stop_port(dev); 1514272407Shselasky } 1515272407Shselasky 1516272407Shselasky mlx4_en_free_resources(priv); 1517272407Shselasky 1518272407Shselasky priv->num_tx_rings_p_up = channel->tx_count; 1519272407Shselasky priv->tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP; 1520272407Shselasky priv->rx_ring_num = channel->rx_count; 1521272407Shselasky 1522272407Shselasky err = mlx4_en_alloc_resources(priv); 1523272407Shselasky if (err) { 1524272407Shselasky en_err(priv, "Failed reallocating port resources\n"); 1525272407Shselasky goto out; 1526272407Shselasky } 1527272407Shselasky 1528272407Shselasky netif_set_real_num_tx_queues(dev, priv->tx_ring_num); 1529272407Shselasky netif_set_real_num_rx_queues(dev, priv->rx_ring_num); 1530272407Shselasky 1531272407Shselasky mlx4_en_setup_tc(dev, MLX4_EN_NUM_UP); 1532272407Shselasky 1533272407Shselasky en_warn(priv, "Using %d TX rings\n", priv->tx_ring_num); 1534272407Shselasky en_warn(priv, "Using %d RX rings\n", priv->rx_ring_num); 1535272407Shselasky 1536272407Shselasky if (port_up) { 1537272407Shselasky err = mlx4_en_start_port(dev); 1538272407Shselasky if (err) 1539272407Shselasky en_err(priv, "Failed starting port\n"); 1540272407Shselasky 1541272407Shselasky for (i = 0; i < priv->rx_ring_num; i++) { 1542272407Shselasky priv->rx_cq[i]->moder_cnt = priv->rx_frames; 1543272407Shselasky priv->rx_cq[i]->moder_time = priv->rx_usecs; 1544272407Shselasky priv->last_moder_time[i] = MLX4_EN_AUTO_CONF; 1545272407Shselasky err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]); 1546272407Shselasky if (err) 1547272407Shselasky goto out; 1548272407Shselasky } 1549272407Shselasky } 1550272407Shselasky 1551272407Shselaskyout: 1552272407Shselasky mutex_unlock(&mdev->state_lock); 1553272407Shselasky return err; 1554272407Shselasky} 1555272407Shselasky 1556272407Shselaskystatic int mlx4_en_get_ts_info(struct net_device *dev, 1557272407Shselasky struct ethtool_ts_info *info) 1558272407Shselasky{ 1559272407Shselasky struct mlx4_en_priv *priv = netdev_priv(dev); 1560272407Shselasky struct mlx4_en_dev *mdev = priv->mdev; 1561272407Shselasky int ret; 1562272407Shselasky 1563272407Shselasky ret = ethtool_op_get_ts_info(dev, info); 1564272407Shselasky if (ret) 1565272407Shselasky return ret; 1566272407Shselasky 1567272407Shselasky if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS) { 1568272407Shselasky info->so_timestamping |= 1569272407Shselasky SOF_TIMESTAMPING_TX_HARDWARE | 1570272407Shselasky SOF_TIMESTAMPING_RX_HARDWARE | 1571272407Shselasky SOF_TIMESTAMPING_RAW_HARDWARE; 1572272407Shselasky 1573272407Shselasky info->tx_types = 1574272407Shselasky (1 << HWTSTAMP_TX_OFF) | 1575272407Shselasky (1 << HWTSTAMP_TX_ON); 1576272407Shselasky 1577272407Shselasky info->rx_filters = 1578272407Shselasky (1 << HWTSTAMP_FILTER_NONE) | 1579272407Shselasky (1 << HWTSTAMP_FILTER_ALL); 1580272407Shselasky } 1581272407Shselasky 1582272407Shselasky return ret; 1583272407Shselasky} 1584272407Shselasky 1585219820Sjeffconst struct ethtool_ops mlx4_en_ethtool_ops = { 1586219820Sjeff .get_drvinfo = mlx4_en_get_drvinfo, 1587219820Sjeff .get_settings = mlx4_en_get_settings, 1588219820Sjeff .set_settings = mlx4_en_set_settings, 1589219820Sjeff .get_link = ethtool_op_get_link, 1590219820Sjeff .get_strings = mlx4_en_get_strings, 1591219820Sjeff .get_sset_count = mlx4_en_get_sset_count, 1592219820Sjeff .get_ethtool_stats = mlx4_en_get_ethtool_stats, 1593219820Sjeff .self_test = mlx4_en_self_test, 1594219820Sjeff .get_wol = mlx4_en_get_wol, 1595220016Sjeff .set_wol = mlx4_en_set_wol, 1596219820Sjeff .get_msglevel = mlx4_en_get_msglevel, 1597219820Sjeff .set_msglevel = mlx4_en_set_msglevel, 1598219820Sjeff .get_coalesce = mlx4_en_get_coalesce, 1599219820Sjeff .set_coalesce = mlx4_en_set_coalesce, 1600219820Sjeff .get_pauseparam = mlx4_en_get_pauseparam, 1601219820Sjeff .set_pauseparam = mlx4_en_set_pauseparam, 1602219820Sjeff .get_ringparam = mlx4_en_get_ringparam, 1603219820Sjeff .set_ringparam = mlx4_en_set_ringparam, 1604272407Shselasky .get_rxnfc = mlx4_en_get_rxnfc, 1605272407Shselasky .set_rxnfc = mlx4_en_set_rxnfc, 1606272407Shselasky .get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size, 1607272407Shselasky .get_rxfh_indir = mlx4_en_get_rxfh_indir, 1608272407Shselasky .set_rxfh_indir = mlx4_en_set_rxfh_indir, 1609272407Shselasky .get_channels = mlx4_en_get_channels, 1610272407Shselasky .set_channels = mlx4_en_set_channels, 1611272407Shselasky .get_ts_info = mlx4_en_get_ts_info, 1612219820Sjeff}; 1613219820Sjeff 1614219820Sjeff 1615219820Sjeff 1616219820Sjeff 1617219820Sjeff 1618