Skip to content

Commit 6d19993

Browse files
Yevgeny Petrilindavem330
authored andcommitted
net/mlx4_en: Re-design multicast attachments flow
Currently, for every change in the net device multicast list, the driver detaches all the addresses from the HW device, and then attaches the updated list. This behavior is wrong from two aspects: first, it causes a load of firmware commands and second, there is period of time where the correct addresses are not attached, which turned into packet loss. To improve - a copy of the multicast list is saved by the driver. For every change in the multicast list, the multicast list copy is used to find the delta between those two lists and add or remove multicast addresses as needed. Reported-by: Shawn Bohrer <[email protected]> Cc: Shawn Bohrer <[email protected]> Signed-off-by: Hadar Hen Zion <[email protected]> Signed-off-by: Yevgeny Petrilin <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent aa1ec3d commit 6d19993

File tree

2 files changed

+124
-35
lines changed

2 files changed

+124
-35
lines changed

drivers/net/ethernet/mellanox/mlx4/en_netdev.c

Lines changed: 110 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -170,33 +170,81 @@ static void mlx4_en_do_set_mac(struct work_struct *work)
170170
static void mlx4_en_clear_list(struct net_device *dev)
171171
{
172172
struct mlx4_en_priv *priv = netdev_priv(dev);
173+
struct mlx4_en_mc_list *tmp, *mc_to_del;
173174

174-
kfree(priv->mc_addrs);
175-
priv->mc_addrs = NULL;
176-
priv->mc_addrs_cnt = 0;
175+
list_for_each_entry_safe(mc_to_del, tmp, &priv->mc_list, list) {
176+
list_del(&mc_to_del->list);
177+
kfree(mc_to_del);
178+
}
177179
}
178180

179181
static void mlx4_en_cache_mclist(struct net_device *dev)
180182
{
181183
struct mlx4_en_priv *priv = netdev_priv(dev);
182184
struct netdev_hw_addr *ha;
183-
char *mc_addrs;
184-
int mc_addrs_cnt = netdev_mc_count(dev);
185-
int i;
185+
struct mlx4_en_mc_list *tmp;
186186

187-
mc_addrs = kmalloc(mc_addrs_cnt * ETH_ALEN, GFP_ATOMIC);
188-
if (!mc_addrs) {
189-
en_err(priv, "failed to allocate multicast list\n");
190-
return;
191-
}
192-
i = 0;
193-
netdev_for_each_mc_addr(ha, dev)
194-
memcpy(mc_addrs + i++ * ETH_ALEN, ha->addr, ETH_ALEN);
195187
mlx4_en_clear_list(dev);
196-
priv->mc_addrs = mc_addrs;
197-
priv->mc_addrs_cnt = mc_addrs_cnt;
188+
netdev_for_each_mc_addr(ha, dev) {
189+
tmp = kzalloc(sizeof(struct mlx4_en_mc_list), GFP_ATOMIC);
190+
if (!tmp) {
191+
en_err(priv, "failed to allocate multicast list\n");
192+
mlx4_en_clear_list(dev);
193+
return;
194+
}
195+
memcpy(tmp->addr, ha->addr, ETH_ALEN);
196+
list_add_tail(&tmp->list, &priv->mc_list);
197+
}
198198
}
199199

