[PATCH net-next] bridge: implement multicast fast leave
From: Cong Wang <hidden>
Date: 2012-12-03 14:36:03
Also in:
bridge
Subsystem:
ethernet bridge, networking [general], the rest · Maintainers:
Nikolay Aleksandrov, Ido Schimmel, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Fast leave allows bridge to immediately stops the multicast traffic on the port receives IGMP Leave when IGMP snooping is enabled, no timeouts are observed. Cc: Herbert Xu <herbert@gondor.apana.org.au> Cc: Stephen Hemminger <redacted> Cc: "David S. Miller" <davem@davemloft.net> Signed-off-by: Cong Wang <redacted> ---
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index d53e4f4..05e0572 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c@@ -1226,6 +1226,40 @@ static void br_multicast_leave_group(struct net_bridge *br, if (!mp) goto out; + if (br->multicast_fast_leave) { + struct net_bridge_port_group __rcu **pp; + + if (!port) { + mp->mglist = false; + + if (mp->ports) + goto out; + + hlist_del_rcu(&mp->hlist[mdb->ver]); + mdb->size--; + del_timer(&mp->timer); + call_rcu_bh(&mp->rcu, br_multicast_free_group); + goto out; + } + + 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); + + 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;
@@ -1567,6 +1601,7 @@ void br_multicast_init(struct net_bridge *br) br->hash_max = 512; br->multicast_router = 1; + br->multicast_fast_leave = 0; br->multicast_querier = 0; br->multicast_last_member_count = 2; br->multicast_startup_query_count = 2;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 6484069..2f5f5b8 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h@@ -230,6 +230,7 @@ struct net_bridge u8 multicast_disabled:1; u8 multicast_querier:1; + u8 multicast_fast_leave:1; u32 hash_elasticity; u32 hash_max;
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 5913a3a..f88389f 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c@@ -375,6 +375,32 @@ static ssize_t store_multicast_snooping(struct device *d, static DEVICE_ATTR(multicast_snooping, S_IRUGO | S_IWUSR, show_multicast_snooping, store_multicast_snooping); +static ssize_t show_multicast_fast_leave(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct net_bridge *br = to_bridge(d); + return sprintf(buf, "%d\n", br->multicast_fast_leave); +} + +static int set_fast_leave(struct net_bridge *br, unsigned long val) +{ + if (br->multicast_disabled) + return -EINVAL; + + br->multicast_fast_leave = !!val; + return 0; +} + +static ssize_t store_multicast_fast_leave(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, set_fast_leave); +} +static DEVICE_ATTR(multicast_fast_leave, S_IRUGO | S_IWUSR, + show_multicast_fast_leave, store_multicast_fast_leave); + static ssize_t show_multicast_querier(struct device *d, struct device_attribute *attr, char *buf)
@@ -715,6 +741,7 @@ static struct attribute *bridge_attrs[] = { #ifdef CONFIG_BRIDGE_IGMP_SNOOPING &dev_attr_multicast_router.attr, &dev_attr_multicast_snooping.attr, + &dev_attr_multicast_fast_leave.attr, &dev_attr_multicast_querier.attr, &dev_attr_hash_elasticity.attr, &dev_attr_hash_max.attr,