diff options
Diffstat (limited to 'net/bridge/br_if.c')
-rw-r--r-- | net/bridge/br_if.c | 49 |
1 files changed, 38 insertions, 11 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 975abe254b7..e6b8bb51d61 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -32,9 +32,8 @@ * 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) +static int port_cost(struct net_device *dev) { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; struct ifreq ifr; mm_segment_t old_fs; @@ -58,10 +57,6 @@ static int br_initial_port_cost(struct net_device *dev) 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; } } @@ -75,6 +70,35 @@ static int br_initial_port_cost(struct net_device *dev) return 100; /* assume old 10Mbps */ } + +/* + * Check for port carrier transistions. + * Called from work queue to allow for calling functions that + * might sleep (such as speed check), and to debounce. + */ +static void port_carrier_check(void *arg) +{ + struct net_bridge_port *p = arg; + + rtnl_lock(); + if (netif_carrier_ok(p->dev)) { + u32 cost = port_cost(p->dev); + + spin_lock_bh(&p->br->lock); + if (p->state == BR_STATE_DISABLED) { + p->path_cost = cost; + br_stp_enable_port(p); + } + spin_unlock_bh(&p->br->lock); + } else { + spin_lock_bh(&p->br->lock); + if (p->state != BR_STATE_DISABLED) + br_stp_disable_port(p); + spin_unlock_bh(&p->br->lock); + } + rtnl_unlock(); +} + static void destroy_nbp(struct net_bridge_port *p) { struct net_device *dev = p->dev; @@ -102,6 +126,9 @@ static void del_nbp(struct net_bridge_port *p) dev->br_port = NULL; dev_set_promiscuity(dev, -1); + cancel_delayed_work(&p->carrier_check); + flush_scheduled_work(); + spin_lock_bh(&br->lock); br_stp_disable_port(p); spin_unlock_bh(&br->lock); @@ -195,10 +222,9 @@ static int find_portno(struct net_bridge *br) return (index >= BR_MAX_PORTS) ? -EXFULL : index; } -/* called with RTNL */ +/* called with RTNL but without bridge lock */ static struct net_bridge_port *new_nbp(struct net_bridge *br, - struct net_device *dev, - unsigned long cost) + struct net_device *dev) { int index; struct net_bridge_port *p; @@ -215,12 +241,13 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, p->br = br; dev_hold(dev); p->dev = dev; - p->path_cost = cost; + p->path_cost = port_cost(dev); p->priority = 0x8000 >> BR_PORT_BITS; dev->br_port = p; p->port_no = index; br_init_port(p); p->state = BR_STATE_DISABLED; + INIT_WORK(&p->carrier_check, port_carrier_check, p); kobject_init(&p->kobj); return p; @@ -351,7 +378,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) if (dev->br_port != NULL) return -EBUSY; - if (IS_ERR(p = new_nbp(br, dev, br_initial_port_cost(dev)))) + if (IS_ERR(p = new_nbp(br, dev))) return PTR_ERR(p); if ((err = br_fdb_insert(br, p, dev->dev_addr))) |