On Thu, Jul 31, 2025 at 10:13 PM Jacob Tanenbaum <jtane...@redhat.com>
wrote:

>
>
> On Thu, Jul 31, 2025 at 3:06 AM Ales Musil <amu...@redhat.com> wrote:
>
>>
>>
>> On Mon, Jul 28, 2025 at 5:42 PM Jacob Tanenbaum via dev <
>> ovs-dev@openvswitch.org> wrote:
>>
>>> Several operations on Port Groups required a full database
>>> recalculation.
>>>
>>> 1. Creating a new Port Group
>>> 2. Deleting a Port Group
>>> 3. adding a port from a switch with no other ports on a port group
>>> 4. removing a port from a port group that is the last one from a
>>>    specific switch
>>>
>>> Those four operations required a full database recalculation to ensure
>>> that that the lflows from the ACLs and the southbound databases were
>>> correctly populated. Instead reprocess only the relevant switches and
>>> ACLs to avoid the cost of a full recalculation. This is done by having
>>> the port_group_handler update the southbound database and pass the names
>>> of the relevant switches to the ls_stateful_acl node.
>>>
>>> Tested using the ovn sandbox 3000 switches and 15000 ports, Creating a
>>> port group with one ACL and one port
>>> Without I-P improvements:
>>>
>>> Time spent on processing nb_cfg 2:
>>>         ovn-northd delay before processing:     1ms
>>>         ovn-northd completion:                  132ms
>>>
>>> With I-P improvements:
>>>
>>> Time spent on processing nb_cfg 5:
>>>         ovn-northd delay before processing:     0ms
>>>         ovn-northd completion:                  26ms
>>>
>>> Reported-at: https://issues.redhat.com/browse/FDP-758
>>> Reported-by: Dumitru Ceara <dce...@redhat.com>
>>> Signed-off-by: Jacob Tanenbaum <jtane...@redhat.com>
>>>
>>
>> Hello Jacob,
>>
>> I have a couple of comments on top of what Lorenzo already wrote.
>>
>>
>>>
>>> diff --git a/northd/en-lflow.c b/northd/en-lflow.c
>>> index 63565ef80..ac8ebba16 100644
>>> --- a/northd/en-lflow.c
>>> +++ b/northd/en-lflow.c
>>> @@ -157,22 +157,6 @@ lflow_northd_handler(struct engine_node *node,
>>>      return EN_HANDLED_UPDATED;
>>>  }
>>>
>>> -enum engine_input_handler_result
>>> -lflow_port_group_handler(struct engine_node *node, void *data
>>> OVS_UNUSED)
>>> -{
>>> -    struct port_group_data *pg_data =
>>> -        engine_get_input_data("port_group", node);
>>> -
>>> -    /* If the set of switches per port group didn't change then there's
>>> no
>>> -     * need to reprocess lflows.  Otherwise, there might be a need to
>>> -     * add/delete port-group ACLs to/from switches. */
>>> -    if (pg_data->ls_port_groups_sets_changed) {
>>> -        return EN_UNHANDLED;
>>> -    }
>>> -
>>> -    return EN_HANDLED_UPDATED;
>>> -}
>>> -
>>>  enum engine_input_handler_result
>>>  lflow_lr_stateful_handler(struct engine_node *node, void *data)
>>>  {
>>> diff --git a/northd/en-lflow.h b/northd/en-lflow.h
>>> index d3c96c027..326be83f9 100644
>>> --- a/northd/en-lflow.h
>>> +++ b/northd/en-lflow.h
>>> @@ -20,8 +20,6 @@ void *en_lflow_init(struct engine_node *node, struct
>>> engine_arg *arg);
>>>  void en_lflow_cleanup(void *data);
>>>  enum engine_input_handler_result lflow_northd_handler(struct
>>> engine_node *,
>>>                                                        void *data);
>>> -enum engine_input_handler_result lflow_port_group_handler(struct
>>> engine_node *,
>>> -                                                          void *data);
>>>  enum engine_input_handler_result
>>>  lflow_lr_stateful_handler(struct engine_node *, void *data);
>>>  enum engine_input_handler_result
>>> diff --git a/northd/en-ls-stateful.c b/northd/en-ls-stateful.c
>>> index 6aec43415..7b2a3e065 100644
>>> --- a/northd/en-ls-stateful.c
>>> +++ b/northd/en-ls-stateful.c
>>> @@ -159,11 +159,15 @@ ls_stateful_northd_handler(struct engine_node
>>> *node, void *data_)
>>>          struct ls_stateful_record *ls_stateful_rec =
>>>              ls_stateful_table_find_(&data->table, od->nbs);
>>>          ovs_assert(ls_stateful_rec);
>>> -        ls_stateful_record_set_acls(ls_stateful_rec, od->nbs,
>>> -                                    input_data.ls_port_groups);
>>> -
>>> -        /* Add the ls_stateful_rec to the tracking data. */
>>> -        hmapx_add(&data->trk_data.crupdated, ls_stateful_rec);
>>> +        /* Ensure that only one handler per engine run calls
>>> +         * ls_stateful_record_set_acls on the same ls_stateful rec. */
>>> +        if (!hmapx_contains(&data->trk_data.crupdated,
>>> ls_stateful_rec)) {
>>>
>>
>>
>> You can replace this with 'if (hmapx_add(..))', as hmapx_add will return
>> a pointer
>> if it's a new item, NULL otherwise.
>>
>>
>>> +            ls_stateful_record_set_acls(ls_stateful_rec, od->nbs,
>>> +                                        input_data.ls_port_groups);
>>> +
>>> +            /* Add the ls_stateful_rec to the tracking data. */
>>> +            hmapx_add(&data->trk_data.crupdated, ls_stateful_rec);
>>> +        }
>>>      }
>>>
>>>      if (ls_stateful_has_tracked_data(&data->trk_data)) {
>>> @@ -179,11 +183,24 @@ ls_stateful_port_group_handler(struct engine_node
>>> *node, void *data_)
>>>      struct port_group_data *pg_data =
>>>          engine_get_input_data("port_group", node);
>>>
>>> -    if (pg_data->ls_port_groups_sets_changed) {
>>> -        return EN_UNHANDLED;
>>> +    struct ed_type_ls_stateful *data = data_;
>>> +    struct hmapx_node *hmap_node;
>>> +    HMAPX_FOR_EACH (hmap_node, &pg_data->ls_port_groups_sets_changed) {
>>> +        const struct nbrec_logical_switch *nbs = hmap_node->data;
>>> +        struct ls_stateful_record *ls_stateful_rec =
>>> +            ls_stateful_table_find_(&data->table, nbs);
>>> +        ovs_assert(ls_stateful_rec);
>>> +        /* Ensure only one handler per engine run calls
>>> +         * ls_stateful_record_set_acls on the same ls_stateful rec. */
>>> +        if (!hmapx_contains(&data->trk_data.crupdated,
>>> ls_stateful_rec)) {
>>>
>>
>> Same here with the hmapx_add.
>>
>>
>>> +            ls_stateful_record_set_acls(ls_stateful_rec,
>>> +                                        nbs,
>>> +                                        &pg_data->ls_port_groups);
>>> +            /* Add the ls_stateful_rec to the tracking data. */
>>> +            hmapx_add(&data->trk_data.crupdated, ls_stateful_rec);
>>> +        }
>>>      }
>>>
>>> -    struct ed_type_ls_stateful *data = data_;
>>>      if (ls_stateful_has_tracked_data(&data->trk_data)) {
>>>          return EN_HANDLED_UPDATED;
>>>      }
>>> diff --git a/northd/en-port-group.c b/northd/en-port-group.c
>>> index 4fc1a4f24..fd573d9df 100644
>>> --- a/northd/en-port-group.c
>>> +++ b/northd/en-port-group.c
>>> @@ -33,18 +33,24 @@ static struct ls_port_group *ls_port_group_create(
>>>  static void ls_port_group_destroy(struct ls_port_group_table *,
>>>                                    struct ls_port_group *);
>>>
>>> -static bool ls_port_group_process(
>>> +static void ls_port_group_process(
>>>      struct ls_port_group_table *,
>>>      struct port_group_ls_table *,
>>> +    struct hmapx *,
>>>      const struct hmap *ls_ports,
>>>      const struct nbrec_port_group *,
>>> -    struct hmapx *updated_ls_port_groups);
>>> +    struct hmapx *updated_ls_port_groups,
>>> +    struct sset *pruned_ls_port_group_recs);
>>>
>>>  static void ls_port_group_record_clear(
>>>      struct ls_port_group_table *,
>>>      struct port_group_ls_record *,
>>>      struct hmapx *cleared_ls_port_groups);
>>> -static bool ls_port_group_record_prune(struct ls_port_group *);
>>> +
>>> +static bool ls_port_group_record_prune(
>>> +    struct ls_port_group *,
>>> +    const struct nbrec_port_group *,
>>> +    struct sset *);
>>>
>>>  static struct ls_port_group_record *ls_port_group_record_create(
>>>      struct ls_port_group *,
>>> @@ -118,8 +124,8 @@ ls_port_group_table_build(
>>>  {
>>>      const struct nbrec_port_group *nb_pg;
>>>      NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_pg, pg_table) {
>>> -        ls_port_group_process(ls_port_groups, port_group_lses,
>>> -                              ls_ports, nb_pg, NULL);
>>> +        ls_port_group_process(ls_port_groups, port_group_lses, NULL,
>>> +                              ls_ports, nb_pg, NULL, NULL);
>>>      }
>>>  }
>>>
>>> @@ -206,20 +212,27 @@ ls_port_group_destroy(struct ls_port_group_table
>>> *ls_port_groups,
>>>      }
>>>  }
>>>
>>> -/* Process a NB.Port_Group record and stores any updated ls_port_groups
>>> - * in updated_ls_port_groups.  Returns true if a new ls_port_group had
>>> - * to be created or destroyed.
>>> +/* Process a NB.Port_Group record updated the ls_port_group_table and
>>> the
>>> + * port_group_ls_table. Stores a few different updates
>>> + *  1. Updated ls_port_groups are stored in updated_ls_port_groups so
>>> the I-P
>>> + *     can update the Southbound database
>>> + *  2. If a port_groups switch set changes the switch is stored in
>>> + *     ls_port_groups_sets_changed so that later I-P nodes can
>>> recalculate
>>> + *     lflows.
>>> + *  3. If a port_group has a switch removed from it's switch set it is
>>> stored
>>> + *     in pruned_ls_port_group_recs so that the SB entry can be deleted.
>>>   */
>>> -static bool
>>> +static void
>>>  ls_port_group_process(struct ls_port_group_table *ls_port_groups,
>>>                        struct port_group_ls_table *port_group_lses,
>>> +                      struct hmapx *ls_port_groups_sets_changed,
>>>                        const struct hmap *ls_ports,
>>>                        const struct nbrec_port_group *nb_pg,
>>> -                      struct hmapx *updated_ls_port_groups)
>>> +                      struct hmapx *updated_ls_port_groups,
>>> +                      struct sset *pruned_ls_port_group_recs)
>>>  {
>>>      struct hmapx cleared_ls_port_groups =
>>>          HMAPX_INITIALIZER(&cleared_ls_port_groups);
>>> -    bool ls_pg_rec_created = false;
>>>
>>>      struct port_group_ls_record *pg_ls =
>>>          port_group_ls_table_find(port_group_lses, nb_pg);
>>> @@ -233,6 +246,12 @@ ls_port_group_process(struct ls_port_group_table
>>> *ls_port_groups,
>>>      }
>>>
>>>      for (size_t i = 0; i < nb_pg->n_ports; i++) {
>>> +        if (nbrec_port_group_is_deleted(nb_pg)) {
>>> +            /* When a port group is deleted we don't need to
>>> +             * updated the port group as the entry will be pruned
>>> +             */
>>> +            break;
>>> +        }
>>>          const char *port_name = nb_pg->ports[i]->name;
>>>          const struct ovn_datapath *od =
>>>              northd_get_datapath_for_port(ls_ports, port_name);
>>> @@ -262,7 +281,10 @@ ls_port_group_process(struct ls_port_group_table
>>> *ls_port_groups,
>>>              ls_port_group_record_find(ls_pg, nb_pg);
>>>          if (!ls_pg_rec) {
>>>              ls_pg_rec = ls_port_group_record_create(ls_pg, nb_pg);
>>> -            ls_pg_rec_created = true;
>>> +            if (ls_port_groups_sets_changed) {
>>> +                hmapx_add(ls_port_groups_sets_changed,
>>> +                          CONST_CAST(struct nbrec_logical_switch *,
>>> od->nbs));
>>> +            }
>>>          }
>>>          sset_add(&ls_pg_rec->ports, port_name);
>>>
>>> @@ -273,13 +295,18 @@ ls_port_group_process(struct ls_port_group_table
>>> *ls_port_groups,
>>>          }
>>>      }
>>>
>>> -    bool ls_pg_rec_destroyed = false;
>>>      struct hmapx_node *node;
>>>      HMAPX_FOR_EACH (node, &cleared_ls_port_groups) {
>>>          struct ls_port_group *ls_pg = node->data;
>>>
>>> -        if (ls_port_group_record_prune(ls_pg)) {
>>> -            ls_pg_rec_destroyed = true;
>>> +        if (ls_port_group_record_prune(ls_pg,
>>> +                                       nb_pg,
>>> +                                       pruned_ls_port_group_recs)) {
>>> +            if (ls_port_groups_sets_changed) {
>>> +                hmapx_add(ls_port_groups_sets_changed,
>>> +                    CONST_CAST(struct nbrec_logical_switch
>>> *,ls_pg->nbs));
>>> +                hmapx_find_and_delete(&pg_ls->switches, ls_pg->nbs);
>>
>>
>>
>> Shouldn't this be outside the second if? It feels like it should be part
>> of the 'ls_port_group_record_prune()' TBH.
>>
>
> I can move the hmapx_find_and_delete() outside of the second if, but I am
> not sure if it should be inside ls_port_group_record_prune(). There are two
> different structs that are cleaned up `struct ls_port_group_record` and
> `struct port_group_ls_record`. They hold the same information but in a
> different way. So the ls_port_group_record_prune() removes a
> ls_port_group_record from the ls_port_group_table and the
> hmapx_find_and_delete() removes a switch from a port_group_ls_record. If
> you feel strongly that the two operations should be combined should the
> function name change? something like reconcile_port_group_tables()?
>

I don't have a strong preference so it can remain outside
'ls_port_group_record_prune()' but keeping it inside the if isn't the
right thing. It gets called even when 'ls_port_groups_sets_changed'
is NULL.

>
>
>>
>> +            }
>>>          }
>>>
>>>          if (hmap_is_empty(&ls_pg->nb_pgs)) {
>>> @@ -287,8 +314,6 @@ ls_port_group_process(struct ls_port_group_table
>>> *ls_port_groups,
>>>          }
>>>      }
>>>      hmapx_destroy(&cleared_ls_port_groups);
>>> -
>>> -    return ls_pg_rec_created || ls_pg_rec_destroyed;
>>>  }
>>>
>>>  /* Destroys all the struct ls_port_group_record that might be
>>> associated to
>>> @@ -320,17 +345,27 @@ ls_port_group_record_clear(struct
>>> ls_port_group_table *ls_to_port_groups,
>>>  }
>>>
>>>  static bool
>>> -ls_port_group_record_prune(struct ls_port_group *ls_pg)
>>> +ls_port_group_record_prune(struct ls_port_group *ls_pg,
>>> +                           const struct nbrec_port_group *nb_pg,
>>> +                           struct sset *pruned_ls_pg_rec)
>>>  {
>>>      struct ls_port_group_record *ls_pg_rec;
>>>      bool records_pruned = false;
>>>
>>> -    HMAP_FOR_EACH_SAFE (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
>>> -        if (sset_is_empty(&ls_pg_rec->ports)) {
>>> -            ls_port_group_record_destroy(ls_pg, ls_pg_rec);
>>> -            records_pruned = true;
>>> +    struct ds sb_pg_name = DS_EMPTY_INITIALIZER;
>>> +    ls_pg_rec = ls_port_group_record_find(ls_pg, nb_pg);
>>> +    if (sset_is_empty(&ls_pg_rec->ports) ||
>>> +        nbrec_port_group_is_deleted(ls_pg_rec->nb_pg)) {
>>> +        if (pruned_ls_pg_rec) {
>>>
>> +            get_sb_port_group_name(ls_pg_rec->nb_pg->name,
>>> +                                   ls_pg->sb_datapath_key,
>>> +                                   &sb_pg_name);
>>> +            sset_add(pruned_ls_pg_rec, ds_cstr(&sb_pg_name));
>>>          }
>>> +        ls_port_group_record_destroy(ls_pg, ls_pg_rec);
>>> +        records_pruned = true;
>>>      }
>>> +    ds_destroy(&sb_pg_name);
>>>      return records_pruned;
>>>  }
>>>
>>> @@ -460,6 +495,10 @@ en_port_group_init(struct engine_node *node
>>> OVS_UNUSED,
>>>                     struct engine_arg *arg OVS_UNUSED)
>>>  {
>>>      struct port_group_data *pg_data = xmalloc(sizeof *pg_data);
>>> +    *pg_data = (struct port_group_data) {
>>> +        .ls_port_groups_sets_changed =
>>> +            HMAPX_INITIALIZER(&pg_data->ls_port_groups_sets_changed),
>>> +    };
>>>
>>>      ls_port_group_table_init(&pg_data->ls_port_groups);
>>>      port_group_ls_table_init(&pg_data->port_groups_lses);
>>> @@ -473,6 +512,7 @@ en_port_group_cleanup(void *data_)
>>>
>>>      ls_port_group_table_destroy(&data->ls_port_groups);
>>>      port_group_ls_table_destroy(&data->port_groups_lses);
>>> +    hmapx_destroy(&data->ls_port_groups_sets_changed);
>>>  }
>>>
>>>  void
>>> @@ -480,7 +520,7 @@ en_port_group_clear_tracked_data(void *data_)
>>>  {
>>>      struct port_group_data *data = data_;
>>>
>>> -    data->ls_port_groups_sets_changed = true;
>>> +    hmapx_clear(&data->ls_port_groups_sets_changed);
>>>  }
>>>
>>>  enum engine_node_state
>>> @@ -514,73 +554,71 @@ port_group_nb_port_group_handler(struct
>>> engine_node *node, void *data_)
>>>      struct port_group_input input_data =
>>> port_group_get_input_data(node);
>>>      const struct engine_context *eng_ctx = engine_get_context();
>>>      struct port_group_data *data = data_;
>>> -    bool success = true;
>>>
>>>      const struct nbrec_port_group_table *nb_pg_table =
>>>          EN_OVSDB_GET(engine_get_input("NB_port_group", node));
>>>      const struct nbrec_port_group *nb_pg;
>>>
>>> -    /* Return false if a port group is created or deleted.
>>> -     * Handle I-P for only updated port groups. */
>>> -    NBREC_PORT_GROUP_TABLE_FOR_EACH_TRACKED (nb_pg, nb_pg_table) {
>>> -        if (nbrec_port_group_is_new(nb_pg) ||
>>> -                nbrec_port_group_is_deleted(nb_pg)) {
>>> -            return EN_UNHANDLED;
>>> -        }
>>> -    }
>>> -
>>>      struct hmapx updated_ls_port_groups =
>>>          HMAPX_INITIALIZER(&updated_ls_port_groups);
>>>
>>> +    struct sset stale_sb_port_groups =
>>> SSET_INITIALIZER(&stale_sb_port_groups);
>>>      NBREC_PORT_GROUP_TABLE_FOR_EACH_TRACKED (nb_pg, nb_pg_table) {
>>> -        if (ls_port_group_process(&data->ls_port_groups,
>>> -                                  &data->port_groups_lses,
>>> -                                  input_data.ls_ports,
>>> -                                  nb_pg, &updated_ls_port_groups)) {
>>> -            success = false;
>>> -            break;
>>> +        ls_port_group_process(&data->ls_port_groups,
>>> +                              &data->port_groups_lses,
>>> +                              &data->ls_port_groups_sets_changed,
>>> +                              input_data.ls_ports,
>>> +                              nb_pg, &updated_ls_port_groups,
>>> +                              &stale_sb_port_groups);
>>> +
>>>          }
>>> -    }
>>>
>>> -    /* If changes have been successfully processed incrementally then
>>> update
>>> +    /* Changes have been successfully processed incrementally now update
>>>       * the SB too. */
>>> -    if (success) {
>>> -        struct ovsdb_idl_index *sbrec_port_group_by_name =
>>> -            engine_ovsdb_node_get_index(
>>> -                    engine_get_input("SB_port_group", node),
>>> -                    "sbrec_port_group_by_name");
>>> -        struct ds sb_pg_name = DS_EMPTY_INITIALIZER;
>>> -
>>> -        struct hmapx_node *updated_node;
>>> -        HMAPX_FOR_EACH (updated_node, &updated_ls_port_groups) {
>>> -            const struct ls_port_group *ls_pg = updated_node->data;
>>> -            struct ls_port_group_record *ls_pg_rec;
>>> -
>>> -            HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
>>> -                get_sb_port_group_name(ls_pg_rec->nb_pg->name,
>>> -                                        ls_pg->sb_datapath_key,
>>> -                                        &sb_pg_name);
>>> -
>>> -                const char *sb_pg_name_cstr = ds_cstr(&sb_pg_name);
>>> -                const struct sbrec_port_group *sb_pg =
>>> -
>>> sb_port_group_lookup_by_name(sbrec_port_group_by_name,
>>> -                                                 sb_pg_name_cstr);
>>> -                if (!sb_pg) {
>>> -                    sb_pg = create_sb_port_group(eng_ctx->ovnsb_idl_txn,
>>> -                                                 sb_pg_name_cstr);
>>> -                }
>>> -                struct sorted_array nb_ports =
>>> -                    sorted_array_from_sset(&ls_pg_rec->ports);
>>> -                update_sb_port_group(&nb_ports, sb_pg);
>>> -                sorted_array_destroy(&nb_ports);
>>> +    struct ovsdb_idl_index *sbrec_port_group_by_name =
>>> +        engine_ovsdb_node_get_index(
>>> +                engine_get_input("SB_port_group", node),
>>> +                "sbrec_port_group_by_name");
>>> +    struct ds sb_pg_name = DS_EMPTY_INITIALIZER;
>>> +
>>> +    struct hmapx_node *updated_node;
>>> +    HMAPX_FOR_EACH (updated_node, &updated_ls_port_groups) {
>>> +        const struct ls_port_group *ls_pg = updated_node->data;
>>> +        struct ls_port_group_record *ls_pg_rec;
>>> +
>>> +        HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
>>> +            get_sb_port_group_name(ls_pg_rec->nb_pg->name,
>>> +                                   ls_pg->sb_datapath_key,
>>> +                                   &sb_pg_name);
>>> +
>>> +            const char *sb_pg_name_cstr = ds_cstr(&sb_pg_name);
>>> +            const struct sbrec_port_group *sb_pg =
>>> +                sb_port_group_lookup_by_name(sbrec_port_group_by_name,
>>> +                                             sb_pg_name_cstr);
>>> +            if (!sb_pg) {
>>> +                sb_pg = create_sb_port_group(eng_ctx->ovnsb_idl_txn,
>>> +                                             sb_pg_name_cstr);
>>>              }
>>> +            struct sorted_array nb_ports =
>>> +                sorted_array_from_sset(&ls_pg_rec->ports);
>>> +            update_sb_port_group(&nb_ports, sb_pg);
>>> +            sorted_array_destroy(&nb_ports);
>>>          }
>>> -        ds_destroy(&sb_pg_name);
>>>      }
>>> +    ds_destroy(&sb_pg_name);
>>> +
>>> +    const char *stale_sb_port_group_name;
>>> +    SSET_FOR_EACH (stale_sb_port_group_name, &stale_sb_port_groups) {
>>> +        const struct sbrec_port_group *sb_pg =
>>> +            sb_port_group_lookup_by_name(sbrec_port_group_by_name,
>>> +                                         stale_sb_port_group_name);
>>> +            sbrec_port_group_delete(sb_pg);
>>> +    }
>>> +    sset_destroy(&stale_sb_port_groups);
>>> +
>>>
>>> -    data->ls_port_groups_sets_changed = !success;
>>>      hmapx_destroy(&updated_ls_port_groups);
>>> -    return success ? EN_HANDLED_UPDATED : EN_UNHANDLED;
>>> +    return EN_HANDLED_UPDATED;
>>>  }
>>>
>>>  static void
>>> diff --git a/northd/en-port-group.h b/northd/en-port-group.h
>>> index 8dcc950da..f07f118aa 100644
>>> --- a/northd/en-port-group.h
>>> +++ b/northd/en-port-group.h
>>> @@ -104,7 +104,7 @@ struct port_group_input {
>>>  struct port_group_data {
>>>      struct ls_port_group_table ls_port_groups;
>>>      struct port_group_ls_table port_groups_lses;
>>> -    bool ls_port_groups_sets_changed;
>>> +    struct hmapx ls_port_groups_sets_changed;
>>>  };
>>>
>>>  void *en_port_group_init(struct engine_node *, struct engine_arg *);
>>> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
>>> index d43bfc16c..fc4ae58f8 100644
>>> --- a/northd/inc-proc-northd.c
>>> +++ b/northd/inc-proc-northd.c
>>> @@ -348,7 +348,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>>>      engine_add_input(&en_lflow, &en_sampling_app, NULL);
>>>
>>>      engine_add_input(&en_lflow, &en_northd, lflow_northd_handler);
>>> -    engine_add_input(&en_lflow, &en_port_group,
>>> lflow_port_group_handler);
>>> +    engine_add_input(&en_lflow, &en_port_group, engine_noop_handler);
>>>
>>
>> Please add a comment explaining why lflow doesn't
>> need to handle PG changes anymore.
>>
>>
>>>      engine_add_input(&en_lflow, &en_lr_stateful,
>>> lflow_lr_stateful_handler);
>>>      engine_add_input(&en_lflow, &en_ls_stateful,
>>> lflow_ls_stateful_handler);
>>>      engine_add_input(&en_lflow, &en_multicast_igmp,
>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>> index 5e0747fcc..61d00e576 100644
>>> --- a/tests/ovn-northd.at
>>> +++ b/tests/ovn-northd.at
>>> @@ -10100,27 +10100,11 @@ AS_BOX([Create new PG1 and PG2])
>>>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>>  check ovn-nbctl --wait=sb -- pg-add pg1 -- pg-add pg2
>>>  dnl The northd node should not recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> northd], [0], [dnl
>>> -Node: northd
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl The port_group node recomputes every time a NB port group is
>>> added/deleted.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> port_group], [0], [dnl
>>> -Node: port_group
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> -dnl The port_group node is an input for the lflow node.  Port_group
>>> -dnl recompute/compute triggers lflow recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> lflow], [0], [dnl
>>> -Node: lflow
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> +check_engine_stats northd norecompute compute
>>> +dnl The port_group node does not recompute when a NB port group is
>>> added/deleted.
>>> +check_engine_stats port_group norecompute compute
>>> +dnl The Port group recompute/compute should not trigger an lflow
>>> recompute.
>>> +check_engine_stats lflow norecompute compute
>>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>
>>>  AS_BOX([Add ACLs on PG1 and PG2])
>>> @@ -10137,28 +10121,15 @@ check_column "sw1.1" sb:Port_Group ports
>>> name="${sw1_key}_pg1"
>>>  check_column "sw2.1" sb:Port_Group ports name="${sw2_key}_pg1"
>>>
>>>  dnl The northd node should not recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> northd], [0], [dnl
>>> -Node: northd
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl The port_group node recomputes also every time a port from a new
>>> switch
>>> -dnl is added to the group.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> port_group], [0], [dnl
>>> -Node: port_group
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> -dnl The port_group node is an input for the lflow node.  Port_group
>>> -dnl recompute/compute triggers lflow recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> lflow], [0], [dnl
>>> -Node: lflow
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> +check_engine_stats northd norecompute compute
>>> +dnl The port_group node does not recompute when a new switch is added
>>> to the
>>> +dnl group
>>> +check_engine_stats port_group norecompute compute
>>> +dnl The port_group node is an input for the ls_statful node.  Port_group
>>> +dnl should not trigger a recompute.
>>> +check_engine_stats ls_stateful norecompute compute
>>> +dnl Port_Group compute/recompute should not trigger an lflow recompute.
>>> +check_engine_stats lflow norecompute compute
>>>  dnl Expect ACL1 on sw1 and sw2
>>>  check_acl_lflows 1 0 1 0
>>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>> @@ -10173,29 +10144,16 @@ check_column "sw1.2" sb:Port_Group ports
>>> name="${sw1_key}_pg2"
>>>  check_column "sw2.2" sb:Port_Group ports name="${sw2_key}_pg2"
>>>
>>>  dnl The northd node should not recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> northd], [0], [dnl
>>> -Node: northd
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl The port_group node recomputes also every time a port from a new
>>> switch
>>> +check_engine_stats northd norecompute compute
>>> +dnl The port_group node should not recompute when a  port from a new
>>> switch
>>>
>>
>> nit: Extra space.
>>
>>
>>>  dnl is added to the group.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> port_group], [0], [dnl
>>> -Node: port_group
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> -dnl The port_group node is an input for the lflow node.  Port_group
>>> -dnl recompute/compute triggers lflow recompute (for ACLs).
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> lflow], [0], [dnl
>>> -Node: lflow
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> -dnl Expect both ACLs on sw1 and sw2
>>> +check_engine_stats port_group norecompute compute
>>> +dnl The port_group node is an input for the ls_statful node.  Port_group
>>> +dnl should not trigger a recompute
>>> +check_engine_stats ls_stateful norecompute compute
>>> +dnl Port_Group compute/recompute should not trigger an lflow recompute.
>>> +check_engine_stats lflow norecompute compute
>>> +dnl Expect both ACLs on sw1 and sw2.
>>>  check_acl_lflows 1 1 1 1
>>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>
>>> @@ -10210,29 +10168,11 @@ check_column "sw1.2 sw1.3" sb:Port_Group ports
>>> name="${sw1_key}_pg2"
>>>  check_column "sw2.2 sw2.3" sb:Port_Group ports name="${sw2_key}_pg2"
>>>
>>>  dnl The northd node should not recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> northd], [0], [dnl
>>> -Node: northd
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl We did not change the set of switches a pg is applied to, there
>>> should be
>>> -dnl no recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> port_group], [0], [dnl
>>> -Node: port_group
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl We did not change the set of switches a pg is applied to, there
>>> should be
>>> -dnl no recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> lflow], [0], [dnl
>>> -Node: lflow
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl Expect both ACLs on sw1 and sw2
>>> +check_engine_stats northd norecompute compute
>>> +check_engine_stats port_group norecompute compute
>>> +check_engine_stats ls_stateful norecompute compute
>>> +check_engine_stats lflow norecompute compute
>>> +dnl Expect both ACLs on sw1 and sw2.
>>>  check_acl_lflows 1 1 1 1
>>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>
>>> @@ -10248,28 +10188,10 @@ check_column "sw2.2" sb:Port_Group ports
>>> name="${sw2_key}_pg2"
>>>
>>>  dnl The northd node should not recompute, it should handle nb_global
>>> update
>>>  dnl though, therefore "compute: 1".
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> northd], [0], [dnl
>>> -Node: northd
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl We did not change the set of switches a pg is applied to, there
>>> should be
>>> -dnl no recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> port_group], [0], [dnl
>>> -Node: port_group
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl We did not change the set of switches a pg is applied to, there
>>> should be
>>> -dnl no recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> lflow], [0], [dnl
>>> -Node: lflow
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> +check_engine_stats northd norecompute compute
>>> +check_engine_stats port_group norecompute compute
>>> +check_engine_stats ls_stateful norecompute compute
>>> +check_engine_stats lflow norecompute compute
>>>  dnl Expect both ACLs on sw1 and sw2
>>>  check_acl_lflows 1 1 1 1
>>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>> @@ -10280,32 +10202,19 @@ check ovn-nbctl --wait=sb pg-set-ports pg2
>>> sw1.2
>>>  check_column "sw1.1" sb:Port_Group ports name="${sw1_key}_pg1"
>>>  check_column "sw2.1" sb:Port_Group ports name="${sw2_key}_pg1"
>>>  check_column "sw1.2" sb:Port_Group ports name="${sw1_key}_pg2"
>>> +
>>>  AT_CHECK([fetch_column sb:Port_Group ports name="${sw2_key}_pg2"], [0],
>>> [
>>>  ])
>>>
>>>  dnl The northd node should not recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> northd], [0], [dnl
>>> -Node: northd
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl We changed the set of switches a pg is applied to, there should be
>>> +check_engine_stats northd norecompute compute
>>> +dnl We changed the set of switches a pg is applied to, this should no
>>> longer require
>>>  dnl a recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> port_group], [0], [dnl
>>> -Node: port_group
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> -dnl We changed the set of switches a pg is applied to, there should be
>>> -dnl a recompute (for ACLs).
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> lflow], [0], [dnl
>>> -Node: lflow
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> +check_engine_stats port_group norecompute compute
>>> +dnl There should be no recompute for ls_stateful
>>> +check_engine_stats ls_stateful norecompute compute
>>> +dnl No recompute should occur.
>>> +check_engine_stats lflow norecompute compute
>>>  dnl Expect both ACLs on sw1 and only the first one on sw2.
>>>  check_acl_lflows 1 1 1 0
>>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>> @@ -10320,29 +10229,11 @@ check_column "sw1.2" sb:Port_Group ports
>>> name="${sw1_key}_pg2"
>>>  AT_CHECK([fetch_column sb:Port_Group ports name="${sw2_key}_pg2"], [0],
>>> [
>>>  ])
>>>
>>> -dnl The northd node should not recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> northd], [0], [dnl
>>> -Node: northd
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl We changed the set of switches a pg is applied to, there should be
>>> -dnl a recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> port_group], [0], [dnl
>>> -Node: port_group
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> -dnl We changed the set of switches a pg is applied to, there should be
>>> -dnl a recompute (for ACLs).
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> lflow], [0], [dnl
>>> -Node: lflow
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> +check_engine_stats northd norecompute compute
>>> +check_engine_stats port_group norecompute compute
>>> +check_engine_stats ls_stateful norecompute compute
>>> +check_engine_stats lflow norecompute compute
>>> +
>>>  dnl Expect both ACLs on sw1 and not on sw2.
>>>  check_acl_lflows 1 1 0 0
>>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>> @@ -10357,36 +10248,18 @@ check_column "sw2.1" sb:Port_Group ports
>>> name="${sw2_key}_pg1"
>>>  check_column "sw1.2" sb:Port_Group ports name="${sw1_key}_pg2"
>>>  check_column "sw2.2" sb:Port_Group ports name="${sw2_key}_pg2"
>>>
>>> -dnl The northd node should not recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> northd], [0], [dnl
>>> -Node: northd
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl We changed the set of switches a pg is applied to, there should be a
>>> -dnl recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> port_group], [0], [dnl
>>> -Node: port_group
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> -dnl We changed the set of switches a pg is applied to, there should be a
>>> -dnl recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> lflow], [0], [dnl
>>> -Node: lflow
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> +check_engine_stats northd norecompute compute
>>> +check_engine_stats port_group norecompute compute
>>> +check_engine_stats ls_stateful norecompute compute
>>> +check_engine_stats lflow norecompute compute
>>> +
>>>  dnl Expect both ACLs on sw1 and sw2
>>>  check_acl_lflows 1 1 1 1
>>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>
>>>  AS_BOX([Remove second port from both PGs])
>>>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>> -check ovn-nbctl --wait=sb         \
>>> +check ovn-nbctl --wait=sb   \
>>>    -- pg-set-ports pg1 sw1.1 \
>>>    -- pg-set-ports pg2 sw1.2
>>>  check_column "sw1.1" sb:Port_Group ports name="${sw1_key}_pg1"
>>> @@ -10396,33 +10269,53 @@ check_column "sw1.2" sb:Port_Group ports
>>> name="${sw1_key}_pg2"
>>>  AT_CHECK([fetch_column sb:Port_Group ports name="${sw2_key}_pg2"], [0],
>>> [
>>>  ])
>>>
>>> -dnl The northd node should not recompute,.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> northd], [0], [dnl
>>> -Node: northd
>>> -- recompute:            0
>>> -- compute:              1
>>> -- cancel:               0
>>> -])
>>> -dnl We changed the set of switches a pg is applied to, there should be a
>>> -dnl recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> port_group], [0], [dnl
>>> -Node: port_group
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> -dnl We changed the set of switches a pg is applied to, there should be a
>>> -dnl recompute.
>>> -AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/show-stats
>>> lflow], [0], [dnl
>>> -Node: lflow
>>> -- recompute:            1
>>> -- compute:              0
>>> -- cancel:               0
>>> -])
>>> -dnl Expect both ACLs on sw1 and no ACLs on sw2
>>> +check_engine_stats northd norecompute compute
>>> +check_engine_stats port_group norecompute compute
>>> +check_engine_stats ls_stateful norecompute compute
>>> +check_engine_stats lflow norecompute compute
>>> +
>>> +dnl Expect both ACLs sw1
>>>  check_acl_lflows 1 1 0 0
>>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>
>>> +AS_BOX([Delete port groups PG1 and PG2])
>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>> +check ovn-nbctl --wait=sb   \
>>> +    -- pg-del pg1           \
>>> +    -- pg-del pg2
>>> +
>>> +check_engine_stats northd norecompute compute
>>> +check_engine_stats port_group norecompute compute
>>> +check_engine_stats ls_stateful norecompute compute
>>> +check_engine_stats lflow norecompute compute
>>> +
>>> +dnl Expect no ACLs on sw1 and no ACLs on sw2
>>> +check_acl_lflows 0 0 0 0
>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>> +
>>> +AS_BOX([Add PG1 and PG2 again setting ports and ACLs in one
>>> transaction])
>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>>> +check ovn-nbctl --wait=sb                                        \
>>> +    -- pg-add pg1                                                \
>>> +    -- pg-add pg2                                                \
>>> +    -- acl-add pg1 from-lport 1 eth.src==41:41:41:41:41:41 allow \
>>> +    -- acl-add pg2 from-lport 1 eth.src==42:42:42:42:42:42 allow \
>>> +    -- pg-set-ports pg1 sw1.1 sw2.1                              \
>>> +    -- pg-set-ports pg2 sw1.2 sw2.2
>>> +check_column "sw1.1" sb:Port_Group ports name="${sw1_key}_pg1"
>>> +check_column "sw2.1" sb:Port_Group ports name="${sw2_key}_pg1"
>>> +check_column "sw1.2" sb:Port_Group ports name="${sw1_key}_pg2"
>>> +check_column "sw2.2" sb:Port_Group ports name="${sw2_key}_pg2"
>>> +
>>> +check_engine_stats northd norecompute compute
>>> +check_engine_stats port_group norecompute compute
>>> +check_engine_stats ls_stateful norecompute compute
>>> +check_engine_stats lflow norecompute compute
>>> +
>>> +dnl Expect both ACLs on sw1 and sw2
>>> +check_acl_lflows 1 1 1 1
>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>> +
>>>  AT_CLEANUP
>>>  ])
>>>
>>> --
>>> 2.50.1
>>>
>>> _______________________________________________
>>> dev mailing list
>>> d...@openvswitch.org
>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>
>>>
>> Thanks,
>> Ales
>>
>
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to