diff options
author | Ron Mercer <ron.mercer@qlogic.com> | 2009-10-30 08:13:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-02 07:25:40 -0500 |
commit | 9dfbbaa6b0b9f7c4d6c9dc3a59006f44e6521138 (patch) | |
tree | d0c93bae849fc79ab01c38974b02b949f1b623ec /drivers/net/qlge | |
parent | 536b2e92f1b7a86e177aeced097e4c051eeebe7d (diff) |
qlge: Add ethtool self-test.
Signed-off-by: Ron Mercer <ron.mercer@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/qlge')
-rw-r--r-- | drivers/net/qlge/qlge.h | 6 | ||||
-rw-r--r-- | drivers/net/qlge/qlge_ethtool.c | 112 | ||||
-rw-r--r-- | drivers/net/qlge/qlge_main.c | 20 |
3 files changed, 137 insertions, 1 deletions
diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h index 73c7fd2badcd..872e95ee40ee 100644 --- a/drivers/net/qlge/qlge.h +++ b/drivers/net/qlge/qlge.h | |||
@@ -1581,6 +1581,8 @@ enum { | |||
1581 | QL_ALLMULTI = 6, | 1581 | QL_ALLMULTI = 6, |
1582 | QL_PORT_CFG = 7, | 1582 | QL_PORT_CFG = 7, |
1583 | QL_CAM_RT_SET = 8, | 1583 | QL_CAM_RT_SET = 8, |
1584 | QL_SELFTEST = 9, | ||
1585 | QL_LB_LINK_UP = 10, | ||
1584 | }; | 1586 | }; |
1585 | 1587 | ||
1586 | /* link_status bit definitions */ | 1588 | /* link_status bit definitions */ |
@@ -1717,6 +1719,7 @@ struct ql_adapter { | |||
1717 | struct completion ide_completion; | 1719 | struct completion ide_completion; |
1718 | struct nic_operations *nic_ops; | 1720 | struct nic_operations *nic_ops; |
1719 | u16 device_id; | 1721 | u16 device_id; |
1722 | atomic_t lb_count; | ||
1720 | }; | 1723 | }; |
1721 | 1724 | ||
1722 | /* | 1725 | /* |
@@ -1808,6 +1811,9 @@ int ql_mb_set_port_cfg(struct ql_adapter *qdev); | |||
1808 | int ql_wait_fifo_empty(struct ql_adapter *qdev); | 1811 | int ql_wait_fifo_empty(struct ql_adapter *qdev); |
1809 | void ql_gen_reg_dump(struct ql_adapter *qdev, | 1812 | void ql_gen_reg_dump(struct ql_adapter *qdev, |
1810 | struct ql_reg_dump *mpi_coredump); | 1813 | struct ql_reg_dump *mpi_coredump); |
1814 | netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev); | ||
1815 | void ql_check_lb_frame(struct ql_adapter *, struct sk_buff *); | ||
1816 | int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget); | ||
1811 | 1817 | ||
1812 | #if 1 | 1818 | #if 1 |
1813 | #define QL_ALL_DUMP | 1819 | #define QL_ALL_DUMP |
diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c index 62c4af057800..058fa0a48c6f 100644 --- a/drivers/net/qlge/qlge_ethtool.c +++ b/drivers/net/qlge/qlge_ethtool.c | |||
@@ -36,6 +36,11 @@ | |||
36 | 36 | ||
37 | #include "qlge.h" | 37 | #include "qlge.h" |
38 | 38 | ||
39 | static const char ql_gstrings_test[][ETH_GSTRING_LEN] = { | ||
40 | "Loopback test (offline)" | ||
41 | }; | ||
42 | #define QLGE_TEST_LEN (sizeof(ql_gstrings_test) / ETH_GSTRING_LEN) | ||
43 | |||
39 | static int ql_update_ring_coalescing(struct ql_adapter *qdev) | 44 | static int ql_update_ring_coalescing(struct ql_adapter *qdev) |
40 | { | 45 | { |
41 | int i, status = 0; | 46 | int i, status = 0; |
@@ -251,6 +256,8 @@ static void ql_get_strings(struct net_device *dev, u32 stringset, u8 *buf) | |||
251 | static int ql_get_sset_count(struct net_device *dev, int sset) | 256 | static int ql_get_sset_count(struct net_device *dev, int sset) |
252 | { | 257 | { |
253 | switch (sset) { | 258 | switch (sset) { |
259 | case ETH_SS_TEST: | ||
260 | return QLGE_TEST_LEN; | ||
254 | case ETH_SS_STATS: | 261 | case ETH_SS_STATS: |
255 | return ARRAY_SIZE(ql_stats_str_arr); | 262 | return ARRAY_SIZE(ql_stats_str_arr); |
256 | default: | 263 | default: |
@@ -429,6 +436,110 @@ static int ql_phys_id(struct net_device *ndev, u32 data) | |||
429 | return 0; | 436 | return 0; |
430 | } | 437 | } |
431 | 438 | ||
439 | static int ql_start_loopback(struct ql_adapter *qdev) | ||
440 | { | ||
441 | if (netif_carrier_ok(qdev->ndev)) { | ||
442 | set_bit(QL_LB_LINK_UP, &qdev->flags); | ||
443 | netif_carrier_off(qdev->ndev); | ||
444 | } else | ||
445 | clear_bit(QL_LB_LINK_UP, &qdev->flags); | ||
446 | qdev->link_config |= CFG_LOOPBACK_PCS; | ||
447 | return ql_mb_set_port_cfg(qdev); | ||
448 | } | ||
449 | |||
450 | static void ql_stop_loopback(struct ql_adapter *qdev) | ||
451 | { | ||
452 | qdev->link_config &= ~CFG_LOOPBACK_PCS; | ||
453 | ql_mb_set_port_cfg(qdev); | ||
454 | if (test_bit(QL_LB_LINK_UP, &qdev->flags)) { | ||
455 | netif_carrier_on(qdev->ndev); | ||
456 | clear_bit(QL_LB_LINK_UP, &qdev->flags); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | static void ql_create_lb_frame(struct sk_buff *skb, | ||
461 | unsigned int frame_size) | ||
462 | { | ||
463 | memset(skb->data, 0xFF, frame_size); | ||
464 | frame_size &= ~1; | ||
465 | memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1); | ||
466 | memset(&skb->data[frame_size / 2 + 10], 0xBE, 1); | ||
467 | memset(&skb->data[frame_size / 2 + 12], 0xAF, 1); | ||
468 | } | ||
469 | |||
470 | void ql_check_lb_frame(struct ql_adapter *qdev, | ||
471 | struct sk_buff *skb) | ||
472 | { | ||
473 | unsigned int frame_size = skb->len; | ||
474 | |||
475 | if ((*(skb->data + 3) == 0xFF) && | ||
476 | (*(skb->data + frame_size / 2 + 10) == 0xBE) && | ||
477 | (*(skb->data + frame_size / 2 + 12) == 0xAF)) { | ||
478 | atomic_dec(&qdev->lb_count); | ||
479 | return; | ||
480 | } | ||
481 | } | ||
482 | |||
483 | static int ql_run_loopback_test(struct ql_adapter *qdev) | ||
484 | { | ||
485 | int i; | ||
486 | netdev_tx_t rc; | ||
487 | struct sk_buff *skb; | ||
488 | unsigned int size = SMALL_BUF_MAP_SIZE; | ||
489 | |||
490 | for (i = 0; i < 64; i++) { | ||
491 | skb = netdev_alloc_skb(qdev->ndev, size); | ||
492 | if (!skb) | ||
493 | return -ENOMEM; | ||
494 | |||
495 | skb->queue_mapping = 0; | ||
496 | skb_put(skb, size); | ||
497 | ql_create_lb_frame(skb, size); | ||
498 | rc = ql_lb_send(skb, qdev->ndev); | ||
499 | if (rc != NETDEV_TX_OK) | ||
500 | return -EPIPE; | ||
501 | atomic_inc(&qdev->lb_count); | ||
502 | } | ||
503 | |||
504 | ql_clean_lb_rx_ring(&qdev->rx_ring[0], 128); | ||
505 | return atomic_read(&qdev->lb_count) ? -EIO : 0; | ||
506 | } | ||
507 | |||
508 | static int ql_loopback_test(struct ql_adapter *qdev, u64 *data) | ||
509 | { | ||
510 | *data = ql_start_loopback(qdev); | ||
511 | if (*data) | ||
512 | goto out; | ||
513 | *data = ql_run_loopback_test(qdev); | ||
514 | out: | ||
515 | ql_stop_loopback(qdev); | ||
516 | return *data; | ||
517 | } | ||
518 | |||
519 | static void ql_self_test(struct net_device *ndev, | ||
520 | struct ethtool_test *eth_test, u64 *data) | ||
521 | { | ||
522 | struct ql_adapter *qdev = netdev_priv(ndev); | ||
523 | |||
524 | if (netif_running(ndev)) { | ||
525 | set_bit(QL_SELFTEST, &qdev->flags); | ||
526 | if (eth_test->flags == ETH_TEST_FL_OFFLINE) { | ||
527 | /* Offline tests */ | ||
528 | if (ql_loopback_test(qdev, &data[0])) | ||
529 | eth_test->flags |= ETH_TEST_FL_FAILED; | ||
530 | |||
531 | } else { | ||
532 | /* Online tests */ | ||
533 | data[0] = 0; | ||
534 | } | ||
535 | clear_bit(QL_SELFTEST, &qdev->flags); | ||
536 | } else { | ||
537 | QPRINTK(qdev, DRV, ERR, | ||
538 | "%s: is down, Loopback test will fail.\n", ndev->name); | ||
539 | eth_test->flags |= ETH_TEST_FL_FAILED; | ||
540 | } | ||
541 | } | ||
542 | |||
432 | static int ql_get_regs_len(struct net_device *ndev) | 543 | static int ql_get_regs_len(struct net_device *ndev) |
433 | { | 544 | { |
434 | return sizeof(struct ql_reg_dump); | 545 | return sizeof(struct ql_reg_dump); |
@@ -575,6 +686,7 @@ const struct ethtool_ops qlge_ethtool_ops = { | |||
575 | .set_msglevel = ql_set_msglevel, | 686 | .set_msglevel = ql_set_msglevel, |
576 | .get_link = ethtool_op_get_link, | 687 | .get_link = ethtool_op_get_link, |
577 | .phys_id = ql_phys_id, | 688 | .phys_id = ql_phys_id, |
689 | .self_test = ql_self_test, | ||
578 | .get_pauseparam = ql_get_pauseparam, | 690 | .get_pauseparam = ql_get_pauseparam, |
579 | .set_pauseparam = ql_set_pauseparam, | 691 | .set_pauseparam = ql_set_pauseparam, |
580 | .get_rx_csum = ql_get_rx_csum, | 692 | .get_rx_csum = ql_get_rx_csum, |
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index 42ad811ec313..4de054505ec6 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c | |||
@@ -1680,6 +1680,13 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev, | |||
1680 | return; | 1680 | return; |
1681 | } | 1681 | } |
1682 | 1682 | ||
1683 | /* loopback self test for ethtool */ | ||
1684 | if (test_bit(QL_SELFTEST, &qdev->flags)) { | ||
1685 | ql_check_lb_frame(qdev, skb); | ||
1686 | dev_kfree_skb_any(skb); | ||
1687 | return; | ||
1688 | } | ||
1689 | |||
1683 | prefetch(skb->data); | 1690 | prefetch(skb->data); |
1684 | skb->dev = ndev; | 1691 | skb->dev = ndev; |
1685 | if (ib_mac_rsp->flags1 & IB_MAC_IOCB_RSP_M_MASK) { | 1692 | if (ib_mac_rsp->flags1 & IB_MAC_IOCB_RSP_M_MASK) { |
@@ -2248,6 +2255,7 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev) | |||
2248 | return NETDEV_TX_OK; | 2255 | return NETDEV_TX_OK; |
2249 | } | 2256 | } |
2250 | 2257 | ||
2258 | |||
2251 | static void ql_free_shadow_space(struct ql_adapter *qdev) | 2259 | static void ql_free_shadow_space(struct ql_adapter *qdev) |
2252 | { | 2260 | { |
2253 | if (qdev->rx_ring_shadow_reg_area) { | 2261 | if (qdev->rx_ring_shadow_reg_area) { |
@@ -4174,7 +4182,6 @@ err_out: | |||
4174 | return err; | 4182 | return err; |
4175 | } | 4183 | } |
4176 | 4184 | ||
4177 | |||
4178 | static const struct net_device_ops qlge_netdev_ops = { | 4185 | static const struct net_device_ops qlge_netdev_ops = { |
4179 | .ndo_open = qlge_open, | 4186 | .ndo_open = qlge_open, |
4180 | .ndo_stop = qlge_close, | 4187 | .ndo_stop = qlge_close, |
@@ -4243,10 +4250,21 @@ static int __devinit qlge_probe(struct pci_dev *pdev, | |||
4243 | } | 4250 | } |
4244 | ql_link_off(qdev); | 4251 | ql_link_off(qdev); |
4245 | ql_display_dev_info(ndev); | 4252 | ql_display_dev_info(ndev); |
4253 | atomic_set(&qdev->lb_count, 0); | ||
4246 | cards_found++; | 4254 | cards_found++; |
4247 | return 0; | 4255 | return 0; |
4248 | } | 4256 | } |
4249 | 4257 | ||
4258 | netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev) | ||
4259 | { | ||
4260 | return qlge_send(skb, ndev); | ||
4261 | } | ||
4262 | |||
4263 | int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget) | ||
4264 | { | ||
4265 | return ql_clean_inbound_rx_ring(rx_ring, budget); | ||
4266 | } | ||
4267 | |||
4250 | static void __devexit qlge_remove(struct pci_dev *pdev) | 4268 | static void __devexit qlge_remove(struct pci_dev *pdev) |
4251 | { | 4269 | { |
4252 | struct net_device *ndev = pci_get_drvdata(pdev); | 4270 | struct net_device *ndev = pci_get_drvdata(pdev); |