Skip to content

Commit 4aa218a

Browse files
Luo bindavem330
authored andcommitted
hinic: add self test support
add support to excute internal and external loopback test with ethtool -t cmd. Signed-off-by: Luo bin <luobin9@huawei.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent a0337c0 commit 4aa218a

File tree

8 files changed

+331
-0
lines changed

8 files changed

+331
-0
lines changed

drivers/net/ethernet/huawei/hinic/hinic_dev.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@
2020

2121
#define HINIC_DRV_NAME "hinic"
2222

23+
#define LP_PKT_CNT 64
24+
2325
enum hinic_flags {
2426
HINIC_LINK_UP = BIT(0),
2527
HINIC_INTF_UP = BIT(1),
2628
HINIC_RSS_ENABLE = BIT(2),
2729
HINIC_LINK_DOWN = BIT(3),
30+
HINIC_LP_TEST = BIT(4),
2831
};
2932

3033
struct hinic_rx_mode_work {
@@ -91,6 +94,9 @@ struct hinic_dev {
9194
struct hinic_intr_coal_info *rx_intr_coalesce;
9295
struct hinic_intr_coal_info *tx_intr_coalesce;
9396
struct hinic_sriov_info sriov_info;
97+
int lb_test_rx_idx;
98+
int lb_pkt_len;
99+
u8 *lb_test_rx_buf;
94100
};
95101

96102
#endif

drivers/net/ethernet/huawei/hinic/hinic_ethtool.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,16 @@ static struct hw2ethtool_link_mode
133133
},
134134
};
135135

