> On May 29, 2015, at 6:40 PM, Ben Pfaff <b...@nicira.com> wrote: > > On Mon, May 18, 2015 at 04:10:24PM -0700, Jarno Rajahalme wrote: >> Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com> > > You know, we could make evictions reversible, since after all we have a > way to mark rules as to-be-deleted and then not delete them. I don't > know whether it's worthwhile.
I’ll look into this. > The handling of "conflicts" seems, well, conflicted. I see one comment > that says we check for conflicts at bundle add time: > > /* Bundle conflicts. A new message being added into the bundle is deemed to > be > * in conflict, if it would delete or modify a flow or a port that was added or > * modified by an earlier message in the bundle. > * > * We check for this at the bundle add time, and rely on the messages not being > * in conflict at the bundle commit time. This simplifies the state management > * requirements for rolling back failing commits. > > and another that says we check for them at bundle commit time: > > /* Check for conflicts. */ > > /* The spec says to return OFPBFC_MSG_CONFLICT when processing the > * BUNDLE_ADD, but with big bundles that may be very expensive. It may be > * a lot cheaper to check that mods/deletes do not target any of the rules > * added/modded by the current bundle at the commit time. */ > > but I couldn't find any actual code that returns OFPBFC_MSG_CONFLICT > anywhere. I’m sorry for the confusion. Both of these comments were remnants of an earlier (incomplete) version. I have removed them now, thanks for spotting them! > > Worse, I couldn't find any good definition of a "conflict" in the > OpenFlow specification. It just says: > > If the message in the request is incompatible with another message > already stored in the bun dle, the switch must refuse to add the > message to the bundle and send an ofp_error_msg with > OFPET_BUNDLE_FAILED type and OFPBFC_MSG_CONFLICT code. > > I think we might have to ask for clarification. > For now I don’t see a case where we’d need to use this. > I wouldn't use ovs_assert() like this: > ovs_assert(classifier_remove(&table->cls, &new_rule->cr)); > because defining NDEBUG will then delete this code. Instead: > if (!classifier_remove(&table->cls, &new_rule->cr)) { > OVS_NOT_REACHED(); > } > OK. > I guess that the following special case in handle_bundle_control() and > handle_flow_mod__() could be moved into ofputil_decode_flow_mod(), so > that we could assume at execution time that the command is a valid one: > + default: > + if (fm->command > 0xff) { > + VLOG_WARN_RL(&rl, "%s: flow_mod has explicit table_id but " > + "flow_mod_table_id extension is not enabled", > + ofproto->name); > + } > + error = OFPERR_OFPFMFC_BAD_COMMAND; > + break; > Done. > I think that some code duplication could be eliminated by writing > handle_flow_mod__() in terms of do_bundle_flow_mod_begin() and > do_bundle_flow_mod_finish(). > Thanks for the hint, this ended up removing more code than I initially thought! > Acked-by: Ben Pfaff <b...@nicira.com> Thanks for the review! I also noticed that in the bundle case I did not call ofconn_report_flow_mod(), I folded that in as well. Applied to master with the following incremental based on the above, Jarno diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 17a0c41..0f9a38d 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -1795,11 +1795,19 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, fm->command = command & 0xff; fm->table_id = command >> 8; } else { + if (command > 0xff) { + VLOG_WARN_RL(&bad_ofmsg_rl, "flow_mod has explicit table_id " + "but flow_mod_table_id extension is not enabled"); + } fm->command = command; fm->table_id = 0xff; } } + if (fm->command > OFPFC_DELETE_STRICT) { + return OFPERR_OFPFMFC_BAD_COMMAND; + } + error = ofpacts_pull_openflow_instructions(&b, b.size, oh->version, ofpacts); if (error) { diff --git a/ofproto/bundles.c b/ofproto/bundles.c index 9f125a5..686d61f 100644 --- a/ofproto/bundles.c +++ b/ofproto/bundles.c @@ -59,11 +59,16 @@ ofp_bundle_create(uint32_t id, uint16_t flags) } void -ofp_bundle_remove__(struct ofconn *ofconn, struct ofp_bundle *bundle) +ofp_bundle_remove__(struct ofconn *ofconn, struct ofp_bundle *bundle, + bool success) { struct ofp_bundle_entry *msg; 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->fm.command); + } ofp_bundle_entry_free(msg); } @@ -81,7 +86,7 @@ ofp_bundle_open(struct ofconn *ofconn, uint32_t id, uint16_t flags) if (bundle) { VLOG_INFO("Bundle %x already exists.", id); - ofp_bundle_remove__(ofconn, bundle); + ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BAD_ID; } @@ -107,12 +112,12 @@ ofp_bundle_close(struct ofconn *ofconn, uint32_t id, uint16_t flags) } if (bundle->state == BS_CLOSED) { - ofp_bundle_remove__(ofconn, bundle); + ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BUNDLE_CLOSED; } if (bundle->flags != flags) { - ofp_bundle_remove__(ofconn, bundle); + ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BAD_FLAGS; } @@ -131,19 +136,11 @@ ofp_bundle_discard(struct ofconn *ofconn, uint32_t id) return OFPERR_OFPBFC_BAD_ID; } - ofp_bundle_remove__(ofconn, bundle); + ofp_bundle_remove__(ofconn, bundle, false); return 0; } -/* Bundle conflicts. A new message being added into the bundle is deemed to be - * in conflict, if it would delete or modify a flow or a port that was added or - * modified by an earlier message in the bundle. - * - * We check for this at the bundle add time, and rely on the messages not being - * in conflict at the bundle commit time. This simplifies the state management - * requirements for rolling back failing commits. - */ enum ofperr ofp_bundle_add_message(struct ofconn *ofconn, uint32_t id, uint16_t flags, struct ofp_bundle_entry *bmsg) @@ -162,20 +159,13 @@ ofp_bundle_add_message(struct ofconn *ofconn, uint32_t id, uint16_t flags, return error; } } else if (bundle->state == BS_CLOSED) { - ofp_bundle_remove__(ofconn, bundle); + ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BUNDLE_CLOSED; } else if (flags != bundle->flags) { - ofp_bundle_remove__(ofconn, bundle); + ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BAD_FLAGS; } - /* Check for conflicts. */ - - /* The spec says to return OFPBFC_MSG_CONFLICT when processing the - * BUNDLE_ADD, but with big bundles that may be very expensive. It may be - * a lot cheaper to check that mods/deletes do not target any of the rules - * added/modded by the current bundle at the commit time. */ - list_push_back(&bundle->msg_list, &bmsg->node); return 0; } diff --git a/ofproto/bundles.h b/ofproto/bundles.h index adc0f89..ab3a137 100644 --- a/ofproto/bundles.h +++ b/ofproto/bundles.h @@ -75,7 +75,7 @@ enum ofperr ofp_bundle_discard(struct ofconn *, uint32_t id); enum ofperr ofp_bundle_add_message(struct ofconn *, uint32_t id, uint16_t flags, struct ofp_bundle_entry *); -void ofp_bundle_remove__(struct ofconn *ofconn, struct ofp_bundle *bundle); +void ofp_bundle_remove__(struct ofconn *, struct ofp_bundle *, bool success); static inline struct ofp_bundle_entry * ofp_bundle_entry_alloc(enum ofptype type, const struct ofp_header *oh) diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c index 9851997..495364f 100644 --- a/ofproto/connmgr.c +++ b/ofproto/connmgr.c @@ -1215,7 +1215,7 @@ bundle_remove_all(struct ofconn *ofconn) struct ofp_bundle *b, *next; HMAP_FOR_EACH_SAFE (b, next, node, &ofconn->bundles) { - ofp_bundle_remove__(ofconn, b); + ofp_bundle_remove__(ofconn, b, false); } } diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index ad8ecca..cb91197 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -253,9 +253,6 @@ struct flow_mod_requester { }; /* OpenFlow. */ -static enum ofperr add_flow(struct ofproto *, struct ofputil_flow_mod *, - const struct flow_mod_requester *); - static enum ofperr modify_flow_check__(struct ofproto *, struct ofputil_flow_mod *, const struct rule *) @@ -281,6 +278,15 @@ static bool ofproto_group_exists(const struct ofproto *ofproto, OVS_EXCLUDED(ofproto->groups_rwlock); static enum ofperr add_group(struct ofproto *, struct ofputil_group_mod *); static void handle_openflow(struct ofconn *, const struct ofpbuf *); +static enum ofperr do_bundle_flow_mod_begin(struct ofproto *, + struct ofputil_flow_mod *, + struct ofp_bundle_entry *) + OVS_REQUIRES(ofproto_mutex); +static void do_bundle_flow_mod_finish(struct ofproto *, + struct ofputil_flow_mod *, + const struct flow_mod_requester *, + struct ofp_bundle_entry *) + OVS_REQUIRES(ofproto_mutex); static enum ofperr handle_flow_mod__(struct ofproto *, struct ofputil_flow_mod *, const struct flow_mod_requester *) @@ -4338,6 +4344,17 @@ set_conjunctions(struct rule *rule, const struct cls_conjunction *conjs, cls_rule_set_conjunctions(cr, conjs, n_conjs); } +/* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT + * in which no matching flow already exists in the flow table. + * + * Adds the flow specified by 'fm', to the ofproto's flow table. Returns 0 on + * success, or an OpenFlow error code on failure. + * + * On successful return the caller must complete the operation either by + * calling add_flow_finish(), or add_flow_revert() if the operation needs to + * be reverted. + * + * The caller retains ownership of 'fm->ofpacts'. */ static enum ofperr add_flow_begin(struct ofproto *ofproto, struct ofputil_flow_mod *fm, struct rule **rulep, bool *modify) @@ -4463,7 +4480,9 @@ add_flow_revert(struct ofproto *ofproto, struct rule *new_rule) if (new_rule) { struct oftable *table = &ofproto->tables[new_rule->table_id]; - ovs_assert(classifier_remove(&table->cls, &new_rule->cr)); + if (!classifier_remove(&table->cls, &new_rule->cr)) { + OVS_NOT_REACHED(); + } classifier_publish(&table->cls); ofproto_rule_remove__(ofproto, new_rule); @@ -4511,30 +4530,6 @@ add_flow_finish(struct ofproto *ofproto, struct ofputil_flow_mod *fm, send_buffered_packet(req, fm->buffer_id, rule); } - -/* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT - * in which no matching flow already exists in the flow table. - * - * Adds the flow specified by 'fm', to the ofproto's flow table. Returns 0 on - * success, or an OpenFlow error code on failure. - * - * The caller retains ownership of 'fm->ofpacts'. */ -static enum ofperr -add_flow(struct ofproto *ofproto, struct ofputil_flow_mod *fm, - const struct flow_mod_requester *req) - OVS_REQUIRES(ofproto_mutex) -{ - struct rule *rule; - bool modify; - enum ofperr error; - - error = add_flow_begin(ofproto, fm, &rule, &modify); - if (!error) { - add_flow_finish(ofproto, fm, req, rule, modify); - } - - return error; -} /* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */ @@ -4789,22 +4784,6 @@ modify_flows_finish(struct ofproto *ofproto, struct ofputil_flow_mod *fm, rule_collection_destroy(rules); } -static enum ofperr -modify_flows_loose(struct ofproto *ofproto, struct ofputil_flow_mod *fm, - const struct flow_mod_requester *req) - OVS_REQUIRES(ofproto_mutex) -{ - struct rule_collection rules; - enum ofperr error; - - error = modify_flows_begin_loose(ofproto, fm, &rules); - if (!error) { - modify_flows_finish(ofproto, fm, req, &rules); - } - - return error; -} - /* Implements OFPFC_MODIFY_STRICT. Returns 0 on success or an OpenFlow error * code on failure. */ static enum ofperr @@ -4832,23 +4811,6 @@ modify_flow_begin_strict(struct ofproto *ofproto, struct ofputil_flow_mod *fm, } return error; } - -static enum ofperr -modify_flow_strict(struct ofproto *ofproto, struct ofputil_flow_mod *fm, - const struct flow_mod_requester *req) - OVS_REQUIRES(ofproto_mutex) -{ - struct rule_collection rules; - enum ofperr error; - - error = modify_flow_begin_strict(ofproto, fm, &rules); - if (!error) { - modify_flows_finish(ofproto, fm, req, &rules); - } - - return error; -} - /* OFPFC_DELETE implementation. */ @@ -4949,23 +4911,6 @@ delete_flows_finish(const struct ofputil_flow_mod *fm, rule_collection_destroy(rules); } -static enum ofperr -delete_flows_loose(struct ofproto *ofproto, - const struct ofputil_flow_mod *fm, - const struct flow_mod_requester *req) - OVS_REQUIRES(ofproto_mutex) -{ - struct rule_collection rules; - enum ofperr error; - - error = delete_flows_begin_loose(ofproto, fm, &rules); - if (!error) { - delete_flows_finish(fm, req, &rules); - } - - return error; -} - /* Implements OFPFC_DELETE_STRICT. */ static enum ofperr delete_flow_begin_strict(struct ofproto *ofproto, @@ -4995,22 +4940,6 @@ delete_flow_begin_strict(struct ofproto *ofproto, return error; } -static enum ofperr -delete_flow_strict(struct ofproto *ofproto, const struct ofputil_flow_mod *fm, - const struct flow_mod_requester *req) - OVS_REQUIRES(ofproto_mutex) -{ - struct rule_collection rules; - enum ofperr error; - - error = delete_flow_begin_strict(ofproto, fm, &rules); - if (!error) { - delete_flows_finish(fm, req, &rules); - } - - return error; -} - static void ofproto_rule_send_removed(struct rule *rule, uint8_t reason) OVS_REQUIRES(ofproto_mutex) @@ -5141,38 +5070,13 @@ handle_flow_mod__(struct ofproto *ofproto, struct ofputil_flow_mod *fm, const struct flow_mod_requester *req) OVS_EXCLUDED(ofproto_mutex) { + struct ofp_bundle_entry be; enum ofperr error; ovs_mutex_lock(&ofproto_mutex); - switch (fm->command) { - case OFPFC_ADD: - error = add_flow(ofproto, fm, req); - break; - - case OFPFC_MODIFY: - error = modify_flows_loose(ofproto, fm, req); - break; - - case OFPFC_MODIFY_STRICT: - error = modify_flow_strict(ofproto, fm, req); - break; - - case OFPFC_DELETE: - error = delete_flows_loose(ofproto, fm, req); - break; - - case OFPFC_DELETE_STRICT: - error = delete_flow_strict(ofproto, fm, req); - break; - - default: - if (fm->command > 0xff) { - VLOG_WARN_RL(&rl, "%s: flow_mod has explicit table_id but " - "flow_mod_table_id extension is not enabled", - ofproto->name); - } - error = OFPERR_OFPFMFC_BAD_COMMAND; - break; + error = do_bundle_flow_mod_begin(ofproto, fm, &be); + if (!error) { + do_bundle_flow_mod_finish(ofproto, fm, req, &be); } ofmonitor_flush(ofproto->connmgr); ovs_mutex_unlock(&ofproto_mutex); @@ -6540,40 +6444,24 @@ do_bundle_flow_mod_begin(struct ofproto *ofproto, struct ofputil_flow_mod *fm, struct ofp_bundle_entry *be) OVS_REQUIRES(ofproto_mutex) { - enum ofperr error; - switch (fm->command) { case OFPFC_ADD: - error = add_flow_begin(ofproto, fm, &be->rule, &be->modify); - break; + return add_flow_begin(ofproto, fm, &be->rule, &be->modify); case OFPFC_MODIFY: - error = modify_flows_begin_loose(ofproto, fm, &be->rules); - break; + return modify_flows_begin_loose(ofproto, fm, &be->rules); case OFPFC_MODIFY_STRICT: - error = modify_flow_begin_strict(ofproto, fm, &be->rules); - break; + return modify_flow_begin_strict(ofproto, fm, &be->rules); case OFPFC_DELETE: - error = delete_flows_begin_loose(ofproto, fm, &be->rules); - break; + return delete_flows_begin_loose(ofproto, fm, &be->rules); case OFPFC_DELETE_STRICT: - error = delete_flow_begin_strict(ofproto, fm, &be->rules); - break; - - default: - if (fm->command > 0xff) { - VLOG_WARN_RL(&rl, "%s: flow_mod has explicit table_id but " - "flow_mod_table_id extension is not enabled", - ofproto->name); - } - error = OFPERR_OFPFMFC_BAD_COMMAND; - break; + return delete_flow_begin_strict(ofproto, fm, &be->rules); } - return error; + return OFPERR_OFPFMFC_BAD_COMMAND; } static void @@ -6706,8 +6594,9 @@ do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags) run_rule_executes(ofproto); } + /* The bundle is discarded regardless the outcome. */ - ofp_bundle_remove__(ofconn, bundle); + ofp_bundle_remove__(ofconn, bundle, !error); return error; } _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev