static struct genl_family brc_genl_family;
 static struct genl_multicast_group brc_mc_group;
 
-/* Completion for vswitchd to notify the ioctl that the operation
- * completed. */
-static DECLARE_COMPLETION(dp_act_done);
- 
-/* Time to wait for vswitchd to respond to a datapath action (in
- * milliseconds) */
-#define DP_ACT_TIMEOUT 5000 
+/* Time to wait for vswitchd to respond to a datapath action, in jiffies. */
+#define BRC_TIMEOUT (HZ * 5)
 
-/* Positive errno as a result of a datapath action.  Calls that make
- * use of this variable are serialized by the br_ioctl_mutex. */
-static int dp_act_err;
+/* Mutex to serialize brcompatd callbacks.  (Some callbacks naturally hold
+ * br_ioctl_mutex, others hold rtnl_lock, but we can't take the former
+ * ourselves and we don't want to hold the latter over a potentially long
+ * period of time.) */
+static DEFINE_MUTEX(brc_serial);
 
-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);
+/* Userspace communication. */
+static DEFINE_SPINLOCK(brc_lock);    /* Ensure atomic access to these vars. */
+static DECLARE_COMPLETION(brc_done); /* Userspace signaled operation done? */
+static int brc_err;                 /* Error code from userspace. */
+static u32 brc_seq;                 /* Sequence number for current op. */
 
+static int brc_send_command(const char *bridge, const char *port, int op);
 
 static int
 get_dp_ifindices(int *indices, int num)
        rcu_read_unlock();
 }
 
-/* Legacy deviceless bridge ioctl's.  Called with br_ioctl_mutex. */
-static int
-old_deviceless(void __user *uarg)
+static int brc_add_del_bridge(char __user *uname, int add)
 {
-       unsigned long args[3];
+       char name[IFNAMSIZ];
 
-       if (copy_from_user(args, uarg, sizeof(args)))
+       if (copy_from_user(name, uname, IFNAMSIZ))
                return -EFAULT;
 
-       switch (args[0]) {
-       case BRCTL_GET_BRIDGES: { 
-               int *indices;
-               int ret = 0;
+       name[IFNAMSIZ - 1] = 0;
+       return brc_send_command(name, NULL,
+                               add ? BRC_GENL_C_DP_ADD : BRC_GENL_C_DP_DEL);
+}
 
-               if (args[2] >= 2048)
-                       return -ENOMEM;
+static int brc_get_bridges(int __user *uindices, int n)
+{
+       int *indices;
+       int ret;
 
-               indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
-               if (indices == NULL)
-                       return -ENOMEM;
+       if (n >= 2048)
+               return -ENOMEM;
 
-               args[2] = get_dp_ifindices(indices, args[2]);
+       indices = kcalloc(n, sizeof(int), GFP_KERNEL);
+       if (indices == NULL)
+               return -ENOMEM;
 
-               ret = copy_to_user((void __user *)args[1], indices, 
-                               args[2]*sizeof(int)) ? -EFAULT : args[2];
+       n = get_dp_ifindices(indices, n);
 
-               kfree(indices);
-               return ret;
-       }
+       ret = copy_to_user(uindices, indices, n * sizeof(int)) ? -EFAULT : n;
 
-       case BRCTL_DEL_BRIDGE: 
-       case BRCTL_ADD_BRIDGE: { 
-               char dp_name[IFNAMSIZ];
+       kfree(indices);
+       return ret;
+}
 
-               if (copy_from_user(dp_name, (void __user *)args[1], IFNAMSIZ))
-                       return -EFAULT;
+/* Legacy deviceless bridge ioctl's.  Called with br_ioctl_mutex. */
+static int
+old_deviceless(void __user *uarg)
+{
+       unsigned long args[3];
 
-               dp_name[IFNAMSIZ-1] = 0;
-               if (args[0] == BRCTL_ADD_BRIDGE) 
-                       return brc_send_dp_add_del(dp_name, 1);
+       if (copy_from_user(args, uarg, sizeof(args)))
+               return -EFAULT;
 
-               return brc_send_dp_add_del(dp_name, 0);
-       }
+       switch (args[0]) {
+       case BRCTL_GET_BRIDGES:
+               return brc_get_bridges((int __user *)args[1], args[2]);
+
+       case BRCTL_ADD_BRIDGE:
+               return brc_add_del_bridge((void __user *)args[1], 1);
+       case BRCTL_DEL_BRIDGE:
+               return brc_add_del_bridge((void __user *)args[1], 0);
        }
 
        return -EOPNOTSUPP;
 {
        switch (cmd) {
        case SIOCGIFBR:
-       case SIOCSIFBR: 
+       case SIOCSIFBR:
                return old_deviceless(uarg);
 
        case SIOCBRADDBR:
-       case SIOCBRDELBR: {
-               char dp_name[IFNAMSIZ];
-
-               if (copy_from_user(dp_name, uarg, IFNAMSIZ))
-                       return -EFAULT;
-
-               dp_name[IFNAMSIZ-1] = 0;
-               return brc_send_dp_add_del(dp_name, cmd == SIOCBRADDBR);
-       }
+               return brc_add_del_bridge(uarg, 1);
+       case SIOCBRDELBR:
+               return brc_add_del_bridge(uarg, 0);
        }
 
        return -EOPNOTSUPP;
 }
 
-/* Legacy ioctl's through SIOCDEVPRIVATE.  Called with rtnl_lock. */
 static int
-old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+brc_add_del_port(struct net_device *dev, int port_ifindex, int add)
 {
-       struct dp_dev *dp_dev = netdev_priv(dev);
-       struct datapath *dp = dp_dev->dp;
-       unsigned long args[4];
+       struct net_device *port;
+       int err;
 
-       if (copy_from_user(args, rq->ifr_data, sizeof(args)))
+       port = __dev_get_by_index(&init_net, port_ifindex);
+       if (!port)
+               return -EINVAL;
+
+       rtnl_unlock();
+       err = brc_send_command(dev->name, port->name,
+                              add ? BRC_GENL_C_PORT_ADD : BRC_GENL_C_PORT_DEL);
+       rtnl_lock();
+
+       return err;
+}
+
+static int
+brc_get_bridge_info(struct net_device *dev, struct __bridge_info __user *ub)
+{
+       struct __bridge_info b;
+       u64 id = 0;
+       int i;
+
+       memset(&b, 0, sizeof(struct __bridge_info));
+
+       for (i=0; i<ETH_ALEN; i++)
+               id |= (u64)dev->dev_addr[i] << (8*(ETH_ALEN-1 - i));
+       b.bridge_id = cpu_to_be64(id);
+       b.stp_enabled = 0;
+
+       if (copy_to_user(ub, &b, sizeof(struct __bridge_info)))
                return -EFAULT;
 
-       switch (args[0]) {
-       case BRCTL_ADD_IF:
-       case BRCTL_DEL_IF: {
-               struct net_device *port;
-               int err;
+       return 0;
+}
 
-               port = __dev_get_by_index(&init_net, args[1]);
-               if (!port) 
-                       return -EINVAL;
+static int
+brc_get_port_list(struct net_device *dev, int __user *uindices, int num)
+{
+       struct dp_dev *dp_dev = netdev_priv(dev);
+       struct datapath *dp = dp_dev->dp;
+       int *indices;
 
-               err = brc_send_port_add_del(dev, port, args[0] == BRCTL_ADD_IF);
-               return err;
-       }
+       if (num < 0)
+               return -EINVAL;
+       if (num == 0)
+               num = 256;
+       if (num > DP_MAX_PORTS)
+               num = DP_MAX_PORTS;
 
-       case BRCTL_GET_BRIDGE_INFO: {
-               struct __bridge_info b;
-               u64 id = 0;
-               int i;
+       indices = kcalloc(num, sizeof(int), GFP_KERNEL);
+       if (indices == NULL)
+               return -ENOMEM;
 
-               memset(&b, 0, sizeof(struct __bridge_info));
+       get_port_ifindices(dp, indices, num);
+       if (copy_to_user(uindices, indices, num * sizeof(int)))
+               num = -EFAULT;
+       kfree(indices);
+       return num;
+}
 
-               for (i=0; i<ETH_ALEN; i++) 
-                       id |= (u64)dev->dev_addr[i] << (8*(ETH_ALEN-1 - i));
-               b.bridge_id = cpu_to_be64(id);
-               b.stp_enabled = 0;
+/* Legacy ioctl's through SIOCDEVPRIVATE.  Called with rtnl_lock. */
+static int
+old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       unsigned long args[4];
 
-               if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
-                       return -EFAULT;
+       if (copy_from_user(args, rq->ifr_data, sizeof(args)))
+               return -EFAULT;
 
-               return 0;
-       }
+       switch (args[0]) {
+       case BRCTL_ADD_IF:
+               return brc_add_del_port(dev, args[1], 1);
+       case BRCTL_DEL_IF:
+               return brc_add_del_port(dev, args[1], 0);
 
-       case BRCTL_GET_PORT_LIST: {
-               int num, *indices;
-
-               num = args[2];
-               if (num < 0)
-                       return -EINVAL;
-               if (num == 0)
-                       num = 256;
-               if (num > DP_MAX_PORTS)
-                       num = DP_MAX_PORTS;
-
-               indices = kcalloc(num, sizeof(int), GFP_KERNEL);
-               if (indices == NULL)
-                       return -ENOMEM;
-
-               get_port_ifindices(dp, indices, num);
-               if (copy_to_user((void __user *)args[1], indices, num*sizeof(int)))
-                       num = -EFAULT;
-               kfree(indices);
-               return num;
-       }
+       case BRCTL_GET_BRIDGE_INFO:
+               return brc_get_bridge_info(dev, (struct __bridge_info __user *)args[1]);
+
+       case BRCTL_GET_PORT_LIST:
+               return brc_get_port_list(dev, (int __user *)args[1], args[2]);
        }
 
        return -EOPNOTSUPP;
                        break;
 
                case SIOCBRADDIF:
-               case SIOCBRDELIF: {
-                       struct net_device *port;
-
-                       port = __dev_get_by_index(&init_net, rq->ifr_ifindex);
-                       if (!port) 
-                               return -EINVAL;
-
-                       err = brc_send_port_add_del(dev, port, cmd == SIOCBRADDIF);
-                       break;
-               }
+                       return brc_add_del_port(dev, rq->ifr_ifindex, 1);
+               case SIOCBRDELIF:
+                       return brc_add_del_port(dev, rq->ifr_ifindex, 0);
 
                default:
                        err = -EOPNOTSUPP;
 static int
 brc_genl_dp_result(struct sk_buff *skb, struct genl_info *info)
 {
+       unsigned long int flags;
+       int err;
+
        if (!info->attrs[BRC_GENL_A_ERR_CODE])
                return -EINVAL;
-       
-       dp_act_err = nla_get_u32(info->attrs[BRC_GENL_A_ERR_CODE]);
-       complete(&dp_act_done);
 
-       return 0;
+       spin_lock_irqsave(&brc_lock, flags);
+       if (brc_seq == info->snd_seq) {
+               brc_err = nla_get_u32(info->attrs[BRC_GENL_A_ERR_CODE]);
+               complete(&brc_done);
+               err = 0;
+       } else {
+               err = -ESTALE;
+       }
+       spin_unlock_irqrestore(&brc_lock, flags);
+
+       return err;
 }
 
 static struct genl_ops brc_genl_ops_dp_result = {
        .dumpit = NULL
 };
 
-int dp_exists(const char *dp_name)
-{
-       struct net_device *dev;
-       int retval;
-
-       dev = dev_get_by_name(&init_net, dp_name);
-       if (!dev)
-               return 0;
-
-       retval = is_dp_dev(dev);
-       dev_put(dev);
-       return retval;
-}
-
-int brc_send_dp_add_del(const char *dp_name, int add)
+static int brc_send_command(const char *bridge, const char *port, int op)
 {
+       unsigned long int flags;
        struct sk_buff *skb;
        void *data;
-       int i, retval;
-
-       skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
-       if (skb == NULL)
-               return -ENOMEM;
-
-       if (add)
-               data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0, 
-                                  BRC_GENL_C_DP_ADD);
-       else
-               data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0, 
-                                  BRC_GENL_C_DP_DEL);
-       if (!data)
-               goto err;
-
-       NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, dp_name);
-
-       init_completion(&dp_act_done);
-
-       genlmsg_end(skb, data);
-       retval = genlmsg_multicast(skb, 0, brc_mc_group.id, GFP_KERNEL);
-       if (retval < 0) 
-               return retval;
-
-       if (!wait_for_completion_timeout(&dp_act_done, 
-                               msecs_to_jiffies(DP_ACT_TIMEOUT))) 
-               return -EIO;
-
-       /* The value is returned as a positive errno, so make it negative */
-       if (dp_act_err) 
-               return -dp_act_err;
-
-       /* The user-space brcompatd can only suggest to vswitchd that it
-        * reconfigure itself.  We need to make sure the changes actually
-        * worked. */
-       for (i = 0; i < 50; i++) {
-               int exists = dp_exists(dp_name);
-               if ((add && exists) || (!add && !exists)) 
-                       return 0;
-                       
-               msleep(100);
-       }
+       int error;
 
-       return -EINVAL;
+       mutex_lock(&brc_serial);
 
-nla_put_failure:
-err:
-       kfree_skb(skb);
-       return -ENOMEM;
-}
-
-int brc_send_port_add_del(struct net_device *dev, struct net_device *port, 
-               int add)
-{
-       struct dp_dev *dp_dev = netdev_priv(dev);
-       struct datapath *dp = dp_dev->dp;
-       struct sk_buff *skb;
-       int error;
-       void *data;
-       int i;
+       /* Increment sequence number first, so that we ignore any replies
+        * to stale requests. */
+       spin_lock_irqsave(&brc_lock, flags);
+       brc_seq++;
+       INIT_COMPLETION(brc_done);
+       spin_unlock_irqrestore(&brc_lock, flags);
 
+       /* Compose message. */
        skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       error = -ENOMEM;
        if (skb == NULL)
-               return -ENOMEM;
-
-       if (add) {
-               /* Only add the port if it's not attached to a datapath. */
-               if (port->br_port != NULL) {
-                       kfree_skb(skb);
-                       return -EBUSY;
-               }
-               data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0, 
-                                  BRC_GENL_C_PORT_ADD);
-       } else {
-               /* Only delete the port if it's attached to this datapath. */
-               if (port->br_port == NULL || port->br_port->dp != dp) {
-                       kfree_skb(skb);
-                       return -ENOENT;
-               }
-               data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0, 
-                                  BRC_GENL_C_PORT_DEL);
-       }
-       if (!data)
-               goto err;
+               goto exit_unlock;
+       data = genlmsg_put(skb, 0, brc_seq, &brc_genl_family, 0, op);
 
-       NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, dev->name);
-       NLA_PUT_STRING(skb, BRC_GENL_A_PORT_NAME, port->name);
+       NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, bridge);
+       if (port)
+               NLA_PUT_STRING(skb, BRC_GENL_A_PORT_NAME, port);
 
        genlmsg_end(skb, data);
+
+       /* Send message. */
        error = genlmsg_multicast(skb, 0, brc_mc_group.id, GFP_KERNEL);
-       if (error)
-               return error;
-
-       for (i = 0; i < 50; i++) {
-               int dev_ifindex = dev->ifindex;
-               int port_ifindex = port->ifindex;
-
-               rtnl_unlock();
-               msleep(100);
-               rtnl_lock();
-
-               /* Since we released the RTNL lock, we have to make sure that
-                * our devices still exist.  (But as long as they still exist,
-                * there's no point in refreshing 'dp' or 'dp_dev', since
-                * ifindexes are reused very slowly if ever.) */
-               dev = __dev_get_by_index(&init_net, dev_ifindex);
-               port = __dev_get_by_index(&init_net, port_ifindex);
-               if (!dev || !port)
-                       return -ENODEV;
-
-               if (add) {
-                       if (port->br_port)
-                               return port->br_port->dp == dp ? 0 : -EBUSY;
-               } else {
-                       if (!port->br_port || port->br_port->dp != dp)
-                               return 0;
-               }
-       }
-       return -ETIMEDOUT;
+       if (error < 0)
+               goto exit_unlock;
+
+       /* Wait for reply. */
+       error = -ETIMEDOUT;
+       if (!wait_for_completion_timeout(&brc_done, BRC_TIMEOUT))
+               goto exit_unlock;
+
+       error = -brc_err;
+       goto exit_unlock;
 
 nla_put_failure:
-err:
        kfree_skb(skb);
-       return -EINVAL;
+exit_unlock:
+       mutex_unlock(&brc_serial);
+       return error;
 }
 
 int brc_add_dp(struct datapath *dp)
        dp_del_if_hook = brc_sysfs_del_if;
 #endif
 
+       /* Randomize the initial sequence number.  This is not a security
+        * feature; it only helps avoid crossed wires between userspace and
+        * the kernel when the module is unloaded and reloaded. */
+       brc_seq = net_random();
+
        /* Register generic netlink family to communicate changes to
         * userspace. */
        err = genl_register_family(&brc_genl_family);
 
 \fBvswitchd\fR to reload its configuration file.
 .PP
 .SH OPTIONS
-.TP
-\fB--vswitchd-pidfile=\fIpidfile\fR
-.
-If the \fB--vswitchd-pidfile\fR flag is provided, the required
-\fIpidfile\fR argument is used as the pidfile location instead of the
-default one.
+.IP "\fB--reload-command=\fIcommand\fR"
+Sets the command that \fBbrcompatd\fR runs to force \fBvswitchd\fR to
+reload its configuration file to \fIcommand\fR.  The command is run in
+a subshell, so it may contain arbitrary shell metacharacters, etc.
+The \fB--help\fR option displays the default reload command.
 .TP
 \fB--prune-timeout=\fIsecs\fR
 .
 
 #include "cfg.h"
 #include "command-line.h"
 #include "daemon.h"
+#include "dirs.h"
 #include "dpif.h"
 #include "fatal-signal.h"
 #include "fault.h"
 static void release_lock(void *aux UNUSED);
 static void parse_options(int argc, char *argv[]);
 static void usage(void) NO_RETURN;
-static int modify_config(const char *br_name, const char *port_name, 
-                  enum bmc_action act);
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 60);
 
 /* Config file shared with vswitchd (usually vswitchd.conf). */
 static char *config_file;
 
-/* Filename containing vswitchd pid. */
-static char *vswitchd_pidfile;
+/* Command to run (via system()) to reload the vswitchd configuration file. */
+static char *reload_command;
 
 /* Netlink socket to listen for interface changes. */
 static struct nl_sock *rtnl_sock;
     return cfg_has_section("bridge.%s", name);
 }
 
-/* Tell vswitchd reconfigure itself. */
-static void
-force_reconfigure(void)
+static int
+rewrite_and_reload_config(void)
 {
-    pid_t pid;
-
-    pid = read_pidfile(vswitchd_pidfile);
-    if (pid < 0) {
-        VLOG_ERR_RL(&rl, "problem read vswitchd pidfile: %s", 
-                strerror(errno));
-        return;
-    }
-    if (kill(pid, SIGHUP) < 0) {
-        VLOG_ERR_RL(&rl, "problem sending HUP to vswitchd: %s", 
-                strerror(errno));
-        return;
+    if (cfg_is_dirty()) {
+        int error1 = cfg_write();
+        int error2 = cfg_read();
+        int error3 = system(reload_command);
+        if (error3 == -1) {
+            VLOG_ERR_RL(&rl, "failed to execute reload command: %s",
+                        strerror(errno));
+        } else if (error3 != 0) {
+            char *msg = process_status_msg(error3);
+            VLOG_ERR_RL(&rl, "reload command exited with error (%s)", msg);
+            free(msg);
+        }
+        return error1 ? error1 : error2 ? error2 : error3 ? ECHILD : 0;
     }
+    return 0;
 }
 
 /* Go through the configuration file and remove any ports that no longer
             cfg_del_match("bridge.*.port=%s", delete.names[i]);
             cfg_del_match("bonding.*.slave=%s", delete.names[i]);
         }
-        cfg_write();
-        cfg_read();
-
+        rewrite_and_reload_config();
         cfg_unlock();
-
-        /* Ask vswitchd to reconfigure itself. */
-        force_reconfigure();
     } else {
         cfg_unlock();
     }
     svec_destroy(&delete);
 }
 
-/* Modify the existing configuration according to 'act'.  The configuration 
- * file will be modified to reflect these changes.  The caller is
- * responsible for causing vswitchd to actually re-read its configuration. 
- * Returns 0 on success, otherwise a postive errno. */ 
-static int
-modify_config(const char *br_name, const char *port_name, enum bmc_action act)
-{
-    int error1, error2;
 
-    switch (act) {
-    case BMC_ADD_DP: 
-        cfg_add_entry("bridge.%s.port=%s", br_name, br_name);
-        break;
-
-    case BMC_ADD_PORT: 
-        cfg_add_entry("bridge.%s.port=%s", br_name, port_name);
-        break;
-
-    case BMC_DEL_DP: 
-        cfg_del_section("bridge.%s", br_name);
-        break;
-
-    case BMC_DEL_PORT: 
-        cfg_del_entry("bridge.%s.port=%s", br_name, port_name);
-        cfg_del_match("bonding.*.slave=%s", port_name);
-        cfg_del_match("vlan.%s.*", port_name);
-        break;
-    }
-
-    error1 = cfg_write();
-    error2 = cfg_read();
+/* Checks whether a network device named 'name' exists and returns true if so,
+ * false otherwise.
+ *
+ * XXX it is possible that this doesn't entirely accomplish what we want in
+ * context, since vswitchd.conf may cause vswitchd to create or destroy network
+ * devices based on iface.*.internal settings.
+ *
+ * XXX may want to move this to lib/netdev. */
+static bool
+netdev_exists(const char *name)
+{
+    struct stat s;
+    char *filename;
+    int error;
 
-    return error1 ? error1 : error2;
+    filename = xasprintf("/sys/class/net/%s", name);
+    error = stat(filename, &s);
+    free(filename);
+    return !error;
 }
 
-static int 
+static int
 add_bridge(const char *br_name)
 {
-    int retval;
-
     if (bridge_exists(br_name)) {
         VLOG_WARN("addbr %s: bridge %s exists", br_name, br_name);
         return EEXIST;
+    } else if (netdev_exists(br_name)) {
+        VLOG_WARN("addbr %s: cannot create bridge %s because a network device "
+                  "named %s already exists", br_name, br_name, br_name);
+        return EEXIST;
     }
 
-    retval = modify_config(br_name, NULL, BMC_ADD_DP);
-    if (retval) {
-        VLOG_WARN("addbr %s: config file update failed: %s",
-                  br_name, strerror(retval));
-        return retval;
-    }
-
+    cfg_add_entry("bridge.%s.port=%s", br_name, br_name);
     VLOG_INFO("addbr %s: success", br_name);
 
-    /* Ask vswitchd to reconfigure itself. */
-    force_reconfigure();
-
     return 0;
 }
 
 static int 
 del_bridge(const char *br_name)
 {
-    int retval;
-
     if (!bridge_exists(br_name)) {
         VLOG_WARN("delbr %s: no bridge named %s", br_name, br_name);
         return ENXIO;
     }
 
-    retval = modify_config(br_name, NULL, BMC_DEL_DP);
-    if (retval) {
-        VLOG_WARN("delbr %s: config file update failed: %s",
-                  br_name, strerror(retval));
-        return retval;
-    }
-
-    /* Ask vswitchd to reconfigure itself. */
-    force_reconfigure();
-
+    cfg_del_section("bridge.%s", br_name);
     VLOG_INFO("delbr %s: success", br_name);
 
     return 0;
 }
 
-static int 
-handle_bridge_cmd(struct ofpbuf *buffer, bool add)
+static int
+parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name,
+              const char **port_name)
 {
-    int br_act_err;
-    int retval;
-    struct nlattr *attrs[ARRAY_SIZE(brc_dp_policy)];
-    const char *br_name;
-    struct ofpbuf msg;
+    static const struct nl_policy policy[] = {
+        [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
+        [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING, .optional = true },
+    };
+    struct nlattr *attrs[ARRAY_SIZE(policy)];
 
-    if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, brc_dp_policy,
-                         attrs, ARRAY_SIZE(brc_dp_policy))) {
+    if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, policy,
+                         attrs, ARRAY_SIZE(policy))
+        || (port_name && !attrs[BRC_GENL_A_PORT_NAME])) {
         return EINVAL;
     }
 
-    br_name = nl_attr_get(attrs[BRC_GENL_A_DP_NAME]);
-
-    if (add) {
-        br_act_err = add_bridge(br_name);
-    } else {
-        br_act_err = del_bridge(br_name);
+    *seq = ((struct nlmsghdr *) buffer->data)->nlmsg_seq;
+    *br_name = nl_attr_get_string(attrs[BRC_GENL_A_DP_NAME]);
+    if (port_name) {
+        *port_name = nl_attr_get_string(attrs[BRC_GENL_A_PORT_NAME]);
     }
+    return 0;
+}
 
-    /* Notify the brcompat kernel module of the result. */
+static void
+send_reply(uint32_t seq, int error)
+{
+    struct ofpbuf msg;
+    int retval;
+
+    /* Compose reply. */
     ofpbuf_init(&msg, 0);
-    nl_msg_put_genlmsghdr(&msg, brc_sock, 32, brc_family, NLM_F_REQUEST, 
-            BRC_GENL_C_DP_RESULT, 1);
-    nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, br_act_err);
+    nl_msg_put_genlmsghdr(&msg, brc_sock, 32, brc_family, NLM_F_REQUEST,
+                          BRC_GENL_C_DP_RESULT, 1);
+    ((struct nlmsghdr *) msg.data)->nlmsg_seq = seq;
+    nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, error);
+
+    /* Send reply. */
     retval = nl_sock_send(brc_sock, &msg, false);
     if (retval) {
-        VLOG_WARN_RL(&rl, "handle_bridge_cmd: %s", strerror(retval));
+        VLOG_WARN_RL(&rl, "replying to brcompat request: %s",
+                     strerror(retval));
     }
     ofpbuf_uninit(&msg);
+}
 
-    return br_act_err;
+static int
+handle_bridge_cmd(struct ofpbuf *buffer, bool add)
+{
+    const char *br_name;
+    uint32_t seq;
+    int error;
+
+    error = parse_command(buffer, &seq, &br_name, NULL);
+    if (!error) {
+        error = add ? add_bridge(br_name) : del_bridge(br_name);
+        if (!error) {
+            error = rewrite_and_reload_config();
+        }
+        send_reply(seq, error);
+    }
+    return error;
 }
 
 static const struct nl_policy brc_port_policy[] = {
     [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING },
 };
 
-static int 
+static void
+del_port(const char *br_name, const char *port_name)
+{
+    cfg_del_entry("bridge.%s.port=%s", br_name, port_name);
+    cfg_del_match("bonding.*.slave=%s", port_name);
+    cfg_del_match("vlan.%s.*", port_name);
+}
+
+static int
 handle_port_cmd(struct ofpbuf *buffer, bool add)
 {
     const char *cmd_name = add ? "add-if" : "del-if";
-    struct nlattr *attrs[ARRAY_SIZE(brc_port_policy)];
     const char *br_name, *port_name;
-    int retval;
-
-    if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, brc_port_policy,
-                         attrs, ARRAY_SIZE(brc_port_policy))) {
-        return EINVAL;
-    }
-
-    br_name   = nl_attr_get(attrs[BRC_GENL_A_DP_NAME]);
-    port_name = nl_attr_get(attrs[BRC_GENL_A_PORT_NAME]);
-
-    if (!bridge_exists(br_name)) {
-        VLOG_WARN("%s %s %s: no bridge named %s",
-                  cmd_name, br_name, port_name, br_name);
-        return EINVAL;
-    }
+    uint32_t seq;
+    int error;
 
-    if (add) {
-        retval = modify_config(br_name, port_name, BMC_ADD_PORT);
-    } else {
-        retval = modify_config(br_name, port_name, BMC_DEL_PORT);
-    }
-    if (retval) {
-        VLOG_WARN("%s %s %s: config file update failed: %s",
-                  cmd_name, br_name, port_name, strerror(retval));
-        return retval;
+    error = parse_command(buffer, &seq, &br_name, &port_name);
+    if (!error) {
+        if (!bridge_exists(br_name)) {
+            VLOG_WARN("%s %s %s: no bridge named %s",
+                      cmd_name, br_name, port_name, br_name);
+            error = EINVAL;
+        } else if (!netdev_exists(port_name)) {
+            VLOG_WARN("%s %s %s: no network device named %s",
+                      cmd_name, br_name, port_name, port_name);
+            error = EINVAL;
+        } else {
+            if (add) {
+                cfg_add_entry("bridge.%s.port=%s", br_name, port_name);
+            } else {
+                del_port(br_name, port_name);
+            }
+            VLOG_INFO("%s %s %s: success", cmd_name, br_name, port_name);
+            error = rewrite_and_reload_config();
+        }
+        send_reply(seq, error);
     }
 
-    VLOG_INFO("%s %s %s: success", cmd_name, br_name, port_name);
-
-    /* Ask vswitchd to reconfigure itself. */
-    force_reconfigure();
-
-    return 0;
+    return error;
 }
 
-static int 
+static int
 brc_recv_update(void)
 {
     int retval;
             cfg_get_all_keys(&ports, "bridge.%s.port", br_name);
             svec_sort(&ports);
             if (svec_contains(&ports, port_name)) {
-                 modify_config(br_name, port_name, BMC_DEL_PORT);
+                del_port(br_name, port_name);
+                rewrite_and_reload_config();
             }
             cfg_unlock();
         }
         }
     }
 
-    /* If a vswitchd pidfile was not explicitly set, assume the default
-     * location. */
-    if (!vswitchd_pidfile) {
-        vswitchd_pidfile = make_pidfile_name("vswitchd.pid");
-    }
-
     cfg_read();
 
     for (;;) {
     enum {
         OPT_LOCK_TIMEOUT = UCHAR_MAX + 1,
         OPT_PRUNE_TIMEOUT,
-        OPT_VSWITCHD_PIDFILE,
+        OPT_RELOAD_COMMAND,
         VLOG_OPTION_ENUMS,
         LEAK_CHECKER_OPTION_ENUMS
     };
         {"version",          no_argument, 0, 'V'},
         {"lock-timeout",     required_argument, 0, OPT_LOCK_TIMEOUT},
         {"prune-timeout",    required_argument, 0, OPT_PRUNE_TIMEOUT},
-        {"vswitchd-pidfile", required_argument, 0, OPT_VSWITCHD_PIDFILE},
+        {"reload-command",   required_argument, 0, OPT_RELOAD_COMMAND},
         DAEMON_LONG_OPTIONS,
         VLOG_LONG_OPTIONS,
         LEAK_CHECKER_LONG_OPTIONS,
     char *short_options = long_options_to_short_options(long_options);
     int error;
 
+    reload_command = xasprintf("ovs-appctl "
+                               "-t %s/vswitchd.`cat %s/vswitchd.pid`.ctl "
+                               "-e vswitchd/reload 2>&1 "
+                               "| /usr/bin/logger -t brcompatd-reload",
+                               ovs_rundir, ovs_rundir);
     for (;;) {
         int c;
 
             prune_timeout = atoi(optarg) * 1000;
             break;
 
-        case OPT_VSWITCHD_PIDFILE:
-            vswitchd_pidfile = optarg;
+        case OPT_RELOAD_COMMAND:
+            reload_command = optarg;
             break;
 
         VLOG_OPTION_HANDLERS
            "CONFIG is the configuration file used by vswitchd.\n",
            program_name, program_name);
     printf("\nConfiguration options:\n"
-           "  --vswitchd-pidfile=FILE use FILE as pid file for vswitchd\n"
+           "  --reload-command=COMMAND  command to run to reload vswitchd\n"
            "  --prune-timeout=SECS    wait at most SECS before pruning ports\n"
            "  --lock-timeout=MSECS    wait at most MSECS for CONFIG to unlock\n"
           );
     daemon_usage();
     vlog_usage();
-    printf("Other options:\n"
+    printf("\nOther options:\n"
            "  -h, --help              display this help message\n"
            "  -V, --version           display version information\n");
     leak_checker_usage();
+    printf("\nThe default reload command is:\n%s\n", reload_command);
     exit(EXIT_SUCCESS);
 }
 
 
         action "Starting brcompatd ($strace_opt$valgrind_opt)" true
         (nice -n "$VSWITCHD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" -P$BRCOMPATD_PIDFILE --vswitchd-pidfile=$VSWITCHD_PIDFILE -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF") &
     else
-        action "Starting brcompatd" nice -n "$BRCOMPATD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" -P$BRCOMPATD_PIDFILE --vswitchd-pidfile=$VSWITCHD_PIDFILE -D -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
+        action "Starting brcompatd" nice -n "$BRCOMPATD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" -P$BRCOMPATD_PIDFILE -D -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
     fi
 }