mlx4_en: Added self diagnostics test implementation
The selftest includes 5 features: 1. Interrupt test: Executing commands and receiving command completion on all our interrupt vectors. 2. Link test: Verifying we are connected to valid link partner. 3. Speed test: Check that we negotiated link speed correctly. 4. Registers test: Activate HW health check command. 5. Loopback test: Send a packet on loopback interface and catch it on RX side. Signed-off-by: Yevgeny Petrilin <yevgenyp@mellanox.co.il> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3005ad40b9
								
							
						
					
					
						commit
						e7c1c2c462
					
				| @ -6,4 +6,4 @@ mlx4_core-y :=	alloc.o catas.o cmd.o cq.o eq.o fw.o icm.o intf.o main.o mcg.o \ | |||||||
| obj-$(CONFIG_MLX4_EN)               += mlx4_en.o | obj-$(CONFIG_MLX4_EN)               += mlx4_en.o | ||||||
| 
 | 
 | ||||||
| mlx4_en-y := 	en_main.o en_tx.o en_rx.o en_ethtool.o en_port.o en_cq.o \
 | mlx4_en-y := 	en_main.o en_tx.o en_rx.o en_ethtool.o en_port.o en_cq.o \
 | ||||||
| 		en_resources.o en_netdev.o | 		en_resources.o en_netdev.o en_selftest.o | ||||||
|  | |||||||
| @ -125,6 +125,14 @@ static const char main_strings[][ETH_GSTRING_LEN] = { | |||||||
| #define NUM_MAIN_STATS	21 | #define NUM_MAIN_STATS	21 | ||||||
| #define NUM_ALL_STATS	(NUM_MAIN_STATS + NUM_PORT_STATS + NUM_PKT_STATS + NUM_PERF_STATS) | #define NUM_ALL_STATS	(NUM_MAIN_STATS + NUM_PORT_STATS + NUM_PKT_STATS + NUM_PERF_STATS) | ||||||
| 
 | 
 | ||||||
|  | static const char mlx4_en_test_names[][ETH_GSTRING_LEN]= { | ||||||
|  | 	"Interupt Test", | ||||||
|  | 	"Link Test", | ||||||
|  | 	"Speed Test", | ||||||
|  | 	"Register Test", | ||||||
|  | 	"Loopback Test", | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| static u32 mlx4_en_get_msglevel(struct net_device *dev) | static u32 mlx4_en_get_msglevel(struct net_device *dev) | ||||||
| { | { | ||||||
| 	return ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable; | 	return ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable; | ||||||
| @ -146,10 +154,15 @@ static int mlx4_en_get_sset_count(struct net_device *dev, int sset) | |||||||
| { | { | ||||||
| 	struct mlx4_en_priv *priv = netdev_priv(dev); | 	struct mlx4_en_priv *priv = netdev_priv(dev); | ||||||
| 
 | 
 | ||||||
| 	if (sset != ETH_SS_STATS) | 	switch (sset) { | ||||||
|  | 	case ETH_SS_STATS: | ||||||
|  | 		return NUM_ALL_STATS + | ||||||
|  | 			(priv->tx_ring_num + priv->rx_ring_num) * 2; | ||||||
|  | 	case ETH_SS_TEST: | ||||||
|  | 		return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.loopback_support) * 2; | ||||||
|  | 	default: | ||||||
| 		return -EOPNOTSUPP; | 		return -EOPNOTSUPP; | ||||||
| 
 | 	} | ||||||
| 	return NUM_ALL_STATS + (priv->tx_ring_num + priv->rx_ring_num) * 2; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void mlx4_en_get_ethtool_stats(struct net_device *dev, | static void mlx4_en_get_ethtool_stats(struct net_device *dev, | ||||||
| @ -181,6 +194,12 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, | |||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void mlx4_en_self_test(struct net_device *dev, | ||||||
|  | 			      struct ethtool_test *etest, u64 *buf) | ||||||
|  | { | ||||||
|  | 	mlx4_en_ex_selftest(dev, &etest->flags, buf); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void mlx4_en_get_strings(struct net_device *dev, | static void mlx4_en_get_strings(struct net_device *dev, | ||||||
| 				uint32_t stringset, uint8_t *data) | 				uint32_t stringset, uint8_t *data) | ||||||
| { | { | ||||||
| @ -188,30 +207,39 @@ static void mlx4_en_get_strings(struct net_device *dev, | |||||||
| 	int index = 0; | 	int index = 0; | ||||||
| 	int i; | 	int i; | ||||||
| 
 | 
 | ||||||
| 	if (stringset != ETH_SS_STATS) | 	switch (stringset) { | ||||||
| 		return; | 	case ETH_SS_TEST: | ||||||
|  | 		for (i = 0; i < MLX4_EN_NUM_SELF_TEST - 2; i++) | ||||||
|  | 			strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); | ||||||
|  | 		if (priv->mdev->dev->caps.loopback_support) | ||||||
|  | 			for (; i < MLX4_EN_NUM_SELF_TEST; i++) | ||||||
|  | 				strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); | ||||||
|  | 		break; | ||||||
| 
 | 
 | ||||||
| 	/* Add main counters */ | 	case ETH_SS_STATS: | ||||||
| 	for (i = 0; i < NUM_MAIN_STATS; i++) | 		/* Add main counters */ | ||||||
| 		strcpy(data + (index++) * ETH_GSTRING_LEN, main_strings[i]); | 		for (i = 0; i < NUM_MAIN_STATS; i++) | ||||||
| 	for (i = 0; i < NUM_PORT_STATS; i++) | 			strcpy(data + (index++) * ETH_GSTRING_LEN, main_strings[i]); | ||||||
| 		strcpy(data + (index++) * ETH_GSTRING_LEN, | 		for (i = 0; i< NUM_PORT_STATS; i++) | ||||||
|  | 			strcpy(data + (index++) * ETH_GSTRING_LEN, | ||||||
| 			main_strings[i + NUM_MAIN_STATS]); | 			main_strings[i + NUM_MAIN_STATS]); | ||||||
| 	for (i = 0; i < priv->tx_ring_num; i++) { | 		for (i = 0; i < priv->tx_ring_num; i++) { | ||||||
| 		sprintf(data + (index++) * ETH_GSTRING_LEN, | 			sprintf(data + (index++) * ETH_GSTRING_LEN, | ||||||
| 			"tx%d_packets", i); | 				"tx%d_packets", i); | ||||||
| 		sprintf(data + (index++) * ETH_GSTRING_LEN, | 			sprintf(data + (index++) * ETH_GSTRING_LEN, | ||||||
| 			"tx%d_bytes", i); | 				"tx%d_bytes", i); | ||||||
| 	} | 		} | ||||||
| 	for (i = 0; i < priv->rx_ring_num; i++) { | 		for (i = 0; i < priv->rx_ring_num; i++) { | ||||||
| 		sprintf(data + (index++) * ETH_GSTRING_LEN, | 			sprintf(data + (index++) * ETH_GSTRING_LEN, | ||||||
| 			"rx%d_packets", i); | 				"rx%d_packets", i); | ||||||
| 		sprintf(data + (index++) * ETH_GSTRING_LEN, | 			sprintf(data + (index++) * ETH_GSTRING_LEN, | ||||||
| 			"rx%d_bytes", i); | 				"rx%d_bytes", i); | ||||||
| 	} | 		} | ||||||
| 	for (i = 0; i < NUM_PKT_STATS; i++) | 		for (i = 0; i< NUM_PKT_STATS; i++) | ||||||
| 		strcpy(data + (index++) * ETH_GSTRING_LEN, | 			strcpy(data + (index++) * ETH_GSTRING_LEN, | ||||||
| 			main_strings[i + NUM_MAIN_STATS + NUM_PORT_STATS]); | 			main_strings[i + NUM_MAIN_STATS + NUM_PORT_STATS]); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | ||||||
| @ -439,6 +467,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = { | |||||||
| 	.get_strings = mlx4_en_get_strings, | 	.get_strings = mlx4_en_get_strings, | ||||||
| 	.get_sset_count = mlx4_en_get_sset_count, | 	.get_sset_count = mlx4_en_get_sset_count, | ||||||
| 	.get_ethtool_stats = mlx4_en_get_ethtool_stats, | 	.get_ethtool_stats = mlx4_en_get_ethtool_stats, | ||||||
|  | 	.self_test = mlx4_en_self_test, | ||||||
| 	.get_wol = mlx4_en_get_wol, | 	.get_wol = mlx4_en_get_wol, | ||||||
| 	.get_msglevel = mlx4_en_get_msglevel, | 	.get_msglevel = mlx4_en_get_msglevel, | ||||||
| 	.set_msglevel = mlx4_en_set_msglevel, | 	.set_msglevel = mlx4_en_set_msglevel, | ||||||
|  | |||||||
| @ -109,7 +109,7 @@ static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) | |||||||
| 	mutex_unlock(&mdev->state_lock); | 	mutex_unlock(&mdev->state_lock); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static u64 mlx4_en_mac_to_u64(u8 *addr) | u64 mlx4_en_mac_to_u64(u8 *addr) | ||||||
| { | { | ||||||
| 	u64 mac = 0; | 	u64 mac = 0; | ||||||
| 	int i; | 	int i; | ||||||
|  | |||||||
| @ -142,6 +142,38 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, | |||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port) | ||||||
|  | { | ||||||
|  | 	struct mlx4_en_query_port_context *qport_context; | ||||||
|  | 	struct mlx4_en_priv *priv = netdev_priv(mdev->pndev[port]); | ||||||
|  | 	struct mlx4_en_port_state *state = &priv->port_state; | ||||||
|  | 	struct mlx4_cmd_mailbox *mailbox; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	mailbox = mlx4_alloc_cmd_mailbox(mdev->dev); | ||||||
|  | 	if (IS_ERR(mailbox)) | ||||||
|  | 		return PTR_ERR(mailbox); | ||||||
|  | 	memset(mailbox->buf, 0, sizeof(*qport_context)); | ||||||
|  | 	err = mlx4_cmd_box(mdev->dev, 0, mailbox->dma, port, 0, | ||||||
|  | 			   MLX4_CMD_QUERY_PORT, MLX4_CMD_TIME_CLASS_B); | ||||||
|  | 	if (err) | ||||||
|  | 		goto out; | ||||||
|  | 	qport_context = mailbox->buf; | ||||||
|  | 
 | ||||||
|  | 	/* This command is always accessed from Ethtool context
 | ||||||
|  | 	 * already synchronized, no need in locking */ | ||||||
|  | 	state->link_state = !!(qport_context->link_up & MLX4_EN_LINK_UP_MASK); | ||||||
|  | 	if ((qport_context->link_speed & MLX4_EN_SPEED_MASK) == | ||||||
|  | 	    MLX4_EN_1G_SPEED) | ||||||
|  | 		state->link_speed = 1000; | ||||||
|  | 	else | ||||||
|  | 		state->link_speed = 10000; | ||||||
|  | 	state->transciver = qport_context->transceiver; | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  | 	mlx4_free_cmd_mailbox(mdev->dev, mailbox); | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) | int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) | ||||||
| { | { | ||||||
|  | |||||||
| @ -84,6 +84,20 @@ enum { | |||||||
| 	MLX4_MCAST_ENABLE       = 2, | 	MLX4_MCAST_ENABLE       = 2, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct mlx4_en_query_port_context { | ||||||
|  | 	u8 link_up; | ||||||
|  | #define MLX4_EN_LINK_UP_MASK	0x80 | ||||||
|  | 	u8 reserved; | ||||||
|  | 	__be16 mtu; | ||||||
|  | 	u8 reserved2; | ||||||
|  | 	u8 link_speed; | ||||||
|  | #define MLX4_EN_SPEED_MASK	0x3 | ||||||
|  | #define MLX4_EN_1G_SPEED	0x2 | ||||||
|  | 	u16 reserved3[5]; | ||||||
|  | 	__be64 mac; | ||||||
|  | 	u8 transceiver; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| struct mlx4_en_stat_out_mbox { | struct mlx4_en_stat_out_mbox { | ||||||
| 	/* Received frames with a length of 64 octets */ | 	/* Received frames with a length of 64 octets */ | ||||||
|  | |||||||
| @ -541,6 +541,21 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, | |||||||
| 	return skb; | 	return skb; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void validate_loopback(struct mlx4_en_priv *priv, struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	int offset = ETH_HLEN; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < MLX4_LOOPBACK_TEST_PAYLOAD; i++, offset++) { | ||||||
|  | 		if (*(skb->data + offset) != (unsigned char) (i & 0xff)) | ||||||
|  | 			goto out_loopback; | ||||||
|  | 	} | ||||||
|  | 	/* Loopback found */ | ||||||
|  | 	priv->loopback_ok = 1; | ||||||
|  | 
 | ||||||
|  | out_loopback: | ||||||
|  | 	dev_kfree_skb_any(skb); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget) | int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget) | ||||||
| { | { | ||||||
| @ -655,6 +670,11 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud | |||||||
| 			goto next; | 			goto next; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |                 if (unlikely(priv->validate_loopback)) { | ||||||
|  | 			validate_loopback(priv, skb); | ||||||
|  | 			goto next; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		skb->ip_summed = ip_summed; | 		skb->ip_summed = ip_summed; | ||||||
| 		skb->protocol = eth_type_trans(skb, dev); | 		skb->protocol = eth_type_trans(skb, dev); | ||||||
| 		skb_record_rx_queue(skb, cq->ring); | 		skb_record_rx_queue(skb, cq->ring); | ||||||
|  | |||||||
							
								
								
									
										179
									
								
								drivers/net/mlx4/en_selftest.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								drivers/net/mlx4/en_selftest.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,179 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2007 Mellanox Technologies. 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. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/kernel.h> | ||||||
|  | #include <linux/ethtool.h> | ||||||
|  | #include <linux/netdevice.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | #include <linux/mlx4/driver.h> | ||||||
|  | 
 | ||||||
|  | #include "mlx4_en.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static int mlx4_en_test_registers(struct mlx4_en_priv *priv) | ||||||
|  | { | ||||||
|  | 	return mlx4_cmd(priv->mdev->dev, 0, 0, 0, MLX4_CMD_HW_HEALTH_CHECK, | ||||||
|  | 			MLX4_CMD_TIME_CLASS_A); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mlx4_en_test_loopback_xmit(struct mlx4_en_priv *priv) | ||||||
|  | { | ||||||
|  | 	struct sk_buff *skb; | ||||||
|  | 	struct ethhdr *ethh; | ||||||
|  | 	unsigned char *packet; | ||||||
|  | 	unsigned int packet_size = MLX4_LOOPBACK_TEST_PAYLOAD; | ||||||
|  | 	unsigned int i; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	/* build the pkt before xmit */ | ||||||
|  | 	skb = netdev_alloc_skb(priv->dev, MLX4_LOOPBACK_TEST_PAYLOAD + ETH_HLEN + NET_IP_ALIGN); | ||||||
|  | 	if (!skb) { | ||||||
|  | 		en_err(priv, "-LOOPBACK_TEST_XMIT- failed to create skb for xmit\n"); | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	} | ||||||
|  | 	skb_reserve(skb, NET_IP_ALIGN); | ||||||
|  | 
 | ||||||
|  | 	ethh = (struct ethhdr *)skb_put(skb, sizeof(struct ethhdr)); | ||||||
|  | 	packet	= (unsigned char *)skb_put(skb, packet_size); | ||||||
|  | 	memcpy(ethh->h_dest, priv->dev->dev_addr, ETH_ALEN); | ||||||
|  | 	memset(ethh->h_source, 0, ETH_ALEN); | ||||||
|  | 	ethh->h_proto = htons(ETH_P_ARP); | ||||||
|  | 	skb_set_mac_header(skb, 0); | ||||||
|  | 	for (i = 0; i < packet_size; ++i)	/* fill our packet */ | ||||||
|  | 		packet[i] = (unsigned char)(i & 0xff); | ||||||
|  | 
 | ||||||
|  | 	/* xmit the pkt */ | ||||||
|  | 	err = mlx4_en_xmit(skb, priv->dev); | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mlx4_en_test_loopback(struct mlx4_en_priv *priv) | ||||||
|  | { | ||||||
|  | 	u32 loopback_ok = 0; | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         priv->loopback_ok = 0; | ||||||
|  | 	priv->validate_loopback = 1; | ||||||
|  | 
 | ||||||
|  | 	/* xmit */ | ||||||
|  | 	if (mlx4_en_test_loopback_xmit(priv)) { | ||||||
|  | 		en_err(priv, "Transmitting loopback packet failed\n"); | ||||||
|  | 		goto mlx4_en_test_loopback_exit; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* polling for result */ | ||||||
|  | 	for (i = 0; i < MLX4_EN_LOOPBACK_RETRIES; ++i) { | ||||||
|  | 		msleep(MLX4_EN_LOOPBACK_TIMEOUT); | ||||||
|  | 		if (priv->loopback_ok) { | ||||||
|  | 			loopback_ok = 1; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (!loopback_ok) | ||||||
|  | 		en_err(priv, "Loopback packet didn't arrive\n"); | ||||||
|  | 
 | ||||||
|  | mlx4_en_test_loopback_exit: | ||||||
|  | 
 | ||||||
|  | 	priv->validate_loopback = 0; | ||||||
|  | 	return (!loopback_ok); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static int mlx4_en_test_link(struct mlx4_en_priv *priv) | ||||||
|  | { | ||||||
|  | 	if (mlx4_en_QUERY_PORT(priv->mdev, priv->port)) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	if (priv->port_state.link_state == 1) | ||||||
|  | 		return 0; | ||||||
|  | 	else | ||||||
|  | 		return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mlx4_en_test_speed(struct mlx4_en_priv *priv) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | 	if (mlx4_en_QUERY_PORT(priv->mdev, priv->port)) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	/* The device currently only supports 10G speed */ | ||||||
|  | 	if (priv->port_state.link_speed != SPEED_10000) | ||||||
|  | 		return priv->port_state.link_speed; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf) | ||||||
|  | { | ||||||
|  | 	struct mlx4_en_priv *priv = netdev_priv(dev); | ||||||
|  | 	struct mlx4_en_dev *mdev = priv->mdev; | ||||||
|  | 	struct mlx4_en_tx_ring *tx_ring; | ||||||
|  | 	int i, carrier_ok; | ||||||
|  | 
 | ||||||
|  | 	memset(buf, 0, sizeof(u64) * MLX4_EN_NUM_SELF_TEST); | ||||||
|  | 
 | ||||||
|  | 	if (*flags & ETH_TEST_FL_OFFLINE) { | ||||||
|  | 		/* disable the interface */ | ||||||
|  | 		carrier_ok = netif_carrier_ok(dev); | ||||||
|  | 
 | ||||||
|  | 		netif_carrier_off(dev); | ||||||
|  | retry_tx: | ||||||
|  | 		/* Wait untill all tx queues are empty.
 | ||||||
|  | 		 * there should not be any additional incoming traffic | ||||||
|  | 		 * since we turned the carrier off */ | ||||||
|  | 		msleep(200); | ||||||
|  | 		for (i = 0; i < priv->tx_ring_num && carrier_ok; i++) { | ||||||
|  | 			tx_ring = &priv->tx_ring[i]; | ||||||
|  | 			if (tx_ring->prod != (tx_ring->cons + tx_ring->last_nr_txbb)) | ||||||
|  | 				goto retry_tx; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (priv->mdev->dev->caps.loopback_support){ | ||||||
|  | 			buf[3] = mlx4_en_test_registers(priv); | ||||||
|  | 			buf[4] = mlx4_en_test_loopback(priv); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (carrier_ok) | ||||||
|  | 			netif_carrier_on(dev); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 	buf[0] = mlx4_test_interrupts(mdev->dev); | ||||||
|  | 	buf[1] = mlx4_en_test_link(priv); | ||||||
|  | 	buf[2] = mlx4_en_test_speed(priv); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < MLX4_EN_NUM_SELF_TEST; i++) { | ||||||
|  | 		if (buf[i]) | ||||||
|  | 			*flags |= ETH_TEST_FL_FAILED; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -600,6 +600,9 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) | |||||||
| 	struct mlx4_wqe_data_seg *data; | 	struct mlx4_wqe_data_seg *data; | ||||||
| 	struct skb_frag_struct *frag; | 	struct skb_frag_struct *frag; | ||||||
| 	struct mlx4_en_tx_info *tx_info; | 	struct mlx4_en_tx_info *tx_info; | ||||||
|  | 	struct ethhdr *ethh; | ||||||
|  | 	u64 mac; | ||||||
|  | 	u32 mac_l, mac_h; | ||||||
| 	int tx_ind = 0; | 	int tx_ind = 0; | ||||||
| 	int nr_txbb; | 	int nr_txbb; | ||||||
| 	int desc_size; | 	int desc_size; | ||||||
| @ -679,6 +682,19 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) | |||||||
| 		priv->port_stats.tx_chksum_offload++; | 		priv->port_stats.tx_chksum_offload++; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (unlikely(priv->validate_loopback)) { | ||||||
|  | 		/* Copy dst mac address to wqe */ | ||||||
|  | 		skb_reset_mac_header(skb); | ||||||
|  | 		ethh = eth_hdr(skb); | ||||||
|  | 		if (ethh && ethh->h_dest) { | ||||||
|  | 			mac = mlx4_en_mac_to_u64(ethh->h_dest); | ||||||
|  | 			mac_h = (u32) ((mac & 0xffff00000000ULL) >> 16); | ||||||
|  | 			mac_l = (u32) (mac & 0xffffffff); | ||||||
|  | 			tx_desc->ctrl.srcrb_flags |= cpu_to_be32(mac_h); | ||||||
|  | 			tx_desc->ctrl.imm = cpu_to_be32(mac_l); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/* Handle LSO (TSO) packets */ | 	/* Handle LSO (TSO) packets */ | ||||||
| 	if (lso_header_size) { | 	if (lso_header_size) { | ||||||
| 		/* Mark opcode as LSO */ | 		/* Mark opcode as LSO */ | ||||||
|  | |||||||
| @ -699,3 +699,47 @@ void mlx4_cleanup_eq_table(struct mlx4_dev *dev) | |||||||
| 
 | 
 | ||||||
| 	kfree(priv->eq_table.uar_map); | 	kfree(priv->eq_table.uar_map); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /* A test that verifies that we can accept interrupts on all
 | ||||||
|  |  * the irq vectors of the device. | ||||||
|  |  * Interrupts are checked using the NOP command. | ||||||
|  |  */ | ||||||
|  | int mlx4_test_interrupts(struct mlx4_dev *dev) | ||||||
|  | { | ||||||
|  | 	struct mlx4_priv *priv = mlx4_priv(dev); | ||||||
|  | 	int i; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	err = mlx4_NOP(dev); | ||||||
|  | 	/* When not in MSI_X, there is only one irq to check */ | ||||||
|  | 	if (!(dev->flags & MLX4_FLAG_MSI_X)) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	/* A loop over all completion vectors, for each vector we will check
 | ||||||
|  | 	 * whether it works by mapping command completions to that vector | ||||||
|  | 	 * and performing a NOP command | ||||||
|  | 	 */ | ||||||
|  | 	for(i = 0; !err && (i < dev->caps.num_comp_vectors); ++i) { | ||||||
|  | 		/* Temporary use polling for command completions */ | ||||||
|  | 		mlx4_cmd_use_polling(dev); | ||||||
|  | 
 | ||||||
|  | 		/* Map the new eq to handle all asyncronous events */ | ||||||
|  | 		err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, | ||||||
|  | 				  priv->eq_table.eq[i].eqn); | ||||||
|  | 		if (err) { | ||||||
|  | 			mlx4_warn(dev, "Failed mapping eq for interrupt test\n"); | ||||||
|  | 			mlx4_cmd_use_events(dev); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/* Go back to using events */ | ||||||
|  | 		mlx4_cmd_use_events(dev); | ||||||
|  | 		err = mlx4_NOP(dev); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Return to default */ | ||||||
|  | 	mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, | ||||||
|  | 		    priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(mlx4_test_interrupts); | ||||||
|  | |||||||
| @ -178,6 +178,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) | |||||||
| #define QUERY_DEV_CAP_MAX_GID_OFFSET		0x3b | #define QUERY_DEV_CAP_MAX_GID_OFFSET		0x3b | ||||||
| #define QUERY_DEV_CAP_RATE_SUPPORT_OFFSET	0x3c | #define QUERY_DEV_CAP_RATE_SUPPORT_OFFSET	0x3c | ||||||
| #define QUERY_DEV_CAP_MAX_PKEY_OFFSET		0x3f | #define QUERY_DEV_CAP_MAX_PKEY_OFFSET		0x3f | ||||||
|  | #define QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET	0x43 | ||||||
| #define QUERY_DEV_CAP_FLAGS_OFFSET		0x44 | #define QUERY_DEV_CAP_FLAGS_OFFSET		0x44 | ||||||
| #define QUERY_DEV_CAP_RSVD_UAR_OFFSET		0x48 | #define QUERY_DEV_CAP_RSVD_UAR_OFFSET		0x48 | ||||||
| #define QUERY_DEV_CAP_UAR_SZ_OFFSET		0x49 | #define QUERY_DEV_CAP_UAR_SZ_OFFSET		0x49 | ||||||
| @ -268,6 +269,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) | |||||||
| 	dev_cap->max_msg_sz = 1 << (field & 0x1f); | 	dev_cap->max_msg_sz = 1 << (field & 0x1f); | ||||||
| 	MLX4_GET(stat_rate, outbox, QUERY_DEV_CAP_RATE_SUPPORT_OFFSET); | 	MLX4_GET(stat_rate, outbox, QUERY_DEV_CAP_RATE_SUPPORT_OFFSET); | ||||||
| 	dev_cap->stat_rate_support = stat_rate; | 	dev_cap->stat_rate_support = stat_rate; | ||||||
|  | 	MLX4_GET(field, outbox, QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET); | ||||||
|  | 	dev_cap->loopback_support = field & 0x1; | ||||||
| 	MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET); | 	MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET); | ||||||
| 	MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET); | 	MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET); | ||||||
| 	dev_cap->reserved_uars = field >> 4; | 	dev_cap->reserved_uars = field >> 4; | ||||||
|  | |||||||
| @ -74,6 +74,7 @@ struct mlx4_dev_cap { | |||||||
| 	u64 def_mac[MLX4_MAX_PORTS + 1]; | 	u64 def_mac[MLX4_MAX_PORTS + 1]; | ||||||
| 	u16 eth_mtu[MLX4_MAX_PORTS + 1]; | 	u16 eth_mtu[MLX4_MAX_PORTS + 1]; | ||||||
| 	u16 stat_rate_support; | 	u16 stat_rate_support; | ||||||
|  | 	int loopback_support; | ||||||
| 	u32 flags; | 	u32 flags; | ||||||
| 	int reserved_uars; | 	int reserved_uars; | ||||||
| 	int uar_size; | 	int uar_size; | ||||||
|  | |||||||
| @ -221,6 +221,7 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) | |||||||
| 	dev->caps.bmme_flags	     = dev_cap->bmme_flags; | 	dev->caps.bmme_flags	     = dev_cap->bmme_flags; | ||||||
| 	dev->caps.reserved_lkey	     = dev_cap->reserved_lkey; | 	dev->caps.reserved_lkey	     = dev_cap->reserved_lkey; | ||||||
| 	dev->caps.stat_rate_support  = dev_cap->stat_rate_support; | 	dev->caps.stat_rate_support  = dev_cap->stat_rate_support; | ||||||
|  | 	dev->caps.loopback_support   = dev_cap->loopback_support; | ||||||
| 	dev->caps.max_gso_sz	     = dev_cap->max_gso_sz; | 	dev->caps.max_gso_sz	     = dev_cap->max_gso_sz; | ||||||
| 
 | 
 | ||||||
| 	dev->caps.log_num_macs  = log_num_mac; | 	dev->caps.log_num_macs  = log_num_mac; | ||||||
|  | |||||||
| @ -45,6 +45,7 @@ | |||||||
| #include <linux/mlx4/cq.h> | #include <linux/mlx4/cq.h> | ||||||
| #include <linux/mlx4/srq.h> | #include <linux/mlx4/srq.h> | ||||||
| #include <linux/mlx4/doorbell.h> | #include <linux/mlx4/doorbell.h> | ||||||
|  | #include <linux/mlx4/cmd.h> | ||||||
| 
 | 
 | ||||||
| #include "en_port.h" | #include "en_port.h" | ||||||
| 
 | 
 | ||||||
| @ -139,10 +140,14 @@ enum { | |||||||
| 
 | 
 | ||||||
| #define SMALL_PACKET_SIZE      (256 - NET_IP_ALIGN) | #define SMALL_PACKET_SIZE      (256 - NET_IP_ALIGN) | ||||||
| #define HEADER_COPY_SIZE       (128 - NET_IP_ALIGN) | #define HEADER_COPY_SIZE       (128 - NET_IP_ALIGN) | ||||||
|  | #define MLX4_LOOPBACK_TEST_PAYLOAD (HEADER_COPY_SIZE - ETH_HLEN) | ||||||
| 
 | 
 | ||||||
| #define MLX4_EN_MIN_MTU		46 | #define MLX4_EN_MIN_MTU		46 | ||||||
| #define ETH_BCAST		0xffffffffffffULL | #define ETH_BCAST		0xffffffffffffULL | ||||||
| 
 | 
 | ||||||
|  | #define MLX4_EN_LOOPBACK_RETRIES	5 | ||||||
|  | #define MLX4_EN_LOOPBACK_TIMEOUT	100 | ||||||
|  | 
 | ||||||
| #ifdef MLX4_EN_PERF_STAT | #ifdef MLX4_EN_PERF_STAT | ||||||
| /* Number of samples to 'average' */ | /* Number of samples to 'average' */ | ||||||
| #define AVG_SIZE			128 | #define AVG_SIZE			128 | ||||||
| @ -356,6 +361,12 @@ struct mlx4_en_rss_context { | |||||||
| 	__be32 rss_key[10]; | 	__be32 rss_key[10]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct mlx4_en_port_state { | ||||||
|  | 	int link_state; | ||||||
|  | 	int link_speed; | ||||||
|  | 	int transciver; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| struct mlx4_en_pkt_stats { | struct mlx4_en_pkt_stats { | ||||||
| 	unsigned long broadcast; | 	unsigned long broadcast; | ||||||
| 	unsigned long rx_prio[8]; | 	unsigned long rx_prio[8]; | ||||||
| @ -404,6 +415,7 @@ struct mlx4_en_priv { | |||||||
| 	struct vlan_group *vlgrp; | 	struct vlan_group *vlgrp; | ||||||
| 	struct net_device_stats stats; | 	struct net_device_stats stats; | ||||||
| 	struct net_device_stats ret_stats; | 	struct net_device_stats ret_stats; | ||||||
|  | 	struct mlx4_en_port_state port_state; | ||||||
| 	spinlock_t stats_lock; | 	spinlock_t stats_lock; | ||||||
| 
 | 
 | ||||||
| 	unsigned long last_moder_packets; | 	unsigned long last_moder_packets; | ||||||
| @ -422,6 +434,8 @@ struct mlx4_en_priv { | |||||||
| 	u16 sample_interval; | 	u16 sample_interval; | ||||||
| 	u16 adaptive_rx_coal; | 	u16 adaptive_rx_coal; | ||||||
| 	u32 msg_enable; | 	u32 msg_enable; | ||||||
|  | 	u32 loopback_ok; | ||||||
|  | 	u32 validate_loopback; | ||||||
| 
 | 
 | ||||||
| 	struct mlx4_hwq_resources res; | 	struct mlx4_hwq_resources res; | ||||||
| 	int link_state; | 	int link_state; | ||||||
| @ -530,6 +544,11 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, | |||||||
| 			   u8 promisc); | 			   u8 promisc); | ||||||
| 
 | 
 | ||||||
| int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset); | int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset); | ||||||
|  | int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port); | ||||||
|  | 
 | ||||||
|  | #define MLX4_EN_NUM_SELF_TEST	5 | ||||||
|  | void mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf); | ||||||
|  | u64 mlx4_en_mac_to_u64(u8 *addr); | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Globals |  * Globals | ||||||
|  | |||||||
| @ -56,6 +56,7 @@ enum { | |||||||
| 	MLX4_CMD_QUERY_HCA	 = 0xb, | 	MLX4_CMD_QUERY_HCA	 = 0xb, | ||||||
| 	MLX4_CMD_QUERY_PORT	 = 0x43, | 	MLX4_CMD_QUERY_PORT	 = 0x43, | ||||||
| 	MLX4_CMD_SENSE_PORT	 = 0x4d, | 	MLX4_CMD_SENSE_PORT	 = 0x4d, | ||||||
|  | 	MLX4_CMD_HW_HEALTH_CHECK = 0x50, | ||||||
| 	MLX4_CMD_SET_PORT	 = 0xc, | 	MLX4_CMD_SET_PORT	 = 0xc, | ||||||
| 	MLX4_CMD_ACCESS_DDR	 = 0x2e, | 	MLX4_CMD_ACCESS_DDR	 = 0x2e, | ||||||
| 	MLX4_CMD_MAP_ICM	 = 0xffa, | 	MLX4_CMD_MAP_ICM	 = 0xffa, | ||||||
|  | |||||||
| @ -229,6 +229,7 @@ struct mlx4_caps { | |||||||
| 	u32			bmme_flags; | 	u32			bmme_flags; | ||||||
| 	u32			reserved_lkey; | 	u32			reserved_lkey; | ||||||
| 	u16			stat_rate_support; | 	u16			stat_rate_support; | ||||||
|  | 	int			loopback_support; | ||||||
| 	u8			port_width_cap[MLX4_MAX_PORTS + 1]; | 	u8			port_width_cap[MLX4_MAX_PORTS + 1]; | ||||||
| 	int			max_gso_sz; | 	int			max_gso_sz; | ||||||
| 	int                     reserved_qps_cnt[MLX4_NUM_QP_REGION]; | 	int                     reserved_qps_cnt[MLX4_NUM_QP_REGION]; | ||||||
| @ -480,5 +481,6 @@ void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr, | |||||||
| 		    u32 *lkey, u32 *rkey); | 		    u32 *lkey, u32 *rkey); | ||||||
| int mlx4_fmr_free(struct mlx4_dev *dev, struct mlx4_fmr *fmr); | int mlx4_fmr_free(struct mlx4_dev *dev, struct mlx4_fmr *fmr); | ||||||
| int mlx4_SYNC_TPT(struct mlx4_dev *dev); | int mlx4_SYNC_TPT(struct mlx4_dev *dev); | ||||||
|  | int mlx4_test_interrupts(struct mlx4_dev *dev); | ||||||
| 
 | 
 | ||||||
| #endif /* MLX4_DEVICE_H */ | #endif /* MLX4_DEVICE_H */ | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user