Commit d1ebfcd6 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller

[BRIDGE]: Fix several locking bugs, plus cleanups.

parent 60c6580a
...@@ -101,7 +101,7 @@ struct __fdb_entry ...@@ -101,7 +101,7 @@ struct __fdb_entry
struct net_bridge; struct net_bridge;
struct net_bridge_port; struct net_bridge_port;
extern int (*br_ioctl_hook)(unsigned long arg); extern void brioctl_set(int (*ioctl_hook)(unsigned long));
extern int (*br_handle_frame_hook)(struct sk_buff *skb); extern int (*br_handle_frame_hook)(struct sk_buff *skb);
extern int (*br_should_route_hook)(struct sk_buff **pskb); extern int (*br_should_route_hook)(struct sk_buff **pskb);
......
...@@ -49,8 +49,9 @@ static int __init br_init(void) ...@@ -49,8 +49,9 @@ static int __init br_init(void)
if (br_netfilter_init()) if (br_netfilter_init())
return 1; return 1;
#endif #endif
brioctl_set(br_ioctl_deviceless_stub);
br_handle_frame_hook = br_handle_frame; br_handle_frame_hook = br_handle_frame;
br_ioctl_hook = br_ioctl_deviceless_stub;
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
br_fdb_get_hook = br_fdb_get; br_fdb_get_hook = br_fdb_get;
br_fdb_put_hook = br_fdb_put; br_fdb_put_hook = br_fdb_put;
...@@ -60,24 +61,18 @@ static int __init br_init(void) ...@@ -60,24 +61,18 @@ static int __init br_init(void)
return 0; return 0;
} }
static void __br_clear_ioctl_hook(void)
{
br_ioctl_hook = NULL;
}
static void __exit br_deinit(void) static void __exit br_deinit(void)
{ {
#ifdef CONFIG_NETFILTER #ifdef CONFIG_NETFILTER
br_netfilter_fini(); br_netfilter_fini();
#endif #endif
unregister_netdevice_notifier(&br_device_notifier); unregister_netdevice_notifier(&br_device_notifier);
br_call_ioctl_atomic(__br_clear_ioctl_hook);
br_write_lock_bh(BR_NETPROTO_LOCK); brioctl_set(NULL);
br_handle_frame_hook = NULL; br_handle_frame_hook = NULL;
br_write_unlock_bh(BR_NETPROTO_LOCK);
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
/* FIX ME. move into hook structure with ref count */
br_fdb_get_hook = NULL; br_fdb_get_hook = NULL;
br_fdb_put_hook = NULL; br_fdb_put_hook = NULL;
#endif #endif
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "br_private.h" #include "br_private.h"
static struct net_bridge *bridge_list; static struct net_bridge *bridge_list;
static spinlock_t bridge_lock = SPIN_LOCK_UNLOCKED;
static int br_initial_port_cost(struct net_device *dev) static int br_initial_port_cost(struct net_device *dev)
{ {
...@@ -69,6 +70,7 @@ static int __br_del_if(struct net_bridge *br, struct net_device *dev) ...@@ -69,6 +70,7 @@ static int __br_del_if(struct net_bridge *br, struct net_device *dev)
return 0; return 0;
} }
/* called with bridge_lock */
static struct net_bridge **__find_br(char *name) static struct net_bridge **__find_br(char *name)
{ {
struct net_bridge **b; struct net_bridge **b;
...@@ -188,8 +190,10 @@ int br_add_bridge(char *name) ...@@ -188,8 +190,10 @@ int br_add_bridge(char *name)
return -EEXIST; return -EEXIST;
} }
spin_lock(&bridge_lock);
br->next = bridge_list; br->next = bridge_list;
bridge_list = br; bridge_list = br;
spin_unlock(&bridge_lock);
br_inc_use_count(); br_inc_use_count();
register_netdev(&br->dev); register_netdev(&br->dev);
...@@ -202,17 +206,22 @@ int br_del_bridge(char *name) ...@@ -202,17 +206,22 @@ int br_del_bridge(char *name)
struct net_bridge **b; struct net_bridge **b;
struct net_bridge *br; struct net_bridge *br;
if ((b = __find_br(name)) == NULL) spin_lock(&bridge_lock);
if ((b = __find_br(name)) == NULL) {
spin_unlock(&bridge_lock);
return -ENXIO; return -ENXIO;
}
br = *b; br = *b;
if (br->dev.flags & IFF_UP) {
if (br->dev.flags & IFF_UP) spin_unlock(&bridge_lock);
return -EBUSY; return -EBUSY;
}
del_ifs(br);
*b = br->next; *b = br->next;
spin_unlock(&bridge_lock);
del_ifs(br);
unregister_netdev(&br->dev); unregister_netdev(&br->dev);
kfree(br); kfree(br);
...@@ -272,6 +281,7 @@ int br_get_bridge_ifindices(int *indices, int num) ...@@ -272,6 +281,7 @@ int br_get_bridge_ifindices(int *indices, int num)
struct net_bridge *br; struct net_bridge *br;
int i; int i;
spin_lock(&bridge_lock);
br = bridge_list; br = bridge_list;
for (i=0;i<num;i++) { for (i=0;i<num;i++) {
if (br == NULL) if (br == NULL)
...@@ -280,6 +290,7 @@ int br_get_bridge_ifindices(int *indices, int num) ...@@ -280,6 +290,7 @@ int br_get_bridge_ifindices(int *indices, int num)
indices[i] = br->dev.ifindex; indices[i] = br->dev.ifindex;
br = br->next; br = br->next;
} }
spin_unlock(&bridge_lock);
return i; return i;
} }
...@@ -289,9 +300,11 @@ void br_get_port_ifindices(struct net_bridge *br, int *ifindices) ...@@ -289,9 +300,11 @@ void br_get_port_ifindices(struct net_bridge *br, int *ifindices)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
read_lock(&br->lock);
p = br->port_list; p = br->port_list;
while (p != NULL) { while (p != NULL) {
ifindices[p->port_no] = p->dev->ifindex; ifindices[p->port_no] = p->dev->ifindex;
p = p->next; p = p->next;
} }
read_unlock(&br->lock);
} }
...@@ -53,6 +53,7 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -53,6 +53,7 @@ static int br_ioctl_device(struct net_bridge *br,
{ {
struct __bridge_info b; struct __bridge_info b;
read_lock(&br->lock);
memset(&b, 0, sizeof(struct __bridge_info)); memset(&b, 0, sizeof(struct __bridge_info));
memcpy(&b.designated_root, &br->designated_root, 8); memcpy(&b.designated_root, &br->designated_root, 8);
memcpy(&b.bridge_id, &br->bridge_id, 8); memcpy(&b.bridge_id, &br->bridge_id, 8);
...@@ -73,6 +74,7 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -73,6 +74,7 @@ static int br_ioctl_device(struct net_bridge *br,
b.tcn_timer_value = br_timer_get_residue(&br->tcn_timer); b.tcn_timer_value = br_timer_get_residue(&br->tcn_timer);
b.topology_change_timer_value = br_timer_get_residue(&br->topology_change_timer); b.topology_change_timer_value = br_timer_get_residue(&br->topology_change_timer);
b.gc_timer_value = br_timer_get_residue(&br->gc_timer); b.gc_timer_value = br_timer_get_residue(&br->gc_timer);
read_unlock(&br->lock);
if (copy_to_user((void *)arg0, &b, sizeof(b))) if (copy_to_user((void *)arg0, &b, sizeof(b)))
return -EFAULT; return -EFAULT;
...@@ -96,21 +98,27 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -96,21 +98,27 @@ static int br_ioctl_device(struct net_bridge *br,
} }
case BRCTL_SET_BRIDGE_FORWARD_DELAY: case BRCTL_SET_BRIDGE_FORWARD_DELAY:
write_lock(&br->lock);
br->bridge_forward_delay = arg0; br->bridge_forward_delay = arg0;
if (br_is_root_bridge(br)) if (br_is_root_bridge(br))
br->forward_delay = arg0; br->forward_delay = arg0;
write_unlock(&br->lock);
return 0; return 0;
case BRCTL_SET_BRIDGE_HELLO_TIME: case BRCTL_SET_BRIDGE_HELLO_TIME:
write_lock(&br->lock);
br->bridge_hello_time = arg0; br->bridge_hello_time = arg0;
if (br_is_root_bridge(br)) if (br_is_root_bridge(br))
br->hello_time = arg0; br->hello_time = arg0;
write_unlock(&br->lock);
return 0; return 0;
case BRCTL_SET_BRIDGE_MAX_AGE: case BRCTL_SET_BRIDGE_MAX_AGE:
write_lock(&br->lock);
br->bridge_max_age = arg0; br->bridge_max_age = arg0;
if (br_is_root_bridge(br)) if (br_is_root_bridge(br))
br->max_age = arg0; br->max_age = arg0;
write_unlock(&br->lock);
return 0; return 0;
case BRCTL_SET_AGEING_TIME: case BRCTL_SET_AGEING_TIME:
...@@ -126,6 +134,7 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -126,6 +134,7 @@ static int br_ioctl_device(struct net_bridge *br,
struct __port_info p; struct __port_info p;
struct net_bridge_port *pt; struct net_bridge_port *pt;
read_lock(&br->lock);
if ((pt = br_get_port(br, arg1)) == NULL) if ((pt = br_get_port(br, arg1)) == NULL)
return -EINVAL; return -EINVAL;
...@@ -143,6 +152,8 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -143,6 +152,8 @@ static int br_ioctl_device(struct net_bridge *br,
p.forward_delay_timer_value = br_timer_get_residue(&pt->forward_delay_timer); p.forward_delay_timer_value = br_timer_get_residue(&pt->forward_delay_timer);
p.hold_timer_value = br_timer_get_residue(&pt->hold_timer); p.hold_timer_value = br_timer_get_residue(&pt->hold_timer);
read_unlock(&br->lock);
if (copy_to_user((void *)arg0, &p, sizeof(p))) if (copy_to_user((void *)arg0, &p, sizeof(p)))
return -EFAULT; return -EFAULT;
...@@ -154,16 +165,20 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -154,16 +165,20 @@ static int br_ioctl_device(struct net_bridge *br,
return 0; return 0;
case BRCTL_SET_BRIDGE_PRIORITY: case BRCTL_SET_BRIDGE_PRIORITY:
write_lock(&br->lock);
br_stp_set_bridge_priority(br, arg0); br_stp_set_bridge_priority(br, arg0);
write_unlock(&br->lock);
return 0; return 0;
case BRCTL_SET_PORT_PRIORITY: case BRCTL_SET_PORT_PRIORITY:
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
write_lock(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL) if ((p = br_get_port(br, arg0)) == NULL)
return -EINVAL; return -EINVAL;
br_stp_set_port_priority(p, arg1); br_stp_set_port_priority(p, arg1);
write_unlock(&br->lock);
return 0; return 0;
} }
...@@ -171,9 +186,11 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -171,9 +186,11 @@ static int br_ioctl_device(struct net_bridge *br,
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
write_lock(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL) if ((p = br_get_port(br, arg0)) == NULL)
return -EINVAL; return -EINVAL;
br_stp_set_path_cost(p, arg1); br_stp_set_path_cost(p, arg1);
write_unlock(&br->lock);
return 0; return 0;
} }
...@@ -230,11 +247,9 @@ static int br_ioctl_deviceless(unsigned int cmd, ...@@ -230,11 +247,9 @@ static int br_ioctl_deviceless(unsigned int cmd,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static DECLARE_MUTEX(ioctl_mutex);
int br_ioctl_deviceless_stub(unsigned long arg) int br_ioctl_deviceless_stub(unsigned long arg)
{ {
int err;
unsigned long i[3]; unsigned long i[3];
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
...@@ -243,11 +258,7 @@ int br_ioctl_deviceless_stub(unsigned long arg) ...@@ -243,11 +258,7 @@ int br_ioctl_deviceless_stub(unsigned long arg)
if (copy_from_user(i, (void *)arg, 3*sizeof(unsigned long))) if (copy_from_user(i, (void *)arg, 3*sizeof(unsigned long)))
return -EFAULT; return -EFAULT;
down(&ioctl_mutex); return br_ioctl_deviceless(i[0], i[1], i[2]);
err = br_ioctl_deviceless(i[0], i[1], i[2]);
up(&ioctl_mutex);
return err;
} }
int br_ioctl(struct net_bridge *br, unsigned int cmd, unsigned long arg0, unsigned long arg1, unsigned long arg2) int br_ioctl(struct net_bridge *br, unsigned int cmd, unsigned long arg0, unsigned long arg1, unsigned long arg2)
...@@ -257,18 +268,9 @@ int br_ioctl(struct net_bridge *br, unsigned int cmd, unsigned long arg0, unsign ...@@ -257,18 +268,9 @@ int br_ioctl(struct net_bridge *br, unsigned int cmd, unsigned long arg0, unsign
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
return -EPERM; return -EPERM;
down(&ioctl_mutex);
err = br_ioctl_deviceless(cmd, arg0, arg1); err = br_ioctl_deviceless(cmd, arg0, arg1);
if (err == -EOPNOTSUPP) if (err == -EOPNOTSUPP)
err = br_ioctl_device(br, cmd, arg0, arg1, arg2); err = br_ioctl_device(br, cmd, arg0, arg1, arg2);
up(&ioctl_mutex);
return err; return err;
} }
void br_call_ioctl_atomic(void (*fn)(void))
{
down(&ioctl_mutex);
fn();
up(&ioctl_mutex);
}
...@@ -21,15 +21,14 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v ...@@ -21,15 +21,14 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
struct notifier_block br_device_notifier = struct notifier_block br_device_notifier =
{ {
br_device_event, .notifier_call = br_device_event
NULL,
0
}; };
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{ {
struct net_device *dev; struct net_device *dev;
struct net_bridge_port *p; struct net_bridge_port *p;
struct net_bridge *br;
dev = ptr; dev = ptr;
p = dev->br_port; p = dev->br_port;
...@@ -37,13 +36,15 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v ...@@ -37,13 +36,15 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
if (p == NULL) if (p == NULL)
return NOTIFY_DONE; return NOTIFY_DONE;
br = p->br;
switch (event) switch (event)
{ {
case NETDEV_CHANGEADDR: case NETDEV_CHANGEADDR:
read_lock(&p->br->lock); write_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr); br_fdb_changeaddr(p, dev->dev_addr);
br_stp_recalculate_bridge_id(p->br); br_stp_recalculate_bridge_id(br);
read_unlock(&p->br->lock); write_unlock_bh(&br->lock);
break; break;
case NETDEV_GOING_DOWN: case NETDEV_GOING_DOWN:
...@@ -51,23 +52,23 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v ...@@ -51,23 +52,23 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
break; break;
case NETDEV_DOWN: case NETDEV_DOWN:
if (p->br->dev.flags & IFF_UP) { if (br->dev.flags & IFF_UP) {
read_lock(&p->br->lock); write_lock_bh(&br->lock);
br_stp_disable_port(dev->br_port); br_stp_disable_port(p);
read_unlock(&p->br->lock); write_unlock_bh(&br->lock);
} }
break; break;
case NETDEV_UP: case NETDEV_UP:
if (p->br->dev.flags & IFF_UP) { if (!(br->dev.flags & IFF_UP)) {
read_lock(&p->br->lock); write_lock_bh(&br->lock);
br_stp_enable_port(dev->br_port); br_stp_enable_port(p);
read_unlock(&p->br->lock); write_unlock_bh(&br->lock);
} }
break; break;
case NETDEV_UNREGISTER: case NETDEV_UNREGISTER:
br_del_if(dev->br_port->br, dev); br_del_if(br, dev);
break; break;
} }
......
...@@ -1433,9 +1433,8 @@ static void net_tx_action(struct softirq_action *h) ...@@ -1433,9 +1433,8 @@ static void net_tx_action(struct softirq_action *h)
} }
} }
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) #if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
#endif
static __inline__ int handle_bridge(struct sk_buff *skb, static __inline__ int handle_bridge(struct sk_buff *skb,
struct packet_type *pt_prev) struct packet_type *pt_prev)
...@@ -1454,6 +1453,7 @@ static __inline__ int handle_bridge(struct sk_buff *skb, ...@@ -1454,6 +1453,7 @@ static __inline__ int handle_bridge(struct sk_buff *skb,
return ret; return ret;
} }
#endif
#ifdef CONFIG_NET_DIVERT #ifdef CONFIG_NET_DIVERT
static inline int handle_diverter(struct sk_buff *skb) static inline int handle_diverter(struct sk_buff *skb)
...@@ -1510,12 +1510,13 @@ int netif_receive_skb(struct sk_buff *skb) ...@@ -1510,12 +1510,13 @@ int netif_receive_skb(struct sk_buff *skb)
#endif /* CONFIG_NET_DIVERT */ #endif /* CONFIG_NET_DIVERT */
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
if (skb->dev->br_port && br_handle_frame_hook) { if (skb->dev->br_port) {
int ret; int ret;
ret = handle_bridge(skb, pt_prev); ret = handle_bridge(skb, pt_prev);
if (br_handle_frame_hook(skb) == 0) if (br_handle_frame_hook(skb) == 0)
return ret; return ret;
pt_prev = NULL; pt_prev = NULL;
} }
#endif #endif
......
...@@ -234,8 +234,8 @@ EXPORT_SYMBOL(scm_detach_fds); ...@@ -234,8 +234,8 @@ EXPORT_SYMBOL(scm_detach_fds);
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
EXPORT_SYMBOL(br_handle_frame_hook); EXPORT_SYMBOL(br_handle_frame_hook);
EXPORT_SYMBOL(brioctl_set);
#endif #endif
EXPORT_SYMBOL(br_ioctl_hook);
#ifdef CONFIG_NET_DIVERT #ifdef CONFIG_NET_DIVERT
EXPORT_SYMBOL(alloc_divert_blk); EXPORT_SYMBOL(alloc_divert_blk);
......
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
#include <linux/wanrouter.h> #include <linux/wanrouter.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/if_bridge.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/cache.h> #include <linux/cache.h>
...@@ -712,7 +713,18 @@ static ssize_t sock_writev(struct file *file, const struct iovec *vector, ...@@ -712,7 +713,18 @@ static ssize_t sock_writev(struct file *file, const struct iovec *vector,
file, vector, count, tot_len); file, vector, count, tot_len);
} }
int (*br_ioctl_hook)(unsigned long arg);
static DECLARE_MUTEX(br_ioctl_mutex);
static int (*br_ioctl_hook)(unsigned long arg) = NULL;
void brioctl_set(int (*hook)(unsigned long))
{
down(&br_ioctl_mutex);
br_ioctl_hook = hook;
up(&br_ioctl_mutex);
}
int (*vlan_ioctl_hook)(unsigned long arg); int (*vlan_ioctl_hook)(unsigned long arg);
#ifdef CONFIG_DLCI #ifdef CONFIG_DLCI
...@@ -759,12 +771,16 @@ static int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -759,12 +771,16 @@ static int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
case SIOCGIFBR: case SIOCGIFBR:
case SIOCSIFBR: case SIOCSIFBR:
err = -ENOPKG; err = -ENOPKG;
#ifdef CONFIG_KMOD #ifdef CONFIG_KMOD
if (!br_ioctl_hook) if (!br_ioctl_hook)
request_module("bridge"); request_module("bridge");
#endif #endif
down(&br_ioctl_mutex);
if (br_ioctl_hook) if (br_ioctl_hook)
err = br_ioctl_hook(arg); err = br_ioctl_hook(arg);
up(&br_ioctl_mutex);
break; break;
case SIOCGIFVLAN: case SIOCGIFVLAN:
case SIOCSIFVLAN: case SIOCSIFVLAN:
......
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