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

net/mlx4_en: Fix race when setting the device MAC address

Remove unnecessary use of workqueue for the device MAC address setting
flow, and fix a race when setting MAC address which was introduced by
commit c07cb4b0 "net/mlx4_en: Manage hash of MAC addresses per port"

The race happened when mlx4_en_replace_mac was being executed in parallel
with a successive call to ndo_set_mac_address, e.g witn an A/B/A MAC
setting configuration test, the third set fails.

With this change we also properly report an error if set MAC fails.
Signed-off-by: default avatarYan Burman <yanb@mellanox.com>
Signed-off-by: default avatarOr Gerlitz <ogerlitz@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e7dbeba8
...@@ -650,28 +650,10 @@ u64 mlx4_en_mac_to_u64(u8 *addr) ...@@ -650,28 +650,10 @@ u64 mlx4_en_mac_to_u64(u8 *addr)
return mac; return mac;
} }
static int mlx4_en_set_mac(struct net_device *dev, void *addr) static int mlx4_en_do_set_mac(struct mlx4_en_priv *priv)
{ {
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
struct sockaddr *saddr = addr;
if (!is_valid_ether_addr(saddr->sa_data))
return -EADDRNOTAVAIL;
memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN);
queue_work(mdev->workqueue, &priv->mac_task);
return 0;
}
static void mlx4_en_do_set_mac(struct work_struct *work)
{
struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv,
mac_task);
struct mlx4_en_dev *mdev = priv->mdev;
int err = 0; int err = 0;
mutex_lock(&mdev->state_lock);
if (priv->port_up) { if (priv->port_up) {
/* Remove old MAC and insert the new one */ /* Remove old MAC and insert the new one */
err = mlx4_en_replace_mac(priv, priv->base_qpn, err = mlx4_en_replace_mac(priv, priv->base_qpn,
...@@ -683,7 +665,26 @@ static void mlx4_en_do_set_mac(struct work_struct *work) ...@@ -683,7 +665,26 @@ static void mlx4_en_do_set_mac(struct work_struct *work)
} else } else
en_dbg(HW, priv, "Port is down while registering mac, exiting...\n"); en_dbg(HW, priv, "Port is down while registering mac, exiting...\n");
return err;
}
static int mlx4_en_set_mac(struct net_device *dev, void *addr)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
struct sockaddr *saddr = addr;
int err;
if (!is_valid_ether_addr(saddr->sa_data))
return -EADDRNOTAVAIL;
memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN);
mutex_lock(&mdev->state_lock);
err = mlx4_en_do_set_mac(priv);
mutex_unlock(&mdev->state_lock); mutex_unlock(&mdev->state_lock);
return err;
} }
static void mlx4_en_clear_list(struct net_device *dev) static void mlx4_en_clear_list(struct net_device *dev)
...@@ -1348,7 +1349,7 @@ static void mlx4_en_do_get_stats(struct work_struct *work) ...@@ -1348,7 +1349,7 @@ static void mlx4_en_do_get_stats(struct work_struct *work)
queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY); queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY);
} }
if (mdev->mac_removed[MLX4_MAX_PORTS + 1 - priv->port]) { if (mdev->mac_removed[MLX4_MAX_PORTS + 1 - priv->port]) {
queue_work(mdev->workqueue, &priv->mac_task); mlx4_en_do_set_mac(priv);
mdev->mac_removed[MLX4_MAX_PORTS + 1 - priv->port] = 0; mdev->mac_removed[MLX4_MAX_PORTS + 1 - priv->port] = 0;
} }
mutex_unlock(&mdev->state_lock); mutex_unlock(&mdev->state_lock);
...@@ -2078,7 +2079,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, ...@@ -2078,7 +2079,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
priv->msg_enable = MLX4_EN_MSG_LEVEL; priv->msg_enable = MLX4_EN_MSG_LEVEL;
spin_lock_init(&priv->stats_lock); spin_lock_init(&priv->stats_lock);
INIT_WORK(&priv->rx_mode_task, mlx4_en_do_set_rx_mode); INIT_WORK(&priv->rx_mode_task, mlx4_en_do_set_rx_mode);
INIT_WORK(&priv->mac_task, mlx4_en_do_set_mac);
INIT_WORK(&priv->watchdog_task, mlx4_en_restart); INIT_WORK(&priv->watchdog_task, mlx4_en_restart);
INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate); INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate);
INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats); INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats);
......
...@@ -509,7 +509,6 @@ struct mlx4_en_priv { ...@@ -509,7 +509,6 @@ struct mlx4_en_priv {
struct mlx4_en_cq rx_cq[MAX_RX_RINGS]; struct mlx4_en_cq rx_cq[MAX_RX_RINGS];
struct mlx4_qp drop_qp; struct mlx4_qp drop_qp;
struct work_struct rx_mode_task; struct work_struct rx_mode_task;
struct work_struct mac_task;
struct work_struct watchdog_task; struct work_struct watchdog_task;
struct work_struct linkstate_task; struct work_struct linkstate_task;
struct delayed_work stats_task; struct delayed_work stats_task;
......
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