netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2.5.68] [BRIDGE] Use RCU for port table
@ 2003-04-25 20:30 Stephen Hemminger
  2003-04-26  4:25 ` David S. Miller
  0 siblings, 1 reply; 2+ messages in thread
From: Stephen Hemminger @ 2003-04-25 20:30 UTC (permalink / raw)
  To: David S. Miller; +Cc: netdev

Use RCU instead of reader/writer lock for the port table. This is cleaner
and simpler, and eliminates dependency on brlock. This patch assumes earlier
patch that uses list_* macros.

Also, fixes issues with timer not getting write lock.  Introduced a little
bit of use of 'const' in the receive side path, just because paranoid that
receive code was only doing read's. Moved locking from dev_open into 
br_stp_enable_bridge because then disable/enable are symmetrical.

Tested with stresstest on 8-way SMP.


diff -Nru a/include/linux/list.h b/include/linux/list.h
--- a/include/linux/list.h	Fri Apr 25 13:19:34 2003
+++ b/include/linux/list.h	Fri Apr 25 13:19:34 2003
@@ -320,6 +320,21 @@
 	for (pos = (head)->next, n = pos->next; pos != (head); \
 		pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
 
+/**
+ * list_for_each_entry_rcu	-	iterate over rcu list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_rcu(pos, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     ({ smp_read_barrier_depends(); 0;}),		\
+		     prefetch(pos->member.next))
+
+
 /* 
  * Double linked lists with a single pointer list head. 
  * Mostly useful for hash tables where the two pointer list head is 
diff -Nru a/net/bridge/br_device.c b/net/bridge/br_device.c
--- a/net/bridge/br_device.c	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_device.c	Fri Apr 25 13:19:34 2003
@@ -74,27 +74,20 @@
 
 int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	struct net_bridge *br;
 	int ret;
 
-	br = dev->priv;
-	read_lock(&br->lock);
+	rcu_read_lock();
 	ret = __br_dev_xmit(skb, dev);
-	read_unlock(&br->lock);
+	rcu_read_unlock();
 
 	return ret;
 }
 
 static int br_dev_open(struct net_device *dev)
 {
-	struct net_bridge *br;
-
 	netif_start_queue(dev);
 
-	br = dev->priv;
-	write_lock(&br->lock);
-	br_stp_enable_bridge(br);
-	write_unlock(&br->lock);
+	br_stp_enable_bridge(dev->priv);
 
 	return 0;
 }
diff -Nru a/net/bridge/br_forward.c b/net/bridge/br_forward.c
--- a/net/bridge/br_forward.c	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_forward.c	Fri Apr 25 13:19:34 2003
@@ -21,7 +21,8 @@
 #include <linux/netfilter_bridge.h>
 #include "br_private.h"
 
-static inline int should_deliver(struct net_bridge_port *p, struct sk_buff *skb)
+static inline int should_deliver(const struct net_bridge_port *p, 
+				 const struct sk_buff *skb)
 {
 	if (skb->dev == p->dev ||
 	    p->state != BR_STATE_FORWARDING)
@@ -52,7 +53,7 @@
 	return 0;
 }
 
-static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 {
 	skb->dev = to->dev;
 #ifdef CONFIG_NETFILTER_DEBUG
@@ -62,7 +63,7 @@
 			br_forward_finish);
 }
 
-static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
 {
 	struct net_device *indev;
 
@@ -73,8 +74,8 @@
 			br_forward_finish);
 }
 
-/* called under bridge lock */
-void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+/* called with rcu_read_lock */
+void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 {
 	if (should_deliver(to, skb)) {
 		__br_deliver(to, skb);
@@ -84,8 +85,8 @@
 	kfree_skb(skb);
 }
 
-/* called under bridge lock */
-void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+/* called with rcu_read_lock */
+void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
 {
 	if (should_deliver(to, skb)) {
 		__br_forward(to, skb);
@@ -97,7 +98,8 @@
 
 /* called under bridge lock */
 static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
-	void (*__packet_hook)(struct net_bridge_port *p, struct sk_buff *skb))
+	void (*__packet_hook)(const struct net_bridge_port *p, 
+			      struct sk_buff *skb))
 {
 	struct net_bridge_port *p;
 	struct net_bridge_port *prev;
@@ -115,7 +117,7 @@
 
 	prev = NULL;
 
-	list_for_each_entry(p, &br->port_list, list) {
+	list_for_each_entry_rcu(p, &br->port_list, list) {
 		if (should_deliver(p, skb)) {
 			if (prev != NULL) {
 				struct sk_buff *skb2;
@@ -141,7 +143,8 @@
 	kfree_skb(skb);
 }
 
-/* called under bridge lock */
+
+/* called with rcu_read_lock */
 void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
 {
 	br_flood(br, skb, clone, __br_deliver);
diff -Nru a/net/bridge/br_if.c b/net/bridge/br_if.c
--- a/net/bridge/br_if.c	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_if.c	Fri Apr 25 13:19:34 2003
@@ -19,7 +19,6 @@
 #include <linux/inetdevice.h>
 #include <linux/module.h>
 #include <linux/rtnetlink.h>
-#include <linux/brlock.h>
 #include <net/sock.h>
 #include <asm/uaccess.h>
 #include "br_private.h"
@@ -38,7 +37,14 @@
 	return 100;
 }
 
-/* called under BR_NETPROTO_LOCK and bridge lock */
+static void destroy_nbp(void *arg)
+{
+	struct net_bridge_port *p = arg;
+	dev_put(p->dev);
+	kfree(p);
+}
+
+/* called under bridge lock */
 static void del_nbp(struct net_bridge_port *p)
 {
 	struct net_device *dev = p->dev;
@@ -48,24 +54,22 @@
 	dev_set_promiscuity(dev, -1);
 	dev->br_port = NULL;
 
-	list_del(&p->list);
+	list_del_rcu(&p->list);
 
 	br_fdb_delete_by_port(p->br, p);
-	kfree(p);
-	dev_put(dev);
+
+	call_rcu(&p->rcu, destroy_nbp, p);
 }
 
 static void del_ifs(struct net_bridge *br)
 {
 	struct list_head *p, *n;
 
-	br_write_lock_bh(BR_NETPROTO_LOCK);
-	write_lock(&br->lock);
+	spin_lock_bh(&br->lock);
 	list_for_each_safe(p, n, &br->port_list) {
 		del_nbp(list_entry(p, struct net_bridge_port, list));
 	}
-	write_unlock(&br->lock);
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	spin_unlock_bh(&br->lock);
 }
 
 static struct net_bridge *new_nb(const char *name)
@@ -87,7 +91,7 @@
 	ether_setup(dev);
 	br_dev_setup(dev);
 
-	br->lock = RW_LOCK_UNLOCKED;
+	br->lock = SPIN_LOCK_UNLOCKED;
 	INIT_LIST_HEAD(&br->port_list);
 	br->hash_lock = RW_LOCK_UNLOCKED;
 
@@ -145,7 +149,7 @@
 	br_init_port(p);
 	p->state = BR_STATE_DISABLED;
 
-	list_add(&p->list, &br->port_list);
+	list_add_rcu(&p->list, &br->port_list);
 
 	return p;
 }
@@ -207,9 +211,9 @@
 		return -ELOOP;
 
 	dev_hold(dev);
-	write_lock_bh(&br->lock);
+	spin_lock_bh(&br->lock);
 	if ((p = new_nbp(br, dev)) == NULL) {
-		write_unlock_bh(&br->lock);
+		spin_unlock_bh(&br->lock);
 		dev_put(dev);
 		return -EXFULL;
 	}
@@ -220,7 +224,7 @@
 	br_fdb_insert(br, p, dev->dev_addr, 1);
 	if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP))
 		br_stp_enable_port(p);
-	write_unlock_bh(&br->lock);
+	spin_unlock_bh(&br->lock);
 
 	return 0;
 }
@@ -230,16 +234,14 @@
 	struct net_bridge_port *p;
 	int retval = 0;
 
-	br_write_lock_bh(BR_NETPROTO_LOCK);
-	write_lock(&br->lock);
+	spin_lock_bh(&br->lock);
 	if ((p = dev->br_port) == NULL || p->br != br)
 		retval = -EINVAL;
 	else {
 		del_nbp(p);
 		br_stp_recalculate_bridge_id(br);
 	}
-	write_unlock(&br->lock);
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	spin_unlock_bh(&br->lock);
 
 	return retval;
 }
@@ -263,11 +265,11 @@
 {
 	struct net_bridge_port *p;
 
-	read_lock(&br->lock);
-	list_for_each_entry(p, &br->port_list, list) {
+	rcu_read_lock();
+	list_for_each_entry_rcu(p, &br->port_list, list) {
 		ifindices[p->port_no] = p->dev->ifindex;
 	}
-	read_unlock(&br->lock);
+	rcu_read_unlock();
 }
 
 
diff -Nru a/net/bridge/br_input.c b/net/bridge/br_input.c
--- a/net/bridge/br_input.c	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_input.c	Fri Apr 25 13:19:34 2003
@@ -59,15 +59,16 @@
 
 	dest = skb->mac.ethernet->h_dest;
 
+	rcu_read_lock();
 	p = skb->dev->br_port;
-	if (p == NULL)
-		goto err_nolock;
+	smp_read_barrier_depends();
 
-	br = p->br;
-	read_lock(&br->lock);
-	if (skb->dev->br_port == NULL)
-		goto err;
+	if (p == NULL || p->state == BR_STATE_DISABLED) {
+		kfree(skb);
+		goto out;
+	}
 
+	br = p->br;
 	passedup = 0;
 	if (br->dev.flags & IFF_PROMISC) {
 		struct sk_buff *skb2;
@@ -105,35 +106,20 @@
 	br_flood_forward(br, skb, 0);
 
 out:
-	read_unlock(&br->lock);
-	return 0;
-
-err:
-	read_unlock(&br->lock);
-err_nolock:
-	kfree_skb(skb);
+	rcu_read_unlock();
 	return 0;
 }
 
 int br_handle_frame(struct sk_buff *skb)
 {
-	struct net_bridge *br;
 	unsigned char *dest;
 	struct net_bridge_port *p;
 
 	dest = skb->mac.ethernet->h_dest;
 
+	rcu_read_lock();
 	p = skb->dev->br_port;
-	if (p == NULL)
-		goto err_nolock;
-
-	br = p->br;
-	read_lock(&br->lock);
-	if (skb->dev->br_port == NULL)
-		goto err;
-
-	if (!(br->dev.flags & IFF_UP) ||
-	    p->state == BR_STATE_DISABLED)
+	if (p == NULL || p->state == BR_STATE_DISABLED)
 		goto err;
 
 	if (skb->mac.ethernet->h_source[0] & 1)
@@ -141,34 +127,30 @@
 
 	if (p->state == BR_STATE_LEARNING ||
 	    p->state == BR_STATE_FORWARDING)
-		br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
+		br_fdb_insert(p->br, p, skb->mac.ethernet->h_source, 0);
 
-	if (br->stp_enabled &&
+	if (p->br->stp_enabled &&
 	    !memcmp(dest, bridge_ula, 5) &&
-	    !(dest[5] & 0xF0))
-		goto handle_special_frame;
+	    !(dest[5] & 0xF0)) {
+		if (!dest[5]) 
+			br_stp_handle_bpdu(skb);
+		goto err;
+	}
 
 	if (p->state == BR_STATE_FORWARDING) {
 		if (br_should_route_hook && br_should_route_hook(&skb)) {
-			read_unlock(&br->lock);
+			rcu_read_unlock();
 			return -1;
 		}
 
 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
 			br_handle_frame_finish);
-		read_unlock(&br->lock);
+		rcu_read_unlock();
 		return 0;
 	}
 
 err:
-	read_unlock(&br->lock);
-err_nolock:
+	rcu_read_unlock();
 	kfree_skb(skb);
 	return 0;
-
-handle_special_frame:
-	read_unlock(&br->lock);
-	if (!dest[5]) 
-		br_stp_handle_bpdu(skb);
-	goto err_nolock;
 }
diff -Nru a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
--- a/net/bridge/br_ioctl.c	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_ioctl.c	Fri Apr 25 13:19:34 2003
@@ -71,8 +71,8 @@
 	{
 		struct __bridge_info b;
 
-	        read_lock(&br->lock);
 		memset(&b, 0, sizeof(struct __bridge_info));
+		rcu_read_lock();
 		memcpy(&b.designated_root, &br->designated_root, 8);
 		memcpy(&b.bridge_id, &br->bridge_id, 8);
 		b.root_path_cost = br->root_path_cost;
@@ -92,7 +92,7 @@
 		b.tcn_timer_value = timer_residue(&br->tcn_timer);
 		b.topology_change_timer_value = timer_residue(&br->topology_change_timer);
 		b.gc_timer_value = timer_residue(&br->gc_timer);
-	        read_unlock(&br->lock);
+	        rcu_read_unlock();
 
 		if (copy_to_user((void *)arg0, &b, sizeof(b)))
 			return -EFAULT;
@@ -119,27 +119,27 @@
 	}
 
 	case BRCTL_SET_BRIDGE_FORWARD_DELAY:
-		write_lock(&br->lock);
+		spin_lock_bh(&br->lock);
 		br->bridge_forward_delay = user_to_ticks(arg0);
 		if (br_is_root_bridge(br))
 			br->forward_delay = br->bridge_forward_delay;
-		write_unlock(&br->lock);
+		spin_unlock_bh(&br->lock);
 		return 0;
 
 	case BRCTL_SET_BRIDGE_HELLO_TIME:
-		write_lock(&br->lock);
+		spin_lock_bh(&br->lock);
 		br->bridge_hello_time = user_to_ticks(arg0);
 		if (br_is_root_bridge(br))
 			br->hello_time = br->bridge_hello_time;
-		write_unlock(&br->lock);
+		spin_unlock_bh(&br->lock);
 		return 0;
 
 	case BRCTL_SET_BRIDGE_MAX_AGE:
-		write_lock(&br->lock);
+		spin_lock_bh(&br->lock);
 		br->bridge_max_age = user_to_ticks(arg0);
 		if (br_is_root_bridge(br))
 			br->max_age = br->bridge_max_age;
-		write_unlock(&br->lock);
+		spin_unlock_bh(&br->lock);
 		return 0;
 
 	case BRCTL_SET_AGEING_TIME:
@@ -155,9 +155,9 @@
 		struct __port_info p;
 		struct net_bridge_port *pt;
 
-		read_lock(&br->lock);
+		rcu_read_lock();
 		if ((pt = br_get_port(br, arg1)) == NULL) {
-			read_unlock(&br->lock);
+			rcu_read_unlock();
 			return -EINVAL;
 		}
 
@@ -175,7 +175,7 @@
 		p.forward_delay_timer_value = timer_residue(&pt->forward_delay_timer);
 		p.hold_timer_value = timer_residue(&pt->hold_timer);
 
-		read_unlock(&br->lock);
+		rcu_read_unlock();
 
 		if (copy_to_user((void *)arg0, &p, sizeof(p)))
 			return -EFAULT;
@@ -188,9 +188,9 @@
 		return 0;
 
 	case BRCTL_SET_BRIDGE_PRIORITY:
-		write_lock(&br->lock);
+		spin_lock_bh(&br->lock);
 		br_stp_set_bridge_priority(br, arg0);
-		write_unlock(&br->lock);
+		spin_unlock_bh(&br->lock);
 		return 0;
 
 	case BRCTL_SET_PORT_PRIORITY:
@@ -198,12 +198,12 @@
 		struct net_bridge_port *p;
 		int ret = 0;
 
-		write_lock(&br->lock);
+		spin_lock_bh(&br->lock);
 		if ((p = br_get_port(br, arg0)) == NULL) 
 			ret = -EINVAL;
 		else
 			br_stp_set_port_priority(p, arg1);
-		write_unlock(&br->lock);
+		spin_unlock_bh(&br->lock);
 		return ret;
 	}
 
@@ -212,12 +212,12 @@
 		struct net_bridge_port *p;
 		int ret = 0;
 
-		write_lock(&br->lock);
+		spin_lock_bh(&br->lock);
 		if ((p = br_get_port(br, arg0)) == NULL)
 			ret = -EINVAL;
 		else
 			br_stp_set_path_cost(p, arg1);
-		write_unlock(&br->lock);
+		spin_unlock_bh(&br->lock);
 		return ret;
 	}
 
diff -Nru a/net/bridge/br_notify.c b/net/bridge/br_notify.c
--- a/net/bridge/br_notify.c	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_notify.c	Fri Apr 25 13:19:34 2003
@@ -41,10 +41,10 @@
 	switch (event) 
 	{
 	case NETDEV_CHANGEADDR:
-		write_lock_bh(&br->lock);
+		spin_lock_bh(&br->lock);
 		br_fdb_changeaddr(p, dev->dev_addr);
 		br_stp_recalculate_bridge_id(br);
-		write_unlock_bh(&br->lock);
+		spin_unlock_bh(&br->lock);
 		break;
 
 	case NETDEV_GOING_DOWN:
@@ -53,17 +53,17 @@
 
 	case NETDEV_DOWN:
 		if (br->dev.flags & IFF_UP) {
-			write_lock_bh(&br->lock);
+			spin_lock_bh(&br->lock);
 			br_stp_disable_port(p);
-			write_unlock_bh(&br->lock);
+			spin_unlock_bh(&br->lock);
 		}
 		break;
 
 	case NETDEV_UP:
 		if (!(br->dev.flags & IFF_UP)) {
-			write_lock_bh(&br->lock);
+			spin_lock_bh(&br->lock);
 			br_stp_enable_port(p);
-			write_unlock_bh(&br->lock);
+			spin_unlock_bh(&br->lock);
 		}
 		break;
 
diff -Nru a/net/bridge/br_private.h b/net/bridge/br_private.h
--- a/net/bridge/br_private.h	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_private.h	Fri Apr 25 13:19:34 2003
@@ -75,11 +75,13 @@
 	struct br_timer			forward_delay_timer;
 	struct br_timer			hold_timer;
 	struct br_timer			message_age_timer;
+
+	struct rcu_head			rcu;
 };
 
 struct net_bridge
 {
-	rwlock_t			lock;
+	spinlock_t			lock;
 	struct list_head		port_list;
 	struct net_device		dev;
 	struct net_device_stats		statistics;
@@ -137,10 +139,10 @@
 		   int is_local);
 
 /* br_forward.c */
-extern void br_deliver(struct net_bridge_port *to,
+extern void br_deliver(const struct net_bridge_port *to,
 		struct sk_buff *skb);
 extern int br_dev_queue_push_xmit(struct sk_buff *skb);
-extern void br_forward(struct net_bridge_port *to,
+extern void br_forward(const struct net_bridge_port *to,
 		struct sk_buff *skb);
 extern int br_forward_finish(struct sk_buff *skb);
 extern void br_flood_deliver(struct net_bridge *br,
diff -Nru a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
--- a/net/bridge/br_stp_bpdu.c	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_stp_bpdu.c	Fri Apr 25 13:19:34 2003
@@ -143,7 +143,7 @@
 	p = skb->dev->br_port;
 	br = p->br;
 
-	write_lock_bh(&br->lock);
+	spin_lock_bh(&br->lock);
 	if (p->state == BR_STATE_DISABLED 
 	    || !(br->dev.flags & IFF_UP)
 	    || !br->stp_enabled 
@@ -192,5 +192,5 @@
 		goto out;
 	}
  out:
-	write_unlock_bh(&br->lock);
+	spin_unlock_bh(&br->lock);
 }
diff -Nru a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
--- a/net/bridge/br_stp_if.c	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_stp_if.c	Fri Apr 25 13:19:34 2003
@@ -44,6 +44,7 @@
 	struct net_bridge_port *p;
 	struct timer_list *timer = &br->tick;
 
+	spin_lock_bh(&br->lock);
 	init_timer(timer);
 	timer->data = (unsigned long) br;
 	timer->function = br_tick;
@@ -59,6 +60,7 @@
 	}
 
 	br_timer_set(&br->gc_timer, jiffies);
+	spin_unlock_bh(&br->lock);
 }
 
 /* NO locks held */
@@ -66,7 +68,7 @@
 {
 	struct net_bridge_port *p;
 
-	write_lock(&br->lock);
+	spin_lock_bh(&br->lock);
 	br->topology_change = 0;
 	br->topology_change_detected = 0;
 	br_timer_clear(&br->hello_timer);
@@ -79,7 +81,7 @@
 		if (p->state != BR_STATE_DISABLED)
 			br_stp_disable_port(p);
 	}
-	write_unlock(&br->lock);
+	spin_unlock_bh(&br->lock);
 
 	del_timer_sync(&br->tick);
 }
diff -Nru a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c
--- a/net/bridge/br_stp_timer.c	Fri Apr 25 13:19:34 2003
+++ b/net/bridge/br_stp_timer.c	Fri Apr 25 13:19:34 2003
@@ -169,10 +169,10 @@
 {
 	struct net_bridge *br = (struct net_bridge *)__data;
 
-	read_lock(&br->lock);
-	br_check_timers(br);
-	read_unlock(&br->lock);
-
+	if (spin_trylock_bh(&br->lock)) {
+		br_check_timers(br);
+		spin_unlock_bh(&br->lock);
+	}
 	br->tick.expires = jiffies + 1;
 	add_timer(&br->tick);
 }

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2003-04-26  4:25 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-04-25 20:30 [PATCH 2.5.68] [BRIDGE] Use RCU for port table Stephen Hemminger
2003-04-26  4:25 ` David S. Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).