I posted this review on the wrong patch, but here it is on the right one: FWIW I think this is the right choice given the options. May be worth adding a comment in the actual code as to why we can't just execute the rule and have to queue it instead.
Acked-by: Ethan Jackson <et...@nicira.com> On Tue, Sep 10, 2013 at 10:27 PM, Ben Pfaff <b...@nicira.com> wrote: > One goal we're moving toward is to be able to execute "learn" actions > in each thread that handles packets that arrive on an interface just before > we forward those packets. The planned strategy there is to have a global > lock that protects everything required to modify the flow table. Generally > this works out well, but rule_execute() is a trouble spot. That's because > it's called during a flow table modification (when that global lock is > held) which means that a "learn" action triggered by the executing the > packet would try to recursively modify the flow table and reacquire the > global lock. > > I can see two ways out of this issue. One would be to make the global lock > a recursive one, or otherwise deal with handling recursive flow_mods. The > other is to just queue up flow_mods due to rule_execute(), which itself is > a corner case (it only happens when a flow_mod sent via OpenFlow includes > a buffer_id). (I guess there could be other more radical solutions, like > just dropping packets that contain "learn" actions if they occur in this > situation.) > > This commit implements the second solution because it seems less likely to > be wrong in a way that crashes the switch. > > Signed-off-by: Ben Pfaff <b...@nicira.com> > --- > ofproto/ofproto-provider.h | 4 ++ > ofproto/ofproto.c | 141 > +++++++++++++++++++++++++++++++++----------- > 2 files changed, 109 insertions(+), 36 deletions(-) > > diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h > index 7c71aad..d23ff9c 100644 > --- a/ofproto/ofproto-provider.h > +++ b/ofproto/ofproto-provider.h > @@ -21,6 +21,7 @@ > > #include "cfm.h" > #include "classifier.h" > +#include "guarded-list.h" > #include "heap.h" > #include "hindex.h" > #include "list.h" > @@ -98,6 +99,9 @@ struct ofproto { > unsigned int n_pending; /* list_size(&pending). */ > struct hmap deletions; /* All OFOPERATION_DELETE "ofoperation"s. */ > > + /* Delayed rule executions. */ > + struct guarded_list rule_executes; > + > /* Flow table operation logging. */ > int n_add, n_delete, n_modify; /* Number of unreported ops of each kind. > */ > long long int first_op, last_op; /* Range of times for unreported ops. */ > diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c > index 7cdca26..10c803c 100644 > --- a/ofproto/ofproto.c > +++ b/ofproto/ofproto.c > @@ -188,6 +188,17 @@ static uint32_t rule_eviction_priority(struct rule *); > static void eviction_group_add_rule(struct rule *); > static void eviction_group_remove_rule(struct rule *); > > +/* A packet that needs to be passed to rule_execute(). */ > +struct rule_execute { > + struct list list_node; /* In struct ofproto's "rule_executes" list. > */ > + struct rule *rule; /* Owns a reference to the rule. */ > + ofp_port_t in_port; > + struct ofpbuf *packet; /* Owns the packet. */ > +}; > + > +static void run_rule_executes(struct ofproto *); > +static void destroy_rule_executes(struct ofproto *); > + > /* ofport. */ > static void ofport_destroy__(struct ofport *); > static void ofport_destroy(struct ofport *); > @@ -449,6 +460,7 @@ ofproto_create(const char *datapath_name, const char > *datapath_type, > list_init(&ofproto->pending); > ofproto->n_pending = 0; > hmap_init(&ofproto->deletions); > + guarded_list_init(&ofproto->rule_executes); > ofproto->n_add = ofproto->n_delete = ofproto->n_modify = 0; > ofproto->first_op = ofproto->last_op = LLONG_MIN; > ofproto->next_op_report = LLONG_MAX; > @@ -1146,6 +1158,9 @@ ofproto_destroy__(struct ofproto *ofproto) > ovs_assert(list_is_empty(&ofproto->pending)); > ovs_assert(!ofproto->n_pending); > > + destroy_rule_executes(ofproto); > + guarded_list_destroy(&ofproto->rule_executes); > + > if (ofproto->meters) { > meter_delete(ofproto, 1, ofproto->meter_features.max_meters); > free(ofproto->meters); > @@ -1287,6 +1302,8 @@ ofproto_run(struct ofproto *p) > VLOG_ERR_RL(&rl, "%s: run failed (%s)", p->name, > ovs_strerror(error)); > } > > + run_rule_executes(p); > + > /* Restore the eviction group heap invariant occasionally. */ > if (p->eviction_group_timer < time_msec()) { > size_t i; > @@ -2416,22 +2433,48 @@ ofoperation_has_out_port(const struct ofoperation > *op, ofp_port_t out_port) > NOT_REACHED(); > } > > -/* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s > - * statistics appropriately. > - * > - * 'packet' doesn't necessarily have to match 'rule'. 'rule' will be > credited > - * with statistics for 'packet' either way. > - * > - * Takes ownership of 'packet'. */ > -static int > -rule_execute(struct rule *rule, ofp_port_t in_port, struct ofpbuf *packet) > +static void > +rule_execute_destroy(struct rule_execute *e) > { > - struct flow flow; > - union flow_in_port in_port_; > + ofproto_rule_unref(e->rule); > + list_remove(&e->list_node); > + free(e); > +} > + > +/* Executes all "rule_execute" operations queued up in > ofproto->rule_executes, > + * by passing them to the ofproto provider. */ > +static void > +run_rule_executes(struct ofproto *ofproto) > +{ > + struct rule_execute *e, *next; > + struct list executes; > + > + guarded_list_steal_all(&ofproto->rule_executes, &executes); > + LIST_FOR_EACH_SAFE (e, next, list_node, &executes) { > + union flow_in_port in_port_; > + struct flow flow; > + > + in_port_.ofp_port = e->in_port; > + flow_extract(e->packet, 0, 0, NULL, &in_port_, &flow); > + ofproto->ofproto_class->rule_execute(e->rule, &flow, e->packet); > > - in_port_.ofp_port = in_port; > - flow_extract(packet, 0, 0, NULL, &in_port_, &flow); > - return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet); > + rule_execute_destroy(e); > + } > +} > + > +/* Destroys and discards all "rule_execute" operations queued up in > + * ofproto->rule_executes. */ > +static void > +destroy_rule_executes(struct ofproto *ofproto) > +{ > + struct rule_execute *e, *next; > + struct list executes; > + > + guarded_list_steal_all(&ofproto->rule_executes, &executes); > + LIST_FOR_EACH_SAFE (e, next, list_node, &executes) { > + ofpbuf_delete(e->packet); > + rule_execute_destroy(e); > + } > } > > /* Returns true if 'rule' should be hidden from the controller. > @@ -4027,35 +4070,46 @@ static enum ofperr > handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn, > struct ofputil_flow_mod *fm, const struct ofp_header *oh) > { > - if (ofproto->n_pending >= 50) { > - ovs_assert(!list_is_empty(&ofproto->pending)); > - return OFPROTO_POSTPONE; > - } > + enum ofperr error; > > - switch (fm->command) { > - case OFPFC_ADD: > - return add_flow(ofproto, ofconn, fm, oh); > + if (ofproto->n_pending < 50) { > + switch (fm->command) { > + case OFPFC_ADD: > + error = add_flow(ofproto, ofconn, fm, oh); > + break; > > - case OFPFC_MODIFY: > - return modify_flows_loose(ofproto, ofconn, fm, oh); > + case OFPFC_MODIFY: > + error = modify_flows_loose(ofproto, ofconn, fm, oh); > + break; > > - case OFPFC_MODIFY_STRICT: > - return modify_flow_strict(ofproto, ofconn, fm, oh); > + case OFPFC_MODIFY_STRICT: > + error = modify_flow_strict(ofproto, ofconn, fm, oh); > + break; > > - case OFPFC_DELETE: > - return delete_flows_loose(ofproto, ofconn, fm, oh); > + case OFPFC_DELETE: > + error = delete_flows_loose(ofproto, ofconn, fm, oh); > + break; > > - case OFPFC_DELETE_STRICT: > - return delete_flow_strict(ofproto, ofconn, fm, oh); > + case OFPFC_DELETE_STRICT: > + error = delete_flow_strict(ofproto, ofconn, fm, oh); > + 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); > + 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 OFPERR_OFPFMFC_BAD_COMMAND; > + } else { > + ovs_assert(!list_is_empty(&ofproto->pending)); > + error = OFPROTO_POSTPONE; > } > + > + run_rule_executes(ofproto); > + return error; > } > > static enum ofperr > @@ -5480,8 +5534,23 @@ ofopgroup_complete(struct ofopgroup *group) > error = ofconn_pktbuf_retrieve(group->ofconn, > group->buffer_id, > &packet, &in_port); > if (packet) { > + struct rule_execute *re; > + > ovs_assert(!error); > - error = rule_execute(op->rule, in_port, packet); > + > + ofproto_rule_ref(op->rule); > + > + re = xmalloc(sizeof *re); > + re->rule = op->rule; > + re->in_port = in_port; > + re->packet = packet; > + > + if (!guarded_list_append(&ofproto->rule_executes, > + &re->list_node, 1024)) { > + ofproto_rule_unref(op->rule); > + ofpbuf_delete(re->packet); > + free(re); > + } > } > break; > } > -- > 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