[ovs-dev] [PATCH v3] ofproto: Reduce bundle memory use.
Jarno Rajahalme
jarno at ovn.org
Mon Aug 8 18:28:49 UTC 2016
I just sent a v4, no need to review this one.
Jarno
> On Jul 29, 2016, at 6:37 PM, Jarno Rajahalme <jarno at ovn.org> wrote:
>
> 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. These can be uninitialized right after the start phase
> during the commit, which may also reduce the total memory needed
> during the bundle commit.
>
> Signed-off-by: Jarno Rajahalme <jarno at ovn.org>
> ---
> v3: Reduced code duplication + use the template rule for the first
> modified instead of creating another rule for it.
>
> ofproto/bundles.c | 2 +-
> ofproto/bundles.h | 4 +-
> ofproto/ofproto-provider.h | 43 +++-
> ofproto/ofproto.c | 577 ++++++++++++++++++++++++++++-----------------
> tests/ofproto.at | 4 -
> 5 files changed, 400 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..0a5383b 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);
> @@ -5003,51 +5017,72 @@ replace_rule_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
> }
> }
>
> +/* 'ofm->new_rules' has the template rule, which this function will consume. */
> 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 +5092,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 +5119,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 +5131,7 @@ modify_flows_start_loose(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
> if (error) {
> rule_collection_destroy(old_rules);
> }
> +
> return error;
> }
>
> @@ -5096,10 +5143,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 +5184,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 +5313,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 +5349,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 +5360,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 +5509,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 +7087,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 +7212,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 +7241,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 +7449,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 +7472,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
>
>
>
More information about the dev
mailing list