]>
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 | ||
77 | port_state = mlx5_query_vport_state(priv->mdev, MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT, 0); | |
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 | ||
d709b2a1 | 101 | #ifdef CONFIG_INET |
0952da79 SM |
102 | /* loopback test */ |
103 | #define MLX5E_TEST_PKT_SIZE (MLX5_MPWRQ_SMALL_PACKET_THRESHOLD - NET_IP_ALIGN) | |
104 | static const char mlx5e_test_text[ETH_GSTRING_LEN] = "MLX5E SELF TEST"; | |
105 | #define MLX5E_TEST_MAGIC 0x5AEED15C001ULL | |
106 | ||
107 | struct mlx5ehdr { | |
108 | __be32 version; | |
109 | __be64 magic; | |
110 | char text[ETH_GSTRING_LEN]; | |
111 | }; | |
112 | ||
113 | static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv) | |
114 | { | |
115 | struct sk_buff *skb = NULL; | |
116 | struct mlx5ehdr *mlxh; | |
117 | struct ethhdr *ethh; | |
118 | struct udphdr *udph; | |
119 | struct iphdr *iph; | |
120 | int datalen, iplen; | |
121 | ||
122 | datalen = MLX5E_TEST_PKT_SIZE - | |
123 | (sizeof(*ethh) + sizeof(*iph) + sizeof(*udph)); | |
124 | ||
125 | skb = netdev_alloc_skb(priv->netdev, MLX5E_TEST_PKT_SIZE); | |
126 | if (!skb) { | |
127 | netdev_err(priv->netdev, "\tFailed to alloc loopback skb\n"); | |
128 | return NULL; | |
129 | } | |
130 | ||
131 | prefetchw(skb->data); | |
132 | skb_reserve(skb, NET_IP_ALIGN); | |
133 | ||
134 | /* Reserve for ethernet and IP header */ | |
135 | ethh = (struct ethhdr *)skb_push(skb, ETH_HLEN); | |
136 | skb_reset_mac_header(skb); | |
137 | ||
138 | skb_set_network_header(skb, skb->len); | |
4df864c1 | 139 | iph = skb_put(skb, sizeof(struct iphdr)); |
0952da79 SM |
140 | |
141 | skb_set_transport_header(skb, skb->len); | |
4df864c1 | 142 | udph = skb_put(skb, sizeof(struct udphdr)); |
0952da79 SM |
143 | |
144 | /* Fill ETH header */ | |
145 | ether_addr_copy(ethh->h_dest, priv->netdev->dev_addr); | |
146 | eth_zero_addr(ethh->h_source); | |
147 | ethh->h_proto = htons(ETH_P_IP); | |
148 | ||
149 | /* Fill UDP header */ | |
150 | udph->source = htons(9); | |
151 | udph->dest = htons(9); /* Discard Protocol */ | |
152 | udph->len = htons(datalen + sizeof(struct udphdr)); | |
153 | udph->check = 0; | |
154 | ||
155 | /* Fill IP header */ | |
156 | iph->ihl = 5; | |
157 | iph->ttl = 32; | |
158 | iph->version = 4; | |
159 | iph->protocol = IPPROTO_UDP; | |
160 | iplen = sizeof(struct iphdr) + sizeof(struct udphdr) + datalen; | |
161 | iph->tot_len = htons(iplen); | |
162 | iph->frag_off = 0; | |
163 | iph->saddr = 0; | |
164 | iph->daddr = 0; | |
165 | iph->tos = 0; | |
166 | iph->id = 0; | |
167 | ip_send_check(iph); | |
168 | ||
169 | /* Fill test header and data */ | |
4df864c1 | 170 | mlxh = skb_put(skb, sizeof(*mlxh)); |
0952da79 SM |
171 | mlxh->version = 0; |
172 | mlxh->magic = cpu_to_be64(MLX5E_TEST_MAGIC); | |
173 | strlcpy(mlxh->text, mlx5e_test_text, sizeof(mlxh->text)); | |
174 | datalen -= sizeof(*mlxh); | |
b080db58 | 175 | skb_put_zero(skb, datalen); |
0952da79 SM |
176 | |
177 | skb->csum = 0; | |
178 | skb->ip_summed = CHECKSUM_PARTIAL; | |
179 | udp4_hwcsum(skb, iph->saddr, iph->daddr); | |
180 | ||
181 | skb->protocol = htons(ETH_P_IP); | |
182 | skb->pkt_type = PACKET_HOST; | |
183 | skb->dev = priv->netdev; | |
184 | ||
185 | return skb; | |
186 | } | |
187 | ||
188 | struct mlx5e_lbt_priv { | |
189 | struct packet_type pt; | |
190 | struct completion comp; | |
191 | bool loopback_ok; | |
192 | }; | |
193 | ||
194 | static int | |
195 | mlx5e_test_loopback_validate(struct sk_buff *skb, | |
196 | struct net_device *ndev, | |
197 | struct packet_type *pt, | |
198 | struct net_device *orig_ndev) | |
199 | { | |
200 | struct mlx5e_lbt_priv *lbtp = pt->af_packet_priv; | |
201 | struct mlx5ehdr *mlxh; | |
202 | struct ethhdr *ethh; | |
203 | struct udphdr *udph; | |
204 | struct iphdr *iph; | |
205 | ||
206 | /* We are only going to peek, no need to clone the SKB */ | |
0952da79 SM |
207 | if (MLX5E_TEST_PKT_SIZE - ETH_HLEN > skb_headlen(skb)) |
208 | goto out; | |
209 | ||
210 | ethh = (struct ethhdr *)skb_mac_header(skb); | |
211 | if (!ether_addr_equal(ethh->h_dest, orig_ndev->dev_addr)) | |
212 | goto out; | |
213 | ||
214 | iph = ip_hdr(skb); | |
215 | if (iph->protocol != IPPROTO_UDP) | |
216 | goto out; | |
217 | ||
218 | udph = udp_hdr(skb); | |
219 | if (udph->dest != htons(9)) | |
220 | goto out; | |
221 | ||
222 | mlxh = (struct mlx5ehdr *)((char *)udph + sizeof(*udph)); | |
223 | if (mlxh->magic != cpu_to_be64(MLX5E_TEST_MAGIC)) | |
224 | goto out; /* so close ! */ | |
225 | ||
226 | /* bingo */ | |
227 | lbtp->loopback_ok = true; | |
228 | complete(&lbtp->comp); | |
229 | out: | |
230 | kfree_skb(skb); | |
231 | return 0; | |
232 | } | |
233 | ||
234 | static int mlx5e_test_loopback_setup(struct mlx5e_priv *priv, | |
235 | struct mlx5e_lbt_priv *lbtp) | |
236 | { | |
237 | int err = 0; | |
238 | ||
b676f653 SM |
239 | err = mlx5e_refresh_tirs(priv, true); |
240 | if (err) | |
0952da79 | 241 | return err; |
0952da79 SM |
242 | |
243 | lbtp->loopback_ok = false; | |
244 | init_completion(&lbtp->comp); | |
245 | ||
ea29bd30 | 246 | lbtp->pt.type = htons(ETH_P_IP); |
0952da79 SM |
247 | lbtp->pt.func = mlx5e_test_loopback_validate; |
248 | lbtp->pt.dev = priv->netdev; | |
249 | lbtp->pt.af_packet_priv = lbtp; | |
250 | dev_add_pack(&lbtp->pt); | |
251 | return err; | |
252 | } | |
253 | ||
254 | static void mlx5e_test_loopback_cleanup(struct mlx5e_priv *priv, | |
255 | struct mlx5e_lbt_priv *lbtp) | |
256 | { | |
257 | dev_remove_pack(&lbtp->pt); | |
b676f653 | 258 | mlx5e_refresh_tirs(priv, false); |
0952da79 SM |
259 | } |
260 | ||
261 | #define MLX5E_LB_VERIFY_TIMEOUT (msecs_to_jiffies(200)) | |
262 | static int mlx5e_test_loopback(struct mlx5e_priv *priv) | |
263 | { | |
264 | struct mlx5e_lbt_priv *lbtp; | |
265 | struct sk_buff *skb = NULL; | |
266 | int err; | |
267 | ||
268 | if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { | |
269 | netdev_err(priv->netdev, | |
270 | "\tCan't perform loobpack test while device is down\n"); | |
271 | return -ENODEV; | |
272 | } | |
273 | ||
274 | lbtp = kzalloc(sizeof(*lbtp), GFP_KERNEL); | |
275 | if (!lbtp) | |
276 | return -ENOMEM; | |
277 | lbtp->loopback_ok = false; | |
278 | ||
279 | err = mlx5e_test_loopback_setup(priv, lbtp); | |
280 | if (err) | |
281 | goto out; | |
282 | ||
283 | skb = mlx5e_test_get_udp_skb(priv); | |
284 | if (!skb) { | |
285 | err = -ENOMEM; | |
286 | goto cleanup; | |
287 | } | |
288 | ||
289 | skb_set_queue_mapping(skb, 0); | |
290 | err = dev_queue_xmit(skb); | |
291 | if (err) { | |
292 | netdev_err(priv->netdev, | |
293 | "\tFailed to xmit loopback packet err(%d)\n", | |
294 | err); | |
295 | goto cleanup; | |
296 | } | |
297 | ||
298 | wait_for_completion_timeout(&lbtp->comp, MLX5E_LB_VERIFY_TIMEOUT); | |
299 | err = !lbtp->loopback_ok; | |
300 | ||
301 | cleanup: | |
302 | mlx5e_test_loopback_cleanup(priv, lbtp); | |
303 | out: | |
304 | kfree(lbtp); | |
305 | return err; | |
306 | } | |
d709b2a1 | 307 | #endif |
0952da79 | 308 | |
d605d668 KH |
309 | static int (*mlx5e_st_func[MLX5E_ST_NUM])(struct mlx5e_priv *) = { |
310 | mlx5e_test_link_state, | |
311 | mlx5e_test_link_speed, | |
312 | mlx5e_test_health_info, | |
d709b2a1 AB |
313 | #ifdef CONFIG_INET |
314 | mlx5e_test_loopback, | |
315 | #endif | |
d605d668 KH |
316 | }; |
317 | ||
318 | void mlx5e_self_test(struct net_device *ndev, struct ethtool_test *etest, | |
319 | u64 *buf) | |
320 | { | |
321 | struct mlx5e_priv *priv = netdev_priv(ndev); | |
322 | int i; | |
323 | ||
324 | memset(buf, 0, sizeof(u64) * MLX5E_ST_NUM); | |
325 | ||
326 | mutex_lock(&priv->state_lock); | |
327 | netdev_info(ndev, "Self test begin..\n"); | |
328 | ||
329 | for (i = 0; i < MLX5E_ST_NUM; i++) { | |
330 | netdev_info(ndev, "\t[%d] %s start..\n", | |
331 | i, mlx5e_self_tests[i]); | |
332 | buf[i] = mlx5e_st_func[i](priv); | |
333 | netdev_info(ndev, "\t[%d] %s end: result(%lld)\n", | |
334 | i, mlx5e_self_tests[i], buf[i]); | |
335 | } | |
336 | ||
337 | mutex_unlock(&priv->state_lock); | |
338 | ||
339 | for (i = 0; i < MLX5E_ST_NUM; i++) { | |
340 | if (buf[i]) { | |
341 | etest->flags |= ETH_TEST_FL_FAILED; | |
342 | break; | |
343 | } | |
344 | } | |
345 | netdev_info(ndev, "Self test out: status flags(0x%x)\n", | |
346 | etest->flags); | |
347 | } |