X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=net%2Fbridge%2Fbr_if.c;h=45f3f8871149cbd2af0a81552b378f81d49128f2;hb=cfb478da70f2213520866c84cb0aea0d7c0e1148;hp=f55ef682ef846e979b7e1b24569399a7c2fbd7da;hpb=576a30eb6453439b3c37ba24455ac7090c247b5a;p=safe%2Fjmp%2Flinux-2.6 diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index f55ef68..45f3f88 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_if.c,v 1.7 2001/12/24 00:59:55 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version @@ -15,12 +13,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include "br_private.h" @@ -29,35 +29,24 @@ * 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. + * Since driver might sleep need to not be holding any locks. */ 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 __user *) &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; + if (dev->ethtool_ops && dev->ethtool_ops->get_settings) { + struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET, }; + + if (!dev->ethtool_ops->get_settings(dev, &ecmd)) { + switch(ecmd.speed) { + case SPEED_10000: + return 2; + case SPEED_1000: + return 4; + case SPEED_100: + return 19; + case SPEED_10: + return 100; + } } } @@ -77,22 +66,15 @@ static int port_cost(struct net_device *dev) * 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) +void br_port_carrier_check(struct net_bridge_port *p) { - struct net_device *dev = arg; - struct net_bridge_port *p; - struct net_bridge *br; - - rtnl_lock(); - p = dev->br_port; - if (!p) - goto done; - br = p->br; + struct net_device *dev = p->dev; + struct net_bridge *br = p->br; if (netif_carrier_ok(dev)) p->path_cost = port_cost(dev); - if (br->dev->flags & IFF_UP) { + if (netif_running(br->dev)) { spin_lock_bh(&br->lock); if (netif_carrier_ok(dev)) { if (p->state == BR_STATE_DISABLED) @@ -103,8 +85,6 @@ static void port_carrier_check(void *arg) } spin_unlock_bh(&br->lock); } -done: - rtnl_unlock(); } static void release_nbp(struct kobject *kobj) @@ -153,30 +133,33 @@ static void del_nbp(struct net_bridge_port *p) struct net_bridge *br = p->br; struct net_device *dev = p->dev; - sysfs_remove_link(&br->ifobj, dev->name); + 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); + br_ifinfo_notify(RTM_DELLINK, p); + + br_fdb_delete_by_port(br, p, 1); list_del_rcu(&p->list); rcu_assign_pointer(dev->br_port, NULL); + br_multicast_del_port(p); + kobject_uevent(&p->kobj, KOBJ_REMOVE); kobject_del(&p->kobj); + br_netpoll_disable(br, dev); call_rcu(&p->rcu, destroy_nbp_rcu); } /* called with RTNL */ -static void del_br(struct net_bridge *br) +static void del_br(struct net_bridge *br, struct list_head *head) { struct net_bridge_port *p, *n; @@ -184,26 +167,35 @@ static void del_br(struct net_bridge *br) del_nbp(p); } + br_netpoll_cleanup(br->dev); + del_timer_sync(&br->gc_timer); br_sysfs_delbr(br->dev); - unregister_netdevice(br->dev); + unregister_netdevice_queue(br->dev, head); } -static struct net_device *new_bridge_dev(const char *name) +static struct net_device *new_bridge_dev(struct net *net, const char *name) { struct net_bridge *br; struct net_device *dev; dev = alloc_netdev(sizeof(struct net_bridge), name, br_dev_setup); - + if (!dev) return NULL; + dev_net_set(dev, net); br = netdev_priv(dev); br->dev = dev; + br->stats = alloc_percpu(struct br_cpu_netstats); + if (!br->stats) { + free_netdev(dev); + return NULL; + } + spin_lock_init(&br->lock); INIT_LIST_HEAD(&br->port_list); spin_lock_init(&br->hash_lock); @@ -214,7 +206,7 @@ static struct net_device *new_bridge_dev(const char *name) memcpy(br->group_addr, br_group_address, ETH_ALEN); br->feature_mask = dev->features; - br->stp_enabled = 0; + br->stp_enabled = BR_NO_STP; br->designated_root = br->bridge_id; br->root_path_cost = 0; br->root_port = 0; @@ -224,9 +216,11 @@ static struct net_device *new_bridge_dev(const char *name) br->topology_change = 0; br->topology_change_detected = 0; br->ageing_time = 300 * HZ; - INIT_LIST_HEAD(&br->age_list); + + br_netfilter_rtable_init(br); br_stp_timer_init(br); + br_multicast_init(br); return dev; } @@ -254,12 +248,12 @@ static int find_portno(struct net_bridge *br) } /* called with RTNL but without bridge lock */ -static struct net_bridge_port *new_nbp(struct net_bridge *br, +static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device *dev) { int index; struct net_bridge_port *p; - + index = find_portno(br); if (index < 0) return ERR_PTR(index); @@ -272,43 +266,42 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, dev_hold(dev); p->dev = dev; p->path_cost = port_cost(dev); - p->priority = 0x8000 >> BR_PORT_BITS; + p->priority = 0x8000 >> BR_PORT_BITS; p->port_no = index; + p->flags = 0; 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; + br_multicast_add_port(p); return p; } -int br_add_bridge(const char *name) +static struct device_type br_type = { + .name = "bridge", +}; + +int br_add_bridge(struct net *net, const char *name) { struct net_device *dev; int ret; - dev = new_bridge_dev(name); - if (!dev) + dev = new_bridge_dev(net, name); + if (!dev) return -ENOMEM; rtnl_lock(); if (strchr(dev->name, '%')) { ret = dev_alloc_name(dev, dev->name); - if (ret < 0) { - free_netdev(dev); - goto out; - } + if (ret < 0) + goto out_free; } + SET_NETDEV_DEVTYPE(dev, &br_type); + ret = register_netdevice(dev); if (ret) - goto out; + goto out_free; ret = br_sysfs_addbr(dev); if (ret) @@ -316,16 +309,20 @@ int br_add_bridge(const char *name) out: rtnl_unlock(); return ret; + +out_free: + free_netdev(dev); + goto out; } -int br_del_bridge(const char *name) +int br_del_bridge(struct net *net, const char *name) { struct net_device *dev; int ret = 0; rtnl_lock(); - dev = __dev_get_by_name(name); - if (dev == NULL) + dev = __dev_get_by_name(net, name); + if (dev == NULL) ret = -ENXIO; /* Could not find device */ else if (!(dev->priv_flags & IFF_EBRIDGE)) { @@ -336,10 +333,10 @@ int br_del_bridge(const char *name) else if (dev->flags & IFF_UP) { /* Not shutdown yet. */ ret = -EBUSY; - } + } - else - del_br(netdev_priv(dev)); + else + del_br(netdev_priv(dev), NULL); rtnl_unlock(); return ret; @@ -370,30 +367,21 @@ int br_min_mtu(const struct net_bridge *br) void br_features_recompute(struct net_bridge *br) { struct net_bridge_port *p; - unsigned long features, checksum; + unsigned long features, mask; - 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; + features = mask = br->feature_mask; + if (list_empty(&br->port_list)) + goto done; - if (feature & NETIF_F_GSO) - feature |= NETIF_F_TSO; - feature |= NETIF_F_GSO; + features &= ~NETIF_F_ONE_FOR_ALL; - features &= feature; + list_for_each_entry(p, &br->port_list, list) { + features = netdev_increment_features(features, + p->dev->features, mask); } - br->dev->features = features | checksum | NETIF_F_LLTX | - NETIF_F_GSO_ROBUST; +done: + br->dev->features = netdev_fix_features(features, NULL); } /* called with RTNL */ @@ -402,24 +390,37 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) struct net_bridge_port *p; int err = 0; - if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER) + /* Don't allow bridging non-ethernet like devices */ + if ((dev->flags & IFF_LOOPBACK) || + dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN) return -EINVAL; - if (dev->hard_start_xmit == br_dev_xmit) + /* No bridging of bridges */ + if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit) return -ELOOP; + /* Device is already being bridged */ if (dev->br_port != NULL) return -EBUSY; + /* No bridging devices that dislike that (e.g. wireless) */ + if (dev->priv_flags & IFF_DONT_BRIDGE) + return -EOPNOTSUPP; + p = new_nbp(br, dev); if (IS_ERR(p)) return PTR_ERR(p); - err = kobject_add(&p->kobj); + err = dev_set_promiscuity(dev, 1); + if (err) + goto put_back; + + err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj), + SYSFS_BRIDGE_PORT_ATTR); if (err) goto err0; - err = br_fdb_insert(br, p, dev->dev_addr); + err = br_fdb_insert(br, p, dev->dev_addr); if (err) goto err1; @@ -428,26 +429,38 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) goto err2; rcu_assign_pointer(dev->br_port, p); - dev_set_promiscuity(dev, 1); + dev_disable_lro(dev); list_add_rcu(&p->list, &br->port_list); spin_lock_bh(&br->lock); br_stp_recalculate_bridge_id(br); br_features_recompute(br); - schedule_delayed_work(&p->carrier_check, BR_PORT_DEBOUNCE); + + if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && + (br->dev->flags & IFF_UP)) + br_stp_enable_port(p); spin_unlock_bh(&br->lock); + br_ifinfo_notify(RTM_NEWLINK, p); + dev_set_mtu(br->dev, br_min_mtu(br)); + kobject_uevent(&p->kobj, KOBJ_ADD); + br_netpoll_enable(br, dev); + return 0; err2: - br_fdb_delete_by_port(br, p); + br_fdb_delete_by_port(br, p, 1); err1: - kobject_del(&p->kobj); -err0: kobject_put(&p->kobj); + p = NULL; /* kobject_put frees */ +err0: + dev_set_promiscuity(dev, -1); +put_back: + dev_put(dev); + kfree(p); return err; } @@ -455,8 +468,8 @@ err0: int br_del_if(struct net_bridge *br, struct net_device *dev) { struct net_bridge_port *p = dev->br_port; - - if (!p || p->br != br) + + if (!p || p->br != br) return -EINVAL; del_nbp(p); @@ -469,16 +482,17 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) return 0; } -void __exit br_cleanup_bridges(void) +void __net_exit br_net_exit(struct net *net) { - struct net_device *dev, *nxt; + struct net_device *dev; + LIST_HEAD(list); rtnl_lock(); - for (dev = dev_base; dev; dev = nxt) { - nxt = dev->next; + for_each_netdev(net, dev) if (dev->priv_flags & IFF_EBRIDGE) - del_br(dev->priv); - } + del_br(netdev_priv(dev), &list); + + unregister_netdevice_many(&list); rtnl_unlock(); }