Skip to content

Commit

Permalink
net/mlx5e: Add support for loopback selftest
Browse files Browse the repository at this point in the history
Extend the self diagnostic tests to support loopback test.

The loopback test doesn't require the offline flag, it will use the
generic dev_queue_xmit and a dedicated packet_type to capture and verify
mlx5e selftest loopback packets.

Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: Kamal Heib <kamalh@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Saeed Mahameed authored and David S. Miller committed Nov 28, 2016
1 parent d605d66 commit 0952da7
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 3 deletions.
3 changes: 2 additions & 1 deletion drivers/net/ethernet/mellanox/mlx5/core/en.h
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,8 @@ void mlx5e_destroy_tir(struct mlx5_core_dev *mdev,
struct mlx5e_tir *tir);
int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev);
void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev);
int mlx5e_refresh_tirs_self_loopback_enable(struct mlx5_core_dev *mdev);
int mlx5e_refresh_tirs_self_loopback(struct mlx5_core_dev *mdev,
bool enable_uc_lb);

struct mlx5_eswitch_rep;
int mlx5e_vport_rep_load(struct mlx5_eswitch *esw,
Expand Down
7 changes: 6 additions & 1 deletion drivers/net/ethernet/mellanox/mlx5/core/en_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev)
mlx5_unmap_free_uar(mdev, &res->cq_uar);
}

int mlx5e_refresh_tirs_self_loopback_enable(struct mlx5_core_dev *mdev)
int mlx5e_refresh_tirs_self_loopback(struct mlx5_core_dev *mdev,
bool enable_uc_lb)
{
struct mlx5e_tir *tir;
void *in;
Expand All @@ -149,6 +150,10 @@ int mlx5e_refresh_tirs_self_loopback_enable(struct mlx5_core_dev *mdev)
if (!in)
return -ENOMEM;

if (enable_uc_lb)
MLX5_SET(modify_tir_in, in, ctx.self_lb_block,
MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST_);

MLX5_SET(modify_tir_in, in, bitmask.self_lb_en, 1);

list_for_each_entry(tir, &mdev->mlx5e_res.td.tirs_list, list) {
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/ethernet/mellanox/mlx5/core/en_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2136,7 +2136,7 @@ int mlx5e_open_locked(struct net_device *netdev)
goto err_clear_state_opened_flag;
}

err = mlx5e_refresh_tirs_self_loopback_enable(priv->mdev);
err = mlx5e_refresh_tirs_self_loopback(priv->mdev, false);
if (err) {
netdev_err(netdev, "%s: mlx5e_refresh_tirs_self_loopback_enable failed, %d\n",
__func__, err);
Expand Down
218 changes: 218 additions & 0 deletions drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,24 @@
* SOFTWARE.
*/

#include <linux/ip.h>
#include <linux/udp.h>
#include <net/udp.h>
#include "en.h"

enum {
MLX5E_ST_LINK_STATE,
MLX5E_ST_LINK_SPEED,
MLX5E_ST_HEALTH_INFO,
MLX5E_ST_LOOPBACK,
MLX5E_ST_NUM,
};

const char mlx5e_self_tests[MLX5E_ST_NUM][ETH_GSTRING_LEN] = {
"Link Test",
"Speed Test",
"Health Test",
"Loopback Test",
};

int mlx5e_self_test_num(struct mlx5e_priv *priv)
Expand Down Expand Up @@ -88,10 +93,223 @@ static int mlx5e_test_link_speed(struct mlx5e_priv *priv)
return 1;
}

/* loopback test */
#define MLX5E_TEST_PKT_SIZE (MLX5_MPWRQ_SMALL_PACKET_THRESHOLD - NET_IP_ALIGN)
static const char mlx5e_test_text[ETH_GSTRING_LEN] = "MLX5E SELF TEST";
#define MLX5E_TEST_MAGIC 0x5AEED15C001ULL

struct mlx5ehdr {
__be32 version;
__be64 magic;
char text[ETH_GSTRING_LEN];
};

static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv)
{
struct sk_buff *skb = NULL;
struct mlx5ehdr *mlxh;
struct ethhdr *ethh;
struct udphdr *udph;
struct iphdr *iph;
int datalen, iplen;

datalen = MLX5E_TEST_PKT_SIZE -
(sizeof(*ethh) + sizeof(*iph) + sizeof(*udph));

skb = netdev_alloc_skb(priv->netdev, MLX5E_TEST_PKT_SIZE);
if (!skb) {
netdev_err(priv->netdev, "\tFailed to alloc loopback skb\n");
return NULL;
}

prefetchw(skb->data);
skb_reserve(skb, NET_IP_ALIGN);

/* Reserve for ethernet and IP header */
ethh = (struct ethhdr *)skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);

skb_set_network_header(skb, skb->len);
iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));

skb_set_transport_header(skb, skb->len);
udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));

/* Fill ETH header */
ether_addr_copy(ethh->h_dest, priv->netdev->dev_addr);
eth_zero_addr(ethh->h_source);
ethh->h_proto = htons(ETH_P_IP);

/* Fill UDP header */
udph->source = htons(9);
udph->dest = htons(9); /* Discard Protocol */
udph->len = htons(datalen + sizeof(struct udphdr));
udph->check = 0;

