On Mon, Jul 18, 2016 at 04:21:16PM -0500, Ryan Moats wrote: > Ensure that ovn flow tables are persisted so that changes to > them chan be applied incrementally - this is a prereq for > making lflow_run and physical_run incremental. > > As part of this change, add a one-to-many hindex for finding > desired flows by their parent's UUID. Also extend the mapping > by match from one-to-one to one-to-many. > > Signed-off-by: Ryan Moats <rmo...@us.ibm.com>
I applied this to master. I folded in the following adjustments to style and comments and the new macro HINDEX_FOR_EACH_WITH_HASH_SAFE. Thanks, Ben. --8<--------------------------cut here-------------------------->8-- diff --git a/lib/hindex.h b/lib/hindex.h index 416da05..876c5a9 100644 --- a/lib/hindex.h +++ b/lib/hindex.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Nicira, Inc. + * Copyright (c) 2013, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,6 +132,15 @@ void hindex_remove(struct hindex *, struct hindex_node *); NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER); \ ASSIGN_CONTAINER(NODE, (NODE)->MEMBER.s, MEMBER)) +/* Safe when NODE may be freed (not needed when NODE may be removed from the + * hash map but its members remain accessible and intact). */ +#define HINDEX_FOR_EACH_WITH_HASH_SAFE(NODE, NEXT, MEMBER, HASH, HINDEX) \ + for (INIT_CONTAINER(NODE, hindex_node_with_hash(HINDEX, HASH), MEMBER); \ + (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER) \ + ? INIT_CONTAINER(NEXT, (NODE)->MEMBER.s, MEMBER), 1 \ + : 0); \ + (NODE) = (NEXT)) + /* Returns the head node in 'hindex' with the given 'hash', or a null pointer * if no nodes have that hash value. */ static inline struct hindex_node * diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c index 40e49a1..d10dcc6 100644 --- a/ovn/controller/ofctrl.c +++ b/ovn/controller/ofctrl.c @@ -54,18 +54,12 @@ struct ovn_flow { uint16_t priority; struct match match; - /* Data. UUID is used for disambiquation. */ + /* Data. UUID is used for disambiguation. */ struct uuid uuid; struct ofpact *ofpacts; size_t ofpacts_len; }; -static inline struct ovn_flow * -ovn_flow_from_node(const struct ovs_list *node) -{ - return CONTAINER_OF(node, struct ovn_flow, list_node); -} - static uint32_t ovn_flow_match_hash(const struct ovn_flow *); static void ovn_flow_lookup(struct hmap *, const struct ovn_flow *target, struct ovs_list *answers); @@ -515,26 +509,32 @@ ofctrl_recv(const struct ofp_header *oh, enum ofptype type) /* Flow table interfaces to the rest of ovn-controller. */ -/* Adds a flow to flow tables with the specified 'match' and 'actions' to - * the OpenFlow table numbered 'table_id' with the given 'priority'. The - * caller retains ownership of 'match' and 'actions'. +/* Adds a flow to the collection associated with 'uuid'. The flow has the + * specified 'match' and 'actions' to the OpenFlow table numbered 'table_id' + * with the given 'priority'. The caller retains ownership of 'match' and + * 'actions'. + * + * Any number of flows may be associated with a given UUID. The flows with a + * given UUID must have a unique (table_id, priority, match) tuple. A + * duplicate within a generally indicates a bug in the ovn-controller code that + * generated it, so this functions logs a warning. * - * Because it is possible for both actions and matches to change on a rule, - * and because the hmap struct only supports a single hash, this method - * uses a hash on the defined key of (table_id, priority, match criteria). - * Actions from different parents is supported, with the parent's UUID - * information stored for disambiguation. + * (table_id, priority, match) tuples should also be unique for flows with + * different UUIDs, but it doesn't necessarily indicate a bug in + * ovn-controller, for two reasons. First, these duplicates could be caused by + * logical flows generated by ovn-northd, which aren't ovn-controller's fault; + * perhaps something should warn about these but the root cause is different. + * Second, these duplicates might be transient, that is, they might go away + * before the next call to ofctrl_run() if a call to ofctrl_remove_flows() + * removes one or the other. * * This just assembles the desired flow tables in memory. Nothing is actually * sent to the switch until a later call to ofctrl_run(). */ - void ofctrl_add_flow(uint8_t table_id, uint16_t priority, const struct match *match, const struct ofpbuf *actions, const struct uuid *uuid) { - struct ovs_list existing; - /* Structure that uses table_id+priority+various things as hashes. */ struct ovn_flow *f = xmalloc(sizeof *f); f->table_id = table_id; @@ -542,38 +542,29 @@ ofctrl_add_flow(uint8_t table_id, uint16_t priority, f->match = *match; f->ofpacts = xmemdup(actions->data, actions->size); f->ofpacts_len = actions->size; - memcpy(&f->uuid, uuid, sizeof f->uuid); + f->uuid = *uuid; f->match_hmap_node.hash = ovn_flow_match_hash(f); f->uuid_hindex_node.hash = uuid_hash(&f->uuid); - /* Check to see if other flows exist with the same key (table_id - * priority, match criteria). If so, check if this flow's uuid - * exists already. If so, discard this flow and log a warning. - * If the uuid isn't present, then add the flow. - * - * If no other flows have this key, then add the flow. */ - + /* Check to see if other flows exist with the same key (table_id priority, + * match criteria) and uuid. If so, discard this flow and log a + * warning. */ + struct ovs_list existing; ovn_flow_lookup(&match_flow_table, f, &existing); - - if (!ovs_list_is_empty(&existing)) { - struct ovn_flow *d; - LIST_FOR_EACH(d, list_node, &existing) { - if (f->table_id == d->table_id && f->priority == d->priority - && ofpacts_equal(f->ofpacts, f->ofpacts_len, - d->ofpacts, d->ofpacts_len)) { - if (uuid_equals(&f->uuid, &d->uuid)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - char *s = ovn_flow_to_string(f); - VLOG_WARN_RL(&rl, "found duplicate flow %s for parent "UUID_FMT, - s, UUID_ARGS(&f->uuid)); - ovn_flow_destroy(f); - free(s); - return; - } - } + struct ovn_flow *d; + LIST_FOR_EACH (d, list_node, &existing) { + if (uuid_equals(&f->uuid, &d->uuid)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + char *s = ovn_flow_to_string(f); + VLOG_WARN_RL(&rl, "found duplicate flow %s for parent "UUID_FMT, + s, UUID_ARGS(&f->uuid)); + ovn_flow_destroy(f); + free(s); + return; } } + /* Otherwise, add the flow. */ hmap_insert(&match_flow_table, &f->match_hmap_node, f->match_hmap_node.hash); hindex_insert(&uuid_flow_table, &f->uuid_hindex_node, @@ -581,28 +572,18 @@ ofctrl_add_flow(uint8_t table_id, uint16_t priority, } /* Removes a bundles of flows from the flow table. */ - void ofctrl_remove_flows(const struct uuid *uuid) { - /* Since there isn't an HINDEX_FOR_EACH_SAFE_WITH_HASH macro, - * first, process the hindex and put matching flows into a local list. - * Then, the local list is processed and the items deleted. */ - struct ovs_list delete_list; - ovs_list_init(&delete_list); - - struct ovn_flow *f; - HINDEX_FOR_EACH_WITH_HASH(f, uuid_hindex_node, uuid_hash(uuid), - &uuid_flow_table) { + struct ovn_flow *f, *next; + HINDEX_FOR_EACH_WITH_HASH_SAFE (f, next, uuid_hindex_node, uuid_hash(uuid), + &uuid_flow_table) { if (uuid_equals(&f->uuid, uuid)) { - ovs_list_push_back(&delete_list, &f->list_node); + hmap_remove(&match_flow_table, &f->match_hmap_node); + hindex_remove(&uuid_flow_table, &f->uuid_hindex_node); + ovn_flow_destroy(f); } } - LIST_FOR_EACH_POP(f, list_node, &delete_list) { - hmap_remove(&match_flow_table, &f->match_hmap_node); - hindex_remove(&uuid_flow_table, &f->uuid_hindex_node); - ovn_flow_destroy(f); - } } /* Shortcut to remove all flows matching the supplied UUID and add this @@ -620,18 +601,18 @@ ofctrl_set_flow(uint8_t table_id, uint16_t priority, /* Duplicate an ovn_flow structure. */ struct ovn_flow * -ofctrl_dup_flow(struct ovn_flow *source) -{ - struct ovn_flow *answer = xmalloc(sizeof *answer); - answer->table_id = source->table_id; - answer->priority = source->priority; - answer->match = source->match; - answer->ofpacts = xmemdup(source->ofpacts, source->ofpacts_len); - answer->ofpacts_len = source->ofpacts_len; - answer->uuid = source->uuid; - answer->match_hmap_node.hash = ovn_flow_match_hash(answer); - answer->uuid_hindex_node.hash = uuid_hash(&source->uuid); - return answer; +ofctrl_dup_flow(struct ovn_flow *src) +{ + struct ovn_flow *dst = xmalloc(sizeof *dst); + dst->table_id = src->table_id; + dst->priority = src->priority; + dst->match = src->match; + dst->ofpacts = xmemdup(src->ofpacts, src->ofpacts_len); + dst->ofpacts_len = src->ofpacts_len; + dst->uuid = src->uuid; + dst->match_hmap_node.hash = ovn_flow_match_hash(dst); + dst->uuid_hindex_node.hash = uuid_hash(&src->uuid); + return dst; } /* Returns a hash of the match key in 'f'. */ @@ -645,19 +626,20 @@ ovn_flow_match_hash(const struct ovn_flow *f) /* Compare two flows and return -1, 0, 1 based on whether a if less than, * equal to or greater than b. */ static int -ovn_flow_compare_flows(struct ovn_flow *a, struct ovn_flow *b) { +ovn_flow_compare_flows(struct ovn_flow *a, struct ovn_flow *b) +{ return uuid_compare_3way(&a->uuid, &b->uuid); } /* Given a list of ovn_flows, goes through the list and returns - * a single flow, based on a helper method. */ - + * a single flow, in a deterministic way. */ static struct ovn_flow * -ovn_flow_select_from_list(struct ovs_list *flows) { +ovn_flow_select_from_list(struct ovs_list *flows) +{ struct ovn_flow *candidate; - struct ovn_flow *answer = ovn_flow_from_node(ovs_list_front(flows)); + struct ovn_flow *answer = NULL; LIST_FOR_EACH (candidate, list_node, flows) { - if (ovn_flow_compare_flows(candidate, answer) < 0) { + if (!answer || ovn_flow_compare_flows(candidate, answer) < 0) { answer = candidate; } } @@ -667,7 +649,7 @@ ovn_flow_select_from_list(struct ovs_list *flows) { /* Initializes and files in the supplied list with ovn_flows from 'flow_table' * whose key is identical to 'target''s key. */ static void -ovn_flow_lookup(struct hmap* flow_table, const struct ovn_flow *target, +ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target, struct ovs_list *answer) { struct ovn_flow *f; @@ -710,8 +692,8 @@ ovn_flow_destroy(struct ovn_flow *f) { if (f) { free(f->ofpacts); + free(f); } - free(f); } /* Flow tables of struct ovn_flow. */ @@ -790,11 +772,8 @@ queue_group_mod(struct ofputil_group_mod *gm) } -/* Replaces the flow table on the switch, if possible, by the flows in - * 'flow_table', which should have been added with ofctrl_add_flow(). - * Regardless of whether the flow table is updated, this deletes all of the - * flows from 'flow_table' and frees them. (The hmap itself isn't - * destroyed.) +/* Replaces the flow table on the switch, if possible, by the flows in added + * with ofctrl_add_flow(). * * Replaces the group table on the switch, if possible, by the groups in * 'group_table->desired_groups'. Regardless of whether the group table @@ -879,7 +858,7 @@ ofctrl_put(struct group_table *group_table) struct ovn_flow *d = ovn_flow_select_from_list(&matches); if (!uuid_equals(&i->uuid, &d->uuid)) { /* Update installed flow's UUID. */ - memcpy(&i->uuid, &d->uuid, sizeof i->uuid); + i->uuid = d->uuid; } if (!ofpacts_equal(i->ofpacts, i->ofpacts_len, d->ofpacts, d->ofpacts_len)) { @@ -915,7 +894,7 @@ ofctrl_put(struct group_table *group_table) * that match this key, and select the one to be installed. */ struct ovs_list candidates; ovn_flow_lookup(&match_flow_table, c, &candidates); - struct ovn_flow *d = ovn_flow_select_from_list(&candidates); + struct ovn_flow *d = ovn_flow_select_from_list(&candidates); /* Send flow_mod to add flow. */ struct ofputil_flow_mod fm = { .match = d->match, diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c index 63d6cb8..226ac72 100644 --- a/ovn/controller/physical.c +++ b/ovn/controller/physical.c @@ -51,7 +51,6 @@ physical_register_ovs_idl(struct ovsdb_idl *ovs_idl) ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids); } - static struct simap localvif_to_ofport = SIMAP_INITIALIZER(&localvif_to_ofport); static struct hmap tunnels = HMAP_INITIALIZER(&tunnels); _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev