Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[sfrench/cifs-2.6.git] / net / bridge / br_if.c
index 0b6b1f2ff7acb4000892dd08e1e9834844d188e7..c03d2c3ff03ed6cb99dc05d7a4cde5033d951190 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
+#include <linux/netpoll.h>
 #include <linux/ethtool.h>
 #include <linux/if_arp.h>
 #include <linux/module.h>
@@ -132,7 +133,7 @@ static void del_nbp(struct net_bridge_port *p)
        struct net_bridge *br = p->br;
        struct net_device *dev = p->dev;
 
-       sysfs_remove_link(br->ifobj, dev->name);
+       sysfs_remove_link(br->ifobj, p->dev->name);
 
        dev_set_promiscuity(dev, -1);
 
@@ -146,13 +147,17 @@ static void del_nbp(struct net_bridge_port *p)
 
        list_del_rcu(&p->list);
 
-       rcu_assign_pointer(dev->br_port, NULL);
+       dev->priv_flags &= ~IFF_BRIDGE_PORT;
+
+       netdev_rx_handler_unregister(dev);
 
        br_multicast_del_port(p);
 
        kobject_uevent(&p->kobj, KOBJ_REMOVE);
        kobject_del(&p->kobj);
 
+       br_netpoll_disable(p);
+
        call_rcu(&p->rcu, destroy_nbp_rcu);
 }
 
@@ -186,6 +191,12 @@ static struct net_device *new_bridge_dev(struct net *net, const char *name)
        br = netdev_priv(dev);
        br->dev = dev;
 
+       br->stats = alloc_percpu(struct br_cpu_netstats);
+       if (!br->stats) {
+               free_netdev(dev);
+               return NULL;
+       }
+
        spin_lock_init(&br->lock);
        INIT_LIST_HEAD(&br->port_list);
        spin_lock_init(&br->hash_lock);
@@ -390,7 +401,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
                return -ELOOP;
 
        /* Device is already being bridged */
-       if (dev->br_port != NULL)
+       if (br_port_exists(dev))
                return -EBUSY;
 
        /* No bridging devices that dislike that (e.g. wireless) */
@@ -418,7 +429,15 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
        if (err)
                goto err2;
 
-       rcu_assign_pointer(dev->br_port, p);
+       if (br_netpoll_info(br) && ((err = br_netpoll_enable(p))))
+               goto err3;
+
+       err = netdev_rx_handler_register(dev, br_handle_frame, p);
+       if (err)
+               goto err3;
+
+       dev->priv_flags |= IFF_BRIDGE_PORT;
+
        dev_disable_lro(dev);
 
        list_add_rcu(&p->list, &br->port_list);
@@ -439,6 +458,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
        kobject_uevent(&p->kobj, KOBJ_ADD);
 
        return 0;
+err3:
+       sysfs_remove_link(br->ifobj, p->dev->name);
 err2:
        br_fdb_delete_by_port(br, p, 1);
 err1:
@@ -455,9 +476,13 @@ put_back:
 /* called with RTNL */
 int br_del_if(struct net_bridge *br, struct net_device *dev)
 {
-       struct net_bridge_port *p = dev->br_port;
+       struct net_bridge_port *p;
+
+       if (!br_port_exists(dev))
+               return -EINVAL;
 
-       if (!p || p->br != br)
+       p = br_port_get(dev);
+       if (p->br != br)
                return -EINVAL;
 
        del_nbp(p);