Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cxgb4: add loopback ethtool self-test

In this test, loopback pkt is created and sent on default queue.
The packet goes until the Multi Port Switch (MPS) just before
the MAC and based on the specified channel number, it either
goes outside the wire on one of the physical ports or looped
back to Rx path by MPS. In this case, we're specifying loopback
channel, instead of physical ports, so the packet gets looped
back to Rx path, instead of getting transmitted on the wire.

v3:
- Modify commit message to include test details.
v2:
- Add only loopback self-test.

Signed-off-by: Vishal Kulkarni <vishal@chelsio.com>
Acked-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Vishal Kulkarni and committed by
David S. Miller
7235ffae 15be4ea3

+166 -1
+8
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
··· 532 532 FW_HDR_FW_VER_BUILD_G(chip##FW_VERSION_BUILD)) 533 533 #define FW_INTFVER(chip, intf) (FW_HDR_INTFVER_##intf) 534 534 535 + struct cxgb4_ethtool_lb_test { 536 + struct completion completion; 537 + int result; 538 + int loopback; 539 + }; 540 + 535 541 struct fw_info { 536 542 u8 chip; 537 543 char *fs_name; ··· 691 685 u16 nmirrorqsets; 692 686 u32 vi_mirror_count; 693 687 struct mutex vi_mirror_mutex; /* Sync access to Mirror VI info */ 688 + struct cxgb4_ethtool_lb_test ethtool_lb; 694 689 }; 695 690 696 691 struct dentry; ··· 1602 1595 void t4_free_ofld_rxqs(struct adapter *adap, int n, struct sge_ofld_rxq *q); 1603 1596 irq_handler_t t4_intr_handler(struct adapter *adap); 1604 1597 netdev_tx_t t4_start_xmit(struct sk_buff *skb, struct net_device *dev); 1598 + int cxgb4_selftest_lb_pkt(struct net_device *netdev); 1605 1599 int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, 1606 1600 const struct pkt_gl *gl); 1607 1601 int t4_mgmt_tx(struct adapter *adap, struct sk_buff *skb);
+52
drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
··· 25 25 netdev2adap(dev)->msg_enable = val; 26 26 } 27 27 28 + enum cxgb4_ethtool_tests { 29 + CXGB4_ETHTOOL_LB_TEST, 30 + CXGB4_ETHTOOL_MAX_TEST, 31 + }; 32 + 33 + static const char cxgb4_selftest_strings[CXGB4_ETHTOOL_MAX_TEST][ETH_GSTRING_LEN] = { 34 + "Loop back test", 35 + }; 36 + 28 37 static const char * const flash_region_strings[] = { 29 38 "All", 30 39 "Firmware", ··· 175 166 ARRAY_SIZE(loopback_stats_strings); 176 167 case ETH_SS_PRIV_FLAGS: 177 168 return ARRAY_SIZE(cxgb4_priv_flags_strings); 169 + case ETH_SS_TEST: 170 + return ARRAY_SIZE(cxgb4_selftest_strings); 178 171 default: 179 172 return -EOPNOTSUPP; 180 173 } ··· 239 228 } else if (stringset == ETH_SS_PRIV_FLAGS) { 240 229 memcpy(data, cxgb4_priv_flags_strings, 241 230 sizeof(cxgb4_priv_flags_strings)); 231 + } else if (stringset == ETH_SS_TEST) { 232 + memcpy(data, cxgb4_selftest_strings, 233 + sizeof(cxgb4_selftest_strings)); 242 234 } 243 235 } 244 236 ··· 2070 2056 return 0; 2071 2057 } 2072 2058 2059 + static void cxgb4_lb_test(struct net_device *netdev, u64 *lb_status) 2060 + { 2061 + int dev_state = netif_running(netdev); 2062 + 2063 + if (dev_state) { 2064 + netif_tx_stop_all_queues(netdev); 2065 + netif_carrier_off(netdev); 2066 + } 2067 + 2068 + *lb_status = cxgb4_selftest_lb_pkt(netdev); 2069 + 2070 + if (dev_state) { 2071 + netif_tx_start_all_queues(netdev); 2072 + netif_carrier_on(netdev); 2073 + } 2074 + } 2075 + 2076 + static void cxgb4_self_test(struct net_device *netdev, 2077 + struct ethtool_test *eth_test, u64 *data) 2078 + { 2079 + struct port_info *pi = netdev_priv(netdev); 2080 + struct adapter *adap = pi->adapter; 2081 + 2082 + memset(data, 0, sizeof(u64) * CXGB4_ETHTOOL_MAX_TEST); 2083 + 2084 + if (!(adap->flags & CXGB4_FW_OK)) { 2085 + eth_test->flags |= ETH_TEST_FL_FAILED; 2086 + return; 2087 + } 2088 + 2089 + if (eth_test->flags == ETH_TEST_FL_OFFLINE) 2090 + cxgb4_lb_test(netdev, &data[CXGB4_ETHTOOL_LB_TEST]); 2091 + 2092 + if (data[CXGB4_ETHTOOL_LB_TEST]) 2093 + eth_test->flags |= ETH_TEST_FL_FAILED; 2094 + } 2095 + 2073 2096 static const struct ethtool_ops cxgb_ethtool_ops = { 2074 2097 .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 2075 2098 ETHTOOL_COALESCE_RX_MAX_FRAMES | ··· 2141 2090 .get_rxfh_indir_size = get_rss_table_size, 2142 2091 .get_rxfh = get_rss_table, 2143 2092 .set_rxfh = set_rss_table, 2093 + .self_test = cxgb4_self_test, 2144 2094 .flash_device = set_flash, 2145 2095 .get_ts_info = get_ts_info, 2146 2096 .set_dump = set_dump,
+106 -1
drivers/net/ethernet/chelsio/cxgb4/sge.c
··· 2537 2537 } 2538 2538 } 2539 2539 2540 + #define CXGB4_SELFTEST_LB_STR "CHELSIO_SELFTEST" 2541 + 2542 + int cxgb4_selftest_lb_pkt(struct net_device *netdev) 2543 + { 2544 + struct port_info *pi = netdev_priv(netdev); 2545 + struct adapter *adap = pi->adapter; 2546 + struct cxgb4_ethtool_lb_test *lb; 2547 + int ret, i = 0, pkt_len, credits; 2548 + struct fw_eth_tx_pkt_wr *wr; 2549 + struct cpl_tx_pkt_core *cpl; 2550 + u32 ctrl0, ndesc, flits; 2551 + struct sge_eth_txq *q; 2552 + u8 *sgl; 2553 + 2554 + pkt_len = ETH_HLEN + sizeof(CXGB4_SELFTEST_LB_STR); 2555 + 2556 + flits = DIV_ROUND_UP(pkt_len + sizeof(struct cpl_tx_pkt) + 2557 + sizeof(*wr), sizeof(__be64)); 2558 + ndesc = flits_to_desc(flits); 2559 + 2560 + lb = &pi->ethtool_lb; 2561 + lb->loopback = 1; 2562 + 2563 + q = &adap->sge.ethtxq[pi->first_qset]; 2564 + 2565 + reclaim_completed_tx(adap, &q->q, -1, true); 2566 + credits = txq_avail(&q->q) - ndesc; 2567 + if (unlikely(credits < 0)) 2568 + return -ENOMEM; 2569 + 2570 + wr = (void *)&q->q.desc[q->q.pidx]; 2571 + memset(wr, 0, sizeof(struct tx_desc)); 2572 + 2573 + wr->op_immdlen = htonl(FW_WR_OP_V(FW_ETH_TX_PKT_WR) | 2574 + FW_WR_IMMDLEN_V(pkt_len + 2575 + sizeof(*cpl))); 2576 + wr->equiq_to_len16 = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(flits, 2))); 2577 + wr->r3 = cpu_to_be64(0); 2578 + 2579 + cpl = (void *)(wr + 1); 2580 + sgl = (u8 *)(cpl + 1); 2581 + 2582 + ctrl0 = TXPKT_OPCODE_V(CPL_TX_PKT_XT) | TXPKT_PF_V(adap->pf) | 2583 + TXPKT_INTF_V(pi->tx_chan + 4); 2584 + 2585 + cpl->ctrl0 = htonl(ctrl0); 2586 + cpl->pack = htons(0); 2587 + cpl->len = htons(pkt_len); 2588 + cpl->ctrl1 = cpu_to_be64(TXPKT_L4CSUM_DIS_F | TXPKT_IPCSUM_DIS_F); 2589 + 2590 + eth_broadcast_addr(sgl); 2591 + i += ETH_ALEN; 2592 + ether_addr_copy(&sgl[i], netdev->dev_addr); 2593 + i += ETH_ALEN; 2594 + 2595 + snprintf(&sgl[i], sizeof(CXGB4_SELFTEST_LB_STR), "%s", 2596 + CXGB4_SELFTEST_LB_STR); 2597 + 2598 + init_completion(&lb->completion); 2599 + txq_advance(&q->q, ndesc); 2600 + cxgb4_ring_tx_db(adap, &q->q, ndesc); 2601 + 2602 + /* wait for the pkt to return */ 2603 + ret = wait_for_completion_timeout(&lb->completion, 10 * HZ); 2604 + if (!ret) 2605 + ret = -ETIMEDOUT; 2606 + else 2607 + ret = lb->result; 2608 + 2609 + lb->loopback = 0; 2610 + 2611 + return ret; 2612 + } 2613 + 2540 2614 /** 2541 2615 * ctrl_xmit - send a packet through an SGE control Tx queue 2542 2616 * @q: the control queue ··· 3487 3413 t4_sge_eth_txq_egress_update(adapter, txq, -1); 3488 3414 } 3489 3415 3416 + static int cxgb4_validate_lb_pkt(struct port_info *pi, const struct pkt_gl *si) 3417 + { 3418 + struct adapter *adap = pi->adapter; 3419 + struct cxgb4_ethtool_lb_test *lb; 3420 + struct sge *s = &adap->sge; 3421 + struct net_device *netdev; 3422 + u8 *data; 3423 + int i; 3424 + 3425 + netdev = adap->port[pi->port_id]; 3426 + lb = &pi->ethtool_lb; 3427 + data = si->va + s->pktshift; 3428 + 3429 + i = ETH_ALEN; 3430 + if (!ether_addr_equal(data + i, netdev->dev_addr)) 3431 + return -1; 3432 + 3433 + i += ETH_ALEN; 3434 + if (strcmp(&data[i], CXGB4_SELFTEST_LB_STR)) 3435 + lb->result = -EIO; 3436 + 3437 + complete(&lb->completion); 3438 + return 0; 3439 + } 3440 + 3490 3441 /** 3491 3442 * t4_ethrx_handler - process an ingress ethernet packet 3492 3443 * @q: the response queue that received the packet ··· 3535 3436 struct port_info *pi; 3536 3437 int ret = 0; 3537 3438 3439 + pi = netdev_priv(q->netdev); 3538 3440 /* If we're looking at TX Queue CIDX Update, handle that separately 3539 3441 * and return. 3540 3442 */ ··· 3563 3463 if (err_vec) 3564 3464 rxq->stats.bad_rx_pkts++; 3565 3465 3466 + if (unlikely(pi->ethtool_lb.loopback && pkt->iff >= NCHAN)) { 3467 + ret = cxgb4_validate_lb_pkt(pi, si); 3468 + if (!ret) 3469 + return 0; 3470 + } 3471 + 3566 3472 if (((pkt->l2info & htonl(RXF_TCP_F)) || 3567 3473 tnl_hdr_len) && 3568 3474 (q->netdev->features & NETIF_F_GRO) && csum_ok && !pkt->ip_frag) { ··· 3582 3476 rxq->stats.rx_drops++; 3583 3477 return 0; 3584 3478 } 3585 - pi = netdev_priv(q->netdev); 3586 3479 3587 3480 /* Handle PTP Event Rx packet */ 3588 3481 if (unlikely(pi->ptp_enable)) {