Skip to content

Commit

Permalink
net/mlx4_en: Add resilience in low memory systems
Browse files Browse the repository at this point in the history
This patch fixes the lost of Ethernet port on low memory system,
when driver frees its resources and fails to allocate new resources.
Issue could happen while changing number of channels, rings size or
changing the timestamp configuration.
This fix is necessary because of removing vmap use in the code.
When vmap was in use driver could allocate non-contiguous memory
and make it contiguous with vmap. Now it could fail to allocate
a large chunk of contiguous memory and lose the port.
Current code tries to allocate new resources and then upon success
frees the old resources.

Fixes: 73898db ('net/mlx4: Avoid wrong virtual mappings')
Signed-off-by: Eugenia Emantayev <eugenia@mellanox.com>
Signed-off-by: Tariq Toukan <tariqt@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Eugenia Emantayev authored and David S. Miller committed Jul 19, 2016
1 parent 30f56e3 commit ec25bc0
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 37 deletions.
54 changes: 32 additions & 22 deletions drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,8 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_en_port_profile new_prof;
struct mlx4_en_priv *tmp;
u32 rx_size, tx_size;
int port_up = 0;
int err = 0;
Expand All @@ -1061,31 +1063,34 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
tx_size == priv->tx_ring[0]->size)
return 0;

tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp)
return -ENOMEM;

mutex_lock(&mdev->state_lock);
memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
new_prof.tx_ring_size = tx_size;
new_prof.rx_ring_size = rx_size;
err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof);
if (err)
goto out;

if (priv->port_up) {
port_up = 1;
mlx4_en_stop_port(dev, 1);
}

mlx4_en_free_resources(priv);

priv->prof->tx_ring_size = tx_size;
priv->prof->rx_ring_size = rx_size;
mlx4_en_safe_replace_resources(priv, tmp);

err = mlx4_en_alloc_resources(priv);
if (err) {
en_err(priv, "Failed reallocating port resources\n");
goto out;
}
if (port_up) {
err = mlx4_en_start_port(dev);
if (err)
en_err(priv, "Failed starting port\n");
}

err = mlx4_en_moderation_update(priv);

out:
kfree(tmp);
mutex_unlock(&mdev->state_lock);
return err;
}
Expand Down Expand Up @@ -1714,6 +1719,8 @@ static int mlx4_en_set_channels(struct net_device *dev,
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_en_port_profile new_prof;
struct mlx4_en_priv *tmp;
int port_up = 0;
int err = 0;

Expand All @@ -1723,23 +1730,26 @@ static int mlx4_en_set_channels(struct net_device *dev,
!channel->tx_count || !channel->rx_count)
return -EINVAL;

tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp)
return -ENOMEM;

mutex_lock(&mdev->state_lock);
memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
new_prof.num_tx_rings_p_up = channel->tx_count;
new_prof.tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP;
new_prof.rx_ring_num = channel->rx_count;

err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof);
if (err)
goto out;

if (priv->port_up) {
port_up = 1;
mlx4_en_stop_port(dev, 1);
}

mlx4_en_free_resources(priv);

priv->num_tx_rings_p_up = channel->tx_count;
priv->tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP;
priv->rx_ring_num = channel->rx_count;

err = mlx4_en_alloc_resources(priv);
if (err) {
en_err(priv, "Failed reallocating port resources\n");
goto out;
}
mlx4_en_safe_replace_resources(priv, tmp);

netif_set_real_num_tx_queues(dev, priv->tx_ring_num);
netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
Expand All @@ -1757,8 +1767,8 @@ static int mlx4_en_set_channels(struct net_device *dev,
}

err = mlx4_en_moderation_update(priv);

out:
kfree(tmp);
mutex_unlock(&mdev->state_lock);
return err;
}
Expand Down
106 changes: 93 additions & 13 deletions drivers/net/ethernet/mellanox/mlx4/en_netdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1954,7 +1954,7 @@ static int mlx4_en_close(struct net_device *dev)
return 0;
}

void mlx4_en_free_resources(struct mlx4_en_priv *priv)
static void mlx4_en_free_resources(struct mlx4_en_priv *priv)
{
int i;

Expand All @@ -1979,7 +1979,7 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv)

}

int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
static int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
{
struct mlx4_en_port_profile *prof = priv->prof;
int i;
Expand Down Expand Up @@ -2044,6 +2044,77 @@ static void mlx4_en_shutdown(struct net_device *dev)
rtnl_unlock();
}

static int mlx4_en_copy_priv(struct mlx4_en_priv *dst,
struct mlx4_en_priv *src,
struct mlx4_en_port_profile *prof)
{
memcpy(&dst->hwtstamp_config, &prof->hwtstamp_config,
sizeof(dst->hwtstamp_config));
dst->num_tx_rings_p_up = src->mdev->profile.num_tx_rings_p_up;
dst->tx_ring_num = prof->tx_ring_num;
dst->rx_ring_num = prof->rx_ring_num;
dst->flags = prof->flags;
dst->mdev = src->mdev;
dst->port = src->port;
dst->dev = src->dev;
dst->prof = prof;
dst->stride = roundup_pow_of_two(sizeof(struct mlx4_en_rx_desc) +
DS_SIZE * MLX4_EN_MAX_RX_FRAGS);

dst->tx_ring = kzalloc(sizeof(struct mlx4_en_tx_ring *) * MAX_TX_RINGS,
GFP_KERNEL);
if (!dst->tx_ring)
return -ENOMEM;

dst->tx_cq = kzalloc(sizeof(struct mlx4_en_cq *) * MAX_TX_RINGS,
GFP_KERNEL);
if (!dst->tx_cq) {
kfree(dst->tx_ring);
return -ENOMEM;
}
return 0;
}

