diff options
author | Yevgeny Petrilin <yevgenyp@mellanox.co.il> | 2010-08-23 23:46:18 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-08-24 17:54:51 -0400 |
commit | e7c1c2c46201e46f8ce817196507d2ffd3dafd8e (patch) | |
tree | 33579da64d2c5dc4502518496097dcf9737e9eb4 | |
parent | 3005ad40b95168aad530f1179cff47411b3ea8da (diff) |
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>
-rw-r--r-- | drivers/net/mlx4/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/mlx4/en_ethtool.c | 79 | ||||
-rw-r--r-- | drivers/net/mlx4/en_netdev.c | 2 | ||||
-rw-r--r-- | drivers/net/mlx4/en_port.c | 32 | ||||
-rw-r--r-- | drivers/net/mlx4/en_port.h | 14 | ||||
-rw-r--r-- | drivers/net/mlx4/en_rx.c | 20 | ||||
-rw-r--r-- | drivers/net/mlx4/en_selftest.c | 179 | ||||
-rw-r--r-- | drivers/net/mlx4/en_tx.c | 16 | ||||
-rw-r--r-- | drivers/net/mlx4/eq.c | 44 | ||||
-rw-r--r-- | drivers/net/mlx4/fw.c | 3 | ||||
-rw-r--r-- | drivers/net/mlx4/fw.h | 1 | ||||
-rw-r--r-- | drivers/net/mlx4/main.c | 1 | ||||
-rw-r--r-- | drivers/net/mlx4/mlx4_en.h | 19 | ||||
-rw-r--r-- | include/linux/mlx4/cmd.h | 1 | ||||
-rw-r--r-- | include/linux/mlx4/device.h | 2 |
15 files changed, 388 insertions, 27 deletions
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile index 1fd068e1d930..d1aa45a15854 100644 --- a/drivers/net/mlx4/Makefile +++ b/drivers/net/mlx4/Makefile | |||
@@ -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 \ | |||
6 | obj-$(CONFIG_MLX4_EN) += mlx4_en.o | 6 | obj-$(CONFIG_MLX4_EN) += mlx4_en.o |
7 | 7 | ||
8 | mlx4_en-y := en_main.o en_tx.o en_rx.o en_ethtool.o en_port.o en_cq.o \ | 8 | mlx4_en-y := en_main.o en_tx.o en_rx.o en_ethtool.o en_port.o en_cq.o \ |
9 | en_resources.o en_netdev.o | 9 | en_resources.o en_netdev.o en_selftest.o |
diff --git a/drivers/net/mlx4/en_ethtool.c b/drivers/net/mlx4/en_ethtool.c index 398d54136967..f7d72d7a8704 100644 --- a/drivers/net/mlx4/en_ethtool.c +++ b/drivers/net/mlx4/en_ethtool.c | |||
@@ -125,6 +125,14 @@ static const char main_strings[][ETH_GSTRING_LEN] = { | |||
125 | #define NUM_MAIN_STATS 21 | 125 | #define NUM_MAIN_STATS 21 |
126 | #define NUM_ALL_STATS (NUM_MAIN_STATS + NUM_PORT_STATS + NUM_PKT_STATS + NUM_PERF_STATS) | 126 | #define NUM_ALL_STATS (NUM_MAIN_STATS + NUM_PORT_STATS + NUM_PKT_STATS + NUM_PERF_STATS) |
127 | 127 | ||
128 | static const char mlx4_en_test_names[][ETH_GSTRING_LEN]= { | ||
129 | "Interupt Test", | ||
130 | "Link Test", | ||
131 | "Speed Test", | ||
132 | "Register Test", | ||
133 | "Loopback Test", | ||
134 | }; | ||
135 | |||
128 | static u32 mlx4_en_get_msglevel(struct net_device *dev) | 136 | static u32 mlx4_en_get_msglevel(struct net_device *dev) |
129 | { | 137 | { |
130 | return ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable; | 138 | 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) | |||
146 | { | 154 | { |
147 | struct mlx4_en_priv *priv = netdev_priv(dev); | 155 | struct mlx4_en_priv *priv = netdev_priv(dev); |
148 | 156 | ||
149 | if (sset != ETH_SS_STATS) | 157 | switch (sset) { |
158 | case ETH_SS_STATS: | ||
159 | return NUM_ALL_STATS + | ||
160 | (priv->tx_ring_num + priv->rx_ring_num) * 2; | ||
161 | case ETH_SS_TEST: | ||
162 | return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.loopback_support) * 2; | ||
163 | default: | ||
150 | return -EOPNOTSUPP; | 164 | return -EOPNOTSUPP; |
151 | 165 | } | |
152 | return NUM_ALL_STATS + (priv->tx_ring_num + priv->rx_ring_num) * 2; | ||
153 | } | 166 | } |
154 | 167 | ||
155 | static void mlx4_en_get_ethtool_stats(struct net_device *dev, | 168 | 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, | |||
181 | 194 | ||
182 | } | 195 | } |
183 | 196 | ||
197 | static void mlx4_en_self_test(struct net_device *dev, | ||
198 | struct ethtool_test *etest, u64 *buf) | ||
199 | { | ||
200 | mlx4_en_ex_selftest(dev, &etest->flags, buf); | ||
201 | } | ||
202 | |||
184 | static void mlx4_en_get_strings(struct net_device *dev, | 203 | static void mlx4_en_get_strings(struct net_device *dev, |
185 | uint32_t stringset, uint8_t *data) | 204 | uint32_t stringset, uint8_t *data) |
186 | { | 205 | { |
@@ -188,30 +207,39 @@ static void mlx4_en_get_strings(struct net_device *dev, | |||
188 | int index = 0; | 207 | int index = 0; |
189 | int i; | 208 | int i; |
190 | 209 | ||
191 | if (stringset != ETH_SS_STATS) | 210 | switch (stringset) { |
192 | return; | 211 | case ETH_SS_TEST: |
193 | 212 | for (i = 0; i < MLX4_EN_NUM_SELF_TEST - 2; i++) | |
194 | /* Add main counters */ | 213 | strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); |
195 | for (i = 0; i < NUM_MAIN_STATS; i++) | 214 | if (priv->mdev->dev->caps.loopback_support) |
196 | strcpy(data + (index++) * ETH_GSTRING_LEN, main_strings[i]); | 215 | for (; i < MLX4_EN_NUM_SELF_TEST; i++) |
197 | for (i = 0; i < NUM_PORT_STATS; i++) | 216 | strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); |
198 | strcpy(data + (index++) * ETH_GSTRING_LEN, | 217 | break; |
218 | |||
219 | case ETH_SS_STATS: | ||
220 | /* Add main counters */ | ||
221 | for (i = 0; i < NUM_MAIN_STATS; i++) | ||
222 | strcpy(data + (index++) * ETH_GSTRING_LEN, main_strings[i]); | ||
223 | for (i = 0; i< NUM_PORT_STATS; i++) | ||
224 | strcpy(data + (index++) * ETH_GSTRING_LEN, | ||
199 | main_strings[i + NUM_MAIN_STATS]); | 225 | main_strings[i + NUM_MAIN_STATS]); |
200 | for (i = 0; i < priv->tx_ring_num; i++) { | 226 | for (i = 0; i < priv->tx_ring_num; i++) { |
201 | sprintf(data + (index++) * ETH_GSTRING_LEN, | 227 | sprintf(data + (index++) * ETH_GSTRING_LEN, |
202 | "tx%d_packets", i); | 228 | "tx%d_packets", i); |
203 | sprintf(data + (index++) * ETH_GSTRING_LEN, | 229 | sprintf(data + (index++) * ETH_GSTRING_LEN, |
204 | "tx%d_bytes", i); | 230 | "tx%d_bytes", i); |
205 | } | 231 | } |
206 | for (i = 0; i < priv->rx_ring_num; i++) { | 232 | for (i = 0; i < priv->rx_ring_num; i++) { |
207 | sprintf(data + (index++) * ETH_GSTRING_LEN, | 233 | sprintf(data + (index++) * ETH_GSTRING_LEN, |
208 | "rx%d_packets", i); | 234 | "rx%d_packets", i); |
209 | sprintf(data + (index++) * ETH_GSTRING_LEN, | 235 | sprintf(data + (index++) * ETH_GSTRING_LEN, |
210 | "rx%d_bytes", i); | 236 | "rx%d_bytes", i); |
211 | } | 237 | } |
212 | for (i = 0; i < NUM_PKT_STATS; i++) | 238 | for (i = 0; i< NUM_PKT_STATS; i++) |
213 | strcpy(data + (index++) * ETH_GSTRING_LEN, | 239 | strcpy(data + (index++) * ETH_GSTRING_LEN, |
214 | main_strings[i + NUM_MAIN_STATS + NUM_PORT_STATS]); | 240 | main_strings[i + NUM_MAIN_STATS + NUM_PORT_STATS]); |
241 | break; | ||
242 | } | ||
215 | } | 243 | } |
216 | 244 | ||
217 | static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | 245 | 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 = { | |||
439 | .get_strings = mlx4_en_get_strings, | 467 | .get_strings = mlx4_en_get_strings, |
440 | .get_sset_count = mlx4_en_get_sset_count, | 468 | .get_sset_count = mlx4_en_get_sset_count, |
441 | .get_ethtool_stats = mlx4_en_get_ethtool_stats, | 469 | .get_ethtool_stats = mlx4_en_get_ethtool_stats, |
470 | .self_test = mlx4_en_self_test, | ||
442 | .get_wol = mlx4_en_get_wol, | 471 | .get_wol = mlx4_en_get_wol, |
443 | .get_msglevel = mlx4_en_get_msglevel, | 472 | .get_msglevel = mlx4_en_get_msglevel, |
444 | .set_msglevel = mlx4_en_set_msglevel, | 473 | .set_msglevel = mlx4_en_set_msglevel, |
diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c index 34cfa3cfbdae..968e75b7acf4 100644 --- a/drivers/net/mlx4/en_netdev.c +++ b/drivers/net/mlx4/en_netdev.c | |||
@@ -109,7 +109,7 @@ static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) | |||
109 | mutex_unlock(&mdev->state_lock); | 109 | mutex_unlock(&mdev->state_lock); |
110 | } | 110 | } |
111 | 111 | ||
112 | static u64 mlx4_en_mac_to_u64(u8 *addr) | 112 | u64 mlx4_en_mac_to_u64(u8 *addr) |
113 | { | 113 | { |
114 | u64 mac = 0; | 114 | u64 mac = 0; |
115 | int i; | 115 | int i; |
diff --git a/drivers/net/mlx4/en_port.c b/drivers/net/mlx4/en_port.c index a29abe845d2e..aa3ef2aee5bf 100644 --- a/drivers/net/mlx4/en_port.c +++ b/drivers/net/mlx4/en_port.c | |||
@@ -142,6 +142,38 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, | |||
142 | return err; | 142 | return err; |
143 | } | 143 | } |
144 | 144 | ||
145 | int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port) | ||
146 | { | ||
147 | struct mlx4_en_query_port_context *qport_context; | ||
148 | struct mlx4_en_priv *priv = netdev_priv(mdev->pndev[port]); | ||
149 | struct mlx4_en_port_state *state = &priv->port_state; | ||
150 | struct mlx4_cmd_mailbox *mailbox; | ||
151 | int err; | ||
152 | |||
153 | mailbox = mlx4_alloc_cmd_mailbox(mdev->dev); | ||
154 | if (IS_ERR(mailbox)) | ||
155 | return PTR_ERR(mailbox); | ||
156 | memset(mailbox->buf, 0, sizeof(*qport_context)); | ||
157 | err = mlx4_cmd_box(mdev->dev, 0, mailbox->dma, port, 0, | ||
158 | MLX4_CMD_QUERY_PORT, MLX4_CMD_TIME_CLASS_B); | ||
159 | if (err) | ||
160 | goto out; | ||
161 | qport_context = mailbox->buf; | ||
162 | |||
163 | /* This command is always accessed from Ethtool context | ||
164 | * already synchronized, no need in locking */ | ||
165 | state->link_state = !!(qport_context->link_up & MLX4_EN_LINK_UP_MASK); | ||
166 | if ((qport_context->link_speed & MLX4_EN_SPEED_MASK) == | ||
167 | MLX4_EN_1G_SPEED) | ||
168 | state->link_speed = 1000; | ||
169 | else | ||
170 | state->link_speed = 10000; | ||
171 | state->transciver = qport_context->transceiver; | ||
172 | |||
173 | out: | ||
174 | mlx4_free_cmd_mailbox(mdev->dev, mailbox); | ||
175 | return err; | ||
176 | } | ||
145 | 177 | ||
146 | int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) | 178 | int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) |
147 | { | 179 | { |
diff --git a/drivers/net/mlx4/en_port.h b/drivers/net/mlx4/en_port.h index e6477f12beb5..f6511aa2b7df 100644 --- a/drivers/net/mlx4/en_port.h +++ b/drivers/net/mlx4/en_port.h | |||
@@ -84,6 +84,20 @@ enum { | |||
84 | MLX4_MCAST_ENABLE = 2, | 84 | MLX4_MCAST_ENABLE = 2, |
85 | }; | 85 | }; |
86 | 86 | ||
87 | struct mlx4_en_query_port_context { | ||
88 | u8 link_up; | ||
89 | #define MLX4_EN_LINK_UP_MASK 0x80 | ||
90 | u8 reserved; | ||
91 | __be16 mtu; | ||
92 | u8 reserved2; | ||
93 | u8 link_speed; | ||
94 | #define MLX4_EN_SPEED_MASK 0x3 | ||
95 | #define MLX4_EN_1G_SPEED 0x2 | ||
96 | u16 reserved3[5]; | ||
97 | __be64 mac; | ||
98 | u8 transceiver; | ||
99 | }; | ||
100 | |||
87 | 101 | ||
88 | struct mlx4_en_stat_out_mbox { | 102 | struct mlx4_en_stat_out_mbox { |
89 | /* Received frames with a length of 64 octets */ | 103 | /* Received frames with a length of 64 octets */ |
diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c index 7a5123c4a366..f421a3d42723 100644 --- a/drivers/net/mlx4/en_rx.c +++ b/drivers/net/mlx4/en_rx.c | |||
@@ -541,6 +541,21 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, | |||
541 | return skb; | 541 | return skb; |
542 | } | 542 | } |
543 | 543 | ||
544 | static void validate_loopback(struct mlx4_en_priv *priv, struct sk_buff *skb) | ||
545 | { | ||
546 | int i; | ||
547 | int offset = ETH_HLEN; | ||
548 | |||
549 | for (i = 0; i < MLX4_LOOPBACK_TEST_PAYLOAD; i++, offset++) { | ||
550 | if (*(skb->data + offset) != (unsigned char) (i & 0xff)) | ||
551 | goto out_loopback; | ||
552 | } | ||
553 | /* Loopback found */ | ||
554 | priv->loopback_ok = 1; | ||
555 | |||
556 | out_loopback: | ||
557 | dev_kfree_skb_any(skb); | ||
558 | } | ||
544 | 559 | ||
545 | int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget) | 560 | int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget) |
546 | { | 561 | { |
@@ -655,6 +670,11 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud | |||
655 | goto next; | 670 | goto next; |
656 | } | 671 | } |
657 | 672 | ||
673 | if (unlikely(priv->validate_loopback)) { | ||
674 | validate_loopback(priv, skb); | ||
675 | goto next; | ||
676 | } | ||
677 | |||
658 | skb->ip_summed = ip_summed; | 678 | skb->ip_summed = ip_summed; |
659 | skb->protocol = eth_type_trans(skb, dev); | 679 | skb->protocol = eth_type_trans(skb, dev); |
660 | skb_record_rx_queue(skb, cq->ring); | 680 | skb_record_rx_queue(skb, cq->ring); |
diff --git a/drivers/net/mlx4/en_selftest.c b/drivers/net/mlx4/en_selftest.c new file mode 100644 index 000000000000..43357d35616a --- /dev/null +++ b/drivers/net/mlx4/en_selftest.c | |||
@@ -0,0 +1,179 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2007 Mellanox Technologies. All rights reserved. | ||
3 | * | ||
4 | * This software is available to you under a choice of one of two | ||
5 | * licenses. You may choose to be licensed under the terms of the GNU | ||
6 | * General Public License (GPL) Version 2, available from the file | ||
7 | * COPYING in the main directory of this source tree, or the | ||
8 | * OpenIB.org BSD license below: | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or | ||
11 | * without modification, are permitted provided that the following | ||
12 | * conditions are met: | ||
13 | * | ||
14 | * - Redistributions of source code must retain the above | ||
15 | * copyright notice, this list of conditions and the following | ||
16 | * disclaimer. | ||
17 | * | ||
18 | * - Redistributions in binary form must reproduce the above | ||
19 | * copyright notice, this list of conditions and the following | ||
20 | * disclaimer in the documentation and/or other materials | ||
21 | * provided with the distribution. | ||
22 | * | ||
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
24 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
25 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
26 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
27 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
28 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
29 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
30 | * SOFTWARE. | ||
31 | * | ||
32 | */ | ||
33 | |||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/ethtool.h> | ||
36 | #include <linux/netdevice.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include <linux/mlx4/driver.h> | ||
39 | |||
40 | #include "mlx4_en.h" | ||
41 | |||
42 | |||
43 | static int mlx4_en_test_registers(struct mlx4_en_priv *priv) | ||
44 | { | ||
45 | return mlx4_cmd(priv->mdev->dev, 0, 0, 0, MLX4_CMD_HW_HEALTH_CHECK, | ||
46 | MLX4_CMD_TIME_CLASS_A); | ||
47 | } | ||
48 | |||
49 | static int mlx4_en_test_loopback_xmit(struct mlx4_en_priv *priv) | ||
50 | { | ||
51 | struct sk_buff *skb; | ||
52 | struct ethhdr *ethh; | ||
53 | unsigned char *packet; | ||
54 | unsigned int packet_size = MLX4_LOOPBACK_TEST_PAYLOAD; | ||
55 | unsigned int i; | ||
56 | int err; | ||
57 | |||
58 | |||
59 | /* build the pkt before xmit */ | ||
60 | skb = netdev_alloc_skb(priv->dev, MLX4_LOOPBACK_TEST_PAYLOAD + ETH_HLEN + NET_IP_ALIGN); | ||
61 | if (!skb) { | ||
62 | en_err(priv, "-LOOPBACK_TEST_XMIT- failed to create skb for xmit\n"); | ||
63 | return -ENOMEM; | ||
64 | } | ||
65 | skb_reserve(skb, NET_IP_ALIGN); | ||
66 | |||
67 | ethh = (struct ethhdr *)skb_put(skb, sizeof(struct ethhdr)); | ||
68 | packet = (unsigned char *)skb_put(skb, packet_size); | ||
69 | memcpy(ethh->h_dest, priv->dev->dev_addr, ETH_ALEN); | ||
70 | memset(ethh->h_source, 0, ETH_ALEN); | ||
71 | ethh->h_proto = htons(ETH_P_ARP); | ||
72 | skb_set_mac_header(skb, 0); | ||
73 | for (i = 0; i < packet_size; ++i) /* fill our packet */ | ||
74 | packet[i] = (unsigned char)(i & 0xff); | ||
75 | |||
76 | /* xmit the pkt */ | ||
77 | err = mlx4_en_xmit(skb, priv->dev); | ||
78 | return err; | ||
79 | } | ||
80 | |||
81 | static int mlx4_en_test_loopback(struct mlx4_en_priv *priv) | ||
82 | { | ||
83 | u32 loopback_ok = 0; | ||
84 | int i; | ||
85 | |||
86 | |||
87 | priv->loopback_ok = 0; | ||
88 | priv->validate_loopback = 1; | ||
89 | |||
90 | /* xmit */ | ||
91 | if (mlx4_en_test_loopback_xmit(priv)) { | ||
92 | en_err(priv, "Transmitting loopback packet failed\n"); | ||
93 | goto mlx4_en_test_loopback_exit; | ||
94 | } | ||
95 | |||
96 | /* polling for result */ | ||
97 | for (i = 0; i < MLX4_EN_LOOPBACK_RETRIES; ++i) { | ||
98 | msleep(MLX4_EN_LOOPBACK_TIMEOUT); | ||
99 | if (priv->loopback_ok) { | ||
100 | loopback_ok = 1; | ||
101 | break; | ||
102 | } | ||
103 | } | ||
104 | if (!loopback_ok) | ||
105 | en_err(priv, "Loopback packet didn't arrive\n"); | ||
106 | |||
107 | mlx4_en_test_loopback_exit: | ||
108 | |||
109 | priv->validate_loopback = 0; | ||
110 | return (!loopback_ok); | ||
111 | } | ||
112 | |||
113 | |||
114 | static int mlx4_en_test_link(struct mlx4_en_priv *priv) | ||
115 | { | ||
116 | if (mlx4_en_QUERY_PORT(priv->mdev, priv->port)) | ||
117 | return -ENOMEM; | ||
118 | if (priv->port_state.link_state == 1) | ||
119 | return 0; | ||
120 | else | ||
121 | return 1; | ||
122 | } | ||
123 | |||
124 | static int mlx4_en_test_speed(struct mlx4_en_priv *priv) | ||
125 | { | ||
126 | |||
127 | if (mlx4_en_QUERY_PORT(priv->mdev, priv->port)) | ||
128 | return -ENOMEM; | ||
129 | |||
130 | /* The device currently only supports 10G speed */ | ||
131 | if (priv->port_state.link_speed != SPEED_10000) | ||
132 | return priv->port_state.link_speed; | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | |||
137 | void mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf) | ||
138 | { | ||
139 | struct mlx4_en_priv *priv = netdev_priv(dev); | ||
140 | struct mlx4_en_dev *mdev = priv->mdev; | ||
141 | struct mlx4_en_tx_ring *tx_ring; | ||
142 | int i, carrier_ok; | ||
143 | |||
144 | memset(buf, 0, sizeof(u64) * MLX4_EN_NUM_SELF_TEST); | ||
145 | |||
146 | if (*flags & ETH_TEST_FL_OFFLINE) { | ||
147 | /* disable the interface */ | ||
148 | carrier_ok = netif_carrier_ok(dev); | ||
149 | |||
150 | netif_carrier_off(dev); | ||
151 | retry_tx: | ||
152 | /* Wait untill all tx queues are empty. | ||
153 | * there should not be any additional incoming traffic | ||
154 | * since we turned the carrier off */ | ||
155 | msleep(200); | ||
156 | for (i = 0; i < priv->tx_ring_num && carrier_ok; i++) { | ||
157 | tx_ring = &priv->tx_ring[i]; | ||
158 | if (tx_ring->prod != (tx_ring->cons + tx_ring->last_nr_txbb)) | ||
159 | goto retry_tx; | ||
160 | } | ||
161 | |||
162 | if (priv->mdev->dev->caps.loopback_support){ | ||
163 | buf[3] = mlx4_en_test_registers(priv); | ||
164 | buf[4] = mlx4_en_test_loopback(priv); | ||
165 | } | ||
166 | |||
167 | if (carrier_ok) | ||
168 | netif_carrier_on(dev); | ||
169 | |||
170 | } | ||
171 | buf[0] = mlx4_test_interrupts(mdev->dev); | ||
172 | buf[1] = mlx4_en_test_link(priv); | ||
173 | buf[2] = mlx4_en_test_speed(priv); | ||
174 | |||
175 | for (i = 0; i < MLX4_EN_NUM_SELF_TEST; i++) { | ||
176 | if (buf[i]) | ||
177 | *flags |= ETH_TEST_FL_FAILED; | ||
178 | } | ||
179 | } | ||
diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index 3deabd1d0357..b875f9c38848 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c | |||
@@ -600,6 +600,9 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) | |||
600 | struct mlx4_wqe_data_seg *data; | 600 | struct mlx4_wqe_data_seg *data; |
601 | struct skb_frag_struct *frag; | 601 | struct skb_frag_struct *frag; |
602 | struct mlx4_en_tx_info *tx_info; | 602 | struct mlx4_en_tx_info *tx_info; |
603 | struct ethhdr *ethh; | ||
604 | u64 mac; | ||
605 | u32 mac_l, mac_h; | ||
603 | int tx_ind = 0; | 606 | int tx_ind = 0; |
604 | int nr_txbb; | 607 | int nr_txbb; |
605 | int desc_size; | 608 | int desc_size; |
@@ -679,6 +682,19 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) | |||
679 | priv->port_stats.tx_chksum_offload++; | 682 | priv->port_stats.tx_chksum_offload++; |
680 | } | 683 | } |
681 | 684 | ||
685 | if (unlikely(priv->validate_loopback)) { | ||
686 | /* Copy dst mac address to wqe */ | ||
687 | skb_reset_mac_header(skb); | ||
688 | ethh = eth_hdr(skb); | ||
689 | if (ethh && ethh->h_dest) { | ||
690 | mac = mlx4_en_mac_to_u64(ethh->h_dest); | ||
691 | mac_h = (u32) ((mac & 0xffff00000000ULL) >> 16); | ||
692 | mac_l = (u32) (mac & 0xffffffff); | ||
693 | tx_desc->ctrl.srcrb_flags |= cpu_to_be32(mac_h); | ||
694 | tx_desc->ctrl.imm = cpu_to_be32(mac_l); | ||
695 | } | ||
696 | } | ||
697 | |||
682 | /* Handle LSO (TSO) packets */ | 698 | /* Handle LSO (TSO) packets */ |
683 | if (lso_header_size) { | 699 | if (lso_header_size) { |
684 | /* Mark opcode as LSO */ | 700 | /* Mark opcode as LSO */ |
diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index 6d7b2bf210ce..552d0fce6f67 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c | |||
@@ -699,3 +699,47 @@ void mlx4_cleanup_eq_table(struct mlx4_dev *dev) | |||
699 | 699 | ||
700 | kfree(priv->eq_table.uar_map); | 700 | kfree(priv->eq_table.uar_map); |
701 | } | 701 | } |
702 | |||
703 | /* A test that verifies that we can accept interrupts on all | ||
704 | * the irq vectors of the device. | ||
705 | * Interrupts are checked using the NOP command. | ||
706 | */ | ||
707 | int mlx4_test_interrupts(struct mlx4_dev *dev) | ||
708 | { | ||
709 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
710 | int i; | ||
711 | int err; | ||
712 | |||
713 | err = mlx4_NOP(dev); | ||
714 | /* When not in MSI_X, there is only one irq to check */ | ||
715 | if (!(dev->flags & MLX4_FLAG_MSI_X)) | ||
716 | return err; | ||
717 | |||
718 | /* A loop over all completion vectors, for each vector we will check | ||
719 | * whether it works by mapping command completions to that vector | ||
720 | * and performing a NOP command | ||
721 | */ | ||
722 | for(i = 0; !err && (i < dev->caps.num_comp_vectors); ++i) { | ||
723 | /* Temporary use polling for command completions */ | ||
724 | mlx4_cmd_use_polling(dev); | ||
725 | |||
726 | /* Map the new eq to handle all asyncronous events */ | ||
727 | err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, | ||
728 | priv->eq_table.eq[i].eqn); | ||
729 | if (err) { | ||
730 | mlx4_warn(dev, "Failed mapping eq for interrupt test\n"); | ||
731 | mlx4_cmd_use_events(dev); | ||
732 | break; | ||
733 | } | ||
734 | |||
735 | /* Go back to using events */ | ||
736 | mlx4_cmd_use_events(dev); | ||
737 | err = mlx4_NOP(dev); | ||
738 | } | ||
739 | |||
740 | /* Return to default */ | ||
741 | mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, | ||
742 | priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); | ||
743 | return err; | ||
744 | } | ||
745 | EXPORT_SYMBOL(mlx4_test_interrupts); | ||
diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c index 04f42ae1eda0..a87bf3c97055 100644 --- a/drivers/net/mlx4/fw.c +++ b/drivers/net/mlx4/fw.c | |||
@@ -178,6 +178,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) | |||
178 | #define QUERY_DEV_CAP_MAX_GID_OFFSET 0x3b | 178 | #define QUERY_DEV_CAP_MAX_GID_OFFSET 0x3b |
179 | #define QUERY_DEV_CAP_RATE_SUPPORT_OFFSET 0x3c | 179 | #define QUERY_DEV_CAP_RATE_SUPPORT_OFFSET 0x3c |
180 | #define QUERY_DEV_CAP_MAX_PKEY_OFFSET 0x3f | 180 | #define QUERY_DEV_CAP_MAX_PKEY_OFFSET 0x3f |
181 | #define QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET 0x43 | ||
181 | #define QUERY_DEV_CAP_FLAGS_OFFSET 0x44 | 182 | #define QUERY_DEV_CAP_FLAGS_OFFSET 0x44 |
182 | #define QUERY_DEV_CAP_RSVD_UAR_OFFSET 0x48 | 183 | #define QUERY_DEV_CAP_RSVD_UAR_OFFSET 0x48 |
183 | #define QUERY_DEV_CAP_UAR_SZ_OFFSET 0x49 | 184 | #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) | |||
268 | dev_cap->max_msg_sz = 1 << (field & 0x1f); | 269 | dev_cap->max_msg_sz = 1 << (field & 0x1f); |
269 | MLX4_GET(stat_rate, outbox, QUERY_DEV_CAP_RATE_SUPPORT_OFFSET); | 270 | MLX4_GET(stat_rate, outbox, QUERY_DEV_CAP_RATE_SUPPORT_OFFSET); |
270 | dev_cap->stat_rate_support = stat_rate; | 271 | dev_cap->stat_rate_support = stat_rate; |
272 | MLX4_GET(field, outbox, QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET); | ||
273 | dev_cap->loopback_support = field & 0x1; | ||
271 | MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET); | 274 | MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET); |
272 | MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET); | 275 | MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET); |
273 | dev_cap->reserved_uars = field >> 4; | 276 | dev_cap->reserved_uars = field >> 4; |
diff --git a/drivers/net/mlx4/fw.h b/drivers/net/mlx4/fw.h index 526d7f30c041..2cc1ba5452e3 100644 --- a/drivers/net/mlx4/fw.h +++ b/drivers/net/mlx4/fw.h | |||
@@ -74,6 +74,7 @@ struct mlx4_dev_cap { | |||
74 | u64 def_mac[MLX4_MAX_PORTS + 1]; | 74 | u64 def_mac[MLX4_MAX_PORTS + 1]; |
75 | u16 eth_mtu[MLX4_MAX_PORTS + 1]; | 75 | u16 eth_mtu[MLX4_MAX_PORTS + 1]; |
76 | u16 stat_rate_support; | 76 | u16 stat_rate_support; |
77 | int loopback_support; | ||
77 | u32 flags; | 78 | u32 flags; |
78 | int reserved_uars; | 79 | int reserved_uars; |
79 | int uar_size; | 80 | int uar_size; |
diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index 5102ab1ac561..7390cdb19414 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c | |||
@@ -221,6 +221,7 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) | |||
221 | dev->caps.bmme_flags = dev_cap->bmme_flags; | 221 | dev->caps.bmme_flags = dev_cap->bmme_flags; |
222 | dev->caps.reserved_lkey = dev_cap->reserved_lkey; | 222 | dev->caps.reserved_lkey = dev_cap->reserved_lkey; |
223 | dev->caps.stat_rate_support = dev_cap->stat_rate_support; | 223 | dev->caps.stat_rate_support = dev_cap->stat_rate_support; |
224 | dev->caps.loopback_support = dev_cap->loopback_support; | ||
224 | dev->caps.max_gso_sz = dev_cap->max_gso_sz; | 225 | dev->caps.max_gso_sz = dev_cap->max_gso_sz; |
225 | 226 | ||
226 | dev->caps.log_num_macs = log_num_mac; | 227 | dev->caps.log_num_macs = log_num_mac; |
diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index edf6523702c0..a09598b2b12e 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h | |||
@@ -45,6 +45,7 @@ | |||
45 | #include <linux/mlx4/cq.h> | 45 | #include <linux/mlx4/cq.h> |
46 | #include <linux/mlx4/srq.h> | 46 | #include <linux/mlx4/srq.h> |
47 | #include <linux/mlx4/doorbell.h> | 47 | #include <linux/mlx4/doorbell.h> |
48 | #include <linux/mlx4/cmd.h> | ||
48 | 49 | ||
49 | #include "en_port.h" | 50 | #include "en_port.h" |
50 | 51 | ||
@@ -139,10 +140,14 @@ enum { | |||
139 | 140 | ||
140 | #define SMALL_PACKET_SIZE (256 - NET_IP_ALIGN) | 141 | #define SMALL_PACKET_SIZE (256 - NET_IP_ALIGN) |
141 | #define HEADER_COPY_SIZE (128 - NET_IP_ALIGN) | 142 | #define HEADER_COPY_SIZE (128 - NET_IP_ALIGN) |
143 | #define MLX4_LOOPBACK_TEST_PAYLOAD (HEADER_COPY_SIZE - ETH_HLEN) | ||
142 | 144 | ||
143 | #define MLX4_EN_MIN_MTU 46 | 145 | #define MLX4_EN_MIN_MTU 46 |
144 | #define ETH_BCAST 0xffffffffffffULL | 146 | #define ETH_BCAST 0xffffffffffffULL |
145 | 147 | ||
148 | #define MLX4_EN_LOOPBACK_RETRIES 5 | ||
149 | #define MLX4_EN_LOOPBACK_TIMEOUT 100 | ||
150 | |||
146 | #ifdef MLX4_EN_PERF_STAT | 151 | #ifdef MLX4_EN_PERF_STAT |
147 | /* Number of samples to 'average' */ | 152 | /* Number of samples to 'average' */ |
148 | #define AVG_SIZE 128 | 153 | #define AVG_SIZE 128 |
@@ -356,6 +361,12 @@ struct mlx4_en_rss_context { | |||
356 | __be32 rss_key[10]; | 361 | __be32 rss_key[10]; |
357 | }; | 362 | }; |
358 | 363 | ||
364 | struct mlx4_en_port_state { | ||
365 | int link_state; | ||
366 | int link_speed; | ||
367 | int transciver; | ||
368 | }; | ||
369 | |||
359 | struct mlx4_en_pkt_stats { | 370 | struct mlx4_en_pkt_stats { |
360 | unsigned long broadcast; | 371 | unsigned long broadcast; |
361 | unsigned long rx_prio[8]; | 372 | unsigned long rx_prio[8]; |
@@ -404,6 +415,7 @@ struct mlx4_en_priv { | |||
404 | struct vlan_group *vlgrp; | 415 | struct vlan_group *vlgrp; |
405 | struct net_device_stats stats; | 416 | struct net_device_stats stats; |
406 | struct net_device_stats ret_stats; | 417 | struct net_device_stats ret_stats; |
418 | struct mlx4_en_port_state port_state; | ||
407 | spinlock_t stats_lock; | 419 | spinlock_t stats_lock; |
408 | 420 | ||
409 | unsigned long last_moder_packets; | 421 | unsigned long last_moder_packets; |
@@ -422,6 +434,8 @@ struct mlx4_en_priv { | |||
422 | u16 sample_interval; | 434 | u16 sample_interval; |
423 | u16 adaptive_rx_coal; | 435 | u16 adaptive_rx_coal; |
424 | u32 msg_enable; | 436 | u32 msg_enable; |
437 | u32 loopback_ok; | ||
438 | u32 validate_loopback; | ||
425 | 439 | ||
426 | struct mlx4_hwq_resources res; | 440 | struct mlx4_hwq_resources res; |
427 | int link_state; | 441 | int link_state; |
@@ -530,6 +544,11 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, | |||
530 | u8 promisc); | 544 | u8 promisc); |
531 | 545 | ||
532 | int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset); | 546 | int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset); |
547 | int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port); | ||
548 | |||
549 | #define MLX4_EN_NUM_SELF_TEST 5 | ||
550 | void mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf); | ||
551 | u64 mlx4_en_mac_to_u64(u8 *addr); | ||
533 | 552 | ||
534 | /* | 553 | /* |
535 | * Globals | 554 | * Globals |
diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index 0f82293a82ed..78a1b9671752 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h | |||
@@ -56,6 +56,7 @@ enum { | |||
56 | MLX4_CMD_QUERY_HCA = 0xb, | 56 | MLX4_CMD_QUERY_HCA = 0xb, |
57 | MLX4_CMD_QUERY_PORT = 0x43, | 57 | MLX4_CMD_QUERY_PORT = 0x43, |
58 | MLX4_CMD_SENSE_PORT = 0x4d, | 58 | MLX4_CMD_SENSE_PORT = 0x4d, |
59 | MLX4_CMD_HW_HEALTH_CHECK = 0x50, | ||
59 | MLX4_CMD_SET_PORT = 0xc, | 60 | MLX4_CMD_SET_PORT = 0xc, |
60 | MLX4_CMD_ACCESS_DDR = 0x2e, | 61 | MLX4_CMD_ACCESS_DDR = 0x2e, |
61 | MLX4_CMD_MAP_ICM = 0xffa, | 62 | MLX4_CMD_MAP_ICM = 0xffa, |
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 7a7f9c1e679a..2cec58722738 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h | |||
@@ -229,6 +229,7 @@ struct mlx4_caps { | |||
229 | u32 bmme_flags; | 229 | u32 bmme_flags; |
230 | u32 reserved_lkey; | 230 | u32 reserved_lkey; |
231 | u16 stat_rate_support; | 231 | u16 stat_rate_support; |
232 | int loopback_support; | ||
232 | u8 port_width_cap[MLX4_MAX_PORTS + 1]; | 233 | u8 port_width_cap[MLX4_MAX_PORTS + 1]; |
233 | int max_gso_sz; | 234 | int max_gso_sz; |
234 | int reserved_qps_cnt[MLX4_NUM_QP_REGION]; | 235 | int reserved_qps_cnt[MLX4_NUM_QP_REGION]; |
@@ -480,5 +481,6 @@ void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr, | |||
480 | u32 *lkey, u32 *rkey); | 481 | u32 *lkey, u32 *rkey); |
481 | int mlx4_fmr_free(struct mlx4_dev *dev, struct mlx4_fmr *fmr); | 482 | int mlx4_fmr_free(struct mlx4_dev *dev, struct mlx4_fmr *fmr); |
482 | int mlx4_SYNC_TPT(struct mlx4_dev *dev); | 483 | int mlx4_SYNC_TPT(struct mlx4_dev *dev); |
484 | int mlx4_test_interrupts(struct mlx4_dev *dev); | ||
483 | 485 | ||
484 | #endif /* MLX4_DEVICE_H */ | 486 | #endif /* MLX4_DEVICE_H */ |