X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fbridge%2Fbr_if.c;h=b1211d5342f6cac5f43926a2c6a39f1651cb6761;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=3e52b610fb5a10e904aec5f4c68db634652bccba;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 3e52b610f..b1211d534 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "br_private.h" @@ -32,16 +33,15 @@ * 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; int err; strncpy(ifr.ifr_name, dev->name, IFNAMSIZ); - ifr.ifr_data = (void *) &ecmd; + ifr.ifr_data = (void __user *) &ecmd; old_fs = get_fs(); set_fs(KERNEL_DS); @@ -58,10 +58,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,56 +71,126 @@ static int br_initial_port_cost(struct net_device *dev) return 100; /* assume old 10Mbps */ } -static void destroy_nbp(void *arg) -{ - struct net_bridge_port *p = arg; - p->dev->br_port = NULL; +/* + * 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_device *dev = arg; + struct net_bridge_port *p; + struct net_bridge *br; - BUG_ON(timer_pending(&p->message_age_timer)); - BUG_ON(timer_pending(&p->forward_delay_timer)); - BUG_ON(timer_pending(&p->hold_timer)); + rtnl_lock(); + p = dev->br_port; + if (!p) + goto done; + br = p->br; + + if (netif_carrier_ok(dev)) + p->path_cost = port_cost(dev); + + if (br->dev->flags & IFF_UP) { + spin_lock_bh(&br->lock); + if (netif_carrier_ok(dev)) { + if (p->state == BR_STATE_DISABLED) + br_stp_enable_port(p); + } else { + if (p->state != BR_STATE_DISABLED) + br_stp_disable_port(p); + } + spin_unlock_bh(&br->lock); + } +done: + rtnl_unlock(); +} - dev_put(p->dev); +static void release_nbp(struct kobject *kobj) +{ + struct net_bridge_port *p + = container_of(kobj, struct net_bridge_port, kobj); kfree(p); } -/* called under bridge lock */ +static struct kobj_type brport_ktype = { +#ifdef CONFIG_SYSFS + .sysfs_ops = &brport_sysfs_ops, +#endif + .release = release_nbp, +}; + +static void destroy_nbp(struct net_bridge_port *p) +{ + struct net_device *dev = p->dev; + + p->br = NULL; + p->dev = NULL; + dev_put(dev); + + kobject_put(&p->kobj); +} + +static void destroy_nbp_rcu(struct rcu_head *head) +{ + struct net_bridge_port *p = + container_of(head, struct net_bridge_port, rcu); + destroy_nbp(p); +} + +/* Delete port(interface) from bridge is done in two steps. + * via RCU. First step, marks device as down. That deletes + * all the timers and stops new packets from flowing through. + * + * Final cleanup doesn't occur until after all CPU's finished + * processing packets. + * + * Protected from multiple admin operations by RTNL mutex + */ static void del_nbp(struct net_bridge_port *p) { + struct net_bridge *br = p->br; struct net_device *dev = p->dev; - br_stp_disable_port(p); + sysfs_remove_link(&br->ifobj, dev->name); dev_set_promiscuity(dev, -1); + cancel_delayed_work(&p->carrier_check); + + spin_lock_bh(&br->lock); + br_stp_disable_port(p); + spin_unlock_bh(&br->lock); + + br_fdb_delete_by_port(br, p); + list_del_rcu(&p->list); - br_fdb_delete_by_port(p->br, p); + rcu_assign_pointer(dev->br_port, NULL); - del_timer(&p->message_age_timer); - del_timer(&p->forward_delay_timer); - del_timer(&p->hold_timer); - - call_rcu(&p->rcu, destroy_nbp, p); + kobject_uevent(&p->kobj, KOBJ_REMOVE); + kobject_del(&p->kobj); + + call_rcu(&p->rcu, destroy_nbp_rcu); } +/* called with RTNL */ static void del_br(struct net_bridge *br) { - struct list_head *p, *n; + struct net_bridge_port *p, *n; - spin_lock_bh(&br->lock); - list_for_each_safe(p, n, &br->port_list) { - del_nbp(list_entry(p, struct net_bridge_port, list)); + list_for_each_entry_safe(p, n, &br->port_list, list) { + del_nbp(p); } - spin_unlock_bh(&br->lock); del_timer_sync(&br->gc_timer); + br_sysfs_delbr(br->dev); unregister_netdevice(br->dev); } -static struct net_bridge *new_nb(const char *name) +static struct net_device *new_bridge_dev(const char *name) { struct net_bridge *br; struct net_device *dev; @@ -135,17 +201,19 @@ static struct net_bridge *new_nb(const char *name) if (!dev) return NULL; - br = dev->priv; + br = netdev_priv(dev); br->dev = dev; - br->lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&br->lock); INIT_LIST_HEAD(&br->port_list); - br->hash_lock = RW_LOCK_UNLOCKED; + spin_lock_init(&br->hash_lock); br->bridge_id.prio[0] = 0x80; br->bridge_id.prio[1] = 0x00; - memset(br->bridge_id.addr, 0, ETH_ALEN); + memcpy(br->group_addr, br_group_address, ETH_ALEN); + + br->feature_mask = dev->features; br->stp_enabled = 0; br->designated_root = br->bridge_id; br->root_path_cost = 0; @@ -160,7 +228,7 @@ static struct net_bridge *new_nb(const char *name) br_stp_timer_init(br); - return br; + return dev; } /* find an available port number */ @@ -170,12 +238,11 @@ static int find_portno(struct net_bridge *br) struct net_bridge_port *p; unsigned long *inuse; - inuse = kmalloc(BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long), - GFP_ATOMIC); + inuse = kcalloc(BITS_TO_LONGS(BR_MAX_PORTS), sizeof(unsigned long), + GFP_KERNEL); if (!inuse) return -ENOMEM; - memset(inuse, 0, BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long)); set_bit(0, inuse); /* zero is reserved */ list_for_each_entry(p, &br->port_list, list) { set_bit(p->port_no, inuse); @@ -186,10 +253,9 @@ static int find_portno(struct net_bridge *br) return (index >= BR_MAX_PORTS) ? -EXFULL : index; } -/* called under bridge lock */ +/* 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; @@ -198,35 +264,57 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, if (index < 0) return ERR_PTR(index); - p = kmalloc(sizeof(*p), GFP_ATOMIC); + p = kzalloc(sizeof(*p), GFP_KERNEL); if (p == NULL) return ERR_PTR(-ENOMEM); - memset(p, 0, sizeof(*p)); 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, dev); + br_stp_port_timer_init(p); + + kobject_init(&p->kobj); + kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR); + p->kobj.ktype = &brport_ktype; + p->kobj.parent = &(dev->class_dev.kobj); + p->kobj.kset = NULL; return p; } int br_add_bridge(const char *name) { - struct net_bridge *br; + struct net_device *dev; int ret; - if ((br = new_nb(name)) == NULL) + dev = new_bridge_dev(name); + if (!dev) return -ENOMEM; - ret = register_netdev(br->dev); + rtnl_lock(); + if (strchr(dev->name, '%')) { + ret = dev_alloc_name(dev, dev->name); + if (ret < 0) { + free_netdev(dev); + goto out; + } + } + + ret = register_netdevice(dev); + if (ret) + goto out; + + ret = br_sysfs_addbr(dev); if (ret) - free_netdev(br->dev); + unregister_netdevice(dev); + out: + rtnl_unlock(); return ret; } @@ -251,16 +339,72 @@ int br_del_bridge(const char *name) } else - del_br(dev->priv); + del_br(netdev_priv(dev)); rtnl_unlock(); return ret; } +/* MTU of the bridge pseudo-device: ETH_DATA_LEN or the minimum of the ports */ +int br_min_mtu(const struct net_bridge *br) +{ + const struct net_bridge_port *p; + int mtu = 0; + + ASSERT_RTNL(); + + if (list_empty(&br->port_list)) + mtu = ETH_DATA_LEN; + else { + list_for_each_entry(p, &br->port_list, list) { + if (!mtu || p->dev->mtu < mtu) + mtu = p->dev->mtu; + } + } + return mtu; +} + +/* + * Recomputes features using slave's features + */ +void br_features_recompute(struct net_bridge *br) +{ + struct net_bridge_port *p; + unsigned long features, checksum; + + checksum = br->feature_mask & NETIF_F_ALL_CSUM ? NETIF_F_NO_CSUM : 0; + features = br->feature_mask & ~NETIF_F_ALL_CSUM; + + list_for_each_entry(p, &br->port_list, list) { + unsigned long feature = p->dev->features; + + if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM)) + checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM; + if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM)) + checksum ^= NETIF_F_HW_CSUM | NETIF_F_IP_CSUM; + if (!(feature & NETIF_F_IP_CSUM)) + checksum = 0; + + if (feature & NETIF_F_GSO) + feature |= NETIF_F_GSO_SOFTWARE; + feature |= NETIF_F_GSO; + + features &= feature; + } + + if (!(checksum & NETIF_F_ALL_CSUM)) + features &= ~NETIF_F_SG; + if (!(features & NETIF_F_SG)) + features &= ~NETIF_F_GSO_MASK; + + br->dev->features = features | checksum | NETIF_F_LLTX | + NETIF_F_GSO_ROBUST; +} + +/* called with RTNL */ int br_add_if(struct net_bridge *br, struct net_device *dev) { struct net_bridge_port *p; - unsigned long cost; int err = 0; if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER) @@ -269,78 +413,67 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) if (dev->hard_start_xmit == br_dev_xmit) return -ELOOP; - cost = br_initial_port_cost(dev); - - spin_lock_bh(&br->lock); if (dev->br_port != NULL) - err = -EBUSY; + return -EBUSY; - else if (IS_ERR(p = new_nbp(br, dev, cost))) - err = PTR_ERR(p); + p = new_nbp(br, dev); + if (IS_ERR(p)) + return PTR_ERR(p); - else if ((err = br_fdb_insert(br, p, dev->dev_addr, 1))) - destroy_nbp(p); - - else { - dev_set_promiscuity(dev, 1); + err = kobject_add(&p->kobj); + if (err) + goto err0; - list_add_rcu(&p->list, &br->port_list); + err = br_fdb_insert(br, p, dev->dev_addr); + if (err) + goto err1; - br_stp_recalculate_bridge_id(br); - if ((br->dev->flags & IFF_UP) && (dev->flags & IFF_UP)) - br_stp_enable_port(p); + err = br_sysfs_addif(p); + if (err) + goto err2; - } - spin_unlock_bh(&br->lock); - return err; -} + rcu_assign_pointer(dev->br_port, p); + dev_set_promiscuity(dev, 1); -int br_del_if(struct net_bridge *br, struct net_device *dev) -{ - struct net_bridge_port *p; - int err = 0; + list_add_rcu(&p->list, &br->port_list); 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); - } + br_stp_recalculate_bridge_id(br); + br_features_recompute(br); + schedule_delayed_work(&p->carrier_check, BR_PORT_DEBOUNCE); spin_unlock_bh(&br->lock); + dev_set_mtu(br->dev, br_min_mtu(br)); + kobject_uevent(&p->kobj, KOBJ_ADD); + + return 0; +err2: + br_fdb_delete_by_port(br, p); +err1: + kobject_del(&p->kobj); +err0: + kobject_put(&p->kobj); return err; } -int br_get_bridge_ifindices(int *indices, int num) +/* called with RTNL */ +int br_del_if(struct net_bridge *br, struct net_device *dev) { - struct net_device *dev; - int i = 0; - - read_lock(&dev_base_lock); - for (dev = dev_base; dev && i < num; dev = dev->next) { - if (dev->priv_flags & IFF_EBRIDGE) - indices[i++] = dev->ifindex; - } - read_unlock(&dev_base_lock); + struct net_bridge_port *p = dev->br_port; + + if (!p || p->br != br) + return -EINVAL; - return i; -} + del_nbp(p); -void br_get_port_ifindices(struct net_bridge *br, int *ifindices, int num) -{ - struct net_bridge_port *p; + spin_lock_bh(&br->lock); + br_stp_recalculate_bridge_id(br); + br_features_recompute(br); + spin_unlock_bh(&br->lock); - rcu_read_lock(); - list_for_each_entry_rcu(p, &br->port_list, list) { - if (p->port_no < num) - ifindices[p->port_no] = p->dev->ifindex; - } - rcu_read_unlock(); + return 0; } - void __exit br_cleanup_bridges(void) { struct net_device *dev, *nxt;