Adding groups support for bundles is simpler if also groups are modified under ofproto_mutex.
Eliminate the search for rules when deleting a group so that we will not keep the mutex for too long. Signed-off-by: Jarno Rajahalme <ja...@ovn.org> --- include/openvswitch/ofp-actions.h | 40 +++++++ ofproto/ofproto-provider.h | 10 +- ofproto/ofproto.c | 234 +++++++++++++++++++++++++------------- 3 files changed, 204 insertions(+), 80 deletions(-) diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h index 0b8ccbb..6355eed 100644 --- a/include/openvswitch/ofp-actions.h +++ b/include/openvswitch/ofp-actions.h @@ -214,12 +214,45 @@ ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len) return (void *) ((uint8_t *) ofpacts + ofpacts_len); } +static inline const struct ofpact * +ofpact_find_type(const struct ofpact *a, enum ofpact_type type, + const struct ofpact * const end) +{ + while (a < end) { + if (a->type == type) { + return a; + } + a = ofpact_next(a); + } + return NULL; +} + +static inline const struct ofpact * +ofpact_find_type_flattened(const struct ofpact *a, enum ofpact_type type, + const struct ofpact * const end) +{ + while (a < end) { + if (a->type == type) { + return a; + } + a = ofpact_next_flattened(a); + } + return NULL; +} + /* Assigns POS to each ofpact, in turn, in the OFPACTS_LEN bytes of ofpacts * starting at OFPACTS. */ #define OFPACT_FOR_EACH(POS, OFPACTS, OFPACTS_LEN) \ for ((POS) = (OFPACTS); (POS) < ofpact_end(OFPACTS, OFPACTS_LEN); \ (POS) = ofpact_next(POS)) +#define OFPACT_FOR_EACH_TYPE(POS, TYPE, OFPACTS, OFPACTS_LEN) \ + for ((POS) = ofpact_find_type(OFPACTS, TYPE, \ + ofpact_end(OFPACTS, OFPACTS_LEN)); \ + (POS); \ + (POS) = ofpact_find_type(ofpact_next(POS), TYPE, \ + ofpact_end(OFPACTS, OFPACTS_LEN))) + /* Assigns POS to each ofpact, in turn, in the OFPACTS_LEN bytes of ofpacts * starting at OFPACTS. * @@ -228,6 +261,13 @@ ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len) #define OFPACT_FOR_EACH_FLATTENED(POS, OFPACTS, OFPACTS_LEN) \ for ((POS) = (OFPACTS); (POS) < ofpact_end(OFPACTS, OFPACTS_LEN); \ (POS) = ofpact_next_flattened(POS)) + +#define OFPACT_FOR_EACH_TYPE_FLATTENED(POS, TYPE, OFPACTS, OFPACTS_LEN) \ + for ((POS) = ofpact_find_type_flattened(OFPACTS, TYPE, \ + ofpact_end(OFPACTS, OFPACTS_LEN)); \ + (POS); \ + (POS) = ofpact_find_type_flattened(ofpact_next_flattened(POS), TYPE, \ + ofpact_end(OFPACTS, OFPACTS_LEN))) /* Action structure for each OFPACT_*. */ diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index dd014ea..8a898a5 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -124,7 +124,6 @@ struct ofproto { int min_mtu; /* Current MTU of non-internal ports. */ /* Groups. */ - struct ovs_rwlock groups_rwlock; struct cmap groups; /* Contains "struct ofgroup"s. */ uint32_t n_groups[4] OVS_GUARDED; /* # of existing groups of each type. */ struct ofputil_group_features ogf; @@ -437,6 +436,7 @@ struct rule_actions { * action whose flags include NX_LEARN_F_DELETE_LEARNED. */ bool has_meter; bool has_learn_with_delete; + bool has_groups; /* Actions. */ uint32_t ofpacts_len; /* Size of 'ofpacts', in bytes. */ @@ -455,11 +455,14 @@ struct rule_collection { size_t n; /* Number of rules collected. */ size_t capacity; /* Number of rules that will fit in 'rules'. */ - struct rule *stub[64]; /* Preallocated rules to avoid malloc(). */ + struct rule *stub[5]; /* Preallocated rules to avoid malloc(). */ }; void rule_collection_init(struct rule_collection *); void rule_collection_add(struct rule_collection *, struct rule *); +void rule_collection_remove(struct rule_collection *, struct rule *); +void rule_collection_move(struct rule_collection *to, + struct rule_collection *from); void rule_collection_ref(struct rule_collection *) OVS_REQUIRES(ofproto_mutex); void rule_collection_unref(struct rule_collection *); void rule_collection_destroy(struct rule_collection *); @@ -513,6 +516,7 @@ struct ofgroup { const struct ofproto *ofproto; /* The ofproto that contains this group. */ const uint32_t group_id; const enum ofp11_group_type type; /* One of OFPGT_*. */ + bool being_deleted; /* Group removal has begun. */ const long long int created; /* Creation time. */ const long long int modified; /* Time of last modification. */ @@ -522,6 +526,8 @@ struct ofgroup { const uint32_t n_buckets; const struct ofputil_group_props props; + + struct rule_collection rules OVS_GUARDED; /* Referring rules. */ }; struct ofgroup *ofproto_group_lookup(const struct ofproto *ofproto, diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 83719a5..7542fc3 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -296,7 +296,8 @@ static void send_buffered_packet(const struct flow_mod_requester *, static bool ofproto_group_exists(const struct ofproto *, uint32_t group_id); static enum ofperr add_group(struct ofproto *, - const struct ofputil_group_mod *); + const struct ofputil_group_mod *) + OVS_REQUIRES(ofproto_mutex); static void handle_openflow(struct ofconn *, const struct ofpbuf *); static enum ofperr ofproto_flow_mod_start(struct ofproto *, struct ofproto_flow_mod *) @@ -559,7 +560,6 @@ ofproto_create(const char *datapath_name, const char *datapath_type, ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name); guarded_list_init(&ofproto->rule_executes); ofproto->min_mtu = INT_MAX; - ovs_rwlock_init(&ofproto->groups_rwlock); cmap_init(&ofproto->groups); ovs_mutex_unlock(&ofproto_mutex); ofproto->ogf.types = 0xf; @@ -1575,7 +1575,8 @@ ofproto_flush__(struct ofproto *ofproto) ovs_mutex_unlock(&ofproto_mutex); } -static void delete_group(struct ofproto *ofproto, uint32_t group_id); +static void delete_group(struct ofproto *ofproto, uint32_t group_id) + OVS_REQUIRES(ofproto_mutex); static void ofproto_destroy__(struct ofproto *ofproto) @@ -1586,7 +1587,6 @@ ofproto_destroy__(struct ofproto *ofproto) destroy_rule_executes(ofproto); guarded_list_destroy(&ofproto->rule_executes); - ovs_rwlock_destroy(&ofproto->groups_rwlock); cmap_destroy(&ofproto->groups); hmap_remove(&all_ofprotos, &ofproto->hmap_node); @@ -2991,8 +2991,10 @@ group_destroy_cb(struct ofgroup *group) void ofproto_group_unref(struct ofgroup *group) + OVS_NO_THREAD_SAFETY_ANALYSIS { if (group && ovs_refcount_unref_relaxed(&group->ref_count) == 1) { + ovs_assert(group->rules.n == 0); ovsrcu_postpone(group_destroy_cb, group); } } @@ -3009,9 +3011,12 @@ rule_actions_create(const struct ofpact *ofpacts, size_t ofpacts_len) actions = xmalloc(sizeof *actions + ofpacts_len); actions->ofpacts_len = ofpacts_len; - actions->has_meter = ofpacts_get_meter(ofpacts, ofpacts_len) != 0; memcpy(actions->ofpacts, ofpacts, ofpacts_len); - + actions->has_meter = ofpacts_get_meter(ofpacts, ofpacts_len) != 0; + actions->has_groups = + (ofpact_find_type_flattened(ofpacts, OFPACT_GROUP, + ofpact_end(ofpacts, ofpacts_len)) + != NULL); actions->has_learn_with_delete = (next_learn_with_delete(actions, NULL) != NULL); @@ -3448,9 +3453,9 @@ ofproto_check_ofpacts(struct ofproto *ofproto, return OFPERR_OFPMMFC_INVALID_METER; } - OFPACT_FOR_EACH_FLATTENED (a, ofpacts, ofpacts_len) { - if (a->type == OFPACT_GROUP - && !ofproto_group_exists(ofproto, ofpact_get_GROUP(a)->group_id)) { + OFPACT_FOR_EACH_TYPE_FLATTENED (a, OFPACT_GROUP, ofpacts, ofpacts_len) { + if (!ofproto_group_exists(ofproto, + ofpact_get_GROUP(a)->group_id)) { return OFPERR_OFPBAC_BAD_OUT_GROUP; } } @@ -3513,6 +3518,8 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh) 0, p->n_tables, ofconn_get_protocol(ofconn)); if (!error) { + /* Should hold ofproto_mutex to guarantee state don't + * change between the check and the execution. */ error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len); if (!error) { error = p->ofproto_class->packet_out(p, payload, &flow, @@ -4068,6 +4075,57 @@ rule_collection_add(struct rule_collection *rules, struct rule *rule) } void +rule_collection_remove(struct rule_collection *rules, struct rule *rule) +{ + size_t i; + + for (i = 0; i < rules->n; i++) { + if (rules->rules[i] == rule) { + break; + } + } + if (i == rules->n) { + return; + } + + rules->n--; + /* Swap the last item in if needed. */ + if (i != rules->n) { + rules->rules[i] = rules->rules[rules->n]; + } + + /* Shrink? Watermark at '/ 4' to avoid hysteresis, but leave spare + * capacity. */ + if (rules->rules != rules->stub && rules->n <= rules->capacity / 4) { + size_t actual_size, new_size; + + actual_size = rules->n * sizeof *rules->rules; + rules->capacity /= 2; + new_size = rules->capacity * sizeof *rules->rules; + + if (new_size <= sizeof(rules->stub)) { + memcpy(rules->stub, rules->rules, actual_size); + free(rules->rules); + rules->rules = rules->stub; + } else { + rules->rules = xrealloc(rules->rules, new_size); + } + } +} + +void +rule_collection_move(struct rule_collection *to, struct rule_collection *from) +{ + ovs_assert(to->n == 0); + + *to = *from; + if (from->rules == from->stub) { + to->rules = to->stub; + } + rule_collection_init(from); +} + +void rule_collection_ref(struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { @@ -4126,6 +4184,7 @@ rule_collection_remove_postponed(struct rule_collection *rules) if (rules->n > 0) { if (rules->n == 1) { ovsrcu_postpone(remove_rule_rcu, rules->rules[0]); + rules->n = 0; } else { ovsrcu_postpone(remove_rules_rcu, rule_collection_detach(rules)); } @@ -4939,8 +4998,8 @@ replace_rule_start(struct ofproto *ofproto, ovs_version_t version, } else { table->n_flows++; } - /* Insert flow to the classifier, so that later flow_mods may relate - * to it. This is reversible, in case later errors require this to + /* Insert flow to ofproto data structures, so that later flow_mods may + * relate to it. This is reversible, in case later errors require this to * be reverted. */ ofproto_rule_insert__(ofproto, new_rule); /* Make the new rule visible for classifier lookups only from the next @@ -4948,8 +5007,9 @@ replace_rule_start(struct ofproto *ofproto, ovs_version_t version, classifier_insert(&table->cls, &new_rule->cr, version, conjs, n_conjs); } -static void replace_rule_revert(struct ofproto *ofproto, - struct rule *old_rule, struct rule *new_rule) +static void +replace_rule_revert(struct ofproto *ofproto, + struct rule *old_rule, struct rule *new_rule) { struct oftable *table = &ofproto->tables[new_rule->table_id]; @@ -5454,10 +5514,6 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) u16_to_ofp(ofproto->max_ports), ofproto->n_tables); if (!error) { - error = ofproto_check_ofpacts(ofproto, ofm.fm.ofpacts, - ofm.fm.ofpacts_len); - } - if (!error) { struct flow_mod_requester req; req.ofconn = ofconn; @@ -6235,7 +6291,7 @@ ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id, return group; } -/* Caller should hold 'ofproto->groups_rwlock' if it is important that the +/* Caller should hold 'ofproto_mutex' if it is important that the * group is not removed by someone else. */ static bool ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id) @@ -6243,36 +6299,21 @@ ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id) return ofproto_group_lookup__(ofproto, group_id) != NULL; } -/* XXX: This is potentially very expensive for large flow tables, and may be - * called in a loop. Maybe explicitly maintain the count of rules referring to - * the group instead?. */ -static uint32_t -group_get_ref_count(struct ofgroup *group) - OVS_EXCLUDED(ofproto_mutex) +static void +group_add_rule(struct ofgroup *group, struct rule *rule) { - struct ofproto *ofproto = CONST_CAST(struct ofproto *, group->ofproto); - struct rule_criteria criteria; - struct rule_collection rules; - struct match match; - enum ofperr error; - uint32_t count; - - match_init_catchall(&match); - rule_criteria_init(&criteria, 0xff, &match, 0, OVS_VERSION_MAX, htonll(0), - htonll(0), OFPP_ANY, group->group_id); - ovs_mutex_lock(&ofproto_mutex); - error = collect_rules_loose(ofproto, &criteria, &rules); - ovs_mutex_unlock(&ofproto_mutex); - rule_criteria_destroy(&criteria); - - count = !error && rules.n < UINT32_MAX ? rules.n : UINT32_MAX; + rule_collection_add(&group->rules, rule); +} - rule_collection_destroy(&rules); - return count; +static void +group_remove_rule(struct ofgroup *group, struct rule *rule) +{ + rule_collection_remove(&group->rules, rule); } static void append_group_stats(struct ofgroup *group, struct ovs_list *replies) + OVS_REQUIRES(ofproto_mutex) { struct ofputil_group_stats ogs; const struct ofproto *ofproto = group->ofproto; @@ -6282,7 +6323,7 @@ append_group_stats(struct ofgroup *group, struct ovs_list *replies) ogs.bucket_stats = xmalloc(group->n_buckets * sizeof *ogs.bucket_stats); /* Provider sets the packet and byte counts, we do the rest. */ - ogs.ref_count = group_get_ref_count(group); + ogs.ref_count = group->rules.n; ogs.n_buckets = group->n_buckets; error = (ofproto->ofproto_class->group_get_stats @@ -6307,25 +6348,26 @@ static void handle_group_request(struct ofconn *ofconn, const struct ofp_header *request, uint32_t group_id, void (*cb)(struct ofgroup *, struct ovs_list *replies)) + OVS_EXCLUDED(ofproto_mutex) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofgroup *group; struct ovs_list replies; ofpmp_init(&replies, request); + /* Must exclude modifications to guarantee iterating groups. */ + ovs_mutex_lock(&ofproto_mutex); if (group_id == OFPG_ALL) { - /* Must exclude modifications to guarantee iterating all groups. */ - ovs_rwlock_rdlock(&ofproto->groups_rwlock); CMAP_FOR_EACH (group, cmap_node, &ofproto->groups) { cb(group, &replies); } - ovs_rwlock_unlock(&ofproto->groups_rwlock); } else { group = ofproto_group_lookup__(ofproto, group_id); if (group) { cb(group, &replies); } } + ovs_mutex_unlock(&ofproto_mutex); ofconn_send_replies(ofconn, &replies); } @@ -6494,6 +6536,7 @@ init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm, *CONST_CAST(long long int *, &((*ofgroup)->created)) = now; *CONST_CAST(long long int *, &((*ofgroup)->modified)) = now; ovs_refcount_init(&(*ofgroup)->ref_count); + (*ofgroup)->being_deleted = false; ovs_list_init(&(*ofgroup)->buckets); ofputil_bucket_clone_list(&(*ofgroup)->buckets, &gm->buckets, NULL); @@ -6503,6 +6546,7 @@ init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm, memcpy(CONST_CAST(struct ofputil_group_props *, &(*ofgroup)->props), &gm->props, sizeof (struct ofputil_group_props)); + rule_collection_init(&(*ofgroup)->rules); /* Construct called BEFORE any locks are held. */ error = ofproto->ofproto_class->group_construct(*ofgroup); @@ -6518,6 +6562,7 @@ init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm, * failure. */ static enum ofperr add_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) + OVS_REQUIRES(ofproto_mutex) { struct ofgroup *ofgroup; enum ofperr error; @@ -6528,10 +6573,6 @@ add_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) return error; } - /* We take the mutex as late as possible to minimize the time we jam any - * other threads: No visible state changes before acquiring the lock. */ - ovs_rwlock_wrlock(&ofproto->groups_rwlock); - if (ofproto->n_groups[gm->type] >= ofproto->ogf.max_groups[gm->type]) { error = OFPERR_OFPGMFC_OUT_OF_GROUPS; goto unlock_out; @@ -6547,11 +6588,9 @@ add_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) hash_int(ofgroup->group_id, 0)); ofproto->n_groups[ofgroup->type]++; - ovs_rwlock_unlock(&ofproto->groups_rwlock); return 0; unlock_out: - ovs_rwlock_unlock(&ofproto->groups_rwlock); group_destroy_cb(ofgroup); return error; } @@ -6661,6 +6700,7 @@ copy_buckets_for_remove_bucket(const struct ofgroup *ofgroup, * the xlate module hold a pointer to the group. */ static enum ofperr modify_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) + OVS_REQUIRES(ofproto_mutex) { struct ofgroup *ofgroup, *new_ofgroup, *retiring; enum ofperr error; @@ -6672,7 +6712,6 @@ modify_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) retiring = new_ofgroup; - ovs_rwlock_wrlock(&ofproto->groups_rwlock); ofgroup = ofproto_group_lookup__(ofproto, gm->group_id); if (!ofgroup) { error = OFPERR_OFPGMFC_UNKNOWN_GROUP; @@ -6713,6 +6752,8 @@ modify_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) /* Replace ofgroup in ofproto's groups hash map with new_ofgroup. */ cmap_replace(&ofproto->groups, &ofgroup->cmap_node, &new_ofgroup->cmap_node, hash_int(ofgroup->group_id, 0)); + /* Transfer rules. */ + rule_collection_move(&new_ofgroup->rules, &ofgroup->rules); if (ofgroup->type != new_ofgroup->type) { ofproto->n_groups[ofgroup->type]--; @@ -6721,7 +6762,6 @@ modify_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) out: ofproto_group_unref(retiring); - ovs_rwlock_unlock(&ofproto->groups_rwlock); return error; } @@ -6729,53 +6769,50 @@ out: * exist yet and modifies it otherwise */ static enum ofperr add_or_modify_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) + OVS_REQUIRES(ofproto_mutex) { enum ofperr error; bool exists; exists = ofproto_group_exists(ofproto, gm->group_id); - /* XXX: Race: Should hold groups_rwlock here. */ - if (!exists) { error = add_group(ofproto, gm); } else { error = modify_group(ofproto, gm); } + return error; } static void delete_group__(struct ofproto *ofproto, struct ofgroup *ofgroup) - OVS_RELEASES(ofproto->groups_rwlock) + OVS_REQUIRES(ofproto_mutex) { - struct match match; - struct ofproto_flow_mod ofm; + /* Makes flow deletion code leave the rule pointers in group's 'rules' + * intact, so that we can later refer to the rules deleted due to the group + * deletion. Rule pointers will be removed from all other groups, if any, + * so we will never try to delete the same rule twice. */ + ofgroup->being_deleted = true; - /* Delete all flow entries containing this group in a group action */ - match_init_catchall(&match); - flow_mod_init(&ofm.fm, &match, 0, NULL, 0, OFPFC_DELETE); - ofm.fm.delete_reason = OFPRR_GROUP_DELETE; - ofm.fm.out_group = ofgroup->group_id; - ofm.fm.table_id = OFPTT_ALL; - handle_flow_mod__(ofproto, &ofm, NULL); + /* Delete all flow entries containing this group in a group action. */ + delete_flows__(&ofgroup->rules, OFPRR_GROUP_DELETE, NULL); cmap_remove(&ofproto->groups, &ofgroup->cmap_node, hash_int(ofgroup->group_id, 0)); /* No-one can find this group any more, but current users may continue to * use it. */ ofproto->n_groups[ofgroup->type]--; - ovs_rwlock_unlock(&ofproto->groups_rwlock); ofproto_group_unref(ofgroup); } /* Implements OFPGC11_DELETE. */ static void delete_group(struct ofproto *ofproto, uint32_t group_id) + OVS_REQUIRES(ofproto_mutex) { struct ofgroup *ofgroup; - ovs_rwlock_wrlock(&ofproto->groups_rwlock); if (group_id == OFPG_ALL) { for (;;) { struct cmap_node *node = cmap_first(&ofproto->groups); @@ -6783,10 +6820,7 @@ delete_group(struct ofproto *ofproto, uint32_t group_id) break; } ofgroup = CONTAINER_OF(node, struct ofgroup, cmap_node); - delete_group__(ofproto, ofgroup); /* Releases the mutex. */ - /* Lock for each node separately, so that we will not jam the - * other threads for too long time. */ - ovs_rwlock_wrlock(&ofproto->groups_rwlock); + delete_group__(ofproto, ofgroup); } } else { CMAP_FOR_EACH_WITH_HASH (ofgroup, cmap_node, @@ -6797,7 +6831,6 @@ delete_group(struct ofproto *ofproto, uint32_t group_id) } } } - ovs_rwlock_unlock(&ofproto->groups_rwlock); } /* Delete all groups from 'ofproto'. @@ -6806,12 +6839,16 @@ delete_group(struct ofproto *ofproto, uint32_t group_id) * function. */ void ofproto_group_delete_all(struct ofproto *ofproto) + OVS_EXCLUDED(ofproto_mutex) { + ovs_mutex_lock(&ofproto_mutex); delete_group(ofproto, OFPG_ALL); + ovs_mutex_unlock(&ofproto_mutex); } static enum ofperr handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh) + OVS_EXCLUDED(ofproto_mutex) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_group_mod gm; @@ -6827,6 +6864,7 @@ handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh) return error; } + ovs_mutex_lock(&ofproto_mutex); switch (gm.command) { case OFPGC11_ADD: error = add_group(ofproto, &gm); @@ -6860,6 +6898,7 @@ handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh) } error = OFPERR_OFPGMFC_BAD_COMMAND; } + ovs_mutex_unlock(&ofproto_mutex); if (!error) { struct ofputil_requestforward rf; @@ -6984,6 +7023,13 @@ static enum ofperr ofproto_flow_mod_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(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); + if (error) { + return error; + } + switch (ofm->fm.command) { case OFPFC_ADD: return add_flow_start(ofproto, ofm); @@ -7263,11 +7309,6 @@ handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh) ofproto->n_tables); /* Move actions to heap. */ bmsg->ofm.fm.ofpacts = ofpbuf_steal_data(&ofpacts); - - if (!error && bmsg->ofm.fm.ofpacts_len) { - error = ofproto_check_ofpacts(ofproto, bmsg->ofm.fm.ofpacts, - bmsg->ofm.fm.ofpacts_len); - } } else { OVS_NOT_REACHED(); } @@ -7980,6 +8021,20 @@ ofproto_rule_insert__(struct ofproto *ofproto, struct rule *rule) if (actions->has_meter) { meter_insert_rule(rule); } + if (actions->has_groups) { + const struct ofpact *a; + + OFPACT_FOR_EACH_TYPE_FLATTENED (a, OFPACT_GROUP, actions->ofpacts, + actions->ofpacts_len) { + struct ofgroup *group; + + group = ofproto_group_lookup(ofproto, + ofpact_get_GROUP(a)->group_id, false); + ovs_assert(group != NULL); + group_add_rule(group, rule); + } + } + rule->removed = false; } @@ -8002,6 +8057,29 @@ ofproto_rule_remove__(struct ofproto *ofproto, struct rule *rule) ovs_list_init(&rule->meter_list_node); } + /* Remove the rule from any groups, except from the group that is being + * deleted, if any. */ + const struct rule_actions *actions = rule_get_actions(rule); + + if (actions->has_groups) { + const struct ofpact *a; + + OFPACT_FOR_EACH_TYPE_FLATTENED (a, OFPACT_GROUP, actions->ofpacts, + actions->ofpacts_len) { + struct ofgroup *group; + + group = ofproto_group_lookup(ofproto, + ofpact_get_GROUP(a)->group_id, false); + ovs_assert(group); + + /* Leave the rule for the group that is being deleted, if any, + * as we still need the list of rules for clean-up. */ + if (!group->being_deleted) { + group_remove_rule(group, rule); + } + } + } + rule->removed = true; } -- 2.1.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev