Commit | Line | Data |
---|---|---|
fbb7ddfe GR |
1 | /******************************************************************************* |
2 | * | |
3 | * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver | |
77d77f9f | 4 | * Copyright(c) 2013 - 2014 Intel Corporation. |
fbb7ddfe GR |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * The full GNU General Public License is included in this distribution in | |
16 | * the file called "COPYING". | |
17 | * | |
18 | * Contact Information: | |
19 | * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> | |
20 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
21 | * | |
22 | ******************************************************************************/ | |
23 | ||
24 | /* ethtool support for i40evf */ | |
25 | #include "i40evf.h" | |
26 | ||
27 | #include <linux/uaccess.h> | |
28 | ||
29 | ||
30 | struct i40evf_stats { | |
31 | char stat_string[ETH_GSTRING_LEN]; | |
32 | int stat_offset; | |
33 | }; | |
34 | ||
35 | #define I40EVF_STAT(_name, _stat) { \ | |
36 | .stat_string = _name, \ | |
37 | .stat_offset = offsetof(struct i40evf_adapter, _stat) \ | |
38 | } | |
39 | ||
40 | /* All stats are u64, so we don't need to track the size of the field. */ | |
41 | static const struct i40evf_stats i40evf_gstrings_stats[] = { | |
42 | I40EVF_STAT("rx_bytes", current_stats.rx_bytes), | |
43 | I40EVF_STAT("rx_unicast", current_stats.rx_unicast), | |
44 | I40EVF_STAT("rx_multicast", current_stats.rx_multicast), | |
45 | I40EVF_STAT("rx_broadcast", current_stats.rx_broadcast), | |
46 | I40EVF_STAT("rx_discards", current_stats.rx_discards), | |
47 | I40EVF_STAT("rx_errors", current_stats.rx_errors), | |
48 | I40EVF_STAT("rx_missed", current_stats.rx_missed), | |
49 | I40EVF_STAT("rx_unknown_protocol", current_stats.rx_unknown_protocol), | |
50 | I40EVF_STAT("tx_bytes", current_stats.tx_bytes), | |
51 | I40EVF_STAT("tx_unicast", current_stats.tx_unicast), | |
52 | I40EVF_STAT("tx_multicast", current_stats.tx_multicast), | |
53 | I40EVF_STAT("tx_broadcast", current_stats.tx_broadcast), | |
54 | I40EVF_STAT("tx_discards", current_stats.tx_discards), | |
55 | I40EVF_STAT("tx_errors", current_stats.tx_errors), | |
56 | }; | |
57 | ||
58 | #define I40EVF_GLOBAL_STATS_LEN ARRAY_SIZE(i40evf_gstrings_stats) | |
59 | #define I40EVF_QUEUE_STATS_LEN \ | |
60 | (((struct i40evf_adapter *) \ | |
61 | netdev_priv(netdev))->vsi_res->num_queue_pairs * 4) | |
62 | #define I40EVF_STATS_LEN (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN) | |
63 | ||
64 | /** | |
65 | * i40evf_get_settings - Get Link Speed and Duplex settings | |
66 | * @netdev: network interface device structure | |
67 | * @ecmd: ethtool command | |
68 | * | |
69 | * Reports speed/duplex settings. Because this is a VF, we don't know what | |
70 | * kind of link we really have, so we fake it. | |
71 | **/ | |
72 | static int i40evf_get_settings(struct net_device *netdev, | |
73 | struct ethtool_cmd *ecmd) | |
74 | { | |
75 | /* In the future the VF will be able to query the PF for | |
76 | * some information - for now use a dummy value | |
77 | */ | |
78 | ecmd->supported = SUPPORTED_10000baseT_Full; | |
79 | ecmd->autoneg = AUTONEG_DISABLE; | |
80 | ecmd->transceiver = XCVR_DUMMY1; | |
81 | ecmd->port = PORT_NONE; | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | /** | |
87 | * i40evf_get_sset_count - Get length of string set | |
88 | * @netdev: network interface device structure | |
89 | * @sset: id of string set | |
90 | * | |
91 | * Reports size of string table. This driver only supports | |
92 | * strings for statistics. | |
93 | **/ | |
94 | static int i40evf_get_sset_count(struct net_device *netdev, int sset) | |
95 | { | |
96 | if (sset == ETH_SS_STATS) | |
97 | return I40EVF_STATS_LEN; | |
98 | else | |
99 | return -ENOTSUPP; | |
100 | } | |
101 | ||
102 | /** | |
103 | * i40evf_get_ethtool_stats - report device statistics | |
104 | * @netdev: network interface device structure | |
105 | * @stats: ethtool statistics structure | |
106 | * @data: pointer to data buffer | |
107 | * | |
108 | * All statistics are added to the data buffer as an array of u64. | |
109 | **/ | |
110 | static void i40evf_get_ethtool_stats(struct net_device *netdev, | |
111 | struct ethtool_stats *stats, u64 *data) | |
112 | { | |
113 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
114 | int i, j; | |
115 | char *p; | |
116 | ||
117 | for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) { | |
118 | p = (char *)adapter + i40evf_gstrings_stats[i].stat_offset; | |
119 | data[i] = *(u64 *)p; | |
120 | } | |
121 | for (j = 0; j < adapter->vsi_res->num_queue_pairs; j++) { | |
122 | data[i++] = adapter->tx_rings[j]->stats.packets; | |
123 | data[i++] = adapter->tx_rings[j]->stats.bytes; | |
124 | } | |
125 | for (j = 0; j < adapter->vsi_res->num_queue_pairs; j++) { | |
126 | data[i++] = adapter->rx_rings[j]->stats.packets; | |
127 | data[i++] = adapter->rx_rings[j]->stats.bytes; | |
128 | } | |
129 | } | |
130 | ||
131 | /** | |
132 | * i40evf_get_strings - Get string set | |
133 | * @netdev: network interface device structure | |
134 | * @sset: id of string set | |
135 | * @data: buffer for string data | |
136 | * | |
137 | * Builds stats string table. | |
138 | **/ | |
139 | static void i40evf_get_strings(struct net_device *netdev, u32 sset, u8 *data) | |
140 | { | |
141 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
142 | u8 *p = data; | |
143 | int i; | |
144 | ||
145 | if (sset == ETH_SS_STATS) { | |
146 | for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) { | |
147 | memcpy(p, i40evf_gstrings_stats[i].stat_string, | |
148 | ETH_GSTRING_LEN); | |
149 | p += ETH_GSTRING_LEN; | |
150 | } | |
151 | for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) { | |
152 | snprintf(p, ETH_GSTRING_LEN, "tx-%u.packets", i); | |
153 | p += ETH_GSTRING_LEN; | |
154 | snprintf(p, ETH_GSTRING_LEN, "tx-%u.bytes", i); | |
155 | p += ETH_GSTRING_LEN; | |
156 | } | |
157 | for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) { | |
158 | snprintf(p, ETH_GSTRING_LEN, "rx-%u.packets", i); | |
159 | p += ETH_GSTRING_LEN; | |
160 | snprintf(p, ETH_GSTRING_LEN, "rx-%u.bytes", i); | |
161 | p += ETH_GSTRING_LEN; | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | /** | |
167 | * i40evf_get_msglevel - Get debug message level | |
168 | * @netdev: network interface device structure | |
169 | * | |
170 | * Returns current debug message level. | |
171 | **/ | |
172 | static u32 i40evf_get_msglevel(struct net_device *netdev) | |
173 | { | |
174 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
175 | return adapter->msg_enable; | |
176 | } | |
177 | ||
178 | /** | |
179 | * i40evf_get_msglevel - Set debug message level | |
180 | * @netdev: network interface device structure | |
181 | * @data: message level | |
182 | * | |
183 | * Set current debug message level. Higher values cause the driver to | |
184 | * be noisier. | |
185 | **/ | |
186 | static void i40evf_set_msglevel(struct net_device *netdev, u32 data) | |
187 | { | |
188 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
189 | adapter->msg_enable = data; | |
190 | } | |
191 | ||
192 | /** | |
193 | * i40evf_get_drvinto - Get driver info | |
194 | * @netdev: network interface device structure | |
195 | * @drvinfo: ethool driver info structure | |
196 | * | |
197 | * Returns information about the driver and device for display to the user. | |
198 | **/ | |
199 | static void i40evf_get_drvinfo(struct net_device *netdev, | |
200 | struct ethtool_drvinfo *drvinfo) | |
201 | { | |
202 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
203 | ||
204 | strlcpy(drvinfo->driver, i40evf_driver_name, 32); | |
205 | strlcpy(drvinfo->version, i40evf_driver_version, 32); | |
206 | ||
207 | strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); | |
208 | } | |
209 | ||
210 | /** | |
211 | * i40evf_get_ringparam - Get ring parameters | |
212 | * @netdev: network interface device structure | |
213 | * @ring: ethtool ringparam structure | |
214 | * | |
215 | * Returns current ring parameters. TX and RX rings are reported separately, | |
216 | * but the number of rings is not reported. | |
217 | **/ | |
218 | static void i40evf_get_ringparam(struct net_device *netdev, | |
219 | struct ethtool_ringparam *ring) | |
220 | { | |
221 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
222 | struct i40e_ring *tx_ring = adapter->tx_rings[0]; | |
223 | struct i40e_ring *rx_ring = adapter->rx_rings[0]; | |
224 | ||
225 | ring->rx_max_pending = I40EVF_MAX_RXD; | |
226 | ring->tx_max_pending = I40EVF_MAX_TXD; | |
227 | ring->rx_pending = rx_ring->count; | |
228 | ring->tx_pending = tx_ring->count; | |
229 | } | |
230 | ||
231 | /** | |
232 | * i40evf_set_ringparam - Set ring parameters | |
233 | * @netdev: network interface device structure | |
234 | * @ring: ethtool ringparam structure | |
235 | * | |
236 | * Sets ring parameters. TX and RX rings are controlled separately, but the | |
237 | * number of rings is not specified, so all rings get the same settings. | |
238 | **/ | |
239 | static int i40evf_set_ringparam(struct net_device *netdev, | |
240 | struct ethtool_ringparam *ring) | |
241 | { | |
242 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
243 | u32 new_rx_count, new_tx_count; | |
77d77f9f | 244 | int i; |
fbb7ddfe GR |
245 | |
246 | if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) | |
247 | return -EINVAL; | |
248 | ||
249 | new_tx_count = clamp_t(u32, ring->tx_pending, | |
250 | I40EVF_MIN_TXD, | |
251 | I40EVF_MAX_TXD); | |
252 | new_tx_count = ALIGN(new_tx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE); | |
253 | ||
254 | new_rx_count = clamp_t(u32, ring->rx_pending, | |
255 | I40EVF_MIN_RXD, | |
256 | I40EVF_MAX_RXD); | |
257 | new_rx_count = ALIGN(new_rx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE); | |
258 | ||
259 | /* if nothing to do return success */ | |
77d77f9f MW |
260 | if ((new_tx_count == adapter->tx_rings[0]->count) && |
261 | (new_rx_count == adapter->rx_rings[0]->count)) | |
fbb7ddfe GR |
262 | return 0; |
263 | ||
77d77f9f MW |
264 | for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) { |
265 | adapter->tx_rings[0]->count = new_tx_count; | |
266 | adapter->rx_rings[0]->count = new_rx_count; | |
267 | } | |
fbb7ddfe GR |
268 | |
269 | if (netif_running(netdev)) | |
270 | i40evf_reinit_locked(adapter); | |
271 | return 0; | |
272 | } | |
273 | ||
274 | /** | |
275 | * i40evf_get_coalesce - Get interrupt coalescing settings | |
276 | * @netdev: network interface device structure | |
277 | * @ec: ethtool coalesce structure | |
278 | * | |
279 | * Returns current coalescing settings. This is referred to elsewhere in the | |
280 | * driver as Interrupt Throttle Rate, as this is how the hardware describes | |
281 | * this functionality. | |
282 | **/ | |
283 | static int i40evf_get_coalesce(struct net_device *netdev, | |
284 | struct ethtool_coalesce *ec) | |
285 | { | |
286 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
287 | struct i40e_vsi *vsi = &adapter->vsi; | |
288 | ||
289 | ec->tx_max_coalesced_frames = vsi->work_limit; | |
290 | ec->rx_max_coalesced_frames = vsi->work_limit; | |
291 | ||
292 | if (ITR_IS_DYNAMIC(vsi->rx_itr_setting)) | |
293 | ec->rx_coalesce_usecs = 1; | |
294 | else | |
295 | ec->rx_coalesce_usecs = vsi->rx_itr_setting; | |
296 | ||
297 | if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) | |
298 | ec->tx_coalesce_usecs = 1; | |
299 | else | |
300 | ec->tx_coalesce_usecs = vsi->tx_itr_setting; | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | /** | |
306 | * i40evf_set_coalesce - Set interrupt coalescing settings | |
307 | * @netdev: network interface device structure | |
308 | * @ec: ethtool coalesce structure | |
309 | * | |
310 | * Change current coalescing settings. | |
311 | **/ | |
312 | static int i40evf_set_coalesce(struct net_device *netdev, | |
313 | struct ethtool_coalesce *ec) | |
314 | { | |
315 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
316 | struct i40e_hw *hw = &adapter->hw; | |
317 | struct i40e_vsi *vsi = &adapter->vsi; | |
318 | struct i40e_q_vector *q_vector; | |
319 | int i; | |
320 | ||
321 | if (ec->tx_max_coalesced_frames || ec->rx_max_coalesced_frames) | |
322 | vsi->work_limit = ec->tx_max_coalesced_frames; | |
323 | ||
324 | switch (ec->rx_coalesce_usecs) { | |
325 | case 0: | |
326 | vsi->rx_itr_setting = 0; | |
327 | break; | |
328 | case 1: | |
329 | vsi->rx_itr_setting = (I40E_ITR_DYNAMIC | |
330 | | ITR_REG_TO_USEC(I40E_ITR_RX_DEF)); | |
331 | break; | |
332 | default: | |
333 | if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) || | |
334 | (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1))) | |
335 | return -EINVAL; | |
336 | vsi->rx_itr_setting = ec->rx_coalesce_usecs; | |
337 | break; | |
338 | } | |
339 | ||
340 | switch (ec->tx_coalesce_usecs) { | |
341 | case 0: | |
342 | vsi->tx_itr_setting = 0; | |
343 | break; | |
344 | case 1: | |
345 | vsi->tx_itr_setting = (I40E_ITR_DYNAMIC | |
346 | | ITR_REG_TO_USEC(I40E_ITR_TX_DEF)); | |
347 | break; | |
348 | default: | |
349 | if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) || | |
350 | (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1))) | |
351 | return -EINVAL; | |
352 | vsi->tx_itr_setting = ec->tx_coalesce_usecs; | |
353 | break; | |
354 | } | |
355 | ||
356 | for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) { | |
357 | q_vector = adapter->q_vector[i]; | |
358 | q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); | |
359 | wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr); | |
360 | q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting); | |
361 | wr32(hw, I40E_VFINT_ITRN1(1, i), q_vector->tx.itr); | |
362 | i40e_flush(hw); | |
363 | } | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
4e9dc31f MW |
368 | /** |
369 | * i40e_get_rss_hash_opts - Get RSS hash Input Set for each flow type | |
370 | * @adapter: board private structure | |
371 | * @cmd: ethtool rxnfc command | |
372 | * | |
373 | * Returns Success if the flow is supported, else Invalid Input. | |
374 | **/ | |
375 | static int i40evf_get_rss_hash_opts(struct i40evf_adapter *adapter, | |
376 | struct ethtool_rxnfc *cmd) | |
377 | { | |
378 | struct i40e_hw *hw = &adapter->hw; | |
379 | u64 hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | |
380 | ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | |
381 | ||
382 | /* We always hash on IP src and dest addresses */ | |
383 | cmd->data = RXH_IP_SRC | RXH_IP_DST; | |
384 | ||
385 | switch (cmd->flow_type) { | |
386 | case TCP_V4_FLOW: | |
387 | if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP)) | |
388 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
389 | break; | |
390 | case UDP_V4_FLOW: | |
391 | if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP)) | |
392 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
393 | break; | |
394 | ||
395 | case SCTP_V4_FLOW: | |
396 | case AH_ESP_V4_FLOW: | |
397 | case AH_V4_FLOW: | |
398 | case ESP_V4_FLOW: | |
399 | case IPV4_FLOW: | |
400 | break; | |
401 | ||
402 | case TCP_V6_FLOW: | |
403 | if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP)) | |
404 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
405 | break; | |
406 | case UDP_V6_FLOW: | |
407 | if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP)) | |
408 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
409 | break; | |
410 | ||
411 | case SCTP_V6_FLOW: | |
412 | case AH_ESP_V6_FLOW: | |
413 | case AH_V6_FLOW: | |
414 | case ESP_V6_FLOW: | |
415 | case IPV6_FLOW: | |
416 | break; | |
417 | default: | |
418 | cmd->data = 0; | |
419 | return -EINVAL; | |
420 | } | |
421 | ||
422 | return 0; | |
423 | } | |
424 | ||
425 | /** | |
426 | * i40evf_get_rxnfc - command to get RX flow classification rules | |
427 | * @netdev: network interface device structure | |
428 | * @cmd: ethtool rxnfc command | |
429 | * | |
430 | * Returns Success if the command is supported. | |
431 | **/ | |
432 | static int i40evf_get_rxnfc(struct net_device *netdev, | |
433 | struct ethtool_rxnfc *cmd, | |
434 | u32 *rule_locs) | |
435 | { | |
436 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
437 | int ret = -EOPNOTSUPP; | |
438 | ||
439 | switch (cmd->cmd) { | |
440 | case ETHTOOL_GRXRINGS: | |
441 | cmd->data = adapter->vsi_res->num_queue_pairs; | |
442 | ret = 0; | |
443 | break; | |
444 | case ETHTOOL_GRXFH: | |
445 | ret = i40evf_get_rss_hash_opts(adapter, cmd); | |
446 | break; | |
447 | default: | |
448 | break; | |
449 | } | |
450 | ||
451 | return ret; | |
452 | } | |
453 | ||
454 | /** | |
455 | * i40evf_set_rss_hash_opt - Enable/Disable flow types for RSS hash | |
456 | * @adapter: board private structure | |
457 | * @cmd: ethtool rxnfc command | |
458 | * | |
459 | * Returns Success if the flow input set is supported. | |
460 | **/ | |
461 | static int i40evf_set_rss_hash_opt(struct i40evf_adapter *adapter, | |
462 | struct ethtool_rxnfc *nfc) | |
463 | { | |
464 | struct i40e_hw *hw = &adapter->hw; | |
465 | ||
466 | u64 hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | |
467 | ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | |
468 | ||
469 | /* RSS does not support anything other than hashing | |
470 | * to queues on src and dst IPs and ports | |
471 | */ | |
472 | if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | | |
473 | RXH_L4_B_0_1 | RXH_L4_B_2_3)) | |
474 | return -EINVAL; | |
475 | ||
476 | /* We need at least the IP SRC and DEST fields for hashing */ | |
477 | if (!(nfc->data & RXH_IP_SRC) || | |
478 | !(nfc->data & RXH_IP_DST)) | |
479 | return -EINVAL; | |
480 | ||
481 | switch (nfc->flow_type) { | |
482 | case TCP_V4_FLOW: | |
483 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
484 | case 0: | |
485 | hena &= ~((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP); | |
486 | break; | |
487 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
488 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP); | |
489 | break; | |
490 | default: | |
491 | return -EINVAL; | |
492 | } | |
493 | break; | |
494 | case TCP_V6_FLOW: | |
495 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
496 | case 0: | |
497 | hena &= ~((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP); | |
498 | break; | |
499 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
500 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP); | |
501 | break; | |
502 | default: | |
503 | return -EINVAL; | |
504 | } | |
505 | break; | |
506 | case UDP_V4_FLOW: | |
507 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
508 | case 0: | |
509 | hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | | |
510 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4)); | |
511 | break; | |
512 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
513 | hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | | |
514 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4)); | |
515 | break; | |
516 | default: | |
517 | return -EINVAL; | |
518 | } | |
519 | break; | |
520 | case UDP_V6_FLOW: | |
521 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
522 | case 0: | |
523 | hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | | |
524 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6)); | |
525 | break; | |
526 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
527 | hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | | |
528 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6)); | |
529 | break; | |
530 | default: | |
531 | return -EINVAL; | |
532 | } | |
533 | break; | |
534 | case AH_ESP_V4_FLOW: | |
535 | case AH_V4_FLOW: | |
536 | case ESP_V4_FLOW: | |
537 | case SCTP_V4_FLOW: | |
538 | if ((nfc->data & RXH_L4_B_0_1) || | |
539 | (nfc->data & RXH_L4_B_2_3)) | |
540 | return -EINVAL; | |
541 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER); | |
542 | break; | |
543 | case AH_ESP_V6_FLOW: | |
544 | case AH_V6_FLOW: | |
545 | case ESP_V6_FLOW: | |
546 | case SCTP_V6_FLOW: | |
547 | if ((nfc->data & RXH_L4_B_0_1) || | |
548 | (nfc->data & RXH_L4_B_2_3)) | |
549 | return -EINVAL; | |
550 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER); | |
551 | break; | |
552 | case IPV4_FLOW: | |
553 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | | |
554 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4); | |
555 | break; | |
556 | case IPV6_FLOW: | |
557 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER) | | |
558 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6); | |
559 | break; | |
560 | default: | |
561 | return -EINVAL; | |
562 | } | |
563 | ||
564 | wr32(hw, I40E_VFQF_HENA(0), (u32)hena); | |
565 | wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); | |
566 | i40e_flush(hw); | |
567 | ||
568 | return 0; | |
569 | } | |
570 | ||
571 | /** | |
572 | * i40evf_set_rxnfc - command to set RX flow classification rules | |
573 | * @netdev: network interface device structure | |
574 | * @cmd: ethtool rxnfc command | |
575 | * | |
576 | * Returns Success if the command is supported. | |
577 | **/ | |
578 | static int i40evf_set_rxnfc(struct net_device *netdev, | |
579 | struct ethtool_rxnfc *cmd) | |
580 | { | |
581 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
582 | int ret = -EOPNOTSUPP; | |
583 | ||
584 | switch (cmd->cmd) { | |
585 | case ETHTOOL_SRXFH: | |
586 | ret = i40evf_set_rss_hash_opt(adapter, cmd); | |
587 | break; | |
588 | default: | |
589 | break; | |
590 | } | |
591 | ||
592 | return ret; | |
593 | } | |
594 | ||
595 | /** | |
596 | * i40evf_get_channels: get the number of channels supported by the device | |
597 | * @netdev: network interface device structure | |
598 | * @ch: channel information structure | |
599 | * | |
600 | * For the purposes of our device, we only use combined channels, i.e. a tx/rx | |
601 | * queue pair. Report one extra channel to match our "other" MSI-X vector. | |
602 | **/ | |
603 | static void i40evf_get_channels(struct net_device *netdev, | |
604 | struct ethtool_channels *ch) | |
605 | { | |
606 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
607 | ||
608 | /* Report maximum channels */ | |
609 | ch->max_combined = adapter->vsi_res->num_queue_pairs; | |
610 | ||
611 | ch->max_other = NONQ_VECS; | |
612 | ch->other_count = NONQ_VECS; | |
613 | ||
614 | ch->combined_count = adapter->vsi_res->num_queue_pairs; | |
615 | } | |
616 | ||
617 | /** | |
618 | * i40evf_get_rxfh_indir_size - get the rx flow hash indirection table size | |
619 | * @netdev: network interface device structure | |
620 | * | |
621 | * Returns the table size. | |
622 | **/ | |
623 | static u32 i40evf_get_rxfh_indir_size(struct net_device *netdev) | |
624 | { | |
625 | return (I40E_VFQF_HLUT_MAX_INDEX + 1) * 4; | |
626 | } | |
627 | ||
628 | /** | |
629 | * i40evf_get_rxfh_indir - get the rx flow hash indirection table | |
630 | * @netdev: network interface device structure | |
631 | * @indir: indirection table | |
632 | * | |
633 | * Reads the indirection table directly from the hardware. Always returns 0. | |
634 | **/ | |
635 | static int i40evf_get_rxfh_indir(struct net_device *netdev, u32 *indir) | |
636 | { | |
637 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
638 | struct i40e_hw *hw = &adapter->hw; | |
639 | u32 hlut_val; | |
640 | int i, j; | |
641 | ||
642 | for (i = 0, j = 0; i < I40E_VFQF_HLUT_MAX_INDEX; i++) { | |
643 | hlut_val = rd32(hw, I40E_VFQF_HLUT(i)); | |
644 | indir[j++] = hlut_val & 0xff; | |
645 | indir[j++] = (hlut_val >> 8) & 0xff; | |
646 | indir[j++] = (hlut_val >> 16) & 0xff; | |
647 | indir[j++] = (hlut_val >> 24) & 0xff; | |
648 | } | |
649 | return 0; | |
650 | } | |
651 | ||
652 | /** | |
653 | * i40evf_set_rxfh_indir - set the rx flow hash indirection table | |
654 | * @netdev: network interface device structure | |
655 | * @indir: indirection table | |
656 | * | |
657 | * Returns -EINVAL if the table specifies an inavlid queue id, otherwise | |
658 | * returns 0 after programming the table. | |
659 | **/ | |
660 | static int i40evf_set_rxfh_indir(struct net_device *netdev, const u32 *indir) | |
661 | { | |
662 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
663 | struct i40e_hw *hw = &adapter->hw; | |
664 | u32 hlut_val; | |
665 | int i, j; | |
666 | ||
667 | for (i = 0, j = 0; i < I40E_VFQF_HLUT_MAX_INDEX + 1; i++) { | |
668 | hlut_val = indir[j++]; | |
669 | hlut_val |= indir[j++] << 8; | |
670 | hlut_val |= indir[j++] << 16; | |
671 | hlut_val |= indir[j++] << 24; | |
672 | wr32(hw, I40E_VFQF_HLUT(i), hlut_val); | |
673 | } | |
674 | ||
675 | return 0; | |
676 | } | |
677 | ||
fbb7ddfe GR |
678 | static struct ethtool_ops i40evf_ethtool_ops = { |
679 | .get_settings = i40evf_get_settings, | |
680 | .get_drvinfo = i40evf_get_drvinfo, | |
681 | .get_link = ethtool_op_get_link, | |
682 | .get_ringparam = i40evf_get_ringparam, | |
683 | .set_ringparam = i40evf_set_ringparam, | |
684 | .get_strings = i40evf_get_strings, | |
685 | .get_ethtool_stats = i40evf_get_ethtool_stats, | |
686 | .get_sset_count = i40evf_get_sset_count, | |
687 | .get_msglevel = i40evf_get_msglevel, | |
688 | .set_msglevel = i40evf_set_msglevel, | |
689 | .get_coalesce = i40evf_get_coalesce, | |
690 | .set_coalesce = i40evf_set_coalesce, | |
4e9dc31f MW |
691 | .get_rxnfc = i40evf_get_rxnfc, |
692 | .set_rxnfc = i40evf_set_rxnfc, | |
693 | .get_rxfh_indir_size = i40evf_get_rxfh_indir_size, | |
694 | .get_rxfh_indir = i40evf_get_rxfh_indir, | |
695 | .set_rxfh_indir = i40evf_set_rxfh_indir, | |
696 | .get_channels = i40evf_get_channels, | |
fbb7ddfe GR |
697 | }; |
698 | ||
699 | /** | |
700 | * i40evf_set_ethtool_ops - Initialize ethtool ops struct | |
701 | * @netdev: network interface device structure | |
702 | * | |
703 | * Sets ethtool ops struct in our netdev so that ethtool can call | |
704 | * our functions. | |
705 | **/ | |
706 | void i40evf_set_ethtool_ops(struct net_device *netdev) | |
707 | { | |
7ad24ea4 | 708 | netdev->ethtool_ops = &i40evf_ethtool_ops; |
fbb7ddfe | 709 | } |