In add_flow, should we take the evict lock on the rule while we're still holding the classifier readlock? It doesn't matter much if add_flow will only be called by the main thread, however if that isn't the case, we don't want the rule we're working on to be evicted from under us.
There's a serious problem with this code which I think pre-dated this patch. Specifically, modify_flows__() can change a rule's actions without holding a lock. If some child thread is using those actions to do xlation, I think we'll run into trouble. I'll put some thought into this. Acked-by: Ethan Jackson <et...@nicira.com> On Mon, Aug 26, 2013 at 5:10 PM, Ben Pfaff <b...@nicira.com> wrote: > add_flow() in ofproto.c has a race: it adds a new flow to the flow table > before calling ->rule_construct(). That means that (in ofproto-dpif) the > new flow is visible to the forwarder threads before gets properly > initialized. > > One solution would be to lock the flow table across the entire operation, > but this is not desirable: > > * We would need a write-lock but this would be expensive for > implementing "learn" flow_mods that often don't really modify anything > at all. > > * The code would need to keep the lock across a couple of differen calls > into the client, which seems undesirable if it can be avoided. > > * The code in add_flow() is difficult to understand already. > > Instead, I've decided to refactor add_flow() to simplify it and make it > easier to understand. I think this will also improve the potential for > concurrency. > > This commit starts off by collapsing two different cases together into > (almost) one. In particular, OpenFlow has two ways to modify a flow with a > "flow_mod" command. You can use an "add" flow_mod to replace the flow, or > you can use a "modify" flow_mod to change it. The differences in semantics > are minor, but until now Open vSwitch has treated them quite differently. > This commit merges both cases, treating them as variants of what was > previously a "modify". The advantage is that add_flow() no longer has to > deal with two flows at a time in the "add" case (the old flow being > deleted, the new flow replacing that one). This does not fix the race, but > it makes it easier to deal with in a later commit. > > Transforming "add" into a form of "modify" requires that "modify" be able > to reset flow statistic counters. OpenFlow 1.2 and later make this an > optional flag in a "flow_mod". Until now, we haven't implemented that > feature of OF1.2+, but we get it pretty much for free with this > refactoring, so this commit also adds that OF1.2+ feature too. > > Signed-off-by: Ben Pfaff <b...@nicira.com> > --- > OPENFLOW-1.1+ | 3 - > ofproto/ofproto-dpif.c | 9 +- > ofproto/ofproto-provider.h | 47 +++----- > ofproto/ofproto.c | 271 > ++++++++++++++++++++++++-------------------- > 4 files changed, 174 insertions(+), 156 deletions(-) > > diff --git a/OPENFLOW-1.1+ b/OPENFLOW-1.1+ > index 190dd29..753dbba 100644 > --- a/OPENFLOW-1.1+ > +++ b/OPENFLOW-1.1+ > @@ -119,9 +119,6 @@ end of the OF1.2 spec. I didn't compare the specs > carefully yet.) > > * OFPT_FLOW_MOD: > > - * New flag OFPFF_RESET_COUNTS. > - [required for OF1.2+] > - > * Add ability to delete flow in all tables. > [required for OF1.2+] > > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > index 80c7c4c..8d82512 100644 > --- a/ofproto/ofproto-dpif.c > +++ b/ofproto/ofproto-dpif.c > @@ -4955,10 +4955,17 @@ rule_execute(struct rule *rule, const struct flow > *flow, > } > > static void > -rule_modify_actions(struct rule *rule_) > +rule_modify_actions(struct rule *rule_, bool reset_counters) > { > struct rule_dpif *rule = rule_dpif_cast(rule_); > > + if (reset_counters) { > + ovs_mutex_lock(&rule->stats_mutex); > + rule->packet_count = 0; > + rule->byte_count = 0; > + ovs_mutex_unlock(&rule->stats_mutex); > + } > + > complete_operation(rule); > } > > diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h > index 72ba3be..43273ec 100644 > --- a/ofproto/ofproto-provider.h > +++ b/ofproto/ofproto-provider.h > @@ -288,7 +288,6 @@ void ofproto_rule_reduce_timeouts(struct rule *rule, > uint16_t idle_timeout, > bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t out_port); > > void ofoperation_complete(struct ofoperation *, enum ofperr); > -struct rule *ofoperation_get_victim(struct ofoperation *); > > bool ofoperation_has_out_port(const struct ofoperation *, ofp_port_t > out_port); > > @@ -890,7 +889,7 @@ struct ofproto_class { > * An ofproto implementation reports the success or failure of an > * asynchronous operation on a rule using the rule's 'pending' member, > * which points to a opaque "struct ofoperation" that represents the > - * ongoing opreation. When the operation completes, the ofproto > + * ongoing operation. When the operation completes, the ofproto > * implementation calls ofoperation_complete(), passing the ofoperation > and > * an error indication. > * > @@ -907,22 +906,22 @@ struct ofproto_class { > * The ofproto base code updates the flow table optimistically, assuming > * that the operation will probably succeed: > * > - * - ofproto adds or replaces the rule in the flow table before calling > + * - ofproto adds the rule in the flow table before calling > * ->rule_construct(). > * > - * - ofproto updates the rule's actions before calling > - * ->rule_modify_actions(). > + * - ofproto updates the rule's actions and other properties before > + * calling ->rule_modify_actions(). > * > * - ofproto removes the rule before calling ->rule_destruct(). > * > * With one exception, when an asynchronous operation completes with an > * error, ofoperation_complete() backs out the already applied changes: > * > - * - If adding or replacing a rule in the flow table fails, ofproto > - * removes the new rule or restores the original rule. > + * - If adding a rule in the flow table fails, ofproto removes the new > + * rule. > * > - * - If modifying a rule's actions fails, ofproto restores the original > - * actions. > + * - If modifying a rule fails, ofproto restores the original actions > + * (and other properties). > * > * - Removing a rule is not allowed to fail. It must always succeed. > * > @@ -938,17 +937,9 @@ struct ofproto_class { > * Construction > * ============ > * > - * When ->rule_construct() is called, the caller has already inserted > - * 'rule' into 'rule->ofproto''s flow table numbered 'rule->table_id'. > - * There are two cases: > - * > - * - 'rule' is a new rule in its flow table. In this case, > - * ofoperation_get_victim(rule) returns NULL. > - * > - * - 'rule' is replacing an existing rule in its flow table that had > the > - * same matching criteria and priority. In this case, > - * ofoperation_get_victim(rule) returns the rule being replaced (the > - * "victim" rule). > + * When ->rule_construct() is called, 'rule' is a new rule in its flow > + * table. The caller has already inserted 'rule' into 'rule->ofproto''s > + * flow table numbered 'rule->table_id'. > * > * ->rule_construct() should set the following in motion: > * > @@ -959,16 +950,10 @@ struct ofproto_class { > * > * - Validate that the datapath can correctly implement > 'rule->ofpacts'. > * > - * - If the rule is valid, update the datapath flow table, adding the > new > - * rule or replacing the existing one. > - * > - * - If 'rule' is replacing an existing rule, uninitialize any derived > - * state for the victim rule, as in step 5 in the "Life Cycle" > - * described above. > + * - If the rule is valid, add the new rule to the datapath flow table. > * > * (On failure, the ofproto code will roll back the insertion from the > flow > - * table, either removing 'rule' or replacing it by the victim rule if > - * there is one.) > + * table by removing 'rule'.) > * > * ->rule_construct() must act in one of the following ways: > * > @@ -1044,6 +1029,10 @@ struct ofproto_class { > * > * - Update the datapath flow table with the new actions. > * > + * - Only if 'reset_counters' is true, reset any packet or byte > counters > + * associated with the rule to zero, so that rule_get_stats() will > not > + * longer count those packets or bytes. > + * > * If the operation synchronously completes, ->rule_modify_actions() may > * call ofoperation_complete() before it returns. Otherwise, ->run() > * should call ofoperation_complete() later, after the operation does > @@ -1054,7 +1043,7 @@ struct ofproto_class { > * > * ->rule_modify_actions() should not modify any base members of struct > * rule. */ > - void (*rule_modify_actions)(struct rule *rule); > + void (*rule_modify_actions)(struct rule *rule, bool reset_counters); > > /* Changes the OpenFlow IP fragment handling policy to 'frag_handling', > * which takes one of the following values, with the corresponding > diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c > index 080592e..623039b 100644 > --- a/ofproto/ofproto.c > +++ b/ofproto/ofproto.c > @@ -76,7 +76,8 @@ enum ofproto_state { > enum ofoperation_type { > OFOPERATION_ADD, > OFOPERATION_DELETE, > - OFOPERATION_MODIFY > + OFOPERATION_MODIFY, > + OFOPERATION_REPLACE > }; > > /* A single OpenFlow request can execute any number of operations. The > @@ -121,10 +122,8 @@ struct ofoperation { > struct rule *rule; /* Rule being operated upon. */ > enum ofoperation_type type; /* Type of operation. */ > > - /* OFOPERATION_ADD. */ > - struct rule *victim; /* Rule being replaced, if any.. */ > - > - /* OFOPERATION_MODIFY: The old actions, if the actions are changing. */ > + /* OFOPERATION_MODIFY, OFOPERATION_REPLACE: The old actions, if the > actions > + * are changing. */ > struct ofpact *ofpacts; > size_t ofpacts_len; > uint32_t meter_id; > @@ -133,6 +132,9 @@ struct ofoperation { > enum ofp_flow_removed_reason reason; /* Reason flow was removed. */ > > ovs_be64 flow_cookie; /* Rule's old flow cookie. */ > + uint16_t idle_timeout; /* Rule's old idle timeout. */ > + uint16_t hard_timeout; /* Rule's old hard timeout. */ > + bool send_flow_removed; /* Rule's old 'send_flow_removed'. */ > enum ofperr error; /* 0 if no error. */ > }; > > @@ -157,8 +159,7 @@ static void oftable_remove_rule(struct rule *rule) > OVS_RELEASES(rule->evict); > static void oftable_remove_rule__(struct ofproto *ofproto, > struct classifier *cls, struct rule *rule) > OVS_REQ_WRLOCK(cls->rwlock) OVS_RELEASES(rule->evict); > -static struct rule *oftable_replace_rule(struct rule *); > -static void oftable_substitute_rule(struct rule *old, struct rule *new); > +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 > @@ -186,7 +187,8 @@ static bool choose_rule_to_evict(struct oftable *table, > struct rule **rulep) > OVS_TRY_WRLOCK(true, (*rulep)->evict); > static void ofproto_evict(struct ofproto *); > static uint32_t rule_eviction_priority(struct rule *); > -static void eviction_group_add_rule(struct rule *rule); > +static void eviction_group_add_rule(struct rule *); > +static void eviction_group_remove_rule(struct rule *); > > /* ofport. */ > static void ofport_destroy__(struct ofport *); > @@ -205,6 +207,9 @@ static bool rule_is_modifiable(const struct rule *); > static enum ofperr add_flow(struct ofproto *, struct ofconn *, > struct ofputil_flow_mod *, > const struct ofp_header *); > +static enum ofperr modify_flows__(struct ofproto *, struct ofconn *, > + struct ofputil_flow_mod *, > + const struct ofp_header *, struct list *); > static void delete_flow__(struct rule *rule, struct ofopgroup *, > enum ofp_flow_removed_reason) > OVS_RELEASES(rule->evict); > @@ -2246,12 +2251,11 @@ ofoperation_has_out_port(const struct ofoperation > *op, ofp_port_t out_port) > > switch (op->type) { > case OFOPERATION_ADD: > - return op->victim && ofproto_rule_has_out_port(op->victim, out_port); > - > case OFOPERATION_DELETE: > return false; > > case OFOPERATION_MODIFY: > + case OFOPERATION_REPLACE: > return ofpacts_output_to_port(op->ofpacts, op->ofpacts_len, > out_port); > } > > @@ -3357,8 +3361,10 @@ add_flow(struct ofproto *ofproto, struct ofconn > *ofconn, > { > struct oftable *table; > struct ofopgroup *group; > - struct rule *victim; > + struct ofoperation *op; > + struct rule *evict; > struct rule *rule; > + size_t n_rules; > uint8_t table_id; > int error; > > @@ -3392,6 +3398,27 @@ add_flow(struct ofproto *ofproto, struct ofconn > *ofconn, > return OFPERR_OFPBRC_EPERM; > } > > + /* Transform "add" into "modify" if there's an existing identical flow. > */ > + ovs_rwlock_rdlock(&table->cls.rwlock); > + rule = rule_from_cls_rule(classifier_find_match_exactly(&table->cls, > + &fm->match, > + fm->priority)); > + ovs_rwlock_unlock(&table->cls.rwlock); > + if (rule) { > + if (!rule_is_modifiable(rule)) { > + return OFPERR_OFPBRC_EPERM; > + } else if (rule->pending) { > + return OFPROTO_POSTPONE; > + } else { > + struct list rules; > + > + list_init(&rules); > + list_push_back(&rules, &rule->ofproto_node); > + fm->modify_cookie = true; > + return modify_flows__(ofproto, ofconn, fm, request, &rules); > + } > + } > + > /* Verify actions. */ > error = ofproto_check_ofpacts(ofproto, fm->ofpacts, fm->ofpacts_len, > &fm->match.flow, table_id); > @@ -3457,62 +3484,53 @@ add_flow(struct ofproto *ofproto, struct ofconn > *ofconn, > ovs_rwlock_init(&rule->evict); > > /* Insert new rule. */ > - victim = oftable_replace_rule(rule); > - if (victim && !rule_is_modifiable(victim)) { > - error = OFPERR_OFPBRC_EPERM; > - } else if (victim && victim->pending) { > - error = OFPROTO_POSTPONE; > - } else { > - struct ofoperation *op; > - struct rule *evict; > - size_t n_rules; > + oftable_insert_rule(rule); > > - ovs_rwlock_rdlock(&table->cls.rwlock); > - n_rules = classifier_count(&table->cls); > - ovs_rwlock_unlock(&table->cls.rwlock); > - if (n_rules > table->max_flows) { > - ovs_rwlock_rdlock(&rule->evict); > - if (choose_rule_to_evict(table, &evict)) { > - ovs_rwlock_unlock(&rule->evict); > - ovs_rwlock_unlock(&evict->evict); > - if (evict->pending) { > - error = OFPROTO_POSTPONE; > - goto exit; > - } > - } else { > - ovs_rwlock_unlock(&rule->evict); > - error = OFPERR_OFPFMFC_TABLE_FULL; > + ovs_rwlock_rdlock(&table->cls.rwlock); > + n_rules = classifier_count(&table->cls); > + ovs_rwlock_unlock(&table->cls.rwlock); > + if (n_rules > table->max_flows) { > + ovs_rwlock_rdlock(&rule->evict); > + if (choose_rule_to_evict(table, &evict)) { > + ovs_rwlock_unlock(&rule->evict); > + ovs_rwlock_unlock(&evict->evict); > + if (evict->pending) { > + error = OFPROTO_POSTPONE; > goto exit; > } > } else { > - evict = NULL; > + ovs_rwlock_unlock(&rule->evict); > + error = OFPERR_OFPFMFC_TABLE_FULL; > + goto exit; > } > + } else { > + evict = NULL; > + } > > - group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id); > - op = ofoperation_create(group, rule, OFOPERATION_ADD, 0); > - op->victim = victim; > + group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id); > + op = ofoperation_create(group, rule, OFOPERATION_ADD, 0); > > - error = ofproto->ofproto_class->rule_construct(rule); > - if (error) { > - op->group->n_running--; > - ofoperation_destroy(rule->pending); > - } else if (evict) { > - /* It would be better if we maintained the lock we took in > - * choose_rule_to_evict() earlier, but that confuses the thread > - * safety analysis, and this code is fragile enough that we > really > - * need it. In the worst case, we'll have to block a little > while > - * before we perform the eviction, which doesn't seem like a big > - * problem. */ > - ovs_rwlock_wrlock(&evict->evict); > - delete_flow__(evict, group, OFPRR_EVICTION); > - } > - ofopgroup_submit(group); > + error = ofproto->ofproto_class->rule_construct(rule); > + if (error) { > + op->group->n_running--; > + ofoperation_destroy(rule->pending); > + } else if (evict) { > + /* It would be better if we maintained the lock we took in > + * choose_rule_to_evict() earlier, but that confuses the thread > + * safety analysis, and this code is fragile enough that we really > + * need it. In the worst case, we'll have to block a little while > + * before we perform the eviction, which doesn't seem like a big > + * problem. */ > + ovs_rwlock_wrlock(&evict->evict); > + delete_flow__(evict, group, OFPRR_EVICTION); > } > + ofopgroup_submit(group); > > exit: > /* Back out if an error occurred. */ > if (error) { > - oftable_substitute_rule(rule, victim); > + ovs_rwlock_wrlock(&rule->evict); > + oftable_remove_rule(rule); > ofproto_rule_destroy__(rule); > } > return error; > @@ -3532,15 +3550,18 @@ modify_flows__(struct ofproto *ofproto, struct ofconn > *ofconn, > struct ofputil_flow_mod *fm, const struct ofp_header *request, > struct list *rules) > { > + enum ofoperation_type type; > struct ofopgroup *group; > struct rule *rule; > enum ofperr error; > > + type = fm->command == OFPFC_ADD ? OFOPERATION_REPLACE : > OFOPERATION_MODIFY; > group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id); > error = OFPERR_OFPBRC_EPERM; > LIST_FOR_EACH (rule, ofproto_node, rules) { > struct ofoperation *op; > bool actions_changed; > + bool reset_counters; > > /* FIXME: Implement OFPFUTIL_FF_RESET_COUNTS */ > > @@ -3561,19 +3582,39 @@ modify_flows__(struct ofproto *ofproto, struct ofconn > *ofconn, > actions_changed = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len, > rule->ofpacts, rule->ofpacts_len); > > - op = ofoperation_create(group, rule, OFOPERATION_MODIFY, 0); > + op = ofoperation_create(group, rule, type, 0); > > if (fm->modify_cookie && fm->new_cookie != htonll(UINT64_MAX)) { > ofproto_rule_change_cookie(ofproto, rule, fm->new_cookie); > } > - if (actions_changed) { > + if (type == OFOPERATION_REPLACE) { > + ovs_mutex_lock(&rule->timeout_mutex); > + rule->idle_timeout = fm->idle_timeout; > + rule->hard_timeout = fm->hard_timeout; > + ovs_mutex_unlock(&rule->timeout_mutex); > + > + rule->send_flow_removed = (fm->flags > + & OFPUTIL_FF_SEND_FLOW_REM) != 0; > + > + if (fm->idle_timeout || fm->hard_timeout) { > + if (!rule->eviction_group) { > + eviction_group_add_rule(rule); > + } > + } else { > + eviction_group_remove_rule(rule); > + } > + } > + > + reset_counters = (fm->flags & OFPUTIL_FF_RESET_COUNTS) != 0; > + if (actions_changed || reset_counters) { > op->ofpacts = rule->ofpacts; > op->ofpacts_len = rule->ofpacts_len; > op->meter_id = rule->meter_id; > rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len); > rule->ofpacts_len = fm->ofpacts_len; > rule->meter_id = find_meter(rule->ofpacts, rule->ofpacts_len); > - rule->ofproto->ofproto_class->rule_modify_actions(rule); > + rule->ofproto->ofproto_class->rule_modify_actions(rule, > + > reset_counters); > } else { > ofoperation_complete(op, 0); > } > @@ -4075,7 +4116,7 @@ ofproto_compose_flow_refresh_update(const struct rule > *rule, > struct ofputil_flow_update fu; > struct match match; > > - if (op && op->type == OFOPERATION_ADD && !op->victim) { > + if (op && op->type == OFOPERATION_ADD) { > /* We'll report the final flow when the operation completes. > Reporting > * it now would cause a duplicate report later. */ > return; > @@ -4104,12 +4145,10 @@ ofproto_compose_flow_refresh_update(const struct rule > *rule, > * actions, so that when the operation commits we report the change. > */ > switch (op->type) { > case OFOPERATION_ADD: > - /* We already verified that there was a victim. */ > - fu.ofpacts = op->victim->ofpacts; > - fu.ofpacts_len = op->victim->ofpacts_len; > - break; > + NOT_REACHED(); > > case OFOPERATION_MODIFY: > + case OFOPERATION_REPLACE: > if (op->ofpacts) { > fu.ofpacts = op->ofpacts; > fu.ofpacts_len = op->ofpacts_len; > @@ -4920,15 +4959,27 @@ ofopgroup_complete(struct ofopgroup *group) > && rule->flow_cookie == op->flow_cookie))) { > /* Check that we can just cast from ofoperation_type to > * nx_flow_update_event. */ > - BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_ADD > - == NXFME_ADDED); > - BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_DELETE > - == NXFME_DELETED); > - BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_MODIFY > - == NXFME_MODIFIED); > - > - ofmonitor_report(ofproto->connmgr, rule, > - (enum nx_flow_update_event) op->type, > + enum nx_flow_update_event event_type; > + > + switch (op->type) { > + case OFOPERATION_ADD: > + case OFOPERATION_REPLACE: > + event_type = NXFME_ADDED; > + break; > + > + case OFOPERATION_DELETE: > + event_type = NXFME_DELETED; > + break; > + > + case OFOPERATION_MODIFY: > + event_type = NXFME_MODIFIED; > + break; > + > + default: > + NOT_REACHED(); > + } > + > + ofmonitor_report(ofproto->connmgr, rule, event_type, > op->reason, abbrev_ofconn, abbrev_xid); > } > > @@ -4939,7 +4990,6 @@ ofopgroup_complete(struct ofopgroup *group) > if (!op->error) { > uint16_t vid_mask; > > - ofproto_rule_destroy__(op->victim); > vid_mask = minimask_get_vid_mask(&rule->cr.match.mask); > if (vid_mask == VLAN_VID_MASK) { > if (ofproto->vlan_bitmap) { > @@ -4953,7 +5003,8 @@ ofopgroup_complete(struct ofopgroup *group) > } > } > } else { > - oftable_substitute_rule(rule, op->victim); > + ovs_rwlock_wrlock(&rule->evict); > + oftable_remove_rule(rule); > ofproto_rule_destroy__(rule); > } > break; > @@ -4965,10 +5016,20 @@ ofopgroup_complete(struct ofopgroup *group) > break; > > case OFOPERATION_MODIFY: > + case OFOPERATION_REPLACE: > if (!op->error) { > - rule->modified = time_msec(); > + long long int now = time_msec(); > + > + rule->modified = now; > + if (op->type == OFOPERATION_REPLACE) { > + rule->created = rule->used = now; > + } > } else { > ofproto_rule_change_cookie(ofproto, rule, op->flow_cookie); > + ovs_mutex_lock(&rule->timeout_mutex); > + rule->idle_timeout = op->idle_timeout; > + rule->hard_timeout = op->hard_timeout; > + ovs_mutex_unlock(&rule->timeout_mutex); > if (op->ofpacts) { > free(rule->ofpacts); > rule->ofpacts = op->ofpacts; > @@ -4976,6 +5037,7 @@ ofopgroup_complete(struct ofopgroup *group) > op->ofpacts = NULL; > op->ofpacts_len = 0; > } > + rule->send_flow_removed = op->send_flow_removed; > } > break; > > @@ -5029,6 +5091,11 @@ ofoperation_create(struct ofopgroup *group, struct > rule *rule, > op->type = type; > op->reason = reason; > op->flow_cookie = rule->flow_cookie; > + ovs_mutex_lock(&rule->timeout_mutex); > + op->idle_timeout = rule->idle_timeout; > + op->hard_timeout = rule->hard_timeout; > + ovs_mutex_unlock(&rule->timeout_mutex); > + op->send_flow_removed = rule->send_flow_removed; > > group->n_running++; > > @@ -5060,14 +5127,7 @@ ofoperation_destroy(struct ofoperation *op) > * indicate success or an OpenFlow error code on failure. > * > * If 'error' is 0, indicating success, the operation will be committed > - * permanently to the flow table. There is one interesting subcase: > - * > - * - If 'op' is an "add flow" operation that is replacing an existing rule > in > - * the flow table (the "victim" rule) by a new one, then the caller must > - * have uninitialized any derived state in the victim rule, as in step 5 > in > - * the "Life Cycle" in ofproto/ofproto-provider.h. > ofoperation_complete() > - * performs steps 6 and 7 for the victim rule, most notably by calling > its > - * ->rule_dealloc() function. > + * permanently to the flow table. > * > * If 'error' is nonzero, then generally the operation will be rolled back: > * > @@ -5099,13 +5159,6 @@ ofoperation_complete(struct ofoperation *op, enum > ofperr error) > ofopgroup_complete(group); > } > } > - > -struct rule * > -ofoperation_get_victim(struct ofoperation *op) > -{ > - ovs_assert(op->type == OFOPERATION_ADD); > - return op->victim; > -} > > static uint64_t > pick_datapath_id(const struct ofproto *ofproto) > @@ -5547,15 +5600,13 @@ oftable_remove_rule(struct rule *rule) > ovs_rwlock_unlock(&table->cls.rwlock); > } > > -/* Inserts 'rule' into its oftable. Removes any existing rule from 'rule''s > - * oftable that has an identical cls_rule. Returns the rule that was > removed, > - * if any, and otherwise NULL. */ > -static struct rule * > -oftable_replace_rule(struct rule *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) > { > struct ofproto *ofproto = rule->ofproto; > struct oftable *table = &ofproto->tables[rule->table_id]; > - struct rule *victim; > bool may_expire; > > ovs_mutex_lock(&rule->timeout_mutex); > @@ -5573,35 +5624,9 @@ oftable_replace_rule(struct rule *rule) > list_insert(&meter->rules, &rule->meter_list_node); > } > ovs_rwlock_wrlock(&table->cls.rwlock); > - victim = rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr)); > + classifier_insert(&table->cls, &rule->cr); > ovs_rwlock_unlock(&table->cls.rwlock); > - if (victim) { > - if (victim->meter_id) { > - list_remove(&victim->meter_list_node); > - } > - cookies_remove(ofproto, victim); > - > - ovs_mutex_lock(&ofproto->expirable_mutex); > - if (!list_is_empty(&victim->expirable)) { > - list_remove(&victim->expirable); > - } > - ovs_mutex_unlock(&ofproto->expirable_mutex); > - eviction_group_remove_rule(victim); > - } > eviction_group_add_rule(rule); > - return victim; > -} > - > -/* Removes 'old' from its oftable then, if 'new' is nonnull, inserts 'new'. > */ > -static void > -oftable_substitute_rule(struct rule *old, struct rule *new) > -{ > - if (new) { > - oftable_replace_rule(new); > - } else { > - ovs_rwlock_wrlock(&old->evict); > - oftable_remove_rule(old); > - } > } > > /* unixctl commands. */ > -- > 1.7.10.4 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev