static enum ofperr evict_rules_from_table(struct oftable *)
OVS_REQUIRES(ofproto_mutex);
-static void oftable_disable_eviction(struct oftable *);
+static void oftable_disable_eviction(struct oftable *)
+ OVS_REQUIRES(ofproto_mutex);
static void oftable_enable_eviction(struct oftable *,
const struct mf_subfield *fields,
- size_t n_fields);
+ size_t n_fields)
+ OVS_REQUIRES(ofproto_mutex);
/* A set of rules within a single OpenFlow table (oftable) that have the same
* values for the oftable's eviction_fields. A rule to be evicted, when one is
for (i = 0; i < n_ofproto_classes; i++) {
ofproto_classes[i]->init(&init_ofp_ports);
}
+
+ ofproto_unixctl_init();
}
/* 'type' should be a normalized datapath type, as returned by
*ofprotop = NULL;
- ofproto_unixctl_init();
-
datapath_type = ofproto_normalize_type(datapath_type);
class = ofproto_class_find__(datapath_type);
if (!class) {
hmap_init(&ofproto->groups);
ovs_mutex_unlock(&ofproto_mutex);
ofproto->ogf.types = 0xf;
- ofproto->ogf.capabilities = OFPGFC_CHAINING | OFPGFC_SELECT_LIVENESS |
- OFPGFC_SELECT_WEIGHT;
+ ofproto->ogf.capabilities = OFPGFC_SELECT_LIVENESS | OFPGFC_SELECT_WEIGHT;
for (i = 0; i < 4; i++) {
ofproto->ogf.max_groups[i] = OFPG_MAX;
ofproto->ogf.ofpacts[i] = (UINT64_C(1) << N_OFPACTS) - 1;
return;
}
- if (s->groups) {
- oftable_enable_eviction(table, s->groups, s->n_groups);
- } else {
- oftable_disable_eviction(table);
- }
-
- table->max_flows = s->max_flows;
-
if (classifier_set_prefix_fields(&table->cls,
s->prefix_fields, s->n_prefix_fields)) {
/* XXX: Trigger revalidation. */
}
ovs_mutex_lock(&ofproto_mutex);
+ if (s->groups) {
+ oftable_enable_eviction(table, s->groups, s->n_groups);
+ } else {
+ oftable_disable_eviction(table);
+ }
+ table->max_flows = s->max_flows;
evict_rules_from_table(table);
ovs_mutex_unlock(&ofproto_mutex);
}
struct oftable *table;
destroy_rule_executes(ofproto);
- delete_group(ofproto, OFPG_ALL);
guarded_list_destroy(&ofproto->rule_executes);
ovs_rwlock_destroy(&ofproto->groups_rwlock);
}
static enum ofperr
-handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
+port_mod_start(struct ofconn *ofconn, struct ofputil_port_mod *pm,
+ struct ofport **port)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
+
+ *port = ofproto_get_port(p, pm->port_no);
+ if (!*port) {
+ return OFPERR_OFPPMFC_BAD_PORT;
+ }
+ if (!eth_addr_equals((*port)->pp.hw_addr, pm->hw_addr)) {
+ return OFPERR_OFPPMFC_BAD_HW_ADDR;
+ }
+ return 0;
+}
+
+static void
+port_mod_finish(struct ofconn *ofconn, struct ofputil_port_mod *pm,
+ struct ofport *port)
+{
+ update_port_config(ofconn, port, pm->config, pm->mask);
+ if (pm->advertise) {
+ netdev_set_advertisements(port->netdev, pm->advertise);
+ }
+}
+
+static enum ofperr
+handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
+{
struct ofputil_port_mod pm;
struct ofport *port;
enum ofperr error;
return error;
}
- port = ofproto_get_port(p, pm.port_no);
- if (!port) {
- return OFPERR_OFPPMFC_BAD_PORT;
- } else if (!eth_addr_equals(port->pp.hw_addr, pm.hw_addr)) {
- return OFPERR_OFPPMFC_BAD_HW_ADDR;
- } else {
- update_port_config(ofconn, port, pm.config, pm.mask);
- if (pm.advertise) {
- netdev_set_advertisements(port->netdev, pm.advertise);
- }
+ error = port_mod_start(ofconn, &pm, &port);
+ if (!error) {
+ port_mod_finish(ofconn, &pm, port);
}
- return 0;
+ return error;
}
static enum ofperr
return error;
}
-static bool
-is_conjunction(const struct ofpact *ofpacts, size_t ofpacts_len)
-{
- return ofpacts_len > 0 && ofpacts->type == OFPACT_CONJUNCTION;
-}
-
static void
get_conjunctions(const struct ofputil_flow_mod *fm,
struct cls_conjunction **conjsp, size_t *n_conjsp)
struct cls_conjunction *conjs = NULL;
int n_conjs = 0;
- if (is_conjunction(fm->ofpacts, fm->ofpacts_len)) {
- const struct ofpact *ofpact;
- int i;
-
- n_conjs = 0;
- OFPACT_FOR_EACH (ofpact, fm->ofpacts, fm->ofpacts_len) {
+ const struct ofpact *ofpact;
+ OFPACT_FOR_EACH (ofpact, fm->ofpacts, fm->ofpacts_len) {
+ if (ofpact->type == OFPACT_CONJUNCTION) {
n_conjs++;
+ } else if (ofpact->type != OFPACT_NOTE) {
+ /* "conjunction" may appear with "note" actions but not with any
+ * other type of actions. */
+ ovs_assert(!n_conjs);
+ break;
}
+ }
+ if (n_conjs) {
+ int i = 0;
conjs = xzalloc(n_conjs * sizeof *conjs);
- i = 0;
OFPACT_FOR_EACH (ofpact, fm->ofpacts, fm->ofpacts_len) {
- struct ofpact_conjunction *oc = ofpact_get_CONJUNCTION(ofpact);
- conjs[i].clause = oc->clause;
- conjs[i].n_clauses = oc->n_clauses;
- conjs[i].id = oc->id;
- i++;
+ if (ofpact->type == OFPACT_CONJUNCTION) {
+ struct ofpact_conjunction *oc = ofpact_get_CONJUNCTION(ofpact);
+ conjs[i].clause = oc->clause;
+ conjs[i].n_clauses = oc->n_clauses;
+ conjs[i].id = oc->id;
+ i++;
+ }
}
}
}
if (request.role != OFPCR12_ROLE_NOCHANGE) {
- if (request.have_generation_id
+ if (request.role != OFPCR12_ROLE_EQUAL
+ && request.have_generation_id
&& !ofconn_set_master_election_id(ofconn, request.generation_id)) {
return OFPERR_OFPRRFC_STALE;
}
ovs_rwlock_unlock(&ofproto->groups_rwlock);
}
+/* Delete all groups from 'ofproto'.
+ *
+ * This is intended for use within an ofproto provider's 'destruct'
+ * function. */
+void
+ofproto_group_delete_all(struct ofproto *ofproto)
+{
+ delete_group(ofproto, OFPG_ALL);
+}
+
static enum ofperr
handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh)
{
* possible. No visible changes were made, so rollback is minimal (remove
* added invisible rules, restore visibility of rules marked for removal).
*
- * 3. Bump the version visible to lookups.
- *
- * 4. Finish: Insert replacement rules to the ofproto provider. Remove replaced
- * and deleted rules from ofproto data structures, and Schedule postponed
- * removal of deleted rules from the classifier. Send notifications, buffered
- * packets, etc.
+ * 3. Finish: Make the changes visible for lookups. Insert replacement rules to
+ * the ofproto provider. Remove replaced and deleted rules from ofproto data
+ * structures, and Schedule postponed removal of deleted rules from the
+ * classifier. Send notifications, buffered packets, etc.
*/
static enum ofperr
do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ cls_version_t visible_version = ofproto->tables_version;
struct ofp_bundle *bundle;
struct ofp_bundle_entry *be;
enum ofperr error;
if (bundle->flags != flags) {
error = OFPERR_OFPBFC_BAD_FLAGS;
} else {
+ bool prev_is_port_mod = false;
+
error = 0;
ovs_mutex_lock(&ofproto_mutex);
/* 1. Begin. */
LIST_FOR_EACH (be, node, &bundle->msg_list) {
if (be->type == OFPTYPE_PORT_MOD) {
- /* Not supported yet. */
- error = OFPERR_OFPBFC_MSG_FAILED;
+ /* Our port mods are not atomic. */
+ if (flags & OFPBF_ATOMIC) {
+ error = OFPERR_OFPBFC_MSG_FAILED;
+ } else {
+ prev_is_port_mod = true;
+ error = port_mod_start(ofconn, &be->pm, &be->port);
+ }
} else if (be->type == OFPTYPE_FLOW_MOD) {
+ /* Flow mods between port mods are applied as a single
+ * version, but the versions are published only after
+ * we know the commit is successful. */
+ if (prev_is_port_mod) {
+ ++ofproto->tables_version;
+ }
+ prev_is_port_mod = false;
error = do_bundle_flow_mod_start(ofproto, &be->fm, be);
} else {
OVS_NOT_REACHED();
}
if (error) {
break;
+ } else {
+ /* Store the version in which the changes should take
+ * effect. */
+ be->version = ofproto->tables_version + 1;
}
}
+
if (error) {
/* Send error referring to the original message. */
if (error) {
if (be->type == OFPTYPE_FLOW_MOD) {
do_bundle_flow_mod_revert(ofproto, &be->fm, be);
}
+ /* Nothing needs to be reverted for a port mod. */
}
} else {
- /* 3. Bump the version. This makes all the changes in the bundle
- * visible to the lookups at once. For this to work an upcall must
- * read the tables_version once at the beginning and keep using the
- * same version number for the whole duration of the upcall
- * processing. */
- ofproto_bump_tables_version(ofproto);
-
/* 4. Finish. */
LIST_FOR_EACH (be, node, &bundle->msg_list) {
+ /* Bump the lookup version to the one of the current message.
+ * This makes all the changes in the bundle at this version
+ * visible to lookups at once. */
+ if (visible_version < be->version) {
+ visible_version = be->version;
+ ofproto->ofproto_class->set_tables_version(
+ ofproto, visible_version);
+ }
if (be->type == OFPTYPE_FLOW_MOD) {
struct flow_mod_requester req = { ofconn, be->ofp_msg };
do_bundle_flow_mod_finish(ofproto, &be->fm, &req, be);
+ } else if (be->type == OFPTYPE_PORT_MOD) {
+ /* Perform the actual port mod. This is not atomic, i.e.,
+ * the effects will be immediately seen by upcall
+ * processing regardless of the lookup version. It should
+ * be noted that port configuration changes can originate
+ * also from OVSDB changes asynchronously to all upcall
+ * processing. */
+ port_mod_finish(ofconn, &be->pm, be->port);
}
}
}
+
+ /* Reset the tables_version. */
+ ofproto->tables_version = visible_version;
+
ofmonitor_flush(ofproto->connmgr);
ovs_mutex_unlock(&ofproto_mutex);
oftable_destroy(struct oftable *table)
{
ovs_assert(classifier_is_empty(&table->cls));
+ ovs_mutex_lock(&ofproto_mutex);
oftable_disable_eviction(table);
+ ovs_mutex_unlock(&ofproto_mutex);
classifier_destroy(&table->cls);
free(table->name);
}