[Bridge] [PATCH 2.6.5] (4/9) bridge - use ethtool to get port speed

Stephen Hemminger shemminger at osdl.org
Tue Apr 13 15:24:40 PDT 2004


The bridge code needs to keep track of a cost estimate for each
port in the bridge.  Instead of a hack based on device name, try
and use ethtool to get port speed from device.  This has been tested
on e100 (uses ethtool_ops) and e1000 (does ethtool the hard way)
and dummy (no ethtool).

Need to export dev_ethtool() to allow bridge module to get to
it easily.

Code takes care to maintain same locking and semantics as if ioctl
was being done from application.

diff -Nru a/net/bridge/br_if.c b/net/bridge/br_if.c
--- a/net/bridge/br_if.c	Mon Apr 12 16:10:23 2004
+++ b/net/bridge/br_if.c	Mon Apr 12 16:10:23 2004
@@ -14,6 +14,8 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
 #include <linux/if_arp.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -25,18 +27,55 @@
 /* Limited to 256 ports because of STP protocol pdu */
 #define  BR_MAX_PORTS	256
 
+/*
+ * Determine initial path cost based on speed.
+ * using recommendations from 802.1d standard
+ *
+ * Need to simulate user ioctl because not all device's that support
+ * ethtool, use ethtool_ops.  Also, since driver might sleep need to
+ * not be holding any locks.
+ */
 static int br_initial_port_cost(struct net_device *dev)
 {
+
+	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+	struct ifreq ifr;
+	mm_segment_t old_fs;
+	int err;
+
+	strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
+	ifr.ifr_data = (void *) &ecmd;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	err = dev_ethtool(&ifr);
+	set_fs(old_fs);
+	
+	if (!err) {
+		switch(ecmd.speed) {
+		case SPEED_100:
+			return 19;
+		case SPEED_1000:
+			return 4;
+		case SPEED_10000:
+			return 2;
+		case SPEED_10:
+			return 100;
+		default:
+			pr_info("bridge: can't decode speed from %s: %d\n",
+				dev->name, ecmd.speed);
+			return 100;
+		}
+	}
+
+	/* Old silly heuristics based on name */
 	if (!strncmp(dev->name, "lec", 3))
 		return 7;
 
-	if (!strncmp(dev->name, "eth", 3))
-		return 100;			/* FIXME handle 100Mbps */
-
 	if (!strncmp(dev->name, "plip", 4))
 		return 2500;
 
-	return 100;
+	return 100;	/* assume old 10Mbps */
 }
 
 static void destroy_nbp(void *arg)
@@ -147,7 +186,9 @@
 }
 
 /* called under bridge lock */
-static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device *dev)
+static struct net_bridge_port *new_nbp(struct net_bridge *br, 
+				       struct net_device *dev,
+				       unsigned long cost)
 {
 	int index;
 	struct net_bridge_port *p;
@@ -162,16 +203,15 @@
 
 	memset(p, 0, sizeof(*p));
 	p->br = br;
+	dev_hold(dev);
 	p->dev = dev;
-	p->path_cost = br_initial_port_cost(dev);
+	p->path_cost = cost;
 	p->priority = 0x80;
 	dev->br_port = p;
 	p->port_no = index;
 	br_init_port(p);
 	p->state = BR_STATE_DISABLED;
 
-	list_add_rcu(&p->list, &br->port_list);
-
 	return p;
 }
 
@@ -216,13 +256,11 @@
 	return ret;
 }
 
-/* called under bridge lock */
 int br_add_if(struct net_bridge *br, struct net_device *dev)
 {
 	struct net_bridge_port *p;
-
-	if (dev->br_port != NULL)
-		return -EBUSY;
+	unsigned long cost;
+	int err = 0;
 
 	if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
 		return -EINVAL;
@@ -230,34 +268,46 @@
 	if (dev->hard_start_xmit == br_dev_xmit)
 		return -ELOOP;
 
-	dev_hold(dev);
-	p = new_nbp(br, dev);
-	if (IS_ERR(p)) {
-		dev_put(dev);
-		return PTR_ERR(p);
-	}
+	cost = br_initial_port_cost(dev);
 
-	dev_set_promiscuity(dev, 1);
+	spin_lock_bh(&br->lock);
+	if (dev->br_port != NULL)
+		err = -EBUSY;
+
+	else if (IS_ERR(p = new_nbp(br, dev, cost)))
+		err = PTR_ERR(p);
 
-	br_stp_recalculate_bridge_id(br);
-	br_fdb_insert(br, p, dev->dev_addr, 1);
-	if ((br->dev->flags & IFF_UP) && (dev->flags & IFF_UP))
-		br_stp_enable_port(p);
+	else {
+		dev_set_promiscuity(dev, 1);
 
-	return 0;
+		list_add_rcu(&p->list, &br->port_list);
+
+		br_stp_recalculate_bridge_id(br);
+		br_fdb_insert(br, p, dev->dev_addr, 1);
+		if ((br->dev->flags & IFF_UP) && (dev->flags & IFF_UP))
+			br_stp_enable_port(p);
+
+	}
+	spin_unlock_bh(&br->lock);
+	return err;
 }
 
-/* called under bridge lock */
 int br_del_if(struct net_bridge *br, struct net_device *dev)
 {
 	struct net_bridge_port *p;
+	int err = 0;
 
-	if ((p = dev->br_port) == NULL || p->br != br)
-		return -EINVAL;
+	spin_lock_bh(&br->lock);
+	p = dev->br_port;
+	if (!p || p->br != br) 
+		err = -EINVAL;
+	else {
+		del_nbp(p);
+		br_stp_recalculate_bridge_id(br);
+	}
+	spin_unlock_bh(&br->lock);
 
-	del_nbp(p);
-	br_stp_recalculate_bridge_id(br);
-	return 0;
+	return err;
 }
 
 int br_get_bridge_ifindices(int *indices, int num)
diff -Nru a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
--- a/net/bridge/br_ioctl.c	Mon Apr 12 16:10:23 2004
+++ b/net/bridge/br_ioctl.c	Mon Apr 12 16:10:23 2004
@@ -48,12 +48,10 @@
 		if (dev == NULL)
 			return -EINVAL;
 
-		spin_lock_bh(&br->lock);
 		if (cmd == BRCTL_ADD_IF)
 			ret = br_add_if(br, dev);
 		else
 			ret = br_del_if(br, dev);
-		spin_unlock_bh(&br->lock);
 
 		dev_put(dev);
 		return ret;
diff -Nru a/net/core/ethtool.c b/net/core/ethtool.c
--- a/net/core/ethtool.c	Mon Apr 12 16:10:23 2004
+++ b/net/core/ethtool.c	Mon Apr 12 16:10:23 2004
@@ -740,6 +740,7 @@
 	return -EOPNOTSUPP;
 }
 
+EXPORT_SYMBOL(dev_ethtool);
 EXPORT_SYMBOL(ethtool_op_get_link);
 EXPORT_SYMBOL(ethtool_op_get_sg);
 EXPORT_SYMBOL(ethtool_op_get_tso);



More information about the Bridge mailing list