Commit af13a450 authored by Stephen Hemminger's avatar Stephen Hemminger

[BRIDGE]: Relax locking on add/delete.

Relax the locking on add/delete interfaces to a bridge.  Since these operations
are already called with RTNL semaphore, only need to hold the bridge lock while
doing operations related to STP and processing path.  This is necessary for later
sysfs support where those operations might sleep.
parent 20d50e8c
...@@ -78,46 +78,44 @@ static int br_initial_port_cost(struct net_device *dev) ...@@ -78,46 +78,44 @@ static int br_initial_port_cost(struct net_device *dev)
static void destroy_nbp(void *arg) static void destroy_nbp(void *arg)
{ {
struct net_bridge_port *p = arg; struct net_bridge_port *p = arg;
struct net_device *dev = p->dev;
p->dev->br_port = NULL; dev->br_port = NULL;
dev_put(dev);
BUG_ON(timer_pending(&p->message_age_timer));
BUG_ON(timer_pending(&p->forward_delay_timer));
BUG_ON(timer_pending(&p->hold_timer));
dev_put(p->dev);
kfree(p); kfree(p);
} }
/* called under bridge lock */ /* called with RTNL */
static void del_nbp(struct net_bridge_port *p) static void del_nbp(struct net_bridge_port *p)
{ {
struct net_bridge *br = p->br;
struct net_device *dev = p->dev; struct net_device *dev = p->dev;
dev_set_promiscuity(dev, -1);
spin_lock_bh(&br->lock);
br_stp_disable_port(p); br_stp_disable_port(p);
spin_unlock_bh(&br->lock);
dev_set_promiscuity(dev, -1); br_fdb_delete_by_port(br, p);
list_del_rcu(&p->list); list_del_rcu(&p->list);
br_fdb_delete_by_port(p->br, p); del_timer_sync(&p->message_age_timer);
del_timer_sync(&p->forward_delay_timer);
del_timer(&p->message_age_timer); del_timer_sync(&p->hold_timer);
del_timer(&p->forward_delay_timer);
del_timer(&p->hold_timer);
call_rcu(&p->rcu, destroy_nbp, p); call_rcu(&p->rcu, destroy_nbp, p);
} }
/* called with RTNL */
static void del_br(struct net_bridge *br) static void del_br(struct net_bridge *br)
{ {
struct list_head *p, *n; struct list_head *p, *n;
spin_lock_bh(&br->lock);
list_for_each_safe(p, n, &br->port_list) { list_for_each_safe(p, n, &br->port_list) {
del_nbp(list_entry(p, struct net_bridge_port, list)); del_nbp(list_entry(p, struct net_bridge_port, list));
} }
spin_unlock_bh(&br->lock);
del_timer_sync(&br->gc_timer); del_timer_sync(&br->gc_timer);
...@@ -171,7 +169,7 @@ static int find_portno(struct net_bridge *br) ...@@ -171,7 +169,7 @@ static int find_portno(struct net_bridge *br)
unsigned long *inuse; unsigned long *inuse;
inuse = kmalloc(BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long), inuse = kmalloc(BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long),
GFP_ATOMIC); GFP_KERNEL);
if (!inuse) if (!inuse)
return -ENOMEM; return -ENOMEM;
...@@ -186,7 +184,7 @@ static int find_portno(struct net_bridge *br) ...@@ -186,7 +184,7 @@ static int find_portno(struct net_bridge *br)
return (index >= BR_MAX_PORTS) ? -EXFULL : index; return (index >= BR_MAX_PORTS) ? -EXFULL : index;
} }
/* called under bridge lock */ /* called with RTNL */
static struct net_bridge_port *new_nbp(struct net_bridge *br, static struct net_bridge_port *new_nbp(struct net_bridge *br,
struct net_device *dev, struct net_device *dev,
unsigned long cost) unsigned long cost)
...@@ -198,7 +196,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, ...@@ -198,7 +196,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
if (index < 0) if (index < 0)
return ERR_PTR(index); return ERR_PTR(index);
p = kmalloc(sizeof(*p), GFP_ATOMIC); p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL) if (p == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -258,10 +256,10 @@ int br_del_bridge(const char *name) ...@@ -258,10 +256,10 @@ int br_del_bridge(const char *name)
return ret; return ret;
} }
/* called with RTNL */
int br_add_if(struct net_bridge *br, struct net_device *dev) int br_add_if(struct net_bridge *br, struct net_device *dev)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
unsigned long cost;
int err = 0; int err = 0;
if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER) if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
...@@ -270,48 +268,45 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -270,48 +268,45 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (dev->hard_start_xmit == br_dev_xmit) if (dev->hard_start_xmit == br_dev_xmit)
return -ELOOP; return -ELOOP;
cost = br_initial_port_cost(dev);
spin_lock_bh(&br->lock);
if (dev->br_port != NULL) if (dev->br_port != NULL)
err = -EBUSY; return -EBUSY;
else if (IS_ERR(p = new_nbp(br, dev, cost))) if (IS_ERR(p = new_nbp(br, dev, br_initial_port_cost(dev))))
err = PTR_ERR(p); return PTR_ERR(p);
else if ((err = br_fdb_insert(br, p, dev->dev_addr, 1))) if ((err = br_fdb_insert(br, p, dev->dev_addr, 1)))
destroy_nbp(p); destroy_nbp(p);
else { else {
dev_set_promiscuity(dev, 1); dev_set_promiscuity(dev, 1);
list_add_rcu(&p->list, &br->port_list); list_add_rcu(&p->list, &br->port_list);
spin_lock_bh(&br->lock);
br_stp_recalculate_bridge_id(br); br_stp_recalculate_bridge_id(br);
if ((br->dev->flags & IFF_UP) && (dev->flags & IFF_UP)) if ((br->dev->flags & IFF_UP) && (dev->flags & IFF_UP))
br_stp_enable_port(p); br_stp_enable_port(p);
spin_unlock_bh(&br->lock);
} }
spin_unlock_bh(&br->lock);
return err; return err;
} }
/* called with RTNL */
int br_del_if(struct net_bridge *br, struct net_device *dev) int br_del_if(struct net_bridge *br, struct net_device *dev)
{ {
struct net_bridge_port *p; struct net_bridge_port *p = dev->br_port;
int err = 0;
if (!p || p->br != br)
return -EINVAL;
del_nbp(p);
spin_lock_bh(&br->lock); spin_lock_bh(&br->lock);
p = dev->br_port; br_stp_recalculate_bridge_id(br);
if (!p || p->br != br)
err = -EINVAL;
else {
del_nbp(p);
br_stp_recalculate_bridge_id(br);
}
spin_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
return err; return 0;
} }
int br_get_bridge_ifindices(int *indices, int num) int br_get_bridge_ifindices(int *indices, int num)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment