Skip to content

Commit

Permalink
net/mlx4_en: Fix race when setting the device MAC address
Browse files Browse the repository at this point in the history
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 c07cb4b "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: Yan Burman <yanb@mellanox.com>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Yan Burman authored and David S. Miller committed Mar 7, 2013
1 parent e7dbeba commit bfa8ab4
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 22 deletions.
42 changes: 21 additions & 21 deletions drivers/net/ethernet/mellanox/mlx4/en_netdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -650,28 +650,10 @@ u64 mlx4_en_mac_to_u64(u8 *addr)
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;

mutex_lock(&mdev->state_lock);
if (priv->port_up) {
/* Remove old MAC and insert the new one */
err = mlx4_en_replace_mac(priv, priv->base_qpn,
Expand All @@ -683,7 +665,26 @@ static void mlx4_en_do_set_mac(struct work_struct *work)
} else
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);

return err;
}

static void mlx4_en_clear_list(struct net_device *dev)
Expand Down Expand Up @@ -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);
}
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;
}
mutex_unlock(&mdev->state_lock);
Expand Down Expand Up @@ -2078,7 +2079,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
priv->msg_enable = MLX4_EN_MSG_LEVEL;
spin_lock_init(&priv->stats_lock);
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->linkstate_task, mlx4_en_linkstate);
INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats);
Expand Down
1 change: 0 additions & 1 deletion drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,6 @@ struct mlx4_en_priv {
struct mlx4_en_cq rx_cq[MAX_RX_RINGS];
struct mlx4_qp drop_qp;
struct work_struct rx_mode_task;
struct work_struct mac_task;
struct work_struct watchdog_task;
struct work_struct linkstate_task;
struct delayed_work stats_task;
Expand Down

0 comments on commit bfa8ab4

Please sign in to comment.