/* Map from datapath name to struct ofproto, for use by unixctl commands. */
static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos);
+/* Initial mappings of port to OpenFlow number mappings. */
+static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports);
+
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-static void
-ofproto_initialize(void)
+/* Must be called to initialize the ofproto library.
+ *
+ * The caller may pass in 'iface_hints', which contains an shash of
+ * "iface_hint" elements indexed by the interface's name. The provider
+ * may use these hints to describe the startup configuration in order to
+ * reinitialize its state. The caller owns the provided data, so a
+ * provider will make copies of anything required. An ofproto provider
+ * will remove any existing state that is not described by the hint, and
+ * may choose to remove it all. */
+void
+ofproto_init(const struct shash *iface_hints)
{
- static bool inited;
+ struct shash_node *node;
+ size_t i;
+
+ ofproto_class_register(&ofproto_dpif_class);
- if (!inited) {
- inited = true;
- ofproto_class_register(&ofproto_dpif_class);
+ /* Make a local copy, since we don't own 'iface_hints' elements. */
+ SHASH_FOR_EACH(node, iface_hints) {
+ const struct iface_hint *orig_hint = node->data;
+ struct iface_hint *new_hint = xmalloc(sizeof *new_hint);
+ const char *br_type = ofproto_normalize_type(orig_hint->br_type);
+
+ new_hint->br_name = xstrdup(orig_hint->br_name);
+ new_hint->br_type = xstrdup(br_type);
+ new_hint->ofp_port = orig_hint->ofp_port;
+
+ shash_add(&init_ofp_ports, node->name, new_hint);
+ }
+
+ for (i = 0; i < n_ofproto_classes; i++) {
+ ofproto_classes[i]->init(&init_ofp_ports);
}
}
{
size_t i;
- ofproto_initialize();
for (i = 0; i < n_ofproto_classes; i++) {
const struct ofproto_class *class = ofproto_classes[i];
struct sset types;
{
size_t i;
- ofproto_initialize();
for (i = 0; i < n_ofproto_classes; i++) {
ofproto_classes[i]->enumerate_types(types);
}
const struct ofproto_class *class;
struct ofproto *ofproto;
int error;
+ int i;
*ofprotop = NULL;
- ofproto_initialize();
ofproto_unixctl_init();
datapath_type = ofproto_normalize_type(datapath_type);
hash_string(ofproto->name, 0));
ofproto->datapath_id = 0;
ofproto_set_flow_eviction_threshold(ofproto,
- OFPROTO_FLOW_EVICTON_THRESHOLD_DEFAULT);
+ OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT);
ofproto->forward_bpdu = false;
ofproto->fallback_dpid = pick_fallback_dpid();
ofproto->mfr_desc = xstrdup(DEFAULT_MFR_DESC);
ofproto->frag_handling = OFPC_FRAG_NORMAL;
hmap_init(&ofproto->ports);
shash_init(&ofproto->port_by_name);
+ simap_init(&ofproto->ofp_requests);
ofproto->max_ports = OFPP_MAX;
ofproto->tables = NULL;
ofproto->n_tables = 0;
return error;
}
+ /* The "max_ports" member should have been set by ->construct(ofproto).
+ * Port 0 is not a valid OpenFlow port, so mark that as unavailable. */
+ ofproto->ofp_port_ids = bitmap_allocate(ofproto->max_ports);
+ bitmap_set1(ofproto->ofp_port_ids, 0);
+
+ /* Check that hidden tables, if any, are at the end. */
assert(ofproto->n_tables);
+ for (i = 0; i + 1 < ofproto->n_tables; i++) {
+ enum oftable_flags flags = ofproto->tables[i].flags;
+ enum oftable_flags next_flags = ofproto->tables[i + 1].flags;
+
+ assert(!(flags & OFTABLE_HIDDEN) || next_flags & OFTABLE_HIDDEN);
+ }
ofproto->datapath_id = pick_datapath_id(ofproto);
init_ports(ofproto);
free(ofproto->dp_desc);
hmap_destroy(&ofproto->ports);
shash_destroy(&ofproto->port_by_name);
+ bitmap_free(ofproto->ofp_port_ids);
+ simap_destroy(&ofproto->ofp_requests);
OFPROTO_FOR_EACH_TABLE (table, ofproto) {
oftable_destroy(table);
}
}
+int
+ofproto_type_run(const char *datapath_type)
+{
+ const struct ofproto_class *class;
+ int error;
+
+ datapath_type = ofproto_normalize_type(datapath_type);
+ class = ofproto_class_find__(datapath_type);
+
+ error = class->type_run ? class->type_run(datapath_type) : 0;
+ if (error && error != EAGAIN) {
+ VLOG_ERR_RL(&rl, "%s: type_run failed (%s)",
+ datapath_type, strerror(error));
+ }
+ return error;
+}
+
+int
+ofproto_type_run_fast(const char *datapath_type)
+{
+ const struct ofproto_class *class;
+ int error;
+
+ datapath_type = ofproto_normalize_type(datapath_type);
+ class = ofproto_class_find__(datapath_type);
+
+ error = class->type_run_fast ? class->type_run_fast(datapath_type) : 0;
+ if (error && error != EAGAIN) {
+ VLOG_ERR_RL(&rl, "%s: type_run_fast failed (%s)",
+ datapath_type, strerror(error));
+ }
+ return error;
+}
+
+void
+ofproto_type_wait(const char *datapath_type)
+{
+ const struct ofproto_class *class;
+
+ datapath_type = ofproto_normalize_type(datapath_type);
+ class = ofproto_class_find__(datapath_type);
+
+ if (class->type_wait) {
+ class->type_wait(datapath_type);
+ }
+}
+
int
ofproto_run(struct ofproto *p)
{
return dump->error == EOF ? 0 : dump->error;
}
-/* Attempts to add 'netdev' as a port on 'ofproto'. If successful, returns 0
- * and sets '*ofp_portp' to the new port's OpenFlow port number (if 'ofp_portp'
- * is non-null). On failure, returns a positive errno value and sets
- * '*ofp_portp' to OFPP_NONE (if 'ofp_portp' is non-null). */
+/* Attempts to add 'netdev' as a port on 'ofproto'. If 'ofp_portp' is
+ * non-null and '*ofp_portp' is not OFPP_NONE, attempts to use that as
+ * the port's OpenFlow port number.
+ *
+ * If successful, returns 0 and sets '*ofp_portp' to the new port's
+ * OpenFlow port number (if 'ofp_portp' is non-null). On failure,
+ * returns a positive errno value and sets '*ofp_portp' to OFPP_NONE (if
+ * 'ofp_portp' is non-null). */
int
ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
uint16_t *ofp_portp)
{
- uint16_t ofp_port;
+ uint16_t ofp_port = ofp_portp ? *ofp_portp : OFPP_NONE;
int error;
- error = ofproto->ofproto_class->port_add(ofproto, netdev, &ofp_port);
+ error = ofproto->ofproto_class->port_add(ofproto, netdev);
if (!error) {
- update_port(ofproto, netdev_get_name(netdev));
+ const char *netdev_name = netdev_get_name(netdev);
+
+ simap_put(&ofproto->ofp_requests, netdev_name, ofp_port);
+ update_port(ofproto, netdev_name);
}
if (ofp_portp) {
- *ofp_portp = error ? OFPP_NONE : ofp_port;
+ struct ofproto_port ofproto_port;
+
+ ofproto_port_query_by_name(ofproto, netdev_get_name(netdev),
+ &ofproto_port);
+ *ofp_portp = error ? OFPP_NONE : ofproto_port.ofp_port;
+ ofproto_port_destroy(&ofproto_port);
}
return error;
}
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
+ struct simap_node *ofp_request_node;
int error;
+ ofp_request_node = simap_find(&ofproto->ofp_requests, name);
+ if (ofp_request_node) {
+ simap_delete(&ofproto->ofp_requests, ofp_request_node);
+ }
+
error = ofproto->ofproto_class->port_del(ofproto, ofp_port);
if (!error && ofport) {
/* 'name' is the netdev's name and update_port() is going to close the
sset_destroy(&devnames);
}
+static uint16_t
+alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name)
+{
+ uint16_t ofp_port;
+
+ ofp_port = simap_get(&ofproto->ofp_requests, netdev_name);
+ ofp_port = ofp_port ? ofp_port : OFPP_NONE;
+
+ if (ofp_port >= ofproto->max_ports
+ || bitmap_is_set(ofproto->ofp_port_ids, ofp_port)) {
+ bool retry = ofproto->alloc_port_no ? true : false;
+
+ /* Search for a free OpenFlow port number. We try not to
+ * immediately reuse them to prevent problems due to old
+ * flows. */
+ while (ofp_port >= ofproto->max_ports) {
+ for (ofproto->alloc_port_no++;
+ ofproto->alloc_port_no < ofproto->max_ports; ) {
+ if (!bitmap_is_set(ofproto->ofp_port_ids,
+ ofproto->alloc_port_no)) {
+ ofp_port = ofproto->alloc_port_no;
+ break;
+ }
+ }
+ if (ofproto->alloc_port_no >= ofproto->max_ports) {
+ if (retry) {
+ ofproto->alloc_port_no = 0;
+ retry = false;
+ } else {
+ return OFPP_NONE;
+ }
+ }
+ }
+ }
+
+ bitmap_set1(ofproto->ofp_port_ids, ofp_port);
+ return ofp_port;
+}
+
+static void
+dealloc_ofp_port(const struct ofproto *ofproto, uint16_t ofp_port)
+{
+ bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+}
+
/* Opens and returns a netdev for 'ofproto_port' in 'ofproto', or a null
* pointer if the netdev cannot be opened. On success, also fills in
* 'opp'. */
static struct netdev *
-ofport_open(const struct ofproto *ofproto,
- const struct ofproto_port *ofproto_port,
+ofport_open(struct ofproto *ofproto,
+ struct ofproto_port *ofproto_port,
struct ofputil_phy_port *pp)
{
enum netdev_flags flags;
return NULL;
}
+ if (ofproto_port->ofp_port == OFPP_NONE) {
+ if (!strcmp(ofproto->name, ofproto_port->name)) {
+ ofproto_port->ofp_port = OFPP_LOCAL;
+ } else {
+ ofproto_port->ofp_port = alloc_ofp_port(ofproto,
+ ofproto_port->name);
+ }
+ }
pp->port_no = ofproto_port->ofp_port;
netdev_get_etheraddr(netdev, pp->hw_addr);
ovs_strlcpy(pp->name, ofproto_port->name, sizeof pp->name);
pp->state = netdev_get_carrier(netdev) ? 0 : OFPUTIL_PS_LINK_DOWN;
netdev_get_features(netdev, &pp->curr, &pp->advertised,
&pp->supported, &pp->peer);
- pp->curr_speed = netdev_features_to_bps(pp->curr);
- pp->max_speed = netdev_features_to_bps(pp->supported);
+ pp->curr_speed = netdev_features_to_bps(pp->curr, 0);
+ pp->max_speed = netdev_features_to_bps(pp->supported, 0);
return netdev;
}
ofport_destroy(struct ofport *port)
{
if (port) {
+ dealloc_ofp_port(port->ofproto, port->ofp_port);
port->ofproto->ofproto_class->port_destruct(port);
ofport_destroy__(port);
}
{
struct ofproto_port_dump dump;
struct ofproto_port ofproto_port;
+ struct shash_node *node, *next;
OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
- uint16_t ofp_port = ofproto_port.ofp_port;
- if (ofproto_get_port(p, ofp_port)) {
- VLOG_WARN_RL(&rl, "%s: ignoring duplicate port %"PRIu16" "
- "in datapath", p->name, ofp_port);
- } else if (shash_find(&p->port_by_name, ofproto_port.name)) {
+ const char *name = ofproto_port.name;
+
+ if (shash_find(&p->port_by_name, name)) {
VLOG_WARN_RL(&rl, "%s: ignoring duplicate device %s in datapath",
- p->name, ofproto_port.name);
+ p->name, name);
} else {
struct ofputil_phy_port pp;
struct netdev *netdev;
+ /* Check if an OpenFlow port number had been requested. */
+ node = shash_find(&init_ofp_ports, name);
+ if (node) {
+ const struct iface_hint *iface_hint = node->data;
+ simap_put(&p->ofp_requests, name, iface_hint->ofp_port);
+ }
+
netdev = ofport_open(p, &ofproto_port, &pp);
if (netdev) {
ofport_install(p, netdev, &pp);
}
}
+ SHASH_FOR_EACH_SAFE(node, next, &init_ofp_ports) {
+ const struct iface_hint *iface_hint = node->data;
+
+ if (!strcmp(iface_hint->br_name, p->name)) {
+ free(iface_hint->br_name);
+ free(iface_hint->br_type);
+ shash_delete(&init_ofp_ports, node);
+ }
+ }
+
return 0;
}
struct ofport *port;
bool arp_match_ip;
struct ofpbuf *b;
+ int n_tables;
+ int i;
ofproto->ofproto_class->get_features(ofproto, &arp_match_ip,
&features.actions);
assert(features.actions & OFPUTIL_A_OUTPUT); /* sanity check */
+ /* Count only non-hidden tables in the number of tables. (Hidden tables,
+ * if present, are always at the end.) */
+ n_tables = ofproto->n_tables;
+ for (i = 0; i < ofproto->n_tables; i++) {
+ if (ofproto->tables[i].flags & OFTABLE_HIDDEN) {
+ n_tables = i;
+ break;
+ }
+ }
+
features.datapath_id = ofproto->datapath_id;
features.n_buffers = pktbuf_capacity();
- features.n_tables = ofproto->n_tables;
+ features.n_tables = n_tables;
features.capabilities = (OFPUTIL_C_FLOW_STATS | OFPUTIL_C_TABLE_STATS |
OFPUTIL_C_PORT_STATS | OFPUTIL_C_QUEUE_STATS);
if (arp_match_ip) {
struct ofproto *p = ofconn_get_ofproto(ofconn);
struct ofp12_table_stats *ots;
struct ofpbuf *msg;
+ int n_tables;
size_t i;
/* Set up default values.
p->ofproto_class->get_tables(p, ots);
+ /* Post-process the tables, dropping hidden tables. */
+ n_tables = p->n_tables;
for (i = 0; i < p->n_tables; i++) {
const struct oftable *table = &p->tables[i];
+ if (table->flags & OFTABLE_HIDDEN) {
+ n_tables = i;
+ break;
+ }
+
if (table->name) {
ovs_strzcpy(ots[i].name, table->name, sizeof ots[i].name);
}
}
}
- msg = ofputil_encode_table_stats_reply(ots, p->n_tables, request);
+ msg = ofputil_encode_table_stats_reply(ots, n_tables, request);
ofconn_send_reply(ofconn, msg);
free(ots);
rule->evictable = was_evictable;
if (!evict) {
- error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ error = OFPERR_OFPFMFC_TABLE_FULL;
goto exit;
} else if (evict->pending) {
error = OFPROTO_POSTPONE;
* is not required in OpenFlow 1.0.1 and removed from OpenFlow 1.1.
* There is no good error code, so just state that the flow table
* is full. */
- error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ error = OFPERR_OFPFMFC_TABLE_FULL;
}
if (!error) {
error = ofpacts_check(fm.ofpacts, fm.ofpacts_len,