aboutsummaryrefslogtreecommitdiffstats
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
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>
-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 073b85b45fc..bedcbb30d38 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</