diff options
Diffstat (limited to 'net/bridge/br_multicast.c')
-rw-r--r-- | net/bridge/br_multicast.c | 141 |
1 files changed, 94 insertions, 47 deletions
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 8dc5c8d69bcd..cb4729539b82 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -239,9 +239,9 @@ static void br_multicast_free_group(struct rcu_head *head) kfree(mp); } -static void br_multicast_group_expired(unsigned long data) +static void br_multicast_group_expired(struct timer_list *t) { - struct net_bridge_mdb_entry *mp = (void *)data; + struct net_bridge_mdb_entry *mp = from_timer(mp, t, timer); struct net_bridge *br = mp->br; struct net_bridge_mdb_htable *mdb; @@ -249,7 +249,8 @@ static void br_multicast_group_expired(unsigned long data) if (!netif_running(br->dev) || timer_pending(&mp->timer)) goto out; - mp->mglist = false; + mp->host_joined = false; + br_mdb_notify(br->dev, NULL, &mp->addr, RTM_DELMDB, 0); if (mp->ports) goto out; @@ -292,7 +293,7 @@ static void br_multicast_del_pg(struct net_bridge *br, p->flags); call_rcu_bh(&p->rcu, br_multicast_free_pg); - if (!mp->ports && !mp->mglist && + if (!mp->ports && !mp->host_joined && netif_running(br->dev)) mod_timer(&mp->timer, jiffies); @@ -302,9 +303,9 @@ static void br_multicast_del_pg(struct net_bridge *br, WARN_ON(1); } -static void br_multicast_port_group_expired(unsigned long data) +static void br_multicast_port_group_expired(struct timer_list *t) { - struct net_bridge_port_group *pg = (void *)data; + struct net_bridge_port_group *pg = from_timer(pg, t, timer); struct net_bridge *br = pg->port->br; spin_lock(&br->multicast_lock); @@ -701,8 +702,7 @@ rehash: mp->br = br; mp->addr = *group; - setup_timer(&mp->timer, br_multicast_group_expired, - (unsigned long)mp); + timer_setup(&mp->timer, br_multicast_group_expired, 0); hlist_add_head_rcu(&mp->hlist[mdb->ver], &mdb->mhash[hash]); mdb->size++; @@ -729,8 +729,7 @@ struct net_bridge_port_group *br_multicast_new_port_group( p->flags = flags; rcu_assign_pointer(p->next, next); hlist_add_head(&p->mglist, &port->mglist); - setup_timer(&p->timer, br_multicast_port_group_expired, - (unsigned long)p); + timer_setup(&p->timer, br_multicast_port_group_expired, 0); if (src) memcpy(p->eth_addr, src, ETH_ALEN); @@ -775,7 +774,10 @@ static int br_multicast_add_group(struct net_bridge *br, goto err; if (!port) { - mp->mglist = true; + if (!mp->host_joined) { + mp->host_joined = true; + br_mdb_notify(br->dev, NULL, &mp->addr, RTM_NEWMDB, 0); + } mod_timer(&mp->timer, now + br->multicast_membership_interval); goto out; } @@ -843,9 +845,10 @@ static int br_ip6_multicast_add_group(struct net_bridge *br, } #endif -static void br_multicast_router_expired(unsigned long data) +static void br_multicast_router_expired(struct timer_list *t) { - struct net_bridge_port *port = (void *)data; + struct net_bridge_port *port = + from_timer(port, t, multicast_router_timer); struct net_bridge *br = port->br; spin_lock(&br->multicast_lock); @@ -859,8 +862,32 @@ out: spin_unlock(&br->multicast_lock); } -static void br_multicast_local_router_expired(unsigned long data) +static void br_mc_router_state_change(struct net_bridge *p, + bool is_mc_router) +{ + struct switchdev_attr attr = { + .orig_dev = p->dev, + .id = SWITCHDEV_ATTR_ID_BRIDGE_MROUTER, + .flags = SWITCHDEV_F_DEFER, + .u.mrouter = is_mc_router, + }; + + switchdev_port_attr_set(p->dev, &attr); +} + +static void br_multicast_local_router_expired(struct timer_list *t) { + struct net_bridge *br = from_timer(br, t, multicast_router_timer); + + spin_lock(&br->multicast_lock); + if (br->multicast_router == MDB_RTR_TYPE_DISABLED || + br->multicast_router == MDB_RTR_TYPE_PERM || + timer_pending(&br->multicast_router_timer)) + goto out; + + br_mc_router_state_change(br, false); +out: + spin_unlock(&br->multicast_lock); } static void br_multicast_querier_expired(struct net_bridge *br, @@ -876,17 +903,17 @@ out: spin_unlock(&br->multicast_lock); } -static void br_ip4_multicast_querier_expired(unsigned long data) +static void br_ip4_multicast_querier_expired(struct timer_list *t) { - struct net_bridge *br = (void *)data; + struct net_bridge *br = from_timer(br, t, ip4_other_query.timer); br_multicast_querier_expired(br, &br->ip4_own_query); } #if IS_ENABLED(CONFIG_IPV6) -static void br_ip6_multicast_querier_expired(unsigned long data) +static void br_ip6_multicast_querier_expired(struct timer_list *t) { - struct net_bridge *br = (void *)data; + struct net_bridge *br = from_timer(br, t, ip6_other_query.timer); br_multicast_querier_expired(br, &br->ip6_own_query); } @@ -987,17 +1014,17 @@ out: spin_unlock(&br->multicast_lock); } -static void br_ip4_multicast_port_query_expired(unsigned long data) +static void br_ip4_multicast_port_query_expired(struct timer_list *t) { - struct net_bridge_port *port = (void *)data; + struct net_bridge_port *port = from_timer(port, t, ip4_own_query.timer); br_multicast_port_query_expired(port, &port->ip4_own_query); } #if IS_ENABLED(CONFIG_IPV6) -static void br_ip6_multicast_port_query_expired(unsigned long data) +static void br_ip6_multicast_port_query_expired(struct timer_list *t) { - struct net_bridge_port *port = (void *)data; + struct net_bridge_port *port = from_timer(port, t, ip6_own_query.timer); br_multicast_port_query_expired(port, &port->ip6_own_query); } @@ -1019,13 +1046,13 @@ int br_multicast_add_port(struct net_bridge_port *port) { port->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; - setup_timer(&port->multicast_router_timer, br_multicast_router_expired, - (unsigned long)port); - setup_timer(&port->ip4_own_query.timer, - br_ip4_multicast_port_query_expired, (unsigned long)port); + timer_setup(&port->multicast_router_timer, + br_multicast_router_expired, 0); + timer_setup(&port->ip4_own_query.timer, + br_ip4_multicast_port_query_expired, 0); #if IS_ENABLED(CONFIG_IPV6) - setup_timer(&port->ip6_own_query.timer, - br_ip6_multicast_port_query_expired, (unsigned long)port); + timer_setup(&port->ip6_own_query.timer, + br_ip6_multicast_port_query_expired, 0); #endif br_mc_disabled_update(port->dev, port->br->multicast_disabled); @@ -1364,9 +1391,12 @@ static void br_multicast_mark_router(struct net_bridge *br, unsigned long now = jiffies; if (!port) { - if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) + if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) { + if (!timer_pending(&br->multicast_router_timer)) + br_mc_router_state_change(br, true); mod_timer(&br->multicast_router_timer, now + br->multicast_querier_interval); + } return; } @@ -1451,7 +1481,7 @@ static int br_ip4_multicast_query(struct net_bridge *br, max_delay *= br->multicast_last_member_count; - if (mp->mglist && + if (mp->host_joined && (timer_pending(&mp->timer) ? time_after(mp->timer.expires, now + max_delay) : try_to_del_timer_sync(&mp->timer) >= 0)) @@ -1535,7 +1565,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, goto out; max_delay *= br->multicast_last_member_count; - if (mp->mglist && + if (mp->host_joined && (timer_pending(&mp->timer) ? time_after(mp->timer.expires, now + max_delay) : try_to_del_timer_sync(&mp->timer) >= 0)) @@ -1596,7 +1626,7 @@ br_multicast_leave_group(struct net_bridge *br, br_mdb_notify(br->dev, port, group, RTM_DELMDB, p->flags); - if (!mp->ports && !mp->mglist && + if (!mp->ports && !mp->host_joined && netif_running(br->dev)) mod_timer(&mp->timer, jiffies); } @@ -1636,7 +1666,7 @@ br_multicast_leave_group(struct net_bridge *br, br->multicast_last_member_interval; if (!port) { - if (mp->mglist && + if (mp->host_joined && (timer_pending(&mp->timer) ? time_after(mp->timer.expires, time) : try_to_del_timer_sync(&mp->timer) >= 0)) { @@ -1906,17 +1936,17 @@ static void br_multicast_query_expired(struct net_bridge *br, spin_unlock(&br->multicast_lock); } -static void br_ip4_multicast_query_expired(unsigned long data) +static void br_ip4_multicast_query_expired(struct timer_list *t) { - struct net_bridge *br = (void *)data; + struct net_bridge *br = from_timer(br, t, ip4_own_query.timer); br_multicast_query_expired(br, &br->ip4_own_query, &br->ip4_querier); } #if IS_ENABLED(CONFIG_IPV6) -static void br_ip6_multicast_query_expired(unsigned long data) +static void br_ip6_multicast_query_expired(struct timer_list *t) { - struct net_bridge *br = (void *)data; + struct net_bridge *br = from_timer(br, t, ip6_own_query.timer); br_multicast_query_expired(br, &br->ip6_own_query, &br->ip6_querier); } @@ -1951,17 +1981,17 @@ void br_multicast_init(struct net_bridge *br) br->has_ipv6_addr = 1; spin_lock_init(&br->multicast_lock); - setup_timer(&br->multicast_router_timer, + timer_setup(&br->multicast_router_timer, br_multicast_local_router_expired, 0); - setup_timer(&br->ip4_other_query.timer, - br_ip4_multicast_querier_expired, (unsigned long)br); - setup_timer(&br->ip4_own_query.timer, br_ip4_multicast_query_expired, - (unsigned long)br); + timer_setup(&br->ip4_other_query.timer, + br_ip4_multicast_querier_expired, 0); + timer_setup(&br->ip4_own_query.timer, + br_ip4_multicast_query_expired, 0); #if IS_ENABLED(CONFIG_IPV6) - setup_timer(&br->ip6_other_query.timer, - br_ip6_multicast_querier_expired, (unsigned long)br); - setup_timer(&br->ip6_own_query.timer, br_ip6_multicast_query_expired, - (unsigned long)br); + timer_setup(&br->ip6_other_query.timer, + br_ip6_multicast_querier_expired, 0); + timer_setup(&br->ip6_own_query.timer, + br_ip6_multicast_query_expired, 0); #endif } @@ -2042,9 +2072,14 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val) switch (val) { case MDB_RTR_TYPE_DISABLED: case MDB_RTR_TYPE_PERM: + br_mc_router_state_change(br, val == MDB_RTR_TYPE_PERM); del_timer(&br->multicast_router_timer); - /* fall through */ + br->multicast_router = val; + err = 0; + break; case MDB_RTR_TYPE_TEMP_QUERY: + if (br->multicast_router != MDB_RTR_TYPE_TEMP_QUERY) + br_mc_router_state_change(br, false); br->multicast_router = val; err = 0; break; @@ -2184,6 +2219,18 @@ bool br_multicast_enabled(const struct net_device *dev) } EXPORT_SYMBOL_GPL(br_multicast_enabled); +bool br_multicast_router(const struct net_device *dev) +{ + struct net_bridge *br = netdev_priv(dev); + bool is_router; + + spin_lock_bh(&br->multicast_lock); + is_router = br_multicast_is_router(br); + spin_unlock_bh(&br->multicast_lock); + return is_router; +} +EXPORT_SYMBOL_GPL(br_multicast_router); + int br_multicast_set_querier(struct net_bridge *br, unsigned long val) { unsigned long max_delay; |