1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2009 - 2018 Intel Corporation. */ |
3 | |
4 | /* ethtool support for igbvf */ |
5 | |
6 | #include <linux/netdevice.h> |
7 | #include <linux/ethtool.h> |
8 | #include <linux/pci.h> |
9 | #include <linux/vmalloc.h> |
10 | #include <linux/delay.h> |
11 | |
12 | #include "igbvf.h" |
13 | #include <linux/if_vlan.h> |
14 | |
15 | struct igbvf_stats { |
16 | char stat_string[ETH_GSTRING_LEN]; |
17 | int sizeof_stat; |
18 | int stat_offset; |
19 | int base_stat_offset; |
20 | }; |
21 | |
22 | #define IGBVF_STAT(current, base) \ |
23 | sizeof(((struct igbvf_adapter *)0)->current), \ |
24 | offsetof(struct igbvf_adapter, current), \ |
25 | offsetof(struct igbvf_adapter, base) |
26 | |
27 | static const struct igbvf_stats igbvf_gstrings_stats[] = { |
28 | { "rx_packets" , IGBVF_STAT(stats.gprc, stats.base_gprc) }, |
29 | { "tx_packets" , IGBVF_STAT(stats.gptc, stats.base_gptc) }, |
30 | { "rx_bytes" , IGBVF_STAT(stats.gorc, stats.base_gorc) }, |
31 | { "tx_bytes" , IGBVF_STAT(stats.gotc, stats.base_gotc) }, |
32 | { "multicast" , IGBVF_STAT(stats.mprc, stats.base_mprc) }, |
33 | { "lbrx_bytes" , IGBVF_STAT(stats.gorlbc, stats.base_gorlbc) }, |
34 | { "lbrx_packets" , IGBVF_STAT(stats.gprlbc, stats.base_gprlbc) }, |
35 | { "tx_restart_queue" , IGBVF_STAT(restart_queue, zero_base) }, |
36 | { "rx_long_byte_count" , IGBVF_STAT(stats.gorc, stats.base_gorc) }, |
37 | { "rx_csum_offload_good" , IGBVF_STAT(hw_csum_good, zero_base) }, |
38 | { "rx_csum_offload_errors" , IGBVF_STAT(hw_csum_err, zero_base) }, |
39 | { "rx_header_split" , IGBVF_STAT(rx_hdr_split, zero_base) }, |
40 | { "alloc_rx_buff_failed" , IGBVF_STAT(alloc_rx_buff_failed, zero_base) }, |
41 | }; |
42 | |
43 | #define IGBVF_GLOBAL_STATS_LEN ARRAY_SIZE(igbvf_gstrings_stats) |
44 | |
45 | static const char igbvf_gstrings_test[][ETH_GSTRING_LEN] = { |
46 | "Link test (on/offline)" |
47 | }; |
48 | |
49 | #define IGBVF_TEST_LEN ARRAY_SIZE(igbvf_gstrings_test) |
50 | |
51 | static int igbvf_get_link_ksettings(struct net_device *netdev, |
52 | struct ethtool_link_ksettings *cmd) |
53 | { |
54 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
55 | struct e1000_hw *hw = &adapter->hw; |
56 | u32 status; |
57 | |
58 | ethtool_link_ksettings_zero_link_mode(cmd, supported); |
59 | ethtool_link_ksettings_add_link_mode(cmd, supported, 1000baseT_Full); |
60 | ethtool_link_ksettings_zero_link_mode(cmd, advertising); |
61 | ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full); |
62 | |
63 | cmd->base.port = -1; |
64 | |
65 | status = er32(STATUS); |
66 | if (status & E1000_STATUS_LU) { |
67 | if (status & E1000_STATUS_SPEED_1000) |
68 | cmd->base.speed = SPEED_1000; |
69 | else if (status & E1000_STATUS_SPEED_100) |
70 | cmd->base.speed = SPEED_100; |
71 | else |
72 | cmd->base.speed = SPEED_10; |
73 | |
74 | if (status & E1000_STATUS_FD) |
75 | cmd->base.duplex = DUPLEX_FULL; |
76 | else |
77 | cmd->base.duplex = DUPLEX_HALF; |
78 | } else { |
79 | cmd->base.speed = SPEED_UNKNOWN; |
80 | cmd->base.duplex = DUPLEX_UNKNOWN; |
81 | } |
82 | |
83 | cmd->base.autoneg = AUTONEG_DISABLE; |
84 | |
85 | return 0; |
86 | } |
87 | |
88 | static int igbvf_set_link_ksettings(struct net_device *netdev, |
89 | const struct ethtool_link_ksettings *cmd) |
90 | { |
91 | return -EOPNOTSUPP; |
92 | } |
93 | |
94 | static void igbvf_get_pauseparam(struct net_device *netdev, |
95 | struct ethtool_pauseparam *pause) |
96 | { |
97 | } |
98 | |
99 | static int igbvf_set_pauseparam(struct net_device *netdev, |
100 | struct ethtool_pauseparam *pause) |
101 | { |
102 | return -EOPNOTSUPP; |
103 | } |
104 | |
105 | static u32 igbvf_get_msglevel(struct net_device *netdev) |
106 | { |
107 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
108 | |
109 | return adapter->msg_enable; |
110 | } |
111 | |
112 | static void igbvf_set_msglevel(struct net_device *netdev, u32 data) |
113 | { |
114 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
115 | |
116 | adapter->msg_enable = data; |
117 | } |
118 | |
119 | static int igbvf_get_regs_len(struct net_device *netdev) |
120 | { |
121 | #define IGBVF_REGS_LEN 8 |
122 | return IGBVF_REGS_LEN * sizeof(u32); |
123 | } |
124 | |
125 | static void igbvf_get_regs(struct net_device *netdev, |
126 | struct ethtool_regs *regs, void *p) |
127 | { |
128 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
129 | struct e1000_hw *hw = &adapter->hw; |
130 | u32 *regs_buff = p; |
131 | |
132 | memset(p, 0, IGBVF_REGS_LEN * sizeof(u32)); |
133 | |
134 | regs->version = (1u << 24) | |
135 | (adapter->pdev->revision << 16) | |
136 | adapter->pdev->device; |
137 | |
138 | regs_buff[0] = er32(CTRL); |
139 | regs_buff[1] = er32(STATUS); |
140 | |
141 | regs_buff[2] = er32(RDLEN(0)); |
142 | regs_buff[3] = er32(RDH(0)); |
143 | regs_buff[4] = er32(RDT(0)); |
144 | |
145 | regs_buff[5] = er32(TDLEN(0)); |
146 | regs_buff[6] = er32(TDH(0)); |
147 | regs_buff[7] = er32(TDT(0)); |
148 | } |
149 | |
150 | static int igbvf_get_eeprom_len(struct net_device *netdev) |
151 | { |
152 | return 0; |
153 | } |
154 | |
155 | static int igbvf_get_eeprom(struct net_device *netdev, |
156 | struct ethtool_eeprom *eeprom, u8 *bytes) |
157 | { |
158 | return -EOPNOTSUPP; |
159 | } |
160 | |
161 | static int igbvf_set_eeprom(struct net_device *netdev, |
162 | struct ethtool_eeprom *eeprom, u8 *bytes) |
163 | { |
164 | return -EOPNOTSUPP; |
165 | } |
166 | |
167 | static void igbvf_get_drvinfo(struct net_device *netdev, |
168 | struct ethtool_drvinfo *drvinfo) |
169 | { |
170 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
171 | |
172 | strscpy(p: drvinfo->driver, q: igbvf_driver_name, size: sizeof(drvinfo->driver)); |
173 | strscpy(p: drvinfo->bus_info, q: pci_name(pdev: adapter->pdev), |
174 | size: sizeof(drvinfo->bus_info)); |
175 | } |
176 | |
177 | static void igbvf_get_ringparam(struct net_device *netdev, |
178 | struct ethtool_ringparam *ring, |
179 | struct kernel_ethtool_ringparam *kernel_ring, |
180 | struct netlink_ext_ack *extack) |
181 | { |
182 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
183 | struct igbvf_ring *tx_ring = adapter->tx_ring; |
184 | struct igbvf_ring *rx_ring = adapter->rx_ring; |
185 | |
186 | ring->rx_max_pending = IGBVF_MAX_RXD; |
187 | ring->tx_max_pending = IGBVF_MAX_TXD; |
188 | ring->rx_pending = rx_ring->count; |
189 | ring->tx_pending = tx_ring->count; |
190 | } |
191 | |
192 | static int igbvf_set_ringparam(struct net_device *netdev, |
193 | struct ethtool_ringparam *ring, |
194 | struct kernel_ethtool_ringparam *kernel_ring, |
195 | struct netlink_ext_ack *extack) |
196 | { |
197 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
198 | struct igbvf_ring *temp_ring; |
199 | int err = 0; |
200 | u32 new_rx_count, new_tx_count; |
201 | |
202 | if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) |
203 | return -EINVAL; |
204 | |
205 | new_rx_count = max_t(u32, ring->rx_pending, IGBVF_MIN_RXD); |
206 | new_rx_count = min_t(u32, new_rx_count, IGBVF_MAX_RXD); |
207 | new_rx_count = ALIGN(new_rx_count, REQ_RX_DESCRIPTOR_MULTIPLE); |
208 | |
209 | new_tx_count = max_t(u32, ring->tx_pending, IGBVF_MIN_TXD); |
210 | new_tx_count = min_t(u32, new_tx_count, IGBVF_MAX_TXD); |
211 | new_tx_count = ALIGN(new_tx_count, REQ_TX_DESCRIPTOR_MULTIPLE); |
212 | |
213 | if ((new_tx_count == adapter->tx_ring->count) && |
214 | (new_rx_count == adapter->rx_ring->count)) { |
215 | /* nothing to do */ |
216 | return 0; |
217 | } |
218 | |
219 | while (test_and_set_bit(nr: __IGBVF_RESETTING, addr: &adapter->state)) |
220 | usleep_range(min: 1000, max: 2000); |
221 | |
222 | if (!netif_running(dev: adapter->netdev)) { |
223 | adapter->tx_ring->count = new_tx_count; |
224 | adapter->rx_ring->count = new_rx_count; |
225 | goto clear_reset; |
226 | } |
227 | |
228 | temp_ring = vmalloc(size: sizeof(struct igbvf_ring)); |
229 | if (!temp_ring) { |
230 | err = -ENOMEM; |
231 | goto clear_reset; |
232 | } |
233 | |
234 | igbvf_down(adapter); |
235 | |
236 | /* We can't just free everything and then setup again, |
237 | * because the ISRs in MSI-X mode get passed pointers |
238 | * to the Tx and Rx ring structs. |
239 | */ |
240 | if (new_tx_count != adapter->tx_ring->count) { |
241 | memcpy(temp_ring, adapter->tx_ring, sizeof(struct igbvf_ring)); |
242 | |
243 | temp_ring->count = new_tx_count; |
244 | err = igbvf_setup_tx_resources(adapter, temp_ring); |
245 | if (err) |
246 | goto err_setup; |
247 | |
248 | igbvf_free_tx_resources(adapter->tx_ring); |
249 | |
250 | memcpy(adapter->tx_ring, temp_ring, sizeof(struct igbvf_ring)); |
251 | } |
252 | |
253 | if (new_rx_count != adapter->rx_ring->count) { |
254 | memcpy(temp_ring, adapter->rx_ring, sizeof(struct igbvf_ring)); |
255 | |
256 | temp_ring->count = new_rx_count; |
257 | err = igbvf_setup_rx_resources(adapter, temp_ring); |
258 | if (err) |
259 | goto err_setup; |
260 | |
261 | igbvf_free_rx_resources(adapter->rx_ring); |
262 | |
263 | memcpy(adapter->rx_ring, temp_ring, sizeof(struct igbvf_ring)); |
264 | } |
265 | err_setup: |
266 | igbvf_up(adapter); |
267 | vfree(addr: temp_ring); |
268 | clear_reset: |
269 | clear_bit(nr: __IGBVF_RESETTING, addr: &adapter->state); |
270 | return err; |
271 | } |
272 | |
273 | static int igbvf_link_test(struct igbvf_adapter *adapter, u64 *data) |
274 | { |
275 | struct e1000_hw *hw = &adapter->hw; |
276 | *data = 0; |
277 | |
278 | spin_lock_bh(lock: &hw->mbx_lock); |
279 | |
280 | hw->mac.ops.check_for_link(hw); |
281 | |
282 | spin_unlock_bh(lock: &hw->mbx_lock); |
283 | |
284 | if (!(er32(STATUS) & E1000_STATUS_LU)) |
285 | *data = 1; |
286 | |
287 | return *data; |
288 | } |
289 | |
290 | static void igbvf_diag_test(struct net_device *netdev, |
291 | struct ethtool_test *eth_test, u64 *data) |
292 | { |
293 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
294 | |
295 | set_bit(nr: __IGBVF_TESTING, addr: &adapter->state); |
296 | |
297 | /* Link test performed before hardware reset so autoneg doesn't |
298 | * interfere with test result |
299 | */ |
300 | if (igbvf_link_test(adapter, data: &data[0])) |
301 | eth_test->flags |= ETH_TEST_FL_FAILED; |
302 | |
303 | clear_bit(nr: __IGBVF_TESTING, addr: &adapter->state); |
304 | msleep_interruptible(msecs: 4 * 1000); |
305 | } |
306 | |
307 | static void igbvf_get_wol(struct net_device *netdev, |
308 | struct ethtool_wolinfo *wol) |
309 | { |
310 | wol->supported = 0; |
311 | wol->wolopts = 0; |
312 | } |
313 | |
314 | static int igbvf_set_wol(struct net_device *netdev, |
315 | struct ethtool_wolinfo *wol) |
316 | { |
317 | return -EOPNOTSUPP; |
318 | } |
319 | |
320 | static int igbvf_get_coalesce(struct net_device *netdev, |
321 | struct ethtool_coalesce *ec, |
322 | struct kernel_ethtool_coalesce *kernel_coal, |
323 | struct netlink_ext_ack *extack) |
324 | { |
325 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
326 | |
327 | if (adapter->requested_itr <= 3) |
328 | ec->rx_coalesce_usecs = adapter->requested_itr; |
329 | else |
330 | ec->rx_coalesce_usecs = adapter->current_itr >> 2; |
331 | |
332 | return 0; |
333 | } |
334 | |
335 | static int igbvf_set_coalesce(struct net_device *netdev, |
336 | struct ethtool_coalesce *ec, |
337 | struct kernel_ethtool_coalesce *kernel_coal, |
338 | struct netlink_ext_ack *extack) |
339 | { |
340 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
341 | struct e1000_hw *hw = &adapter->hw; |
342 | |
343 | if ((ec->rx_coalesce_usecs >= IGBVF_MIN_ITR_USECS) && |
344 | (ec->rx_coalesce_usecs <= IGBVF_MAX_ITR_USECS)) { |
345 | adapter->current_itr = ec->rx_coalesce_usecs << 2; |
346 | adapter->requested_itr = 1000000000 / |
347 | (adapter->current_itr * 256); |
348 | } else if ((ec->rx_coalesce_usecs == 3) || |
349 | (ec->rx_coalesce_usecs == 2)) { |
350 | adapter->current_itr = IGBVF_START_ITR; |
351 | adapter->requested_itr = ec->rx_coalesce_usecs; |
352 | } else if (ec->rx_coalesce_usecs == 0) { |
353 | /* The user's desire is to turn off interrupt throttling |
354 | * altogether, but due to HW limitations, we can't do that. |
355 | * Instead we set a very small value in EITR, which would |
356 | * allow ~967k interrupts per second, but allow the adapter's |
357 | * internal clocking to still function properly. |
358 | */ |
359 | adapter->current_itr = 4; |
360 | adapter->requested_itr = 1000000000 / |
361 | (adapter->current_itr * 256); |
362 | } else { |
363 | return -EINVAL; |
364 | } |
365 | |
366 | writel(val: adapter->current_itr, |
367 | addr: hw->hw_addr + adapter->rx_ring->itr_register); |
368 | |
369 | return 0; |
370 | } |
371 | |
372 | static int igbvf_nway_reset(struct net_device *netdev) |
373 | { |
374 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
375 | |
376 | if (netif_running(dev: netdev)) |
377 | igbvf_reinit_locked(adapter); |
378 | return 0; |
379 | } |
380 | |
381 | static void igbvf_get_ethtool_stats(struct net_device *netdev, |
382 | struct ethtool_stats *stats, |
383 | u64 *data) |
384 | { |
385 | struct igbvf_adapter *adapter = netdev_priv(dev: netdev); |
386 | int i; |
387 | |
388 | igbvf_update_stats(adapter); |
389 | for (i = 0; i < IGBVF_GLOBAL_STATS_LEN; i++) { |
390 | char *p = (char *)adapter + |
391 | igbvf_gstrings_stats[i].stat_offset; |
392 | char *b = (char *)adapter + |
393 | igbvf_gstrings_stats[i].base_stat_offset; |
394 | data[i] = ((igbvf_gstrings_stats[i].sizeof_stat == |
395 | sizeof(u64)) ? (*(u64 *)p - *(u64 *)b) : |
396 | (*(u32 *)p - *(u32 *)b)); |
397 | } |
398 | } |
399 | |
400 | static int igbvf_get_sset_count(struct net_device *dev, int stringset) |
401 | { |
402 | switch (stringset) { |
403 | case ETH_SS_TEST: |
404 | return IGBVF_TEST_LEN; |
405 | case ETH_SS_STATS: |
406 | return IGBVF_GLOBAL_STATS_LEN; |
407 | default: |
408 | return -EINVAL; |
409 | } |
410 | } |
411 | |
412 | static void igbvf_get_strings(struct net_device *netdev, u32 stringset, |
413 | u8 *data) |
414 | { |
415 | u8 *p = data; |
416 | int i; |
417 | |
418 | switch (stringset) { |
419 | case ETH_SS_TEST: |
420 | memcpy(data, *igbvf_gstrings_test, sizeof(igbvf_gstrings_test)); |
421 | break; |
422 | case ETH_SS_STATS: |
423 | for (i = 0; i < IGBVF_GLOBAL_STATS_LEN; i++) { |
424 | memcpy(p, igbvf_gstrings_stats[i].stat_string, |
425 | ETH_GSTRING_LEN); |
426 | p += ETH_GSTRING_LEN; |
427 | } |
428 | break; |
429 | } |
430 | } |
431 | |
432 | static const struct ethtool_ops igbvf_ethtool_ops = { |
433 | .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS, |
434 | .get_drvinfo = igbvf_get_drvinfo, |
435 | .get_regs_len = igbvf_get_regs_len, |
436 | .get_regs = igbvf_get_regs, |
437 | .get_wol = igbvf_get_wol, |
438 | .set_wol = igbvf_set_wol, |
439 | .get_msglevel = igbvf_get_msglevel, |
440 | .set_msglevel = igbvf_set_msglevel, |
441 | .nway_reset = igbvf_nway_reset, |
442 | .get_link = ethtool_op_get_link, |
443 | .get_eeprom_len = igbvf_get_eeprom_len, |
444 | .get_eeprom = igbvf_get_eeprom, |
445 | .set_eeprom = igbvf_set_eeprom, |
446 | .get_ringparam = igbvf_get_ringparam, |
447 | .set_ringparam = igbvf_set_ringparam, |
448 | .get_pauseparam = igbvf_get_pauseparam, |
449 | .set_pauseparam = igbvf_set_pauseparam, |
450 | .self_test = igbvf_diag_test, |
451 | .get_sset_count = igbvf_get_sset_count, |
452 | .get_strings = igbvf_get_strings, |
453 | .get_ethtool_stats = igbvf_get_ethtool_stats, |
454 | .get_coalesce = igbvf_get_coalesce, |
455 | .set_coalesce = igbvf_set_coalesce, |
456 | .get_link_ksettings = igbvf_get_link_ksettings, |
457 | .set_link_ksettings = igbvf_set_link_ksettings, |
458 | }; |
459 | |
460 | void igbvf_set_ethtool_ops(struct net_device *netdev) |
461 | { |
462 | netdev->ethtool_ops = &igbvf_ethtool_ops; |
463 | } |
464 | |