Commit | Line | Data |
---|---|---|
f1ee87fe MHY |
1 | /* |
2 | * Copyright (c) 2013-2015, 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 | #include <linux/mlx5/driver.h> | |
34 | #include "mlx5_core.h" | |
35 | ||
36 | static LIST_HEAD(intf_list); | |
37 | static LIST_HEAD(mlx5_dev_list); | |
38 | /* intf dev list mutex */ | |
39 | static DEFINE_MUTEX(mlx5_intf_mutex); | |
40 | ||
41 | struct mlx5_device_context { | |
42 | struct list_head list; | |
43 | struct mlx5_interface *intf; | |
44 | void *context; | |
45 | unsigned long state; | |
46 | }; | |
47 | ||
48 | enum { | |
49 | MLX5_INTERFACE_ADDED, | |
50 | MLX5_INTERFACE_ATTACHED, | |
51 | }; | |
52 | ||
53 | void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) | |
54 | { | |
55 | struct mlx5_device_context *dev_ctx; | |
56 | struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); | |
57 | ||
58 | if (!mlx5_lag_intf_add(intf, priv)) | |
59 | return; | |
60 | ||
61 | dev_ctx = kzalloc(sizeof(*dev_ctx), GFP_KERNEL); | |
62 | if (!dev_ctx) | |
63 | return; | |
64 | ||
65 | dev_ctx->intf = intf; | |
66 | dev_ctx->context = intf->add(dev); | |
67 | set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state); | |
68 | if (intf->attach) | |
69 | set_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state); | |
70 | ||
71 | if (dev_ctx->context) { | |
72 | spin_lock_irq(&priv->ctx_lock); | |
73 | list_add_tail(&dev_ctx->list, &priv->ctx_list); | |
d9aaed83 AK |
74 | #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING |
75 | if (dev_ctx->intf->pfault) { | |
76 | if (priv->pfault) { | |
77 | mlx5_core_err(dev, "multiple page fault handlers not supported"); | |
78 | } else { | |
79 | priv->pfault_ctx = dev_ctx->context; | |
80 | priv->pfault = dev_ctx->intf->pfault; | |
81 | } | |
82 | } | |
83 | #endif | |
f1ee87fe MHY |
84 | spin_unlock_irq(&priv->ctx_lock); |
85 | } else { | |
86 | kfree(dev_ctx); | |
87 | } | |
88 | } | |
89 | ||
90 | static struct mlx5_device_context *mlx5_get_device(struct mlx5_interface *intf, | |
91 | struct mlx5_priv *priv) | |
92 | { | |
93 | struct mlx5_device_context *dev_ctx; | |
94 | ||
95 | list_for_each_entry(dev_ctx, &priv->ctx_list, list) | |
96 | if (dev_ctx->intf == intf) | |
97 | return dev_ctx; | |
98 | return NULL; | |
99 | } | |
100 | ||
101 | void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv) | |
102 | { | |
103 | struct mlx5_device_context *dev_ctx; | |
104 | struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); | |
105 | ||
106 | dev_ctx = mlx5_get_device(intf, priv); | |
107 | if (!dev_ctx) | |
108 | return; | |
109 | ||
d9aaed83 AK |
110 | #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING |
111 | spin_lock_irq(&priv->ctx_lock); | |
112 | if (priv->pfault == dev_ctx->intf->pfault) | |
113 | priv->pfault = NULL; | |
114 | spin_unlock_irq(&priv->ctx_lock); | |
115 | ||
116 | synchronize_srcu(&priv->pfault_srcu); | |
117 | #endif | |
118 | ||
f1ee87fe MHY |
119 | spin_lock_irq(&priv->ctx_lock); |
120 | list_del(&dev_ctx->list); | |
121 | spin_unlock_irq(&priv->ctx_lock); | |
122 | ||
123 | if (test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state)) | |
124 | intf->remove(dev, dev_ctx->context); | |
125 | ||
126 | kfree(dev_ctx); | |
127 | } | |
128 | ||
129 | static void mlx5_attach_interface(struct mlx5_interface *intf, struct mlx5_priv *priv) | |
130 | { | |
131 | struct mlx5_device_context *dev_ctx; | |
132 | struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); | |
133 | ||
134 | dev_ctx = mlx5_get_device(intf, priv); | |
135 | if (!dev_ctx) | |
136 | return; | |
137 | ||
138 | if (intf->attach) { | |
139 | if (test_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state)) | |
140 | return; | |
141 | intf->attach(dev, dev_ctx->context); | |
142 | set_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state); | |
143 | } else { | |
144 | if (test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state)) | |
145 | return; | |
146 | dev_ctx->context = intf->add(dev); | |
147 | set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state); | |
148 | } | |
149 | } | |
150 | ||
151 | void mlx5_attach_device(struct mlx5_core_dev *dev) | |
152 | { | |
153 | struct mlx5_priv *priv = &dev->priv; | |
154 | struct mlx5_interface *intf; | |
155 | ||
156 | mutex_lock(&mlx5_intf_mutex); | |
157 | list_for_each_entry(intf, &intf_list, list) | |
158 | mlx5_attach_interface(intf, priv); | |
159 | mutex_unlock(&mlx5_intf_mutex); | |
160 | } | |
161 | ||
162 | static void mlx5_detach_interface(struct mlx5_interface *intf, struct mlx5_priv *priv) | |
163 | { | |
164 | struct mlx5_device_context *dev_ctx; | |
165 | struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); | |
166 | ||
167 | dev_ctx = mlx5_get_device(intf, priv); | |
168 | if (!dev_ctx) | |
169 | return; | |
170 | ||
171 | if (intf->detach) { | |
172 | if (!test_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state)) | |
173 | return; | |
174 | intf->detach(dev, dev_ctx->context); | |
175 | clear_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state); | |
176 | } else { | |
177 | if (!test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state)) | |
178 | return; | |
179 | intf->remove(dev, dev_ctx->context); | |
180 | clear_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state); | |
181 | } | |
182 | } | |
183 | ||
184 | void mlx5_detach_device(struct mlx5_core_dev *dev) | |
185 | { | |
186 | struct mlx5_priv *priv = &dev->priv; | |
187 | struct mlx5_interface *intf; | |
188 | ||
189 | mutex_lock(&mlx5_intf_mutex); | |
190 | list_for_each_entry(intf, &intf_list, list) | |
191 | mlx5_detach_interface(intf, priv); | |
192 | mutex_unlock(&mlx5_intf_mutex); | |
193 | } | |
194 | ||
195 | bool mlx5_device_registered(struct mlx5_core_dev *dev) | |
196 | { | |
197 | struct mlx5_priv *priv; | |
198 | bool found = false; | |
199 | ||
200 | mutex_lock(&mlx5_intf_mutex); | |
201 | list_for_each_entry(priv, &mlx5_dev_list, dev_list) | |
202 | if (priv == &dev->priv) | |
203 | found = true; | |
204 | mutex_unlock(&mlx5_intf_mutex); | |
205 | ||
206 | return found; | |
207 | } | |
208 | ||
209 | int mlx5_register_device(struct mlx5_core_dev *dev) | |
210 | { | |
211 | struct mlx5_priv *priv = &dev->priv; | |
212 | struct mlx5_interface *intf; | |
213 | ||
214 | mutex_lock(&mlx5_intf_mutex); | |
215 | list_add_tail(&priv->dev_list, &mlx5_dev_list); | |
216 | list_for_each_entry(intf, &intf_list, list) | |
217 | mlx5_add_device(intf, priv); | |
218 | mutex_unlock(&mlx5_intf_mutex); | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | void mlx5_unregister_device(struct mlx5_core_dev *dev) | |
224 | { | |
225 | struct mlx5_priv *priv = &dev->priv; | |
226 | struct mlx5_interface *intf; | |
227 | ||
228 | mutex_lock(&mlx5_intf_mutex); | |
229 | list_for_each_entry(intf, &intf_list, list) | |
230 | mlx5_remove_device(intf, priv); | |
231 | list_del(&priv->dev_list); | |
232 | mutex_unlock(&mlx5_intf_mutex); | |
233 | } | |
234 | ||
235 | int mlx5_register_interface(struct mlx5_interface *intf) | |
236 | { | |
237 | struct mlx5_priv *priv; | |
238 | ||
239 | if (!intf->add || !intf->remove) | |
240 | return -EINVAL; | |
241 | ||
242 | mutex_lock(&mlx5_intf_mutex); | |
243 | list_add_tail(&intf->list, &intf_list); | |
244 | list_for_each_entry(priv, &mlx5_dev_list, dev_list) | |
245 | mlx5_add_device(intf, priv); | |
246 | mutex_unlock(&mlx5_intf_mutex); | |
247 | ||
248 | return 0; | |
249 | } | |
250 | EXPORT_SYMBOL(mlx5_register_interface); | |
251 | ||
252 | void mlx5_unregister_interface(struct mlx5_interface *intf) | |
253 | { | |
254 | struct mlx5_priv *priv; | |
255 | ||
256 | mutex_lock(&mlx5_intf_mutex); | |
257 | list_for_each_entry(priv, &mlx5_dev_list, dev_list) | |
258 | mlx5_remove_device(intf, priv); | |
259 | list_del(&intf->list); | |
260 | mutex_unlock(&mlx5_intf_mutex); | |
261 | } | |
262 | EXPORT_SYMBOL(mlx5_unregister_interface); | |
263 | ||
264 | void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol) | |
265 | { | |
266 | struct mlx5_priv *priv = &mdev->priv; | |
267 | struct mlx5_device_context *dev_ctx; | |
268 | unsigned long flags; | |
269 | void *result = NULL; | |
270 | ||
271 | spin_lock_irqsave(&priv->ctx_lock, flags); | |
272 | ||
273 | list_for_each_entry(dev_ctx, &mdev->priv.ctx_list, list) | |
274 | if ((dev_ctx->intf->protocol == protocol) && | |
275 | dev_ctx->intf->get_dev) { | |
276 | result = dev_ctx->intf->get_dev(dev_ctx->context); | |
277 | break; | |
278 | } | |
279 | ||
280 | spin_unlock_irqrestore(&priv->ctx_lock, flags); | |
281 | ||
282 | return result; | |
283 | } | |
284 | EXPORT_SYMBOL(mlx5_get_protocol_dev); | |
285 | ||
286 | /* Must be called with intf_mutex held */ | |
287 | void mlx5_add_dev_by_protocol(struct mlx5_core_dev *dev, int protocol) | |
288 | { | |
289 | struct mlx5_interface *intf; | |
290 | ||
291 | list_for_each_entry(intf, &intf_list, list) | |
292 | if (intf->protocol == protocol) { | |
293 | mlx5_add_device(intf, &dev->priv); | |
294 | break; | |
295 | } | |
296 | } | |
297 | ||
298 | /* Must be called with intf_mutex held */ | |
299 | void mlx5_remove_dev_by_protocol(struct mlx5_core_dev *dev, int protocol) | |
300 | { | |
301 | struct mlx5_interface *intf; | |
302 | ||
303 | list_for_each_entry(intf, &intf_list, list) | |
304 | if (intf->protocol == protocol) { | |
305 | mlx5_remove_device(intf, &dev->priv); | |
306 | break; | |
307 | } | |
308 | } | |
309 | ||
310 | static u16 mlx5_gen_pci_id(struct mlx5_core_dev *dev) | |
311 | { | |
312 | return (u16)((dev->pdev->bus->number << 8) | | |
313 | PCI_SLOT(dev->pdev->devfn)); | |
314 | } | |
315 | ||
316 | /* Must be called with intf_mutex held */ | |
317 | struct mlx5_core_dev *mlx5_get_next_phys_dev(struct mlx5_core_dev *dev) | |
318 | { | |
319 | u16 pci_id = mlx5_gen_pci_id(dev); | |
320 | struct mlx5_core_dev *res = NULL; | |
321 | struct mlx5_core_dev *tmp_dev; | |
322 | struct mlx5_priv *priv; | |
323 | ||
324 | list_for_each_entry(priv, &mlx5_dev_list, dev_list) { | |
325 | tmp_dev = container_of(priv, struct mlx5_core_dev, priv); | |
326 | if ((dev != tmp_dev) && (mlx5_gen_pci_id(tmp_dev) == pci_id)) { | |
327 | res = tmp_dev; | |
328 | break; | |
329 | } | |
330 | } | |
331 | ||
332 | return res; | |
333 | } | |
334 | ||
335 | void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, | |
336 | unsigned long param) | |
337 | { | |
338 | struct mlx5_priv *priv = &dev->priv; | |
339 | struct mlx5_device_context *dev_ctx; | |
340 | unsigned long flags; | |
341 | ||
342 | spin_lock_irqsave(&priv->ctx_lock, flags); | |
343 | ||
344 | list_for_each_entry(dev_ctx, &priv->ctx_list, list) | |
345 | if (dev_ctx->intf->event) | |
346 | dev_ctx->intf->event(dev, dev_ctx->context, event, param); | |
347 | ||
348 | spin_unlock_irqrestore(&priv->ctx_lock, flags); | |
349 | } | |
350 | ||
d9aaed83 AK |
351 | #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING |
352 | void mlx5_core_page_fault(struct mlx5_core_dev *dev, | |
353 | struct mlx5_pagefault *pfault) | |
354 | { | |
355 | struct mlx5_priv *priv = &dev->priv; | |
356 | int srcu_idx; | |
357 | ||
358 | srcu_idx = srcu_read_lock(&priv->pfault_srcu); | |
359 | if (priv->pfault) | |
360 | priv->pfault(dev, priv->pfault_ctx, pfault); | |
361 | srcu_read_unlock(&priv->pfault_srcu, srcu_idx); | |
362 | } | |
363 | #endif | |
364 | ||
f1ee87fe MHY |
365 | void mlx5_dev_list_lock(void) |
366 | { | |
367 | mutex_lock(&mlx5_intf_mutex); | |
368 | } | |
369 | ||
370 | void mlx5_dev_list_unlock(void) | |
371 | { | |
372 | mutex_unlock(&mlx5_intf_mutex); | |
373 | } | |
374 | ||
375 | int mlx5_dev_list_trylock(void) | |
376 | { | |
377 | return mutex_trylock(&mlx5_intf_mutex); | |
378 | } |