Commit | Line | Data |
---|---|---|
c27a02cd YP |
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> | |
af22d9de | 37 | #include <linux/mlx4/driver.h> |
c27a02cd YP |
38 | |
39 | #include "mlx4_en.h" | |
40 | #include "en_port.h" | |
41 | ||
82067281 | 42 | #define EN_ETHTOOL_QP_ATTACH (1ull << 63) |
82067281 HHZ |
43 | #define EN_ETHTOOL_SHORT_MASK cpu_to_be16(0xffff) |
44 | #define EN_ETHTOOL_WORD_MASK cpu_to_be32(0xffffffff) | |
c27a02cd | 45 | |
79c54b6b AV |
46 | static int mlx4_en_moderation_update(struct mlx4_en_priv *priv) |
47 | { | |
48 | int i; | |
49 | int err = 0; | |
50 | ||
51 | for (i = 0; i < priv->tx_ring_num; i++) { | |
52 | priv->tx_cq[i].moder_cnt = priv->tx_frames; | |
53 | priv->tx_cq[i].moder_time = priv->tx_usecs; | |
54 | err = mlx4_en_set_cq_moder(priv, &priv->tx_cq[i]); | |
55 | if (err) | |
56 | return err; | |
57 | } | |
58 | ||
59 | if (priv->adaptive_rx_coal) | |
60 | return 0; | |
61 | ||
62 | for (i = 0; i < priv->rx_ring_num; i++) { | |
63 | priv->rx_cq[i].moder_cnt = priv->rx_frames; | |
64 | priv->rx_cq[i].moder_time = priv->rx_usecs; | |
65 | priv->last_moder_time[i] = MLX4_EN_AUTO_CONF; | |
66 | err = mlx4_en_set_cq_moder(priv, &priv->rx_cq[i]); | |
67 | if (err) | |
68 | return err; | |
69 | } | |
70 | ||
71 | return err; | |
72 | } | |
73 | ||
c27a02cd YP |
74 | static void |
75 | mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) | |
76 | { | |
77 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
78 | struct mlx4_en_dev *mdev = priv->mdev; | |
79 | ||
612a94d6 RJ |
80 | strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); |
81 | strlcpy(drvinfo->version, DRV_VERSION " (" DRV_RELDATE ")", | |
82 | sizeof(drvinfo->version)); | |
83 | snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), | |
84 | "%d.%d.%d", | |
c27a02cd YP |
85 | (u16) (mdev->dev->caps.fw_ver >> 32), |
86 | (u16) ((mdev->dev->caps.fw_ver >> 16) & 0xffff), | |
87 | (u16) (mdev->dev->caps.fw_ver & 0xffff)); | |
612a94d6 RJ |
88 | strlcpy(drvinfo->bus_info, pci_name(mdev->dev->pdev), |
89 | sizeof(drvinfo->bus_info)); | |
c27a02cd YP |
90 | drvinfo->n_stats = 0; |
91 | drvinfo->regdump_len = 0; | |
92 | drvinfo->eedump_len = 0; | |
93 | } | |
94 | ||
c27a02cd YP |
95 | static const char main_strings[][ETH_GSTRING_LEN] = { |
96 | "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors", | |
97 | "tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions", | |
98 | "rx_length_errors", "rx_over_errors", "rx_crc_errors", | |
99 | "rx_frame_errors", "rx_fifo_errors", "rx_missed_errors", | |
100 | "tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors", | |
101 | "tx_heartbeat_errors", "tx_window_errors", | |
102 | ||
103 | /* port statistics */ | |
fa37a958 | 104 | "tso_packets", |
c27a02cd YP |
105 | "queue_stopped", "wake_queue", "tx_timeout", "rx_alloc_failed", |
106 | "rx_csum_good", "rx_csum_none", "tx_chksum_offload", | |
107 | ||
108 | /* packet statistics */ | |
109 | "broadcast", "rx_prio_0", "rx_prio_1", "rx_prio_2", "rx_prio_3", | |
110 | "rx_prio_4", "rx_prio_5", "rx_prio_6", "rx_prio_7", "tx_prio_0", | |
111 | "tx_prio_1", "tx_prio_2", "tx_prio_3", "tx_prio_4", "tx_prio_5", | |
112 | "tx_prio_6", "tx_prio_7", | |
113 | }; | |
d61702f1 | 114 | #define NUM_MAIN_STATS 21 |
c27a02cd YP |
115 | #define NUM_ALL_STATS (NUM_MAIN_STATS + NUM_PORT_STATS + NUM_PKT_STATS + NUM_PERF_STATS) |
116 | ||
e7c1c2c4 | 117 | static const char mlx4_en_test_names[][ETH_GSTRING_LEN]= { |
fd9071ec | 118 | "Interrupt Test", |
e7c1c2c4 YP |
119 | "Link Test", |
120 | "Speed Test", | |
121 | "Register Test", | |
122 | "Loopback Test", | |
123 | }; | |
124 | ||
c27a02cd YP |
125 | static u32 mlx4_en_get_msglevel(struct net_device *dev) |
126 | { | |
127 | return ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable; | |
128 | } | |
129 | ||
130 | static void mlx4_en_set_msglevel(struct net_device *dev, u32 val) | |
131 | { | |
132 | ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable = val; | |
133 | } | |
134 | ||
135 | static void mlx4_en_get_wol(struct net_device *netdev, | |
136 | struct ethtool_wolinfo *wol) | |
137 | { | |
14c07b13 YP |
138 | struct mlx4_en_priv *priv = netdev_priv(netdev); |
139 | int err = 0; | |
140 | u64 config = 0; | |
559a9f1d | 141 | u64 mask; |
14c07b13 | 142 | |
559a9f1d OD |
143 | if ((priv->port < 1) || (priv->port > 2)) { |
144 | en_err(priv, "Failed to get WoL information\n"); | |
145 | return; | |
146 | } | |
147 | ||
148 | mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 : | |
149 | MLX4_DEV_CAP_FLAG_WOL_PORT2; | |
150 | ||
151 | if (!(priv->mdev->dev->caps.flags & mask)) { | |
14c07b13 YP |
152 | wol->supported = 0; |
153 | wol->wolopts = 0; | |
154 | return; | |
155 | } | |
156 | ||
157 | err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); | |
158 | if (err) { | |
159 | en_err(priv, "Failed to get WoL information\n"); | |
160 | return; | |
161 | } | |
162 | ||
163 | if (config & MLX4_EN_WOL_MAGIC) | |
164 | wol->supported = WAKE_MAGIC; | |
165 | else | |
166 | wol->supported = 0; | |
167 | ||
168 | if (config & MLX4_EN_WOL_ENABLED) | |
169 | wol->wolopts = WAKE_MAGIC; | |
170 | else | |
171 | wol->wolopts = 0; | |
172 | } | |
173 | ||
174 | static int mlx4_en_set_wol(struct net_device *netdev, | |
175 | struct ethtool_wolinfo *wol) | |
176 | { | |
177 | struct mlx4_en_priv *priv = netdev_priv(netdev); | |
178 | u64 config = 0; | |
179 | int err = 0; | |
559a9f1d OD |
180 | u64 mask; |
181 | ||
182 | if ((priv->port < 1) || (priv->port > 2)) | |
183 | return -EOPNOTSUPP; | |
184 | ||
185 | mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 : | |
186 | MLX4_DEV_CAP_FLAG_WOL_PORT2; | |
14c07b13 | 187 | |
559a9f1d | 188 | if (!(priv->mdev->dev->caps.flags & mask)) |
14c07b13 YP |
189 | return -EOPNOTSUPP; |
190 | ||
191 | if (wol->supported & ~WAKE_MAGIC) | |
192 | return -EINVAL; | |
193 | ||
194 | err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); | |
195 | if (err) { | |
196 | en_err(priv, "Failed to get WoL info, unable to modify\n"); | |
197 | return err; | |
198 | } | |
199 | ||
200 | if (wol->wolopts & WAKE_MAGIC) { | |
201 | config |= MLX4_EN_WOL_DO_MODIFY | MLX4_EN_WOL_ENABLED | | |
202 | MLX4_EN_WOL_MAGIC; | |
203 | } else { | |
204 | config &= ~(MLX4_EN_WOL_ENABLED | MLX4_EN_WOL_MAGIC); | |
205 | config |= MLX4_EN_WOL_DO_MODIFY; | |
206 | } | |
207 | ||
208 | err = mlx4_wol_write(priv->mdev->dev, config, priv->port); | |
209 | if (err) | |
210 | en_err(priv, "Failed to set WoL information\n"); | |
211 | ||
212 | return err; | |
c27a02cd YP |
213 | } |
214 | ||
215 | static int mlx4_en_get_sset_count(struct net_device *dev, int sset) | |
216 | { | |
217 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
93ece0c1 | 218 | int bit_count = hweight64(priv->stats_bitmap); |
c27a02cd | 219 | |
e7c1c2c4 YP |
220 | switch (sset) { |
221 | case ETH_SS_STATS: | |
93ece0c1 | 222 | return (priv->stats_bitmap ? bit_count : NUM_ALL_STATS) + |
e7c1c2c4 YP |
223 | (priv->tx_ring_num + priv->rx_ring_num) * 2; |
224 | case ETH_SS_TEST: | |
ccf86321 OG |
225 | return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags |
226 | & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2; | |
e7c1c2c4 | 227 | default: |
c27a02cd | 228 | return -EOPNOTSUPP; |
e7c1c2c4 | 229 | } |
c27a02cd YP |
230 | } |
231 | ||
232 | static void mlx4_en_get_ethtool_stats(struct net_device *dev, | |
233 | struct ethtool_stats *stats, uint64_t *data) | |
234 | { | |
235 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
236 | int index = 0; | |
93ece0c1 | 237 | int i, j = 0; |
c27a02cd YP |
238 | |
239 | spin_lock_bh(&priv->stats_lock); | |
240 | ||
93ece0c1 EE |
241 | if (!(priv->stats_bitmap)) { |
242 | for (i = 0; i < NUM_MAIN_STATS; i++) | |
243 | data[index++] = | |
244 | ((unsigned long *) &priv->stats)[i]; | |
245 | for (i = 0; i < NUM_PORT_STATS; i++) | |
246 | data[index++] = | |
247 | ((unsigned long *) &priv->port_stats)[i]; | |
248 | for (i = 0; i < NUM_PKT_STATS; i++) | |
249 | data[index++] = | |
250 | ((unsigned long *) &priv->pkstats)[i]; | |
251 | } else { | |
252 | for (i = 0; i < NUM_MAIN_STATS; i++) { | |
253 | if ((priv->stats_bitmap >> j) & 1) | |
254 | data[index++] = | |
255 | ((unsigned long *) &priv->stats)[i]; | |
256 | j++; | |
257 | } | |
258 | for (i = 0; i < NUM_PORT_STATS; i++) { | |
259 | if ((priv->stats_bitmap >> j) & 1) | |
260 | data[index++] = | |
261 | ((unsigned long *) &priv->port_stats)[i]; | |
262 | j++; | |
263 | } | |
264 | } | |
c27a02cd YP |
265 | for (i = 0; i < priv->tx_ring_num; i++) { |
266 | data[index++] = priv->tx_ring[i].packets; | |
267 | data[index++] = priv->tx_ring[i].bytes; | |
268 | } | |
269 | for (i = 0; i < priv->rx_ring_num; i++) { | |
270 | data[index++] = priv->rx_ring[i].packets; | |
271 | data[index++] = priv->rx_ring[i].bytes; | |
272 | } | |
c27a02cd YP |
273 | spin_unlock_bh(&priv->stats_lock); |
274 | ||
275 | } | |
276 | ||
e7c1c2c4 YP |
277 | static void mlx4_en_self_test(struct net_device *dev, |
278 | struct ethtool_test *etest, u64 *buf) | |
279 | { | |
280 | mlx4_en_ex_selftest(dev, &etest->flags, buf); | |
281 | } | |
282 | ||
c27a02cd YP |
283 | static void mlx4_en_get_strings(struct net_device *dev, |
284 | uint32_t stringset, uint8_t *data) | |
285 | { | |
286 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
287 | int index = 0; | |
288 | int i; | |
289 | ||
e7c1c2c4 YP |
290 | switch (stringset) { |
291 | case ETH_SS_TEST: | |
292 | for (i = 0; i < MLX4_EN_NUM_SELF_TEST - 2; i++) | |
293 | strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); | |
ccf86321 | 294 | if (priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) |
e7c1c2c4 YP |
295 | for (; i < MLX4_EN_NUM_SELF_TEST; i++) |
296 | strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); | |
297 | break; | |
298 | ||
299 | case ETH_SS_STATS: | |
300 | /* Add main counters */ | |
93ece0c1 EE |
301 | if (!priv->stats_bitmap) { |
302 | for (i = 0; i < NUM_MAIN_STATS; i++) | |
303 | strcpy(data + (index++) * ETH_GSTRING_LEN, | |
304 | main_strings[i]); | |
305 | for (i = 0; i < NUM_PORT_STATS; i++) | |
306 | strcpy(data + (index++) * ETH_GSTRING_LEN, | |
307 | main_strings[i + | |
308 | NUM_MAIN_STATS]); | |
309 | for (i = 0; i < NUM_PKT_STATS; i++) | |
310 | strcpy(data + (index++) * ETH_GSTRING_LEN, | |
311 | main_strings[i + | |
312 | NUM_MAIN_STATS + | |
313 | NUM_PORT_STATS]); | |
314 | } else | |
315 | for (i = 0; i < NUM_MAIN_STATS + NUM_PORT_STATS; i++) { | |
316 | if ((priv->stats_bitmap >> i) & 1) { | |
317 | strcpy(data + | |
318 | (index++) * ETH_GSTRING_LEN, | |
319 | main_strings[i]); | |
320 | } | |
321 | if (!(priv->stats_bitmap >> i)) | |
322 | break; | |
323 | } | |
e7c1c2c4 YP |
324 | for (i = 0; i < priv->tx_ring_num; i++) { |
325 | sprintf(data + (index++) * ETH_GSTRING_LEN, | |
326 | "tx%d_packets", i); | |
327 | sprintf(data + (index++) * ETH_GSTRING_LEN, | |
328 | "tx%d_bytes", i); | |
329 | } | |
330 | for (i = 0; i < priv->rx_ring_num; i++) { | |
331 | sprintf(data + (index++) * ETH_GSTRING_LEN, | |
332 | "rx%d_packets", i); | |
333 | sprintf(data + (index++) * ETH_GSTRING_LEN, | |
334 | "rx%d_bytes", i); | |
335 | } | |
e7c1c2c4 YP |
336 | break; |
337 | } | |
c27a02cd YP |
338 | } |
339 | ||
340 | static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | |
341 | { | |
7699517d YP |
342 | struct mlx4_en_priv *priv = netdev_priv(dev); |
343 | int trans_type; | |
344 | ||
c27a02cd YP |
345 | cmd->autoneg = AUTONEG_DISABLE; |
346 | cmd->supported = SUPPORTED_10000baseT_Full; | |
7699517d YP |
347 | cmd->advertising = ADVERTISED_10000baseT_Full; |
348 | ||
349 | if (mlx4_en_QUERY_PORT(priv->mdev, priv->port)) | |
350 | return -ENOMEM; | |
351 | ||
352 | trans_type = priv->port_state.transciver; | |
c27a02cd | 353 | if (netif_carrier_ok(dev)) { |
70739497 | 354 | ethtool_cmd_speed_set(cmd, priv->port_state.link_speed); |
c27a02cd YP |
355 | cmd->duplex = DUPLEX_FULL; |
356 | } else { | |
70739497 | 357 | ethtool_cmd_speed_set(cmd, -1); |
c27a02cd YP |
358 | cmd->duplex = -1; |
359 | } | |
7699517d YP |
360 | |
361 | if (trans_type > 0 && trans_type <= 0xC) { | |
362 | cmd->port = PORT_FIBRE; | |
363 | cmd->transceiver = XCVR_EXTERNAL; | |
364 | cmd->supported |= SUPPORTED_FIBRE; | |
365 | cmd->advertising |= ADVERTISED_FIBRE; | |
366 | } else if (trans_type == 0x80 || trans_type == 0) { | |
367 | cmd->port = PORT_TP; | |
368 | cmd->transceiver = XCVR_INTERNAL; | |
369 | cmd->supported |= SUPPORTED_TP; | |
370 | cmd->advertising |= ADVERTISED_TP; | |
371 | } else { | |
372 | cmd->port = -1; | |
373 | cmd->transceiver = -1; | |
374 | } | |
c27a02cd YP |
375 | return 0; |
376 | } | |
377 | ||
378 | static int mlx4_en_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) | |
379 | { | |
380 | if ((cmd->autoneg == AUTONEG_ENABLE) || | |
25db0338 DD |
381 | (ethtool_cmd_speed(cmd) != SPEED_10000) || |
382 | (cmd->duplex != DUPLEX_FULL)) | |
c27a02cd YP |
383 | return -EINVAL; |
384 | ||
385 | /* Nothing to change */ | |
386 | return 0; | |
387 | } | |
388 | ||
389 | static int mlx4_en_get_coalesce(struct net_device *dev, | |
390 | struct ethtool_coalesce *coal) | |
391 | { | |
392 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
393 | ||
a19a848a YP |
394 | coal->tx_coalesce_usecs = priv->tx_usecs; |
395 | coal->tx_max_coalesced_frames = priv->tx_frames; | |
c27a02cd YP |
396 | coal->rx_coalesce_usecs = priv->rx_usecs; |
397 | coal->rx_max_coalesced_frames = priv->rx_frames; | |
398 | ||
399 | coal->pkt_rate_low = priv->pkt_rate_low; | |
400 | coal->rx_coalesce_usecs_low = priv->rx_usecs_low; | |
401 | coal->pkt_rate_high = priv->pkt_rate_high; | |
402 | coal->rx_coalesce_usecs_high = priv->rx_usecs_high; | |
403 | coal->rate_sample_interval = priv->sample_interval; | |
404 | coal->use_adaptive_rx_coalesce = priv->adaptive_rx_coal; | |
405 | return 0; | |
406 | } | |
407 | ||
408 | static int mlx4_en_set_coalesce(struct net_device *dev, | |
409 | struct ethtool_coalesce *coal) | |
410 | { | |
411 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
c27a02cd YP |
412 | |
413 | priv->rx_frames = (coal->rx_max_coalesced_frames == | |
414 | MLX4_EN_AUTO_CONF) ? | |
3db36fb2 | 415 | MLX4_EN_RX_COAL_TARGET : |
c27a02cd YP |
416 | coal->rx_max_coalesced_frames; |
417 | priv->rx_usecs = (coal->rx_coalesce_usecs == | |
418 | MLX4_EN_AUTO_CONF) ? | |
419 | MLX4_EN_RX_COAL_TIME : | |
420 | coal->rx_coalesce_usecs; | |
421 | ||
a19a848a YP |
422 | /* Setting TX coalescing parameters */ |
423 | if (coal->tx_coalesce_usecs != priv->tx_usecs || | |
424 | coal->tx_max_coalesced_frames != priv->tx_frames) { | |
425 | priv->tx_usecs = coal->tx_coalesce_usecs; | |
426 | priv->tx_frames = coal->tx_max_coalesced_frames; | |
a19a848a YP |
427 | } |
428 | ||
c27a02cd YP |
429 | /* Set adaptive coalescing params */ |
430 | priv->pkt_rate_low = coal->pkt_rate_low; | |
431 | priv->rx_usecs_low = coal->rx_coalesce_usecs_low; | |
432 | priv->pkt_rate_high = coal->pkt_rate_high; | |
433 | priv->rx_usecs_high = coal->rx_coalesce_usecs_high; | |
434 | priv->sample_interval = coal->rate_sample_interval; | |
435 | priv->adaptive_rx_coal = coal->use_adaptive_rx_coalesce; | |
c27a02cd | 436 | |
79c54b6b | 437 | return mlx4_en_moderation_update(priv); |
c27a02cd YP |
438 | } |
439 | ||
440 | static int mlx4_en_set_pauseparam(struct net_device *dev, | |
441 | struct ethtool_pauseparam *pause) | |
442 | { | |
443 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
444 | struct mlx4_en_dev *mdev = priv->mdev; | |
445 | int err; | |
446 | ||
d53b93f2 YP |
447 | priv->prof->tx_pause = pause->tx_pause != 0; |
448 | priv->prof->rx_pause = pause->rx_pause != 0; | |
c27a02cd YP |
449 | err = mlx4_SET_PORT_general(mdev->dev, priv->port, |
450 | priv->rx_skb_size + ETH_FCS_LEN, | |
d53b93f2 YP |
451 | priv->prof->tx_pause, |
452 | priv->prof->tx_ppp, | |
453 | priv->prof->rx_pause, | |
454 | priv->prof->rx_ppp); | |
c27a02cd | 455 | if (err) |
453a6082 | 456 | en_err(priv, "Failed setting pause params\n"); |
c27a02cd YP |
457 | |
458 | return err; | |
459 | } | |
460 | ||
461 | static void mlx4_en_get_pauseparam(struct net_device *dev, | |
462 | struct ethtool_pauseparam *pause) | |
463 | { | |
464 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
c27a02cd | 465 | |
d53b93f2 YP |
466 | pause->tx_pause = priv->prof->tx_pause; |
467 | pause->rx_pause = priv->prof->rx_pause; | |
c27a02cd YP |
468 | } |
469 | ||
18cc42a3 YP |
470 | static int mlx4_en_set_ringparam(struct net_device *dev, |
471 | struct ethtool_ringparam *param) | |
472 | { | |
473 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
474 | struct mlx4_en_dev *mdev = priv->mdev; | |
475 | u32 rx_size, tx_size; | |
476 | int port_up = 0; | |
477 | int err = 0; | |
478 | ||
479 | if (param->rx_jumbo_pending || param->rx_mini_pending) | |
480 | return -EINVAL; | |
481 | ||
482 | rx_size = roundup_pow_of_two(param->rx_pending); | |
483 | rx_size = max_t(u32, rx_size, MLX4_EN_MIN_RX_SIZE); | |
bd531e36 | 484 | rx_size = min_t(u32, rx_size, MLX4_EN_MAX_RX_SIZE); |
18cc42a3 YP |
485 | tx_size = roundup_pow_of_two(param->tx_pending); |
486 | tx_size = max_t(u32, tx_size, MLX4_EN_MIN_TX_SIZE); | |
bd531e36 | 487 | tx_size = min_t(u32, tx_size, MLX4_EN_MAX_TX_SIZE); |
18cc42a3 | 488 | |
bc081cec YP |
489 | if (rx_size == (priv->port_up ? priv->rx_ring[0].actual_size : |
490 | priv->rx_ring[0].size) && | |
491 | tx_size == priv->tx_ring[0].size) | |
18cc42a3 YP |
492 | return 0; |
493 | ||
494 | mutex_lock(&mdev->state_lock); | |
495 | if (priv->port_up) { | |
496 | port_up = 1; | |
497 | mlx4_en_stop_port(dev); | |
498 | } | |
499 | ||
fe0af03c | 500 | mlx4_en_free_resources(priv); |
18cc42a3 YP |
501 | |
502 | priv->prof->tx_ring_size = tx_size; | |
503 | priv->prof->rx_ring_size = rx_size; | |
504 | ||
505 | err = mlx4_en_alloc_resources(priv); | |
506 | if (err) { | |
453a6082 | 507 | en_err(priv, "Failed reallocating port resources\n"); |
18cc42a3 YP |
508 | goto out; |
509 | } | |
510 | if (port_up) { | |
511 | err = mlx4_en_start_port(dev); | |
512 | if (err) | |
453a6082 | 513 | en_err(priv, "Failed starting port\n"); |
18cc42a3 YP |
514 | } |
515 | ||
79c54b6b | 516 | err = mlx4_en_moderation_update(priv); |
6b4d8d9f | 517 | |
18cc42a3 YP |
518 | out: |
519 | mutex_unlock(&mdev->state_lock); | |
520 | return err; | |
521 | } | |
522 | ||
c27a02cd YP |
523 | static void mlx4_en_get_ringparam(struct net_device *dev, |
524 | struct ethtool_ringparam *param) | |
525 | { | |
526 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
c27a02cd YP |
527 | |
528 | memset(param, 0, sizeof(*param)); | |
bd531e36 YP |
529 | param->rx_max_pending = MLX4_EN_MAX_RX_SIZE; |
530 | param->tx_max_pending = MLX4_EN_MAX_TX_SIZE; | |
bc081cec YP |
531 | param->rx_pending = priv->port_up ? |
532 | priv->rx_ring[0].actual_size : priv->rx_ring[0].size; | |
533 | param->tx_pending = priv->tx_ring[0].size; | |
c27a02cd YP |
534 | } |
535 | ||
93d3e367 YP |
536 | static u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev) |
537 | { | |
538 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
539 | ||
540 | return priv->rx_ring_num; | |
541 | } | |
542 | ||
543 | static int mlx4_en_get_rxfh_indir(struct net_device *dev, u32 *ring_index) | |
544 | { | |
545 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
546 | struct mlx4_en_rss_map *rss_map = &priv->rss_map; | |
547 | int rss_rings; | |
548 | size_t n = priv->rx_ring_num; | |
549 | int err = 0; | |
550 | ||
551 | rss_rings = priv->prof->rss_rings ?: priv->rx_ring_num; | |
552 | ||
553 | while (n--) { | |
554 | ring_index[n] = rss_map->qps[n % rss_rings].qpn - | |
555 | rss_map->base_qpn; | |
556 | } | |
557 | ||
558 | return err; | |
559 | } | |
560 | ||
561 | static int mlx4_en_set_rxfh_indir(struct net_device *dev, | |
562 | const u32 *ring_index) | |
563 | { | |
564 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
565 | struct mlx4_en_dev *mdev = priv->mdev; | |
566 | int port_up = 0; | |
567 | int err = 0; | |
568 | int i; | |
569 | int rss_rings = 0; | |
570 | ||
571 | /* Calculate RSS table size and make sure flows are spread evenly | |
572 | * between rings | |
573 | */ | |
574 | for (i = 0; i < priv->rx_ring_num; i++) { | |
575 | if (i > 0 && !ring_index[i] && !rss_rings) | |
576 | rss_rings = i; | |
577 | ||
578 | if (ring_index[i] != (i % (rss_rings ?: priv->rx_ring_num))) | |
579 | return -EINVAL; | |
580 | } | |
581 | ||
582 | if (!rss_rings) | |
583 | rss_rings = priv->rx_ring_num; | |
584 | ||
585 | /* RSS table size must be an order of 2 */ | |
586 | if (!is_power_of_2(rss_rings)) | |
587 | return -EINVAL; | |
588 | ||
589 | mutex_lock(&mdev->state_lock); | |
590 | if (priv->port_up) { | |
591 | port_up = 1; | |
592 | mlx4_en_stop_port(dev); | |
593 | } | |
594 | ||
595 | priv->prof->rss_rings = rss_rings; | |
596 | ||
597 | if (port_up) { | |
598 | err = mlx4_en_start_port(dev); | |
599 | if (err) | |
600 | en_err(priv, "Failed starting port\n"); | |
601 | } | |
602 | ||
603 | mutex_unlock(&mdev->state_lock); | |
604 | return err; | |
605 | } | |
606 | ||
82067281 HHZ |
607 | #define all_zeros_or_all_ones(field) \ |
608 | ((field) == 0 || (field) == (__force typeof(field))-1) | |
609 | ||
610 | static int mlx4_en_validate_flow(struct net_device *dev, | |
611 | struct ethtool_rxnfc *cmd) | |
612 | { | |
613 | struct ethtool_usrip4_spec *l3_mask; | |
614 | struct ethtool_tcpip4_spec *l4_mask; | |
615 | struct ethhdr *eth_mask; | |
616 | u64 full_mac = ~0ull; | |
617 | u64 zero_mac = 0; | |
618 | ||
619 | if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) | |
620 | return -EINVAL; | |
621 | ||
622 | switch (cmd->fs.flow_type & ~FLOW_EXT) { | |
623 | case TCP_V4_FLOW: | |
624 | case UDP_V4_FLOW: | |
625 | if (cmd->fs.m_u.tcp_ip4_spec.tos) | |
626 | return -EINVAL; | |
627 | l4_mask = &cmd->fs.m_u.tcp_ip4_spec; | |
628 | /* don't allow mask which isn't all 0 or 1 */ | |
629 | if (!all_zeros_or_all_ones(l4_mask->ip4src) || | |
630 | !all_zeros_or_all_ones(l4_mask->ip4dst) || | |
631 | !all_zeros_or_all_ones(l4_mask->psrc) || | |
632 | !all_zeros_or_all_ones(l4_mask->pdst)) | |
633 | return -EINVAL; | |
634 | break; | |
635 | case IP_USER_FLOW: | |
636 | l3_mask = &cmd->fs.m_u.usr_ip4_spec; | |
637 | if (l3_mask->l4_4_bytes || l3_mask->tos || l3_mask->proto || | |
638 | cmd->fs.h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4 || | |
639 | (!l3_mask->ip4src && !l3_mask->ip4dst) || | |
640 | !all_zeros_or_all_ones(l3_mask->ip4src) || | |
641 | !all_zeros_or_all_ones(l3_mask->ip4dst)) | |
642 | return -EINVAL; | |
643 | break; | |
644 | case ETHER_FLOW: | |
645 | eth_mask = &cmd->fs.m_u.ether_spec; | |
646 | /* source mac mask must not be set */ | |
647 | if (memcmp(eth_mask->h_source, &zero_mac, ETH_ALEN)) | |
648 | return -EINVAL; | |
649 | ||
650 | /* dest mac mask must be ff:ff:ff:ff:ff:ff */ | |
651 | if (memcmp(eth_mask->h_dest, &full_mac, ETH_ALEN)) | |
652 | return -EINVAL; | |
653 | ||
654 | if (!all_zeros_or_all_ones(eth_mask->h_proto)) | |
655 | return -EINVAL; | |
656 | break; | |
657 | default: | |
658 | return -EINVAL; | |
659 | } | |
660 | ||
661 | if ((cmd->fs.flow_type & FLOW_EXT)) { | |
662 | if (cmd->fs.m_ext.vlan_etype || | |
663 | !(cmd->fs.m_ext.vlan_tci == 0 || | |
664 | cmd->fs.m_ext.vlan_tci == cpu_to_be16(0xfff))) | |
665 | return -EINVAL; | |
666 | } | |
667 | ||
668 | return 0; | |
669 | } | |
670 | ||
671 | static int add_ip_rule(struct mlx4_en_priv *priv, | |
672 | struct ethtool_rxnfc *cmd, | |
673 | struct list_head *list_h) | |
674 | { | |
675 | struct mlx4_spec_list *spec_l3; | |
676 | struct ethtool_usrip4_spec *l3_mask = &cmd->fs.m_u.usr_ip4_spec; | |
677 | ||
678 | spec_l3 = kzalloc(sizeof *spec_l3, GFP_KERNEL); | |
679 | if (!spec_l3) { | |
680 | en_err(priv, "Fail to alloc ethtool rule.\n"); | |
681 | return -ENOMEM; | |
682 | } | |
683 | ||
684 | spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4; | |
685 | spec_l3->ipv4.src_ip = cmd->fs.h_u.usr_ip4_spec.ip4src; | |
686 | if (l3_mask->ip4src) | |
687 | spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK; | |
688 | spec_l3->ipv4.dst_ip = cmd->fs.h_u.usr_ip4_spec.ip4dst; | |
689 | if (l3_mask->ip4dst) | |
690 | spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK; | |
691 | list_add_tail(&spec_l3->list, list_h); | |
692 | ||
693 | return 0; | |
694 | } | |
695 | ||
696 | static int add_tcp_udp_rule(struct mlx4_en_priv *priv, | |
697 | struct ethtool_rxnfc *cmd, | |
698 | struct list_head *list_h, int proto) | |
699 | { | |
700 | struct mlx4_spec_list *spec_l3; | |
701 | struct mlx4_spec_list *spec_l4; | |
702 | struct ethtool_tcpip4_spec *l4_mask = &cmd->fs.m_u.tcp_ip4_spec; | |
703 | ||
704 | spec_l3 = kzalloc(sizeof *spec_l3, GFP_KERNEL); | |
705 | spec_l4 = kzalloc(sizeof *spec_l4, GFP_KERNEL); | |
706 | if (!spec_l4 || !spec_l3) { | |
707 | en_err(priv, "Fail to alloc ethtool rule.\n"); | |
708 | kfree(spec_l3); | |
709 | kfree(spec_l4); | |
710 | return -ENOMEM; | |
711 | } | |
712 | ||
713 | spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4; | |
714 | ||
715 | if (proto == TCP_V4_FLOW) { | |
716 | spec_l4->id = MLX4_NET_TRANS_RULE_ID_TCP; | |
717 | spec_l3->ipv4.src_ip = cmd->fs.h_u.tcp_ip4_spec.ip4src; | |
718 | spec_l3->ipv4.dst_ip = cmd->fs.h_u.tcp_ip4_spec.ip4dst; | |
719 | spec_l4->tcp_udp.src_port = cmd->fs.h_u.tcp_ip4_spec.psrc; | |
720 | spec_l4->tcp_udp.dst_port = cmd->fs.h_u.tcp_ip4_spec.pdst; | |
721 | } else { | |
722 | spec_l4->id = MLX4_NET_TRANS_RULE_ID_UDP; | |
723 | spec_l3->ipv4.src_ip = cmd->fs.h_u.udp_ip4_spec.ip4src; | |
724 | spec_l3->ipv4.dst_ip = cmd->fs.h_u.udp_ip4_spec.ip4dst; | |
725 | spec_l4->tcp_udp.src_port = cmd->fs.h_u.udp_ip4_spec.psrc; | |
726 | spec_l4->tcp_udp.dst_port = cmd->fs.h_u.udp_ip4_spec.pdst; | |
727 | } | |
728 | ||
729 | if (l4_mask->ip4src) | |
730 | spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK; | |
731 | if (l4_mask->ip4dst) | |
732 | spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK; | |
733 | ||
734 | if (l4_mask->psrc) | |
735 | spec_l4->tcp_udp.src_port_msk = EN_ETHTOOL_SHORT_MASK; | |
736 | if (l4_mask->pdst) | |
737 | spec_l4->tcp_udp.dst_port_msk = EN_ETHTOOL_SHORT_MASK; | |
738 | ||
739 | list_add_tail(&spec_l3->list, list_h); | |
740 | list_add_tail(&spec_l4->list, list_h); | |
741 | ||
742 | return 0; | |
743 | } | |
744 | ||
745 | static int mlx4_en_ethtool_to_net_trans_rule(struct net_device *dev, | |
746 | struct ethtool_rxnfc *cmd, | |
747 | struct list_head *rule_list_h) | |
748 | { | |
749 | int err; | |
750 | u64 mac; | |
751 | __be64 be_mac; | |
752 | struct ethhdr *eth_spec; | |
753 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
754 | struct mlx4_spec_list *spec_l2; | |
af22d9de | 755 | __be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16); |
82067281 HHZ |
756 | |
757 | err = mlx4_en_validate_flow(dev, cmd); | |
758 | if (err) | |
759 | return err; | |
760 | ||
761 | spec_l2 = kzalloc(sizeof *spec_l2, GFP_KERNEL); | |
762 | if (!spec_l2) | |
763 | return -ENOMEM; | |
764 | ||
af22d9de | 765 | mac = priv->mac & MLX4_MAC_MASK; |
82067281 HHZ |
766 | be_mac = cpu_to_be64(mac << 16); |
767 | ||
768 | spec_l2->id = MLX4_NET_TRANS_RULE_ID_ETH; | |
769 | memcpy(spec_l2->eth.dst_mac_msk, &mac_msk, ETH_ALEN); | |
770 | if ((cmd->fs.flow_type & ~FLOW_EXT) != ETHER_FLOW) | |
771 | memcpy(spec_l2->eth.dst_mac, &be_mac, ETH_ALEN); | |
772 | ||
773 | if ((cmd->fs.flow_type & FLOW_EXT) && cmd->fs.m_ext.vlan_tci) { | |
774 | spec_l2->eth.vlan_id = cmd->fs.h_ext.vlan_tci; | |
775 | spec_l2->eth.vlan_id_msk = cpu_to_be16(0xfff); | |
776 | } | |
777 | ||
778 | list_add_tail(&spec_l2->list, rule_list_h); | |
779 | ||
780 | switch (cmd->fs.flow_type & ~FLOW_EXT) { | |
781 | case ETHER_FLOW: | |
782 | eth_spec = &cmd->fs.h_u.ether_spec; | |
783 | memcpy(&spec_l2->eth.dst_mac, eth_spec->h_dest, ETH_ALEN); | |
784 | spec_l2->eth.ether_type = eth_spec->h_proto; | |
785 | if (eth_spec->h_proto) | |
786 | spec_l2->eth.ether_type_enable = 1; | |
787 | break; | |
788 | case IP_USER_FLOW: | |
789 | err = add_ip_rule(priv, cmd, rule_list_h); | |
790 | break; | |
791 | case TCP_V4_FLOW: | |
792 | err = add_tcp_udp_rule(priv, cmd, rule_list_h, TCP_V4_FLOW); | |
793 | break; | |
794 | case UDP_V4_FLOW: | |
795 | err = add_tcp_udp_rule(priv, cmd, rule_list_h, UDP_V4_FLOW); | |
796 | break; | |
797 | } | |
798 | ||
799 | return err; | |
800 | } | |
801 | ||
802 | static int mlx4_en_flow_replace(struct net_device *dev, | |
803 | struct ethtool_rxnfc *cmd) | |
804 | { | |
805 | int err; | |
806 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
807 | struct ethtool_flow_id *loc_rule; | |
808 | struct mlx4_spec_list *spec, *tmp_spec; | |
809 | u32 qpn; | |
810 | u64 reg_id; | |
811 | ||
812 | struct mlx4_net_trans_rule rule = { | |
813 | .queue_mode = MLX4_NET_TRANS_Q_FIFO, | |
814 | .exclusive = 0, | |
815 | .allow_loopback = 1, | |
816 | .promisc_mode = MLX4_FS_PROMISC_NONE, | |
817 | }; | |
818 | ||
819 | rule.port = priv->port; | |
820 | rule.priority = MLX4_DOMAIN_ETHTOOL | cmd->fs.location; | |
821 | INIT_LIST_HEAD(&rule.list); | |
822 | ||
823 | /* Allow direct QP attaches if the EN_ETHTOOL_QP_ATTACH flag is set */ | |
824 | if (cmd->fs.ring_cookie == RX_CLS_FLOW_DISC) | |
cabdc8ee | 825 | qpn = priv->drop_qp.qpn; |
82067281 HHZ |
826 | else if (cmd->fs.ring_cookie & EN_ETHTOOL_QP_ATTACH) { |
827 | qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1); | |
828 | } else { | |
829 | if (cmd->fs.ring_cookie >= priv->rx_ring_num) { | |
830 | en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist.\n", | |
831 | cmd->fs.ring_cookie); | |
832 | return -EINVAL; | |
833 | } | |
834 | qpn = priv->rss_map.qps[cmd->fs.ring_cookie].qpn; | |
835 | if (!qpn) { | |
836 | en_warn(priv, "rxnfc: RX ring (%llu) is inactive.\n", | |
837 | cmd->fs.ring_cookie); | |
838 | return -EINVAL; | |
839 | } | |
840 | } | |
841 | rule.qpn = qpn; | |
842 | err = mlx4_en_ethtool_to_net_trans_rule(dev, cmd, &rule.list); | |
843 | if (err) | |
844 | goto out_free_list; | |
845 | ||
846 | loc_rule = &priv->ethtool_rules[cmd->fs.location]; | |
847 | if (loc_rule->id) { | |
848 | err = mlx4_flow_detach(priv->mdev->dev, loc_rule->id); | |
849 | if (err) { | |
850 | en_err(priv, "Fail to detach network rule at location %d. registration id = %llx\n", | |
851 | cmd->fs.location, loc_rule->id); | |
852 | goto out_free_list; | |
853 | } | |
854 | loc_rule->id = 0; | |
855 | memset(&loc_rule->flow_spec, 0, | |
856 | sizeof(struct ethtool_rx_flow_spec)); | |
857 | } | |
858 | err = mlx4_flow_attach(priv->mdev->dev, &rule, ®_id); | |
859 | if (err) { | |
860 | en_err(priv, "Fail to attach network rule at location %d.\n", | |
861 | cmd->fs.location); | |
862 | goto out_free_list; | |
863 | } | |
864 | loc_rule->id = reg_id; | |
865 | memcpy(&loc_rule->flow_spec, &cmd->fs, | |
866 | sizeof(struct ethtool_rx_flow_spec)); | |
867 | ||
868 | out_free_list: | |
869 | list_for_each_entry_safe(spec, tmp_spec, &rule.list, list) { | |
870 | list_del(&spec->list); | |
871 | kfree(spec); | |
872 | } | |
873 | return err; | |
874 | } | |
875 | ||
876 | static int mlx4_en_flow_detach(struct net_device *dev, | |
877 | struct ethtool_rxnfc *cmd) | |
878 | { | |
879 | int err = 0; | |
880 | struct ethtool_flow_id *rule; | |
881 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
882 | ||
883 | if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) | |
884 | return -EINVAL; | |
885 | ||
886 | rule = &priv->ethtool_rules[cmd->fs.location]; | |
887 | if (!rule->id) { | |
888 | err = -ENOENT; | |
889 | goto out; | |
890 | } | |
891 | ||
892 | err = mlx4_flow_detach(priv->mdev->dev, rule->id); | |
893 | if (err) { | |
894 | en_err(priv, "Fail to detach network rule at location %d. registration id = 0x%llx\n", | |
895 | cmd->fs.location, rule->id); | |
896 | goto out; | |
897 | } | |
898 | rule->id = 0; | |
899 | memset(&rule->flow_spec, 0, sizeof(struct ethtool_rx_flow_spec)); | |
900 | out: | |
901 | return err; | |
902 | ||
903 | } | |
904 | ||
905 | static int mlx4_en_get_flow(struct net_device *dev, struct ethtool_rxnfc *cmd, | |
906 | int loc) | |
907 | { | |
908 | int err = 0; | |
909 | struct ethtool_flow_id *rule; | |
910 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
911 | ||
912 | if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES) | |
913 | return -EINVAL; | |
914 | ||
915 | rule = &priv->ethtool_rules[loc]; | |
916 | if (rule->id) | |
917 | memcpy(&cmd->fs, &rule->flow_spec, | |
918 | sizeof(struct ethtool_rx_flow_spec)); | |
919 | else | |
920 | err = -ENOENT; | |
921 | ||
922 | return err; | |
923 | } | |
924 | ||
925 | static int mlx4_en_get_num_flows(struct mlx4_en_priv *priv) | |
926 | { | |
927 | ||
928 | int i, res = 0; | |
929 | for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) { | |
930 | if (priv->ethtool_rules[i].id) | |
931 | res++; | |
932 | } | |
933 | return res; | |
934 | ||
935 | } | |
936 | ||
93d3e367 YP |
937 | static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, |
938 | u32 *rule_locs) | |
939 | { | |
940 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
82067281 | 941 | struct mlx4_en_dev *mdev = priv->mdev; |
93d3e367 | 942 | int err = 0; |
82067281 HHZ |
943 | int i = 0, priority = 0; |
944 | ||
945 | if ((cmd->cmd == ETHTOOL_GRXCLSRLCNT || | |
946 | cmd->cmd == ETHTOOL_GRXCLSRULE || | |
947 | cmd->cmd == ETHTOOL_GRXCLSRLALL) && | |
948 | mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) | |
949 | return -EINVAL; | |
93d3e367 YP |
950 | |
951 | switch (cmd->cmd) { | |
952 | case ETHTOOL_GRXRINGS: | |
953 | cmd->data = priv->rx_ring_num; | |
954 | break; | |
82067281 HHZ |
955 | case ETHTOOL_GRXCLSRLCNT: |
956 | cmd->rule_cnt = mlx4_en_get_num_flows(priv); | |
957 | break; | |
958 | case ETHTOOL_GRXCLSRULE: | |
959 | err = mlx4_en_get_flow(dev, cmd, cmd->fs.location); | |
960 | break; | |
961 | case ETHTOOL_GRXCLSRLALL: | |
962 | while ((!err || err == -ENOENT) && priority < cmd->rule_cnt) { | |
963 | err = mlx4_en_get_flow(dev, cmd, i); | |
964 | if (!err) | |
965 | rule_locs[priority++] = i; | |
966 | i++; | |
967 | } | |
968 | err = 0; | |
969 | break; | |
93d3e367 YP |
970 | default: |
971 | err = -EOPNOTSUPP; | |
972 | break; | |
973 | } | |
974 | ||
975 | return err; | |
976 | } | |
977 | ||
82067281 HHZ |
978 | static int mlx4_en_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) |
979 | { | |
980 | int err = 0; | |
981 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
982 | struct mlx4_en_dev *mdev = priv->mdev; | |
983 | ||
984 | if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) | |
985 | return -EINVAL; | |
986 | ||
987 | switch (cmd->cmd) { | |
988 | case ETHTOOL_SRXCLSRLINS: | |
989 | err = mlx4_en_flow_replace(dev, cmd); | |
990 | break; | |
991 | case ETHTOOL_SRXCLSRLDEL: | |
992 | err = mlx4_en_flow_detach(dev, cmd); | |
993 | break; | |
994 | default: | |
995 | en_warn(priv, "Unsupported ethtool command. (%d)\n", cmd->cmd); | |
996 | return -EINVAL; | |
997 | } | |
998 | ||
999 | return err; | |
1000 | } | |
1001 | ||
d317966b AV |
1002 | static void mlx4_en_get_channels(struct net_device *dev, |
1003 | struct ethtool_channels *channel) | |
1004 | { | |
1005 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
1006 | ||
1007 | memset(channel, 0, sizeof(*channel)); | |
1008 | ||
1009 | channel->max_rx = MAX_RX_RINGS; | |
1010 | channel->max_tx = MLX4_EN_MAX_TX_RING_P_UP; | |
1011 | ||
1012 | channel->rx_count = priv->rx_ring_num; | |
1013 | channel->tx_count = priv->tx_ring_num / MLX4_EN_NUM_UP; | |
1014 | } | |
1015 | ||
1016 | static int mlx4_en_set_channels(struct net_device *dev, | |
1017 | struct ethtool_channels *channel) | |
1018 | { | |
1019 | struct mlx4_en_priv *priv = netdev_priv(dev); | |
1020 | struct mlx4_en_dev *mdev = priv->mdev; | |
1021 | int port_up; | |
1022 | int err = 0; | |
1023 | ||
1024 | if (channel->other_count || channel->combined_count || | |
1025 | channel->tx_count > MLX4_EN_MAX_TX_RING_P_UP || | |
1026 | channel->rx_count > MAX_RX_RINGS || | |
1027 | !channel->tx_count || !channel->rx_count) | |
1028 | return -EINVAL; | |
1029 | ||
1030 | mutex_lock(&mdev->state_lock); | |
1031 | if (priv->port_up) { | |
1032 | port_up = 1; | |
1033 | mlx4_en_stop_port(dev); | |
1034 | } | |
1035 | ||
1036 | mlx4_en_free_resources(priv); | |
1037 | ||
1038 | priv->num_tx_rings_p_up = channel->tx_count; | |
1039 | priv->tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP; | |
1040 | priv->rx_ring_num = channel->rx_count; | |
1041 | ||
1042 | err = mlx4_en_alloc_resources(priv); | |
1043 | if (err) { | |
1044 | en_err(priv, "Failed reallocating port resources\n"); | |
1045 | goto out; | |
1046 | } | |
1047 | ||
1048 | netif_set_real_num_tx_queues(dev, priv->tx_ring_num); | |
1049 | netif_set_real_num_rx_queues(dev, priv->rx_ring_num); | |
1050 | ||
1051 | mlx4_en_setup_tc(dev, MLX4_EN_NUM_UP); | |
1052 | ||
1053 | en_warn(priv, "Using %d TX rings\n", priv->tx_ring_num); | |
1054 | en_warn(priv, "Using %d RX rings\n", priv->rx_ring_num); | |
1055 | ||
1056 | if (port_up) { | |
1057 | err = mlx4_en_start_port(dev); | |
1058 | if (err) | |
1059 | en_err(priv, "Failed starting port\n"); | |
1060 | } | |
1061 | ||
1062 | err = mlx4_en_moderation_update(priv); | |
1063 | ||
1064 | out: | |
1065 | mutex_unlock(&mdev->state_lock); | |
1066 | return err; | |
1067 | } | |
1068 | ||
c27a02cd YP |
1069 | const struct ethtool_ops mlx4_en_ethtool_ops = { |
1070 | .get_drvinfo = mlx4_en_get_drvinfo, | |
1071 | .get_settings = mlx4_en_get_settings, | |
1072 | .set_settings = mlx4_en_set_settings, | |
c27a02cd | 1073 | .get_link = ethtool_op_get_link, |
c27a02cd YP |
1074 | .get_strings = mlx4_en_get_strings, |
1075 | .get_sset_count = mlx4_en_get_sset_count, | |
1076 | .get_ethtool_stats = mlx4_en_get_ethtool_stats, | |
e7c1c2c4 | 1077 | .self_test = mlx4_en_self_test, |
c27a02cd | 1078 | .get_wol = mlx4_en_get_wol, |
14c07b13 | 1079 | .set_wol = mlx4_en_set_wol, |
c27a02cd YP |
1080 | .get_msglevel = mlx4_en_get_msglevel, |
1081 | .set_msglevel = mlx4_en_set_msglevel, | |
1082 | .get_coalesce = mlx4_en_get_coalesce, | |
1083 | .set_coalesce = mlx4_en_set_coalesce, | |
1084 | .get_pauseparam = mlx4_en_get_pauseparam, | |
1085 | .set_pauseparam = mlx4_en_set_pauseparam, | |
1086 | .get_ringparam = mlx4_en_get_ringparam, | |
18cc42a3 | 1087 | .set_ringparam = mlx4_en_set_ringparam, |
93d3e367 | 1088 | .get_rxnfc = mlx4_en_get_rxnfc, |
82067281 | 1089 | .set_rxnfc = mlx4_en_set_rxnfc, |
93d3e367 YP |
1090 | .get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size, |
1091 | .get_rxfh_indir = mlx4_en_get_rxfh_indir, | |
1092 | .set_rxfh_indir = mlx4_en_set_rxfh_indir, | |
d317966b AV |
1093 | .get_channels = mlx4_en_get_channels, |
1094 | .set_channels = mlx4_en_set_channels, | |
c27a02cd YP |
1095 | }; |
1096 | ||
1097 | ||
1098 | ||
1099 | ||
1100 |