summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2003-04-09 11:01:10 -0700
committerDavid S. Miller <davem@nuts.ninka.net>2003-04-09 11:01:10 -0700
commitd1ebfcd6e608b71bb13cb245544dafa6d0c05674 (patch)
treea0863fa204e53feb95c71583ae2ee0a468b282d3 /net
parent60c6580a3e197510248bd574e42a78c4e585cf9b (diff)
[BRIDGE]: Fix several locking bugs, plus cleanups.
Diffstat (limited to 'net')
-rw-r--r--net/bridge/br.c13
-rw-r--r--net/bridge/br_if.c23
-rw-r--r--net/bridge/br_ioctl.c34
-rw-r--r--net/bridge/br_notify.c33
-rw-r--r--net/core/dev.c9
-rw-r--r--net/netsyms.c2
-rw-r--r--net/socket.c20
7 files changed, 81 insertions, 53 deletions
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 23b44c9c9b82..35b3045cf6b0 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -49,8 +49,9 @@ static int __init br_init(void)
if (br_netfilter_init())
return 1;
#endif
+ brioctl_set(br_ioctl_deviceless_stub);
br_handle_frame_hook = br_handle_frame;
- br_ioctl_hook = br_ioctl_deviceless_stub;
+
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
br_fdb_get_hook = br_fdb_get;
br_fdb_put_hook = br_fdb_put;
@@ -60,24 +61,18 @@ static int __init br_init(void)
return 0;
}
-static void __br_clear_ioctl_hook(void)
-{
- br_ioctl_hook = NULL;
-}
-
static void __exit br_deinit(void)
{
#ifdef CONFIG_NETFILTER
br_netfilter_fini();
#endif
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_write_unlock_bh(BR_NETPROTO_LOCK);
#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_put_hook = NULL;
#endif
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 39427429bf56..f3b70d7fda57 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -23,6 +23,7 @@
#include "br_private.h"
static struct net_bridge *bridge_list;
+static spinlock_t bridge_lock = SPIN_LOCK_UNLOCKED;
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)
return 0;
}
+/* called with bridge_lock */
static struct net_bridge **__find_br(char *name)
{
struct net_bridge **b;
@@ -188,8 +190,10 @@ int br_add_bridge(char *name)
return -EEXIST;
}
+ spin_lock(&bridge_lock);
br->next = bridge_list;
bridge_list = br;
+ spin_unlock(&bridge_lock);
br_inc_use_count();
register_netdev(&br->dev);
@@ -202,17 +206,22 @@ int br_del_bridge(char *name)
struct net_bridge **b;
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;
+ }
br = *b;
-
- if (br->dev.flags & IFF_UP)
+ if (br->dev.flags & IFF_UP) {
+ spin_unlock(&bridge_lock);
return -EBUSY;
-
- del_ifs(br);
+ }
*b = br->next;
+ spin_unlock(&bridge_lock);
+
+ del_ifs(br);
unregister_netdev(&br->dev);
kfree(br);
@@ -272,6 +281,7 @@ int br_get_bridge_ifindices(int *indices, int num)
struct net_bridge *br;
int i;
+ spin_lock(&bridge_lock);
br = bridge_list;
for (i=0;i<num;i++) {
if (br == NULL)
@@ -280,6 +290,7 @@ int br_get_bridge_ifindices(int *indices, int num)
indices[i] = br->dev.ifindex;
br = br->next;
}
+ spin_unlock(&bridge_lock);
return i;
}
@@ -289,9 +300,11 @@ void br_get_port_ifindices(struct net_bridge *br, int *ifindices)
{
struct net_bridge_port *p;
+ read_lock(&br->lock);
p = br->port_list;
while (p != NULL) {
ifindices[p->port_no] = p->dev->ifindex;
p = p->next;
}
+ read_unlock(&br->lock);
}
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index fb353e586103..f9f18146e1db 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -53,6 +53,7 @@ static int br_ioctl_device(struct net_bridge *br,
{
struct __bridge_info b;
+ read_lock(&br->lock);
memset(&b, 0, sizeof(struct __bridge_info));
memcpy(&b.designated_root, &br->designated_root, 8);
memcpy(&b.bridge_id, &br->bridge_id, 8);
@@ -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.topology_change_timer_value = br_timer_get_residue(&br->topology_change_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)))
return -EFAULT;
@@ -96,21 +98,27 @@ static int br_ioctl_device(struct net_bridge *br,
}
case BRCTL_SET_BRIDGE_FORWARD_DELAY:
+ write_lock(&br->lock);
br->bridge_forward_delay = arg0;
if (br_is_root_bridge(br))
br->forward_delay = arg0;
+ write_unlock(&br->lock);
return 0;
case BRCTL_SET_BRIDGE_HELLO_TIME:
+ write_lock(&br->lock);
br->bridge_hello_time = arg0;
if (br_is_root_bridge(br))
br->hello_time = arg0;
+ write_unlock(&br->lock);
return 0;
case BRCTL_SET_BRIDGE_MAX_AGE:
+ write_lock(&br->lock);
br->bridge_max_age = arg0;
if (br_is_root_bridge(br))
br->max_age = arg0;
+ write_unlock(&br->lock);
return 0;
case BRCTL_SET_AGEING_TIME:
@@ -126,6 +134,7 @@ static int br_ioctl_device(struct net_bridge *br,
struct __port_info p;
struct net_bridge_port *pt;
+ read_lock(&br->lock);
if ((pt = br_get_port(br, arg1)) == NULL)
return -EINVAL;
@@ -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.hold_timer_value = br_timer_get_residue(&pt->hold_timer);
+ read_unlock(&br->lock);
+
if (copy_to_user((void *)arg0, &p, sizeof(p)))
return -EFAULT;
@@ -154,16 +165,20 @@ static int br_ioctl_device(struct net_bridge *br,
return 0;
case BRCTL_SET_BRIDGE_PRIORITY:
+ write_lock(&br->lock);
br_stp_set_bridge_priority(br, arg0);
+ write_unlock(&br->lock);
return 0;
case BRCTL_SET_PORT_PRIORITY:
{
struct net_bridge_port *p;
+ write_lock(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL)
return -EINVAL;
br_stp_set_port_priority(p, arg1);
+ write_unlock(&br->lock);
return 0;
}
@@ -171,9 +186,11 @@ static int br_ioctl_device(struct net_bridge *br,
{
struct net_bridge_port *p;
+ write_lock(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL)
return -EINVAL;
br_stp_set_path_cost(p, arg1);
+ write_unlock(&br->lock);
return 0;
}
@@ -230,11 +247,9 @@ static int br_ioctl_deviceless(unsigned int cmd,
return -EOPNOTSUPP;
}
-static DECLARE_MUTEX(ioctl_mutex);
int br_ioctl_deviceless_stub(unsigned long arg)
{
- int err;
unsigned long i[3];
if (!capable(CAP_NET_ADMIN))
@@ -243,11 +258,7 @@ int br_ioctl_deviceless_stub(unsigned long arg)
if (copy_from_user(i, (void *)arg, 3*sizeof(unsigned long)))
return -EFAULT;
- down(&ioctl_mutex);
- err = br_ioctl_deviceless(i[0], i[1], i[2]);
- up(&ioctl_mutex);
-
- return err;
+ return br_ioctl_deviceless(i[0], i[1], i[2]);
}
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
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- down(&ioctl_mutex);
err = br_ioctl_deviceless(cmd, arg0, arg1);
if (err == -EOPNOTSUPP)
err = br_ioctl_device(br, cmd, arg0, arg1, arg2);
- up(&ioctl_mutex);
return err;
}
-
-void br_call_ioctl_atomic(void (*fn)(void))
-{
- down(&ioctl_mutex);
- fn();
- up(&ioctl_mutex);
-}
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
index 6ed309b755bb..f63df779b8cf 100644
--- a/net/bridge/br_notify.c
+++ b/net/bridge/br_notify.c
@@ -21,15 +21,14 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
struct notifier_block br_device_notifier =
{
- br_device_event,
- NULL,
- 0
+ .notifier_call = br_device_event
};
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{
struct net_device *dev;
struct net_bridge_port *p;
+ struct net_bridge *br;
dev = ptr;
p = dev->br_port;
@@ -37,13 +36,15 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
if (p == NULL)
return NOTIFY_DONE;
- switch (event)
+ br = p->br;
+
+ switch (event)
{
case NETDEV_CHANGEADDR:
- read_lock(&p->br->lock);
+ write_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr);
- br_stp_recalculate_bridge_id(p->br);
- read_unlock(&p->br->lock);
+ br_stp_recalculate_bridge_id(br);
+ write_unlock_bh(&br->lock);
break;
case NETDEV_GOING_DOWN:
@@ -51,23 +52,23 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
break;
case NETDEV_DOWN:
- if (p->br->dev.flags & IFF_UP) {
- read_lock(&p->br->lock);
- br_stp_disable_port(dev->br_port);
- read_unlock(&p->br->lock);
+ if (br->dev.flags & IFF_UP) {
+ write_lock_bh(&br->lock);
+ br_stp_disable_port(p);
+ write_unlock_bh(&br->lock);
}
break;
case NETDEV_UP:
- if (p->br->dev.flags & IFF_UP) {
- read_lock(&p->br->lock);
- br_stp_enable_port(dev->br_port);
- read_unlock(&p->br->lock);
+ if (!(br->dev.flags & IFF_UP)) {
+ write_lock_bh(&br->lock);
+ br_stp_enable_port(p);
+ write_unlock_bh(&br->lock);
}
break;
case NETDEV_UNREGISTER:
- br_del_if(dev->br_port->br, dev);
+ br_del_if(br, dev);
break;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index 17e32722281b..a8ef547eb08d 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -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;
-#endif
static __inline__ int handle_bridge(struct sk_buff *skb,
struct packet_type *pt_prev)
@@ -1454,6 +1453,7 @@ static __inline__ int handle_bridge(struct sk_buff *skb,
return ret;
}
+#endif
#ifdef CONFIG_NET_DIVERT
static inline int handle_diverter(struct sk_buff *skb)
@@ -1510,12 +1510,13 @@ int netif_receive_skb(struct sk_buff *skb)
#endif /* CONFIG_NET_DIVERT */
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
- if (skb->dev->br_port && br_handle_frame_hook) {
+ if (skb->dev->br_port) {
int ret;
ret = handle_bridge(skb, pt_prev);
- if (br_handle_frame_hook(skb) == 0)
+ if (br_handle_frame_hook(skb) == 0)
return ret;
+
pt_prev = NULL;
}
#endif
diff --git a/net/netsyms.c b/net/netsyms.c
index b678bb9df90d..b55b5fed2c81 100644
--- a/net/netsyms.c
+++ b/net/netsyms.c
@@ -234,8 +234,8 @@ EXPORT_SYMBOL(scm_detach_fds);
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
EXPORT_SYMBOL(br_handle_frame_hook);
+EXPORT_SYMBOL(brioctl_set);
#endif
-EXPORT_SYMBOL(br_ioctl_hook);
#ifdef CONFIG_NET_DIVERT
EXPORT_SYMBOL(alloc_divert_blk);
diff --git a/net/socket.c b/net/socket.c
index 0df13a726ce5..0d549c4602b5 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -71,6 +71,7 @@
#include <linux/wanrouter.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include <linux/if_bridge.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/cache.h>
@@ -712,7 +713,18 @@ static ssize_t sock_writev(struct file *file, const struct iovec *vector,
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);
#ifdef CONFIG_DLCI
@@ -759,12 +771,16 @@ static int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
case SIOCGIFBR:
case SIOCSIFBR:
err = -ENOPKG;
+
#ifdef CONFIG_KMOD
if (!br_ioctl_hook)
request_module("bridge");
#endif
- if (br_ioctl_hook)
+
+ down(&br_ioctl_mutex);
+ if (br_ioctl_hook)
err = br_ioctl_hook(arg);
+ up(&br_ioctl_mutex);
break;
case SIOCGIFVLAN:
case SIOCSIFVLAN: