Skip to content

Commit

Permalink
net: hns3: Add mac loopback selftest support in hns3 driver
Browse files Browse the repository at this point in the history
This patch adds mac loopback selftest support for ethtool cmd
by checking if a transmitted packet can be received correctly
when mac loopback is enabled.

Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Yunsheng Lin authored and davem330 committed Oct 22, 2017
1 parent d43e5ac commit c39c4d9
Show file tree
Hide file tree
Showing 2 changed files with 327 additions and 0 deletions.
54 changes: 54 additions & 0 deletions drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3149,6 +3149,59 @@ static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable)
"mac enable fail, ret =%d.\n", ret);
}

static int hclge_set_loopback(struct hnae3_handle *handle,
enum hnae3_loop loop_mode, bool en)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_config_mac_mode_cmd *req;
struct hclge_dev *hdev = vport->back;
struct hclge_desc desc;
u32 loop_en;
int ret;

switch (loop_mode) {
case HNAE3_MAC_INTER_LOOP_MAC:
req = (struct hclge_config_mac_mode_cmd *)&desc.data[0];
/* 1 Read out the MAC mode config at first */
hclge_cmd_setup_basic_desc(&desc,
HCLGE_OPC_CONFIG_MAC_MODE,
true);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"mac loopback get fail, ret =%d.\n",
ret);
return ret;
}

/* 2 Then setup the loopback flag */
loop_en = le32_to_cpu(req->txrx_pad_fcs_loop_en);
if (en)
hnae_set_bit(loop_en, HCLGE_MAC_APP_LP_B, 1);
else
hnae_set_bit(loop_en, HCLGE_MAC_APP_LP_B, 0);

req->txrx_pad_fcs_loop_en = cpu_to_le32(loop_en);

/* 3 Config mac work mode with loopback flag
* and its original configure parameters
*/
hclge_cmd_reuse_desc(&desc, false);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"mac loopback set fail, ret =%d.\n", ret);
break;
default:
ret = -ENOTSUPP;
dev_err(&hdev->pdev->dev,
"loop_mode %d is not supported\n", loop_mode);
break;
}

return ret;
}

static int hclge_tqp_enable(struct hclge_dev *hdev, int tqp_id,
int stream_id, bool enable)
{
Expand Down Expand Up @@ -4485,6 +4538,7 @@ static const struct hnae3_ae_ops hclge_ops = {
.unmap_ring_from_vector = hclge_unmap_ring_from_vector,
.get_vector = hclge_get_vector,
.set_promisc_mode = hclge_set_promisc_mode,
.set_loopback = hclge_set_loopback,
.start = hclge_ae_start,
.stop = hclge_ae_stop,
.get_status = hclge_get_status,
Expand Down
273 changes: 273 additions & 0 deletions drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ static const struct hns3_stats hns3_rxq_stats[] = {

#define HNS3_TQP_STATS_COUNT (HNS3_TXQ_STATS_COUNT + HNS3_RXQ_STATS_COUNT)

#define HNS3_SELF_TEST_TPYE_NUM 1
#define HNS3_NIC_LB_TEST_PKT_NUM 1
#define HNS3_NIC_LB_TEST_RING_ID 0
#define HNS3_NIC_LB_TEST_PACKET_SIZE 128

/* Nic loopback test err */
#define HNS3_NIC_LB_TEST_NO_MEM_ERR 1
#define HNS3_NIC_LB_TEST_TX_CNT_ERR 2
#define HNS3_NIC_LB_TEST_RX_CNT_ERR 3

struct hns3_link_mode_mapping {
u32 hns3_link_mode;
u32 ethtool_link_mode;
Expand All @@ -77,6 +87,268 @@ static const struct hns3_link_mode_mapping hns3_lm_map[] = {
{HNS3_LM_1000BASET_FULL_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT},
};

static int hns3_lp_setup(struct net_device *ndev, enum hnae3_loop loop)
{
struct hnae3_handle *h = hns3_get_handle(ndev);
int ret;

if (!h->ae_algo->ops->set_loopback ||
!h->ae_algo->ops->set_promisc_mode)
return -EOPNOTSUPP;

switch (loop) {
case HNAE3_MAC_INTER_LOOP_MAC:
ret = h->ae_algo->ops->set_loopback(h, loop, true);
break;
case HNAE3_MAC_LOOP_NONE:
ret = h->ae_algo->ops->set_loopback(h,
HNAE3_MAC_INTER_LOOP_MAC, false);
break;
default:
ret = -ENOTSUPP;
break;
}

if (ret)
return ret;

if (loop == HNAE3_MAC_LOOP_NONE)
h->ae_algo->ops->set_promisc_mode(h, ndev->flags & IFF_PROMISC);
else
h->ae_algo->ops->set_promisc_mode(h, 1);

return ret;
}

static int hns3_lp_up(struct net_device *ndev, enum hnae3_loop loop_mode)
{
struct hnae3_handle *h = hns3_get_handle(ndev);
int ret;

if (!h->ae_algo->ops->start)
return -EOPNOTSUPP;

ret = h->ae_algo->ops->start(h);
if (ret) {
netdev_err(ndev,
"hns3_lb_up ae start return error: %d\n", ret);
return ret;
}

ret = hns3_lp_setup(ndev, loop_mode);
usleep_range(10000, 20000);

return ret;
}

static int hns3_lp_down(struct net_device *ndev)
{
struct hnae3_handle *h = hns3_get_handle(ndev);
int ret;

if (!h->ae_algo->ops->stop)
return -EOPNOTSUPP;

ret = hns3_lp_setup(ndev, HNAE3_MAC_LOOP_NONE);
if (ret) {
netdev_err(ndev, "lb_setup return error: %d\n", ret);
return ret;
}

h->ae_algo->ops->stop(h);
usleep_range(10000, 20000);

return 0;
}

static void hns3_lp_setup_skb(struct sk_buff *skb)
{
struct net_device *ndev = skb->dev;
unsigned char *packet;
struct ethhdr *ethh;
unsigned int i;

skb_reserve(skb, NET_IP_ALIGN);
ethh = skb_put(skb, sizeof(struct ethhdr));
packet = skb_put(skb, HNS3_NIC_LB_TEST_PACKET_SIZE);

memcpy(ethh->h_dest, ndev->dev_addr, ETH_ALEN);
eth_zero_addr(ethh->h_source);
ethh->h_proto = htons(ETH_P_ARP);
skb_reset_mac_header(skb);

for (i = 0; i < HNS3_NIC_LB_TEST_PACKET_SIZE; i++)
packet[i] = (unsigned char)(i & 0xff);
}

static void hns3_lb_check_skb_data(struct hns3_enet_ring *ring,
struct sk_buff *skb)
{
struct hns3_enet_tqp_vector *tqp_vector = ring->tqp_vector;
unsigned char *packet = skb->data;
u32 i;

for (i = 0; i < skb->len; i++)
if (packet[i] != (unsigned char)(i & 0xff))
break;

/* The packet is correctly received */
if (i == skb->len)
tqp_vector->rx_group.total_packets++;
else
print_hex_dump(KERN_ERR, "selftest:", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb->len, true);

dev_kfree_skb_any(skb);
}

static u32 hns3_lb_check_rx_ring(struct hns3_nic_priv *priv, u32 budget)
{
struct hnae3_handle *h = priv->ae_handle;
struct hnae3_knic_private_info *kinfo;
u32 i, rcv_good_pkt_total = 0;

kinfo = &h->kinfo;
for (i = kinfo->num_tqps; i < kinfo->num_tqps * 2; i++) {
struct hns3_enet_ring *ring = priv->ring_data[i].ring;
struct hns3_enet_ring_group *rx_group;
u64 pre_rx_pkt;

rx_group = &ring->tqp_vector->rx_group;
pre_rx_pkt = rx_group->total_packets;

hns3_clean_rx_ring(ring, budget, hns3_lb_check_skb_data);

rcv_good_pkt_total += (rx_group->total_packets - pre_rx_pkt);
rx_group->total_packets = pre_rx_pkt;
}
return rcv_good_pkt_total;
}

static void hns3_lb_clear_tx_ring(struct hns3_nic_priv *priv, u32 start_ringid,
u32 end_ringid, u32 budget)
{
u32 i;

for (i = start_ringid; i <= end_ringid; i++) {
struct hns3_enet_ring *ring = priv->ring_data[i].ring;

hns3_clean_tx_ring(ring, budget);
}
}

/**
* hns3_lp_run_test - run loopback test
* @ndev: net device
* @mode: loopback type
*/
static int hns3_lp_run_test(struct net_device *ndev, enum hnae3_loop mode)
{
struct hns3_nic_priv *priv = netdev_priv(ndev);
struct sk_buff *skb;
u32 i, good_cnt;
int ret_val = 0;

skb = alloc_skb(HNS3_NIC_LB_TEST_PACKET_SIZE + ETH_HLEN + NET_IP_ALIGN,
GFP_KERNEL);
if (!skb)
return HNS3_NIC_LB_TEST_NO_MEM_ERR;

skb->dev = ndev;
hns3_lp_setup_skb(skb);
skb->queue_mapping = HNS3_NIC_LB_TEST_RING_ID;

good_cnt = 0;
for (i = 0; i < HNS3_NIC_LB_TEST_PKT_NUM; i++) {
netdev_tx_t tx_ret;

skb_get(skb);
tx_ret = hns3_nic_net_xmit(skb, ndev);
if (tx_ret == NETDEV_TX_OK)
good_cnt++;
else
netdev_err(ndev, "hns3_lb_run_test xmit failed: %d\n",
tx_ret);
}
if (good_cnt != HNS3_NIC_LB_TEST_PKT_NUM) {
ret_val = HNS3_NIC_LB_TEST_TX_CNT_ERR;
netdev_err(ndev, "mode %d sent fail, cnt=0x%x, budget=0x%x\n",
mode, good_cnt, HNS3_NIC_LB_TEST_PKT_NUM);
goto out;
}

/* Allow 200 milliseconds for packets to go from Tx to Rx */
msleep(200);

good_cnt = hns3_lb_check_rx_ring(priv, HNS3_NIC_LB_TEST_PKT_NUM);
if (good_cnt != HNS3_NIC_LB_TEST_PKT_NUM) {
ret_val = HNS3_NIC_LB_TEST_RX_CNT_ERR;
netdev_err(ndev, "mode %d recv fail, cnt=0x%x, budget=0x%x\n",
mode, good_cnt, HNS3_NIC_LB_TEST_PKT_NUM);
}

out:
hns3_lb_clear_tx_ring(priv, HNS3_NIC_LB_TEST_RING_ID,
HNS3_NIC_LB_TEST_RING_ID,
HNS3_NIC_LB_TEST_PKT_NUM);

kfree_skb(skb);
return ret_val;
}

/**
* hns3_nic_self_test - self test
* @ndev: net device
* @eth_test: test cmd
* @data: test result
*/
static void hns3_self_test(struct net_device *ndev,
struct ethtool_test *eth_test, u64 *data)
{
struct hns3_nic_priv *priv = netdev_priv(ndev);
struct hnae3_handle *h = priv->ae_handle;
int st_param[HNS3_SELF_TEST_TPYE_NUM][2];
bool if_running = netif_running(ndev);
int test_index = 0;
u32 i;

/* Only do offline selftest, or pass by default */
if (eth_test->flags != ETH_TEST_FL_OFFLINE)
return;

st_param[HNAE3_MAC_INTER_LOOP_MAC][0] = HNAE3_MAC_INTER_LOOP_MAC;
st_param[HNAE3_MAC_INTER_LOOP_MAC][1] =
h->flags & HNAE3_SUPPORT_MAC_LOOPBACK;

if (if_running)
dev_close(ndev);

set_bit(HNS3_NIC_STATE_TESTING, &priv->state);

for (i = 0; i < HNS3_SELF_TEST_TPYE_NUM; i++) {
enum hnae3_loop loop_type = (enum hnae3_loop)st_param[i][0];

if (!st_param[i][1])
continue;

data[test_index] = hns3_lp_up(ndev, loop_type);
if (!data[test_index]) {
data[test_index] = hns3_lp_run_test(ndev, loop_type);
hns3_lp_down(ndev);
}

if (data[test_index])
eth_test->flags |= ETH_TEST_FL_FAILED;

test_index++;
}

clear_bit(HNS3_NIC_STATE_TESTING, &priv->state);

if (if_running)
dev_open(ndev);
}

static void hns3_driv_to_eth_caps(u32 caps, struct ethtool_link_ksettings *cmd,
bool is_advertised)
{
Expand Down Expand Up @@ -553,6 +825,7 @@ static int hns3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
}

static const struct ethtool_ops hns3_ethtool_ops = {
.self_test = hns3_self_test,
.get_drvinfo = hns3_get_drvinfo,
.get_link = hns3_get_link,
.get_ringparam = hns3_get_ringparam,
Expand Down

0 comments on commit c39c4d9

Please sign in to comment.