diff mbox series

[ovs-dev,2/3] northd.c: Maintain links between ovn_port and lflow.

Message ID 20230618061755.3962521-3-hzhou@ovn.org
State Accepted
Headers show
Series ovn-northd incremental processing for VIF udpates and deletions end-to-end. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes fail github build: failed

Commit Message

Han Zhou June 18, 2023, 6:17 a.m. UTC
For incremental processing, it is important to maintain relationship
between the inputs and the logical flows generated. This patch creates
the links between ovn_port and logical flows. The same data structure
may be expanded to maintain links between logical flows and other types
of inputs.

This patch also refactors the temp_lflow_list operations to
collected_lflows with helper functions to start and end collecting. It
still uses global variables just to avoid updating all the lflow_add_...
related code all over the northd.c file.

Signed-off-by: Han Zhou <hzhou@ovn.org>
---
 northd/northd.c | 271 +++++++++++++++++++++++++++++++-----------------
 1 file changed, 178 insertions(+), 93 deletions(-)

Comments

Numan Siddique June 26, 2023, 2:25 p.m. UTC | #1
On Sun, Jun 18, 2023 at 11:48 AM Han Zhou <hzhou@ovn.org> wrote:
>
> For incremental processing, it is important to maintain relationship
> between the inputs and the logical flows generated. This patch creates
> the links between ovn_port and logical flows. The same data structure
> may be expanded to maintain links between logical flows and other types
> of inputs.
>
> This patch also refactors the temp_lflow_list operations to
> collected_lflows with helper functions to start and end collecting. It
> still uses global variables just to avoid updating all the lflow_add_...
> related code all over the northd.c file.
>
> Signed-off-by: Han Zhou <hzhou@ovn.org>

Hi Han,

Please see a few comments below.  I did review all the 3 patches in the series.
They LGTM overall.  I'd like to do some more testing before providing my Acks.


> ---
>  northd/northd.c | 271 +++++++++++++++++++++++++++++++-----------------
>  1 file changed, 178 insertions(+), 93 deletions(-)
>
> diff --git a/northd/northd.c b/northd/northd.c
> index 98f528f93cfc..aa0f853ce2db 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -1457,6 +1457,19 @@ struct ovn_port_routable_addresses {
>      size_t n_addrs;
>  };
>
> +/* A node that maintains link between an object (such as an ovn_port) and
> + * a lflow. */
> +struct lflow_ref_node {
> +    /* This list follows different lflows referenced by the same object. List
> +     * head is, for example, ovn_port->lflows.  */
> +    struct ovs_list lflow_list_node;
> +    /* This list follows different objects that reference the same lflow. List
> +     * head is ovn_lflow->referenced_by. */
> +    struct ovs_list ref_list_node;
> +    /* The lflow. */
> +    struct ovn_lflow *lflow;
> +};
> +
>  /* A logical switch port or logical router port.
>   *
>   * In steady state, an ovn_port points to a northbound Logical_Switch_Port
> @@ -1548,6 +1561,28 @@ struct ovn_port {
>
>      /* Temporarily used for traversing a list (or hmap) of ports. */
>      bool visited;
> +
> +    /* List of struct lflow_ref_node that points to the lflows generated by
> +     * this ovn_port.
> +     *
> +     * This data is initialized and destroyed by the en_northd node, but
> +     * populated and used only by the en_lflow node. Ideally this data should
> +     * be maintained as part of en_lflow's data (struct lflow_data): a hash
> +     * index from ovn_port key to lflows.  However, it would be less efficient
> +     * and more complex:
> +     *
> +     * 1. It would require an extra search (using the index) to find the
> +     * lflows.
> +     *
> +     * 2. Building the index needs to be thread-safe, using either a global
> +     * lock which is obviously less efficient, or hash-based lock array which
> +     * is more complex.
> +     *
> +     * Adding the list here is more straightforward. The drawback is that we
> +     * need to keep in mind that this data belongs to en_lflow node, so never
> +     * access it from any other nodes.
> +     */
> +    struct ovs_list lflows;
>  };


>
>  static bool lsp_can_be_inc_processed(const struct nbrec_logical_switch_port *);
> @@ -1635,6 +1670,8 @@ ovn_port_create(struct hmap *ports, const char *key,
>      ovn_port_set_nb(op, nbsp, nbrp);
>      op->l3dgw_port = op->cr_port = NULL;
>      hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
> +
> +    ovs_list_init(&op->lflows);
>      return op;
>  }
>
> @@ -1665,6 +1702,13 @@ ovn_port_destroy_orphan(struct ovn_port *port)
>      destroy_lport_addresses(&port->proxy_arp_addrs);
>      free(port->json_key);
>      free(port->key);
> +
> +    struct lflow_ref_node *l;
> +    LIST_FOR_EACH_SAFE (l, lflow_list_node, &port->lflows) {
> +        ovs_list_remove(&l->lflow_list_node);
> +        ovs_list_remove(&l->ref_list_node);
> +        free(l);
> +    }
>      free(port);
>  }
>
> @@ -4893,6 +4937,7 @@ static struct ovn_port *
>  ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports,
>                 const char *key, const struct nbrec_logical_switch_port *nbsp,
>                 struct ovn_datapath *od, const struct sbrec_port_binding *sb,
> +               struct ovs_list *lflows,
>                 const struct sbrec_mirror_table *sbrec_mirror_table,
>                 const struct sbrec_chassis_table *sbrec_chassis_table,
>                 struct ovsdb_idl_index *sbrec_chassis_by_name,
> @@ -4903,6 +4948,9 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports,
>      parse_lsp_addrs(op);
>      op->od = od;
>      hmap_insert(&od->ports, &op->dp_node, hmap_node_hash(&op->key_node));
> +    if (lflows) {
> +        ovs_list_splice(&op->lflows, lflows->next, lflows);
> +    }
>
>      /* Assign explicitly requested tunnel ids first. */
>      if (!ovn_port_assign_requested_tnl_id(sbrec_chassis_table, op)) {
> @@ -5082,7 +5130,7 @@ northd_handle_ls_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
>                      goto fail;
>                  }
>                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> -                                    new_nbsp->name, new_nbsp, od, NULL,
> +                                    new_nbsp->name, new_nbsp, od, NULL, NULL,
>                                      ni->sbrec_mirror_table,
>                                      ni->sbrec_chassis_table,
>                                      ni->sbrec_chassis_by_name,
> @@ -5114,13 +5162,16 @@ northd_handle_ls_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
>                      op->visited = true;
>                      continue;
>                  }
> +                struct ovs_list lflows = OVS_LIST_INITIALIZER(&lflows);
> +                ovs_list_splice(&lflows, op->lflows.next, &op->lflows);
>                  ovn_port_destroy(&nd->ls_ports, op);
>                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> -                                    new_nbsp->name, new_nbsp, od, sb,
> +                                    new_nbsp->name, new_nbsp, od, sb, &lflows,
>                                      ni->sbrec_mirror_table,
>                                      ni->sbrec_chassis_table,
>                                      ni->sbrec_chassis_by_name,
>                                      ni->sbrec_chassis_by_hostname);
> +                ovs_assert(ovs_list_is_empty(&lflows));
>                  if (!op) {
>                      goto fail;
>                  }
> @@ -5577,7 +5628,8 @@ ovn_igmp_group_destroy(struct hmap *igmp_groups,
>
>  struct ovn_lflow {
>      struct hmap_node hmap_node;
> -    struct ovs_list list_node;
> +    struct ovs_list list_node;   /* For temporary list of lflows. Don't remove
> +                                    at destroy. */
>
>      struct ovn_datapath *od;     /* 'logical_datapath' in SB schema.  */
>      unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by their 'index'.*/
> @@ -5591,6 +5643,8 @@ struct ovn_lflow {
>      size_t n_ods;                /* Number of datapaths referenced by 'od' and
>                                    * 'dpg_bitmap'. */
>      struct ovn_dp_group *dpg;    /* Link to unique Sb datapath group. */
> +
> +    struct ovs_list referenced_by;  /* List of struct lflow_ref_node. */
>      const char *where;
>
>      struct uuid sb_uuid;            /* SB DB row uuid, specified by northd. */
> @@ -5640,6 +5694,7 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
>                 char *stage_hint, const char *where)
>  {
>      ovs_list_init(&lflow->list_node);
> +    ovs_list_init(&lflow->referenced_by);
>      lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
>      lflow->od = od;
>      lflow->stage = stage;
> @@ -5767,20 +5822,30 @@ ovn_dp_group_add_with_reference(struct ovn_lflow *lflow_ref,
>      }
>  }
>
> +/* This global variable collects the lflows generated by do_ovn_lflow_add().
> + * start_collecting_lflows() will enable the lflow collection and the calls to
> + * do_ovn_lflow_add (or the macros ovn_lflow_add_...) will add generated lflows
> + * to the list. end_collecting_lflows() will disable it. */
> +static thread_local struct ovs_list collected_lflows;
> +static thread_local bool collecting_lflows = false;
> +
> +static void
> +start_collecting_lflows(void)
> +{
> +    ovs_assert(!collecting_lflows);
> +    ovs_list_init(&collected_lflows);
> +    collecting_lflows = true;
> +}
> +
> +static void
> +end_collecting_lflows(void)
> +{
> +    ovs_assert(collecting_lflows);
> +    collecting_lflows = false;
> +}
> +

I think we can avoid these functions and the thread local variable
"collected_lflows".

I'd suggest the below

----------------------------

static void
do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
                 const unsigned long *dp_bitmap, size_t dp_bitmap_len,
                 uint32_t hash, enum ovn_stage stage, uint16_t priority,
                 const char *match, const char *actions, const char *io_port,
                 struct ovs_list *lflow_ref_list,
                 const struct ovsdb_idl_row *stage_hint,
                 const char *where, const char *ctrl_meter)
    OVS_REQUIRES(fake_hash_mutex)
{
    ...
    ...
    /* At the end. */
    if (lflow_ref_list) {
        struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
        lfrn->lflow = lflow;
        ovs_list_insert(lflow_ref_list, &lfrn->lflow_list_node);
        ovs_list_insert(&lflow->referenced_by, &lfrn->ref_list_node);
    }
}


#define ovn_lflow_add_with_lport_and_hint(LFLOW_MAP, OD, STAGE, PRIORITY, \
                                          MATCH, ACTIONS, IN_OUT_PORT, \
                                          LFLOW_REF_LIST, STAGE_HINT) \
    ovn_lflow_add_at(LFLOW_MAP, OD, NULL, 0, STAGE, PRIORITY, MATCH, ACTIONS, \
                     IN_OUT_PORT, LFLOW_REF_LIST, NULL, STAGE_HINT, \
                     OVS_SOURCE_LOCATOR)

And pass &op->lflows in ovn_lflow_add_with_lport_and_hint()

-----------------------------

What do you think ?  Definitely this would result in a bit more work
as it would require a lot of (tedious) changes.
I think this is a better approach.

Also I'm planning to work on top of your patches to add I-P for load
balancers in lflow engine (or perhaps I-P for datapath changes)

My idea is to have a lflow ref list stored in "struct ovn_datapath"
similar to the way you have done in this patch in "struct ovn_port"

And while adding the flows using one of the macro variants
'ovn_lflow_add*' pass &od->lflows.

Please let me know your comments.

Only concern I have with this patch is the "op->lflows" modified by
the lflow engine node.
But I agree with your added comments and also thinking to use the same
approach for datapath I-P handling,
And I don't have a better approach at the moment. So I'm fine with it.

Thanks
Numan


>  /* Adds a row with the specified contents to the Logical_Flow table.
> - * Version to use when hash bucket locking is NOT required.
> - *
> - * Note: This function can add generated lflows to the global variable
> - * temp_lflow_list as its output, controlled by the global variable
> - * add_lflow_to_temp_list. The caller of the ovn_lflow_add_... marcros can get
> - * a list of lflows generated by setting add_lflow_to_temp_list to true. The
> - * caller is responsible for initializing the temp_lflow_list, and also
> - * reset the add_lflow_to_temp_list to false when it is no longer needed.
> - * XXX: this mechanism is temporary and will be replaced when we add hash index
> - * to lflow_data and refactor related functions.
> - */
> -static bool add_lflow_to_temp_list = false;
> -static struct ovs_list temp_lflow_list;
> + * Version to use when hash bucket locking is NOT required. */
>  static void
>  do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
>                   const unsigned long *dp_bitmap, size_t dp_bitmap_len,
> @@ -5797,7 +5862,7 @@ do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
>      size_t bitmap_len = od ? ods_size(od->datapaths) : dp_bitmap_len;
>      ovs_assert(bitmap_len);
>
> -    if (add_lflow_to_temp_list) {
> +    if (collecting_lflows) {
>          ovs_assert(od);
>          ovs_assert(!dp_bitmap);
>      } else {
> @@ -5829,8 +5894,8 @@ do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
>          thread_lflow_counter++;
>      }
>
> -    if (add_lflow_to_temp_list) {
> -        ovs_list_insert(&temp_lflow_list, &lflow->list_node);
> +    if (collecting_lflows) {
> +        ovs_list_insert(&collected_lflows, &lflow->list_node);
>      }
>  }
>
> @@ -5950,10 +6015,28 @@ ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow)
>          free(lflow->io_port);
>          free(lflow->stage_hint);
>          free(lflow->ctrl_meter);
> +        struct lflow_ref_node *l;
> +        LIST_FOR_EACH_SAFE (l, ref_list_node, &lflow->referenced_by) {
> +            ovs_list_remove(&l->lflow_list_node);
> +            ovs_list_remove(&l->ref_list_node);
> +            free(l);
> +        }
>          free(lflow);
>      }
>  }
>
> +static void
> +link_ovn_port_to_lflows(struct ovn_port *op, struct ovs_list *lflows)
> +{
> +    struct ovn_lflow *f;
> +    LIST_FOR_EACH (f, list_node, lflows) {
> +        struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> +        lfrn->lflow = f;
> +        ovs_list_insert(&op->lflows, &lfrn->lflow_list_node);
> +        ovs_list_insert(&f->referenced_by, &lfrn->ref_list_node);
> +    }
> +}
> +
>  static bool
>  build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
>                      struct ds *options_action, struct ds *response_action,
> @@ -15483,6 +15566,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op,
>                                           struct hmap *lflows)
>  {
>      ovs_assert(op->nbsp);
> +    start_collecting_lflows();
>
>      /* Build Logical Switch Flows. */
>      build_lswitch_port_sec_op(op, lflows, actions, match);
> @@ -15497,6 +15581,9 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op,
>      /* Build Logical Router Flows. */
>      build_ip_routing_flows_for_router_type_lsp(op, lr_ports, lflows);
>      build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions);
> +
> +    link_ovn_port_to_lflows(op, &collected_lflows);
> +    end_collecting_lflows();
>  }
>
>  /* Helper function to combine all lflow generation which is iterated by logical
> @@ -16223,14 +16310,10 @@ bool lflow_handle_northd_ls_changes(struct ovsdb_idl_txn *ovnsb_txn,
>  {
>      struct ls_change *ls_change;
>      LIST_FOR_EACH (ls_change, list_node, &ls_changes->updated) {
> -        ovs_list_init(&temp_lflow_list);
> -        add_lflow_to_temp_list = true;
>          if (!ovs_list_is_empty(&ls_change->updated_ports) ||
>              !ovs_list_is_empty(&ls_change->deleted_ports)) {
>              /* XXX: implement lflow index so that we can handle updated and
>               * deleted LSPs incrementally. */
> -            ovs_list_init(&temp_lflow_list);
> -            add_lflow_to_temp_list = false;
>              return false;
>          }
>
> @@ -16277,83 +16360,85 @@ bool lflow_handle_northd_ls_changes(struct ovsdb_idl_txn *ovnsb_txn,
>                  sbrec_multicast_group_update_ports_addvalue(sbmc_unknown,
>                                                              op->sb);
>              }
> -        }
> -        /* Sync the newly added flows to SB. */
> -        struct ovn_lflow *lflow;
> -        LIST_FOR_EACH (lflow, list_node, &temp_lflow_list) {
> -            size_t n_datapaths;
> -            struct ovn_datapath **datapaths_array;
> -            if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH) {
> -                n_datapaths = ods_size(lflow_input->ls_datapaths);
> -                datapaths_array = lflow_input->ls_datapaths->array;
> -            } else {
> -                n_datapaths = ods_size(lflow_input->lr_datapaths);
> -                datapaths_array = lflow_input->lr_datapaths->array;
> -            }
> -            uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap, n_datapaths);
> -            ovs_assert(n_ods == 1);
> -            /* There is only one datapath, so it should be moved out of the
> -             * group to a single 'od'. */
> -            size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
> -                                       n_datapaths);
>
> -            bitmap_set0(lflow->dpg_bitmap, index);
> -            lflow->od = datapaths_array[index];
> -
> -            /* Logical flow should be re-hashed to allow lookups. */
> -            uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> -            /* Remove from lflows. */
> -            hmap_remove(lflows, &lflow->hmap_node);
> -            hash = ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid,
> -                                                  hash);
> -            /* Add back. */
> -            hmap_insert(lflows, &lflow->hmap_node, hash);
> -
> -            /* Sync to SB. */
> -            const struct sbrec_logical_flow *sbflow;
> -            lflow->sb_uuid = uuid_random();
> -            sbflow = sbrec_logical_flow_insert_persist_uuid(ovnsb_txn,
> -                                                            &lflow->sb_uuid);
> -            const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
> -            uint8_t table = ovn_stage_get_table(lflow->stage);
> -            sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
> -            sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
> -            sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> -            sbrec_logical_flow_set_table_id(sbflow, table);
> -            sbrec_logical_flow_set_priority(sbflow, lflow->priority);
> -            sbrec_logical_flow_set_match(sbflow, lflow->match);
> -            sbrec_logical_flow_set_actions(sbflow, lflow->actions);
> -            if (lflow->io_port) {
> -                struct smap tags = SMAP_INITIALIZER(&tags);
> -                smap_add(&tags, "in_out_port", lflow->io_port);
> -                sbrec_logical_flow_set_tags(sbflow, &tags);
> -                smap_destroy(&tags);
> -            }
> -            sbrec_logical_flow_set_controller_meter(sbflow, lflow->ctrl_meter);
> -            /* Trim the source locator lflow->where, which looks something like
> -             * "ovn/northd/northd.c:1234", down to just the part following the
> -             * last slash, e.g. "northd.c:1234". */
> -            const char *slash = strrchr(lflow->where, '/');
> +            /* Sync the newly added flows to SB. */
> +            struct lflow_ref_node *lfrn;
> +            LIST_FOR_EACH (lfrn, lflow_list_node, &op->lflows) {
> +                struct ovn_lflow *lflow = lfrn->lflow;
> +                size_t n_datapaths;
> +                struct ovn_datapath **datapaths_array;
> +                if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH) {
> +                    n_datapaths = ods_size(lflow_input->ls_datapaths);
> +                    datapaths_array = lflow_input->ls_datapaths->array;
> +                } else {
> +                    n_datapaths = ods_size(lflow_input->lr_datapaths);
> +                    datapaths_array = lflow_input->lr_datapaths->array;
> +                }
> +                uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap, n_datapaths);
> +                ovs_assert(n_ods == 1);
> +                /* There is only one datapath, so it should be moved out of the
> +                 * group to a single 'od'. */
> +                size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
> +                                           n_datapaths);
> +
> +                bitmap_set0(lflow->dpg_bitmap, index);
> +                lflow->od = datapaths_array[index];
> +
> +                /* Logical flow should be re-hashed to allow lookups. */
> +                uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> +                /* Remove from lflows. */
> +                hmap_remove(lflows, &lflow->hmap_node);
> +                hash = ovn_logical_flow_hash_datapath(
> +                                          &lflow->od->sb->header_.uuid, hash);
> +                /* Add back. */
> +                hmap_insert(lflows, &lflow->hmap_node, hash);
> +
> +                /* Sync to SB. */
> +                const struct sbrec_logical_flow *sbflow;
> +                lflow->sb_uuid = uuid_random();
> +                sbflow = sbrec_logical_flow_insert_persist_uuid(
> +                                                ovnsb_txn, &lflow->sb_uuid);
> +                const char *pipeline = ovn_stage_get_pipeline_name(
> +                                                               lflow->stage);
> +                uint8_t table = ovn_stage_get_table(lflow->stage);
> +                sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
> +                sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
> +                sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> +                sbrec_logical_flow_set_table_id(sbflow, table);
> +                sbrec_logical_flow_set_priority(sbflow, lflow->priority);
> +                sbrec_logical_flow_set_match(sbflow, lflow->match);
> +                sbrec_logical_flow_set_actions(sbflow, lflow->actions);
> +                if (lflow->io_port) {
> +                    struct smap tags = SMAP_INITIALIZER(&tags);
> +                    smap_add(&tags, "in_out_port", lflow->io_port);
> +                    sbrec_logical_flow_set_tags(sbflow, &tags);
> +                    smap_destroy(&tags);
> +                }
> +                sbrec_logical_flow_set_controller_meter(sbflow,
> +                                                        lflow->ctrl_meter);
> +                /* Trim the source locator lflow->where, which looks something
> +                 * like "ovn/northd/northd.c:1234", down to just the part
> +                 * following the last slash, e.g. "northd.c:1234". */
> +                const char *slash = strrchr(lflow->where, '/');
>  #if _WIN32
> -            const char *backslash = strrchr(lflow->where, '\\');
> -            if (!slash || backslash > slash) {
> -                slash = backslash;
> -            }
> +                const char *backslash = strrchr(lflow->where, '\\');
> +                if (!slash || backslash > slash) {
> +                    slash = backslash;
> +                }
>  #endif
> -            const char *where = slash ? slash + 1 : lflow->where;
> +                const char *where = slash ? slash + 1 : lflow->where;
>
> -            struct smap ids = SMAP_INITIALIZER(&ids);
> -            smap_add(&ids, "stage-name", ovn_stage_to_str(lflow->stage));
> -            smap_add(&ids, "source", where);
> -            if (lflow->stage_hint) {
> -                smap_add(&ids, "stage-hint", lflow->stage_hint);
> +                struct smap ids = SMAP_INITIALIZER(&ids);
> +                smap_add(&ids, "stage-name", ovn_stage_to_str(lflow->stage));
> +                smap_add(&ids, "source", where);
> +                if (lflow->stage_hint) {
> +                    smap_add(&ids, "stage-hint", lflow->stage_hint);
> +                }
> +                sbrec_logical_flow_set_external_ids(sbflow, &ids);
> +                smap_destroy(&ids);
>              }
> -            sbrec_logical_flow_set_external_ids(sbflow, &ids);
> -            smap_destroy(&ids);
>          }
>      }
> -    ovs_list_init(&temp_lflow_list);
> -    add_lflow_to_temp_list = false;
>      return true;
>
>  }
> --
> 2.30.2
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Han Zhou June 26, 2023, 5 p.m. UTC | #2
On Mon, Jun 26, 2023 at 7:25 AM Numan Siddique <numans@ovn.org> wrote:
>
> On Sun, Jun 18, 2023 at 11:48 AM Han Zhou <hzhou@ovn.org> wrote:
> >
> > For incremental processing, it is important to maintain relationship
> > between the inputs and the logical flows generated. This patch creates
> > the links between ovn_port and logical flows. The same data structure
> > may be expanded to maintain links between logical flows and other types
> > of inputs.
> >
> > This patch also refactors the temp_lflow_list operations to
> > collected_lflows with helper functions to start and end collecting. It
> > still uses global variables just to avoid updating all the lflow_add_...
> > related code all over the northd.c file.
> >
> > Signed-off-by: Han Zhou <hzhou@ovn.org>
>
> Hi Han,
>
> Please see a few comments below.  I did review all the 3 patches in the
series.
> They LGTM overall.  I'd like to do some more testing before providing my
Acks.
>

Thanks for your review!

>
> > ---
> >  northd/northd.c | 271 +++++++++++++++++++++++++++++++-----------------
> >  1 file changed, 178 insertions(+), 93 deletions(-)
> >
> > diff --git a/northd/northd.c b/northd/northd.c
> > index 98f528f93cfc..aa0f853ce2db 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -1457,6 +1457,19 @@ struct ovn_port_routable_addresses {
> >      size_t n_addrs;
> >  };
> >
> > +/* A node that maintains link between an object (such as an ovn_port)
and
> > + * a lflow. */
> > +struct lflow_ref_node {
> > +    /* This list follows different lflows referenced by the same
object. List
> > +     * head is, for example, ovn_port->lflows.  */
> > +    struct ovs_list lflow_list_node;
> > +    /* This list follows different objects that reference the same
lflow. List
> > +     * head is ovn_lflow->referenced_by. */
> > +    struct ovs_list ref_list_node;
> > +    /* The lflow. */
> > +    struct ovn_lflow *lflow;
> > +};
> > +
> >  /* A logical switch port or logical router port.
> >   *
> >   * In steady state, an ovn_port points to a northbound
Logical_Switch_Port
> > @@ -1548,6 +1561,28 @@ struct ovn_port {
> >
> >      /* Temporarily used for traversing a list (or hmap) of ports. */
> >      bool visited;
> > +
> > +    /* List of struct lflow_ref_node that points to the lflows
generated by
> > +     * this ovn_port.
> > +     *
> > +     * This data is initialized and destroyed by the en_northd node,
but
> > +     * populated and used only by the en_lflow node. Ideally this data
should
> > +     * be maintained as part of en_lflow's data (struct lflow_data): a
hash
> > +     * index from ovn_port key to lflows.  However, it would be less
efficient
> > +     * and more complex:
> > +     *
> > +     * 1. It would require an extra search (using the index) to find
the
> > +     * lflows.
> > +     *
> > +     * 2. Building the index needs to be thread-safe, using either a
global
> > +     * lock which is obviously less efficient, or hash-based lock
array which
> > +     * is more complex.
> > +     *
> > +     * Adding the list here is more straightforward. The drawback is
that we
> > +     * need to keep in mind that this data belongs to en_lflow node,
so never
> > +     * access it from any other nodes.
> > +     */
> > +    struct ovs_list lflows;
> >  };
>
>
> >
> >  static bool lsp_can_be_inc_processed(const struct
nbrec_logical_switch_port *);
> > @@ -1635,6 +1670,8 @@ ovn_port_create(struct hmap *ports, const char
*key,
> >      ovn_port_set_nb(op, nbsp, nbrp);
> >      op->l3dgw_port = op->cr_port = NULL;
> >      hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
> > +
> > +    ovs_list_init(&op->lflows);
> >      return op;
> >  }
> >
> > @@ -1665,6 +1702,13 @@ ovn_port_destroy_orphan(struct ovn_port *port)
> >      destroy_lport_addresses(&port->proxy_arp_addrs);
> >      free(port->json_key);
> >      free(port->key);
> > +
> > +    struct lflow_ref_node *l;
> > +    LIST_FOR_EACH_SAFE (l, lflow_list_node, &port->lflows) {
> > +        ovs_list_remove(&l->lflow_list_node);
> > +        ovs_list_remove(&l->ref_list_node);
> > +        free(l);
> > +    }
> >      free(port);
> >  }
> >
> > @@ -4893,6 +4937,7 @@ static struct ovn_port *
> >  ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports,
> >                 const char *key, const struct nbrec_logical_switch_port
*nbsp,
> >                 struct ovn_datapath *od, const struct
sbrec_port_binding *sb,
> > +               struct ovs_list *lflows,
> >                 const struct sbrec_mirror_table *sbrec_mirror_table,
> >                 const struct sbrec_chassis_table *sbrec_chassis_table,
> >                 struct ovsdb_idl_index *sbrec_chassis_by_name,
> > @@ -4903,6 +4948,9 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn,
struct hmap *ls_ports,
> >      parse_lsp_addrs(op);
> >      op->od = od;
> >      hmap_insert(&od->ports, &op->dp_node,
hmap_node_hash(&op->key_node));
> > +    if (lflows) {
> > +        ovs_list_splice(&op->lflows, lflows->next, lflows);
> > +    }
> >
> >      /* Assign explicitly requested tunnel ids first. */
> >      if (!ovn_port_assign_requested_tnl_id(sbrec_chassis_table, op)) {
> > @@ -5082,7 +5130,7 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
*ovnsb_idl_txn,
> >                      goto fail;
> >                  }
> >                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> > -                                    new_nbsp->name, new_nbsp, od, NULL,
> > +                                    new_nbsp->name, new_nbsp, od,
NULL, NULL,
> >                                      ni->sbrec_mirror_table,
> >                                      ni->sbrec_chassis_table,
> >                                      ni->sbrec_chassis_by_name,
> > @@ -5114,13 +5162,16 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
*ovnsb_idl_txn,
> >                      op->visited = true;
> >                      continue;
> >                  }
> > +                struct ovs_list lflows = OVS_LIST_INITIALIZER(&lflows);
> > +                ovs_list_splice(&lflows, op->lflows.next, &op->lflows);
> >                  ovn_port_destroy(&nd->ls_ports, op);
> >                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> > -                                    new_nbsp->name, new_nbsp, od, sb,
> > +                                    new_nbsp->name, new_nbsp, od, sb,
&lflows,
> >                                      ni->sbrec_mirror_table,
> >                                      ni->sbrec_chassis_table,
> >                                      ni->sbrec_chassis_by_name,
> >                                      ni->sbrec_chassis_by_hostname);
> > +                ovs_assert(ovs_list_is_empty(&lflows));
> >                  if (!op) {
> >                      goto fail;
> >                  }
> > @@ -5577,7 +5628,8 @@ ovn_igmp_group_destroy(struct hmap *igmp_groups,
> >
> >  struct ovn_lflow {
> >      struct hmap_node hmap_node;
> > -    struct ovs_list list_node;
> > +    struct ovs_list list_node;   /* For temporary list of lflows.
Don't remove
> > +                                    at destroy. */
> >
> >      struct ovn_datapath *od;     /* 'logical_datapath' in SB schema.
 */
> >      unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by their
'index'.*/
> > @@ -5591,6 +5643,8 @@ struct ovn_lflow {
> >      size_t n_ods;                /* Number of datapaths referenced by
'od' and
> >                                    * 'dpg_bitmap'. */
> >      struct ovn_dp_group *dpg;    /* Link to unique Sb datapath group.
*/
> > +
> > +    struct ovs_list referenced_by;  /* List of struct lflow_ref_node.
*/
> >      const char *where;
> >
> >      struct uuid sb_uuid;            /* SB DB row uuid, specified by
northd. */
> > @@ -5640,6 +5694,7 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct
ovn_datapath *od,
> >                 char *stage_hint, const char *where)
> >  {
> >      ovs_list_init(&lflow->list_node);
> > +    ovs_list_init(&lflow->referenced_by);
> >      lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
> >      lflow->od = od;
> >      lflow->stage = stage;
> > @@ -5767,20 +5822,30 @@ ovn_dp_group_add_with_reference(struct
ovn_lflow *lflow_ref,
> >      }
> >  }
> >
> > +/* This global variable collects the lflows generated by
do_ovn_lflow_add().
> > + * start_collecting_lflows() will enable the lflow collection and the
calls to
> > + * do_ovn_lflow_add (or the macros ovn_lflow_add_...) will add
generated lflows
> > + * to the list. end_collecting_lflows() will disable it. */
> > +static thread_local struct ovs_list collected_lflows;
> > +static thread_local bool collecting_lflows = false;
> > +
> > +static void
> > +start_collecting_lflows(void)
> > +{
> > +    ovs_assert(!collecting_lflows);
> > +    ovs_list_init(&collected_lflows);
> > +    collecting_lflows = true;
> > +}
> > +
> > +static void
> > +end_collecting_lflows(void)
> > +{
> > +    ovs_assert(collecting_lflows);
> > +    collecting_lflows = false;
> > +}
> > +
>
> I think we can avoid these functions and the thread local variable
> "collected_lflows".
>
> I'd suggest the below
>
> ----------------------------
>
> static void
> do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
>                  const unsigned long *dp_bitmap, size_t dp_bitmap_len,
>                  uint32_t hash, enum ovn_stage stage, uint16_t priority,
>                  const char *match, const char *actions, const char
*io_port,
>                  struct ovs_list *lflow_ref_list,
>                  const struct ovsdb_idl_row *stage_hint,
>                  const char *where, const char *ctrl_meter)
>     OVS_REQUIRES(fake_hash_mutex)
> {
>     ...
>     ...
>     /* At the end. */
>     if (lflow_ref_list) {
>         struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
>         lfrn->lflow = lflow;
>         ovs_list_insert(lflow_ref_list, &lfrn->lflow_list_node);
>         ovs_list_insert(&lflow->referenced_by, &lfrn->ref_list_node);
>     }
> }
>
>
> #define ovn_lflow_add_with_lport_and_hint(LFLOW_MAP, OD, STAGE, PRIORITY,
\
>                                           MATCH, ACTIONS, IN_OUT_PORT, \
>                                           LFLOW_REF_LIST, STAGE_HINT) \
>     ovn_lflow_add_at(LFLOW_MAP, OD, NULL, 0, STAGE, PRIORITY, MATCH,
ACTIONS, \
>                      IN_OUT_PORT, LFLOW_REF_LIST, NULL, STAGE_HINT, \
>                      OVS_SOURCE_LOCATOR)
>
> And pass &op->lflows in ovn_lflow_add_with_lport_and_hint()
>
> -----------------------------
>
> What do you think ?  Definitely this would result in a bit more work
> as it would require a lot of (tedious) changes.
> I think this is a better approach.
>
Firstly, I think it is not good to use "lflow_ref_list" directly in the
parameter, because there can be more than one object contributing to the
lflow generation. When we implement I-P for more inputs, a single lflow may
be referenced by multiple objects. We can't pass multiple lflow_ref_list to
the function either, because the number of such lists is unknown. For
example, a lflow may be generated as a result of a LSP, a DP and a LB
backend. If we want to implement I-P for LSP, DP and LB backend, we need to
track the reference for all of them. So the current idea is just to collect
a list of lflows generated by a higher level function, such as the
build_lswitch_and_lrouter_iterate_by_lsp. When implementing I-P for more
than one object of the same lflow, this needs to be more fine-grained.

Secondly, I agree with you adding a new parameter to the do_ovn_lflow_add
is cleaner. For the collected list I mentioned, it can be a parameter
instead of a thread local variable. However, as you mentioned, the change
is going to be all over the northd.c code, not only for the
ovn_lflow_add_xxx, but also the functions calling the ovn_lflow_add_xxx
macros, and upper layer functions calling those functions, and so on.
Unfortunately C doesn't support optional args. At this moment I am not sure
if the interface is stable enough for the incremental-processing, so I am
not sure if it is worth such a big change. If we need to modify them again
later, all such changes are going to be wasted. On the other hand, although
the thread local variable is not the best way, I think it is still clear
and manageable, if we call the start_collecting_lflows and
end_collecting_lflows in pairs. So, is it ok to leave it for this patch and
in the future when this mechanism proves to work well for more I-P, we can
have a separate patch to refactor (which will include all the tedious
function call changes). What do you think?

Thanks,
Han

> Also I'm planning to work on top of your patches to add I-P for load
> balancers in lflow engine (or perhaps I-P for datapath changes)
>
> My idea is to have a lflow ref list stored in "struct ovn_datapath"
> similar to the way you have done in this patch in "struct ovn_port"
>
> And while adding the flows using one of the macro variants
> 'ovn_lflow_add*' pass &od->lflows.
>
> Please let me know your comments.
>
> Only concern I have with this patch is the "op->lflows" modified by
> the lflow engine node.
> But I agree with your added comments and also thinking to use the same
> approach for datapath I-P handling,
> And I don't have a better approach at the moment. So I'm fine with it.
>
> Thanks
> Numan
>
>
> >  /* Adds a row with the specified contents to the Logical_Flow table.
> > - * Version to use when hash bucket locking is NOT required.
> > - *
> > - * Note: This function can add generated lflows to the global variable
> > - * temp_lflow_list as its output, controlled by the global variable
> > - * add_lflow_to_temp_list. The caller of the ovn_lflow_add_... marcros
can get
> > - * a list of lflows generated by setting add_lflow_to_temp_list to
true. The
> > - * caller is responsible for initializing the temp_lflow_list, and also
> > - * reset the add_lflow_to_temp_list to false when it is no longer
needed.
> > - * XXX: this mechanism is temporary and will be replaced when we add
hash index
> > - * to lflow_data and refactor related functions.
> > - */
> > -static bool add_lflow_to_temp_list = false;
> > -static struct ovs_list temp_lflow_list;
> > + * Version to use when hash bucket locking is NOT required. */
> >  static void
> >  do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
> >                   const unsigned long *dp_bitmap, size_t dp_bitmap_len,
> > @@ -5797,7 +5862,7 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
struct ovn_datapath *od,
> >      size_t bitmap_len = od ? ods_size(od->datapaths) : dp_bitmap_len;
> >      ovs_assert(bitmap_len);
> >
> > -    if (add_lflow_to_temp_list) {
> > +    if (collecting_lflows) {
> >          ovs_assert(od);
> >          ovs_assert(!dp_bitmap);
> >      } else {
> > @@ -5829,8 +5894,8 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
struct ovn_datapath *od,
> >          thread_lflow_counter++;
> >      }
> >
> > -    if (add_lflow_to_temp_list) {
> > -        ovs_list_insert(&temp_lflow_list, &lflow->list_node);
> > +    if (collecting_lflows) {
> > +        ovs_list_insert(&collected_lflows, &lflow->list_node);
> >      }
> >  }
> >
> > @@ -5950,10 +6015,28 @@ ovn_lflow_destroy(struct hmap *lflows, struct
ovn_lflow *lflow)
> >          free(lflow->io_port);
> >          free(lflow->stage_hint);
> >          free(lflow->ctrl_meter);
> > +        struct lflow_ref_node *l;
> > +        LIST_FOR_EACH_SAFE (l, ref_list_node, &lflow->referenced_by) {
> > +            ovs_list_remove(&l->lflow_list_node);
> > +            ovs_list_remove(&l->ref_list_node);
> > +            free(l);
> > +        }
> >          free(lflow);
> >      }
> >  }
> >
> > +static void
> > +link_ovn_port_to_lflows(struct ovn_port *op, struct ovs_list *lflows)
> > +{
> > +    struct ovn_lflow *f;
> > +    LIST_FOR_EACH (f, list_node, lflows) {
> > +        struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> > +        lfrn->lflow = f;
> > +        ovs_list_insert(&op->lflows, &lfrn->lflow_list_node);
> > +        ovs_list_insert(&f->referenced_by, &lfrn->ref_list_node);
> > +    }
> > +}
> > +
> >  static bool
> >  build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
> >                      struct ds *options_action, struct ds
*response_action,
> > @@ -15483,6 +15566,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct
ovn_port *op,
> >                                           struct hmap *lflows)
> >  {
> >      ovs_assert(op->nbsp);
> > +    start_collecting_lflows();
> >
> >      /* Build Logical Switch Flows. */
> >      build_lswitch_port_sec_op(op, lflows, actions, match);
> > @@ -15497,6 +15581,9 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct
ovn_port *op,
> >      /* Build Logical Router Flows. */
> >      build_ip_routing_flows_for_router_type_lsp(op, lr_ports, lflows);
> >      build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match,
actions);
> > +
> > +    link_ovn_port_to_lflows(op, &collected_lflows);
> > +    end_collecting_lflows();
> >  }
> >
> >  /* Helper function to combine all lflow generation which is iterated
by logical
> > @@ -16223,14 +16310,10 @@ bool lflow_handle_northd_ls_changes(struct
ovsdb_idl_txn *ovnsb_txn,
> >  {
> >      struct ls_change *ls_change;
> >      LIST_FOR_EACH (ls_change, list_node, &ls_changes->updated) {
> > -        ovs_list_init(&temp_lflow_list);
> > -        add_lflow_to_temp_list = true;
> >          if (!ovs_list_is_empty(&ls_change->updated_ports) ||
> >              !ovs_list_is_empty(&ls_change->deleted_ports)) {
> >              /* XXX: implement lflow index so that we can handle
updated and
> >               * deleted LSPs incrementally. */
> > -            ovs_list_init(&temp_lflow_list);
> > -            add_lflow_to_temp_list = false;
> >              return false;
> >          }
> >
> > @@ -16277,83 +16360,85 @@ bool lflow_handle_northd_ls_changes(struct
ovsdb_idl_txn *ovnsb_txn,
> >
 sbrec_multicast_group_update_ports_addvalue(sbmc_unknown,
> >                                                              op->sb);
> >              }
> > -        }
> > -        /* Sync the newly added flows to SB. */
> > -        struct ovn_lflow *lflow;
> > -        LIST_FOR_EACH (lflow, list_node, &temp_lflow_list) {
> > -            size_t n_datapaths;
> > -            struct ovn_datapath **datapaths_array;
> > -            if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH)
{
> > -                n_datapaths = ods_size(lflow_input->ls_datapaths);
> > -                datapaths_array = lflow_input->ls_datapaths->array;
> > -            } else {
> > -                n_datapaths = ods_size(lflow_input->lr_datapaths);
> > -                datapaths_array = lflow_input->lr_datapaths->array;
> > -            }
> > -            uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
n_datapaths);
> > -            ovs_assert(n_ods == 1);
> > -            /* There is only one datapath, so it should be moved out
of the
> > -             * group to a single 'od'. */
> > -            size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
> > -                                       n_datapaths);
> >
> > -            bitmap_set0(lflow->dpg_bitmap, index);
> > -            lflow->od = datapaths_array[index];
> > -
> > -            /* Logical flow should be re-hashed to allow lookups. */
> > -            uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> > -            /* Remove from lflows. */
> > -            hmap_remove(lflows, &lflow->hmap_node);
> > -            hash =
ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid,
> > -                                                  hash);
> > -            /* Add back. */
> > -            hmap_insert(lflows, &lflow->hmap_node, hash);
> > -
> > -            /* Sync to SB. */
> > -            const struct sbrec_logical_flow *sbflow;
> > -            lflow->sb_uuid = uuid_random();
> > -            sbflow = sbrec_logical_flow_insert_persist_uuid(ovnsb_txn,
> > -
 &lflow->sb_uuid);
> > -            const char *pipeline =
ovn_stage_get_pipeline_name(lflow->stage);
> > -            uint8_t table = ovn_stage_get_table(lflow->stage);
> > -            sbrec_logical_flow_set_logical_datapath(sbflow,
lflow->od->sb);
> > -            sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
> > -            sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> > -            sbrec_logical_flow_set_table_id(sbflow, table);
> > -            sbrec_logical_flow_set_priority(sbflow, lflow->priority);
> > -            sbrec_logical_flow_set_match(sbflow, lflow->match);
> > -            sbrec_logical_flow_set_actions(sbflow, lflow->actions);
> > -            if (lflow->io_port) {
> > -                struct smap tags = SMAP_INITIALIZER(&tags);
> > -                smap_add(&tags, "in_out_port", lflow->io_port);
> > -                sbrec_logical_flow_set_tags(sbflow, &tags);
> > -                smap_destroy(&tags);
> > -            }
> > -            sbrec_logical_flow_set_controller_meter(sbflow,
lflow->ctrl_meter);
> > -            /* Trim the source locator lflow->where, which looks
something like
> > -             * "ovn/northd/northd.c:1234", down to just the part
following the
> > -             * last slash, e.g. "northd.c:1234". */
> > -            const char *slash = strrchr(lflow->where, '/');
> > +            /* Sync the newly added flows to SB. */
> > +            struct lflow_ref_node *lfrn;
> > +            LIST_FOR_EACH (lfrn, lflow_list_node, &op->lflows) {
> > +                struct ovn_lflow *lflow = lfrn->lflow;
> > +                size_t n_datapaths;
> > +                struct ovn_datapath **datapaths_array;
> > +                if (ovn_stage_to_datapath_type(lflow->stage) ==
DP_SWITCH) {
> > +                    n_datapaths = ods_size(lflow_input->ls_datapaths);
> > +                    datapaths_array = lflow_input->ls_datapaths->array;
> > +                } else {
> > +                    n_datapaths = ods_size(lflow_input->lr_datapaths);
> > +                    datapaths_array = lflow_input->lr_datapaths->array;
> > +                }
> > +                uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
n_datapaths);
> > +                ovs_assert(n_ods == 1);
> > +                /* There is only one datapath, so it should be moved
out of the
> > +                 * group to a single 'od'. */
> > +                size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
> > +                                           n_datapaths);
> > +
> > +                bitmap_set0(lflow->dpg_bitmap, index);
> > +                lflow->od = datapaths_array[index];
> > +
> > +                /* Logical flow should be re-hashed to allow lookups.
*/
> > +                uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> > +                /* Remove from lflows. */
> > +                hmap_remove(lflows, &lflow->hmap_node);
> > +                hash = ovn_logical_flow_hash_datapath(
> > +
 &lflow->od->sb->header_.uuid, hash);
> > +                /* Add back. */
> > +                hmap_insert(lflows, &lflow->hmap_node, hash);
> > +
> > +                /* Sync to SB. */
> > +                const struct sbrec_logical_flow *sbflow;
> > +                lflow->sb_uuid = uuid_random();
> > +                sbflow = sbrec_logical_flow_insert_persist_uuid(
> > +                                                ovnsb_txn,
&lflow->sb_uuid);
> > +                const char *pipeline = ovn_stage_get_pipeline_name(
> > +
lflow->stage);
> > +                uint8_t table = ovn_stage_get_table(lflow->stage);
> > +                sbrec_logical_flow_set_logical_datapath(sbflow,
lflow->od->sb);
> > +                sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
> > +                sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> > +                sbrec_logical_flow_set_table_id(sbflow, table);
> > +                sbrec_logical_flow_set_priority(sbflow,
lflow->priority);
> > +                sbrec_logical_flow_set_match(sbflow, lflow->match);
> > +                sbrec_logical_flow_set_actions(sbflow, lflow->actions);
> > +                if (lflow->io_port) {
> > +                    struct smap tags = SMAP_INITIALIZER(&tags);
> > +                    smap_add(&tags, "in_out_port", lflow->io_port);
> > +                    sbrec_logical_flow_set_tags(sbflow, &tags);
> > +                    smap_destroy(&tags);
> > +                }
> > +                sbrec_logical_flow_set_controller_meter(sbflow,
> > +
 lflow->ctrl_meter);
> > +                /* Trim the source locator lflow->where, which looks
something
> > +                 * like "ovn/northd/northd.c:1234", down to just the
part
> > +                 * following the last slash, e.g. "northd.c:1234". */
> > +                const char *slash = strrchr(lflow->where, '/');
> >  #if _WIN32
> > -            const char *backslash = strrchr(lflow->where, '\\');
> > -            if (!slash || backslash > slash) {
> > -                slash = backslash;
> > -            }
> > +                const char *backslash = strrchr(lflow->where, '\\');
> > +                if (!slash || backslash > slash) {
> > +                    slash = backslash;
> > +                }
> >  #endif
> > -            const char *where = slash ? slash + 1 : lflow->where;
> > +                const char *where = slash ? slash + 1 : lflow->where;
> >
> > -            struct smap ids = SMAP_INITIALIZER(&ids);
> > -            smap_add(&ids, "stage-name",
ovn_stage_to_str(lflow->stage));
> > -            smap_add(&ids, "source", where);
> > -            if (lflow->stage_hint) {
> > -                smap_add(&ids, "stage-hint", lflow->stage_hint);
> > +                struct smap ids = SMAP_INITIALIZER(&ids);
> > +                smap_add(&ids, "stage-name",
ovn_stage_to_str(lflow->stage));
> > +                smap_add(&ids, "source", where);
> > +                if (lflow->stage_hint) {
> > +                    smap_add(&ids, "stage-hint", lflow->stage_hint);
> > +                }
> > +                sbrec_logical_flow_set_external_ids(sbflow, &ids);
> > +                smap_destroy(&ids);
> >              }
> > -            sbrec_logical_flow_set_external_ids(sbflow, &ids);
> > -            smap_destroy(&ids);
> >          }
> >      }
> > -    ovs_list_init(&temp_lflow_list);
> > -    add_lflow_to_temp_list = false;
> >      return true;
> >
> >  }
> > --
> > 2.30.2
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
Numan Siddique June 27, 2023, 8:23 a.m. UTC | #3
On Mon, Jun 26, 2023 at 10:34 PM Han Zhou <hzhou@ovn.org> wrote:
>
> On Mon, Jun 26, 2023 at 7:25 AM Numan Siddique <numans@ovn.org> wrote:
> >
> > On Sun, Jun 18, 2023 at 11:48 AM Han Zhou <hzhou@ovn.org> wrote:
> > >
> > > For incremental processing, it is important to maintain relationship
> > > between the inputs and the logical flows generated. This patch creates
> > > the links between ovn_port and logical flows. The same data structure
> > > may be expanded to maintain links between logical flows and other types
> > > of inputs.
> > >
> > > This patch also refactors the temp_lflow_list operations to
> > > collected_lflows with helper functions to start and end collecting. It
> > > still uses global variables just to avoid updating all the lflow_add_...
> > > related code all over the northd.c file.
> > >
> > > Signed-off-by: Han Zhou <hzhou@ovn.org>
> >
> > Hi Han,
> >
> > Please see a few comments below.  I did review all the 3 patches in the
> series.
> > They LGTM overall.  I'd like to do some more testing before providing my
> Acks.
> >
>
> Thanks for your review!
>
> >
> > > ---
> > >  northd/northd.c | 271 +++++++++++++++++++++++++++++++-----------------
> > >  1 file changed, 178 insertions(+), 93 deletions(-)
> > >
> > > diff --git a/northd/northd.c b/northd/northd.c
> > > index 98f528f93cfc..aa0f853ce2db 100644
> > > --- a/northd/northd.c
> > > +++ b/northd/northd.c
> > > @@ -1457,6 +1457,19 @@ struct ovn_port_routable_addresses {
> > >      size_t n_addrs;
> > >  };
> > >
> > > +/* A node that maintains link between an object (such as an ovn_port)
> and
> > > + * a lflow. */
> > > +struct lflow_ref_node {
> > > +    /* This list follows different lflows referenced by the same
> object. List
> > > +     * head is, for example, ovn_port->lflows.  */
> > > +    struct ovs_list lflow_list_node;
> > > +    /* This list follows different objects that reference the same
> lflow. List
> > > +     * head is ovn_lflow->referenced_by. */
> > > +    struct ovs_list ref_list_node;
> > > +    /* The lflow. */
> > > +    struct ovn_lflow *lflow;
> > > +};
> > > +
> > >  /* A logical switch port or logical router port.
> > >   *
> > >   * In steady state, an ovn_port points to a northbound
> Logical_Switch_Port
> > > @@ -1548,6 +1561,28 @@ struct ovn_port {
> > >
> > >      /* Temporarily used for traversing a list (or hmap) of ports. */
> > >      bool visited;
> > > +
> > > +    /* List of struct lflow_ref_node that points to the lflows
> generated by
> > > +     * this ovn_port.
> > > +     *
> > > +     * This data is initialized and destroyed by the en_northd node,
> but
> > > +     * populated and used only by the en_lflow node. Ideally this data
> should
> > > +     * be maintained as part of en_lflow's data (struct lflow_data): a
> hash
> > > +     * index from ovn_port key to lflows.  However, it would be less
> efficient
> > > +     * and more complex:
> > > +     *
> > > +     * 1. It would require an extra search (using the index) to find
> the
> > > +     * lflows.
> > > +     *
> > > +     * 2. Building the index needs to be thread-safe, using either a
> global
> > > +     * lock which is obviously less efficient, or hash-based lock
> array which
> > > +     * is more complex.
> > > +     *
> > > +     * Adding the list here is more straightforward. The drawback is
> that we
> > > +     * need to keep in mind that this data belongs to en_lflow node,
> so never
> > > +     * access it from any other nodes.
> > > +     */
> > > +    struct ovs_list lflows;
> > >  };
> >
> >
> > >
> > >  static bool lsp_can_be_inc_processed(const struct
> nbrec_logical_switch_port *);
> > > @@ -1635,6 +1670,8 @@ ovn_port_create(struct hmap *ports, const char
> *key,
> > >      ovn_port_set_nb(op, nbsp, nbrp);
> > >      op->l3dgw_port = op->cr_port = NULL;
> > >      hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
> > > +
> > > +    ovs_list_init(&op->lflows);
> > >      return op;
> > >  }
> > >
> > > @@ -1665,6 +1702,13 @@ ovn_port_destroy_orphan(struct ovn_port *port)
> > >      destroy_lport_addresses(&port->proxy_arp_addrs);
> > >      free(port->json_key);
> > >      free(port->key);
> > > +
> > > +    struct lflow_ref_node *l;
> > > +    LIST_FOR_EACH_SAFE (l, lflow_list_node, &port->lflows) {
> > > +        ovs_list_remove(&l->lflow_list_node);
> > > +        ovs_list_remove(&l->ref_list_node);
> > > +        free(l);
> > > +    }
> > >      free(port);
> > >  }
> > >
> > > @@ -4893,6 +4937,7 @@ static struct ovn_port *
> > >  ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports,
> > >                 const char *key, const struct nbrec_logical_switch_port
> *nbsp,
> > >                 struct ovn_datapath *od, const struct
> sbrec_port_binding *sb,
> > > +               struct ovs_list *lflows,
> > >                 const struct sbrec_mirror_table *sbrec_mirror_table,
> > >                 const struct sbrec_chassis_table *sbrec_chassis_table,
> > >                 struct ovsdb_idl_index *sbrec_chassis_by_name,
> > > @@ -4903,6 +4948,9 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn,
> struct hmap *ls_ports,
> > >      parse_lsp_addrs(op);
> > >      op->od = od;
> > >      hmap_insert(&od->ports, &op->dp_node,
> hmap_node_hash(&op->key_node));
> > > +    if (lflows) {
> > > +        ovs_list_splice(&op->lflows, lflows->next, lflows);
> > > +    }
> > >
> > >      /* Assign explicitly requested tunnel ids first. */
> > >      if (!ovn_port_assign_requested_tnl_id(sbrec_chassis_table, op)) {
> > > @@ -5082,7 +5130,7 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
> > >                      goto fail;
> > >                  }
> > >                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> > > -                                    new_nbsp->name, new_nbsp, od, NULL,
> > > +                                    new_nbsp->name, new_nbsp, od,
> NULL, NULL,
> > >                                      ni->sbrec_mirror_table,
> > >                                      ni->sbrec_chassis_table,
> > >                                      ni->sbrec_chassis_by_name,
> > > @@ -5114,13 +5162,16 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
> > >                      op->visited = true;
> > >                      continue;
> > >                  }
> > > +                struct ovs_list lflows = OVS_LIST_INITIALIZER(&lflows);
> > > +                ovs_list_splice(&lflows, op->lflows.next, &op->lflows);
> > >                  ovn_port_destroy(&nd->ls_ports, op);
> > >                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> > > -                                    new_nbsp->name, new_nbsp, od, sb,
> > > +                                    new_nbsp->name, new_nbsp, od, sb,
> &lflows,
> > >                                      ni->sbrec_mirror_table,
> > >                                      ni->sbrec_chassis_table,
> > >                                      ni->sbrec_chassis_by_name,
> > >                                      ni->sbrec_chassis_by_hostname);
> > > +                ovs_assert(ovs_list_is_empty(&lflows));
> > >                  if (!op) {
> > >                      goto fail;
> > >                  }
> > > @@ -5577,7 +5628,8 @@ ovn_igmp_group_destroy(struct hmap *igmp_groups,
> > >
> > >  struct ovn_lflow {
> > >      struct hmap_node hmap_node;
> > > -    struct ovs_list list_node;
> > > +    struct ovs_list list_node;   /* For temporary list of lflows.
> Don't remove
> > > +                                    at destroy. */
> > >
> > >      struct ovn_datapath *od;     /* 'logical_datapath' in SB schema.
>  */
> > >      unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by their
> 'index'.*/
> > > @@ -5591,6 +5643,8 @@ struct ovn_lflow {
> > >      size_t n_ods;                /* Number of datapaths referenced by
> 'od' and
> > >                                    * 'dpg_bitmap'. */
> > >      struct ovn_dp_group *dpg;    /* Link to unique Sb datapath group.
> */
> > > +
> > > +    struct ovs_list referenced_by;  /* List of struct lflow_ref_node.
> */
> > >      const char *where;
> > >
> > >      struct uuid sb_uuid;            /* SB DB row uuid, specified by
> northd. */
> > > @@ -5640,6 +5694,7 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct
> ovn_datapath *od,
> > >                 char *stage_hint, const char *where)
> > >  {
> > >      ovs_list_init(&lflow->list_node);
> > > +    ovs_list_init(&lflow->referenced_by);
> > >      lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
> > >      lflow->od = od;
> > >      lflow->stage = stage;
> > > @@ -5767,20 +5822,30 @@ ovn_dp_group_add_with_reference(struct
> ovn_lflow *lflow_ref,
> > >      }
> > >  }
> > >
> > > +/* This global variable collects the lflows generated by
> do_ovn_lflow_add().
> > > + * start_collecting_lflows() will enable the lflow collection and the
> calls to
> > > + * do_ovn_lflow_add (or the macros ovn_lflow_add_...) will add
> generated lflows
> > > + * to the list. end_collecting_lflows() will disable it. */
> > > +static thread_local struct ovs_list collected_lflows;
> > > +static thread_local bool collecting_lflows = false;
> > > +
> > > +static void
> > > +start_collecting_lflows(void)
> > > +{
> > > +    ovs_assert(!collecting_lflows);
> > > +    ovs_list_init(&collected_lflows);
> > > +    collecting_lflows = true;
> > > +}
> > > +
> > > +static void
> > > +end_collecting_lflows(void)
> > > +{
> > > +    ovs_assert(collecting_lflows);
> > > +    collecting_lflows = false;
> > > +}
> > > +
> >
> > I think we can avoid these functions and the thread local variable
> > "collected_lflows".
> >
> > I'd suggest the below
> >
> > ----------------------------
> >
> > static void
> > do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
> >                  const unsigned long *dp_bitmap, size_t dp_bitmap_len,
> >                  uint32_t hash, enum ovn_stage stage, uint16_t priority,
> >                  const char *match, const char *actions, const char
> *io_port,
> >                  struct ovs_list *lflow_ref_list,
> >                  const struct ovsdb_idl_row *stage_hint,
> >                  const char *where, const char *ctrl_meter)
> >     OVS_REQUIRES(fake_hash_mutex)
> > {
> >     ...
> >     ...
> >     /* At the end. */
> >     if (lflow_ref_list) {
> >         struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> >         lfrn->lflow = lflow;
> >         ovs_list_insert(lflow_ref_list, &lfrn->lflow_list_node);
> >         ovs_list_insert(&lflow->referenced_by, &lfrn->ref_list_node);
> >     }
> > }
> >
> >
> > #define ovn_lflow_add_with_lport_and_hint(LFLOW_MAP, OD, STAGE, PRIORITY,
> \
> >                                           MATCH, ACTIONS, IN_OUT_PORT, \
> >                                           LFLOW_REF_LIST, STAGE_HINT) \
> >     ovn_lflow_add_at(LFLOW_MAP, OD, NULL, 0, STAGE, PRIORITY, MATCH,
> ACTIONS, \
> >                      IN_OUT_PORT, LFLOW_REF_LIST, NULL, STAGE_HINT, \
> >                      OVS_SOURCE_LOCATOR)
> >
> > And pass &op->lflows in ovn_lflow_add_with_lport_and_hint()
> >
> > -----------------------------
> >
> > What do you think ?  Definitely this would result in a bit more work
> > as it would require a lot of (tedious) changes.
> > I think this is a better approach.
> >
> Firstly, I think it is not good to use "lflow_ref_list" directly in the
> parameter, because there can be more than one object contributing to the
> lflow generation. When we implement I-P for more inputs, a single lflow may
> be referenced by multiple objects. We can't pass multiple lflow_ref_list to
> the function either, because the number of such lists is unknown. For
> example, a lflow may be generated as a result of a LSP, a DP and a LB
> backend. If we want to implement I-P for LSP, DP and LB backend, we need to
> track the reference for all of them. So the current idea is just to collect
> a list of lflows generated by a higher level function, such as the
> build_lswitch_and_lrouter_iterate_by_lsp. When implementing I-P for more
> than one object of the same lflow, this needs to be more fine-grained.

Agree.  I'm working on the I-P for datapath changes in lflow engine and
as a flow can be referenced by multiple datapaths,  I agree this needs to
be tracked properly.  And definitely that is out of scope of this patch series.


>
> Secondly, I agree with you adding a new parameter to the do_ovn_lflow_add
> is cleaner. For the collected list I mentioned, it can be a parameter
> instead of a thread local variable. However, as you mentioned, the change
> is going to be all over the northd.c code, not only for the
> ovn_lflow_add_xxx, but also the functions calling the ovn_lflow_add_xxx
> macros, and upper layer functions calling those functions, and so on.
> Unfortunately C doesn't support optional args. At this moment I am not sure
> if the interface is stable enough for the incremental-processing, so I am
> not sure if it is worth such a big change. If we need to modify them again
> later, all such changes are going to be wasted. On the other hand, although
> the thread local variable is not the best way, I think it is still clear
> and manageable, if we call the start_collecting_lflows and
> end_collecting_lflows in pairs. So, is it ok to leave it for this patch and
> in the future when this mechanism proves to work well for more I-P, we can
> have a separate patch to refactor (which will include all the tedious
> function call changes). What do you think?

I agree.  It is definitely tedious to do all the changes.  I'm ok with
the approach taken
in this patch series.

Also request to please take a look at the load balancer I-P patch
series - http://patchwork.ozlabs.org/project/ovn/list/?series=361083&state=*

Thanks
Numan

>
> Thanks,
> Han
>
> > Also I'm planning to work on top of your patches to add I-P for load
> > balancers in lflow engine (or perhaps I-P for datapath changes)
> >
> > My idea is to have a lflow ref list stored in "struct ovn_datapath"
> > similar to the way you have done in this patch in "struct ovn_port"
> >
> > And while adding the flows using one of the macro variants
> > 'ovn_lflow_add*' pass &od->lflows.
> >
> > Please let me know your comments.
> >
> > Only concern I have with this patch is the "op->lflows" modified by
> > the lflow engine node.
> > But I agree with your added comments and also thinking to use the same
> > approach for datapath I-P handling,
> > And I don't have a better approach at the moment. So I'm fine with it.
> >
> > Thanks
> > Numan
> >
> >
> > >  /* Adds a row with the specified contents to the Logical_Flow table.
> > > - * Version to use when hash bucket locking is NOT required.
> > > - *
> > > - * Note: This function can add generated lflows to the global variable
> > > - * temp_lflow_list as its output, controlled by the global variable
> > > - * add_lflow_to_temp_list. The caller of the ovn_lflow_add_... marcros
> can get
> > > - * a list of lflows generated by setting add_lflow_to_temp_list to
> true. The
> > > - * caller is responsible for initializing the temp_lflow_list, and also
> > > - * reset the add_lflow_to_temp_list to false when it is no longer
> needed.
> > > - * XXX: this mechanism is temporary and will be replaced when we add
> hash index
> > > - * to lflow_data and refactor related functions.
> > > - */
> > > -static bool add_lflow_to_temp_list = false;
> > > -static struct ovs_list temp_lflow_list;
> > > + * Version to use when hash bucket locking is NOT required. */
> > >  static void
> > >  do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
> > >                   const unsigned long *dp_bitmap, size_t dp_bitmap_len,
> > > @@ -5797,7 +5862,7 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
> struct ovn_datapath *od,
> > >      size_t bitmap_len = od ? ods_size(od->datapaths) : dp_bitmap_len;
> > >      ovs_assert(bitmap_len);
> > >
> > > -    if (add_lflow_to_temp_list) {
> > > +    if (collecting_lflows) {
> > >          ovs_assert(od);
> > >          ovs_assert(!dp_bitmap);
> > >      } else {
> > > @@ -5829,8 +5894,8 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
> struct ovn_datapath *od,
> > >          thread_lflow_counter++;
> > >      }
> > >
> > > -    if (add_lflow_to_temp_list) {
> > > -        ovs_list_insert(&temp_lflow_list, &lflow->list_node);
> > > +    if (collecting_lflows) {
> > > +        ovs_list_insert(&collected_lflows, &lflow->list_node);
> > >      }
> > >  }
> > >
> > > @@ -5950,10 +6015,28 @@ ovn_lflow_destroy(struct hmap *lflows, struct
> ovn_lflow *lflow)
> > >          free(lflow->io_port);
> > >          free(lflow->stage_hint);
> > >          free(lflow->ctrl_meter);
> > > +        struct lflow_ref_node *l;
> > > +        LIST_FOR_EACH_SAFE (l, ref_list_node, &lflow->referenced_by) {
> > > +            ovs_list_remove(&l->lflow_list_node);
> > > +            ovs_list_remove(&l->ref_list_node);
> > > +            free(l);
> > > +        }
> > >          free(lflow);
> > >      }
> > >  }
> > >
> > > +static void
> > > +link_ovn_port_to_lflows(struct ovn_port *op, struct ovs_list *lflows)
> > > +{
> > > +    struct ovn_lflow *f;
> > > +    LIST_FOR_EACH (f, list_node, lflows) {
> > > +        struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> > > +        lfrn->lflow = f;
> > > +        ovs_list_insert(&op->lflows, &lfrn->lflow_list_node);
> > > +        ovs_list_insert(&f->referenced_by, &lfrn->ref_list_node);
> > > +    }
> > > +}
> > > +
> > >  static bool
> > >  build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
> > >                      struct ds *options_action, struct ds
> *response_action,
> > > @@ -15483,6 +15566,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct
> ovn_port *op,
> > >                                           struct hmap *lflows)
> > >  {
> > >      ovs_assert(op->nbsp);
> > > +    start_collecting_lflows();
> > >
> > >      /* Build Logical Switch Flows. */
> > >      build_lswitch_port_sec_op(op, lflows, actions, match);
> > > @@ -15497,6 +15581,9 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct
> ovn_port *op,
> > >      /* Build Logical Router Flows. */
> > >      build_ip_routing_flows_for_router_type_lsp(op, lr_ports, lflows);
> > >      build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match,
> actions);
> > > +
> > > +    link_ovn_port_to_lflows(op, &collected_lflows);
> > > +    end_collecting_lflows();
> > >  }
> > >
> > >  /* Helper function to combine all lflow generation which is iterated
> by logical
> > > @@ -16223,14 +16310,10 @@ bool lflow_handle_northd_ls_changes(struct
> ovsdb_idl_txn *ovnsb_txn,
> > >  {
> > >      struct ls_change *ls_change;
> > >      LIST_FOR_EACH (ls_change, list_node, &ls_changes->updated) {
> > > -        ovs_list_init(&temp_lflow_list);
> > > -        add_lflow_to_temp_list = true;
> > >          if (!ovs_list_is_empty(&ls_change->updated_ports) ||
> > >              !ovs_list_is_empty(&ls_change->deleted_ports)) {
> > >              /* XXX: implement lflow index so that we can handle
> updated and
> > >               * deleted LSPs incrementally. */
> > > -            ovs_list_init(&temp_lflow_list);
> > > -            add_lflow_to_temp_list = false;
> > >              return false;
> > >          }
> > >
> > > @@ -16277,83 +16360,85 @@ bool lflow_handle_northd_ls_changes(struct
> ovsdb_idl_txn *ovnsb_txn,
> > >
>  sbrec_multicast_group_update_ports_addvalue(sbmc_unknown,
> > >                                                              op->sb);
> > >              }
> > > -        }
> > > -        /* Sync the newly added flows to SB. */
> > > -        struct ovn_lflow *lflow;
> > > -        LIST_FOR_EACH (lflow, list_node, &temp_lflow_list) {
> > > -            size_t n_datapaths;
> > > -            struct ovn_datapath **datapaths_array;
> > > -            if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH)
> {
> > > -                n_datapaths = ods_size(lflow_input->ls_datapaths);
> > > -                datapaths_array = lflow_input->ls_datapaths->array;
> > > -            } else {
> > > -                n_datapaths = ods_size(lflow_input->lr_datapaths);
> > > -                datapaths_array = lflow_input->lr_datapaths->array;
> > > -            }
> > > -            uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
> n_datapaths);
> > > -            ovs_assert(n_ods == 1);
> > > -            /* There is only one datapath, so it should be moved out
> of the
> > > -             * group to a single 'od'. */
> > > -            size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
> > > -                                       n_datapaths);
> > >
> > > -            bitmap_set0(lflow->dpg_bitmap, index);
> > > -            lflow->od = datapaths_array[index];
> > > -
> > > -            /* Logical flow should be re-hashed to allow lookups. */
> > > -            uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> > > -            /* Remove from lflows. */
> > > -            hmap_remove(lflows, &lflow->hmap_node);
> > > -            hash =
> ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid,
> > > -                                                  hash);
> > > -            /* Add back. */
> > > -            hmap_insert(lflows, &lflow->hmap_node, hash);
> > > -
> > > -            /* Sync to SB. */
> > > -            const struct sbrec_logical_flow *sbflow;
> > > -            lflow->sb_uuid = uuid_random();
> > > -            sbflow = sbrec_logical_flow_insert_persist_uuid(ovnsb_txn,
> > > -
>  &lflow->sb_uuid);
> > > -            const char *pipeline =
> ovn_stage_get_pipeline_name(lflow->stage);
> > > -            uint8_t table = ovn_stage_get_table(lflow->stage);
> > > -            sbrec_logical_flow_set_logical_datapath(sbflow,
> lflow->od->sb);
> > > -            sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
> > > -            sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> > > -            sbrec_logical_flow_set_table_id(sbflow, table);
> > > -            sbrec_logical_flow_set_priority(sbflow, lflow->priority);
> > > -            sbrec_logical_flow_set_match(sbflow, lflow->match);
> > > -            sbrec_logical_flow_set_actions(sbflow, lflow->actions);
> > > -            if (lflow->io_port) {
> > > -                struct smap tags = SMAP_INITIALIZER(&tags);
> > > -                smap_add(&tags, "in_out_port", lflow->io_port);
> > > -                sbrec_logical_flow_set_tags(sbflow, &tags);
> > > -                smap_destroy(&tags);
> > > -            }
> > > -            sbrec_logical_flow_set_controller_meter(sbflow,
> lflow->ctrl_meter);
> > > -            /* Trim the source locator lflow->where, which looks
> something like
> > > -             * "ovn/northd/northd.c:1234", down to just the part
> following the
> > > -             * last slash, e.g. "northd.c:1234". */
> > > -            const char *slash = strrchr(lflow->where, '/');
> > > +            /* Sync the newly added flows to SB. */
> > > +            struct lflow_ref_node *lfrn;
> > > +            LIST_FOR_EACH (lfrn, lflow_list_node, &op->lflows) {
> > > +                struct ovn_lflow *lflow = lfrn->lflow;
> > > +                size_t n_datapaths;
> > > +                struct ovn_datapath **datapaths_array;
> > > +                if (ovn_stage_to_datapath_type(lflow->stage) ==
> DP_SWITCH) {
> > > +                    n_datapaths = ods_size(lflow_input->ls_datapaths);
> > > +                    datapaths_array = lflow_input->ls_datapaths->array;
> > > +                } else {
> > > +                    n_datapaths = ods_size(lflow_input->lr_datapaths);
> > > +                    datapaths_array = lflow_input->lr_datapaths->array;
> > > +                }
> > > +                uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
> n_datapaths);
> > > +                ovs_assert(n_ods == 1);
> > > +                /* There is only one datapath, so it should be moved
> out of the
> > > +                 * group to a single 'od'. */
> > > +                size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
> > > +                                           n_datapaths);
> > > +
> > > +                bitmap_set0(lflow->dpg_bitmap, index);
> > > +                lflow->od = datapaths_array[index];
> > > +
> > > +                /* Logical flow should be re-hashed to allow lookups.
> */
> > > +                uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> > > +                /* Remove from lflows. */
> > > +                hmap_remove(lflows, &lflow->hmap_node);
> > > +                hash = ovn_logical_flow_hash_datapath(
> > > +
>  &lflow->od->sb->header_.uuid, hash);
> > > +                /* Add back. */
> > > +                hmap_insert(lflows, &lflow->hmap_node, hash);
> > > +
> > > +                /* Sync to SB. */
> > > +                const struct sbrec_logical_flow *sbflow;
> > > +                lflow->sb_uuid = uuid_random();
> > > +                sbflow = sbrec_logical_flow_insert_persist_uuid(
> > > +                                                ovnsb_txn,
> &lflow->sb_uuid);
> > > +                const char *pipeline = ovn_stage_get_pipeline_name(
> > > +
> lflow->stage);
> > > +                uint8_t table = ovn_stage_get_table(lflow->stage);
> > > +                sbrec_logical_flow_set_logical_datapath(sbflow,
> lflow->od->sb);
> > > +                sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
> > > +                sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> > > +                sbrec_logical_flow_set_table_id(sbflow, table);
> > > +                sbrec_logical_flow_set_priority(sbflow,
> lflow->priority);
> > > +                sbrec_logical_flow_set_match(sbflow, lflow->match);
> > > +                sbrec_logical_flow_set_actions(sbflow, lflow->actions);
> > > +                if (lflow->io_port) {
> > > +                    struct smap tags = SMAP_INITIALIZER(&tags);
> > > +                    smap_add(&tags, "in_out_port", lflow->io_port);
> > > +                    sbrec_logical_flow_set_tags(sbflow, &tags);
> > > +                    smap_destroy(&tags);
> > > +                }
> > > +                sbrec_logical_flow_set_controller_meter(sbflow,
> > > +
>  lflow->ctrl_meter);
> > > +                /* Trim the source locator lflow->where, which looks
> something
> > > +                 * like "ovn/northd/northd.c:1234", down to just the
> part
> > > +                 * following the last slash, e.g. "northd.c:1234". */
> > > +                const char *slash = strrchr(lflow->where, '/');
> > >  #if _WIN32
> > > -            const char *backslash = strrchr(lflow->where, '\\');
> > > -            if (!slash || backslash > slash) {
> > > -                slash = backslash;
> > > -            }
> > > +                const char *backslash = strrchr(lflow->where, '\\');
> > > +                if (!slash || backslash > slash) {
> > > +                    slash = backslash;
> > > +                }
> > >  #endif
> > > -            const char *where = slash ? slash + 1 : lflow->where;
> > > +                const char *where = slash ? slash + 1 : lflow->where;
> > >
> > > -            struct smap ids = SMAP_INITIALIZER(&ids);
> > > -            smap_add(&ids, "stage-name",
> ovn_stage_to_str(lflow->stage));
> > > -            smap_add(&ids, "source", where);
> > > -            if (lflow->stage_hint) {
> > > -                smap_add(&ids, "stage-hint", lflow->stage_hint);
> > > +                struct smap ids = SMAP_INITIALIZER(&ids);
> > > +                smap_add(&ids, "stage-name",
> ovn_stage_to_str(lflow->stage));
> > > +                smap_add(&ids, "source", where);
> > > +                if (lflow->stage_hint) {
> > > +                    smap_add(&ids, "stage-hint", lflow->stage_hint);
> > > +                }
> > > +                sbrec_logical_flow_set_external_ids(sbflow, &ids);
> > > +                smap_destroy(&ids);
> > >              }
> > > -            sbrec_logical_flow_set_external_ids(sbflow, &ids);
> > > -            smap_destroy(&ids);
> > >          }
> > >      }
> > > -    ovs_list_init(&temp_lflow_list);
> > > -    add_lflow_to_temp_list = false;
> > >      return true;
> > >
> > >  }
> > > --
> > > 2.30.2
> > >
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Dumitru Ceara June 29, 2023, 4:19 p.m. UTC | #4
On 6/27/23 10:23, Numan Siddique wrote:
> On Mon, Jun 26, 2023 at 10:34 PM Han Zhou <hzhou@ovn.org> wrote:
>>
>> On Mon, Jun 26, 2023 at 7:25 AM Numan Siddique <numans@ovn.org> wrote:
>>>
>>> On Sun, Jun 18, 2023 at 11:48 AM Han Zhou <hzhou@ovn.org> wrote:
>>>>
>>>> For incremental processing, it is important to maintain relationship
>>>> between the inputs and the logical flows generated. This patch creates
>>>> the links between ovn_port and logical flows. The same data structure
>>>> may be expanded to maintain links between logical flows and other types
>>>> of inputs.
>>>>
>>>> This patch also refactors the temp_lflow_list operations to
>>>> collected_lflows with helper functions to start and end collecting. It
>>>> still uses global variables just to avoid updating all the lflow_add_...
>>>> related code all over the northd.c file.
>>>>
>>>> Signed-off-by: Han Zhou <hzhou@ovn.org>
>>>
>>> Hi Han,
>>>
>>> Please see a few comments below.  I did review all the 3 patches in the
>> series.
>>> They LGTM overall.  I'd like to do some more testing before providing my
>> Acks.
>>>
>>
>> Thanks for your review!
>>
>>>
>>>> ---
>>>>  northd/northd.c | 271 +++++++++++++++++++++++++++++++-----------------
>>>>  1 file changed, 178 insertions(+), 93 deletions(-)
>>>>
>>>> diff --git a/northd/northd.c b/northd/northd.c
>>>> index 98f528f93cfc..aa0f853ce2db 100644
>>>> --- a/northd/northd.c
>>>> +++ b/northd/northd.c
>>>> @@ -1457,6 +1457,19 @@ struct ovn_port_routable_addresses {
>>>>      size_t n_addrs;
>>>>  };
>>>>
>>>> +/* A node that maintains link between an object (such as an ovn_port)
>> and
>>>> + * a lflow. */
>>>> +struct lflow_ref_node {
>>>> +    /* This list follows different lflows referenced by the same
>> object. List
>>>> +     * head is, for example, ovn_port->lflows.  */
>>>> +    struct ovs_list lflow_list_node;
>>>> +    /* This list follows different objects that reference the same
>> lflow. List
>>>> +     * head is ovn_lflow->referenced_by. */
>>>> +    struct ovs_list ref_list_node;
>>>> +    /* The lflow. */
>>>> +    struct ovn_lflow *lflow;
>>>> +};
>>>> +
>>>>  /* A logical switch port or logical router port.
>>>>   *
>>>>   * In steady state, an ovn_port points to a northbound
>> Logical_Switch_Port
>>>> @@ -1548,6 +1561,28 @@ struct ovn_port {
>>>>
>>>>      /* Temporarily used for traversing a list (or hmap) of ports. */
>>>>      bool visited;
>>>> +
>>>> +    /* List of struct lflow_ref_node that points to the lflows
>> generated by
>>>> +     * this ovn_port.
>>>> +     *
>>>> +     * This data is initialized and destroyed by the en_northd node,
>> but
>>>> +     * populated and used only by the en_lflow node. Ideally this data
>> should
>>>> +     * be maintained as part of en_lflow's data (struct lflow_data): a
>> hash
>>>> +     * index from ovn_port key to lflows.  However, it would be less
>> efficient
>>>> +     * and more complex:
>>>> +     *
>>>> +     * 1. It would require an extra search (using the index) to find
>> the
>>>> +     * lflows.
>>>> +     *
>>>> +     * 2. Building the index needs to be thread-safe, using either a
>> global
>>>> +     * lock which is obviously less efficient, or hash-based lock
>> array which
>>>> +     * is more complex.
>>>> +     *
>>>> +     * Adding the list here is more straightforward. The drawback is
>> that we
>>>> +     * need to keep in mind that this data belongs to en_lflow node,
>> so never
>>>> +     * access it from any other nodes.
>>>> +     */
>>>> +    struct ovs_list lflows;
>>>>  };
>>>
>>>
>>>>
>>>>  static bool lsp_can_be_inc_processed(const struct
>> nbrec_logical_switch_port *);
>>>> @@ -1635,6 +1670,8 @@ ovn_port_create(struct hmap *ports, const char
>> *key,
>>>>      ovn_port_set_nb(op, nbsp, nbrp);
>>>>      op->l3dgw_port = op->cr_port = NULL;
>>>>      hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
>>>> +
>>>> +    ovs_list_init(&op->lflows);
>>>>      return op;
>>>>  }
>>>>
>>>> @@ -1665,6 +1702,13 @@ ovn_port_destroy_orphan(struct ovn_port *port)
>>>>      destroy_lport_addresses(&port->proxy_arp_addrs);
>>>>      free(port->json_key);
>>>>      free(port->key);
>>>> +
>>>> +    struct lflow_ref_node *l;
>>>> +    LIST_FOR_EACH_SAFE (l, lflow_list_node, &port->lflows) {
>>>> +        ovs_list_remove(&l->lflow_list_node);
>>>> +        ovs_list_remove(&l->ref_list_node);
>>>> +        free(l);
>>>> +    }
>>>>      free(port);
>>>>  }
>>>>
>>>> @@ -4893,6 +4937,7 @@ static struct ovn_port *
>>>>  ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports,
>>>>                 const char *key, const struct nbrec_logical_switch_port
>> *nbsp,
>>>>                 struct ovn_datapath *od, const struct
>> sbrec_port_binding *sb,
>>>> +               struct ovs_list *lflows,
>>>>                 const struct sbrec_mirror_table *sbrec_mirror_table,
>>>>                 const struct sbrec_chassis_table *sbrec_chassis_table,
>>>>                 struct ovsdb_idl_index *sbrec_chassis_by_name,
>>>> @@ -4903,6 +4948,9 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn,
>> struct hmap *ls_ports,
>>>>      parse_lsp_addrs(op);
>>>>      op->od = od;
>>>>      hmap_insert(&od->ports, &op->dp_node,
>> hmap_node_hash(&op->key_node));
>>>> +    if (lflows) {
>>>> +        ovs_list_splice(&op->lflows, lflows->next, lflows);
>>>> +    }
>>>>
>>>>      /* Assign explicitly requested tunnel ids first. */
>>>>      if (!ovn_port_assign_requested_tnl_id(sbrec_chassis_table, op)) {
>>>> @@ -5082,7 +5130,7 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
>> *ovnsb_idl_txn,
>>>>                      goto fail;
>>>>                  }
>>>>                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
>>>> -                                    new_nbsp->name, new_nbsp, od, NULL,
>>>> +                                    new_nbsp->name, new_nbsp, od,
>> NULL, NULL,
>>>>                                      ni->sbrec_mirror_table,
>>>>                                      ni->sbrec_chassis_table,
>>>>                                      ni->sbrec_chassis_by_name,
>>>> @@ -5114,13 +5162,16 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
>> *ovnsb_idl_txn,
>>>>                      op->visited = true;
>>>>                      continue;
>>>>                  }
>>>> +                struct ovs_list lflows = OVS_LIST_INITIALIZER(&lflows);
>>>> +                ovs_list_splice(&lflows, op->lflows.next, &op->lflows);
>>>>                  ovn_port_destroy(&nd->ls_ports, op);
>>>>                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
>>>> -                                    new_nbsp->name, new_nbsp, od, sb,
>>>> +                                    new_nbsp->name, new_nbsp, od, sb,
>> &lflows,
>>>>                                      ni->sbrec_mirror_table,
>>>>                                      ni->sbrec_chassis_table,
>>>>                                      ni->sbrec_chassis_by_name,
>>>>                                      ni->sbrec_chassis_by_hostname);
>>>> +                ovs_assert(ovs_list_is_empty(&lflows));
>>>>                  if (!op) {
>>>>                      goto fail;
>>>>                  }
>>>> @@ -5577,7 +5628,8 @@ ovn_igmp_group_destroy(struct hmap *igmp_groups,
>>>>
>>>>  struct ovn_lflow {
>>>>      struct hmap_node hmap_node;
>>>> -    struct ovs_list list_node;
>>>> +    struct ovs_list list_node;   /* For temporary list of lflows.
>> Don't remove
>>>> +                                    at destroy. */
>>>>
>>>>      struct ovn_datapath *od;     /* 'logical_datapath' in SB schema.
>>  */
>>>>      unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by their
>> 'index'.*/
>>>> @@ -5591,6 +5643,8 @@ struct ovn_lflow {
>>>>      size_t n_ods;                /* Number of datapaths referenced by
>> 'od' and
>>>>                                    * 'dpg_bitmap'. */
>>>>      struct ovn_dp_group *dpg;    /* Link to unique Sb datapath group.
>> */
>>>> +
>>>> +    struct ovs_list referenced_by;  /* List of struct lflow_ref_node.
>> */
>>>>      const char *where;
>>>>
>>>>      struct uuid sb_uuid;            /* SB DB row uuid, specified by
>> northd. */
>>>> @@ -5640,6 +5694,7 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct
>> ovn_datapath *od,
>>>>                 char *stage_hint, const char *where)
>>>>  {
>>>>      ovs_list_init(&lflow->list_node);
>>>> +    ovs_list_init(&lflow->referenced_by);
>>>>      lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
>>>>      lflow->od = od;
>>>>      lflow->stage = stage;
>>>> @@ -5767,20 +5822,30 @@ ovn_dp_group_add_with_reference(struct
>> ovn_lflow *lflow_ref,
>>>>      }
>>>>  }
>>>>
>>>> +/* This global variable collects the lflows generated by
>> do_ovn_lflow_add().
>>>> + * start_collecting_lflows() will enable the lflow collection and the
>> calls to
>>>> + * do_ovn_lflow_add (or the macros ovn_lflow_add_...) will add
>> generated lflows
>>>> + * to the list. end_collecting_lflows() will disable it. */
>>>> +static thread_local struct ovs_list collected_lflows;
>>>> +static thread_local bool collecting_lflows = false;
>>>> +
>>>> +static void
>>>> +start_collecting_lflows(void)
>>>> +{
>>>> +    ovs_assert(!collecting_lflows);
>>>> +    ovs_list_init(&collected_lflows);
>>>> +    collecting_lflows = true;
>>>> +}
>>>> +
>>>> +static void
>>>> +end_collecting_lflows(void)
>>>> +{
>>>> +    ovs_assert(collecting_lflows);
>>>> +    collecting_lflows = false;
>>>> +}
>>>> +
>>>
>>> I think we can avoid these functions and the thread local variable
>>> "collected_lflows".
>>>
>>> I'd suggest the below
>>>
>>> ----------------------------
>>>
>>> static void
>>> do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
>>>                  const unsigned long *dp_bitmap, size_t dp_bitmap_len,
>>>                  uint32_t hash, enum ovn_stage stage, uint16_t priority,
>>>                  const char *match, const char *actions, const char
>> *io_port,
>>>                  struct ovs_list *lflow_ref_list,
>>>                  const struct ovsdb_idl_row *stage_hint,
>>>                  const char *where, const char *ctrl_meter)
>>>     OVS_REQUIRES(fake_hash_mutex)
>>> {
>>>     ...
>>>     ...
>>>     /* At the end. */
>>>     if (lflow_ref_list) {
>>>         struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
>>>         lfrn->lflow = lflow;
>>>         ovs_list_insert(lflow_ref_list, &lfrn->lflow_list_node);
>>>         ovs_list_insert(&lflow->referenced_by, &lfrn->ref_list_node);
>>>     }
>>> }
>>>
>>>
>>> #define ovn_lflow_add_with_lport_and_hint(LFLOW_MAP, OD, STAGE, PRIORITY,
>> \
>>>                                           MATCH, ACTIONS, IN_OUT_PORT, \
>>>                                           LFLOW_REF_LIST, STAGE_HINT) \
>>>     ovn_lflow_add_at(LFLOW_MAP, OD, NULL, 0, STAGE, PRIORITY, MATCH,
>> ACTIONS, \
>>>                      IN_OUT_PORT, LFLOW_REF_LIST, NULL, STAGE_HINT, \
>>>                      OVS_SOURCE_LOCATOR)
>>>
>>> And pass &op->lflows in ovn_lflow_add_with_lport_and_hint()
>>>
>>> -----------------------------
>>>
>>> What do you think ?  Definitely this would result in a bit more work
>>> as it would require a lot of (tedious) changes.
>>> I think this is a better approach.
>>>
>> Firstly, I think it is not good to use "lflow_ref_list" directly in the
>> parameter, because there can be more than one object contributing to the
>> lflow generation. When we implement I-P for more inputs, a single lflow may
>> be referenced by multiple objects. We can't pass multiple lflow_ref_list to
>> the function either, because the number of such lists is unknown. For
>> example, a lflow may be generated as a result of a LSP, a DP and a LB
>> backend. If we want to implement I-P for LSP, DP and LB backend, we need to
>> track the reference for all of them. So the current idea is just to collect
>> a list of lflows generated by a higher level function, such as the
>> build_lswitch_and_lrouter_iterate_by_lsp. When implementing I-P for more
>> than one object of the same lflow, this needs to be more fine-grained.

I'm still reviewing this patch but my first impression was that we can
probably try to use 'struct objdep_mgr' (defined in lib/objdep.h) to
model all these (potentially many-to-many) relationships.  We do similar
things in ovn-controller.

