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