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; | |
134 | ||
135 | enic_dev_fw_info(enic, &fw_info); | |
136 | ||
137 | strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); | |
138 | strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); | |
139 | strlcpy(drvinfo->fw_version, fw_info->fw_version, | |
140 | sizeof(drvinfo->fw_version)); | |
141 | strlcpy(drvinfo->bus_info, pci_name(enic->pdev), | |
142 | sizeof(drvinfo->bus_info)); | |
143 | } | |
144 | ||
145 | static void enic_get_strings(struct net_device *netdev, u32 stringset, | |
146 | u8 *data) | |
147 | { | |
148 | unsigned int i; | |
149 | ||
150 | switch (stringset) { | |
151 | case ETH_SS_STATS: | |
152 | for (i = 0; i < enic_n_tx_stats; i++) { | |
153 | memcpy(data, enic_tx_stats[i].name, ETH_GSTRING_LEN); | |
154 | data += ETH_GSTRING_LEN; | |
155 | } | |
156 | for (i = 0; i < enic_n_rx_stats; i++) { | |
157 | memcpy(data, enic_rx_stats[i].name, ETH_GSTRING_LEN); | |
158 | data += ETH_GSTRING_LEN; | |
159 | } | |
58feff07 GV |
160 | for (i = 0; i < enic_n_gen_stats; i++) { |
161 | memcpy(data, enic_gen_stats[i].name, ETH_GSTRING_LEN); | |
162 | data += ETH_GSTRING_LEN; | |
163 | } | |
f13bbc2f NP |
164 | break; |
165 | } | |
166 | } | |
167 | ||
168 | static int enic_get_sset_count(struct net_device *netdev, int sset) | |
169 | { | |
170 | switch (sset) { | |
171 | case ETH_SS_STATS: | |
58feff07 | 172 | return enic_n_tx_stats + enic_n_rx_stats + enic_n_gen_stats; |
f13bbc2f NP |
173 | default: |
174 | return -EOPNOTSUPP; | |
175 | } | |
176 | } | |
177 | ||
178 | static void enic_get_ethtool_stats(struct net_device *netdev, | |
179 | struct ethtool_stats *stats, u64 *data) | |
180 | { | |
181 | struct enic *enic = netdev_priv(netdev); | |
182 | struct vnic_stats *vstats; | |
183 | unsigned int i; | |
184 | ||
185 | enic_dev_stats_dump(enic, &vstats); | |
186 | ||
187 | for (i = 0; i < enic_n_tx_stats; i++) | |
188 | *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].index]; | |
189 | for (i = 0; i < enic_n_rx_stats; i++) | |
190 | *(data++) = ((u64 *)&vstats->rx)[enic_rx_stats[i].index]; | |
58feff07 GV |
191 | for (i = 0; i < enic_n_gen_stats; i++) |
192 | *(data++) = ((u64 *)&enic->gen_stats)[enic_gen_stats[i].index]; | |
f13bbc2f NP |
193 | } |
194 | ||
195 | static u32 enic_get_msglevel(struct net_device *netdev) | |
196 | { | |
197 | struct enic *enic = netdev_priv(netdev); | |
198 | return enic->msg_enable; | |
199 | } | |
200 | ||
201 | static void enic_set_msglevel(struct net_device *netdev, u32 value) | |
202 | { | |
203 | struct enic *enic = netdev_priv(netdev); | |
204 | enic->msg_enable = value; | |
205 | } | |
206 | ||
207 | static int enic_get_coalesce(struct net_device *netdev, | |
208 | struct ethtool_coalesce *ecmd) | |
209 | { | |
210 | struct enic *enic = netdev_priv(netdev); | |
7c2ce6e6 | 211 | struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; |
f13bbc2f NP |
212 | |
213 | ecmd->tx_coalesce_usecs = enic->tx_coalesce_usecs; | |
214 | ecmd->rx_coalesce_usecs = enic->rx_coalesce_usecs; | |
7c2ce6e6 SS |
215 | if (rxcoal->use_adaptive_rx_coalesce) |
216 | ecmd->use_adaptive_rx_coalesce = 1; | |
217 | ecmd->rx_coalesce_usecs_low = rxcoal->small_pkt_range_start; | |
218 | ecmd->rx_coalesce_usecs_high = rxcoal->range_end; | |
f13bbc2f NP |
219 | |
220 | return 0; | |
221 | } | |
222 | ||
223 | static int enic_set_coalesce(struct net_device *netdev, | |
224 | struct ethtool_coalesce *ecmd) | |
225 | { | |
226 | struct enic *enic = netdev_priv(netdev); | |
227 | u32 tx_coalesce_usecs; | |
228 | u32 rx_coalesce_usecs; | |
7c2ce6e6 SS |
229 | u32 rx_coalesce_usecs_low; |
230 | u32 rx_coalesce_usecs_high; | |
231 | u32 coalesce_usecs_max; | |
f13bbc2f | 232 | unsigned int i, intr; |
7c2ce6e6 | 233 | struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; |
f13bbc2f | 234 | |
7c2ce6e6 | 235 | coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev); |
f13bbc2f | 236 | tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs, |
7c2ce6e6 | 237 | coalesce_usecs_max); |
f13bbc2f | 238 | rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs, |
7c2ce6e6 SS |
239 | coalesce_usecs_max); |
240 | ||
241 | rx_coalesce_usecs_low = min_t(u32, ecmd->rx_coalesce_usecs_low, | |
242 | coalesce_usecs_max); | |
243 | rx_coalesce_usecs_high = min_t(u32, ecmd->rx_coalesce_usecs_high, | |
244 | coalesce_usecs_max); | |
f13bbc2f NP |
245 | |
246 | switch (vnic_dev_get_intr_mode(enic->vdev)) { | |
247 | case VNIC_DEV_INTR_MODE_INTX: | |
248 | if (tx_coalesce_usecs != rx_coalesce_usecs) | |
249 | return -EINVAL; | |
7c2ce6e6 SS |
250 | if (ecmd->use_adaptive_rx_coalesce || |
251 | ecmd->rx_coalesce_usecs_low || | |
252 | ecmd->rx_coalesce_usecs_high) | |
a16a3361 | 253 | return -EINVAL; |
f13bbc2f NP |
254 | |
255 | intr = enic_legacy_io_intr(); | |
256 | vnic_intr_coalescing_timer_set(&enic->intr[intr], | |
257 | tx_coalesce_usecs); | |
258 | break; | |
259 | case VNIC_DEV_INTR_MODE_MSI: | |
260 | if (tx_coalesce_usecs != rx_coalesce_usecs) | |
261 | return -EINVAL; | |
7c2ce6e6 SS |
262 | if (ecmd->use_adaptive_rx_coalesce || |
263 | ecmd->rx_coalesce_usecs_low || | |
264 | ecmd->rx_coalesce_usecs_high) | |
a16a3361 | 265 | return -EINVAL; |
f13bbc2f NP |
266 | |
267 | vnic_intr_coalescing_timer_set(&enic->intr[0], | |
268 | tx_coalesce_usecs); | |
269 | break; | |
270 | case VNIC_DEV_INTR_MODE_MSIX: | |
a16a3361 GV |
271 | if (ecmd->rx_coalesce_usecs_high && |
272 | (rx_coalesce_usecs_high < | |
273 | rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF)) | |
274 | return -EINVAL; | |
275 | ||
f13bbc2f NP |
276 | for (i = 0; i < enic->wq_count; i++) { |
277 | intr = enic_msix_wq_intr(enic, i); | |
278 | vnic_intr_coalescing_timer_set(&enic->intr[intr], | |
279 | tx_coalesce_usecs); | |
280 | } | |
281 | ||
a16a3361 GV |
282 | rxcoal->use_adaptive_rx_coalesce = |
283 | !!ecmd->use_adaptive_rx_coalesce; | |
284 | if (!rxcoal->use_adaptive_rx_coalesce) | |
285 | enic_intr_coal_set_rx(enic, rx_coalesce_usecs); | |
f13bbc2f | 286 | |
7c2ce6e6 | 287 | if (ecmd->rx_coalesce_usecs_high) { |
7c2ce6e6 SS |
288 | rxcoal->range_end = rx_coalesce_usecs_high; |
289 | rxcoal->small_pkt_range_start = rx_coalesce_usecs_low; | |
290 | rxcoal->large_pkt_range_start = rx_coalesce_usecs_low + | |
291 | ENIC_AIC_LARGE_PKT_DIFF; | |
292 | } | |
f13bbc2f NP |
293 | break; |
294 | default: | |
295 | break; | |
296 | } | |
297 | ||
298 | enic->tx_coalesce_usecs = tx_coalesce_usecs; | |
299 | enic->rx_coalesce_usecs = rx_coalesce_usecs; | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
3762ff8f GV |
304 | static int enic_grxclsrlall(struct enic *enic, struct ethtool_rxnfc *cmd, |
305 | u32 *rule_locs) | |
306 | { | |
307 | int j, ret = 0, cnt = 0; | |
308 | ||
309 | cmd->data = enic->rfs_h.max - enic->rfs_h.free; | |
310 | for (j = 0; j < (1 << ENIC_RFS_FLW_BITSHIFT); j++) { | |
311 | struct hlist_head *hhead; | |
312 | struct hlist_node *tmp; | |
313 | struct enic_rfs_fltr_node *n; | |
314 | ||
315 | hhead = &enic->rfs_h.ht_head[j]; | |
316 | hlist_for_each_entry_safe(n, tmp, hhead, node) { | |
317 | if (cnt == cmd->rule_cnt) | |
318 | return -EMSGSIZE; | |
319 | rule_locs[cnt] = n->fltr_id; | |
320 | cnt++; | |
321 | } | |
322 | } | |
323 | cmd->rule_cnt = cnt; | |
324 | ||
325 | return ret; | |
326 | } | |
327 | ||
328 | static int enic_grxclsrule(struct enic *enic, struct ethtool_rxnfc *cmd) | |
329 | { | |
330 | struct ethtool_rx_flow_spec *fsp = | |
331 | (struct ethtool_rx_flow_spec *)&cmd->fs; | |
332 | struct enic_rfs_fltr_node *n; | |
333 | ||
334 | n = htbl_fltr_search(enic, (u16)fsp->location); | |
335 | if (!n) | |
336 | return -EINVAL; | |
06635a35 | 337 | switch (n->keys.basic.ip_proto) { |
3762ff8f GV |
338 | case IPPROTO_TCP: |
339 | fsp->flow_type = TCP_V4_FLOW; | |
340 | break; | |
341 | case IPPROTO_UDP: | |
342 | fsp->flow_type = UDP_V4_FLOW; | |
343 | break; | |
344 | default: | |
345 | return -EINVAL; | |
346 | break; | |
347 | } | |
348 | ||
c3f83241 | 349 | fsp->h_u.tcp_ip4_spec.ip4src = flow_get_u32_src(&n->keys); |
3762ff8f GV |
350 | fsp->m_u.tcp_ip4_spec.ip4src = (__u32)~0; |
351 | ||
c3f83241 | 352 | fsp->h_u.tcp_ip4_spec.ip4dst = flow_get_u32_dst(&n->keys); |
3762ff8f GV |
353 | fsp->m_u.tcp_ip4_spec.ip4dst = (__u32)~0; |
354 | ||
59346afe | 355 | fsp->h_u.tcp_ip4_spec.psrc = n->keys.ports.src; |
3762ff8f GV |
356 | fsp->m_u.tcp_ip4_spec.psrc = (__u16)~0; |
357 | ||
59346afe | 358 | fsp->h_u.tcp_ip4_spec.pdst = n->keys.ports.dst; |
3762ff8f GV |
359 | fsp->m_u.tcp_ip4_spec.pdst = (__u16)~0; |
360 | ||
361 | fsp->ring_cookie = n->rq_id; | |
362 | ||
363 | return 0; | |
364 | } | |
365 | ||
366 | static int enic_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, | |
367 | u32 *rule_locs) | |
368 | { | |
369 | struct enic *enic = netdev_priv(dev); | |
370 | int ret = 0; | |
371 | ||
372 | switch (cmd->cmd) { | |
373 | case ETHTOOL_GRXRINGS: | |
374 | cmd->data = enic->rq_count; | |
375 | break; | |
376 | case ETHTOOL_GRXCLSRLCNT: | |
377 | spin_lock_bh(&enic->rfs_h.lock); | |
378 | cmd->rule_cnt = enic->rfs_h.max - enic->rfs_h.free; | |
379 | cmd->data = enic->rfs_h.max; | |
380 | spin_unlock_bh(&enic->rfs_h.lock); | |
381 | break; | |
382 | case ETHTOOL_GRXCLSRLALL: | |
383 | spin_lock_bh(&enic->rfs_h.lock); | |
384 | ret = enic_grxclsrlall(enic, cmd, rule_locs); | |
385 | spin_unlock_bh(&enic->rfs_h.lock); | |
386 | break; | |
387 | case ETHTOOL_GRXCLSRULE: | |
388 | spin_lock_bh(&enic->rfs_h.lock); | |
389 | ret = enic_grxclsrule(enic, cmd); | |
390 | spin_unlock_bh(&enic->rfs_h.lock); | |
391 | break; | |
392 | default: | |
393 | ret = -EOPNOTSUPP; | |
394 | break; | |
395 | } | |
396 | ||
397 | return ret; | |
398 | } | |
399 | ||
d4ad30b1 GV |
400 | static int enic_get_tunable(struct net_device *dev, |
401 | const struct ethtool_tunable *tuna, void *data) | |
402 | { | |
403 | struct enic *enic = netdev_priv(dev); | |
404 | int ret = 0; | |
405 | ||
406 | switch (tuna->id) { | |
407 | case ETHTOOL_RX_COPYBREAK: | |
408 | *(u32 *)data = enic->rx_copybreak; | |
409 | break; | |
410 | default: | |
411 | ret = -EINVAL; | |
412 | break; | |
413 | } | |
414 | ||
415 | return ret; | |
416 | } | |
417 | ||
418 | static int enic_set_tunable(struct net_device *dev, | |
419 | const struct ethtool_tunable *tuna, | |
420 | const void *data) | |
421 | { | |
422 | struct enic *enic = netdev_priv(dev); | |
423 | int ret = 0; | |
424 | ||
425 | switch (tuna->id) { | |
426 | case ETHTOOL_RX_COPYBREAK: | |
427 | enic->rx_copybreak = *(u32 *)data; | |
428 | break; | |
429 | default: | |
430 | ret = -EINVAL; | |
431 | break; | |
432 | } | |
433 | ||
434 | return ret; | |
435 | } | |
436 | ||
4f675eb2 GV |
437 | static u32 enic_get_rxfh_key_size(struct net_device *netdev) |
438 | { | |
439 | return ENIC_RSS_LEN; | |
440 | } | |
441 | ||
442 | static int enic_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey, | |
443 | u8 *hfunc) | |
444 | { | |
445 | struct enic *enic = netdev_priv(netdev); | |
446 | ||
447 | if (hkey) | |
448 | memcpy(hkey, enic->rss_key, ENIC_RSS_LEN); | |
449 | ||
450 | if (hfunc) | |
451 | *hfunc = ETH_RSS_HASH_TOP; | |
452 | ||
453 | return 0; | |
454 | } | |
455 | ||
456 | static int enic_set_rxfh(struct net_device *netdev, const u32 *indir, | |
457 | const u8 *hkey, const u8 hfunc) | |
458 | { | |
459 | struct enic *enic = netdev_priv(netdev); | |
460 | ||
461 | if ((hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) || | |
462 | indir) | |
463 | return -EINVAL; | |
464 | ||
465 | if (hkey) | |
466 | memcpy(enic->rss_key, hkey, ENIC_RSS_LEN); | |
467 | ||
468 | return __enic_set_rsskey(enic); | |
469 | } | |
470 | ||
f13bbc2f NP |
471 | static const struct ethtool_ops enic_ethtool_ops = { |
472 | .get_settings = enic_get_settings, | |
473 | .get_drvinfo = enic_get_drvinfo, | |
474 | .get_msglevel = enic_get_msglevel, | |
475 | .set_msglevel = enic_set_msglevel, | |
476 | .get_link = ethtool_op_get_link, | |
477 | .get_strings = enic_get_strings, | |
478 | .get_sset_count = enic_get_sset_count, | |
479 | .get_ethtool_stats = enic_get_ethtool_stats, | |
480 | .get_coalesce = enic_get_coalesce, | |
481 | .set_coalesce = enic_set_coalesce, | |
3762ff8f | 482 | .get_rxnfc = enic_get_rxnfc, |
d4ad30b1 GV |
483 | .get_tunable = enic_get_tunable, |
484 | .set_tunable = enic_set_tunable, | |
4f675eb2 GV |
485 | .get_rxfh_key_size = enic_get_rxfh_key_size, |
486 | .get_rxfh = enic_get_rxfh, | |
487 | .set_rxfh = enic_set_rxfh, | |
f13bbc2f NP |
488 | }; |
489 | ||
490 | void enic_set_ethtool_ops(struct net_device *netdev) | |
491 | { | |
7ad24ea4 | 492 | netdev->ethtool_ops = &enic_ethtool_ops; |
f13bbc2f | 493 | } |