1 | /* |
2 | * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. |
3 | * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved. |
4 | * |
5 | * This software is available to you under a choice of one of two |
6 | * licenses. You may choose to be licensed under the terms of the GNU |
7 | * General Public License (GPL) Version 2, available from the file |
8 | * COPYING in the main directory of this source tree, or the |
9 | * OpenIB.org BSD license below: |
10 | * |
11 | * Redistribution and use in source and binary forms, with or |
12 | * without modification, are permitted provided that the following |
13 | * conditions are met: |
14 | * |
15 | * - Redistributions of source code must retain the above |
16 | * copyright notice, this list of conditions and the following |
17 | * disclaimer. |
18 | * |
19 | * - Redistributions in binary form must reproduce the above |
20 | * copyright notice, this list of conditions and the following |
21 | * disclaimer in the documentation and/or other materials |
22 | * provided with the distribution. |
23 | * |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
31 | * SOFTWARE. |
32 | */ |
33 | |
34 | #include <linux/slab.h> |
35 | #include <linux/export.h> |
36 | #include <linux/errno.h> |
37 | #include <net/devlink.h> |
38 | |
39 | #include "mlx4.h" |
40 | |
41 | static DEFINE_MUTEX(intf_mutex); |
42 | static DEFINE_IDA(mlx4_adev_ida); |
43 | |
44 | static bool is_eth_supported(struct mlx4_dev *dev) |
45 | { |
46 | for (int port = 1; port <= dev->caps.num_ports; port++) |
47 | if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) |
48 | return true; |
49 | |
50 | return false; |
51 | } |
52 | |
53 | static bool is_ib_supported(struct mlx4_dev *dev) |
54 | { |
55 | for (int port = 1; port <= dev->caps.num_ports; port++) |
56 | if (dev->caps.port_type[port] == MLX4_PORT_TYPE_IB) |
57 | return true; |
58 | |
59 | if (dev->caps.flags & MLX4_DEV_CAP_FLAG_IBOE) |
60 | return true; |
61 | |
62 | return false; |
63 | } |
64 | |
65 | static const struct mlx4_adev_device { |
66 | const char *suffix; |
67 | bool (*is_supported)(struct mlx4_dev *dev); |
68 | } mlx4_adev_devices[] = { |
69 | { "eth" , is_eth_supported }, |
70 | { "ib" , is_ib_supported }, |
71 | }; |
72 | |
73 | int mlx4_adev_init(struct mlx4_dev *dev) |
74 | { |
75 | struct mlx4_priv *priv = mlx4_priv(dev); |
76 | |
77 | priv->adev_idx = ida_alloc(ida: &mlx4_adev_ida, GFP_KERNEL); |
78 | if (priv->adev_idx < 0) |
79 | return priv->adev_idx; |
80 | |
81 | priv->adev = kcalloc(ARRAY_SIZE(mlx4_adev_devices), |
82 | size: sizeof(struct mlx4_adev *), GFP_KERNEL); |
83 | if (!priv->adev) { |
84 | ida_free(&mlx4_adev_ida, id: priv->adev_idx); |
85 | return -ENOMEM; |
86 | } |
87 | |
88 | return 0; |
89 | } |
90 | |
91 | void mlx4_adev_cleanup(struct mlx4_dev *dev) |
92 | { |
93 | struct mlx4_priv *priv = mlx4_priv(dev); |
94 | |
95 | kfree(objp: priv->adev); |
96 | ida_free(&mlx4_adev_ida, id: priv->adev_idx); |
97 | } |
98 | |
99 | static void adev_release(struct device *dev) |
100 | { |
101 | struct mlx4_adev *mlx4_adev = |
102 | container_of(dev, struct mlx4_adev, adev.dev); |
103 | struct mlx4_priv *priv = mlx4_priv(dev: mlx4_adev->mdev); |
104 | int idx = mlx4_adev->idx; |
105 | |
106 | kfree(objp: mlx4_adev); |
107 | priv->adev[idx] = NULL; |
108 | } |
109 | |
110 | static struct mlx4_adev *add_adev(struct mlx4_dev *dev, int idx) |
111 | { |
112 | struct mlx4_priv *priv = mlx4_priv(dev); |
113 | const char *suffix = mlx4_adev_devices[idx].suffix; |
114 | struct auxiliary_device *adev; |
115 | struct mlx4_adev *madev; |
116 | int ret; |
117 | |
118 | madev = kzalloc(size: sizeof(*madev), GFP_KERNEL); |
119 | if (!madev) |
120 | return ERR_PTR(error: -ENOMEM); |
121 | |
122 | adev = &madev->adev; |
123 | adev->id = priv->adev_idx; |
124 | adev->name = suffix; |
125 | adev->dev.parent = &dev->persist->pdev->dev; |
126 | adev->dev.release = adev_release; |
127 | madev->mdev = dev; |
128 | madev->idx = idx; |
129 | |
130 | ret = auxiliary_device_init(auxdev: adev); |
131 | if (ret) { |
132 | kfree(objp: madev); |
133 | return ERR_PTR(error: ret); |
134 | } |
135 | |
136 | ret = auxiliary_device_add(adev); |
137 | if (ret) { |
138 | auxiliary_device_uninit(auxdev: adev); |
139 | return ERR_PTR(error: ret); |
140 | } |
141 | return madev; |
142 | } |
143 | |
144 | static void del_adev(struct auxiliary_device *adev) |
145 | { |
146 | auxiliary_device_delete(auxdev: adev); |
147 | auxiliary_device_uninit(auxdev: adev); |
148 | } |
149 | |
150 | int mlx4_register_auxiliary_driver(struct mlx4_adrv *madrv) |
151 | { |
152 | return auxiliary_driver_register(&madrv->adrv); |
153 | } |
154 | EXPORT_SYMBOL_GPL(mlx4_register_auxiliary_driver); |
155 | |
156 | void mlx4_unregister_auxiliary_driver(struct mlx4_adrv *madrv) |
157 | { |
158 | auxiliary_driver_unregister(auxdrv: &madrv->adrv); |
159 | } |
160 | EXPORT_SYMBOL_GPL(mlx4_unregister_auxiliary_driver); |
161 | |
162 | int mlx4_do_bond(struct mlx4_dev *dev, bool enable) |
163 | { |
164 | struct mlx4_priv *priv = mlx4_priv(dev); |
165 | int i, ret; |
166 | |
167 | if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP)) |
168 | return -EOPNOTSUPP; |
169 | |
170 | ret = mlx4_disable_rx_port_check(dev, dis: enable); |
171 | if (ret) { |
172 | mlx4_err(dev, "Fail to %s rx port check\n" , |
173 | enable ? "enable" : "disable" ); |
174 | return ret; |
175 | } |
176 | if (enable) { |
177 | dev->flags |= MLX4_FLAG_BONDED; |
178 | } else { |
179 | ret = mlx4_virt2phy_port_map(dev, port1: 1, port2: 2); |
180 | if (ret) { |
181 | mlx4_err(dev, "Fail to reset port map\n" ); |
182 | return ret; |
183 | } |
184 | dev->flags &= ~MLX4_FLAG_BONDED; |
185 | } |
186 | |
187 | mutex_lock(&intf_mutex); |
188 | |
189 | for (i = 0; i < ARRAY_SIZE(mlx4_adev_devices); i++) { |
190 | struct mlx4_adev *madev = priv->adev[i]; |
191 | struct mlx4_adrv *madrv; |
192 | enum mlx4_protocol protocol; |
193 | |
194 | if (!madev) |
195 | continue; |
196 | |
197 | device_lock(dev: &madev->adev.dev); |
198 | if (!madev->adev.dev.driver) { |
199 | device_unlock(dev: &madev->adev.dev); |
200 | continue; |
201 | } |
202 | |
203 | madrv = container_of(madev->adev.dev.driver, struct mlx4_adrv, |
204 | adrv.driver); |
205 | if (!(madrv->flags & MLX4_INTFF_BONDING)) { |
206 | device_unlock(dev: &madev->adev.dev); |
207 | continue; |
208 | } |
209 | |
210 | if (mlx4_is_mfunc(dev)) { |
211 | mlx4_dbg(dev, |
212 | "SRIOV, disabled HA mode for intf proto %d\n" , |
213 | madrv->protocol); |
214 | device_unlock(dev: &madev->adev.dev); |
215 | continue; |
216 | } |
217 | |
218 | protocol = madrv->protocol; |
219 | device_unlock(dev: &madev->adev.dev); |
220 | |
221 | del_adev(adev: &madev->adev); |
222 | priv->adev[i] = add_adev(dev, idx: i); |
223 | if (IS_ERR(ptr: priv->adev[i])) { |
224 | mlx4_warn(dev, "Device[%d] (%s) failed to load\n" , i, |
225 | mlx4_adev_devices[i].suffix); |
226 | priv->adev[i] = NULL; |
227 | continue; |
228 | } |
229 | |
230 | mlx4_dbg(dev, |
231 | "Interface for protocol %d restarted with bonded mode %s\n" , |
232 | protocol, enable ? "enabled" : "disabled" ); |
233 | } |
234 | |
235 | mutex_unlock(lock: &intf_mutex); |
236 | |
237 | return 0; |
238 | } |
239 | |
240 | void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, |
241 | void *param) |
242 | { |
243 | struct mlx4_priv *priv = mlx4_priv(dev); |
244 | |
245 | atomic_notifier_call_chain(nh: &priv->event_nh, val: type, v: param); |
246 | } |
247 | |
248 | int mlx4_register_event_notifier(struct mlx4_dev *dev, |
249 | struct notifier_block *nb) |
250 | { |
251 | struct mlx4_priv *priv = mlx4_priv(dev); |
252 | |
253 | return atomic_notifier_chain_register(nh: &priv->event_nh, nb); |
254 | } |
255 | EXPORT_SYMBOL(mlx4_register_event_notifier); |
256 | |
257 | int mlx4_unregister_event_notifier(struct mlx4_dev *dev, |
258 | struct notifier_block *nb) |
259 | { |
260 | struct mlx4_priv *priv = mlx4_priv(dev); |
261 | |
262 | return atomic_notifier_chain_unregister(nh: &priv->event_nh, nb); |
263 | } |
264 | EXPORT_SYMBOL(mlx4_unregister_event_notifier); |
265 | |
266 | static int add_drivers(struct mlx4_dev *dev) |
267 | { |
268 | struct mlx4_priv *priv = mlx4_priv(dev); |
269 | int i, ret = 0; |
270 | |
271 | for (i = 0; i < ARRAY_SIZE(mlx4_adev_devices); i++) { |
272 | bool is_supported = false; |
273 | |
274 | if (priv->adev[i]) |
275 | continue; |
276 | |
277 | if (mlx4_adev_devices[i].is_supported) |
278 | is_supported = mlx4_adev_devices[i].is_supported(dev); |
279 | |
280 | if (!is_supported) |
281 | continue; |
282 | |
283 | priv->adev[i] = add_adev(dev, idx: i); |
284 | if (IS_ERR(ptr: priv->adev[i])) { |
285 | mlx4_warn(dev, "Device[%d] (%s) failed to load\n" , i, |
286 | mlx4_adev_devices[i].suffix); |
287 | /* We continue to rescan drivers and leave to the caller |
288 | * to make decision if to release everything or |
289 | * continue. */ |
290 | ret = PTR_ERR(ptr: priv->adev[i]); |
291 | priv->adev[i] = NULL; |
292 | } |
293 | } |
294 | return ret; |
295 | } |
296 | |
297 | static void delete_drivers(struct mlx4_dev *dev) |
298 | { |
299 | struct mlx4_priv *priv = mlx4_priv(dev); |
300 | bool delete_all; |
301 | int i; |
302 | |
303 | delete_all = !(dev->persist->interface_state & MLX4_INTERFACE_STATE_UP); |
304 | |
305 | for (i = ARRAY_SIZE(mlx4_adev_devices) - 1; i >= 0; i--) { |
306 | bool is_supported = false; |
307 | |
308 | if (!priv->adev[i]) |
309 | continue; |
310 | |
311 | if (mlx4_adev_devices[i].is_supported && !delete_all) |
312 | is_supported = mlx4_adev_devices[i].is_supported(dev); |
313 | |
314 | if (is_supported) |
315 | continue; |
316 | |
317 | del_adev(adev: &priv->adev[i]->adev); |
318 | priv->adev[i] = NULL; |
319 | } |
320 | } |
321 | |
322 | /* This function is used after mlx4_dev is reconfigured. |
323 | */ |
324 | static int rescan_drivers_locked(struct mlx4_dev *dev) |
325 | { |
326 | lockdep_assert_held(&intf_mutex); |
327 | |
328 | delete_drivers(dev); |
329 | if (!(dev->persist->interface_state & MLX4_INTERFACE_STATE_UP)) |
330 | return 0; |
331 | |
332 | return add_drivers(dev); |
333 | } |
334 | |
335 | int mlx4_register_device(struct mlx4_dev *dev) |
336 | { |
337 | int ret; |
338 | |
339 | mutex_lock(&intf_mutex); |
340 | |
341 | dev->persist->interface_state |= MLX4_INTERFACE_STATE_UP; |
342 | |
343 | ret = rescan_drivers_locked(dev); |
344 | |
345 | mutex_unlock(lock: &intf_mutex); |
346 | |
347 | if (ret) { |
348 | mlx4_unregister_device(dev); |
349 | return ret; |
350 | } |
351 | |
352 | mlx4_start_catas_poll(dev); |
353 | |
354 | return ret; |
355 | } |
356 | |
357 | void mlx4_unregister_device(struct mlx4_dev *dev) |
358 | { |
359 | if (!(dev->persist->interface_state & MLX4_INTERFACE_STATE_UP)) |
360 | return; |
361 | |
362 | mlx4_stop_catas_poll(dev); |
363 | if (dev->persist->interface_state & MLX4_INTERFACE_STATE_DELETION && |
364 | mlx4_is_slave(dev)) { |
365 | /* In mlx4_remove_one on a VF */ |
366 | u32 slave_read = |
367 | swab32(readl(&mlx4_priv(dev)->mfunc.comm->slave_read)); |
368 | |
369 | if (mlx4_comm_internal_err(slave_read)) { |
370 | mlx4_dbg(dev, "%s: comm channel is down, entering error state.\n" , |
371 | __func__); |
372 | mlx4_enter_error_state(persist: dev->persist); |
373 | } |
374 | } |
375 | mutex_lock(&intf_mutex); |
376 | |
377 | dev->persist->interface_state &= ~MLX4_INTERFACE_STATE_UP; |
378 | |
379 | rescan_drivers_locked(dev); |
380 | |
381 | mutex_unlock(lock: &intf_mutex); |
382 | } |
383 | |
384 | struct devlink_port *mlx4_get_devlink_port(struct mlx4_dev *dev, int port) |
385 | { |
386 | struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; |
387 | |
388 | return &info->devlink_port; |
389 | } |
390 | EXPORT_SYMBOL_GPL(mlx4_get_devlink_port); |
391 | |