The problem was that the br_add_bridge ended up calling register_netdev
that did a rtnl_lock. This fixes that, and gets rid of the bridge_list,
there is no need to keep a separate device list of just bridges. By not
having multiple lists, races are avoided.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
diff -urNp -X dontdiff linux-2.4/net/bridge/br_if.c
bridge-2.4/net/bridge/br_if.c
--- linux-2.4/net/bridge/br_if.c 2004-08-02 10:22:45.331010728 -0700
+++ bridge-2.4/net/bridge/br_if.c 2004-08-02 12:57:58.405208248 -0700
@@ -23,8 +23,6 @@
#include <asm/uaccess.h>
#include "br_private.h"
-static struct net_bridge *bridge_list;
-
static int br_initial_port_cost(struct net_device *dev)
{
if (!strncmp(dev->name, "lec", 3))
@@ -70,22 +68,6 @@ static int __br_del_if(struct net_bridge
return 0;
}
-static struct net_bridge **__find_br(char *name)
-{
- struct net_bridge **b;
- struct net_bridge *br;
-
- b = &bridge_list;
- while ((br = *b) != NULL) {
- if (!strncmp(br->dev.name, name, IFNAMSIZ))
- return b;
-
- b = &(br->next);
- }
-
- return NULL;
-}
-
static void del_ifs(struct net_bridge *br)
{
br_write_lock_bh(BR_NETPROTO_LOCK);
@@ -178,42 +160,48 @@ static struct net_bridge_port *new_nbp(s
int br_add_bridge(char *name)
{
struct net_bridge *br;
+ struct net_device *dev;
+ int err;
if ((br = new_nb(name)) == NULL)
return -ENOMEM;
- if (__dev_get_by_name(name) != NULL) {
- kfree(br);
- return -EEXIST;
+ dev = &br->dev;
+ if (strchr(dev->name, '%')) {
+ err = dev_alloc_name(dev, dev->name);
+ if (err < 0)
+ goto out;
}
- br->next = bridge_list;
- bridge_list = br;
+ err = register_netdevice(dev);
+ if (err == 0)
+ br_inc_use_count();
- br_inc_use_count();
- register_netdev(&br->dev);
-
- return 0;
+ out:
+ return err;
}
int br_del_bridge(char *name)
{
- struct net_bridge **b;
+ struct net_device *dev;
struct net_bridge *br;
- if ((b = __find_br(name)) == NULL)
+ dev = __dev_get_by_name(name);
+ if (!dev)
return -ENXIO;
- br = *b;
+ if (dev->hard_start_xmit != br_dev_xmit)
+ return -EPERM;
- if (br->dev.flags & IFF_UP)
+ if (dev->flags & IFF_UP)
return -EBUSY;
- del_ifs(br);
+ br = dev->priv;
+ BUG_ON(&br->dev != dev);
- *b = br->next;
+ del_ifs(br);
- unregister_netdev(&br->dev);
+ unregister_netdevice(dev);
kfree(br);
br_dec_use_count();
@@ -271,16 +259,12 @@ int br_del_if(struct net_bridge *br, str
int br_get_bridge_ifindices(int *indices, int num)
{
- struct net_bridge *br;
- int i;
-
- br = bridge_list;
- for (i=0;i<num;i++) {
- if (br == NULL)
- break;
+ struct net_device *dev;
+ int i = 0;
- indices[i] = br->dev.ifindex;
- br = br->next;
+ for (dev = dev_base; i < num && dev != NULL; dev = dev->next) {
+ if (dev->hard_start_xmit == br_dev_xmit)
+ indices[i++] = dev->ifindex;
}
return i;
diff -urNp -X dontdiff linux-2.4/net/bridge/br_ioctl.c
bridge-2.4/net/bridge/br_ioctl.c
--- linux-2.4/net/bridge/br_ioctl.c 2004-08-02 10:22:45.332010576 -0700
+++ bridge-2.4/net/bridge/br_ioctl.c 2004-08-02 12:57:58.407207944 -0700
@@ -250,14 +250,9 @@ int br_ioctl_deviceless_stub(unsigned lo
int br_ioctl(struct net_bridge *br, unsigned int cmd, unsigned long arg0,
unsigned long arg1, unsigned long arg2)
{
- int err;
-
if (!capable(CAP_NET_ADMIN))
return -EPERM;
ASSERT_RTNL();
- err = br_ioctl_deviceless(cmd, arg0, arg1);
- if (err == -EOPNOTSUPP)
- err = br_ioctl_device(br, cmd, arg0, arg1, arg2);
- return err;
+ return br_ioctl_device(br, cmd, arg0, arg1, arg2);
}
diff -urNp -X dontdiff linux-2.4/net/bridge/br_private.h
bridge-2.4/net/bridge/br_private.h
--- linux-2.4/net/bridge/br_private.h 2004-08-02 10:22:45.333010424 -0700
+++ bridge-2.4/net/bridge/br_private.h 2004-08-02 10:39:09.582381776 -0700
@@ -79,7 +79,6 @@ struct net_bridge_port
struct net_bridge
{
- struct net_bridge *next;
rwlock_t lock;
struct net_bridge_port *port_list;
struct net_device dev;