Add support for sysfs and ethtool.
authorJustin Pettit <jpettit@nicira.com>
Fri, 9 Jan 2009 01:16:33 +0000 (17:16 -0800)
committerJustin Pettit <jpettit@nicira.com>
Fri, 9 Jan 2009 01:16:33 +0000 (17:16 -0800)
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.

12 files changed:
datapath/brc_sysfs.h [new file with mode: 0644]
datapath/brc_sysfs_dp.c [new file with mode: 0644]
datapath/brc_sysfs_if.c [new file with mode: 0644]
datapath/brcompat.c
datapath/datapath.c
datapath/datapath.h
datapath/dp_dev.c
datapath/dp_dev.h
datapath/linux-2.6/.gitignore
datapath/linux-2.6/Modules.mk
datapath/linux-2.6/compat-2.6/include/linux/netdevice.h [new file with mode: 0644]
vswitchd/brcompat.c

diff --git a/datapath/brc_sysfs.h b/datapath/brc_sysfs.h
new file mode 100644 (file)
index 0000000..fa972b0
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef BRC_SYSFS_H
+#define BRC_SYSFS_H 1
+
+struct datapath;
+
+#include <linux/version.h>
+#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 (file)
index 0000000..cb323cb
--- /dev/null
@@ -0,0 +1,520 @@
+#include <linux/version.h>
+#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 <linux/capability.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <linux/times.h>
+#include <linux/version.h>
+
+#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 (file)
index 0000000..cc0bd50
--- /dev/null
@@ -0,0 +1,334 @@
+#include <linux/version.h>
+#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 <linux/capability.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+
+#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 */
index 5c3b40fa03bd281d739a8dff443fabb267b23102..5b86a70b38c01da95803c958333124ba9c0a0d65 100644 (file)
@@ -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; i<DP_MAX; i++) {
+               if (dp_get_by_idx(i)) {
+                       rcu_read_unlock();
+                       printk(KERN_EMERG "brcompat: no datapaths may exist!\n");
+                       return -EEXIST;
+               }
+       }
+       rcu_read_unlock();
+
        /* Set the bridge ioctl handler */
        brioctl_set(brc_ioctl_deviceless_stub);
 
        /* Set the OpenFlow device ioctl handler */
        dp_ioctl_hook = brc_dev_ioctl;
 
+       /* Register hooks for datapath adds and deletes */
+       dp_add_dp_hook = brc_add_dp;
+       dp_del_dp_hook = brc_del_dp;
+
+       /* Register hooks for interface adds and deletes */
+#if CONFIG_SYSFS
+       dp_add_if_hook = brc_sysfs_add_if;
+       dp_del_if_hook = brc_sysfs_del_if;
+#endif
+
        /* Register generic netlink family to communicate changes to
         * userspace. */
        err = genl_register_family(&brc_genl_family);
        if (err)
-               return err;
+               goto error;
 
        err = genl_register_ops(&brc_genl_family, &brc_genl_ops_query_dp);
        if (err != 0) 
@@ -442,16 +473,26 @@ __init brc_init(void)
 
 err_unregister:
        genl_unregister_family(&brc_genl_family);
+error:
+       printk(KERN_EMERG "brcompat: failed to install!");
        return err;
 }
 
 static void 
 brc_cleanup(void)
 {
-       /* Check refcount for datapaths so hook doesn't disappear? */
+       /* Unregister hooks for datapath adds and deletes */
+       dp_add_dp_hook = NULL;
+       dp_del_dp_hook = NULL;
+       
+       /* Unregister hooks for interface adds and deletes */
+       dp_add_if_hook = NULL;
+       dp_del_if_hook = NULL;
+
+       /* Unregister ioctl hooks */
        dp_ioctl_hook = NULL;
-
        brioctl_set(NULL);
+
        genl_unregister_family(&brc_genl_family);
 }
 
@@ -459,5 +500,5 @@ module_init(brc_init);
 module_exit(brc_cleanup);
 
 MODULE_DESCRIPTION("OpenFlow bridge compatibility");
-MODULE_AUTHOR("Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University");
+MODULE_AUTHOR("Copyright (c) 2009 The Board of Trustees of The Leland Stanford Junior University");
 MODULE_LICENSE("GPL");
index 4b5103e36dfe73b5709f7c9b84adc55083639159..c3a107e0598da8148321939501a332646fa30d9c 100644 (file)
@@ -71,6 +71,18 @@ MODULE_PARM(serial_num, "s");
 int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
 EXPORT_SYMBOL(dp_ioctl_hook);
 
+int (*dp_add_dp_hook)(struct datapath *dp);
+EXPORT_SYMBOL(dp_add_dp_hook);
+
+int (*dp_del_dp_hook)(struct datapath *dp);
+EXPORT_SYMBOL(dp_del_dp_hook);
+
+int (*dp_add_if_hook)(struct net_bridge_port *p);
+EXPORT_SYMBOL(dp_add_if_hook);
+
+int (*dp_del_if_hook)(struct net_bridge_port *p);
+EXPORT_SYMBOL(dp_del_if_hook);
+
 /* Number of milliseconds between runs of the maintenance thread. */
 #define MAINT_SLEEP_MSECS 1000
 
@@ -340,6 +352,9 @@ static int new_dp(int dp_idx, const char *dp_name)
        mutex_unlock(&dp_mutex);
        rtnl_unlock();
 
+       if (dp_add_dp_hook)
+               dp_add_dp_hook(dp);
+
        return 0;
 
 err_destroy_local_port:
@@ -416,6 +431,9 @@ int add_switch_port(struct datapath *dp, struct net_device *dev)
 
        init_port_status(p);
 
+       if (dp_add_if_hook)
+               dp_add_if_hook(p);
+
        /* Notify the ctlpath that this port has been added */
        dp_send_port_status(p, OFPPR_ADD);
 
@@ -430,6 +448,11 @@ int dp_del_switch_port(struct net_bridge_port *p)
        unsigned long flags;
 #endif
 
+#if CONFIG_SYSFS
+       if ((p->port_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. */
index 4f71e371b778dd42ab1a112db29d621aec8adb2e..ffd045e8fa2fffcf0a3932387f2608f74054f04d 100644 (file)
@@ -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);
index 94a717488b2e8863a6fdfbfdcd5fa5786fde8077..cec7fbfaf9c934b1fe99aed3a326ae1e175e5134 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
 #include <linux/rcupdate.h>
 #include <linux/skbuff.h>
 #include <linux/workqueue.h>
@@ -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;
index 6afc467206b85cb91c5faea9d9c78d371aeb4843..26f724851f701007e92c11370b483e1121e42918 100644 (file)
@@ -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 */
index b172e746f0d9ece0559cb3dcc63435933442414c..fc4226148c40bdec0cd621969b640084b206a636 100644 (file)
@@ -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
index 809e4176d469087bd0af3e5344ec02a81d20a972..d66d1016aab357f23b627587573e99cd2dc19c90 100644 (file)
@@ -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 (file)
index 0000000..7abeb3b
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __LINUX_NETDEVICE_WRAPPER_H
+#define __LINUX_NETDEVICE_WRAPPER_H 1
+
+#include_next <linux/netdevice.h>
+
+#ifndef to_net_dev
+#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+#endif
+
+#endif
index 89b4bfb7e9e0ed10c807b020d8d977820cd4dacd..75aa5bc2aaf316433a311ead98df597b2c91e98d 100644 (file)
@@ -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) {