565 lines · c
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 two5 * licenses. You may choose to be licensed under the terms of the GNU6 * General Public License (GPL) Version 2, available from the file7 * COPYING in the main directory of this source tree, or the8 * OpenIB.org BSD license below:9 *10 * Redistribution and use in source and binary forms, with or11 * without modification, are permitted provided that the following12 * conditions are met:13 *14 * - Redistributions of source code must retain the above15 * copyright notice, this list of conditions and the following16 * disclaimer.17 *18 * - Redistributions in binary form must reproduce the above19 * copyright notice, this list of conditions and the following20 * disclaimer in the documentation and/or other materials21 * 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 OF25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30 * SOFTWARE.31 */32 33#include <linux/mlx5/driver.h>34#include <linux/mlx5/eswitch.h>35#include <linux/mlx5/mlx5_ifc_vdpa.h>36#include <linux/mlx5/vport.h>37#include "mlx5_core.h"38#include "devlink.h"39#include "lag/lag.h"40 41static DEFINE_IDA(mlx5_adev_ida);42 43static bool is_eth_rep_supported(struct mlx5_core_dev *dev)44{45 if (!IS_ENABLED(CONFIG_MLX5_ESWITCH))46 return false;47 48 if (!MLX5_ESWITCH_MANAGER(dev))49 return false;50 51 if (!is_mdev_switchdev_mode(dev))52 return false;53 54 return true;55}56 57bool mlx5_eth_supported(struct mlx5_core_dev *dev)58{59 if (!IS_ENABLED(CONFIG_MLX5_CORE_EN))60 return false;61 62 if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)63 return false;64 65 if (!MLX5_CAP_GEN(dev, eth_net_offloads)) {66 mlx5_core_warn(dev, "Missing eth_net_offloads capability\n");67 return false;68 }69 70 if (!MLX5_CAP_GEN(dev, nic_flow_table)) {71 mlx5_core_warn(dev, "Missing nic_flow_table capability\n");72 return false;73 }74 75 if (!MLX5_CAP_ETH(dev, csum_cap)) {76 mlx5_core_warn(dev, "Missing csum_cap capability\n");77 return false;78 }79 80 if (!MLX5_CAP_ETH(dev, max_lso_cap)) {81 mlx5_core_warn(dev, "Missing max_lso_cap capability\n");82 return false;83 }84 85 if (!MLX5_CAP_ETH(dev, vlan_cap)) {86 mlx5_core_warn(dev, "Missing vlan_cap capability\n");87 return false;88 }89 90 if (!MLX5_CAP_ETH(dev, rss_ind_tbl_cap)) {91 mlx5_core_warn(dev, "Missing rss_ind_tbl_cap capability\n");92 return false;93 }94 95 if (MLX5_CAP_FLOWTABLE(dev,96 flow_table_properties_nic_receive.max_ft_level) < 3) {97 mlx5_core_warn(dev, "max_ft_level < 3\n");98 return false;99 }100 101 if (!MLX5_CAP_ETH(dev, self_lb_en_modifiable))102 mlx5_core_warn(dev, "Self loop back prevention is not supported\n");103 if (!MLX5_CAP_GEN(dev, cq_moderation))104 mlx5_core_warn(dev, "CQ moderation is not supported\n");105 106 return true;107}108 109bool mlx5_vnet_supported(struct mlx5_core_dev *dev)110{111 if (!IS_ENABLED(CONFIG_MLX5_VDPA_NET))112 return false;113 114 if (mlx5_core_is_pf(dev))115 return false;116 117 if (!(MLX5_CAP_GEN_64(dev, general_obj_types) &118 MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q))119 return false;120 121 if (!(MLX5_CAP_DEV_VDPA_EMULATION(dev, event_mode) &122 MLX5_VIRTIO_Q_EVENT_MODE_QP_MODE))123 return false;124 125 if (!MLX5_CAP_DEV_VDPA_EMULATION(dev, eth_frame_offload_type))126 return false;127 128 return true;129}130 131static bool is_vnet_enabled(struct mlx5_core_dev *dev)132{133 union devlink_param_value val;134 int err;135 136 err = devl_param_driverinit_value_get(priv_to_devlink(dev),137 DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,138 &val);139 return err ? false : val.vbool;140}141 142static bool is_ib_rep_supported(struct mlx5_core_dev *dev)143{144 if (!IS_ENABLED(CONFIG_MLX5_INFINIBAND))145 return false;146 147 if (dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_IB_ADEV)148 return false;149 150 if (!is_eth_rep_supported(dev))151 return false;152 153 if (mlx5_core_mp_enabled(dev))154 return false;155 156 return true;157}158 159static bool is_mp_supported(struct mlx5_core_dev *dev)160{161 if (!IS_ENABLED(CONFIG_MLX5_INFINIBAND))162 return false;163 164 if (dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_IB_ADEV)165 return false;166 167 if (is_ib_rep_supported(dev))168 return false;169 170 if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)171 return false;172 173 if (!mlx5_core_is_mp_slave(dev))174 return false;175 176 return true;177}178 179bool mlx5_rdma_supported(struct mlx5_core_dev *dev)180{181 if (!IS_ENABLED(CONFIG_MLX5_INFINIBAND))182 return false;183 184 if (dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_IB_ADEV)185 return false;186 187 if (is_ib_rep_supported(dev))188 return false;189 190 if (is_mp_supported(dev))191 return false;192 193 return true;194}195 196static bool is_ib_enabled(struct mlx5_core_dev *dev)197{198 union devlink_param_value val;199 int err;200 201 err = devl_param_driverinit_value_get(priv_to_devlink(dev),202 DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,203 &val);204 return err ? false : val.vbool;205}206 207static bool is_dpll_supported(struct mlx5_core_dev *dev)208{209 if (!IS_ENABLED(CONFIG_MLX5_DPLL))210 return false;211 212 if (!MLX5_CAP_MCAM_REG2(dev, synce_registers)) {213 mlx5_core_dbg(dev, "Missing SyncE capability\n");214 return false;215 }216 217 return true;218}219 220enum {221 MLX5_INTERFACE_PROTOCOL_ETH,222 MLX5_INTERFACE_PROTOCOL_ETH_REP,223 224 MLX5_INTERFACE_PROTOCOL_IB,225 MLX5_INTERFACE_PROTOCOL_IB_REP,226 MLX5_INTERFACE_PROTOCOL_MPIB,227 228 MLX5_INTERFACE_PROTOCOL_VNET,229 230 MLX5_INTERFACE_PROTOCOL_DPLL,231};232 233static const struct mlx5_adev_device {234 const char *suffix;235 bool (*is_supported)(struct mlx5_core_dev *dev);236 bool (*is_enabled)(struct mlx5_core_dev *dev);237} mlx5_adev_devices[] = {238 [MLX5_INTERFACE_PROTOCOL_VNET] = { .suffix = "vnet",239 .is_supported = &mlx5_vnet_supported,240 .is_enabled = &is_vnet_enabled },241 [MLX5_INTERFACE_PROTOCOL_IB] = { .suffix = "rdma",242 .is_supported = &mlx5_rdma_supported,243 .is_enabled = &is_ib_enabled },244 [MLX5_INTERFACE_PROTOCOL_ETH] = { .suffix = "eth",245 .is_supported = &mlx5_eth_supported,246 .is_enabled = &mlx5_core_is_eth_enabled },247 [MLX5_INTERFACE_PROTOCOL_ETH_REP] = { .suffix = "eth-rep",248 .is_supported = &is_eth_rep_supported },249 [MLX5_INTERFACE_PROTOCOL_IB_REP] = { .suffix = "rdma-rep",250 .is_supported = &is_ib_rep_supported },251 [MLX5_INTERFACE_PROTOCOL_MPIB] = { .suffix = "multiport",252 .is_supported = &is_mp_supported },253 [MLX5_INTERFACE_PROTOCOL_DPLL] = { .suffix = "dpll",254 .is_supported = &is_dpll_supported },255};256 257int mlx5_adev_idx_alloc(void)258{259 return ida_alloc(&mlx5_adev_ida, GFP_KERNEL);260}261 262void mlx5_adev_idx_free(int idx)263{264 ida_free(&mlx5_adev_ida, idx);265}266 267int mlx5_adev_init(struct mlx5_core_dev *dev)268{269 struct mlx5_priv *priv = &dev->priv;270 271 priv->adev = kcalloc(ARRAY_SIZE(mlx5_adev_devices),272 sizeof(struct mlx5_adev *), GFP_KERNEL);273 if (!priv->adev)274 return -ENOMEM;275 276 return 0;277}278 279void mlx5_adev_cleanup(struct mlx5_core_dev *dev)280{281 struct mlx5_priv *priv = &dev->priv;282 283 kfree(priv->adev);284}285 286static void adev_release(struct device *dev)287{288 struct mlx5_adev *mlx5_adev =289 container_of(dev, struct mlx5_adev, adev.dev);290 struct mlx5_priv *priv = &mlx5_adev->mdev->priv;291 int idx = mlx5_adev->idx;292 293 kfree(mlx5_adev);294 priv->adev[idx] = NULL;295}296 297static struct mlx5_adev *add_adev(struct mlx5_core_dev *dev, int idx)298{299 const char *suffix = mlx5_adev_devices[idx].suffix;300 struct auxiliary_device *adev;301 struct mlx5_adev *madev;302 int ret;303 304 madev = kzalloc(sizeof(*madev), GFP_KERNEL);305 if (!madev)306 return ERR_PTR(-ENOMEM);307 308 adev = &madev->adev;309 adev->id = dev->priv.adev_idx;310 adev->name = suffix;311 adev->dev.parent = dev->device;312 adev->dev.release = adev_release;313 madev->mdev = dev;314 madev->idx = idx;315 316 ret = auxiliary_device_init(adev);317 if (ret) {318 kfree(madev);319 return ERR_PTR(ret);320 }321 322 ret = auxiliary_device_add(adev);323 if (ret) {324 auxiliary_device_uninit(adev);325 return ERR_PTR(ret);326 }327 return madev;328}329 330static void del_adev(struct auxiliary_device *adev)331{332 auxiliary_device_delete(adev);333 auxiliary_device_uninit(adev);334}335 336void mlx5_dev_set_lightweight(struct mlx5_core_dev *dev)337{338 mlx5_devcom_comp_lock(dev->priv.hca_devcom_comp);339 dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV;340 mlx5_devcom_comp_unlock(dev->priv.hca_devcom_comp);341}342 343bool mlx5_dev_is_lightweight(struct mlx5_core_dev *dev)344{345 return dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV;346}347 348int mlx5_attach_device(struct mlx5_core_dev *dev)349{350 struct mlx5_priv *priv = &dev->priv;351 struct auxiliary_device *adev;352 const struct auxiliary_driver *adrv;353 int ret = 0, i;354 355 devl_assert_locked(priv_to_devlink(dev));356 mlx5_devcom_comp_lock(dev->priv.hca_devcom_comp);357 priv->flags &= ~MLX5_PRIV_FLAGS_DETACH;358 for (i = 0; i < ARRAY_SIZE(mlx5_adev_devices); i++) {359 if (!priv->adev[i]) {360 bool is_supported = false;361 362 if (mlx5_adev_devices[i].is_enabled) {363 bool enabled;364 365 enabled = mlx5_adev_devices[i].is_enabled(dev);366 if (!enabled)367 continue;368 }369 370 if (mlx5_adev_devices[i].is_supported)371 is_supported = mlx5_adev_devices[i].is_supported(dev);372 373 if (!is_supported)374 continue;375 376 priv->adev[i] = add_adev(dev, i);377 if (IS_ERR(priv->adev[i])) {378 ret = PTR_ERR(priv->adev[i]);379 priv->adev[i] = NULL;380 }381 } else {382 adev = &priv->adev[i]->adev;383 384 /* Pay attention that this is not PCI driver that385 * mlx5_core_dev is connected, but auxiliary driver.386 */387 if (!adev->dev.driver)388 continue;389 adrv = to_auxiliary_drv(adev->dev.driver);390 391 if (adrv->resume)392 ret = adrv->resume(adev);393 }394 if (ret) {395 mlx5_core_warn(dev, "Device[%d] (%s) failed to load\n",396 i, mlx5_adev_devices[i].suffix);397 398 break;399 }400 }401 mlx5_devcom_comp_unlock(dev->priv.hca_devcom_comp);402 return ret;403}404 405void mlx5_detach_device(struct mlx5_core_dev *dev, bool suspend)406{407 struct mlx5_priv *priv = &dev->priv;408 struct auxiliary_device *adev;409 const struct auxiliary_driver *adrv;410 pm_message_t pm = {};411 int i;412 413 devl_assert_locked(priv_to_devlink(dev));414 mlx5_devcom_comp_lock(dev->priv.hca_devcom_comp);415 for (i = ARRAY_SIZE(mlx5_adev_devices) - 1; i >= 0; i--) {416 if (!priv->adev[i])417 continue;418 419 if (mlx5_adev_devices[i].is_enabled) {420 bool enabled;421 422 enabled = mlx5_adev_devices[i].is_enabled(dev);423 if (!enabled)424 goto skip_suspend;425 }426 427 adev = &priv->adev[i]->adev;428 /* Auxiliary driver was unbind manually through sysfs */429 if (!adev->dev.driver)430 goto skip_suspend;431 432 adrv = to_auxiliary_drv(adev->dev.driver);433 434 if (adrv->suspend && suspend) {435 adrv->suspend(adev, pm);436 continue;437 }438 439skip_suspend:440 del_adev(&priv->adev[i]->adev);441 priv->adev[i] = NULL;442 }443 priv->flags |= MLX5_PRIV_FLAGS_DETACH;444 mlx5_devcom_comp_unlock(dev->priv.hca_devcom_comp);445}446 447int mlx5_register_device(struct mlx5_core_dev *dev)448{449 int ret;450 451 devl_assert_locked(priv_to_devlink(dev));452 mlx5_devcom_comp_lock(dev->priv.hca_devcom_comp);453 dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV;454 ret = mlx5_rescan_drivers_locked(dev);455 mlx5_devcom_comp_unlock(dev->priv.hca_devcom_comp);456 if (ret)457 mlx5_unregister_device(dev);458 459 return ret;460}461 462void mlx5_unregister_device(struct mlx5_core_dev *dev)463{464 devl_assert_locked(priv_to_devlink(dev));465 mlx5_devcom_comp_lock(dev->priv.hca_devcom_comp);466 dev->priv.flags = MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV;467 mlx5_rescan_drivers_locked(dev);468 mlx5_devcom_comp_unlock(dev->priv.hca_devcom_comp);469}470 471static int add_drivers(struct mlx5_core_dev *dev)472{473 struct mlx5_priv *priv = &dev->priv;474 int i, ret = 0;475 476 for (i = 0; i < ARRAY_SIZE(mlx5_adev_devices); i++) {477 bool is_supported = false;478 479 if (priv->adev[i])480 continue;481 482 if (mlx5_adev_devices[i].is_enabled &&483 !(mlx5_adev_devices[i].is_enabled(dev)))484 continue;485 486 if (mlx5_adev_devices[i].is_supported)487 is_supported = mlx5_adev_devices[i].is_supported(dev);488 489 if (!is_supported)490 continue;491 492 priv->adev[i] = add_adev(dev, i);493 if (IS_ERR(priv->adev[i])) {494 mlx5_core_warn(dev, "Device[%d] (%s) failed to load\n",495 i, mlx5_adev_devices[i].suffix);496 /* We continue to rescan drivers and leave to the caller497 * to make decision if to release everything or continue.498 */499 ret = PTR_ERR(priv->adev[i]);500 priv->adev[i] = NULL;501 }502 }503 return ret;504}505 506static void delete_drivers(struct mlx5_core_dev *dev)507{508 struct mlx5_priv *priv = &dev->priv;509 bool delete_all;510 int i;511 512 delete_all = priv->flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV;513 514 for (i = ARRAY_SIZE(mlx5_adev_devices) - 1; i >= 0; i--) {515 bool is_supported = false;516 517 if (!priv->adev[i])518 continue;519 520 if (mlx5_adev_devices[i].is_enabled) {521 bool enabled;522 523 enabled = mlx5_adev_devices[i].is_enabled(dev);524 if (!enabled)525 goto del_adev;526 }527 528 if (mlx5_adev_devices[i].is_supported && !delete_all)529 is_supported = mlx5_adev_devices[i].is_supported(dev);530 531 if (is_supported)532 continue;533 534del_adev:535 del_adev(&priv->adev[i]->adev);536 priv->adev[i] = NULL;537 }538}539 540/* This function is used after mlx5_core_dev is reconfigured.541 */542int mlx5_rescan_drivers_locked(struct mlx5_core_dev *dev)543{544 struct mlx5_priv *priv = &dev->priv;545 546 if (priv->flags & MLX5_PRIV_FLAGS_DETACH)547 return 0;548 549 delete_drivers(dev);550 if (priv->flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)551 return 0;552 553 return add_drivers(dev);554}555 556bool mlx5_same_hw_devs(struct mlx5_core_dev *dev, struct mlx5_core_dev *peer_dev)557{558 u64 fsystem_guid, psystem_guid;559 560 fsystem_guid = mlx5_query_nic_system_image_guid(dev);561 psystem_guid = mlx5_query_nic_system_image_guid(peer_dev);562 563 return (fsystem_guid && psystem_guid && fsystem_guid == psystem_guid);564}565