bridge: Add multicast_snooping sysfs toggle
authorHerbert Xu <herbert@gondor.apana.org.au>
Sat, 27 Feb 2010 19:41:50 +0000 (19:41 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 28 Feb 2010 08:49:46 +0000 (00:49 -0800)
This patch allows the user to disable IGMP snooping completely
through a sysfs toggle.  It also allows the user to reenable
snooping when it has been automatically disabled due to hash
collisions.  If the collisions have not been resolved however
the system will refuse to reenable snooping.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/bridge/br_multicast.c
net/bridge/br_private.h
net/bridge/br_sysfs_br.c

index 674224b6729d9d8b5af4f9d7cf535d20044d2021..c7a1095ed84a65acffc7e1c128b15fe788c6981e 100644 (file)
@@ -656,6 +656,15 @@ void br_multicast_del_port(struct net_bridge_port *port)
        del_timer_sync(&port->multicast_router_timer);
 }
 
+static void __br_multicast_enable_port(struct net_bridge_port *port)
+{
+       port->multicast_startup_queries_sent = 0;
+
+       if (try_to_del_timer_sync(&port->multicast_query_timer) >= 0 ||
+           del_timer(&port->multicast_query_timer))
+               mod_timer(&port->multicast_query_timer, jiffies);
+}
+
 void br_multicast_enable_port(struct net_bridge_port *port)
 {
        struct net_bridge *br = port->br;
@@ -664,11 +673,7 @@ void br_multicast_enable_port(struct net_bridge_port *port)
        if (br->multicast_disabled || !netif_running(br->dev))
                goto out;
 
-       port->multicast_startup_queries_sent = 0;
-
-       if (try_to_del_timer_sync(&port->multicast_query_timer) >= 0 ||
-           del_timer(&port->multicast_query_timer))
-               mod_timer(&port->multicast_query_timer, jiffies);
+       __br_multicast_enable_port(port);
 
 out:
        spin_unlock(&br->multicast_lock);
@@ -1210,3 +1215,49 @@ unlock:
 
        return err;
 }
+
+int br_multicast_toggle(struct net_bridge *br, unsigned long val)
+{
+       struct net_bridge_port *port;
+       int err = -ENOENT;
+
+       spin_lock(&br->multicast_lock);
+       if (!netif_running(br->dev))
+               goto unlock;
+
+       err = 0;
+       if (br->multicast_disabled == !val)
+               goto unlock;
+
+       br->multicast_disabled = !val;
+       if (br->multicast_disabled)
+               goto unlock;
+
+       if (br->mdb) {
+               if (br->mdb->old) {
+                       err = -EEXIST;
+rollback:
+                       br->multicast_disabled = !!val;
+                       goto unlock;
+               }
+
+               err = br_mdb_rehash(&br->mdb, br->mdb->max,
+                                   br->hash_elasticity);
+               if (err)
+                       goto rollback;
+       }
+
+       br_multicast_open(br);
+       list_for_each_entry(port, &br->port_list, list) {
+               if (port->state == BR_STATE_DISABLED ||
+                   port->state == BR_STATE_BLOCKING)
+                       continue;
+
+               __br_multicast_enable_port(port);
+       }
+
+unlock:
+       spin_unlock(&br->multicast_lock);
+
+       return err;
+}
index dcdfafbe4b17c3b6b4376c51f4ae7498a03ce616..bf162fa6b23bab04953535fd9568e57bf6efe4ba 100644 (file)
@@ -300,6 +300,7 @@ extern void br_multicast_forward(struct net_bridge_mdb_entry *mdst,
 extern int br_multicast_set_router(struct net_bridge *br, unsigned long val);
 extern int br_multicast_set_port_router(struct net_bridge_port *p,
                                        unsigned long val);
+extern int br_multicast_toggle(struct net_bridge *br, unsigned long val);
 #else
 static inline int br_multicast_rcv(struct net_bridge *br,
                                   struct net_bridge_port *port,
index cb742016db21e0046145bfdfa979d42ef394d03c..0ab288332fc5eb5dc7aad673f0314b179ae53cc2 100644 (file)
@@ -361,6 +361,23 @@ static ssize_t store_multicast_router(struct device *d,
 }
 static DEVICE_ATTR(multicast_router, S_IRUGO | S_IWUSR, show_multicast_router,
                   store_multicast_router);
+
+static ssize_t show_multicast_snooping(struct device *d,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct net_bridge *br = to_bridge(d);
+       return sprintf(buf, "%d\n", !br->multicast_disabled);
+}
+
+static ssize_t store_multicast_snooping(struct device *d,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       return store_bridge_parm(d, buf, len, br_multicast_toggle);
+}
+static DEVICE_ATTR(multicast_snooping, S_IRUGO | S_IWUSR,
+                  show_multicast_snooping, store_multicast_snooping);
 #endif
 
 static struct attribute *bridge_attrs[] = {
@@ -384,6 +401,7 @@ static struct attribute *bridge_attrs[] = {
        &dev_attr_flush.attr,
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
        &dev_attr_multicast_router.attr,
+       &dev_attr_multicast_snooping.attr,
 #endif
        NULL
 };