From: Justin Pettit Date: Fri, 9 Jan 2009 01:16:33 +0000 (-0800) Subject: Add support for sysfs and ethtool. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4dcfd4004ae42ff1287386b546f90cc114cce9b2;p=openvswitch Add support for sysfs and ethtool. Add support for sysfs when the bridge compatibility module is running. Currently, this only works for 2.6.18 kernels. Working on all kernels should be fixed soon. Also, add ethtool support to the datapath device. --- diff --git a/datapath/brc_sysfs.h b/datapath/brc_sysfs.h new file mode 100644 index 00000000..fa972b00 --- /dev/null +++ b/datapath/brc_sysfs.h @@ -0,0 +1,23 @@ +#ifndef BRC_SYSFS_H +#define BRC_SYSFS_H 1 + +struct datapath; + +#include +#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18) +/* brc_sysfs_dp.c */ +int brc_sysfs_add_dp(struct datapath *dp); +int brc_sysfs_del_dp(struct datapath *dp); + +/* brc_sysfs_if.c */ +int brc_sysfs_add_if(struct net_bridge_port *p); +int brc_sysfs_del_if(struct net_bridge_port *p); +#else +static inline int brc_sysfs_add_dp(struct datapath *dp) { return 0; } +static inline int brc_sysfs_del_dp(struct datapath *dp) { return 0; } +static inline int brc_sysfs_add_if(struct net_bridge_port *p) { return 0; } +static inline int brc_sysfs_del_if(struct net_bridge_port *p) { return 0; } +#endif + +#endif /* brc_sysfs.h */ + diff --git a/datapath/brc_sysfs_dp.c b/datapath/brc_sysfs_dp.c new file mode 100644 index 00000000..cb323cbb --- /dev/null +++ b/datapath/brc_sysfs_dp.c @@ -0,0 +1,520 @@ +#include +#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18) + +/* + * Sysfs attributes of bridge for OpenFlow + * + * This has been shamelessly copied from the kernel sources. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "brc_sysfs.h" +#include "datapath.h" +#include "dp_dev.h" + +#define to_dev(obj) container_of(obj, struct device, kobj) + +/* Hack to attempt to build on more platforms. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) +#define to_kobj(d) &(d)->class_dev.kobj +#define BRC_DEVICE_ATTR CLASS_DEVICE_ATTR +#else +#define to_kobj(d) &(d)->dev.kobj +#define BRC_DEVICE_ATTR DEVICE_ATTR +#endif + + +/* + * Common code for storing bridge parameters. + */ +static ssize_t store_bridge_parm(struct class_device *d, + const char *buf, size_t len, + void (*set)(struct datapath *, unsigned long)) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); +#endif + char *endp; + unsigned long val; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + val = simple_strtoul(buf, &endp, 0); + if (endp == buf) + return -EINVAL; + +#if 0 + spin_lock_bh(&br->lock); + (*set)(br, val); + spin_unlock_bh(&br->lock); +#else + printk("xxx writing dp parms not supported yet!\n"); +#endif + return len; +} + + +static ssize_t show_forward_delay(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} + +static void set_forward_delay(struct datapath *dp, unsigned long val) +{ +#if 0 + unsigned long delay = clock_t_to_jiffies(val); + br->forward_delay = delay; + if (br_is_root_bridge(br)) + br->bridge_forward_delay = delay; +#else + printk("xxx attempt to set_forward_delay()\n"); +#endif +} + +static ssize_t store_forward_delay(struct class_device *d, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, set_forward_delay); +} +static BRC_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR, + show_forward_delay, store_forward_delay); + +static ssize_t show_hello_time(struct class_device *d, char *buf) +{ +#if 0 + return sprintf(buf, "%lu\n", + jiffies_to_clock_t(to_bridge(d)->hello_time)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} + +static void set_hello_time(struct datapath *dp, unsigned long val) +{ +#if 0 + unsigned long t = clock_t_to_jiffies(val); + br->hello_time = t; + if (br_is_root_bridge(br)) + br->bridge_hello_time = t; +#else + printk("xxx attempt to set_hello_time()\n"); +#endif +} + +static ssize_t store_hello_time(struct class_device *d, + const char *buf, + size_t len) +{ + return store_bridge_parm(d, buf, len, set_hello_time); +} +static BRC_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time, + store_hello_time); + +static ssize_t show_max_age(struct class_device *d, + char *buf) +{ +#if 0 + return sprintf(buf, "%lu\n", + jiffies_to_clock_t(to_bridge(d)->max_age)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} + +static void set_max_age(struct datapath *dp, unsigned long val) +{ +#if 0 + unsigned long t = clock_t_to_jiffies(val); + br->max_age = t; + if (br_is_root_bridge(br)) + br->bridge_max_age = t; +#else + printk("xxx attempt to set_max_age()\n"); +#endif +} + +static ssize_t store_max_age(struct class_device *d, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, set_max_age); +} +static BRC_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age); + +static ssize_t show_ageing_time(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} + +static void set_ageing_time(struct datapath *dp, unsigned long val) +{ +#if 0 + br->ageing_time = clock_t_to_jiffies(val); +#else + printk("xxx attempt to set_ageing_time()\n"); +#endif +} + +static ssize_t store_ageing_time(struct class_device *d, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, set_ageing_time); +} +static BRC_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time, + store_ageing_time); + +static ssize_t show_stp_state(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%d\n", br->stp_enabled); +#else + return sprintf(buf, "%d\n", 0); +#endif +} + + +static ssize_t store_stp_state(struct class_device *d, + const char *buf, + size_t len) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + char *endp; + unsigned long val; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + val = simple_strtoul(buf, &endp, 0); + if (endp == buf) + return -EINVAL; + + rtnl_lock(); + br_stp_set_enabled(br, val); + rtnl_unlock(); +#else + printk("xxx attempt to set_stp_state()\n"); +#endif + + return len; +} +static BRC_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state, + store_stp_state); + +static ssize_t show_priority(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%d\n", + (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]); +#else + return sprintf(buf, "%d\n", 0); +#endif +} + +static void set_priority(struct datapath *dp, unsigned long val) +{ +#if 0 + br_stp_set_bridge_priority(br, (u16) val); +#else + printk("xxx attempt to set_priority()\n"); +#endif +} + +static ssize_t store_priority(struct class_device *d, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, set_priority); +} +static BRC_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority); + +static ssize_t show_root_id(struct class_device *d, + char *buf) +{ +#if 0 + return br_show_bridge_id(buf, &to_bridge(d)->designated_root); +#else + return sprintf(buf, "0000.010203040506\n"); +#endif +} +static BRC_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL); + +static ssize_t show_bridge_id(struct class_device *d, + char *buf) +{ + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + const unsigned char *addr = dp->netdev->dev_addr; + + /* xxx Do we need a lock of some sort? */ + return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n", + 0, 0, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); +} +static BRC_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL); + +static ssize_t show_root_port(struct class_device *d, + char *buf) +{ +#if 0 + return sprintf(buf, "%d\n", to_bridge(d)->root_port); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRC_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL); + +static ssize_t show_root_path_cost(struct class_device *d, + char *buf) +{ +#if 0 + return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRC_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL); + +static ssize_t show_topology_change(struct class_device *d, + char *buf) +{ +#if 0 + return sprintf(buf, "%d\n", to_bridge(d)->topology_change); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRC_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL); + +static ssize_t show_topology_change_detected(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%d\n", br->topology_change_detected); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRC_DEVICE_ATTR(topology_change_detected, S_IRUGO, + show_topology_change_detected, NULL); + +static ssize_t show_hello_timer(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRC_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL); + +static ssize_t show_tcn_timer(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRC_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL); + +static ssize_t show_topology_change_timer(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRC_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer, + NULL); + +static ssize_t show_gc_timer(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRC_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL); + +static ssize_t show_group_addr(struct class_device *d, + char *buf) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + return sprintf(buf, "%x:%x:%x:%x:%x:%x\n", + br->group_addr[0], br->group_addr[1], + br->group_addr[2], br->group_addr[3], + br->group_addr[4], br->group_addr[5]); +#else + return sprintf(buf, "00:01:02:03:04:05\n"); +#endif +} + +static ssize_t store_group_addr(struct class_device *d, + const char *buf, size_t len) +{ +#if 0 + struct datapath *dp = dp_dev_get_dp(to_net_dev(d)); + unsigned new_addr[6]; + int i; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (sscanf(buf, "%x:%x:%x:%x:%x:%x", + &new_addr[0], &new_addr[1], &new_addr[2], + &new_addr[3], &new_addr[4], &new_addr[5]) != 6) + return -EINVAL; + + /* Must be 01:80:c2:00:00:0X */ + for (i = 0; i < 5; i++) + if (new_addr[i] != br_group_address[i]) + return -EINVAL; + + if (new_addr[5] & ~0xf) + return -EINVAL; + + if (new_addr[5] == 1 /* 802.3x Pause address */ + || new_addr[5] == 2 /* 802.3ad Slow protocols */ + || new_addr[5] == 3) /* 802.1X PAE address */ + return -EINVAL; + + spin_lock_bh(&br->lock); + for (i = 0; i < 6; i++) + br->group_addr[i] = new_addr[i]; + spin_unlock_bh(&br->lock); +#else + printk("xxx attempt to store_group_addr()\n"); +#endif + return len; +} + +static BRC_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR, + show_group_addr, store_group_addr); + +static struct attribute *bridge_attrs[] = { + &class_device_attr_forward_delay.attr, + &class_device_attr_hello_time.attr, + &class_device_attr_max_age.attr, + &class_device_attr_ageing_time.attr, + &class_device_attr_stp_state.attr, + &class_device_attr_priority.attr, + &class_device_attr_bridge_id.attr, + &class_device_attr_root_id.attr, + &class_device_attr_root_path_cost.attr, + &class_device_attr_root_port.attr, + &class_device_attr_topology_change.attr, + &class_device_attr_topology_change_detected.attr, + &class_device_attr_hello_timer.attr, + &class_device_attr_tcn_timer.attr, + &class_device_attr_topology_change_timer.attr, + &class_device_attr_gc_timer.attr, + &class_device_attr_group_addr.attr, + NULL +}; + +static struct attribute_group bridge_group = { + .name = SYSFS_BRIDGE_ATTR, + .attrs = bridge_attrs, +}; + +/* + * Add entries in sysfs onto the existing network class device + * for the bridge. + * Adds a attribute group "bridge" containing tuning parameters. + * Sub directory to hold links to interfaces. + * + * Note: the ifobj exists only to be a subdirectory + * to hold links. The ifobj exists in the same data structure + * as its parent the bridge so reference counting works. + */ +int brc_sysfs_add_dp(struct datapath *dp) +{ + struct net_device *dev = dp->netdev; + struct kobject *kobj = to_kobj(dev); + int err; + + err = sysfs_create_group(kobj, &bridge_group); + if (err) { + pr_info("%s: can't create group %s/%s\n", + __func__, dev->name, bridge_group.name); + goto out1; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + kobject_set_name(&dp->ifobj, SYSFS_BRIDGE_PORT_SUBDIR); + dp->ifobj.ktype = NULL; + dp->ifobj.kset = NULL; + dp->ifobj.parent = kobj; + + err = kobject_register(&dp->ifobj); + if (err) { + pr_info("%s: can't add kobject (directory) %s/%s\n", + __FUNCTION__, dev->name, dp->ifobj.name); + goto out2; + } +#else + br->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, kobj); + if (!br->ifobj) { + pr_info("%s: can't add kobject (directory) %s/%s\n", + __func__, dev->name, SYSFS_BRIDGE_PORT_SUBDIR); + goto out2; + } +#endif + return 0; + + out2: + sysfs_remove_group(to_kobj(dev), &bridge_group); + out1: + return err; +} + +int brc_sysfs_del_dp(struct datapath *dp) +{ + struct net_device *dev = dp->netdev; + struct kobject *kobj = to_kobj(dev); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + kobject_unregister(&dp->ifobj); +#else + kobject_put(dp->ifobj); +#endif + sysfs_remove_group(kobj, &bridge_group); + + return 0; +} +#endif /* Only support 2.6.18 */ diff --git a/datapath/brc_sysfs_if.c b/datapath/brc_sysfs_if.c new file mode 100644 index 00000000..cc0bd504 --- /dev/null +++ b/datapath/brc_sysfs_if.c @@ -0,0 +1,334 @@ +#include +#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18) + +/* + * Sysfs attributes of bridge ports for OpenFlow + * + * This has been shamelessly copied from the kernel sources. + */ + +#include +#include +#include +#include +#include +#include + +#include "brc_sysfs.h" +#include "datapath.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) +{ +#if 0 + return sprintf(buf, "%d\n", p->path_cost); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v) +{ +#if 0 + br_stp_set_path_cost(p, v); +#endif + 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) +{ +#if 0 + return sprintf(buf, "%d\n", p->priority); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static ssize_t store_priority(struct net_bridge_port *p, unsigned long v) +{ +#if 0 + if (v >= (1<<(16-BR_PORT_BITS))) + return -ERANGE; + br_stp_set_port_priority(p, v); +#endif + 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) +{ +#if 0 + return br_show_bridge_id(buf, &p->designated_root); +#else + return sprintf(buf, "0000.010203040506\n"); +#endif +} +static BRPORT_ATTR(designated_root, S_IRUGO, show_designated_root, NULL); + +static ssize_t show_designated_bridge(struct net_bridge_port *p, char *buf) +{ +#if 0 + return br_show_bridge_id(buf, &p->designated_bridge); +#else + return sprintf(buf, "0000.060504030201\n"); +#endif +} +static BRPORT_ATTR(designated_bridge, S_IRUGO, show_designated_bridge, NULL); + +static ssize_t show_designated_port(struct net_bridge_port *p, char *buf) +{ +#if 0 + return sprintf(buf, "%d\n", p->designated_port); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRPORT_ATTR(designated_port, S_IRUGO, show_designated_port, NULL); + +static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf) +{ +#if 0 + return sprintf(buf, "%d\n", p->designated_cost); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRPORT_ATTR(designated_cost, S_IRUGO, show_designated_cost, NULL); + +static ssize_t show_port_id(struct net_bridge_port *p, char *buf) +{ +#if 0 + return sprintf(buf, "0x%x\n", p->port_id); +#else + return sprintf(buf, "0x%x\n", 0); +#endif +} +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) +{ +#if 0 + return sprintf(buf, "%d\n", p->topology_change_ack); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRPORT_ATTR(change_ack, S_IRUGO, show_change_ack, NULL); + +static ssize_t show_config_pending(struct net_bridge_port *p, char *buf) +{ +#if 0 + return sprintf(buf, "%d\n", p->config_pending); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRPORT_ATTR(config_pending, S_IRUGO, show_config_pending, NULL); + +static ssize_t show_port_state(struct net_bridge_port *p, char *buf) +{ +#if 0 + return sprintf(buf, "%d\n", p->state); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +static BRPORT_ATTR(state, S_IRUGO, show_port_state, NULL); + +static ssize_t show_message_age_timer(struct net_bridge_port *p, + char *buf) +{ +#if 0 + return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +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) +{ +#if 0 + return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +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) +{ +#if 0 + return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer)); +#else + return sprintf(buf, "%d\n", 0); +#endif +} +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) +{ +#if 0 + struct brport_attribute * brport_attr = to_brport_attr(attr); + struct net_bridge_port * p = to_brport(kobj); + char *endp; + unsigned long val; +#endif + ssize_t ret = -EINVAL; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + +#if 0 + 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(); + } +#else + printk("xxx writing port parms not supported yet!\n"); +#endif + return ret; +} + +struct sysfs_ops brport_sysfs_ops = { + .show = brport_show, + .store = brport_store, +}; + +static void release_nbp(struct kobject *kobj) +{ + struct net_bridge_port *p + = container_of(kobj, struct net_bridge_port, kobj); + kfree(p); +} + +struct kobj_type brport_ktype = { + .sysfs_ops = &brport_sysfs_ops, + .release = release_nbp +}; + +/* + * 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 brc_sysfs_add_if(struct net_bridge_port *p) +{ + struct datapath *dp = p->dp; + struct brport_attribute **a; + int err; + + kobject_init(&p->kobj); + kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR); + p->kobj.ktype = &brport_ktype; + p->kobj.kset = NULL; + p->kobj.parent = &(p->dev->class_dev.kobj); + + err = kobject_add(&p->kobj); + if (err) + goto err_put; + + err = sysfs_create_link(&p->kobj, &dp->netdev->class_dev.kobj, + SYSFS_BRIDGE_PORT_LINK); + if (err) + goto err_del; + + for (a = brport_attrs; *a; ++a) { + err = sysfs_create_file(&p->kobj, &((*a)->attr)); + if (err) + goto err_del; + } + + err = sysfs_create_link(&dp->ifobj, &p->kobj, p->dev->name); + if (err) + goto err_del; + + kobject_uevent(&p->kobj, KOBJ_ADD); + + return err; + +err_del: + kobject_del(&p->kobj); +err_put: + kobject_put(&p->kobj); + return err; +} + +int brc_sysfs_del_if(struct net_bridge_port *p) +{ + struct net_device *dev = p->dev; + + kobject_uevent(&p->kobj, KOBJ_REMOVE); + kobject_del(&p->kobj); + + dev_put(dev); + + kobject_put(&p->kobj); + + return 0; +} +#endif /* Only support 2.6.18 */ diff --git a/datapath/brcompat.c b/datapath/brcompat.c index 5c3b40fa..5b86a70b 100644 --- a/datapath/brcompat.c +++ b/datapath/brcompat.c @@ -9,6 +9,7 @@ #include "compat.h" #include "openflow/openflow-netlink.h" #include "openflow/brcompat-netlink.h" +#include "brc_sysfs.h" #include "datapath.h" #include "dp_dev.h" @@ -31,25 +32,24 @@ int brc_send_dp_add_del(const char *dp_name, int add); int brc_send_port_add_del(struct net_device *dev, struct net_device *port, int add); -/* Called with RTNL lock. */ + static int get_dp_ifindices(int *indices, int num) { int i, index = 0; - mutex_lock(&dp_mutex); + rcu_read_lock(); for (i=0; i < DP_MAX && index < num; i++) { struct datapath *dp = dp_get_by_idx(i); if (!dp) continue; indices[index++] = dp->netdev->ifindex; } - mutex_unlock(&dp_mutex); + rcu_read_unlock(); return index; } -/* Called with RTNL lock. */ static void get_port_ifindices(struct datapath *dp, int *ifindices, int num) { @@ -63,12 +63,7 @@ get_port_ifindices(struct datapath *dp, int *ifindices, int num) rcu_read_unlock(); } -/* Legacy deviceless bridge ioctl's. The primary reason to do this - * is to provide support for 'brctl show' without having to implement - * the bridge's sysfs system. Also, 'brctl' will fall back on the - * legacy device ioctl's if an attempt to add or delete a bridge results in - * an error. By re-attempting the failed add or delete call, the error - * messages from 'brctl' are meaningful. */ +/* Legacy deviceless bridge ioctl's. Called with br_ioctl_mutex. */ static int old_deviceless(void __user *uarg) { @@ -106,7 +101,7 @@ old_deviceless(void __user *uarg) return -EFAULT; dp_name[IFNAMSIZ-1] = 0; - if (args[0] == BRCTL_ADD_BRIDGE) + if (args[0] == BRCTL_ADD_BRIDGE) return brc_send_dp_add_del(dp_name, 1); return brc_send_dp_add_del(dp_name, 0); @@ -126,7 +121,7 @@ brc_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg) { switch (cmd) { case SIOCGIFBR: - case SIOCSIFBR: + case SIOCSIFBR: return old_deviceless(uarg); case SIOCBRADDBR: @@ -137,22 +132,14 @@ brc_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg) return -EFAULT; dp_name[IFNAMSIZ-1] = 0; - if (cmd == SIOCBRADDBR) - return brc_send_dp_add_del(dp_name, 1); - - return brc_send_dp_add_del(dp_name, 0); + return brc_send_dp_add_del(dp_name, cmd == SIOCBRADDBR); } } return -EOPNOTSUPP; } -/* Legacy ioctl's through SIOCDEVPRIVATE. The primary reason to do this - * is to provide support for 'brctl show' without having to implement - * the bridge's sysfs system. Also, 'brctl' will fall back on the - * legacy device ioctl's if an attempt to modify an interface results in - * an error. By re-attempting the failed modify call, the error messages - * from 'brctl' are meaningful. */ +/* Legacy ioctl's through SIOCDEVPRIVATE. Called with rtnl_lock. */ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { @@ -172,6 +159,7 @@ old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) port = dev_get_by_index(&init_net, args[1]); if (!port) return -EINVAL; + err = brc_send_port_add_del(dev, port, args[0] == BRCTL_ADD_IF); dev_put(port); return err; @@ -239,6 +227,7 @@ brc_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) port = dev_get_by_index(&init_net, rq->ifr_ifindex); if (!port) return -EINVAL; + err = brc_send_port_add_del(dev, port, cmd == SIOCBRADDIF); dev_put(port); break; @@ -406,24 +395,66 @@ err: return -EINVAL; } +int brc_add_dp(struct datapath *dp) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; +#if CONFIG_SYSFS + brc_sysfs_add_dp(dp); +#endif + + return 0; +} + +int brc_del_dp(struct datapath *dp) +{ +#if CONFIG_SYSFS + brc_sysfs_del_dp(dp); +#endif + module_put(THIS_MODULE); + + return 0; +} + static int __init brc_init(void) { + int i; int err; printk("OpenFlow Bridge Compatibility, built "__DATE__" "__TIME__"\n"); + rcu_read_lock(); + for (i=0; iport_no != OFPP_LOCAL) && dp_del_if_hook) + sysfs_remove_link(&p->dp->ifobj, p->dev->name); +#endif + /* First drop references to device. */ dev_set_promiscuity(p->dev, -1); list_del_rcu(&p->node); @@ -450,8 +473,12 @@ int dp_del_switch_port(struct net_bridge_port *p) /* Notify the ctlpath that this port no longer exists */ dp_send_port_status(p, OFPPR_DELETE); - dev_put(p->dev); - kfree(p); + if ((p->port_no != OFPP_LOCAL) && dp_del_if_hook) { + dp_del_if_hook(p); + } else { + dev_put(p->dev); + kfree(p); + } return 0; } @@ -466,6 +493,10 @@ static void del_dp(struct datapath *dp) /* Drop references to DP. */ list_for_each_entry_safe (p, n, &dp->port_list, node) dp_del_switch_port(p); + + if (dp_del_dp_hook) + dp_del_dp_hook(dp); + rcu_assign_pointer(dps[dp->dp_idx], NULL); /* Kill off local_port dev references from buffered packets that have @@ -1143,6 +1174,7 @@ static struct genl_ops dp_genl_ops_add_dp = { .dumpit = NULL, }; +/* Must be called with rcu_read_lock or dp_mutex. */ struct datapath *dp_get_by_idx(int dp_idx) { if (dp_idx < 0 || dp_idx >= DP_MAX) @@ -1151,6 +1183,7 @@ struct datapath *dp_get_by_idx(int dp_idx) } EXPORT_SYMBOL(dp_get_by_idx); +/* Must be called with rcu_read_lock or dp_mutex. */ struct datapath *dp_get_by_name(const char *dp_name) { int i; @@ -1162,6 +1195,7 @@ struct datapath *dp_get_by_name(const char *dp_name) return NULL; } +/* Must be called with rcu_read_lock or dp_mutex. */ static struct datapath * lookup_dp(struct genl_info *info) { @@ -1928,7 +1962,11 @@ static int __init dp_init(void) if (err) goto error_unreg_notifier; - dp_ioctl_hook = NULL; + dp_ioctl_hook = NULL; + dp_add_dp_hook = NULL; + dp_del_dp_hook = NULL; + dp_add_if_hook = NULL; + dp_del_if_hook = NULL; /* Check if better descriptions of the switch are available than the * defaults. */ diff --git a/datapath/datapath.h b/datapath/datapath.h index 4f71e371..ffd045e8 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -61,6 +61,8 @@ struct datapath { /* Flag controlling whether Flow End messages are generated. */ uint8_t send_flow_end; + struct kobject ifobj; + /* Switch ports. */ struct net_bridge_port *ports[DP_MAX_PORTS]; struct net_bridge_port *local_port; /* OFPP_LOCAL port. */ @@ -81,6 +83,7 @@ struct net_bridge_port { spinlock_t lock; struct datapath *dp; struct net_device *dev; + struct kobject kobj; struct snat_conf *snat; /* Only set if SNAT is configured for this port. */ struct list_head node; /* Element in datapath.ports. */ }; @@ -88,6 +91,10 @@ struct net_bridge_port { extern struct mutex dp_mutex; extern struct notifier_block dp_device_notifier; extern int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd); +extern int (*dp_add_dp_hook)(struct datapath *dp); +extern int (*dp_del_dp_hook)(struct datapath *dp); +extern int (*dp_add_if_hook)(struct net_bridge_port *p); +extern int (*dp_del_if_hook)(struct net_bridge_port *p); int dp_del_switch_port(struct net_bridge_port *); int dp_xmit_skb(struct sk_buff *skb); diff --git a/datapath/dp_dev.c b/datapath/dp_dev.c index 94a71748..cec7fbfa 100644 --- a/datapath/dp_dev.c +++ b/datapath/dp_dev.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,12 @@ static struct dp_dev *dp_dev_priv(struct net_device *netdev) return netdev_priv(netdev); } +struct datapath *dp_dev_get_dp(struct net_device *netdev) +{ + return dp_dev_priv(netdev)->dp; +} +EXPORT_SYMBOL(dp_dev_get_dp); + static struct net_device_stats *dp_dev_get_stats(struct net_device *netdev) { struct dp_dev *dp_dev = dp_dev_priv(netdev); @@ -154,6 +161,22 @@ set_uuid_mac(struct net_device *netdev) memcpy(netdev->dev_addr, mac, ETH_ALEN); } +static void dp_getinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strcpy(info->driver, "openflow"); + sprintf(info->version, "0x%d", OFP_VERSION); + strcpy(info->fw_version, "N/A"); + strcpy(info->bus_info, "N/A"); +} + +static struct ethtool_ops dp_ethtool_ops = { + .get_drvinfo = dp_getinfo, + .get_link = ethtool_op_get_link, + .get_sg = ethtool_op_get_sg, + .get_tx_csum = ethtool_op_get_tx_csum, + .get_tso = ethtool_op_get_tso, +}; + static void do_setup(struct net_device *netdev) { @@ -163,6 +186,7 @@ do_setup(struct net_device *netdev) netdev->get_stats = dp_dev_get_stats; netdev->hard_start_xmit = dp_dev_xmit; netdev->open = dp_dev_open; + SET_ETHTOOL_OPS(netdev, &dp_ethtool_ops); netdev->stop = dp_dev_stop; netdev->tx_queue_len = 100; netdev->set_mac_address = dp_dev_mac_addr; diff --git a/datapath/dp_dev.h b/datapath/dp_dev.h index 6afc4672..26f72485 100644 --- a/datapath/dp_dev.h +++ b/datapath/dp_dev.h @@ -12,5 +12,6 @@ int dp_dev_setup(struct datapath *, const char *); void dp_dev_destroy(struct datapath *); int dp_dev_recv(struct net_device *, struct sk_buff *); int is_dp_dev(struct net_device *); +struct datapath *dp_dev_get_dp(struct net_device *); #endif /* dp_dev.h */ diff --git a/datapath/linux-2.6/.gitignore b/datapath/linux-2.6/.gitignore index b172e746..fc422614 100644 --- a/datapath/linux-2.6/.gitignore +++ b/datapath/linux-2.6/.gitignore @@ -2,6 +2,8 @@ /Makefile /Makefile.main /brcompat.c +/brc_sysfs_dp.c +/brc_sysfs_if.c /chain.c /crc32.c /crc_t.c @@ -12,7 +14,8 @@ /flow.c /forward.c /forward_t.c -/genetlink.c +/genetlink-brcompat.c +/genetlink-openflow.c /hwtable_dummy.c /nx_act.c /nx_act_snat.c diff --git a/datapath/linux-2.6/Modules.mk b/datapath/linux-2.6/Modules.mk index 809e4176..d66d1016 100644 --- a/datapath/linux-2.6/Modules.mk +++ b/datapath/linux-2.6/Modules.mk @@ -31,6 +31,9 @@ openflow_headers += \ brcompat_sources = \ linux-2.6/compat-2.6/genetlink-brcompat.c \ - brcompat.c + brcompat.c \ + brc_sysfs_dp.c \ + brc_sysfs_if.c -brcompat_headers = +brcompat_headers = \ + brc_sysfs.h diff --git a/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h new file mode 100644 index 00000000..7abeb3bf --- /dev/null +++ b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h @@ -0,0 +1,10 @@ +#ifndef __LINUX_NETDEVICE_WRAPPER_H +#define __LINUX_NETDEVICE_WRAPPER_H 1 + +#include_next + +#ifndef to_net_dev +#define to_net_dev(class) container_of(class, struct net_device, class_dev) +#endif + +#endif diff --git a/vswitchd/brcompat.c b/vswitchd/brcompat.c index 89b4bfb7..75aa5bc2 100644 --- a/vswitchd/brcompat.c +++ b/vswitchd/brcompat.c @@ -239,23 +239,14 @@ brc_modify_config(const char *dp_name, const char *port_name, xasprintf("bridge.%s.port = %s\n", old_br.names[i], old_keys.names[j])); } - - cfg_get_all_keys(&old_keys, "bridge.%s.enabled", old_br.names[i]); - for (j = 0; j < old_keys.n; j++) { - svec_add_nocopy(&new_cfg, - xasprintf("bridge.%s.enabled = true\n", - old_br.names[i])); - } } svec_destroy(&old_br); svec_destroy(&old_keys); - /* If the last interface was removed from the datapath, we'll add - * this dummy entry so that it's still created when vswitchd is - * restarted. */ - if ((act == BMC_ADD_DP) || (n_ports == 0)) { + /* Always add the datapath's own device. */ + if (act == BMC_ADD_DP) { svec_add_nocopy(&new_cfg, - xasprintf("bridge.%s.enabled = true\n", dp_name)); + xasprintf("bridge.%s.port = %s\n", dp_name, dp_name)); } if (act == BMC_ADD_PORT) {