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 54 void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) 55 { 56 struct mlx5_device_context *dev_ctx; 57 struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); 58 59 if (!mlx5_lag_intf_add(intf, priv)) 60 return; 61 62 dev_ctx = kzalloc(sizeof(*dev_ctx), GFP_KERNEL); 63 if (!dev_ctx) 64 return; 65 66 dev_ctx->intf = intf; 67 68 dev_ctx->context = intf->add(dev); 69 if (dev_ctx->context) { 70 set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state); 71 if (intf->attach) 72 set_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state); 73 74 spin_lock_irq(&priv->ctx_lock); 75 list_add_tail(&dev_ctx->list, &priv->ctx_list); 76 spin_unlock_irq(&priv->ctx_lock); 77 } 78 79 if (!dev_ctx->context) 80 kfree(dev_ctx); 81 } 82 83 static struct mlx5_device_context *mlx5_get_device(struct mlx5_interface *intf, 84 struct mlx5_priv *priv) 85 { 86 struct mlx5_device_context *dev_ctx; 87 88 list_for_each_entry(dev_ctx, &priv->ctx_list, list) 89 if (dev_ctx->intf == intf) 90 return dev_ctx; 91 return NULL; 92 } 93 94 void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv) 95 { 96 struct mlx5_device_context *dev_ctx; 97 struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); 98 99 dev_ctx = mlx5_get_device(intf, priv); 100 if (!dev_ctx) 101 return; 102 103 spin_lock_irq(&priv->ctx_lock); 104 list_del(&dev_ctx->list); 105 spin_unlock_irq(&priv->ctx_lock); 106 107 if (test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state)) 108 intf->remove(dev, dev_ctx->context); 109 110 kfree(dev_ctx); 111 } 112 113 static void mlx5_attach_interface(struct mlx5_interface *intf, struct mlx5_priv *priv) 114 { 115 struct mlx5_device_context *dev_ctx; 116 struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); 117 118 dev_ctx = mlx5_get_device(intf, priv); 119 if (!dev_ctx) 120 return; 121 122 if (intf->attach) { 123 if (test_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state)) 124 return; 125 if (intf->attach(dev, dev_ctx->context)) 126 return; 127 set_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state); 128 } else { 129 if (test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state)) 130 return; 131 dev_ctx->context = intf->add(dev); 132 if (!dev_ctx->context) 133 return; 134 set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state); 135 } 136 } 137 138 void mlx5_attach_device(struct mlx5_core_dev *dev) 139 { 140 struct mlx5_priv *priv = &dev->priv; 141 struct mlx5_interface *intf; 142 143 mutex_lock(&mlx5_intf_mutex); 144 list_for_each_entry(intf, &intf_list, list) 145 mlx5_attach_interface(intf, priv); 146 mutex_unlock(&mlx5_intf_mutex); 147 } 148 149 static void mlx5_detach_interface(struct mlx5_interface *intf, struct mlx5_priv *priv) 150 { 151 struct mlx5_device_context *dev_ctx; 152 struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); 153 154 dev_ctx = mlx5_get_device(intf, priv); 155 if (!dev_ctx) 156 return; 157 158 if (intf->detach) { 159 if (!test_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state)) 160 return; 161 intf->detach(dev, dev_ctx->context); 162 clear_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state); 163 } else { 164 if (!test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state)) 165 return; 166 intf->remove(dev, dev_ctx->context); 167 clear_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state); 168 } 169 } 170 171 void mlx5_detach_device(struct mlx5_core_dev *dev) 172 { 173 struct mlx5_priv *priv = &dev->priv; 174 struct mlx5_interface *intf; 175 176 mutex_lock(&mlx5_intf_mutex); 177 list_for_each_entry(intf, &intf_list, list) 178 mlx5_detach_interface(intf, priv); 179 mutex_unlock(&mlx5_intf_mutex); 180 } 181 182 bool mlx5_device_registered(struct mlx5_core_dev *dev) 183 { 184 struct mlx5_priv *priv; 185 bool found = false; 186 187 mutex_lock(&mlx5_intf_mutex); 188 list_for_each_entry(priv, &mlx5_dev_list, dev_list) 189 if (priv == &dev->priv) 190 found = true; 191 mutex_unlock(&mlx5_intf_mutex); 192 193 return found; 194 } 195 196 void mlx5_register_device(struct mlx5_core_dev *dev) 197 { 198 struct mlx5_priv *priv = &dev->priv; 199 struct mlx5_interface *intf; 200 201 mutex_lock(&mlx5_intf_mutex); 202 list_add_tail(&priv->dev_list, &mlx5_dev_list); 203 list_for_each_entry(intf, &intf_list, list) 204 mlx5_add_device(intf, priv); 205 mutex_unlock(&mlx5_intf_mutex); 206 } 207 208 void mlx5_unregister_device(struct mlx5_core_dev *dev) 209 { 210 struct mlx5_priv *priv = &dev->priv; 211 struct mlx5_interface *intf; 212 213 mutex_lock(&mlx5_intf_mutex); 214 list_for_each_entry_reverse(intf, &intf_list, list) 215 mlx5_remove_device(intf, priv); 216 list_del(&priv->dev_list); 217 mutex_unlock(&mlx5_intf_mutex); 218 } 219 220 int mlx5_register_interface(struct mlx5_interface *intf) 221 { 222 struct mlx5_priv *priv; 223 224 if (!intf->add || !intf->remove) 225 return -EINVAL; 226 227 mutex_lock(&mlx5_intf_mutex); 228 list_add_tail(&intf->list, &intf_list); 229 list_for_each_entry(priv, &mlx5_dev_list, dev_list) 230 mlx5_add_device(intf, priv); 231 mutex_unlock(&mlx5_intf_mutex); 232 233 return 0; 234 } 235 EXPORT_SYMBOL(mlx5_register_interface); 236 237 void mlx5_unregister_interface(struct mlx5_interface *intf) 238 { 239 struct mlx5_priv *priv; 240 241 mutex_lock(&mlx5_intf_mutex); 242 list_for_each_entry(priv, &mlx5_dev_list, dev_list) 243 mlx5_remove_device(intf, priv); 244 list_del(&intf->list); 245 mutex_unlock(&mlx5_intf_mutex); 246 } 247 EXPORT_SYMBOL(mlx5_unregister_interface); 248 249 /* Must be called with intf_mutex held */ 250 static bool mlx5_has_added_dev_by_protocol(struct mlx5_core_dev *mdev, int protocol) 251 { 252 struct mlx5_device_context *dev_ctx; 253 struct mlx5_interface *intf; 254 bool found = false; 255 256 list_for_each_entry(intf, &intf_list, list) { 257 if (intf->protocol == protocol) { 258 dev_ctx = mlx5_get_device(intf, &mdev->priv); 259 if (dev_ctx && test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state)) 260 found = true; 261 break; 262 } 263 } 264 265 return found; 266 } 267 268 void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol) 269 { 270 mutex_lock(&mlx5_intf_mutex); 271 if (mlx5_has_added_dev_by_protocol(mdev, protocol)) { 272 mlx5_remove_dev_by_protocol(mdev, protocol); 273 mlx5_add_dev_by_protocol(mdev, protocol); 274 } 275 mutex_unlock(&mlx5_intf_mutex); 276 } 277 278 /* Must be called with intf_mutex held */ 279 void mlx5_add_dev_by_protocol(struct mlx5_core_dev *dev, int protocol) 280 { 281 struct mlx5_interface *intf; 282 283 list_for_each_entry(intf, &intf_list, list) 284 if (intf->protocol == protocol) { 285 mlx5_add_device(intf, &dev->priv); 286 break; 287 } 288 } 289 290 /* Must be called with intf_mutex held */ 291 void mlx5_remove_dev_by_protocol(struct mlx5_core_dev *dev, int protocol) 292 { 293 struct mlx5_interface *intf; 294 295 list_for_each_entry(intf, &intf_list, list) 296 if (intf->protocol == protocol) { 297 mlx5_remove_device(intf, &dev->priv); 298 break; 299 } 300 } 301 302 static u32 mlx5_gen_pci_id(struct mlx5_core_dev *dev) 303 { 304 return (u32)((pci_domain_nr(dev->pdev->bus) << 16) | 305 (dev->pdev->bus->number << 8) | 306 PCI_SLOT(dev->pdev->devfn)); 307 } 308 309 /* Must be called with intf_mutex held */ 310 struct mlx5_core_dev *mlx5_get_next_phys_dev(struct mlx5_core_dev *dev) 311 { 312 struct mlx5_core_dev *res = NULL; 313 struct mlx5_core_dev *tmp_dev; 314 struct mlx5_priv *priv; 315 u32 pci_id; 316 317 if (!mlx5_core_is_pf(dev)) 318 return NULL; 319 320 pci_id = mlx5_gen_pci_id(dev); 321 list_for_each_entry(priv, &mlx5_dev_list, dev_list) { 322 tmp_dev = container_of(priv, struct mlx5_core_dev, priv); 323 if (!mlx5_core_is_pf(tmp_dev)) 324 continue; 325 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 336 void mlx5_dev_list_lock(void) 337 { 338 mutex_lock(&mlx5_intf_mutex); 339 } 340 341 void mlx5_dev_list_unlock(void) 342 { 343 mutex_unlock(&mlx5_intf_mutex); 344 } 345 346 int mlx5_dev_list_trylock(void) 347 { 348 return mutex_trylock(&mlx5_intf_mutex); 349 } 350