> 
> Agree.  I'm working on the I-P for datapath changes in lflow engine and
> as a flow can be referenced by multiple datapaths,  I agree this needs to
> be tracked properly.  And definitely that is out of scope of this patch series.
> 
> 
>>
>> Secondly, I agree with you adding a new parameter to the do_ovn_lflow_add
>> is cleaner. For the collected list I mentioned, it can be a parameter
>> instead of a thread local variable. However, as you mentioned, the change
>> is going to be all over the northd.c code, not only for the
>> ovn_lflow_add_xxx, but also the functions calling the ovn_lflow_add_xxx
>> macros, and upper layer functions calling those functions, and so on.
>> Unfortunately C doesn't support optional args. At this moment I am not sure
>> if the interface is stable enough for the incremental-processing, so I am
>> not sure if it is worth such a big change. If we need to modify them again
>> later, all such changes are going to be wasted. On the other hand, although
>> the thread local variable is not the best way, I think it is still clear
>> and manageable, if we call the start_collecting_lflows and
>> end_collecting_lflows in pairs. So, is it ok to leave it for this patch and
>> in the future when this mechanism proves to work well for more I-P, we can
>> have a separate patch to refactor (which will include all the tedious
>> function call changes). What do you think?
> 
> I agree.  It is definitely tedious to do all the changes.  I'm ok with
> the approach taken
> in this patch series.
> 
> Also request to please take a look at the load balancer I-P patch
> series - http://patchwork.ozlabs.org/project/ovn/list/?series=361083&state=*
> 
> Thanks
> Numan
> 
>>
>> Thanks,
>> Han
>>
>>> Also I'm planning to work on top of your patches to add I-P for load
>>> balancers in lflow engine (or perhaps I-P for datapath changes)
>>>
>>> My idea is to have a lflow ref list stored in "struct ovn_datapath"
>>> similar to the way you have done in this patch in "struct ovn_port"
>>>
>>> And while adding the flows using one of the macro variants
>>> 'ovn_lflow_add*' pass &od->lflows.
>>>
>>> Please let me know your comments.
>>>
>>> Only concern I have with this patch is the "op->lflows" modified by
>>> the lflow engine node.
>>> But I agree with your added comments and also thinking to use the same
>>> approach for datapath I-P handling,
>>> And I don't have a better approach at the moment. So I'm fine with it.
>>>
>>> Thanks
>>> Numan
>>>
>>>
>>>>  /* Adds a row with the specified contents to the Logical_Flow table.
>>>> - * Version to use when hash bucket locking is NOT required.
>>>> - *
>>>> - * Note: This function can add generated lflows to the global variable
>>>> - * temp_lflow_list as its output, controlled by the global variable
>>>> - * add_lflow_to_temp_list. The caller of the ovn_lflow_add_... marcros
>> can get
>>>> - * a list of lflows generated by setting add_lflow_to_temp_list to
>> true. The
>>>> - * caller is responsible for initializing the temp_lflow_list, and also
>>>> - * reset the add_lflow_to_temp_list to false when it is no longer
>> needed.
>>>> - * XXX: this mechanism is temporary and will be replaced when we add
>> hash index
>>>> - * to lflow_data and refactor related functions.
>>>> - */
>>>> -static bool add_lflow_to_temp_list = false;
>>>> -static struct ovs_list temp_lflow_list;
>>>> + * Version to use when hash bucket locking is NOT required. */
>>>>  static void
>>>>  do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
>>>>                   const unsigned long *dp_bitmap, size_t dp_bitmap_len,
>>>> @@ -5797,7 +5862,7 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
>> struct ovn_datapath *od,
>>>>      size_t bitmap_len = od ? ods_size(od->datapaths) : dp_bitmap_len;
>>>>      ovs_assert(bitmap_len);
>>>>
>>>> -    if (add_lflow_to_temp_list) {
>>>> +    if (collecting_lflows) {
>>>>          ovs_assert(od);
>>>>          ovs_assert(!dp_bitmap);
>>>>      } else {
>>>> @@ -5829,8 +5894,8 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
>> struct ovn_datapath *od,
>>>>          thread_lflow_counter++;
>>>>      }
>>>>
>>>> -    if (add_lflow_to_temp_list) {
>>>> -        ovs_list_insert(&temp_lflow_list, &lflow->list_node);
>>>> +    if (collecting_lflows) {
>>>> +        ovs_list_insert(&collected_lflows, &lflow->list_node);
>>>>      }
>>>>  }
>>>>
>>>> @@ -5950,10 +6015,28 @@ ovn_lflow_destroy(struct hmap *lflows, struct
>> ovn_lflow *lflow)
>>>>          free(lflow->io_port);
>>>>          free(lflow->stage_hint);
>>>>          free(lflow->ctrl_meter);
>>>> +        struct lflow_ref_node *l;
>>>> +        LIST_FOR_EACH_SAFE (l, ref_list_node, &lflow->referenced_by) {
>>>> +            ovs_list_remove(&l->lflow_list_node);
>>>> +            ovs_list_remove(&l->ref_list_node);
>>>> +            free(l);
>>>> +        }
>>>>          free(lflow);
>>>>      }
>>>>  }
>>>>
>>>> +static void
>>>> +link_ovn_port_to_lflows(struct ovn_port *op, struct ovs_list *lflows)
>>>> +{
>>>> +    struct ovn_lflow *f;
>>>> +    LIST_FOR_EACH (f, list_node, lflows) {
>>>> +        struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
>>>> +        lfrn->lflow = f;
>>>> +        ovs_list_insert(&op->lflows, &lfrn->lflow_list_node);
>>>> +        ovs_list_insert(&f->referenced_by, &lfrn->ref_list_node);
>>>> +    }
>>>> +}
>>>> +
>>>>  static bool
>>>>  build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
>>>>                      struct ds *options_action, struct ds
>> *response_action,
>>>> @@ -15483,6 +15566,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct
>> ovn_port *op,
>>>>                                           struct hmap *lflows)
>>>>  {
>>>>      ovs_assert(op->nbsp);
>>>> +    start_collecting_lflows();
>>>>
>>>>      /* Build Logical Switch Flows. */
>>>>      build_lswitch_port_sec_op(op, lflows, actions, match);
>>>> @@ -15497,6 +15581,9 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct
>> ovn_port *op,
>>>>      /* Build Logical Router Flows. */
>>>>      build_ip_routing_flows_for_router_type_lsp(op, lr_ports, lflows);
>>>>      build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match,
>> actions);
>>>> +
>>>> +    link_ovn_port_to_lflows(op, &collected_lflows);
>>>> +    end_collecting_lflows();
>>>>  }
>>>>
>>>>  /* Helper function to combine all lflow generation which is iterated
>> by logical
>>>> @@ -16223,14 +16310,10 @@ bool lflow_handle_northd_ls_changes(struct
>> ovsdb_idl_txn *ovnsb_txn,
>>>>  {
>>>>      struct ls_change *ls_change;
>>>>      LIST_FOR_EACH (ls_change, list_node, &ls_changes->updated) {
>>>> -        ovs_list_init(&temp_lflow_list);
>>>> -        add_lflow_to_temp_list = true;
>>>>          if (!ovs_list_is_empty(&ls_change->updated_ports) ||
>>>>              !ovs_list_is_empty(&ls_change->deleted_ports)) {
>>>>              /* XXX: implement lflow index so that we can handle
>> updated and
>>>>               * deleted LSPs incrementally. */
>>>> -            ovs_list_init(&temp_lflow_list);
>>>> -            add_lflow_to_temp_list = false;
>>>>              return false;
>>>>          }
>>>>
>>>> @@ -16277,83 +16360,85 @@ bool lflow_handle_northd_ls_changes(struct
>> ovsdb_idl_txn *ovnsb_txn,
>>>>
>>  sbrec_multicast_group_update_ports_addvalue(sbmc_unknown,
>>>>                                                              op->sb);
>>>>              }
>>>> -        }
>>>> -        /* Sync the newly added flows to SB. */
>>>> -        struct ovn_lflow *lflow;
>>>> -        LIST_FOR_EACH (lflow, list_node, &temp_lflow_list) {
>>>> -            size_t n_datapaths;
>>>> -            struct ovn_datapath **datapaths_array;
>>>> -            if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH)
>> {
>>>> -                n_datapaths = ods_size(lflow_input->ls_datapaths);
>>>> -                datapaths_array = lflow_input->ls_datapaths->array;
>>>> -            } else {
>>>> -                n_datapaths = ods_size(lflow_input->lr_datapaths);
>>>> -                datapaths_array = lflow_input->lr_datapaths->array;
>>>> -            }
>>>> -            uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
>> n_datapaths);
>>>> -            ovs_assert(n_ods == 1);
>>>> -            /* There is only one datapath, so it should be moved out
>> of the
>>>> -             * group to a single 'od'. */
>>>> -            size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
>>>> -                                       n_datapaths);
>>>>
>>>> -            bitmap_set0(lflow->dpg_bitmap, index);
>>>> -            lflow->od = datapaths_array[index];
>>>> -
>>>> -            /* Logical flow should be re-hashed to allow lookups. */
>>>> -            uint32_t hash = hmap_node_hash(&lflow->hmap_node);
>>>> -            /* Remove from lflows. */
>>>> -            hmap_remove(lflows, &lflow->hmap_node);
>>>> -            hash =
>> ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid,
>>>> -                                                  hash);
>>>> -            /* Add back. */
>>>> -            hmap_insert(lflows, &lflow->hmap_node, hash);
>>>> -
>>>> -            /* Sync to SB. */
>>>> -            const struct sbrec_logical_flow *sbflow;
>>>> -            lflow->sb_uuid = uuid_random();
>>>> -            sbflow = sbrec_logical_flow_insert_persist_uuid(ovnsb_txn,
>>>> -
>>  &lflow->sb_uuid);
>>>> -            const char *pipeline =
>> ovn_stage_get_pipeline_name(lflow->stage);
>>>> -            uint8_t table = ovn_stage_get_table(lflow->stage);
>>>> -            sbrec_logical_flow_set_logical_datapath(sbflow,
>> lflow->od->sb);
>>>> -            sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
>>>> -            sbrec_logical_flow_set_pipeline(sbflow, pipeline);
>>>> -            sbrec_logical_flow_set_table_id(sbflow, table);
>>>> -            sbrec_logical_flow_set_priority(sbflow, lflow->priority);
>>>> -            sbrec_logical_flow_set_match(sbflow, lflow->match);
>>>> -            sbrec_logical_flow_set_actions(sbflow, lflow->actions);
>>>> -            if (lflow->io_port) {
>>>> -                struct smap tags = SMAP_INITIALIZER(&tags);
>>>> -                smap_add(&tags, "in_out_port", lflow->io_port);
>>>> -                sbrec_logical_flow_set_tags(sbflow, &tags);
>>>> -                smap_destroy(&tags);
>>>> -            }
>>>> -            sbrec_logical_flow_set_controller_meter(sbflow,
>> lflow->ctrl_meter);
>>>> -            /* Trim the source locator lflow->where, which looks
>> something like
>>>> -             * "ovn/northd/northd.c:1234", down to just the part
>> following the
>>>> -             * last slash, e.g. "northd.c:1234". */
>>>> -            const char *slash = strrchr(lflow->where, '/');
>>>> +            /* Sync the newly added flows to SB. */
>>>> +            struct lflow_ref_node *lfrn;
>>>> +            LIST_FOR_EACH (lfrn, lflow_list_node, &op->lflows) {
>>>> +                struct ovn_lflow *lflow = lfrn->lflow;
>>>> +                size_t n_datapaths;
>>>> +                struct ovn_datapath **datapaths_array;
>>>> +                if (ovn_stage_to_datapath_type(lflow->stage) ==
>> DP_SWITCH) {
>>>> +                    n_datapaths = ods_size(lflow_input->ls_datapaths);
>>>> +                    datapaths_array = lflow_input->ls_datapaths->array;
>>>> +                } else {
>>>> +                    n_datapaths = ods_size(lflow_input->lr_datapaths);
>>>> +                    datapaths_array = lflow_input->lr_datapaths->array;
>>>> +                }
>>>> +                uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
>> n_datapaths);
>>>> +                ovs_assert(n_ods == 1);
>>>> +                /* There is only one datapath, so it should be moved
>> out of the
>>>> +                 * group to a single 'od'. */
>>>> +                size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
>>>> +                                           n_datapaths);
>>>> +
>>>> +                bitmap_set0(lflow->dpg_bitmap, index);
>>>> +                lflow->od = datapaths_array[index];
>>>> +
>>>> +                /* Logical flow should be re-hashed to allow lookups.
>> */
>>>> +                uint32_t hash = hmap_node_hash(&lflow->hmap_node);
>>>> +                /* Remove from lflows. */
>>>> +                hmap_remove(lflows, &lflow->hmap_node);
>>>> +                hash = ovn_logical_flow_hash_datapath(
>>>> +
>>  &lflow->od->sb->header_.uuid, hash);
>>>> +                /* Add back. */
>>>> +                hmap_insert(lflows, &lflow->hmap_node, hash);
>>>> +
>>>> +                /* Sync to SB. */
>>>> +                const struct sbrec_logical_flow *sbflow;
>>>> +                lflow->sb_uuid = uuid_random();
>>>> +                sbflow = sbrec_logical_flow_insert_persist_uuid(
>>>> +                                                ovnsb_txn,
>> &lflow->sb_uuid);
>>>> +                const char *pipeline = ovn_stage_get_pipeline_name(
>>>> +
>> lflow->stage);
>>>> +                uint8_t table = ovn_stage_get_table(lflow->stage);
>>>> +                sbrec_logical_flow_set_logical_datapath(sbflow,
>> lflow->od->sb);
>>>> +                sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
>>>> +                sbrec_logical_flow_set_pipeline(sbflow, pipeline);
>>>> +                sbrec_logical_flow_set_table_id(sbflow, table);
>>>> +                sbrec_logical_flow_set_priority(sbflow,
>> lflow->priority);
>>>> +                sbrec_logical_flow_set_match(sbflow, lflow->match);
>>>> +                sbrec_logical_flow_set_actions(sbflow, lflow->actions);
>>>> +                if (lflow->io_port) {
>>>> +                    struct smap tags = SMAP_INITIALIZER(&tags);
>>>> +                    smap_add(&tags, "in_out_port", lflow->io_port);
>>>> +                    sbrec_logical_flow_set_tags(sbflow, &tags);
>>>> +                    smap_destroy(&tags);
>>>> +                }
>>>> +                sbrec_logical_flow_set_controller_meter(sbflow,
>>>> +
>>  lflow->ctrl_meter);
>>>> +                /* Trim the source locator lflow->where, which looks
>> something
>>>> +                 * like "ovn/northd/northd.c:1234", down to just the
>> part
>>>> +                 * following the last slash, e.g. "northd.c:1234". */
>>>> +                const char *slash = strrchr(lflow->where, '/');
>>>>  #if _WIN32
>>>> -            const char *backslash = strrchr(lflow->where, '\\');
>>>> -            if (!slash || backslash > slash) {
>>>> -                slash = backslash;
>>>> -            }
>>>> +                const char *backslash = strrchr(lflow->where, '\\');
>>>> +                if (!slash || backslash > slash) {
>>>> +                    slash = backslash;
>>>> +                }
>>>>  #endif
>>>> -            const char *where = slash ? slash + 1 : lflow->where;
>>>> +                const char *where = slash ? slash + 1 : lflow->where;
>>>>
>>>> -            struct smap ids = SMAP_INITIALIZER(&ids);
>>>> -            smap_add(&ids, "stage-name",
>> ovn_stage_to_str(lflow->stage));
>>>> -            smap_add(&ids, "source", where);
>>>> -            if (lflow->stage_hint) {
>>>> -                smap_add(&ids, "stage-hint", lflow->stage_hint);
>>>> +                struct smap ids = SMAP_INITIALIZER(&ids);
>>>> +                smap_add(&ids, "stage-name",
>> ovn_stage_to_str(lflow->stage));
>>>> +                smap_add(&ids, "source", where);
>>>> +                if (lflow->stage_hint) {
>>>> +                    smap_add(&ids, "stage-hint", lflow->stage_hint);
>>>> +                }
>>>> +                sbrec_logical_flow_set_external_ids(sbflow, &ids);
>>>> +                smap_destroy(&ids);
>>>>              }
>>>> -            sbrec_logical_flow_set_external_ids(sbflow, &ids);
>>>> -            smap_destroy(&ids);
>>>>          }
>>>>      }
>>>> -    ovs_list_init(&temp_lflow_list);
>>>> -    add_lflow_to_temp_list = false;
>>>>      return true;
>>>>
>>>>  }
>>>> --
>>>> 2.30.2
>>>>
>>>> _______________________________________________
>>>> dev mailing list
>>>> dev@openvswitch.org
>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>>
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Han Zhou June 30, 2023, 1:09 a.m. UTC | #5
On Thu, Jun 29, 2023 at 9:19 AM Dumitru Ceara <dceara@redhat.com> wrote:
>
> On 6/27/23 10:23, Numan Siddique wrote:
> > On Mon, Jun 26, 2023 at 10:34 PM Han Zhou <hzhou@ovn.org> wrote:
> >>
> >> On Mon, Jun 26, 2023 at 7:25 AM Numan Siddique <numans@ovn.org> wrote:
> >>>
> >>> On Sun, Jun 18, 2023 at 11:48 AM Han Zhou <hzhou@ovn.org> wrote:
> >>>>
> >>>> For incremental processing, it is important to maintain relationship
> >>>> between the inputs and the logical flows generated. This patch
creates
> >>>> the links between ovn_port and logical flows. The same data structure
> >>>> may be expanded to maintain links between logical flows and other
types
> >>>> of inputs.
> >>>>
> >>>> This patch also refactors the temp_lflow_list operations to
> >>>> collected_lflows with helper functions to start and end collecting.
It
> >>>> still uses global variables just to avoid updating all the
lflow_add_...
> >>>> related code all over the northd.c file.
> >>>>
> >>>> Signed-off-by: Han Zhou <hzhou@ovn.org>
> >>>
> >>> Hi Han,
> >>>
> >>> Please see a few comments below.  I did review all the 3 patches in
the
> >> series.
> >>> They LGTM overall.  I'd like to do some more testing before providing
my
> >> Acks.
> >>>
> >>
> >> Thanks for your review!
> >>
> >>>
> >>>> ---
> >>>>  northd/northd.c | 271
+++++++++++++++++++++++++++++++-----------------
> >>>>  1 file changed, 178 insertions(+), 93 deletions(-)
> >>>>
> >>>> diff --git a/northd/northd.c b/northd/northd.c
> >>>> index 98f528f93cfc..aa0f853ce2db 100644
> >>>> --- a/northd/northd.c
> >>>> +++ b/northd/northd.c
> >>>> @@ -1457,6 +1457,19 @@ struct ovn_port_routable_addresses {
> >>>>      size_t n_addrs;
> >>>>  };
> >>>>
> >>>> +/* A node that maintains link between an object (such as an
ovn_port)
> >> and
> >>>> + * a lflow. */
> >>>> +struct lflow_ref_node {
> >>>> +    /* This list follows different lflows referenced by the same
> >> object. List
> >>>> +     * head is, for example, ovn_port->lflows.  */
> >>>> +    struct ovs_list lflow_list_node;
> >>>> +    /* This list follows different objects that reference the same
> >> lflow. List
> >>>> +     * head is ovn_lflow->referenced_by. */
> >>>> +    struct ovs_list ref_list_node;
> >>>> +    /* The lflow. */
> >>>> +    struct ovn_lflow *lflow;
> >>>> +};
> >>>> +
> >>>>  /* A logical switch port or logical router port.
> >>>>   *
> >>>>   * In steady state, an ovn_port points to a northbound
> >> Logical_Switch_Port
> >>>> @@ -1548,6 +1561,28 @@ struct ovn_port {
> >>>>
> >>>>      /* Temporarily used for traversing a list (or hmap) of ports. */
> >>>>      bool visited;
> >>>> +
> >>>> +    /* List of struct lflow_ref_node that points to the lflows
> >> generated by
> >>>> +     * this ovn_port.
> >>>> +     *
> >>>> +     * This data is initialized and destroyed by the en_northd node,
> >> but
> >>>> +     * populated and used only by the en_lflow node. Ideally this
data
> >> should
> >>>> +     * be maintained as part of en_lflow's data (struct
lflow_data): a
> >> hash
> >>>> +     * index from ovn_port key to lflows.  However, it would be less
> >> efficient
> >>>> +     * and more complex:
> >>>> +     *
> >>>> +     * 1. It would require an extra search (using the index) to find
> >> the
> >>>> +     * lflows.
> >>>> +     *
> >>>> +     * 2. Building the index needs to be thread-safe, using either a
> >> global
> >>>> +     * lock which is obviously less efficient, or hash-based lock
> >> array which
> >>>> +     * is more complex.
> >>>> +     *
> >>>> +     * Adding the list here is more straightforward. The drawback is
> >> that we
> >>>> +     * need to keep in mind that this data belongs to en_lflow node,
> >> so never
> >>>> +     * access it from any other nodes.
> >>>> +     */
> >>>> +    struct ovs_list lflows;
> >>>>  };
> >>>
> >>>
> >>>>
> >>>>  static bool lsp_can_be_inc_processed(const struct
> >> nbrec_logical_switch_port *);
> >>>> @@ -1635,6 +1670,8 @@ ovn_port_create(struct hmap *ports, const char
> >> *key,
> >>>>      ovn_port_set_nb(op, nbsp, nbrp);
> >>>>      op->l3dgw_port = op->cr_port = NULL;
> >>>>      hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
> >>>> +
> >>>> +    ovs_list_init(&op->lflows);
> >>>>      return op;
> >>>>  }
> >>>>
> >>>> @@ -1665,6 +1702,13 @@ ovn_port_destroy_orphan(struct ovn_port *port)
> >>>>      destroy_lport_addresses(&port->proxy_arp_addrs);
> >>>>      free(port->json_key);
> >>>>      free(port->key);
> >>>> +
> >>>> +    struct lflow_ref_node *l;
> >>>> +    LIST_FOR_EACH_SAFE (l, lflow_list_node, &port->lflows) {
> >>>> +        ovs_list_remove(&l->lflow_list_node);
> >>>> +        ovs_list_remove(&l->ref_list_node);
> >>>> +        free(l);
> >>>> +    }
> >>>>      free(port);
> >>>>  }
> >>>>
> >>>> @@ -4893,6 +4937,7 @@ static struct ovn_port *
> >>>>  ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap
*ls_ports,
> >>>>                 const char *key, const struct
nbrec_logical_switch_port
> >> *nbsp,
> >>>>                 struct ovn_datapath *od, const struct
> >> sbrec_port_binding *sb,
> >>>> +               struct ovs_list *lflows,
> >>>>                 const struct sbrec_mirror_table *sbrec_mirror_table,
> >>>>                 const struct sbrec_chassis_table
*sbrec_chassis_table,
> >>>>                 struct ovsdb_idl_index *sbrec_chassis_by_name,
> >>>> @@ -4903,6 +4948,9 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn,
> >> struct hmap *ls_ports,
> >>>>      parse_lsp_addrs(op);
> >>>>      op->od = od;
> >>>>      hmap_insert(&od->ports, &op->dp_node,
> >> hmap_node_hash(&op->key_node));
> >>>> +    if (lflows) {
> >>>> +        ovs_list_splice(&op->lflows, lflows->next, lflows);
> >>>> +    }
> >>>>
> >>>>      /* Assign explicitly requested tunnel ids first. */
> >>>>      if (!ovn_port_assign_requested_tnl_id(sbrec_chassis_table, op))
{
> >>>> @@ -5082,7 +5130,7 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
> >> *ovnsb_idl_txn,
> >>>>                      goto fail;
> >>>>                  }
> >>>>                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> >>>> -                                    new_nbsp->name, new_nbsp, od,
NULL,
> >>>> +                                    new_nbsp->name, new_nbsp, od,
> >> NULL, NULL,
> >>>>                                      ni->sbrec_mirror_table,
> >>>>                                      ni->sbrec_chassis_table,
> >>>>                                      ni->sbrec_chassis_by_name,
> >>>> @@ -5114,13 +5162,16 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
> >> *ovnsb_idl_txn,
> >>>>                      op->visited = true;
> >>>>                      continue;
> >>>>                  }
> >>>> +                struct ovs_list lflows =
OVS_LIST_INITIALIZER(&lflows);
> >>>> +                ovs_list_splice(&lflows, op->lflows.next,
&op->lflows);
> >>>>                  ovn_port_destroy(&nd->ls_ports, op);
> >>>>                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> >>>> -                                    new_nbsp->name, new_nbsp, od,
sb,
> >>>> +                                    new_nbsp->name, new_nbsp, od,
sb,
> >> &lflows,
> >>>>                                      ni->sbrec_mirror_table,
> >>>>                                      ni->sbrec_chassis_table,
> >>>>                                      ni->sbrec_chassis_by_name,
> >>>>                                      ni->sbrec_chassis_by_hostname);
> >>>> +                ovs_assert(ovs_list_is_empty(&lflows));
> >>>>                  if (!op) {
> >>>>                      goto fail;
> >>>>                  }
> >>>> @@ -5577,7 +5628,8 @@ ovn_igmp_group_destroy(struct hmap
*igmp_groups,
> >>>>
> >>>>  struct ovn_lflow {
> >>>>      struct hmap_node hmap_node;
> >>>> -    struct ovs_list list_node;
> >>>> +    struct ovs_list list_node;   /* For temporary list of lflows.
> >> Don't remove
> >>>> +                                    at destroy. */
> >>>>
> >>>>      struct ovn_datapath *od;     /* 'logical_datapath' in SB schema.
> >>  */
> >>>>      unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by their
> >> 'index'.*/
> >>>> @@ -5591,6 +5643,8 @@ struct ovn_lflow {
> >>>>      size_t n_ods;                /* Number of datapaths referenced
by
> >> 'od' and
> >>>>                                    * 'dpg_bitmap'. */
> >>>>      struct ovn_dp_group *dpg;    /* Link to unique Sb datapath
group.
> >> */
> >>>> +
> >>>> +    struct ovs_list referenced_by;  /* List of struct
lflow_ref_node.
> >> */
> >>>>      const char *where;
> >>>>
> >>>>      struct uuid sb_uuid;            /* SB DB row uuid, specified by
> >> northd. */
> >>>> @@ -5640,6 +5694,7 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct
> >> ovn_datapath *od,
> >>>>                 char *stage_hint, const char *where)
> >>>>  {
> >>>>      ovs_list_init(&lflow->list_node);
> >>>> +    ovs_list_init(&lflow->referenced_by);
> >>>>      lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
> >>>>      lflow->od = od;
> >>>>      lflow->stage = stage;
> >>>> @@ -5767,20 +5822,30 @@ ovn_dp_group_add_with_reference(struct
> >> ovn_lflow *lflow_ref,
> >>>>      }
> >>>>  }
> >>>>
> >>>> +/* This global variable collects the lflows generated by
> >> do_ovn_lflow_add().
> >>>> + * start_collecting_lflows() will enable the lflow collection and
the
> >> calls to
> >>>> + * do_ovn_lflow_add (or the macros ovn_lflow_add_...) will add
> >> generated lflows
> >>>> + * to the list. end_collecting_lflows() will disable it. */
> >>>> +static thread_local struct ovs_list collected_lflows;
> >>>> +static thread_local bool collecting_lflows = false;
> >>>> +
> >>>> +static void
> >>>> +start_collecting_lflows(void)
> >>>> +{
> >>>> +    ovs_assert(!collecting_lflows);
> >>>> +    ovs_list_init(&collected_lflows);
> >>>> +    collecting_lflows = true;
> >>>> +}
> >>>> +
> >>>> +static void
> >>>> +end_collecting_lflows(void)
> >>>> +{
> >>>> +    ovs_assert(collecting_lflows);
> >>>> +    collecting_lflows = false;
> >>>> +}
> >>>> +
> >>>
> >>> I think we can avoid these functions and the thread local variable
> >>> "collected_lflows".
> >>>
> >>> I'd suggest the below
> >>>
> >>> ----------------------------
> >>>
> >>> static void
> >>> do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath
*od,
> >>>                  const unsigned long *dp_bitmap, size_t dp_bitmap_len,
> >>>                  uint32_t hash, enum ovn_stage stage, uint16_t
priority,
> >>>                  const char *match, const char *actions, const char
> >> *io_port,
> >>>                  struct ovs_list *lflow_ref_list,
> >>>                  const struct ovsdb_idl_row *stage_hint,
> >>>                  const char *where, const char *ctrl_meter)
> >>>     OVS_REQUIRES(fake_hash_mutex)
> >>> {
> >>>     ...
> >>>     ...
> >>>     /* At the end. */
> >>>     if (lflow_ref_list) {
> >>>         struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> >>>         lfrn->lflow = lflow;
> >>>         ovs_list_insert(lflow_ref_list, &lfrn->lflow_list_node);
> >>>         ovs_list_insert(&lflow->referenced_by, &lfrn->ref_list_node);
> >>>     }
> >>> }
> >>>
> >>>
> >>> #define ovn_lflow_add_with_lport_and_hint(LFLOW_MAP, OD, STAGE,
PRIORITY,
> >> \
> >>>                                           MATCH, ACTIONS,
IN_OUT_PORT, \
> >>>                                           LFLOW_REF_LIST, STAGE_HINT)
\
> >>>     ovn_lflow_add_at(LFLOW_MAP, OD, NULL, 0, STAGE, PRIORITY, MATCH,
> >> ACTIONS, \
> >>>                      IN_OUT_PORT, LFLOW_REF_LIST, NULL, STAGE_HINT, \
> >>>                      OVS_SOURCE_LOCATOR)
> >>>
> >>> And pass &op->lflows in ovn_lflow_add_with_lport_and_hint()
> >>>
> >>> -----------------------------
> >>>
> >>> What do you think ?  Definitely this would result in a bit more work
> >>> as it would require a lot of (tedious) changes.
> >>> I think this is a better approach.
> >>>
> >> Firstly, I think it is not good to use "lflow_ref_list" directly in the
> >> parameter, because there can be more than one object contributing to
the
> >> lflow generation. When we implement I-P for more inputs, a single
lflow may
> >> be referenced by multiple objects. We can't pass multiple
lflow_ref_list to
> >> the function either, because the number of such lists is unknown. For
> >> example, a lflow may be generated as a result of a LSP, a DP and a LB
> >> backend. If we want to implement I-P for LSP, DP and LB backend, we
need to
> >> track the reference for all of them. So the current idea is just to
collect
> >> a list of lflows generated by a higher level function, such as the
> >> build_lswitch_and_lrouter_iterate_by_lsp. When implementing I-P for
more
> >> than one object of the same lflow, this needs to be more fine-grained.
>
> I'm still reviewing this patch but my first impression was that we can
> probably try to use 'struct objdep_mgr' (defined in lib/objdep.h) to
> model all these (potentially many-to-many) relationships.  We do similar
> things in ovn-controller.
>
My first impression was similar, but later I figured out it is different
(and more complex than ovn-controller in general) and we will need to make
changes to objdep_mgr. However, until more input I-P is implemented I am
not sure if I will be able to get a general enough abstraction in
objdep_mgr that can handle all the use cases. On the other hand, the
references between LSP and related lflows are quite simple, so I went ahead
and implemented the lists (embeded into ovn_port and lflow without extra
hash tables), with minimum effort, to make it work at least for this
scenario and should also work for similar scenarios. I think we can
refactor the reference implementation any time but it may be better to get
more I-P implemented, during the process we can always revisit and see if
objdep_mgr can be extended or another common dependency/reference library
for ovn-northd can be created, etc. What do you think?

Regards,
Han

> >
> > Agree.  I'm working on the I-P for datapath changes in lflow engine and
> > as a flow can be referenced by multiple datapaths,  I agree this needs
to
> > be tracked properly.  And definitely that is out of scope of this patch
series.
> >
> >
> >>
> >> Secondly, I agree with you adding a new parameter to the
do_ovn_lflow_add
> >> is cleaner. For the collected list I mentioned, it can be a parameter
> >> instead of a thread local variable. However, as you mentioned, the
change
> >> is going to be all over the northd.c code, not only for the
> >> ovn_lflow_add_xxx, but also the functions calling the ovn_lflow_add_xxx
> >> macros, and upper layer functions calling those functions, and so on.
> >> Unfortunately C doesn't support optional args. At this moment I am not
sure
> >> if the interface is stable enough for the incremental-processing, so I
am
> >> not sure if it is worth such a big change. If we need to modify them
again
> >> later, all such changes are going to be wasted. On the other hand,
although
> >> the thread local variable is not the best way, I think it is still
clear
> >> and manageable, if we call the start_collecting_lflows and
> >> end_collecting_lflows in pairs. So, is it ok to leave it for this
patch and
> >> in the future when this mechanism proves to work well for more I-P, we
can
> >> have a separate patch to refactor (which will include all the tedious
> >> function call changes). What do you think?
> >
> > I agree.  It is definitely tedious to do all the changes.  I'm ok with
> > the approach taken
> > in this patch series.
> >
> > Also request to please take a look at the load balancer I-P patch
> > series -
http://patchwork.ozlabs.org/project/ovn/list/?series=361083&state=*
> >
> > Thanks
> > Numan
> >
> >>
> >> Thanks,
> >> Han
> >>
> >>> Also I'm planning to work on top of your patches to add I-P for load
> >>> balancers in lflow engine (or perhaps I-P for datapath changes)
> >>>
> >>> My idea is to have a lflow ref list stored in "struct ovn_datapath"
> >>> similar to the way you have done in this patch in "struct ovn_port"
> >>>
> >>> And while adding the flows using one of the macro variants
> >>> 'ovn_lflow_add*' pass &od->lflows.
> >>>
> >>> Please let me know your comments.
> >>>
> >>> Only concern I have with this patch is the "op->lflows" modified by
> >>> the lflow engine node.
> >>> But I agree with your added comments and also thinking to use the same
> >>> approach for datapath I-P handling,
> >>> And I don't have a better approach at the moment. So I'm fine with it.
> >>>
> >>> Thanks
> >>> Numan
> >>>
> >>>
> >>>>  /* Adds a row with the specified contents to the Logical_Flow table.
> >>>> - * Version to use when hash bucket locking is NOT required.
> >>>> - *
> >>>> - * Note: This function can add generated lflows to the global
variable
> >>>> - * temp_lflow_list as its output, controlled by the global variable
> >>>> - * add_lflow_to_temp_list. The caller of the ovn_lflow_add_...
marcros
> >> can get
> >>>> - * a list of lflows generated by setting add_lflow_to_temp_list to
> >> true. The
> >>>> - * caller is responsible for initializing the temp_lflow_list, and
also
> >>>> - * reset the add_lflow_to_temp_list to false when it is no longer
> >> needed.
> >>>> - * XXX: this mechanism is temporary and will be replaced when we add
> >> hash index
> >>>> - * to lflow_data and refactor related functions.
> >>>> - */
> >>>> -static bool add_lflow_to_temp_list = false;
> >>>> -static struct ovs_list temp_lflow_list;
> >>>> + * Version to use when hash bucket locking is NOT required. */
> >>>>  static void
> >>>>  do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath
*od,
> >>>>                   const unsigned long *dp_bitmap, size_t
dp_bitmap_len,
> >>>> @@ -5797,7 +5862,7 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
> >> struct ovn_datapath *od,
> >>>>      size_t bitmap_len = od ? ods_size(od->datapaths) :
dp_bitmap_len;
> >>>>      ovs_assert(bitmap_len);
> >>>>
> >>>> -    if (add_lflow_to_temp_list) {
> >>>> +    if (collecting_lflows) {
> >>>>          ovs_assert(od);
> >>>>          ovs_assert(!dp_bitmap);
> >>>>      } else {
> >>>> @@ -5829,8 +5894,8 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
> >> struct ovn_datapath *od,
> >>>>          thread_lflow_counter++;
> >>>>      }
> >>>>
> >>>> -    if (add_lflow_to_temp_list) {
> >>>> -        ovs_list_insert(&temp_lflow_list, &lflow->list_node);
> >>>> +    if (collecting_lflows) {
> >>>> +        ovs_list_insert(&collected_lflows, &lflow->list_node);
> >>>>      }
> >>>>  }
> >>>>
> >>>> @@ -5950,10 +6015,28 @@ ovn_lflow_destroy(struct hmap *lflows, struct
> >> ovn_lflow *lflow)
> >>>>          free(lflow->io_port);
> >>>>          free(lflow->stage_hint);
> >>>>          free(lflow->ctrl_meter);
> >>>> +        struct lflow_ref_node *l;
> >>>> +        LIST_FOR_EACH_SAFE (l, ref_list_node,
&lflow->referenced_by) {
> >>>> +            ovs_list_remove(&l->lflow_list_node);
> >>>> +            ovs_list_remove(&l->ref_list_node);
> >>>> +            free(l);
> >>>> +        }
> >>>>          free(lflow);
> >>>>      }
> >>>>  }
> >>>>
> >>>> +static void
> >>>> +link_ovn_port_to_lflows(struct ovn_port *op, struct ovs_list
*lflows)
> >>>> +{
> >>>> +    struct ovn_lflow *f;
> >>>> +    LIST_FOR_EACH (f, list_node, lflows) {
> >>>> +        struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> >>>> +        lfrn->lflow = f;
> >>>> +        ovs_list_insert(&op->lflows, &lfrn->lflow_list_node);
> >>>> +        ovs_list_insert(&f->referenced_by, &lfrn->ref_list_node);
> >>>> +    }
> >>>> +}
> >>>> +
> >>>>  static bool
> >>>>  build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
> >>>>                      struct ds *options_action, struct ds
> >> *response_action,
> >>>> @@ -15483,6 +15566,7 @@
build_lswitch_and_lrouter_iterate_by_lsp(struct
> >> ovn_port *op,
> >>>>                                           struct hmap *lflows)
> >>>>  {
> >>>>      ovs_assert(op->nbsp);
> >>>> +    start_collecting_lflows();
> >>>>
> >>>>      /* Build Logical Switch Flows. */
> >>>>      build_lswitch_port_sec_op(op, lflows, actions, match);
> >>>> @@ -15497,6 +15581,9 @@
build_lswitch_and_lrouter_iterate_by_lsp(struct
> >> ovn_port *op,
> >>>>      /* Build Logical Router Flows. */
> >>>>      build_ip_routing_flows_for_router_type_lsp(op, lr_ports,
lflows);
> >>>>      build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match,
> >> actions);
> >>>> +
> >>>> +    link_ovn_port_to_lflows(op, &collected_lflows);
> >>>> +    end_collecting_lflows();
> >>>>  }
> >>>>
> >>>>  /* Helper function to combine all lflow generation which is iterated
> >> by logical
> >>>> @@ -16223,14 +16310,10 @@ bool lflow_handle_northd_ls_changes(struct
> >> ovsdb_idl_txn *ovnsb_txn,
> >>>>  {
> >>>>      struct ls_change *ls_change;
> >>>>      LIST_FOR_EACH (ls_change, list_node, &ls_changes->updated) {
> >>>> -        ovs_list_init(&temp_lflow_list);
> >>>> -        add_lflow_to_temp_list = true;
> >>>>          if (!ovs_list_is_empty(&ls_change->updated_ports) ||
> >>>>              !ovs_list_is_empty(&ls_change->deleted_ports)) {
> >>>>              /* XXX: implement lflow index so that we can handle
> >> updated and
> >>>>               * deleted LSPs incrementally. */
> >>>> -            ovs_list_init(&temp_lflow_list);
> >>>> -            add_lflow_to_temp_list = false;
> >>>>              return false;
> >>>>          }
> >>>>
> >>>> @@ -16277,83 +16360,85 @@ bool lflow_handle_northd_ls_changes(struct
> >> ovsdb_idl_txn *ovnsb_txn,
> >>>>
> >>  sbrec_multicast_group_update_ports_addvalue(sbmc_unknown,
> >>>>                                                              op->sb);
> >>>>              }
> >>>> -        }
> >>>> -        /* Sync the newly added flows to SB. */
> >>>> -        struct ovn_lflow *lflow;
> >>>> -        LIST_FOR_EACH (lflow, list_node, &temp_lflow_list) {
> >>>> -            size_t n_datapaths;
> >>>> -            struct ovn_datapath **datapaths_array;
> >>>> -            if (ovn_stage_to_datapath_type(lflow->stage) ==
DP_SWITCH)
> >> {
> >>>> -                n_datapaths = ods_size(lflow_input->ls_datapaths);
> >>>> -                datapaths_array = lflow_input->ls_datapaths->array;
> >>>> -            } else {
> >>>> -                n_datapaths = ods_size(lflow_input->lr_datapaths);
> >>>> -                datapaths_array = lflow_input->lr_datapaths->array;
> >>>> -            }
> >>>> -            uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
> >> n_datapaths);
> >>>> -            ovs_assert(n_ods == 1);
> >>>> -            /* There is only one datapath, so it should be moved out
> >> of the
> >>>> -             * group to a single 'od'. */
> >>>> -            size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
> >>>> -                                       n_datapaths);
> >>>>
> >>>> -            bitmap_set0(lflow->dpg_bitmap, index);
> >>>> -            lflow->od = datapaths_array[index];
> >>>> -
> >>>> -            /* Logical flow should be re-hashed to allow lookups. */
> >>>> -            uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> >>>> -            /* Remove from lflows. */
> >>>> -            hmap_remove(lflows, &lflow->hmap_node);
> >>>> -            hash =
> >> ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid,
> >>>> -                                                  hash);
> >>>> -            /* Add back. */
> >>>> -            hmap_insert(lflows, &lflow->hmap_node, hash);
> >>>> -
> >>>> -            /* Sync to SB. */
> >>>> -            const struct sbrec_logical_flow *sbflow;
> >>>> -            lflow->sb_uuid = uuid_random();
> >>>> -            sbflow =
sbrec_logical_flow_insert_persist_uuid(ovnsb_txn,
> >>>> -
> >>  &lflow->sb_uuid);
> >>>> -            const char *pipeline =
> >> ovn_stage_get_pipeline_name(lflow->stage);
> >>>> -            uint8_t table = ovn_stage_get_table(lflow->stage);
> >>>> -            sbrec_logical_flow_set_logical_datapath(sbflow,
> >> lflow->od->sb);
> >>>> -            sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
> >>>> -            sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> >>>> -            sbrec_logical_flow_set_table_id(sbflow, table);
> >>>> -            sbrec_logical_flow_set_priority(sbflow,
lflow->priority);
> >>>> -            sbrec_logical_flow_set_match(sbflow, lflow->match);
> >>>> -            sbrec_logical_flow_set_actions(sbflow, lflow->actions);
> >>>> -            if (lflow->io_port) {
> >>>> -                struct smap tags = SMAP_INITIALIZER(&tags);
> >>>> -                smap_add(&tags, "in_out_port", lflow->io_port);
> >>>> -                sbrec_logical_flow_set_tags(sbflow, &tags);
> >>>> -                smap_destroy(&tags);
> >>>> -            }
> >>>> -            sbrec_logical_flow_set_controller_meter(sbflow,
> >> lflow->ctrl_meter);
> >>>> -            /* Trim the source locator lflow->where, which looks
> >> something like
> >>>> -             * "ovn/northd/northd.c:1234", down to just the part
> >> following the
> >>>> -             * last slash, e.g. "northd.c:1234". */
> >>>> -            const char *slash = strrchr(lflow->where, '/');
> >>>> +            /* Sync the newly added flows to SB. */
> >>>> +            struct lflow_ref_node *lfrn;
> >>>> +            LIST_FOR_EACH (lfrn, lflow_list_node, &op->lflows) {
> >>>> +                struct ovn_lflow *lflow = lfrn->lflow;
> >>>> +                size_t n_datapaths;
> >>>> +                struct ovn_datapath **datapaths_array;
> >>>> +                if (ovn_stage_to_datapath_type(lflow->stage) ==
> >> DP_SWITCH) {
> >>>> +                    n_datapaths =
ods_size(lflow_input->ls_datapaths);
> >>>> +                    datapaths_array =
lflow_input->ls_datapaths->array;
> >>>> +                } else {
> >>>> +                    n_datapaths =
ods_size(lflow_input->lr_datapaths);
> >>>> +                    datapaths_array =
lflow_input->lr_datapaths->array;
> >>>> +                }
> >>>> +                uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
> >> n_datapaths);
> >>>> +                ovs_assert(n_ods == 1);
> >>>> +                /* There is only one datapath, so it should be moved
> >> out of the
> >>>> +                 * group to a single 'od'. */
> >>>> +                size_t index = bitmap_scan(lflow->dpg_bitmap, true,
0,
> >>>> +                                           n_datapaths);
> >>>> +
> >>>> +                bitmap_set0(lflow->dpg_bitmap, index);
> >>>> +                lflow->od = datapaths_array[index];
> >>>> +
> >>>> +                /* Logical flow should be re-hashed to allow
lookups.
> >> */
> >>>> +                uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> >>>> +                /* Remove from lflows. */
> >>>> +                hmap_remove(lflows, &lflow->hmap_node);
> >>>> +                hash = ovn_logical_flow_hash_datapath(
> >>>> +
> >>  &lflow->od->sb->header_.uuid, hash);
> >>>> +                /* Add back. */
> >>>> +                hmap_insert(lflows, &lflow->hmap_node, hash);
> >>>> +
> >>>> +                /* Sync to SB. */
> >>>> +                const struct sbrec_logical_flow *sbflow;
> >>>> +                lflow->sb_uuid = uuid_random();
> >>>> +                sbflow = sbrec_logical_flow_insert_persist_uuid(
> >>>> +                                                ovnsb_txn,
> >> &lflow->sb_uuid);
> >>>> +                const char *pipeline = ovn_stage_get_pipeline_name(
> >>>> +
> >> lflow->stage);
> >>>> +                uint8_t table = ovn_stage_get_table(lflow->stage);
> >>>> +                sbrec_logical_flow_set_logical_datapath(sbflow,
> >> lflow->od->sb);
> >>>> +                sbrec_logical_flow_set_logical_dp_group(sbflow,
NULL);
> >>>> +                sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> >>>> +                sbrec_logical_flow_set_table_id(sbflow, table);
> >>>> +                sbrec_logical_flow_set_priority(sbflow,
> >> lflow->priority);
> >>>> +                sbrec_logical_flow_set_match(sbflow, lflow->match);
> >>>> +                sbrec_logical_flow_set_actions(sbflow,
lflow->actions);
> >>>> +                if (lflow->io_port) {
> >>>> +                    struct smap tags = SMAP_INITIALIZER(&tags);
> >>>> +                    smap_add(&tags, "in_out_port", lflow->io_port);
> >>>> +                    sbrec_logical_flow_set_tags(sbflow, &tags);
> >>>> +                    smap_destroy(&tags);
> >>>> +                }
> >>>> +                sbrec_logical_flow_set_controller_meter(sbflow,
> >>>> +
> >>  lflow->ctrl_meter);
> >>>> +                /* Trim the source locator lflow->where, which looks
> >> something
> >>>> +                 * like "ovn/northd/northd.c:1234", down to just the
> >> part
> >>>> +                 * following the last slash, e.g. "northd.c:1234".
*/
> >>>> +                const char *slash = strrchr(lflow->where, '/');
> >>>>  #if _WIN32
> >>>> -            const char *backslash = strrchr(lflow->where, '\\');
> >>>> -            if (!slash || backslash > slash) {
> >>>> -                slash = backslash;
> >>>> -            }
> >>>> +                const char *backslash = strrchr(lflow->where, '\\');
> >>>> +                if (!slash || backslash > slash) {
> >>>> +                    slash = backslash;
> >>>> +                }
> >>>>  #endif
> >>>> -            const char *where = slash ? slash + 1 : lflow->where;
> >>>> +                const char *where = slash ? slash + 1 :
lflow->where;
> >>>>
> >>>> -            struct smap ids = SMAP_INITIALIZER(&ids);
> >>>> -            smap_add(&ids, "stage-name",
> >> ovn_stage_to_str(lflow->stage));
> >>>> -            smap_add(&ids, "source", where);
> >>>> -            if (lflow->stage_hint) {
> >>>> -                smap_add(&ids, "stage-hint", lflow->stage_hint);
> >>>> +                struct smap ids = SMAP_INITIALIZER(&ids);
> >>>> +                smap_add(&ids, "stage-name",
> >> ovn_stage_to_str(lflow->stage));
> >>>> +                smap_add(&ids, "source", where);
> >>>> +                if (lflow->stage_hint) {
> >>>> +                    smap_add(&ids, "stage-hint", lflow->stage_hint);
> >>>> +                }
> >>>> +                sbrec_logical_flow_set_external_ids(sbflow, &ids);
> >>>> +                smap_destroy(&ids);
> >>>>              }
> >>>> -            sbrec_logical_flow_set_external_ids(sbflow, &ids);
> >>>> -            smap_destroy(&ids);
> >>>>          }
> >>>>      }
> >>>> -    ovs_list_init(&temp_lflow_list);
> >>>> -    add_lflow_to_temp_list = false;
> >>>>      return true;
> >>>>
> >>>>  }
> >>>> --
> >>>> 2.30.2
> >>>>
> >>>> _______________________________________________
> >>>> dev mailing list
> >>>> dev@openvswitch.org
> >>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >>>>
> >> _______________________________________________
> >> dev mailing list
> >> dev@openvswitch.org
> >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Numan Siddique June 30, 2023, 5:10 a.m. UTC | #6
On Fri, Jun 30, 2023 at 7:00 AM Han Zhou <hzhou@ovn.org> wrote:
>
> On Thu, Jun 29, 2023 at 9:19 AM Dumitru Ceara <dceara@redhat.com> wrote:
> >
> > On 6/27/23 10:23, Numan Siddique wrote:
> > > On Mon, Jun 26, 2023 at 10:34 PM Han Zhou <hzhou@ovn.org> wrote:
> > >>
> > >> On Mon, Jun 26, 2023 at 7:25 AM Numan Siddique <numans@ovn.org> wrote:
> > >>>
> > >>> On Sun, Jun 18, 2023 at 11:48 AM Han Zhou <hzhou@ovn.org> wrote:
> > >>>>
> > >>>> For incremental processing, it is important to maintain relationship
> > >>>> between the inputs and the logical flows generated. This patch
> creates
> > >>>> the links between ovn_port and logical flows. The same data structure
> > >>>> may be expanded to maintain links between logical flows and other
> types
> > >>>> of inputs.
> > >>>>
> > >>>> This patch also refactors the temp_lflow_list operations to
> > >>>> collected_lflows with helper functions to start and end collecting.
> It
> > >>>> still uses global variables just to avoid updating all the
> lflow_add_...
> > >>>> related code all over the northd.c file.
> > >>>>
> > >>>> Signed-off-by: Han Zhou <hzhou@ovn.org>
> > >>>
> > >>> Hi Han,
> > >>>
> > >>> Please see a few comments below.  I did review all the 3 patches in
> the
> > >> series.
> > >>> They LGTM overall.  I'd like to do some more testing before providing
> my
> > >> Acks.
> > >>>
> > >>
> > >> Thanks for your review!
> > >>
> > >>>
> > >>>> ---
> > >>>>  northd/northd.c | 271
> +++++++++++++++++++++++++++++++-----------------
> > >>>>  1 file changed, 178 insertions(+), 93 deletions(-)
> > >>>>
> > >>>> diff --git a/northd/northd.c b/northd/northd.c
> > >>>> index 98f528f93cfc..aa0f853ce2db 100644
> > >>>> --- a/northd/northd.c
> > >>>> +++ b/northd/northd.c
> > >>>> @@ -1457,6 +1457,19 @@ struct ovn_port_routable_addresses {
> > >>>>      size_t n_addrs;
> > >>>>  };
> > >>>>
> > >>>> +/* A node that maintains link between an object (such as an
> ovn_port)
> > >> and
> > >>>> + * a lflow. */
> > >>>> +struct lflow_ref_node {
> > >>>> +    /* This list follows different lflows referenced by the same
> > >> object. List
> > >>>> +     * head is, for example, ovn_port->lflows.  */
> > >>>> +    struct ovs_list lflow_list_node;
> > >>>> +    /* This list follows different objects that reference the same
> > >> lflow. List
> > >>>> +     * head is ovn_lflow->referenced_by. */
> > >>>> +    struct ovs_list ref_list_node;
> > >>>> +    /* The lflow. */
> > >>>> +    struct ovn_lflow *lflow;
> > >>>> +};
> > >>>> +
> > >>>>  /* A logical switch port or logical router port.
> > >>>>   *
> > >>>>   * In steady state, an ovn_port points to a northbound
> > >> Logical_Switch_Port
> > >>>> @@ -1548,6 +1561,28 @@ struct ovn_port {
> > >>>>
> > >>>>      /* Temporarily used for traversing a list (or hmap) of ports. */
> > >>>>      bool visited;
> > >>>> +
> > >>>> +    /* List of struct lflow_ref_node that points to the lflows
> > >> generated by
> > >>>> +     * this ovn_port.
> > >>>> +     *
> > >>>> +     * This data is initialized and destroyed by the en_northd node,
> > >> but
> > >>>> +     * populated and used only by the en_lflow node. Ideally this
> data
> > >> should
> > >>>> +     * be maintained as part of en_lflow's data (struct
> lflow_data): a
> > >> hash
> > >>>> +     * index from ovn_port key to lflows.  However, it would be less
> > >> efficient
> > >>>> +     * and more complex:
> > >>>> +     *
> > >>>> +     * 1. It would require an extra search (using the index) to find
> > >> the
> > >>>> +     * lflows.
> > >>>> +     *
> > >>>> +     * 2. Building the index needs to be thread-safe, using either a
> > >> global
> > >>>> +     * lock which is obviously less efficient, or hash-based lock
> > >> array which
> > >>>> +     * is more complex.
> > >>>> +     *
> > >>>> +     * Adding the list here is more straightforward. The drawback is
> > >> that we
> > >>>> +     * need to keep in mind that this data belongs to en_lflow node,
> > >> so never
> > >>>> +     * access it from any other nodes.
> > >>>> +     */
> > >>>> +    struct ovs_list lflows;
> > >>>>  };
> > >>>
> > >>>
> > >>>>
> > >>>>  static bool lsp_can_be_inc_processed(const struct
> > >> nbrec_logical_switch_port *);
> > >>>> @@ -1635,6 +1670,8 @@ ovn_port_create(struct hmap *ports, const char
> > >> *key,
> > >>>>      ovn_port_set_nb(op, nbsp, nbrp);
> > >>>>      op->l3dgw_port = op->cr_port = NULL;
> > >>>>      hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
> > >>>> +
> > >>>> +    ovs_list_init(&op->lflows);
> > >>>>      return op;
> > >>>>  }
> > >>>>
> > >>>> @@ -1665,6 +1702,13 @@ ovn_port_destroy_orphan(struct ovn_port *port)
> > >>>>      destroy_lport_addresses(&port->proxy_arp_addrs);
> > >>>>      free(port->json_key);
> > >>>>      free(port->key);
> > >>>> +
> > >>>> +    struct lflow_ref_node *l;
> > >>>> +    LIST_FOR_EACH_SAFE (l, lflow_list_node, &port->lflows) {
> > >>>> +        ovs_list_remove(&l->lflow_list_node);
> > >>>> +        ovs_list_remove(&l->ref_list_node);
> > >>>> +        free(l);
> > >>>> +    }
> > >>>>      free(port);
> > >>>>  }
> > >>>>
> > >>>> @@ -4893,6 +4937,7 @@ static struct ovn_port *
> > >>>>  ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap
> *ls_ports,
> > >>>>                 const char *key, const struct
> nbrec_logical_switch_port
> > >> *nbsp,
> > >>>>                 struct ovn_datapath *od, const struct
> > >> sbrec_port_binding *sb,
> > >>>> +               struct ovs_list *lflows,
> > >>>>                 const struct sbrec_mirror_table *sbrec_mirror_table,
> > >>>>                 const struct sbrec_chassis_table
> *sbrec_chassis_table,
> > >>>>                 struct ovsdb_idl_index *sbrec_chassis_by_name,
> > >>>> @@ -4903,6 +4948,9 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn,
> > >> struct hmap *ls_ports,
> > >>>>      parse_lsp_addrs(op);
> > >>>>      op->od = od;
> > >>>>      hmap_insert(&od->ports, &op->dp_node,
> > >> hmap_node_hash(&op->key_node));
> > >>>> +    if (lflows) {
> > >>>> +        ovs_list_splice(&op->lflows, lflows->next, lflows);
> > >>>> +    }
> > >>>>
> > >>>>      /* Assign explicitly requested tunnel ids first. */
> > >>>>      if (!ovn_port_assign_requested_tnl_id(sbrec_chassis_table, op))
> {
> > >>>> @@ -5082,7 +5130,7 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
> > >> *ovnsb_idl_txn,
> > >>>>                      goto fail;
> > >>>>                  }
> > >>>>                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> > >>>> -                                    new_nbsp->name, new_nbsp, od,
> NULL,
> > >>>> +                                    new_nbsp->name, new_nbsp, od,
> > >> NULL, NULL,
> > >>>>                                      ni->sbrec_mirror_table,
> > >>>>                                      ni->sbrec_chassis_table,
> > >>>>                                      ni->sbrec_chassis_by_name,
> > >>>> @@ -5114,13 +5162,16 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
> > >> *ovnsb_idl_txn,
> > >>>>                      op->visited = true;
> > >>>>                      continue;
> > >>>>                  }
> > >>>> +                struct ovs_list lflows =
> OVS_LIST_INITIALIZER(&lflows);
> > >>>> +                ovs_list_splice(&lflows, op->lflows.next,
> &op->lflows);
> > >>>>                  ovn_port_destroy(&nd->ls_ports, op);
> > >>>>                  op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
> > >>>> -                                    new_nbsp->name, new_nbsp, od,
> sb,
> > >>>> +                                    new_nbsp->name, new_nbsp, od,
> sb,
> > >> &lflows,
> > >>>>                                      ni->sbrec_mirror_table,
> > >>>>                                      ni->sbrec_chassis_table,
> > >>>>                                      ni->sbrec_chassis_by_name,
> > >>>>                                      ni->sbrec_chassis_by_hostname);
> > >>>> +                ovs_assert(ovs_list_is_empty(&lflows));
> > >>>>                  if (!op) {
> > >>>>                      goto fail;
> > >>>>                  }
> > >>>> @@ -5577,7 +5628,8 @@ ovn_igmp_group_destroy(struct hmap
> *igmp_groups,
> > >>>>
> > >>>>  struct ovn_lflow {
> > >>>>      struct hmap_node hmap_node;
> > >>>> -    struct ovs_list list_node;
> > >>>> +    struct ovs_list list_node;   /* For temporary list of lflows.
> > >> Don't remove
> > >>>> +                                    at destroy. */
> > >>>>
> > >>>>      struct ovn_datapath *od;     /* 'logical_datapath' in SB schema.
> > >>  */
> > >>>>      unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by their
> > >> 'index'.*/
> > >>>> @@ -5591,6 +5643,8 @@ struct ovn_lflow {
> > >>>>      size_t n_ods;                /* Number of datapaths referenced
> by
> > >> 'od' and
> > >>>>                                    * 'dpg_bitmap'. */
> > >>>>      struct ovn_dp_group *dpg;    /* Link to unique Sb datapath
> group.
> > >> */
> > >>>> +
> > >>>> +    struct ovs_list referenced_by;  /* List of struct
> lflow_ref_node.
> > >> */
> > >>>>      const char *where;
> > >>>>
> > >>>>      struct uuid sb_uuid;            /* SB DB row uuid, specified by
> > >> northd. */
> > >>>> @@ -5640,6 +5694,7 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct
> > >> ovn_datapath *od,
> > >>>>                 char *stage_hint, const char *where)
> > >>>>  {
> > >>>>      ovs_list_init(&lflow->list_node);
> > >>>> +    ovs_list_init(&lflow->referenced_by);
> > >>>>      lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
> > >>>>      lflow->od = od;
> > >>>>      lflow->stage = stage;
> > >>>> @@ -5767,20 +5822,30 @@ ovn_dp_group_add_with_reference(struct
> > >> ovn_lflow *lflow_ref,
> > >>>>      }
> > >>>>  }
> > >>>>
> > >>>> +/* This global variable collects the lflows generated by
> > >> do_ovn_lflow_add().
> > >>>> + * start_collecting_lflows() will enable the lflow collection and
> the
> > >> calls to
> > >>>> + * do_ovn_lflow_add (or the macros ovn_lflow_add_...) will add
> > >> generated lflows
> > >>>> + * to the list. end_collecting_lflows() will disable it. */
> > >>>> +static thread_local struct ovs_list collected_lflows;
> > >>>> +static thread_local bool collecting_lflows = false;
> > >>>> +
> > >>>> +static void
> > >>>> +start_collecting_lflows(void)
> > >>>> +{
> > >>>> +    ovs_assert(!collecting_lflows);
> > >>>> +    ovs_list_init(&collected_lflows);
> > >>>> +    collecting_lflows = true;
> > >>>> +}
> > >>>> +
> > >>>> +static void
> > >>>> +end_collecting_lflows(void)
> > >>>> +{
> > >>>> +    ovs_assert(collecting_lflows);
> > >>>> +    collecting_lflows = false;
> > >>>> +}
> > >>>> +
> > >>>
> > >>> I think we can avoid these functions and the thread local variable
> > >>> "collected_lflows".
> > >>>
> > >>> I'd suggest the below
> > >>>
> > >>> ----------------------------
> > >>>
> > >>> static void
> > >>> do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath
> *od,
> > >>>                  const unsigned long *dp_bitmap, size_t dp_bitmap_len,
> > >>>                  uint32_t hash, enum ovn_stage stage, uint16_t
> priority,
> > >>>                  const char *match, const char *actions, const char
> > >> *io_port,
> > >>>                  struct ovs_list *lflow_ref_list,
> > >>>                  const struct ovsdb_idl_row *stage_hint,
> > >>>                  const char *where, const char *ctrl_meter)
> > >>>     OVS_REQUIRES(fake_hash_mutex)
> > >>> {
> > >>>     ...
> > >>>     ...
> > >>>     /* At the end. */
> > >>>     if (lflow_ref_list) {
> > >>>         struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> > >>>         lfrn->lflow = lflow;
> > >>>         ovs_list_insert(lflow_ref_list, &lfrn->lflow_list_node);
> > >>>         ovs_list_insert(&lflow->referenced_by, &lfrn->ref_list_node);
> > >>>     }
> > >>> }
> > >>>
> > >>>
> > >>> #define ovn_lflow_add_with_lport_and_hint(LFLOW_MAP, OD, STAGE,
> PRIORITY,
> > >> \
> > >>>                                           MATCH, ACTIONS,
> IN_OUT_PORT, \
> > >>>                                           LFLOW_REF_LIST, STAGE_HINT)
> \
> > >>>     ovn_lflow_add_at(LFLOW_MAP, OD, NULL, 0, STAGE, PRIORITY, MATCH,
> > >> ACTIONS, \
> > >>>                      IN_OUT_PORT, LFLOW_REF_LIST, NULL, STAGE_HINT, \
> > >>>                      OVS_SOURCE_LOCATOR)
> > >>>
> > >>> And pass &op->lflows in ovn_lflow_add_with_lport_and_hint()
> > >>>
> > >>> -----------------------------
> > >>>
> > >>> What do you think ?  Definitely this would result in a bit more work
> > >>> as it would require a lot of (tedious) changes.
> > >>> I think this is a better approach.
> > >>>
> > >> Firstly, I think it is not good to use "lflow_ref_list" directly in the
> > >> parameter, because there can be more than one object contributing to
> the
> > >> lflow generation. When we implement I-P for more inputs, a single
> lflow may
> > >> be referenced by multiple objects. We can't pass multiple
> lflow_ref_list to
> > >> the function either, because the number of such lists is unknown. For
> > >> example, a lflow may be generated as a result of a LSP, a DP and a LB
> > >> backend. If we want to implement I-P for LSP, DP and LB backend, we
> need to
> > >> track the reference for all of them. So the current idea is just to
> collect
> > >> a list of lflows generated by a higher level function, such as the
> > >> build_lswitch_and_lrouter_iterate_by_lsp. When implementing I-P for
> more
> > >> than one object of the same lflow, this needs to be more fine-grained.
> >
> > I'm still reviewing this patch but my first impression was that we can
> > probably try to use 'struct objdep_mgr' (defined in lib/objdep.h) to
> > model all these (potentially many-to-many) relationships.  We do similar
> > things in ovn-controller.
> >
> My first impression was similar, but later I figured out it is different
> (and more complex than ovn-controller in general) and we will need to make
> changes to objdep_mgr. However, until more input I-P is implemented I am
> not sure if I will be able to get a general enough abstraction in
> objdep_mgr that can handle all the use cases. On the other hand, the
> references between LSP and related lflows are quite simple, so I went ahead
> and implemented the lists (embeded into ovn_port and lflow without extra
> hash tables), with minimum effort, to make it work at least for this
> scenario and should also work for similar scenarios. I think we can
> refactor the reference implementation any time but it may be better to get
> more I-P implemented, during the process we can always revisit and see if
> objdep_mgr can be extended or another common dependency/reference library
> for ovn-northd can be created, etc. What do you think?

As part of my refactor and northd I-P for load balancers and data
paths,  I'll try to see
if objdep_mgr can be used.  For now with just lport I-P,  I suppose it's fine.

Acked-by: Numan Siddique <numans@ovn.org>

Numan

>
> Regards,
> Han
>
> > >
> > > Agree.  I'm working on the I-P for datapath changes in lflow engine and
> > > as a flow can be referenced by multiple datapaths,  I agree this needs
> to
> > > be tracked properly.  And definitely that is out of scope of this patch
> series.
> > >
> > >
> > >>
> > >> Secondly, I agree with you adding a new parameter to the
> do_ovn_lflow_add
> > >> is cleaner. For the collected list I mentioned, it can be a parameter
> > >> instead of a thread local variable. However, as you mentioned, the
> change
> > >> is going to be all over the northd.c code, not only for the
> > >> ovn_lflow_add_xxx, but also the functions calling the ovn_lflow_add_xxx
> > >> macros, and upper layer functions calling those functions, and so on.
> > >> Unfortunately C doesn't support optional args. At this moment I am not
> sure
> > >> if the interface is stable enough for the incremental-processing, so I
> am
> > >> not sure if it is worth such a big change. If we need to modify them
> again
> > >> later, all such changes are going to be wasted. On the other hand,
> although
> > >> the thread local variable is not the best way, I think it is still
> clear
> > >> and manageable, if we call the start_collecting_lflows and
> > >> end_collecting_lflows in pairs. So, is it ok to leave it for this
> patch and
> > >> in the future when this mechanism proves to work well for more I-P, we
> can
> > >> have a separate patch to refactor (which will include all the tedious
> > >> function call changes). What do you think?
> > >
> > > I agree.  It is definitely tedious to do all the changes.  I'm ok with
> > > the approach taken
> > > in this patch series.
> > >
> > > Also request to please take a look at the load balancer I-P patch
> > > series -
> http://patchwork.ozlabs.org/project/ovn/list/?series=361083&state=*
> > >
> > > Thanks
> > > Numan
> > >
> > >>
> > >> Thanks,
> > >> Han
> > >>
> > >>> Also I'm planning to work on top of your patches to add I-P for load
> > >>> balancers in lflow engine (or perhaps I-P for datapath changes)
> > >>>
> > >>> My idea is to have a lflow ref list stored in "struct ovn_datapath"
> > >>> similar to the way you have done in this patch in "struct ovn_port"
> > >>>
> > >>> And while adding the flows using one of the macro variants
> > >>> 'ovn_lflow_add*' pass &od->lflows.
> > >>>
> > >>> Please let me know your comments.
> > >>>
> > >>> Only concern I have with this patch is the "op->lflows" modified by
> > >>> the lflow engine node.
> > >>> But I agree with your added comments and also thinking to use the same
> > >>> approach for datapath I-P handling,
> > >>> And I don't have a better approach at the moment. So I'm fine with it.
> > >>>
> > >>> Thanks
> > >>> Numan
> > >>>
> > >>>
> > >>>>  /* Adds a row with the specified contents to the Logical_Flow table.
> > >>>> - * Version to use when hash bucket locking is NOT required.
> > >>>> - *
> > >>>> - * Note: This function can add generated lflows to the global
> variable
> > >>>> - * temp_lflow_list as its output, controlled by the global variable
> > >>>> - * add_lflow_to_temp_list. The caller of the ovn_lflow_add_...
> marcros
> > >> can get
> > >>>> - * a list of lflows generated by setting add_lflow_to_temp_list to
> > >> true. The
> > >>>> - * caller is responsible for initializing the temp_lflow_list, and
> also
> > >>>> - * reset the add_lflow_to_temp_list to false when it is no longer
> > >> needed.
> > >>>> - * XXX: this mechanism is temporary and will be replaced when we add
> > >> hash index
> > >>>> - * to lflow_data and refactor related functions.
> > >>>> - */
> > >>>> -static bool add_lflow_to_temp_list = false;
> > >>>> -static struct ovs_list temp_lflow_list;
> > >>>> + * Version to use when hash bucket locking is NOT required. */
> > >>>>  static void
> > >>>>  do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath
> *od,
> > >>>>                   const unsigned long *dp_bitmap, size_t
> dp_bitmap_len,
> > >>>> @@ -5797,7 +5862,7 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
> > >> struct ovn_datapath *od,
> > >>>>      size_t bitmap_len = od ? ods_size(od->datapaths) :
> dp_bitmap_len;
> > >>>>      ovs_assert(bitmap_len);
> > >>>>
> > >>>> -    if (add_lflow_to_temp_list) {
> > >>>> +    if (collecting_lflows) {
> > >>>>          ovs_assert(od);
> > >>>>          ovs_assert(!dp_bitmap);
> > >>>>      } else {
> > >>>> @@ -5829,8 +5894,8 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
> > >> struct ovn_datapath *od,
> > >>>>          thread_lflow_counter++;
> > >>>>      }
> > >>>>
> > >>>> -    if (add_lflow_to_temp_list) {
> > >>>> -        ovs_list_insert(&temp_lflow_list, &lflow->list_node);
> > >>>> +    if (collecting_lflows) {
> > >>>> +        ovs_list_insert(&collected_lflows, &lflow->list_node);
> > >>>>      }
> > >>>>  }
> > >>>>
> > >>>> @@ -5950,10 +6015,28 @@ ovn_lflow_destroy(struct hmap *lflows, struct
> > >> ovn_lflow *lflow)
> > >>>>          free(lflow->io_port);
> > >>>>          free(lflow->stage_hint);
> > >>>>          free(lflow->ctrl_meter);
> > >>>> +        struct lflow_ref_node *l;
> > >>>> +        LIST_FOR_EACH_SAFE (l, ref_list_node,
> &lflow->referenced_by) {
> > >>>> +            ovs_list_remove(&l->lflow_list_node);
> > >>>> +            ovs_list_remove(&l->ref_list_node);
> > >>>> +            free(l);
> > >>>> +        }
> > >>>>          free(lflow);
> > >>>>      }
> > >>>>  }
> > >>>>
> > >>>> +static void
> > >>>> +link_ovn_port_to_lflows(struct ovn_port *op, struct ovs_list
> *lflows)
> > >>>> +{
> > >>>> +    struct ovn_lflow *f;
> > >>>> +    LIST_FOR_EACH (f, list_node, lflows) {
> > >>>> +        struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> > >>>> +        lfrn->lflow = f;
> > >>>> +        ovs_list_insert(&op->lflows, &lfrn->lflow_list_node);
> > >>>> +        ovs_list_insert(&f->referenced_by, &lfrn->ref_list_node);
> > >>>> +    }
> > >>>> +}
> > >>>> +
> > >>>>  static bool
> > >>>>  build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
> > >>>>                      struct ds *options_action, struct ds
> > >> *response_action,
> > >>>> @@ -15483,6 +15566,7 @@
> build_lswitch_and_lrouter_iterate_by_lsp(struct
> > >> ovn_port *op,
> > >>>>                                           struct hmap *lflows)
> > >>>>  {
> > >>>>      ovs_assert(op->nbsp);
> > >>>> +    start_collecting_lflows();
> > >>>>
> > >>>>      /* Build Logical Switch Flows. */
> > >>>>      build_lswitch_port_sec_op(op, lflows, actions, match);
> > >>>> @@ -15497,6 +15581,9 @@
> build_lswitch_and_lrouter_iterate_by_lsp(struct
> > >> ovn_port *op,
> > >>>>      /* Build Logical Router Flows. */
> > >>>>      build_ip_routing_flows_for_router_type_lsp(op, lr_ports,
> lflows);
> > >>>>      build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match,
> > >> actions);
> > >>>> +
> > >>>> +    link_ovn_port_to_lflows(op, &collected_lflows);
> > >>>> +    end_collecting_lflows();
> > >>>>  }
> > >>>>
> > >>>>  /* Helper function to combine all lflow generation which is iterated
> > >> by logical
> > >>>> @@ -16223,14 +16310,10 @@ bool lflow_handle_northd_ls_changes(struct
> > >> ovsdb_idl_txn *ovnsb_txn,
> > >>>>  {
> > >>>>      struct ls_change *ls_change;
> > >>>>      LIST_FOR_EACH (ls_change, list_node, &ls_changes->updated) {
> > >>>> -        ovs_list_init(&temp_lflow_list);
> > >>>> -        add_lflow_to_temp_list = true;
> > >>>>          if (!ovs_list_is_empty(&ls_change->updated_ports) ||
> > >>>>              !ovs_list_is_empty(&ls_change->deleted_ports)) {
> > >>>>              /* XXX: implement lflow index so that we can handle
> > >> updated and
> > >>>>               * deleted LSPs incrementally. */
> > >>>> -            ovs_list_init(&temp_lflow_list);
> > >>>> -            add_lflow_to_temp_list = false;
> > >>>>              return false;
> > >>>>          }
> > >>>>
> > >>>> @@ -16277,83 +16360,85 @@ bool lflow_handle_northd_ls_changes(struct
> > >> ovsdb_idl_txn *ovnsb_txn,
> > >>>>
> > >>  sbrec_multicast_group_update_ports_addvalue(sbmc_unknown,
> > >>>>                                                              op->sb);
> > >>>>              }
> > >>>> -        }
> > >>>> -        /* Sync the newly added flows to SB. */
> > >>>> -        struct ovn_lflow *lflow;
> > >>>> -        LIST_FOR_EACH (lflow, list_node, &temp_lflow_list) {
> > >>>> -            size_t n_datapaths;
> > >>>> -            struct ovn_datapath **datapaths_array;
> > >>>> -            if (ovn_stage_to_datapath_type(lflow->stage) ==
> DP_SWITCH)
> > >> {
> > >>>> -                n_datapaths = ods_size(lflow_input->ls_datapaths);
> > >>>> -                datapaths_array = lflow_input->ls_datapaths->array;
> > >>>> -            } else {
> > >>>> -                n_datapaths = ods_size(lflow_input->lr_datapaths);
> > >>>> -                datapaths_array = lflow_input->lr_datapaths->array;
> > >>>> -            }
> > >>>> -            uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
> > >> n_datapaths);
> > >>>> -            ovs_assert(n_ods == 1);
> > >>>> -            /* There is only one datapath, so it should be moved out
> > >> of the
> > >>>> -             * group to a single 'od'. */
> > >>>> -            size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
> > >>>> -                                       n_datapaths);
> > >>>>
> > >>>> -            bitmap_set0(lflow->dpg_bitmap, index);
> > >>>> -            lflow->od = datapaths_array[index];
> > >>>> -
> > >>>> -            /* Logical flow should be re-hashed to allow lookups. */
> > >>>> -            uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> > >>>> -            /* Remove from lflows. */
> > >>>> -            hmap_remove(lflows, &lflow->hmap_node);
> > >>>> -            hash =
> > >> ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid,
> > >>>> -                                                  hash);
> > >>>> -            /* Add back. */
> > >>>> -            hmap_insert(lflows, &lflow->hmap_node, hash);
> > >>>> -
> > >>>> -            /* Sync to SB. */
> > >>>> -            const struct sbrec_logical_flow *sbflow;
> > >>>> -            lflow->sb_uuid = uuid_random();
> > >>>> -            sbflow =
> sbrec_logical_flow_insert_persist_uuid(ovnsb_txn,
> > >>>> -
> > >>  &lflow->sb_uuid);
> > >>>> -            const char *pipeline =
> > >> ovn_stage_get_pipeline_name(lflow->stage);
> > >>>> -            uint8_t table = ovn_stage_get_table(lflow->stage);
> > >>>> -            sbrec_logical_flow_set_logical_datapath(sbflow,
> > >> lflow->od->sb);
> > >>>> -            sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
> > >>>> -            sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> > >>>> -            sbrec_logical_flow_set_table_id(sbflow, table);
> > >>>> -            sbrec_logical_flow_set_priority(sbflow,
> lflow->priority);
> > >>>> -            sbrec_logical_flow_set_match(sbflow, lflow->match);
> > >>>> -            sbrec_logical_flow_set_actions(sbflow, lflow->actions);
> > >>>> -            if (lflow->io_port) {
> > >>>> -                struct smap tags = SMAP_INITIALIZER(&tags);
> > >>>> -                smap_add(&tags, "in_out_port", lflow->io_port);
> > >>>> -                sbrec_logical_flow_set_tags(sbflow, &tags);
> > >>>> -                smap_destroy(&tags);
> > >>>> -            }
> > >>>> -            sbrec_logical_flow_set_controller_meter(sbflow,
> > >> lflow->ctrl_meter);
> > >>>> -            /* Trim the source locator lflow->where, which looks
> > >> something like
> > >>>> -             * "ovn/northd/northd.c:1234", down to just the part
> > >> following the
> > >>>> -             * last slash, e.g. "northd.c:1234". */
> > >>>> -            const char *slash = strrchr(lflow->where, '/');
> > >>>> +            /* Sync the newly added flows to SB. */
> > >>>> +            struct lflow_ref_node *lfrn;
> > >>>> +            LIST_FOR_EACH (lfrn, lflow_list_node, &op->lflows) {
> > >>>> +                struct ovn_lflow *lflow = lfrn->lflow;
> > >>>> +                size_t n_datapaths;
> > >>>> +                struct ovn_datapath **datapaths_array;
> > >>>> +                if (ovn_stage_to_datapath_type(lflow->stage) ==
> > >> DP_SWITCH) {
> > >>>> +                    n_datapaths =
> ods_size(lflow_input->ls_datapaths);
> > >>>> +                    datapaths_array =
> lflow_input->ls_datapaths->array;
> > >>>> +                } else {
> > >>>> +                    n_datapaths =
> ods_size(lflow_input->lr_datapaths);
> > >>>> +                    datapaths_array =
> lflow_input->lr_datapaths->array;
> > >>>> +                }
> > >>>> +                uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
> > >> n_datapaths);
> > >>>> +                ovs_assert(n_ods == 1);
> > >>>> +                /* There is only one datapath, so it should be moved
> > >> out of the
> > >>>> +                 * group to a single 'od'. */
> > >>>> +                size_t index = bitmap_scan(lflow->dpg_bitmap, true,
> 0,
> > >>>> +                                           n_datapaths);
> > >>>> +
> > >>>> +                bitmap_set0(lflow->dpg_bitmap, index);
> > >>>> +                lflow->od = datapaths_array[index];
> > >>>> +
> > >>>> +                /* Logical flow should be re-hashed to allow
> lookups.
> > >> */
> > >>>> +                uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> > >>>> +                /* Remove from lflows. */
> > >>>> +                hmap_remove(lflows, &lflow->hmap_node);
> > >>>> +                hash = ovn_logical_flow_hash_datapath(
> > >>>> +
> > >>  &lflow->od->sb->header_.uuid, hash);
> > >>>> +                /* Add back. */
> > >>>> +                hmap_insert(lflows, &lflow->hmap_node, hash);
> > >>>> +
> > >>>> +                /* Sync to SB. */
> > >>>> +                const struct sbrec_logical_flow *sbflow;
> > >>>> +                lflow->sb_uuid = uuid_random();
> > >>>> +                sbflow = sbrec_logical_flow_insert_persist_uuid(
> > >>>> +                                                ovnsb_txn,
> > >> &lflow->sb_uuid);
> > >>>> +                const char *pipeline = ovn_stage_get_pipeline_name(
> > >>>> +
> > >> lflow->stage);
> > >>>> +                uint8_t table = ovn_stage_get_table(lflow->stage);
> > >>>> +                sbrec_logical_flow_set_logical_datapath(sbflow,
> > >> lflow->od->sb);
> > >>>> +                sbrec_logical_flow_set_logical_dp_group(sbflow,
> NULL);
> > >>>> +                sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> > >>>> +                sbrec_logical_flow_set_table_id(sbflow, table);
> > >>>> +                sbrec_logical_flow_set_priority(sbflow,
> > >> lflow->priority);
> > >>>> +                sbrec_logical_flow_set_match(sbflow, lflow->match);
> > >>>> +                sbrec_logical_flow_set_actions(sbflow,
> lflow->actions);
> > >>>> +                if (lflow->io_port) {
> > >>>> +                    struct smap tags = SMAP_INITIALIZER(&tags);
> > >>>> +                    smap_add(&tags, "in_out_port", lflow->io_port);
> > >>>> +                    sbrec_logical_flow_set_tags(sbflow, &tags);
> > >>>> +                    smap_destroy(&tags);
> > >>>> +                }
> > >>>> +                sbrec_logical_flow_set_controller_meter(sbflow,
> > >>>> +
> > >>  lflow->ctrl_meter);
> > >>>> +                /* Trim the source locator lflow->where, which looks
> > >> something
> > >>>> +                 * like "ovn/northd/northd.c:1234", down to just the
> > >> part
> > >>>> +                 * following the last slash, e.g. "northd.c:1234".
> */
> > >>>> +                const char *slash = strrchr(lflow->where, '/');
> > >>>>  #if _WIN32
> > >>>> -            const char *backslash = strrchr(lflow->where, '\\');
> > >>>> -            if (!slash || backslash > slash) {
> > >>>> -                slash = backslash;
> > >>>> -            }
> > >>>> +                const char *backslash = strrchr(lflow->where, '\\');
> > >>>> +                if (!slash || backslash > slash) {
> > >>>> +                    slash = backslash;
> > >>>> +                }
> > >>>>  #endif
> > >>>> -            const char *where = slash ? slash + 1 : lflow->where;
> > >>>> +                const char *where = slash ? slash + 1 :
> lflow->where;
> > >>>>
> > >>>> -            struct smap ids = SMAP_INITIALIZER(&ids);
> > >>>> -            smap_add(&ids, "stage-name",
> > >> ovn_stage_to_str(lflow->stage));
> > >>>> -            smap_add(&ids, "source", where);
> > >>>> -            if (lflow->stage_hint) {
> > >>>> -                smap_add(&ids, "stage-hint", lflow->stage_hint);
> > >>>> +                struct smap ids = SMAP_INITIALIZER(&ids);
> > >>>> +                smap_add(&ids, "stage-name",
> > >> ovn_stage_to_str(lflow->stage));
> > >>>> +                smap_add(&ids, "source", where);
> > >>>> +                if (lflow->stage_hint) {
> > >>>> +                    smap_add(&ids, "stage-hint", lflow->stage_hint);
> > >>>> +                }
> > >>>> +                sbrec_logical_flow_set_external_ids(sbflow, &ids);
> > >>>> +                smap_destroy(&ids);
> > >>>>              }
> > >>>> -            sbrec_logical_flow_set_external_ids(sbflow, &ids);
> > >>>> -            smap_destroy(&ids);
> > >>>>          }
> > >>>>      }
> > >>>> -    ovs_list_init(&temp_lflow_list);
> > >>>> -    add_lflow_to_temp_list = false;
> > >>>>      return true;
> > >>>>
> > >>>>  }
> > >>>> --
> > >>>> 2.30.2
> > >>>>
> > >>>> _______________________________________________
> > >>>> dev mailing list
> > >>>> dev@openvswitch.org
> > >>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >>>>
> > >> _______________________________________________
> > >> dev mailing list
> > >> dev@openvswitch.org
> > >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Han Zhou July 6, 2023, 10:13 a.m. UTC | #7
On Thu, Jun 29, 2023 at 10:10 PM Numan Siddique <numans@ovn.org> wrote:
>
> On Fri, Jun 30, 2023 at 7:00 AM Han Zhou <hzhou@ovn.org> wrote:
> >
> > On Thu, Jun 29, 2023 at 9:19 AM Dumitru Ceara <dceara@redhat.com> wrote:
> > >
> > > On 6/27/23 10:23, Numan Siddique wrote:
> > > > On Mon, Jun 26, 2023 at 10:34 PM Han Zhou <hzhou@ovn.org> wrote:
> > > >>
> > > >> On Mon, Jun 26, 2023 at 7:25 AM Numan Siddique <numans@ovn.org>
wrote:
> > > >>>
> > > >>> On Sun, Jun 18, 2023 at 11:48 AM Han Zhou <hzhou@ovn.org> wrote:
> > > >>>>
> > > >>>> For incremental processing, it is important to maintain
relationship
> > > >>>> between the inputs and the logical flows generated. This patch
> > creates
> > > >>>> the links between ovn_port and logical flows. The same data
structure
> > > >>>> may be expanded to maintain links between logical flows and other
> > types
> > > >>>> of inputs.
> > > >>>>
> > > >>>> This patch also refactors the temp_lflow_list operations to
> > > >>>> collected_lflows with helper functions to start and end
collecting.
> > It
> > > >>>> still uses global variables just to avoid updating all the
> > lflow_add_...
> > > >>>> related code all over the northd.c file.
> > > >>>>
> > > >>>> Signed-off-by: Han Zhou <hzhou@ovn.org>
> > > >>>
> > > >>> Hi Han,
> > > >>>
> > > >>> Please see a few comments below.  I did review all the 3 patches
in
> > the
> > > >> series.
> > > >>> They LGTM overall.  I'd like to do some more testing before
providing
> > my
> > > >> Acks.
> > > >>>
> > > >>
> > > >> Thanks for your review!
> > > >>
> > > >>>
> > > >>>> ---
> > > >>>>  northd/northd.c | 271
> > +++++++++++++++++++++++++++++++-----------------
> > > >>>>  1 file changed, 178 insertions(+), 93 deletions(-)
> > > >>>>
> > > >>>> diff --git a/northd/northd.c b/northd/northd.c
> > > >>>> index 98f528f93cfc..aa0f853ce2db 100644
> > > >>>> --- a/northd/northd.c
> > > >>>> +++ b/northd/northd.c
> > > >>>> @@ -1457,6 +1457,19 @@ struct ovn_port_routable_addresses {
> > > >>>>      size_t n_addrs;
> > > >>>>  };
> > > >>>>
> > > >>>> +/* A node that maintains link between an object (such as an
> > ovn_port)
> > > >> and
> > > >>>> + * a lflow. */
> > > >>>> +struct lflow_ref_node {
> > > >>>> +    /* This list follows different lflows referenced by the same
> > > >> object. List
> > > >>>> +     * head is, for example, ovn_port->lflows.  */
> > > >>>> +    struct ovs_list lflow_list_node;
> > > >>>> +    /* This list follows different objects that reference the
same
> > > >> lflow. List
> > > >>>> +     * head is ovn_lflow->referenced_by. */
> > > >>>> +    struct ovs_list ref_list_node;
> > > >>>> +    /* The lflow. */
> > > >>>> +    struct ovn_lflow *lflow;
> > > >>>> +};
> > > >>>> +
> > > >>>>  /* A logical switch port or logical router port.
> > > >>>>   *
> > > >>>>   * In steady state, an ovn_port points to a northbound
> > > >> Logical_Switch_Port
> > > >>>> @@ -1548,6 +1561,28 @@ struct ovn_port {
> > > >>>>
> > > >>>>      /* Temporarily used for traversing a list (or hmap) of
ports. */
> > > >>>>      bool visited;
> > > >>>> +
> > > >>>> +    /* List of struct lflow_ref_node that points to the lflows
> > > >> generated by
> > > >>>> +     * this ovn_port.
> > > >>>> +     *
> > > >>>> +     * This data is initialized and destroyed by the en_northd
node,
> > > >> but
> > > >>>> +     * populated and used only by the en_lflow node. Ideally
this
> > data
> > > >> should
> > > >>>> +     * be maintained as part of en_lflow's data (struct
> > lflow_data): a
> > > >> hash
> > > >>>> +     * index from ovn_port key to lflows.  However, it would be
less
> > > >> efficient
> > > >>>> +     * and more complex:
> > > >>>> +     *
> > > >>>> +     * 1. It would require an extra search (using the index) to
find
> > > >> the
> > > >>>> +     * lflows.
> > > >>>> +     *
> > > >>>> +     * 2. Building the index needs to be thread-safe, using
either a
> > > >> global
> > > >>>> +     * lock which is obviously less efficient, or hash-based
lock
> > > >> array which
> > > >>>> +     * is more complex.
> > > >>>> +     *
> > > >>>> +     * Adding the list here is more straightforward. The
drawback is
> > > >> that we
> > > >>>> +     * need to keep in mind that this data belongs to en_lflow
node,
> > > >> so never
> > > >>>> +     * access it from any other nodes.
> > > >>>> +     */
> > > >>>> +    struct ovs_list lflows;
> > > >>>>  };
> > > >>>
> > > >>>
> > > >>>>
> > > >>>>  static bool lsp_can_be_inc_processed(const struct
> > > >> nbrec_logical_switch_port *);
> > > >>>> @@ -1635,6 +1670,8 @@ ovn_port_create(struct hmap *ports, const
char
> > > >> *key,
> > > >>>>      ovn_port_set_nb(op, nbsp, nbrp);
> > > >>>>      op->l3dgw_port = op->cr_port = NULL;
> > > >>>>      hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
> > > >>>> +
> > > >>>> +    ovs_list_init(&op->lflows);
> > > >>>>      return op;
> > > >>>>  }
> > > >>>>
> > > >>>> @@ -1665,6 +1702,13 @@ ovn_port_destroy_orphan(struct ovn_port
*port)
> > > >>>>      destroy_lport_addresses(&port->proxy_arp_addrs);
> > > >>>>      free(port->json_key);
> > > >>>>      free(port->key);
> > > >>>> +
> > > >>>> +    struct lflow_ref_node *l;
> > > >>>> +    LIST_FOR_EACH_SAFE (l, lflow_list_node, &port->lflows) {
> > > >>>> +        ovs_list_remove(&l->lflow_list_node);
> > > >>>> +        ovs_list_remove(&l->ref_list_node);
> > > >>>> +        free(l);
> > > >>>> +    }
> > > >>>>      free(port);
> > > >>>>  }
> > > >>>>
> > > >>>> @@ -4893,6 +4937,7 @@ static struct ovn_port *
> > > >>>>  ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap
> > *ls_ports,
> > > >>>>                 const char *key, const struct
> > nbrec_logical_switch_port
> > > >> *nbsp,
> > > >>>>                 struct ovn_datapath *od, const struct
> > > >> sbrec_port_binding *sb,
> > > >>>> +               struct ovs_list *lflows,
> > > >>>>                 const struct sbrec_mirror_table
*sbrec_mirror_table,
> > > >>>>                 const struct sbrec_chassis_table
> > *sbrec_chassis_table,
> > > >>>>                 struct ovsdb_idl_index *sbrec_chassis_by_name,
> > > >>>> @@ -4903,6 +4948,9 @@ ls_port_create(struct ovsdb_idl_txn
*ovnsb_txn,
> > > >> struct hmap *ls_ports,
> > > >>>>      parse_lsp_addrs(op);
> > > >>>>      op->od = od;
> > > >>>>      hmap_insert(&od->ports, &op->dp_node,
> > > >> hmap_node_hash(&op->key_node));
> > > >>>> +    if (lflows) {
> > > >>>> +        ovs_list_splice(&op->lflows, lflows->next, lflows);
> > > >>>> +    }
> > > >>>>
> > > >>>>      /* Assign explicitly requested tunnel ids first. */
> > > >>>>      if (!ovn_port_assign_requested_tnl_id(sbrec_chassis_table,
op))
> > {
> > > >>>> @@ -5082,7 +5130,7 @@ northd_handle_ls_changes(struct
ovsdb_idl_txn
> > > >> *ovnsb_idl_txn,
> > > >>>>                      goto fail;
> > > >>>>                  }
> > > >>>>                  op = ls_port_create(ovnsb_idl_txn,
&nd->ls_ports,
> > > >>>> -                                    new_nbsp->name, new_nbsp,
od,
> > NULL,
> > > >>>> +                                    new_nbsp->name, new_nbsp,
od,
> > > >> NULL, NULL,
> > > >>>>                                      ni->sbrec_mirror_table,
> > > >>>>                                      ni->sbrec_chassis_table,
> > > >>>>                                      ni->sbrec_chassis_by_name,
> > > >>>> @@ -5114,13 +5162,16 @@ northd_handle_ls_changes(struct
ovsdb_idl_txn
> > > >> *ovnsb_idl_txn,
> > > >>>>                      op->visited = true;
> > > >>>>                      continue;
> > > >>>>                  }
> > > >>>> +                struct ovs_list lflows =
> > OVS_LIST_INITIALIZER(&lflows);
> > > >>>> +                ovs_list_splice(&lflows, op->lflows.next,
> > &op->lflows);
> > > >>>>                  ovn_port_destroy(&nd->ls_ports, op);
> > > >>>>                  op = ls_port_create(ovnsb_idl_txn,
&nd->ls_ports,
> > > >>>> -                                    new_nbsp->name, new_nbsp,
od,
> > sb,
> > > >>>> +                                    new_nbsp->name, new_nbsp,
od,
> > sb,
> > > >> &lflows,
> > > >>>>                                      ni->sbrec_mirror_table,
> > > >>>>                                      ni->sbrec_chassis_table,
> > > >>>>                                      ni->sbrec_chassis_by_name,
> > > >>>>
 ni->sbrec_chassis_by_hostname);
> > > >>>> +                ovs_assert(ovs_list_is_empty(&lflows));
> > > >>>>                  if (!op) {
> > > >>>>                      goto fail;
> > > >>>>                  }
> > > >>>> @@ -5577,7 +5628,8 @@ ovn_igmp_group_destroy(struct hmap
> > *igmp_groups,
> > > >>>>
> > > >>>>  struct ovn_lflow {
> > > >>>>      struct hmap_node hmap_node;
> > > >>>> -    struct ovs_list list_node;
> > > >>>> +    struct ovs_list list_node;   /* For temporary list of
lflows.
> > > >> Don't remove
> > > >>>> +                                    at destroy. */
> > > >>>>
> > > >>>>      struct ovn_datapath *od;     /* 'logical_datapath' in SB
schema.
> > > >>  */
> > > >>>>      unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by
their
> > > >> 'index'.*/
> > > >>>> @@ -5591,6 +5643,8 @@ struct ovn_lflow {
> > > >>>>      size_t n_ods;                /* Number of datapaths
referenced
> > by
> > > >> 'od' and
> > > >>>>                                    * 'dpg_bitmap'. */
> > > >>>>      struct ovn_dp_group *dpg;    /* Link to unique Sb datapath
> > group.
> > > >> */
> > > >>>> +
> > > >>>> +    struct ovs_list referenced_by;  /* List of struct
> > lflow_ref_node.
> > > >> */
> > > >>>>      const char *where;
> > > >>>>
> > > >>>>      struct uuid sb_uuid;            /* SB DB row uuid,
specified by
> > > >> northd. */
> > > >>>> @@ -5640,6 +5694,7 @@ ovn_lflow_init(struct ovn_lflow *lflow,
struct
> > > >> ovn_datapath *od,
> > > >>>>                 char *stage_hint, const char *where)
> > > >>>>  {
> > > >>>>      ovs_list_init(&lflow->list_node);
> > > >>>> +    ovs_list_init(&lflow->referenced_by);
> > > >>>>      lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
> > > >>>>      lflow->od = od;
> > > >>>>      lflow->stage = stage;
> > > >>>> @@ -5767,20 +5822,30 @@ ovn_dp_group_add_with_reference(struct
> > > >> ovn_lflow *lflow_ref,
> > > >>>>      }
> > > >>>>  }
> > > >>>>
> > > >>>> +/* This global variable collects the lflows generated by
> > > >> do_ovn_lflow_add().
> > > >>>> + * start_collecting_lflows() will enable the lflow collection
and
> > the
> > > >> calls to
> > > >>>> + * do_ovn_lflow_add (or the macros ovn_lflow_add_...) will add
> > > >> generated lflows
> > > >>>> + * to the list. end_collecting_lflows() will disable it. */
> > > >>>> +static thread_local struct ovs_list collected_lflows;
> > > >>>> +static thread_local bool collecting_lflows = false;
> > > >>>> +
> > > >>>> +static void
> > > >>>> +start_collecting_lflows(void)
> > > >>>> +{
> > > >>>> +    ovs_assert(!collecting_lflows);
> > > >>>> +    ovs_list_init(&collected_lflows);
> > > >>>> +    collecting_lflows = true;
> > > >>>> +}
> > > >>>> +
> > > >>>> +static void
> > > >>>> +end_collecting_lflows(void)
> > > >>>> +{
> > > >>>> +    ovs_assert(collecting_lflows);
> > > >>>> +    collecting_lflows = false;
> > > >>>> +}
> > > >>>> +
> > > >>>
> > > >>> I think we can avoid these functions and the thread local variable
> > > >>> "collected_lflows".
> > > >>>
> > > >>> I'd suggest the below
> > > >>>
> > > >>> ----------------------------
> > > >>>
> > > >>> static void
> > > >>> do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath
> > *od,
> > > >>>                  const unsigned long *dp_bitmap, size_t
dp_bitmap_len,
> > > >>>                  uint32_t hash, enum ovn_stage stage, uint16_t
> > priority,
> > > >>>                  const char *match, const char *actions, const
char
> > > >> *io_port,
> > > >>>                  struct ovs_list *lflow_ref_list,
> > > >>>                  const struct ovsdb_idl_row *stage_hint,
> > > >>>                  const char *where, const char *ctrl_meter)
> > > >>>     OVS_REQUIRES(fake_hash_mutex)
> > > >>> {
> > > >>>     ...
> > > >>>     ...
> > > >>>     /* At the end. */
> > > >>>     if (lflow_ref_list) {
> > > >>>         struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> > > >>>         lfrn->lflow = lflow;
> > > >>>         ovs_list_insert(lflow_ref_list, &lfrn->lflow_list_node);
> > > >>>         ovs_list_insert(&lflow->referenced_by,
&lfrn->ref_list_node);
> > > >>>     }
> > > >>> }
> > > >>>
> > > >>>
> > > >>> #define ovn_lflow_add_with_lport_and_hint(LFLOW_MAP, OD, STAGE,
> > PRIORITY,
> > > >> \
> > > >>>                                           MATCH, ACTIONS,
> > IN_OUT_PORT, \
> > > >>>                                           LFLOW_REF_LIST,
STAGE_HINT)
> > \
> > > >>>     ovn_lflow_add_at(LFLOW_MAP, OD, NULL, 0, STAGE, PRIORITY,
MATCH,
> > > >> ACTIONS, \
> > > >>>                      IN_OUT_PORT, LFLOW_REF_LIST, NULL,
STAGE_HINT, \
> > > >>>                      OVS_SOURCE_LOCATOR)
> > > >>>
> > > >>> And pass &op->lflows in ovn_lflow_add_with_lport_and_hint()
> > > >>>
> > > >>> -----------------------------
> > > >>>
> > > >>> What do you think ?  Definitely this would result in a bit more
work
> > > >>> as it would require a lot of (tedious) changes.
> > > >>> I think this is a better approach.
> > > >>>
> > > >> Firstly, I think it is not good to use "lflow_ref_list" directly
in the
> > > >> parameter, because there can be more than one object contributing
to
> > the
> > > >> lflow generation. When we implement I-P for more inputs, a single
> > lflow may
> > > >> be referenced by multiple objects. We can't pass multiple
> > lflow_ref_list to
> > > >> the function either, because the number of such lists is unknown.
For
> > > >> example, a lflow may be generated as a result of a LSP, a DP and a
LB
> > > >> backend. If we want to implement I-P for LSP, DP and LB backend, we
> > need to
> > > >> track the reference for all of them. So the current idea is just to
> > collect
> > > >> a list of lflows generated by a higher level function, such as the
> > > >> build_lswitch_and_lrouter_iterate_by_lsp. When implementing I-P for
> > more
> > > >> than one object of the same lflow, this needs to be more
fine-grained.
> > >
> > > I'm still reviewing this patch but my first impression was that we can
> > > probably try to use 'struct objdep_mgr' (defined in lib/objdep.h) to
> > > model all these (potentially many-to-many) relationships.  We do
similar
> > > things in ovn-controller.
> > >
> > My first impression was similar, but later I figured out it is different
> > (and more complex than ovn-controller in general) and we will need to
make
> > changes to objdep_mgr. However, until more input I-P is implemented I am
> > not sure if I will be able to get a general enough abstraction in
> > objdep_mgr that can handle all the use cases. On the other hand, the
> > references between LSP and related lflows are quite simple, so I went
ahead
> > and implemented the lists (embeded into ovn_port and lflow without extra
> > hash tables), with minimum effort, to make it work at least for this
> > scenario and should also work for similar scenarios. I think we can
> > refactor the reference implementation any time but it may be better to
get
> > more I-P implemented, during the process we can always revisit and see
if
> > objdep_mgr can be extended or another common dependency/reference
library
> > for ovn-northd can be created, etc. What do you think?
>
> As part of my refactor and northd I-P for load balancers and data
> paths,  I'll try to see
> if objdep_mgr can be used.  For now with just lport I-P,  I suppose it's
fine.
>
> Acked-by: Numan Siddique <numans@ovn.org>
>
Haven't heard back from Dumitru for a while, so I assume you are fine with
this (but please share any time if you have more comments).
Thank you both, Numan and Dumitru. I applied this to main.

Han

> Numan
>
> >
> > Regards,
> > Han
> >
> > > >
> > > > Agree.  I'm working on the I-P for datapath changes in lflow engine
and
> > > > as a flow can be referenced by multiple datapaths,  I agree this
needs
> > to
> > > > be tracked properly.  And definitely that is out of scope of this
patch
> > series.
> > > >
> > > >
> > > >>
> > > >> Secondly, I agree with you adding a new parameter to the
> > do_ovn_lflow_add
> > > >> is cleaner. For the collected list I mentioned, it can be a
parameter
> > > >> instead of a thread local variable. However, as you mentioned, the
> > change
> > > >> is going to be all over the northd.c code, not only for the
> > > >> ovn_lflow_add_xxx, but also the functions calling the
ovn_lflow_add_xxx
> > > >> macros, and upper layer functions calling those functions, and so
on.
> > > >> Unfortunately C doesn't support optional args. At this moment I am
not
> > sure
> > > >> if the interface is stable enough for the incremental-processing,
so I
> > am
> > > >> not sure if it is worth such a big change. If we need to modify
them
> > again
> > > >> later, all such changes are going to be wasted. On the other hand,
> > although
> > > >> the thread local variable is not the best way, I think it is still
> > clear
> > > >> and manageable, if we call the start_collecting_lflows and
> > > >> end_collecting_lflows in pairs. So, is it ok to leave it for this
> > patch and
> > > >> in the future when this mechanism proves to work well for more
I-P, we
> > can
> > > >> have a separate patch to refactor (which will include all the
tedious
> > > >> function call changes). What do you think?
> > > >
> > > > I agree.  It is definitely tedious to do all the changes.  I'm ok
with
> > > > the approach taken
> > > > in this patch series.
> > > >
> > > > Also request to please take a look at the load balancer I-P patch
> > > > series -
> > http://patchwork.ozlabs.org/project/ovn/list/?series=361083&state=*
> > > >
> > > > Thanks
> > > > Numan
> > > >
> > > >>
> > > >> Thanks,
> > > >> Han
> > > >>
> > > >>> Also I'm planning to work on top of your patches to add I-P for
load
> > > >>> balancers in lflow engine (or perhaps I-P for datapath changes)
> > > >>>
> > > >>> My idea is to have a lflow ref list stored in "struct
ovn_datapath"
> > > >>> similar to the way you have done in this patch in "struct
ovn_port"
> > > >>>
> > > >>> And while adding the flows using one of the macro variants
> > > >>> 'ovn_lflow_add*' pass &od->lflows.
> > > >>>
> > > >>> Please let me know your comments.
> > > >>>
> > > >>> Only concern I have with this patch is the "op->lflows" modified
by
> > > >>> the lflow engine node.
> > > >>> But I agree with your added comments and also thinking to use the
same
> > > >>> approach for datapath I-P handling,
> > > >>> And I don't have a better approach at the moment. So I'm fine
with it.
> > > >>>
> > > >>> Thanks
> > > >>> Numan
> > > >>>
> > > >>>
> > > >>>>  /* Adds a row with the specified contents to the Logical_Flow
table.
> > > >>>> - * Version to use when hash bucket locking is NOT required.
> > > >>>> - *
> > > >>>> - * Note: This function can add generated lflows to the global
> > variable
> > > >>>> - * temp_lflow_list as its output, controlled by the global
variable
> > > >>>> - * add_lflow_to_temp_list. The caller of the ovn_lflow_add_...
> > marcros
> > > >> can get
> > > >>>> - * a list of lflows generated by setting add_lflow_to_temp_list
to
> > > >> true. The
> > > >>>> - * caller is responsible for initializing the temp_lflow_list,
and
> > also
> > > >>>> - * reset the add_lflow_to_temp_list to false when it is no
longer
> > > >> needed.
> > > >>>> - * XXX: this mechanism is temporary and will be replaced when
we add
> > > >> hash index
> > > >>>> - * to lflow_data and refactor related functions.
> > > >>>> - */
> > > >>>> -static bool add_lflow_to_temp_list = false;
> > > >>>> -static struct ovs_list temp_lflow_list;
> > > >>>> + * Version to use when hash bucket locking is NOT required. */
> > > >>>>  static void
> > > >>>>  do_ovn_lflow_add(struct hmap *lflow_map, const struct
ovn_datapath
> > *od,
> > > >>>>                   const unsigned long *dp_bitmap, size_t
> > dp_bitmap_len,
> > > >>>> @@ -5797,7 +5862,7 @@ do_ovn_lflow_add(struct hmap *lflow_map,
const
> > > >> struct ovn_datapath *od,
> > > >>>>      size_t bitmap_len = od ? ods_size(od->datapaths) :
> > dp_bitmap_len;
> > > >>>>      ovs_assert(bitmap_len);
> > > >>>>
> > > >>>> -    if (add_lflow_to_temp_list) {
> > > >>>> +    if (collecting_lflows) {
> > > >>>>          ovs_assert(od);
> > > >>>>          ovs_assert(!dp_bitmap);
> > > >>>>      } else {
> > > >>>> @@ -5829,8 +5894,8 @@ do_ovn_lflow_add(struct hmap *lflow_map,
const
> > > >> struct ovn_datapath *od,
> > > >>>>          thread_lflow_counter++;
> > > >>>>      }
> > > >>>>
> > > >>>> -    if (add_lflow_to_temp_list) {
> > > >>>> -        ovs_list_insert(&temp_lflow_list, &lflow->list_node);
> > > >>>> +    if (collecting_lflows) {
> > > >>>> +        ovs_list_insert(&collected_lflows, &lflow->list_node);
> > > >>>>      }
> > > >>>>  }
> > > >>>>
> > > >>>> @@ -5950,10 +6015,28 @@ ovn_lflow_destroy(struct hmap *lflows,
struct
> > > >> ovn_lflow *lflow)
> > > >>>>          free(lflow->io_port);
> > > >>>>          free(lflow->stage_hint);
> > > >>>>          free(lflow->ctrl_meter);
> > > >>>> +        struct lflow_ref_node *l;
> > > >>>> +        LIST_FOR_EACH_SAFE (l, ref_list_node,
> > &lflow->referenced_by) {
> > > >>>> +            ovs_list_remove(&l->lflow_list_node);
> > > >>>> +            ovs_list_remove(&l->ref_list_node);
> > > >>>> +            free(l);
> > > >>>> +        }
> > > >>>>          free(lflow);
> > > >>>>      }
> > > >>>>  }
> > > >>>>
> > > >>>> +static void
> > > >>>> +link_ovn_port_to_lflows(struct ovn_port *op, struct ovs_list
> > *lflows)
> > > >>>> +{
> > > >>>> +    struct ovn_lflow *f;
> > > >>>> +    LIST_FOR_EACH (f, list_node, lflows) {
> > > >>>> +        struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
> > > >>>> +        lfrn->lflow = f;
> > > >>>> +        ovs_list_insert(&op->lflows, &lfrn->lflow_list_node);
> > > >>>> +        ovs_list_insert(&f->referenced_by,
&lfrn->ref_list_node);
> > > >>>> +    }
> > > >>>> +}
> > > >>>> +
> > > >>>>  static bool
> > > >>>>  build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
> > > >>>>                      struct ds *options_action, struct ds
> > > >> *response_action,
> > > >>>> @@ -15483,6 +15566,7 @@
> > build_lswitch_and_lrouter_iterate_by_lsp(struct
> > > >> ovn_port *op,
> > > >>>>                                           struct hmap *lflows)
> > > >>>>  {
> > > >>>>      ovs_assert(op->nbsp);
> > > >>>> +    start_collecting_lflows();
> > > >>>>
> > > >>>>      /* Build Logical Switch Flows. */
> > > >>>>      build_lswitch_port_sec_op(op, lflows, actions, match);
> > > >>>> @@ -15497,6 +15581,9 @@
> > build_lswitch_and_lrouter_iterate_by_lsp(struct
> > > >> ovn_port *op,
> > > >>>>      /* Build Logical Router Flows. */
> > > >>>>      build_ip_routing_flows_for_router_type_lsp(op, lr_ports,
> > lflows);
> > > >>>>      build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match,
> > > >> actions);
> > > >>>> +
> > > >>>> +    link_ovn_port_to_lflows(op, &collected_lflows);
> > > >>>> +    end_collecting_lflows();
> > > >>>>  }
> > > >>>>
> > > >>>>  /* Helper function to combine all lflow generation which is
iterated
> > > >> by logical
> > > >>>> @@ -16223,14 +16310,10 @@ bool
lflow_handle_northd_ls_changes(struct
> > > >> ovsdb_idl_txn *ovnsb_txn,
> > > >>>>  {
> > > >>>>      struct ls_change *ls_change;
> > > >>>>      LIST_FOR_EACH (ls_change, list_node, &ls_changes->updated) {
> > > >>>> -        ovs_list_init(&temp_lflow_list);
> > > >>>> -        add_lflow_to_temp_list = true;
> > > >>>>          if (!ovs_list_is_empty(&ls_change->updated_ports) ||
> > > >>>>              !ovs_list_is_empty(&ls_change->deleted_ports)) {
> > > >>>>              /* XXX: implement lflow index so that we can handle
> > > >> updated and
> > > >>>>               * deleted LSPs incrementally. */
> > > >>>> -            ovs_list_init(&temp_lflow_list);
> > > >>>> -            add_lflow_to_temp_list = false;
> > > >>>>              return false;
> > > >>>>          }
> > > >>>>
> > > >>>> @@ -16277,83 +16360,85 @@ bool
lflow_handle_northd_ls_changes(struct
> > > >> ovsdb_idl_txn *ovnsb_txn,
> > > >>>>
> > > >>  sbrec_multicast_group_update_ports_addvalue(sbmc_unknown,
> > > >>>>
 op->sb);
> > > >>>>              }
> > > >>>> -        }
> > > >>>> -        /* Sync the newly added flows to SB. */
> > > >>>> -        struct ovn_lflow *lflow;
> > > >>>> -        LIST_FOR_EACH (lflow, list_node, &temp_lflow_list) {
> > > >>>> -            size_t n_datapaths;
> > > >>>> -            struct ovn_datapath **datapaths_array;
> > > >>>> -            if (ovn_stage_to_datapath_type(lflow->stage) ==
> > DP_SWITCH)
> > > >> {
> > > >>>> -                n_datapaths =
ods_size(lflow_input->ls_datapaths);
> > > >>>> -                datapaths_array =
lflow_input->ls_datapaths->array;
> > > >>>> -            } else {
> > > >>>> -                n_datapaths =
ods_size(lflow_input->lr_datapaths);
> > > >>>> -                datapaths_array =
lflow_input->lr_datapaths->array;
> > > >>>> -            }
> > > >>>> -            uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap,
> > > >> n_datapaths);
> > > >>>> -            ovs_assert(n_ods == 1);
> > > >>>> -            /* There is only one datapath, so it should be
moved out
> > > >> of the
> > > >>>> -             * group to a single 'od'. */
> > > >>>> -            size_t index = bitmap_scan(lflow->dpg_bitmap, true,
0,
> > > >>>> -                                       n_datapaths);
> > > >>>>
> > > >>>> -            bitmap_set0(lflow->dpg_bitmap, index);
> > > >>>> -            lflow->od = datapaths_array[index];
> > > >>>> -
> > > >>>> -            /* Logical flow should be re-hashed to allow
lookups. */
> > > >>>> -            uint32_t hash = hmap_node_hash(&lflow->hmap_node);
> > > >>>> -            /* Remove from lflows. */
> > > >>>> -            hmap_remove(lflows, &lflow->hmap_node);
> > > >>>> -            hash =
> > > >> ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid,
> > > >>>> -                                                  hash);
> > > >>>> -            /* Add back. */
> > > >>>> -            hmap_insert(lflows, &lflow->hmap_node, hash);
> > > >>>> -
> > > >>>> -            /* Sync to SB. */
> > > >>>> -            const struct sbrec_logical_flow *sbflow;
> > > >>>> -            lflow->sb_uuid = uuid_random();
> > > >>>> -            sbflow =
> > sbrec_logical_flow_insert_persist_uuid(ovnsb_txn,
> > > >>>> -
> > > >>  &lflow->sb_uuid);
> > > >>>> -            const char *pipeline =
> > > >> ovn_stage_get_pipeline_name(lflow->stage);
> > > >>>> -            uint8_t table = ovn_stage_get_table(lflow->stage);
> > > >>>> -            sbrec_logical_flow_set_logical_datapath(sbflow,
> > > >> lflow->od->sb);
> > > >>>> -            sbrec_logical_flow_set_logical_dp_group(sbflow,
NULL);
> > > >>>> -            sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> > > >>>> -            sbrec_logical_flow_set_table_id(sbflow, table);
> > > >>>> -            sbrec_logical_flow_set_priority(sbflow,
> > lflow->priority);
> > > >>>> -            sbrec_logical_flow_set_match(sbflow, lflow->match);
> > > >>>> -            sbrec_logical_flow_set_actions(sbflow,
lflow->actions);
> > > >>>> -            if (lflow->io_port) {
> > > >>>> -                struct smap tags = SMAP_INITIALIZER(&tags);
> > > >>>> -                smap_add(&tags, "in_out_port", lflow->io_port);
> > > >>>> -                sbrec_logical_flow_set_tags(sbflow, &tags);
> > > >>>> -                smap_destroy(&tags);
> > > >>>> -            }
> > > >>>> -            sbrec_logical_flow_set_controller_meter(sbflow,
> > > >> lflow->ctrl_meter);
> > > >>>> -            /* Trim the source locator lflow->where, which looks
> > > >> something like
> > > >>>> -             * "ovn/northd/northd.c:1234", down to just the part
> > > >> following the
> > > >>>> -             * last slash, e.g. "northd.c:1234". */
> > > >>>> -            const char *slash = strrchr(lflow->where, '/');
> > > >>>> +            /* Sync the newly added flows to SB. */
> > > >>>> +            struct lflow_ref_node *lfrn;
> > > >>>> +            LIST_FOR_EACH (lfrn, lflow_list_node, &op->lflows) {
> > > >>>> +                struct ovn_lflow *lflow = lfrn->lflow;
> > > >>>> +                size_t n_datapaths;
> > > >>>> +                struct ovn_datapath **datapaths_array;
> > > >>>> +                if (ovn_stage_to_datapath_type(lflow->stage) ==
> > > >> DP_SWITCH) {
> > > >>>> +                    n_datapaths =
> > ods_size(lflow_input->ls_datapaths);
> > > >>>> +                    datapaths_array =
> > lflow_input->ls_datapaths->array;
> > > >>>> +                } else {
> > > >>>> +                    n_datapaths =
> > ods_size(lflow_input->lr_datapaths);
> > > >>>> +                    datapaths_array =
> > lflow_input->lr_datapaths->array;
> > > >>>> +                }
> > > >>>> +                uint32_t n_ods =
bitmap_count1(lflow->dpg_bitmap,
> > > >> n_datapaths);
> > > >>>> +                ovs_assert(n_ods == 1);
> > > >>>> +                /* There is only one datapath, so it should be
moved
> > > >> out of the
> > > >>>> +                 * group to a single 'od'. */
> > > >>>> +                size_t index = bitmap_scan(lflow->dpg_bitmap,
true,
> > 0,
> > > >>>> +                                           n_datapaths);
> > > >>>> +
> > > >>>> +                bitmap_set0(lflow->dpg_bitmap, index);
> > > >>>> +                lflow->od = datapaths_array[index];
> > > >>>> +
> > > >>>> +                /* Logical flow should be re-hashed to allow
> > lookups.
> > > >> */
> > > >>>> +                uint32_t hash =
hmap_node_hash(&lflow->hmap_node);
> > > >>>> +                /* Remove from lflows. */
> > > >>>> +                hmap_remove(lflows, &lflow->hmap_node);
> > > >>>> +                hash = ovn_logical_flow_hash_datapath(
> > > >>>> +
> > > >>  &lflow->od->sb->header_.uuid, hash);
> > > >>>> +                /* Add back. */
> > > >>>> +                hmap_insert(lflows, &lflow->hmap_node, hash);
> > > >>>> +
> > > >>>> +                /* Sync to SB. */
> > > >>>> +                const struct sbrec_logical_flow *sbflow;
> > > >>>> +                lflow->sb_uuid = uuid_random();
> > > >>>> +                sbflow = sbrec_logical_flow_insert_persist_uuid(
> > > >>>> +                                                ovnsb_txn,
> > > >> &lflow->sb_uuid);
> > > >>>> +                const char *pipeline =
ovn_stage_get_pipeline_name(
> > > >>>> +
> > > >> lflow->stage);
> > > >>>> +                uint8_t table =
ovn_stage_get_table(lflow->stage);
> > > >>>> +                sbrec_logical_flow_set_logical_datapath(sbflow,
> > > >> lflow->od->sb);
> > > >>>> +                sbrec_logical_flow_set_logical_dp_group(sbflow,
> > NULL);
> > > >>>> +                sbrec_logical_flow_set_pipeline(sbflow,
pipeline);
> > > >>>> +                sbrec_logical_flow_set_table_id(sbflow, table);
> > > >>>> +                sbrec_logical_flow_set_priority(sbflow,
> > > >> lflow->priority);
> > > >>>> +                sbrec_logical_flow_set_match(sbflow,
lflow->match);
> > > >>>> +                sbrec_logical_flow_set_actions(sbflow,
> > lflow->actions);
> > > >>>> +                if (lflow->io_port) {
> > > >>>> +                    struct smap tags = SMAP_INITIALIZER(&tags);
> > > >>>> +                    smap_add(&tags, "in_out_port",
lflow->io_port);
> > > >>>> +                    sbrec_logical_flow_set_tags(sbflow, &tags);
> > > >>>> +                    smap_destroy(&tags);
> > > >>>> +                }
> > > >>>> +                sbrec_logical_flow_set_controller_meter(sbflow,
> > > >>>> +
> > > >>  lflow->ctrl_meter);
> > > >>>> +                /* Trim the source locator lflow->where, which
looks
> > > >> something
> > > >>>> +                 * like "ovn/northd/northd.c:1234", down to
just the
> > > >> part
> > > >>>> +                 * following the last slash, e.g.
"northd.c:1234".
> > */
> > > >>>> +                const char *slash = strrchr(lflow->where, '/');
> > > >>>>  #if _WIN32
> > > >>>> -            const char *backslash = strrchr(lflow->where, '\\');
> > > >>>> -            if (!slash || backslash > slash) {
> > > >>>> -                slash = backslash;
> > > >>>> -            }
> > > >>>> +                const char *backslash = strrchr(lflow->where,
'\\');
> > > >>>> +                if (!slash || backslash > slash) {
> > > >>>> +                    slash = backslash;
> > > >>>> +                }
> > > >>>>  #endif
> > > >>>> -            const char *where = slash ? slash + 1 :
lflow->where;
> > > >>>> +                const char *where = slash ? slash + 1 :
> > lflow->where;
> > > >>>>
> > > >>>> -            struct smap ids = SMAP_INITIALIZER(&ids);
> > > >>>> -            smap_add(&ids, "stage-name",
> > > >> ovn_stage_to_str(lflow->stage));
> > > >>>> -            smap_add(&ids, "source", where);
> > > >>>> -            if (lflow->stage_hint) {
> > > >>>> -                smap_add(&ids, "stage-hint", lflow->stage_hint);
> > > >>>> +                struct smap ids = SMAP_INITIALIZER(&ids);
> > > >>>> +                smap_add(&ids, "stage-name",
> > > >> ovn_stage_to_str(lflow->stage));
> > > >>>> +                smap_add(&ids, "source", where);
> > > >>>> +                if (lflow->stage_hint) {
> > > >>>> +                    smap_add(&ids, "stage-hint",
lflow->stage_hint);
> > > >>>> +                }
> > > >>>> +                sbrec_logical_flow_set_external_ids(sbflow,
&ids);
> > > >>>> +                smap_destroy(&ids);
> > > >>>>              }
> > > >>>> -            sbrec_logical_flow_set_external_ids(sbflow, &ids);
> > > >>>> -            smap_destroy(&ids);
> > > >>>>          }
> > > >>>>      }
> > > >>>> -    ovs_list_init(&temp_lflow_list);
> > > >>>> -    add_lflow_to_temp_list = false;
> > > >>>>      return true;
> > > >>>>
> > > >>>>  }
> > > >>>> --
> > > >>>> 2.30.2
> > > >>>>
> > > >>>> _______________________________________________
> > > >>>> dev mailing list
> > > >>>> dev@openvswitch.org
> > > >>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > >>>>
> > > >> _______________________________________________
> > > >> dev mailing list
> > > >> dev@openvswitch.org
> > > >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > > _______________________________________________
> > > > dev mailing list
> > > > dev@openvswitch.org
> > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Dumitru Ceara July 6, 2023, 10:19 a.m. UTC | #8
On 7/6/23 12:13, Han Zhou wrote:
> On Thu, Jun 29, 2023 at 10:10 PM Numan Siddique <numans@ovn.org> wrote:
>>
>> On Fri, Jun 30, 2023 at 7:00 AM Han Zhou <hzhou@ovn.org> wrote:
>>>
>>> On Thu, Jun 29, 2023 at 9:19 AM Dumitru Ceara <dceara@redhat.com> wrote:
>>>>
>>>> On 6/27/23 10:23, Numan Siddique wrote:
>>>>> On Mon, Jun 26, 2023 at 10:34 PM Han Zhou <hzhou@ovn.org> wrote:
>>>>>>
>>>>>> On Mon, Jun 26, 2023 at 7:25 AM Numan Siddique <numans@ovn.org>
> wrote:
>>>>>>>
>>>>>>> On Sun, Jun 18, 2023 at 11:48 AM Han Zhou <hzhou@ovn.org> wrote:
>>>>>>>>
>>>>>>>> For incremental processing, it is important to maintain
> relationship
>>>>>>>> between the inputs and the logical flows generated. This patch
>>> creates
>>>>>>>> the links between ovn_port and logical flows. The same data
> structure
>>>>>>>> may be expanded to maintain links between logical flows and other
>>> types
>>>>>>>> of inputs.
>>>>>>>>
>>>>>>>> This patch also refactors the temp_lflow_list operations to
>>>>>>>> collected_lflows with helper functions to start and end
> collecting.
>>> It
>>>>>>>> still uses global variables just to avoid updating all the
>>> lflow_add_...
>>>>>>>> related code all over the northd.c file.
>>>>>>>>
>>>>>>>> Signed-off-by: Han Zhou <hzhou@ovn.org>
>>>>>>>
>>>>>>> Hi Han,
>>>>>>>
>>>>>>> Please see a few comments below.  I did review all the 3 patches
> in
>>> the
>>>>>> series.
>>>>>>> They LGTM overall.  I'd like to do some more testing before
> providing
>>> my
>>>>>> Acks.
>>>>>>>
>>>>>>
>>>>>> Thanks for your review!
>>>>>>
>>>>>>>
>>>>>>>> ---
>>>>>>>>  northd/northd.c | 271
>>> +++++++++++++++++++++++++++++++-----------------
>>>>>>>>  1 file changed, 178 insertions(+), 93 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/northd/northd.c b/northd/northd.c
>>>>>>>> index 98f528f93cfc..aa0f853ce2db 100644
>>>>>>>> --- a/northd/northd.c
>>>>>>>> +++ b/northd/northd.c
>>>>>>>> @@ -1457,6 +1457,19 @@ struct ovn_port_routable_addresses {
>>>>>>>>      size_t n_addrs;
>>>>>>>>  };
>>>>>>>>
>>>>>>>> +/* A node that maintains link between an object (such as an
>>> ovn_port)
>>>>>> and
>>>>>>>> + * a lflow. */
>>>>>>>> +struct lflow_ref_node {
>>>>>>>> +    /* This list follows different lflows referenced by the same
>>>>>> object. List
>>>>>>>> +     * head is, for example, ovn_port->lflows.  */
>>>>>>>> +    struct ovs_list lflow_list_node;
>>>>>>>> +    /* This list follows different objects that reference the
> same
>>>>>> lflow. List
>>>>>>>> +     * head is ovn_lflow->referenced_by. */
>>>>>>>> +    struct ovs_list ref_list_node;
>>>>>>>> +    /* The lflow. */
>>>>>>>> +    struct ovn_lflow *lflow;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>>  /* A logical switch port or logical router port.
>>>>>>>>   *
>>>>>>>>   * In steady state, an ovn_port points to a northbound
>>>>>> Logical_Switch_Port
>>>>>>>> @@ -1548,6 +1561,28 @@ struct ovn_port {
>>>>>>>>
>>>>>>>>      /* Temporarily used for traversing a list (or hmap) of
> ports. */
>>>>>>>>      bool visited;
>>>>>>>> +
>>>>>>>> +    /* List of struct lflow_ref_node that points to the lflows
>>>>>> generated by
>>>>>>>> +     * this ovn_port.
>>>>>>>> +     *
>>>>>>>> +     * This data is initialized and destroyed by the en_northd
> node,
>>>>>> but
>>>>>>>> +     * populated and used only by the en_lflow node. Ideally
> this
>>> data
>>>>>> should
>>>>>>>> +     * be maintained as part of en_lflow's data (struct
>>> lflow_data): a
>>>>>> hash
>>>>>>>> +     * index from ovn_port key to lflows.  However, it would be
> less
>>>>>> efficient
>>>>>>>> +     * and more complex:
>>>>>>>> +     *
>>>>>>>> +     * 1. It would require an extra search (using the index) to
> find
>>>>>> the
>>>>>>>> +     * lflows.
>>>>>>>> +     *
>>>>>>>> +     * 2. Building the index needs to be thread-safe, using
> either a
>>>>>> global
>>>>>>>> +     * lock which is obviously less efficient, or hash-based
> lock
>>>>>> array which
>>>>>>>> +     * is more complex.
>>>>>>>> +     *
>>>>>>>> +     * Adding the list here is more straightforward. The
> drawback is
>>>>>> that we
>>>>>>>> +     * need to keep in mind that this data belongs to en_lflow
> node,
>>>>>> so never
>>>>>>>> +     * access it from any other nodes.
>>>>>>>> +     */
>>>>>>>> +    struct ovs_list lflows;
>>>>>>>>  };
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>  static bool lsp_can_be_inc_processed(const struct
>>>>>> nbrec_logical_switch_port *);
>>>>>>>> @@ -1635,6 +1670,8 @@ ovn_port_create(struct hmap *ports, const
> char
>>>>>> *key,
>>>>>>>>      ovn_port_set_nb(op, nbsp, nbrp);
>>>>>>>>      op->l3dgw_port = op->cr_port = NULL;
>>>>>>>>      hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
>>>>>>>> +
>>>>>>>> +    ovs_list_init(&op->lflows);
>>>>>>>>      return op;
>>>>>>>>  }
>>>>>>>>
>>>>>>>> @@ -1665,6 +1702,13 @@ ovn_port_destroy_orphan(struct ovn_port
> *port)
>>>>>>>>      destroy_lport_addresses(&port->proxy_arp_addrs);
>>>>>>>>      free(port->json_key);
>>>>>>>>      free(port->key);
>>>>>>>> +
>>>>>>>> +    struct lflow_ref_node *l;
>>>>>>>> +    LIST_FOR_EACH_SAFE (l, lflow_list_node, &port->lflows) {
>>>>>>>> +        ovs_list_remove(&l->lflow_list_node);
>>>>>>>> +        ovs_list_remove(&l->ref_list_node);
>>>>>>>> +        free(l);
>>>>>>>> +    }
>>>>>>>>      free(port);
>>>>>>>>  }
>>>>>>>>
>>>>>>>> @@ -4893,6 +4937,7 @@ static struct ovn_port *
>>>>>>>>  ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap
>>> *ls_ports,
>>>>>>>>                 const char *key, const struct
>>> nbrec_logical_switch_port
>>>>>> *nbsp,
>>>>>>>>                 struct ovn_datapath *od, const struct
>>>>>> sbrec_port_binding *sb,
>>>>>>>> +               struct ovs_list *lflows,
>>>>>>>>                 const struct sbrec_mirror_table
> *sbrec_mirror_table,
>>>>>>>>                 const struct sbrec_chassis_table
>>> *sbrec_chassis_table,
>>>>>>>>                 struct ovsdb_idl_index *sbrec_chassis_by_name,
>>>>>>>> @@ -4903,6 +4948,9 @@ ls_port_create(struct ovsdb_idl_txn
> *ovnsb_txn,
>>>>>> struct hmap *ls_ports,
>>>>>>>>      parse_lsp_addrs(op);
>>>>>>>>      op->od = od;
>>>>>>>>      hmap_insert(&od->ports, &op->dp_node,
>>>>>> hmap_node_hash(&op->key_node));
>>>>>>>> +    if (lflows) {
>>>>>>>> +        ovs_list_splice(&op->lflows, lflows->next, lflows);
>>>>>>>> +    }
>>>>>>>>
>>>>>>>>      /* Assign explicitly requested tunnel ids first. */
>>>>>>>>      if (!ovn_port_assign_requested_tnl_id(sbrec_chassis_table,
> op))
>>> {
>>>>>>>> @@ -5082,7 +5130,7 @@ northd_handle_ls_changes(struct
> ovsdb_idl_txn
>>>>>> *ovnsb_idl_txn,
>>>>>>>>                      goto fail;
>>>>>>>>                  }
>>>>>>>>                  op = ls_port_create(ovnsb_idl_txn,
> &nd->ls_ports,
>>>>>>>> -                                    new_nbsp->name, new_nbsp,
> od,
>>> NULL,
>>>>>>>> +                                    new_nbsp->name, new_nbsp,
> od,
>>>>>> NULL, NULL,
>>>>>>>>                                      ni->sbrec_mirror_table,
>>>>>>>>                                      ni->sbrec_chassis_table,
>>>>>>>>                                      ni->sbrec_chassis_by_name,
>>>>>>>> @@ -5114,13 +5162,16 @@ northd_handle_ls_changes(struct
> ovsdb_idl_txn
>>>>>> *ovnsb_idl_txn,
>>>>>>>>                      op->visited = true;
>>>>>>>>                      continue;
>>>>>>>>                  }
>>>>>>>> +                struct ovs_list lflows =
>>> OVS_LIST_INITIALIZER(&lflows);
>>>>>>>> +                ovs_list_splice(&lflows, op->lflows.next,
>>> &op->lflows);
>>>>>>>>                  ovn_port_destroy(&nd->ls_ports, op);
>>>>>>>>                  op = ls_port_create(ovnsb_idl_txn,
> &nd->ls_ports,
>>>>>>>> -                                    new_nbsp->name, new_nbsp,
> od,
>>> sb,
>>>>>>>> +                                    new_nbsp->name, new_nbsp,
> od,
>>> sb,
>>>>>> &lflows,
>>>>>>>>                                      ni->sbrec_mirror_table,
>>>>>>>>                                      ni->sbrec_chassis_table,
>>>>>>>>                                      ni->sbrec_chassis_by_name,
>>>>>>>>
>  ni->sbrec_chassis_by_hostname);
>>>>>>>> +                ovs_assert(ovs_list_is_empty(&lflows));
>>>>>>>>                  if (!op) {
>>>>>>>>                      goto fail;
>>>>>>>>                  }
>>>>>>>> @@ -5577,7 +5628,8 @@ ovn_igmp_group_destroy(struct hmap
>>> *igmp_groups,
>>>>>>>>
>>>>>>>>  struct ovn_lflow {
>>>>>>>>      struct hmap_node hmap_node;
>>>>>>>> -    struct ovs_list list_node;
>>>>>>>> +    struct ovs_list list_node;   /* For temporary list of
> lflows.
>>>>>> Don't remove
>>>>>>>> +                                    at destroy. */
>>>>>>>>
>>>>>>>>      struct ovn_datapath *od;     /* 'logical_datapath' in SB
> schema.
>>>>>>  */
>>>>>>>>      unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by
> their
>>>>>> 'index'.*/
>>>>>>>> @@ -5591,6 +5643,8 @@ struct ovn_lflow {
>>>>>>>>      size_t n_ods;                /* Number of datapaths
> referenced
>>> by
>>>>>> 'od' and
>>>>>>>>                                    * 'dpg_bitmap'. */
>>>>>>>>      struct ovn_dp_group *dpg;    /* Link to unique Sb datapath
>>> group.
>>>>>> */
>>>>>>>> +
>>>>>>>> +    struct ovs_list referenced_by;  /* List of struct
>>> lflow_ref_node.
>>>>>> */
>>>>>>>>      const char *where;
>>>>>>>>
>>>>>>>>      struct uuid sb_uuid;            /* SB DB row uuid,
> specified by
>>>>>> northd. */
>>>>>>>> @@ -5640,6 +5694,7 @@ ovn_lflow_init(struct ovn_lflow *lflow,
> struct
>>>>>> ovn_datapath *od,
>>>>>>>>                 char *stage_hint, const char *where)
>>>>>>>>  {
>>>>>>>>      ovs_list_init(&lflow->list_node);
>>>>>>>> +    ovs_list_init(&lflow->referenced_by);
>>>>>>>>      lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
>>>>>>>>      lflow->od = od;
>>>>>>>>      lflow->stage = stage;
>>>>>>>> @@ -5767,20 +5822,30 @@ ovn_dp_group_add_with_reference(struct
>>>>>> ovn_lflow *lflow_ref,
>>>>>>>>      }
>>>>>>>>  }
>>>>>>>>
>>>>>>>> +/* This global variable collects the lflows generated by
>>>>>> do_ovn_lflow_add().
>>>>>>>> + * start_collecting_lflows() will enable the lflow collection
> and
>>> the
>>>>>> calls to
>>>>>>>> + * do_ovn_lflow_add (or the macros ovn_lflow_add_...) will add
>>>>>> generated lflows
>>>>>>>> + * to the list. end_collecting_lflows() will disable it. */
>>>>>>>> +static thread_local struct ovs_list collected_lflows;
>>>>>>>> +static thread_local bool collecting_lflows = false;
>>>>>>>> +
>>>>>>>> +static void
>>>>>>>> +start_collecting_lflows(void)
>>>>>>>> +{
>>>>>>>> +    ovs_assert(!collecting_lflows);
>>>>>>>> +    ovs_list_init(&collected_lflows);
>>>>>>>> +    collecting_lflows = true;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void
>>>>>>>> +end_collecting_lflows(void)
>>>>>>>> +{
>>>>>>>> +    ovs_assert(collecting_lflows);
>>>>>>>> +    collecting_lflows = false;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>
>>>>>>> I think we can avoid these functions and the thread local variable
>>>>>>> "collected_lflows".
>>>>>>>
>>>>>>> I'd suggest the below
>>>>>>>
>>>>>>> ----------------------------
>>>>>>>
>>>>>>> static void
>>>>>>> do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath
>>> *od,
>>>>>>>                  const unsigned long *dp_bitmap, size_t
> dp_bitmap_len,
>>>>>>>                  uint32_t hash, enum ovn_stage stage, uint16_t
>>> priority,
>>>>>>>                  const char *match, const char *actions, const
> char
>>>>>> *io_port,
>>>>>>>                  struct ovs_list *lflow_ref_list,
>>>>>>>                  const struct ovsdb_idl_row *stage_hint,
>>>>>>>                  const char *where, const char *ctrl_meter)
>>>>>>>     OVS_REQUIRES(fake_hash_mutex)
>>>>>>> {
>>>>>>>     ...
>>>>>>>     ...
>>>>>>>     /* At the end. */
>>>>>>>     if (lflow_ref_list) {
>>>>>>>         struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
>>>>>>>         lfrn->lflow = lflow;
>>>>>>>         ovs_list_insert(lflow_ref_list, &lfrn->lflow_list_node);
>>>>>>>         ovs_list_insert(&lflow->referenced_by,
> &lfrn->ref_list_node);
>>>>>>>     }
>>>>>>> }
>>>>>>>
>>>>>>>
>>>>>>> #define ovn_lflow_add_with_lport_and_hint(LFLOW_MAP, OD, STAGE,
>>> PRIORITY,
>>>>>> \
>>>>>>>                                           MATCH, ACTIONS,
>>> IN_OUT_PORT, \
>>>>>>>                                           LFLOW_REF_LIST,
> STAGE_HINT)
>>> \
>>>>>>>     ovn_lflow_add_at(LFLOW_MAP, OD, NULL, 0, STAGE, PRIORITY,
> MATCH,
>>>>>> ACTIONS, \
>>>>>>>                      IN_OUT_PORT, LFLOW_REF_LIST, NULL,
> STAGE_HINT, \
>>>>>>>                      OVS_SOURCE_LOCATOR)
>>>>>>>
>>>>>>> And pass &op->lflows in ovn_lflow_add_with_lport_and_hint()
>>>>>>>
>>>>>>> -----------------------------
>>>>>>>
>>>>>>> What do you think ?  Definitely this would result in a bit more
> work
>>>>>>> as it would require a lot of (tedious) changes.
>>>>>>> I think this is a better approach.
>>>>>>>
>>>>>> Firstly, I think it is not good to use "lflow_ref_list" directly
> in the
>>>>>> parameter, because there can be more than one object contributing
> to
>>> the
>>>>>> lflow generation. When we implement I-P for more inputs, a single
>>> lflow may
>>>>>> be referenced by multiple objects. We can't pass multiple
>>> lflow_ref_list to
>>>>>> the function either, because the number of such lists is unknown.
> For
>>>>>> example, a lflow may be generated as a result of a LSP, a DP and a
> LB
>>>>>> backend. If we want to implement I-P for LSP, DP and LB backend, we
>>> need to
>>>>>> track the reference for all of them. So the current idea is just to
>>> collect
>>>>>> a list of lflows generated by a higher level function, such as the
>>>>>> build_lswitch_and_lrouter_iterate_by_lsp. When implementing I-P for
>>> more
>>>>>> than one object of the same lflow, this needs to be more
> fine-grained.
>>>>
>>>> I'm still reviewing this patch but my first impression was that we can
>>>> probably try to use 'struct objdep_mgr' (defined in lib/objdep.h) to
>>>> model all these (potentially many-to-many) relationships.  We do
> similar
>>>> things in ovn-controller.
>>>>
>>> My first impression was similar, but later I figured out it is different
>>> (and more complex than ovn-controller in general) and we will need to
> make
>>> changes to objdep_mgr. However, until more input I-P is implemented I am
>>> not sure if I will be able to get a general enough abstraction in
>>> objdep_mgr that can handle all the use cases. On the other hand, the
>>> references between LSP and related lflows are quite simple, so I went
> ahead
>>> and implemented the lists (embeded into ovn_port and lflow without extra
>>> hash tables), with minimum effort, to make it work at least for this
>>> scenario and should also work for similar scenarios. I think we can
>>> refactor the reference implementation any time but it may be better to
> get
>>> more I-P implemented, during the process we can always revisit and see
> if
>>> objdep_mgr can be extended or another common dependency/reference
> library
>>> for ovn-northd can be created, etc. What do you think?
>>
>> As part of my refactor and northd I-P for load balancers and data
>> paths,  I'll try to see
>> if objdep_mgr can be used.  For now with just lport I-P,  I suppose it's
> fine.
>>
>> Acked-by: Numan Siddique <numans@ovn.org>
>>
> Haven't heard back from Dumitru for a while, so I assume you are fine with
> this (but please share any time if you have more comments).
> Thank you both, Numan and Dumitru. I applied this to main.
> 

Sorry, Han, I'm quite slow in reviewing.  I'm OK with you applying the
patches, I wouldn't want to block development.  If I find anything later
on I'll post follow up fixes or reports.

I still think it's best to reuse objdep_mgr and I think Numan is working
on changing to that in his series so we're fine.

Regards,
Dumitru
diff mbox series

Patch

diff --git a/northd/northd.c b/northd/northd.c
index 98f528f93cfc..aa0f853ce2db 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -1457,6 +1457,19 @@  struct ovn_port_routable_addresses {
     size_t n_addrs;
 };
 
+/* A node that maintains link between an object (such as an ovn_port) and
+ * a lflow. */
+struct lflow_ref_node {
+    /* This list follows different lflows referenced by the same object. List
+     * head is, for example, ovn_port->lflows.  */
+    struct ovs_list lflow_list_node;
+    /* This list follows different objects that reference the same lflow. List
+     * head is ovn_lflow->referenced_by. */
+    struct ovs_list ref_list_node;
+    /* The lflow. */
+    struct ovn_lflow *lflow;
+};
+
 /* A logical switch port or logical router port.
  *
  * In steady state, an ovn_port points to a northbound Logical_Switch_Port
@@ -1548,6 +1561,28 @@  struct ovn_port {
 
     /* Temporarily used for traversing a list (or hmap) of ports. */
     bool visited;
+
+    /* List of struct lflow_ref_node that points to the lflows generated by
+     * this ovn_port.
+     *
+     * This data is initialized and destroyed by the en_northd node, but
+     * populated and used only by the en_lflow node. Ideally this data should
+     * be maintained as part of en_lflow's data (struct lflow_data): a hash
+     * index from ovn_port key to lflows.  However, it would be less efficient
+     * and more complex:
+     *
+     * 1. It would require an extra search (using the index) to find the
+     * lflows.
+     *
+     * 2. Building the index needs to be thread-safe, using either a global
+     * lock which is obviously less efficient, or hash-based lock array which
+     * is more complex.
+     *
+     * Adding the list here is more straightforward. The drawback is that we
+     * need to keep in mind that this data belongs to en_lflow node, so never
+     * access it from any other nodes.
+     */
+    struct ovs_list lflows;
 };
 
 static bool lsp_can_be_inc_processed(const struct nbrec_logical_switch_port *);
@@ -1635,6 +1670,8 @@  ovn_port_create(struct hmap *ports, const char *key,
     ovn_port_set_nb(op, nbsp, nbrp);
     op->l3dgw_port = op->cr_port = NULL;
     hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
+
+    ovs_list_init(&op->lflows);
     return op;
 }
 
@@ -1665,6 +1702,13 @@  ovn_port_destroy_orphan(struct ovn_port *port)
     destroy_lport_addresses(&port->proxy_arp_addrs);
     free(port->json_key);
     free(port->key);
+
+    struct lflow_ref_node *l;
+    LIST_FOR_EACH_SAFE (l, lflow_list_node, &port->lflows) {
+        ovs_list_remove(&l->lflow_list_node);
+        ovs_list_remove(&l->ref_list_node);
+        free(l);
+    }
     free(port);
 }
 
@@ -4893,6 +4937,7 @@  static struct ovn_port *
 ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports,
                const char *key, const struct nbrec_logical_switch_port *nbsp,
                struct ovn_datapath *od, const struct sbrec_port_binding *sb,
+               struct ovs_list *lflows,
                const struct sbrec_mirror_table *sbrec_mirror_table,
                const struct sbrec_chassis_table *sbrec_chassis_table,
                struct ovsdb_idl_index *sbrec_chassis_by_name,
@@ -4903,6 +4948,9 @@  ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports,
     parse_lsp_addrs(op);
     op->od = od;
     hmap_insert(&od->ports, &op->dp_node, hmap_node_hash(&op->key_node));
+    if (lflows) {
+        ovs_list_splice(&op->lflows, lflows->next, lflows);
+    }
 
     /* Assign explicitly requested tunnel ids first. */
     if (!ovn_port_assign_requested_tnl_id(sbrec_chassis_table, op)) {
@@ -5082,7 +5130,7 @@  northd_handle_ls_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
                     goto fail;
                 }
                 op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
-                                    new_nbsp->name, new_nbsp, od, NULL,
+                                    new_nbsp->name, new_nbsp, od, NULL, NULL,
                                     ni->sbrec_mirror_table,
                                     ni->sbrec_chassis_table,
                                     ni->sbrec_chassis_by_name,
@@ -5114,13 +5162,16 @@  northd_handle_ls_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
                     op->visited = true;
                     continue;
                 }
+                struct ovs_list lflows = OVS_LIST_INITIALIZER(&lflows);
+                ovs_list_splice(&lflows, op->lflows.next, &op->lflows);
                 ovn_port_destroy(&nd->ls_ports, op);
                 op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports,
-                                    new_nbsp->name, new_nbsp, od, sb,
+                                    new_nbsp->name, new_nbsp, od, sb, &lflows,
                                     ni->sbrec_mirror_table,
                                     ni->sbrec_chassis_table,
                                     ni->sbrec_chassis_by_name,
                                     ni->sbrec_chassis_by_hostname);
+                ovs_assert(ovs_list_is_empty(&lflows));
                 if (!op) {
                     goto fail;
                 }
@@ -5577,7 +5628,8 @@  ovn_igmp_group_destroy(struct hmap *igmp_groups,
 
 struct ovn_lflow {
     struct hmap_node hmap_node;
-    struct ovs_list list_node;
+    struct ovs_list list_node;   /* For temporary list of lflows. Don't remove
+                                    at destroy. */
 
     struct ovn_datapath *od;     /* 'logical_datapath' in SB schema.  */
     unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by their 'index'.*/
@@ -5591,6 +5643,8 @@  struct ovn_lflow {
     size_t n_ods;                /* Number of datapaths referenced by 'od' and
                                   * 'dpg_bitmap'. */
     struct ovn_dp_group *dpg;    /* Link to unique Sb datapath group. */
+
+    struct ovs_list referenced_by;  /* List of struct lflow_ref_node. */
     const char *where;
 
     struct uuid sb_uuid;            /* SB DB row uuid, specified by northd. */
@@ -5640,6 +5694,7 @@  ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
                char *stage_hint, const char *where)
 {
     ovs_list_init(&lflow->list_node);
+    ovs_list_init(&lflow->referenced_by);
     lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
     lflow->od = od;
     lflow->stage = stage;
@@ -5767,20 +5822,30 @@  ovn_dp_group_add_with_reference(struct ovn_lflow *lflow_ref,
     }
 }
 
+/* This global variable collects the lflows generated by do_ovn_lflow_add().
+ * start_collecting_lflows() will enable the lflow collection and the calls to
+ * do_ovn_lflow_add (or the macros ovn_lflow_add_...) will add generated lflows
+ * to the list. end_collecting_lflows() will disable it. */
+static thread_local struct ovs_list collected_lflows;
+static thread_local bool collecting_lflows = false;
+
+static void
+start_collecting_lflows(void)
+{
+    ovs_assert(!collecting_lflows);
+    ovs_list_init(&collected_lflows);
+    collecting_lflows = true;
+}
+
+static void
+end_collecting_lflows(void)
+{
+    ovs_assert(collecting_lflows);
+    collecting_lflows = false;
+}
+
 /* Adds a row with the specified contents to the Logical_Flow table.
- * Version to use when hash bucket locking is NOT required.
- *
- * Note: This function can add generated lflows to the global variable
- * temp_lflow_list as its output, controlled by the global variable
- * add_lflow_to_temp_list. The caller of the ovn_lflow_add_... marcros can get
- * a list of lflows generated by setting add_lflow_to_temp_list to true. The
- * caller is responsible for initializing the temp_lflow_list, and also
- * reset the add_lflow_to_temp_list to false when it is no longer needed.
- * XXX: this mechanism is temporary and will be replaced when we add hash index
- * to lflow_data and refactor related functions.
- */
-static bool add_lflow_to_temp_list = false;
-static struct ovs_list temp_lflow_list;
+ * Version to use when hash bucket locking is NOT required. */
 static void
 do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
                  const unsigned long *dp_bitmap, size_t dp_bitmap_len,
@@ -5797,7 +5862,7 @@  do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
     size_t bitmap_len = od ? ods_size(od->datapaths) : dp_bitmap_len;
     ovs_assert(bitmap_len);
 
-    if (add_lflow_to_temp_list) {
+    if (collecting_lflows) {
         ovs_assert(od);
         ovs_assert(!dp_bitmap);
     } else {
@@ -5829,8 +5894,8 @@  do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
         thread_lflow_counter++;
     }
 
-    if (add_lflow_to_temp_list) {
-        ovs_list_insert(&temp_lflow_list, &lflow->list_node);
+    if (collecting_lflows) {
+        ovs_list_insert(&collected_lflows, &lflow->list_node);
     }
 }
 
@@ -5950,10 +6015,28 @@  ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow)
         free(lflow->io_port);
         free(lflow->stage_hint);
         free(lflow->ctrl_meter);
+        struct lflow_ref_node *l;
+        LIST_FOR_EACH_SAFE (l, ref_list_node, &lflow->referenced_by) {
+            ovs_list_remove(&l->lflow_list_node);
+            ovs_list_remove(&l->ref_list_node);
+            free(l);
+        }
         free(lflow);
     }
 }
 
+static void
+link_ovn_port_to_lflows(struct ovn_port *op, struct ovs_list *lflows)
+{
+    struct ovn_lflow *f;
+    LIST_FOR_EACH (f, list_node, lflows) {
+        struct lflow_ref_node *lfrn = xmalloc(sizeof *lfrn);
+        lfrn->lflow = f;
+        ovs_list_insert(&op->lflows, &lfrn->lflow_list_node);
+        ovs_list_insert(&f->referenced_by, &lfrn->ref_list_node);
+    }
+}
+
 static bool
 build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
                     struct ds *options_action, struct ds *response_action,
@@ -15483,6 +15566,7 @@  build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op,
                                          struct hmap *lflows)
 {
     ovs_assert(op->nbsp);
+    start_collecting_lflows();
 
     /* Build Logical Switch Flows. */
     build_lswitch_port_sec_op(op, lflows, actions, match);
@@ -15497,6 +15581,9 @@  build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op,
     /* Build Logical Router Flows. */
     build_ip_routing_flows_for_router_type_lsp(op, lr_ports, lflows);
     build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions);
+
+    link_ovn_port_to_lflows(op, &collected_lflows);
+    end_collecting_lflows();
 }
 
 /* Helper function to combine all lflow generation which is iterated by logical
@@ -16223,14 +16310,10 @@  bool lflow_handle_northd_ls_changes(struct ovsdb_idl_txn *ovnsb_txn,
 {
     struct ls_change *ls_change;
     LIST_FOR_EACH (ls_change, list_node, &ls_changes->updated) {
-        ovs_list_init(&temp_lflow_list);
-        add_lflow_to_temp_list = true;
         if (!ovs_list_is_empty(&ls_change->updated_ports) ||
             !ovs_list_is_empty(&ls_change->deleted_ports)) {
             /* XXX: implement lflow index so that we can handle updated and
              * deleted LSPs incrementally. */
-            ovs_list_init(&temp_lflow_list);
-            add_lflow_to_temp_list = false;
             return false;
         }
 
@@ -16277,83 +16360,85 @@  bool lflow_handle_northd_ls_changes(struct ovsdb_idl_txn *ovnsb_txn,
                 sbrec_multicast_group_update_ports_addvalue(sbmc_unknown,
                                                             op->sb);
             }
-        }
-        /* Sync the newly added flows to SB. */
-        struct ovn_lflow *lflow;
-        LIST_FOR_EACH (lflow, list_node, &temp_lflow_list) {
-            size_t n_datapaths;
-            struct ovn_datapath **datapaths_array;
-            if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH) {
-                n_datapaths = ods_size(lflow_input->ls_datapaths);
-                datapaths_array = lflow_input->ls_datapaths->array;
-            } else {
-                n_datapaths = ods_size(lflow_input->lr_datapaths);
-                datapaths_array = lflow_input->lr_datapaths->array;
-            }
-            uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap, n_datapaths);
-            ovs_assert(n_ods == 1);
-            /* There is only one datapath, so it should be moved out of the
-             * group to a single 'od'. */
-            size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
-                                       n_datapaths);
 
-            bitmap_set0(lflow->dpg_bitmap, index);
-            lflow->od = datapaths_array[index];
-
-            /* Logical flow should be re-hashed to allow lookups. */
-            uint32_t hash = hmap_node_hash(&lflow->hmap_node);
-            /* Remove from lflows. */
-            hmap_remove(lflows, &lflow->hmap_node);
-            hash = ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid,
-                                                  hash);
-            /* Add back. */
-            hmap_insert(lflows, &lflow->hmap_node, hash);
-
-            /* Sync to SB. */
-            const struct sbrec_logical_flow *sbflow;
-            lflow->sb_uuid = uuid_random();
-            sbflow = sbrec_logical_flow_insert_persist_uuid(ovnsb_txn,
-                                                            &lflow->sb_uuid);
-            const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
-            uint8_t table = ovn_stage_get_table(lflow->stage);
-            sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
-            sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
-            sbrec_logical_flow_set_pipeline(sbflow, pipeline);
-            sbrec_logical_flow_set_table_id(sbflow, table);
-            sbrec_logical_flow_set_priority(sbflow, lflow->priority);
-            sbrec_logical_flow_set_match(sbflow, lflow->match);
-            sbrec_logical_flow_set_actions(sbflow, lflow->actions);
-            if (lflow->io_port) {
-                struct smap tags = SMAP_INITIALIZER(&tags);
-                smap_add(&tags, "in_out_port", lflow->io_port);
-                sbrec_logical_flow_set_tags(sbflow, &tags);
-                smap_destroy(&tags);
-            }
-            sbrec_logical_flow_set_controller_meter(sbflow, lflow->ctrl_meter);
-            /* Trim the source locator lflow->where, which looks something like
-             * "ovn/northd/northd.c:1234", down to just the part following the
-             * last slash, e.g. "northd.c:1234". */
-            const char *slash = strrchr(lflow->where, '/');
+            /* Sync the newly added flows to SB. */
+            struct lflow_ref_node *lfrn;
+            LIST_FOR_EACH (lfrn, lflow_list_node, &op->lflows) {
+                struct ovn_lflow *lflow = lfrn->lflow;
+                size_t n_datapaths;
+                struct ovn_datapath **datapaths_array;
+                if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH) {
+                    n_datapaths = ods_size(lflow_input->ls_datapaths);
+                    datapaths_array = lflow_input->ls_datapaths->array;
+                } else {
+                    n_datapaths = ods_size(lflow_input->lr_datapaths);
+                    datapaths_array = lflow_input->lr_datapaths->array;
+                }
+                uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap, n_datapaths);
+                ovs_assert(n_ods == 1);
+                /* There is only one datapath, so it should be moved out of the
+                 * group to a single 'od'. */
+                size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
+                                           n_datapaths);
+
+                bitmap_set0(lflow->dpg_bitmap, index);
+                lflow->od = datapaths_array[index];
+
+                /* Logical flow should be re-hashed to allow lookups. */
+                uint32_t hash = hmap_node_hash(&lflow->hmap_node);
+                /* Remove from lflows. */
+                hmap_remove(lflows, &lflow->hmap_node);
+                hash = ovn_logical_flow_hash_datapath(
+                                          &lflow->od->sb->header_.uuid, hash);
+                /* Add back. */
+                hmap_insert(lflows, &lflow->hmap_node, hash);
+
+                /* Sync to SB. */
+                const struct sbrec_logical_flow *sbflow;
+                lflow->sb_uuid = uuid_random();
+                sbflow = sbrec_logical_flow_insert_persist_uuid(
+                                                ovnsb_txn, &lflow->sb_uuid);
+                const char *pipeline = ovn_stage_get_pipeline_name(
+                                                               lflow->stage);
+                uint8_t table = ovn_stage_get_table(lflow->stage);
+                sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
+                sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
+                sbrec_logical_flow_set_pipeline(sbflow, pipeline);
+                sbrec_logical_flow_set_table_id(sbflow, table);
+                sbrec_logical_flow_set_priority(sbflow, lflow->priority);
+                sbrec_logical_flow_set_match(sbflow, lflow->match);
+                sbrec_logical_flow_set_actions(sbflow, lflow->actions);
+                if (lflow->io_port) {
+                    struct smap tags = SMAP_INITIALIZER(&tags);
+                    smap_add(&tags, "in_out_port", lflow->io_port);
+                    sbrec_logical_flow_set_tags(sbflow, &tags);
+                    smap_destroy(&tags);
+                }
+                sbrec_logical_flow_set_controller_meter(sbflow,
+                                                        lflow->ctrl_meter);
+                /* Trim the source locator lflow->where, which looks something
+                 * like "ovn/northd/northd.c:1234", down to just the part
+                 * following the last slash, e.g. "northd.c:1234". */
+                const char *slash = strrchr(lflow->where, '/');
 #if _WIN32
-            const char *backslash = strrchr(lflow->where, '\\');
-            if (!slash || backslash > slash) {
-                slash = backslash;
-            }
+                const char *backslash = strrchr(lflow->where, '\\');
+                if (!slash || backslash > slash) {
+                    slash = backslash;
+                }
 #endif
-            const char *where = slash ? slash + 1 : lflow->where;
+                const char *where = slash ? slash + 1 : lflow->where;
 
-            struct smap ids = SMAP_INITIALIZER(&ids);
-            smap_add(&ids, "stage-name", ovn_stage_to_str(lflow->stage));
-            smap_add(&ids, "source", where);
-            if (lflow->stage_hint) {
-                smap_add(&ids, "stage-hint", lflow->stage_hint);
+                struct smap ids = SMAP_INITIALIZER(&ids);
+                smap_add(&ids, "stage-name", ovn_stage_to_str(lflow->stage));
+                smap_add(&ids, "source", where);
+                if (lflow->stage_hint) {
+                    smap_add(&ids, "stage-hint", lflow->stage_hint);
+                }
+                sbrec_logical_flow_set_external_ids(sbflow, &ids);
+                smap_destroy(&ids);
             }
-            sbrec_logical_flow_set_external_ids(sbflow, &ids);
-            smap_destroy(&ids);
         }
     }
-    ovs_list_init(&temp_lflow_list);
-    add_lflow_to_temp_list = false;
     return true;
 
 }