2016-11-27 08:02:09 -07:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016, Mellanox Technologies, Ltd. All rights reserved.
|
|
|
|
*
|
|
|
|
* This software is available to you under a choice of one of two
|
|
|
|
* licenses. You may choose to be licensed under the terms of the GNU
|
|
|
|
* General Public License (GPL) Version 2, available from the file
|
|
|
|
* COPYING in the main directory of this source tree, or the
|
|
|
|
* OpenIB.org BSD license below:
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or
|
|
|
|
* without modification, are permitted provided that the following
|
|
|
|
* conditions are met:
|
|
|
|
*
|
|
|
|
* - Redistributions of source code must retain the above
|
|
|
|
* copyright notice, this list of conditions and the following
|
|
|
|
* disclaimer.
|
|
|
|
*
|
|
|
|
* - Redistributions in binary form must reproduce the above
|
|
|
|
* copyright notice, this list of conditions and the following
|
|
|
|
* disclaimer in the documentation and/or other materials
|
|
|
|
* provided with the distribution.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
* SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2017-02-22 08:20:11 -07:00
|
|
|
#include <linux/prefetch.h>
|
2016-11-27 08:02:10 -07:00
|
|
|
#include <linux/ip.h>
|
|
|
|
#include <linux/udp.h>
|
|
|
|
#include <net/udp.h>
|
2016-11-27 08:02:09 -07:00
|
|
|
#include "en.h"
|
|
|
|
|
|
|
|
enum {
|
|
|
|
MLX5E_ST_LINK_STATE,
|
|
|
|
MLX5E_ST_LINK_SPEED,
|
|
|
|
MLX5E_ST_HEALTH_INFO,
|
2016-11-30 14:05:39 -07:00
|
|
|
#ifdef CONFIG_INET
|
2016-11-27 08:02:10 -07:00
|
|
|
MLX5E_ST_LOOPBACK,
|
2016-11-30 14:05:39 -07:00
|
|
|
#endif
|
2016-11-27 08:02:09 -07:00
|
|
|
MLX5E_ST_NUM,
|
|
|
|
};
|
|
|
|
|
|
|
|
const char mlx5e_self_tests[MLX5E_ST_NUM][ETH_GSTRING_LEN] = {
|
|
|
|
"Link Test",
|
|
|
|
"Speed Test",
|
|
|
|
"Health Test",
|
2016-11-30 14:05:39 -07:00
|
|
|
#ifdef CONFIG_INET
|
2016-11-27 08:02:10 -07:00
|
|
|
"Loopback Test",
|
2016-11-30 14:05:39 -07:00
|
|
|
#endif
|
2016-11-27 08:02:09 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
int mlx5e_self_test_num(struct mlx5e_priv *priv)
|
|
|
|
{
|
|
|
|
return ARRAY_SIZE(mlx5e_self_tests);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mlx5e_test_health_info(struct mlx5e_priv *priv)
|
|
|
|
{
|
|
|
|
struct mlx5_core_health *health = &priv->mdev->priv.health;
|
|
|
|
|
|
|
|
return health->sick ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mlx5e_test_link_state(struct mlx5e_priv *priv)
|
|
|
|
{
|
|
|
|
u8 port_state;
|
|
|
|
|
|
|
|
if (!netif_carrier_ok(priv->netdev))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
port_state = mlx5_query_vport_state(priv->mdev, MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT, 0);
|
|
|
|
return port_state == VPORT_STATE_UP ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mlx5e_test_link_speed(struct mlx5e_priv *priv)
|
|
|
|
{
|
|
|
|
u32 out[MLX5_ST_SZ_DW(ptys_reg)];
|
|
|
|
u32 eth_proto_oper;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!netif_carrier_ok(priv->netdev))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (mlx5_query_port_ptys(priv->mdev, out, sizeof(out), MLX5_PTYS_EN, 1))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
eth_proto_oper = MLX5_GET(ptys_reg, out, eth_proto_oper);
|
|
|
|
for (i = 0; i < MLX5E_LINK_MODES_NUMBER; i++) {
|
|
|
|
if (eth_proto_oper & MLX5E_PROT_MASK(i))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-11-30 14:05:39 -07:00
|
|
|
#ifdef CONFIG_INET
|
2016-11-27 08:02:10 -07:00
|
|
|
/* 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 */
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:23 -06:00
|
|
|
ethh = skb_push(skb, ETH_HLEN);
|
2016-11-27 08:02:10 -07:00
|
|
|
skb_reset_mac_header(skb);
|
|
|
|
|
|
|
|
skb_set_network_header(skb, skb->len);
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:21 -06:00
|
|
|
iph = skb_put(skb, sizeof(struct iphdr));
|
2016-11-27 08:02:10 -07:00
|
|
|
|
|
|
|
skb_set_transport_header(skb, skb->len);
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:21 -06:00
|
|
|
udph = skb_put(skb, sizeof(struct udphdr));
|
2016-11-27 08:02:10 -07:00
|
|
|
|
|
|
|
/* 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 */
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:21 -06:00
|
|
|
mlxh = skb_put(skb, sizeof(*mlxh));
|
2016-11-27 08:02:10 -07:00
|
|
|
mlxh->version = 0;
|
|
|
|
mlxh->magic = cpu_to_be64(MLX5E_TEST_MAGIC);
|
|
|
|
strlcpy(mlxh->text, mlx5e_test_text, sizeof(mlxh->text));
|
|
|
|
datalen -= sizeof(*mlxh);
|
networking: convert many more places to skb_put_zero()
There were many places that my previous spatch didn't find,
as pointed out by yuan linyu in various patches.
The following spatch found many more and also removes the
now unnecessary casts:
@@
identifier p, p2;
expression len;
expression skb;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_zero(skb, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_zero(skb, len);
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, len);
|
-memset(p, 0, len);
)
@@
type t, t2;
identifier p, p2;
expression skb;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, sizeof(*p));
|
-memset(p, 0, sizeof(*p));
)
@@
expression skb, len;
@@
-memset(skb_put(skb, len), 0, len);
+skb_put_zero(skb, len);
Apply it to the tree (with one manual fixup to keep the
comment in vxlan.c, which spatch removed.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:19 -06:00
|
|
|
skb_put_zero(skb, datalen);
|
2016-11-27 08:02:10 -07:00
|
|
|
|
|
|
|
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 (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;
|
|
|
|
|
2016-12-20 08:30:20 -07:00
|
|
|
err = mlx5e_refresh_tirs(priv, true);
|
|
|
|
if (err)
|
2016-11-27 08:02:10 -07:00
|
|
|
return err;
|
|
|
|
|
|
|
|
lbtp->loopback_ok = false;
|
|
|
|
init_completion(&lbtp->comp);
|
|
|
|
|
2017-03-10 05:33:05 -07:00
|
|
|
lbtp->pt.type = htons(ETH_P_IP);
|
2016-11-27 08:02:10 -07:00
|
|
|
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);
|
2016-12-20 08:30:20 -07:00
|
|
|
mlx5e_refresh_tirs(priv, false);
|
2016-11-27 08:02:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
2016-11-30 14:05:39 -07:00
|
|
|
#endif
|
2016-11-27 08:02:10 -07:00
|
|
|
|
2016-11-27 08:02:09 -07:00
|
|
|
static int (*mlx5e_st_func[MLX5E_ST_NUM])(struct mlx5e_priv *) = {
|
|
|
|
mlx5e_test_link_state,
|
|
|
|
mlx5e_test_link_speed,
|
|
|
|
mlx5e_test_health_info,
|
2016-11-30 14:05:39 -07:00
|
|
|
#ifdef CONFIG_INET
|
|
|
|
mlx5e_test_loopback,
|
|
|
|
#endif
|
2016-11-27 08:02:09 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
void mlx5e_self_test(struct net_device *ndev, struct ethtool_test *etest,
|
|
|
|
u64 *buf)
|
|
|
|
{
|
|
|
|
struct mlx5e_priv *priv = netdev_priv(ndev);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
memset(buf, 0, sizeof(u64) * MLX5E_ST_NUM);
|
|
|
|
|
|
|
|
mutex_lock(&priv->state_lock);
|
|
|
|
netdev_info(ndev, "Self test begin..\n");
|
|
|
|
|
|
|
|
for (i = 0; i < MLX5E_ST_NUM; i++) {
|
|
|
|
netdev_info(ndev, "\t[%d] %s start..\n",
|
|
|
|
i, mlx5e_self_tests[i]);
|
|
|
|
buf[i] = mlx5e_st_func[i](priv);
|
|
|
|
netdev_info(ndev, "\t[%d] %s end: result(%lld)\n",
|
|
|
|
i, mlx5e_self_tests[i], buf[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&priv->state_lock);
|
|
|
|
|
|
|
|
for (i = 0; i < MLX5E_ST_NUM; i++) {
|
|
|
|
if (buf[i]) {
|
|
|
|
etest->flags |= ETH_TEST_FL_FAILED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
netdev_info(ndev, "Self test out: status flags(0x%x)\n",
|
|
|
|
etest->flags);
|
|
|
|
}
|