#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtnetlink.h>
+#include <linux/if_ether.h>
#include <net/sock.h>
#include "br_private.h"
* 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);
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;
}
}
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;
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;
br_stp_timer_init(br);
- return br;
+ return dev;
}
/* find an available port number */
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);
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;
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)
- free_netdev(br->dev);
+ goto out;
+
+ ret = br_sysfs_addbr(dev);
+ if (ret)
+ unregister_netdevice(dev);
+ out:
+ rtnl_unlock();
return ret;
}
}
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;
+
+ features = br->feature_mask &~ NETIF_F_IP_CSUM;
+ checksum = br->feature_mask & NETIF_F_IP_CSUM;
+
+ list_for_each_entry(p, &br->port_list, list) {
+ if (!(p->dev->features
+ & (NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM)))
+ checksum = 0;
+ features &= p->dev->features;
+ }
+
+ br->dev->features = features | checksum | NETIF_F_LLTX;
+}
+
+/* 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)
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;