Jakub Kicinski | 4c35236 | 2015-12-01 14:55:22 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 Netronome Systems, Inc. |
| 3 | * |
| 4 | * This software is dual licensed under the GNU General License Version 2, |
| 5 | * June 1991 as shown in the file COPYING in the top-level directory of this |
| 6 | * source tree or the BSD 2-Clause License provided below. You have the |
| 7 | * option to license this software under the complete terms of either license. |
| 8 | * |
| 9 | * The BSD 2-Clause License: |
| 10 | * |
| 11 | * Redistribution and use in source and binary forms, with or |
| 12 | * without modification, are permitted provided that the following |
| 13 | * conditions are met: |
| 14 | * |
| 15 | * 1. Redistributions of source code must retain the above |
| 16 | * copyright notice, this list of conditions and the following |
| 17 | * disclaimer. |
| 18 | * |
| 19 | * 2. Redistributions in binary form must reproduce the above |
| 20 | * copyright notice, this list of conditions and the following |
| 21 | * disclaimer in the documentation and/or other materials |
| 22 | * provided with the distribution. |
| 23 | * |
| 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| 28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| 29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 31 | * SOFTWARE. |
| 32 | */ |
| 33 | |
| 34 | /* |
| 35 | * nfp_net_ethtool.c |
| 36 | * Netronome network device driver: ethtool support |
| 37 | * Authors: Jakub Kicinski <jakub.kicinski@netronome.com> |
| 38 | * Jason McMullan <jason.mcmullan@netronome.com> |
| 39 | * Rolf Neugebauer <rolf.neugebauer@netronome.com> |
| 40 | * Brad Petrus <brad.petrus@netronome.com> |
| 41 | */ |
| 42 | |
Jakub Kicinski | 4c35236 | 2015-12-01 14:55:22 +0000 | [diff] [blame] | 43 | #include <linux/kernel.h> |
| 44 | #include <linux/netdevice.h> |
| 45 | #include <linux/etherdevice.h> |
| 46 | #include <linux/interrupt.h> |
| 47 | #include <linux/pci.h> |
| 48 | #include <linux/ethtool.h> |
| 49 | |
| 50 | #include "nfp_net_ctrl.h" |
| 51 | #include "nfp_net.h" |
| 52 | |
| 53 | /* Support for stats. Returns netdev, driver, and device stats */ |
| 54 | enum { NETDEV_ET_STATS, NFP_NET_DRV_ET_STATS, NFP_NET_DEV_ET_STATS }; |
| 55 | struct _nfp_net_et_stats { |
| 56 | char name[ETH_GSTRING_LEN]; |
| 57 | int type; |
| 58 | int sz; |
| 59 | int off; |
| 60 | }; |
| 61 | |
| 62 | #define NN_ET_NETDEV_STAT(m) NETDEV_ET_STATS, \ |
| 63 | FIELD_SIZEOF(struct net_device_stats, m), \ |
| 64 | offsetof(struct net_device_stats, m) |
| 65 | /* For stats in the control BAR (other than Q stats) */ |
| 66 | #define NN_ET_DEV_STAT(m) NFP_NET_DEV_ET_STATS, \ |
| 67 | sizeof(u64), \ |
| 68 | (m) |
| 69 | static const struct _nfp_net_et_stats nfp_net_et_stats[] = { |
| 70 | /* netdev stats */ |
| 71 | {"rx_packets", NN_ET_NETDEV_STAT(rx_packets)}, |
| 72 | {"tx_packets", NN_ET_NETDEV_STAT(tx_packets)}, |
| 73 | {"rx_bytes", NN_ET_NETDEV_STAT(rx_bytes)}, |
| 74 | {"tx_bytes", NN_ET_NETDEV_STAT(tx_bytes)}, |
| 75 | {"rx_errors", NN_ET_NETDEV_STAT(rx_errors)}, |
| 76 | {"tx_errors", NN_ET_NETDEV_STAT(tx_errors)}, |
| 77 | {"rx_dropped", NN_ET_NETDEV_STAT(rx_dropped)}, |
| 78 | {"tx_dropped", NN_ET_NETDEV_STAT(tx_dropped)}, |
| 79 | {"multicast", NN_ET_NETDEV_STAT(multicast)}, |
| 80 | {"collisions", NN_ET_NETDEV_STAT(collisions)}, |
| 81 | {"rx_over_errors", NN_ET_NETDEV_STAT(rx_over_errors)}, |
| 82 | {"rx_crc_errors", NN_ET_NETDEV_STAT(rx_crc_errors)}, |
| 83 | {"rx_frame_errors", NN_ET_NETDEV_STAT(rx_frame_errors)}, |
| 84 | {"rx_fifo_errors", NN_ET_NETDEV_STAT(rx_fifo_errors)}, |
| 85 | {"rx_missed_errors", NN_ET_NETDEV_STAT(rx_missed_errors)}, |
| 86 | {"tx_aborted_errors", NN_ET_NETDEV_STAT(tx_aborted_errors)}, |
| 87 | {"tx_carrier_errors", NN_ET_NETDEV_STAT(tx_carrier_errors)}, |
| 88 | {"tx_fifo_errors", NN_ET_NETDEV_STAT(tx_fifo_errors)}, |
| 89 | /* Stats from the device */ |
| 90 | {"dev_rx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_DISCARDS)}, |
| 91 | {"dev_rx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_ERRORS)}, |
| 92 | {"dev_rx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_OCTETS)}, |
| 93 | {"dev_rx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_UC_OCTETS)}, |
| 94 | {"dev_rx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_OCTETS)}, |
| 95 | {"dev_rx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_OCTETS)}, |
| 96 | {"dev_rx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_FRAMES)}, |
| 97 | {"dev_rx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_FRAMES)}, |
| 98 | {"dev_rx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_FRAMES)}, |
| 99 | |
| 100 | {"dev_tx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_DISCARDS)}, |
| 101 | {"dev_tx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_ERRORS)}, |
| 102 | {"dev_tx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_OCTETS)}, |
| 103 | {"dev_tx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_UC_OCTETS)}, |
| 104 | {"dev_tx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_OCTETS)}, |
| 105 | {"dev_tx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_OCTETS)}, |
| 106 | {"dev_tx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_FRAMES)}, |
| 107 | {"dev_tx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_FRAMES)}, |
| 108 | {"dev_tx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_FRAMES)}, |
| 109 | }; |
| 110 | |
| 111 | #define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats) |
| 112 | #define NN_ET_RVEC_STATS_LEN (nn->num_r_vecs * 3) |
| 113 | #define NN_ET_RVEC_GATHER_STATS 7 |
| 114 | #define NN_ET_QUEUE_STATS_LEN ((nn->num_tx_rings + nn->num_rx_rings) * 2) |
| 115 | #define NN_ET_STATS_LEN (NN_ET_GLOBAL_STATS_LEN + NN_ET_RVEC_GATHER_STATS + \ |
| 116 | NN_ET_RVEC_STATS_LEN + NN_ET_QUEUE_STATS_LEN) |
| 117 | |
| 118 | static void nfp_net_get_drvinfo(struct net_device *netdev, |
| 119 | struct ethtool_drvinfo *drvinfo) |
| 120 | { |
| 121 | struct nfp_net *nn = netdev_priv(netdev); |
| 122 | |
| 123 | strlcpy(drvinfo->driver, nfp_net_driver_name, sizeof(drvinfo->driver)); |
| 124 | strlcpy(drvinfo->version, nfp_net_driver_version, |
| 125 | sizeof(drvinfo->version)); |
| 126 | |
| 127 | snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), |
| 128 | "%d.%d.%d.%d", |
| 129 | nn->fw_ver.resv, nn->fw_ver.class, |
| 130 | nn->fw_ver.major, nn->fw_ver.minor); |
| 131 | strlcpy(drvinfo->bus_info, pci_name(nn->pdev), |
| 132 | sizeof(drvinfo->bus_info)); |
| 133 | |
| 134 | drvinfo->n_stats = NN_ET_STATS_LEN; |
| 135 | drvinfo->regdump_len = NFP_NET_CFG_BAR_SZ; |
| 136 | } |
| 137 | |
| 138 | static void nfp_net_get_ringparam(struct net_device *netdev, |
| 139 | struct ethtool_ringparam *ring) |
| 140 | { |
| 141 | struct nfp_net *nn = netdev_priv(netdev); |
| 142 | |
| 143 | ring->rx_max_pending = NFP_NET_MAX_RX_DESCS; |
| 144 | ring->tx_max_pending = NFP_NET_MAX_TX_DESCS; |
| 145 | ring->rx_pending = nn->rxd_cnt; |
| 146 | ring->tx_pending = nn->txd_cnt; |
| 147 | } |
| 148 | |
| 149 | static int nfp_net_set_ringparam(struct net_device *netdev, |
| 150 | struct ethtool_ringparam *ring) |
| 151 | { |
| 152 | struct nfp_net *nn = netdev_priv(netdev); |
| 153 | u32 rxd_cnt, txd_cnt; |
| 154 | |
Jakub Kicinski | 4c35236 | 2015-12-01 14:55:22 +0000 | [diff] [blame] | 155 | /* We don't have separate queues/rings for small/large frames. */ |
| 156 | if (ring->rx_mini_pending || ring->rx_jumbo_pending) |
| 157 | return -EINVAL; |
| 158 | |
| 159 | /* Round up to supported values */ |
| 160 | rxd_cnt = roundup_pow_of_two(ring->rx_pending); |
Jakub Kicinski | 4c35236 | 2015-12-01 14:55:22 +0000 | [diff] [blame] | 161 | txd_cnt = roundup_pow_of_two(ring->tx_pending); |
Jakub Kicinski | 4c35236 | 2015-12-01 14:55:22 +0000 | [diff] [blame] | 162 | |
Jakub Kicinski | cc7c033 | 2016-04-07 19:39:48 +0100 | [diff] [blame] | 163 | if (rxd_cnt < NFP_NET_MIN_RX_DESCS || rxd_cnt > NFP_NET_MAX_RX_DESCS || |
| 164 | txd_cnt < NFP_NET_MIN_TX_DESCS || txd_cnt > NFP_NET_MAX_TX_DESCS) |
| 165 | return -EINVAL; |
Jakub Kicinski | 4c35236 | 2015-12-01 14:55:22 +0000 | [diff] [blame] | 166 | |
Jakub Kicinski | cc7c033 | 2016-04-07 19:39:48 +0100 | [diff] [blame] | 167 | if (nn->rxd_cnt == rxd_cnt && nn->txd_cnt == txd_cnt) |
| 168 | return 0; |
Jakub Kicinski | 4c35236 | 2015-12-01 14:55:22 +0000 | [diff] [blame] | 169 | |
Jakub Kicinski | cc7c033 | 2016-04-07 19:39:48 +0100 | [diff] [blame] | 170 | nn_dbg(nn, "Change ring size: RxQ %u->%u, TxQ %u->%u\n", |
| 171 | nn->rxd_cnt, rxd_cnt, nn->txd_cnt, txd_cnt); |
| 172 | |
| 173 | return nfp_net_set_ring_size(nn, rxd_cnt, txd_cnt); |
Jakub Kicinski | 4c35236 | 2015-12-01 14:55:22 +0000 | [diff] [blame] | 174 | } |
| 175 | |
| 176 | static void nfp_net_get_strings(struct net_device *netdev, |
| 177 | u32 stringset, u8 *data) |
| 178 | { |
| 179 | struct nfp_net *nn = netdev_priv(netdev); |
| 180 | u8 *p = data; |
| 181 | int i; |
| 182 | |
| 183 | switch (stringset) { |
| 184 | case ETH_SS_STATS: |
| 185 | for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) { |
| 186 | memcpy(p, nfp_net_et_stats[i].name, ETH_GSTRING_LEN); |
| 187 | p += ETH_GSTRING_LEN; |
| 188 | } |
| 189 | for (i = 0; i < nn->num_r_vecs; i++) { |
| 190 | sprintf(p, "rvec_%u_rx_pkts", i); |
| 191 | p += ETH_GSTRING_LEN; |
| 192 | sprintf(p, "rvec_%u_tx_pkts", i); |
| 193 | p += ETH_GSTRING_LEN; |
| 194 | sprintf(p, "rvec_%u_tx_busy", i); |
| 195 | p += ETH_GSTRING_LEN; |
| 196 | } |
| 197 | strncpy(p, "hw_rx_csum_ok", ETH_GSTRING_LEN); |
| 198 | p += ETH_GSTRING_LEN; |
| 199 | strncpy(p, "hw_rx_csum_inner_ok", ETH_GSTRING_LEN); |
| 200 | p += ETH_GSTRING_LEN; |
| 201 | strncpy(p, "hw_rx_csum_err", ETH_GSTRING_LEN); |
| 202 | p += ETH_GSTRING_LEN; |
| 203 | strncpy(p, "hw_tx_csum", ETH_GSTRING_LEN); |
| 204 | p += ETH_GSTRING_LEN; |
| 205 | strncpy(p, "hw_tx_inner_csum", ETH_GSTRING_LEN); |
| 206 | p += ETH_GSTRING_LEN; |
| 207 | strncpy(p, "tx_gather", ETH_GSTRING_LEN); |
| 208 | p += ETH_GSTRING_LEN; |
| 209 | strncpy(p, "tx_lso", ETH_GSTRING_LEN); |
| 210 | p += ETH_GSTRING_LEN; |
| 211 | for (i = 0; i < nn->num_tx_rings; i++) { |
| 212 | sprintf(p, "txq_%u_pkts", i); |
| 213 | p += ETH_GSTRING_LEN; |
| 214 | sprintf(p, "txq_%u_bytes", i); |
| 215 | p += ETH_GSTRING_LEN; |
| 216 | } |
| 217 | for (i = 0; i < nn->num_rx_rings; i++) { |
| 218 | sprintf(p, "rxq_%u_pkts", i); |
| 219 | p += ETH_GSTRING_LEN; |
| 220 | sprintf(p, "rxq_%u_bytes", i); |
| 221 | p += ETH_GSTRING_LEN; |
| 222 | } |
| 223 | break; |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | static void nfp_net_get_stats(struct net_device *netdev, |
| 228 | struct ethtool_stats *stats, u64 *data) |
| 229 | { |
| 230 | u64 gathered_stats[NN_ET_RVEC_GATHER_STATS] = {}; |
| 231 | struct nfp_net *nn = netdev_priv(netdev); |
| 232 | struct rtnl_link_stats64 *netdev_stats; |
| 233 | struct rtnl_link_stats64 temp = {}; |
| 234 | u64 tmp[NN_ET_RVEC_GATHER_STATS]; |
| 235 | u8 __iomem *io_p; |
| 236 | int i, j, k; |
| 237 | u8 *p; |
| 238 | |
| 239 | netdev_stats = dev_get_stats(netdev, &temp); |
| 240 | |
| 241 | for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) { |
| 242 | switch (nfp_net_et_stats[i].type) { |
| 243 | case NETDEV_ET_STATS: |
| 244 | p = (char *)netdev_stats + nfp_net_et_stats[i].off; |
| 245 | data[i] = nfp_net_et_stats[i].sz == sizeof(u64) ? |
| 246 | *(u64 *)p : *(u32 *)p; |
| 247 | break; |
| 248 | |
| 249 | case NFP_NET_DEV_ET_STATS: |
| 250 | io_p = nn->ctrl_bar + nfp_net_et_stats[i].off; |
| 251 | data[i] = readq(io_p); |
| 252 | break; |
| 253 | } |
| 254 | } |
| 255 | for (j = 0; j < nn->num_r_vecs; j++) { |
| 256 | unsigned int start; |
| 257 | |
| 258 | do { |
| 259 | start = u64_stats_fetch_begin(&nn->r_vecs[j].rx_sync); |
| 260 | data[i++] = nn->r_vecs[j].rx_pkts; |
| 261 | tmp[0] = nn->r_vecs[j].hw_csum_rx_ok; |
| 262 | tmp[1] = nn->r_vecs[j].hw_csum_rx_inner_ok; |
| 263 | tmp[2] = nn->r_vecs[j].hw_csum_rx_error; |
| 264 | } while (u64_stats_fetch_retry(&nn->r_vecs[j].rx_sync, start)); |
| 265 | |
| 266 | do { |
| 267 | start = u64_stats_fetch_begin(&nn->r_vecs[j].tx_sync); |
| 268 | data[i++] = nn->r_vecs[j].tx_pkts; |
| 269 | data[i++] = nn->r_vecs[j].tx_busy; |
| 270 | tmp[3] = nn->r_vecs[j].hw_csum_tx; |
| 271 | tmp[4] = nn->r_vecs[j].hw_csum_tx_inner; |
| 272 | tmp[5] = nn->r_vecs[j].tx_gather; |
| 273 | tmp[6] = nn->r_vecs[j].tx_lso; |
| 274 | } while (u64_stats_fetch_retry(&nn->r_vecs[j].tx_sync, start)); |
| 275 | |
| 276 | for (k = 0; k < NN_ET_RVEC_GATHER_STATS; k++) |
| 277 | gathered_stats[k] += tmp[k]; |
| 278 | } |
| 279 | for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++) |
| 280 | data[i++] = gathered_stats[j]; |
| 281 | for (j = 0; j < nn->num_tx_rings; j++) { |
| 282 | io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j); |
| 283 | data[i++] = readq(io_p); |
| 284 | io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j) + 8; |
| 285 | data[i++] = readq(io_p); |
| 286 | } |
| 287 | for (j = 0; j < nn->num_rx_rings; j++) { |
| 288 | io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j); |
| 289 | data[i++] = readq(io_p); |
| 290 | io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j) + 8; |
| 291 | data[i++] = readq(io_p); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | static int nfp_net_get_sset_count(struct net_device *netdev, int sset) |
| 296 | { |
| 297 | struct nfp_net *nn = netdev_priv(netdev); |
| 298 | |
| 299 | switch (sset) { |
| 300 | case ETH_SS_STATS: |
| 301 | return NN_ET_STATS_LEN; |
| 302 | default: |
| 303 | return -EOPNOTSUPP; |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | /* RX network flow classification (RSS, filters, etc) |
| 308 | */ |
| 309 | static u32 ethtool_flow_to_nfp_flag(u32 flow_type) |
| 310 | { |
| 311 | static const u32 xlate_ethtool_to_nfp[IPV6_FLOW + 1] = { |
| 312 | [TCP_V4_FLOW] = NFP_NET_CFG_RSS_IPV4_TCP, |
| 313 | [TCP_V6_FLOW] = NFP_NET_CFG_RSS_IPV6_TCP, |
| 314 | [UDP_V4_FLOW] = NFP_NET_CFG_RSS_IPV4_UDP, |
| 315 | [UDP_V6_FLOW] = NFP_NET_CFG_RSS_IPV6_UDP, |
| 316 | [IPV4_FLOW] = NFP_NET_CFG_RSS_IPV4, |
| 317 | [IPV6_FLOW] = NFP_NET_CFG_RSS_IPV6, |
| 318 | }; |
| 319 | |
| 320 | if (flow_type >= ARRAY_SIZE(xlate_ethtool_to_nfp)) |
| 321 | return 0; |
| 322 | |
| 323 | return xlate_ethtool_to_nfp[flow_type]; |
| 324 | } |
| 325 | |
| 326 | static int nfp_net_get_rss_hash_opts(struct nfp_net *nn, |
| 327 | struct ethtool_rxnfc *cmd) |
| 328 | { |
| 329 | u32 nfp_rss_flag; |
| 330 | |
| 331 | cmd->data = 0; |
| 332 | |
| 333 | if (!(nn->cap & NFP_NET_CFG_CTRL_RSS)) |
| 334 | return -EOPNOTSUPP; |
| 335 | |
| 336 | nfp_rss_flag = ethtool_flow_to_nfp_flag(cmd->flow_type); |
| 337 | if (!nfp_rss_flag) |
| 338 | return -EINVAL; |
| 339 | |
| 340 | cmd->data |= RXH_IP_SRC | RXH_IP_DST; |
| 341 | if (nn->rss_cfg & nfp_rss_flag) |
| 342 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; |
| 343 | |
| 344 | return 0; |
| 345 | } |
| 346 | |
| 347 | static int nfp_net_get_rxnfc(struct net_device *netdev, |
| 348 | struct ethtool_rxnfc *cmd, u32 *rule_locs) |
| 349 | { |
| 350 | struct nfp_net *nn = netdev_priv(netdev); |
| 351 | |
| 352 | switch (cmd->cmd) { |
| 353 | case ETHTOOL_GRXRINGS: |
| 354 | cmd->data = nn->num_rx_rings; |
| 355 | return 0; |
| 356 | case ETHTOOL_GRXFH: |
| 357 | return nfp_net_get_rss_hash_opts(nn, cmd); |
| 358 | default: |
| 359 | return -EOPNOTSUPP; |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | static int nfp_net_set_rss_hash_opt(struct nfp_net *nn, |
| 364 | struct ethtool_rxnfc *nfc) |
| 365 | { |
| 366 | u32 new_rss_cfg = nn->rss_cfg; |
| 367 | u32 nfp_rss_flag; |
| 368 | int err; |
| 369 | |
| 370 | if (!(nn->cap & NFP_NET_CFG_CTRL_RSS)) |
| 371 | return -EOPNOTSUPP; |
| 372 | |
| 373 | /* RSS only supports IP SA/DA and L4 src/dst ports */ |
| 374 | if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | |
| 375 | RXH_L4_B_0_1 | RXH_L4_B_2_3)) |
| 376 | return -EINVAL; |
| 377 | |
| 378 | /* We need at least the IP SA/DA fields for hashing */ |
| 379 | if (!(nfc->data & RXH_IP_SRC) || |
| 380 | !(nfc->data & RXH_IP_DST)) |
| 381 | return -EINVAL; |
| 382 | |
| 383 | nfp_rss_flag = ethtool_flow_to_nfp_flag(nfc->flow_type); |
| 384 | if (!nfp_rss_flag) |
| 385 | return -EINVAL; |
| 386 | |
| 387 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { |
| 388 | case 0: |
| 389 | new_rss_cfg &= ~nfp_rss_flag; |
| 390 | break; |
| 391 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): |
| 392 | new_rss_cfg |= nfp_rss_flag; |
| 393 | break; |
| 394 | default: |
| 395 | return -EINVAL; |
| 396 | } |
| 397 | |
| 398 | new_rss_cfg |= NFP_NET_CFG_RSS_TOEPLITZ; |
| 399 | new_rss_cfg |= NFP_NET_CFG_RSS_MASK; |
| 400 | |
| 401 | if (new_rss_cfg == nn->rss_cfg) |
| 402 | return 0; |
| 403 | |
| 404 | writel(new_rss_cfg, nn->ctrl_bar + NFP_NET_CFG_RSS_CTRL); |
| 405 | err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RSS); |
| 406 | if (err) |
| 407 | return err; |
| 408 | |
| 409 | nn->rss_cfg = new_rss_cfg; |
| 410 | |
| 411 | nn_dbg(nn, "Changed RSS config to 0x%x\n", nn->rss_cfg); |
| 412 | return 0; |
| 413 | } |
| 414 | |
| 415 | static int nfp_net_set_rxnfc(struct net_device *netdev, |
| 416 | struct ethtool_rxnfc *cmd) |
| 417 | { |
| 418 | struct nfp_net *nn = netdev_priv(netdev); |
| 419 | |
| 420 | switch (cmd->cmd) { |
| 421 | case ETHTOOL_SRXFH: |
| 422 | return nfp_net_set_rss_hash_opt(nn, cmd); |
| 423 | default: |
| 424 | return -EOPNOTSUPP; |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | static u32 nfp_net_get_rxfh_indir_size(struct net_device *netdev) |
| 429 | { |
| 430 | struct nfp_net *nn = netdev_priv(netdev); |
| 431 | |
| 432 | if (!(nn->cap & NFP_NET_CFG_CTRL_RSS)) |
| 433 | return 0; |
| 434 | |
| 435 | return ARRAY_SIZE(nn->rss_itbl); |
| 436 | } |
| 437 | |
| 438 | static u32 nfp_net_get_rxfh_key_size(struct net_device *netdev) |
| 439 | { |
| 440 | return NFP_NET_CFG_RSS_KEY_SZ; |
| 441 | } |
| 442 | |
| 443 | static int nfp_net_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, |
| 444 | u8 *hfunc) |
| 445 | { |
| 446 | struct nfp_net *nn = netdev_priv(netdev); |
| 447 | int i; |
| 448 | |
| 449 | if (!(nn->cap & NFP_NET_CFG_CTRL_RSS)) |
| 450 | return -EOPNOTSUPP; |
| 451 | |
| 452 | if (indir) |
| 453 | for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++) |
| 454 | indir[i] = nn->rss_itbl[i]; |
| 455 | if (key) |
| 456 | memcpy(key, nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ); |
| 457 | if (hfunc) |
| 458 | *hfunc = ETH_RSS_HASH_TOP; |
| 459 | |
| 460 | return 0; |
| 461 | } |
| 462 | |
| 463 | static int nfp_net_set_rxfh(struct net_device *netdev, |
| 464 | const u32 *indir, const u8 *key, |
| 465 | const u8 hfunc) |
| 466 | { |
| 467 | struct nfp_net *nn = netdev_priv(netdev); |
| 468 | int i; |
| 469 | |
| 470 | if (!(nn->cap & NFP_NET_CFG_CTRL_RSS) || |
| 471 | !(hfunc == ETH_RSS_HASH_NO_CHANGE || hfunc == ETH_RSS_HASH_TOP)) |
| 472 | return -EOPNOTSUPP; |
| 473 | |
| 474 | if (!key && !indir) |
| 475 | return 0; |
| 476 | |
| 477 | if (key) { |
| 478 | memcpy(nn->rss_key, key, NFP_NET_CFG_RSS_KEY_SZ); |
| 479 | nfp_net_rss_write_key(nn); |
| 480 | } |
| 481 | if (indir) { |
| 482 | for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++) |
| 483 | nn->rss_itbl[i] = indir[i]; |
| 484 | |
| 485 | nfp_net_rss_write_itbl(nn); |
| 486 | } |
| 487 | |
| 488 | return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RSS); |
| 489 | } |
| 490 | |
| 491 | /* Dump BAR registers |
| 492 | */ |
| 493 | static int nfp_net_get_regs_len(struct net_device *netdev) |
| 494 | { |
| 495 | return NFP_NET_CFG_BAR_SZ; |
| 496 | } |
| 497 | |
| 498 | static void nfp_net_get_regs(struct net_device *netdev, |
| 499 | struct ethtool_regs *regs, void *p) |
| 500 | { |
| 501 | struct nfp_net *nn = netdev_priv(netdev); |
| 502 | u32 *regs_buf = p; |
| 503 | int i; |
| 504 | |
| 505 | regs->version = nn_readl(nn, NFP_NET_CFG_VERSION); |
| 506 | |
| 507 | for (i = 0; i < NFP_NET_CFG_BAR_SZ / sizeof(u32); i++) |
| 508 | regs_buf[i] = readl(nn->ctrl_bar + (i * sizeof(u32))); |
| 509 | } |
| 510 | |
| 511 | static int nfp_net_get_coalesce(struct net_device *netdev, |
| 512 | struct ethtool_coalesce *ec) |
| 513 | { |
| 514 | struct nfp_net *nn = netdev_priv(netdev); |
| 515 | |
| 516 | if (!(nn->cap & NFP_NET_CFG_CTRL_IRQMOD)) |
| 517 | return -EINVAL; |
| 518 | |
| 519 | ec->rx_coalesce_usecs = nn->rx_coalesce_usecs; |
| 520 | ec->rx_max_coalesced_frames = nn->rx_coalesce_max_frames; |
| 521 | ec->tx_coalesce_usecs = nn->tx_coalesce_usecs; |
| 522 | ec->tx_max_coalesced_frames = nn->tx_coalesce_max_frames; |
| 523 | |
| 524 | return 0; |
| 525 | } |
| 526 | |
| 527 | static int nfp_net_set_coalesce(struct net_device *netdev, |
| 528 | struct ethtool_coalesce *ec) |
| 529 | { |
| 530 | struct nfp_net *nn = netdev_priv(netdev); |
| 531 | unsigned int factor; |
| 532 | |
| 533 | if (ec->rx_coalesce_usecs_irq || |
| 534 | ec->rx_max_coalesced_frames_irq || |
| 535 | ec->tx_coalesce_usecs_irq || |
| 536 | ec->tx_max_coalesced_frames_irq || |
| 537 | ec->stats_block_coalesce_usecs || |
| 538 | ec->use_adaptive_rx_coalesce || |
| 539 | ec->use_adaptive_tx_coalesce || |
| 540 | ec->pkt_rate_low || |
| 541 | ec->rx_coalesce_usecs_low || |
| 542 | ec->rx_max_coalesced_frames_low || |
| 543 | ec->tx_coalesce_usecs_low || |
| 544 | ec->tx_max_coalesced_frames_low || |
| 545 | ec->pkt_rate_high || |
| 546 | ec->rx_coalesce_usecs_high || |
| 547 | ec->rx_max_coalesced_frames_high || |
| 548 | ec->tx_coalesce_usecs_high || |
| 549 | ec->tx_max_coalesced_frames_high || |
| 550 | ec->rate_sample_interval) |
| 551 | return -ENOTSUPP; |
| 552 | |
| 553 | /* Compute factor used to convert coalesce '_usecs' parameters to |
| 554 | * ME timestamp ticks. There are 16 ME clock cycles for each timestamp |
| 555 | * count. |
| 556 | */ |
| 557 | factor = nn->me_freq_mhz / 16; |
| 558 | |
| 559 | /* Each pair of (usecs, max_frames) fields specifies that interrupts |
| 560 | * should be coalesced until |
| 561 | * (usecs > 0 && time_since_first_completion >= usecs) || |
| 562 | * (max_frames > 0 && completed_frames >= max_frames) |
| 563 | * |
| 564 | * It is illegal to set both usecs and max_frames to zero as this would |
| 565 | * cause interrupts to never be generated. To disable coalescing, set |
| 566 | * usecs = 0 and max_frames = 1. |
| 567 | * |
| 568 | * Some implementations ignore the value of max_frames and use the |
| 569 | * condition time_since_first_completion >= usecs |
| 570 | */ |
| 571 | |
| 572 | if (!(nn->cap & NFP_NET_CFG_CTRL_IRQMOD)) |
| 573 | return -EINVAL; |
| 574 | |
| 575 | /* ensure valid configuration */ |
| 576 | if (!ec->rx_coalesce_usecs && !ec->rx_max_coalesced_frames) |
| 577 | return -EINVAL; |
| 578 | |
| 579 | if (!ec->tx_coalesce_usecs && !ec->tx_max_coalesced_frames) |
| 580 | return -EINVAL; |
| 581 | |
| 582 | if (ec->rx_coalesce_usecs * factor >= ((1 << 16) - 1)) |
| 583 | return -EINVAL; |
| 584 | |
| 585 | if (ec->tx_coalesce_usecs * factor >= ((1 << 16) - 1)) |
| 586 | return -EINVAL; |
| 587 | |
| 588 | if (ec->rx_max_coalesced_frames >= ((1 << 16) - 1)) |
| 589 | return -EINVAL; |
| 590 | |
| 591 | if (ec->tx_max_coalesced_frames >= ((1 << 16) - 1)) |
| 592 | return -EINVAL; |
| 593 | |
| 594 | /* configuration is valid */ |
| 595 | nn->rx_coalesce_usecs = ec->rx_coalesce_usecs; |
| 596 | nn->rx_coalesce_max_frames = ec->rx_max_coalesced_frames; |
| 597 | nn->tx_coalesce_usecs = ec->tx_coalesce_usecs; |
| 598 | nn->tx_coalesce_max_frames = ec->tx_max_coalesced_frames; |
| 599 | |
| 600 | /* write configuration to device */ |
| 601 | nfp_net_coalesce_write_cfg(nn); |
| 602 | return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_IRQMOD); |
| 603 | } |
| 604 | |
| 605 | static const struct ethtool_ops nfp_net_ethtool_ops = { |
| 606 | .get_drvinfo = nfp_net_get_drvinfo, |
Jakub Kicinski | 2370def | 2016-06-29 21:55:55 +0100 | [diff] [blame] | 607 | .get_link = ethtool_op_get_link, |
Jakub Kicinski | 4c35236 | 2015-12-01 14:55:22 +0000 | [diff] [blame] | 608 | .get_ringparam = nfp_net_get_ringparam, |
| 609 | .set_ringparam = nfp_net_set_ringparam, |
| 610 | .get_strings = nfp_net_get_strings, |
| 611 | .get_ethtool_stats = nfp_net_get_stats, |
| 612 | .get_sset_count = nfp_net_get_sset_count, |
| 613 | .get_rxnfc = nfp_net_get_rxnfc, |
| 614 | .set_rxnfc = nfp_net_set_rxnfc, |
| 615 | .get_rxfh_indir_size = nfp_net_get_rxfh_indir_size, |
| 616 | .get_rxfh_key_size = nfp_net_get_rxfh_key_size, |
| 617 | .get_rxfh = nfp_net_get_rxfh, |
| 618 | .set_rxfh = nfp_net_set_rxfh, |
| 619 | .get_regs_len = nfp_net_get_regs_len, |
| 620 | .get_regs = nfp_net_get_regs, |
| 621 | .get_coalesce = nfp_net_get_coalesce, |
| 622 | .set_coalesce = nfp_net_set_coalesce, |
| 623 | }; |
| 624 | |
| 625 | void nfp_net_set_ethtool_ops(struct net_device *netdev) |
| 626 | { |
| 627 | netdev->ethtool_ops = &nfp_net_ethtool_ops; |
| 628 | } |