/* Fill IP header */
iph->ihl = 5;
iph->ttl = 32;
iph->version = 4;
iph->protocol = IPPROTO_UDP;
iplen = sizeof(struct iphdr) + sizeof(struct udphdr) + datalen;
iph->tot_len = htons(iplen);
iph->frag_off = 0;
iph->saddr = 0;
iph->daddr = 0;
iph->tos = 0;
iph->id = 0;
ip_send_check(iph);

/* Fill test header and data */
mlxh = (struct mlx5ehdr *)skb_put(skb, sizeof(*mlxh));
mlxh->version = 0;
mlxh->magic = cpu_to_be64(MLX5E_TEST_MAGIC);
strlcpy(mlxh->text, mlx5e_test_text, sizeof(mlxh->text));
datalen -= sizeof(*mlxh);
memset(skb_put(skb, datalen), 0, datalen);

skb->csum = 0;
skb->ip_summed = CHECKSUM_PARTIAL;
udp4_hwcsum(skb, iph->saddr, iph->daddr);

skb->protocol = htons(ETH_P_IP);
skb->pkt_type = PACKET_HOST;
skb->dev = priv->netdev;

return skb;
}

struct mlx5e_lbt_priv {
struct packet_type pt;
struct completion comp;
bool loopback_ok;
};

static int
mlx5e_test_loopback_validate(struct sk_buff *skb,
struct net_device *ndev,
struct packet_type *pt,
struct net_device *orig_ndev)
{
struct mlx5e_lbt_priv *lbtp = pt->af_packet_priv;
struct mlx5ehdr *mlxh;
struct ethhdr *ethh;
struct udphdr *udph;
struct iphdr *iph;

/* We are only going to peek, no need to clone the SKB */
if (skb->protocol != htons(ETH_P_IP))
goto out;

if (MLX5E_TEST_PKT_SIZE - ETH_HLEN > skb_headlen(skb))
goto out;

ethh = (struct ethhdr *)skb_mac_header(skb);
if (!ether_addr_equal(ethh->h_dest, orig_ndev->dev_addr))
goto out;

iph = ip_hdr(skb);
if (iph->protocol != IPPROTO_UDP)
goto out;

udph = udp_hdr(skb);
if (udph->dest != htons(9))
goto out;

mlxh = (struct mlx5ehdr *)((char *)udph + sizeof(*udph));
if (mlxh->magic != cpu_to_be64(MLX5E_TEST_MAGIC))
goto out; /* so close ! */

/* bingo */
lbtp->loopback_ok = true;
complete(&lbtp->comp);
out:
kfree_skb(skb);
return 0;
}

static int mlx5e_test_loopback_setup(struct mlx5e_priv *priv,
struct mlx5e_lbt_priv *lbtp)
{
int err = 0;

err = mlx5e_refresh_tirs_self_loopback(priv->mdev, true);
if (err) {
netdev_err(priv->netdev,
"\tFailed to enable UC loopback err(%d)\n", err);
return err;
}

lbtp->loopback_ok = false;
init_completion(&lbtp->comp);

lbtp->pt.type = htons(ETH_P_ALL);
lbtp->pt.func = mlx5e_test_loopback_validate;
lbtp->pt.dev = priv->netdev;
lbtp->pt.af_packet_priv = lbtp;
dev_add_pack(&lbtp->pt);
return err;
}

static void mlx5e_test_loopback_cleanup(struct mlx5e_priv *priv,
struct mlx5e_lbt_priv *lbtp)
{
dev_remove_pack(&lbtp->pt);
mlx5e_refresh_tirs_self_loopback(priv->mdev, false);
}

#define MLX5E_LB_VERIFY_TIMEOUT (msecs_to_jiffies(200))
static int mlx5e_test_loopback(struct mlx5e_priv *priv)
{
struct mlx5e_lbt_priv *lbtp;
struct sk_buff *skb = NULL;
int err;

if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
netdev_err(priv->netdev,
"\tCan't perform loobpack test while device is down\n");
return -ENODEV;
}

lbtp = kzalloc(sizeof(*lbtp), GFP_KERNEL);
if (!lbtp)
return -ENOMEM;
lbtp->loopback_ok = false;

err = mlx5e_test_loopback_setup(priv, lbtp);
if (err)
goto out;

skb = mlx5e_test_get_udp_skb(priv);
if (!skb) {
err = -ENOMEM;
goto cleanup;
}

skb_set_queue_mapping(skb, 0);
err = dev_queue_xmit(skb);
if (err) {
netdev_err(priv->netdev,
"\tFailed to xmit loopback packet err(%d)\n",
err);
goto cleanup;
}

wait_for_completion_timeout(&lbtp->comp, MLX5E_LB_VERIFY_TIMEOUT);
err = !lbtp->loopback_ok;

cleanup:
mlx5e_test_loopback_cleanup(priv, lbtp);
out:
kfree(lbtp);
return err;
}

static int (*mlx5e_st_func[MLX5E_ST_NUM])(struct mlx5e_priv *) = {
mlx5e_test_link_state,
mlx5e_test_link_speed,
mlx5e_test_health_info,
mlx5e_test_loopback
};

void mlx5e_self_test(struct net_device *ndev, struct ethtool_test *etest,
Expand Down

0 comments on commit 0952da7

Please sign in to comment.