136+
#define LP_DEFAULT_TIME 5 /* seconds */
137+
#define LP_PKT_LEN 1514
138+
139+
#define PORT_DOWN_ERR_IDX 0
140+
enum diag_test_index {
141+
INTERNAL_LP_TEST = 0,
142+
EXTERNAL_LP_TEST = 1,
143+
DIAG_TEST_MAX = 2,
144+
};
145+
136146
static void set_link_speed(struct ethtool_link_ksettings *link_ksettings,
137147
enum hinic_speed speed)
138148
{
@@ -1244,6 +1254,11 @@ static struct hinic_stats hinic_function_stats[] = {
12441254
HINIC_FUNC_STAT(rx_err_vport),
12451255
};
12461256

1257+
static char hinic_test_strings[][ETH_GSTRING_LEN] = {
1258+
"Internal lb test (on/offline)",
1259+
"External lb test (external_lb)",
1260+
};
1261+
12471262
#define HINIC_PORT_STAT(_stat_item) { \
12481263
.name = #_stat_item, \
12491264
.size = sizeof_field(struct hinic_phy_port_stats, _stat_item), \
@@ -1453,6 +1468,8 @@ static int hinic_get_sset_count(struct net_device *netdev, int sset)
14531468
int count, q_num;
14541469

14551470
switch (sset) {
1471+
case ETH_SS_TEST:
1472+
return ARRAY_LEN(hinic_test_strings);
14561473
case ETH_SS_STATS:
14571474
q_num = nic_dev->num_qps;
14581475
count = ARRAY_LEN(hinic_function_stats) +
@@ -1475,6 +1492,9 @@ static void hinic_get_strings(struct net_device *netdev,
14751492
u16 i, j;
14761493

14771494
switch (stringset) {
1495+
case ETH_SS_TEST:
1496+
memcpy(data, *hinic_test_strings, sizeof(hinic_test_strings));
1497+
return;
14781498
case ETH_SS_STATS:
14791499
for (i = 0; i < ARRAY_LEN(hinic_function_stats); i++) {
14801500
memcpy(p, hinic_function_stats[i].name,
@@ -1508,6 +1528,162 @@ static void hinic_get_strings(struct net_device *netdev,
15081528
}
15091529
}
15101530

1531+
static int hinic_run_lp_test(struct hinic_dev *nic_dev, u32 test_time)
1532+
{
1533+
u8 *lb_test_rx_buf = nic_dev->lb_test_rx_buf;
1534+
struct net_device *netdev = nic_dev->netdev;
1535+
struct sk_buff *skb_tmp = NULL;
1536+
struct sk_buff *skb = NULL;
1537+
u32 cnt = test_time * 5;
1538+
u8 *test_data = NULL;
1539+
u32 i;
1540+
u8 j;
1541+
1542+
skb_tmp = alloc_skb(LP_PKT_LEN, GFP_ATOMIC);
1543+
if (!skb_tmp)
1544+
return -ENOMEM;
1545+
1546+
test_data = __skb_put(skb_tmp, LP_PKT_LEN);
1547+
1548+
memset(test_data, 0xFF, 2 * ETH_ALEN);
1549+
test_data[ETH_ALEN] = 0xFE;
1550+
test_data[2 * ETH_ALEN] = 0x08;
1551+
test_data[2 * ETH_ALEN + 1] = 0x0;
1552+
1553+
for (i = ETH_HLEN; i < LP_PKT_LEN; i++)
1554+
test_data[i] = i & 0xFF;
1555+
1556+
skb_tmp->queue_mapping = 0;
1557+
skb_tmp->ip_summed = CHECKSUM_COMPLETE;
1558+
skb_tmp->dev = netdev;
1559+
1560+
for (i = 0; i < cnt; i++) {
1561+
nic_dev->lb_test_rx_idx = 0;
1562+
memset(lb_test_rx_buf, 0, LP_PKT_CNT * LP_PKT_LEN);
1563+
1564+
for (j = 0; j < LP_PKT_CNT; j++) {
1565+
skb = pskb_copy(skb_tmp, GFP_ATOMIC);
1566+
if (!skb) {
1567+
dev_kfree_skb_any(skb_tmp);
1568+
netif_err(nic_dev, drv, netdev,
1569+
"Copy skb failed for loopback test\n");
1570+
return -ENOMEM;
1571+
}
1572+
1573+
/* mark index for every pkt */
1574+
skb->data[LP_PKT_LEN - 1] = j;
1575+
1576+
if (hinic_lb_xmit_frame(skb, netdev)) {
1577+
dev_kfree_skb_any(skb);
1578+
dev_kfree_skb_any(skb_tmp);
1579+
netif_err(nic_dev, drv, netdev,
1580+
"Xmit pkt failed for loopback test\n");
1581+
return -EBUSY;
1582+
}
1583+
}
1584+
1585+
/* wait till all pkts received to RX buffer */
1586+
msleep(200);
1587+
1588+
for (j = 0; j < LP_PKT_CNT; j++) {
1589+
if (memcmp(lb_test_rx_buf + j * LP_PKT_LEN,
1590+
skb_tmp->data, LP_PKT_LEN - 1) ||
1591+
(*(lb_test_rx_buf + j * LP_PKT_LEN +
1592+
LP_PKT_LEN - 1) != j)) {
1593+
dev_kfree_skb_any(skb_tmp);
1594+
netif_err(nic_dev, drv, netdev,
1595+
"Compare pkt failed in loopback test(index=0x%02x, data[%d]=0x%02x)\n",
1596+
j + i * LP_PKT_CNT,
1597+
LP_PKT_LEN - 1,
1598+
*(lb_test_rx_buf + j * LP_PKT_LEN +
1599+
LP_PKT_LEN - 1));
1600+
return -EIO;
1601+
}
1602+
}
1603+
}
1604+
1605+
dev_kfree_skb_any(skb_tmp);
1606+
return 0;
1607+
}
1608+
1609+
static int do_lp_test(struct hinic_dev *nic_dev, u32 flags, u32 test_time,
1610+
enum diag_test_index *test_index)
1611+
{
1612+
struct net_device *netdev = nic_dev->netdev;
1613+
u8 *lb_test_rx_buf = NULL;
1614+
int err = 0;
1615+
1616+
if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) {
1617+
*test_index = INTERNAL_LP_TEST;
1618+
if (hinic_set_loopback_mode(nic_dev->hwdev,
1619+
HINIC_INTERNAL_LP_MODE, true)) {
1620+
netif_err(nic_dev, drv, netdev,
1621+
"Failed to set port loopback mode before loopback test\n");
1622+
return -EIO;
1623+
}
1624+
} else {
1625+
*test_index = EXTERNAL_LP_TEST;
1626+
}
1627+
1628+
lb_test_rx_buf = vmalloc(LP_PKT_CNT * LP_PKT_LEN);
1629+
if (!lb_test_rx_buf) {
1630+
err = -ENOMEM;
1631+
} else {
1632+
nic_dev->lb_test_rx_buf = lb_test_rx_buf;
1633+
nic_dev->lb_pkt_len = LP_PKT_LEN;
1634+
nic_dev->flags |= HINIC_LP_TEST;
1635+
err = hinic_run_lp_test(nic_dev, test_time);
1636+
nic_dev->flags &= ~HINIC_LP_TEST;
1637+
msleep(100);
1638+
vfree(lb_test_rx_buf);
1639+
nic_dev->lb_test_rx_buf = NULL;
1640+
}
1641+
1642+
if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) {
1643+
if (hinic_set_loopback_mode(nic_dev->hwdev,
1644+
HINIC_INTERNAL_LP_MODE, false)) {
1645+
netif_err(nic_dev, drv, netdev,
1646+
"Failed to cancel port loopback mode after loopback test\n");
1647+
err = -EIO;
1648+
}
1649+
}
1650+
1651+
return err;
1652+
}
1653+
1654+
static void hinic_diag_test(struct net_device *netdev,
1655+
struct ethtool_test *eth_test, u64 *data)
1656+
{
1657+
struct hinic_dev *nic_dev = netdev_priv(netdev);
1658+
enum hinic_port_link_state link_state;
1659+
enum diag_test_index test_index = 0;
1660+
int err = 0;
1661+
1662+
memset(data, 0, DIAG_TEST_MAX * sizeof(u64));
1663+
1664+
/* don't support loopback test when netdev is closed. */
1665+
if (!(nic_dev->flags & HINIC_INTF_UP)) {
1666+
netif_err(nic_dev, drv, netdev,
1667+
"Do not support loopback test when netdev is closed\n");
1668+
eth_test->flags |= ETH_TEST_FL_FAILED;
1669+
data[PORT_DOWN_ERR_IDX] = 1;
1670+
return;
1671+
}
1672+
1673+
netif_carrier_off(netdev);
1674+
1675+
err = do_lp_test(nic_dev, eth_test->flags, LP_DEFAULT_TIME,
1676+
&test_index);
1677+
if (err) {
1678+
eth_test->flags |= ETH_TEST_FL_FAILED;
1679+
data[test_index] = 1;
1680+
}
1681+
1682+
err = hinic_port_link_state(nic_dev, &link_state);
1683+
if (!err && link_state == HINIC_LINK_STATE_UP)
1684+
netif_carrier_on(netdev);
1685+
}
1686+
15111687
static const struct ethtool_ops hinic_ethtool_ops = {
15121688
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
15131689
ETHTOOL_COALESCE_RX_MAX_FRAMES |
@@ -1537,6 +1713,7 @@ static const struct ethtool_ops hinic_ethtool_ops = {
15371713
.get_sset_count = hinic_get_sset_count,
15381714
.get_ethtool_stats = hinic_get_ethtool_stats,
15391715
.get_strings = hinic_get_strings,
1716+
.self_test = hinic_diag_test,
15401717
};
15411718

15421719
static const struct ethtool_ops hinicvf_ethtool_ops = {

drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ enum hinic_port_cmd {
9797

9898
HINIC_PORT_CMD_FWCTXT_INIT = 69,
9999

100+
HINIC_PORT_CMD_GET_LOOPBACK_MODE = 72,
101+
HINIC_PORT_CMD_SET_LOOPBACK_MODE,
102+
100103
HINIC_PORT_CMD_ENABLE_SPOOFCHK = 78,
101104

102105
HINIC_PORT_CMD_GET_MGMT_VERSION = 88,

drivers/net/ethernet/huawei/hinic/hinic_port.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,3 +1241,30 @@ int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap)
12411241

12421242
return 0;
12431243
}
1244+
1245+
int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable)
1246+
{
1247+
struct hinic_port_loopback lb = {0};
1248+
u16 out_size = sizeof(lb);
1249+
int err;
1250+
1251+
lb.mode = mode;
1252+
lb.en = enable;
1253+
1254+
if (mode < LOOP_MODE_MIN || mode > LOOP_MODE_MAX) {
1255+
dev_err(&hwdev->hwif->pdev->dev,
1256+
"Invalid loopback mode %d to set\n", mode);
1257+
return -EINVAL;
1258+
}
1259+
1260+
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LOOPBACK_MODE,
1261+
&lb, sizeof(lb), &lb, &out_size);
1262+
if (err || !out_size || lb.status) {
1263+
dev_err(&hwdev->hwif->pdev->dev,
1264+
"Failed to set loopback mode %d en %d, err: %d, status: 0x%x, out size: 0x%x\n",
1265+
mode, enable, err, lb.status, out_size);
1266+
return -EIO;
1267+
}
1268+
1269+
return 0;
1270+
}

drivers/net/ethernet/huawei/hinic/hinic_port.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,20 @@ struct hinic_set_pfc {
652652
u8 rsvd1[4];
653653
};
654654

655+
/* get or set loopback mode, need to modify by base API */
656+
#define HINIC_INTERNAL_LP_MODE 5
657+
#define LOOP_MODE_MIN 1
658+
#define LOOP_MODE_MAX 6
659+
660+
struct hinic_port_loopback {
661+
u8 status;
662+
u8 version;
663+
u8 rsvd[6];
664+
665+
u32 mode;
666+
u32 en;
667+
};
668+
655669
int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
656670
u16 vlan_id);
657671

@@ -749,6 +763,8 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
749763

750764
int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap);
751765

766+
int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable);
767+
752768
int hinic_open(struct net_device *netdev);
753769

754770
int hinic_close(struct net_device *netdev);

drivers/net/ethernet/huawei/hinic/hinic_rx.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,39 @@ static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb,
316316
return num_wqes;
317317
}
318318

319+
static void hinic_copy_lp_data(struct hinic_dev *nic_dev,
320+
struct sk_buff *skb)
321+
{
322+
struct net_device *netdev = nic_dev->netdev;
323+
u8 *lb_buf = nic_dev->lb_test_rx_buf;
324+
int lb_len = nic_dev->lb_pkt_len;
325+
int pkt_offset, frag_len, i;
326+
void *frag_data = NULL;
327+
328+
if (nic_dev->lb_test_rx_idx == LP_PKT_CNT) {
329+
nic_dev->lb_test_rx_idx = 0;
330+
netif_warn(nic_dev, drv, netdev, "Loopback test warning, receive too more test pkts\n");
331+
}
332+
333+
if (skb->len != nic_dev->lb_pkt_len) {
334+
netif_warn(nic_dev, drv, netdev, "Wrong packet length\n");
335+
nic_dev->lb_test_rx_idx++;
336+
return;
337+
}
338+
339+
pkt_offset = nic_dev->lb_test_rx_idx * lb_len;
340+
frag_len = (int)skb_headlen(skb);
341+
memcpy(lb_buf + pkt_offset, skb->data, frag_len);
342+
pkt_offset += frag_len;
343+
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
344+
frag_data = skb_frag_address(&skb_shinfo(skb)->frags[i]);
345+
frag_len = (int)skb_frag_size(&skb_shinfo(skb)->frags[i]);
346+
memcpy((lb_buf + pkt_offset), frag_data, frag_len);
347+
pkt_offset += frag_len;
348+
}
349+
nic_dev->lb_test_rx_idx++;
350+
}
351+
319352
/**
320353
* rxq_recv - Rx handler
321354
* @rxq: rx queue
@@ -330,6 +363,7 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
330363
u64 pkt_len = 0, rx_bytes = 0;
331364
struct hinic_rq *rq = rxq->rq;
332365
struct hinic_rq_wqe *rq_wqe;
366+
struct hinic_dev *nic_dev;
333367
unsigned int free_wqebbs;
334368
struct hinic_rq_cqe *cqe;
335369
int num_wqes, pkts = 0;
@@ -342,6 +376,8 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
342376
u32 vlan_len;
343377
u16 vid;
344378

379+
nic_dev = netdev_priv(netdev);
380+
345381
while (pkts < budget) {
346382
num_wqes = 0;
347383

@@ -384,6 +420,9 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
384420
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
385421
}
386422

423+
if (unlikely(nic_dev->flags & HINIC_LP_TEST))
424+
hinic_copy_lp_data(nic_dev, skb);
425+
387426
skb_record_rx_queue(skb, qp->q_id);
388427
skb->protocol = eth_type_trans(skb, rxq->netdev);
389428

0 commit comments

Comments
 (0)