From patchwork Mon Apr 16 20:14:21 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Han Zhou X-Patchwork-Id: 898920 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Vsy4VwUW"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40Q0BY1f5tz9s19 for ; Tue, 17 Apr 2018 06:20:53 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 176E0FA3; Mon, 16 Apr 2018 20:15:25 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 78C67D4F for ; Mon, 16 Apr 2018 20:15:21 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pl0-f52.google.com (mail-pl0-f52.google.com [209.85.160.52]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 98C78628 for ; Mon, 16 Apr 2018 20:15:14 +0000 (UTC) Received: by mail-pl0-f52.google.com with SMTP id 59-v6so10636303plc.13 for ; Mon, 16 Apr 2018 13:15:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=yb1cQ8odDlHp5sHgO86Z5abAcj4JOHbBUbGytx/IyvA=; b=Vsy4VwUW42auqGBFj4iaIdPLR1rTCZuO0DD1LYFUMS7IV979oBk5RCcd0APfA9a92C c17BOGkorgjPxbE8XDYbADwacbwDBpTUkNNxHX7wq5es1gVaEXOZtrvABaw024M2zkX2 J5dAfMBnFFby4vx+c0dhNACjYtJaBFDbaRlg6RDQAQmpawmheYNX88t0MA3xm/NIo0ay IfvrlpuQS4oFdjmE+94N+CKtx6fPwm3TB3NbYZRks6STXmWcpMLRwQbIj37N3xcq2rEh aLHKft4uNnjCuCggZSG0BnDsOJ0vGQmVHNDhwr3yxiYUWts5iEMGZpXGp9gXPxzflxAa dTCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=yb1cQ8odDlHp5sHgO86Z5abAcj4JOHbBUbGytx/IyvA=; b=Dptbil2UDXVRkUIf/1ms+5bq+OuVRZQAt5SegBZdHVwjALpyytigWLD6a3jDkWgXj4 zUqx2ocmN43ORcjTypJWI5xCHpz7MNv9DIUxGUXkf/xXic0ZJ9X3Qo1+g1ZDOJ6QExdN OjUnEc3ISkU9lFkLNM2kUw3c9mrpmx82UHnLYDK/j9l4tfbNRWkPOnMFxrKzlr4Dhd4z 6iQWPbV+PRb1SSTq6yrtiyI4w1tISP3PvohDq8hArNxEXjBUWYs/gxirXWrl2S6ByAvV NI1GnRtZj6D+yqEHcJm0sr/C2wrefe4r2LLahFI/dqUnEySAnBfgbwL2UJr7WJC3CRHb sp5A== X-Gm-Message-State: ALQs6tB7VOQhWwCwHCxpFa+lhdvqLQqXtvPzgNqnugsK7IbBfKCjNlcR R9/gBP2AyiiPY5qYtSRGrHt0rQ== X-Google-Smtp-Source: AIpwx48cPEeh42vNSGj6E5YmLTWf6mgeq8UpZqGYyCW+wLKEgVRpvQ9UkEljh9SqkExa+p6e1cf8pw== X-Received: by 2002:a17:902:4001:: with SMTP id b1-v6mr16312133pld.273.1523909712715; Mon, 16 Apr 2018 13:15:12 -0700 (PDT) Received: from localhost.localdomain.localdomain ([216.113.160.71]) by smtp.gmail.com with ESMTPSA id m7sm2306746pga.46.2018.04.16.13.15.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 16 Apr 2018 13:15:12 -0700 (PDT) From: Han Zhou X-Google-Original-From: Han Zhou To: dev@openvswitch.org Date: Mon, 16 Apr 2018 13:14:21 -0700 Message-Id: <1523909665-27961-7-git-send-email-hzhou8@ebay.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1523909665-27961-1-git-send-email-hzhou8@ebay.com> References: <1523909665-27961-1-git-send-email-hzhou8@ebay.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [RFC v3 06/10] ovn-controller: Incremental logical flow processing X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Persistents flow-table and implements change handler of flow_output for SB lflow changes. Signed-off-by: Han Zhou --- include/ovn/actions.h | 3 + ovn/controller/lflow.c | 178 +++++++++++++++++++++++------ ovn/controller/lflow.h | 20 +++- ovn/controller/ofctrl.c | 241 ++++++++++++++++++++++++++++------------ ovn/controller/ofctrl.h | 29 ++++- ovn/controller/ovn-controller.c | 116 +++++++++++++------ ovn/controller/physical.c | 86 +++++++------- ovn/controller/physical.h | 5 +- ovn/lib/actions.c | 6 +- ovn/lib/extend-table.c | 60 +++++++--- ovn/lib/extend-table.h | 16 ++- 11 files changed, 550 insertions(+), 210 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index fb8f515..b6014a2 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -513,6 +513,9 @@ struct ovnact_encode_params { /* A struct to figure out the meter_id for meter actions. */ struct ovn_extend_table *meter_table; + /* The logical flow uuid that drove this action. */ + struct uuid lflow_uuid; + /* OVN maps each logical flow table (ltable), one-to-one, onto a physical * OpenFlow flow table (ptable). A number of parameters describe this * mapping and data related to flow tables: diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c index f022aa3..d75e03f 100644 --- a/ovn/controller/lflow.c +++ b/ovn/controller/lflow.c @@ -58,7 +58,8 @@ struct condition_aux { const struct chassis_index *chassis_index; }; -static void consider_logical_flow(struct controller_ctx *ctx, +static bool consider_logical_flow(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx, const struct chassis_index *chassis_index, const struct sbrec_logical_flow *lflow, const struct hmap *local_datapaths, @@ -71,7 +72,6 @@ static void consider_logical_flow(struct controller_ctx *ctx, uint32_t *conj_id_ofs, const struct shash *addr_sets, const struct shash *port_groups, - struct hmap *flow_table, struct sset *active_tunnels, struct sset *local_lport_ids); @@ -134,7 +134,8 @@ is_switch(const struct sbrec_datapath_binding *ldp) /* Adds the logical flows from the Logical_Flow table to flow tables. */ static void -add_logical_flows(struct controller_ctx *ctx, +add_logical_flows(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx, const struct chassis_index *chassis_index, const struct hmap *local_datapaths, struct ovn_extend_table *group_table, @@ -142,11 +143,10 @@ add_logical_flows(struct controller_ctx *ctx, const struct sbrec_chassis *chassis, const struct shash *addr_sets, const struct shash *port_groups, - struct hmap *flow_table, struct sset *active_tunnels, - struct sset *local_lport_ids) + struct sset *local_lport_ids, + uint32_t *conj_id_ofs) { - uint32_t conj_id_ofs = 1; const struct sbrec_logical_flow *lflow; struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts); @@ -168,12 +168,16 @@ add_logical_flows(struct controller_ctx *ctx, nd_ra_opts_init(&nd_ra_opts); SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) { - consider_logical_flow(ctx, chassis_index, - lflow, local_datapaths, - group_table, meter_table, chassis, - &dhcp_opts, &dhcpv6_opts, &nd_ra_opts, - &conj_id_ofs, addr_sets, port_groups, - flow_table, active_tunnels, local_lport_ids); + if (!consider_logical_flow(flow_table, ctx, chassis_index, + lflow, local_datapaths, + group_table, meter_table, chassis, + &dhcp_opts, &dhcpv6_opts, &nd_ra_opts, + conj_id_ofs, addr_sets, port_groups, + active_tunnels, local_lport_ids)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); + VLOG_ERR_RL(&rl, "Conjunction id overflow when processing lflow " + UUID_FMT, UUID_ARGS(&lflow->header_.uuid)); + } } dhcp_opts_destroy(&dhcp_opts); @@ -181,8 +185,95 @@ add_logical_flows(struct controller_ctx *ctx, nd_ra_opts_destroy(&nd_ra_opts); } -static void -consider_logical_flow(struct controller_ctx *ctx, +bool +lflow_handle_changed_flows(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx, + const struct sbrec_chassis *chassis, + const struct chassis_index *chassis_index, + const struct hmap *local_datapaths, + struct ovn_extend_table *group_table, + struct ovn_extend_table *meter_table, + const struct shash *addr_sets, + const struct shash *port_groups, + struct sset *active_tunnels, + struct sset *local_lport_ids, + uint32_t *conj_id_ofs) +{ + bool ret = true; + const struct sbrec_logical_flow *lflow; + + struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts); + struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts); + const struct sbrec_dhcp_options *dhcp_opt_row; + SBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opt_row, ctx->ovnsb_idl) { + dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code, + dhcp_opt_row->type); + } + + + const struct sbrec_dhcpv6_options *dhcpv6_opt_row; + SBREC_DHCPV6_OPTIONS_FOR_EACH(dhcpv6_opt_row, ctx->ovnsb_idl) { + dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code, + dhcpv6_opt_row->type); + } + + struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts); + nd_ra_opts_init(&nd_ra_opts); + + /* Handle removed flows first, and then other flows, so that when + * the flows being added and removed have same match conditions + * can be processed in the proper order */ + SBREC_LOGICAL_FLOW_FOR_EACH_TRACKED (lflow, ctx->ovnsb_idl) { + /* Remove any flows that should be removed. */ + if (sbrec_logical_flow_is_deleted(lflow)) { + VLOG_DBG("handle deleted lflow "UUID_FMT, + UUID_ARGS(&lflow->header_.uuid)); + ofctrl_remove_flows(flow_table, &lflow->header_.uuid); + } + } + SBREC_LOGICAL_FLOW_FOR_EACH_TRACKED (lflow, ctx->ovnsb_idl) { + if (!sbrec_logical_flow_is_deleted(lflow)) { + /* Now, add/modify existing flows. If the logical + * flow is a modification, just remove the flows + * for this row, and then add new flows. */ + if (!sbrec_logical_flow_is_new(lflow)) { + VLOG_DBG("handle updated lflow "UUID_FMT, + UUID_ARGS(&lflow->header_.uuid)); + ofctrl_remove_flows(flow_table, &lflow->header_.uuid); + } + VLOG_DBG("handle new lflow "UUID_FMT, + UUID_ARGS(&lflow->header_.uuid)); + if (!consider_logical_flow(flow_table, ctx, chassis_index, + lflow, local_datapaths, + group_table, meter_table, chassis, + &dhcp_opts, &dhcpv6_opts, &nd_ra_opts, + conj_id_ofs, addr_sets, port_groups, + active_tunnels, local_lport_ids)) { + ret = false; + break; + } + } + } + dhcp_opts_destroy(&dhcp_opts); + dhcp_opts_destroy(&dhcpv6_opts); + nd_ra_opts_destroy(&nd_ra_opts); + return ret; +} + +static bool +update_conj_id_ofs(uint32_t *conj_id_ofs, uint32_t n_conjs) +{ + if (*conj_id_ofs + n_conjs < *conj_id_ofs) { + /* overflow */ + return false; + } + *conj_id_ofs += n_conjs; + return true; +} + +static bool +consider_logical_flow(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx, const struct chassis_index *chassis_index, const struct sbrec_logical_flow *lflow, const struct hmap *local_datapaths, @@ -195,7 +286,6 @@ consider_logical_flow(struct controller_ctx *ctx, uint32_t *conj_id_ofs, const struct shash *addr_sets, const struct shash *port_groups, - struct hmap *flow_table, struct sset *active_tunnels, struct sset *local_lport_ids) { @@ -204,10 +294,14 @@ consider_logical_flow(struct controller_ctx *ctx, const struct sbrec_datapath_binding *ldp = lflow->logical_datapath; if (!ldp) { - return; + VLOG_DBG("lflow "UUID_FMT" has no datapath binding, skip", + UUID_ARGS(&lflow->header_.uuid)); + return true; } if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) { - return; + VLOG_DBG("lflow "UUID_FMT" is not for local datapath, skip", + UUID_ARGS(&lflow->header_.uuid)); + return true; } /* Determine translation of logical table IDs to physical table IDs. */ @@ -245,7 +339,7 @@ consider_logical_flow(struct controller_ctx *ctx, free(error); ovnacts_free(ovnacts.data, ovnacts.size); ofpbuf_uninit(&ovnacts); - return; + return true; } /* Translate OVN match into table of OpenFlow matches. */ @@ -269,7 +363,7 @@ consider_logical_flow(struct controller_ctx *ctx, free(error); ovnacts_free(ovnacts.data, ovnacts.size); ofpbuf_uninit(&ovnacts); - return; + return true; } struct lookup_port_aux aux = { @@ -285,10 +379,12 @@ consider_logical_flow(struct controller_ctx *ctx, expr_destroy(expr); if (hmap_is_empty(&matches)) { + VLOG_DBG("lflow "UUID_FMT" matches are empty, skip", + UUID_ARGS(&lflow->header_.uuid)); ovnacts_free(ovnacts.data, ovnacts.size); ofpbuf_uninit(&ovnacts); expr_matches_destroy(&matches); - return; + return true; } /* Encode OVN logical actions into OpenFlow. */ @@ -300,6 +396,7 @@ consider_logical_flow(struct controller_ctx *ctx, .is_switch = is_switch(ldp), .group_table = group_table, .meter_table = meter_table, + .lflow_uuid = lflow->header_.uuid, .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS, .ingress_ptable = OFTABLE_LOG_INGRESS_PIPELINE, @@ -328,13 +425,18 @@ consider_logical_flow(struct controller_ctx *ctx, char buf[16]; snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64, dp_id, port_id); if (!sset_contains(local_lport_ids, buf)) { + VLOG_DBG("lflow "UUID_FMT + " port %s in match is not local, skip", + UUID_ARGS(&lflow->header_.uuid), + buf); continue; } } } if (!m->n) { ofctrl_add_flow(flow_table, ptable, lflow->priority, - lflow->header_.uuid.parts[0], &m->match, &ofpacts); + lflow->header_.uuid.parts[0], &m->match, &ofpacts, + &lflow->header_.uuid); } else { uint64_t conj_stubs[64 / 8]; struct ofpbuf conj; @@ -350,7 +452,7 @@ consider_logical_flow(struct controller_ctx *ctx, dst->n_clauses = src->n_clauses; } ofctrl_add_flow(flow_table, ptable, lflow->priority, 0, &m->match, - &conj); + &conj, &lflow->header_.uuid); ofpbuf_uninit(&conj); } } @@ -358,7 +460,7 @@ consider_logical_flow(struct controller_ctx *ctx, /* Clean up. */ expr_matches_destroy(&matches); ofpbuf_uninit(&ofpacts); - *conj_id_ofs += n_conjs; + return update_conj_id_ofs(conj_id_ofs, n_conjs); } static void @@ -374,9 +476,9 @@ put_load(const uint8_t *data, size_t len, } static void -consider_neighbor_flow(struct controller_ctx *ctx, - const struct sbrec_mac_binding *b, - struct hmap *flow_table) +consider_neighbor_flow(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx, + const struct sbrec_mac_binding *b) { const struct sbrec_port_binding *pb = lport_lookup_by_name(ctx->ovnsb_idl, b->logical_port); @@ -418,26 +520,28 @@ consider_neighbor_flow(struct controller_ctx *ctx, uint64_t stub[1024 / 8]; struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub); put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts); - ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts); + ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts, + &b->header_.uuid); ofpbuf_uninit(&ofpacts); } /* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN * southbound database. */ static void -add_neighbor_flows(struct controller_ctx *ctx, - struct hmap *flow_table) +add_neighbor_flows(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx) { const struct sbrec_mac_binding *b; SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) { - consider_neighbor_flow(ctx, b, flow_table); + consider_neighbor_flow(flow_table, ctx, b); } } /* Translates logical flows in the Logical_Flow table in the OVN_SB database * into OpenFlow flows. See ovn-architecture(7) for more information. */ void -lflow_run(struct controller_ctx *ctx, +lflow_run(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx, const struct sbrec_chassis *chassis, const struct chassis_index *chassis_index, const struct hmap *local_datapaths, @@ -445,15 +549,15 @@ lflow_run(struct controller_ctx *ctx, struct ovn_extend_table *meter_table, const struct shash *addr_sets, const struct shash *port_groups, - struct hmap *flow_table, struct sset *active_tunnels, - struct sset *local_lport_ids) + struct sset *local_lport_ids, + uint32_t *conj_id_ofs) { - add_logical_flows(ctx, chassis_index, local_datapaths, + add_logical_flows(flow_table, ctx, chassis_index, local_datapaths, group_table, meter_table, chassis, addr_sets, - port_groups, flow_table, active_tunnels, - local_lport_ids); - add_neighbor_flows(ctx, flow_table); + port_groups, active_tunnels, local_lport_ids, + conj_id_ofs); + add_neighbor_flows(flow_table, ctx); } void diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h index dcf2fe7..4e96d50 100644 --- a/ovn/controller/lflow.h +++ b/ovn/controller/lflow.h @@ -38,6 +38,7 @@ struct chassis_index; struct controller_ctx; struct ovn_extend_table; +struct ovn_desired_flow_table; struct hmap; struct sbrec_chassis; struct simap; @@ -62,7 +63,8 @@ struct uuid; #define LOG_PIPELINE_LEN 24 void lflow_init(void); -void lflow_run(struct controller_ctx *, +void lflow_run(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *, const struct sbrec_chassis *chassis, const struct chassis_index *, const struct hmap *local_datapaths, @@ -70,9 +72,21 @@ void lflow_run(struct controller_ctx *, struct ovn_extend_table *meter_table, const struct shash *addr_sets, const struct shash *port_groups, - struct hmap *flow_table, struct sset *active_tunnels, - struct sset *local_lport_ids); + struct sset *local_lport_ids, + uint32_t *conj_id_ofs); +bool lflow_handle_changed_flows(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx, + const struct sbrec_chassis *chassis, + const struct chassis_index *chassis_index, + const struct hmap *local_datapaths, + struct ovn_extend_table *group_table, + struct ovn_extend_table *extend_table, + const struct shash *addr_sets, + const struct shash *port_groups, + struct sset *active_tunnels, + struct sset *local_lport_ids, + uint32_t *conj_id_ofs); void lflow_destroy(void); #endif /* ovn/lflow.h */ diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c index 134f0e5..f251cd6 100644 --- a/ovn/controller/ofctrl.c +++ b/ovn/controller/ofctrl.c @@ -20,6 +20,7 @@ #include "dp-packet.h" #include "flow.h" #include "hash.h" +#include "hindex.h" #include "lflow.h" #include "ofctrl.h" #include "openflow/openflow.h" @@ -52,7 +53,8 @@ VLOG_DEFINE_THIS_MODULE(ofctrl); /* An OpenFlow flow. */ struct ovn_flow { - struct hmap_node hmap_node; /* For match based hashing. */ + struct hmap_node match_hmap_node; /* For match based hashing. */ + struct hindex_node uuid_hindex_node; /* For uuid based hashing. */ struct ovs_list list_node; /* For handling lists of flows. */ /* Key. */ @@ -61,14 +63,16 @@ struct ovn_flow { struct minimatch match; /* Data. */ + struct uuid sb_uuid; struct ofpact *ofpacts; size_t ofpacts_len; uint64_t cookie; }; -static uint32_t ovn_flow_hash(const struct ovn_flow *); +static uint32_t ovn_flow_match_hash(const struct ovn_flow *); static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table, - const struct ovn_flow *target); + const struct ovn_flow *target, + bool cmp_sb_uuid); static char *ovn_flow_to_string(const struct ovn_flow *); static void ovn_flow_log(const struct ovn_flow *, const char *action); static void ovn_flow_destroy(struct ovn_flow *); @@ -146,6 +150,10 @@ static struct ovn_extend_table *meters; * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */ static enum mf_field_id mff_ovn_geneve; +/* indicates if flows need to be reinstalled for scenarios when ovs + * is restarted, even if there is no change in the desired flow table */ +static bool need_reinstall_flows; + static ovs_be32 queue_msg(struct ofpbuf *); static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *); @@ -154,8 +162,8 @@ static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *); static struct ofpbuf *encode_meter_mod(const struct ofputil_meter_mod *); -static void ovn_flow_table_clear(struct hmap *flow_table); -static void ovn_flow_table_destroy(struct hmap *flow_table); +static void ovn_installed_flow_table_clear(void); +static void ovn_installed_flow_table_destroy(void); static void ofctrl_recv(const struct ofp_header *, enum ofptype); @@ -375,6 +383,7 @@ run_S_CLEAR_FLOWS(void) { VLOG_DBG("clearing all flows"); + need_reinstall_flows = true; /* Send a flow_mod to delete all flows. */ struct ofputil_flow_mod fm = { .table_id = OFPTT_ALL, @@ -384,9 +393,6 @@ run_S_CLEAR_FLOWS(void) queue_msg(encode_flow_mod(&fm)); minimatch_destroy(&fm.match); - /* Clear installed_flows, to match the state of the switch. */ - ovn_flow_table_clear(&installed_flows); - /* Send a group_mod to delete all groups. */ struct ofputil_group_mod gm; memset(&gm, 0, sizeof gm); @@ -397,6 +403,9 @@ run_S_CLEAR_FLOWS(void) queue_msg(encode_group_mod(&gm)); ofputil_uninit_group_mod(&gm); + /* Clear installed_flows, to match the state of the switch. */ + ovn_installed_flow_table_clear(); + /* Clear existing groups, to match the state of the switch. */ if (groups) { ovn_extend_table_clear(groups, true); @@ -580,7 +589,7 @@ void ofctrl_destroy(void) { rconn_destroy(swconn); - ovn_flow_table_destroy(&installed_flows); + ovn_installed_flow_table_destroy(); rconn_packet_counter_destroy(tx_counter); expr_symtab_destroy(&symtab); shash_destroy(&symtab); @@ -632,14 +641,17 @@ ofctrl_recv(const struct ofp_header *oh, enum ofptype type) * the OpenFlow table numbered 'table_id' with the given 'priority' and * OpenFlow 'cookie'. The caller retains ownership of 'match' and 'actions'. * + * The flow is also added to a hash index based on sb_uuid. + * * This just assembles the desired flow table in memory. Nothing is actually * sent to the switch until a later call to ofctrl_put(). * * The caller should initialize its own hmap to hold the flows. */ void -ofctrl_add_flow(struct hmap *desired_flows, +ofctrl_add_flow(struct ovn_desired_flow_table *flow_table, uint8_t table_id, uint16_t priority, uint64_t cookie, - const struct match *match, const struct ofpbuf *actions) + const struct match *match, const struct ofpbuf *actions, + const struct uuid *sb_uuid) { struct ovn_flow *f = xmalloc(sizeof *f); f->table_id = table_id; @@ -647,10 +659,14 @@ ofctrl_add_flow(struct hmap *desired_flows, minimatch_init(&f->match, match); f->ofpacts = xmemdup(actions->data, actions->size); f->ofpacts_len = actions->size; - f->hmap_node.hash = ovn_flow_hash(f); + f->sb_uuid = *sb_uuid; + f->match_hmap_node.hash = ovn_flow_match_hash(f); + f->uuid_hindex_node.hash = uuid_hash(&f->sb_uuid); f->cookie = cookie; - if (ovn_flow_lookup(desired_flows, f)) { + ovn_flow_log(f, "ofctrl_add_flow"); + + if (ovn_flow_lookup(&flow_table->match_flow_table, f, true)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); if (!VLOG_DROP_DBG(&rl)) { char *s = ovn_flow_to_string(f); @@ -662,19 +678,44 @@ ofctrl_add_flow(struct hmap *desired_flows, return; } - hmap_insert(desired_flows, &f->hmap_node, f->hmap_node.hash); + hmap_insert(&flow_table->match_flow_table, &f->match_hmap_node, + f->match_hmap_node.hash); + hindex_insert(&flow_table->uuid_flow_table, &f->uuid_hindex_node, + f->uuid_hindex_node.hash); +} + +/* Removes a bundles of flows from the flow table. */ +void +ofctrl_remove_flows(struct ovn_desired_flow_table *flow_table, + const struct uuid *sb_uuid) +{ + struct ovn_flow *f, *next; + HINDEX_FOR_EACH_WITH_HASH_SAFE (f, next, uuid_hindex_node, + uuid_hash(sb_uuid), + &flow_table->uuid_flow_table) { + if (uuid_equals(&f->sb_uuid, sb_uuid)) { + ovn_flow_log(f, "ofctrl_remove_flow"); + hmap_remove(&flow_table->match_flow_table, + &f->match_hmap_node); + hindex_remove(&flow_table->uuid_flow_table, &f->uuid_hindex_node); + ovn_flow_destroy(f); + } + } + + /* remove any related group and meter info */ + ovn_extend_table_remove_desired(groups, sb_uuid); + ovn_extend_table_remove_desired(meters, sb_uuid); } /* ovn_flow. */ -/* Returns a hash of the key in 'f'. */ +/* Returns a hash of the match key in 'f'. */ static uint32_t -ovn_flow_hash(const struct ovn_flow *f) +ovn_flow_match_hash(const struct ovn_flow *f) { return hash_2words((f->table_id << 16) | f->priority, minimatch_hash(&f->match, 0)); - } /* Duplicate an ovn_flow structure. */ @@ -687,23 +728,28 @@ ofctrl_dup_flow(struct ovn_flow *src) minimatch_clone(&dst->match, &src->match); dst->ofpacts = xmemdup(src->ofpacts, src->ofpacts_len); dst->ofpacts_len = src->ofpacts_len; - dst->hmap_node.hash = ovn_flow_hash(dst); + dst->sb_uuid = src->sb_uuid; + dst->match_hmap_node.hash = ovn_flow_match_hash(dst); + dst->uuid_hindex_node.hash = uuid_hash(&src->sb_uuid); return dst; } /* Finds and returns an ovn_flow in 'flow_table' whose key is identical to * 'target''s key, or NULL if there is none. */ static struct ovn_flow * -ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target) +ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target, + bool cmp_sb_uuid) { struct ovn_flow *f; - HMAP_FOR_EACH_WITH_HASH (f, hmap_node, target->hmap_node.hash, + HMAP_FOR_EACH_WITH_HASH (f, match_hmap_node, target->match_hmap_node.hash, flow_table) { if (f->table_id == target->table_id && f->priority == target->priority && minimatch_equal(&f->match, &target->match)) { - return f; + if (!cmp_sb_uuid || uuid_equals(&target->sb_uuid, &f->sb_uuid)) { + return f; + } } } return NULL; @@ -713,6 +759,7 @@ static char * ovn_flow_to_string(const struct ovn_flow *f) { struct ds s = DS_EMPTY_INITIALIZER; + ds_put_format(&s, "sb_uuid="UUID_FMT", ", UUID_ARGS(&f->sb_uuid)); ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id); ds_put_format(&s, "priority=%"PRIu16", ", f->priority); minimatch_format(&f->match, NULL, NULL, &s, OFP_DEFAULT_PRIORITY); @@ -743,22 +790,48 @@ ovn_flow_destroy(struct ovn_flow *f) } /* Flow tables of struct ovn_flow. */ +void +ovn_desired_flow_table_init(struct ovn_desired_flow_table *flow_table) +{ + hmap_init(&flow_table->match_flow_table); + hindex_init(&flow_table->uuid_flow_table); +} + +void +ovn_desired_flow_table_clear(struct ovn_desired_flow_table *flow_table) +{ + struct ovn_flow *f, *next; + HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, + &flow_table->match_flow_table) { + hmap_remove(&flow_table->match_flow_table, &f->match_hmap_node); + hindex_remove(&flow_table->uuid_flow_table, &f->uuid_hindex_node); + ovn_flow_destroy(f); + } +} + +void +ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *flow_table) +{ + ovn_desired_flow_table_clear(flow_table); + hmap_destroy(&flow_table->match_flow_table); + hindex_destroy(&flow_table->uuid_flow_table); +} static void -ovn_flow_table_clear(struct hmap *flow_table) +ovn_installed_flow_table_clear(void) { struct ovn_flow *f, *next; - HMAP_FOR_EACH_SAFE (f, next, hmap_node, flow_table) { - hmap_remove(flow_table, &f->hmap_node); + HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, &installed_flows) { + hmap_remove(&installed_flows, &f->match_hmap_node); ovn_flow_destroy(f); } } static void -ovn_flow_table_destroy(struct hmap *flow_table) +ovn_installed_flow_table_destroy(void) { - ovn_flow_table_clear(flow_table); - hmap_destroy(flow_table); + ovn_installed_flow_table_clear(); + hmap_destroy(&installed_flows); } /* Flow table update. */ @@ -823,7 +896,7 @@ add_ct_flush_zone(uint16_t zone_id, struct ovs_list *msgs) * in the correct state and not backlogged with existing flow_mods. (Our * criteria for being backlogged appear very conservative, but the socket * between ovn-controller and OVS provides some buffering.) */ -bool +static bool ofctrl_can_put(void) { if (state != S_UPDATE_FLOWS @@ -838,9 +911,7 @@ ofctrl_can_put(void) * with ofctrl_add_flow(). * * Replaces the group table and meter table on the switch, if possible, by the - * contents of 'groups->desired'. Regardless of whether the group table - * is updated, this deletes all the groups from the 'groups->desired' and frees - * them. (The hmap itself isn't destroyed.) + * contents of 'desired'. * * Sends conntrack flush messages to each zone in 'pending_ct_zones' that * is in the CT_ZONE_OF_QUEUED state and then moves the zone into the @@ -848,16 +919,42 @@ ofctrl_can_put(void) * * This should be called after ofctrl_run() within the main loop. */ void -ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones, - int64_t nb_cfg) +ofctrl_put(struct ovn_desired_flow_table *flow_table, + struct shash *pending_ct_zones, + int64_t nb_cfg, + bool flow_changed) { + static bool skipped_last_time = false; + static int64_t old_nb_cfg = 0; + bool need_put = false; + if (flow_changed || skipped_last_time || need_reinstall_flows) { + need_put = true; + } else if (nb_cfg != old_nb_cfg) { + /* nb_cfg changed since last ofctrl_put() call */ + if (cur_cfg == old_nb_cfg) { + /* we were up-to-date already, so just update with the + * new nb_cfg */ + cur_cfg = nb_cfg; + } else { + need_put = true; + } + } + + old_nb_cfg = nb_cfg; + + if (!need_put) { + VLOG_DBG("ofctrl_put not needed"); + return; + } if (!ofctrl_can_put()) { - ovn_flow_table_clear(flow_table); - ovn_extend_table_clear(groups, false); - ovn_extend_table_clear(meters, false); + VLOG_DBG("ofctrl_put can't be performed"); + skipped_last_time = true; return; } + skipped_last_time = false; + need_reinstall_flows = false; + /* OpenFlow messages to send to the switch to bring it up-to-date. */ struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs); @@ -921,8 +1018,9 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones, * longer desired, delete them; if any of them should have different * actions, update them. */ struct ovn_flow *i, *next; - HMAP_FOR_EACH_SAFE (i, next, hmap_node, &installed_flows) { - struct ovn_flow *d = ovn_flow_lookup(flow_table, i); + HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) { + struct ovn_flow *d = ovn_flow_lookup(&flow_table->match_flow_table, + i, false); if (!d) { /* Installed flow is no longer desirable. Delete it from the * switch and from installed_flows. */ @@ -935,9 +1033,13 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones, add_flow_mod(&fm, &msgs); ovn_flow_log(i, "removing installed"); - hmap_remove(&installed_flows, &i->hmap_node); + hmap_remove(&installed_flows, &i->match_hmap_node); ovn_flow_destroy(i); } else { + if (!uuid_equals(&i->sb_uuid, &d->sb_uuid)) { + /* Update installed flow's UUID. */ + i->sb_uuid = d->sb_uuid; + } if (!ofpacts_equal(i->ofpacts, i->ofpacts_len, d->ofpacts, d->ofpacts_len)) { /* Update actions in installed flow. */ @@ -954,38 +1056,37 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones, /* Replace 'i''s actions by 'd''s. */ free(i->ofpacts); - i->ofpacts = d->ofpacts; + i->ofpacts = xmemdup(d->ofpacts, d->ofpacts_len); i->ofpacts_len = d->ofpacts_len; - d->ofpacts = NULL; - d->ofpacts_len = 0; } - hmap_remove(flow_table, &d->hmap_node); - ovn_flow_destroy(d); } } - /* The previous loop removed from 'flow_table' all of the flows that are - * already installed. Thus, any flows remaining in 'flow_table' need to - * be added to the flow table. */ + /* Iterate through the desired flows and add those that aren't found + * in the installed flow table. */ struct ovn_flow *d; - HMAP_FOR_EACH_SAFE (d, next, hmap_node, flow_table) { - /* Send flow_mod to add flow. */ - struct ofputil_flow_mod fm = { - .match = d->match, - .priority = d->priority, - .table_id = d->table_id, - .ofpacts = d->ofpacts, - .ofpacts_len = d->ofpacts_len, - .new_cookie = htonll(d->cookie), - .command = OFPFC_ADD, - }; - add_flow_mod(&fm, &msgs); - ovn_flow_log(d, "adding installed"); - - /* Move 'd' from 'flow_table' to installed_flows. */ - hmap_remove(flow_table, &d->hmap_node); - hmap_insert(&installed_flows, &d->hmap_node, d->hmap_node.hash); + HMAP_FOR_EACH (d, match_hmap_node, &flow_table->match_flow_table) { + i = ovn_flow_lookup(&installed_flows, d, false); + if (!i) { + /* Send flow_mod to add flow. */ + struct ofputil_flow_mod fm = { + .match = d->match, + .priority = d->priority, + .table_id = d->table_id, + .ofpacts = d->ofpacts, + .ofpacts_len = d->ofpacts_len, + .new_cookie = htonll(d->cookie), + .command = OFPFC_ADD, + }; + add_flow_mod(&fm, &msgs); + ovn_flow_log(d, "adding installed"); + + /* Copy 'd' from 'flow_table' to installed_flows. */ + struct ovn_flow *new_node = ofctrl_dup_flow(d); + hmap_insert(&installed_flows, &new_node->match_hmap_node, + new_node->match_hmap_node.hash); + } } /* Iterate through the installed groups from previous runs. If they @@ -1010,11 +1111,11 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones, } free(group_string); ofputil_uninit_group_mod(&gm); - ovn_extend_table_remove(groups, installed); + ovn_extend_table_remove_existing(groups, installed); } - /* Move the contents of groups->desired to groups->existing. */ - ovn_extend_table_move(groups); + /* Sync the contents of groups->desired to groups->existing. */ + ovn_extend_table_sync(groups); /* Iterate through the installed meters from previous runs. If they * are not needed delete them. */ @@ -1037,11 +1138,11 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones, free(error); } free(meter_string); - ovn_extend_table_remove(meters, m_installed); + ovn_extend_table_remove_existing(meters, m_installed); } - /* Move the contents of meters->desired to meters->existing. */ - ovn_extend_table_move(meters); + /* Sync the contents of meters->desired to meters->existing. */ + ovn_extend_table_sync(meters); if (!ovs_list_is_empty(&msgs)) { /* Add a barrier to the list of messages. */ diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h index 45081e5..f05f8e6 100644 --- a/ovn/controller/ofctrl.h +++ b/ovn/controller/ofctrl.h @@ -21,6 +21,7 @@ #include "openvswitch/meta-flow.h" #include "ovsdb-idl.h" +#include "hindex.h" struct controller_ctx; struct ovn_extend_table; @@ -30,15 +31,24 @@ struct ofpbuf; struct ovsrec_bridge; struct shash; +struct ovn_desired_flow_table { + /* hash map flow table using flow match conditions as hash key */ + struct hmap match_flow_table; + + /* SB uuid index for the nodes in match_flow_table */ + struct hindex uuid_flow_table; +}; + /* Interface for OVN main loop. */ void ofctrl_init(struct ovn_extend_table *group_table, struct ovn_extend_table *meter_table); void ofctrl_run(const struct ovsrec_bridge *br_int, struct shash *pending_ct_zones); enum mf_field_id ofctrl_get_mf_field_id(void); -bool ofctrl_can_put(void); -void ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones, - int64_t nb_cfg); +void ofctrl_put(struct ovn_desired_flow_table *flow_table, + struct shash *pending_ct_zones, + int64_t nb_cfg, + bool flow_changed); void ofctrl_wait(void); void ofctrl_destroy(void); int64_t ofctrl_get_cur_cfg(void); @@ -52,8 +62,17 @@ char *ofctrl_inject_pkt(const struct ovsrec_bridge *br_int, const struct shash *port_groups); /* Flow table interfaces to the rest of ovn-controller. */ -void ofctrl_add_flow(struct hmap *desired_flows, uint8_t table_id, +void ofctrl_add_flow(struct ovn_desired_flow_table *flow_table, + uint8_t table_id, uint16_t priority, uint64_t cookie, - const struct match *, const struct ofpbuf *ofpacts); + const struct match *, const struct ofpbuf *ofpacts, + const struct uuid *lflow_uuid); + +void ofctrl_remove_flows(struct ovn_desired_flow_table *flow_table, + const struct uuid *lflow_uuid); + +void ovn_desired_flow_table_init(struct ovn_desired_flow_table *flow_table); +void ovn_desired_flow_table_clear(struct ovn_desired_flow_table *flow_table); +void ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *flow_table); #endif /* ovn/ofctrl.h */ diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index 1f8ae05..240a2da 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -747,11 +747,13 @@ runtime_data_run(struct engine_node *node) struct ed_type_flow_output { /* desired flows */ - struct hmap flow_table; + struct ovn_desired_flow_table flow_table; /* group ids for load balancing */ struct ovn_extend_table group_table; /* meter ids for QoS */ struct ovn_extend_table meter_table; + /* conjunction id offset */ + uint32_t conj_id_ofs; }; static void @@ -759,9 +761,10 @@ flow_output_init(struct engine_node *node) { struct ed_type_flow_output *data = (struct ed_type_flow_output *)node->data; - hmap_init(&data->flow_table); + ovn_desired_flow_table_init(&data->flow_table); ovn_extend_table_init(&data->group_table); ovn_extend_table_init(&data->meter_table); + data->conj_id_ofs = 1; } static void @@ -769,7 +772,7 @@ flow_output_cleanup(struct engine_node *node) { struct ed_type_flow_output *data = (struct ed_type_flow_output *)node->data; - hmap_destroy(&data->flow_table); + ovn_desired_flow_table_destroy(&data->flow_table); ovn_extend_table_destroy(&data->group_table); ovn_extend_table_destroy(&data->meter_table); } @@ -803,29 +806,75 @@ flow_output_run(struct engine_node *node) struct ed_type_flow_output *fod = (struct ed_type_flow_output *)node->data; - struct hmap *flow_table = &fod->flow_table; + struct ovn_desired_flow_table *flow_table = &fod->flow_table; struct ovn_extend_table *group_table = &fod->group_table; struct ovn_extend_table *meter_table = &fod->meter_table; + uint32_t *conj_id_ofs = &fod->conj_id_ofs; static bool first_run = true; if (first_run) { first_run = false; } else { - hmap_clear(flow_table); + ovn_desired_flow_table_clear(&fod->flow_table); + ovn_extend_table_clear(&fod->group_table, false /* desired */); + ovn_extend_table_clear(&fod->meter_table, false /* desired */); } - lflow_run(ctx, chassis, + *conj_id_ofs = 1; + lflow_run(flow_table, ctx, chassis, chassis_index, local_datapaths, group_table, - meter_table, addr_sets, port_groups, flow_table, - active_tunnels, local_lport_ids); + meter_table, addr_sets, port_groups, active_tunnels, + local_lport_ids, conj_id_ofs); enum mf_field_id mff_ovn_geneve = ofctrl_get_mf_field_id(); - physical_run(ctx, mff_ovn_geneve, + physical_run(flow_table, ctx, mff_ovn_geneve, br_int, chassis, ct_zones, - flow_table, local_datapaths, local_lports, + local_datapaths, local_lports, chassis_index, active_tunnels); + + node->changed = true; +} + +static bool +flow_output_sb_logical_flow_handler(struct engine_node *node) +{ + struct controller_ctx *ctx = (struct controller_ctx *)node->context; + struct ed_type_runtime_data *data = + (struct ed_type_runtime_data *)engine_get_input( + "runtime_data", node)->data; + struct hmap *local_datapaths = &data->local_datapaths; + struct sset *local_lport_ids = &data->local_lport_ids; + struct sset *active_tunnels = &data->active_tunnels; + struct chassis_index *chassis_index = &data->chassis_index; + struct shash *addr_sets = &data->addr_sets; + struct shash *port_groups = &data->port_groups; + const struct ovsrec_bridge *br_int = get_br_int(ctx); + + const char *chassis_id = get_chassis_id(ctx->ovs_idl); + + + const struct sbrec_chassis *chassis = NULL; + if (chassis_id) { + chassis = get_chassis(ctx->ovnsb_idl, chassis_id); + } + + ovs_assert(br_int && chassis); + + struct ed_type_flow_output *fod = + (struct ed_type_flow_output *)node->data; + struct ovn_desired_flow_table *flow_table = &fod->flow_table; + struct ovn_extend_table *group_table = &fod->group_table; + struct ovn_extend_table *meter_table = &fod->meter_table; + uint32_t *conj_id_ofs = &fod->conj_id_ofs; + + bool handled = lflow_handle_changed_flows(flow_table, ctx, chassis, + chassis_index, local_datapaths, group_table, meter_table, + addr_sets, port_groups, active_tunnels, local_lport_ids, + conj_id_ofs); + node->changed = true; + return handled; } int @@ -910,13 +959,11 @@ main(int argc, char *argv[]) engine_add_input(&en_flow_output, &en_sb_chassis, NULL); engine_add_input(&en_flow_output, &en_sb_encap, NULL); - engine_add_input(&en_flow_output, &en_sb_address_set, NULL); - engine_add_input(&en_flow_output, &en_sb_port_group, NULL); engine_add_input(&en_flow_output, &en_sb_multicast_group, NULL); engine_add_input(&en_flow_output, &en_sb_datapath_binding, NULL); engine_add_input(&en_flow_output, &en_sb_port_binding, NULL); engine_add_input(&en_flow_output, &en_sb_mac_binding, NULL); - engine_add_input(&en_flow_output, &en_sb_logical_flow, NULL); + engine_add_input(&en_flow_output, &en_sb_logical_flow, flow_output_sb_logical_flow_handler); engine_add_input(&en_flow_output, &en_sb_dhcp_options, NULL); engine_add_input(&en_flow_output, &en_sb_dhcpv6_options, NULL); engine_add_input(&en_flow_output, &en_sb_dns, NULL); @@ -927,6 +974,7 @@ main(int argc, char *argv[]) engine_add_input(&en_runtime_data, &en_sb_chassis, NULL); engine_add_input(&en_runtime_data, &en_sb_address_set, NULL); + engine_add_input(&en_runtime_data, &en_sb_port_group, NULL); engine_add_input(&en_runtime_data, &en_sb_datapath_binding, NULL); engine_add_input(&en_runtime_data, &en_sb_port_binding, NULL); engine_add_input(&en_runtime_data, &en_sb_gateway_chassis, NULL); @@ -944,6 +992,7 @@ main(int argc, char *argv[]) uint64_t engine_run_id = 0; uint64_t old_engine_run_id = 0; + /* Main loop. */ exiting = false; while (!exiting) { @@ -980,33 +1029,30 @@ main(int argc, char *argv[]) patch_run(&ctx, br_int, chassis); encaps_run(&ctx, br_int, chassis_id); - if (ofctrl_can_put()) { - stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME, - time_msec()); - engine_run(&en_flow_output, ++engine_run_id); - stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, - time_msec()); - - if (ctx.ovs_idl_txn) { - commit_ct_zones(br_int, &ed_runtime_data.pending_ct_zones); - bfd_run(&ctx, br_int, chassis, - &ed_runtime_data.local_datapaths, - &ed_runtime_data.chassis_index); - } - if (en_flow_output.changed) { - ofctrl_put(&ed_flow_output.flow_table, - &ed_runtime_data.pending_ct_zones, - get_nb_cfg(ctx.ovnsb_idl)); - } + stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME, + time_msec()); + engine_run(&en_flow_output, ++engine_run_id); + stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, + time_msec()); + if (ctx.ovs_idl_txn) { + commit_ct_zones(br_int, &ed_runtime_data.pending_ct_zones); + bfd_run(&ctx, br_int, chassis, + &ed_runtime_data.local_datapaths, + &ed_runtime_data.chassis_index); } - + ofctrl_put(&ed_flow_output.flow_table, + &ed_runtime_data.pending_ct_zones, + get_nb_cfg(ctx.ovnsb_idl), + en_flow_output.changed); pinctrl_run(&ctx, br_int, chassis, &ed_runtime_data.chassis_index, &ed_runtime_data.local_datapaths, &ed_runtime_data.active_tunnels); - update_sb_monitors(ctx.ovnsb_idl, chassis, - &ed_runtime_data.local_lports, - &ed_runtime_data.local_datapaths); + if (en_runtime_data.changed) { + update_sb_monitors(ctx.ovnsb_idl, chassis, + &ed_runtime_data.local_lports, + &ed_runtime_data.local_datapaths); + } } if (old_engine_run_id == engine_run_id || diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c index fc418e7..513d986 100644 --- a/ovn/controller/physical.c +++ b/ovn/controller/physical.c @@ -44,6 +44,9 @@ VLOG_DEFINE_THIS_MODULE(physical); +/* UUID to identify OF flows not associated with ovsdb rows. */ +static struct uuid *hc_uuid = NULL; + void physical_register_ovs_idl(struct ovsdb_idl *ovs_idl) { @@ -188,9 +191,10 @@ get_zone_ids(const struct sbrec_port_binding *binding, } static void -put_local_common_flows(uint32_t dp_key, uint32_t port_key, +put_local_common_flows(struct ovn_desired_flow_table *flow_table, + uint32_t dp_key, uint32_t port_key, bool nested_container, const struct zone_ids *zone_ids, - struct ofpbuf *ofpacts_p, struct hmap *flow_table) + struct ofpbuf *ofpacts_p) { struct match match; @@ -224,7 +228,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key, /* Resubmit to table 34. */ put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p); ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0, - &match, ofpacts_p); + &match, ofpacts_p, hc_uuid); /* Table 34, Priority 100. * ======================= @@ -239,7 +243,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key, match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key); match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 100, 0, - &match, ofpacts_p); + &match, ofpacts_p, hc_uuid); /* Table 64, Priority 100. * ======================= @@ -263,7 +267,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key, put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p); put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p)); ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0, - &match, ofpacts_p); + &match, ofpacts_p, hc_uuid); } static void @@ -291,7 +295,8 @@ load_logical_ingress_metadata(const struct sbrec_port_binding *binding, } static void -consider_port_binding(struct controller_ctx *ctx, +consider_port_binding(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, const struct simap *ct_zones, const struct chassis_index *chassis_index, @@ -299,8 +304,7 @@ consider_port_binding(struct controller_ctx *ctx, struct hmap *local_datapaths, const struct sbrec_port_binding *binding, const struct sbrec_chassis *chassis, - struct ofpbuf *ofpacts_p, - struct hmap *flow_table) + struct ofpbuf *ofpacts_p) { uint32_t dp_key = binding->datapath->tunnel_key; uint32_t port_key = binding->tunnel_key; @@ -328,8 +332,8 @@ consider_port_binding(struct controller_ctx *ctx, } struct zone_ids binding_zones = get_zone_ids(binding, ct_zones); - put_local_common_flows(dp_key, port_key, false, &binding_zones, - ofpacts_p, flow_table); + put_local_common_flows(flow_table, dp_key, port_key, false, + &binding_zones, ofpacts_p); match_init_catchall(&match); ofpbuf_clear(ofpacts_p); @@ -356,7 +360,7 @@ consider_port_binding(struct controller_ctx *ctx, ofpact_finish_CLONE(ofpacts_p, &clone); ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0, - &match, ofpacts_p); + &match, ofpacts_p, hc_uuid); return; } @@ -424,7 +428,7 @@ consider_port_binding(struct controller_ctx *ctx, } ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0, - &match, ofpacts_p); + &match, ofpacts_p, hc_uuid); goto out; } @@ -522,8 +526,8 @@ consider_port_binding(struct controller_ctx *ctx, */ struct zone_ids zone_ids = get_zone_ids(binding, ct_zones); - put_local_common_flows(dp_key, port_key, nested_container, &zone_ids, - ofpacts_p, flow_table); + put_local_common_flows(flow_table, dp_key, port_key, nested_container, + &zone_ids, ofpacts_p); /* Table 0, Priority 150 and 100. * ============================== @@ -567,7 +571,7 @@ consider_port_binding(struct controller_ctx *ctx, /* Resubmit to first logical ingress pipeline table. */ put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p); ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, - tag ? 150 : 100, 0, &match, ofpacts_p); + tag ? 150 : 100, 0, &match, ofpacts_p, hc_uuid); if (!tag && (!strcmp(binding->type, "localnet") || !strcmp(binding->type, "l2gateway"))) { @@ -577,7 +581,7 @@ consider_port_binding(struct controller_ctx *ctx, * action. */ ofpbuf_pull(ofpacts_p, ofpacts_orig_size); match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI)); - ofctrl_add_flow(flow_table, 0, 100, 0, &match, ofpacts_p); + ofctrl_add_flow(flow_table, 0, 100, 0, &match, ofpacts_p, hc_uuid); } /* Table 65, Priority 100. @@ -605,7 +609,7 @@ consider_port_binding(struct controller_ctx *ctx, ofpact_put_STRIP_VLAN(ofpacts_p); } ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0, - &match, ofpacts_p); + &match, ofpacts_p, hc_uuid); } else if (!tun && !is_ha_remote) { /* Remote port connected by localnet port */ /* Table 33, priority 100. @@ -628,7 +632,7 @@ consider_port_binding(struct controller_ctx *ctx, /* Resubmit to table 33. */ put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p); ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0, - &match, ofpacts_p); + &match, ofpacts_p, hc_uuid); } else { /* Remote port connected by tunnel */ @@ -719,7 +723,7 @@ consider_port_binding(struct controller_ctx *ctx, ofpact_finish_BUNDLE(ofpacts_p, &bundle); } ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0, - &match, ofpacts_p); + &match, ofpacts_p, hc_uuid); } out: if (gateway_chassis) { @@ -728,14 +732,14 @@ out: } static void -consider_mc_group(enum mf_field_id mff_ovn_geneve, +consider_mc_group(struct ovn_desired_flow_table *flow_table, + enum mf_field_id mff_ovn_geneve, const struct simap *ct_zones, struct hmap *local_datapaths, const struct sbrec_chassis *chassis, const struct sbrec_multicast_group *mc, struct ofpbuf *ofpacts_p, - struct ofpbuf *remote_ofpacts_p, - struct hmap *flow_table) + struct ofpbuf *remote_ofpacts_p) { uint32_t dp_key = mc->datapath->tunnel_key; if (!get_local_datapath(local_datapaths, dp_key)) { @@ -811,7 +815,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve, put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p); ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0, - &match, ofpacts_p); + &match, ofpacts_p, hc_uuid); } /* Table 32, priority 100. @@ -849,7 +853,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve, put_resubmit(OFTABLE_LOCAL_OUTPUT, remote_ofpacts_p); } ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0, - &match, remote_ofpacts_p); + &match, remote_ofpacts_p, hc_uuid); } } sset_destroy(&remote_chassis); @@ -867,16 +871,22 @@ update_ofports(struct simap *old, struct simap *new) } void -physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, +physical_run(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, const struct ovsrec_bridge *br_int, const struct sbrec_chassis *chassis, const struct simap *ct_zones, - struct hmap *flow_table, struct hmap *local_datapaths, + struct hmap *local_datapaths, const struct sset *local_lports, struct chassis_index *chassis_index, struct sset *active_tunnels) { + if (!hc_uuid) { + hc_uuid = xmalloc(sizeof(struct uuid)); + uuid_generate(hc_uuid); + } + /* This bool tracks physical mapping changes. */ bool physical_map_changed = false; @@ -995,10 +1005,10 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, * 64 for logical-to-physical translation. */ const struct sbrec_port_binding *binding; SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { - consider_port_binding(ctx, mff_ovn_geneve, ct_zones, + consider_port_binding(flow_table, ctx, mff_ovn_geneve, ct_zones, chassis_index, active_tunnels, local_datapaths, binding, chassis, - &ofpacts, flow_table); + &ofpacts); } /* Handle output to multicast groups, in tables 32 and 33. */ @@ -1019,10 +1029,10 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, ofpbuf_clear(&ofpacts); put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0, &match, - &ofpacts); + &ofpacts, hc_uuid); - consider_mc_group(mff_ovn_geneve, ct_zones, local_datapaths, chassis, - mc, &ofpacts, &remote_ofpacts, flow_table); + consider_mc_group(flow_table, mff_ovn_geneve, ct_zones, local_datapaths, chassis, + mc, &ofpacts, &remote_ofpacts); } ofpbuf_uninit(&remote_ofpacts); @@ -1063,7 +1073,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match, - &ofpacts); + &ofpacts, hc_uuid); } /* Add flows for VXLAN encapsulations. Due to the limited amount of @@ -1096,7 +1106,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match, - &ofpacts); + &ofpacts, hc_uuid); } } @@ -1117,7 +1127,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, ofpbuf_clear(&ofpacts); put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0, - &match, &ofpacts); + &match, &ofpacts, hc_uuid); /* Table 32, priority 150. * ======================= @@ -1140,7 +1150,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, pb->tunnel_key); match_set_metadata(&match, htonll(pb->datapath->tunnel_key)); ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0, - &match, &ofpacts); + &match, &ofpacts, hc_uuid); } } @@ -1152,7 +1162,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, match_init_catchall(&match); ofpbuf_clear(&ofpacts); put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts); - ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, 0, &match, &ofpacts); + ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, 0, &match, &ofpacts, hc_uuid); /* Table 34, Priority 0. * ======================= @@ -1167,7 +1177,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, } put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, 0, &match, - &ofpacts); + &ofpacts, hc_uuid); /* Table 64, Priority 0. * ======================= @@ -1177,7 +1187,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, match_init_catchall(&match); ofpbuf_clear(&ofpacts); put_resubmit(OFTABLE_LOG_TO_PHY, &ofpacts); - ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, 0, &match, &ofpacts); + ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, 0, &match, &ofpacts, hc_uuid); ofpbuf_uninit(&ofpacts); diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h index f8dbb49..ac2da2a 100644 --- a/ovn/controller/physical.h +++ b/ovn/controller/physical.h @@ -43,11 +43,12 @@ struct chassis_index; #define OVN_GENEVE_LEN 4 void physical_register_ovs_idl(struct ovsdb_idl *); -void physical_run(struct controller_ctx *, enum mf_field_id mff_ovn_geneve, +void physical_run(struct ovn_desired_flow_table *flow_table, + struct controller_ctx *, enum mf_field_id mff_ovn_geneve, const struct ovsrec_bridge *br_int, const struct sbrec_chassis *chassis, const struct simap *ct_zones, - struct hmap *flow_table, struct hmap *local_datapaths, + struct hmap *local_datapaths, const struct sset *local_lports, struct chassis_index *chassis_index, struct sset *active_tunnels); diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index a694581..655085d 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -1049,7 +1049,8 @@ encode_CT_LB(const struct ovnact_ct_lb *cl, recirc_table, zone_reg); } - table_id = ovn_extend_table_assign_id(ep->group_table, &ds); + table_id = ovn_extend_table_assign_id(ep->group_table, &ds, + ep->lflow_uuid); ds_destroy(&ds); if (table_id == EXT_TABLE_ID_INVALID) { return; @@ -2208,7 +2209,8 @@ encode_SET_METER(const struct ovnact_set_meter *cl, cl->rate); } - table_id = ovn_extend_table_assign_id(ep->meter_table, &ds); + table_id = ovn_extend_table_assign_id(ep->meter_table, &ds, + ep->lflow_uuid); if (table_id == EXT_TABLE_ID_INVALID) { ds_destroy(&ds); return; diff --git a/ovn/lib/extend-table.c b/ovn/lib/extend-table.c index e18713b..e4dd52a 100644 --- a/ovn/lib/extend-table.c +++ b/ovn/lib/extend-table.c @@ -17,6 +17,7 @@ #include #include "bitmap.h" #include "hash.h" +#include "lib/uuid.h" #include "openvswitch/vlog.h" #include "ovn/lib/extend-table.h" @@ -89,9 +90,10 @@ ovn_extend_table_clear(struct ovn_extend_table *table, bool existing) } } +/* Remove an entry from existing table */ void -ovn_extend_table_remove(struct ovn_extend_table *table, - struct ovn_extend_table_info *existing) +ovn_extend_table_remove_existing(struct ovn_extend_table *table, + struct ovn_extend_table_info *existing) { /* Remove 'existing' from 'groups->existing' */ hmap_remove(&table->existing, &existing->hmap_node); @@ -102,21 +104,50 @@ ovn_extend_table_remove(struct ovn_extend_table *table, free(existing); } +/* Remove entries in desired table that are created by the lflow_uuid */ void -ovn_extend_table_move(struct ovn_extend_table *table) +ovn_extend_table_remove_desired(struct ovn_extend_table *table, + const struct uuid *lflow_uuid) +{ + struct ovn_extend_table_info *e, *next_e; + HMAP_FOR_EACH_SAFE (e, next_e, hmap_node, &table->desired) { + if (uuid_equals(&e->lflow_uuid, lflow_uuid)) { + hmap_remove(&table->desired, &e->hmap_node); + ds_destroy(&e->info); + if (e->new_table_id) { + bitmap_set0(table->table_ids, e->table_id); + } + free(e); + } + } + +} + +static struct ovn_extend_table_info* +ovn_extend_info_clone(struct ovn_extend_table_info *source) +{ + struct ovn_extend_table_info *clone = xmalloc(sizeof *clone); + ds_clone(&clone->info, &source->info); + clone->table_id = source->table_id; + clone->new_table_id = source->new_table_id; + clone->hmap_node.hash = source->hmap_node.hash; + clone->lflow_uuid = source->lflow_uuid; + return clone; +} + +void +ovn_extend_table_sync(struct ovn_extend_table *table) { struct ovn_extend_table_info *desired, *next; - /* Move the contents of desired to existing. */ + /* Copy the contents of desired to existing. */ HMAP_FOR_EACH_SAFE (desired, next, hmap_node, &table->desired) { - hmap_remove(&table->desired, &desired->hmap_node); - if (!ovn_extend_table_lookup(&table->existing, desired)) { - hmap_insert(&table->existing, &desired->hmap_node, - desired->hmap_node.hash); - } else { - ds_destroy(&desired->info); - free(desired); + desired->new_table_id = false; + struct ovn_extend_table_info *clone = + ovn_extend_info_clone(desired); + hmap_insert(&table->existing, &clone->hmap_node, + clone->hmap_node.hash); } } } @@ -124,7 +155,8 @@ ovn_extend_table_move(struct ovn_extend_table *table) /* Assign a new table ID for the table information from the bitmap. * If it already exists, return the old ID. */ uint32_t -ovn_extend_table_assign_id(struct ovn_extend_table *table, struct ds *ds) +ovn_extend_table_assign_id(struct ovn_extend_table *table, struct ds *ds, + struct uuid lflow_uuid) { uint32_t table_id = 0, hash; struct ovn_extend_table_info *table_info; @@ -133,7 +165,8 @@ ovn_extend_table_assign_id(struct ovn_extend_table *table, struct ds *ds) /* Check whether we have non installed but allocated group_id. */ HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->desired) { - if (!strcmp(ds_cstr(&table_info->info), ds_cstr(ds))) { + if (!strcmp(ds_cstr(&table_info->info), ds_cstr(ds)) && + table_info->new_table_id) { return table_info->table_id; } } @@ -165,6 +198,7 @@ ovn_extend_table_assign_id(struct ovn_extend_table *table, struct ds *ds) table_info->table_id = table_id; table_info->hmap_node.hash = hash; table_info->new_table_id = new_table_id; + table_info->lflow_uuid = lflow_uuid; hmap_insert(&table->desired, &table_info->hmap_node, table_info->hmap_node.hash); diff --git a/ovn/lib/extend-table.h b/ovn/lib/extend-table.h index d9ae549..f0b8fd9 100644 --- a/ovn/lib/extend-table.h +++ b/ovn/lib/extend-table.h @@ -23,6 +23,7 @@ #include "openvswitch/dynamic-string.h" #include "openvswitch/hmap.h" #include "openvswitch/list.h" +#include "openvswitch/uuid.h" /* Used to manage expansion tables associated with Flow table, * such as the Group Table or Meter Table. */ @@ -37,6 +38,7 @@ struct ovn_extend_table { struct ovn_extend_table_info { struct hmap_node hmap_node; struct ds info; /* Details string for the table entity. */ + struct uuid lflow_uuid; uint32_t table_id; bool new_table_id; /* 'True' if 'table_id' was reserved from * ovn_extend_table's 'table_ids' bitmap. */ @@ -51,14 +53,18 @@ struct ovn_extend_table_info *ovn_extend_table_lookup( void ovn_extend_table_clear(struct ovn_extend_table *, bool); -void ovn_extend_table_remove(struct ovn_extend_table *, - struct ovn_extend_table_info *); +void ovn_extend_table_remove_existing(struct ovn_extend_table *, + struct ovn_extend_table_info *); -/* Move the contents of desired to existing. */ -void ovn_extend_table_move(struct ovn_extend_table *); +void ovn_extend_table_remove_desired(struct ovn_extend_table *table, + const struct uuid *lflow_uuid); + +/* Copy the contents of desired to existing. */ +void ovn_extend_table_sync(struct ovn_extend_table *); uint32_t ovn_extend_table_assign_id(struct ovn_extend_table *, - struct ds *); + struct ds *, + struct uuid lflow_uuid); /* Iterates 'DESIRED' through all of the 'ovn_extend_table_info's in * 'TABLE'->desired that are not in 'TABLE'->existing. (The loop body