Commit | Line | Data |
---|---|---|
fbb7ddfe GR |
1 | /******************************************************************************* |
2 | * | |
3 | * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver | |
00e5ec4b | 4 | * Copyright(c) 2013 - 2016 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 | ||
fbb7ddfe GR |
32 | struct i40evf_stats { |
33 | char stat_string[ETH_GSTRING_LEN]; | |
34 | int stat_offset; | |
35 | }; | |
36 | ||
37 | #define I40EVF_STAT(_name, _stat) { \ | |
38 | .stat_string = _name, \ | |
39 | .stat_offset = offsetof(struct i40evf_adapter, _stat) \ | |
40 | } | |
41 | ||
42 | /* All stats are u64, so we don't need to track the size of the field. */ | |
43 | static const struct i40evf_stats i40evf_gstrings_stats[] = { | |
44 | I40EVF_STAT("rx_bytes", current_stats.rx_bytes), | |
45 | I40EVF_STAT("rx_unicast", current_stats.rx_unicast), | |
46 | I40EVF_STAT("rx_multicast", current_stats.rx_multicast), | |
47 | I40EVF_STAT("rx_broadcast", current_stats.rx_broadcast), | |
48 | I40EVF_STAT("rx_discards", current_stats.rx_discards), | |
fbb7ddfe GR |
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) | |
c7b8d978 | 59 | #define I40EVF_QUEUE_STATS_LEN(_dev) \ |
75a64435 | 60 | (((struct i40evf_adapter *)\ |
cc052927 | 61 | netdev_priv(_dev))->num_active_queues \ |
c7b8d978 MW |
62 | * 2 * (sizeof(struct i40e_queue_stats) / sizeof(u64))) |
63 | #define I40EVF_STATS_LEN(_dev) \ | |
64 | (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN(_dev)) | |
fbb7ddfe GR |
65 | |
66 | /** | |
67 | * i40evf_get_settings - Get Link Speed and Duplex settings | |
68 | * @netdev: network interface device structure | |
69 | * @ecmd: ethtool command | |
70 | * | |
71 | * Reports speed/duplex settings. Because this is a VF, we don't know what | |
72 | * kind of link we really have, so we fake it. | |
73 | **/ | |
74 | static int i40evf_get_settings(struct net_device *netdev, | |
75 | struct ethtool_cmd *ecmd) | |
76 | { | |
fe458e50 MW |
77 | struct i40evf_adapter *adapter = netdev_priv(netdev); |
78 | ||
107f3018 | 79 | ecmd->supported = 0; |
fbb7ddfe GR |
80 | ecmd->autoneg = AUTONEG_DISABLE; |
81 | ecmd->transceiver = XCVR_DUMMY1; | |
82 | ecmd->port = PORT_NONE; | |
fe458e50 MW |
83 | /* Set speed and duplex */ |
84 | switch (adapter->link_speed) { | |
85 | case I40E_LINK_SPEED_40GB: | |
86 | ethtool_cmd_speed_set(ecmd, SPEED_40000); | |
87 | break; | |
88 | case I40E_LINK_SPEED_20GB: | |
89 | ethtool_cmd_speed_set(ecmd, SPEED_20000); | |
90 | break; | |
91 | case I40E_LINK_SPEED_10GB: | |
92 | ethtool_cmd_speed_set(ecmd, SPEED_10000); | |
93 | break; | |
94 | case I40E_LINK_SPEED_1GB: | |
95 | ethtool_cmd_speed_set(ecmd, SPEED_1000); | |
96 | break; | |
97 | case I40E_LINK_SPEED_100MB: | |
98 | ethtool_cmd_speed_set(ecmd, SPEED_100); | |
99 | break; | |
100 | default: | |
101 | break; | |
102 | } | |
103 | ecmd->duplex = DUPLEX_FULL; | |
fbb7ddfe GR |
104 | |
105 | return 0; | |
106 | } | |
107 | ||
108 | /** | |
109 | * i40evf_get_sset_count - Get length of string set | |
110 | * @netdev: network interface device structure | |
111 | * @sset: id of string set | |
112 | * | |
113 | * Reports size of string table. This driver only supports | |
114 | * strings for statistics. | |
115 | **/ | |
116 | static int i40evf_get_sset_count(struct net_device *netdev, int sset) | |
117 | { | |
118 | if (sset == ETH_SS_STATS) | |
c7b8d978 | 119 | return I40EVF_STATS_LEN(netdev); |
fbb7ddfe | 120 | else |
c7b8d978 | 121 | return -EINVAL; |
fbb7ddfe GR |
122 | } |
123 | ||
124 | /** | |
125 | * i40evf_get_ethtool_stats - report device statistics | |
126 | * @netdev: network interface device structure | |
127 | * @stats: ethtool statistics structure | |
128 | * @data: pointer to data buffer | |
129 | * | |
130 | * All statistics are added to the data buffer as an array of u64. | |
131 | **/ | |
132 | static void i40evf_get_ethtool_stats(struct net_device *netdev, | |
133 | struct ethtool_stats *stats, u64 *data) | |
134 | { | |
135 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
136 | int i, j; | |
137 | char *p; | |
138 | ||
139 | for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) { | |
140 | p = (char *)adapter + i40evf_gstrings_stats[i].stat_offset; | |
141 | data[i] = *(u64 *)p; | |
142 | } | |
cc052927 | 143 | for (j = 0; j < adapter->num_active_queues; j++) { |
0dd438d8 MW |
144 | data[i++] = adapter->tx_rings[j].stats.packets; |
145 | data[i++] = adapter->tx_rings[j].stats.bytes; | |
fbb7ddfe | 146 | } |
cc052927 | 147 | for (j = 0; j < adapter->num_active_queues; j++) { |
0dd438d8 MW |
148 | data[i++] = adapter->rx_rings[j].stats.packets; |
149 | data[i++] = adapter->rx_rings[j].stats.bytes; | |
fbb7ddfe GR |
150 | } |
151 | } | |
152 | ||
153 | /** | |
154 | * i40evf_get_strings - Get string set | |
155 | * @netdev: network interface device structure | |
156 | * @sset: id of string set | |
157 | * @data: buffer for string data | |
158 | * | |
159 | * Builds stats string table. | |
160 | **/ | |
161 | static void i40evf_get_strings(struct net_device *netdev, u32 sset, u8 *data) | |
162 | { | |
163 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
164 | u8 *p = data; | |
165 | int i; | |
166 | ||
167 | if (sset == ETH_SS_STATS) { | |
168 | for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) { | |
169 | memcpy(p, i40evf_gstrings_stats[i].stat_string, | |
170 | ETH_GSTRING_LEN); | |
171 | p += ETH_GSTRING_LEN; | |
172 | } | |
cc052927 | 173 | for (i = 0; i < adapter->num_active_queues; i++) { |
fbb7ddfe GR |
174 | snprintf(p, ETH_GSTRING_LEN, "tx-%u.packets", i); |
175 | p += ETH_GSTRING_LEN; | |
176 | snprintf(p, ETH_GSTRING_LEN, "tx-%u.bytes", i); | |
177 | p += ETH_GSTRING_LEN; | |
178 | } | |
cc052927 | 179 | for (i = 0; i < adapter->num_active_queues; i++) { |
fbb7ddfe GR |
180 | snprintf(p, ETH_GSTRING_LEN, "rx-%u.packets", i); |
181 | p += ETH_GSTRING_LEN; | |
182 | snprintf(p, ETH_GSTRING_LEN, "rx-%u.bytes", i); | |
183 | p += ETH_GSTRING_LEN; | |
184 | } | |
185 | } | |
186 | } | |
187 | ||
188 | /** | |
189 | * i40evf_get_msglevel - Get debug message level | |
190 | * @netdev: network interface device structure | |
191 | * | |
192 | * Returns current debug message level. | |
193 | **/ | |
194 | static u32 i40evf_get_msglevel(struct net_device *netdev) | |
195 | { | |
196 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
75a64435 | 197 | |
fbb7ddfe GR |
198 | return adapter->msg_enable; |
199 | } | |
200 | ||
201 | /** | |
0e888980 | 202 | * i40evf_set_msglevel - Set debug message level |
fbb7ddfe GR |
203 | * @netdev: network interface device structure |
204 | * @data: message level | |
205 | * | |
206 | * Set current debug message level. Higher values cause the driver to | |
207 | * be noisier. | |
208 | **/ | |
209 | static void i40evf_set_msglevel(struct net_device *netdev, u32 data) | |
210 | { | |
211 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
75a64435 | 212 | |
0e888980 AS |
213 | if (I40E_DEBUG_USER & data) |
214 | adapter->hw.debug_mask = data; | |
fbb7ddfe GR |
215 | adapter->msg_enable = data; |
216 | } | |
217 | ||
218 | /** | |
b39c1e2c | 219 | * i40evf_get_drvinfo - Get driver info |
fbb7ddfe GR |
220 | * @netdev: network interface device structure |
221 | * @drvinfo: ethool driver info structure | |
222 | * | |
223 | * Returns information about the driver and device for display to the user. | |
224 | **/ | |
225 | static void i40evf_get_drvinfo(struct net_device *netdev, | |
226 | struct ethtool_drvinfo *drvinfo) | |
227 | { | |
228 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
229 | ||
230 | strlcpy(drvinfo->driver, i40evf_driver_name, 32); | |
231 | strlcpy(drvinfo->version, i40evf_driver_version, 32); | |
cc470a81 | 232 | strlcpy(drvinfo->fw_version, "N/A", 4); |
fbb7ddfe GR |
233 | strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); |
234 | } | |
235 | ||
236 | /** | |
237 | * i40evf_get_ringparam - Get ring parameters | |
238 | * @netdev: network interface device structure | |
239 | * @ring: ethtool ringparam structure | |
240 | * | |
241 | * Returns current ring parameters. TX and RX rings are reported separately, | |
242 | * but the number of rings is not reported. | |
243 | **/ | |
244 | static void i40evf_get_ringparam(struct net_device *netdev, | |
75a64435 | 245 | struct ethtool_ringparam *ring) |
fbb7ddfe GR |
246 | { |
247 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
fbb7ddfe GR |
248 | |
249 | ring->rx_max_pending = I40EVF_MAX_RXD; | |
250 | ring->tx_max_pending = I40EVF_MAX_TXD; | |
d732a184 MW |
251 | ring->rx_pending = adapter->rx_desc_count; |
252 | ring->tx_pending = adapter->tx_desc_count; | |
fbb7ddfe GR |
253 | } |
254 | ||
255 | /** | |
256 | * i40evf_set_ringparam - Set ring parameters | |
257 | * @netdev: network interface device structure | |
258 | * @ring: ethtool ringparam structure | |
259 | * | |
260 | * Sets ring parameters. TX and RX rings are controlled separately, but the | |
261 | * number of rings is not specified, so all rings get the same settings. | |
262 | **/ | |
263 | static int i40evf_set_ringparam(struct net_device *netdev, | |
264 | struct ethtool_ringparam *ring) | |
265 | { | |
266 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
267 | u32 new_rx_count, new_tx_count; | |
268 | ||
269 | if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) | |
270 | return -EINVAL; | |
271 | ||
272 | new_tx_count = clamp_t(u32, ring->tx_pending, | |
273 | I40EVF_MIN_TXD, | |
274 | I40EVF_MAX_TXD); | |
275 | new_tx_count = ALIGN(new_tx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE); | |
276 | ||
277 | new_rx_count = clamp_t(u32, ring->rx_pending, | |
278 | I40EVF_MIN_RXD, | |
279 | I40EVF_MAX_RXD); | |
280 | new_rx_count = ALIGN(new_rx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE); | |
281 | ||
282 | /* if nothing to do return success */ | |
d732a184 MW |
283 | if ((new_tx_count == adapter->tx_desc_count) && |
284 | (new_rx_count == adapter->rx_desc_count)) | |
fbb7ddfe GR |
285 | return 0; |
286 | ||
d732a184 MW |
287 | adapter->tx_desc_count = new_tx_count; |
288 | adapter->rx_desc_count = new_rx_count; | |
fbb7ddfe | 289 | |
67c818a1 MW |
290 | if (netif_running(netdev)) { |
291 | adapter->flags |= I40EVF_FLAG_RESET_NEEDED; | |
292 | schedule_work(&adapter->reset_task); | |
293 | } | |
d732a184 | 294 | |
fbb7ddfe GR |
295 | return 0; |
296 | } | |
297 | ||
298 | /** | |
299 | * i40evf_get_coalesce - Get interrupt coalescing settings | |
300 | * @netdev: network interface device structure | |
301 | * @ec: ethtool coalesce structure | |
302 | * | |
303 | * Returns current coalescing settings. This is referred to elsewhere in the | |
304 | * driver as Interrupt Throttle Rate, as this is how the hardware describes | |
305 | * this functionality. | |
306 | **/ | |
307 | static int i40evf_get_coalesce(struct net_device *netdev, | |
75a64435 | 308 | struct ethtool_coalesce *ec) |
fbb7ddfe GR |
309 | { |
310 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
311 | struct i40e_vsi *vsi = &adapter->vsi; | |
312 | ||
313 | ec->tx_max_coalesced_frames = vsi->work_limit; | |
314 | ec->rx_max_coalesced_frames = vsi->work_limit; | |
315 | ||
316 | if (ITR_IS_DYNAMIC(vsi->rx_itr_setting)) | |
32f5f54a | 317 | ec->use_adaptive_rx_coalesce = 1; |
fbb7ddfe GR |
318 | |
319 | if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) | |
32f5f54a MW |
320 | ec->use_adaptive_tx_coalesce = 1; |
321 | ||
322 | ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC; | |
323 | ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC; | |
fbb7ddfe GR |
324 | |
325 | return 0; | |
326 | } | |
327 | ||
328 | /** | |
329 | * i40evf_set_coalesce - Set interrupt coalescing settings | |
330 | * @netdev: network interface device structure | |
331 | * @ec: ethtool coalesce structure | |
332 | * | |
333 | * Change current coalescing settings. | |
334 | **/ | |
335 | static int i40evf_set_coalesce(struct net_device *netdev, | |
75a64435 | 336 | struct ethtool_coalesce *ec) |
fbb7ddfe GR |
337 | { |
338 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
339 | struct i40e_hw *hw = &adapter->hw; | |
340 | struct i40e_vsi *vsi = &adapter->vsi; | |
341 | struct i40e_q_vector *q_vector; | |
342 | int i; | |
343 | ||
32f5f54a MW |
344 | if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq) |
345 | vsi->work_limit = ec->tx_max_coalesced_frames_irq; | |
346 | ||
347 | if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && | |
348 | (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1))) | |
fbb7ddfe | 349 | vsi->rx_itr_setting = ec->rx_coalesce_usecs; |
fbb7ddfe | 350 | |
32f5f54a MW |
351 | else |
352 | return -EINVAL; | |
353 | ||
354 | if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && | |
355 | (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1))) | |
fbb7ddfe | 356 | vsi->tx_itr_setting = ec->tx_coalesce_usecs; |
32f5f54a MW |
357 | else if (ec->use_adaptive_tx_coalesce) |
358 | vsi->tx_itr_setting = (I40E_ITR_DYNAMIC | | |
359 | ITR_REG_TO_USEC(I40E_ITR_RX_DEF)); | |
360 | else | |
361 | return -EINVAL; | |
362 | ||
363 | if (ec->use_adaptive_rx_coalesce) | |
364 | vsi->rx_itr_setting |= I40E_ITR_DYNAMIC; | |
365 | else | |
366 | vsi->rx_itr_setting &= ~I40E_ITR_DYNAMIC; | |
367 | ||
368 | if (ec->use_adaptive_tx_coalesce) | |
369 | vsi->tx_itr_setting |= I40E_ITR_DYNAMIC; | |
370 | else | |
371 | vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC; | |
fbb7ddfe GR |
372 | |
373 | for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) { | |
7d96ba1a | 374 | q_vector = &adapter->q_vectors[i]; |
fbb7ddfe GR |
375 | q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); |
376 | wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr); | |
377 | q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting); | |
378 | wr32(hw, I40E_VFINT_ITRN1(1, i), q_vector->tx.itr); | |
379 | i40e_flush(hw); | |
380 | } | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
4e9dc31f MW |
385 | /** |
386 | * i40evf_get_rxnfc - command to get RX flow classification rules | |
387 | * @netdev: network interface device structure | |
388 | * @cmd: ethtool rxnfc command | |
389 | * | |
390 | * Returns Success if the command is supported. | |
391 | **/ | |
392 | static int i40evf_get_rxnfc(struct net_device *netdev, | |
393 | struct ethtool_rxnfc *cmd, | |
394 | u32 *rule_locs) | |
395 | { | |
396 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
397 | int ret = -EOPNOTSUPP; | |
398 | ||
399 | switch (cmd->cmd) { | |
400 | case ETHTOOL_GRXRINGS: | |
cc052927 | 401 | cmd->data = adapter->num_active_queues; |
4e9dc31f MW |
402 | ret = 0; |
403 | break; | |
404 | case ETHTOOL_GRXFH: | |
b29699b3 CW |
405 | netdev_info(netdev, |
406 | "RSS hash info is not available to vf, use pf.\n"); | |
4e9dc31f MW |
407 | break; |
408 | default: | |
409 | break; | |
410 | } | |
411 | ||
412 | return ret; | |
413 | } | |
4e9dc31f MW |
414 | /** |
415 | * i40evf_get_channels: get the number of channels supported by the device | |
416 | * @netdev: network interface device structure | |
417 | * @ch: channel information structure | |
418 | * | |
419 | * For the purposes of our device, we only use combined channels, i.e. a tx/rx | |
420 | * queue pair. Report one extra channel to match our "other" MSI-X vector. | |
421 | **/ | |
422 | static void i40evf_get_channels(struct net_device *netdev, | |
423 | struct ethtool_channels *ch) | |
424 | { | |
425 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
426 | ||
427 | /* Report maximum channels */ | |
cc052927 | 428 | ch->max_combined = adapter->num_active_queues; |
4e9dc31f MW |
429 | |
430 | ch->max_other = NONQ_VECS; | |
431 | ch->other_count = NONQ_VECS; | |
432 | ||
cc052927 | 433 | ch->combined_count = adapter->num_active_queues; |
4e9dc31f MW |
434 | } |
435 | ||
43a3d9ba MW |
436 | /** |
437 | * i40evf_get_rxfh_key_size - get the RSS hash key size | |
438 | * @netdev: network interface device structure | |
439 | * | |
440 | * Returns the table size. | |
441 | **/ | |
442 | static u32 i40evf_get_rxfh_key_size(struct net_device *netdev) | |
443 | { | |
444 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
445 | ||
446 | return adapter->rss_key_size; | |
447 | } | |
448 | ||
4e9dc31f MW |
449 | /** |
450 | * i40evf_get_rxfh_indir_size - get the rx flow hash indirection table size | |
451 | * @netdev: network interface device structure | |
452 | * | |
453 | * Returns the table size. | |
454 | **/ | |
455 | static u32 i40evf_get_rxfh_indir_size(struct net_device *netdev) | |
456 | { | |
43a3d9ba MW |
457 | struct i40evf_adapter *adapter = netdev_priv(netdev); |
458 | ||
459 | return adapter->rss_lut_size; | |
4e9dc31f MW |
460 | } |
461 | ||
462 | /** | |
fe62d001 | 463 | * i40evf_get_rxfh - get the rx flow hash indirection table |
4e9dc31f MW |
464 | * @netdev: network interface device structure |
465 | * @indir: indirection table | |
2cda3f3b | 466 | * @key: hash key |
4e9dc31f MW |
467 | * |
468 | * Reads the indirection table directly from the hardware. Always returns 0. | |
469 | **/ | |
892311f6 EP |
470 | static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, |
471 | u8 *hfunc) | |
4e9dc31f MW |
472 | { |
473 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
90b02b43 | 474 | u16 i; |
4e9dc31f | 475 | |
892311f6 EP |
476 | if (hfunc) |
477 | *hfunc = ETH_RSS_HASH_TOP; | |
478 | if (!indir) | |
479 | return 0; | |
480 | ||
43a3d9ba | 481 | memcpy(key, adapter->rss_key, adapter->rss_key_size); |
90b02b43 HZ |
482 | |
483 | /* Each 32 bits pointed by 'indir' is stored with a lut entry */ | |
43a3d9ba MW |
484 | for (i = 0; i < adapter->rss_lut_size; i++) |
485 | indir[i] = (u32)adapter->rss_lut[i]; | |
90b02b43 | 486 | |
43a3d9ba | 487 | return 0; |
4e9dc31f MW |
488 | } |
489 | ||
490 | /** | |
fe62d001 | 491 | * i40evf_set_rxfh - set the rx flow hash indirection table |
4e9dc31f MW |
492 | * @netdev: network interface device structure |
493 | * @indir: indirection table | |
2cda3f3b | 494 | * @key: hash key |
4e9dc31f MW |
495 | * |
496 | * Returns -EINVAL if the table specifies an inavlid queue id, otherwise | |
497 | * returns 0 after programming the table. | |
498 | **/ | |
fe62d001 | 499 | static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, |
892311f6 | 500 | const u8 *key, const u8 hfunc) |
4e9dc31f MW |
501 | { |
502 | struct i40evf_adapter *adapter = netdev_priv(netdev); | |
2c86ac3c | 503 | u16 i; |
4e9dc31f | 504 | |
892311f6 EP |
505 | /* We do not allow change in unsupported parameters */ |
506 | if (key || | |
507 | (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) | |
508 | return -EOPNOTSUPP; | |
509 | if (!indir) | |
510 | return 0; | |
511 | ||
2c86ac3c | 512 | if (key) { |
43a3d9ba | 513 | memcpy(adapter->rss_key, key, adapter->rss_key_size); |
4e9dc31f MW |
514 | } |
515 | ||
2c86ac3c | 516 | /* Each 32 bits pointed by 'indir' is stored with a lut entry */ |
43a3d9ba MW |
517 | for (i = 0; i < adapter->rss_lut_size; i++) |
518 | adapter->rss_lut[i] = (u8)(indir[i]); | |
2c86ac3c | 519 | |
43a3d9ba | 520 | return i40evf_config_rss(adapter); |
4e9dc31f MW |
521 | } |
522 | ||
f0c53c76 | 523 | static const struct ethtool_ops i40evf_ethtool_ops = { |
fbb7ddfe GR |
524 | .get_settings = i40evf_get_settings, |
525 | .get_drvinfo = i40evf_get_drvinfo, | |
526 | .get_link = ethtool_op_get_link, | |
527 | .get_ringparam = i40evf_get_ringparam, | |
528 | .set_ringparam = i40evf_set_ringparam, | |
529 | .get_strings = i40evf_get_strings, | |
530 | .get_ethtool_stats = i40evf_get_ethtool_stats, | |
531 | .get_sset_count = i40evf_get_sset_count, | |
532 | .get_msglevel = i40evf_get_msglevel, | |
533 | .set_msglevel = i40evf_set_msglevel, | |
534 | .get_coalesce = i40evf_get_coalesce, | |
535 | .set_coalesce = i40evf_set_coalesce, | |
4e9dc31f | 536 | .get_rxnfc = i40evf_get_rxnfc, |
4e9dc31f | 537 | .get_rxfh_indir_size = i40evf_get_rxfh_indir_size, |
fe62d001 BH |
538 | .get_rxfh = i40evf_get_rxfh, |
539 | .set_rxfh = i40evf_set_rxfh, | |
4e9dc31f | 540 | .get_channels = i40evf_get_channels, |
43a3d9ba | 541 | .get_rxfh_key_size = i40evf_get_rxfh_key_size, |
fbb7ddfe GR |
542 | }; |
543 | ||
544 | /** | |
545 | * i40evf_set_ethtool_ops - Initialize ethtool ops struct | |
546 | * @netdev: network interface device structure | |
547 | * | |
548 | * Sets ethtool ops struct in our netdev so that ethtool can call | |
549 | * our functions. | |
550 | **/ | |
551 | void i40evf_set_ethtool_ops(struct net_device *netdev) | |
552 | { | |
7ad24ea4 | 553 | netdev->ethtool_ops = &i40evf_ethtool_ops; |
fbb7ddfe | 554 | } |