static void mlx4_en_update_priv(struct mlx4_en_priv *dst,
struct mlx4_en_priv *src)
{
memcpy(dst->rx_ring, src->rx_ring,
sizeof(struct mlx4_en_rx_ring *) * src->rx_ring_num);
memcpy(dst->rx_cq, src->rx_cq,
sizeof(struct mlx4_en_cq *) * src->rx_ring_num);
memcpy(&dst->hwtstamp_config, &src->hwtstamp_config,
sizeof(dst->hwtstamp_config));
dst->tx_ring_num = src->tx_ring_num;
dst->rx_ring_num = src->rx_ring_num;
dst->tx_ring = src->tx_ring;
dst->tx_cq = src->tx_cq;
memcpy(dst->prof, src->prof, sizeof(struct mlx4_en_port_profile));
}

int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv,
struct mlx4_en_priv *tmp,
struct mlx4_en_port_profile *prof)
{
mlx4_en_copy_priv(tmp, priv, prof);

if (mlx4_en_alloc_resources(tmp)) {
en_warn(priv,
"%s: Resource allocation failed, using previous configuration\n",
__func__);
kfree(tmp->tx_ring);
kfree(tmp->tx_cq);
return -ENOMEM;
}
return 0;
}

void mlx4_en_safe_replace_resources(struct mlx4_en_priv *priv,
struct mlx4_en_priv *tmp)
{
mlx4_en_free_resources(priv);
mlx4_en_update_priv(priv, tmp);
}

void mlx4_en_destroy_netdev(struct net_device *dev)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
Expand Down Expand Up @@ -3128,6 +3199,8 @@ int mlx4_en_reset_config(struct net_device *dev,
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_en_port_profile new_prof;
struct mlx4_en_priv *tmp;
int port_up = 0;
int err = 0;

Expand All @@ -3144,19 +3217,29 @@ int mlx4_en_reset_config(struct net_device *dev,
return -EINVAL;
}

tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp)
return -ENOMEM;

mutex_lock(&mdev->state_lock);

memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
memcpy(&new_prof.hwtstamp_config, &ts_config, sizeof(ts_config));

err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof);
if (err)
goto out;

if (priv->port_up) {
port_up = 1;
mlx4_en_stop_port(dev, 1);
}

mlx4_en_free_resources(priv);

en_warn(priv, "Changing device configuration rx filter(%x) rx vlan(%x)\n",
ts_config.rx_filter, !!(features & NETIF_F_HW_VLAN_CTAG_RX));
ts_config.rx_filter,
!!(features & NETIF_F_HW_VLAN_CTAG_RX));

priv->hwtstamp_config.tx_type = ts_config.tx_type;
priv->hwtstamp_config.rx_filter = ts_config.rx_filter;
mlx4_en_safe_replace_resources(priv, tmp);

if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX)) {
if (features & NETIF_F_HW_VLAN_CTAG_RX)
Expand Down Expand Up @@ -3190,11 +3273,6 @@ int mlx4_en_reset_config(struct net_device *dev,
dev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
}

err = mlx4_en_alloc_resources(priv);
if (err) {
en_err(priv, "Failed reallocating port resources\n");
goto out;
}
if (port_up) {
err = mlx4_en_start_port(dev);
if (err)
Expand All @@ -3203,6 +3281,8 @@ int mlx4_en_reset_config(struct net_device *dev,

out:
mutex_unlock(&mdev->state_lock);
netdev_features_change(dev);
kfree(tmp);
if (!err)
netdev_features_change(dev);
return err;
}
9 changes: 7 additions & 2 deletions drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,12 +353,14 @@ struct mlx4_en_port_profile {
u32 rx_ring_num;
u32 tx_ring_size;
u32 rx_ring_size;
u8 num_tx_rings_p_up;
u8 rx_pause;
u8 rx_ppp;
u8 tx_pause;
u8 tx_ppp;
int rss_rings;
int inline_thold;
struct hwtstamp_config hwtstamp_config;
};

struct mlx4_en_profile {
Expand Down Expand Up @@ -623,8 +625,11 @@ void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev,
u8 rx_ppp, u8 rx_pause,
u8 tx_ppp, u8 tx_pause);

void mlx4_en_free_resources(struct mlx4_en_priv *priv);
int mlx4_en_alloc_resources(struct mlx4_en_priv *priv);
int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv,
struct mlx4_en_priv *tmp,
struct mlx4_en_port_profile *prof);
void mlx4_en_safe_replace_resources(struct mlx4_en_priv *priv,
struct mlx4_en_priv *tmp);

int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq,
int entries, int ring, enum cq_type mode, int node);
Expand Down

0 comments on commit ec25bc0

Please sign in to comment.