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