When packets travel among nodes in OVN over tunnels, a tunnel key value is needed to convey the logical port to which the packet is destined. This commit adds a tunnel_key column to the Bindings table and adds code to ovn-northd to assign a unique tunnel_key value to each logical port.
Signed-off-by: Ben Pfaff <b...@nicira.com> --- ovn/northd/ovn-northd.c | 104 ++++++++++++++++++++++++++++++++++++++---------- ovn/ovn-sb.ovsschema | 6 ++- ovn/ovn-sb.xml | 14 +++++++ 3 files changed, 102 insertions(+), 22 deletions(-) diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 34ac995..0b5becf 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -444,6 +444,45 @@ tags_equal(const struct sbrec_bindings *binding, return binding->n_tag ? (binding->tag[0] == lport->tag[0]) : true; } +struct binding_hash_node { + struct hmap_node lp_node; /* In 'lp_map', by binding->logical_port. */ + struct hmap_node tk_node; /* In 'tk_map', by binding->tunnel_key. */ + const struct sbrec_bindings *binding; +}; + +static bool +tunnel_key_in_use(const struct hmap *tk_hmap, uint16_t tunnel_key) +{ + const struct binding_hash_node *hash_node; + + HMAP_FOR_EACH_IN_BUCKET (hash_node, tk_node, hash_int(tunnel_key, 0), + tk_hmap) { + if (hash_node->binding->tunnel_key == tunnel_key) { + return true; + } + } + return false; +} + +/* Chooses and returns a positive tunnel key that is not already in use in + * 'tk_hmap'. Returns 0 if all tunnel keys are in use. */ +static uint16_t +choose_tunnel_key(const struct hmap *tk_hmap) +{ + static uint16_t prev; + + for (uint16_t key = prev + 1; key != prev; key++) { + if (!tunnel_key_in_use(tk_hmap, key)) { + prev = key; + return key; + } + } + + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "all tunnel keys exhausted"); + return 0; +} + /* * When a change has occurred in the OVN_Northbound database, we go through and * make sure that the contents of the Bindings table in the OVN_Southbound @@ -453,38 +492,41 @@ tags_equal(const struct sbrec_bindings *binding, static void set_bindings(struct northd_context *ctx) { - struct hmap bindings_hmap; const struct sbrec_bindings *binding; const struct nbrec_logical_port *lport; - struct binding_hash_node { - struct hmap_node node; - const struct sbrec_bindings *binding; - } *hash_node, *hash_node_next; - /* * We will need to look up a binding for every logical port. We don't want * to have to do an O(n) search for every binding, so start out by hashing * them on the logical port. * * As we go through every logical port, we will update the binding if it - * exists or create one otherwise. When the update is done, we'll remove it - * from the hashmap. At the end, any bindings left in the hashmap are for - * logical ports that have been deleted. + * exists or create one otherwise. When the update is done, we'll remove + * it from the hashmap. At the end, any bindings left in the hashmap are + * for logical ports that have been deleted. + * + * We index the logical_port column because that's the shared key between + * the OVN_NB and OVN_SB databases. We index the tunnel_key column to + * allow us to choose a unique tunnel key for any Binding rows we have to + * add. */ - hmap_init(&bindings_hmap); + struct hmap lp_hmap = HMAP_INITIALIZER(&lp_hmap); + struct hmap tk_hmap = HMAP_INITIALIZER(&tk_hmap); SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) { - hash_node = xzalloc(sizeof *hash_node); + struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node); hash_node->binding = binding; - hmap_insert(&bindings_hmap, &hash_node->node, - hash_string(binding->logical_port, 0)); + hmap_insert(&lp_hmap, &hash_node->lp_node, + hash_string(binding->logical_port, 0)); + hmap_insert(&tk_hmap, &hash_node->tk_node, + hash_int(binding->tunnel_key, 0)); } NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) { + struct binding_hash_node *hash_node; binding = NULL; - HMAP_FOR_EACH_WITH_HASH(hash_node, node, - hash_string(lport->name, 0), &bindings_hmap) { + HMAP_FOR_EACH_WITH_HASH(hash_node, lp_node, + hash_string(lport->name, 0), &lp_hmap) { if (!strcmp(lport->name, hash_node->binding->logical_port)) { binding = hash_node->binding; break; @@ -502,9 +544,7 @@ set_bindings(struct northd_context *ctx) /* We found an existing binding for this logical port. Update its * contents. */ - hmap_remove(&bindings_hmap, &hash_node->node); - free(hash_node); - hash_node = NULL; + hmap_remove(&lp_hmap, &hash_node->lp_node); if (!macs_equal(binding->mac, binding->n_mac, lport->macs, lport->n_macs)) { @@ -524,6 +564,11 @@ set_bindings(struct northd_context *ctx) } else { /* There is no binding for this logical port, so create one. */ + uint16_t tunnel_key = choose_tunnel_key(&tk_hmap); + if (!tunnel_key) { + continue; + } + binding = sbrec_bindings_insert(ctx->ovnsb_txn); sbrec_bindings_set_logical_port(binding, lport->name); sbrec_bindings_set_mac(binding, @@ -533,16 +578,32 @@ set_bindings(struct northd_context *ctx) sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag); } + sbrec_bindings_set_tunnel_key(binding, tunnel_key); sbrec_bindings_set_logical_datapath(binding, logical_datapath); + + /* Add the tunnel key to the tk_hmap so that we don't try to use it + * for another port. (We don't want it in the lp_hmap because that + * would just get the Bindings record deleted later.) */ + struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node); + hash_node->binding = binding; + hmap_insert(&tk_hmap, &hash_node->tk_node, + hash_int(binding->tunnel_key, 0)); } } - HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &bindings_hmap) { - hmap_remove(&bindings_hmap, &hash_node->node); + struct binding_hash_node *hash_node; + HMAP_FOR_EACH (hash_node, lp_node, &lp_hmap) { + hmap_remove(&lp_hmap, &hash_node->lp_node); sbrec_bindings_delete(hash_node->binding); + } + hmap_destroy(&lp_hmap); + + struct binding_hash_node *hash_node_next; + HMAP_FOR_EACH_SAFE (hash_node, hash_node_next, tk_node, &tk_hmap) { + hmap_remove(&tk_hmap, &hash_node->tk_node); free(hash_node); } - hmap_destroy(&bindings_hmap); + hmap_destroy(&tk_hmap); } static void @@ -733,6 +794,7 @@ main(int argc, char *argv[]) ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tag); ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_parent_port); ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_logical_datapath); + ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tunnel_key); ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_logical_datapath); ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_logical_datapath); ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_table_id); diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema index 5f2d1a4..9fd5363 100644 --- a/ovn/ovn-sb.ovsschema +++ b/ovn/ovn-sb.ovsschema @@ -47,6 +47,10 @@ "columns": { "logical_datapath": {"type": "uuid"}, "logical_port": {"type": "string"}, + "tunnel_key": { + "type": {"key": {"type": "integer", + "minInteger": 1, + "maxInteger": 65535}}}, "parent_port": {"type": {"key": "string", "min": 0, "max": 1}}, "tag": { "type": {"key": {"type": "integer", @@ -57,6 +61,6 @@ "mac": {"type": {"key": "string", "min": 0, "max": "unlimited"}}}, - "indexes": [["logical_port"]], + "indexes": [["logical_port"], ["tunnel_key"]], "isRoot": true}}, "version": "1.0.0"} diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index 1a27617..31ee871 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -665,6 +665,20 @@ prescribe a particular format for the logical port ID. </column> + <column name="tunnel_key"> + <p> + A number that represents the logical port in the key (e.g. VXLAN VNI or + STT key) field carried within tunnel protocol packets. (This avoids + wasting space for a whole UUID in tunneled packets. It also allows OVN + to support encapsulations that cannot fit an entire UUID in their + tunnel keys.) + </p> + + <p> + Tunnel ID 0 is reserved for internal use within OVN. + </p> + </column> + <column name="parent_port"> For containers created inside a VM, this is taken from <ref table="Logical_Port" column="parent_name" db="OVN_Northbound"/> -- 2.1.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev