aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElad Raz <eladr@mellanox.com>2016-04-21 06:52:45 -0400
committerDavid S. Miller <davem@davemloft.net>2016-04-24 14:23:32 -0400
commit45ebcce56823d14d196dbdecd26783b3d5f464a6 (patch)
tree3fab543500b3d8ee7ccb7def33b957e2c97886f7
parent6dd684c0feb207f30180570bad24264b922d9476 (diff)
bridge: mdb: Marking port-group as offloaded
There is a race-condition when updating the mdb offload flag without using the mulicast_lock. This reverts commit 9e8430f8d60d98 ("bridge: mdb: Passing the port-group pointer to br_mdb module"). This patch marks offloaded MDB entry as "offload" by changing the port- group flags and marks it as MDB_PG_FLAGS_OFFLOAD. When switchdev PORT_MDB succeeded and adds a multicast group, a completion callback is been invoked "br_mdb_complete". The completion function locks the multicast_lock and finds the right net_bridge_port_group and marks it as offloaded. Fixes: 9e8430f8d60d98 ("bridge: mdb: Passing the port-group pointer to br_mdb module") Reported-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: Elad Raz <eladr@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/bridge/br_mdb.c91
-rw-r--r--net/bridge/br_multicast.c8
-rw-r--r--net/bridge/br_private.h4
3 files changed, 71 insertions, 32 deletions
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index b258120e1145..7dbc80d01eb0 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -256,9 +256,45 @@ static inline size_t rtnl_mdb_nlmsg_size(void)
256 + nla_total_size(sizeof(struct br_mdb_entry)); 256 + nla_total_size(sizeof(struct br_mdb_entry));
257} 257}
258 258
259static void __br_mdb_notify(struct net_device *dev, struct br_mdb_entry *entry, 259struct br_mdb_complete_info {
260 int type, struct net_bridge_port_group *pg) 260 struct net_bridge_port *port;
261 struct br_ip ip;
262};
263
264static void br_mdb_complete(struct net_device *dev, int err, void *priv)
261{ 265{
266 struct br_mdb_complete_info *data = priv;
267 struct net_bridge_port_group __rcu **pp;
268 struct net_bridge_port_group *p;
269 struct net_bridge_mdb_htable *mdb;
270 struct net_bridge_mdb_entry *mp;
271 struct net_bridge_port *port = data->port;
272 struct net_bridge *br = port->br;
273
274 if (err)
275 goto err;
276
277 spin_lock_bh(&br->multicast_lock);
278 mdb = mlock_dereference(br->mdb, br);
279 mp = br_mdb_ip_get(mdb, &data->ip);
280 if (!mp)
281 goto out;
282 for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
283 pp = &p->next) {
284 if (p->port != port)
285 continue;
286 p->flags |= MDB_PG_FLAGS_OFFLOAD;
287 }
288out:
289 spin_unlock_bh(&br->multicast_lock);
290err:
291 kfree(priv);
292}
293
294static void __br_mdb_notify(struct net_device *dev, struct net_bridge_port *p,
295 struct br_mdb_entry *entry, int type)
296{
297 struct br_mdb_complete_info *complete_info;
262 struct switchdev_obj_port_mdb mdb = { 298 struct switchdev_obj_port_mdb mdb = {
263 .obj = { 299 .obj = {
264 .id = SWITCHDEV_OBJ_ID_PORT_MDB, 300 .id = SWITCHDEV_OBJ_ID_PORT_MDB,
@@ -281,9 +317,14 @@ static void __br_mdb_notify(struct net_device *dev, struct br_mdb_entry *entry,
281 317
282 mdb.obj.orig_dev = port_dev; 318 mdb.obj.orig_dev = port_dev;
283 if (port_dev && type == RTM_NEWMDB) { 319 if (port_dev && type == RTM_NEWMDB) {
284 err = switchdev_port_obj_add(port_dev, &mdb.obj); 320 complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC);
285 if (!err && pg) 321 if (complete_info) {
286 pg->flags |= MDB_PG_FLAGS_OFFLOAD; 322 complete_info->port = p;
323 __mdb_entry_to_br_ip(entry, &complete_info->ip);
324 mdb.obj.complete_priv = complete_info;
325 mdb.obj.complete = br_mdb_complete;
326 switchdev_port_obj_add(port_dev, &mdb.obj);
327 }
287 } else if (port_dev && type == RTM_DELMDB) { 328 } else if (port_dev && type == RTM_DELMDB) {
288 switchdev_port_obj_del(port_dev, &mdb.obj); 329 switchdev_port_obj_del(port_dev, &mdb.obj);
289 } 330 }
@@ -304,21 +345,21 @@ errout:
304 rtnl_set_sk_err(net, RTNLGRP_MDB, err); 345 rtnl_set_sk_err(net, RTNLGRP_MDB, err);
305} 346}
306 347
307void br_mdb_notify(struct net_device *dev, struct net_bridge_port_group *pg, 348void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
308 int type) 349 struct br_ip *group, int type, u8 flags)
309{ 350{
310 struct br_mdb_entry entry; 351 struct br_mdb_entry entry;
311 352
312 memset(&entry, 0, sizeof(entry)); 353 memset(&entry, 0, sizeof(entry));
313 entry.ifindex = pg->port->dev->ifindex; 354 entry.ifindex = port->dev->ifindex;
314 entry.addr.proto = pg->addr.proto; 355 entry.addr.proto = group->proto;
315 entry.addr.u.ip4 = pg->addr.u.ip4; 356 entry.addr.u.ip4 = group->u.ip4;
316#if IS_ENABLED(CONFIG_IPV6) 357#if IS_ENABLED(CONFIG_IPV6)
317 entry.addr.u.ip6 = pg->addr.u.ip6; 358 entry.addr.u.ip6 = group->u.ip6;
318#endif 359#endif
319 entry.vid = pg->addr.vid; 360 entry.vid = group->vid;
320 __mdb_entry_fill_flags(&entry, pg->flags); 361 __mdb_entry_fill_flags(&entry, flags);
321 __br_mdb_notify(dev, &entry, type, pg); 362 __br_mdb_notify(dev, port, &entry, type);
322} 363}
323 364
324static int nlmsg_populate_rtr_fill(struct sk_buff *skb, 365static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
@@ -463,8 +504,7 @@ static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh,
463} 504}
464 505
465static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, 506static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
466 struct br_ip *group, unsigned char state, 507 struct br_ip *group, unsigned char state)
467 struct net_bridge_port_group **pg)
468{ 508{
469 struct net_bridge_mdb_entry *mp; 509 struct net_bridge_mdb_entry *mp;
470 struct net_bridge_port_group *p; 510 struct net_bridge_port_group *p;
@@ -495,7 +535,6 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
495 if (unlikely(!p)) 535 if (unlikely(!p))
496 return -ENOMEM; 536 return -ENOMEM;
497 rcu_assign_pointer(*pp, p); 537 rcu_assign_pointer(*pp, p);
498 *pg = p;
499 if (state == MDB_TEMPORARY) 538 if (state == MDB_TEMPORARY)
500 mod_timer(&p->timer, now + br->multicast_membership_interval); 539 mod_timer(&p->timer, now + br->multicast_membership_interval);
501 540
@@ -503,8 +542,7 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
503} 542}
504 543
505static int __br_mdb_add(struct net *net, struct net_bridge *br, 544static int __br_mdb_add(struct net *net, struct net_bridge *br,
506 struct br_mdb_entry *entry, 545 struct br_mdb_entry *entry)
507 struct net_bridge_port_group **pg)
508{ 546{
509 struct br_ip ip; 547 struct br_ip ip;
510 struct net_device *dev; 548 struct net_device *dev;
@@ -525,7 +563,7 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br,
525 __mdb_entry_to_br_ip(entry, &ip); 563 __mdb_entry_to_br_ip(entry, &ip);
526 564
527 spin_lock_bh(&br->multicast_lock); 565 spin_lock_bh(&br->multicast_lock);
528 ret = br_mdb_add_group(br, p, &ip, entry->state, pg); 566 ret = br_mdb_add_group(br, p, &ip, entry->state);
529 spin_unlock_bh(&br->multicast_lock); 567 spin_unlock_bh(&br->multicast_lock);
530 return ret; 568 return ret;
531} 569}
@@ -533,7 +571,6 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br,
533static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) 571static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
534{ 572{
535 struct net *net = sock_net(skb->sk); 573 struct net *net = sock_net(skb->sk);
536 struct net_bridge_port_group *pg;
537 struct net_bridge_vlan_group *vg; 574 struct net_bridge_vlan_group *vg;
538 struct net_device *dev, *pdev; 575 struct net_device *dev, *pdev;
539 struct br_mdb_entry *entry; 576 struct br_mdb_entry *entry;
@@ -563,15 +600,15 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
563 if (br_vlan_enabled(br) && vg && entry->vid == 0) { 600 if (br_vlan_enabled(br) && vg && entry->vid == 0) {
564 list_for_each_entry(v, &vg->vlan_list, vlist) { 601 list_for_each_entry(v, &vg->vlan_list, vlist) {
565 entry->vid = v->vid; 602 entry->vid = v->vid;
566 err = __br_mdb_add(net, br, entry, &pg); 603 err = __br_mdb_add(net, br, entry);
567 if (err) 604 if (err)
568 break; 605 break;
569 __br_mdb_notify(dev, entry, RTM_NEWMDB, pg); 606 __br_mdb_notify(dev, p, entry, RTM_NEWMDB);
570 } 607 }
571 } else { 608 } else {
572 err = __br_mdb_add(net, br, entry, &pg); 609 err = __br_mdb_add(net, br, entry);
573 if (!err) 610 if (!err)
574 __br_mdb_notify(dev, entry, RTM_NEWMDB, pg); 611 __br_mdb_notify(dev, p, entry, RTM_NEWMDB);
575 } 612 }
576 613
577 return err; 614 return err;
@@ -659,12 +696,12 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
659 entry->vid = v->vid; 696 entry->vid = v->vid;
660 err = __br_mdb_del(br, entry); 697 err = __br_mdb_del(br, entry);
661 if (!err) 698 if (!err)
662 __br_mdb_notify(dev, entry, RTM_DELMDB, NULL); 699 __br_mdb_notify(dev, p, entry, RTM_DELMDB);
663 } 700 }
664 } else { 701 } else {
665 err = __br_mdb_del(br, entry); 702 err = __br_mdb_del(br, entry);
666 if (!err) 703 if (!err)
667 __br_mdb_notify(dev, entry, RTM_DELMDB, NULL); 704 __br_mdb_notify(dev, p, entry, RTM_DELMDB);
668 } 705 }
669 706
670 return err; 707 return err;
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index a4c15df2b792..191ea66e4d92 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -283,7 +283,8 @@ static void br_multicast_del_pg(struct net_bridge *br,
283 rcu_assign_pointer(*pp, p->next); 283 rcu_assign_pointer(*pp, p->next);
284 hlist_del_init(&p->mglist); 284 hlist_del_init(&p->mglist);
285 del_timer(&p->timer); 285 del_timer(&p->timer);
286 br_mdb_notify(br->dev, p, RTM_DELMDB); 286 br_mdb_notify(br->dev, p->port, &pg->addr, RTM_DELMDB,
287 p->flags);
287 call_rcu_bh(&p->rcu, br_multicast_free_pg); 288 call_rcu_bh(&p->rcu, br_multicast_free_pg);
288 289
289 if (!mp->ports && !mp->mglist && 290 if (!mp->ports && !mp->mglist &&
@@ -705,7 +706,7 @@ static int br_multicast_add_group(struct net_bridge *br,
705 if (unlikely(!p)) 706 if (unlikely(!p))
706 goto err; 707 goto err;
707 rcu_assign_pointer(*pp, p); 708 rcu_assign_pointer(*pp, p);
708 br_mdb_notify(br->dev, p, RTM_NEWMDB); 709 br_mdb_notify(br->dev, port, group, RTM_NEWMDB, 0);
709 710
710found: 711found:
711 mod_timer(&p->timer, now + br->multicast_membership_interval); 712 mod_timer(&p->timer, now + br->multicast_membership_interval);
@@ -1461,7 +1462,8 @@ br_multicast_leave_group(struct net_bridge *br,
1461 hlist_del_init(&p->mglist); 1462 hlist_del_init(&p->mglist);
1462 del_timer(&p->timer); 1463 del_timer(&p->timer);
1463 call_rcu_bh(&p->rcu, br_multicast_free_pg); 1464 call_rcu_bh(&p->rcu, br_multicast_free_pg);
1464 br_mdb_notify(br->dev, p, RTM_DELMDB); 1465 br_mdb_notify(br->dev, port, group, RTM_DELMDB,
1466 p->flags);
1465 1467
1466 if (!mp->ports && !mp->mglist && 1468 if (!mp->ports && !mp->mglist &&
1467 netif_running(br->dev)) 1469 netif_running(br->dev))
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 1b5d145dfcbf..d9da857182ef 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -560,8 +560,8 @@ br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
560 unsigned char flags); 560 unsigned char flags);
561void br_mdb_init(void); 561void br_mdb_init(void);
562void br_mdb_uninit(void); 562void br_mdb_uninit(void);
563void br_mdb_notify(struct net_device *dev, struct net_bridge_port_group *pg, 563void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
564 int type); 564 struct br_ip *group, int type, u8 flags);
565void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port, 565void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
566 int type); 566 int type);
567 567