Commit bab1deea authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller

[BRIDGE]: fix error handling for add interface to bridge

Refactor how the bridge code interacts with kobject system.
It should still use kobjects even if not using sysfs.
Fix the error unwind handling in br_add_if.
Signed-off-by: default avatarStephen Hemminger <shemminger@osdl.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5dce971a
......@@ -106,6 +106,20 @@ done:
rtnl_unlock();
}
static void release_nbp(struct kobject *kobj)
{
struct net_bridge_port *p
= container_of(kobj, struct net_bridge_port, kobj);
kfree(p);
}
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;
......@@ -114,7 +128,7 @@ static void destroy_nbp(struct net_bridge_port *p)
p->dev = NULL;
dev_put(dev);
br_sysfs_freeif(p);
kobject_put(&p->kobj);
}
static void destroy_nbp_rcu(struct rcu_head *head)
......@@ -138,6 +152,8 @@ 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);
dev_set_promiscuity(dev, -1);
cancel_delayed_work(&p->carrier_check);
......@@ -152,6 +168,8 @@ static void del_nbp(struct net_bridge_port *p)
rcu_assign_pointer(dev->br_port, NULL);
kobject_del(&p->kobj);
call_rcu(&p->rcu, destroy_nbp_rcu);
}
......@@ -161,7 +179,6 @@ static void del_br(struct net_bridge *br)
struct net_bridge_port *p, *n;
list_for_each_entry_safe(p, n, &br->port_list, list) {
br_sysfs_removeif(p);
del_nbp(p);
}
......@@ -261,6 +278,11 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
INIT_WORK(&p->carrier_check, port_carrier_check, dev);
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;
}
......@@ -388,15 +410,22 @@ 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)))
p = new_nbp(br, dev);
if (IS_ERR(p))
return PTR_ERR(p);
if ((err = br_fdb_insert(br, p, dev->dev_addr)))
destroy_nbp(p);
err = kobject_add(&p->kobj);
if (err)
goto err0;
err = br_fdb_insert(br, p, dev->dev_addr);
if (err)
goto err1;
err = br_sysfs_addif(p);
if (err)
goto err2;
else if ((err = br_sysfs_addif(p)))
del_nbp(p);
else {
rcu_assign_pointer(dev->br_port, p);
dev_set_promiscuity(dev, 1);
......@@ -405,14 +434,19 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
spin_lock_bh(&br->lock);
br_stp_recalculate_bridge_id(br);
br_features_recompute(br);
if ((br->dev->flags & IFF_UP)
&& (dev->flags & IFF_UP) && netif_carrier_ok(dev))
br_stp_enable_port(p);
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;
}
......@@ -424,7 +458,6 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
if (!p || p->br != br)
return -EINVAL;
br_sysfs_removeif(p);
del_nbp(p);
spin_lock_bh(&br->lock);
......
......@@ -232,9 +232,8 @@ extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
#ifdef CONFIG_SYSFS
/* br_sysfs_if.c */
extern struct sysfs_ops brport_sysfs_ops;
extern int br_sysfs_addif(struct net_bridge_port *p);
extern void br_sysfs_removeif(struct net_bridge_port *p);
extern void br_sysfs_freeif(struct net_bridge_port *p);
/* br_sysfs_br.c */
extern int br_sysfs_addbr(struct net_device *dev);
......@@ -243,8 +242,6 @@ extern void br_sysfs_delbr(struct net_device *dev);
#else
#define br_sysfs_addif(p) (0)
#define br_sysfs_removeif(p) do { } while(0)
#define br_sysfs_freeif(p) kfree(p)
#define br_sysfs_addbr(dev) (0)
#define br_sysfs_delbr(dev) do { } while(0)
#endif /* CONFIG_SYSFS */
......
......@@ -195,23 +195,11 @@ static ssize_t brport_store(struct kobject * kobj,
return ret;
}
/* called from kobject_put when port ref count goes to zero. */
static void brport_release(struct kobject *kobj)
{
kfree(container_of(kobj, struct net_bridge_port, kobj));
}
static struct sysfs_ops brport_sysfs_ops = {
struct sysfs_ops brport_sysfs_ops = {
.show = brport_show,
.store = brport_store,
};
static struct kobj_type brport_ktype = {
.sysfs_ops = &brport_sysfs_ops,
.release = brport_release,
};
/*
* Add sysfs entries to ethernet device added to a bridge.
* Creates a brport subdirectory with bridge attributes.
......@@ -223,17 +211,6 @@ int br_sysfs_addif(struct net_bridge_port *p)
struct brport_attribute **a;
int err;
ASSERT_RTNL();
kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR);
p->kobj.ktype = &brport_ktype;
p->kobj.parent = &(p->dev->class_dev.kobj);
p->kobj.kset = NULL;
err = kobject_add(&p->kobj);
if(err)
goto out1;
err = sysfs_create_link(&p->kobj, &br->dev->class_dev.kobj,
SYSFS_BRIDGE_PORT_LINK);
if (err)
......@@ -245,28 +222,7 @@ int br_sysfs_addif(struct net_bridge_port *p)
goto out2;
}
err = sysfs_create_link(&br->ifobj, &p->kobj, p->dev->name);
if (err)
goto out2;
kobject_uevent(&p->kobj, KOBJ_ADD);
return 0;
out2:
kobject_del(&p->kobj);
out1:
err= sysfs_create_link(&br->ifobj, &p->kobj, p->dev->name);
out2:
return err;
}
void br_sysfs_removeif(struct net_bridge_port *p)
{
pr_debug("br_sysfs_removeif\n");
sysfs_remove_link(&p->br->ifobj, p->dev->name);
kobject_uevent(&p->kobj, KOBJ_REMOVE);
kobject_del(&p->kobj);
}
void br_sysfs_freeif(struct net_bridge_port *p)
{
pr_debug("br_sysfs_freeif\n");
kobject_put(&p->kobj);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment