Commit | Line | Data |
---|---|---|
f13bbc2f NP |
1 | /** |
2 | * Copyright 2013 Cisco Systems, Inc. All rights reserved. | |
3 | * | |
4 | * This program is free software; you may redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; version 2 of the License. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
9 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
10 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
11 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
12 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
15 | * SOFTWARE. | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/netdevice.h> | |
20 | #include <linux/ethtool.h> | |
21 | ||
22 | #include "enic_res.h" | |
23 | #include "enic.h" | |
24 | #include "enic_dev.h" | |
3762ff8f | 25 | #include "enic_clsf.h" |
4f675eb2 | 26 | #include "vnic_rss.h" |
58feff07 | 27 | #include "vnic_stats.h" |
f13bbc2f NP |
28 | |
29 | struct enic_stat { | |
30 | char name[ETH_GSTRING_LEN]; | |
31 | unsigned int index; | |
32 | }; | |
33 | ||
34 | #define ENIC_TX_STAT(stat) { \ | |
35 | .name = #stat, \ | |
36 | .index = offsetof(struct vnic_tx_stats, stat) / sizeof(u64) \ | |
37 | } | |
38 | ||
39 | #define ENIC_RX_STAT(stat) { \ | |
40 | .name = #stat, \ | |
41 | .index = offsetof(struct vnic_rx_stats, stat) / sizeof(u64) \ | |
42 | } | |
43 | ||
58feff07 GV |
44 | #define ENIC_GEN_STAT(stat) { \ |
45 | .name = #stat, \ | |
46 | .index = offsetof(struct vnic_gen_stats, stat) / sizeof(u64)\ | |
47 | } | |
48 | ||
f13bbc2f NP |
49 | static const struct enic_stat enic_tx_stats[] = { |
50 | ENIC_TX_STAT(tx_frames_ok), | |
51 | ENIC_TX_STAT(tx_unicast_frames_ok), | |
52 | ENIC_TX_STAT(tx_multicast_frames_ok), | |
53 | ENIC_TX_STAT(tx_broadcast_frames_ok), | |
54 | ENIC_TX_STAT(tx_bytes_ok), | |
55 | ENIC_TX_STAT(tx_unicast_bytes_ok), | |
56 | ENIC_TX_STAT(tx_multicast_bytes_ok), | |
57 | ENIC_TX_STAT(tx_broadcast_bytes_ok), | |
58 | ENIC_TX_STAT(tx_drops), | |
59 | ENIC_TX_STAT(tx_errors), | |
60 | ENIC_TX_STAT(tx_tso), | |
61 | }; | |
62 | ||
63 | static const struct enic_stat enic_rx_stats[] = { | |
64 | ENIC_RX_STAT(rx_frames_ok), | |
65 | ENIC_RX_STAT(rx_frames_total), | |
66 | ENIC_RX_STAT(rx_unicast_frames_ok), | |
67 | ENIC_RX_STAT(rx_multicast_frames_ok), | |
68 | ENIC_RX_STAT(rx_broadcast_frames_ok), | |
69 | ENIC_RX_STAT(rx_bytes_ok), | |
70 | ENIC_RX_STAT(rx_unicast_bytes_ok), | |
71 | ENIC_RX_STAT(rx_multicast_bytes_ok), | |
72 | ENIC_RX_STAT(rx_broadcast_bytes_ok), | |
73 | ENIC_RX_STAT(rx_drop), | |
74 | ENIC_RX_STAT(rx_no_bufs), | |
75 | ENIC_RX_STAT(rx_errors), | |
76 | ENIC_RX_STAT(rx_rss), | |
77 | ENIC_RX_STAT(rx_crc_errors), | |
78 | ENIC_RX_STAT(rx_frames_64), | |
79 | ENIC_RX_STAT(rx_frames_127), | |
80 | ENIC_RX_STAT(rx_frames_255), | |
81 | ENIC_RX_STAT(rx_frames_511), | |
82 | ENIC_RX_STAT(rx_frames_1023), | |
83 | ENIC_RX_STAT(rx_frames_1518), | |
84 | ENIC_RX_STAT(rx_frames_to_max), | |
85 | }; | |
86 | ||
58feff07 GV |
87 | static const struct enic_stat enic_gen_stats[] = { |
88 | ENIC_GEN_STAT(dma_map_error), | |
89 | }; | |
90 | ||
f13bbc2f NP |
91 | static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats); |
92 | static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats); | |
58feff07 | 93 | static const unsigned int enic_n_gen_stats = ARRAY_SIZE(enic_gen_stats); |
f13bbc2f | 94 | |
a20667bf | 95 | static void enic_intr_coal_set_rx(struct enic *enic, u32 timer) |
7c2ce6e6 SS |
96 | { |
97 | int i; | |
98 | int intr; | |
99 | ||
100 | for (i = 0; i < enic->rq_count; i++) { | |
101 | intr = enic_msix_rq_intr(enic, i); | |
102 | vnic_intr_coalescing_timer_set(&enic->intr[intr], timer); | |
103 | } | |
104 | } | |
105 | ||
f13bbc2f NP |
106 | static int enic_get_settings(struct net_device *netdev, |
107 | struct ethtool_cmd *ecmd) | |
108 | { | |
109 | struct enic *enic = netdev_priv(netdev); | |
110 | ||
111 | ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); | |
112 | ecmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); | |
113 | ecmd->port = PORT_FIBRE; | |
114 | ecmd->transceiver = XCVR_EXTERNAL; | |
115 | ||
116 | if (netif_carrier_ok(netdev)) { | |
117 | ethtool_cmd_speed_set(ecmd, vnic_dev_port_speed(enic->vdev)); | |
118 | ecmd->duplex = DUPLEX_FULL; | |
119 | } else { | |
537fae01 JP |
120 | ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); |
121 | ecmd->duplex = DUPLEX_UNKNOWN; | |
f13bbc2f NP |
122 | } |
123 | ||
124 | ecmd->autoneg = AUTONEG_DISABLE; | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | static void enic_get_drvinfo(struct net_device *netdev, | |
130 | struct ethtool_drvinfo *drvinfo) | |
131 | { | |
132 | struct enic *enic = netdev_priv(netdev); | |
133 | struct vnic_devcmd_fw_info *fw_info; | |
19b596bd | 134 | int err; |
f13bbc2f | 135 | |
19b596bd GV |
136 | err = enic_dev_fw_info(enic, &fw_info); |
137 | /* return only when pci_zalloc_consistent fails in vnic_dev_fw_info | |
138 | * For other failures, like devcmd failure, we return previously | |
139 | * recorded info. | |
140 | */ | |
141 | if (err == -ENOMEM) | |
142 | return; | |
f13bbc2f NP |
143 | |
144 | strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); | |
145 | strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); | |
146 | strlcpy(drvinfo->fw_version, fw_info->fw_version, | |
147 | sizeof(drvinfo->fw_version)); | |
148 | strlcpy(drvinfo->bus_info, pci_name(enic->pdev), | |
149 | sizeof(drvinfo->bus_info)); | |
150 | } | |
151 | ||
152 | static void enic_get_strings(struct net_device *netdev, u32 stringset, | |
153 | u8 *data) | |
154 | { | |
155 | unsigned int i; | |
156 | ||
157 | switch (stringset) { | |
158 | case ETH_SS_STATS: | |
159 | for (i = 0; i < enic_n_tx_stats; i++) { | |
160 | memcpy(data, enic_tx_stats[i].name, ETH_GSTRING_LEN); | |
161 | data += ETH_GSTRING_LEN; | |
162 | } | |
163 | for (i = 0; i < enic_n_rx_stats; i++) { | |
164 | memcpy(data, enic_rx_stats[i].name, ETH_GSTRING_LEN); | |
165 | data += ETH_GSTRING_LEN; | |
166 | } | |
58feff07 GV |
167 | for (i = 0; i < enic_n_gen_stats; i++) { |
168 | memcpy(data, enic_gen_stats[i].name, ETH_GSTRING_LEN); | |
169 | data += ETH_GSTRING_LEN; | |
170 | } | |
f13bbc2f NP |
171 | break; |
172 | } | |
173 | } | |
174 | ||
175 | static int enic_get_sset_count(struct net_device *netdev, int sset) | |
176 | { | |
177 | switch (sset) { | |
178 | case ETH_SS_STATS: | |
58feff07 | 179 | return enic_n_tx_stats + enic_n_rx_stats + enic_n_gen_stats; |
f13bbc2f NP |
180 | default: |
181 | return -EOPNOTSUPP; | |
182 | } | |
183 | } | |
184 | ||
185 | static void enic_get_ethtool_stats(struct net_device *netdev, | |
186 | struct ethtool_stats *stats, u64 *data) | |
187 | { | |
188 | struct enic *enic = netdev_priv(netdev); | |
189 | struct vnic_stats *vstats; | |
190 | unsigned int i; | |
19b596bd GV |
191 | int err; |
192 | ||
193 | err = enic_dev_stats_dump(enic, &vstats); | |
194 | /* return only when pci_zalloc_consistent fails in vnic_dev_stats_dump | |
195 | * For other failures, like devcmd failure, we return previously | |
196 | * recorded stats. | |
197 | */ | |
198 | if (err == -ENOMEM) | |
199 | return; | |
f13bbc2f NP |
200 | |
201 | for (i = 0; i < enic_n_tx_stats; i++) | |
202 | *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].index]; | |
203 | for (i = 0; i < enic_n_rx_stats; i++) | |
204 | *(data++) = ((u64 *)&vstats->rx)[enic_rx_stats[i].index]; | |
58feff07 GV |
205 | for (i = 0; i < enic_n_gen_stats; i++) |
206 | *(data++) = ((u64 *)&enic->gen_stats)[enic_gen_stats[i].index]; | |
f13bbc2f NP |
207 | } |
208 | ||
209 | static u32 enic_get_msglevel(struct net_device *netdev) | |
210 | { | |
211 | struct enic *enic = netdev_priv(netdev); | |
212 | return enic->msg_enable; | |
213 | } | |
214 | ||
215 | static void enic_set_msglevel(struct net_device *netdev, u32 value) | |
216 | { | |
217 | struct enic *enic = netdev_priv(netdev); | |
218 | enic->msg_enable = value; | |
219 | } | |
220 | ||
221 | static int enic_get_coalesce(struct net_device *netdev, | |
222 | struct ethtool_coalesce *ecmd) | |
223 | { | |
224 | struct enic *enic = netdev_priv(netdev); | |
7c2ce6e6 | 225 | struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; |
f13bbc2f NP |
226 | |
227 | ecmd->tx_coalesce_usecs = enic->tx_coalesce_usecs; | |
228 | ecmd->rx_coalesce_usecs = enic->rx_coalesce_usecs; | |
7c2ce6e6 SS |
229 | if (rxcoal->use_adaptive_rx_coalesce) |
230 | ecmd->use_adaptive_rx_coalesce = 1; | |
231 | ecmd->rx_coalesce_usecs_low = rxcoal->small_pkt_range_start; | |
232 | ecmd->rx_coalesce_usecs_high = rxcoal->range_end; | |
f13bbc2f NP |
233 | |
234 | return 0; | |
235 | } | |
236 | ||
237 | static int enic_set_coalesce(struct net_device *netdev, | |
238 | struct ethtool_coalesce *ecmd) | |
239 | { | |
240 | struct enic *enic = netdev_priv(netdev); | |
241 | u32 tx_coalesce_usecs; | |
242 | u32 rx_coalesce_usecs; | |
7c2ce6e6 SS |
243 | u32 rx_coalesce_usecs_low; |
244 | u32 rx_coalesce_usecs_high; | |
245 | u32 coalesce_usecs_max; | |
f13bbc2f | 246 | unsigned int i, intr; |
7c2ce6e6 | 247 | struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; |
f13bbc2f | 248 | |
7c2ce6e6 | 249 | coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev); |
f13bbc2f | 250 | tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs, |
7c2ce6e6 | 251 | coalesce_usecs_max); |
f13bbc2f | 252 | rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs, |
7c2ce6e6 SS |
253 | coalesce_usecs_max); |
254 | ||
255 | rx_coalesce_usecs_low = min_t(u32, ecmd->rx_coalesce_usecs_low, | |
256 | coalesce_usecs_max); | |
257 | rx_coalesce_usecs_high = min_t(u32, ecmd->rx_coalesce_usecs_high, | |
258 | coalesce_usecs_max); | |
f13bbc2f NP |
259 | |
260 | switch (vnic_dev_get_intr_mode(enic->vdev)) { | |
261 | case VNIC_DEV_INTR_MODE_INTX: | |
262 | if (tx_coalesce_usecs != rx_coalesce_usecs) | |
263 | return -EINVAL; | |
7c2ce6e6 SS |
264 | if (ecmd->use_adaptive_rx_coalesce || |
265 | ecmd->rx_coalesce_usecs_low || | |
266 | ecmd->rx_coalesce_usecs_high) | |
a16a3361 | 267 | return -EINVAL; |
f13bbc2f NP |
268 | |
269 | intr = enic_legacy_io_intr(); | |
270 | vnic_intr_coalescing_timer_set(&enic->intr[intr], | |
271 | tx_coalesce_usecs); | |
272 | break; | |
273 | case VNIC_DEV_INTR_MODE_MSI: | |
274 | if (tx_coalesce_usecs != rx_coalesce_usecs) | |
275 | return -EINVAL; | |
7c2ce6e6 SS |
276 | if (ecmd->use_adaptive_rx_coalesce || |
277 | ecmd->rx_coalesce_usecs_low || | |
278 | ecmd->rx_coalesce_usecs_high) | |
a16a3361 | 279 | return -EINVAL; |
f13bbc2f NP |
280 | |
281 | vnic_intr_coalescing_timer_set(&enic->intr[0], | |
282 | tx_coalesce_usecs); | |
283 | break; | |
284 | case VNIC_DEV_INTR_MODE_MSIX: | |
a16a3361 GV |
285 | if (ecmd->rx_coalesce_usecs_high && |
286 | (rx_coalesce_usecs_high < | |
287 | rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF)) | |
288 | return -EINVAL; | |
289 | ||
f13bbc2f NP |
290 | for (i = 0; i < enic->wq_count; i++) { |
291 | intr = enic_msix_wq_intr(enic, i); | |
292 | vnic_intr_coalescing_timer_set(&enic->intr[intr], | |
293 | tx_coalesce_usecs); | |
294 | } | |
295 | ||
a16a3361 GV |
296 | rxcoal->use_adaptive_rx_coalesce = |
297 | !!ecmd->use_adaptive_rx_coalesce; | |
298 | if (!rxcoal->use_adaptive_rx_coalesce) | |
299 | enic_intr_coal_set_rx(enic, rx_coalesce_usecs); | |
f13bbc2f | 300 | |
7c2ce6e6 | 301 | if (ecmd->rx_coalesce_usecs_high) { |
7c2ce6e6 SS |
302 | rxcoal->range_end = rx_coalesce_usecs_high; |
303 | rxcoal->small_pkt_range_start = rx_coalesce_usecs_low; | |
304 | rxcoal->large_pkt_range_start = rx_coalesce_usecs_low + | |
305 | ENIC_AIC_LARGE_PKT_DIFF; | |
306 | } | |
f13bbc2f NP |
307 | break; |
308 | default: | |
309 | break; | |
310 | } | |
311 | ||
312 | enic->tx_coalesce_usecs = tx_coalesce_usecs; | |
313 | enic->rx_coalesce_usecs = rx_coalesce_usecs; | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
3762ff8f GV |
318 | static int enic_grxclsrlall(struct enic *enic, struct ethtool_rxnfc *cmd, |
319 | u32 *rule_locs) | |
320 | { | |
321 | int j, ret = 0, cnt = 0; | |
322 | ||
323 | cmd->data = enic->rfs_h.max - enic->rfs_h.free; | |
324 | for (j = 0; j < (1 << ENIC_RFS_FLW_BITSHIFT); j++) { | |
325 | struct hlist_head *hhead; | |
326 | struct hlist_node *tmp; | |
327 | struct enic_rfs_fltr_node *n; | |
328 | ||
329 | hhead = &enic->rfs_h.ht_head[j]; | |
330 | hlist_for_each_entry_safe(n, tmp, hhead, node) { | |
331 | if (cnt == cmd->rule_cnt) | |
332 | return -EMSGSIZE; | |
333 | rule_locs[cnt] = n->fltr_id; | |
334 | cnt++; | |
335 | } | |
336 | } | |
337 | cmd->rule_cnt = cnt; | |
338 | ||
339 | return ret; | |
340 | } | |
341 | ||
342 | static int enic_grxclsrule(struct enic *enic, struct ethtool_rxnfc *cmd) | |
343 | { | |
344 | struct ethtool_rx_flow_spec *fsp = | |
345 | (struct ethtool_rx_flow_spec *)&cmd->fs; | |
346 | struct enic_rfs_fltr_node *n; | |
347 | ||
348 | n = htbl_fltr_search(enic, (u16)fsp->location); | |
349 | if (!n) | |
350 | return -EINVAL; | |
351 | switch (n->keys.ip_proto) { | |
352 | case IPPROTO_TCP: | |
353 | fsp->flow_type = TCP_V4_FLOW; | |
354 | break; | |
355 | case IPPROTO_UDP: | |
356 | fsp->flow_type = UDP_V4_FLOW; | |
357 | break; | |
358 | default: | |
359 | return -EINVAL; | |
360 | break; | |
361 | } | |
362 | ||
363 | fsp->h_u.tcp_ip4_spec.ip4src = n->keys.src; | |
364 | fsp->m_u.tcp_ip4_spec.ip4src = (__u32)~0; | |
365 | ||
366 | fsp->h_u.tcp_ip4_spec.ip4dst = n->keys.dst; | |
367 | fsp->m_u.tcp_ip4_spec.ip4dst = (__u32)~0; | |
368 | ||
369 | fsp->h_u.tcp_ip4_spec.psrc = n->keys.port16[0]; | |
370 | fsp->m_u.tcp_ip4_spec.psrc = (__u16)~0; | |
371 | ||
372 | fsp->h_u.tcp_ip4_spec.pdst = n->keys.port16[1]; | |
373 | fsp->m_u.tcp_ip4_spec.pdst = (__u16)~0; | |
374 | ||
375 | fsp->ring_cookie = n->rq_id; | |
376 | ||
377 | return 0; | |
378 | } | |
379 | ||
380 | static int enic_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, | |
381 | u32 *rule_locs) | |
382 | { | |
383 | struct enic *enic = netdev_priv(dev); | |
384 | int ret = 0; | |
385 | ||
386 | switch (cmd->cmd) { | |
387 | case ETHTOOL_GRXRINGS: | |
388 | cmd->data = enic->rq_count; | |
389 | break; | |
390 | case ETHTOOL_GRXCLSRLCNT: | |
391 | spin_lock_bh(&enic->rfs_h.lock); | |
392 | cmd->rule_cnt = enic->rfs_h.max - enic->rfs_h.free; | |
393 | cmd->data = enic->rfs_h.max; | |
394 | spin_unlock_bh(&enic->rfs_h.lock); | |
395 | break; | |
396 | case ETHTOOL_GRXCLSRLALL: | |
397 | spin_lock_bh(&enic->rfs_h.lock); | |
398 | ret = enic_grxclsrlall(enic, cmd, rule_locs); | |
399 | spin_unlock_bh(&enic->rfs_h.lock); | |
400 | break; | |
401 | case ETHTOOL_GRXCLSRULE: | |
402 | spin_lock_bh(&enic->rfs_h.lock); | |
403 | ret = enic_grxclsrule(enic, cmd); | |
404 | spin_unlock_bh(&enic->rfs_h.lock); | |
405 | break; | |
406 | default: | |
407 | ret = -EOPNOTSUPP; | |
408 | break; | |
409 | } | |
410 | ||
411 | return ret; | |
412 | } | |
413 | ||
d4ad30b1 GV |
414 | static int enic_get_tunable(struct net_device *dev, |
415 | const struct ethtool_tunable *tuna, void *data) | |
416 | { | |
417 | struct enic *enic = netdev_priv(dev); | |
418 | int ret = 0; | |
419 | ||
420 | switch (tuna->id) { | |
421 | case ETHTOOL_RX_COPYBREAK: | |
422 | *(u32 *)data = enic->rx_copybreak; | |
423 | break; | |
424 | default: | |
425 | ret = -EINVAL; | |
426 | break; | |
427 | } | |
428 | ||
429 | return ret; | |
430 | } | |
431 | ||
432 | static int enic_set_tunable(struct net_device *dev, | |
433 | const struct ethtool_tunable *tuna, | |
434 | const void *data) | |
435 | { | |
436 | struct enic *enic = netdev_priv(dev); | |
437 | int ret = 0; | |
438 | ||
439 | switch (tuna->id) { | |
440 | case ETHTOOL_RX_COPYBREAK: | |
441 | enic->rx_copybreak = *(u32 *)data; | |
442 | break; | |
443 | default: | |
444 | ret = -EINVAL; | |
445 | break; | |
446 | } | |
447 | ||
448 | return ret; | |
449 | } | |
450 | ||
4f675eb2 GV |
451 | static u32 enic_get_rxfh_key_size(struct net_device *netdev) |
452 | { | |
453 | return ENIC_RSS_LEN; | |
454 | } | |
455 | ||
456 | static int enic_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey, | |
457 | u8 *hfunc) | |
458 | { | |
459 | struct enic *enic = netdev_priv(netdev); | |
460 | ||
461 | if (hkey) | |
462 | memcpy(hkey, enic->rss_key, ENIC_RSS_LEN); | |
463 | ||
464 | if (hfunc) | |
465 | *hfunc = ETH_RSS_HASH_TOP; | |
466 | ||
467 | return 0; | |
468 | } | |
469 | ||
470 | static int enic_set_rxfh(struct net_device *netdev, const u32 *indir, | |
471 | const u8 *hkey, const u8 hfunc) | |
472 | { | |
473 | struct enic *enic = netdev_priv(netdev); | |
474 | ||
475 | if ((hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) || | |
476 | indir) | |
477 | return -EINVAL; | |
478 | ||
479 | if (hkey) | |
480 | memcpy(enic->rss_key, hkey, ENIC_RSS_LEN); | |
481 | ||
482 | return __enic_set_rsskey(enic); | |
483 | } | |
484 | ||
f13bbc2f NP |
485 | static const struct ethtool_ops enic_ethtool_ops = { |
486 | .get_settings = enic_get_settings, | |
487 | .get_drvinfo = enic_get_drvinfo, | |
488 | .get_msglevel = enic_get_msglevel, | |
489 | .set_msglevel = enic_set_msglevel, | |
490 | .get_link = ethtool_op_get_link, | |
491 | .get_strings = enic_get_strings, | |
492 | .get_sset_count = enic_get_sset_count, | |
493 | .get_ethtool_stats = enic_get_ethtool_stats, | |
494 | .get_coalesce = enic_get_coalesce, | |
495 | .set_coalesce = enic_set_coalesce, | |
3762ff8f | 496 | .get_rxnfc = enic_get_rxnfc, |
d4ad30b1 GV |
497 | .get_tunable = enic_get_tunable, |
498 | .set_tunable = enic_set_tunable, | |
4f675eb2 GV |
499 | .get_rxfh_key_size = enic_get_rxfh_key_size, |
500 | .get_rxfh = enic_get_rxfh, | |
501 | .set_rxfh = enic_set_rxfh, | |
f13bbc2f NP |
502 | }; |
503 | ||
504 | void enic_set_ethtool_ops(struct net_device *netdev) | |
505 | { | |
7ad24ea4 | 506 | netdev->ethtool_ops = &enic_ethtool_ops; |
f13bbc2f | 507 | } |