/* Called directly after IPoIB netdevice was created to initialize SW structs */ void mlx5i_init(struct mlx5_core_dev *mdev, struct net_device *netdev, const struct mlx5e_profile *profile, void *ppriv) { struct mlx5e_priv *priv = mlx5i_epriv(netdev); /* priv init */ priv->mdev = mdev; priv->netdev = netdev; priv->profile = profile; priv->ppriv = ppriv; priv->hard_mtu = MLX5_IB_GRH_BYTES + MLX5_IPOIB_HARD_LEN; mutex_init(&priv->state_lock); mlx5e_build_nic_params(mdev, &priv->channels.params, profile->max_nch(mdev)); mlx5i_build_nic_params(mdev, &priv->channels.params); /* netdev init */ netdev->hw_features |= NETIF_F_SG; netdev->hw_features |= NETIF_F_IP_CSUM; netdev->hw_features |= NETIF_F_IPV6_CSUM; netdev->hw_features |= NETIF_F_GRO; netdev->hw_features |= NETIF_F_TSO; netdev->hw_features |= NETIF_F_TSO6; netdev->hw_features |= NETIF_F_RXCSUM; netdev->hw_features |= NETIF_F_RXHASH; netdev->netdev_ops = &mlx5i_netdev_ops; netdev->ethtool_ops = &mlx5i_ethtool_ops; }
static int mlx5i_change_mtu(struct net_device *netdev, int new_mtu) { struct mlx5e_priv *priv = mlx5i_epriv(netdev); struct mlx5e_channels new_channels = {}; struct mlx5e_params *params; int err = 0; mutex_lock(&priv->state_lock); params = &priv->channels.params; if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { params->sw_mtu = new_mtu; netdev->mtu = params->sw_mtu; goto out; } new_channels.params = *params; new_channels.params.sw_mtu = new_mtu; err = mlx5e_open_channels(priv, &new_channels); if (err) goto out; mlx5e_switch_priv_channels(priv, &new_channels, NULL); netdev->mtu = new_channels.params.sw_mtu; out: mutex_unlock(&priv->state_lock); return err; }
static int mlx5i_close(struct net_device *netdev) { struct mlx5e_priv *epriv = mlx5i_epriv(netdev); struct mlx5i_priv *ipriv = epriv->ppriv; struct mlx5_core_dev *mdev = epriv->mdev; /* May already be CLOSED in case a previous configuration operation * (e.g RX/TX queue size change) that involves close&open failed. */ mutex_lock(&epriv->state_lock); if (!test_bit(MLX5E_STATE_OPENED, &epriv->state)) goto unlock; clear_bit(MLX5E_STATE_OPENED, &epriv->state); netif_carrier_off(epriv->netdev); mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn); mlx5i_uninit_underlay_qp(epriv); mlx5e_deactivate_priv_channels(epriv); mlx5e_close_channels(&epriv->channels); unlock: mutex_unlock(&epriv->state_lock); return 0; }
static int mlx5i_pkey_change_mtu(struct net_device *netdev, int new_mtu) { struct mlx5e_priv *priv = mlx5i_epriv(netdev); mutex_lock(&priv->state_lock); netdev->mtu = new_mtu; mutex_unlock(&priv->state_lock); return 0; }
void mlx5i_dev_cleanup(struct net_device *dev) { struct mlx5e_priv *priv = mlx5i_epriv(dev); struct mlx5i_priv *ipriv = priv->ppriv; mlx5i_uninit_underlay_qp(priv); /* Delete QPN to net-device mapping from HT */ mlx5i_pkey_del_qpn(dev, ipriv->qp.qpn); }
static int mlx5i_xmit(struct net_device *dev, struct sk_buff *skb, struct ib_ah *address, u32 dqpn) { struct mlx5e_priv *epriv = mlx5i_epriv(dev); struct mlx5e_txqsq *sq = epriv->txq2sq[skb_get_queue_mapping(skb)]; struct mlx5_ib_ah *mah = to_mah(address); struct mlx5i_priv *ipriv = epriv->ppriv; return mlx5i_sq_xmit(sq, skb, &mah->av, dqpn, ipriv->qkey); }
int mlx5i_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mlx5e_priv *priv = mlx5i_epriv(dev); switch (cmd) { case SIOCSHWTSTAMP: return mlx5e_hwstamp_set(priv, ifr); case SIOCGHWTSTAMP: return mlx5e_hwstamp_get(priv, ifr); default: return -EOPNOTSUPP; } }
static int mlx5i_pkey_open(struct net_device *netdev) { struct mlx5e_priv *epriv = mlx5i_epriv(netdev); struct mlx5i_priv *ipriv = epriv->ppriv; struct mlx5_core_dev *mdev = epriv->mdev; int err; mutex_lock(&epriv->state_lock); set_bit(MLX5E_STATE_OPENED, &epriv->state); err = mlx5i_init_underlay_qp(epriv); if (err) { mlx5_core_warn(mdev, "prepare child underlay qp state failed, %d\n", err); goto err_release_lock; } err = mlx5_fs_add_rx_underlay_qpn(mdev, ipriv->qp.qpn); if (err) { mlx5_core_warn(mdev, "attach child underlay qp to ft failed, %d\n", err); goto err_unint_underlay_qp; } err = mlx5e_create_tis(mdev, 0 /* tc */, ipriv->qp.qpn, &epriv->tisn[0]); if (err) { mlx5_core_warn(mdev, "create child tis failed, %d\n", err); goto err_remove_rx_uderlay_qp; } err = mlx5e_open_channels(epriv, &epriv->channels); if (err) { mlx5_core_warn(mdev, "opening child channels failed, %d\n", err); goto err_clear_state_opened_flag; } mlx5e_refresh_tirs(epriv, false); mlx5e_activate_priv_channels(epriv); mutex_unlock(&epriv->state_lock); return 0; err_clear_state_opened_flag: mlx5e_destroy_tis(mdev, epriv->tisn[0]); err_remove_rx_uderlay_qp: mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn); err_unint_underlay_qp: mlx5i_uninit_underlay_qp(epriv); err_release_lock: clear_bit(MLX5E_STATE_OPENED, &epriv->state); mutex_unlock(&epriv->state_lock); return err; }
int mlx5i_dev_init(struct net_device *dev) { struct mlx5e_priv *priv = mlx5i_epriv(dev); struct mlx5i_priv *ipriv = priv->ppriv; /* Set dev address using underlay QP */ dev->dev_addr[1] = (ipriv->qp.qpn >> 16) & 0xff; dev->dev_addr[2] = (ipriv->qp.qpn >> 8) & 0xff; dev->dev_addr[3] = (ipriv->qp.qpn) & 0xff; /* Add QPN to net-device mapping to HT */ mlx5i_pkey_add_qpn(dev ,ipriv->qp.qpn); return 0; }
void mlx5_rdma_netdev_free(struct net_device *netdev) { struct mlx5e_priv *priv = mlx5i_epriv(netdev); struct mlx5i_priv *ipriv = priv->ppriv; const struct mlx5e_profile *profile = priv->profile; mlx5e_detach_netdev(priv); profile->cleanup(priv); destroy_workqueue(priv->wq); if (!ipriv->sub_interface) { mlx5i_pkey_qpn_ht_cleanup(netdev); mlx5e_destroy_mdev_resources(priv->mdev); } free_netdev(netdev); }
static int mlx5i_detach_mcast(struct net_device *netdev, struct ib_device *hca, union ib_gid *gid, u16 lid) { struct mlx5e_priv *epriv = mlx5i_epriv(netdev); struct mlx5_core_dev *mdev = epriv->mdev; struct mlx5i_priv *ipriv = epriv->ppriv; int err; mlx5_core_dbg(mdev, "detaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw); err = mlx5_core_detach_mcg(mdev, gid, ipriv->qp.qpn); if (err) mlx5_core_dbg(mdev, "failed detaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw); return err; }
/* Called directly after IPoIB netdevice was created to initialize SW structs */ static void mlx5i_pkey_init(struct mlx5_core_dev *mdev, struct net_device *netdev, const struct mlx5e_profile *profile, void *ppriv) { struct mlx5e_priv *priv = mlx5i_epriv(netdev); mlx5i_init(mdev, netdev, profile, ppriv); /* Override parent ndo */ netdev->netdev_ops = &mlx5i_pkey_netdev_ops; /* Set child limited ethtool support */ netdev->ethtool_ops = &mlx5i_pkey_ethtool_ops; /* Use dummy rqs */ priv->channels.params.log_rq_size = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE; }
static int mlx5i_open(struct net_device *netdev) { struct mlx5e_priv *epriv = mlx5i_epriv(netdev); struct mlx5i_priv *ipriv = epriv->ppriv; struct mlx5_core_dev *mdev = epriv->mdev; int err; mutex_lock(&epriv->state_lock); set_bit(MLX5E_STATE_OPENED, &epriv->state); err = mlx5i_init_underlay_qp(epriv); if (err) { mlx5_core_warn(mdev, "prepare underlay qp state failed, %d\n", err); goto err_clear_state_opened_flag; } err = mlx5_fs_add_rx_underlay_qpn(mdev, ipriv->qp.qpn); if (err) { mlx5_core_warn(mdev, "attach underlay qp to ft failed, %d\n", err); goto err_reset_qp; } err = mlx5e_open_channels(epriv, &epriv->channels); if (err) goto err_remove_fs_underlay_qp; mlx5e_refresh_tirs(epriv, false); mlx5e_activate_priv_channels(epriv); mlx5e_timestamp_set(epriv); mutex_unlock(&epriv->state_lock); return 0; err_remove_fs_underlay_qp: mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn); err_reset_qp: mlx5i_uninit_underlay_qp(epriv); err_clear_state_opened_flag: clear_bit(MLX5E_STATE_OPENED, &epriv->state); mutex_unlock(&epriv->state_lock); return err; }
int mlx5i_pkey_del_qpn(struct net_device *netdev, u32 qpn) { struct mlx5e_priv *epriv = mlx5i_epriv(netdev); struct mlx5i_priv *ipriv = epriv->ppriv; struct mlx5i_pkey_qpn_ht *ht = ipriv->qpn_htbl; struct qpn_to_netdev *node; node = mlx5i_find_qpn_to_netdev_node(ht->buckets, qpn); if (!node) { mlx5_core_warn(epriv->mdev, "QPN to netdev delete from HT failed\n"); return -EINVAL; } spin_lock_bh(&ht->ht_lock); hlist_del_init(&node->hlist); spin_unlock_bh(&ht->ht_lock); kfree(node); return 0; }
/* IPoIB RDMA netdev callbacks */ static int mlx5i_attach_mcast(struct net_device *netdev, struct ib_device *hca, union ib_gid *gid, u16 lid, int set_qkey, u32 qkey) { struct mlx5e_priv *epriv = mlx5i_epriv(netdev); struct mlx5_core_dev *mdev = epriv->mdev; struct mlx5i_priv *ipriv = epriv->ppriv; int err; mlx5_core_dbg(mdev, "attaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw); err = mlx5_core_attach_mcg(mdev, gid, ipriv->qp.qpn); if (err) mlx5_core_warn(mdev, "failed attaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw); if (set_qkey) { mlx5_core_dbg(mdev, "%s setting qkey 0x%x\n", netdev->name, qkey); ipriv->qkey = qkey; } return err; }
static int mlx5i_pkey_close(struct net_device *netdev) { struct mlx5e_priv *priv = mlx5i_epriv(netdev); struct mlx5i_priv *ipriv = priv->ppriv; struct mlx5_core_dev *mdev = priv->mdev; mutex_lock(&priv->state_lock); if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) goto unlock; clear_bit(MLX5E_STATE_OPENED, &priv->state); netif_carrier_off(priv->netdev); mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn); mlx5i_uninit_underlay_qp(priv); mlx5e_deactivate_priv_channels(priv); mlx5e_close_channels(&priv->channels); mlx5e_destroy_tis(mdev, priv->tisn[0]); unlock: mutex_unlock(&priv->state_lock); return 0; }
/* Child NDOs */ static int mlx5i_pkey_dev_init(struct net_device *dev) { struct mlx5e_priv *priv = mlx5i_epriv(dev); struct mlx5i_priv *ipriv, *parent_ipriv; struct net_device *parent_dev; int parent_ifindex; ipriv = priv->ppriv; /* Get QPN to netdevice hash table from parent */ parent_ifindex = dev->netdev_ops->ndo_get_iflink(dev); parent_dev = dev_get_by_index(dev_net(dev), parent_ifindex); if (!parent_dev) { mlx5_core_warn(priv->mdev, "failed to get parent device\n"); return -EINVAL; } parent_ipriv = netdev_priv(parent_dev); ipriv->qpn_htbl = parent_ipriv->qpn_htbl; dev_put(parent_dev); return mlx5i_dev_init(dev); }
/* Called directly after IPoIB netdevice was created to initialize SW structs */ void mlx5i_init(struct mlx5_core_dev *mdev, struct net_device *netdev, const struct mlx5e_profile *profile, void *ppriv) { struct mlx5e_priv *priv = mlx5i_epriv(netdev); u16 max_mtu; /* priv init */ priv->mdev = mdev; priv->netdev = netdev; priv->profile = profile; priv->ppriv = ppriv; mutex_init(&priv->state_lock); mlx5_query_port_max_mtu(mdev, &max_mtu, 1); netdev->mtu = max_mtu; mlx5e_build_nic_params(mdev, &priv->channels.params, profile->max_nch(mdev), netdev->mtu); mlx5i_build_nic_params(mdev, &priv->channels.params); mlx5e_timestamp_init(priv); /* netdev init */ netdev->hw_features |= NETIF_F_SG; netdev->hw_features |= NETIF_F_IP_CSUM; netdev->hw_features |= NETIF_F_IPV6_CSUM; netdev->hw_features |= NETIF_F_GRO; netdev->hw_features |= NETIF_F_TSO; netdev->hw_features |= NETIF_F_TSO6; netdev->hw_features |= NETIF_F_RXCSUM; netdev->hw_features |= NETIF_F_RXHASH; netdev->netdev_ops = &mlx5i_netdev_ops; netdev->ethtool_ops = &mlx5i_ethtool_ops; }
struct net_device *mlx5_rdma_netdev_alloc(struct mlx5_core_dev *mdev, struct ib_device *ibdev, const char *name, void (*setup)(struct net_device *)) { const struct mlx5e_profile *profile; struct net_device *netdev; struct mlx5i_priv *ipriv; struct mlx5e_priv *epriv; struct rdma_netdev *rn; bool sub_interface; int nch; int err; if (mlx5i_check_required_hca_cap(mdev)) { mlx5_core_warn(mdev, "Accelerated mode is not supported\n"); return ERR_PTR(-EOPNOTSUPP); } /* TODO: Need to find a better way to check if child device*/ sub_interface = (mdev->mlx5e_res.pdn != 0); if (sub_interface) profile = mlx5i_pkey_get_profile(); else profile = &mlx5i_nic_profile; nch = profile->max_nch(mdev); netdev = alloc_netdev_mqs(sizeof(struct mlx5i_priv) + sizeof(struct mlx5e_priv), name, NET_NAME_UNKNOWN, setup, nch * MLX5E_MAX_NUM_TC, nch); if (!netdev) { mlx5_core_warn(mdev, "alloc_netdev_mqs failed\n"); return NULL; } ipriv = netdev_priv(netdev); epriv = mlx5i_epriv(netdev); epriv->wq = create_singlethread_workqueue("mlx5i"); if (!epriv->wq) goto err_free_netdev; ipriv->sub_interface = sub_interface; if (!ipriv->sub_interface) { err = mlx5i_pkey_qpn_ht_init(netdev); if (err) { mlx5_core_warn(mdev, "allocate qpn_to_netdev ht failed\n"); goto destroy_wq; } /* This should only be called once per mdev */ err = mlx5e_create_mdev_resources(mdev); if (err) goto destroy_ht; } profile->init(mdev, netdev, profile, ipriv); mlx5e_attach_netdev(epriv); netif_carrier_off(netdev); /* set rdma_netdev func pointers */ rn = &ipriv->rn; rn->hca = ibdev; rn->send = mlx5i_xmit; rn->attach_mcast = mlx5i_attach_mcast; rn->detach_mcast = mlx5i_detach_mcast; rn->set_id = mlx5i_set_pkey_index; return netdev; destroy_ht: mlx5i_pkey_qpn_ht_cleanup(netdev); destroy_wq: destroy_workqueue(epriv->wq); err_free_netdev: free_netdev(netdev); return NULL; }