]>
Commit | Line | Data |
---|---|---|
d605d668 KH |
1 | /* |
2 | * Copyright (c) 2016, Mellanox Technologies, Ltd. 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 | ||
18bcf742 | 33 | #include <linux/prefetch.h> |
0952da79 SM |
34 | #include <linux/ip.h> |
35 | #include <linux/udp.h> | |
36 | #include <net/udp.h> | |
d605d668 KH |
37 | #include "en.h" |
38 | ||
39 | enum { | |
40 | MLX5E_ST_LINK_STATE, | |
41 | MLX5E_ST_LINK_SPEED, | |
42 | MLX5E_ST_HEALTH_INFO, | |
d709b2a1 | 43 | #ifdef CONFIG_INET |
0952da79 | 44 | MLX5E_ST_LOOPBACK, |
d709b2a1 | 45 | #endif |
d605d668 KH |
46 | MLX5E_ST_NUM, |
47 | }; | |
48 | ||
49 | const char mlx5e_self_tests[MLX5E_ST_NUM][ETH_GSTRING_LEN] = { | |
50 | "Link Test", | |
51 | "Speed Test", | |
52 | "Health Test", | |
d709b2a1 | 53 | #ifdef CONFIG_INET |
0952da79 | 54 | "Loopback Test", |
d709b2a1 | 55 | #endif |
d605d668 KH |
56 | }; |
57 | ||
58 | int mlx5e_self_test_num(struct mlx5e_priv *priv) | |
59 | { | |
60 | return ARRAY_SIZE(mlx5e_self_tests); | |
61 | } | |
62 | ||
63 | static int mlx5e_test_health_info(struct mlx5e_priv *priv) | |
64 | { | |
65 | struct mlx5_core_health *health = &priv->mdev->priv.health; | |
66 | ||
67 | return health->sick ? 1 : 0; | |
68 | } | |
69 | ||
70 | static int mlx5e_test_link_state(struct mlx5e_priv *priv) | |
71 | { | |
72 | u8 port_state; | |
73 | ||
74 | if (!netif_carrier_ok(priv->netdev)) | |
75 | return 1; | |
76 | ||
cc9c82a8 | 77 | port_state = mlx5_query_vport_state(priv->mdev, MLX5_VPORT_STATE_OP_MOD_VNIC_VPORT, 0); |
d605d668 KH |
78 | return port_state == VPORT_STATE_UP ? 0 : 1; |
79 | } | |
80 | ||
81 | static int mlx5e_test_link_speed(struct mlx5e_priv *priv) | |
82 | { | |
83 | u32 out[MLX5_ST_SZ_DW(ptys_reg)]; | |
84 | u32 eth_proto_oper; | |
85 | int i; | |
86 | ||
87 | if (!netif_carrier_ok(priv->netdev)) | |
88 | return 1; | |
89 | ||
90 | if (mlx5_query_port_ptys(priv->mdev, out, sizeof(out), MLX5_PTYS_EN, 1)) | |
91 | return 1; | |
92 | ||
93 | eth_proto_oper = MLX5_GET(ptys_reg, out, eth_proto_oper); | |
94 | for (i = 0; i < MLX5E_LINK_MODES_NUMBER; i++) { | |
95 | if (eth_proto_oper & MLX5E_PROT_MASK(i)) | |
96 | return 0; | |
97 | } | |
98 | return 1; | |
99 | } | |
100 | ||
0952da79 SM |
101 | struct mlx5ehdr { |
102 | __be32 version; | |
103 | __be64 magic; | |
0952da79 SM |
104 | }; |
105 | ||
228c4cd0 VF |
106 | #ifdef CONFIG_INET |
107 | /* loopback test */ | |
108 | #define MLX5E_TEST_PKT_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) +\ | |
109 | sizeof(struct udphdr) + sizeof(struct mlx5ehdr)) | |
110 | #define MLX5E_TEST_MAGIC 0x5AEED15C001ULL | |
111 | ||
0952da79 SM |
112 | static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv) |
113 | { | |
114 | struct sk_buff *skb = NULL; | |
115 | struct mlx5ehdr *mlxh; | |
116 | struct ethhdr *ethh; | |
117 | struct udphdr *udph; | |
118 | struct iphdr *iph; | |
228c4cd0 | 119 | int iplen; |
0952da79 SM |
120 | |
121 | skb = netdev_alloc_skb(priv->netdev, MLX5E_TEST_PKT_SIZE); | |
122 | if (!skb) { | |
123 | netdev_err(priv->netdev, "\tFailed to alloc loopback skb\n"); | |
124 | return NULL; | |
125 | } | |
126 | ||
127 | prefetchw(skb->data); | |
128 | skb_reserve(skb, NET_IP_ALIGN); | |
129 | ||
130 | /* Reserve for ethernet and IP header */ | |
d58ff351 | 131 | ethh = skb_push(skb, ETH_HLEN); |
0952da79 SM |
132 | skb_reset_mac_header(skb); |
133 | ||
134 | skb_set_network_header(skb, skb->len); | |
4df864c1 | 135 | iph = skb_put(skb, sizeof(struct iphdr)); |
0952da79 SM |
136 | |
137 | skb_set_transport_header(skb, skb->len); | |
4df864c1 | 138 | udph = skb_put(skb, sizeof(struct udphdr)); |
0952da79 SM |
139 | |
140 | /* Fill ETH header */ | |
141 | ether_addr_copy(ethh->h_dest, priv->netdev->dev_addr); | |
142 | eth_zero_addr(ethh->h_source); | |
143 | ethh->h_proto = htons(ETH_P_IP); | |
144 | ||
145 | /* Fill UDP header */ | |
146 | udph->source = htons(9); | |
147 | udph->dest = htons(9); /* Discard Protocol */ | |
228c4cd0 | 148 | udph->len = htons(sizeof(struct mlx5ehdr) + sizeof(struct udphdr)); |
0952da79 SM |
149 | udph->check = 0; |
150 | ||
151 | /* Fill IP header */ | |
152 | iph->ihl = 5; | |
153 | iph->ttl = 32; | |
154 | iph->version = 4; | |
155 | iph->protocol = IPPROTO_UDP; | |
228c4cd0 VF |
156 | iplen = sizeof(struct iphdr) + sizeof(struct udphdr) + |
157 | sizeof(struct mlx5ehdr); | |
0952da79 SM |
158 | iph->tot_len = htons(iplen); |
159 | iph->frag_off = 0; | |
160 | iph->saddr = 0; | |
161 | iph->daddr = 0; | |
162 | iph->tos = 0; | |
163 | iph->id = 0; | |
164 | ip_send_check(iph); | |
165 | ||
166 | /* Fill test header and data */ | |
4df864c1 | 167 | mlxh = skb_put(skb, sizeof(*mlxh)); |
0952da79 SM |
168 | mlxh->version = 0; |
169 | mlxh->magic = cpu_to_be64(MLX5E_TEST_MAGIC); | |
0952da79 SM |
170 | |
171 | skb->csum = 0; | |
172 | skb->ip_summed = CHECKSUM_PARTIAL; | |
173 | udp4_hwcsum(skb, iph->saddr, iph->daddr); | |
174 | ||
175 | skb->protocol = htons(ETH_P_IP); | |
176 | skb->pkt_type = PACKET_HOST; | |
177 | skb->dev = priv->netdev; | |
178 | ||
179 | return skb; | |
180 | } | |
181 | ||
182 | struct mlx5e_lbt_priv { | |
183 | struct packet_type pt; | |
184 | struct completion comp; | |
185 | bool loopback_ok; | |
2c43c5a0 | 186 | bool local_lb; |
0952da79 SM |
187 | }; |
188 | ||
189 | static int | |
190 | mlx5e_test_loopback_validate(struct sk_buff *skb, | |
191 | struct net_device *ndev, | |
192 | struct packet_type *pt, | |
193 | struct net_device *orig_ndev) | |
194 | { | |
195 | struct mlx5e_lbt_priv *lbtp = pt->af_packet_priv; | |
196 | struct mlx5ehdr *mlxh; | |
197 | struct ethhdr *ethh; | |
198 | struct udphdr *udph; | |
199 | struct iphdr *iph; | |
200 | ||
201 | /* We are only going to peek, no need to clone the SKB */ | |
0952da79 SM |
202 | if (MLX5E_TEST_PKT_SIZE - ETH_HLEN > skb_headlen(skb)) |
203 | goto out; | |
204 | ||
205 | ethh = (struct ethhdr *)skb_mac_header(skb); | |
206 | if (!ether_addr_equal(ethh->h_dest, orig_ndev->dev_addr)) | |
207 | goto out; | |
208 | ||
209 | iph = ip_hdr(skb); | |
210 | if (iph->protocol != IPPROTO_UDP) | |
211 | goto out; | |
212 | ||
ef7a3518 IK |
213 | /* Don't assume skb_transport_header() was set */ |
214 | udph = (struct udphdr *)((u8 *)iph + 4 * iph->ihl); | |
0952da79 SM |
215 | if (udph->dest != htons(9)) |
216 | goto out; | |
217 | ||
218 | mlxh = (struct mlx5ehdr *)((char *)udph + sizeof(*udph)); | |
219 | if (mlxh->magic != cpu_to_be64(MLX5E_TEST_MAGIC)) | |
220 | goto out; /* so close ! */ | |
221 | ||
222 | /* bingo */ | |
223 | lbtp->loopback_ok = true; | |
224 | complete(&lbtp->comp); | |
225 | out: | |
226 | kfree_skb(skb); | |
227 | return 0; | |
228 | } | |
229 | ||
230 | static int mlx5e_test_loopback_setup(struct mlx5e_priv *priv, | |
231 | struct mlx5e_lbt_priv *lbtp) | |
232 | { | |
233 | int err = 0; | |
234 | ||
2c43c5a0 | 235 | /* Temporarily enable local_lb */ |
8978cc92 EBE |
236 | err = mlx5_nic_vport_query_local_lb(priv->mdev, &lbtp->local_lb); |
237 | if (err) | |
238 | return err; | |
239 | ||
240 | if (!lbtp->local_lb) { | |
241 | err = mlx5_nic_vport_update_local_lb(priv->mdev, true); | |
242 | if (err) | |
243 | return err; | |
2c43c5a0 HN |
244 | } |
245 | ||
b676f653 SM |
246 | err = mlx5e_refresh_tirs(priv, true); |
247 | if (err) | |
8978cc92 | 248 | goto out; |
0952da79 SM |
249 | |
250 | lbtp->loopback_ok = false; | |
251 | init_completion(&lbtp->comp); | |
252 | ||
ea29bd30 | 253 | lbtp->pt.type = htons(ETH_P_IP); |
0952da79 SM |
254 | lbtp->pt.func = mlx5e_test_loopback_validate; |
255 | lbtp->pt.dev = priv->netdev; | |
256 | lbtp->pt.af_packet_priv = lbtp; | |
257 | dev_add_pack(&lbtp->pt); | |
8978cc92 EBE |
258 | |
259 | return 0; | |
260 | ||
261 | out: | |
262 | if (!lbtp->local_lb) | |
263 | mlx5_nic_vport_update_local_lb(priv->mdev, false); | |
264 | ||
0952da79 SM |
265 | return err; |
266 | } | |
267 | ||
268 | static void mlx5e_test_loopback_cleanup(struct mlx5e_priv *priv, | |
269 | struct mlx5e_lbt_priv *lbtp) | |
270 | { | |
8978cc92 EBE |
271 | if (!lbtp->local_lb) |
272 | mlx5_nic_vport_update_local_lb(priv->mdev, false); | |
2c43c5a0 | 273 | |
0952da79 | 274 | dev_remove_pack(&lbtp->pt); |
b676f653 | 275 | mlx5e_refresh_tirs(priv, false); |
0952da79 SM |
276 | } |
277 | ||
278 | #define MLX5E_LB_VERIFY_TIMEOUT (msecs_to_jiffies(200)) | |
279 | static int mlx5e_test_loopback(struct mlx5e_priv *priv) | |
280 | { | |
281 | struct mlx5e_lbt_priv *lbtp; | |
282 | struct sk_buff *skb = NULL; | |
283 | int err; | |
284 | ||
285 | if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { | |
286 | netdev_err(priv->netdev, | |
4e11581c | 287 | "\tCan't perform loopback test while device is down\n"); |
0952da79 SM |
288 | return -ENODEV; |
289 | } | |
290 | ||
291 | lbtp = kzalloc(sizeof(*lbtp), GFP_KERNEL); | |
292 | if (!lbtp) | |
293 | return -ENOMEM; | |
294 | lbtp->loopback_ok = false; | |
295 | ||
296 | err = mlx5e_test_loopback_setup(priv, lbtp); | |
297 | if (err) | |
298 | goto out; | |
299 | ||
300 | skb = mlx5e_test_get_udp_skb(priv); | |
301 | if (!skb) { | |
302 | err = -ENOMEM; | |
303 | goto cleanup; | |
304 | } | |
305 | ||
306 | skb_set_queue_mapping(skb, 0); | |
307 | err = dev_queue_xmit(skb); | |
308 | if (err) { | |
309 | netdev_err(priv->netdev, | |
310 | "\tFailed to xmit loopback packet err(%d)\n", | |
311 | err); | |
312 | goto cleanup; | |
313 | } | |
314 | ||
315 | wait_for_completion_timeout(&lbtp->comp, MLX5E_LB_VERIFY_TIMEOUT); | |
316 | err = !lbtp->loopback_ok; | |
317 | ||
318 | cleanup: | |
319 | mlx5e_test_loopback_cleanup(priv, lbtp); | |
320 | out: | |
321 | kfree(lbtp); | |
322 | return err; | |
323 | } | |
d709b2a1 | 324 | #endif |
0952da79 | 325 | |
d605d668 KH |
326 | static int (*mlx5e_st_func[MLX5E_ST_NUM])(struct mlx5e_priv *) = { |
327 | mlx5e_test_link_state, | |
328 | mlx5e_test_link_speed, | |
329 | mlx5e_test_health_info, | |
d709b2a1 AB |
330 | #ifdef CONFIG_INET |
331 | mlx5e_test_loopback, | |
332 | #endif | |
d605d668 KH |
333 | }; |
334 | ||
335 | void mlx5e_self_test(struct net_device *ndev, struct ethtool_test *etest, | |
336 | u64 *buf) | |
337 | { | |
338 | struct mlx5e_priv *priv = netdev_priv(ndev); | |
339 | int i; | |
340 | ||
341 | memset(buf, 0, sizeof(u64) * MLX5E_ST_NUM); | |
342 | ||
343 | mutex_lock(&priv->state_lock); | |
344 | netdev_info(ndev, "Self test begin..\n"); | |
345 | ||
346 | for (i = 0; i < MLX5E_ST_NUM; i++) { | |
347 | netdev_info(ndev, "\t[%d] %s start..\n", | |
348 | i, mlx5e_self_tests[i]); | |
349 | buf[i] = mlx5e_st_func[i](priv); | |
350 | netdev_info(ndev, "\t[%d] %s end: result(%lld)\n", | |
351 | i, mlx5e_self_tests[i], buf[i]); | |
352 | } | |
353 | ||
354 | mutex_unlock(&priv->state_lock); | |
355 | ||
356 | for (i = 0; i < MLX5E_ST_NUM; i++) { | |
357 | if (buf[i]) { | |
358 | etest->flags |= ETH_TEST_FL_FAILED; | |
359 | break; | |
360 | } | |
361 | } | |
362 | netdev_info(ndev, "Self test out: status flags(0x%x)\n", | |
363 | etest->flags); | |
364 | } |