1/* 2 * Copyright (c) 2007 Mellanox Technologies. All rights reserved. 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 * 32 */ 33 34#include <linux/kernel.h> 35#include <linux/ethtool.h> 36#include <linux/netdevice.h> 37 38#include "mlx4_en.h" 39#include "en_port.h" 40 41 42static void mlx4_en_update_lro_stats(struct mlx4_en_priv *priv) 43{ 44 int i; 45 46 priv->port_stats.lro_aggregated = 0; 47 priv->port_stats.lro_flushed = 0; 48 priv->port_stats.lro_no_desc = 0; 49 50 for (i = 0; i < priv->rx_ring_num; i++) { 51 priv->port_stats.lro_aggregated += priv->rx_ring[i].lro.stats.aggregated; 52 priv->port_stats.lro_flushed += priv->rx_ring[i].lro.stats.flushed; 53 priv->port_stats.lro_no_desc += priv->rx_ring[i].lro.stats.no_desc; 54 } 55} 56 57static void 58mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) 59{ 60 struct mlx4_en_priv *priv = netdev_priv(dev); 61 struct mlx4_en_dev *mdev = priv->mdev; 62 63 sprintf(drvinfo->driver, DRV_NAME " (%s)", mdev->dev->board_id); 64 strncpy(drvinfo->version, DRV_VERSION " (" DRV_RELDATE ")", 32); 65 sprintf(drvinfo->fw_version, "%d.%d.%d", 66 (u16) (mdev->dev->caps.fw_ver >> 32), 67 (u16) ((mdev->dev->caps.fw_ver >> 16) & 0xffff), 68 (u16) (mdev->dev->caps.fw_ver & 0xffff)); 69 strncpy(drvinfo->bus_info, pci_name(mdev->dev->pdev), 32); 70 drvinfo->n_stats = 0; 71 drvinfo->regdump_len = 0; 72 drvinfo->eedump_len = 0; 73} 74 75static u32 mlx4_en_get_tso(struct net_device *dev) 76{ 77 return (dev->features & NETIF_F_TSO) != 0; 78} 79 80static int mlx4_en_set_tso(struct net_device *dev, u32 data) 81{ 82 struct mlx4_en_priv *priv = netdev_priv(dev); 83 84 if (data) { 85 if (!priv->mdev->LSO_support) 86 return -EPERM; 87 dev->features |= (NETIF_F_TSO | NETIF_F_TSO6); 88 } else 89 dev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6); 90 return 0; 91} 92 93static u32 mlx4_en_get_rx_csum(struct net_device *dev) 94{ 95 struct mlx4_en_priv *priv = netdev_priv(dev); 96 return priv->rx_csum; 97} 98 99static int mlx4_en_set_rx_csum(struct net_device *dev, u32 data) 100{ 101 struct mlx4_en_priv *priv = netdev_priv(dev); 102 priv->rx_csum = (data != 0); 103 return 0; 104} 105 106static const char main_strings[][ETH_GSTRING_LEN] = { 107 "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors", 108 "tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions", 109 "rx_length_errors", "rx_over_errors", "rx_crc_errors", 110 "rx_frame_errors", "rx_fifo_errors", "rx_missed_errors", 111 "tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors", 112 "tx_heartbeat_errors", "tx_window_errors", 113 114 /* port statistics */ 115 "lro_aggregated", "lro_flushed", "lro_no_desc", "tso_packets", 116 "queue_stopped", "wake_queue", "tx_timeout", "rx_alloc_failed", 117 "rx_csum_good", "rx_csum_none", "tx_chksum_offload", 118 119 /* packet statistics */ 120 "broadcast", "rx_prio_0", "rx_prio_1", "rx_prio_2", "rx_prio_3", 121 "rx_prio_4", "rx_prio_5", "rx_prio_6", "rx_prio_7", "tx_prio_0", 122 "tx_prio_1", "tx_prio_2", "tx_prio_3", "tx_prio_4", "tx_prio_5", 123 "tx_prio_6", "tx_prio_7", 124}; 125#define NUM_MAIN_STATS 21 126#define NUM_ALL_STATS (NUM_MAIN_STATS + NUM_PORT_STATS + NUM_PKT_STATS + NUM_PERF_STATS) 127 128static u32 mlx4_en_get_msglevel(struct net_device *dev) 129{ 130 return ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable; 131} 132 133static void mlx4_en_set_msglevel(struct net_device *dev, u32 val) 134{ 135 ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable = val; 136} 137 138static void mlx4_en_get_wol(struct net_device *netdev, 139 struct ethtool_wolinfo *wol) 140{ 141 wol->supported = 0; 142 wol->wolopts = 0; 143} 144 145static int mlx4_en_get_sset_count(struct net_device *dev, int sset) 146{ 147 struct mlx4_en_priv *priv = netdev_priv(dev); 148 149 if (sset != ETH_SS_STATS) 150 return -EOPNOTSUPP; 151 152 return NUM_ALL_STATS + (priv->tx_ring_num + priv->rx_ring_num) * 2; 153} 154 155static void mlx4_en_get_ethtool_stats(struct net_device *dev, 156 struct ethtool_stats *stats, uint64_t *data) 157{ 158 struct mlx4_en_priv *priv = netdev_priv(dev); 159 int index = 0; 160 int i; 161 162 spin_lock_bh(&priv->stats_lock); 163 164 mlx4_en_update_lro_stats(priv); 165 166 for (i = 0; i < NUM_MAIN_STATS; i++) 167 data[index++] = ((unsigned long *) &priv->stats)[i]; 168 for (i = 0; i < NUM_PORT_STATS; i++) 169 data[index++] = ((unsigned long *) &priv->port_stats)[i]; 170 for (i = 0; i < priv->tx_ring_num; i++) { 171 data[index++] = priv->tx_ring[i].packets; 172 data[index++] = priv->tx_ring[i].bytes; 173 } 174 for (i = 0; i < priv->rx_ring_num; i++) { 175 data[index++] = priv->rx_ring[i].packets; 176 data[index++] = priv->rx_ring[i].bytes; 177 } 178 for (i = 0; i < NUM_PKT_STATS; i++) 179 data[index++] = ((unsigned long *) &priv->pkstats)[i]; 180 spin_unlock_bh(&priv->stats_lock); 181 182} 183 184static void mlx4_en_get_strings(struct net_device *dev, 185 uint32_t stringset, uint8_t *data) 186{ 187 struct mlx4_en_priv *priv = netdev_priv(dev); 188 int index = 0; 189 int i; 190 191 if (stringset != ETH_SS_STATS) 192 return; 193 194 /* Add main counters */ 195 for (i = 0; i < NUM_MAIN_STATS; i++) 196 strcpy(data + (index++) * ETH_GSTRING_LEN, main_strings[i]); 197 for (i = 0; i < NUM_PORT_STATS; i++) 198 strcpy(data + (index++) * ETH_GSTRING_LEN, 199 main_strings[i + NUM_MAIN_STATS]); 200 for (i = 0; i < priv->tx_ring_num; i++) { 201 sprintf(data + (index++) * ETH_GSTRING_LEN, 202 "tx%d_packets", i); 203 sprintf(data + (index++) * ETH_GSTRING_LEN, 204 "tx%d_bytes", i); 205 } 206 for (i = 0; i < priv->rx_ring_num; i++) { 207 sprintf(data + (index++) * ETH_GSTRING_LEN, 208 "rx%d_packets", i); 209 sprintf(data + (index++) * ETH_GSTRING_LEN, 210 "rx%d_bytes", i); 211 } 212 for (i = 0; i < NUM_PKT_STATS; i++) 213 strcpy(data + (index++) * ETH_GSTRING_LEN, 214 main_strings[i + NUM_MAIN_STATS + NUM_PORT_STATS]); 215} 216 217static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) 218{ 219 cmd->autoneg = AUTONEG_DISABLE; 220 cmd->supported = SUPPORTED_10000baseT_Full; 221 cmd->advertising = ADVERTISED_1000baseT_Full; 222 if (netif_carrier_ok(dev)) { 223 cmd->speed = SPEED_10000; 224 cmd->duplex = DUPLEX_FULL; 225 } else { 226 cmd->speed = -1; 227 cmd->duplex = -1; 228 } 229 return 0; 230} 231 232static int mlx4_en_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) 233{ 234 if ((cmd->autoneg == AUTONEG_ENABLE) || 235 (cmd->speed != SPEED_10000) || (cmd->duplex != DUPLEX_FULL)) 236 return -EINVAL; 237 238 /* Nothing to change */ 239 return 0; 240} 241 242static int mlx4_en_get_coalesce(struct net_device *dev, 243 struct ethtool_coalesce *coal) 244{ 245 struct mlx4_en_priv *priv = netdev_priv(dev); 246 247 coal->tx_coalesce_usecs = 0; 248 coal->tx_max_coalesced_frames = 0; 249 coal->rx_coalesce_usecs = priv->rx_usecs; 250 coal->rx_max_coalesced_frames = priv->rx_frames; 251 252 coal->pkt_rate_low = priv->pkt_rate_low; 253 coal->rx_coalesce_usecs_low = priv->rx_usecs_low; 254 coal->pkt_rate_high = priv->pkt_rate_high; 255 coal->rx_coalesce_usecs_high = priv->rx_usecs_high; 256 coal->rate_sample_interval = priv->sample_interval; 257 coal->use_adaptive_rx_coalesce = priv->adaptive_rx_coal; 258 return 0; 259} 260 261static int mlx4_en_set_coalesce(struct net_device *dev, 262 struct ethtool_coalesce *coal) 263{ 264 struct mlx4_en_priv *priv = netdev_priv(dev); 265 int err, i; 266 267 priv->rx_frames = (coal->rx_max_coalesced_frames == 268 MLX4_EN_AUTO_CONF) ? 269 MLX4_EN_RX_COAL_TARGET : 270 coal->rx_max_coalesced_frames; 271 priv->rx_usecs = (coal->rx_coalesce_usecs == 272 MLX4_EN_AUTO_CONF) ? 273 MLX4_EN_RX_COAL_TIME : 274 coal->rx_coalesce_usecs; 275 276 /* Set adaptive coalescing params */ 277 priv->pkt_rate_low = coal->pkt_rate_low; 278 priv->rx_usecs_low = coal->rx_coalesce_usecs_low; 279 priv->pkt_rate_high = coal->pkt_rate_high; 280 priv->rx_usecs_high = coal->rx_coalesce_usecs_high; 281 priv->sample_interval = coal->rate_sample_interval; 282 priv->adaptive_rx_coal = coal->use_adaptive_rx_coalesce; 283 priv->last_moder_time = MLX4_EN_AUTO_CONF; 284 if (priv->adaptive_rx_coal) 285 return 0; 286 287 for (i = 0; i < priv->rx_ring_num; i++) { 288 priv->rx_cq[i].moder_cnt = priv->rx_frames; 289 priv->rx_cq[i].moder_time = priv->rx_usecs; 290 err = mlx4_en_set_cq_moder(priv, &priv->rx_cq[i]); 291 if (err) 292 return err; 293 } 294 return 0; 295} 296 297static int mlx4_en_set_pauseparam(struct net_device *dev, 298 struct ethtool_pauseparam *pause) 299{ 300 struct mlx4_en_priv *priv = netdev_priv(dev); 301 struct mlx4_en_dev *mdev = priv->mdev; 302 int err; 303 304 priv->prof->tx_pause = pause->tx_pause != 0; 305 priv->prof->rx_pause = pause->rx_pause != 0; 306 err = mlx4_SET_PORT_general(mdev->dev, priv->port, 307 priv->rx_skb_size + ETH_FCS_LEN, 308 priv->prof->tx_pause, 309 priv->prof->tx_ppp, 310 priv->prof->rx_pause, 311 priv->prof->rx_ppp); 312 if (err) 313 en_err(priv, "Failed setting pause params\n"); 314 315 return err; 316} 317 318static void mlx4_en_get_pauseparam(struct net_device *dev, 319 struct ethtool_pauseparam *pause) 320{ 321 struct mlx4_en_priv *priv = netdev_priv(dev); 322 323 pause->tx_pause = priv->prof->tx_pause; 324 pause->rx_pause = priv->prof->rx_pause; 325} 326 327static int mlx4_en_set_ringparam(struct net_device *dev, 328 struct ethtool_ringparam *param) 329{ 330 struct mlx4_en_priv *priv = netdev_priv(dev); 331 struct mlx4_en_dev *mdev = priv->mdev; 332 u32 rx_size, tx_size; 333 int port_up = 0; 334 int err = 0; 335 336 if (param->rx_jumbo_pending || param->rx_mini_pending) 337 return -EINVAL; 338 339 rx_size = roundup_pow_of_two(param->rx_pending); 340 rx_size = max_t(u32, rx_size, MLX4_EN_MIN_RX_SIZE); 341 rx_size = min_t(u32, rx_size, MLX4_EN_MAX_RX_SIZE); 342 tx_size = roundup_pow_of_two(param->tx_pending); 343 tx_size = max_t(u32, tx_size, MLX4_EN_MIN_TX_SIZE); 344 tx_size = min_t(u32, tx_size, MLX4_EN_MAX_TX_SIZE); 345 346 if (rx_size == priv->prof->rx_ring_size && 347 tx_size == priv->prof->tx_ring_size) 348 return 0; 349 350 mutex_lock(&mdev->state_lock); 351 if (priv->port_up) { 352 port_up = 1; 353 mlx4_en_stop_port(dev); 354 } 355 356 mlx4_en_free_resources(priv); 357 358 priv->prof->tx_ring_size = tx_size; 359 priv->prof->rx_ring_size = rx_size; 360 361 err = mlx4_en_alloc_resources(priv); 362 if (err) { 363 en_err(priv, "Failed reallocating port resources\n"); 364 goto out; 365 } 366 if (port_up) { 367 err = mlx4_en_start_port(dev); 368 if (err) 369 en_err(priv, "Failed starting port\n"); 370 } 371 372out: 373 mutex_unlock(&mdev->state_lock); 374 return err; 375} 376 377static void mlx4_en_get_ringparam(struct net_device *dev, 378 struct ethtool_ringparam *param) 379{ 380 struct mlx4_en_priv *priv = netdev_priv(dev); 381 struct mlx4_en_dev *mdev = priv->mdev; 382 383 memset(param, 0, sizeof(*param)); 384 param->rx_max_pending = MLX4_EN_MAX_RX_SIZE; 385 param->tx_max_pending = MLX4_EN_MAX_TX_SIZE; 386 param->rx_pending = mdev->profile.prof[priv->port].rx_ring_size; 387 param->tx_pending = mdev->profile.prof[priv->port].tx_ring_size; 388} 389 390static int mlx4_ethtool_op_set_flags(struct net_device *dev, u32 data) 391{ 392 struct mlx4_en_priv *priv = netdev_priv(dev); 393 struct mlx4_en_dev *mdev = priv->mdev; 394 int rc = 0; 395 int changed = 0; 396 397 if (data & ~ETH_FLAG_LRO) 398 return -EOPNOTSUPP; 399 400 if (data & ETH_FLAG_LRO) { 401 if (mdev->profile.num_lro == 0) 402 return -EOPNOTSUPP; 403 if (!(dev->features & NETIF_F_LRO)) 404 changed = 1; 405 } else if (dev->features & NETIF_F_LRO) { 406 changed = 1; 407 } 408 409 if (changed) { 410 if (netif_running(dev)) { 411 mutex_lock(&mdev->state_lock); 412 mlx4_en_stop_port(dev); 413 } 414 dev->features ^= NETIF_F_LRO; 415 if (netif_running(dev)) { 416 rc = mlx4_en_start_port(dev); 417 if (rc) 418 en_err(priv, "Failed to restart port\n"); 419 mutex_unlock(&mdev->state_lock); 420 } 421 } 422 423 return rc; 424} 425 426const struct ethtool_ops mlx4_en_ethtool_ops = { 427 .get_drvinfo = mlx4_en_get_drvinfo, 428 .get_settings = mlx4_en_get_settings, 429 .set_settings = mlx4_en_set_settings, 430#ifdef NETIF_F_TSO 431 .get_tso = mlx4_en_get_tso, 432 .set_tso = mlx4_en_set_tso, 433#endif 434 .get_sg = ethtool_op_get_sg, 435 .set_sg = ethtool_op_set_sg, 436 .get_link = ethtool_op_get_link, 437 .get_rx_csum = mlx4_en_get_rx_csum, 438 .set_rx_csum = mlx4_en_set_rx_csum, 439 .get_tx_csum = ethtool_op_get_tx_csum, 440 .set_tx_csum = ethtool_op_set_tx_ipv6_csum, 441 .get_strings = mlx4_en_get_strings, 442 .get_sset_count = mlx4_en_get_sset_count, 443 .get_ethtool_stats = mlx4_en_get_ethtool_stats, 444 .get_wol = mlx4_en_get_wol, 445 .get_msglevel = mlx4_en_get_msglevel, 446 .set_msglevel = mlx4_en_set_msglevel, 447 .get_coalesce = mlx4_en_get_coalesce, 448 .set_coalesce = mlx4_en_set_coalesce, 449 .get_pauseparam = mlx4_en_get_pauseparam, 450 .set_pauseparam = mlx4_en_set_pauseparam, 451 .get_ringparam = mlx4_en_get_ringparam, 452 .set_ringparam = mlx4_en_set_ringparam, 453 .get_flags = ethtool_op_get_flags, 454 .set_flags = mlx4_ethtool_op_set_flags, 455}; 456