Commit 3d702922 authored by Stephen Hemminger's avatar Stephen Hemminger

[BRIDGE]: Add sysfs support.

parent f1d65789
...@@ -17,6 +17,12 @@ ...@@ -17,6 +17,12 @@
#include <linux/types.h> #include <linux/types.h>
#define SYSFS_BRIDGE_ATTR "bridge"
#define SYSFS_BRIDGE_FDB "brforward"
#define SYSFS_BRIDGE_PORT_SUBDIR "brif"
#define SYSFS_BRIDGE_PORT_ATTR "brport"
#define SYSFS_BRIDGE_PORT_LINK "bridge"
#define BRCTL_VERSION 1 #define BRCTL_VERSION 1
#define BRCTL_GET_VERSION 0 #define BRCTL_GET_VERSION 0
......
...@@ -8,6 +8,8 @@ bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ ...@@ -8,6 +8,8 @@ bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \ br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o br_stp_if.o br_stp_timer.o
bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
...@@ -33,6 +33,8 @@ static int __init br_init(void) ...@@ -33,6 +33,8 @@ static int __init br_init(void)
{ {
br_fdb_init(); br_fdb_init();
br_sysfs_init();
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
if (br_netfilter_init()) if (br_netfilter_init())
return 1; return 1;
...@@ -67,6 +69,7 @@ static void __exit br_deinit(void) ...@@ -67,6 +69,7 @@ static void __exit br_deinit(void)
#endif #endif
br_handle_frame_hook = NULL; br_handle_frame_hook = NULL;
br_sysfs_fini();
br_fdb_fini(); br_fdb_fini();
} }
......
...@@ -81,8 +81,11 @@ static void destroy_nbp(void *arg) ...@@ -81,8 +81,11 @@ static void destroy_nbp(void *arg)
struct net_device *dev = p->dev; struct net_device *dev = p->dev;
dev->br_port = NULL; dev->br_port = NULL;
p->br = NULL;
p->dev = NULL;
dev_put(dev); dev_put(dev);
kfree(p);
br_sysfs_freeif(p);
} }
/* called with RTNL */ /* called with RTNL */
...@@ -111,14 +114,16 @@ static void del_nbp(struct net_bridge_port *p) ...@@ -111,14 +114,16 @@ static void del_nbp(struct net_bridge_port *p)
/* called with RTNL */ /* called with RTNL */
static void del_br(struct net_bridge *br) static void del_br(struct net_bridge *br)
{ {
struct list_head *p, *n; struct net_bridge_port *p, *n;
list_for_each_safe(p, n, &br->port_list) { list_for_each_entry_safe(p, n, &br->port_list, list) {
del_nbp(list_entry(p, struct net_bridge_port, list)); br_sysfs_removeif(p);
del_nbp(p);
} }
del_timer_sync(&br->gc_timer); del_timer_sync(&br->gc_timer);
br_sysfs_delbr(br->dev);
unregister_netdevice(br->dev); unregister_netdevice(br->dev);
} }
...@@ -210,6 +215,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, ...@@ -210,6 +215,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
p->port_no = index; p->port_no = index;
br_init_port(p); br_init_port(p);
p->state = BR_STATE_DISABLED; p->state = BR_STATE_DISABLED;
kobject_init(&p->kobj);
return p; return p;
} }
...@@ -223,10 +229,37 @@ int br_add_bridge(const char *name) ...@@ -223,10 +229,37 @@ int br_add_bridge(const char *name)
if (!dev) if (!dev)
return -ENOMEM; return -ENOMEM;
ret = register_netdev(dev); rtnl_lock();
if (strchr(dev->name, '%')) {
ret = dev_alloc_name(dev, dev->name);
if (ret < 0)
goto err1;
}
ret = register_netdevice(dev);
if (ret) if (ret)
free_netdev(dev); goto err2;
/* network device kobject is not setup until
* after rtnl_unlock does it's hotplug magic.
* so hold reference to avoid race.
*/
dev_hold(dev);
rtnl_unlock();
ret = br_sysfs_addbr(dev);
dev_put(dev);
if (ret)
unregister_netdev(dev);
out:
return ret; return ret;
err2:
free_netdev(dev);
err1:
rtnl_unlock();
goto out;
} }
int br_del_bridge(const char *name) int br_del_bridge(const char *name)
...@@ -277,6 +310,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -277,6 +310,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if ((err = br_fdb_insert(br, p, dev->dev_addr, 1))) if ((err = br_fdb_insert(br, p, dev->dev_addr, 1)))
destroy_nbp(p); destroy_nbp(p);
else if ((err = br_sysfs_addif(p)))
del_nbp(p);
else { else {
dev_set_promiscuity(dev, 1); dev_set_promiscuity(dev, 1);
...@@ -300,6 +335,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) ...@@ -300,6 +335,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
if (!p || p->br != br) if (!p || p->br != br)
return -EINVAL; return -EINVAL;
br_sysfs_removeif(p);
del_nbp(p); del_nbp(p);
spin_lock_bh(&br->lock); spin_lock_bh(&br->lock);
......
...@@ -76,6 +76,7 @@ struct net_bridge_port ...@@ -76,6 +76,7 @@ struct net_bridge_port
struct timer_list forward_delay_timer; struct timer_list forward_delay_timer;
struct timer_list hold_timer; struct timer_list hold_timer;
struct timer_list message_age_timer; struct timer_list message_age_timer;
struct kobject kobj;
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -110,6 +111,7 @@ struct net_bridge ...@@ -110,6 +111,7 @@ struct net_bridge
struct timer_list tcn_timer; struct timer_list tcn_timer;
struct timer_list topology_change_timer; struct timer_list topology_change_timer;
struct timer_list gc_timer; struct timer_list gc_timer;
struct kobject ifobj;
}; };
extern struct notifier_block br_device_notifier; extern struct notifier_block br_device_notifier;
...@@ -198,6 +200,7 @@ extern void br_stp_set_port_priority(struct net_bridge_port *p, ...@@ -198,6 +200,7 @@ extern void br_stp_set_port_priority(struct net_bridge_port *p,
u8 newprio); u8 newprio);
extern void br_stp_set_path_cost(struct net_bridge_port *p, extern void br_stp_set_path_cost(struct net_bridge_port *p,
u32 path_cost); u32 path_cost);
extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);
/* br_stp_bpdu.c */ /* br_stp_bpdu.c */
extern int br_stp_handle_bpdu(struct sk_buff *skb); extern int br_stp_handle_bpdu(struct sk_buff *skb);
...@@ -207,4 +210,28 @@ extern void br_stp_timer_init(struct net_bridge *br); ...@@ -207,4 +210,28 @@ extern void br_stp_timer_init(struct net_bridge *br);
extern void br_stp_port_timer_init(struct net_bridge_port *p); extern void br_stp_port_timer_init(struct net_bridge_port *p);
extern unsigned long br_timer_value(const struct timer_list *timer); extern unsigned long br_timer_value(const struct timer_list *timer);
#ifdef CONFIG_SYSFS
/* br_sysfs_if.c */
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 struct subsystem bridge_subsys;
extern void br_sysfs_init(void);
extern void br_sysfs_fini(void);
extern int br_sysfs_addbr(struct net_device *dev);
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_init() do { } while(0)
#define br_sysfs_fini() do { } while(0)
#define br_sysfs_addbr(dev) (0)
#define br_sysfs_delbr(dev) do { } while(0)
#endif /* CONFIG_SYSFS */
#endif #endif
...@@ -212,3 +212,11 @@ void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost) ...@@ -212,3 +212,11 @@ void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost)
br_configuration_update(p->br); br_configuration_update(p->br);
br_port_state_selection(p->br); br_port_state_selection(p->br);
} }
ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id)
{
return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
id->prio[0], id->prio[1],
id->addr[0], id->addr[1], id->addr[2],
id->addr[3], id->addr[4], id->addr[5]);
}
This diff is collapsed.
/*
* Sysfs attributes of bridge ports
* Linux ethernet bridge
*
* Authors:
* Stephen Hemminger <shemminger@osdl.org>
*
* 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
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
#include "br_private.h"
struct brport_attribute {
struct attribute attr;
ssize_t (*show)(struct net_bridge_port *, char *);
ssize_t (*store)(struct net_bridge_port *, unsigned long);
};
#define BRPORT_ATTR(_name,_mode,_show,_store) \
struct brport_attribute brport_attr_##_name = { \
.attr = {.name = __stringify(_name), \
.mode = _mode, \
.owner = THIS_MODULE, }, \
.show = _show, \
.store = _store, \
};
static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->path_cost);
}
static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v)
{
br_stp_set_path_cost(p, v);
return 0;
}
static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR,
show_path_cost, store_path_cost);
static ssize_t show_priority(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->priority);
}
static ssize_t store_priority(struct net_bridge_port *p, unsigned long v)
{
if (v >= (1<<(16-BR_PORT_BITS)))
return -ERANGE;
br_stp_set_port_priority(p, v);
return 0;
}
static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR,
show_priority, store_priority);
static ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
{
return br_show_bridge_id(buf, &p->designated_root);
}
static BRPORT_ATTR(designated_root, S_IRUGO, show_designated_root, NULL);
static ssize_t show_designated_bridge(struct net_bridge_port *p, char *buf)
{
return br_show_bridge_id(buf, &p->designated_bridge);
}
static BRPORT_ATTR(designated_bridge, S_IRUGO, show_designated_bridge, NULL);
static ssize_t show_designated_port(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->designated_port);
}
static BRPORT_ATTR(designated_port, S_IRUGO, show_designated_port, NULL);
static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->designated_cost);
}
static BRPORT_ATTR(designated_cost, S_IRUGO, show_designated_cost, NULL);
static ssize_t show_port_id(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "0x%x\n", p->port_id);
}
static BRPORT_ATTR(port_id, S_IRUGO, show_port_id, NULL);
static ssize_t show_port_no(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "0x%x\n", p->port_no);
}
static BRPORT_ATTR(port_no, S_IRUGO, show_port_no, NULL);
static ssize_t show_change_ack(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->topology_change_ack);
}
static BRPORT_ATTR(change_ack, S_IRUGO, show_change_ack, NULL);
static ssize_t show_config_pending(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->config_pending);
}
static BRPORT_ATTR(config_pending, S_IRUGO, show_config_pending, NULL);
static ssize_t show_port_state(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->state);
}
static BRPORT_ATTR(state, S_IRUGO, show_port_state, NULL);
static ssize_t show_message_age_timer(struct net_bridge_port *p,
char *buf)
{
return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer));
}
static BRPORT_ATTR(message_age_timer, S_IRUGO, show_message_age_timer, NULL);
static ssize_t show_forward_delay_timer(struct net_bridge_port *p,
char *buf)
{
return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer));
}
static BRPORT_ATTR(forward_delay_timer, S_IRUGO, show_forward_delay_timer, NULL);
static ssize_t show_hold_timer(struct net_bridge_port *p,
char *buf)
{
return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer));
}
static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
static struct brport_attribute *brport_attrs[] = {
&brport_attr_path_cost,
&brport_attr_priority,
&brport_attr_port_id,
&brport_attr_port_no,
&brport_attr_designated_root,
&brport_attr_designated_bridge,
&brport_attr_designated_port,
&brport_attr_designated_cost,
&brport_attr_state,
&brport_attr_change_ack,
&brport_attr_config_pending,
&brport_attr_message_age_timer,
&brport_attr_forward_delay_timer,
&brport_attr_hold_timer,
NULL
};
#define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr)
#define to_brport(obj) container_of(obj, struct net_bridge_port, kobj)
static ssize_t brport_show(struct kobject * kobj,
struct attribute * attr, char * buf)
{
struct brport_attribute * brport_attr = to_brport_attr(attr);
struct net_bridge_port * p = to_brport(kobj);
return brport_attr->show(p, buf);
}
static ssize_t brport_store(struct kobject * kobj,
struct attribute * attr,
const char * buf, size_t count)
{
struct brport_attribute * brport_attr = to_brport_attr(attr);
struct net_bridge_port * p = to_brport(kobj);
ssize_t ret = -EINVAL;
char *endp;
unsigned long val;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
val = simple_strtoul(buf, &endp, 0);
if (endp != buf) {
rtnl_lock();
if (p->dev && p->br && brport_attr->store) {
spin_lock_bh(&p->br->lock);
ret = brport_attr->store(p, val);
spin_unlock_bh(&p->br->lock);
if (ret == 0)
ret = count;
}
rtnl_unlock();
}
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 = {
.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.
* Puts symlink in bridge's brport subdirectory
*/
int br_sysfs_addif(struct net_bridge_port *p)
{
struct net_bridge *br = p->br;
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 = &bridge_subsys.kset;
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)
goto out2;
for (a = brport_attrs; *a; ++a) {
err = sysfs_create_file(&p->kobj, &((*a)->attr));
if (err)
goto out2;
}
err = sysfs_create_link(&br->ifobj, &p->kobj, p->dev->name);
if (err)
goto out2;
return 0;
out2:
kobject_del(&p->kobj);
out1:
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_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