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) \ |
75a64435 | 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); | |
75a64435 | 178 | |
fbb7ddfe GR |
179 | return adapter->msg_enable; |
180 | } | |
181 | ||
182 | /** | |
183 | * i40evf_get_msglevel - Set debug message level | |
184 | * @netdev: network interface device structure | |
185 | * @data: message level | |
186 | * | |
187 | * Set current debug message level. Higher values cause the driver to | |
188 | * be noisier. | |
189 | **/ | |
190 | static void i40evf_set_msglevel(struct net_device *netdev, u32 data) | |
191 | { | |
192 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
75a64435 | 193 | |
fbb7ddfe GR |
194 | adapter->msg_enable = data; |
195 | } | |
196 | ||
197 | /** | |
b39c1e2c | 198 | * i40evf_get_drvinfo - Get driver info |
fbb7ddfe GR |
199 | * @netdev: network interface device structure |
200 | * @drvinfo: ethool driver info structure | |
201 | * | |
202 | * Returns information about the driver and device for display to the user. | |
203 | **/ | |
204 | static void i40evf_get_drvinfo(struct net_device *netdev, | |
205 | struct ethtool_drvinfo *drvinfo) | |
206 | { | |
207 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
208 | ||
209 | strlcpy(drvinfo->driver, i40evf_driver_name, 32); | |
210 | strlcpy(drvinfo->version, i40evf_driver_version, 32); | |
211 | ||
212 | strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); | |
213 | } | |
214 | ||
215 | /** | |
216 | * i40evf_get_ringparam - Get ring parameters | |
217 | * @netdev: network interface device structure | |
218 | * @ring: ethtool ringparam structure | |
219 | * | |
220 | * Returns current ring parameters. TX and RX rings are reported separately, | |
221 | * but the number of rings is not reported. | |
222 | **/ | |
223 | static void i40evf_get_ringparam(struct net_device *netdev, | |
75a64435 | 224 | struct ethtool_ringparam *ring) |
fbb7ddfe GR |
225 | { |
226 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
fbb7ddfe GR |
227 | |
228 | ring->rx_max_pending = I40EVF_MAX_RXD; | |
229 | ring->tx_max_pending = I40EVF_MAX_TXD; | |
d732a184 MW |
230 | ring->rx_pending = adapter->rx_desc_count; |
231 | ring->tx_pending = adapter->tx_desc_count; | |
fbb7ddfe GR |
232 | } |
233 | ||
234 | /** | |
235 | * i40evf_set_ringparam - Set ring parameters | |
236 | * @netdev: network interface device structure | |
237 | * @ring: ethtool ringparam structure | |
238 | * | |
239 | * Sets ring parameters. TX and RX rings are controlled separately, but the | |
240 | * number of rings is not specified, so all rings get the same settings. | |
241 | **/ | |
242 | static int i40evf_set_ringparam(struct net_device *netdev, | |
243 | struct ethtool_ringparam *ring) | |
244 | { | |
245 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
246 | u32 new_rx_count, new_tx_count; | |
247 | ||
248 | if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) | |
249 | return -EINVAL; | |
250 | ||
251 | new_tx_count = clamp_t(u32, ring->tx_pending, | |
252 | I40EVF_MIN_TXD, | |
253 | I40EVF_MAX_TXD); | |
254 | new_tx_count = ALIGN(new_tx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE); | |
255 | ||
256 | new_rx_count = clamp_t(u32, ring->rx_pending, | |
257 | I40EVF_MIN_RXD, | |
258 | I40EVF_MAX_RXD); | |
259 | new_rx_count = ALIGN(new_rx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE); | |
260 | ||
261 | /* if nothing to do return success */ | |
d732a184 MW |
262 | if ((new_tx_count == adapter->tx_desc_count) && |
263 | (new_rx_count == adapter->rx_desc_count)) | |
fbb7ddfe GR |
264 | return 0; |
265 | ||
d732a184 MW |
266 | adapter->tx_desc_count = new_tx_count; |
267 | adapter->rx_desc_count = new_rx_count; | |
fbb7ddfe GR |
268 | |
269 | if (netif_running(netdev)) | |
270 | i40evf_reinit_locked(adapter); | |
d732a184 | 271 | |
fbb7ddfe GR |
272 | return 0; |
273 | } | |
274 | ||
275 | /** | |
276 | * i40evf_get_coalesce - Get interrupt coalescing settings | |
277 | * @netdev: network interface device structure | |
278 | * @ec: ethtool coalesce structure | |
279 | * | |
280 | * Returns current coalescing settings. This is referred to elsewhere in the | |
281 | * driver as Interrupt Throttle Rate, as this is how the hardware describes | |
282 | * this functionality. | |
283 | **/ | |
284 | static int i40evf_get_coalesce(struct net_device *netdev, | |
75a64435 | 285 | struct ethtool_coalesce *ec) |
fbb7ddfe GR |
286 | { |
287 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
288 | struct i40e_vsi *vsi = &adapter->vsi; | |
289 | ||
290 | ec->tx_max_coalesced_frames = vsi->work_limit; | |
291 | ec->rx_max_coalesced_frames = vsi->work_limit; | |
292 | ||
293 | if (ITR_IS_DYNAMIC(vsi->rx_itr_setting)) | |
32f5f54a | 294 | ec->use_adaptive_rx_coalesce = 1; |
fbb7ddfe GR |
295 | |
296 | if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) | |
32f5f54a MW |
297 | ec->use_adaptive_tx_coalesce = 1; |
298 | ||
299 | ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC; | |
300 | ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC; | |
fbb7ddfe GR |
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, | |
75a64435 | 313 | struct ethtool_coalesce *ec) |
fbb7ddfe GR |
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 | ||
32f5f54a MW |
321 | if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq) |
322 | vsi->work_limit = ec->tx_max_coalesced_frames_irq; | |
323 | ||
324 | if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && | |
325 | (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1))) | |
fbb7ddfe | 326 | vsi->rx_itr_setting = ec->rx_coalesce_usecs; |
fbb7ddfe | 327 | |
32f5f54a MW |
328 | else |
329 | return -EINVAL; | |
330 | ||
331 | if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && | |
332 | (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1))) | |
fbb7ddfe | 333 | vsi->tx_itr_setting = ec->tx_coalesce_usecs; |
32f5f54a MW |
334 | else if (ec->use_adaptive_tx_coalesce) |
335 | vsi->tx_itr_setting = (I40E_ITR_DYNAMIC | | |
336 | ITR_REG_TO_USEC(I40E_ITR_RX_DEF)); | |
337 | else | |
338 | return -EINVAL; | |
339 | ||
340 | if (ec->use_adaptive_rx_coalesce) | |
341 | vsi->rx_itr_setting |= I40E_ITR_DYNAMIC; | |
342 | else | |
343 | vsi->rx_itr_setting &= ~I40E_ITR_DYNAMIC; | |
344 | ||
345 | if (ec->use_adaptive_tx_coalesce) | |
346 | vsi->tx_itr_setting |= I40E_ITR_DYNAMIC; | |
347 | else | |
348 | vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC; | |
fbb7ddfe GR |
349 | |
350 | for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) { | |
351 | q_vector = adapter->q_vector[i]; | |
352 | q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); | |
353 | wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr); | |
354 | q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting); | |
355 | wr32(hw, I40E_VFINT_ITRN1(1, i), q_vector->tx.itr); | |
356 | i40e_flush(hw); | |
357 | } | |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
4e9dc31f MW |
362 | /** |
363 | * i40e_get_rss_hash_opts - Get RSS hash Input Set for each flow type | |
364 | * @adapter: board private structure | |
365 | * @cmd: ethtool rxnfc command | |
366 | * | |
367 | * Returns Success if the flow is supported, else Invalid Input. | |
368 | **/ | |
369 | static int i40evf_get_rss_hash_opts(struct i40evf_adapter *adapter, | |
370 | struct ethtool_rxnfc *cmd) | |
371 | { | |
372 | struct i40e_hw *hw = &adapter->hw; | |
373 | u64 hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | |
374 | ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | |
375 | ||
376 | /* We always hash on IP src and dest addresses */ | |
377 | cmd->data = RXH_IP_SRC | RXH_IP_DST; | |
378 | ||
379 | switch (cmd->flow_type) { | |
380 | case TCP_V4_FLOW: | |
381 | if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP)) | |
382 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
383 | break; | |
384 | case UDP_V4_FLOW: | |
385 | if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP)) | |
386 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
387 | break; | |
388 | ||
389 | case SCTP_V4_FLOW: | |
390 | case AH_ESP_V4_FLOW: | |
391 | case AH_V4_FLOW: | |
392 | case ESP_V4_FLOW: | |
393 | case IPV4_FLOW: | |
394 | break; | |
395 | ||
396 | case TCP_V6_FLOW: | |
397 | if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP)) | |
398 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
399 | break; | |
400 | case UDP_V6_FLOW: | |
401 | if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP)) | |
402 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
403 | break; | |
404 | ||
405 | case SCTP_V6_FLOW: | |
406 | case AH_ESP_V6_FLOW: | |
407 | case AH_V6_FLOW: | |
408 | case ESP_V6_FLOW: | |
409 | case IPV6_FLOW: | |
410 | break; | |
411 | default: | |
412 | cmd->data = 0; | |
413 | return -EINVAL; | |
414 | } | |
415 | ||
416 | return 0; | |
417 | } | |
418 | ||
419 | /** | |
420 | * i40evf_get_rxnfc - command to get RX flow classification rules | |
421 | * @netdev: network interface device structure | |
422 | * @cmd: ethtool rxnfc command | |
423 | * | |
424 | * Returns Success if the command is supported. | |
425 | **/ | |
426 | static int i40evf_get_rxnfc(struct net_device *netdev, | |
427 | struct ethtool_rxnfc *cmd, | |
428 | u32 *rule_locs) | |
429 | { | |
430 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
431 | int ret = -EOPNOTSUPP; | |
432 | ||
433 | switch (cmd->cmd) { | |
434 | case ETHTOOL_GRXRINGS: | |
cc052927 | 435 | cmd->data = adapter->num_active_queues; |
4e9dc31f MW |
436 | ret = 0; |
437 | break; | |
438 | case ETHTOOL_GRXFH: | |
439 | ret = i40evf_get_rss_hash_opts(adapter, cmd); | |
440 | break; | |
441 | default: | |
442 | break; | |
443 | } | |
444 | ||
445 | return ret; | |
446 | } | |
447 | ||
448 | /** | |
449 | * i40evf_set_rss_hash_opt - Enable/Disable flow types for RSS hash | |
450 | * @adapter: board private structure | |
451 | * @cmd: ethtool rxnfc command | |
452 | * | |
453 | * Returns Success if the flow input set is supported. | |
454 | **/ | |
455 | static int i40evf_set_rss_hash_opt(struct i40evf_adapter *adapter, | |
456 | struct ethtool_rxnfc *nfc) | |
457 | { | |
458 | struct i40e_hw *hw = &adapter->hw; | |
459 | ||
460 | u64 hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | |
461 | ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | |
462 | ||
463 | /* RSS does not support anything other than hashing | |
464 | * to queues on src and dst IPs and ports | |
465 | */ | |
466 | if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | | |
467 | RXH_L4_B_0_1 | RXH_L4_B_2_3)) | |
468 | return -EINVAL; | |
469 | ||
470 | /* We need at least the IP SRC and DEST fields for hashing */ | |
471 | if (!(nfc->data & RXH_IP_SRC) || | |
472 | !(nfc->data & RXH_IP_DST)) | |
473 | return -EINVAL; | |
474 | ||
475 | switch (nfc->flow_type) { | |
476 | case TCP_V4_FLOW: | |
477 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
478 | case 0: | |
479 | hena &= ~((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP); | |
480 | break; | |
481 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
482 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP); | |
483 | break; | |
484 | default: | |
485 | return -EINVAL; | |
486 | } | |
487 | break; | |
488 | case TCP_V6_FLOW: | |
489 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
490 | case 0: | |
491 | hena &= ~((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP); | |
492 | break; | |
493 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
494 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP); | |
495 | break; | |
496 | default: | |
497 | return -EINVAL; | |
498 | } | |
499 | break; | |
500 | case UDP_V4_FLOW: | |
501 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
502 | case 0: | |
503 | hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | | |
504 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4)); | |
505 | break; | |
506 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
507 | hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | | |
508 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4)); | |
509 | break; | |
510 | default: | |
511 | return -EINVAL; | |
512 | } | |
513 | break; | |
514 | case UDP_V6_FLOW: | |
515 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
516 | case 0: | |
517 | hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | | |
518 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6)); | |
519 | break; | |
520 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
521 | hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | | |
522 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6)); | |
523 | break; | |
524 | default: | |
525 | return -EINVAL; | |
526 | } | |
527 | break; | |
528 | case AH_ESP_V4_FLOW: | |
529 | case AH_V4_FLOW: | |
530 | case ESP_V4_FLOW: | |
531 | case SCTP_V4_FLOW: | |
532 | if ((nfc->data & RXH_L4_B_0_1) || | |
533 | (nfc->data & RXH_L4_B_2_3)) | |
534 | return -EINVAL; | |
535 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER); | |
536 | break; | |
537 | case AH_ESP_V6_FLOW: | |
538 | case AH_V6_FLOW: | |
539 | case ESP_V6_FLOW: | |
540 | case SCTP_V6_FLOW: | |
541 | if ((nfc->data & RXH_L4_B_0_1) || | |
542 | (nfc->data & RXH_L4_B_2_3)) | |
543 | return -EINVAL; | |
544 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER); | |
545 | break; | |
546 | case IPV4_FLOW: | |
547 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | | |
548 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4); | |
549 | break; | |
550 | case IPV6_FLOW: | |
551 | hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER) | | |
552 | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6); | |
553 | break; | |
554 | default: | |
555 | return -EINVAL; | |
556 | } | |
557 | ||
558 | wr32(hw, I40E_VFQF_HENA(0), (u32)hena); | |
559 | wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); | |
560 | i40e_flush(hw); | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
565 | /** | |
566 | * i40evf_set_rxnfc - command to set RX flow classification rules | |
567 | * @netdev: network interface device structure | |
568 | * @cmd: ethtool rxnfc command | |
569 | * | |
570 | * Returns Success if the command is supported. | |
571 | **/ | |
572 | static int i40evf_set_rxnfc(struct net_device *netdev, | |
573 | struct ethtool_rxnfc *cmd) | |
574 | { | |
575 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
576 | int ret = -EOPNOTSUPP; | |
577 | ||
578 | switch (cmd->cmd) { | |
579 | case ETHTOOL_SRXFH: | |
580 | ret = i40evf_set_rss_hash_opt(adapter, cmd); | |
581 | break; | |
582 | default: | |
583 | break; | |
584 | } | |
585 | ||
586 | return ret; | |
587 | } | |
588 | ||
589 | /** | |
590 | * i40evf_get_channels: get the number of channels supported by the device | |
591 | * @netdev: network interface device structure | |
592 | * @ch: channel information structure | |
593 | * | |
594 | * For the purposes of our device, we only use combined channels, i.e. a tx/rx | |
595 | * queue pair. Report one extra channel to match our "other" MSI-X vector. | |
596 | **/ | |
597 | static void i40evf_get_channels(struct net_device *netdev, | |
598 | struct ethtool_channels *ch) | |
599 | { | |
600 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
601 | ||
602 | /* Report maximum channels */ | |
cc052927 | 603 | ch->max_combined = adapter->num_active_queues; |
4e9dc31f MW |
604 | |
605 | ch->max_other = NONQ_VECS; | |
606 | ch->other_count = NONQ_VECS; | |
607 | ||
cc052927 | 608 | ch->combined_count = adapter->num_active_queues; |
4e9dc31f MW |
609 | } |
610 | ||
611 | /** | |
612 | * i40evf_get_rxfh_indir_size - get the rx flow hash indirection table size | |
613 | * @netdev: network interface device structure | |
614 | * | |
615 | * Returns the table size. | |
616 | **/ | |
617 | static u32 i40evf_get_rxfh_indir_size(struct net_device *netdev) | |
618 | { | |
619 | return (I40E_VFQF_HLUT_MAX_INDEX + 1) * 4; | |
620 | } | |
621 | ||
622 | /** | |
fe62d001 | 623 | * i40evf_get_rxfh - get the rx flow hash indirection table |
4e9dc31f MW |
624 | * @netdev: network interface device structure |
625 | * @indir: indirection table | |
2cda3f3b | 626 | * @key: hash key |
4e9dc31f MW |
627 | * |
628 | * Reads the indirection table directly from the hardware. Always returns 0. | |
629 | **/ | |
fe62d001 | 630 | static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key) |
4e9dc31f MW |
631 | { |
632 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
633 | struct i40e_hw *hw = &adapter->hw; | |
634 | u32 hlut_val; | |
635 | int i, j; | |
636 | ||
cc70b080 | 637 | for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) { |
4e9dc31f MW |
638 | hlut_val = rd32(hw, I40E_VFQF_HLUT(i)); |
639 | indir[j++] = hlut_val & 0xff; | |
640 | indir[j++] = (hlut_val >> 8) & 0xff; | |
641 | indir[j++] = (hlut_val >> 16) & 0xff; | |
642 | indir[j++] = (hlut_val >> 24) & 0xff; | |
643 | } | |
644 | return 0; | |
645 | } | |
646 | ||
647 | /** | |
fe62d001 | 648 | * i40evf_set_rxfh - set the rx flow hash indirection table |
4e9dc31f MW |
649 | * @netdev: network interface device structure |
650 | * @indir: indirection table | |
2cda3f3b | 651 | * @key: hash key |
4e9dc31f MW |
652 | * |
653 | * Returns -EINVAL if the table specifies an inavlid queue id, otherwise | |
654 | * returns 0 after programming the table. | |
655 | **/ | |
fe62d001 BH |
656 | static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, |
657 | const u8 *key) | |
4e9dc31f MW |
658 | { |
659 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
660 | struct i40e_hw *hw = &adapter->hw; | |
661 | u32 hlut_val; | |
662 | int i, j; | |
663 | ||
cc70b080 | 664 | for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) { |
4e9dc31f MW |
665 | hlut_val = indir[j++]; |
666 | hlut_val |= indir[j++] << 8; | |
667 | hlut_val |= indir[j++] << 16; | |
668 | hlut_val |= indir[j++] << 24; | |
669 | wr32(hw, I40E_VFQF_HLUT(i), hlut_val); | |
670 | } | |
671 | ||
672 | return 0; | |
673 | } | |
674 | ||
f0c53c76 | 675 | static const struct ethtool_ops i40evf_ethtool_ops = { |
fbb7ddfe GR |
676 | .get_settings = i40evf_get_settings, |
677 | .get_drvinfo = i40evf_get_drvinfo, | |
678 | .get_link = ethtool_op_get_link, | |
679 | .get_ringparam = i40evf_get_ringparam, | |
680 | .set_ringparam = i40evf_set_ringparam, | |
681 | .get_strings = i40evf_get_strings, | |
682 | .get_ethtool_stats = i40evf_get_ethtool_stats, | |
683 | .get_sset_count = i40evf_get_sset_count, | |
684 | .get_msglevel = i40evf_get_msglevel, | |
685 | .set_msglevel = i40evf_set_msglevel, | |
686 | .get_coalesce = i40evf_get_coalesce, | |
687 | .set_coalesce = i40evf_set_coalesce, | |
4e9dc31f MW |
688 | .get_rxnfc = i40evf_get_rxnfc, |
689 | .set_rxnfc = i40evf_set_rxnfc, | |
690 | .get_rxfh_indir_size = i40evf_get_rxfh_indir_size, | |
fe62d001 BH |
691 | .get_rxfh = i40evf_get_rxfh, |
692 | .set_rxfh = i40evf_set_rxfh, | |
4e9dc31f | 693 | .get_channels = i40evf_get_channels, |
fbb7ddfe GR |
694 | }; |
695 | ||
696 | /** | |
697 | * i40evf_set_ethtool_ops - Initialize ethtool ops struct | |
698 | * @netdev: network interface device structure | |
699 | * | |
700 | * Sets ethtool ops struct in our netdev so that ethtool can call | |
701 | * our functions. | |
702 | **/ | |
703 | void i40evf_set_ethtool_ops(struct net_device *netdev) | |
704 | { | |
7ad24ea4 | 705 | netdev->ethtool_ops = &i40evf_ethtool_ops; |
fbb7ddfe | 706 | } |