Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[platform/kernel/linux-rpi.git] / net / bridge / br_multicast.c
index 79db489..66efdc2 100644 (file)
@@ -283,6 +283,8 @@ static void br_multicast_del_pg(struct net_bridge *br,
                rcu_assign_pointer(*pp, p->next);
                hlist_del_init(&p->mglist);
                del_timer(&p->timer);
+               br_mdb_notify(br->dev, p->port, &pg->addr, RTM_DELMDB,
+                             p->state);
                call_rcu_bh(&p->rcu, br_multicast_free_pg);
 
                if (!mp->ports && !mp->mglist &&
@@ -704,7 +706,7 @@ static int br_multicast_add_group(struct net_bridge *br,
        if (unlikely(!p))
                goto err;
        rcu_assign_pointer(*pp, p);
-       br_mdb_notify(br->dev, port, group, RTM_NEWMDB);
+       br_mdb_notify(br->dev, port, group, RTM_NEWMDB, MDB_TEMPORARY);
 
 found:
        mod_timer(&p->timer, now + br->multicast_membership_interval);
@@ -764,6 +766,7 @@ static void br_multicast_router_expired(unsigned long data)
                goto out;
 
        hlist_del_init_rcu(&port->rlist);
+       br_rtr_notify(br->dev, port, RTM_DELMDB);
 
 out:
        spin_unlock(&br->multicast_lock);
@@ -924,6 +927,15 @@ void br_multicast_add_port(struct net_bridge_port *port)
 
 void br_multicast_del_port(struct net_bridge_port *port)
 {
+       struct net_bridge *br = port->br;
+       struct net_bridge_port_group *pg;
+       struct hlist_node *n;
+
+       /* Take care of the remaining groups, only perm ones should be left */
+       spin_lock_bh(&br->multicast_lock);
+       hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
+               br_multicast_del_pg(br, pg);
+       spin_unlock_bh(&br->multicast_lock);
        del_timer_sync(&port->multicast_router_timer);
 }
 
@@ -963,10 +975,13 @@ void br_multicast_disable_port(struct net_bridge_port *port)
 
        spin_lock(&br->multicast_lock);
        hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
-               br_multicast_del_pg(br, pg);
+               if (pg->state == MDB_TEMPORARY)
+                       br_multicast_del_pg(br, pg);
 
-       if (!hlist_unhashed(&port->rlist))
+       if (!hlist_unhashed(&port->rlist)) {
                hlist_del_init_rcu(&port->rlist);
+               br_rtr_notify(br->dev, port, RTM_DELMDB);
+       }
        del_timer(&port->multicast_router_timer);
        del_timer(&port->ip4_own_query.timer);
 #if IS_ENABLED(CONFIG_IPV6)
@@ -1204,6 +1219,7 @@ static void br_multicast_add_router(struct net_bridge *br,
                hlist_add_behind_rcu(&port->rlist, slot);
        else
                hlist_add_head_rcu(&port->rlist, &br->router_list);
+       br_rtr_notify(br->dev, port, RTM_NEWMDB);
 }
 
 static void br_multicast_mark_router(struct net_bridge *br,
@@ -1416,8 +1432,7 @@ br_multicast_leave_group(struct net_bridge *br,
 
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) ||
-           (port && port->state == BR_STATE_DISABLED) ||
-           timer_pending(&other_query->timer))
+           (port && port->state == BR_STATE_DISABLED))
                goto out;
 
        mdb = mlock_dereference(br->mdb, br);
@@ -1425,6 +1440,32 @@ br_multicast_leave_group(struct net_bridge *br,
        if (!mp)
                goto out;
 
+       if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) {
+               struct net_bridge_port_group __rcu **pp;
+
+               for (pp = &mp->ports;
+                    (p = mlock_dereference(*pp, br)) != NULL;
+                    pp = &p->next) {
+                       if (p->port != port)
+                               continue;
+
+                       rcu_assign_pointer(*pp, p->next);
+                       hlist_del_init(&p->mglist);
+                       del_timer(&p->timer);
+                       call_rcu_bh(&p->rcu, br_multicast_free_pg);
+                       br_mdb_notify(br->dev, port, group, RTM_DELMDB,
+                                     p->state);
+
+                       if (!mp->ports && !mp->mglist &&
+                           netif_running(br->dev))
+                               mod_timer(&mp->timer, jiffies);
+               }
+               goto out;
+       }
+
+       if (timer_pending(&other_query->timer))
+               goto out;
+
        if (br->multicast_querier) {
                __br_multicast_send_query(br, port, &mp->addr);
 
@@ -1450,28 +1491,6 @@ br_multicast_leave_group(struct net_bridge *br,
                }
        }
 
-       if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) {
-               struct net_bridge_port_group __rcu **pp;
-
-               for (pp = &mp->ports;
-                    (p = mlock_dereference(*pp, br)) != NULL;
-                    pp = &p->next) {
-                       if (p->port != port)
-                               continue;
-
-                       rcu_assign_pointer(*pp, p->next);
-                       hlist_del_init(&p->mglist);
-                       del_timer(&p->timer);
-                       call_rcu_bh(&p->rcu, br_multicast_free_pg);
-                       br_mdb_notify(br->dev, port, group, RTM_DELMDB);
-
-                       if (!mp->ports && !mp->mglist &&
-                           netif_running(br->dev))
-                               mod_timer(&mp->timer, jiffies);
-               }
-               goto out;
-       }
-
        now = jiffies;
        time = now + br->multicast_last_member_count *
                     br->multicast_last_member_interval;
@@ -1589,7 +1608,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
                break;
        }
 
-       if (skb_trimmed)
+       if (skb_trimmed && skb_trimmed != skb)
                kfree_skb(skb_trimmed);
 
        return err;
@@ -1634,7 +1653,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
                break;
        }
 
-       if (skb_trimmed)
+       if (skb_trimmed && skb_trimmed != skb)
                kfree_skb(skb_trimmed);
 
        return err;
@@ -1752,12 +1771,6 @@ void br_multicast_open(struct net_bridge *br)
 
 void br_multicast_stop(struct net_bridge *br)
 {
-       struct net_bridge_mdb_htable *mdb;
-       struct net_bridge_mdb_entry *mp;
-       struct hlist_node *n;
-       u32 ver;
-       int i;
-
        del_timer_sync(&br->multicast_router_timer);
        del_timer_sync(&br->ip4_other_query.timer);
        del_timer_sync(&br->ip4_own_query.timer);
@@ -1765,6 +1778,15 @@ void br_multicast_stop(struct net_bridge *br)
        del_timer_sync(&br->ip6_other_query.timer);
        del_timer_sync(&br->ip6_own_query.timer);
 #endif
+}
+
+void br_multicast_dev_del(struct net_bridge *br)
+{
+       struct net_bridge_mdb_htable *mdb;
+       struct net_bridge_mdb_entry *mp;
+       struct hlist_node *n;
+       u32 ver;
+       int i;
 
        spin_lock_bh(&br->multicast_lock);
        mdb = mlock_dereference(br->mdb, br);
@@ -1832,8 +1854,10 @@ int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
                p->multicast_router = val;
                err = 0;
 
-               if (val < 2 && !hlist_unhashed(&p->rlist))
+               if (val < 2 && !hlist_unhashed(&p->rlist)) {
                        hlist_del_init_rcu(&p->rlist);
+                       br_rtr_notify(br->dev, p, RTM_DELMDB);
+               }
 
                if (val == 1)
                        break;