-/* 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.
struct netdev *netdev; /* Network device. */
const char *type; /* Usually same as cfg->type. */
const struct ovsrec_interface *cfg;
- bool need_refresh; /* Refresh iface after create. */
};
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 void iface_refresh_type(struct iface *);
+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 *);
static struct iface *iface_lookup(const struct bridge *, const char *name);
static struct iface *iface_find(const char *name);
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));
}
}
* 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);
}
}
}
}
-/* 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 ofproto_port ofproto_port;
- 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;
- iface->need_refresh = true;
- hmap_insert(&br->iface_by_name, &iface->name_node,
- hash_string(iface->name, 0));
- list_push_back(&port->ifaces, &iface->port_elem);
- iface_refresh_type(iface);
- 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);
- /* Populate stats columns in new Interface rows. */
- if (iface->netdev && iface->need_refresh) {
- iface_refresh_stats(iface);
- iface_refresh_status(iface);
- iface->need_refresh = false;
+ /* 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;
}
- /* 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 {
- 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);
+ /* Get or create the port structure. */
+ port = port_lookup(br, port_cfg->name);
+ if (!port) {
+ port = port_create(br, port_cfg);
}
- if (list_is_empty(&port->ifaces)) {
- port_destroy(port);
- return;
- }
+ /* 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;
+
+ iface_set_ofp_port(iface, ofp_port);
+
+ /* 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)) {
+ struct ofproto_port ofproto_port;
+
if (ofproto_port_query_by_name(br->ofproto, port->name,
&ofproto_port)) {
struct netdev *netdev;
ofproto_port_destroy(&ofproto_port);
}
}
+
+ return true;
}
/* Set Flow eviction threshold */
}
}
+ /* Update iface->cfg and iface->type in interfaces that still exist.
+ * Add new interfaces to creation queue. */
SHASH_FOR_EACH (port_node, &new_ports) {
const struct ovsrec_port *port = port_node->data;
size_t i;
if (iface) {
iface->cfg = cfg;
- iface_refresh_type(iface);
+ iface->type = iface_get_type(cfg, br->cfg);
} else {
bridge_queue_if_cfg(br, cfg, port);
}
sset_init(&new_ifaces);
for (i = 0; i < port->cfg->n_interfaces; i++) {
const char *name = port->cfg->interfaces[i]->name;
- const char *type = port->cfg->interfaces[i]->name;
+ const char *type = port->cfg->interfaces[i]->type;
if (strcmp(type, "null")) {
sset_add(&new_ifaces, name);
}
\f
/* Interface functions. */
-static void
-iface_refresh_type(struct iface *iface)
-{
- /* Determine interface type. The local port always has type
- * "internal". Other ports take their type from the database and
- * default to "system" if none is specified. */
- iface->type = (!strcmp(iface->name, iface->port->bridge->name) ? "internal"
- : iface->cfg->type[0] ? iface->cfg->type
- : "system");
+/* Returns the correct network device type for interface 'iface' in bridge
+ * 'br'. */
+static const char *
+iface_get_type(const struct ovsrec_interface *iface,
+ const struct ovsrec_bridge *br)
+{
+ /* The local port always has type "internal". Other ports take their type
+ * from the database and default to "system" if none is specified. */
+ return (!strcmp(iface->name, br->name) ? "internal"
+ : iface->type[0] ? iface->type
+ : "system");
}
static void