200+
static void update_mclist_flags(struct mlx4_en_priv *priv,
201+
struct list_head *dst,
202+
struct list_head *src)
203+
{
204+
struct mlx4_en_mc_list *dst_tmp, *src_tmp, *new_mc;
205+
bool found;
206+
207+
/* Find all the entries that should be removed from dst,
208+
* These are the entries that are not found in src
209+
*/
210+
list_for_each_entry(dst_tmp, dst, list) {
211+
found = false;
212+
list_for_each_entry(src_tmp, src, list) {
213+
if (!memcmp(dst_tmp->addr, src_tmp->addr, ETH_ALEN)) {
214+
found = true;
215+
break;
216+
}
217+
}
218+
if (!found)
219+
dst_tmp->action = MCLIST_REM;
220+
}
221+
222+
/* Add entries that exist in src but not in dst
223+
* mark them as need to add
224+
*/
225+
list_for_each_entry(src_tmp, src, list) {
226+
found = false;
227+
list_for_each_entry(dst_tmp, dst, list) {
228+
if (!memcmp(dst_tmp->addr, src_tmp->addr, ETH_ALEN)) {
229+
dst_tmp->action = MCLIST_NONE;
230+
found = true;
231+
break;
232+
}
233+
}
234+
if (!found) {
235+
new_mc = kmalloc(sizeof(struct mlx4_en_mc_list),
236+
GFP_KERNEL);
237+
if (!new_mc) {
238+
en_err(priv, "Failed to allocate current multicast list\n");
239+
return;
240+
}
241+
memcpy(new_mc, src_tmp,
242+
sizeof(struct mlx4_en_mc_list));
243+
new_mc->action = MCLIST_ADD;
244+
list_add_tail(&new_mc->list, dst);
245+
}
246+
}
247+
}
200248

201249
static void mlx4_en_set_multicast(struct net_device *dev)
202250
{
@@ -214,6 +262,7 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
214262
mcast_task);
215263
struct mlx4_en_dev *mdev = priv->mdev;
216264
struct net_device *dev = priv->dev;
265+
struct mlx4_en_mc_list *mclist, *tmp;
217266
u64 mcast_addr = 0;
218267
u8 mc_list[16] = {0};
219268
int err;
@@ -336,7 +385,6 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
336385
priv->flags |= MLX4_EN_FLAG_MC_PROMISC;
337386
}
338387
} else {
339-
int i;
340388
/* Disable Multicast promisc */
341389
if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) {
342390
err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn,
@@ -351,13 +399,6 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
351399
if (err)
352400
en_err(priv, "Failed disabling multicast filter\n");
353401

354-
/* Detach our qp from all the multicast addresses */
355-
for (i = 0; i < priv->mc_addrs_cnt; i++) {
356-
memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN);
357-
mc_list[5] = priv->port;
358-
mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp,
359-
mc_list, MLX4_PROT_ETH);
360-
}
361402
/* Flush mcast filter and init it with broadcast address */
362403
mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, ETH_BCAST,
363404
1, MLX4_MCAST_CONFIG);
@@ -367,20 +408,47 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
367408
netif_tx_lock_bh(dev);
368409
mlx4_en_cache_mclist(dev);
369410
netif_tx_unlock_bh(dev);
370-
for (i = 0; i < priv->mc_addrs_cnt; i++) {
371-
mcast_addr =
372-
mlx4_en_mac_to_u64(priv->mc_addrs + i * ETH_ALEN);
373-
memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN);
374-
mc_list[5] = priv->port;
375-
mlx4_multicast_attach(mdev->dev, &priv->rss_map.indir_qp,
376-
mc_list, 0, MLX4_PROT_ETH);
411+
list_for_each_entry(mclist, &priv->mc_list, list) {
412+
mcast_addr = mlx4_en_mac_to_u64(mclist->addr);
377413
mlx4_SET_MCAST_FLTR(mdev->dev, priv->port,
378414
mcast_addr, 0, MLX4_MCAST_CONFIG);
379415
}
380416
err = mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0,
381417
0, MLX4_MCAST_ENABLE);
382418
if (err)
383419
en_err(priv, "Failed enabling multicast filter\n");
420+
421+
update_mclist_flags(priv, &priv->curr_list, &priv->mc_list);
422+
list_for_each_entry_safe(mclist, tmp, &priv->curr_list, list) {
423+
if (mclist->action == MCLIST_REM) {
424+
/* detach this address and delete from list */
425+
memcpy(&mc_list[10], mclist->addr, ETH_ALEN);
426+
mc_list[5] = priv->port;
427+
err = mlx4_multicast_detach(mdev->dev,
428+
&priv->rss_map.indir_qp,
429+
mc_list,
430+
MLX4_PROT_ETH);
431+
if (err)
432+
en_err(priv, "Fail to detach multicast address\n");
433+
434+
/* remove from list */
435+
list_del(&mclist->list);
436+
kfree(mclist);
437+
}
438+
439+
if (mclist->action == MCLIST_ADD) {
440+
/* attach the address */
441+
memcpy(&mc_list[10], mclist->addr, ETH_ALEN);
442+
mc_list[5] = priv->port;
443+
err = mlx4_multicast_attach(mdev->dev,
444+
&priv->rss_map.indir_qp,
445+
mc_list, 0,
446+
MLX4_PROT_ETH);
447+
if (err)
448+
en_err(priv, "Fail to attach multicast address\n");
449+
450+
}
451+
}
384452
}
385453
out:
386454
mutex_unlock(&mdev->state_lock);
@@ -605,6 +673,9 @@ int mlx4_en_start_port(struct net_device *dev)
605673
return 0;
606674
}
607675

