aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/mellanox/mlx4
diff options
context:
space:
mode:
authorYevgeny Petrilin <yevgenyp@mellanox.co.il>2012-07-05 00:03:43 -0400
committerDavid S. Miller <davem@davemloft.net>2012-07-07 19:23:05 -0400
commit6d19993788e080edb557178cc6aba2d963edce4e (patch)
treec9280404ba3c132e95fecaf0094e3d07d86f6888 /drivers/net/ethernet/mellanox/mlx4
parentaa1ec3dde1d818dcc94e307e25df98242aff5538 (diff)
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 <sbohrer@rgmadvisors.com> Cc: Shawn Bohrer <sbohrer@rgmadvisors.com> Signed-off-by: Hadar Hen Zion <hadarh@mellanox.co.il> Signed-off-by: Yevgeny Petrilin <yevgenyp@mellanox.co.il> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlx4')
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c143
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h16
2 files changed, 124 insertions, 35 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 073b85b45fc5..bedcbb30d38f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -170,33 +170,81 @@ static void mlx4_en_do_set_mac(struct work_struct *work)
170static void mlx4_en_clear_list(struct net_device *dev) 170static void mlx4_en_clear_list(struct net_device *dev)
171{ 171{
172 struct mlx4_en_priv *priv = netdev_priv(dev); 172 struct mlx4_en_priv *priv = netdev_priv(dev);
173 struct mlx4_en_mc_list *tmp, *mc_to_del;
173 174
174 kfree(priv->mc_addrs); 175 list_for_each_entry_safe(mc_to_del, tmp, &priv->mc_list, list) {
175 priv->mc_addrs = NULL; 176 list_del(&mc_to_del->list);
176 priv->mc_addrs_cnt = 0; 177 kfree(mc_to_del);
178 }
177} 179}
178 180
179static void mlx4_en_cache_mclist(struct net_device *dev) 181static void mlx4_en_cache_mclist(struct net_device *dev)
180{ 182{
181 struct mlx4_en_priv *priv = netdev_priv(dev); 183 struct mlx4_en_priv *priv = netdev_priv(dev);
182 struct netdev_hw_addr *ha; 184 struct netdev_hw_addr *ha;
183 char *mc_addrs; 185 struct mlx4_en_mc_list *tmp;
184 int mc_addrs_cnt = netdev_mc_count(dev);
185 int i;
186 186
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);
195 mlx4_en_clear_list(dev); 187 mlx4_en_clear_list(dev);
196 priv->mc_addrs = mc_addrs; 188 netdev_for_each_mc_addr(ha, dev) {
197 priv->mc_addrs_cnt = mc_addrs_cnt; 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 }
198} 198}
199 199
200static 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}
200 248
201static void mlx4_en_set_multicast(struct net_device *dev) 249static void mlx4_en_set_multicast(struct net_device *dev)
202{ 250{
@@ -214,6 +262,7 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
214 mcast_task); 262 mcast_task);
215 struct mlx4_en_dev *mdev = priv->mdev; 263 struct mlx4_en_dev *mdev = priv->mdev;
216 struct net_device *dev = priv->dev; 264 struct net_device *dev = priv->dev;
265 struct mlx4_en_mc_list *mclist, *tmp;
217 u64 mcast_addr = 0; 266 u64 mcast_addr = 0;
218 u8 mc_list[16] = {0}; 267 u8 mc_list[16] = {0};
219 int err; 268 int err;
@@ -336,7 +385,6 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
336 priv->flags |= MLX4_EN_FLAG_MC_PROMISC; 385 priv->flags |= MLX4_EN_FLAG_MC_PROMISC;
337 } 386 }
338 } else { 387 } else {
339 int i;
340 /* Disable Multicast promisc */ 388 /* Disable Multicast promisc */
341 if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { 389 if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) {
342 err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, 390 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)
351 if (err) 399 if (err)
352 en_err(priv, "Failed disabling multicast filter\n"); 400 en_err(priv, "Failed disabling multicast filter\n");
353 401
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 }
361 /* Flush mcast filter and init it with broadcast address */ 402 /* Flush mcast filter and init it with broadcast address */
362 mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, ETH_BCAST, 403 mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, ETH_BCAST,
363 1, MLX4_MCAST_CONFIG); 404 1, MLX4_MCAST_CONFIG);
@@ -367,13 +408,8 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
367 netif_tx_lock_bh(dev); 408 netif_tx_lock_bh(dev);
368 mlx4_en_cache_mclist(dev); 409 mlx4_en_cache_mclist(dev);
369 netif_tx_unlock_bh(dev); 410 netif_tx_unlock_bh(dev);
370 for (i = 0; i < priv->mc_addrs_cnt; i++) { 411 list_for_each_entry(mclist, &priv->mc_list, list) {
371 mcast_addr = 412 mcast_addr = mlx4_en_mac_to_u64(mclist->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);
377 mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 413 mlx4_SET_MCAST_FLTR(mdev->dev, priv->port,
378 mcast_addr, 0, MLX4_MCAST_CONFIG); 414 mcast_addr, 0, MLX4_MCAST_CONFIG);
379 } 415 }
@@ -381,6 +417,38 @@ static void mlx4_en_do_set_multicast(struct work_struct *work)
381 0, MLX4_MCAST_ENABLE); 417 0, MLX4_MCAST_ENABLE);
382 if (err) 418 if (err)
383 en_err(priv, "Failed enabling multicast filter\n"); 419 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 }
384 } 452 }
385out: 453out:
386 mutex_unlock(&mdev->state_lock); 454 mutex_unlock(&mdev->state_lock);
@@ -605,6 +673,9 @@ int mlx4_en_start_port(struct net_device *dev)
605 return 0; 673 return 0;
606 } 674 }
607 675
676 INIT_LIST_HEAD(&priv->mc_list);
677 INIT_LIST_HEAD(&priv->curr_list);
678
608 /* Calculate Rx buf size */ 679 /* Calculate Rx buf size */
609 dev->mtu = min(dev->mtu, priv->max_mtu); 680 dev->mtu = min(dev->mtu, priv->max_mtu);
610 mlx4_en_calc_rx_buf(dev); 681 mlx4_en_calc_rx_buf(dev);
@@ -760,6 +831,7 @@ void mlx4_en_stop_port(struct net_device *dev)
760{ 831{
761 struct mlx4_en_priv *priv = netdev_priv(dev); 832 struct mlx4_en_priv *priv = netdev_priv(dev);
762 struct mlx4_en_dev *mdev = priv->mdev; 833 struct mlx4_en_dev *mdev = priv->mdev;
834 struct mlx4_en_mc_list *mclist, *tmp;
763 int i; 835 int i;
764 u8 mc_list[16] = {0}; 836 u8 mc_list[16] = {0};
765 837
@@ -781,13 +853,18 @@ void mlx4_en_stop_port(struct net_device *dev)
781 mc_list[5] = priv->port; 853 mc_list[5] = priv->port;
782 mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, 854 mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list,
783 MLX4_PROT_ETH); 855 MLX4_PROT_ETH);
784 for (i = 0; i < priv->mc_addrs_cnt; i++) { 856 list_for_each_entry(mclist, &priv->curr_list, list) {
785 memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN); 857 memcpy(&mc_list[10], mclist->addr, ETH_ALEN);
786 mc_list[5] = priv->port; 858 mc_list[5] = priv->port;
787 mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, 859 mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp,
788 mc_list, MLX4_PROT_ETH); 860 mc_list, MLX4_PROT_ETH);
789 } 861 }
790 mlx4_en_clear_list(dev); 862 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
791 /* Flush multicast filter */ 868 /* Flush multicast filter */
792 mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, 1, MLX4_MCAST_CONFIG); 869 mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, 1, MLX4_MCAST_CONFIG);
793 870
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 225c20d47900..1bb00cd22d42 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -404,6 +404,18 @@ struct mlx4_en_perf_stats {
404#define NUM_PERF_COUNTERS 6 404#define NUM_PERF_COUNTERS 6
405}; 405};
406 406
407enum mlx4_en_mclist_act {
408 MCLIST_NONE,
409 MCLIST_REM,
410 MCLIST_ADD,
411};
412
413struct mlx4_en_mc_list {
414 struct list_head list;
415 enum mlx4_en_mclist_act action;
416 u8 addr[ETH_ALEN];
417};
418
407struct mlx4_en_frag_info { 419struct mlx4_en_frag_info {
408 u16 frag_size; 420 u16 frag_size;
409 u16 frag_prefix_size; 421 u16 frag_prefix_size;
@@ -489,8 +501,8 @@ struct mlx4_en_priv {
489 struct mlx4_en_pkt_stats pkstats; 501 struct mlx4_en_pkt_stats pkstats;
490 struct mlx4_en_port_stats port_stats; 502 struct mlx4_en_port_stats port_stats;
491 u64 stats_bitmap; 503 u64 stats_bitmap;
492 char *mc_addrs; 504 struct list_head mc_list;
493 int mc_addrs_cnt; 505 struct list_head curr_list;
494 struct mlx4_en_stat_out_mbox hw_stats; 506 struct mlx4_en_stat_out_mbox hw_stats;
495 int vids[128]; 507 int vids[128];
496 bool wol; 508 bool wol;