Instead of storing the (big) struct ofputil_flow_mod, create the new rule and/or create the rule criteria for matching at bundle message insert time. This change reduces the size of a bundle flow mod from 3.5kb to 272 bytes, not counting the created rule, which was anyway created during bundle commit.
In successful bundles this shifts work out of the ofproto_mutex critical section and should thus reduce the time the mutex is held during bundle commit. Signed-off-by: Jarno Rajahalme <ja...@ovn.org> --- v4: Fixed comments & memory leak. ofproto/bundles.c | 2 +- ofproto/bundles.h | 4 +- ofproto/ofproto-provider.h | 43 +++- ofproto/ofproto.c | 576 ++++++++++++++++++++++++++++----------------- tests/ofproto.at | 4 - 5 files changed, 399 insertions(+), 230 deletions(-) diff --git a/ofproto/bundles.c b/ofproto/bundles.c index aa8e58c..e8fb7ba 100644 --- a/ofproto/bundles.c +++ b/ofproto/bundles.c @@ -66,7 +66,7 @@ ofp_bundle_remove__(struct ofconn *ofconn, struct ofp_bundle *bundle, LIST_FOR_EACH_POP (msg, node, &bundle->msg_list) { if (success && msg->type == OFPTYPE_FLOW_MOD) { /* Tell connmgr about successful flow mods. */ - ofconn_report_flow_mod(ofconn, msg->ofm.fm.command); + ofconn_report_flow_mod(ofconn, msg->ofm.command); } ofp_bundle_entry_free(msg); } diff --git a/ofproto/bundles.h b/ofproto/bundles.h index 2054303..802de77 100644 --- a/ofproto/bundles.h +++ b/ofproto/bundles.h @@ -36,7 +36,7 @@ struct ofp_bundle_entry { enum ofptype type; /* OFPTYPE_FLOW_MOD, OFPTYPE_PORT_MOD, or * OFPTYPE_GROUP_MOD. */ union { - struct ofproto_flow_mod ofm; /* ofm.fm.ofpacts must be malloced. */ + struct ofproto_flow_mod ofm; struct ofproto_port_mod opm; struct ofproto_group_mod ogm; }; @@ -90,7 +90,7 @@ ofp_bundle_entry_free(struct ofp_bundle_entry *entry) { if (entry) { if (entry->type == OFPTYPE_FLOW_MOD) { - free(entry->ofm.fm.ofpacts); + ofproto_flow_mod_uninit(&entry->ofm); } else if (entry->type == OFPTYPE_GROUP_MOD) { ofputil_uninit_group_mod(&entry->ogm.gm); } diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 67a85e0..401d1b5 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -1838,25 +1838,64 @@ extern const struct ofproto_class ofproto_dpif_class; int ofproto_class_register(const struct ofproto_class *); int ofproto_class_unregister(const struct ofproto_class *); +/* Criteria that flow_mod and other operations use for selecting rules on + * which to operate. */ +struct rule_criteria { + /* An OpenFlow table or 255 for all tables. */ + uint8_t table_id; + + /* OpenFlow matching criteria. Interpreted different in "loose" way by + * collect_rules_loose() and "strict" way by collect_rules_strict(), as + * defined in the OpenFlow spec. */ + struct cls_rule cr; + ovs_version_t version; + + /* Matching criteria for the OpenFlow cookie. Consider a bit B in a rule's + * cookie and the corresponding bits C in 'cookie' and M in 'cookie_mask'. + * The rule will not be selected if M is 1 and B != C. */ + ovs_be64 cookie; + ovs_be64 cookie_mask; + + /* Selection based on actions within a rule: + * + * If out_port != OFPP_ANY, selects only rules that output to out_port. + * If out_group != OFPG_ALL, select only rules that output to out_group. */ + ofp_port_t out_port; + uint32_t out_group; + + /* If true, collects only rules that are modifiable. */ + bool include_hidden; + bool include_readonly; +}; + /* flow_mod with execution context. */ struct ofproto_flow_mod { - struct ofputil_flow_mod fm; + /* Allocated by 'init' phase, may be freed after 'start' phase, as these + * are not needed for 'revert' nor 'finish'. */ + struct rule *temp_rule; + struct rule_criteria criteria; + struct cls_conjunction *conjs; + size_t n_conjs; /* Replicate needed fields from ofputil_flow_mod to not need it after the * flow has been created. */ uint16_t command; uint32_t buffer_id; bool modify_cookie; - + /* Fields derived from ofputil_flow_mod. */ bool modify_may_add_flow; enum nx_flow_update_event event; + /* These are only used during commit execution. + * ofproto_flow_mod_uninit() does NOT clean these up. */ ovs_version_t version; /* Version in which changes take * effect. */ struct rule_collection old_rules; /* Affected rules. */ struct rule_collection new_rules; /* Replacement rules. */ }; +void ofproto_flow_mod_uninit(struct ofproto_flow_mod *); + /* port_mod with execution context. */ struct ofproto_port_mod { struct ofputil_port_mod pm; diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 8e59c69..ddd7f9c 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -129,36 +129,6 @@ static void eviction_group_add_rule(struct rule *) 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. */ -struct rule_criteria { - /* An OpenFlow table or 255 for all tables. */ - uint8_t table_id; - - /* OpenFlow matching criteria. Interpreted different in "loose" way by - * collect_rules_loose() and "strict" way by collect_rules_strict(), as - * defined in the OpenFlow spec. */ - struct cls_rule cr; - ovs_version_t version; - - /* Matching criteria for the OpenFlow cookie. Consider a bit B in a rule's - * cookie and the corresponding bits C in 'cookie' and M in 'cookie_mask'. - * The rule will not be selected if M is 1 and B != C. */ - ovs_be64 cookie; - ovs_be64 cookie_mask; - - /* Selection based on actions within a rule: - * - * If out_port != OFPP_ANY, selects only rules that output to out_port. - * If out_group != OFPG_ALL, select only rules that output to out_group. */ - ofp_port_t out_port; - uint32_t out_group; - - /* If true, collects only rules that are modifiable. */ - bool include_hidden; - bool include_readonly; -}; - static void rule_criteria_init(struct rule_criteria *, uint8_t table_id, const struct match *match, int priority, ovs_version_t version, @@ -264,17 +234,19 @@ struct openflow_mod_requester { }; /* OpenFlow. */ -static enum ofperr replace_rule_create(struct ofproto *, - struct ofproto_flow_mod *ofm, - const struct ofputil_flow_mod *, - struct cls_rule *cr, uint8_t table_id, - struct rule *old_rule, +static enum ofperr ofproto_rule_create(struct ofproto *, struct cls_rule *, + uint8_t table_id, ovs_be64 new_cookie, + uint16_t idle_timeout, + uint16_t hard_timeout, + enum ofputil_flow_mod_flags flags, + uint16_t importance, + const struct ofpact *ofpacts, + size_t ofpacts_len, struct rule **new_rule) - OVS_REQUIRES(ofproto_mutex); + OVS_NO_THREAD_SAFETY_ANALYSIS; -static void replace_rule_start(struct ofproto *, ovs_version_t version, - struct rule *old_rule, struct rule *new_rule, - struct cls_conjunction *, size_t n_conjs) +static void replace_rule_start(struct ofproto *, struct ofproto_flow_mod *, + struct rule *old_rule, struct rule *new_rule) OVS_REQUIRES(ofproto_mutex); static void replace_rule_revert(struct ofproto *, struct rule *old_rule, @@ -297,6 +269,10 @@ static void send_buffered_packet(const struct openflow_mod_requester *, static bool ofproto_group_exists(const struct ofproto *, uint32_t group_id); static void handle_openflow(struct ofconn *, const struct ofpbuf *); +static enum ofperr ofproto_flow_mod_init(struct ofproto *, + struct ofproto_flow_mod *, + const struct ofputil_flow_mod *fm) + OVS_EXCLUDED(ofproto_mutex); static enum ofperr ofproto_flow_mod_start(struct ofproto *, struct ofproto_flow_mod *) OVS_REQUIRES(ofproto_mutex); @@ -2196,8 +2172,7 @@ ofproto_flow_mod(struct ofproto *ofproto, const struct ofputil_flow_mod *fm) OVS_VERSION_MAX)); if (rule) { /* Reading many of the rule fields and writing on 'modified' - * requires the rule->mutex. Also, rule->actions may change - * if rule->mutex is not held. */ + * requires the rule->mutex. */ const struct rule_actions *actions; ovs_mutex_lock(&rule->mutex); @@ -4073,6 +4048,7 @@ static void rule_criteria_destroy(struct rule_criteria *criteria) { cls_rule_destroy(&criteria->cr); + criteria->version = OVS_VERSION_NOT_REMOVED; /* Mark as destroyed. */ } /* Schedules postponed removal of rules, destroys 'rules'. */ @@ -4627,7 +4603,6 @@ evict_rules_from_table(struct oftable *table) static void get_conjunctions(const struct ofputil_flow_mod *fm, struct cls_conjunction **conjsp, size_t *n_conjsp) - OVS_REQUIRES(ofproto_mutex) { struct cls_conjunction *conjs = NULL; int n_conjs = 0; @@ -4673,24 +4648,28 @@ get_conjunctions(const struct ofputil_flow_mod *fm, * be reverted. * * The caller retains ownership of 'fm->ofpacts'. */ + +/* Creates a new flow according to 'fm' and stores it to 'ofm' for later + * reference. If the flow replaces other flow, it will be updated to match + * modify semantics later. + * + * On successful return the caller must complete the operation by calling + * add_flow_start(), and if that succeeds, then either add_flow_finish(), or + * add_flow_revert() if the operation needs to be reverted due to a later + * failure. + */ static enum ofperr -add_flow_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) - OVS_REQUIRES(ofproto_mutex) +add_flow_init(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, + const struct ofputil_flow_mod *fm) + OVS_EXCLUDED(ofproto_mutex) { - struct ofputil_flow_mod *fm = &ofm->fm; - struct rule **old_rule = rule_collection_stub(&ofm->old_rules); - struct rule **new_rule = rule_collection_stub(&ofm->new_rules); struct oftable *table; struct cls_rule cr; - struct rule *rule; uint8_t table_id; - struct cls_conjunction *conjs; - size_t n_conjs; enum ofperr error; if (!check_table_id(ofproto, fm->table_id)) { - error = OFPERR_OFPBRC_BAD_TABLE_ID; - return error; + return OFPERR_OFPBRC_BAD_TABLE_ID; } /* Pick table. */ @@ -4727,47 +4706,71 @@ add_flow_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) cls_rule_init(&cr, &fm->match, fm->priority); + /* Allocate new rule. Destroys 'cr'. */ + error = ofproto_rule_create(ofproto, &cr, table - ofproto->tables, + fm->new_cookie, fm->idle_timeout, + fm->hard_timeout, fm->flags, fm->importance, + fm->ofpacts, fm->ofpacts_len, &ofm->temp_rule); + if (error) { + return error; + } + + get_conjunctions(fm, &ofm->conjs, &ofm->n_conjs); + return 0; +} + +static enum ofperr +add_flow_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) + OVS_REQUIRES(ofproto_mutex) +{ + struct rule *old_rule = NULL; + struct rule *new_rule = ofm->temp_rule; + const struct rule_actions *actions = rule_get_actions(new_rule); + struct oftable *table = &ofproto->tables[new_rule->table_id]; + enum ofperr error; + + /* Must check actions while holding ofproto_mutex to avoid a race. */ + error = ofproto_check_ofpacts(ofproto, actions->ofpacts, + actions->ofpacts_len); + if (error) { + return error; + } + /* Check for the existence of an identical rule. * This will not return rules earlier marked for removal. */ - rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls, &cr, - ofm->version)); - *old_rule = rule; - if (!rule) { + old_rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls, + &new_rule->cr, + ofm->version)); + if (!old_rule) { /* Check for overlap, if requested. */ - if (fm->flags & OFPUTIL_FF_CHECK_OVERLAP - && classifier_rule_overlaps(&table->cls, &cr, ofm->version)) { - cls_rule_destroy(&cr); + if (new_rule->flags & OFPUTIL_FF_CHECK_OVERLAP + && classifier_rule_overlaps(&table->cls, &new_rule->cr, + ofm->version)) { return OFPERR_OFPFMFC_OVERLAP; } /* If necessary, evict an existing rule to clear out space. */ if (table->n_flows >= table->max_flows) { - if (!choose_rule_to_evict(table, &rule)) { - error = OFPERR_OFPFMFC_TABLE_FULL; - cls_rule_destroy(&cr); - return error; + if (!choose_rule_to_evict(table, &old_rule)) { + return OFPERR_OFPFMFC_TABLE_FULL; } - eviction_group_remove_rule(rule); - /* Marks '*old_rule' as an evicted rule rather than replaced rule. + eviction_group_remove_rule(old_rule); + /* Marks 'old_rule' as an evicted rule rather than replaced rule. */ - rule->removed_reason = OFPRR_EVICTION; - *old_rule = rule; + old_rule->removed_reason = OFPRR_EVICTION; } } else { ofm->modify_cookie = true; } - /* Allocate new rule. */ - error = replace_rule_create(ofproto, ofm, fm, &cr, table - ofproto->tables, - rule, new_rule); - if (error) { - return error; + if (old_rule) { + rule_collection_add(&ofm->old_rules, old_rule); } + /* Take ownership of the temp_rule. */ + rule_collection_stub(&ofm->new_rules)[0] = new_rule; + ofm->temp_rule = NULL; - get_conjunctions(fm, &conjs, &n_conjs); - replace_rule_start(ofproto, ofm->version, rule, *new_rule, conjs, n_conjs); - free(conjs); - + replace_rule_start(ofproto, ofm, old_rule, new_rule); return 0; } @@ -4776,14 +4779,10 @@ static void add_flow_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { - struct rule *old_rule = rule_collection_stub(&ofm->old_rules)[0]; + struct rule *old_rule = rule_collection_n(&ofm->old_rules) + ? rule_collection_stub(&ofm->old_rules)[0] : NULL; struct rule *new_rule = rule_collection_stub(&ofm->new_rules)[0]; - if (old_rule && old_rule->removed_reason == OFPRR_EVICTION) { - /* Revert the eviction. */ - eviction_group_add_rule(old_rule); - } - replace_rule_revert(ofproto, old_rule, new_rule); } @@ -4793,7 +4792,8 @@ add_flow_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, const struct openflow_mod_requester *req) OVS_REQUIRES(ofproto_mutex) { - struct rule *old_rule = rule_collection_stub(&ofm->old_rules)[0]; + struct rule *old_rule = rule_collection_n(&ofm->old_rules) + ? rule_collection_stub(&ofm->old_rules)[0] : NULL; struct rule *new_rule = rule_collection_stub(&ofm->new_rules)[0]; struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies); @@ -4816,14 +4816,16 @@ add_flow_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, /* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */ -/* Create a new rule based on attributes in 'fm', match in 'cr', 'table_id', - * and 'old_rule'. Note that the rule is NOT inserted into a any data +/* Create a new rule. Note that the rule is NOT inserted into a any data * structures yet. Takes ownership of 'cr'. */ static enum ofperr -replace_rule_create(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, - const struct ofputil_flow_mod *fm, - struct cls_rule *cr, uint8_t table_id, - struct rule *old_rule, struct rule **new_rule) +ofproto_rule_create(struct ofproto *ofproto, struct cls_rule *cr, + uint8_t table_id, ovs_be64 new_cookie, + uint16_t idle_timeout, uint16_t hard_timeout, + enum ofputil_flow_mod_flags flags, uint16_t importance, + const struct ofpact *ofpacts, size_t ofpacts_len, + struct rule **new_rule) + OVS_NO_THREAD_SAFETY_ANALYSIS { struct rule *rule; enum ofperr error; @@ -4840,46 +4842,28 @@ replace_rule_create(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, *CONST_CAST(struct ofproto **, &rule->ofproto) = ofproto; cls_rule_move(CONST_CAST(struct cls_rule *, &rule->cr), cr); ovs_refcount_init(&rule->ref_count); - rule->flow_cookie = fm->new_cookie; - rule->created = rule->modified = time_msec(); ovs_mutex_init(&rule->mutex); ovs_mutex_lock(&rule->mutex); - rule->idle_timeout = fm->idle_timeout; - rule->hard_timeout = fm->hard_timeout; - *CONST_CAST(uint16_t *, &rule->importance) = fm->importance; + rule->flow_cookie = new_cookie; + rule->created = rule->modified = time_msec(); + rule->idle_timeout = idle_timeout; + rule->hard_timeout = hard_timeout; + *CONST_CAST(uint16_t *, &rule->importance) = importance; rule->removed_reason = OVS_OFPRR_NONE; *CONST_CAST(uint8_t *, &rule->table_id) = table_id; - rule->flags = fm->flags & OFPUTIL_FF_STATE; + rule->flags = flags & OFPUTIL_FF_STATE; + *CONST_CAST(const struct rule_actions **, &rule->actions) - = rule_actions_create(fm->ofpacts, fm->ofpacts_len); + = rule_actions_create(ofpacts, ofpacts_len); + ovs_list_init(&rule->meter_list_node); rule->eviction_group = NULL; - ovs_list_init(&rule->expirable); rule->monitor_flags = 0; rule->add_seqno = 0; rule->modify_seqno = 0; - - /* Copy values from old rule for modify semantics. */ - if (old_rule && old_rule->removed_reason != OFPRR_EVICTION) { - bool change_cookie = (ofm->modify_cookie - && fm->new_cookie != OVS_BE64_MAX - && fm->new_cookie != old_rule->flow_cookie); - - ovs_mutex_lock(&old_rule->mutex); - if (fm->command != OFPFC_ADD) { - rule->idle_timeout = old_rule->idle_timeout; - rule->hard_timeout = old_rule->hard_timeout; - *CONST_CAST(uint16_t *, &rule->importance) = old_rule->importance; - rule->flags = old_rule->flags; - rule->created = old_rule->created; - } - if (!change_cookie) { - rule->flow_cookie = old_rule->flow_cookie; - } - ovs_mutex_unlock(&old_rule->mutex); - } + ovs_list_init(&rule->expirable); ovs_mutex_unlock(&rule->mutex); /* Construct rule, initializing derived state. */ @@ -4896,16 +4880,37 @@ replace_rule_create(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, } static void -replace_rule_start(struct ofproto *ofproto, ovs_version_t version, - struct rule *old_rule, struct rule *new_rule, - struct cls_conjunction *conjs, size_t n_conjs) +replace_rule_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, + struct rule *old_rule, struct rule *new_rule) { struct oftable *table = &ofproto->tables[new_rule->table_id]; /* 'old_rule' may be either an evicted rule or replaced rule. */ if (old_rule) { + /* Copy values from old rule for modify semantics. */ + if (old_rule->removed_reason != OFPRR_EVICTION) { + bool change_cookie = (ofm->modify_cookie + && new_rule->flow_cookie != OVS_BE64_MAX + && new_rule->flow_cookie != old_rule->flow_cookie); + + ovs_mutex_lock(&new_rule->mutex); + ovs_mutex_lock(&old_rule->mutex); + if (ofm->command != OFPFC_ADD) { + new_rule->idle_timeout = old_rule->idle_timeout; + new_rule->hard_timeout = old_rule->hard_timeout; + *CONST_CAST(uint16_t *, &new_rule->importance) = old_rule->importance; + new_rule->flags = old_rule->flags; + new_rule->created = old_rule->created; + } + if (!change_cookie) { + new_rule->flow_cookie = old_rule->flow_cookie; + } + ovs_mutex_unlock(&old_rule->mutex); + ovs_mutex_unlock(&new_rule->mutex); + } + /* Mark the old rule for removal in the next version. */ - cls_rule_make_invisible_in_version(&old_rule->cr, version); + cls_rule_make_invisible_in_version(&old_rule->cr, ofm->version); /* Remove the old rule from data structures. */ ofproto_rule_remove__(ofproto, old_rule); @@ -4918,7 +4923,8 @@ replace_rule_start(struct ofproto *ofproto, ovs_version_t version, ofproto_rule_insert__(ofproto, new_rule); /* Make the new rule visible for classifier lookups only from the next * version. */ - classifier_insert(&table->cls, &new_rule->cr, version, conjs, n_conjs); + classifier_insert(&table->cls, &new_rule->cr, ofm->version, ofm->conjs, + ofm->n_conjs); } static void @@ -4928,6 +4934,11 @@ replace_rule_revert(struct ofproto *ofproto, struct oftable *table = &ofproto->tables[new_rule->table_id]; if (old_rule) { + if (old_rule->removed_reason == OFPRR_EVICTION) { + /* Revert the eviction. */ + eviction_group_add_rule(old_rule); + } + /* Restore the old rule to data structures. */ ofproto_rule_insert__(ofproto, old_rule); @@ -4978,6 +4989,9 @@ replace_rule_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, learned_cookies_dec(ofproto, old_actions, dead_cookies); if (replaced_rule) { + enum nx_flow_update_event event = ofm->command == OFPFC_ADD + ? NXFME_ADDED : NXFME_MODIFIED; + bool changed_cookie = (new_rule->flow_cookie != old_rule->flow_cookie); @@ -4986,9 +5000,9 @@ replace_rule_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, old_actions->ofpacts, old_actions->ofpacts_len); - if (ofm->event != NXFME_MODIFIED || changed_actions + if (event != NXFME_MODIFIED || changed_actions || changed_cookie) { - ofmonitor_report(ofproto->connmgr, new_rule, ofm->event, 0, + ofmonitor_report(ofproto->connmgr, new_rule, event, 0, req ? req->ofconn : NULL, req ? req->request->xid : 0, changed_actions ? old_actions : NULL); @@ -5007,47 +5021,67 @@ static enum ofperr modify_flows_start__(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { - struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *old_rules = &ofm->old_rules; struct rule_collection *new_rules = &ofm->new_rules; enum ofperr error; - rule_collection_init(new_rules); - if (rule_collection_n(old_rules) > 0) { - struct cls_conjunction *conjs; - size_t n_conjs; - /* Create a new 'modified' rule for each old rule. */ - struct rule *old_rule; - RULE_COLLECTION_FOR_EACH (old_rule, old_rules) { - struct rule *new_rule; - struct cls_rule cr; + struct rule *old_rule, *new_rule; + const struct rule_actions *actions = rule_get_actions(ofm->temp_rule); - cls_rule_clone(&cr, &old_rule->cr); - error = replace_rule_create(ofproto, ofm, fm, &cr, - old_rule->table_id, old_rule, - &new_rule); - if (!error) { - rule_collection_add(new_rules, new_rule); + /* Must check actions while holding ofproto_mutex to avoid a race. */ + error = ofproto_check_ofpacts(ofproto, actions->ofpacts, + actions->ofpacts_len); + if (error) { + return error; + } + + /* Use the temp rule as the first new rule, and as the template for + * the rest. */ + struct rule *temp = ofm->temp_rule; + ofm->temp_rule = NULL; /* We consume the template. */ + + bool first = true; + RULE_COLLECTION_FOR_EACH (old_rule, old_rules) { + if (first) { + /* The template rule's match is possibly a loose one, so it + * must be replaced with the old rule's match so that the new + * rule actually replaces the old one. */ + cls_rule_destroy(CONST_CAST(struct cls_rule *, &temp->cr)); + cls_rule_clone(CONST_CAST(struct cls_rule *, &temp->cr), + &old_rule->cr); + *CONST_CAST(uint8_t *, &temp->table_id) = old_rule->table_id; + rule_collection_add(new_rules, temp); + first = false; } else { - rule_collection_unref(new_rules); - rule_collection_destroy(new_rules); - return error; + struct cls_rule cr; + cls_rule_clone(&cr, &old_rule->cr); + error = ofproto_rule_create(ofproto, &cr, old_rule->table_id, + temp->flow_cookie, + temp->idle_timeout, + temp->hard_timeout, temp->flags, + temp->importance, + temp->actions->ofpacts, + temp->actions->ofpacts_len, + &new_rule); + if (!error) { + rule_collection_add(new_rules, new_rule); + } else { + rule_collection_unref(new_rules); + rule_collection_destroy(new_rules); + return error; + } } } ovs_assert(rule_collection_n(new_rules) == rule_collection_n(old_rules)); - get_conjunctions(fm, &conjs, &n_conjs); - struct rule *new_rule; RULE_COLLECTIONS_FOR_EACH (old_rule, new_rule, old_rules, new_rules) { - replace_rule_start(ofproto, ofm->version, old_rule, new_rule, - conjs, n_conjs); + replace_rule_start(ofproto, ofm, old_rule, new_rule); } - free(conjs); } else if (ofm->modify_may_add_flow) { - /* No match, add a new flow. */ + /* No match, add a new flow, consumes 'temp'. */ error = add_flow_start(ofproto, ofm); new_rules->collection.n = 1; } else { @@ -5057,6 +5091,24 @@ modify_flows_start__(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) return error; } +static enum ofperr +modify_flows_init_loose(struct ofproto *ofproto, + struct ofproto_flow_mod *ofm, + const struct ofputil_flow_mod *fm) + OVS_EXCLUDED(ofproto_mutex) +{ + rule_criteria_init(&ofm->criteria, fm->table_id, &fm->match, 0, + OVS_VERSION_MAX, fm->cookie, fm->cookie_mask, OFPP_ANY, + OFPG_ANY); + rule_criteria_require_rw(&ofm->criteria, + (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); + /* Must create a new flow in advance for the case that no matches are + * found. Also used for template for multiple modified flows. */ + add_flow_init(ofproto, ofm, fm); + + return 0; +} + /* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code on * failure. * @@ -5066,17 +5118,10 @@ static enum ofperr modify_flows_start_loose(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { - struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *old_rules = &ofm->old_rules; - struct rule_criteria criteria; enum ofperr error; - rule_criteria_init(&criteria, fm->table_id, &fm->match, 0, OVS_VERSION_MAX, - fm->cookie, fm->cookie_mask, OFPP_ANY, OFPG_ANY); - rule_criteria_require_rw(&criteria, - (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); - error = collect_rules_loose(ofproto, &criteria, old_rules); - rule_criteria_destroy(&criteria); + error = collect_rules_loose(ofproto, &ofm->criteria, old_rules); if (!error) { error = modify_flows_start__(ofproto, ofm); @@ -5085,6 +5130,7 @@ modify_flows_start_loose(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) if (error) { rule_collection_destroy(old_rules); } + return error; } @@ -5096,10 +5142,7 @@ modify_flows_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) struct rule_collection *new_rules = &ofm->new_rules; /* Old rules were not changed yet, only need to revert new rules. */ - if (rule_collection_n(old_rules) == 0 - && rule_collection_n(new_rules) == 1) { - add_flow_revert(ofproto, ofm); - } else if (rule_collection_n(old_rules) > 0) { + if (rule_collection_n(old_rules) > 0) { struct rule *old_rule, *new_rule; RULE_COLLECTIONS_FOR_EACH (old_rule, new_rule, old_rules, new_rules) { replace_rule_revert(ofproto, old_rule, new_rule); @@ -5140,33 +5183,40 @@ modify_flows_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, } } +static enum ofperr +modify_flow_init_strict(struct ofproto *ofproto OVS_UNUSED, + struct ofproto_flow_mod *ofm, + const struct ofputil_flow_mod *fm) + OVS_EXCLUDED(ofproto_mutex) +{ + rule_criteria_init(&ofm->criteria, fm->table_id, &fm->match, fm->priority, + OVS_VERSION_MAX, fm->cookie, fm->cookie_mask, OFPP_ANY, + OFPG_ANY); + rule_criteria_require_rw(&ofm->criteria, + (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); + /* Must create a new flow in advance for the case that no matches are + * found. Also used for template for multiple modified flows. */ + add_flow_init(ofproto, ofm, fm); + + return 0; +} + /* Implements OFPFC_MODIFY_STRICT. Returns 0 on success or an OpenFlow error * code on failure. */ static enum ofperr modify_flow_start_strict(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { - struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *old_rules = &ofm->old_rules; - struct rule_criteria criteria; enum ofperr error; - rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority, - OVS_VERSION_MAX, fm->cookie, fm->cookie_mask, OFPP_ANY, - OFPG_ANY); - rule_criteria_require_rw(&criteria, - (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); - error = collect_rules_strict(ofproto, &criteria, old_rules); - rule_criteria_destroy(&criteria); + error = collect_rules_strict(ofproto, &ofm->criteria, old_rules); if (!error) { /* collect_rules_strict() can return max 1 rule. */ error = modify_flows_start__(ofproto, ofm); } - if (error) { - rule_collection_destroy(old_rules); - } return error; } @@ -5262,23 +5312,29 @@ delete_flows__(struct rule_collection *rules, } } +static enum ofperr +delete_flows_init_loose(struct ofproto *ofproto OVS_UNUSED, + struct ofproto_flow_mod *ofm, + const struct ofputil_flow_mod *fm) + OVS_EXCLUDED(ofproto_mutex) +{ + rule_criteria_init(&ofm->criteria, fm->table_id, &fm->match, 0, + OVS_VERSION_MAX, fm->cookie, fm->cookie_mask, + fm->out_port, fm->out_group); + rule_criteria_require_rw(&ofm->criteria, + (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); + return 0; +} + /* Implements OFPFC_DELETE. */ static enum ofperr delete_flows_start_loose(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { - const struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *rules = &ofm->old_rules; - struct rule_criteria criteria; enum ofperr error; - rule_criteria_init(&criteria, fm->table_id, &fm->match, 0, OVS_VERSION_MAX, - fm->cookie, fm->cookie_mask, fm->out_port, - fm->out_group); - rule_criteria_require_rw(&criteria, - (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); - error = collect_rules_loose(ofproto, &criteria, rules); - rule_criteria_destroy(&criteria); + error = collect_rules_loose(ofproto, &ofm->criteria, rules); if (!error) { delete_flows_start__(ofproto, ofm->version, rules); @@ -5292,7 +5348,6 @@ delete_flows_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { delete_flows_revert__(ofproto, &ofm->old_rules); - rule_collection_destroy(&ofm->old_rules); } static void @@ -5304,24 +5359,30 @@ delete_flows_finish(struct ofproto *ofproto, delete_flows_finish__(ofproto, &ofm->old_rules, OFPRR_DELETE, req); } +static enum ofperr +delete_flows_init_strict(struct ofproto *ofproto OVS_UNUSED, + struct ofproto_flow_mod *ofm, + const struct ofputil_flow_mod *fm) + OVS_EXCLUDED(ofproto_mutex) +{ + rule_criteria_init(&ofm->criteria, fm->table_id, &fm->match, fm->priority, + OVS_VERSION_MAX, fm->cookie, fm->cookie_mask, + fm->out_port, fm->out_group); + rule_criteria_require_rw(&ofm->criteria, + (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); + return 0; +} + /* Implements OFPFC_DELETE_STRICT. */ static enum ofperr delete_flow_start_strict(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { - const struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *rules = &ofm->old_rules; - struct rule_criteria criteria; enum ofperr error; - rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority, - OVS_VERSION_MAX, fm->cookie, fm->cookie_mask, - fm->out_port, fm->out_group); - rule_criteria_require_rw(&criteria, - (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); - error = collect_rules_strict(ofproto, &criteria, rules); - rule_criteria_destroy(&criteria); + error = collect_rules_strict(ofproto, &ofm->criteria, rules); if (!error) { delete_flows_start__(ofproto, ofm->version, rules); @@ -5447,7 +5508,10 @@ handle_flow_mod__(struct ofproto *ofproto, const struct ofputil_flow_mod *fm, struct ofproto_flow_mod ofm; enum ofperr error; - ofm.fm = *fm; + error = ofproto_flow_mod_init(ofproto, &ofm, fm); + if (error) { + return error; + } ovs_mutex_lock(&ofproto_mutex); ofm.version = ofproto->tables_version + 1; @@ -7022,46 +7086,107 @@ handle_table_mod(struct ofconn *ofconn, const struct ofp_header *oh) return table_mod(ofproto, &tm); } +/* Free resources that may be allocated by ofproto_flow_mod_init(). */ +void +ofproto_flow_mod_uninit(struct ofproto_flow_mod *ofm) +{ + if (ofm->temp_rule) { + ofproto_rule_unref(ofm->temp_rule); + ofm->temp_rule = NULL; + } + if (ofm->criteria.version != OVS_VERSION_NOT_REMOVED) { + rule_criteria_destroy(&ofm->criteria); + } + if (ofm->conjs) { + free(ofm->conjs); + ofm->conjs = NULL; + ofm->n_conjs = 0; + } +} + static enum ofperr -ofproto_flow_mod_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) - OVS_REQUIRES(ofproto_mutex) +ofproto_flow_mod_init(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, + const struct ofputil_flow_mod *fm) + OVS_EXCLUDED(ofproto_mutex) { - /* Must check actions while holding ofproto_mutex to avoid a race. */ - enum ofperr error = ofproto_check_ofpacts(ofproto, ofm->fm.ofpacts, - ofm->fm.ofpacts_len); + enum ofperr error; + + /* Forward flow mod fields we need later. */ + ofm->command = fm->command; + ofm->buffer_id = fm->buffer_id; + ofm->modify_cookie = fm->modify_cookie; + + ofm->modify_may_add_flow = (fm->new_cookie != OVS_BE64_MAX + && fm->cookie_mask == htonll(0)); + + /* Initialize state needed by ofproto_flow_mod_uninit(). */ + ofm->temp_rule = NULL; + ofm->criteria.version = OVS_VERSION_NOT_REMOVED; + ofm->conjs = NULL; + ofm->n_conjs = 0; + + switch (ofm->command) { + case OFPFC_ADD: + error = add_flow_init(ofproto, ofm, fm); + break; + case OFPFC_MODIFY: + error = modify_flows_init_loose(ofproto, ofm, fm); + break; + case OFPFC_MODIFY_STRICT: + error = modify_flow_init_strict(ofproto, ofm, fm); + break; + case OFPFC_DELETE: + error = delete_flows_init_loose(ofproto, ofm, fm); + break; + case OFPFC_DELETE_STRICT: + error = delete_flows_init_strict(ofproto, ofm, fm); + break; + default: + error = OFPERR_OFPFMFC_BAD_COMMAND; + break; + } if (error) { - return error; + ofproto_flow_mod_uninit(ofm); } + return error; +} - /* Forward flow mod fields we need later. */ - ofm->command = ofm->fm.command; - ofm->buffer_id = ofm->fm.buffer_id; - ofm->modify_cookie = ofm->fm.modify_cookie; +static enum ofperr +ofproto_flow_mod_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) + OVS_REQUIRES(ofproto_mutex) +{ + enum ofperr error; - ofm->modify_may_add_flow = (ofm->fm.new_cookie != OVS_BE64_MAX - && ofm->fm.cookie_mask == htonll(0)); + rule_collection_init(&ofm->old_rules); + rule_collection_init(&ofm->new_rules); switch (ofm->command) { case OFPFC_ADD: - ofm->event = NXFME_ADDED; - return add_flow_start(ofproto, ofm); - /* , &be->old_rules.stub[0], - &be->new_rules.stub[0]); */ + error = add_flow_start(ofproto, ofm); + break; case OFPFC_MODIFY: - ofm->event = NXFME_MODIFIED; - return modify_flows_start_loose(ofproto, ofm); + error = modify_flows_start_loose(ofproto, ofm); + break; case OFPFC_MODIFY_STRICT: - ofm->event = NXFME_MODIFIED; - return modify_flow_start_strict(ofproto, ofm); + error = modify_flow_start_strict(ofproto, ofm); + break; case OFPFC_DELETE: - ofm->event = NXFME_DELETED; - return delete_flows_start_loose(ofproto, ofm); + error = delete_flows_start_loose(ofproto, ofm); + break; case OFPFC_DELETE_STRICT: - ofm->event = NXFME_DELETED; - return delete_flow_start_strict(ofproto, ofm); + error = delete_flow_start_strict(ofproto, ofm); + break; + default: + OVS_NOT_REACHED(); } + /* Release resources not needed after start. */ + ofproto_flow_mod_uninit(ofm); - return OFPERR_OFPFMFC_BAD_COMMAND; + if (error) { + rule_collection_destroy(&ofm->old_rules); + rule_collection_destroy(&ofm->new_rules); + } + return error; } static void @@ -7086,6 +7211,8 @@ ofproto_flow_mod_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) default: break; } + rule_collection_destroy(&ofm->old_rules); + rule_collection_destroy(&ofm->new_rules); } static void @@ -7113,6 +7240,9 @@ ofproto_flow_mod_finish(struct ofproto *ofproto, break; } + rule_collection_destroy(&ofm->old_rules); + rule_collection_destroy(&ofm->new_rules); + if (req) { ofconn_report_flow_mod(req->ofconn, ofm->command); } @@ -7318,6 +7448,7 @@ handle_bundle_control(struct ofconn *ofconn, const struct ofp_header *oh) static enum ofperr handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh) + OVS_EXCLUDED(ofproto_mutex) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); enum ofperr error; @@ -7340,17 +7471,20 @@ handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh) if (type == OFPTYPE_PORT_MOD) { error = ofputil_decode_port_mod(badd.msg, &bmsg->opm.pm, false); } else if (type == OFPTYPE_FLOW_MOD) { + struct ofputil_flow_mod fm; struct ofpbuf ofpacts; uint64_t ofpacts_stub[1024 / 8]; ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); - error = ofputil_decode_flow_mod(&bmsg->ofm.fm, badd.msg, + error = ofputil_decode_flow_mod(&fm, badd.msg, ofconn_get_protocol(ofconn), &ofpacts, u16_to_ofp(ofproto->max_ports), ofproto->n_tables); - /* Move actions to heap. */ - bmsg->ofm.fm.ofpacts = ofpbuf_steal_data(&ofpacts); + if (!error) { + error = ofproto_flow_mod_init(ofproto, &bmsg->ofm, &fm); + } + ofpbuf_uninit(&ofpacts); } else if (type == OFPTYPE_GROUP_MOD) { error = ofputil_decode_group_mod(badd.msg, &bmsg->ogm.gm); } else { diff --git a/tests/ofproto.at b/tests/ofproto.at index 9a7ee89..147ac23 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -4921,8 +4921,6 @@ add table=254 actions=drop AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt 2>&1 | sed '/talking to/,$d'], [0], [dnl Error OFPBRC_EPERM for: OFPT_FLOW_MOD (OF1.4) (xid=0xb): ADD table:254 actions=drop -Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4) (xid=0xe): - bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl @@ -5408,8 +5406,6 @@ add table=254 actions=drop AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle add-flows br0 flows.txt 2>&1 | sed '/talking to/,$d'], [0], [dnl Error OFPBRC_EPERM for: OFPT_FLOW_MOD (OF1.3) (xid=0xb): ADD table:254 actions=drop -Error OFPBFC_MSG_FAILED for: ONFT_BUNDLE_CONTROL (OF1.3) (xid=0xe): - bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl -- 2.1.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev