On Thu, Nov 6, 2025 at 8:41 AM Numan Siddique <[email protected]> wrote:
>
> On Wed, Nov 5, 2025 at 6:52 PM Han Zhou <[email protected]> wrote:
> >
> >
> >
> > On Mon, Nov 3, 2025 at 6:19 PM <[email protected]> wrote:
> > >
> > > From: Numan Siddique <[email protected]>
> > >
> > > Signed-off-by: Numan Siddique <[email protected]>
> > > ---
> > > br-controller/automake.mk | 2 +
> > > br-controller/br-flow-mgr.c | 1263
+++++++++++++++++++++++++++++++++++
> > > br-controller/br-flow-mgr.h | 65 ++
> > > 3 files changed, 1330 insertions(+)
> > > create mode 100644 br-controller/br-flow-mgr.c
> > > create mode 100644 br-controller/br-flow-mgr.h
> > >
> > > diff --git a/br-controller/automake.mk b/br-controller/automake.mk
> > > index b77e2abcff..ea515a85e4 100644
> > > --- a/br-controller/automake.mk
> > > +++ b/br-controller/automake.mk
> > > @@ -1,5 +1,7 @@
> > > bin_PROGRAMS += br-controller/ovn-br-controller
> > > br_controller_ovn_br_controller_SOURCES = \
> > > + br-controller/br-flow-mgr.c \
> > > + br-controller/br-flow-mgr.h \
> > > br-controller/en-bridge-data.c \
> > > br-controller/en-bridge-data.h \
> > > br-controller/en-lflow.c \
> > > diff --git a/br-controller/br-flow-mgr.c b/br-controller/br-flow-mgr.c
> > > new file mode 100644
> > > index 0000000000..cc9fb8f16d
> > > --- /dev/null
> > > +++ b/br-controller/br-flow-mgr.c
> > > @@ -0,0 +1,1263 @@
> > > +/*
> > > + * Licensed under the Apache License, Version 2.0 (the "License");
> > > + * you may not use this file except in compliance with the License.
> > > + * You may obtain a copy of the License at:
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
software
> > > + * distributed under the License is distributed on an "AS IS" BASIS,
> > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
> > > + * See the License for the specific language governing permissions
and
> > > + * limitations under the License.
> > > + */
> > > +
> > > +#include <config.h>
> > > +
> > > +/* OVS includes. */
> > > +#include "lib/byte-order.h"
> > > +#include "lib/uuid.h"
> > > +#include "lib/hash.h"
> > > +#include "lib/hmapx.h"
> > > +#include "lib/ovs-atomic.h"
> > > +#include "openvswitch/ofp-actions.h"
> > > +#include "openvswitch/ofp-flow.h"
> > > +#include "openvswitch/ofp-group.h"
> > > +#include "openvswitch/ofp-match.h"
> > > +#include "openvswitch/ofp-msgs.h"
> > > +#include "openvswitch/ofp-meter.h"
> > > +#include "openvswitch/ofp-packet.h"
> > > +#include "openvswitch/ofp-print.h"
> > > +#include "openvswitch/ofp-util.h"
> > > +#include "openvswitch/ofpbuf.h"
> > > +#include "openvswitch/vlog.h"
> > > +
> > > +/* OVN includes. */
> > > +#include "br-flow-mgr.h"
> > > +
> > > +VLOG_DEFINE_THIS_MODULE(brflowmgr);
> > > +
> > > +static struct hmap br_flow_tables =
HMAP_INITIALIZER(&br_flow_tables);
> > > +
> > > +#define CONJ_ACT_COOKIE UINT64_MAX
> > > +
> > > +/* Flow handling. */
> > > +struct ovn_flow;
> > > +
> > > +/* An ovn_flow action. */
> > > +struct ovn_flow_action {
> > > + struct ovs_list flow_uuid_action_node;
> > > +
> > > + struct ofpact *ofpacts;
> > > + size_t ofpacts_len;
> > > + uint64_t cookie;
> > > + struct uuid flow_uuid;
> > > + bool stale;
> > > +
> > > + struct ovs_list list_node;
> > > + struct ovn_flow *flow;
> > > +};
> > > +
> > > +/* An OpenFlow flow. */
> > > +struct ovn_flow {
> > > + struct hmap_node hmap_node;
> > > + struct hmap *match_flow_table; /* Points to the match flow table
hmap for
> > > + * easy access. */
> > > + struct hmap *uuid_action_flow_table; /* Points to the uuid
action flow
> > > + * table hmap for easy
access. */
> > > + struct ovs_list flow_list_node; /* List node in
> > > + * br_flow_table.flows_list[]. */
> > > +
> > > + /* Flow list in which this 'ovn_flow.flow_list_node' is present
> > > + * (for easy access). */
> > > + struct ovs_list *flow_list;
> > > +
> > > + /* Key. */
> > > + uint8_t table_id;
> > > + uint16_t priority;
> > > + struct minimatch match;
> > > +
> > > + /* Hash. */
> > > + uint32_t hash;
> > > +
> > > + /* Actions associated with the flow.
> > > + * An ovn_flow can have
> > > + * - A normal action - has precedence over all the other below
actions
> > > + * if set.
> > > + * - a list of allow actions - has precedence over drop and
conjunctive
> > > + * actions.
> > > + * - a list of drop actions - has precedence over conjunctive
actions.
> > > + * - a list of conjunctive actions.
> > > + */
> >
> > Is this overriding behavior documented anywhere for the users?
>
> Not really. Presently if I understand correctly, ofctrl.c gives the
> same precedence for multiple logical flows
> which have the same match. (Most likely in case of conjunction flows).
>
> For the provider brides, I don't expect CMS to program such complex
> flows using conjunction, but you never know :).
>
>
> >
> > > + struct ovn_flow_action *normal_act;
> > > + struct ovs_list allow_act_list;
> > > + struct ovs_list drop_act_list;
> > > + struct ovs_list conj_act_list;
> > > +
> > > + /* Presently installed ofpacts. */
> > > + struct ofpact *installed_ofpacts;
> > > + size_t installed_ofpacts_len;
> > > +
> > > + /* Cookie of the ovn_action which is presently active. */
> > > + uint64_t active_cookie;
> > > +};
> > > +
> > > +/* Represents a list of 'ovn flow actions' associated with
> > > + * a flow uuid. */
> > > +struct flow_uuid_to_acts {
> > > + struct hmap_node hmap_node; /* Node in
> > > + * ovn_flow_table.uuid_flow_table. */
> > > + struct uuid flow_uuid;
> > > + struct ovs_list acts; /* A list of struct ovn_flow_action nodes
that
> > > + * are referenced by the uuid. */
> > > +};
> > > +
> > > +/* Represents a flow table. */
> > > +struct ovn_flow_table {
> > > + /* Hash map of flow table using flow match conditions as hash
key.*/
> > > + struct hmap match_flow_table;
> > > +
> > > + /* Hash map of ovn_flow_action list table using uuid as hash
key.*/
> > > + struct hmap uuid_action_flow_table;
> > > +};
> > > +
> > > +/* Represents a bridge flow table.
> > > + *
> > > + * We maintain 2 lists for openflows list. One represents an active
flow
> > > + * list and the other represents an old flow list. When a flow is
added to
> > > + * the br flow table, it is always added to the active flow list. If
that flow
> > > + * is present in the old flow list, it is removed from the old one.
> > > + *
> > > + * The function br_flow_populate_oflow_msgs() clears all the
> > > + * flows present in the old flow list.
> > > + *
> > > + * User can swap the lists by calling br_flow_switch_oflow_tables().
> > > + *
> > > + */
> > > +struct br_flow_table {
> > > + struct hmap_node hmap_node;
> > > + char *bridge; /* key. */
> > > +
> > > + struct ovn_flow_table lflow_table; /* For logical flows. */
> > > + struct ovn_flow_table pflow_table; /* For physical flows. */
> > > +
> > > + struct ovs_list lflows_list[2];
> > > + struct ovs_list pflows_list[2];
> > > +
> > > + struct ovs_list *active_lflows; /* Points to one of the element
in
> > > + * lflows_list. */
> > > + struct ovs_list *old_lflows; /* Points to other element in
> > > + * lflows_list. */
> > > +
> > > + struct ovs_list *active_pflows;
> > > + struct ovs_list *old_pflows;
> > > +
> > > + struct hmapx modified_lflows;
> > > + struct hmapx modified_pflows;
> > > +};
> > > +
> > > +/* Static functions. */
> > > +static void br_flow_switch_lflow_table__(struct br_flow_table *);
> > > +static void br_flow_switch_pflow_table__(struct br_flow_table *);
> > > +static void br_flow_table_destroy__(struct br_flow_table *);
> > > +
> > > +static void br_flow_add_openflow__(struct br_flow_table *,
> > > + bool lflow_table,
> > > + uint8_t table_id, uint16_t
priority,
> > > + uint64_t cookie, const struct
match *match,
> > > + const struct ofpbuf *actions,
> > > + const struct uuid *flow_uuid);
> > > +static void br_flows_remove(struct br_flow_table *, bool lflow_table,
> > > + const struct uuid *flow_uuid);
> > > +
> > > +static void ovn_flow_table_init(struct ovn_flow_table *);
> > > +static void ovn_flow_table_clear(struct ovn_flow_table *);
> > > +static void ovn_flow_table_destroy(struct ovn_flow_table *);
> > > +
> > > +static uint32_t ovn_flow_match_hash__(uint8_t table_id, uint16_t
priority,
> > > + const struct minimatch *);
> > > +static uint32_t ovn_flow_match_hash(const struct ovn_flow *);
> > > +
> > > +static void ovn_flow_init(struct ovn_flow *, uint8_t table_id,
> > > + uint16_t priority, const struct match *);
> > > +static void ovn_flow_uninit(struct ovn_flow *);
> > > +static struct ovn_flow *ovn_flow_alloc(uint8_t table_id, uint16_t
priority,
> > > + const struct match *);
> > > +static void ovn_flow_destroy(struct ovn_flow *);
> > > +
> > > +static struct ovn_flow_action *ovn_flow_action_alloc(const struct
ofpbuf *,
> > > + uint64_t cookie,
> > > + const struct
uuid *);
> > > +static void ovn_flow_action_init(struct ovn_flow_action *,
> > > + const struct ofpbuf *, uint64_t
cookie,
> > > + const struct uuid *);
> > > +static void ovn_flow_action_destroy(struct ovn_flow_action *);
> > > +static struct ovn_flow *ovn_flow_lookup(struct ovn_flow_table *,
> > > + uint8_t table_id, uint16_t
priority,
> > > + const struct minimatch *);
> > > +static void ovn_flow_invalidate_all_actions(struct ovn_flow *f);
> > > +static void ovn_flow_clear_actions_from_list(struct ovs_list
*act_list);
> > > +static void ovn_flow_invalidate_actions_from_list(struct ovs_list
*act_list);
> > > +static void ovn_flow_clear_actions(struct ovn_flow *);
> > > +static bool ovn_flow_has_active_actions(struct ovn_flow *);
> > > +
> > > +static bool ovn_flow_action_is_normal(const struct ovn_flow_action
*);
> > > +static bool ovn_flow_action_is_allow(const struct ovn_flow_action *);
> > > +static bool ovn_flow_action_is_drop(const struct ovn_flow_action *);
> > > +static bool ovn_flow_action_is_conj(const struct ovn_flow_action *);
> > > +
> > > +static struct flow_uuid_to_acts *flow_uuid_to_acts_lookup(
> > > + struct hmap *uuid_action_table, const struct uuid *flow_uuid);
> > > +static void ovn_flow_action_link_to_flow_uuid(
> > > + struct hmap *uuid_action_table, struct ovn_flow_action *);
> > > +static void ovn_flow_unref_action__(struct ovn_flow *,
> > > + struct ovn_flow_action *);
> > > +static struct ovn_flow_action * ovn_flow_action_get_matching_in_list(
> > > + struct ovs_list *act_list, struct ovn_flow_action *a);
> > > +static void ovn_flow_unref_action(struct ovn_flow *, struct
ovn_flow_action *);
> > > +static struct ovn_flow_action *ovn_flow_get_matching_action(
> > > + struct ovn_flow *, struct ovn_flow_action *);
> > > +
> > > +static bool ovn_flow_update_actions(struct ovn_flow_table *,
> > > + struct ovn_flow *,
> > > + struct ovn_flow_action *);
> > > +
> > > +static void ovn_flow_prepare_ofmsg(struct ovn_flow *, struct
ovs_list *msgs);
> > > +static void br_flow_populate_oflow_msgs__(struct br_flow_table *,
> > > + struct ovs_list *msgs);
> > > +
> > > +static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *);
> > > +static void add_flow_mod(struct ofputil_flow_mod *, struct ovs_list
*msgs);
> > > +static void ovn_flow_add_oflow(struct ovn_flow *, struct
ovn_flow_action *,
> > > + struct ovs_list *msgs);
> > > +static void ovn_flow_mod_oflow(struct ovn_flow *, struct
ovn_flow_action *,
> > > + struct ovs_list *msgs);
> > > +static void ovn_flow_del_oflow(struct ovn_flow *, struct ovs_list
*msgs);
> > > +static void ovn_flow_log(const struct ovn_flow *);
> > > +static char * ovn_flow_to_string(const struct ovn_flow *);
> > > +
> > > +void
> > > +br_flow_tables_init(void)
> > > +{
> > > +
> > > +}
> > > +
> > > +void
> > > +br_flow_tables_destroy(void)
> > > +{
> > > + struct br_flow_table *br_table;
> > > + HMAP_FOR_EACH_POP (br_table, hmap_node, &br_flow_tables) {
> > > + br_flow_table_destroy__(br_table);
> > > + }
> > > +
> > > + hmap_destroy(&br_flow_tables);
> > > +}
> > > +
> > > +struct br_flow_table *
> > > +br_flow_table_get(const char *bridge)
> > > +{
> > > + struct br_flow_table *br_table;
> > > + uint32_t hash = hash_string(bridge, 0);
> > > + HMAP_FOR_EACH_WITH_HASH (br_table, hmap_node, hash,
&br_flow_tables) {
> > > + if (!strcmp(br_table->bridge, bridge)) {
> > > + return br_table;
> > > + }
> > > + }
> > > +
> > > + return NULL;
> > > +}
> > > +
> > > +struct br_flow_table *
> > > +br_flow_table_alloc(const char *bridge)
> > > +{
> > > + struct br_flow_table *br_table = xzalloc(sizeof *br_table);
> > > + ovn_flow_table_init(&br_table->lflow_table);
> > > + ovn_flow_table_init(&br_table->pflow_table);
> > > +
> > > + ovs_list_init(&br_table->lflows_list[0]);
> > > + ovs_list_init(&br_table->lflows_list[1]);
> > > +
> > > + ovs_list_init(&br_table->pflows_list[0]);
> > > + ovs_list_init(&br_table->pflows_list[1]);
> > > +
> > > + hmapx_init(&br_table->modified_lflows);
> > > + hmapx_init(&br_table->modified_pflows);
> > > +
> > > + br_table->active_lflows = &br_table->lflows_list[0];
> > > + br_table->old_lflows = &br_table->lflows_list[1];
> > > +
> > > + br_table->active_pflows = &br_table->pflows_list[0];
> > > + br_table->old_pflows = &br_table->pflows_list[1];
> > > +
> > > + br_table->bridge = xstrdup(bridge);
> > > + hmap_insert(&br_flow_tables, &br_table->hmap_node,
hash_string(bridge, 0));
> > > + return br_table;
> > > +}
> > > +
> > > +void br_flow_table_destroy(const char *bridge)
> > > +{
> > > + struct br_flow_table *br_table = br_flow_table_get(bridge);
> > > + if (br_table) {
> > > + hmap_remove(&br_flow_tables, &br_table->hmap_node);
> > > + br_flow_table_destroy__(br_table);
> > > + }
> > > +}
> > > +
> > > +void
> > > +br_flow_switch_logical_oflow_tables(void)
> > > +{
> > > + struct br_flow_table *br_ftable;
> > > + HMAP_FOR_EACH (br_ftable, hmap_node, &br_flow_tables) {
> > > + br_flow_switch_lflow_table__(br_ftable);
> > > + }
> > > +}
> > > +
> > > +void
> > > +br_flow_switch_logical_oflow_table(const char *bridge)
> > > +{
> > > + struct br_flow_table *br_ftable;
> > > + br_ftable = br_flow_table_get(bridge);
> > > + if (br_ftable) {
> > > + br_flow_switch_lflow_table__(br_ftable);
> > > + }
> > > +}
> > > +
> > > +void
> > > +br_flow_switch_physical_oflow_tables(void)
> > > +{
> > > + struct br_flow_table *br_ftable;
> > > + HMAP_FOR_EACH (br_ftable, hmap_node, &br_flow_tables) {
> > > + br_flow_switch_pflow_table__(br_ftable);
> > > + }
> > > +}
> > > +
> > > +void
> > > +br_flow_switch_physical_oflow_table(const char *bridge)
> > > +{
> > > + struct br_flow_table *br_ftable;
> > > + br_ftable = br_flow_table_get(bridge);
> > > + if (br_ftable) {
> > > + br_flow_switch_pflow_table__(br_ftable);
> > > + }
> > > +}
> > > +
> > > +void
> > > +br_flow_add_logical_oflow(const char *bridge, uint8_t table_id,
> > > + uint16_t priority, uint64_t cookie,
> > > + const struct match *match,
> > > + const struct ofpbuf *actions,
> > > + const struct uuid *flow_uuid)
> > > +{
> > > + struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > + if (!br_ftable) {
> > > + br_ftable = br_flow_table_alloc(bridge);
> > > + }
> > > +
> > > + br_flow_add_openflow__(br_ftable, true, table_id, priority,
cookie, match,
> > > + actions, flow_uuid);
> > > +}
> > > +
> > > +void
> > > +br_flow_add_physical_oflow(const char *bridge, uint8_t table_id,
> > > + uint16_t priority, uint64_t cookie,
> > > + const struct match *match,
> > > + const struct ofpbuf *actions,
> > > + const struct uuid *flow_uuid)
> > > +{
> > > + struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > + if (!br_ftable) {
> > > + br_ftable = br_flow_table_alloc(bridge);
> > > + }
> > > +
> > > + br_flow_add_openflow__(br_ftable, false, table_id, priority,
cookie, match,
> > > + actions, flow_uuid);
> > > +}
> > > +
> > > +void
> > > +br_flow_remove_logical_oflows_all(const struct uuid *flow_uuid)
> > > +{
> > > + struct br_flow_table *br_ftable;
> > > + HMAP_FOR_EACH (br_ftable, hmap_node, &br_flow_tables) {
> > > + br_flows_remove(br_ftable, true, flow_uuid);
> > > + }
> > > +}
> > > +
> > > +void
> > > +br_flow_remove_logical_oflows(const char *bridge, const struct uuid
*flow_uuid)
> > > +{
> > > + struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > + if (br_ftable) {
> > > + br_flows_remove(br_ftable, true, flow_uuid);
> > > + }
> > > +}
> > > +
> > > +void
> > > +br_flow_remove_physical_oflows(const char *bridge,
> > > + const struct uuid *flow_uuid)
> > > +{
> > > + struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > + if (br_ftable) {
> > > + br_flows_remove(br_ftable, false, flow_uuid);
> > > + }
> > > +}
> > > +
> > > +void
> > > +br_flow_flush_all_oflows(void)
> > > +{
> > > + struct br_flow_table *br_table;
> > > + HMAP_FOR_EACH_POP (br_table, hmap_node, &br_flow_tables) {
> > > + br_flow_table_destroy__(br_table);
> > > + }
> > > +
> > > + hmap_destroy(&br_flow_tables);
> > > + hmap_init(&br_flow_tables);
> > > +}
> > > +
> > > +void
> > > +br_flow_flush_oflows(const char *bridge)
> > > +{
> > > + struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > + if (br_ftable) {
> > > + hmap_remove(&br_flow_tables, &br_ftable->hmap_node);
> > > + br_flow_table_destroy__(br_ftable);
> > > + }
> > > +}
> > > +
> > > +void
> > > +br_flow_populate_oflow_msgs(const char *bridge, struct ovs_list
*msgs)
> > > +{
> > > + struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > + if (br_ftable) {
> > > + br_flow_populate_oflow_msgs__(br_ftable, msgs);
> > > + }
> > > +}
> > > +
> > > +/* Static functions. */
> > > +
> > > +static void
> > > +br_flow_switch_lflow_table__(struct br_flow_table *br_ftable)
> > > +{
> > > + struct ovs_list *t = br_ftable->active_lflows;
> > > + br_ftable->active_lflows = br_ftable->old_lflows;
> > > + br_ftable->old_lflows = t;
> > > +
> > > + hmapx_clear(&br_ftable->modified_lflows);
> > > +}
> > > +
> > > +static void
> > > +br_flow_switch_pflow_table__(struct br_flow_table *br_ftable)
> > > +{
> > > + struct ovs_list *t = br_ftable->active_pflows;
> > > + br_ftable->active_pflows = br_ftable->old_pflows;
> > > + br_ftable->old_pflows = t;
> > > +
> > > + hmapx_clear(&br_ftable->modified_pflows);
> > > +}
> > > +
> > > +static void
> > > +br_flow_table_destroy__(struct br_flow_table *br_ftable)
> > > +{
> > > + hmapx_clear(&br_ftable->modified_lflows);
> > > + hmapx_destroy(&br_ftable->modified_lflows);
> > > + ovn_flow_table_destroy(&br_ftable->lflow_table);
> > > +
> > > + hmapx_clear(&br_ftable->modified_pflows);
> > > + hmapx_destroy(&br_ftable->modified_pflows);
> > > + ovn_flow_table_destroy(&br_ftable->pflow_table);
> > > +
> > > + free(br_ftable->bridge);
> > > + free(br_ftable);
> > > +}
> > > +
> > > +/* This function
> > > + * - Looks up in the bridge flow table if the flow F
> > > + * with the provided (match, table_id, priority) is already
> > > + * present or not.
> > > + *
> > > + * - If not present it creates an ovn_flow 'F' with the provided
> > > + * (match, table_id, priority) and inserts into the flow table.
> > > + *
> > > + * - Allocates ovn_flow_action 'A' with the given -
> > > + * (actions, cookie, flow_uuid).
> > > + *
> > > + * - If 'F' already has 'A' it does nothing and returns. Otherwise
> > > + * it associates 'A' to the 'F' actions.
> > > + *
> > > + * - Adds 'F' to the active flow list.
> > > + */
> > > +static void
> > > +br_flow_add_openflow__(struct br_flow_table *br_ftable, bool
lflow_table,
> > > + uint8_t table_id, uint16_t priority,
> > > + uint64_t cookie, const struct match *match,
> > > + const struct ofpbuf *actions,
> > > + const struct uuid *flow_uuid)
> > > +{
> > > + struct ovn_flow_table *ftable;
> > > + struct hmapx *modified_flows;
> > > + struct ovs_list *active_flows;
> > > +
> > > + if (lflow_table) {
> > > + ftable = &br_ftable->lflow_table;
> > > + modified_flows = &br_ftable->modified_lflows;
> > > + active_flows = br_ftable->active_lflows;
> > > + } else {
> > > + ftable = &br_ftable->pflow_table;
> > > + modified_flows = &br_ftable->modified_pflows;
> > > + active_flows = br_ftable->active_pflows;
> > > + }
> > > +
> > > + struct minimatch minimatch;
> > > + minimatch_init(&minimatch, match);
> > > +
> > > + struct ovn_flow *f = ovn_flow_lookup(ftable, table_id, priority,
> > > + &minimatch);
> > > +
> > > + minimatch_destroy(&minimatch);
> > > +
> > > + bool active_flow_list_changed = false;
> > > +
> > > + if (!f) {
> > > + f = ovn_flow_alloc(table_id, priority, match);
> > > + f->match_flow_table = &ftable->match_flow_table;
> > > + f->uuid_action_flow_table = &ftable->uuid_action_flow_table;
> > > + ovs_list_init(&f->flow_list_node);
> > > + hmap_insert(&ftable->match_flow_table, &f->hmap_node,
> > > + f->hash);
> > > + } else {
> > > + ovs_list_remove(&f->flow_list_node);
> > > + active_flow_list_changed = (active_flows != f->flow_list);
> > > + }
> > > +
> > > + f->flow_list = active_flows;
> > > + ovs_list_push_back(active_flows, &f->flow_list_node);
> > > +
> > > + struct ovn_flow_action *a = ovn_flow_action_alloc(actions,
cookie,
> > > + flow_uuid);
> > > +
> > > + if (active_flow_list_changed) {
> > > + ovn_flow_invalidate_all_actions(f);
> > > + }
> > > +
> > > + struct ovn_flow_action *existing_a =
> > > + f ? ovn_flow_get_matching_action(f, a): NULL;
> > > + bool push_flow_to_switch = true;
> > > +
> > > + if (existing_a && uuid_equals(&existing_a->flow_uuid,
flow_uuid)) {
> > > + existing_a->stale = false;
> > > +
> > > + if (!active_flow_list_changed) {
> > > + /* The flow-action pair already exists. Nothing to be
done. */
> > > + push_flow_to_switch = false;
> > > + }
> > > + ovn_flow_action_destroy(a);
> > > + } else {
> > > + ovn_flow_update_actions(ftable, f, a);
> > > + }
> > > +
> > > + if (push_flow_to_switch) {
> > > + ovn_flow_log(f);
> > > + hmapx_add(modified_flows, (void *) f);
> > > + }
> > > +}
> > > +
> > > +static void
> > > +br_flows_remove(struct br_flow_table *br_ftable, bool lflow_table,
> > > + const struct uuid *flow_uuid)
> > > +{
> > > + struct ovn_flow_table *flow_table;
> > > + struct hmapx *modified_flows;
> > > +
> > > + if (lflow_table) {
> > > + flow_table = &br_ftable->lflow_table;
> > > + modified_flows = &br_ftable->modified_lflows;
> > > + } else {
> > > + flow_table = &br_ftable->pflow_table;
> > > + modified_flows = &br_ftable->modified_pflows;
> > > + }
> > > +
> > > + struct flow_uuid_to_acts *f_uuid_to_acts =
> > > + flow_uuid_to_acts_lookup(&flow_table->uuid_action_flow_table,
> > > + flow_uuid);
> > > +
> > > + if (!f_uuid_to_acts) {
> > > + return;
> > > + }
> > > +
> > > + struct ovn_flow_action *a;
> > > + LIST_FOR_EACH_POP (a, flow_uuid_action_node,
&f_uuid_to_acts->acts) {
> > > + struct ovn_flow *f = a->flow;
> > > + ovn_flow_unref_action__(a->flow, a);
> > > + ovn_flow_action_destroy(a);
> > > + hmapx_add(modified_flows, (void *) f);
> > > + }
> > > +
> > > + hmap_remove(&flow_table->uuid_action_flow_table,
> > > + &f_uuid_to_acts->hmap_node);
> > > + free(f_uuid_to_acts);
> > > +}
> > > +
> > > +/* ovn flow table functions. */
> > > +static void
> > > +ovn_flow_table_init(struct ovn_flow_table *flow_table)
> > > +{
> > > + hmap_init(&flow_table->match_flow_table);
> > > + hmap_init(&flow_table->uuid_action_flow_table);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_table_clear(struct ovn_flow_table *flow_table)
> > > +{
> > > + struct flow_uuid_to_acts *f_uuid_to_acts;
> > > + HMAP_FOR_EACH_POP (f_uuid_to_acts, hmap_node,
> > > + &flow_table->uuid_action_flow_table) {
> > > + struct ovn_flow_action *a;
> > > + LIST_FOR_EACH_POP (a, flow_uuid_action_node,
> > > + &f_uuid_to_acts->acts) {
> > > + ovn_flow_unref_action__(a->flow, a);
> > > + ovn_flow_action_destroy(a);
> > > + }
> > > +
> > > + free(f_uuid_to_acts);
> > > + }
> > > +
> > > + struct ovn_flow *f;
> > > + HMAP_FOR_EACH_POP (f, hmap_node, &flow_table->match_flow_table) {
> > > + ovn_flow_destroy(f);
> > > + }
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_table_destroy(struct ovn_flow_table *flow_table)
> > > +{
> > > + ovn_flow_table_clear(flow_table);
> > > + hmap_destroy(&flow_table->match_flow_table);
> > > + hmap_destroy(&flow_table->uuid_action_flow_table);
> > > +}
> > > +
> > > +static uint32_t
> > > +ovn_flow_match_hash__(uint8_t table_id, uint16_t priority,
> > > + const struct minimatch *match)
> > > +{
> > > + return hash_2words((table_id << 16) | priority,
> > > + minimatch_hash(match, 0));
> > > +}
> > > +
> > > +/* Returns a hash of the match key in 'f'. */
> > > +static uint32_t
> > > +ovn_flow_match_hash(const struct ovn_flow *f)
> > > +{
> > > + return ovn_flow_match_hash__(f->table_id, f->priority,
&f->match);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_action_init(struct ovn_flow_action *a, const struct ofpbuf
*actions,
> > > + uint64_t cookie, const struct uuid *flow_uuid)
> > > +{
> > > + a->ofpacts = xmemdup(actions->data, actions->size);
> > > + a->ofpacts_len = actions->size;
> > > + a->cookie = cookie;
> > > + a->flow_uuid = *flow_uuid;
> > > + a->stale = false;
> > > + ovs_list_init(&a->list_node);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_action_destroy(struct ovn_flow_action *a)
> > > +{
> > > + free(a->ofpacts);
> > > + free(a);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_init(struct ovn_flow *f, uint8_t table_id, uint16_t
priority,
> > > + const struct match *match)
> > > +{
> > > + f->table_id = table_id;
> > > + f->priority = priority;
> > > + minimatch_init(&f->match, match);
> > > +
> > > + f->hash = ovn_flow_match_hash(f);
> > > +
> > > + ovs_list_init(&f->allow_act_list);
> > > + ovs_list_init(&f->drop_act_list);
> > > + ovs_list_init(&f->conj_act_list);
> > > +
> > > + f->installed_ofpacts = NULL;
> > > + f->installed_ofpacts_len = 0;
> > > +
> > > + f->active_cookie = 0;
> > > +}
> > > +
> > > +static struct ovn_flow *
> > > +ovn_flow_alloc(uint8_t table_id, uint16_t priority,
> > > + const struct match *match)
> > > +{
> > > + struct ovn_flow *f = xzalloc(sizeof *f);
> > > + ovn_flow_init(f, table_id, priority, match);
> > > +
> > > + return f;
> > > +}
> > > +
> > > +static struct ovn_flow_action *
> > > +ovn_flow_action_alloc(const struct ofpbuf *actions,
> > > + uint64_t cookie, const struct uuid *flow_uuid)
> > > +{
> > > + struct ovn_flow_action *a = xzalloc(sizeof *a);
> > > + ovn_flow_action_init(a, actions, cookie, flow_uuid);
> > > +
> > > + return a;
> > > +}
> > > +
> > > +/* Finds and returns a 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 ovn_flow_table *flow_table,
> > > + uint8_t table_id, uint16_t priority,
> > > + const struct minimatch *match)
> > > +{
> > > + size_t hash = ovn_flow_match_hash__(table_id, priority, match);
> > > +
> > > + struct ovn_flow *f;
> > > + HMAP_FOR_EACH_WITH_HASH (f, hmap_node, hash,
> > > + &flow_table->match_flow_table) {
> > > + if (f->priority == priority && minimatch_equal(&f->match,
match)) {
> > > + return f;
> > > + }
> > > + }
> > > +
> > > + return NULL;
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_clear_actions_from_list(struct ovs_list *act_list)
> > > +{
> > > + struct ovn_flow_action *a;
> > > + LIST_FOR_EACH_POP (a, list_node, act_list) {
> > > + ovs_list_remove(&a->flow_uuid_action_node);
> > > + ovn_flow_action_destroy(a);
> > > + }
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_invalidate_actions_from_list(struct ovs_list *act_list)
> > > +{
> > > + struct ovn_flow_action *a;
> > > + LIST_FOR_EACH (a, list_node, act_list) {
> > > + a->stale = true;
> > > + }
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_clear_actions(struct ovn_flow *f)
> > > +{
> > > + if (f->normal_act) {
> > > + ovs_list_remove(&f->normal_act->flow_uuid_action_node);
> > > + ovn_flow_action_destroy(f->normal_act);
> > > + f->normal_act = NULL;
> > > + }
> > > +
> > > + ovn_flow_clear_actions_from_list(&f->allow_act_list);
> > > + ovn_flow_clear_actions_from_list(&f->drop_act_list);
> > > + ovn_flow_clear_actions_from_list(&f->conj_act_list);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_invalidate_all_actions(struct ovn_flow *f)
> > > +{
> > > + if (f->normal_act) {
> > > + f->normal_act->stale = true;
> > > + }
> > > +
> > > + ovn_flow_invalidate_actions_from_list(&f->allow_act_list);
> > > + ovn_flow_invalidate_actions_from_list(&f->drop_act_list);
> > > + ovn_flow_invalidate_actions_from_list(&f->conj_act_list);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_delete_stale_actions_from_list(struct ovs_list *act_list)
> > > +{
> > > + struct ovn_flow_action *a, *next;
> > > + LIST_FOR_EACH_SAFE (a, next, list_node, act_list) {
> > > + if (a->stale) {
> > > + ovn_flow_unref_action(a->flow, a);
> > > + }
> > > + }
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_delete_stale_actions(struct ovn_flow *f)
> > > +{
> > > + if (f->normal_act && f->normal_act->stale) {
> > > + ovn_flow_unref_action(f, f->normal_act);
> > > + f->normal_act = NULL;
> > > + }
> > > +
> > > + ovn_flow_delete_stale_actions_from_list(&f->allow_act_list);
> > > + ovn_flow_delete_stale_actions_from_list(&f->drop_act_list);
> > > + ovn_flow_delete_stale_actions_from_list(&f->conj_act_list);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_uninit(struct ovn_flow *f)
> > > +{
> > > + minimatch_destroy(&f->match);
> > > + ovn_flow_clear_actions(f);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_destroy(struct ovn_flow *f)
> > > +{
> > > + if (f) {
> > > + ovn_flow_uninit(f);
> > > + free(f->installed_ofpacts);
> > > + free(f);
> > > + }
> > > +}
> > > +
> > > +static bool
> > > +ovn_flow_action_is_drop(const struct ovn_flow_action *f)
> > > +{
> > > + return f->ofpacts_len == 0;
> > > +}
> > > +
> > > +static bool
> > > +ovn_flow_action_is_conj(const struct ovn_flow_action *f)
> > > +{
> > > + const struct ofpact *a = NULL;
> > > +
> > > + OFPACT_FOR_EACH (a, f->ofpacts, f->ofpacts_len) {
> > > + if (a->type == OFPACT_CONJUNCTION) {
> > > + return true;
> > > + }
> > > + }
> > > + return false;
> > > +}
> > > +
> > > +static bool
> > > +ovn_flow_action_is_allow(const struct ovn_flow_action *f)
> > > +{
> > > + if (ovn_flow_action_is_drop(f)) {
> > > + return false;
> > > + }
> > > +
> > > + const struct ofpact *a = f->ofpacts;
> > > + return (a->type == OFPACT_RESUBMIT &&
> > > + ofpact_last(a, f->ofpacts, f->ofpacts_len));
> > > +}
> > > +
> > > +static bool
> > > +ovn_flow_action_is_normal(const struct ovn_flow_action *f)
> > > +{
> > > + return (!ovn_flow_action_is_allow(f) &&
!ovn_flow_action_is_drop(f) &&
> > > + !ovn_flow_action_is_conj(f));
> > > +}
> > > +
> > > +static struct ovn_flow_action *
> > > +ovn_flow_action_get_matching_in_list(struct ovs_list *act_list,
> > > + struct ovn_flow_action *a)
> > > +{
> > > + struct ovn_flow_action *act_in_list;
> > > + LIST_FOR_EACH (act_in_list, list_node, act_list) {
> > > + if (ofpacts_equal(act_in_list->ofpacts,
> > > + act_in_list->ofpacts_len,
> > > + a->ofpacts,
> > > + a->ofpacts_len)) {
> > > + return act_in_list;
> > > + }
> > > + }
> > > +
> > > + return NULL;
> > > +}
> > > +
> > > +static struct ovn_flow_action *
> > > +ovn_flow_get_matching_action(struct ovn_flow *f, struct
ovn_flow_action *a)
> > > +{
> > > + if (f->normal_act && ovn_flow_action_is_normal(a)) {
> > > + if (ofpacts_equal(f->normal_act->ofpacts,
> > > + f->normal_act->ofpacts_len,
> > > + a->ofpacts,
> > > + a->ofpacts_len)) {
> > > + return f->normal_act;
> > > + }
> > > + }
> > > +
> > > + if (ovn_flow_action_is_allow(a)) {
> > > + return
ovn_flow_action_get_matching_in_list(&f->allow_act_list, a);
> > > + }
> > > +
> > > + if (ovn_flow_action_is_drop(a)) {
> > > + return
ovn_flow_action_get_matching_in_list(&f->drop_act_list, a);
> > > + }
> > > +
> > > + if (ovn_flow_action_is_conj(a)) {
> > > + return
ovn_flow_action_get_matching_in_list(&f->conj_act_list, a);
> > > + }
> > > +
> > > + return NULL;
> > > +}
> > > +
> > > +static bool
> > > +ovn_flow_has_active_actions(struct ovn_flow *f)
> > > +{
> > > + if (f->normal_act) {
> > > + return true;
> > > + }
> > > +
> > > + if (!ovs_list_is_empty(&f->allow_act_list)) {
> > > + return true;
> > > + }
> > > +
> > > + if (!ovs_list_is_empty(&f->drop_act_list)) {
> > > + return true;
> > > + }
> > > +
> > > + return !ovs_list_is_empty(&f->conj_act_list);
> > > +}
> > > +
> > > +static struct flow_uuid_to_acts *
> > > +flow_uuid_to_acts_lookup(struct hmap *uuid_action_table,
> > > + const struct uuid *flow_uuid)
> > > +{
> > > + struct flow_uuid_to_acts *f_uuid_to_acts;
> > > + HMAP_FOR_EACH_WITH_HASH (f_uuid_to_acts, hmap_node,
uuid_hash(flow_uuid),
> > > + uuid_action_table) {
> > > + if (uuid_equals(flow_uuid, &f_uuid_to_acts->flow_uuid)) {
> > > + return f_uuid_to_acts;
> > > + }
> > > + }
> > > + return NULL;
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_action_link_to_flow_uuid(struct hmap *uuid_action_table,
> > > + struct ovn_flow_action *a)
> > > +{
> > > + struct flow_uuid_to_acts *f_uuid_to_acts;
> > > + f_uuid_to_acts = flow_uuid_to_acts_lookup(uuid_action_table,
> > > + &a->flow_uuid);
> > > + if (!f_uuid_to_acts) {
> > > + f_uuid_to_acts = xzalloc(sizeof *f_uuid_to_acts);
> > > + f_uuid_to_acts->flow_uuid = a->flow_uuid;
> > > + ovs_list_init(&f_uuid_to_acts->acts);
> > > + hmap_insert(uuid_action_table, &f_uuid_to_acts->hmap_node,
> > > + uuid_hash(&a->flow_uuid));
> > > + }
> > > +
> > > + ovs_list_push_back(&f_uuid_to_acts->acts,
&a->flow_uuid_action_node);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_unref_action__(struct ovn_flow *f, struct ovn_flow_action
*a)
> > > +{
> > > + if (f->normal_act == a) {
> > > + f->normal_act = NULL;
> > > + } else if (!ovs_list_is_empty(&a->list_node)) {
> > > + ovs_list_remove(&a->list_node);
> > > + }
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_unref_action(struct ovn_flow *f, struct ovn_flow_action *a)
> > > +{
> > > + ovs_list_remove(&a->flow_uuid_action_node);
> > > + ovn_flow_unref_action__(f, a);
> > > + ovn_flow_action_destroy(a);
> > > +}
> > > +
> > > +static bool
> > > +ovn_flow_update_actions(struct ovn_flow_table *ftable,
> > > + struct ovn_flow *f, struct ovn_flow_action
*a)
> > > +{
> > > + bool flow_updated = true;
> > > + if (ovn_flow_action_is_normal(a)) {
> > > + if (f->normal_act) {
> > > + ovn_flow_unref_action(f, f->normal_act);
> > > + }
> > > + f->normal_act = a;
> > > + } else if (ovn_flow_action_is_allow(a)) {
> > > + flow_updated = ovs_list_is_empty(&f->allow_act_list);
> >
> > If I understand correctly, these lists allow_act_list, drop_act_list,
etc. are to handle conflict flow additions that have the same match but
different actions. So, if there is already an existing allow action, here
we are adding another to the list, should this flow be considered as
updated? But with the above logic, the flow_updated would be false.
>
> We don't need to consider it as updated. Let's say we have 2 Logical
> flows L1 and L2 which both result in the same openflow match and have
> the allow (or next;) action.
> When L2 is created after L1, we don't have to update the flow in the
> switch because the openflow already exists. This code stores the
> "allow" action of L2 in the F's allow_act_list.
> Later if L1 is deleted, we still want to have the same openflow rule
> present because of L2.
>
I see. So this is just an optimization so that in such cases it is not
considered as updated so that we don't need to call handled_modified.
However, I just noticed that the return value of this function is never
checked, unfortunately, probably a miss?
In addition, I think there is another situation that may end up with
inconsistency between the desired and the actual flows. If there are two
flows with the same match but different "normal" actions: flow1 -> action1,
flow2 -> action2. For normal actions, only one of them is saved. So if
flow2 is added after flow1, action1 is replaced by action2 for the match.
Then if flow2 is deleted, action2 is destroyed and the flow doesn't have
any actions so it will be destroyed. But in fact flow1 still exists in the
DB and we should honor action1 here. Correct?
Regards,
Han
> Let me know if you've any further questions.
>
> Numan
>
> >
> > Best regards,
> > Han
> >
> > > + ovs_list_push_back(&f->allow_act_list, &a->list_node);
> > > + } else if (ovn_flow_action_is_drop(a)) {
> > > + flow_updated = ovs_list_is_empty(&f->drop_act_list);
> > > + ovs_list_push_back(&f->drop_act_list, &a->list_node);
> > > + } else {
> > > + /* conj action. */
> > > + ovs_list_push_back(&f->conj_act_list, &a->list_node);
> > > + }
> > > +
> > > + a->flow = f;
> > > + a->stale = false;
> > > +
> > > +
ovn_flow_action_link_to_flow_uuid(&ftable->uuid_action_flow_table, a);
> > > +
> > > + return flow_updated;
> > > +}
> > > +
> > > +static bool
> > > +ovn_flow_needs_flow_mod(struct ovn_flow *f, struct ovn_flow_action
*a)
> > > +{
> > > + if (f->installed_ofpacts_len != a->ofpacts_len) {
> > > + return true;
> > > + }
> > > +
> > > + if (f->installed_ofpacts_len && a->ofpacts_len) {
> > > + return !ofpacts_equal(f->installed_ofpacts,
> > > + f->installed_ofpacts_len,
> > > + a->ofpacts,
> > > + a->ofpacts_len);
> > > + }
> > > +
> > > + return f->active_cookie ? false : true;
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_prepare_ofmsg(struct ovn_flow *f, struct ovs_list *msgs)
> > > +{
> > > + struct ovn_flow_action *a = NULL;
> > > + struct ovn_flow_action *conj_act = NULL;
> > > +
> > > + if (f->normal_act) {
> > > + a = f->normal_act;
> > > + } else if (!ovs_list_is_empty(&f->allow_act_list)) {
> > > + a = CONTAINER_OF(ovs_list_front(&f->allow_act_list),
> > > + struct ovn_flow_action,
> > > + list_node);
> > > + } else if (!ovs_list_is_empty(&f->drop_act_list)) {
> > > + a = CONTAINER_OF(ovs_list_front(&f->drop_act_list),
> > > + struct ovn_flow_action,
> > > + list_node);
> > > + } else if (!ovs_list_is_empty(&f->conj_act_list)) {
> > > + struct ovn_flow_action *c;
> > > + uint64_t ofpacts_stub[1024 / 8];
> > > + struct ofpbuf ofpacts =
OFPBUF_STUB_INITIALIZER(ofpacts_stub);
> > > +
> > > + LIST_FOR_EACH (c, list_node, &f->conj_act_list) {
> > > + ofpbuf_put(&ofpacts, c->ofpacts, c->ofpacts_len);
> > > + }
> > > + conj_act = xzalloc(sizeof *conj_act);
> > > + conj_act->cookie = CONJ_ACT_COOKIE;
> > > + conj_act->ofpacts = xmemdup(ofpacts.data, ofpacts.size);
> > > + conj_act->ofpacts_len = ofpacts.size;
> > > + ofpbuf_uninit(&ofpacts);
> > > + a = conj_act;
> > > + }
> > > +
> > > + if (a) {
> > > + /* check if there is really a need to install or not. */
> > > + if (ovn_flow_needs_flow_mod(f, a)) {
> > > + if (f->active_cookie == 0) {
> > > + ovn_flow_add_oflow(f, a, msgs);
> > > + } else {
> > > + ovn_flow_mod_oflow(f, a, msgs);
> > > + }
> > > + free(f->installed_ofpacts);
> > > + if (a->ofpacts_len) {
> > > + f->installed_ofpacts = xmemdup(a->ofpacts,
a->ofpacts_len);
> > > + f->installed_ofpacts_len = a->ofpacts_len;
> > > + } else {
> > > + f->installed_ofpacts = NULL;
> > > + f->installed_ofpacts_len = 0;
> > > + }
> > > + }
> > > + } else {
> > > + ovn_flow_del_oflow(f, msgs);
> > > + free(f->installed_ofpacts);
> > > + f->installed_ofpacts = NULL;
> > > + f->installed_ofpacts_len = 0;
> > > + f->active_cookie = 0;
> > > + }
> > > +
> > > + if (conj_act) {
> > > + free(conj_act->ofpacts);
> > > + free(conj_act);
> > > + }
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_handle_modified(struct ovn_flow *f, struct ovs_list *msgs)
> > > +{
> > > + ovn_flow_delete_stale_actions(f);
> > > + ovn_flow_prepare_ofmsg(f, msgs);
> > > + if (!ovn_flow_has_active_actions(f)) {
> > > + hmap_remove(f->match_flow_table, &f->hmap_node);
> > > + ovs_list_remove(&f->flow_list_node);
> > > + ovn_flow_destroy(f);
> > > + }
> > > +}
> > > +
> > > +static void
> > > +br_flow_populate_oflow_msgs__(struct br_flow_table *br_ftable,
> > > + struct ovs_list *msgs)
> > > +{
> > > + struct ovn_flow *f;
> > > +
> > > + LIST_FOR_EACH_POP (f, flow_list_node, br_ftable->old_lflows) {
> > > + ovn_flow_clear_actions(f);
> > > + ovn_flow_prepare_ofmsg(f, msgs);
> > > + hmap_remove(f->match_flow_table, &f->hmap_node);
> > > + hmapx_find_and_delete(&br_ftable->modified_lflows, f);
> > > + ovn_flow_destroy(f);
> > > + }
> > > +
> > > + LIST_FOR_EACH_POP (f, flow_list_node, br_ftable->old_pflows) {
> > > + ovn_flow_clear_actions(f);
> > > + ovn_flow_prepare_ofmsg(f, msgs);
> > > + hmap_remove(f->match_flow_table, &f->hmap_node);
> > > + hmapx_find_and_delete(&br_ftable->modified_pflows, f);
> > > + ovn_flow_destroy(f);
> > > + }
> > > +
> > > + struct hmapx_node *node;
> > > + HMAPX_FOR_EACH (node, &br_ftable->modified_lflows) {
> > > + f = node->data;
> > > + ovn_flow_handle_modified(f, msgs);
> > > + }
> > > +
> > > + hmapx_clear(&br_ftable->modified_lflows);
> > > +
> > > + HMAPX_FOR_EACH (node, &br_ftable->modified_pflows) {
> > > + f = node->data;
> > > + ovn_flow_handle_modified(f, msgs);
> > > + }
> > > +
> > > + hmapx_clear(&br_ftable->modified_pflows);
> > > +}
> > > +
> > > +/* Flow table update. */
> > > +
> > > +static struct ofpbuf *
> > > +encode_flow_mod(struct ofputil_flow_mod *fm)
> > > +{
> > > + fm->buffer_id = UINT32_MAX;
> > > + fm->out_port = OFPP_ANY;
> > > + fm->out_group = OFPG_ANY;
> > > + return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF15_OXM);
> > > +}
> > > +
> > > +static void
> > > +add_flow_mod(struct ofputil_flow_mod *fm, struct ovs_list *msgs)
> > > +{
> > > + struct ofpbuf *msg = encode_flow_mod(fm);
> > > + ovs_list_push_back(msgs, &msg->list_node);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_add_oflow(struct ovn_flow *f, struct ovn_flow_action *a,
> > > + struct ovs_list *msgs)
> > > +{
> > > + /* Send flow_mod to add flow. */
> > > + struct ofputil_flow_mod fm = {
> > > + .match = f->match,
> > > + .priority = f->priority,
> > > + .table_id = f->table_id,
> > > + .ofpacts = a->ofpacts,
> > > + .ofpacts_len = a->ofpacts_len,
> > > + .new_cookie = htonll(a->cookie),
> > > + .command = OFPFC_ADD,
> > > + };
> > > + add_flow_mod(&fm, msgs);
> > > + f->active_cookie = a->cookie;
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_mod_oflow(struct ovn_flow *f, struct ovn_flow_action *a,
> > > + struct ovs_list *msgs)
> > > +{
> > > + /* Update actions in installed flow. */
> > > + struct ofputil_flow_mod fm = {
> > > + .match = f->match,
> > > + .priority = f->priority,
> > > + .table_id = f->table_id,
> > > + .ofpacts = a->ofpacts,
> > > + .ofpacts_len = a->ofpacts_len,
> > > + .command = OFPFC_MODIFY_STRICT,
> > > + };
> > > + /* Update cookie if it is changed. */
> > > + if (f->active_cookie != a->cookie) {
> > > + fm.modify_cookie = true;
> > > + fm.new_cookie = htonll(a->cookie);
> > > + /* Use OFPFC_ADD so that cookie can be updated. */
> > > + fm.command = OFPFC_ADD;
> > > + }
> > > + add_flow_mod(&fm, msgs);
> > > + f->active_cookie = a->cookie;
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_del_oflow(struct ovn_flow *f, struct ovs_list *msgs)
> > > +{
> > > + struct ofputil_flow_mod fm = {
> > > + .match = f->match,
> > > + .priority = f->priority,
> > > + .table_id = f->table_id,
> > > + .command = OFPFC_DELETE_STRICT,
> > > + };
> > > + add_flow_mod(&fm, msgs);
> > > + f->active_cookie = 0;
> > > +}
> > > +
> > > +static char *
> > > +ovn_flow_to_string(const struct ovn_flow *f)
> > > +{
> > > + struct ds s = DS_EMPTY_INITIALIZER;
> > > +
> > > + struct ovn_flow_action *a = NULL;
> > > + struct ovn_flow_action *conj_act = NULL;
> > > +
> > > + if (f->normal_act) {
> > > + a = f->normal_act;
> > > + } else if (!ovs_list_is_empty(&f->allow_act_list)) {
> > > + a = CONTAINER_OF(ovs_list_front(&f->allow_act_list),
> > > + struct ovn_flow_action,
> > > + list_node);
> > > + } else if (!ovs_list_is_empty(&f->drop_act_list)) {
> > > + a = CONTAINER_OF(ovs_list_front(&f->drop_act_list),
> > > + struct ovn_flow_action,
> > > + list_node);
> > > + } else if (!ovs_list_is_empty(&f->conj_act_list)) {
> > > + struct ovn_flow_action *c;
> > > + uint64_t ofpacts_stub[1024 / 8];
> > > + struct ofpbuf ofpacts =
OFPBUF_STUB_INITIALIZER(ofpacts_stub);
> > > +
> > > + LIST_FOR_EACH (c, list_node, &f->conj_act_list) {
> > > + ofpbuf_put(&ofpacts, c->ofpacts, c->ofpacts_len);
> > > + }
> > > + conj_act = xzalloc(sizeof *conj_act);
> > > + conj_act->cookie = CONJ_ACT_COOKIE;
> > > + conj_act->ofpacts = xmemdup(ofpacts.data, ofpacts.size);
> > > + conj_act->ofpacts_len = ofpacts.size;
> > > + ofpbuf_uninit(&ofpacts);
> > > + a = conj_act;
> > > + }
> > > +
> > > + if (a) {
> > > + ds_put_format(&s, "cookie=%"PRIx64", ", a->cookie);
> > > + }
> > > + 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);
> > > + ds_put_cstr(&s, ", actions=");
> > > + struct ofpact_format_params fp = { .s = &s };
> > > + if (a) {
> > > + ofpacts_format(a->ofpacts, a->ofpacts_len, &fp);
> > > + } else {
> > > + ds_put_cstr(&s, "<EMPTY>");
> > > + }
> > > +
> > > + if (conj_act) {
> > > + free(conj_act->ofpacts);
> > > + free(conj_act);
> > > + }
> > > + return ds_steal_cstr(&s);
> > > +}
> > > +
> > > +static void
> > > +ovn_flow_log(const struct ovn_flow *f)
> > > +{
> > > + if (VLOG_IS_DBG_ENABLED()) {
> > > + char *s = ovn_flow_to_string(f);
> > > + VLOG_DBG("flow: %s", s);
> > > + free(s);
> > > + }
> > > +}
> > > diff --git a/br-controller/br-flow-mgr.h b/br-controller/br-flow-mgr.h
> > > new file mode 100644
> > > index 0000000000..68545941a9
> > > --- /dev/null
> > > +++ b/br-controller/br-flow-mgr.h
> > > @@ -0,0 +1,65 @@
> > > +/*
> > > + * Licensed under the Apache License, Version 2.0 (the "License");
> > > + * you may not use this file except in compliance with the License.
> > > + * You may obtain a copy of the License at:
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
software
> > > + * distributed under the License is distributed on an "AS IS" BASIS,
> > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
> > > + * See the License for the specific language governing permissions
and
> > > + * limitations under the License.
> > > + */
> > > +
> > > +#ifndef BR_FLOW_MGR_H
> > > +#define BR_FLOW_MGR_H 1
> > > +
> > > +#include <stdio.h>
> > > +
> > > +/* OVS includes. */
> > > +#include "openvswitch/hmap.h"
> > > +#include "openvswitch/list.h"
> > > +
> > > +/* OVN includes. */
> > > +
> > > +struct match;
> > > +struct ofpbuf;
> > > +struct uuid;
> > > +
> > > +#define DP_FLOW_TABLE_GLOBAL_KEY 0
> > > +
> > > +void br_flow_tables_init(void);
> > > +void br_flow_tables_destroy(void);
> > > +
> > > +struct br_flow_table *br_flow_table_alloc(const char *bridge);
> > > +struct br_flow_table *br_flow_table_get(const char *bridge);
> > > +void br_flow_table_destroy(const char *bridge);
> > > +
> > > +void br_flow_switch_logical_oflow_tables(void);
> > > +void br_flow_switch_logical_oflow_table(const char *bridge);
> > > +void br_flow_switch_physical_oflow_tables(void);
> > > +void br_flow_switch_physical_oflow_table(const char *bridge);
> > > +
> > > +void br_flow_add_logical_oflow(const char *bridge, uint8_t table_id,
> > > + uint16_t priority, uint64_t cookie,
> > > + const struct match *match,
> > > + const struct ofpbuf *actions,
> > > + const struct uuid *flow_uuid);
> > > +void br_flow_add_physical_oflow(const char *bridge, uint8_t table_id,
> > > + uint16_t priority, uint64_t cookie,
> > > + const struct match *match,
> > > + const struct ofpbuf *actions,
> > > + const struct uuid *flow_uuid);
> > > +
> > > +void br_flow_remove_logical_oflows_all(const struct uuid *flow_uuid);
> > > +void br_flow_remove_logical_oflows(const char *bridge,
> > > + const struct uuid *flow_uuid);
> > > +void br_flow_remove_physical_oflows(const char *bridge,
> > > + const struct uuid *flow_uuid);
> > > +void br_flow_flush_oflows(const char *bridge);
> > > +void br_flow_flush_all_oflows(void);
> > > +
> > > +void br_flow_populate_oflow_msgs(const char *bridge, struct ovs_list
*msgs);
> > > +
> > > +#endif /* BR_FLOW_MGR_H */
> > > --
> > > 2.51.0
> > >
> > > _______________________________________________
> > > dev mailing list
> > > [email protected]
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev