brcompat: Move more brcompat implementation into userspace.
authorBen Pfaff <blp@nicira.com>
Thu, 21 May 2009 16:31:11 +0000 (09:31 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 21 May 2009 16:42:54 +0000 (09:42 -0700)
Until now, the brcompat kernel module has had to do more of the work of
simulating a bridge than is ideal, because brcompatd was not able to find
out when the vswitchd had finished reloading.  It is important that the
bridge reconfiguration be complete by the time that the bridge ioctl
returns, so this had to be done from the kernel.

Now it is possible for brcompatd to wait for the reload to finish (by
using the equivalent of "ovs-appctl -e vswitchd/reload") so this part of
the work can be done in userspace.  This commit moves it there.

This commit also moves to userspace some error-checking work, in particular
checking whether a bridge or a port operation is correct.  This is
important for an upcoming commit, where brcompatd wishes to send a fake
success return value for an operation that must be ignored.  When the error
checking is done in the kernel, it defeats this purpose.

datapath/brcompat.c
vswitchd/brcompatd.8.in
vswitchd/brcompatd.c
xenserver/etc_init.d_vswitch

index 5b68c8b4c845c37e8310dbe84262be982b2145fd..3958459022f2e4a6a72b6b72b7579d77ea085f89 100644 (file)
 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)
@@ -66,49 +66,55 @@ get_port_ifindices(struct datapath *dp, int *ifindices, 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;
@@ -124,88 +130,101 @@ 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:
-       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;
@@ -223,16 +242,9 @@ brc_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                        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;
@@ -297,13 +309,23 @@ static struct nla_policy brc_genl_policy[BRC_GENL_A_MAX + 1] = {
 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 = {
@@ -322,148 +344,53 @@ static struct genl_ops brc_genl_ops_set_proc = {
        .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)
@@ -521,6 +448,11 @@ __init brc_init(void)
        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);
index b72b1d9f34f3d6c020e8458b7812bbca1f4d139a..8f47525f7c9e0134a510125b4e86cf02eb064d11 100644 (file)
@@ -16,12 +16,11 @@ that attach to them.  It modifies \fIconfig\fR and forces
 \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
 .
index 8e450727a471f77bb19863a3b58393ff55dbe5c2..08151f88aeceb38325588167c70c12959a9d610f 100644 (file)
@@ -36,6 +36,7 @@
 #include "cfg.h"
 #include "command-line.h"
 #include "daemon.h"
+#include "dirs.h"
 #include "dpif.h"
 #include "fatal-signal.h"
 #include "fault.h"
@@ -69,8 +70,6 @@ enum bmc_action {
 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);
 
@@ -85,8 +84,8 @@ static int prune_timeout = 5000;
 /* 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;
@@ -177,23 +176,24 @@ bridge_exists(const char *name)
     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
@@ -272,138 +272,130 @@ prune_ports(void)
             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[] = {
@@ -411,48 +403,48 @@ 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;
@@ -577,7 +569,8 @@ rtnl_recv_update(void)
             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();
         }
@@ -620,12 +613,6 @@ main(int argc, char *argv[])
         }
     }
 
-    /* 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 (;;) {
@@ -664,7 +651,7 @@ parse_options(int argc, char *argv[])
     enum {
         OPT_LOCK_TIMEOUT = UCHAR_MAX + 1,
         OPT_PRUNE_TIMEOUT,
-        OPT_VSWITCHD_PIDFILE,
+        OPT_RELOAD_COMMAND,
         VLOG_OPTION_ENUMS,
         LEAK_CHECKER_OPTION_ENUMS
     };
@@ -673,7 +660,7 @@ parse_options(int argc, char *argv[])
         {"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,
@@ -682,6 +669,11 @@ parse_options(int argc, char *argv[])
     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;
 
@@ -707,8 +699,8 @@ parse_options(int argc, char *argv[])
             prune_timeout = atoi(optarg) * 1000;
             break;
 
-        case OPT_VSWITCHD_PIDFILE:
-            vswitchd_pidfile = optarg;
+        case OPT_RELOAD_COMMAND:
+            reload_command = optarg;
             break;
 
         VLOG_OPTION_HANDLERS
@@ -748,16 +740,17 @@ usage(void)
            "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);
 }
 
index 162f30b308a2e09c9794df5777e4a8e77a6b9633..27e582f55b325f50e339838828e937b6ddf511c3 100755 (executable)
@@ -247,7 +247,7 @@ function start_brcompatd {
         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
 }