aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/qlcnic
diff options
context:
space:
mode:
authorAmit Kumar Salecha <amit.salecha@qlogic.com>2010-02-01 00:25:00 -0500
committerDavid S. Miller <davem@davemloft.net>2010-02-02 18:55:46 -0500
commitcdaff1854f32ac9ddb4733530f617d32188665ed (patch)
tree5d1e7b5bcf656d0071840431f1f3cbc94b828e21 /drivers/net/qlcnic
parent7eb9855d68faabe0004ed18c2af1f0974d3c2c63 (diff)
qlcnic: add loopback diagnostic test
Loopback test (offline) added in ethtool self test. o Set device in loopback mode o Send packets o Process receive packets in qlcnic_process_rcv_ring_diag() o Compare packets o Reset device in normal mode. Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/qlcnic')
-rw-r--r--drivers/net/qlcnic/qlcnic.h6
-rw-r--r--drivers/net/qlcnic/qlcnic_ethtool.c82
-rw-r--r--drivers/net/qlcnic/qlcnic_hw.c52
-rw-r--r--drivers/net/qlcnic/qlcnic_init.c75
-rw-r--r--drivers/net/qlcnic/qlcnic_main.c20
5 files changed, 225 insertions, 10 deletions
diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h
index a5a67e9b5e10..b40a851ec7d1 100644
--- a/drivers/net/qlcnic/qlcnic.h
+++ b/drivers/net/qlcnic/qlcnic.h
@@ -897,6 +897,7 @@ struct qlcnic_mac_req {
897#define __QLCNIC_START_FW 4 897#define __QLCNIC_START_FW 4
898 898
899#define QLCNIC_INTERRUPT_TEST 1 899#define QLCNIC_INTERRUPT_TEST 1
900#define QLCNIC_LOOPBACK_TEST 2
900 901
901struct qlcnic_adapter { 902struct qlcnic_adapter {
902 struct qlcnic_hardware_context ahw; 903 struct qlcnic_hardware_context ahw;
@@ -1066,6 +1067,8 @@ int qlcnic_send_lro_cleanup(struct qlcnic_adapter *adapter);
1066void qlcnic_update_cmd_producer(struct qlcnic_adapter *adapter, 1067void qlcnic_update_cmd_producer(struct qlcnic_adapter *adapter,
1067 struct qlcnic_host_tx_ring *tx_ring); 1068 struct qlcnic_host_tx_ring *tx_ring);
1068int qlcnic_get_mac_addr(struct qlcnic_adapter *adapter, u64 *mac); 1069int qlcnic_get_mac_addr(struct qlcnic_adapter *adapter, u64 *mac);
1070void qlcnic_clear_ilb_mode(struct qlcnic_adapter *adapter);
1071int qlcnic_set_ilb_mode(struct qlcnic_adapter *adapter);
1069 1072
1070/* Functions from qlcnic_main.c */ 1073/* Functions from qlcnic_main.c */
1071int qlcnic_reset_context(struct qlcnic_adapter *); 1074int qlcnic_reset_context(struct qlcnic_adapter *);
@@ -1073,6 +1076,9 @@ u32 qlcnic_issue_cmd(struct qlcnic_adapter *adapter,
1073 u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd); 1076 u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd);
1074void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings); 1077void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings);
1075int qlcnic_diag_alloc_res(struct net_device *netdev, int test); 1078int qlcnic_diag_alloc_res(struct net_device *netdev, int test);
1079int qlcnic_check_loopback_buff(unsigned char *data);
1080netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
1081void qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring);
1076 1082
1077/* 1083/*
1078 * QLOGIC Board information 1084 * QLOGIC Board information
diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c
index 58c50ed791de..8da6ec8c13b9 100644
--- a/drivers/net/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/qlcnic/qlcnic_ethtool.c
@@ -66,7 +66,8 @@ static const struct qlcnic_stats qlcnic_gstrings_stats[] = {
66static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = { 66static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = {
67 "Register_Test_on_offline", 67 "Register_Test_on_offline",
68 "Link_Test_on_offline", 68 "Link_Test_on_offline",
69 "Interrupt_Test_offline" 69 "Interrupt_Test_offline",
70 "Loopback_Test_offline"
70}; 71};
71 72
72#define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) 73#define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test)
@@ -614,6 +615,80 @@ static int qlcnic_get_sset_count(struct net_device *dev, int sset)
614 } 615 }
615} 616}
616 617
618#define QLC_ILB_PKT_SIZE 64
619
620static void qlcnic_create_loopback_buff(unsigned char *data)
621{
622 unsigned char random_data[] = {0xa8, 0x06, 0x45, 0x00};
623 memset(data, 0x4e, QLC_ILB_PKT_SIZE);
624 memset(data, 0xff, 12);
625 memcpy(data + 12, random_data, sizeof(random_data));
626}
627
628int qlcnic_check_loopback_buff(unsigned char *data)
629{
630 unsigned char buff[QLC_ILB_PKT_SIZE];
631 qlcnic_create_loopback_buff(buff);
632 return memcmp(data, buff, QLC_ILB_PKT_SIZE);
633}
634
635static int qlcnic_do_ilb_test(struct qlcnic_adapter *adapter)
636{
637 struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx;
638 struct qlcnic_host_sds_ring *sds_ring = &recv_ctx->sds_rings[0];
639 struct sk_buff *skb;
640 int i;
641
642 for (i = 0; i < 16; i++) {
643 skb = dev_alloc_skb(QLC_ILB_PKT_SIZE);
644 qlcnic_create_loopback_buff(skb->data);
645 skb_put(skb, QLC_ILB_PKT_SIZE);
646
647 adapter->diag_cnt = 0;
648
649 qlcnic_xmit_frame(skb, adapter->netdev);
650
651 msleep(5);
652
653 qlcnic_process_rcv_ring_diag(sds_ring);
654
655 dev_kfree_skb_any(skb);
656 if (!adapter->diag_cnt)
657 return -1;
658 }
659 return 0;
660}
661
662static int qlcnic_loopback_test(struct net_device *netdev)
663{
664 struct qlcnic_adapter *adapter = netdev_priv(netdev);
665 int max_sds_rings = adapter->max_sds_rings;
666 int ret;
667
668 if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
669 return -EIO;
670
671 ret = qlcnic_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST);
672 if (ret)
673 goto clear_it;
674
675 ret = qlcnic_set_ilb_mode(adapter);
676 if (ret)
677 goto done;
678
679 ret = qlcnic_do_ilb_test(adapter);
680
681 qlcnic_clear_ilb_mode(adapter);
682
683done:
684 qlcnic_diag_free_res(netdev, max_sds_rings);
685
686clear_it:
687 adapter->max_sds_rings = max_sds_rings;
688 clear_bit(__QLCNIC_RESETTING, &adapter->state);
689 return ret;
690}
691
617static int qlcnic_irq_test(struct net_device *netdev) 692static int qlcnic_irq_test(struct net_device *netdev)
618{ 693{
619 struct qlcnic_adapter *adapter = netdev_priv(netdev); 694 struct qlcnic_adapter *adapter = netdev_priv(netdev);
@@ -656,6 +731,11 @@ qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test,
656 data[2] = qlcnic_irq_test(dev); 731 data[2] = qlcnic_irq_test(dev);
657 if (data[2]) 732 if (data[2])
658 eth_test->flags |= ETH_TEST_FL_FAILED; 733 eth_test->flags |= ETH_TEST_FL_FAILED;
734
735 data[3] = qlcnic_loopback_test(dev);
736 if (data[3])
737 eth_test->flags |= ETH_TEST_FL_FAILED;
738
659 } 739 }
660 740
661 data[0] = qlcnic_reg_test(dev); 741 data[0] = qlcnic_reg_test(dev);
diff --git a/drivers/net/qlcnic/qlcnic_hw.c b/drivers/net/qlcnic/qlcnic_hw.c
index 8724e561ed72..dc6cd69d6d93 100644
--- a/drivers/net/qlcnic/qlcnic_hw.c
+++ b/drivers/net/qlcnic/qlcnic_hw.c
@@ -1221,3 +1221,55 @@ int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)
1221 1221
1222 return rv; 1222 return rv;
1223} 1223}
1224
1225static int qlcnic_set_fw_loopback(struct qlcnic_adapter *adapter, u32 flag)
1226{
1227 struct qlcnic_nic_req req;
1228 int rv;
1229 u64 word;
1230
1231 memset(&req, 0, sizeof(struct qlcnic_nic_req));
1232 req.qhdr = cpu_to_le64(QLCNIC_HOST_REQUEST << 23);
1233
1234 word = QLCNIC_H2C_OPCODE_CONFIG_LOOPBACK |
1235 ((u64)adapter->portnum << 16);
1236 req.req_hdr = cpu_to_le64(word);
1237 req.words[0] = cpu_to_le64(flag);
1238
1239 rv = qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);
1240 if (rv)
1241 dev_err(&adapter->pdev->dev,
1242 "%sting loopback mode failed.\n",
1243 flag ? "Set" : "Reset");
1244 return rv;
1245}
1246
1247int qlcnic_set_ilb_mode(struct qlcnic_adapter *adapter)
1248{
1249 if (qlcnic_set_fw_loopback(adapter, 1))
1250 return -EIO;
1251
1252 if (qlcnic_nic_set_promisc(adapter,
1253 VPORT_MISS_MODE_ACCEPT_ALL)) {
1254 qlcnic_set_fw_loopback(adapter, 0);
1255 return -EIO;
1256 }
1257
1258 msleep(1000);
1259 return 0;
1260}
1261
1262void qlcnic_clear_ilb_mode(struct qlcnic_adapter *adapter)
1263{
1264 int mode = VPORT_MISS_MODE_DROP;
1265 struct net_device *netdev = adapter->netdev;
1266
1267 qlcnic_set_fw_loopback(adapter, 0);
1268
1269 if (netdev->flags & IFF_PROMISC)
1270 mode = VPORT_MISS_MODE_ACCEPT_ALL;
1271 else if (netdev->flags & IFF_ALLMULTI)
1272 mode = VPORT_MISS_MODE_ACCEPT_MULTI;
1273
1274 qlcnic_nic_set_promisc(adapter, mode);
1275}
diff --git a/drivers/net/qlcnic/qlcnic_init.c b/drivers/net/qlcnic/qlcnic_init.c
index 7ae8bcc1e439..ea00ab4d4feb 100644
--- a/drivers/net/qlcnic/qlcnic_init.c
+++ b/drivers/net/qlcnic/qlcnic_init.c
@@ -1464,3 +1464,78 @@ qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter,
1464 spin_unlock(&rds_ring->lock); 1464 spin_unlock(&rds_ring->lock);
1465} 1465}
1466 1466
1467static struct qlcnic_rx_buffer *
1468qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter,
1469 struct qlcnic_host_sds_ring *sds_ring,
1470 int ring, u64 sts_data0)
1471{
1472 struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx;
1473 struct qlcnic_rx_buffer *buffer;
1474 struct sk_buff *skb;
1475 struct qlcnic_host_rds_ring *rds_ring;
1476 int index, length, cksum, pkt_offset;
1477
1478 if (unlikely(ring >= adapter->max_rds_rings))
1479 return NULL;
1480
1481 rds_ring = &recv_ctx->rds_rings[ring];
1482
1483 index = qlcnic_get_sts_refhandle(sts_data0);
1484 if (unlikely(index >= rds_ring->num_desc))
1485 return NULL;
1486
1487 buffer = &rds_ring->rx_buf_arr[index];
1488
1489 length = qlcnic_get_sts_totallength(sts_data0);
1490 cksum = qlcnic_get_sts_status(sts_data0);
1491 pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);
1492
1493 skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
1494 if (!skb)
1495 return buffer;
1496
1497 skb_put(skb, rds_ring->skb_size);
1498
1499 if (pkt_offset)
1500 skb_pull(skb, pkt_offset);
1501
1502 skb->truesize = skb->len + sizeof(struct sk_buff);
1503
1504 if (!qlcnic_check_loopback_buff(skb->data))
1505 adapter->diag_cnt++;
1506
1507 dev_kfree_skb_any(skb);
1508
1509 return buffer;
1510}
1511
1512void
1513qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
1514{
1515 struct qlcnic_adapter *adapter = sds_ring->adapter;
1516 struct status_desc *desc;
1517 struct qlcnic_rx_buffer *rxbuf;
1518 u64 sts_data0;
1519
1520 int opcode, ring, desc_cnt;
1521 u32 consumer = sds_ring->consumer;
1522
1523 desc = &sds_ring->desc_head[consumer];
1524 sts_data0 = le64_to_cpu(desc->status_desc_data[0]);
1525
1526 if (!(sts_data0 & STATUS_OWNER_HOST))
1527 return;
1528
1529 desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
1530 opcode = qlcnic_get_sts_opcode(sts_data0);
1531
1532 ring = qlcnic_get_sts_type(sts_data0);
1533 rxbuf = qlcnic_process_rcv_diag(adapter, sds_ring,
1534 ring, sts_data0);
1535
1536 desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM);
1537 consumer = get_next_index(consumer, sds_ring->num_desc);
1538
1539 sds_ring->consumer = consumer;
1540 writel(consumer, sds_ring->crb_sts_consumer);
1541}
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c
index a8b07120d362..665e8e56b6a8 100644
--- a/drivers/net/qlcnic/qlcnic_main.c
+++ b/drivers/net/qlcnic/qlcnic_main.c
@@ -65,8 +65,6 @@ static int __devinit qlcnic_probe(struct pci_dev *pdev,
65static void __devexit qlcnic_remove(struct pci_dev *pdev); 65static void __devexit qlcnic_remove(struct pci_dev *pdev);
66static int qlcnic_open(struct net_device *netdev); 66static int qlcnic_open(struct net_device *netdev);
67static int qlcnic_close(struct net_device *netdev); 67static int qlcnic_close(struct net_device *netdev);
68static netdev_tx_t qlcnic_xmit_frame(struct sk_buff *,
69 struct net_device *);
70static void qlcnic_tx_timeout(struct net_device *netdev); 68static void qlcnic_tx_timeout(struct net_device *netdev);
71static void qlcnic_tx_timeout_task(struct work_struct *work); 69static void qlcnic_tx_timeout_task(struct work_struct *work);
72static void qlcnic_attach_work(struct work_struct *work); 70static void qlcnic_attach_work(struct work_struct *work);
@@ -937,9 +935,11 @@ void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings)
937 struct qlcnic_host_sds_ring *sds_ring; 935 struct qlcnic_host_sds_ring *sds_ring;
938 int ring; 936 int ring;
939 937
940 for (ring = 0; ring < adapter->max_sds_rings; ring++) { 938 if (adapter->diag_test == QLCNIC_INTERRUPT_TEST) {
941 sds_ring = &adapter->recv_ctx.sds_rings[ring]; 939 for (ring = 0; ring < adapter->max_sds_rings; ring++) {
942 qlcnic_disable_int(sds_ring); 940 sds_ring = &adapter->recv_ctx.sds_rings[ring];
941 qlcnic_disable_int(sds_ring);
942 }
943 } 943 }
944 944
945 qlcnic_detach(adapter); 945 qlcnic_detach(adapter);
@@ -977,9 +977,11 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
977 if (ret) 977 if (ret)
978 return ret; 978 return ret;
979 979
980 for (ring = 0; ring < adapter->max_sds_rings; ring++) { 980 if (adapter->diag_test == QLCNIC_INTERRUPT_TEST) {
981 sds_ring = &adapter->recv_ctx.sds_rings[ring]; 981 for (ring = 0; ring < adapter->max_sds_rings; ring++) {
982 qlcnic_enable_int(sds_ring); 982 sds_ring = &adapter->recv_ctx.sds_rings[ring];
983 qlcnic_enable_int(sds_ring);
984 }
983 } 985 }
984 986
985 return 0; 987 return 0;
@@ -1549,7 +1551,7 @@ qlcnic_clear_cmddesc(u64 *desc)
1549 desc[2] = 0ULL; 1551 desc[2] = 0ULL;
1550} 1552}
1551 1553
1552static netdev_tx_t 1554netdev_tx_t
1553qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) 1555qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
1554{ 1556{
1555 struct qlcnic_adapter *adapter = netdev_priv(netdev); 1557 struct qlcnic_adapter *adapter = netdev_priv(netdev);