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(dev: priv->mdev->dev, in_param: 0, in_modifier: 0, op_modifier: 0, op: MLX4_CMD_HW_HEALTH_CHECK, |
46 | timeout: MLX4_CMD_TIME_CLASS_A, native: MLX4_CMD_WRAPPED); |
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(dev: priv->dev, MLX4_LOOPBACK_TEST_PAYLOAD + ETH_HLEN + NET_IP_ALIGN); |
61 | if (!skb) |
62 | return -ENOMEM; |
63 | |
64 | skb_reserve(skb, NET_IP_ALIGN); |
65 | |
66 | ethh = skb_put(skb, len: sizeof(struct ethhdr)); |
67 | packet = skb_put(skb, len: packet_size); |
68 | memcpy(ethh->h_dest, priv->dev->dev_addr, ETH_ALEN); |
69 | eth_zero_addr(addr: ethh->h_source); |
70 | ethh->h_proto = htons(ETH_P_ARP); |
71 | skb_reset_mac_header(skb); |
72 | for (i = 0; i < packet_size; ++i) /* fill our packet */ |
73 | packet[i] = (unsigned char)(i & 0xff); |
74 | |
75 | /* xmit the pkt */ |
76 | err = mlx4_en_xmit(skb, dev: priv->dev); |
77 | return err; |
78 | } |
79 | |
80 | static int mlx4_en_test_loopback(struct mlx4_en_priv *priv) |
81 | { |
82 | u32 loopback_ok = 0; |
83 | int i; |
84 | |
85 | priv->loopback_ok = 0; |
86 | priv->validate_loopback = 1; |
87 | |
88 | mlx4_en_update_loopback_state(dev: priv->dev, features: priv->dev->features); |
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 | |
111 | mlx4_en_update_loopback_state(dev: priv->dev, features: priv->dev->features); |
112 | return !loopback_ok; |
113 | } |
114 | |
115 | static int mlx4_en_test_interrupts(struct mlx4_en_priv *priv) |
116 | { |
117 | struct mlx4_en_dev *mdev = priv->mdev; |
118 | int err = 0; |
119 | int i = 0; |
120 | |
121 | err = mlx4_test_async(dev: mdev->dev); |
122 | /* When not in MSI_X or slave, test only async */ |
123 | if (!(mdev->dev->flags & MLX4_FLAG_MSI_X) || mlx4_is_slave(dev: mdev->dev)) |
124 | return err; |
125 | |
126 | /* A loop over all completion vectors of current port, |
127 | * for each vector check whether it works by mapping command |
128 | * completions to that vector and performing a NOP command |
129 | */ |
130 | for (i = 0; i < priv->rx_ring_num; i++) { |
131 | err = mlx4_test_interrupt(dev: mdev->dev, vector: priv->rx_cq[i]->vector); |
132 | if (err) |
133 | break; |
134 | } |
135 | |
136 | return err; |
137 | } |
138 | |
139 | static int mlx4_en_test_link(struct mlx4_en_priv *priv) |
140 | { |
141 | if (mlx4_en_QUERY_PORT(mdev: priv->mdev, port: priv->port)) |
142 | return -ENOMEM; |
143 | if (priv->port_state.link_state == 1) |
144 | return 0; |
145 | else |
146 | return 1; |
147 | } |
148 | |
149 | static int mlx4_en_test_speed(struct mlx4_en_priv *priv) |
150 | { |
151 | |
152 | if (mlx4_en_QUERY_PORT(mdev: priv->mdev, port: priv->port)) |
153 | return -ENOMEM; |
154 | |
155 | /* The device supports 100M, 1G, 10G, 20G, 40G and 56G speed */ |
156 | if (priv->port_state.link_speed != SPEED_100 && |
157 | priv->port_state.link_speed != SPEED_1000 && |
158 | priv->port_state.link_speed != SPEED_10000 && |
159 | priv->port_state.link_speed != SPEED_20000 && |
160 | priv->port_state.link_speed != SPEED_40000 && |
161 | priv->port_state.link_speed != SPEED_56000) |
162 | return priv->port_state.link_speed; |
163 | |
164 | return 0; |
165 | } |
166 | |
167 | |
168 | void mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf) |
169 | { |
170 | struct mlx4_en_priv *priv = netdev_priv(dev); |
171 | int i, carrier_ok; |
172 | |
173 | memset(buf, 0, sizeof(u64) * MLX4_EN_NUM_SELF_TEST); |
174 | |
175 | if (*flags & ETH_TEST_FL_OFFLINE) { |
176 | /* disable the interface */ |
177 | carrier_ok = netif_carrier_ok(dev); |
178 | |
179 | netif_carrier_off(dev); |
180 | /* Wait until all tx queues are empty. |
181 | * there should not be any additional incoming traffic |
182 | * since we turned the carrier off */ |
183 | msleep(msecs: 200); |
184 | |
185 | if (priv->mdev->dev->caps.flags & |
186 | MLX4_DEV_CAP_FLAG_UC_LOOPBACK) { |
187 | buf[3] = mlx4_en_test_registers(priv); |
188 | if (priv->port_up && dev->mtu >= MLX4_SELFTEST_LB_MIN_MTU) |
189 | buf[4] = mlx4_en_test_loopback(priv); |
190 | } |
191 | |
192 | if (carrier_ok) |
193 | netif_carrier_on(dev); |
194 | |
195 | } |
196 | buf[0] = mlx4_en_test_interrupts(priv); |
197 | buf[1] = mlx4_en_test_link(priv); |
198 | buf[2] = mlx4_en_test_speed(priv); |
199 | |
200 | for (i = 0; i < MLX4_EN_NUM_SELF_TEST; i++) { |
201 | if (buf[i]) |
202 | *flags |= ETH_TEST_FL_FAILED; |
203 | } |
204 | } |
205 | |