1/* 2 * Copyright (c) 2017, 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/hash.h> 34#include "ipoib.h" 35 36#define MLX5I_MAX_LOG_PKEY_SUP 7 37 38struct qpn_to_netdev { 39 struct net_device *netdev; 40 struct hlist_node hlist; 41 u32 underlay_qpn; 42}; 43 44struct mlx5i_pkey_qpn_ht { 45 struct hlist_head buckets[1 << MLX5I_MAX_LOG_PKEY_SUP]; 46 spinlock_t ht_lock; /* Synchronise with NAPI */ 47}; 48 49int mlx5i_pkey_qpn_ht_init(struct net_device *netdev) 50{ 51 struct mlx5i_priv *ipriv = netdev_priv(netdev); 52 struct mlx5i_pkey_qpn_ht *qpn_htbl; 53 54 qpn_htbl = kzalloc(sizeof(*qpn_htbl), GFP_KERNEL); 55 if (!qpn_htbl) 56 return -ENOMEM; 57 58 ipriv->qpn_htbl = qpn_htbl; 59 spin_lock_init(&qpn_htbl->ht_lock); 60 61 return 0; 62} 63 64void mlx5i_pkey_qpn_ht_cleanup(struct net_device *netdev) 65{ 66 struct mlx5i_priv *ipriv = netdev_priv(netdev); 67 68 kfree(ipriv->qpn_htbl); 69} 70 71static struct qpn_to_netdev *mlx5i_find_qpn_to_netdev_node(struct hlist_head *buckets, 72 u32 qpn) 73{ 74 struct hlist_head *h = &buckets[hash_32(qpn, MLX5I_MAX_LOG_PKEY_SUP)]; 75 struct qpn_to_netdev *node; 76 77 hlist_for_each_entry(node, h, hlist) { 78 if (node->underlay_qpn == qpn) 79 return node; 80 } 81 82 return NULL; 83} 84 85int mlx5i_pkey_add_qpn(struct net_device *netdev, u32 qpn) 86{ 87 struct mlx5i_priv *ipriv = netdev_priv(netdev); 88 struct mlx5i_pkey_qpn_ht *ht = ipriv->qpn_htbl; 89 u8 key = hash_32(qpn, MLX5I_MAX_LOG_PKEY_SUP); 90 struct qpn_to_netdev *new_node; 91 92 new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); 93 if (!new_node) 94 return -ENOMEM; 95 96 new_node->netdev = netdev; 97 new_node->underlay_qpn = qpn; 98 spin_lock_bh(&ht->ht_lock); 99 hlist_add_head(&new_node->hlist, &ht->buckets[key]); 100 spin_unlock_bh(&ht->ht_lock); 101 102 return 0; 103} 104 105int mlx5i_pkey_del_qpn(struct net_device *netdev, u32 qpn) 106{ 107 struct mlx5e_priv *epriv = mlx5i_epriv(netdev); 108 struct mlx5i_priv *ipriv = epriv->ppriv; 109 struct mlx5i_pkey_qpn_ht *ht = ipriv->qpn_htbl; 110 struct qpn_to_netdev *node; 111 112 node = mlx5i_find_qpn_to_netdev_node(ht->buckets, qpn); 113 if (!node) { 114 mlx5_core_warn(epriv->mdev, "QPN to netdev delete from HT failed\n"); 115 return -EINVAL; 116 } 117 118 spin_lock_bh(&ht->ht_lock); 119 hlist_del_init(&node->hlist); 120 spin_unlock_bh(&ht->ht_lock); 121 kfree(node); 122 123 return 0; 124} 125 126struct net_device *mlx5i_pkey_get_netdev(struct net_device *netdev, u32 qpn) 127{ 128 struct mlx5i_priv *ipriv = netdev_priv(netdev); 129 struct qpn_to_netdev *node; 130 131 node = mlx5i_find_qpn_to_netdev_node(ipriv->qpn_htbl->buckets, qpn); 132 if (!node) 133 return NULL; 134 135 return node->netdev; 136} 137 138static int mlx5i_pkey_open(struct net_device *netdev); 139static int mlx5i_pkey_close(struct net_device *netdev); 140static int mlx5i_pkey_dev_init(struct net_device *dev); 141static void mlx5i_pkey_dev_cleanup(struct net_device *netdev); 142static int mlx5i_pkey_change_mtu(struct net_device *netdev, int new_mtu); 143static int mlx5i_pkey_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); 144 145static const struct net_device_ops mlx5i_pkey_netdev_ops = { 146 .ndo_open = mlx5i_pkey_open, 147 .ndo_stop = mlx5i_pkey_close, 148 .ndo_init = mlx5i_pkey_dev_init, 149 .ndo_get_stats64 = mlx5i_get_stats, 150 .ndo_uninit = mlx5i_pkey_dev_cleanup, 151 .ndo_change_mtu = mlx5i_pkey_change_mtu, 152 .ndo_eth_ioctl = mlx5i_pkey_ioctl, 153}; 154 155/* Child NDOs */ 156static int mlx5i_pkey_dev_init(struct net_device *dev) 157{ 158 struct mlx5e_priv *priv = mlx5i_epriv(dev); 159 struct mlx5i_priv *ipriv, *parent_ipriv; 160 struct net_device *parent_dev; 161 162 ipriv = priv->ppriv; 163 164 /* Link to parent */ 165 parent_dev = mlx5i_parent_get(dev); 166 if (!parent_dev) { 167 mlx5_core_warn(priv->mdev, "failed to get parent device\n"); 168 return -EINVAL; 169 } 170 171 if (dev->num_rx_queues < parent_dev->real_num_rx_queues) { 172 mlx5_core_warn(priv->mdev, 173 "failed to create child device with rx queues [%d] less than parent's [%d]\n", 174 dev->num_rx_queues, 175 parent_dev->real_num_rx_queues); 176 mlx5i_parent_put(dev); 177 return -EINVAL; 178 } 179 180 /* Get QPN to netdevice hash table from parent */ 181 parent_ipriv = netdev_priv(parent_dev); 182 ipriv->qpn_htbl = parent_ipriv->qpn_htbl; 183 184 return mlx5i_dev_init(dev); 185} 186 187static int mlx5i_pkey_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 188{ 189 return mlx5i_ioctl(dev, ifr, cmd); 190} 191 192static void mlx5i_pkey_dev_cleanup(struct net_device *netdev) 193{ 194 mlx5i_parent_put(netdev); 195 return mlx5i_dev_cleanup(netdev); 196} 197 198static int mlx5i_pkey_open(struct net_device *netdev) 199{ 200 struct mlx5e_priv *epriv = mlx5i_epriv(netdev); 201 struct mlx5i_priv *ipriv = epriv->ppriv; 202 struct mlx5_core_dev *mdev = epriv->mdev; 203 int err; 204 205 mutex_lock(&epriv->state_lock); 206 207 set_bit(MLX5E_STATE_OPENED, &epriv->state); 208 209 err = mlx5i_init_underlay_qp(epriv); 210 if (err) { 211 mlx5_core_warn(mdev, "prepare child underlay qp state failed, %d\n", err); 212 goto err_release_lock; 213 } 214 215 err = mlx5_fs_add_rx_underlay_qpn(mdev, ipriv->qpn); 216 if (err) { 217 mlx5_core_warn(mdev, "attach child underlay qp to ft failed, %d\n", err); 218 goto err_unint_underlay_qp; 219 } 220 221 err = mlx5i_create_tis(mdev, ipriv->qpn, &ipriv->tisn); 222 if (err) { 223 mlx5_core_warn(mdev, "create child tis failed, %d\n", err); 224 goto err_remove_rx_uderlay_qp; 225 } 226 227 err = mlx5e_open_channels(epriv, &epriv->channels); 228 if (err) { 229 mlx5_core_warn(mdev, "opening child channels failed, %d\n", err); 230 goto err_clear_state_opened_flag; 231 } 232 err = epriv->profile->update_rx(epriv); 233 if (err) 234 goto err_close_channels; 235 mlx5e_activate_priv_channels(epriv); 236 mutex_unlock(&epriv->state_lock); 237 238 return 0; 239 240err_close_channels: 241 mlx5e_close_channels(&epriv->channels); 242err_clear_state_opened_flag: 243 mlx5e_destroy_tis(mdev, ipriv->tisn); 244err_remove_rx_uderlay_qp: 245 mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qpn); 246err_unint_underlay_qp: 247 mlx5i_uninit_underlay_qp(epriv); 248err_release_lock: 249 clear_bit(MLX5E_STATE_OPENED, &epriv->state); 250 mutex_unlock(&epriv->state_lock); 251 return err; 252} 253 254static int mlx5i_pkey_close(struct net_device *netdev) 255{ 256 struct mlx5e_priv *priv = mlx5i_epriv(netdev); 257 struct mlx5i_priv *ipriv = priv->ppriv; 258 struct mlx5_core_dev *mdev = priv->mdev; 259 260 mutex_lock(&priv->state_lock); 261 262 if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) 263 goto unlock; 264 265 clear_bit(MLX5E_STATE_OPENED, &priv->state); 266 267 netif_carrier_off(priv->netdev); 268 mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qpn); 269 mlx5i_uninit_underlay_qp(priv); 270 mlx5e_deactivate_priv_channels(priv); 271 mlx5e_close_channels(&priv->channels); 272 mlx5e_destroy_tis(mdev, ipriv->tisn); 273unlock: 274 mutex_unlock(&priv->state_lock); 275 return 0; 276} 277 278static int mlx5i_pkey_change_mtu(struct net_device *netdev, int new_mtu) 279{ 280 struct mlx5e_priv *priv = mlx5i_epriv(netdev); 281 282 mutex_lock(&priv->state_lock); 283 netdev->mtu = new_mtu; 284 mutex_unlock(&priv->state_lock); 285 286 return 0; 287} 288 289/* Called directly after IPoIB netdevice was created to initialize SW structs */ 290static int mlx5i_pkey_init(struct mlx5_core_dev *mdev, 291 struct net_device *netdev) 292{ 293 struct mlx5e_priv *priv = mlx5i_epriv(netdev); 294 int err; 295 296 err = mlx5i_init(mdev, netdev); 297 if (err) 298 return err; 299 300 /* Override parent ndo */ 301 netdev->netdev_ops = &mlx5i_pkey_netdev_ops; 302 303 /* Set child limited ethtool support */ 304 netdev->ethtool_ops = &mlx5i_pkey_ethtool_ops; 305 306 /* Use dummy rqs */ 307 priv->channels.params.log_rq_mtu_frames = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE; 308 309 return 0; 310} 311 312/* Called directly before IPoIB netdevice is destroyed to cleanup SW structs */ 313static void mlx5i_pkey_cleanup(struct mlx5e_priv *priv) 314{ 315 mlx5i_cleanup(priv); 316} 317 318static int mlx5i_pkey_init_tx(struct mlx5e_priv *priv) 319{ 320 int err; 321 322 err = mlx5i_create_underlay_qp(priv); 323 if (err) 324 mlx5_core_warn(priv->mdev, "create child underlay QP failed, %d\n", err); 325 326 return err; 327} 328 329static void mlx5i_pkey_cleanup_tx(struct mlx5e_priv *priv) 330{ 331 struct mlx5i_priv *ipriv = priv->ppriv; 332 333 mlx5i_destroy_underlay_qp(priv->mdev, ipriv->qpn); 334} 335 336static int mlx5i_pkey_init_rx(struct mlx5e_priv *priv) 337{ 338 /* Since the rx resources are shared between child and parent, the 339 * parent interface is taking care of rx resource allocation and init 340 */ 341 return 0; 342} 343 344static void mlx5i_pkey_cleanup_rx(struct mlx5e_priv *priv) 345{ 346 /* Since the rx resources are shared between child and parent, the 347 * parent interface is taking care of rx resource free and de-init 348 */ 349} 350 351static const struct mlx5e_profile mlx5i_pkey_nic_profile = { 352 .init = mlx5i_pkey_init, 353 .cleanup = mlx5i_pkey_cleanup, 354 .init_tx = mlx5i_pkey_init_tx, 355 .cleanup_tx = mlx5i_pkey_cleanup_tx, 356 .init_rx = mlx5i_pkey_init_rx, 357 .cleanup_rx = mlx5i_pkey_cleanup_rx, 358 .enable = NULL, 359 .disable = NULL, 360 .update_rx = mlx5i_update_nic_rx, 361 .update_stats = NULL, 362 .rx_handlers = &mlx5i_rx_handlers, 363 .max_tc = MLX5I_MAX_NUM_TC, 364 .get_tisn = mlx5i_get_tisn, 365}; 366 367const struct mlx5e_profile *mlx5i_pkey_get_profile(void) 368{ 369 return &mlx5i_pkey_nic_profile; 370} 371