static void oftable_remove_rule(struct rule *rule) OVS_REQUIRES(ofproto_mutex);
static void oftable_remove_rule__(struct ofproto *, struct rule *)
OVS_REQUIRES(ofproto_mutex);
-static void oftable_insert_rule(struct rule *);
/* 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
struct heap rules; /* Contains "struct rule"s. */
};
-static bool choose_rule_to_evict(struct oftable *table, struct rule **rulep);
-static void ofproto_evict(struct ofproto *) OVS_EXCLUDED(ofproto_mutex);
-static uint32_t rule_eviction_priority(struct ofproto *ofproto, struct rule *);
-static void eviction_group_add_rule(struct rule *);
-static void eviction_group_remove_rule(struct rule *);
+static bool choose_rule_to_evict(struct oftable *table, struct rule **rulep)
+ OVS_REQUIRES(ofproto_mutex);
+static void ofproto_evict(struct ofproto *)
+ OVS_EXCLUDED(ofproto_mutex);
+static uint32_t rule_eviction_priority(struct ofproto *ofproto, struct rule *)
+ OVS_REQUIRES(ofproto_mutex);;
+static void eviction_group_add_rule(struct rule *)
+ OVS_REQUIRES(ofproto_mutex);
+static void eviction_group_remove_rule(struct rule *)
+ OVS_REQUIRES(ofproto_mutex);
/* Criteria that flow_mod and other operations use for selecting rules on
* which to operate. */
static enum ofperr add_flow(struct ofproto *, struct ofconn *,
struct ofputil_flow_mod *,
const struct ofp_header *);
-static void do_add_flow(struct ofproto *, struct ofconn *,
- const struct ofp_header *request, uint32_t buffer_id,
- struct rule *);
static enum ofperr modify_flows__(struct ofproto *, struct ofconn *,
struct ofputil_flow_mod *,
const struct ofp_header *,
static void ofproto_destroy__(struct ofproto *);
static void update_mtu(struct ofproto *, struct ofport *);
static void meter_delete(struct ofproto *, uint32_t first, uint32_t last);
+static void meter_insert_rule(struct rule *);
/* unixctl. */
static void ofproto_unixctl_init(void);
hmap_destroy(&ofproto->deletions);
+ ovs_assert(hindex_is_empty(&ofproto->cookies));
+ hindex_destroy(&ofproto->cookies);
+
free(ofproto->vlan_bitmap);
ofproto->ofproto_class->dealloc(ofproto);
fm->flags = 0;
fm->ofpacts = CONST_CAST(struct ofpact *, ofpacts);
fm->ofpacts_len = ofpacts_len;
+ fm->delete_reason = OFPRR_DELETE;
}
static int
return handle_flow_mod__(ofproto, NULL, fm, NULL);
}
-/* Resets the modified time for 'rule' or an equivalent rule. If 'rule' is not
- * in the classifier, but an equivalent rule is, unref 'rule' and ref the new
- * rule. Otherwise if 'rule' is no longer installed in the classifier,
- * reinstall it.
- *
- * Returns the rule whose modified time has been reset. */
-struct rule *
-ofproto_refresh_rule(struct rule *rule)
-{
- const struct oftable *table = &rule->ofproto->tables[rule->table_id];
- const struct cls_rule *cr = &rule->cr;
- struct rule *r;
-
- /* do_add_flow() requires that the rule is not installed. We lock the
- * ofproto_mutex here so that another thread cannot add the flow before
- * we get a chance to add it.*/
- ovs_mutex_lock(&ofproto_mutex);
-
- fat_rwlock_rdlock(&table->cls.rwlock);
- r = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls, cr));
- if (r != rule) {
- ofproto_rule_ref(r);
- }
- fat_rwlock_unlock(&table->cls.rwlock);
-
- if (!r) {
- do_add_flow(rule->ofproto, NULL, NULL, 0, rule);
- } else if (r != rule) {
- ofproto_rule_unref(rule);
- rule = r;
- }
- ovs_mutex_unlock(&ofproto_mutex);
-
- /* Refresh the modified time for the rule. */
- ovs_mutex_lock(&rule->mutex);
- rule->modified = MAX(rule->modified, time_msec());
- ovs_mutex_unlock(&rule->mutex);
-
- return rule;
-}
-
/* Searches for a rule with matching criteria exactly equal to 'target' in
* ofproto's table 0 and, if it finds one, deletes it.
*
}
}
-/* Returns true if 'rule' should be hidden from the controller.
- *
- * Rules with priority higher than UINT16_MAX are set up by ofproto itself
- * (e.g. by in-band control) and are intentionally hidden from the
- * controller. */
-static bool
-ofproto_rule_is_hidden(const struct rule *rule)
-{
- return (rule->cr.priority > UINT16_MAX);
-}
-
static bool
oftable_is_modifiable(const struct oftable *table,
enum ofputil_flow_mod_flags flags)
* rules to be selected. (This doesn't allow OpenFlow clients to meddle
* with hidden flows because OpenFlow uses only a 16-bit field to specify
* priority.) */
- if (ofproto_rule_is_hidden(rule) && c->cr.priority <= UINT16_MAX) {
+ if (rule_is_hidden(rule) && c->cr.priority <= UINT16_MAX) {
return 0;
} else if (rule->pending) {
return OFPROTO_POSTPONE;
struct ofputil_flow_mod *fm, const struct ofp_header *request)
OVS_REQUIRES(ofproto_mutex)
{
+ const struct rule_actions *actions;
+ struct ofopgroup *group;
struct oftable *table;
struct cls_rule cr;
struct rule *rule;
*CONST_CAST(uint8_t *, &rule->table_id) = table - ofproto->tables;
rule->flags = fm->flags & OFPUTIL_FF_STATE;
- ovsrcu_set(&rule->actions,
- rule_actions_create(ofproto, fm->ofpacts, fm->ofpacts_len));
+ actions = rule_actions_create(ofproto, fm->ofpacts, fm->ofpacts_len);
+ ovsrcu_set(&rule->actions, actions);
list_init(&rule->meter_list_node);
rule->eviction_group = NULL;
list_init(&rule->expirable);
return error;
}
- /* Insert rule. */
- do_add_flow(ofproto, ofconn, request, fm->buffer_id, rule);
-
- return error;
-}
-
-static void
-do_add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
- const struct ofp_header *request, uint32_t buffer_id,
- struct rule *rule)
- OVS_REQUIRES(ofproto_mutex)
-{
- struct ofopgroup *group;
+ if (fm->hard_timeout || fm->idle_timeout) {
+ list_insert(&ofproto->expirable, &rule->expirable);
+ }
+ cookies_insert(ofproto, rule);
+ eviction_group_add_rule(rule);
+ if (actions->provider_meter_id != UINT32_MAX) {
+ meter_insert_rule(rule);
+ }
- oftable_insert_rule(rule);
+ fat_rwlock_wrlock(&table->cls.rwlock);
+ classifier_insert(&table->cls, CONST_CAST(struct cls_rule *, &rule->cr));
+ fat_rwlock_unlock(&table->cls.rwlock);
- group = ofopgroup_create(ofproto, ofconn, request, buffer_id);
+ group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
ofoperation_create(group, rule, OFOPERATION_ADD, 0);
ofproto->ofproto_class->rule_insert(rule);
ofopgroup_submit(group);
+
+ return error;
}
\f
/* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */
enum ofperr error;
size_t i;
+ if (ofproto->ofproto_class->rule_premodify_actions) {
+ for (i = 0; i < rules->n; i++) {
+ struct rule *rule = rules->rules[i];
+
+ if (rule_is_modifiable(rule, fm->flags)) {
+ error = ofproto->ofproto_class->rule_premodify_actions(
+ rule, fm->ofpacts, fm->ofpacts_len);
+ if (error) {
+ return error;
+ }
+ }
+ }
+ }
+
type = fm->command == OFPFC_ADD ? OFOPERATION_REPLACE : OFOPERATION_MODIFY;
group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
error = OFPERR_OFPBRC_EPERM;
ovsrcu_set(&rule->actions, new_actions);
- rule->ofproto->ofproto_class->rule_modify_actions(rule,
- reset_counters);
+ ofproto->ofproto_class->rule_modify_actions(rule, reset_counters);
} else {
ofoperation_complete(op, 0);
}
rule_criteria_destroy(&criteria);
if (!error && rules.n > 0) {
- error = delete_flows__(ofproto, ofconn, request, &rules, OFPRR_DELETE);
+ error = delete_flows__(ofproto, ofconn, request, &rules,
+ fm->delete_reason);
}
rule_collection_destroy(&rules);
rule_criteria_destroy(&criteria);
if (!error && rules.n > 0) {
- error = delete_flows__(ofproto, ofconn, request, &rules, OFPRR_DELETE);
+ error = delete_flows__(ofproto, ofconn, request, &rules,
+ fm->delete_reason);
}
rule_collection_destroy(&rules);
struct ofputil_flow_removed fr;
long long int used;
- if (ofproto_rule_is_hidden(rule) ||
- !(rule->flags & OFPUTIL_FF_SEND_FLOW_REM)) {
+ if (rule_is_hidden(rule) || !(rule->flags & OFPUTIL_FF_SEND_FLOW_REM)) {
return;
}
{
enum nx_flow_monitor_flags update;
- if (ofproto_rule_is_hidden(rule)) {
+ if (rule_is_hidden(rule)) {
return;
}
/* Meters implementation.
*
* Meter table entry, indexed by the OpenFlow meter_id.
- * These are always dynamically allocated to allocate enough space for
- * the bands.
* 'created' is used to compute the duration for meter stats.
* 'list rules' is needed so that we can delete the dependent rules when the
* meter table entry is deleted.
return UINT32_MAX;
}
+/* Finds the meter invoked by 'rule''s actions and adds 'rule' to the meter's
+ * list of rules. */
+static void
+meter_insert_rule(struct rule *rule)
+{
+ const struct rule_actions *a = rule_get_actions(rule);
+ uint32_t meter_id = ofpacts_get_meter(a->ofpacts, a->ofpacts_len);
+ struct meter *meter = rule->ofproto->meters[meter_id];
+
+ list_insert(&meter->rules, &rule->meter_list_node);
+}
+
static void
meter_update(struct meter *meter, const struct ofputil_meter_config *config)
{
return 0;
}
-/* If the group exists, this function increments the groups's reference count.
- *
- * Make sure to call ofproto_group_unref() after no longer needing to maintain
- * a reference to the group. */
-bool
-ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id,
- struct ofgroup **group)
+static bool
+ofproto_group_lookup__(const struct ofproto *ofproto, uint32_t group_id,
+ struct ofgroup **group)
+ OVS_REQ_RDLOCK(ofproto->groups_rwlock)
{
- ovs_rwlock_rdlock(&ofproto->groups_rwlock);
HMAP_FOR_EACH_IN_BUCKET (*group, hmap_node,
hash_int(group_id, 0), &ofproto->groups) {
if ((*group)->group_id == group_id) {
- ofproto_group_ref(*group);
- ovs_rwlock_unlock(&ofproto->groups_rwlock);
return true;
}
}
- ovs_rwlock_unlock(&ofproto->groups_rwlock);
+
return false;
}
-static bool
-ofproto_group_write_lookup(const struct ofproto *ofproto, uint32_t group_id,
- struct ofgroup **group)
- OVS_ACQUIRES(ofproto->groups_rwlock)
+/* If the group exists, this function increments the groups's reference count.
+ *
+ * Make sure to call ofproto_group_unref() after no longer needing to maintain
+ * a reference to the group. */
+bool
+ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id,
+ struct ofgroup **group)
{
- ovs_rwlock_wrlock(&ofproto->groups_rwlock);
- HMAP_FOR_EACH_IN_BUCKET (*group, hmap_node,
- hash_int(group_id, 0), &ofproto->groups) {
- if ((*group)->group_id == group_id) {
- return true;
- }
+ bool found;
+
+ ovs_rwlock_rdlock(&ofproto->groups_rwlock);
+ found = ofproto_group_lookup__(ofproto, group_id, group);
+ if (found) {
+ ofproto_group_ref(*group);
}
- return false;
+ ovs_rwlock_unlock(&ofproto->groups_rwlock);
+ return found;
}
static bool
return error;
}
-/* Implements OFPGC11_ADD
- * in which no matching flow already exists in the flow table.
- *
- * Adds the flow specified by 'ofm', which is followed by 'n_actions'
- * ofp_actions, to the ofproto's flow table. Returns 0 on success, an OpenFlow
- * error code on failure, or OFPROTO_POSTPONE if the operation cannot be
- * initiated now but may be retried later.
- *
- * Upon successful return, takes ownership of 'fm->ofpacts'. On failure,
- * ownership remains with the caller.
- *
- * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
- * if any. */
+/* Implements the OFPGC11_ADD operation specified by 'gm', adding a group to
+ * 'ofproto''s group table. Returns 0 on success or an OpenFlow error code on
+ * failure. */
static enum ofperr
add_group(struct ofproto *ofproto, struct ofputil_group_mod *gm)
{
return error;
}
-/* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code on
- * failure.
+/* Implements OFPGC11_MODIFY. Returns 0 on success or an OpenFlow error code
+ * on failure.
*
* Note that the group is re-created and then replaces the old group in
* ofproto's ofgroup hash map. Thus, the group is never altered while users of
- * the xlate module hold a pointer to the group.
- *
- * 'ofconn' is used to retrieve the packet buffer specified in fm->buffer_id,
- * if any. */
+ * the xlate module hold a pointer to the group. */
static enum ofperr
modify_group(struct ofproto *ofproto, struct ofputil_group_mod *gm)
{
retiring = new_ofgroup;
- if (!ofproto_group_write_lookup(ofproto, gm->group_id, &ofgroup)) {
+ ovs_rwlock_wrlock(&ofproto->groups_rwlock);
+ if (!ofproto_group_lookup__(ofproto, gm->group_id, &ofgroup)) {
error = OFPERR_OFPGMFC_UNKNOWN_GROUP;
goto out;
}
/* Delete all flow entries containing this group in a group action */
match_init_catchall(&match);
flow_mod_init(&fm, &match, 0, NULL, 0, OFPFC_DELETE);
+ fm.delete_reason = OFPRR_GROUP_DELETE;
fm.out_group = ofgroup->group_id;
handle_flow_mod__(ofproto, NULL, &fm, NULL);
ofproto_group_unref(ofgroup);
}
-/* Implements OFPGC_DELETE. */
+/* Implements OFPGC11_DELETE. */
static void
delete_group(struct ofproto *ofproto, uint32_t group_id)
{
- The operation's only effect was to update rule->modified. */
if (!(op->error
- || ofproto_rule_is_hidden(rule)
+ || rule_is_hidden(rule)
|| (op->type == OFOPERATION_MODIFY
- && op->actions
+ && !op->actions
&& rule->flow_cookie == op->flow_cookie))) {
- /* Check that we can just cast from ofoperation_type to
- * nx_flow_update_event. */
enum nx_flow_update_event event_type;
switch (op->type) {
rule->pending = NULL;
+ ovs_assert(!op->error || op->type == OFOPERATION_ADD);
switch (op->type) {
case OFOPERATION_ADD:
if (!op->error) {
break;
case OFOPERATION_DELETE:
- ovs_assert(!op->error);
ofproto_rule_unref(rule);
op->rule = NULL;
break;
case OFOPERATION_MODIFY:
- case OFOPERATION_REPLACE:
- if (!op->error) {
- long long int now = time_msec();
+ case OFOPERATION_REPLACE: {
+ long long now = time_msec();
- ovs_mutex_lock(&rule->mutex);
- rule->modified = now;
- if (op->type == OFOPERATION_REPLACE) {
- rule->created = now;
- }
- ovs_mutex_unlock(&rule->mutex);
- } else {
- ofproto_rule_change_cookie(ofproto, rule, op->flow_cookie);
- ovs_mutex_lock(&rule->mutex);
- rule->idle_timeout = op->idle_timeout;
- rule->hard_timeout = op->hard_timeout;
- ovs_mutex_unlock(&rule->mutex);
- if (op->actions) {
- const struct rule_actions *old_actions;
-
- ovs_mutex_lock(&rule->mutex);
- old_actions = rule_get_actions(rule);
- ovsrcu_set(&rule->actions, op->actions);
- ovs_mutex_unlock(&rule->mutex);
-
- op->actions = NULL;
- rule_actions_destroy(old_actions);
- }
- rule->flags = op->flags;
+ ovs_mutex_lock(&rule->mutex);
+ rule->modified = now;
+ if (op->type == OFOPERATION_REPLACE) {
+ rule->created = now;
}
+ ovs_mutex_unlock(&rule->mutex);
break;
+ }
default:
OVS_NOT_REACHED();
* If 'error' is 0, indicating success, the operation will be committed
* permanently to the flow table.
*
- * If 'error' is nonzero, then generally the operation will be rolled back:
- *
- * - If 'op' is an "add flow" operation, ofproto removes the new rule or
- * restores the original rule. The caller must have uninitialized any
- * derived state in the new rule, as in step 5 of in the "Life Cycle" in
- * ofproto/ofproto-provider.h. ofoperation_complete() performs steps 6 and
- * and 7 for the new rule, calling its ->rule_dealloc() function.
- *
- * - If 'op' is a "modify flow" operation, ofproto restores the original
- * actions.
- *
- * - 'op' must not be a "delete flow" operation. Removing a rule is not
- * allowed to fail. It must always succeed.
+ * Flow modifications and deletions must always succeed. Flow additions may
+ * fail, indicated by nonzero 'error'. If an "add flow" operation fails, this
+ * function removes the new rule. The caller must have uninitialized any
+ * derived state in the new rule, as in step 5 of in the "Life Cycle" in
+ * ofproto/ofproto-provider.h. ofoperation_complete() performs steps 6 and and
+ * 7 for the new rule, calling its ->rule_dealloc() function.
*
* Please see the large comment in ofproto/ofproto-provider.h titled
* "Asynchronous Operation Support" for more information. */
struct ofopgroup *group = op->group;
ovs_assert(group->n_running > 0);
- ovs_assert(!error || op->type != OFOPERATION_DELETE);
+ ovs_assert(!error || op->type == OFOPERATION_ADD);
op->error = error;
if (!--group->n_running && !list_is_empty(&group->ofproto_node)) {
classifier_set_prefix_fields(&table->cls, default_prefix_fields,
ARRAY_SIZE(default_prefix_fields));
fat_rwlock_unlock(&table->cls.rwlock);
+
+ atomic_init(&table->n_matched, 0);
+ atomic_init(&table->n_missed, 0);
}
/* Destroys 'table', including its classifier and eviction groups.
{
oftable_remove_rule__(rule->ofproto, rule);
}
-
-/* Inserts 'rule' into its oftable, which must not already contain any rule for
- * the same cls_rule. */
-static void
-oftable_insert_rule(struct rule *rule)
- OVS_REQUIRES(ofproto_mutex)
-{
- struct ofproto *ofproto = rule->ofproto;
- struct oftable *table = &ofproto->tables[rule->table_id];
- const struct rule_actions *actions;
- bool may_expire;
-
- ovs_mutex_lock(&rule->mutex);
- may_expire = rule->hard_timeout || rule->idle_timeout;
- ovs_mutex_unlock(&rule->mutex);
-
- if (may_expire) {
- list_insert(&ofproto->expirable, &rule->expirable);
- }
-
- cookies_insert(ofproto, rule);
-
- actions = rule_get_actions(rule);
- if (actions->provider_meter_id != UINT32_MAX) {
- uint32_t meter_id = ofpacts_get_meter(actions->ofpacts,
- actions->ofpacts_len);
- struct meter *meter = ofproto->meters[meter_id];
- list_insert(&meter->rules, &rule->meter_list_node);
- }
- fat_rwlock_wrlock(&table->cls.rwlock);
- classifier_insert(&table->cls, CONST_CAST(struct cls_rule *, &rule->cr));
- fat_rwlock_unlock(&table->cls.rwlock);
- eviction_group_add_rule(rule);
-}
\f
/* unixctl commands. */
}
fat_rwlock_unlock(&oftable->cls.rwlock);
}
+
+ cls_rule_destroy(&target);
}
/* Returns true if new VLANs have come into use by the flow table since the