diff options
Diffstat (limited to 'drivers/net/sfc/ethtool.c')
-rw-r--r-- | drivers/net/sfc/ethtool.c | 259 |
1 files changed, 258 insertions, 1 deletions
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index ad541badbd98..e2c75d101610 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c | |||
@@ -12,12 +12,26 @@ | |||
12 | #include <linux/ethtool.h> | 12 | #include <linux/ethtool.h> |
13 | #include <linux/rtnetlink.h> | 13 | #include <linux/rtnetlink.h> |
14 | #include "net_driver.h" | 14 | #include "net_driver.h" |
15 | #include "selftest.h" | ||
15 | #include "efx.h" | 16 | #include "efx.h" |
16 | #include "ethtool.h" | 17 | #include "ethtool.h" |
17 | #include "falcon.h" | 18 | #include "falcon.h" |
18 | #include "gmii.h" | 19 | #include "gmii.h" |
19 | #include "mac.h" | 20 | #include "mac.h" |
20 | 21 | ||
22 | const char *efx_loopback_mode_names[] = { | ||
23 | [LOOPBACK_NONE] = "NONE", | ||
24 | [LOOPBACK_MAC] = "MAC", | ||
25 | [LOOPBACK_XGMII] = "XGMII", | ||
26 | [LOOPBACK_XGXS] = "XGXS", | ||
27 | [LOOPBACK_XAUI] = "XAUI", | ||
28 | [LOOPBACK_PHY] = "PHY", | ||
29 | [LOOPBACK_PHYXS] = "PHY(XS)", | ||
30 | [LOOPBACK_PCS] = "PHY(PCS)", | ||
31 | [LOOPBACK_PMAPMD] = "PHY(PMAPMD)", | ||
32 | [LOOPBACK_NETWORK] = "NETWORK", | ||
33 | }; | ||
34 | |||
21 | static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable); | 35 | static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable); |
22 | 36 | ||
23 | struct ethtool_string { | 37 | struct ethtool_string { |
@@ -217,23 +231,179 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev, | |||
217 | strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); | 231 | strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); |
218 | } | 232 | } |
219 | 233 | ||
234 | /** | ||
235 | * efx_fill_test - fill in an individual self-test entry | ||
236 | * @test_index: Index of the test | ||
237 | * @strings: Ethtool strings, or %NULL | ||
238 | * @data: Ethtool test results, or %NULL | ||
239 | * @test: Pointer to test result (used only if data != %NULL) | ||
240 | * @unit_format: Unit name format (e.g. "channel\%d") | ||
241 | * @unit_id: Unit id (e.g. 0 for "channel0") | ||
242 | * @test_format: Test name format (e.g. "loopback.\%s.tx.sent") | ||
243 | * @test_id: Test id (e.g. "PHY" for "loopback.PHY.tx_sent") | ||
244 | * | ||
245 | * Fill in an individual self-test entry. | ||
246 | */ | ||
247 | static void efx_fill_test(unsigned int test_index, | ||
248 | struct ethtool_string *strings, u64 *data, | ||
249 | int *test, const char *unit_format, int unit_id, | ||
250 | const char *test_format, const char *test_id) | ||
251 | { | ||
252 | struct ethtool_string unit_str, test_str; | ||
253 | |||
254 | /* Fill data value, if applicable */ | ||
255 | if (data) | ||
256 | data[test_index] = *test; | ||
257 | |||
258 | /* Fill string, if applicable */ | ||
259 | if (strings) { | ||
260 | snprintf(unit_str.name, sizeof(unit_str.name), | ||
261 | unit_format, unit_id); | ||
262 | snprintf(test_str.name, sizeof(test_str.name), | ||
263 | test_format, test_id); | ||
264 | snprintf(strings[test_index].name, | ||
265 | sizeof(strings[test_index].name), | ||
266 | "%-9s%-17s", unit_str.name, test_str.name); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | #define EFX_PORT_NAME "port%d", 0 | ||
271 | #define EFX_CHANNEL_NAME(_channel) "channel%d", _channel->channel | ||
272 | #define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue | ||
273 | #define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue | ||
274 | #define EFX_LOOPBACK_NAME(_mode, _counter) \ | ||
275 | "loopback.%s." _counter, LOOPBACK_MODE_NAME(mode) | ||
276 | |||
277 | /** | ||
278 | * efx_fill_loopback_test - fill in a block of loopback self-test entries | ||
279 | * @efx: Efx NIC | ||
280 | * @lb_tests: Efx loopback self-test results structure | ||
281 | * @mode: Loopback test mode | ||
282 | * @test_index: Starting index of the test | ||
283 | * @strings: Ethtool strings, or %NULL | ||
284 | * @data: Ethtool test results, or %NULL | ||
285 | */ | ||
286 | static int efx_fill_loopback_test(struct efx_nic *efx, | ||
287 | struct efx_loopback_self_tests *lb_tests, | ||
288 | enum efx_loopback_mode mode, | ||
289 | unsigned int test_index, | ||
290 | struct ethtool_string *strings, u64 *data) | ||
291 | { | ||
292 | struct efx_tx_queue *tx_queue; | ||
293 | |||
294 | efx_for_each_tx_queue(tx_queue, efx) { | ||
295 | efx_fill_test(test_index++, strings, data, | ||
296 | &lb_tests->tx_sent[tx_queue->queue], | ||
297 | EFX_TX_QUEUE_NAME(tx_queue), | ||
298 | EFX_LOOPBACK_NAME(mode, "tx_sent")); | ||
299 | efx_fill_test(test_index++, strings, data, | ||
300 | &lb_tests->tx_done[tx_queue->queue], | ||
301 | EFX_TX_QUEUE_NAME(tx_queue), | ||
302 | EFX_LOOPBACK_NAME(mode, "tx_done")); | ||
303 | } | ||
304 | efx_fill_test(test_index++, strings, data, | ||
305 | &lb_tests->rx_good, | ||
306 | EFX_PORT_NAME, | ||
307 | EFX_LOOPBACK_NAME(mode, "rx_good")); | ||
308 | efx_fill_test(test_index++, strings, data, | ||
309 | &lb_tests->rx_bad, | ||
310 | EFX_PORT_NAME, | ||
311 | EFX_LOOPBACK_NAME(mode, "rx_bad")); | ||
312 | |||
313 | return test_index; | ||
314 | } | ||
315 | |||
316 | /** | ||
317 | * efx_ethtool_fill_self_tests - get self-test details | ||
318 | * @efx: Efx NIC | ||
319 | * @tests: Efx self-test results structure, or %NULL | ||
320 | * @strings: Ethtool strings, or %NULL | ||
321 | * @data: Ethtool test results, or %NULL | ||
322 | */ | ||
323 | static int efx_ethtool_fill_self_tests(struct efx_nic *efx, | ||
324 | struct efx_self_tests *tests, | ||
325 | struct ethtool_string *strings, | ||
326 | u64 *data) | ||
327 | { | ||
328 | struct efx_channel *channel; | ||
329 | unsigned int n = 0; | ||
330 | enum efx_loopback_mode mode; | ||
331 | |||
332 | /* Interrupt */ | ||
333 | efx_fill_test(n++, strings, data, &tests->interrupt, | ||
334 | "core", 0, "interrupt", NULL); | ||
335 | |||
336 | /* Event queues */ | ||
337 | efx_for_each_channel(channel, efx) { | ||
338 | efx_fill_test(n++, strings, data, | ||
339 | &tests->eventq_dma[channel->channel], | ||
340 | EFX_CHANNEL_NAME(channel), | ||
341 | "eventq.dma", NULL); | ||
342 | efx_fill_test(n++, strings, data, | ||
343 | &tests->eventq_int[channel->channel], | ||
344 | EFX_CHANNEL_NAME(channel), | ||
345 | "eventq.int", NULL); | ||
346 | efx_fill_test(n++, strings, data, | ||
347 | &tests->eventq_poll[channel->channel], | ||
348 | EFX_CHANNEL_NAME(channel), | ||
349 | "eventq.poll", NULL); | ||
350 | } | ||
351 | |||
352 | /* PHY presence */ | ||
353 | efx_fill_test(n++, strings, data, &tests->phy_ok, | ||
354 | EFX_PORT_NAME, "phy_ok", NULL); | ||
355 | |||
356 | /* Loopback tests */ | ||
357 | efx_fill_test(n++, strings, data, &tests->loopback_speed, | ||
358 | EFX_PORT_NAME, "loopback.speed", NULL); | ||
359 | efx_fill_test(n++, strings, data, &tests->loopback_full_duplex, | ||
360 | EFX_PORT_NAME, "loopback.full_duplex", NULL); | ||
361 | for (mode = LOOPBACK_NONE; mode < LOOPBACK_TEST_MAX; mode++) { | ||
362 | if (!(efx->loopback_modes & (1 << mode))) | ||
363 | continue; | ||
364 | n = efx_fill_loopback_test(efx, | ||
365 | &tests->loopback[mode], mode, n, | ||
366 | strings, data); | ||
367 | } | ||
368 | |||
369 | return n; | ||
370 | } | ||
371 | |||
220 | static int efx_ethtool_get_stats_count(struct net_device *net_dev) | 372 | static int efx_ethtool_get_stats_count(struct net_device *net_dev) |
221 | { | 373 | { |
222 | return EFX_ETHTOOL_NUM_STATS; | 374 | return EFX_ETHTOOL_NUM_STATS; |
223 | } | 375 | } |
224 | 376 | ||
377 | static int efx_ethtool_self_test_count(struct net_device *net_dev) | ||
378 | { | ||
379 | struct efx_nic *efx = net_dev->priv; | ||
380 | |||
381 | return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); | ||
382 | } | ||
383 | |||
225 | static void efx_ethtool_get_strings(struct net_device *net_dev, | 384 | static void efx_ethtool_get_strings(struct net_device *net_dev, |
226 | u32 string_set, u8 *strings) | 385 | u32 string_set, u8 *strings) |
227 | { | 386 | { |
387 | struct efx_nic *efx = net_dev->priv; | ||
228 | struct ethtool_string *ethtool_strings = | 388 | struct ethtool_string *ethtool_strings = |
229 | (struct ethtool_string *)strings; | 389 | (struct ethtool_string *)strings; |
230 | int i; | 390 | int i; |
231 | 391 | ||
232 | if (string_set == ETH_SS_STATS) | 392 | switch (string_set) { |
393 | case ETH_SS_STATS: | ||
233 | for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) | 394 | for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) |
234 | strncpy(ethtool_strings[i].name, | 395 | strncpy(ethtool_strings[i].name, |
235 | efx_ethtool_stats[i].name, | 396 | efx_ethtool_stats[i].name, |
236 | sizeof(ethtool_strings[i].name)); | 397 | sizeof(ethtool_strings[i].name)); |
398 | break; | ||
399 | case ETH_SS_TEST: | ||
400 | efx_ethtool_fill_self_tests(efx, NULL, | ||
401 | ethtool_strings, NULL); | ||
402 | break; | ||
403 | default: | ||
404 | /* No other string sets */ | ||
405 | break; | ||
406 | } | ||
237 | } | 407 | } |
238 | 408 | ||
239 | static void efx_ethtool_get_stats(struct net_device *net_dev, | 409 | static void efx_ethtool_get_stats(struct net_device *net_dev, |
@@ -272,6 +442,22 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, | |||
272 | } | 442 | } |
273 | } | 443 | } |
274 | 444 | ||
445 | static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable) | ||
446 | { | ||
447 | int rc; | ||
448 | |||
449 | /* Our TSO requires TX checksumming, so force TX checksumming | ||
450 | * on when TSO is enabled. | ||
451 | */ | ||
452 | if (enable) { | ||
453 | rc = efx_ethtool_set_tx_csum(net_dev, 1); | ||
454 | if (rc) | ||
455 | return rc; | ||
456 | } | ||
457 | |||
458 | return ethtool_op_set_tso(net_dev, enable); | ||
459 | } | ||
460 | |||
275 | static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) | 461 | static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) |
276 | { | 462 | { |
277 | struct efx_nic *efx = net_dev->priv; | 463 | struct efx_nic *efx = net_dev->priv; |
@@ -283,6 +469,15 @@ static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) | |||
283 | 469 | ||
284 | efx_flush_queues(efx); | 470 | efx_flush_queues(efx); |
285 | 471 | ||
472 | /* Our TSO requires TX checksumming, so disable TSO when | ||
473 | * checksumming is disabled | ||
474 | */ | ||
475 | if (!enable) { | ||
476 | rc = efx_ethtool_set_tso(net_dev, 0); | ||
477 | if (rc) | ||
478 | return rc; | ||
479 | } | ||
480 | |||
286 | return 0; | 481 | return 0; |
287 | } | 482 | } |
288 | 483 | ||
@@ -305,6 +500,64 @@ static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev) | |||
305 | return efx->rx_checksum_enabled; | 500 | return efx->rx_checksum_enabled; |
306 | } | 501 | } |
307 | 502 | ||
503 | static void efx_ethtool_self_test(struct net_device *net_dev, | ||
504 | struct ethtool_test *test, u64 *data) | ||
505 | { | ||
506 | struct efx_nic *efx = net_dev->priv; | ||
507 | struct efx_self_tests efx_tests; | ||
508 | int offline, already_up; | ||
509 | int rc; | ||
510 | |||
511 | ASSERT_RTNL(); | ||
512 | if (efx->state != STATE_RUNNING) { | ||
513 | rc = -EIO; | ||
514 | goto fail1; | ||
515 | } | ||
516 | |||
517 | /* We need rx buffers and interrupts. */ | ||
518 | already_up = (efx->net_dev->flags & IFF_UP); | ||
519 | if (!already_up) { | ||
520 | rc = dev_open(efx->net_dev); | ||
521 | if (rc) { | ||
522 | EFX_ERR(efx, "failed opening device.\n"); | ||
523 | goto fail2; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | memset(&efx_tests, 0, sizeof(efx_tests)); | ||
528 | offline = (test->flags & ETH_TEST_FL_OFFLINE); | ||
529 | |||
530 | /* Perform online self tests first */ | ||
531 | rc = efx_online_test(efx, &efx_tests); | ||
532 | if (rc) | ||
533 | goto out; | ||
534 | |||
535 | /* Perform offline tests only if online tests passed */ | ||
536 | if (offline) { | ||
537 | /* Stop the kernel from sending packets during the test. */ | ||
538 | efx_stop_queue(efx); | ||
539 | rc = efx_flush_queues(efx); | ||
540 | if (!rc) | ||
541 | rc = efx_offline_test(efx, &efx_tests, | ||
542 | efx->loopback_modes); | ||
543 | efx_wake_queue(efx); | ||
544 | } | ||
545 | |||
546 | out: | ||
547 | if (!already_up) | ||
548 | dev_close(efx->net_dev); | ||
549 | |||
550 | EFX_LOG(efx, "%s all %sline self-tests\n", | ||
551 | rc == 0 ? "passed" : "failed", offline ? "off" : "on"); | ||
552 | |||
553 | fail2: | ||
554 | fail1: | ||
555 | /* Fill ethtool results structures */ | ||
556 | efx_ethtool_fill_self_tests(efx, &efx_tests, NULL, data); | ||
557 | if (rc) | ||
558 | test->flags |= ETH_TEST_FL_FAILED; | ||
559 | } | ||
560 | |||
308 | /* Restart autonegotiation */ | 561 | /* Restart autonegotiation */ |
309 | static int efx_ethtool_nway_reset(struct net_device *net_dev) | 562 | static int efx_ethtool_nway_reset(struct net_device *net_dev) |
310 | { | 563 | { |
@@ -451,8 +704,12 @@ struct ethtool_ops efx_ethtool_ops = { | |||
451 | .set_tx_csum = efx_ethtool_set_tx_csum, | 704 | .set_tx_csum = efx_ethtool_set_tx_csum, |
452 | .get_sg = ethtool_op_get_sg, | 705 | .get_sg = ethtool_op_get_sg, |
453 | .set_sg = ethtool_op_set_sg, | 706 | .set_sg = ethtool_op_set_sg, |
707 | .get_tso = ethtool_op_get_tso, | ||
708 | .set_tso = efx_ethtool_set_tso, | ||
454 | .get_flags = ethtool_op_get_flags, | 709 | .get_flags = ethtool_op_get_flags, |
455 | .set_flags = ethtool_op_set_flags, | 710 | .set_flags = ethtool_op_set_flags, |
711 | .self_test_count = efx_ethtool_self_test_count, | ||
712 | .self_test = efx_ethtool_self_test, | ||
456 | .get_strings = efx_ethtool_get_strings, | 713 | .get_strings = efx_ethtool_get_strings, |
457 | .phys_id = efx_ethtool_phys_id, | 714 | .phys_id = efx_ethtool_phys_id, |
458 | .get_stats_count = efx_ethtool_get_stats_count, | 715 | .get_stats_count = efx_ethtool_get_stats_count, |