676+
INIT_LIST_HEAD(&priv->mc_list);
677+
INIT_LIST_HEAD(&priv->curr_list);
678+
608679
/* Calculate Rx buf size */
609680
dev->mtu = min(dev->mtu, priv->max_mtu);
610681
mlx4_en_calc_rx_buf(dev);
@@ -760,6 +831,7 @@ void mlx4_en_stop_port(struct net_device *dev)
760831
{
761832
struct mlx4_en_priv *priv = netdev_priv(dev);
762833
struct mlx4_en_dev *mdev = priv->mdev;
834+
struct mlx4_en_mc_list *mclist, *tmp;
763835
int i;
764836
u8 mc_list[16] = {0};
765837

@@ -781,13 +853,18 @@ void mlx4_en_stop_port(struct net_device *dev)
781853
mc_list[5] = priv->port;
782854
mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list,
783855
MLX4_PROT_ETH);
784-
for (i = 0; i < priv->mc_addrs_cnt; i++) {
785-
memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN);
856+
list_for_each_entry(mclist, &priv->curr_list, list) {
857+
memcpy(&mc_list[10], mclist->addr, ETH_ALEN);
786858
mc_list[5] = priv->port;
787859
mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp,
788860
mc_list, MLX4_PROT_ETH);
789861
}
790862
mlx4_en_clear_list(dev);
863+
list_for_each_entry_safe(mclist, tmp, &priv->curr_list, list) {
864+
list_del(&mclist->list);
865+
kfree(mclist);
866+
}
867+
791868
/* Flush multicast filter */
792869
mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, 1, MLX4_MCAST_CONFIG);
793870

drivers/net/ethernet/mellanox/mlx4/mlx4_en.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,18 @@ struct mlx4_en_perf_stats {
404404
#define NUM_PERF_COUNTERS 6
405405
};
406406

407+
enum mlx4_en_mclist_act {
408+
MCLIST_NONE,
409+
MCLIST_REM,
410+
MCLIST_ADD,
411+
};
412+
413+
struct mlx4_en_mc_list {
414+
struct list_head list;
415+
enum mlx4_en_mclist_act action;
416+
u8 addr[ETH_ALEN];
417+
};
418+
407419
struct mlx4_en_frag_info {
408420
u16 frag_size;
409421
u16 frag_prefix_size;
@@ -489,8 +501,8 @@ struct mlx4_en_priv {
489501
struct mlx4_en_pkt_stats pkstats;
490502
struct mlx4_en_port_stats port_stats;
491503
u64 stats_bitmap;
492-
char *mc_addrs;
493-
int mc_addrs_cnt;
504+
struct list_head mc_list;
505+
struct list_head curr_list;
494506
struct mlx4_en_stat_out_mbox hw_stats;
495507
int vids[128];
496508
bool wol;

0 commit comments

Comments
 (0)