Global replace of Nicira Networks.
[sliver-openvswitch.git] / vswitchd / bridge.c
index 208396b..13eb80b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -227,7 +227,7 @@ static bool mirror_configure(struct mirror *);
 static void mirror_refresh_stats(struct mirror *);
 
 static void iface_configure_lacp(struct iface *, struct lacp_slave_settings *);
-static void iface_create(struct bridge *, struct if_cfg *, int ofp_port);
+static bool iface_create(struct bridge *, struct if_cfg *, int ofp_port);
 static const char *iface_get_type(const struct ovsrec_interface *,
                                   const struct ovsrec_bridge *);
 static void iface_destroy(struct iface *);
@@ -1144,16 +1144,85 @@ iface_set_ofp_port(struct iface *iface, int ofp_port)
     iface_set_ofport(iface->cfg, ofp_port);
 }
 
-static void
-bridge_ofproto_port_del(struct bridge *br, struct ofproto_port ofproto_port)
+/* Configures 'netdev' based on the "options" column in 'iface_cfg'.
+ * Returns 0 if successful, otherwise a positive errno value. */
+static int
+iface_set_netdev_config(const struct ovsrec_interface *iface_cfg,
+                        struct netdev *netdev)
 {
-    int error = ofproto_port_del(br->ofproto, ofproto_port.ofp_port);
+    struct shash args;
+    int error;
+
+    shash_init(&args);
+    shash_from_ovs_idl_map(iface_cfg->key_options,
+                           iface_cfg->value_options,
+                           iface_cfg->n_options, &args);
+    error = netdev_set_config(netdev, &args);
+    shash_destroy(&args);
+
     if (error) {
-        VLOG_WARN("bridge %s: failed to remove %s interface (%s)",
-                  br->name, ofproto_port.name, strerror(error));
+        VLOG_WARN("could not configure network device %s (%s)",
+                  iface_cfg->name, strerror(error));
+    }
+    return error;
+}
+
+/* This function determines whether 'ofproto_port', which is attached to
+ * br->ofproto's datapath, is one that we want in 'br'.
+ *
+ * If it is, it returns true, after creating an iface (if necessary),
+ * configuring the iface's netdev according to the iface's options, and setting
+ * iface's ofp_port member to 'ofproto_port->ofp_port'.
+ *
+ * If, on the other hand, 'port' should be removed, it returns false.  The
+ * caller should later detach the port from br->ofproto. */
+static bool
+bridge_refresh_one_ofp_port(struct bridge *br,
+                            const struct ofproto_port *ofproto_port)
+{
+    const char *name = ofproto_port->name;
+    const char *type = ofproto_port->type;
+    uint16_t ofp_port = ofproto_port->ofp_port;
+
+    struct iface *iface = iface_lookup(br, name);
+    if (iface) {
+        /* Check that the name-to-number mapping is one-to-one. */
+        if (iface->ofp_port >= 0) {
+            VLOG_WARN("bridge %s: interface %s reported twice",
+                      br->name, name);
+            return false;
+        } else if (iface_from_ofp_port(br, ofp_port)) {
+            VLOG_WARN("bridge %s: interface %"PRIu16" reported twice",
+                      br->name, ofp_port);
+            return false;
+        }
+
+        /* There's a configured interface named 'name'. */
+        if (strcmp(type, iface->type)
+            || iface_set_netdev_config(iface->cfg, iface->netdev)) {
+            /* It's the wrong type, or it's the right type but can't be
+             * configured as the user requested, so we must destroy it. */
+            return false;
+        } else {
+            /* It's the right type and configured correctly.  keep it. */
+            iface_set_ofp_port(iface, ofp_port);
+            return true;
+        }
+    } else if (bridge_has_bond_fake_iface(br, name)
+               && !strcmp(type, "internal")) {
+        /* It's a bond fake iface.  Keep it. */
+        return true;
     } else {
-        VLOG_INFO("bridge %s: removed interface %s (%d)", br->name,
-                  ofproto_port.name, ofproto_port.ofp_port);
+        /* There's no configured interface named 'name', but there might be an
+         * interface of that name queued to be created.
+         *
+         * If there is, and it has the correct type, then try to configure it
+         * and add it.  If that's successful, we'll keep it.  Otherwise, we'll
+         * delete it and later try to re-add it. */
+        struct if_cfg *if_cfg = if_cfg_lookup(br, name);
+        return (if_cfg
+                && !strcmp(type, iface_get_type(if_cfg->cfg, br->cfg))
+                && iface_create(br, if_cfg, ofp_port));
     }
 }
 
@@ -1180,33 +1249,10 @@ bridge_refresh_ofp_port(struct bridge *br)
      * already exist in the datapath and promote them to full fledged "struct
      * iface"s.  Mark ports in the datapath which don't belong as garbage. */
     OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
