/* * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include "mlx4.h" static DEFINE_MUTEX(intf_mutex); static DEFINE_IDA(mlx4_adev_ida); static bool is_eth_supported(struct mlx4_dev *dev) { for (int port = 1; port <= dev->caps.num_ports; port++) if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) return true; return false; } static bool is_ib_supported(struct mlx4_dev *dev) { for (int port = 1; port <= dev->caps.num_ports; port++) if (dev->caps.port_type[port] == MLX4_PORT_TYPE_IB) return true; if (dev->caps.flags & MLX4_DEV_CAP_FLAG_IBOE) return true; return false; } static const struct mlx4_adev_device { const char *suffix; bool (*is_supported)(struct mlx4_dev *dev); } mlx4_adev_devices[] = { { "eth", is_eth_supported }, { "ib", is_ib_supported }, }; int mlx4_adev_init(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); priv->adev_idx = ida_alloc(&mlx4_adev_ida, GFP_KERNEL); if (priv->adev_idx < 0) return priv->adev_idx; priv->adev = kcalloc(ARRAY_SIZE(mlx4_adev_devices), sizeof(struct mlx4_adev *), GFP_KERNEL); if (!priv->adev) { ida_free(&mlx4_adev_ida, priv->adev_idx); return -ENOMEM; } return 0; } void mlx4_adev_cleanup(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); kfree(priv->adev); ida_free(&mlx4_adev_ida, priv->adev_idx); } static void adev_release(struct device *dev) { struct mlx4_adev *mlx4_adev = container_of(dev, struct mlx4_adev, adev.dev); struct mlx4_priv *priv = mlx4_priv(mlx4_adev->mdev); int idx = mlx4_adev->idx; kfree(mlx4_adev); priv->adev[idx] = NULL; } static struct mlx4_adev *add_adev(struct mlx4_dev *dev, int idx) { struct mlx4_priv *priv = mlx4_priv(dev); const char *suffix = mlx4_adev_devices[idx].suffix; struct auxiliary_device *adev; struct mlx4_adev *madev; int ret; madev = kzalloc(sizeof(*madev), GFP_KERNEL); if (!madev) return ERR_PTR(-ENOMEM); adev = &madev->adev; adev->id = priv->adev_idx; adev->name = suffix; adev->dev.parent = &dev->persist->pdev->dev; adev->dev.release = adev_release; madev->mdev = dev; madev->idx = idx; ret = auxiliary_device_init(adev); if (ret) { kfree(madev); return ERR_PTR(ret); } ret = auxiliary_device_add(adev); if (ret) { auxiliary_device_uninit(adev); return ERR_PTR(ret); } return madev; } static void del_adev(struct auxiliary_device *adev) { auxiliary_device_delete(adev); auxiliary_device_uninit(adev); } int mlx4_register_auxiliary_driver(struct mlx4_adrv *madrv) { return auxiliary_driver_register(&madrv->adrv); } EXPORT_SYMBOL_GPL(mlx4_register_auxiliary_driver); void mlx4_unregister_auxiliary_driver(struct mlx4_adrv *madrv) { auxiliary_driver_unregister(&madrv->adrv); } EXPORT_SYMBOL_GPL(mlx4_unregister_auxiliary_driver); int mlx4_do_bond(struct mlx4_dev *dev, bool enable) { struct mlx4_priv *priv = mlx4_priv(dev); int i, ret; if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP)) return -EOPNOTSUPP; ret = mlx4_disable_rx_port_check(dev, enable); if (ret) { mlx4_err(dev, "Fail to %s rx port check\n", enable ? "enable" : "disable"); return ret; } if (enable) { dev->flags |= MLX4_FLAG_BONDED; } else { ret = mlx4_virt2phy_port_map(dev, 1, 2); if (ret) { mlx4_err(dev, "Fail to reset port map\n"); return ret; } dev->flags &= ~MLX4_FLAG_BONDED; } mutex_lock(&intf_mutex); for (i = 0; i < ARRAY_SIZE(mlx4_adev_devices); i++) { struct mlx4_adev *madev = priv->adev[i]; struct mlx4_adrv *madrv; enum mlx4_protocol protocol; if (!madev) continue; device_lock(&madev->adev.dev); if (!madev->adev.dev.driver) { device_unlock(&madev->adev.dev); continue; } madrv = container_of(madev->adev.dev.driver, struct mlx4_adrv, adrv.driver); if (!(madrv->flags & MLX4_INTFF_BONDING)) { device_unlock(&madev->adev.dev); continue; } if (mlx4_is_mfunc(dev)) { mlx4_dbg(dev, "SRIOV, disabled HA mode for intf proto %d\n", madrv->protocol); device_unlock(&madev->adev.dev); continue; } protocol = madrv->protocol; device_unlock(&madev->adev.dev); del_adev(&madev->adev); priv->adev[i] = add_adev(dev, i); if (IS_ERR(priv->adev[i])) { mlx4_warn(dev, "Device[%d] (%s) failed to load\n", i, mlx4_adev_devices[i].suffix); priv->adev[i] = NULL; continue; } mlx4_dbg(dev, "Interface for protocol %d restarted with bonded mode %s\n", protocol, enable ? "enabled" : "disabled"); } mutex_unlock(&intf_mutex); return 0; } void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, void *param) { struct mlx4_priv *priv = mlx4_priv(dev); atomic_notifier_call_chain(&priv->event_nh, type, param); } int mlx4_register_event_notifier(struct mlx4_dev *dev, struct notifier_block *nb) { struct mlx4_priv *priv = mlx4_priv(dev); return atomic_notifier_chain_register(&priv->event_nh, nb); } EXPORT_SYMBOL(mlx4_register_event_notifier); int mlx4_unregister_event_notifier(struct mlx4_dev *dev, struct notifier_block *nb) { struct mlx4_priv *priv = mlx4_priv(dev); return atomic_notifier_chain_unregister(&priv->event_nh, nb); } EXPORT_SYMBOL(mlx4_unregister_event_notifier); static int add_drivers(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); int i, ret = 0; for (i = 0; i < ARRAY_SIZE(mlx4_adev_devices); i++) { bool is_supported = false; if (priv->adev[i]) continue; if (mlx4_adev_devices[i].is_supported) is_supported = mlx4_adev_devices[i].is_supported(dev); if (!is_supported) continue; priv->adev[i] = add_adev(dev, i); if (IS_ERR(priv->adev[i])) { mlx4_warn(dev, "Device[%d] (%s) failed to load\n", i, mlx4_adev_devices[i].suffix); /* We continue to rescan drivers and leave to the caller * to make decision if to release everything or * continue. */ ret = PTR_ERR(priv->adev[i]); priv->adev[i] = NULL; } } return ret; } static void delete_drivers(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); bool delete_all; int i; delete_all = !(dev->persist->interface_state & MLX4_INTERFACE_STATE_UP); for (i = ARRAY_SIZE(mlx4_adev_devices) - 1; i >= 0; i--) { bool is_supported = false; if (!priv->adev[i]) continue; if (mlx4_adev_devices[i].is_supported && !delete_all) is_supported = mlx4_adev_devices[i].is_supported(dev); if (is_supported) continue; del_adev(&priv->adev[i]->adev); priv->adev[i] = NULL; } } /* This function is used after mlx4_dev is reconfigured. */ static int rescan_drivers_locked(struct mlx4_dev *dev) { lockdep_assert_held(&intf_mutex); delete_drivers(dev); if (!(dev->persist->interface_state & MLX4_INTERFACE_STATE_UP)) return 0; return add_drivers(dev); } int mlx4_register_device(struct mlx4_dev *dev) { int ret; mutex_lock(&intf_mutex); dev->persist->interface_state |= MLX4_INTERFACE_STATE_UP; ret = rescan_drivers_locked(dev); mutex_unlock(&intf_mutex); if (ret) { mlx4_unregister_device(dev); return ret; } mlx4_start_catas_poll(dev); return ret; } void mlx4_unregister_device(struct mlx4_dev *dev) { if (!(dev->persist->interface_state & MLX4_INTERFACE_STATE_UP)) return; mlx4_stop_catas_poll(dev); if (dev->persist->interface_state & MLX4_INTERFACE_STATE_DELETION && mlx4_is_slave(dev)) { /* In mlx4_remove_one on a VF */ u32 slave_read = swab32(readl(&mlx4_priv(dev)->mfunc.comm->slave_read)); if (mlx4_comm_internal_err(slave_read)) { mlx4_dbg(dev, "%s: comm channel is down, entering error state.\n", __func__); mlx4_enter_error_state(dev->persist); } } mutex_lock(&intf_mutex); dev->persist->interface_state &= ~MLX4_INTERFACE_STATE_UP; rescan_drivers_locked(dev); mutex_unlock(&intf_mutex); } struct devlink_port *mlx4_get_devlink_port(struct mlx4_dev *dev, int port) { struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; return &info->devlink_port; } EXPORT_SYMBOL_GPL(mlx4_get_devlink_port);