|
@@ -420,6 +420,206 @@ static void mlx4_en_u64_to_mac(unsigned char dst_mac[ETH_ALEN + 2], u64 src_mac)
|
|
|
memset(&dst_mac[ETH_ALEN], 0, 2);
|
|
|
}
|
|
|
|
|
|
+static int mlx4_en_uc_steer_add(struct mlx4_en_priv *priv,
|
|
|
+ unsigned char *mac, int *qpn, u64 *reg_id)
|
|
|
+{
|
|
|
+ struct mlx4_en_dev *mdev = priv->mdev;
|
|
|
+ struct mlx4_dev *dev = mdev->dev;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ switch (dev->caps.steering_mode) {
|
|
|
+ case MLX4_STEERING_MODE_B0: {
|
|
|
+ struct mlx4_qp qp;
|
|
|
+ u8 gid[16] = {0};
|
|
|
+
|
|
|
+ qp.qpn = *qpn;
|
|
|
+ memcpy(&gid[10], mac, ETH_ALEN);
|
|
|
+ gid[5] = priv->port;
|
|
|
+
|
|
|
+ err = mlx4_unicast_attach(dev, &qp, gid, 0, MLX4_PROT_ETH);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case MLX4_STEERING_MODE_DEVICE_MANAGED: {
|
|
|
+ struct mlx4_spec_list spec_eth = { {NULL} };
|
|
|
+ __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16);
|
|
|
+
|
|
|
+ struct mlx4_net_trans_rule rule = {
|
|
|
+ .queue_mode = MLX4_NET_TRANS_Q_FIFO,
|
|
|
+ .exclusive = 0,
|
|
|
+ .allow_loopback = 1,
|
|
|
+ .promisc_mode = MLX4_FS_PROMISC_NONE,
|
|
|
+ .priority = MLX4_DOMAIN_NIC,
|
|
|
+ };
|
|
|
+
|
|
|
+ rule.port = priv->port;
|
|
|
+ rule.qpn = *qpn;
|
|
|
+ INIT_LIST_HEAD(&rule.list);
|
|
|
+
|
|
|
+ spec_eth.id = MLX4_NET_TRANS_RULE_ID_ETH;
|
|
|
+ memcpy(spec_eth.eth.dst_mac, mac, ETH_ALEN);
|
|
|
+ memcpy(spec_eth.eth.dst_mac_msk, &mac_mask, ETH_ALEN);
|
|
|
+ list_add_tail(&spec_eth.list, &rule.list);
|
|
|
+
|
|
|
+ err = mlx4_flow_attach(dev, &rule, reg_id);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ if (err)
|
|
|
+ en_warn(priv, "Failed Attaching Unicast\n");
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static void mlx4_en_uc_steer_release(struct mlx4_en_priv *priv,
|
|
|
+ unsigned char *mac, int qpn, u64 reg_id)
|
|
|
+{
|
|
|
+ struct mlx4_en_dev *mdev = priv->mdev;
|
|
|
+ struct mlx4_dev *dev = mdev->dev;
|
|
|
+
|
|
|
+ switch (dev->caps.steering_mode) {
|
|
|
+ case MLX4_STEERING_MODE_B0: {
|
|
|
+ struct mlx4_qp qp;
|
|
|
+ u8 gid[16] = {0};
|
|
|
+
|
|
|
+ qp.qpn = qpn;
|
|
|
+ memcpy(&gid[10], mac, ETH_ALEN);
|
|
|
+ gid[5] = priv->port;
|
|
|
+
|
|
|
+ mlx4_unicast_detach(dev, &qp, gid, MLX4_PROT_ETH);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case MLX4_STEERING_MODE_DEVICE_MANAGED: {
|
|
|
+ mlx4_flow_detach(dev, reg_id);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ en_err(priv, "Invalid steering mode.\n");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int mlx4_en_get_qp(struct mlx4_en_priv *priv)
|
|
|
+{
|
|
|
+ struct mlx4_en_dev *mdev = priv->mdev;
|
|
|
+ struct mlx4_dev *dev = mdev->dev;
|
|
|
+ struct mlx4_mac_entry *entry;
|
|
|
+ int index = 0;
|
|
|
+ int err = 0;
|
|
|
+ u64 reg_id;
|
|
|
+ int *qpn = &priv->base_qpn;
|
|
|
+ u64 mac = mlx4_en_mac_to_u64(priv->dev->dev_addr);
|
|
|
+
|
|
|
+ en_dbg(DRV, priv, "Registering MAC: %pM for adding\n",
|
|
|
+ priv->dev->dev_addr);
|
|
|
+ index = mlx4_register_mac(dev, priv->port, mac);
|
|
|
+ if (index < 0) {
|
|
|
+ err = index;
|
|
|
+ en_err(priv, "Failed adding MAC: %pM\n",
|
|
|
+ priv->dev->dev_addr);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) {
|
|
|
+ int base_qpn = mlx4_get_base_qpn(dev, priv->port);
|
|
|
+ *qpn = base_qpn + index;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = mlx4_qp_reserve_range(dev, 1, 1, qpn);
|
|
|
+ en_dbg(DRV, priv, "Reserved qp %d\n", *qpn);
|
|
|
+ if (err) {
|
|
|
+ en_err(priv, "Failed to reserve qp for mac registration\n");
|
|
|
+ goto qp_err;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = mlx4_en_uc_steer_add(priv, priv->dev->dev_addr, qpn, ®_id);
|
|
|
+ if (err)
|
|
|
+ goto steer_err;
|
|
|
+
|
|
|
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
|
|
+ if (!entry) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto alloc_err;
|
|
|
+ }
|
|
|
+ memcpy(entry->mac, priv->dev->dev_addr, sizeof(entry->mac));
|
|
|
+ entry->reg_id = reg_id;
|
|
|
+
|
|
|
+ err = radix_tree_insert(&priv->mac_tree, *qpn, entry);
|
|
|
+ if (err)
|
|
|
+ goto insert_err;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+insert_err:
|
|
|
+ kfree(entry);
|
|
|
+
|
|
|
+alloc_err:
|
|
|
+ mlx4_en_uc_steer_release(priv, priv->dev->dev_addr, *qpn, reg_id);
|
|
|
+
|
|
|
+steer_err:
|
|
|
+ mlx4_qp_release_range(dev, *qpn, 1);
|
|
|
+
|
|
|
+qp_err:
|
|
|
+ mlx4_unregister_mac(dev, priv->port, mac);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static void mlx4_en_put_qp(struct mlx4_en_priv *priv)
|
|
|
+{
|
|
|
+ struct mlx4_en_dev *mdev = priv->mdev;
|
|
|
+ struct mlx4_dev *dev = mdev->dev;
|
|
|
+ struct mlx4_mac_entry *entry;
|
|
|
+ int qpn = priv->base_qpn;
|
|
|
+ u64 mac = mlx4_en_mac_to_u64(priv->dev->dev_addr);
|
|
|
+
|
|
|
+ en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n",
|
|
|
+ priv->dev->dev_addr);
|
|
|
+ mlx4_unregister_mac(dev, priv->port, mac);
|
|
|
+
|
|
|
+ if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) {
|
|
|
+ entry = radix_tree_lookup(&priv->mac_tree, qpn);
|
|
|
+ if (entry) {
|
|
|
+ en_dbg(DRV, priv, "Releasing qp: port %d, MAC %pM, qpn %d\n",
|
|
|
+ priv->port, entry->mac, qpn);
|
|
|
+ mlx4_en_uc_steer_release(priv, entry->mac,
|
|
|
+ qpn, entry->reg_id);
|
|
|
+ mlx4_qp_release_range(dev, qpn, 1);
|
|
|
+ radix_tree_delete(&priv->mac_tree, qpn);
|
|
|
+ kfree(entry);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int mlx4_en_replace_mac(struct mlx4_en_priv *priv, int qpn,
|
|
|
+ unsigned char *new_mac)
|
|
|
+{
|
|
|
+ struct mlx4_en_dev *mdev = priv->mdev;
|
|
|
+ struct mlx4_dev *dev = mdev->dev;
|
|
|
+ struct mlx4_mac_entry *entry;
|
|
|
+ int err = 0;
|
|
|
+ u64 new_mac_u64 = mlx4_en_mac_to_u64(new_mac);
|
|
|
+
|
|
|
+ if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) {
|
|
|
+ u64 prev_mac_u64;
|
|
|
+
|
|
|
+ entry = radix_tree_lookup(&priv->mac_tree, qpn);
|
|
|
+ if (!entry)
|
|
|
+ return -EINVAL;
|
|
|
+ prev_mac_u64 = mlx4_en_mac_to_u64(entry->mac);
|
|
|
+ mlx4_en_uc_steer_release(priv, entry->mac,
|
|
|
+ qpn, entry->reg_id);
|
|
|
+ mlx4_unregister_mac(dev, priv->port, prev_mac_u64);
|
|
|
+ memcpy(entry->mac, new_mac, ETH_ALEN);
|
|
|
+ entry->reg_id = 0;
|
|
|
+ mlx4_register_mac(dev, priv->port, new_mac_u64);
|
|
|
+ err = mlx4_en_uc_steer_add(priv, new_mac,
|
|
|
+ &qpn, &entry->reg_id);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __mlx4_replace_mac(dev, priv->port, qpn, new_mac_u64);
|
|
|
+}
|
|
|
+
|
|
|
u64 mlx4_en_mac_to_u64(u8 *addr)
|
|
|
{
|
|
|
u64 mac = 0;
|
|
@@ -456,9 +656,8 @@ static void mlx4_en_do_set_mac(struct work_struct *work)
|
|
|
mutex_lock(&mdev->state_lock);
|
|
|
if (priv->port_up) {
|
|
|
/* Remove old MAC and insert the new one */
|
|
|
- u64 mac = mlx4_en_mac_to_u64(priv->dev->dev_addr);
|
|
|
- err = mlx4_replace_mac(mdev->dev, priv->port,
|
|
|
- priv->base_qpn, mac);
|
|
|
+ err = mlx4_en_replace_mac(priv, priv->base_qpn,
|
|
|
+ priv->dev->dev_addr);
|
|
|
if (err)
|
|
|
en_err(priv, "Failed changing HW MAC address\n");
|
|
|
memcpy(priv->prev_mac, priv->dev->dev_addr,
|
|
@@ -1035,7 +1234,6 @@ int mlx4_en_start_port(struct net_device *dev)
|
|
|
int i;
|
|
|
int j;
|
|
|
u8 mc_list[16] = {0};
|
|
|
- u64 mac = mlx4_en_mac_to_u64(dev->dev_addr);
|
|
|
|
|
|
if (priv->port_up) {
|
|
|
en_dbg(DRV, priv, "start port called while port already up\n");
|
|
@@ -1082,8 +1280,7 @@ int mlx4_en_start_port(struct net_device *dev)
|
|
|
|
|
|
/* Set qp number */
|
|
|
en_dbg(DRV, priv, "Getting qp number for port %d\n", priv->port);
|
|
|
- err = mlx4_get_eth_qp(mdev->dev, priv->port,
|
|
|
- mac, &priv->base_qpn);
|
|
|
+ err = mlx4_en_get_qp(priv);
|
|
|
if (err) {
|
|
|
en_err(priv, "Failed getting eth qp\n");
|
|
|
goto cq_err;
|
|
@@ -1196,7 +1393,7 @@ tx_err:
|
|
|
rss_err:
|
|
|
mlx4_en_release_rss_steer(priv);
|
|
|
mac_err:
|
|
|
- mlx4_put_eth_qp(mdev->dev, priv->port, mac, priv->base_qpn);
|
|
|
+ mlx4_en_put_qp(priv);
|
|
|
cq_err:
|
|
|
while (rx_index--)
|
|
|
mlx4_en_deactivate_cq(priv, &priv->rx_cq[rx_index]);
|
|
@@ -1215,7 +1412,6 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
|
|
|
struct ethtool_flow_id *flow, *tmp_flow;
|
|
|
int i;
|
|
|
u8 mc_list[16] = {0};
|
|
|
- u64 mac = mlx4_en_mac_to_u64(dev->dev_addr);
|
|
|
|
|
|
if (!priv->port_up) {
|
|
|
en_dbg(DRV, priv, "stop port called while port already down\n");
|
|
@@ -1296,7 +1492,7 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
|
|
|
mlx4_en_release_rss_steer(priv);
|
|
|
|
|
|
/* Unregister Mac address for the port */
|
|
|
- mlx4_put_eth_qp(mdev->dev, priv->port, mac, priv->base_qpn);
|
|
|
+ mlx4_en_put_qp(priv);
|
|
|
if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN))
|
|
|
mdev->mac_removed[priv->port] = 1;
|
|
|
|
|
@@ -1661,6 +1857,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
|
|
|
dev->dcbnl_ops = &mlx4_en_dcbnl_ops;
|
|
|
#endif
|
|
|
|
|
|
+ INIT_RADIX_TREE(&priv->mac_tree, GFP_KERNEL);
|
|
|
+
|
|
|
/* Query for default mac and max mtu */
|
|
|
priv->max_mtu = mdev->dev->caps.eth_mtu_cap[priv->port];
|
|
|
|