-        struct iface *iface = iface_lookup(br, ofproto_port.name);
-        if (iface) {
-            if (iface->ofp_port >= 0) {
-                VLOG_WARN("bridge %s: interface %s reported twice",
-                          br->name, ofproto_port.name);
-            } else if (iface_from_ofp_port(br, ofproto_port.ofp_port)) {
-                VLOG_WARN("bridge %s: interface %"PRIu16" reported twice",
-                          br->name, ofproto_port.ofp_port);
-            } else if (!strcmp(ofproto_port.type, iface->type)) {
-                iface_set_ofp_port(iface, ofproto_port.ofp_port);
-            } else {
-                /* Port has incorrect type so delete it later. */
-            }
-        } else {
-            struct if_cfg *if_cfg = if_cfg_lookup(br, ofproto_port.name);
-
-            if (if_cfg) {
-                iface_create(br, if_cfg, ofproto_port.ofp_port);
-            } else if (bridge_has_bond_fake_iface(br, ofproto_port.name)
-                       && strcmp(ofproto_port.type, "internal")) {
-                /* Bond fake iface with the wrong type. */
-                bridge_ofproto_port_del(br, ofproto_port);
-            } else {
-                struct ofpp_garbage *garbage = xmalloc(sizeof *garbage);
-                garbage->ofp_port = ofproto_port.ofp_port;
-                list_push_front(&br->ofpp_garbage, &garbage->list_node);
-            }
+        if (!bridge_refresh_one_ofp_port(br, &ofproto_port)) {
+            struct ofpp_garbage *garbage = xmalloc(sizeof *garbage);
+            garbage->ofp_port = ofproto_port.ofp_port;
+            list_push_front(&br->ofpp_garbage, &garbage->list_node);
         }
     }
 
@@ -1229,124 +1275,117 @@ bridge_refresh_ofp_port(struct bridge *br)
     }
 }
 
-/* Creates a new iface on 'br' based on 'if_cfg'.  The new iface has OpenFlow
- * port number 'ofp_port'.  If ofp_port is negative, an OpenFlow port is
- * automatically allocated for the iface.  Takes ownership of and
- * deallocates 'if_cfg'. */
-static void
-iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
+/* Opens a network device for 'iface_cfg' and configures it.  If '*ofp_portp'
+ * is negative, adds the network device to br->ofproto and stores the OpenFlow
+ * port number in '*ofp_portp'; otherwise leaves br->ofproto and '*ofp_portp'
+ * untouched.
+ *
+ * If successful, returns 0 and stores the network device in '*netdevp'.  On
+ * failure, returns a positive errno value and stores NULL in '*netdevp'. */
+static int
+iface_do_create(const struct bridge *br,
+                const struct ovsrec_interface *iface_cfg,
+                const struct ovsrec_port *port_cfg,
+                int *ofp_portp, struct netdev **netdevp)
 {
-    struct iface *iface;
-    struct port *port;
+    struct netdev *netdev;
     int error;
 
-    assert(!iface_lookup(br, if_cfg->cfg->name));
-
-    port = port_lookup(br, if_cfg->parent->name);
-    if (!port) {
-        port = port_create(br, if_cfg->parent);
+    error = netdev_open(iface_cfg->name,
+                        iface_get_type(iface_cfg, br->cfg), &netdev);
+    if (error) {
+        VLOG_WARN("could not open network device %s (%s)",
+                  iface_cfg->name, strerror(error));
+        goto error;
     }
 
-    iface = xzalloc(sizeof *iface);
-    iface->port = port;
-    iface->name = xstrdup(if_cfg->cfg->name);
-    iface->ofp_port = -1;
-    iface->netdev = NULL;
-    iface->cfg = if_cfg->cfg;
-    hmap_insert(&br->iface_by_name, &iface->name_node,
-                hash_string(iface->name, 0));
-    list_push_back(&port->ifaces, &iface->port_elem);
-    iface->type = iface_get_type(iface->cfg, br->cfg);
-    if (ofp_port >= 0) {
-        iface_set_ofp_port(iface, ofp_port);
+    error = iface_set_netdev_config(iface_cfg, netdev);
+    if (error) {
+        goto error;
     }
 
-    hmap_remove(&br->if_cfg_todo, &if_cfg->hmap_node);
-    free(if_cfg);
-    if_cfg = NULL;
+    if (*ofp_portp < 0) {
+        uint16_t ofp_port;
 
-    error = netdev_open(iface->name, iface->type, &iface->netdev);
-    if (error) {
-        VLOG_WARN("could not open network device %s (%s)", iface->name,
-                  strerror(error));
+        error = ofproto_port_add(br->ofproto, netdev, &ofp_port);
+        if (error) {
+            goto error;
+        }
+        *ofp_portp = ofp_port;
+
+        VLOG_INFO("bridge %s: added interface %s on port %d",
+                  br->name, iface_cfg->name, *ofp_portp);
+    } else {
+        VLOG_DBG("bridge %s: interface %s is on port %d",
+                 br->name, iface_cfg->name, *ofp_portp);
     }
 
-    if (iface->netdev
-        && port->cfg->vlan_mode
-        && !strcmp(port->cfg->vlan_mode, "splinter")) {
-        netdev_turn_flags_on(iface->netdev, NETDEV_UP, true);
+    if (port_cfg->vlan_mode && !strcmp(port_cfg->vlan_mode, "splinter")) {
+        netdev_turn_flags_on(netdev, NETDEV_UP, true);
     }
 
-    /* Configure the netdev. */
-    if (iface->netdev) {
-        struct shash args;
+    *netdevp = netdev;
+    return 0;
 
-        shash_init(&args);
-        shash_from_ovs_idl_map(iface->cfg->key_options,
-                               iface->cfg->value_options,
-                               iface->cfg->n_options, &args);
-        error = netdev_set_config(iface->netdev, &args);
-        shash_destroy(&args);
+error:
+    *netdevp = NULL;
+    netdev_close(netdev);
+    return error;
+}
 
-        if (error) {
-            VLOG_WARN("could not configure network device %s (%s)",
-                      iface->name, strerror(error));
-            netdev_close(iface->netdev);
-            iface->netdev = NULL;
-        }
-    }
+/* Creates a new iface on 'br' based on 'if_cfg'.  The new iface has OpenFlow
+ * port number 'ofp_port'.  If ofp_port is negative, an OpenFlow port is
+ * automatically allocated for the iface.  Takes ownership of and
+ * deallocates 'if_cfg'.
+ *
+ * Return true if an iface is successfully created, false otherwise. */
+static bool
+iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
+{
+    const struct ovsrec_interface *iface_cfg = if_cfg->cfg;
+    const struct ovsrec_port *port_cfg = if_cfg->parent;
 
-    /* Add the port, if necessary. */
-    if (iface->netdev && iface->ofp_port < 0) {
-        uint16_t new_ofp_port;
-        int error;
+    struct netdev *netdev;
+    struct iface *iface;
+    struct port *port;
+    int error;
 
-        error = ofproto_port_add(br->ofproto, iface->netdev, &new_ofp_port);
-        if (!error) {
-            VLOG_INFO("bridge %s: added interface %s (%d)", br->name,
-                      iface->name, new_ofp_port);
-            iface_set_ofp_port(iface, new_ofp_port);
-        } else {
-            netdev_close(iface->netdev);
-            iface->netdev = NULL;
-        }
+    /* Get rid of 'if_cfg' itself.  We already copied out the interesting
+     * bits. */
+    hmap_remove(&br->if_cfg_todo, &if_cfg->hmap_node);
+    free(if_cfg);
+
+    /* Do the bits that can fail up front. */
+    assert(!iface_lookup(br, iface_cfg->name));
+    error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev);
+    if (error) {
+        iface_clear_db_record(iface_cfg);
+        return false;
     }
 
-    /* Initially populate stats columns. */
-    if (iface->netdev) {
-        iface_refresh_stats(iface);
-        iface_refresh_status(iface);
+    /* Get or create the port structure. */
+    port = port_lookup(br, port_cfg->name);
+    if (!port) {
+        port = port_create(br, port_cfg);
     }
 
-    /* Delete the iface if we failed. */
-    if (iface->netdev && iface->ofp_port >= 0) {
-        VLOG_DBG("bridge %s: interface %s is on port %d",
-                 br->name, iface->name, iface->ofp_port);
-    } else {
-        struct ofproto_port ofproto_port;
+    /* Create the iface structure. */
+    iface = xzalloc(sizeof *iface);
+    list_push_back(&port->ifaces, &iface->port_elem);
+    hmap_insert(&br->iface_by_name, &iface->name_node,
+                hash_string(iface_cfg->name, 0));
+    iface->port = port;
+    iface->name = xstrdup(iface_cfg->name);
+    iface->ofp_port = -1;
+    iface->netdev = netdev;
+    iface->type = iface_get_type(iface_cfg, br->cfg);
+    iface->cfg = iface_cfg;
 
-        if (iface->netdev) {
-            VLOG_ERR("bridge %s: missing %s interface, dropping",
-                     br->name, iface->name);
-        } else {
-            /* We already reported a related error, don't bother
-             * duplicating it. */
-        }
-        if (!ofproto_port_query_by_name(br->ofproto, port->name,
-                                        &ofproto_port)) {
-            VLOG_INFO("bridge %s: removed interface %s (%d)",
-                      br->name, port->name, ofproto_port.ofp_port);
-            bridge_ofproto_port_del(br, ofproto_port);
-            ofproto_port_destroy(&ofproto_port);
-        }
-        iface_clear_db_record(iface->cfg);
-        iface_destroy(iface);
-    }
+    iface_set_ofp_port(iface, ofp_port);
 
-    if (list_is_empty(&port->ifaces)) {
-        port_destroy(port);
-        return;
-    }
+    /* Populate initial status in database. */
+    iface_refresh_stats(iface);
+    iface_refresh_status(iface);
 
     /* Add bond fake iface if necessary. */
     if (port_is_bond_fake_iface(port)) {
@@ -1370,6 +1409,8 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
             ofproto_port_destroy(&ofproto_port);
         }
     }
+
+    return true;
 }
 
 /* Set Flow eviction threshold */