Commit cc5387f7 authored by Yan Burman's avatar Yan Burman Committed by David S. Miller

net/mlx4_en: Add unicast MAC filtering

Implement and advertise unicast MAC filtering, such that setting macvlan
instance over mlx4_en interfaces will not require the networking core
to put mlx4_en devices in promiscuous mode.

If for some reason adding a unicast address filter fails e.g as of missing space in
the HW mac table, the device forces itself into promiscuous mode (and out of this
forced state when enough space is available).
Signed-off-by: default avatarYan Burman <yanb@mellanox.com>
Signed-off-by: default avatarAmir Vadai <amirv@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c07cb4b0
...@@ -1016,6 +1016,126 @@ static void mlx4_en_do_multicast(struct mlx4_en_priv *priv, ...@@ -1016,6 +1016,126 @@ static void mlx4_en_do_multicast(struct mlx4_en_priv *priv,
} }
} }
static void mlx4_en_do_uc_filter(struct mlx4_en_priv *priv,
struct net_device *dev,
struct mlx4_en_dev *mdev)
{
struct netdev_hw_addr *ha;
struct mlx4_mac_entry *entry;
struct hlist_node *n, *tmp;
bool found;
u64 mac;
int err = 0;
struct hlist_head *bucket;
unsigned int i;
int removed = 0;
u32 prev_flags;
/* Note that we do not need to protect our mac_hash traversal with rcu,
* since all modification code is protected by mdev->state_lock
*/
/* find what to remove */
for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) {
bucket = &priv->mac_hash[i];
hlist_for_each_entry_safe(entry, n, tmp, bucket, hlist) {
found = false;
netdev_for_each_uc_addr(ha, dev) {
if (ether_addr_equal_64bits(entry->mac,
ha->addr)) {
found = true;
break;
}
}
/* MAC address of the port is not in uc list */
if (ether_addr_equal_64bits(entry->mac, dev->dev_addr))
found = true;
if (!found) {
mac = mlx4_en_mac_to_u64(entry->mac);
mlx4_en_uc_steer_release(priv, entry->mac,
priv->base_qpn,
entry->reg_id);
mlx4_unregister_mac(mdev->dev, priv->port, mac);
hlist_del_rcu(&entry->hlist);
kfree_rcu(entry, rcu);
en_dbg(DRV, priv, "Removed MAC %pM on port:%d\n",
entry->mac, priv->port);
++removed;
}
}
}
/* if we didn't remove anything, there is no use in trying to add
* again once we are in a forced promisc mode state
*/
if ((priv->flags & MLX4_EN_FLAG_FORCE_PROMISC) && 0 == removed)
return;
prev_flags = priv->flags;
priv->flags &= ~MLX4_EN_FLAG_FORCE_PROMISC;
/* find what to add */
netdev_for_each_uc_addr(ha, dev) {
found = false;
bucket = &priv->mac_hash[ha->addr[MLX4_EN_MAC_HASH_IDX]];
hlist_for_each_entry(entry, n, bucket, hlist) {
if (ether_addr_equal_64bits(entry->mac, ha->addr)) {
found = true;
break;
}
}
if (!found) {
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
en_err(priv, "Failed adding MAC %pM on port:%d (out of memory)\n",
ha->addr, priv->port);
priv->flags |= MLX4_EN_FLAG_FORCE_PROMISC;
break;
}
mac = mlx4_en_mac_to_u64(ha->addr);
memcpy(entry->mac, ha->addr, ETH_ALEN);
err = mlx4_register_mac(mdev->dev, priv->port, mac);
if (err < 0) {
en_err(priv, "Failed registering MAC %pM on port %d: %d\n",
ha->addr, priv->port, err);
kfree(entry);
priv->flags |= MLX4_EN_FLAG_FORCE_PROMISC;
break;
}
err = mlx4_en_uc_steer_add(priv, ha->addr,
&priv->base_qpn,
&entry->reg_id);
if (err) {
en_err(priv, "Failed adding MAC %pM on port %d: %d\n",
ha->addr, priv->port, err);
mlx4_unregister_mac(mdev->dev, priv->port, mac);
kfree(entry);
priv->flags |= MLX4_EN_FLAG_FORCE_PROMISC;
break;
} else {
unsigned int mac_hash;
en_dbg(DRV, priv, "Added MAC %pM on port:%d\n",
ha->addr, priv->port);
mac_hash = ha->addr[MLX4_EN_MAC_HASH_IDX];
bucket = &priv->mac_hash[mac_hash];
hlist_add_head_rcu(&entry->hlist, bucket);
}
}
}
if (priv->flags & MLX4_EN_FLAG_FORCE_PROMISC) {
en_warn(priv, "Forcing promiscuous mode on port:%d\n",
priv->port);
} else if (prev_flags & MLX4_EN_FLAG_FORCE_PROMISC) {
en_warn(priv, "Stop forcing promiscuous mode on port:%d\n",
priv->port);
}
}
static void mlx4_en_do_set_rx_mode(struct work_struct *work) static void mlx4_en_do_set_rx_mode(struct work_struct *work)
{ {
struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv, struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv,
...@@ -1043,8 +1163,12 @@ static void mlx4_en_do_set_rx_mode(struct work_struct *work) ...@@ -1043,8 +1163,12 @@ static void mlx4_en_do_set_rx_mode(struct work_struct *work)
} }
} }
if (dev->priv_flags & IFF_UNICAST_FLT)
mlx4_en_do_uc_filter(priv, dev, mdev);
/* Promsicuous mode: disable all filters */ /* Promsicuous mode: disable all filters */
if (dev->flags & IFF_PROMISC) { if ((dev->flags & IFF_PROMISC) ||
(priv->flags & MLX4_EN_FLAG_FORCE_PROMISC)) {
mlx4_en_set_promisc_mode(priv, mdev); mlx4_en_set_promisc_mode(priv, mdev);
goto out; goto out;
} }
...@@ -1961,6 +2085,9 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, ...@@ -1961,6 +2085,9 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
MLX4_STEERING_MODE_DEVICE_MANAGED) MLX4_STEERING_MODE_DEVICE_MANAGED)
dev->hw_features |= NETIF_F_NTUPLE; dev->hw_features |= NETIF_F_NTUPLE;
if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0)
dev->priv_flags |= IFF_UNICAST_FLT;
mdev->pndev[port] = dev; mdev->pndev[port] = dev;
netif_carrier_off(dev); netif_carrier_off(dev);
......
...@@ -439,7 +439,8 @@ enum { ...@@ -439,7 +439,8 @@ enum {
*/ */
MLX4_EN_FLAG_ENABLE_HW_LOOPBACK = (1 << 2), MLX4_EN_FLAG_ENABLE_HW_LOOPBACK = (1 << 2),
/* whether we need to drop packets that hardware loopback-ed */ /* whether we need to drop packets that hardware loopback-ed */
MLX4_EN_FLAG_RX_FILTER_NEEDED = (1 << 3) MLX4_EN_FLAG_RX_FILTER_NEEDED = (1 << 3),
MLX4_EN_FLAG_FORCE_PROMISC = (1 << 4)
}; };
#define MLX4_EN_MAC_HASH_SIZE (1 << BITS_PER_BYTE) #define MLX4_EN_MAC_HASH_SIZE (1 << BITS_PER_BYTE)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment