diff mbox series

[ovs-dev,ovn,v7,7/9] ovn-controller: Handle runtime data changes in flow output engine

Message ID 20200520194017.2352088-1-numans@ovn.org
State Superseded
Headers show
Series Incremental processing improvements. | expand

Commit Message

Numan Siddique May 20, 2020, 7:40 p.m. UTC
From: Numan Siddique <numans@ovn.org>

In order to handle runtime data changes incrementally, the flow outut
runtime data handle should know the changed runtime data.
Runtime data now tracks the changed data for any OVS interface
and SB port binding changes. The tracked data contains a hmap
of tracked datapaths (which changed during runtime data processing.

The flow outout runtime_data handler in this patch doesn't do much
with the tracked data. It returns false if there is tracked data available
so that flow_output run is called. If no tracked data is available
then there is no need for flow computation and the handler returns true.

Next patch in the series processes the tracked data incrementally.

Acked-by: Mark Michelson <mmichels@redhat.com>
Co-Authored-by: Venkata Anil <anilvenkata@redhat.com>
Signed-off-by: Venkata Anil <anilvenkata@redhat.com>
Signed-off-by: Numan Siddique <numans@ovn.org>
---
 controller/binding.c        | 159 ++++++++++++++++++++++++++++++++----
 controller/binding.h        |  21 +++++
 controller/ovn-controller.c |  62 +++++++++++++-
 tests/ovn-performance.at    |  28 +++----
 4 files changed, 240 insertions(+), 30 deletions(-)

Comments

Han Zhou May 21, 2020, 6:57 a.m. UTC | #1
Please see my comments below.

On Wed, May 20, 2020 at 12:41 PM <numans@ovn.org> wrote:
>
> From: Numan Siddique <numans@ovn.org>
>
> In order to handle runtime data changes incrementally, the flow outut
> runtime data handle should know the changed runtime data.
> Runtime data now tracks the changed data for any OVS interface
> and SB port binding changes. The tracked data contains a hmap
> of tracked datapaths (which changed during runtime data processing.
>
> The flow outout runtime_data handler in this patch doesn't do much
> with the tracked data. It returns false if there is tracked data available
> so that flow_output run is called. If no tracked data is available
> then there is no need for flow computation and the handler returns true.
>
> Next patch in the series processes the tracked data incrementally.
>
> Acked-by: Mark Michelson <mmichels@redhat.com>
> Co-Authored-by: Venkata Anil <anilvenkata@redhat.com>
> Signed-off-by: Venkata Anil <anilvenkata@redhat.com>
> Signed-off-by: Numan Siddique <numans@ovn.org>
> ---
>  controller/binding.c        | 159 ++++++++++++++++++++++++++++++++----
>  controller/binding.h        |  21 +++++
>  controller/ovn-controller.c |  62 +++++++++++++-
>  tests/ovn-performance.at    |  28 +++----
>  4 files changed, 240 insertions(+), 30 deletions(-)
>
> diff --git a/controller/binding.c b/controller/binding.c
> index f00c975ce..9b3d46b23 100644
> --- a/controller/binding.c
> +++ b/controller/binding.c
> @@ -69,13 +69,20 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>      ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
>  }
>
> +static struct tracked_binding_datapath *tracked_binding_datapath_create(
> +    const struct sbrec_datapath_binding *,
> +    bool is_new, struct hmap *tracked_dps);
> +static struct tracked_binding_datapath *tracked_binding_datapath_find(
> +    struct hmap *, const struct sbrec_datapath_binding *);
> +
>  static void
>  add_local_datapath__(struct ovsdb_idl_index
*sbrec_datapath_binding_by_key,
>                       struct ovsdb_idl_index
*sbrec_port_binding_by_datapath,
>                       struct ovsdb_idl_index *sbrec_port_binding_by_name,
>                       const struct sbrec_datapath_binding *datapath,
>                       bool has_local_l3gateway, int depth,
> -                     struct hmap *local_datapaths)
> +                     struct hmap *local_datapaths,
> +                     struct hmap *updated_dp_bindings)
>  {
>      uint32_t dp_key = datapath->tunnel_key;
>      struct local_datapath *ld = get_local_datapath(local_datapaths,
dp_key);
> @@ -92,6 +99,11 @@ add_local_datapath__(struct ovsdb_idl_index
*sbrec_datapath_binding_by_key,
>      ld->localnet_port = NULL;
>      ld->has_local_l3gateway = has_local_l3gateway;
>
> +    if (updated_dp_bindings &&
> +        !tracked_binding_datapath_find(updated_dp_bindings, datapath)) {
> +        tracked_binding_datapath_create(datapath, true,
updated_dp_bindings);
> +    }
> +
>      if (depth >= 100) {
>          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
>          VLOG_WARN_RL(&rl, "datapaths nested too deep");
> @@ -124,7 +136,8 @@ add_local_datapath__(struct ovsdb_idl_index
*sbrec_datapath_binding_by_key,
>
sbrec_port_binding_by_datapath,
>                                               sbrec_port_binding_by_name,
>                                               peer->datapath, false,
> -                                             depth + 1, local_datapaths);
> +                                             depth + 1, local_datapaths,
> +                                             updated_dp_bindings);
>                      }
>                      ld->n_peer_ports++;
>                      if (ld->n_peer_ports > ld->n_allocated_peer_ports) {
> @@ -147,12 +160,14 @@ add_local_datapath(struct ovsdb_idl_index
*sbrec_datapath_binding_by_key,
>                     struct ovsdb_idl_index
*sbrec_port_binding_by_datapath,
>                     struct ovsdb_idl_index *sbrec_port_binding_by_name,
>                     const struct sbrec_datapath_binding *datapath,
> -                   bool has_local_l3gateway, struct hmap
*local_datapaths)
> +                   bool has_local_l3gateway, struct hmap
*local_datapaths,
> +                   struct hmap *updated_dp_bindings)
>  {
>      add_local_datapath__(sbrec_datapath_binding_by_key,
>                           sbrec_port_binding_by_datapath,
>                           sbrec_port_binding_by_name,
> -                         datapath, has_local_l3gateway, 0,
local_datapaths);
> +                         datapath, has_local_l3gateway, 0,
local_datapaths,
> +                         updated_dp_bindings);
>  }
>
>  static void
> @@ -623,6 +638,71 @@ is_lport_container(const struct sbrec_port_binding
*pb)
>      return !pb->type[0] && pb->parent_port && pb->parent_port[0];
>  }
>
> +static struct tracked_binding_datapath *
> +tracked_binding_datapath_create(const struct sbrec_datapath_binding *dp,
> +                                bool is_new,
> +                                struct hmap *tracked_datapaths)
> +{
> +    struct tracked_binding_datapath *t_dp = xzalloc(sizeof *t_dp);
> +    t_dp->dp = dp;
> +    t_dp->is_new = is_new;
> +    ovs_list_init(&t_dp->lports_head);
> +    hmap_insert(tracked_datapaths, &t_dp->node,
uuid_hash(&dp->header_.uuid));
> +    return t_dp;
> +}
> +
> +static struct tracked_binding_datapath *
> +tracked_binding_datapath_find(struct hmap *tracked_datapaths,
> +                              const struct sbrec_datapath_binding *dp)
> +{
> +    struct tracked_binding_datapath *t_dp;
> +    size_t hash = uuid_hash(&dp->header_.uuid);
> +    HMAP_FOR_EACH_WITH_HASH (t_dp, node, hash, tracked_datapaths) {
> +        if (uuid_equals(&t_dp->dp->header_.uuid, &dp->header_.uuid)) {
> +            return t_dp;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +static void
> +tracked_binding_datapath_lport_add(const struct sbrec_port_binding *pb,
> +                                   bool deleted,
> +                                   struct hmap *tracked_datapaths)
> +{
> +    if (!tracked_datapaths) {
> +        return;
> +    }
> +
> +    struct tracked_binding_datapath *tracked_dp =
> +        tracked_binding_datapath_find(tracked_datapaths, pb->datapath);
> +    if (!tracked_dp) {
> +        tracked_dp = tracked_binding_datapath_create(pb->datapath, false,
> +                                                     tracked_datapaths);
> +    }
> +    struct tracked_binding_lport *lport = xmalloc(sizeof *lport);
> +    lport->pb = pb;
> +    lport->deleted = deleted;
> +    ovs_list_push_back(&tracked_dp->lports_head, &lport->list_node);
> +}
> +
> +void
> +binding_tracked_dp_destroy(struct hmap *tracked_datapaths)
> +{
> +    struct tracked_binding_datapath *t_dp;
> +    HMAP_FOR_EACH_POP (t_dp, node, tracked_datapaths) {
> +    struct tracked_binding_lport *lport, *next;
> +        LIST_FOR_EACH_SAFE (lport, next, list_node, &t_dp->lports_head) {
> +            ovs_list_remove(&lport->list_node);
> +            free(lport);
> +        }
> +        free(t_dp);
> +    }
> +
> +    hmap_destroy(tracked_datapaths);
> +}
> +
>  /* Corresponds to each Port_Binding.type. */
>  enum en_lport_type {
>      LP_UNKNOWN,
> @@ -770,7 +850,8 @@ can_bind_on_this_chassis(const struct sbrec_chassis
*chassis_rec,
>  static bool
>  release_local_binding_children(const struct sbrec_chassis *chassis_rec,
>                                 struct local_binding *lbinding,
> -                               bool sb_readonly)
> +                               bool sb_readonly,
> +                               struct hmap *tracked_dp_bindings)
>  {
>      struct shash_node *node;
>      SHASH_FOR_EACH (node, &lbinding->children) {
> @@ -780,6 +861,10 @@ release_local_binding_children(const struct
sbrec_chassis *chassis_rec,
>                  return false;
>              }
>          }
> +        if (tracked_dp_bindings) {
> +            tracked_binding_datapath_lport_add(l->pb, true,
> +                                               tracked_dp_bindings);
> +        }
>      }
>
>      return true;
> @@ -787,10 +872,11 @@ release_local_binding_children(const struct
sbrec_chassis *chassis_rec,
>
>  static bool
>  release_local_binding(const struct sbrec_chassis *chassis_rec,
> -                      struct local_binding *lbinding, bool sb_readonly)
> +                      struct local_binding *lbinding, bool sb_readonly,
> +                      struct hmap *tracked_dp_bindings)
>  {
>      if (!release_local_binding_children(chassis_rec, lbinding,
> -                                        sb_readonly)) {
> +                                        sb_readonly,
tracked_dp_bindings)) {
>          return false;
>      }
>
> @@ -798,6 +884,10 @@ release_local_binding(const struct sbrec_chassis
*chassis_rec,
>          return release_lport(lbinding->pb, sb_readonly);
>      }
>
> +    if (tracked_dp_bindings) {
> +        tracked_binding_datapath_lport_add(lbinding->pb, true,
> +                                           tracked_dp_bindings);
> +    }
>      return true;
>  }
>
> @@ -821,7 +911,8 @@ consider_vif_lport_(const struct sbrec_port_binding
*pb,
>              add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
>                              b_ctx_in->sbrec_port_binding_by_datapath,
>                              b_ctx_in->sbrec_port_binding_by_name,
> -                            pb->datapath, false,
b_ctx_out->local_datapaths);
> +                            pb->datapath, false,
b_ctx_out->local_datapaths,
> +                            b_ctx_out->tracked_dp_bindings);
>              update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
>              if (lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn) {
>                  get_qos_params(pb, qos_map);
> @@ -1003,7 +1094,8 @@ consider_nonvif_lport_(const struct
sbrec_port_binding *pb,
>                             b_ctx_in->sbrec_port_binding_by_datapath,
>                             b_ctx_in->sbrec_port_binding_by_name,
>                             pb->datapath, has_local_l3gateway,
> -                           b_ctx_out->local_datapaths);
> +                           b_ctx_out->local_datapaths,
> +                           b_ctx_out->tracked_dp_bindings);
>
>          update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
>          return claim_lport(pb, b_ctx_in->chassis_rec, NULL,
> @@ -1080,7 +1172,8 @@ consider_ha_lport(const struct sbrec_port_binding
*pb,
>                             b_ctx_in->sbrec_port_binding_by_datapath,
>                             b_ctx_in->sbrec_port_binding_by_name,
>                             pb->datapath, false,
> -                           b_ctx_out->local_datapaths);
> +                           b_ctx_out->local_datapaths,
> +                           b_ctx_out->tracked_dp_bindings);
>      }
>
>      return consider_nonvif_lport_(pb, our_chassis, false, b_ctx_in,
b_ctx_out);
> @@ -1367,7 +1460,8 @@ add_local_datapath_peer_port(const struct
sbrec_port_binding *pb,
>                               b_ctx_in->sbrec_port_binding_by_datapath,
>                               b_ctx_in->sbrec_port_binding_by_name,
>                               peer->datapath, false,
> -                             1, b_ctx_out->local_datapaths);
> +                             1, b_ctx_out->local_datapaths,
> +                             b_ctx_out->tracked_dp_bindings);
>          return;
>      }
>
> @@ -1444,6 +1538,18 @@ remove_pb_from_local_datapath(const struct
sbrec_port_binding *pb,
>      }
>  }
>
> +static void
> +update_lport_tracking(const struct sbrec_port_binding *pb,
> +                      bool old_claim, bool new_claim,
> +                      struct hmap *tracked_dp_bindings)
> +{
> +    if (!tracked_dp_bindings || !pb || (old_claim == new_claim)) {
> +        return;
> +    }
> +
> +    tracked_binding_datapath_lport_add(pb, old_claim,
tracked_dp_bindings);
> +}
> +
>  static bool
>  handle_iface_add(const struct ovsrec_interface *iface_rec,
>                   const char *iface_id,
> @@ -1472,6 +1578,7 @@ handle_iface_add(const struct ovsrec_interface
*iface_rec,
>          }
>      }
>
> +    bool claimed = is_lbinding_this_chassis(lbinding,
b_ctx_in->chassis_rec);
>      if (lbinding->pb) {
>          if (!consider_vif_lport(lbinding->pb, b_ctx_in, b_ctx_out,
>                                  lbinding, qos_map)) {
> @@ -1479,6 +1586,10 @@ handle_iface_add(const struct ovsrec_interface
*iface_rec,
>          }
>      }
>
> +    bool now_claimed =
> +        is_lbinding_this_chassis(lbinding, b_ctx_in->chassis_rec);
> +    update_lport_tracking(lbinding->pb, claimed, now_claimed,
> +                          b_ctx_out->tracked_dp_bindings);
>      /* Update the child local_binding's iface (if any children) and try
to
>       *  claim the container lbindings. */
>      struct shash_node *node;
> @@ -1486,10 +1597,15 @@ handle_iface_add(const struct ovsrec_interface
*iface_rec,
>          struct local_binding *child = node->data;
>          child->iface = iface_rec;
>          if (child->type == BT_CONTAINER) {
> +            claimed = is_lbinding_this_chassis(child,
b_ctx_in->chassis_rec);
>              if (!consider_container_lport(child->pb, b_ctx_in, b_ctx_out,
>                                            qos_map)) {
>                  return false;
>              }
> +            now_claimed =
> +                is_lbinding_this_chassis(child, b_ctx_in->chassis_rec);
> +            update_lport_tracking(child->pb, claimed, now_claimed,
> +                                  b_ctx_out->tracked_dp_bindings);
>          }
>      }
>
> @@ -1508,7 +1624,8 @@ handle_iface_delete(const char *lport_name, const
char *iface_name,
>          lbinding->pb->chassis == b_ctx_in->chassis_rec) {
>
>          if (!release_local_binding(b_ctx_in->chassis_rec, lbinding,
> -                                   !b_ctx_in->ovnsb_idl_txn)) {
> +                                   !b_ctx_in->ovnsb_idl_txn,
> +                                   b_ctx_out->tracked_dp_bindings)) {
>              return false;
>          }
>
> @@ -1556,6 +1673,8 @@ binding_handle_ovs_interface_changes(struct
binding_ctx_in *b_ctx_in,
>      bool handled = true;
>      *changed = false;
>
> +    *b_ctx_out->local_lports_changed = false;
> +
>      /* Run the tracked interfaces loop twice. One to handle deleted
>       * changes. And another to handle add/update changes.
>       * This will ensure correctness.
> @@ -1698,9 +1817,10 @@ handle_deleted_vif_lport(const struct
sbrec_port_binding *pb,
>           * clear the 'chassis' column of 'pb'. But we need to do
>           * for the local_binding's children. */
>          if (lbinding->type == BT_VIF &&
> -                !release_local_binding_children(b_ctx_in->chassis_rec,
> -                                                lbinding,
> -
 !b_ctx_in->ovnsb_idl_txn)) {
> +                !release_local_binding_children(
> +                    b_ctx_in->chassis_rec, lbinding,
> +                    !b_ctx_in->ovnsb_idl_txn,
> +                    b_ctx_out->tracked_dp_bindings)) {
>              return false;
>          }
>
> @@ -1746,6 +1866,9 @@ handle_updated_vif_lport(const struct
sbrec_port_binding *pb,
>          return true;
>      }
>
> +    update_lport_tracking(pb, claimed, now_claimed,
> +                          b_ctx_out->tracked_dp_bindings);
> +
>      struct local_binding *lbinding =
>          local_binding_find(b_ctx_out->local_bindings, pb->logical_port);
>
> @@ -1755,11 +1878,17 @@ handle_updated_vif_lport(const struct
sbrec_port_binding *pb,
>      SHASH_FOR_EACH (node, &lbinding->children) {
>          struct local_binding *child = node->data;
>          if (child->type == BT_CONTAINER) {
> +            claimed = is_lbinding_this_chassis(child,
b_ctx_in->chassis_rec);
>              handled = consider_container_lport(child->pb, b_ctx_in,
b_ctx_out,
>                                                 qos_map);
>              if (!handled) {
>                  return false;
>              }
> +
> +            now_claimed = is_lbinding_this_chassis(child,
> +
b_ctx_in->chassis_rec);
> +            update_lport_tracking(child->pb, claimed, now_claimed,
> +                                  b_ctx_out->tracked_dp_bindings);
>          }
>      }
>
> diff --git a/controller/binding.h b/controller/binding.h
> index 21118ecd4..fc2a673e5 100644
> --- a/controller/binding.h
> +++ b/controller/binding.h
> @@ -19,6 +19,9 @@
>
>  #include <stdbool.h>
>  #include "openvswitch/shash.h"
> +#include "openvswitch/hmap.h"
> +#include "openvswitch/uuid.h"
> +#include "openvswitch/list.h"
>
>  struct hmap;
>  struct ovsdb_idl;
> @@ -58,6 +61,8 @@ struct binding_ctx_out {
>      struct sset *local_lport_ids;
>      struct sset *egress_ifaces;
>      struct smap *local_iface_ids;
> +    struct hmap *tracked_dp_bindings;
> +    bool *local_lports_changed;
>  };
>
>  enum local_binding_type {
> @@ -82,6 +87,21 @@ local_binding_find(struct shash *local_bindings, const
char *name)
>      return shash_find_data(local_bindings, name);
>  }
>
> +/* Represents a tracked binding logical port. */
> +struct tracked_binding_lport {
> +    const struct sbrec_port_binding *pb;
> +    struct ovs_list list_node;
> +    bool deleted;
> +};
> +
> +/* Represent a tracked binding datapath. */
> +struct tracked_binding_datapath {
> +    struct hmap_node node;
> +    const struct sbrec_datapath_binding *dp;
> +    bool is_new;
> +    struct ovs_list lports_head; /* List of struct
tracked_binding_lport. */
> +};
> +
>  void binding_register_ovs_idl(struct ovsdb_idl *);
>  void binding_run(struct binding_ctx_in *, struct binding_ctx_out *);
>  bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> @@ -96,4 +116,5 @@ bool binding_handle_ovs_interface_changes(struct
binding_ctx_in *,
>  bool binding_handle_port_binding_changes(struct binding_ctx_in *,
>                                           struct binding_ctx_out *,
>                                           bool *changed);
> +void binding_tracked_dp_destroy(struct hmap *tracked_datapaths);
>  #endif /* controller/binding.h */
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 8f95fff1f..dc790f0f7 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -978,6 +978,23 @@ struct ed_type_runtime_data {
>      struct smap local_iface_ids;
>  };
>
> +struct ed_type_runtime_tracked_data {
> +    struct hmap tracked_dp_bindings;
> +    bool local_lports_changed;
> +    bool tracked;
> +};
> +
> +static void
> +en_runtime_clear_tracked_data(void *tracked_data)
> +{
> +    struct ed_type_runtime_tracked_data *data = tracked_data;
> +
> +    binding_tracked_dp_destroy(&data->tracked_dp_bindings);
> +    hmap_init(&data->tracked_dp_bindings);
> +    data->local_lports_changed = false;
> +    data->tracked = false;
> +}
> +
>  static void *
>  en_runtime_data_init(struct engine_node *node OVS_UNUSED,
>                       struct engine_arg *arg OVS_UNUSED)
> @@ -991,6 +1008,13 @@ en_runtime_data_init(struct engine_node *node
OVS_UNUSED,
>      sset_init(&data->egress_ifaces);
>      smap_init(&data->local_iface_ids);
>      local_bindings_init(&data->local_bindings);
> +
> +    struct ed_type_runtime_tracked_data *tracked_data =
> +        xzalloc(sizeof *tracked_data);
> +    hmap_init(&tracked_data->tracked_dp_bindings);
> +    node->tracked_data = tracked_data;
> +    node->clear_tracked_data = en_runtime_clear_tracked_data;
> +
>      return data;
>  }
>
> @@ -1093,6 +1117,8 @@ init_binding_ctx(struct engine_node *node,
>      b_ctx_out->egress_ifaces = &rt_data->egress_ifaces;
>      b_ctx_out->local_bindings = &rt_data->local_bindings;
>      b_ctx_out->local_iface_ids = &rt_data->local_iface_ids;
> +    b_ctx_out->tracked_dp_bindings = NULL;
> +    b_ctx_out->local_lports_changed = NULL;
>  }
>
>  static void
> @@ -1104,6 +1130,8 @@ en_runtime_data_run(struct engine_node *node, void
*data)
>      struct sset *local_lport_ids = &rt_data->local_lport_ids;
>      struct sset *active_tunnels = &rt_data->active_tunnels;
>
> +    en_runtime_clear_tracked_data(node->tracked_data);
> +
>      static bool first_run = true;
>      if (first_run) {
>          /* don't cleanup since there is no data yet */
> @@ -1156,9 +1184,13 @@ static bool
>  runtime_data_ovs_interface_handler(struct engine_node *node, void *data)
>  {
>      struct ed_type_runtime_data *rt_data = data;
> +    struct ed_type_runtime_tracked_data *tracked_data =
node->tracked_data;
>      struct binding_ctx_in b_ctx_in;
>      struct binding_ctx_out b_ctx_out;
>      init_binding_ctx(node, rt_data, &b_ctx_in, &b_ctx_out);
> +    tracked_data->tracked = true;
> +    b_ctx_out.tracked_dp_bindings = &tracked_data->tracked_dp_bindings;
> +    b_ctx_out.local_lports_changed = &tracked_data->local_lports_changed;
>
>      bool changed = false;
>      if (!binding_handle_ovs_interface_changes(&b_ctx_in, &b_ctx_out,
> @@ -1191,6 +1223,10 @@ runtime_data_sb_port_binding_handler(struct
engine_node *node, void *data)
>          return false;
>      }
>
> +    struct ed_type_runtime_tracked_data *tracked_data =
node->tracked_data;
> +    tracked_data->tracked = true;
> +    b_ctx_out.tracked_dp_bindings = &tracked_data->tracked_dp_bindings;
> +
>      bool changed = false;
>      if (!binding_handle_port_binding_changes(&b_ctx_in, &b_ctx_out,
>                                               &changed)) {
> @@ -1862,6 +1898,29 @@ physical_flow_changes_ovs_iface_handler(struct
engine_node *node OVS_UNUSED,
>      return true;
>  }
>
> +static bool
> +flow_output_runtime_data_handler(struct engine_node *node,
> +                                 void *data OVS_UNUSED)
> +{
> +    struct ed_type_runtime_tracked_data *tracked_data =
> +        engine_get_input_tracked_data("runtime_data", node);
> +
> +    if (!tracked_data || !tracked_data->tracked) {
> +        return false;
> +    }
> +
> +    if (!hmap_is_empty(&tracked_data->tracked_dp_bindings)) {
> +        /* We are not yet handling the tracked datapath binding
> +         * changes. Return false to trigger full recompute. */
> +        return false;
> +    }
> +
> +    if (tracked_data->local_lports_changed) {
> +        engine_set_node_state(node, EN_UPDATED);
> +    }
> +    return true;

Once the change handler is added and when it returns true, it means it has
handled the changes incrementally with full confidence. However, I didn't
see handling of the changes in runtime_data for local_bindings,
local_lport_ids, active_tunnels, egress_ifaces, local_iface_ids, etc. (they
are not involved in the tracked data, if I read the code correctly). Even
for local_lports, why do we return true even if there are changes tracked
for it? So how could we be confident that those changes can be ignored by
flow_output node? If they can be ignored, why would they be needed in
flow_output_run(), which passes them to flow_run()/physical_run()? Is there
something missing?

> +}
> +
>  struct ovn_controller_exit_args {
>      bool *exiting;
>      bool *restart;
> @@ -2014,7 +2073,8 @@ main(int argc, char *argv[])
>                       flow_output_addr_sets_handler);
>      engine_add_input(&en_flow_output, &en_port_groups,
>                       flow_output_port_groups_handler);
> -    engine_add_input(&en_flow_output, &en_runtime_data, NULL);
> +    engine_add_input(&en_flow_output, &en_runtime_data,
> +                     flow_output_runtime_data_handler);
>      engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
>      engine_add_input(&en_flow_output, &en_physical_flow_changes,
>                       flow_output_physical_flow_changes_handler);
> diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
> index 5cc1960b6..6e064e64f 100644
> --- a/tests/ovn-performance.at
> +++ b/tests/ovn-performance.at
> @@ -274,7 +274,7 @@ for i in 1 2; do
>      )
>
>      # Add router port to $ls
> -    OVN_CONTROLLER_EXPECT_HIT(
> +    OVN_CONTROLLER_EXPECT_NO_HIT(
>          [hv1 hv2], [lflow_run],
>          [ovn-nbctl --wait=hv lrp-add lr1 $lrp 02:00:00:00:0$i:01
10.0.$i.1/24]
>      )
> @@ -282,15 +282,15 @@ for i in 1 2; do
>          [hv1 hv2], [lflow_run],
>          [ovn-nbctl --wait=hv lsp-add $ls $lsp]
>      )
> -    OVN_CONTROLLER_EXPECT_HIT(
> +    OVN_CONTROLLER_EXPECT_NO_HIT(
>          [hv1 hv2], [lflow_run],
>          [ovn-nbctl --wait=hv lsp-set-type $lsp router]
>      )
> -    OVN_CONTROLLER_EXPECT_HIT(
> +    OVN_CONTROLLER_EXPECT_NO_HIT(
>          [hv1 hv2], [lflow_run],
>          [ovn-nbctl --wait=hv lsp-set-options $lsp router-port=$lrp]
>      )
> -    OVN_CONTROLLER_EXPECT_HIT(
> +    OVN_CONTROLLER_EXPECT_NO_HIT(
>          [hv1 hv2], [lflow_run],
>          [ovn-nbctl --wait=hv lsp-set-addresses $lsp router]
>      )
> @@ -404,8 +404,8 @@ for i in 1 2; do
>      lp=lp$i
>
>      # Delete port $lp
> -    OVN_CONTROLLER_EXPECT_HIT_COND(
> -        [hv$i hv$j], [lflow_run], [>0 =0],
> +    OVN_CONTROLLER_EXPECT_NO_HIT(
> +        [hv$i hv$j], [lflow_run],
>          [ovn-nbctl --wait=hv lsp-del $lp]
>      )
>  done
> @@ -416,15 +416,15 @@ OVN_CONTROLLER_EXPECT_NO_HIT(
>      [ovn-nbctl --wait=hv destroy Port_Group pg1]
>  )
>
> -for i in 1 2; do
> -    ls=ls$i
> +OVN_CONTROLLER_EXPECT_HIT(
> +    [hv1 hv2], [lflow_run],
> +    [ovn-nbctl --wait=hv ls-del ls1]
> +)
>
> -    # Delete switch $ls
> -    OVN_CONTROLLER_EXPECT_HIT(
> -        [hv1 hv2], [lflow_run],
> -        [ovn-nbctl --wait=hv ls-del $ls]
> -    )
> -done
> +OVN_CONTROLLER_EXPECT_NO_HIT(
> +    [hv1 hv2], [lflow_run],
> +    [ovn-nbctl --wait=hv ls-del ls2]
> +)
>
>  # Delete router lr1
>  OVN_CONTROLLER_EXPECT_NO_HIT(
> --
> 2.26.2
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Numan Siddique May 23, 2020, 10:26 a.m. UTC | #2
On Thu, May 21, 2020 at 12:28 PM Han Zhou <hzhou@ovn.org> wrote:

> Please see my comments below.
>
> On Wed, May 20, 2020 at 12:41 PM <numans@ovn.org> wrote:
> >
> > From: Numan Siddique <numans@ovn.org>
> >
> > In order to handle runtime data changes incrementally, the flow outut
> > runtime data handle should know the changed runtime data.
> > Runtime data now tracks the changed data for any OVS interface
> > and SB port binding changes. The tracked data contains a hmap
> > of tracked datapaths (which changed during runtime data processing.
> >
> > The flow outout runtime_data handler in this patch doesn't do much
> > with the tracked data. It returns false if there is tracked data
> available
> > so that flow_output run is called. If no tracked data is available
> > then there is no need for flow computation and the handler returns true.
> >
> > Next patch in the series processes the tracked data incrementally.
> >
> > Acked-by: Mark Michelson <mmichels@redhat.com>
> > Co-Authored-by: Venkata Anil <anilvenkata@redhat.com>
> > Signed-off-by: Venkata Anil <anilvenkata@redhat.com>
> > Signed-off-by: Numan Siddique <numans@ovn.org>
> > ---
> >  controller/binding.c        | 159 ++++++++++++++++++++++++++++++++----
> >  controller/binding.h        |  21 +++++
> >  controller/ovn-controller.c |  62 +++++++++++++-
> >  tests/ovn-performance.at    |  28 +++----
> >  4 files changed, 240 insertions(+), 30 deletions(-)
> >
> > diff --git a/controller/binding.c b/controller/binding.c
> > index f00c975ce..9b3d46b23 100644
> > --- a/controller/binding.c
> > +++ b/controller/binding.c
> > @@ -69,13 +69,20 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> >      ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
> >  }
> >
> > +static struct tracked_binding_datapath *tracked_binding_datapath_create(
> > +    const struct sbrec_datapath_binding *,
> > +    bool is_new, struct hmap *tracked_dps);
> > +static struct tracked_binding_datapath *tracked_binding_datapath_find(
> > +    struct hmap *, const struct sbrec_datapath_binding *);
> > +
> >  static void
> >  add_local_datapath__(struct ovsdb_idl_index
> *sbrec_datapath_binding_by_key,
> >                       struct ovsdb_idl_index
> *sbrec_port_binding_by_datapath,
> >                       struct ovsdb_idl_index *sbrec_port_binding_by_name,
> >                       const struct sbrec_datapath_binding *datapath,
> >                       bool has_local_l3gateway, int depth,
> > -                     struct hmap *local_datapaths)
> > +                     struct hmap *local_datapaths,
> > +                     struct hmap *updated_dp_bindings)
> >  {
> >      uint32_t dp_key = datapath->tunnel_key;
> >      struct local_datapath *ld = get_local_datapath(local_datapaths,
> dp_key);
> > @@ -92,6 +99,11 @@ add_local_datapath__(struct ovsdb_idl_index
> *sbrec_datapath_binding_by_key,
> >      ld->localnet_port = NULL;
> >      ld->has_local_l3gateway = has_local_l3gateway;
> >
> > +    if (updated_dp_bindings &&
> > +        !tracked_binding_datapath_find(updated_dp_bindings, datapath)) {
> > +        tracked_binding_datapath_create(datapath, true,
> updated_dp_bindings);
> > +    }
> > +
> >      if (depth >= 100) {
> >          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> >          VLOG_WARN_RL(&rl, "datapaths nested too deep");
> > @@ -124,7 +136,8 @@ add_local_datapath__(struct ovsdb_idl_index
> *sbrec_datapath_binding_by_key,
> >
> sbrec_port_binding_by_datapath,
> >                                               sbrec_port_binding_by_name,
> >                                               peer->datapath, false,
> > -                                             depth + 1,
> local_datapaths);
> > +                                             depth + 1, local_datapaths,
> > +                                             updated_dp_bindings);
> >                      }
> >                      ld->n_peer_ports++;
> >                      if (ld->n_peer_ports > ld->n_allocated_peer_ports) {
> > @@ -147,12 +160,14 @@ add_local_datapath(struct ovsdb_idl_index
> *sbrec_datapath_binding_by_key,
> >                     struct ovsdb_idl_index
> *sbrec_port_binding_by_datapath,
> >                     struct ovsdb_idl_index *sbrec_port_binding_by_name,
> >                     const struct sbrec_datapath_binding *datapath,
> > -                   bool has_local_l3gateway, struct hmap
> *local_datapaths)
> > +                   bool has_local_l3gateway, struct hmap
> *local_datapaths,
> > +                   struct hmap *updated_dp_bindings)
> >  {
> >      add_local_datapath__(sbrec_datapath_binding_by_key,
> >                           sbrec_port_binding_by_datapath,
> >                           sbrec_port_binding_by_name,
> > -                         datapath, has_local_l3gateway, 0,
> local_datapaths);
> > +                         datapath, has_local_l3gateway, 0,
> local_datapaths,
> > +                         updated_dp_bindings);
> >  }
> >
> >  static void
> > @@ -623,6 +638,71 @@ is_lport_container(const struct sbrec_port_binding
> *pb)
> >      return !pb->type[0] && pb->parent_port && pb->parent_port[0];
> >  }
> >
> > +static struct tracked_binding_datapath *
> > +tracked_binding_datapath_create(const struct sbrec_datapath_binding *dp,
> > +                                bool is_new,
> > +                                struct hmap *tracked_datapaths)
> > +{
> > +    struct tracked_binding_datapath *t_dp = xzalloc(sizeof *t_dp);
> > +    t_dp->dp = dp;
> > +    t_dp->is_new = is_new;
> > +    ovs_list_init(&t_dp->lports_head);
> > +    hmap_insert(tracked_datapaths, &t_dp->node,
> uuid_hash(&dp->header_.uuid));
> > +    return t_dp;
> > +}
> > +
> > +static struct tracked_binding_datapath *
> > +tracked_binding_datapath_find(struct hmap *tracked_datapaths,
> > +                              const struct sbrec_datapath_binding *dp)
> > +{
> > +    struct tracked_binding_datapath *t_dp;
> > +    size_t hash = uuid_hash(&dp->header_.uuid);
> > +    HMAP_FOR_EACH_WITH_HASH (t_dp, node, hash, tracked_datapaths) {
> > +        if (uuid_equals(&t_dp->dp->header_.uuid, &dp->header_.uuid)) {
> > +            return t_dp;
> > +        }
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +static void
> > +tracked_binding_datapath_lport_add(const struct sbrec_port_binding *pb,
> > +                                   bool deleted,
> > +                                   struct hmap *tracked_datapaths)
> > +{
> > +    if (!tracked_datapaths) {
> > +        return;
> > +    }
> > +
> > +    struct tracked_binding_datapath *tracked_dp =
> > +        tracked_binding_datapath_find(tracked_datapaths, pb->datapath);
> > +    if (!tracked_dp) {
> > +        tracked_dp = tracked_binding_datapath_create(pb->datapath,
> false,
> > +                                                     tracked_datapaths);
> > +    }
> > +    struct tracked_binding_lport *lport = xmalloc(sizeof *lport);
> > +    lport->pb = pb;
> > +    lport->deleted = deleted;
> > +    ovs_list_push_back(&tracked_dp->lports_head, &lport->list_node);
> > +}
> > +
> > +void
> > +binding_tracked_dp_destroy(struct hmap *tracked_datapaths)
> > +{
> > +    struct tracked_binding_datapath *t_dp;
> > +    HMAP_FOR_EACH_POP (t_dp, node, tracked_datapaths) {
> > +    struct tracked_binding_lport *lport, *next;
> > +        LIST_FOR_EACH_SAFE (lport, next, list_node, &t_dp->lports_head)
> {
> > +            ovs_list_remove(&lport->list_node);
> > +            free(lport);
> > +        }
> > +        free(t_dp);
> > +    }
> > +
> > +    hmap_destroy(tracked_datapaths);
> > +}
> > +
> >  /* Corresponds to each Port_Binding.type. */
> >  enum en_lport_type {
> >      LP_UNKNOWN,
> > @@ -770,7 +850,8 @@ can_bind_on_this_chassis(const struct sbrec_chassis
> *chassis_rec,
> >  static bool
> >  release_local_binding_children(const struct sbrec_chassis *chassis_rec,
> >                                 struct local_binding *lbinding,
> > -                               bool sb_readonly)
> > +                               bool sb_readonly,
> > +                               struct hmap *tracked_dp_bindings)
> >  {
> >      struct shash_node *node;
> >      SHASH_FOR_EACH (node, &lbinding->children) {
> > @@ -780,6 +861,10 @@ release_local_binding_children(const struct
> sbrec_chassis *chassis_rec,
> >                  return false;
> >              }
> >          }
> > +        if (tracked_dp_bindings) {
> > +            tracked_binding_datapath_lport_add(l->pb, true,
> > +                                               tracked_dp_bindings);
> > +        }
> >      }
> >
> >      return true;
> > @@ -787,10 +872,11 @@ release_local_binding_children(const struct
> sbrec_chassis *chassis_rec,
> >
> >  static bool
> >  release_local_binding(const struct sbrec_chassis *chassis_rec,
> > -                      struct local_binding *lbinding, bool sb_readonly)
> > +                      struct local_binding *lbinding, bool sb_readonly,
> > +                      struct hmap *tracked_dp_bindings)
> >  {
> >      if (!release_local_binding_children(chassis_rec, lbinding,
> > -                                        sb_readonly)) {
> > +                                        sb_readonly,
> tracked_dp_bindings)) {
> >          return false;
> >      }
> >
> > @@ -798,6 +884,10 @@ release_local_binding(const struct sbrec_chassis
> *chassis_rec,
> >          return release_lport(lbinding->pb, sb_readonly);
> >      }
> >
> > +    if (tracked_dp_bindings) {
> > +        tracked_binding_datapath_lport_add(lbinding->pb, true,
> > +                                           tracked_dp_bindings);
> > +    }
> >      return true;
> >  }
> >
> > @@ -821,7 +911,8 @@ consider_vif_lport_(const struct sbrec_port_binding
> *pb,
> >              add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
> >                              b_ctx_in->sbrec_port_binding_by_datapath,
> >                              b_ctx_in->sbrec_port_binding_by_name,
> > -                            pb->datapath, false,
> b_ctx_out->local_datapaths);
> > +                            pb->datapath, false,
> b_ctx_out->local_datapaths,
> > +                            b_ctx_out->tracked_dp_bindings);
> >              update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
> >              if (lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn) {
> >                  get_qos_params(pb, qos_map);
> > @@ -1003,7 +1094,8 @@ consider_nonvif_lport_(const struct
> sbrec_port_binding *pb,
> >                             b_ctx_in->sbrec_port_binding_by_datapath,
> >                             b_ctx_in->sbrec_port_binding_by_name,
> >                             pb->datapath, has_local_l3gateway,
> > -                           b_ctx_out->local_datapaths);
> > +                           b_ctx_out->local_datapaths,
> > +                           b_ctx_out->tracked_dp_bindings);
> >
> >          update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
> >          return claim_lport(pb, b_ctx_in->chassis_rec, NULL,
> > @@ -1080,7 +1172,8 @@ consider_ha_lport(const struct sbrec_port_binding
> *pb,
> >                             b_ctx_in->sbrec_port_binding_by_datapath,
> >                             b_ctx_in->sbrec_port_binding_by_name,
> >                             pb->datapath, false,
> > -                           b_ctx_out->local_datapaths);
> > +                           b_ctx_out->local_datapaths,
> > +                           b_ctx_out->tracked_dp_bindings);
> >      }
> >
> >      return consider_nonvif_lport_(pb, our_chassis, false, b_ctx_in,
> b_ctx_out);
> > @@ -1367,7 +1460,8 @@ add_local_datapath_peer_port(const struct
> sbrec_port_binding *pb,
> >                               b_ctx_in->sbrec_port_binding_by_datapath,
> >                               b_ctx_in->sbrec_port_binding_by_name,
> >                               peer->datapath, false,
> > -                             1, b_ctx_out->local_datapaths);
> > +                             1, b_ctx_out->local_datapaths,
> > +                             b_ctx_out->tracked_dp_bindings);
> >          return;
> >      }
> >
> > @@ -1444,6 +1538,18 @@ remove_pb_from_local_datapath(const struct
> sbrec_port_binding *pb,
> >      }
> >  }
> >
> > +static void
> > +update_lport_tracking(const struct sbrec_port_binding *pb,
> > +                      bool old_claim, bool new_claim,
> > +                      struct hmap *tracked_dp_bindings)
> > +{
> > +    if (!tracked_dp_bindings || !pb || (old_claim == new_claim)) {
> > +        return;
> > +    }
> > +
> > +    tracked_binding_datapath_lport_add(pb, old_claim,
> tracked_dp_bindings);
> > +}
> > +
> >  static bool
> >  handle_iface_add(const struct ovsrec_interface *iface_rec,
> >                   const char *iface_id,
> > @@ -1472,6 +1578,7 @@ handle_iface_add(const struct ovsrec_interface
> *iface_rec,
> >          }
> >      }
> >
> > +    bool claimed = is_lbinding_this_chassis(lbinding,
> b_ctx_in->chassis_rec);
> >      if (lbinding->pb) {
> >          if (!consider_vif_lport(lbinding->pb, b_ctx_in, b_ctx_out,
> >                                  lbinding, qos_map)) {
> > @@ -1479,6 +1586,10 @@ handle_iface_add(const struct ovsrec_interface
> *iface_rec,
> >          }
> >      }
> >
> > +    bool now_claimed =
> > +        is_lbinding_this_chassis(lbinding, b_ctx_in->chassis_rec);
> > +    update_lport_tracking(lbinding->pb, claimed, now_claimed,
> > +                          b_ctx_out->tracked_dp_bindings);
> >      /* Update the child local_binding's iface (if any children) and try
> to
> >       *  claim the container lbindings. */
> >      struct shash_node *node;
> > @@ -1486,10 +1597,15 @@ handle_iface_add(const struct ovsrec_interface
> *iface_rec,
> >          struct local_binding *child = node->data;
> >          child->iface = iface_rec;
> >          if (child->type == BT_CONTAINER) {
> > +            claimed = is_lbinding_this_chassis(child,
> b_ctx_in->chassis_rec);
> >              if (!consider_container_lport(child->pb, b_ctx_in,
> b_ctx_out,
> >                                            qos_map)) {
> >                  return false;
> >              }
> > +            now_claimed =
> > +                is_lbinding_this_chassis(child, b_ctx_in->chassis_rec);
> > +            update_lport_tracking(child->pb, claimed, now_claimed,
> > +                                  b_ctx_out->tracked_dp_bindings);
> >          }
> >      }
> >
> > @@ -1508,7 +1624,8 @@ handle_iface_delete(const char *lport_name, const
> char *iface_name,
> >          lbinding->pb->chassis == b_ctx_in->chassis_rec) {
> >
> >          if (!release_local_binding(b_ctx_in->chassis_rec, lbinding,
> > -                                   !b_ctx_in->ovnsb_idl_txn)) {
> > +                                   !b_ctx_in->ovnsb_idl_txn,
> > +                                   b_ctx_out->tracked_dp_bindings)) {
> >              return false;
> >          }
> >
> > @@ -1556,6 +1673,8 @@ binding_handle_ovs_interface_changes(struct
> binding_ctx_in *b_ctx_in,
> >      bool handled = true;
> >      *changed = false;
> >
> > +    *b_ctx_out->local_lports_changed = false;
> > +
> >      /* Run the tracked interfaces loop twice. One to handle deleted
> >       * changes. And another to handle add/update changes.
> >       * This will ensure correctness.
> > @@ -1698,9 +1817,10 @@ handle_deleted_vif_lport(const struct
> sbrec_port_binding *pb,
> >           * clear the 'chassis' column of 'pb'. But we need to do
> >           * for the local_binding's children. */
> >          if (lbinding->type == BT_VIF &&
> > -                !release_local_binding_children(b_ctx_in->chassis_rec,
> > -                                                lbinding,
> > -
>  !b_ctx_in->ovnsb_idl_txn)) {
> > +                !release_local_binding_children(
> > +                    b_ctx_in->chassis_rec, lbinding,
> > +                    !b_ctx_in->ovnsb_idl_txn,
> > +                    b_ctx_out->tracked_dp_bindings)) {
> >              return false;
> >          }
> >
> > @@ -1746,6 +1866,9 @@ handle_updated_vif_lport(const struct
> sbrec_port_binding *pb,
> >          return true;
> >      }
> >
> > +    update_lport_tracking(pb, claimed, now_claimed,
> > +                          b_ctx_out->tracked_dp_bindings);
> > +
> >      struct local_binding *lbinding =
> >          local_binding_find(b_ctx_out->local_bindings, pb->logical_port);
> >
> > @@ -1755,11 +1878,17 @@ handle_updated_vif_lport(const struct
> sbrec_port_binding *pb,
> >      SHASH_FOR_EACH (node, &lbinding->children) {
> >          struct local_binding *child = node->data;
> >          if (child->type == BT_CONTAINER) {
> > +            claimed = is_lbinding_this_chassis(child,
> b_ctx_in->chassis_rec);
> >              handled = consider_container_lport(child->pb, b_ctx_in,
> b_ctx_out,
> >                                                 qos_map);
> >              if (!handled) {
> >                  return false;
> >              }
> > +
> > +            now_claimed = is_lbinding_this_chassis(child,
> > +
> b_ctx_in->chassis_rec);
> > +            update_lport_tracking(child->pb, claimed, now_claimed,
> > +                                  b_ctx_out->tracked_dp_bindings);
> >          }
> >      }
> >
> > diff --git a/controller/binding.h b/controller/binding.h
> > index 21118ecd4..fc2a673e5 100644
> > --- a/controller/binding.h
> > +++ b/controller/binding.h
> > @@ -19,6 +19,9 @@
> >
> >  #include <stdbool.h>
> >  #include "openvswitch/shash.h"
> > +#include "openvswitch/hmap.h"
> > +#include "openvswitch/uuid.h"
> > +#include "openvswitch/list.h"
> >
> >  struct hmap;
> >  struct ovsdb_idl;
> > @@ -58,6 +61,8 @@ struct binding_ctx_out {
> >      struct sset *local_lport_ids;
> >      struct sset *egress_ifaces;
> >      struct smap *local_iface_ids;
> > +    struct hmap *tracked_dp_bindings;
> > +    bool *local_lports_changed;
> >  };
> >
> >  enum local_binding_type {
> > @@ -82,6 +87,21 @@ local_binding_find(struct shash *local_bindings, const
> char *name)
> >      return shash_find_data(local_bindings, name);
> >  }
> >
> > +/* Represents a tracked binding logical port. */
> > +struct tracked_binding_lport {
> > +    const struct sbrec_port_binding *pb;
> > +    struct ovs_list list_node;
> > +    bool deleted;
> > +};
> > +
> > +/* Represent a tracked binding datapath. */
> > +struct tracked_binding_datapath {
> > +    struct hmap_node node;
> > +    const struct sbrec_datapath_binding *dp;
> > +    bool is_new;
> > +    struct ovs_list lports_head; /* List of struct
> tracked_binding_lport. */
> > +};
> > +
> >  void binding_register_ovs_idl(struct ovsdb_idl *);
> >  void binding_run(struct binding_ctx_in *, struct binding_ctx_out *);
> >  bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> > @@ -96,4 +116,5 @@ bool binding_handle_ovs_interface_changes(struct
> binding_ctx_in *,
> >  bool binding_handle_port_binding_changes(struct binding_ctx_in *,
> >                                           struct binding_ctx_out *,
> >                                           bool *changed);
> > +void binding_tracked_dp_destroy(struct hmap *tracked_datapaths);
> >  #endif /* controller/binding.h */
> > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> > index 8f95fff1f..dc790f0f7 100644
> > --- a/controller/ovn-controller.c
> > +++ b/controller/ovn-controller.c
> > @@ -978,6 +978,23 @@ struct ed_type_runtime_data {
> >      struct smap local_iface_ids;
> >  };
> >
> > +struct ed_type_runtime_tracked_data {
> > +    struct hmap tracked_dp_bindings;
> > +    bool local_lports_changed;
> > +    bool tracked;
> > +};
> > +
> > +static void
> > +en_runtime_clear_tracked_data(void *tracked_data)
> > +{
> > +    struct ed_type_runtime_tracked_data *data = tracked_data;
> > +
> > +    binding_tracked_dp_destroy(&data->tracked_dp_bindings);
> > +    hmap_init(&data->tracked_dp_bindings);
> > +    data->local_lports_changed = false;
> > +    data->tracked = false;
> > +}
> > +
> >  static void *
> >  en_runtime_data_init(struct engine_node *node OVS_UNUSED,
> >                       struct engine_arg *arg OVS_UNUSED)
> > @@ -991,6 +1008,13 @@ en_runtime_data_init(struct engine_node *node
> OVS_UNUSED,
> >      sset_init(&data->egress_ifaces);
> >      smap_init(&data->local_iface_ids);
> >      local_bindings_init(&data->local_bindings);
> > +
> > +    struct ed_type_runtime_tracked_data *tracked_data =
> > +        xzalloc(sizeof *tracked_data);
> > +    hmap_init(&tracked_data->tracked_dp_bindings);
> > +    node->tracked_data = tracked_data;
> > +    node->clear_tracked_data = en_runtime_clear_tracked_data;
> > +
> >      return data;
> >  }
> >
> > @@ -1093,6 +1117,8 @@ init_binding_ctx(struct engine_node *node,
> >      b_ctx_out->egress_ifaces = &rt_data->egress_ifaces;
> >      b_ctx_out->local_bindings = &rt_data->local_bindings;
> >      b_ctx_out->local_iface_ids = &rt_data->local_iface_ids;
> > +    b_ctx_out->tracked_dp_bindings = NULL;
> > +    b_ctx_out->local_lports_changed = NULL;
> >  }
> >
> >  static void
> > @@ -1104,6 +1130,8 @@ en_runtime_data_run(struct engine_node *node, void
> *data)
> >      struct sset *local_lport_ids = &rt_data->local_lport_ids;
> >      struct sset *active_tunnels = &rt_data->active_tunnels;
> >
> > +    en_runtime_clear_tracked_data(node->tracked_data);
> > +
> >      static bool first_run = true;
> >      if (first_run) {
> >          /* don't cleanup since there is no data yet */
> > @@ -1156,9 +1184,13 @@ static bool
> >  runtime_data_ovs_interface_handler(struct engine_node *node, void *data)
> >  {
> >      struct ed_type_runtime_data *rt_data = data;
> > +    struct ed_type_runtime_tracked_data *tracked_data =
> node->tracked_data;
> >      struct binding_ctx_in b_ctx_in;
> >      struct binding_ctx_out b_ctx_out;
> >      init_binding_ctx(node, rt_data, &b_ctx_in, &b_ctx_out);
> > +    tracked_data->tracked = true;
> > +    b_ctx_out.tracked_dp_bindings = &tracked_data->tracked_dp_bindings;
> > +    b_ctx_out.local_lports_changed =
> &tracked_data->local_lports_changed;
> >
> >      bool changed = false;
> >      if (!binding_handle_ovs_interface_changes(&b_ctx_in, &b_ctx_out,
> > @@ -1191,6 +1223,10 @@ runtime_data_sb_port_binding_handler(struct
> engine_node *node, void *data)
> >          return false;
> >      }
> >
> > +    struct ed_type_runtime_tracked_data *tracked_data =
> node->tracked_data;
> > +    tracked_data->tracked = true;
> > +    b_ctx_out.tracked_dp_bindings = &tracked_data->tracked_dp_bindings;
> > +
> >      bool changed = false;
> >      if (!binding_handle_port_binding_changes(&b_ctx_in, &b_ctx_out,
> >                                               &changed)) {
> > @@ -1862,6 +1898,29 @@ physical_flow_changes_ovs_iface_handler(struct
> engine_node *node OVS_UNUSED,
> >      return true;
> >  }
> >
> > +static bool
> > +flow_output_runtime_data_handler(struct engine_node *node,
> > +                                 void *data OVS_UNUSED)
> > +{
> > +    struct ed_type_runtime_tracked_data *tracked_data =
> > +        engine_get_input_tracked_data("runtime_data", node);
> > +
> > +    if (!tracked_data || !tracked_data->tracked) {
> > +        return false;
> > +    }
> > +
> > +    if (!hmap_is_empty(&tracked_data->tracked_dp_bindings)) {
> > +        /* We are not yet handling the tracked datapath binding
> > +         * changes. Return false to trigger full recompute. */
> > +        return false;
> > +    }
> > +
> > +    if (tracked_data->local_lports_changed) {
> > +        engine_set_node_state(node, EN_UPDATED);
> > +    }
> > +    return true;
>
> Once the change handler is added and when it returns true, it means it has
> handled the changes incrementally with full confidence. However, I didn't
> see handling of the changes in runtime_data for local_bindings,
> local_lport_ids, active_tunnels, egress_ifaces, local_iface_ids, etc. (they
> are not involved in the tracked data, if I read the code correctly). Even
> for local_lports, why do we return true even if there are changes tracked
> for it?


I really don't understand the need for addding all the information in the
tracked data. And I'm not sure how would flow_output handlers will use all
of them.

The tracked data will be used by the flow_output_runtime_data_handler().

If we take each of the  runtime data:

 - active_tunnels: This is not updated during the I-P of OVS interface and
Port binding changes.
                    active_tunnels are built in en_runtime_data_run() when
it calls bfd_calculate_active_tunnels()
                    and the tunnel OVS interfaces are considered.
                    If any non VIF OVS interrace changes presently we fall
back to full recompute of runtime data.
                    So I don't think there is a need to have this as part
of tracked data.

 - egress_ifaces:  This is used by only binding.c and is not used during
flow computation. So I don't see why it
                   should be part of tracked data.

 - local_iface_ids: This is used by only binding.c and is not used during
flow computation. So I don't see why it
                    should be part of tracked data.

 - local_lport_ids:  When an OVS interface is added/updated with
external_ids:iface-id set, then we add this to
                     this sset. It is used in
                     1. ovn-controller.c for monitoring any Port bindings
with this name.
                     2. And also in lflows.c to check if we need to skip
the logical flow

   Lets say an OVS interface was added with external_ids:iface-id=foo and if
   'foo' logical port is not yet present. In this case local_lport_ids is
updated with 'foo'.
   This doesn't result in any port binding changes. Hence we only need to
do is to call update_sb_monitors
   and that's why flow_output_runtime_data_handler() sets the engine node
to updated and returns true
   as there is no need to do any flow calculations. And to indicate this
'local_lports_changed' bool
   is added in the 'struct ed_type_runtime_tracked_data'.

Lets say during the I-P handling of OVS interface change, a port 'foo' is
bound and it is
the first port of its logical switch (say sw0) to bound.

With this patch series we add the following tracked data -  tracked dp
bindings = [ls = sw0, is_new = true, tracked ports = [lport = foo, deleted
= false]]
and it also would have updated local_lport_ids, local_bindings,
local_datapaths etc.

Lets say we also put - tracked local_lport_ids, tracked local_iface_ids,
tracked local_binding etc into the tracked data. My question how it will be
used
by the flow output handlers ?

Something like ?

for each tracked dp bindings
   calculate flows for these dp bindings
done

for each tracked local_lport_ids
    calculate flows for these lport ids
done

for each tracked local_bindings
    calculate flows for these local bindings
done

Can you please tell how the flow output handlers will use this data as I'm
not clear.



> So how could we be confident that those changes can be ignored by
> flow_output node? If they can be ignored, why would they be needed in
> flow_output_run(), which passes them to flow_run()/physical_run()? Is there
> something missing?
>

In my opinion, run time data handlers need to indicate to the flow_output
(or its parent engine) that
these port bindings added/deleted and these datapaths added/deleted. And
flow computation can be
done for these tracked/changed entities. And any changes  to the runtime
data  is captured in the
hmap of 'struct tracked_binding_datapath'.

Thanks
Numan


> > +}
> > +
> >  struct ovn_controller_exit_args {
> >      bool *exiting;
> >      bool *restart;
> > @@ -2014,7 +2073,8 @@ main(int argc, char *argv[])
> >                       flow_output_addr_sets_handler);
> >      engine_add_input(&en_flow_output, &en_port_groups,
> >                       flow_output_port_groups_handler);
> > -    engine_add_input(&en_flow_output, &en_runtime_data, NULL);
> > +    engine_add_input(&en_flow_output, &en_runtime_data,
> > +                     flow_output_runtime_data_handler);
> >      engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
> >      engine_add_input(&en_flow_output, &en_physical_flow_changes,
> >                       flow_output_physical_flow_changes_handler);
> > diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
> > index 5cc1960b6..6e064e64f 100644
> > --- a/tests/ovn-performance.at
> > +++ b/tests/ovn-performance.at
> > @@ -274,7 +274,7 @@ for i in 1 2; do
> >      )
> >
> >      # Add router port to $ls
> > -    OVN_CONTROLLER_EXPECT_HIT(
> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> >          [hv1 hv2], [lflow_run],
> >          [ovn-nbctl --wait=hv lrp-add lr1 $lrp 02:00:00:00:0$i:01
> 10.0.$i.1/24]
> >      )
> > @@ -282,15 +282,15 @@ for i in 1 2; do
> >          [hv1 hv2], [lflow_run],
> >          [ovn-nbctl --wait=hv lsp-add $ls $lsp]
> >      )
> > -    OVN_CONTROLLER_EXPECT_HIT(
> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> >          [hv1 hv2], [lflow_run],
> >          [ovn-nbctl --wait=hv lsp-set-type $lsp router]
> >      )
> > -    OVN_CONTROLLER_EXPECT_HIT(
> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> >          [hv1 hv2], [lflow_run],
> >          [ovn-nbctl --wait=hv lsp-set-options $lsp router-port=$lrp]
> >      )
> > -    OVN_CONTROLLER_EXPECT_HIT(
> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> >          [hv1 hv2], [lflow_run],
> >          [ovn-nbctl --wait=hv lsp-set-addresses $lsp router]
> >      )
> > @@ -404,8 +404,8 @@ for i in 1 2; do
> >      lp=lp$i
> >
> >      # Delete port $lp
> > -    OVN_CONTROLLER_EXPECT_HIT_COND(
> > -        [hv$i hv$j], [lflow_run], [>0 =0],
> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> > +        [hv$i hv$j], [lflow_run],
> >          [ovn-nbctl --wait=hv lsp-del $lp]
> >      )
> >  done
> > @@ -416,15 +416,15 @@ OVN_CONTROLLER_EXPECT_NO_HIT(
> >      [ovn-nbctl --wait=hv destroy Port_Group pg1]
> >  )
> >
> > -for i in 1 2; do
> > -    ls=ls$i
> > +OVN_CONTROLLER_EXPECT_HIT(
> > +    [hv1 hv2], [lflow_run],
> > +    [ovn-nbctl --wait=hv ls-del ls1]
> > +)
> >
> > -    # Delete switch $ls
> > -    OVN_CONTROLLER_EXPECT_HIT(
> > -        [hv1 hv2], [lflow_run],
> > -        [ovn-nbctl --wait=hv ls-del $ls]
> > -    )
> > -done
> > +OVN_CONTROLLER_EXPECT_NO_HIT(
> > +    [hv1 hv2], [lflow_run],
> > +    [ovn-nbctl --wait=hv ls-del ls2]
> > +)
> >
> >  # Delete router lr1
> >  OVN_CONTROLLER_EXPECT_NO_HIT(
> > --
> > 2.26.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
>
>
Han Zhou May 24, 2020, 5:40 a.m. UTC | #3
On Sat, May 23, 2020 at 3:26 AM Numan Siddique <numans@ovn.org> wrote:
>
>
>
> On Thu, May 21, 2020 at 12:28 PM Han Zhou <hzhou@ovn.org> wrote:
>>
>> Please see my comments below.
>>
>> On Wed, May 20, 2020 at 12:41 PM <numans@ovn.org> wrote:
>> >
>> > From: Numan Siddique <numans@ovn.org>
>> >
>> > In order to handle runtime data changes incrementally, the flow outut
>> > runtime data handle should know the changed runtime data.
>> > Runtime data now tracks the changed data for any OVS interface
>> > and SB port binding changes. The tracked data contains a hmap
>> > of tracked datapaths (which changed during runtime data processing.
>> >
>> > The flow outout runtime_data handler in this patch doesn't do much
>> > with the tracked data. It returns false if there is tracked data
available
>> > so that flow_output run is called. If no tracked data is available
>> > then there is no need for flow computation and the handler returns
true.
>> >
>> > Next patch in the series processes the tracked data incrementally.
>> >
>> > Acked-by: Mark Michelson <mmichels@redhat.com>
>> > Co-Authored-by: Venkata Anil <anilvenkata@redhat.com>
>> > Signed-off-by: Venkata Anil <anilvenkata@redhat.com>
>> > Signed-off-by: Numan Siddique <numans@ovn.org>
>> > ---
>> >  controller/binding.c        | 159 ++++++++++++++++++++++++++++++++----
>> >  controller/binding.h        |  21 +++++
>> >  controller/ovn-controller.c |  62 +++++++++++++-
>> >  tests/ovn-performance.at    |  28 +++----
>> >  4 files changed, 240 insertions(+), 30 deletions(-)
>> >
>> > diff --git a/controller/binding.c b/controller/binding.c
>> > index f00c975ce..9b3d46b23 100644
>> > --- a/controller/binding.c
>> > +++ b/controller/binding.c
>> > @@ -69,13 +69,20 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>> >      ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
>> >  }
>> >
>> > +static struct tracked_binding_datapath
*tracked_binding_datapath_create(
>> > +    const struct sbrec_datapath_binding *,
>> > +    bool is_new, struct hmap *tracked_dps);
>> > +static struct tracked_binding_datapath *tracked_binding_datapath_find(
>> > +    struct hmap *, const struct sbrec_datapath_binding *);
>> > +
>> >  static void
>> >  add_local_datapath__(struct ovsdb_idl_index
>> *sbrec_datapath_binding_by_key,
>> >                       struct ovsdb_idl_index
>> *sbrec_port_binding_by_datapath,
>> >                       struct ovsdb_idl_index
*sbrec_port_binding_by_name,
>> >                       const struct sbrec_datapath_binding *datapath,
>> >                       bool has_local_l3gateway, int depth,
>> > -                     struct hmap *local_datapaths)
>> > +                     struct hmap *local_datapaths,
>> > +                     struct hmap *updated_dp_bindings)
>> >  {
>> >      uint32_t dp_key = datapath->tunnel_key;
>> >      struct local_datapath *ld = get_local_datapath(local_datapaths,
>> dp_key);
>> > @@ -92,6 +99,11 @@ add_local_datapath__(struct ovsdb_idl_index
>> *sbrec_datapath_binding_by_key,
>> >      ld->localnet_port = NULL;
>> >      ld->has_local_l3gateway = has_local_l3gateway;
>> >
>> > +    if (updated_dp_bindings &&
>> > +        !tracked_binding_datapath_find(updated_dp_bindings,
datapath)) {
>> > +        tracked_binding_datapath_create(datapath, true,
>> updated_dp_bindings);
>> > +    }
>> > +
>> >      if (depth >= 100) {
>> >          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
>> >          VLOG_WARN_RL(&rl, "datapaths nested too deep");
>> > @@ -124,7 +136,8 @@ add_local_datapath__(struct ovsdb_idl_index
>> *sbrec_datapath_binding_by_key,
>> >
>> sbrec_port_binding_by_datapath,
>> >
sbrec_port_binding_by_name,
>> >                                               peer->datapath, false,
>> > -                                             depth + 1,
local_datapaths);
>> > +                                             depth + 1,
local_datapaths,
>> > +                                             updated_dp_bindings);
>> >                      }
>> >                      ld->n_peer_ports++;
>> >                      if (ld->n_peer_ports >
ld->n_allocated_peer_ports) {
>> > @@ -147,12 +160,14 @@ add_local_datapath(struct ovsdb_idl_index
>> *sbrec_datapath_binding_by_key,
>> >                     struct ovsdb_idl_index
>> *sbrec_port_binding_by_datapath,
>> >                     struct ovsdb_idl_index *sbrec_port_binding_by_name,
>> >                     const struct sbrec_datapath_binding *datapath,
>> > -                   bool has_local_l3gateway, struct hmap
>> *local_datapaths)
>> > +                   bool has_local_l3gateway, struct hmap
>> *local_datapaths,
>> > +                   struct hmap *updated_dp_bindings)
>> >  {
>> >      add_local_datapath__(sbrec_datapath_binding_by_key,
>> >                           sbrec_port_binding_by_datapath,
>> >                           sbrec_port_binding_by_name,
>> > -                         datapath, has_local_l3gateway, 0,
>> local_datapaths);
>> > +                         datapath, has_local_l3gateway, 0,
>> local_datapaths,
>> > +                         updated_dp_bindings);
>> >  }
>> >
>> >  static void
>> > @@ -623,6 +638,71 @@ is_lport_container(const struct sbrec_port_binding
>> *pb)
>> >      return !pb->type[0] && pb->parent_port && pb->parent_port[0];
>> >  }
>> >
>> > +static struct tracked_binding_datapath *
>> > +tracked_binding_datapath_create(const struct sbrec_datapath_binding
*dp,
>> > +                                bool is_new,
>> > +                                struct hmap *tracked_datapaths)
>> > +{
>> > +    struct tracked_binding_datapath *t_dp = xzalloc(sizeof *t_dp);
>> > +    t_dp->dp = dp;
>> > +    t_dp->is_new = is_new;
>> > +    ovs_list_init(&t_dp->lports_head);
>> > +    hmap_insert(tracked_datapaths, &t_dp->node,
>> uuid_hash(&dp->header_.uuid));
>> > +    return t_dp;
>> > +}
>> > +
>> > +static struct tracked_binding_datapath *
>> > +tracked_binding_datapath_find(struct hmap *tracked_datapaths,
>> > +                              const struct sbrec_datapath_binding *dp)
>> > +{
>> > +    struct tracked_binding_datapath *t_dp;
>> > +    size_t hash = uuid_hash(&dp->header_.uuid);
>> > +    HMAP_FOR_EACH_WITH_HASH (t_dp, node, hash, tracked_datapaths) {
>> > +        if (uuid_equals(&t_dp->dp->header_.uuid, &dp->header_.uuid)) {
>> > +            return t_dp;
>> > +        }
>> > +    }
>> > +
>> > +    return NULL;
>> > +}
>> > +
>> > +static void
>> > +tracked_binding_datapath_lport_add(const struct sbrec_port_binding
*pb,
>> > +                                   bool deleted,
>> > +                                   struct hmap *tracked_datapaths)
>> > +{
>> > +    if (!tracked_datapaths) {
>> > +        return;
>> > +    }
>> > +
>> > +    struct tracked_binding_datapath *tracked_dp =
>> > +        tracked_binding_datapath_find(tracked_datapaths,
pb->datapath);
>> > +    if (!tracked_dp) {
>> > +        tracked_dp = tracked_binding_datapath_create(pb->datapath,
false,
>> > +
tracked_datapaths);
>> > +    }
>> > +    struct tracked_binding_lport *lport = xmalloc(sizeof *lport);
>> > +    lport->pb = pb;
>> > +    lport->deleted = deleted;
>> > +    ovs_list_push_back(&tracked_dp->lports_head, &lport->list_node);
>> > +}
>> > +
>> > +void
>> > +binding_tracked_dp_destroy(struct hmap *tracked_datapaths)
>> > +{
>> > +    struct tracked_binding_datapath *t_dp;
>> > +    HMAP_FOR_EACH_POP (t_dp, node, tracked_datapaths) {
>> > +    struct tracked_binding_lport *lport, *next;
>> > +        LIST_FOR_EACH_SAFE (lport, next, list_node,
&t_dp->lports_head) {
>> > +            ovs_list_remove(&lport->list_node);
>> > +            free(lport);
>> > +        }
>> > +        free(t_dp);
>> > +    }
>> > +
>> > +    hmap_destroy(tracked_datapaths);
>> > +}
>> > +
>> >  /* Corresponds to each Port_Binding.type. */
>> >  enum en_lport_type {
>> >      LP_UNKNOWN,
>> > @@ -770,7 +850,8 @@ can_bind_on_this_chassis(const struct sbrec_chassis
>> *chassis_rec,
>> >  static bool
>> >  release_local_binding_children(const struct sbrec_chassis
*chassis_rec,
>> >                                 struct local_binding *lbinding,
>> > -                               bool sb_readonly)
>> > +                               bool sb_readonly,
>> > +                               struct hmap *tracked_dp_bindings)
>> >  {
>> >      struct shash_node *node;
>> >      SHASH_FOR_EACH (node, &lbinding->children) {
>> > @@ -780,6 +861,10 @@ release_local_binding_children(const struct
>> sbrec_chassis *chassis_rec,
>> >                  return false;
>> >              }
>> >          }
>> > +        if (tracked_dp_bindings) {
>> > +            tracked_binding_datapath_lport_add(l->pb, true,
>> > +                                               tracked_dp_bindings);
>> > +        }
>> >      }
>> >
>> >      return true;
>> > @@ -787,10 +872,11 @@ release_local_binding_children(const struct
>> sbrec_chassis *chassis_rec,
>> >
>> >  static bool
>> >  release_local_binding(const struct sbrec_chassis *chassis_rec,
>> > -                      struct local_binding *lbinding, bool
sb_readonly)
>> > +                      struct local_binding *lbinding, bool
sb_readonly,
>> > +                      struct hmap *tracked_dp_bindings)
>> >  {
>> >      if (!release_local_binding_children(chassis_rec, lbinding,
>> > -                                        sb_readonly)) {
>> > +                                        sb_readonly,
>> tracked_dp_bindings)) {
>> >          return false;
>> >      }
>> >
>> > @@ -798,6 +884,10 @@ release_local_binding(const struct sbrec_chassis
>> *chassis_rec,
>> >          return release_lport(lbinding->pb, sb_readonly);
>> >      }
>> >
>> > +    if (tracked_dp_bindings) {
>> > +        tracked_binding_datapath_lport_add(lbinding->pb, true,
>> > +                                           tracked_dp_bindings);
>> > +    }
>> >      return true;
>> >  }
>> >
>> > @@ -821,7 +911,8 @@ consider_vif_lport_(const struct sbrec_port_binding
>> *pb,
>> >
 add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
>> >                              b_ctx_in->sbrec_port_binding_by_datapath,
>> >                              b_ctx_in->sbrec_port_binding_by_name,
>> > -                            pb->datapath, false,
>> b_ctx_out->local_datapaths);
>> > +                            pb->datapath, false,
>> b_ctx_out->local_datapaths,
>> > +                            b_ctx_out->tracked_dp_bindings);
>> >              update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
>> >              if (lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn) {
>> >                  get_qos_params(pb, qos_map);
>> > @@ -1003,7 +1094,8 @@ consider_nonvif_lport_(const struct
>> sbrec_port_binding *pb,
>> >                             b_ctx_in->sbrec_port_binding_by_datapath,
>> >                             b_ctx_in->sbrec_port_binding_by_name,
>> >                             pb->datapath, has_local_l3gateway,
>> > -                           b_ctx_out->local_datapaths);
>> > +                           b_ctx_out->local_datapaths,
>> > +                           b_ctx_out->tracked_dp_bindings);
>> >
>> >          update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
>> >          return claim_lport(pb, b_ctx_in->chassis_rec, NULL,
>> > @@ -1080,7 +1172,8 @@ consider_ha_lport(const struct sbrec_port_binding
>> *pb,
>> >                             b_ctx_in->sbrec_port_binding_by_datapath,
>> >                             b_ctx_in->sbrec_port_binding_by_name,
>> >                             pb->datapath, false,
>> > -                           b_ctx_out->local_datapaths);
>> > +                           b_ctx_out->local_datapaths,
>> > +                           b_ctx_out->tracked_dp_bindings);
>> >      }
>> >
>> >      return consider_nonvif_lport_(pb, our_chassis, false, b_ctx_in,
>> b_ctx_out);
>> > @@ -1367,7 +1460,8 @@ add_local_datapath_peer_port(const struct
>> sbrec_port_binding *pb,
>> >                               b_ctx_in->sbrec_port_binding_by_datapath,
>> >                               b_ctx_in->sbrec_port_binding_by_name,
>> >                               peer->datapath, false,
>> > -                             1, b_ctx_out->local_datapaths);
>> > +                             1, b_ctx_out->local_datapaths,
>> > +                             b_ctx_out->tracked_dp_bindings);
>> >          return;
>> >      }
>> >
>> > @@ -1444,6 +1538,18 @@ remove_pb_from_local_datapath(const struct
>> sbrec_port_binding *pb,
>> >      }
>> >  }
>> >
>> > +static void
>> > +update_lport_tracking(const struct sbrec_port_binding *pb,
>> > +                      bool old_claim, bool new_claim,
>> > +                      struct hmap *tracked_dp_bindings)
>> > +{
>> > +    if (!tracked_dp_bindings || !pb || (old_claim == new_claim)) {
>> > +        return;
>> > +    }
>> > +
>> > +    tracked_binding_datapath_lport_add(pb, old_claim,
>> tracked_dp_bindings);
>> > +}
>> > +
>> >  static bool
>> >  handle_iface_add(const struct ovsrec_interface *iface_rec,
>> >                   const char *iface_id,
>> > @@ -1472,6 +1578,7 @@ handle_iface_add(const struct ovsrec_interface
>> *iface_rec,
>> >          }
>> >      }
>> >
>> > +    bool claimed = is_lbinding_this_chassis(lbinding,
>> b_ctx_in->chassis_rec);
>> >      if (lbinding->pb) {
>> >          if (!consider_vif_lport(lbinding->pb, b_ctx_in, b_ctx_out,
>> >                                  lbinding, qos_map)) {
>> > @@ -1479,6 +1586,10 @@ handle_iface_add(const struct ovsrec_interface
>> *iface_rec,
>> >          }
>> >      }
>> >
>> > +    bool now_claimed =
>> > +        is_lbinding_this_chassis(lbinding, b_ctx_in->chassis_rec);
>> > +    update_lport_tracking(lbinding->pb, claimed, now_claimed,
>> > +                          b_ctx_out->tracked_dp_bindings);
>> >      /* Update the child local_binding's iface (if any children) and
try
>> to
>> >       *  claim the container lbindings. */
>> >      struct shash_node *node;
>> > @@ -1486,10 +1597,15 @@ handle_iface_add(const struct ovsrec_interface
>> *iface_rec,
>> >          struct local_binding *child = node->data;
>> >          child->iface = iface_rec;
>> >          if (child->type == BT_CONTAINER) {
>> > +            claimed = is_lbinding_this_chassis(child,
>> b_ctx_in->chassis_rec);
>> >              if (!consider_container_lport(child->pb, b_ctx_in,
b_ctx_out,
>> >                                            qos_map)) {
>> >                  return false;
>> >              }
>> > +            now_claimed =
>> > +                is_lbinding_this_chassis(child,
b_ctx_in->chassis_rec);
>> > +            update_lport_tracking(child->pb, claimed, now_claimed,
>> > +                                  b_ctx_out->tracked_dp_bindings);
>> >          }
>> >      }
>> >
>> > @@ -1508,7 +1624,8 @@ handle_iface_delete(const char *lport_name, const
>> char *iface_name,
>> >          lbinding->pb->chassis == b_ctx_in->chassis_rec) {
>> >
>> >          if (!release_local_binding(b_ctx_in->chassis_rec, lbinding,
>> > -                                   !b_ctx_in->ovnsb_idl_txn)) {
>> > +                                   !b_ctx_in->ovnsb_idl_txn,
>> > +                                   b_ctx_out->tracked_dp_bindings)) {
>> >              return false;
>> >          }
>> >
>> > @@ -1556,6 +1673,8 @@ binding_handle_ovs_interface_changes(struct
>> binding_ctx_in *b_ctx_in,
>> >      bool handled = true;
>> >      *changed = false;
>> >
>> > +    *b_ctx_out->local_lports_changed = false;
>> > +
>> >      /* Run the tracked interfaces loop twice. One to handle deleted
>> >       * changes. And another to handle add/update changes.
>> >       * This will ensure correctness.
>> > @@ -1698,9 +1817,10 @@ handle_deleted_vif_lport(const struct
>> sbrec_port_binding *pb,
>> >           * clear the 'chassis' column of 'pb'. But we need to do
>> >           * for the local_binding's children. */
>> >          if (lbinding->type == BT_VIF &&
>> > -                !release_local_binding_children(b_ctx_in->chassis_rec,
>> > -                                                lbinding,
>> > -
>>  !b_ctx_in->ovnsb_idl_txn)) {
>> > +                !release_local_binding_children(
>> > +                    b_ctx_in->chassis_rec, lbinding,
>> > +                    !b_ctx_in->ovnsb_idl_txn,
>> > +                    b_ctx_out->tracked_dp_bindings)) {
>> >              return false;
>> >          }
>> >
>> > @@ -1746,6 +1866,9 @@ handle_updated_vif_lport(const struct
>> sbrec_port_binding *pb,
>> >          return true;
>> >      }
>> >
>> > +    update_lport_tracking(pb, claimed, now_claimed,
>> > +                          b_ctx_out->tracked_dp_bindings);
>> > +
>> >      struct local_binding *lbinding =
>> >          local_binding_find(b_ctx_out->local_bindings,
pb->logical_port);
>> >
>> > @@ -1755,11 +1878,17 @@ handle_updated_vif_lport(const struct
>> sbrec_port_binding *pb,
>> >      SHASH_FOR_EACH (node, &lbinding->children) {
>> >          struct local_binding *child = node->data;
>> >          if (child->type == BT_CONTAINER) {
>> > +            claimed = is_lbinding_this_chassis(child,
>> b_ctx_in->chassis_rec);
>> >              handled = consider_container_lport(child->pb, b_ctx_in,
>> b_ctx_out,
>> >                                                 qos_map);
>> >              if (!handled) {
>> >                  return false;
>> >              }
>> > +
>> > +            now_claimed = is_lbinding_this_chassis(child,
>> > +
>> b_ctx_in->chassis_rec);
>> > +            update_lport_tracking(child->pb, claimed, now_claimed,
>> > +                                  b_ctx_out->tracked_dp_bindings);
>> >          }
>> >      }
>> >
>> > diff --git a/controller/binding.h b/controller/binding.h
>> > index 21118ecd4..fc2a673e5 100644
>> > --- a/controller/binding.h
>> > +++ b/controller/binding.h
>> > @@ -19,6 +19,9 @@
>> >
>> >  #include <stdbool.h>
>> >  #include "openvswitch/shash.h"
>> > +#include "openvswitch/hmap.h"
>> > +#include "openvswitch/uuid.h"
>> > +#include "openvswitch/list.h"
>> >
>> >  struct hmap;
>> >  struct ovsdb_idl;
>> > @@ -58,6 +61,8 @@ struct binding_ctx_out {
>> >      struct sset *local_lport_ids;
>> >      struct sset *egress_ifaces;
>> >      struct smap *local_iface_ids;
>> > +    struct hmap *tracked_dp_bindings;
>> > +    bool *local_lports_changed;
>> >  };
>> >
>> >  enum local_binding_type {
>> > @@ -82,6 +87,21 @@ local_binding_find(struct shash *local_bindings,
const
>> char *name)
>> >      return shash_find_data(local_bindings, name);
>> >  }
>> >
>> > +/* Represents a tracked binding logical port. */
>> > +struct tracked_binding_lport {
>> > +    const struct sbrec_port_binding *pb;
>> > +    struct ovs_list list_node;
>> > +    bool deleted;
>> > +};
>> > +
>> > +/* Represent a tracked binding datapath. */
>> > +struct tracked_binding_datapath {
>> > +    struct hmap_node node;
>> > +    const struct sbrec_datapath_binding *dp;
>> > +    bool is_new;
>> > +    struct ovs_list lports_head; /* List of struct
>> tracked_binding_lport. */
>> > +};
>> > +
>> >  void binding_register_ovs_idl(struct ovsdb_idl *);
>> >  void binding_run(struct binding_ctx_in *, struct binding_ctx_out *);
>> >  bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
>> > @@ -96,4 +116,5 @@ bool binding_handle_ovs_interface_changes(struct
>> binding_ctx_in *,
>> >  bool binding_handle_port_binding_changes(struct binding_ctx_in *,
>> >                                           struct binding_ctx_out *,
>> >                                           bool *changed);
>> > +void binding_tracked_dp_destroy(struct hmap *tracked_datapaths);
>> >  #endif /* controller/binding.h */
>> > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
>> > index 8f95fff1f..dc790f0f7 100644
>> > --- a/controller/ovn-controller.c
>> > +++ b/controller/ovn-controller.c
>> > @@ -978,6 +978,23 @@ struct ed_type_runtime_data {
>> >      struct smap local_iface_ids;
>> >  };
>> >
>> > +struct ed_type_runtime_tracked_data {
>> > +    struct hmap tracked_dp_bindings;
>> > +    bool local_lports_changed;
>> > +    bool tracked;
>> > +};
>> > +
>> > +static void
>> > +en_runtime_clear_tracked_data(void *tracked_data)
>> > +{
>> > +    struct ed_type_runtime_tracked_data *data = tracked_data;
>> > +
>> > +    binding_tracked_dp_destroy(&data->tracked_dp_bindings);
>> > +    hmap_init(&data->tracked_dp_bindings);
>> > +    data->local_lports_changed = false;
>> > +    data->tracked = false;
>> > +}
>> > +
>> >  static void *
>> >  en_runtime_data_init(struct engine_node *node OVS_UNUSED,
>> >                       struct engine_arg *arg OVS_UNUSED)
>> > @@ -991,6 +1008,13 @@ en_runtime_data_init(struct engine_node *node
>> OVS_UNUSED,
>> >      sset_init(&data->egress_ifaces);
>> >      smap_init(&data->local_iface_ids);
>> >      local_bindings_init(&data->local_bindings);
>> > +
>> > +    struct ed_type_runtime_tracked_data *tracked_data =
>> > +        xzalloc(sizeof *tracked_data);
>> > +    hmap_init(&tracked_data->tracked_dp_bindings);
>> > +    node->tracked_data = tracked_data;
>> > +    node->clear_tracked_data = en_runtime_clear_tracked_data;
>> > +
>> >      return data;
>> >  }
>> >
>> > @@ -1093,6 +1117,8 @@ init_binding_ctx(struct engine_node *node,
>> >      b_ctx_out->egress_ifaces = &rt_data->egress_ifaces;
>> >      b_ctx_out->local_bindings = &rt_data->local_bindings;
>> >      b_ctx_out->local_iface_ids = &rt_data->local_iface_ids;
>> > +    b_ctx_out->tracked_dp_bindings = NULL;
>> > +    b_ctx_out->local_lports_changed = NULL;
>> >  }
>> >
>> >  static void
>> > @@ -1104,6 +1130,8 @@ en_runtime_data_run(struct engine_node *node,
void
>> *data)
>> >      struct sset *local_lport_ids = &rt_data->local_lport_ids;
>> >      struct sset *active_tunnels = &rt_data->active_tunnels;
>> >
>> > +    en_runtime_clear_tracked_data(node->tracked_data);
>> > +
>> >      static bool first_run = true;
>> >      if (first_run) {
>> >          /* don't cleanup since there is no data yet */
>> > @@ -1156,9 +1184,13 @@ static bool
>> >  runtime_data_ovs_interface_handler(struct engine_node *node, void
*data)
>> >  {
>> >      struct ed_type_runtime_data *rt_data = data;
>> > +    struct ed_type_runtime_tracked_data *tracked_data =
>> node->tracked_data;
>> >      struct binding_ctx_in b_ctx_in;
>> >      struct binding_ctx_out b_ctx_out;
>> >      init_binding_ctx(node, rt_data, &b_ctx_in, &b_ctx_out);
>> > +    tracked_data->tracked = true;
>> > +    b_ctx_out.tracked_dp_bindings =
&tracked_data->tracked_dp_bindings;
>> > +    b_ctx_out.local_lports_changed =
&tracked_data->local_lports_changed;
>> >
>> >      bool changed = false;
>> >      if (!binding_handle_ovs_interface_changes(&b_ctx_in, &b_ctx_out,
>> > @@ -1191,6 +1223,10 @@ runtime_data_sb_port_binding_handler(struct
>> engine_node *node, void *data)
>> >          return false;
>> >      }
>> >
>> > +    struct ed_type_runtime_tracked_data *tracked_data =
>> node->tracked_data;
>> > +    tracked_data->tracked = true;
>> > +    b_ctx_out.tracked_dp_bindings =
&tracked_data->tracked_dp_bindings;
>> > +
>> >      bool changed = false;
>> >      if (!binding_handle_port_binding_changes(&b_ctx_in, &b_ctx_out,
>> >                                               &changed)) {
>> > @@ -1862,6 +1898,29 @@ physical_flow_changes_ovs_iface_handler(struct
>> engine_node *node OVS_UNUSED,
>> >      return true;
>> >  }
>> >
>> > +static bool
>> > +flow_output_runtime_data_handler(struct engine_node *node,
>> > +                                 void *data OVS_UNUSED)
>> > +{
>> > +    struct ed_type_runtime_tracked_data *tracked_data =
>> > +        engine_get_input_tracked_data("runtime_data", node);
>> > +
>> > +    if (!tracked_data || !tracked_data->tracked) {
>> > +        return false;
>> > +    }
>> > +
>> > +    if (!hmap_is_empty(&tracked_data->tracked_dp_bindings)) {
>> > +        /* We are not yet handling the tracked datapath binding
>> > +         * changes. Return false to trigger full recompute. */
>> > +        return false;
>> > +    }
>> > +
>> > +    if (tracked_data->local_lports_changed) {
>> > +        engine_set_node_state(node, EN_UPDATED);
>> > +    }
>> > +    return true;
>>
>> Once the change handler is added and when it returns true, it means it
has
>> handled the changes incrementally with full confidence. However, I didn't
>> see handling of the changes in runtime_data for local_bindings,
>> local_lport_ids, active_tunnels, egress_ifaces, local_iface_ids, etc.
(they
>> are not involved in the tracked data, if I read the code correctly). Even
>> for local_lports, why do we return true even if there are changes tracked
>> for it?
>
>
> I really don't understand the need for addding all the information in the
> tracked data. And I'm not sure how would flow_output handlers will use
all of them.
>
> The tracked data will be used by the flow_output_runtime_data_handler().
>
> If we take each of the  runtime data:
>
>  - active_tunnels: This is not updated during the I-P of OVS interface
and Port binding changes.
>                     active_tunnels are built in en_runtime_data_run()
when it calls bfd_calculate_active_tunnels()
>                     and the tunnel OVS interfaces are considered.
>                     If any non VIF OVS interrace changes presently we
fall back to full recompute of runtime data.
>                     So I don't think there is a need to have this as part
of tracked data.
>
Thanks for the explain. This reason is important but it is implicit. From
the dependency graph point of view, one can't easily tell why is it
ignored, so I think it deserves a XXX comment, for code reader to
understand and track for future improvement when we do incremental
processing for non-VIF interface changes.

>  - egress_ifaces:  This is used by only binding.c and is not used during
flow computation. So I don't see why it
>                    should be part of tracked data.
>
>  - local_iface_ids: This is used by only binding.c and is not used during
flow computation. So I don't see why it
>                     should be part of tracked data.
>

This reason is more obvious. I'd suggest to add a comment in the struct
definition of the ed_type_runtime_data to note it as "engine private data,
not depended by other nodes". The I-P engine framework doesn't express this
kind of encapsulation well, but I think some comments would be helpful.

>  - local_lport_ids:  When an OVS interface is added/updated with
external_ids:iface-id set, then we add this to
>                      this sset. It is used in
>                      1. ovn-controller.c for monitoring any Port bindings
with this name.
>                      2. And also in lflows.c to check if we need to skip
the logical flow
>
>    Lets say an OVS interface was added with external_ids:iface-id=foo and
if
>    'foo' logical port is not yet present. In this case local_lport_ids is
updated with 'foo'.
>    This doesn't result in any port binding changes. Hence we only need to
do is to call update_sb_monitors
>    and that's why flow_output_runtime_data_handler() sets the engine node
to updated and returns true
>    as there is no need to do any flow calculations. And to indicate this
'local_lports_changed' bool
>    is added in the 'struct ed_type_runtime_tracked_data'.
>
> Lets say during the I-P handling of OVS interface change, a port 'foo' is
bound and it is
> the first port of its logical switch (say sw0) to bound.
>
> With this patch series we add the following tracked data -  tracked dp
bindings = [ls = sw0, is_new = true, tracked ports = [lport = foo, deleted
= false]]
> and it also would have updated local_lport_ids, local_bindings,
local_datapaths etc.
>

I see, so it is handled indirectly by handling tracked_binding_lport in
tracked_data->tracked_dp_bindings->lports_head. I think it should be ok,
but it deserves a comment to help understanding why we don't need track and
handle local_lport_ids change directly.

> Lets say we also put - tracked local_lport_ids, tracked local_iface_ids,
tracked local_binding etc into the tracked data. My question how it will be
used
> by the flow output handlers ?
>
> Something like ?
>
> for each tracked dp bindings
>    calculate flows for these dp bindings
> done
>
> for each tracked local_lport_ids
>     calculate flows for these lport ids
> done
>
> for each tracked local_bindings
>     calculate flows for these local bindings
> done
>
> Can you please tell how the flow output handlers will use this data as
I'm not clear.
>

Usually the straightforward way to implement change-handlers (my
understanding) is to check how the full computing is using each of the
input data. Consider each input as a DB table (for runtime_data, since it
has multiple members, it is like multiple sub-tables combined, and ignore
the private members that are not used as inputs), the full compute is
considered as a join between the tables, but the "join" is not a simple
relational DB join but highly customerized functions. Now for I-P change
handler, to handle each tracked change, ideally it is a probe to other
related tables, depending on how the "join" was implemented in full
computing. However, such kind of probe is hard to implement, so the general
approach is to link each table item to the key of the output table (for
lflow_output, the key is the uuid of lflow for logical flows, or uuid of PB
for physical flows) by building an index for efficient lookup, such as the
lflow_resource_ref index, and handle the changes of any input table by
finding the key in the output and recompute the items related to that key.
This is like always probe in the same order of table traverse. With this
approach it is easier to reason the correctness. It is essentially no
difference from the DDlog approach, but just all logic are manually
implemented.

Take local_lport_ids as example, in full computing it is used to see if a
lflow should be bypassed on current chassis. So, if we tracked the changes
of this "sub-table", we can do:

for each tracked local_lport_ids:
    find the lflow uuid related to this local_lport_id // this requires
building the index, as you already done, which is perfect
    delete all ovn-flows that is generated by this lflow
    call consider_lflow() again
done

This ensures the impacted lflow get bypassed/unbypassed properly because
the related lflows are reconsidered.

>
>>
>> So how could we be confident that those changes can be ignored by
>> flow_output node? If they can be ignored, why would they be needed in
>> flow_output_run(), which passes them to flow_run()/physical_run()? Is
there
>> something missing?
>
>
> In my opinion, run time data handlers need to indicate to the flow_output
(or its parent engine) that
> these port bindings added/deleted and these datapaths added/deleted. And
flow computation can be
> done for these tracked/changed entities. And any changes  to the runtime
data  is captured in the
> hmap of 'struct tracked_binding_datapath'.
>
It is ok if you have evaluated all inputs regarding how they are used in
full compute and ensured the way you tracked and handled the changes
guarenteed exactly same result. For maintenanbility, I'd suggest at least
document clearly how is it linked to the original full recompute for each
member of the input data.

> Thanks
> Numan
>
>>
>> > +}
>> > +
>> >  struct ovn_controller_exit_args {
>> >      bool *exiting;
>> >      bool *restart;
>> > @@ -2014,7 +2073,8 @@ main(int argc, char *argv[])
>> >                       flow_output_addr_sets_handler);
>> >      engine_add_input(&en_flow_output, &en_port_groups,
>> >                       flow_output_port_groups_handler);
>> > -    engine_add_input(&en_flow_output, &en_runtime_data, NULL);
>> > +    engine_add_input(&en_flow_output, &en_runtime_data,
>> > +                     flow_output_runtime_data_handler);
>> >      engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
>> >      engine_add_input(&en_flow_output, &en_physical_flow_changes,
>> >                       flow_output_physical_flow_changes_handler);
>> > diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
>> > index 5cc1960b6..6e064e64f 100644
>> > --- a/tests/ovn-performance.at
>> > +++ b/tests/ovn-performance.at
>> > @@ -274,7 +274,7 @@ for i in 1 2; do
>> >      )
>> >
>> >      # Add router port to $ls
>> > -    OVN_CONTROLLER_EXPECT_HIT(
>> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
>> >          [hv1 hv2], [lflow_run],
>> >          [ovn-nbctl --wait=hv lrp-add lr1 $lrp 02:00:00:00:0$i:01
>> 10.0.$i.1/24]
>> >      )
>> > @@ -282,15 +282,15 @@ for i in 1 2; do
>> >          [hv1 hv2], [lflow_run],
>> >          [ovn-nbctl --wait=hv lsp-add $ls $lsp]
>> >      )
>> > -    OVN_CONTROLLER_EXPECT_HIT(
>> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
>> >          [hv1 hv2], [lflow_run],
>> >          [ovn-nbctl --wait=hv lsp-set-type $lsp router]
>> >      )
>> > -    OVN_CONTROLLER_EXPECT_HIT(
>> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
>> >          [hv1 hv2], [lflow_run],
>> >          [ovn-nbctl --wait=hv lsp-set-options $lsp router-port=$lrp]
>> >      )
>> > -    OVN_CONTROLLER_EXPECT_HIT(
>> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
>> >          [hv1 hv2], [lflow_run],
>> >          [ovn-nbctl --wait=hv lsp-set-addresses $lsp router]
>> >      )
>> > @@ -404,8 +404,8 @@ for i in 1 2; do
>> >      lp=lp$i
>> >
>> >      # Delete port $lp
>> > -    OVN_CONTROLLER_EXPECT_HIT_COND(
>> > -        [hv$i hv$j], [lflow_run], [>0 =0],
>> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
>> > +        [hv$i hv$j], [lflow_run],
>> >          [ovn-nbctl --wait=hv lsp-del $lp]
>> >      )
>> >  done
>> > @@ -416,15 +416,15 @@ OVN_CONTROLLER_EXPECT_NO_HIT(
>> >      [ovn-nbctl --wait=hv destroy Port_Group pg1]
>> >  )
>> >
>> > -for i in 1 2; do
>> > -    ls=ls$i
>> > +OVN_CONTROLLER_EXPECT_HIT(
>> > +    [hv1 hv2], [lflow_run],
>> > +    [ovn-nbctl --wait=hv ls-del ls1]
>> > +)
>> >
>> > -    # Delete switch $ls
>> > -    OVN_CONTROLLER_EXPECT_HIT(
>> > -        [hv1 hv2], [lflow_run],
>> > -        [ovn-nbctl --wait=hv ls-del $ls]
>> > -    )
>> > -done
>> > +OVN_CONTROLLER_EXPECT_NO_HIT(
>> > +    [hv1 hv2], [lflow_run],
>> > +    [ovn-nbctl --wait=hv ls-del ls2]
>> > +)
>> >
>> >  # Delete router lr1
>> >  OVN_CONTROLLER_EXPECT_NO_HIT(
>> > --
>> > 2.26.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
>>
Numan Siddique May 26, 2020, 12:59 p.m. UTC | #4
On Sun, May 24, 2020 at 11:12 AM Han Zhou <hzhou@ovn.org> wrote:

> On Sat, May 23, 2020 at 3:26 AM Numan Siddique <numans@ovn.org> wrote:
> >
> >
> >
> > On Thu, May 21, 2020 at 12:28 PM Han Zhou <hzhou@ovn.org> wrote:
> >>
> >> Please see my comments below.
> >>
> >> On Wed, May 20, 2020 at 12:41 PM <numans@ovn.org> wrote:
> >> >
> >> > From: Numan Siddique <numans@ovn.org>
> >> >
> >> > In order to handle runtime data changes incrementally, the flow outut
> >> > runtime data handle should know the changed runtime data.
> >> > Runtime data now tracks the changed data for any OVS interface
> >> > and SB port binding changes. The tracked data contains a hmap
> >> > of tracked datapaths (which changed during runtime data processing.
> >> >
> >> > The flow outout runtime_data handler in this patch doesn't do much
> >> > with the tracked data. It returns false if there is tracked data
> available
> >> > so that flow_output run is called. If no tracked data is available
> >> > then there is no need for flow computation and the handler returns
> true.
> >> >
> >> > Next patch in the series processes the tracked data incrementally.
> >> >
> >> > Acked-by: Mark Michelson <mmichels@redhat.com>
> >> > Co-Authored-by: Venkata Anil <anilvenkata@redhat.com>
> >> > Signed-off-by: Venkata Anil <anilvenkata@redhat.com>
> >> > Signed-off-by: Numan Siddique <numans@ovn.org>
> >> > ---
> >> >  controller/binding.c        | 159
> ++++++++++++++++++++++++++++++++----
> >> >  controller/binding.h        |  21 +++++
> >> >  controller/ovn-controller.c |  62 +++++++++++++-
> >> >  tests/ovn-performance.at    |  28 +++----
> >> >  4 files changed, 240 insertions(+), 30 deletions(-)
> >> >
> >> > diff --git a/controller/binding.c b/controller/binding.c
> >> > index f00c975ce..9b3d46b23 100644
> >> > --- a/controller/binding.c
> >> > +++ b/controller/binding.c
> >> > @@ -69,13 +69,20 @@ binding_register_ovs_idl(struct ovsdb_idl
> *ovs_idl)
> >> >      ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
> >> >  }
> >> >
> >> > +static struct tracked_binding_datapath
> *tracked_binding_datapath_create(
> >> > +    const struct sbrec_datapath_binding *,
> >> > +    bool is_new, struct hmap *tracked_dps);
> >> > +static struct tracked_binding_datapath
> *tracked_binding_datapath_find(
> >> > +    struct hmap *, const struct sbrec_datapath_binding *);
> >> > +
> >> >  static void
> >> >  add_local_datapath__(struct ovsdb_idl_index
> >> *sbrec_datapath_binding_by_key,
> >> >                       struct ovsdb_idl_index
> >> *sbrec_port_binding_by_datapath,
> >> >                       struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> >> >                       const struct sbrec_datapath_binding *datapath,
> >> >                       bool has_local_l3gateway, int depth,
> >> > -                     struct hmap *local_datapaths)
> >> > +                     struct hmap *local_datapaths,
> >> > +                     struct hmap *updated_dp_bindings)
> >> >  {
> >> >      uint32_t dp_key = datapath->tunnel_key;
> >> >      struct local_datapath *ld = get_local_datapath(local_datapaths,
> >> dp_key);
> >> > @@ -92,6 +99,11 @@ add_local_datapath__(struct ovsdb_idl_index
> >> *sbrec_datapath_binding_by_key,
> >> >      ld->localnet_port = NULL;
> >> >      ld->has_local_l3gateway = has_local_l3gateway;
> >> >
> >> > +    if (updated_dp_bindings &&
> >> > +        !tracked_binding_datapath_find(updated_dp_bindings,
> datapath)) {
> >> > +        tracked_binding_datapath_create(datapath, true,
> >> updated_dp_bindings);
> >> > +    }
> >> > +
> >> >      if (depth >= 100) {
> >> >          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1,
> 1);
> >> >          VLOG_WARN_RL(&rl, "datapaths nested too deep");
> >> > @@ -124,7 +136,8 @@ add_local_datapath__(struct ovsdb_idl_index
> >> *sbrec_datapath_binding_by_key,
> >> >
> >> sbrec_port_binding_by_datapath,
> >> >
> sbrec_port_binding_by_name,
> >> >                                               peer->datapath, false,
> >> > -                                             depth + 1,
> local_datapaths);
> >> > +                                             depth + 1,
> local_datapaths,
> >> > +                                             updated_dp_bindings);
> >> >                      }
> >> >                      ld->n_peer_ports++;
> >> >                      if (ld->n_peer_ports >
> ld->n_allocated_peer_ports) {
> >> > @@ -147,12 +160,14 @@ add_local_datapath(struct ovsdb_idl_index
> >> *sbrec_datapath_binding_by_key,
> >> >                     struct ovsdb_idl_index
> >> *sbrec_port_binding_by_datapath,
> >> >                     struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> >> >                     const struct sbrec_datapath_binding *datapath,
> >> > -                   bool has_local_l3gateway, struct hmap
> >> *local_datapaths)
> >> > +                   bool has_local_l3gateway, struct hmap
> >> *local_datapaths,
> >> > +                   struct hmap *updated_dp_bindings)
> >> >  {
> >> >      add_local_datapath__(sbrec_datapath_binding_by_key,
> >> >                           sbrec_port_binding_by_datapath,
> >> >                           sbrec_port_binding_by_name,
> >> > -                         datapath, has_local_l3gateway, 0,
> >> local_datapaths);
> >> > +                         datapath, has_local_l3gateway, 0,
> >> local_datapaths,
> >> > +                         updated_dp_bindings);
> >> >  }
> >> >
> >> >  static void
> >> > @@ -623,6 +638,71 @@ is_lport_container(const struct
> sbrec_port_binding
> >> *pb)
> >> >      return !pb->type[0] && pb->parent_port && pb->parent_port[0];
> >> >  }
> >> >
> >> > +static struct tracked_binding_datapath *
> >> > +tracked_binding_datapath_create(const struct sbrec_datapath_binding
> *dp,
> >> > +                                bool is_new,
> >> > +                                struct hmap *tracked_datapaths)
> >> > +{
> >> > +    struct tracked_binding_datapath *t_dp = xzalloc(sizeof *t_dp);
> >> > +    t_dp->dp = dp;
> >> > +    t_dp->is_new = is_new;
> >> > +    ovs_list_init(&t_dp->lports_head);
> >> > +    hmap_insert(tracked_datapaths, &t_dp->node,
> >> uuid_hash(&dp->header_.uuid));
> >> > +    return t_dp;
> >> > +}
> >> > +
> >> > +static struct tracked_binding_datapath *
> >> > +tracked_binding_datapath_find(struct hmap *tracked_datapaths,
> >> > +                              const struct sbrec_datapath_binding
> *dp)
> >> > +{
> >> > +    struct tracked_binding_datapath *t_dp;
> >> > +    size_t hash = uuid_hash(&dp->header_.uuid);
> >> > +    HMAP_FOR_EACH_WITH_HASH (t_dp, node, hash, tracked_datapaths) {
> >> > +        if (uuid_equals(&t_dp->dp->header_.uuid, &dp->header_.uuid))
> {
> >> > +            return t_dp;
> >> > +        }
> >> > +    }
> >> > +
> >> > +    return NULL;
> >> > +}
> >> > +
> >> > +static void
> >> > +tracked_binding_datapath_lport_add(const struct sbrec_port_binding
> *pb,
> >> > +                                   bool deleted,
> >> > +                                   struct hmap *tracked_datapaths)
> >> > +{
> >> > +    if (!tracked_datapaths) {
> >> > +        return;
> >> > +    }
> >> > +
> >> > +    struct tracked_binding_datapath *tracked_dp =
> >> > +        tracked_binding_datapath_find(tracked_datapaths,
> pb->datapath);
> >> > +    if (!tracked_dp) {
> >> > +        tracked_dp = tracked_binding_datapath_create(pb->datapath,
> false,
> >> > +
> tracked_datapaths);
> >> > +    }
> >> > +    struct tracked_binding_lport *lport = xmalloc(sizeof *lport);
> >> > +    lport->pb = pb;
> >> > +    lport->deleted = deleted;
> >> > +    ovs_list_push_back(&tracked_dp->lports_head, &lport->list_node);
> >> > +}
> >> > +
> >> > +void
> >> > +binding_tracked_dp_destroy(struct hmap *tracked_datapaths)
> >> > +{
> >> > +    struct tracked_binding_datapath *t_dp;
> >> > +    HMAP_FOR_EACH_POP (t_dp, node, tracked_datapaths) {
> >> > +    struct tracked_binding_lport *lport, *next;
> >> > +        LIST_FOR_EACH_SAFE (lport, next, list_node,
> &t_dp->lports_head) {
> >> > +            ovs_list_remove(&lport->list_node);
> >> > +            free(lport);
> >> > +        }
> >> > +        free(t_dp);
> >> > +    }
> >> > +
> >> > +    hmap_destroy(tracked_datapaths);
> >> > +}
> >> > +
> >> >  /* Corresponds to each Port_Binding.type. */
> >> >  enum en_lport_type {
> >> >      LP_UNKNOWN,
> >> > @@ -770,7 +850,8 @@ can_bind_on_this_chassis(const struct
> sbrec_chassis
> >> *chassis_rec,
> >> >  static bool
> >> >  release_local_binding_children(const struct sbrec_chassis
> *chassis_rec,
> >> >                                 struct local_binding *lbinding,
> >> > -                               bool sb_readonly)
> >> > +                               bool sb_readonly,
> >> > +                               struct hmap *tracked_dp_bindings)
> >> >  {
> >> >      struct shash_node *node;
> >> >      SHASH_FOR_EACH (node, &lbinding->children) {
> >> > @@ -780,6 +861,10 @@ release_local_binding_children(const struct
> >> sbrec_chassis *chassis_rec,
> >> >                  return false;
> >> >              }
> >> >          }
> >> > +        if (tracked_dp_bindings) {
> >> > +            tracked_binding_datapath_lport_add(l->pb, true,
> >> > +                                               tracked_dp_bindings);
> >> > +        }
> >> >      }
> >> >
> >> >      return true;
> >> > @@ -787,10 +872,11 @@ release_local_binding_children(const struct
> >> sbrec_chassis *chassis_rec,
> >> >
> >> >  static bool
> >> >  release_local_binding(const struct sbrec_chassis *chassis_rec,
> >> > -                      struct local_binding *lbinding, bool
> sb_readonly)
> >> > +                      struct local_binding *lbinding, bool
> sb_readonly,
> >> > +                      struct hmap *tracked_dp_bindings)
> >> >  {
> >> >      if (!release_local_binding_children(chassis_rec, lbinding,
> >> > -                                        sb_readonly)) {
> >> > +                                        sb_readonly,
> >> tracked_dp_bindings)) {
> >> >          return false;
> >> >      }
> >> >
> >> > @@ -798,6 +884,10 @@ release_local_binding(const struct sbrec_chassis
> >> *chassis_rec,
> >> >          return release_lport(lbinding->pb, sb_readonly);
> >> >      }
> >> >
> >> > +    if (tracked_dp_bindings) {
> >> > +        tracked_binding_datapath_lport_add(lbinding->pb, true,
> >> > +                                           tracked_dp_bindings);
> >> > +    }
> >> >      return true;
> >> >  }
> >> >
> >> > @@ -821,7 +911,8 @@ consider_vif_lport_(const struct
> sbrec_port_binding
> >> *pb,
> >> >
>  add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
> >> >                              b_ctx_in->sbrec_port_binding_by_datapath,
> >> >                              b_ctx_in->sbrec_port_binding_by_name,
> >> > -                            pb->datapath, false,
> >> b_ctx_out->local_datapaths);
> >> > +                            pb->datapath, false,
> >> b_ctx_out->local_datapaths,
> >> > +                            b_ctx_out->tracked_dp_bindings);
> >> >              update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
> >> >              if (lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn)
> {
> >> >                  get_qos_params(pb, qos_map);
> >> > @@ -1003,7 +1094,8 @@ consider_nonvif_lport_(const struct
> >> sbrec_port_binding *pb,
> >> >                             b_ctx_in->sbrec_port_binding_by_datapath,
> >> >                             b_ctx_in->sbrec_port_binding_by_name,
> >> >                             pb->datapath, has_local_l3gateway,
> >> > -                           b_ctx_out->local_datapaths);
> >> > +                           b_ctx_out->local_datapaths,
> >> > +                           b_ctx_out->tracked_dp_bindings);
> >> >
> >> >          update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
> >> >          return claim_lport(pb, b_ctx_in->chassis_rec, NULL,
> >> > @@ -1080,7 +1172,8 @@ consider_ha_lport(const struct
> sbrec_port_binding
> >> *pb,
> >> >                             b_ctx_in->sbrec_port_binding_by_datapath,
> >> >                             b_ctx_in->sbrec_port_binding_by_name,
> >> >                             pb->datapath, false,
> >> > -                           b_ctx_out->local_datapaths);
> >> > +                           b_ctx_out->local_datapaths,
> >> > +                           b_ctx_out->tracked_dp_bindings);
> >> >      }
> >> >
> >> >      return consider_nonvif_lport_(pb, our_chassis, false, b_ctx_in,
> >> b_ctx_out);
> >> > @@ -1367,7 +1460,8 @@ add_local_datapath_peer_port(const struct
> >> sbrec_port_binding *pb,
> >> >
>  b_ctx_in->sbrec_port_binding_by_datapath,
> >> >                               b_ctx_in->sbrec_port_binding_by_name,
> >> >                               peer->datapath, false,
> >> > -                             1, b_ctx_out->local_datapaths);
> >> > +                             1, b_ctx_out->local_datapaths,
> >> > +                             b_ctx_out->tracked_dp_bindings);
> >> >          return;
> >> >      }
> >> >
> >> > @@ -1444,6 +1538,18 @@ remove_pb_from_local_datapath(const struct
> >> sbrec_port_binding *pb,
> >> >      }
> >> >  }
> >> >
> >> > +static void
> >> > +update_lport_tracking(const struct sbrec_port_binding *pb,
> >> > +                      bool old_claim, bool new_claim,
> >> > +                      struct hmap *tracked_dp_bindings)
> >> > +{
> >> > +    if (!tracked_dp_bindings || !pb || (old_claim == new_claim)) {
> >> > +        return;
> >> > +    }
> >> > +
> >> > +    tracked_binding_datapath_lport_add(pb, old_claim,
> >> tracked_dp_bindings);
> >> > +}
> >> > +
> >> >  static bool
> >> >  handle_iface_add(const struct ovsrec_interface *iface_rec,
> >> >                   const char *iface_id,
> >> > @@ -1472,6 +1578,7 @@ handle_iface_add(const struct ovsrec_interface
> >> *iface_rec,
> >> >          }
> >> >      }
> >> >
> >> > +    bool claimed = is_lbinding_this_chassis(lbinding,
> >> b_ctx_in->chassis_rec);
> >> >      if (lbinding->pb) {
> >> >          if (!consider_vif_lport(lbinding->pb, b_ctx_in, b_ctx_out,
> >> >                                  lbinding, qos_map)) {
> >> > @@ -1479,6 +1586,10 @@ handle_iface_add(const struct ovsrec_interface
> >> *iface_rec,
> >> >          }
> >> >      }
> >> >
> >> > +    bool now_claimed =
> >> > +        is_lbinding_this_chassis(lbinding, b_ctx_in->chassis_rec);
> >> > +    update_lport_tracking(lbinding->pb, claimed, now_claimed,
> >> > +                          b_ctx_out->tracked_dp_bindings);
> >> >      /* Update the child local_binding's iface (if any children) and
> try
> >> to
> >> >       *  claim the container lbindings. */
> >> >      struct shash_node *node;
> >> > @@ -1486,10 +1597,15 @@ handle_iface_add(const struct ovsrec_interface
> >> *iface_rec,
> >> >          struct local_binding *child = node->data;
> >> >          child->iface = iface_rec;
> >> >          if (child->type == BT_CONTAINER) {
> >> > +            claimed = is_lbinding_this_chassis(child,
> >> b_ctx_in->chassis_rec);
> >> >              if (!consider_container_lport(child->pb, b_ctx_in,
> b_ctx_out,
> >> >                                            qos_map)) {
> >> >                  return false;
> >> >              }
> >> > +            now_claimed =
> >> > +                is_lbinding_this_chassis(child,
> b_ctx_in->chassis_rec);
> >> > +            update_lport_tracking(child->pb, claimed, now_claimed,
> >> > +                                  b_ctx_out->tracked_dp_bindings);
> >> >          }
> >> >      }
> >> >
> >> > @@ -1508,7 +1624,8 @@ handle_iface_delete(const char *lport_name,
> const
> >> char *iface_name,
> >> >          lbinding->pb->chassis == b_ctx_in->chassis_rec) {
> >> >
> >> >          if (!release_local_binding(b_ctx_in->chassis_rec, lbinding,
> >> > -                                   !b_ctx_in->ovnsb_idl_txn)) {
> >> > +                                   !b_ctx_in->ovnsb_idl_txn,
> >> > +                                   b_ctx_out->tracked_dp_bindings)) {
> >> >              return false;
> >> >          }
> >> >
> >> > @@ -1556,6 +1673,8 @@ binding_handle_ovs_interface_changes(struct
> >> binding_ctx_in *b_ctx_in,
> >> >      bool handled = true;
> >> >      *changed = false;
> >> >
> >> > +    *b_ctx_out->local_lports_changed = false;
> >> > +
> >> >      /* Run the tracked interfaces loop twice. One to handle deleted
> >> >       * changes. And another to handle add/update changes.
> >> >       * This will ensure correctness.
> >> > @@ -1698,9 +1817,10 @@ handle_deleted_vif_lport(const struct
> >> sbrec_port_binding *pb,
> >> >           * clear the 'chassis' column of 'pb'. But we need to do
> >> >           * for the local_binding's children. */
> >> >          if (lbinding->type == BT_VIF &&
> >> > -
> !release_local_binding_children(b_ctx_in->chassis_rec,
> >> > -                                                lbinding,
> >> > -
> >>  !b_ctx_in->ovnsb_idl_txn)) {
> >> > +                !release_local_binding_children(
> >> > +                    b_ctx_in->chassis_rec, lbinding,
> >> > +                    !b_ctx_in->ovnsb_idl_txn,
> >> > +                    b_ctx_out->tracked_dp_bindings)) {
> >> >              return false;
> >> >          }
> >> >
> >> > @@ -1746,6 +1866,9 @@ handle_updated_vif_lport(const struct
> >> sbrec_port_binding *pb,
> >> >          return true;
> >> >      }
> >> >
> >> > +    update_lport_tracking(pb, claimed, now_claimed,
> >> > +                          b_ctx_out->tracked_dp_bindings);
> >> > +
> >> >      struct local_binding *lbinding =
> >> >          local_binding_find(b_ctx_out->local_bindings,
> pb->logical_port);
> >> >
> >> > @@ -1755,11 +1878,17 @@ handle_updated_vif_lport(const struct
> >> sbrec_port_binding *pb,
> >> >      SHASH_FOR_EACH (node, &lbinding->children) {
> >> >          struct local_binding *child = node->data;
> >> >          if (child->type == BT_CONTAINER) {
> >> > +            claimed = is_lbinding_this_chassis(child,
> >> b_ctx_in->chassis_rec);
> >> >              handled = consider_container_lport(child->pb, b_ctx_in,
> >> b_ctx_out,
> >> >                                                 qos_map);
> >> >              if (!handled) {
> >> >                  return false;
> >> >              }
> >> > +
> >> > +            now_claimed = is_lbinding_this_chassis(child,
> >> > +
> >> b_ctx_in->chassis_rec);
> >> > +            update_lport_tracking(child->pb, claimed, now_claimed,
> >> > +                                  b_ctx_out->tracked_dp_bindings);
> >> >          }
> >> >      }
> >> >
> >> > diff --git a/controller/binding.h b/controller/binding.h
> >> > index 21118ecd4..fc2a673e5 100644
> >> > --- a/controller/binding.h
> >> > +++ b/controller/binding.h
> >> > @@ -19,6 +19,9 @@
> >> >
> >> >  #include <stdbool.h>
> >> >  #include "openvswitch/shash.h"
> >> > +#include "openvswitch/hmap.h"
> >> > +#include "openvswitch/uuid.h"
> >> > +#include "openvswitch/list.h"
> >> >
> >> >  struct hmap;
> >> >  struct ovsdb_idl;
> >> > @@ -58,6 +61,8 @@ struct binding_ctx_out {
> >> >      struct sset *local_lport_ids;
> >> >      struct sset *egress_ifaces;
> >> >      struct smap *local_iface_ids;
> >> > +    struct hmap *tracked_dp_bindings;
> >> > +    bool *local_lports_changed;
> >> >  };
> >> >
> >> >  enum local_binding_type {
> >> > @@ -82,6 +87,21 @@ local_binding_find(struct shash *local_bindings,
> const
> >> char *name)
> >> >      return shash_find_data(local_bindings, name);
> >> >  }
> >> >
> >> > +/* Represents a tracked binding logical port. */
> >> > +struct tracked_binding_lport {
> >> > +    const struct sbrec_port_binding *pb;
> >> > +    struct ovs_list list_node;
> >> > +    bool deleted;
> >> > +};
> >> > +
> >> > +/* Represent a tracked binding datapath. */
> >> > +struct tracked_binding_datapath {
> >> > +    struct hmap_node node;
> >> > +    const struct sbrec_datapath_binding *dp;
> >> > +    bool is_new;
> >> > +    struct ovs_list lports_head; /* List of struct
> >> tracked_binding_lport. */
> >> > +};
> >> > +
> >> >  void binding_register_ovs_idl(struct ovsdb_idl *);
> >> >  void binding_run(struct binding_ctx_in *, struct binding_ctx_out *);
> >> >  bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> >> > @@ -96,4 +116,5 @@ bool binding_handle_ovs_interface_changes(struct
> >> binding_ctx_in *,
> >> >  bool binding_handle_port_binding_changes(struct binding_ctx_in *,
> >> >                                           struct binding_ctx_out *,
> >> >                                           bool *changed);
> >> > +void binding_tracked_dp_destroy(struct hmap *tracked_datapaths);
> >> >  #endif /* controller/binding.h */
> >> > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> >> > index 8f95fff1f..dc790f0f7 100644
> >> > --- a/controller/ovn-controller.c
> >> > +++ b/controller/ovn-controller.c
> >> > @@ -978,6 +978,23 @@ struct ed_type_runtime_data {
> >> >      struct smap local_iface_ids;
> >> >  };
> >> >
> >> > +struct ed_type_runtime_tracked_data {
> >> > +    struct hmap tracked_dp_bindings;
> >> > +    bool local_lports_changed;
> >> > +    bool tracked;
> >> > +};
> >> > +
> >> > +static void
> >> > +en_runtime_clear_tracked_data(void *tracked_data)
> >> > +{
> >> > +    struct ed_type_runtime_tracked_data *data = tracked_data;
> >> > +
> >> > +    binding_tracked_dp_destroy(&data->tracked_dp_bindings);
> >> > +    hmap_init(&data->tracked_dp_bindings);
> >> > +    data->local_lports_changed = false;
> >> > +    data->tracked = false;
> >> > +}
> >> > +
> >> >  static void *
> >> >  en_runtime_data_init(struct engine_node *node OVS_UNUSED,
> >> >                       struct engine_arg *arg OVS_UNUSED)
> >> > @@ -991,6 +1008,13 @@ en_runtime_data_init(struct engine_node *node
> >> OVS_UNUSED,
> >> >      sset_init(&data->egress_ifaces);
> >> >      smap_init(&data->local_iface_ids);
> >> >      local_bindings_init(&data->local_bindings);
> >> > +
> >> > +    struct ed_type_runtime_tracked_data *tracked_data =
> >> > +        xzalloc(sizeof *tracked_data);
> >> > +    hmap_init(&tracked_data->tracked_dp_bindings);
> >> > +    node->tracked_data = tracked_data;
> >> > +    node->clear_tracked_data = en_runtime_clear_tracked_data;
> >> > +
> >> >      return data;
> >> >  }
> >> >
> >> > @@ -1093,6 +1117,8 @@ init_binding_ctx(struct engine_node *node,
> >> >      b_ctx_out->egress_ifaces = &rt_data->egress_ifaces;
> >> >      b_ctx_out->local_bindings = &rt_data->local_bindings;
> >> >      b_ctx_out->local_iface_ids = &rt_data->local_iface_ids;
> >> > +    b_ctx_out->tracked_dp_bindings = NULL;
> >> > +    b_ctx_out->local_lports_changed = NULL;
> >> >  }
> >> >
> >> >  static void
> >> > @@ -1104,6 +1130,8 @@ en_runtime_data_run(struct engine_node *node,
> void
> >> *data)
> >> >      struct sset *local_lport_ids = &rt_data->local_lport_ids;
> >> >      struct sset *active_tunnels = &rt_data->active_tunnels;
> >> >
> >> > +    en_runtime_clear_tracked_data(node->tracked_data);
> >> > +
> >> >      static bool first_run = true;
> >> >      if (first_run) {
> >> >          /* don't cleanup since there is no data yet */
> >> > @@ -1156,9 +1184,13 @@ static bool
> >> >  runtime_data_ovs_interface_handler(struct engine_node *node, void
> *data)
> >> >  {
> >> >      struct ed_type_runtime_data *rt_data = data;
> >> > +    struct ed_type_runtime_tracked_data *tracked_data =
> >> node->tracked_data;
> >> >      struct binding_ctx_in b_ctx_in;
> >> >      struct binding_ctx_out b_ctx_out;
> >> >      init_binding_ctx(node, rt_data, &b_ctx_in, &b_ctx_out);
> >> > +    tracked_data->tracked = true;
> >> > +    b_ctx_out.tracked_dp_bindings =
> &tracked_data->tracked_dp_bindings;
> >> > +    b_ctx_out.local_lports_changed =
> &tracked_data->local_lports_changed;
> >> >
> >> >      bool changed = false;
> >> >      if (!binding_handle_ovs_interface_changes(&b_ctx_in, &b_ctx_out,
> >> > @@ -1191,6 +1223,10 @@ runtime_data_sb_port_binding_handler(struct
> >> engine_node *node, void *data)
> >> >          return false;
> >> >      }
> >> >
> >> > +    struct ed_type_runtime_tracked_data *tracked_data =
> >> node->tracked_data;
> >> > +    tracked_data->tracked = true;
> >> > +    b_ctx_out.tracked_dp_bindings =
> &tracked_data->tracked_dp_bindings;
> >> > +
> >> >      bool changed = false;
> >> >      if (!binding_handle_port_binding_changes(&b_ctx_in, &b_ctx_out,
> >> >                                               &changed)) {
> >> > @@ -1862,6 +1898,29 @@ physical_flow_changes_ovs_iface_handler(struct
> >> engine_node *node OVS_UNUSED,
> >> >      return true;
> >> >  }
> >> >
> >> > +static bool
> >> > +flow_output_runtime_data_handler(struct engine_node *node,
> >> > +                                 void *data OVS_UNUSED)
> >> > +{
> >> > +    struct ed_type_runtime_tracked_data *tracked_data =
> >> > +        engine_get_input_tracked_data("runtime_data", node);
> >> > +
> >> > +    if (!tracked_data || !tracked_data->tracked) {
> >> > +        return false;
> >> > +    }
> >> > +
> >> > +    if (!hmap_is_empty(&tracked_data->tracked_dp_bindings)) {
> >> > +        /* We are not yet handling the tracked datapath binding
> >> > +         * changes. Return false to trigger full recompute. */
> >> > +        return false;
> >> > +    }
> >> > +
> >> > +    if (tracked_data->local_lports_changed) {
> >> > +        engine_set_node_state(node, EN_UPDATED);
> >> > +    }
> >> > +    return true;
> >>
> >> Once the change handler is added and when it returns true, it means it
> has
> >> handled the changes incrementally with full confidence. However, I
> didn't
> >> see handling of the changes in runtime_data for local_bindings,
> >> local_lport_ids, active_tunnels, egress_ifaces, local_iface_ids, etc.
> (they
> >> are not involved in the tracked data, if I read the code correctly).
> Even
> >> for local_lports, why do we return true even if there are changes
> tracked
> >> for it?
> >
> >
> > I really don't understand the need for addding all the information in the
> > tracked data. And I'm not sure how would flow_output handlers will use
> all of them.
> >
> > The tracked data will be used by the flow_output_runtime_data_handler().
> >
> > If we take each of the  runtime data:
> >
> >  - active_tunnels: This is not updated during the I-P of OVS interface
> and Port binding changes.
> >                     active_tunnels are built in en_runtime_data_run()
> when it calls bfd_calculate_active_tunnels()
> >                     and the tunnel OVS interfaces are considered.
> >                     If any non VIF OVS interrace changes presently we
> fall back to full recompute of runtime data.
> >                     So I don't think there is a need to have this as part
> of tracked data.
> >
> Thanks for the explain. This reason is important but it is implicit. From
> the dependency graph point of view, one can't easily tell why is it
> ignored, so I think it deserves a XXX comment, for code reader to
> understand and track for future improvement when we do incremental
> processing for non-VIF interface changes.
>
> >  - egress_ifaces:  This is used by only binding.c and is not used during
> flow computation. So I don't see why it
> >                    should be part of tracked data.
> >
> >  - local_iface_ids: This is used by only binding.c and is not used during
> flow computation. So I don't see why it
> >                     should be part of tracked data.
> >
>
> This reason is more obvious. I'd suggest to add a comment in the struct
> definition of the ed_type_runtime_data to note it as "engine private data,
> not depended by other nodes". The I-P engine framework doesn't express this
> kind of encapsulation well, but I think some comments would be helpful.
>
> >  - local_lport_ids:  When an OVS interface is added/updated with
> external_ids:iface-id set, then we add this to
> >                      this sset. It is used in
> >                      1. ovn-controller.c for monitoring any Port bindings
> with this name.
> >                      2. And also in lflows.c to check if we need to skip
> the logical flow
> >
> >    Lets say an OVS interface was added with external_ids:iface-id=foo and
> if
> >    'foo' logical port is not yet present. In this case local_lport_ids is
> updated with 'foo'.
> >    This doesn't result in any port binding changes. Hence we only need to
> do is to call update_sb_monitors
> >    and that's why flow_output_runtime_data_handler() sets the engine node
> to updated and returns true
> >    as there is no need to do any flow calculations. And to indicate this
> 'local_lports_changed' bool
> >    is added in the 'struct ed_type_runtime_tracked_data'.
> >
> > Lets say during the I-P handling of OVS interface change, a port 'foo' is
> bound and it is
> > the first port of its logical switch (say sw0) to bound.
> >
> > With this patch series we add the following tracked data -  tracked dp
> bindings = [ls = sw0, is_new = true, tracked ports = [lport = foo, deleted
> = false]]
> > and it also would have updated local_lport_ids, local_bindings,
> local_datapaths etc.
> >
>
> I see, so it is handled indirectly by handling tracked_binding_lport in
> tracked_data->tracked_dp_bindings->lports_head. I think it should be ok,
> but it deserves a comment to help understanding why we don't need track and
> handle local_lport_ids change directly.
>
> > Lets say we also put - tracked local_lport_ids, tracked local_iface_ids,
> tracked local_binding etc into the tracked data. My question how it will be
> used
> > by the flow output handlers ?
> >
> > Something like ?
> >
> > for each tracked dp bindings
> >    calculate flows for these dp bindings
> > done
> >
> > for each tracked local_lport_ids
> >     calculate flows for these lport ids
> > done
> >
> > for each tracked local_bindings
> >     calculate flows for these local bindings
> > done
> >
> > Can you please tell how the flow output handlers will use this data as
> I'm not clear.
> >
>
> Usually the straightforward way to implement change-handlers (my
> understanding) is to check how the full computing is using each of the
> input data. Consider each input as a DB table (for runtime_data, since it
> has multiple members, it is like multiple sub-tables combined, and ignore
> the private members that are not used as inputs), the full compute is
> considered as a join between the tables, but the "join" is not a simple
> relational DB join but highly customerized functions. Now for I-P change
> handler, to handle each tracked change, ideally it is a probe to other
> related tables, depending on how the "join" was implemented in full
> computing. However, such kind of probe is hard to implement, so the general
> approach is to link each table item to the key of the output table (for
> lflow_output, the key is the uuid of lflow for logical flows, or uuid of PB
> for physical flows) by building an index for efficient lookup, such as the
> lflow_resource_ref index, and handle the changes of any input table by
> finding the key in the output and recompute the items related to that key.
> This is like always probe in the same order of table traverse. With this
> approach it is easier to reason the correctness. It is essentially no
> difference from the DDlog approach, but just all logic are manually
> implemented.
>
> Take local_lport_ids as example, in full computing it is used to see if a
> lflow should be bypassed on current chassis. So, if we tracked the changes
> of this "sub-table", we can do:
>
> for each tracked local_lport_ids:
>     find the lflow uuid related to this local_lport_id // this requires
> building the index, as you already done, which is perfect
>     delete all ovn-flows that is generated by this lflow
>     call consider_lflow() again
> done
>
> This ensures the impacted lflow get bypassed/unbypassed properly because
> the related lflows are reconsidered.
>
> >
> >>
> >> So how could we be confident that those changes can be ignored by
> >> flow_output node? If they can be ignored, why would they be needed in
> >> flow_output_run(), which passes them to flow_run()/physical_run()? Is
> there
> >> something missing?
> >
> >
> > In my opinion, run time data handlers need to indicate to the flow_output
> (or its parent engine) that
> > these port bindings added/deleted and these datapaths added/deleted. And
> flow computation can be
> > done for these tracked/changed entities. And any changes  to the runtime
> data  is captured in the
> > hmap of 'struct tracked_binding_datapath'.
> >
> It is ok if you have evaluated all inputs regarding how they are used in
> full compute and ensured the way you tracked and handled the changes
> guarenteed exactly same result. For maintenanbility, I'd suggest at least
> document clearly how is it linked to the original full recompute for each
> member of the input data.
>

I've added the comments in v8. Request to please take a look.

Thanks
Numan


>
> > Thanks
> > Numan
> >
> >>
> >> > +}
> >> > +
> >> >  struct ovn_controller_exit_args {
> >> >      bool *exiting;
> >> >      bool *restart;
> >> > @@ -2014,7 +2073,8 @@ main(int argc, char *argv[])
> >> >                       flow_output_addr_sets_handler);
> >> >      engine_add_input(&en_flow_output, &en_port_groups,
> >> >                       flow_output_port_groups_handler);
> >> > -    engine_add_input(&en_flow_output, &en_runtime_data, NULL);
> >> > +    engine_add_input(&en_flow_output, &en_runtime_data,
> >> > +                     flow_output_runtime_data_handler);
> >> >      engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
> >> >      engine_add_input(&en_flow_output, &en_physical_flow_changes,
> >> >                       flow_output_physical_flow_changes_handler);
> >> > diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
> >> > index 5cc1960b6..6e064e64f 100644
> >> > --- a/tests/ovn-performance.at
> >> > +++ b/tests/ovn-performance.at
> >> > @@ -274,7 +274,7 @@ for i in 1 2; do
> >> >      )
> >> >
> >> >      # Add router port to $ls
> >> > -    OVN_CONTROLLER_EXPECT_HIT(
> >> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> >> >          [hv1 hv2], [lflow_run],
> >> >          [ovn-nbctl --wait=hv lrp-add lr1 $lrp 02:00:00:00:0$i:01
> >> 10.0.$i.1/24]
> >> >      )
> >> > @@ -282,15 +282,15 @@ for i in 1 2; do
> >> >          [hv1 hv2], [lflow_run],
> >> >          [ovn-nbctl --wait=hv lsp-add $ls $lsp]
> >> >      )
> >> > -    OVN_CONTROLLER_EXPECT_HIT(
> >> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> >> >          [hv1 hv2], [lflow_run],
> >> >          [ovn-nbctl --wait=hv lsp-set-type $lsp router]
> >> >      )
> >> > -    OVN_CONTROLLER_EXPECT_HIT(
> >> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> >> >          [hv1 hv2], [lflow_run],
> >> >          [ovn-nbctl --wait=hv lsp-set-options $lsp router-port=$lrp]
> >> >      )
> >> > -    OVN_CONTROLLER_EXPECT_HIT(
> >> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> >> >          [hv1 hv2], [lflow_run],
> >> >          [ovn-nbctl --wait=hv lsp-set-addresses $lsp router]
> >> >      )
> >> > @@ -404,8 +404,8 @@ for i in 1 2; do
> >> >      lp=lp$i
> >> >
> >> >      # Delete port $lp
> >> > -    OVN_CONTROLLER_EXPECT_HIT_COND(
> >> > -        [hv$i hv$j], [lflow_run], [>0 =0],
> >> > +    OVN_CONTROLLER_EXPECT_NO_HIT(
> >> > +        [hv$i hv$j], [lflow_run],
> >> >          [ovn-nbctl --wait=hv lsp-del $lp]
> >> >      )
> >> >  done
> >> > @@ -416,15 +416,15 @@ OVN_CONTROLLER_EXPECT_NO_HIT(
> >> >      [ovn-nbctl --wait=hv destroy Port_Group pg1]
> >> >  )
> >> >
> >> > -for i in 1 2; do
> >> > -    ls=ls$i
> >> > +OVN_CONTROLLER_EXPECT_HIT(
> >> > +    [hv1 hv2], [lflow_run],
> >> > +    [ovn-nbctl --wait=hv ls-del ls1]
> >> > +)
> >> >
> >> > -    # Delete switch $ls
> >> > -    OVN_CONTROLLER_EXPECT_HIT(
> >> > -        [hv1 hv2], [lflow_run],
> >> > -        [ovn-nbctl --wait=hv ls-del $ls]
> >> > -    )
> >> > -done
> >> > +OVN_CONTROLLER_EXPECT_NO_HIT(
> >> > +    [hv1 hv2], [lflow_run],
> >> > +    [ovn-nbctl --wait=hv ls-del ls2]
> >> > +)
> >> >
> >> >  # Delete router lr1
> >> >  OVN_CONTROLLER_EXPECT_NO_HIT(
> >> > --
> >> > 2.26.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
>
>
diff mbox series

Patch

diff --git a/controller/binding.c b/controller/binding.c
index f00c975ce..9b3d46b23 100644
--- a/controller/binding.c
+++ b/controller/binding.c
@@ -69,13 +69,20 @@  binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
     ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
 }
 
+static struct tracked_binding_datapath *tracked_binding_datapath_create(
+    const struct sbrec_datapath_binding *,
+    bool is_new, struct hmap *tracked_dps);
+static struct tracked_binding_datapath *tracked_binding_datapath_find(
+    struct hmap *, const struct sbrec_datapath_binding *);
+
 static void
 add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
                      struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
                      struct ovsdb_idl_index *sbrec_port_binding_by_name,
                      const struct sbrec_datapath_binding *datapath,
                      bool has_local_l3gateway, int depth,
-                     struct hmap *local_datapaths)
+                     struct hmap *local_datapaths,
+                     struct hmap *updated_dp_bindings)
 {
     uint32_t dp_key = datapath->tunnel_key;
     struct local_datapath *ld = get_local_datapath(local_datapaths, dp_key);
@@ -92,6 +99,11 @@  add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
     ld->localnet_port = NULL;
     ld->has_local_l3gateway = has_local_l3gateway;
 
+    if (updated_dp_bindings &&
+        !tracked_binding_datapath_find(updated_dp_bindings, datapath)) {
+        tracked_binding_datapath_create(datapath, true, updated_dp_bindings);
+    }
+
     if (depth >= 100) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
         VLOG_WARN_RL(&rl, "datapaths nested too deep");
@@ -124,7 +136,8 @@  add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
                                              sbrec_port_binding_by_datapath,
                                              sbrec_port_binding_by_name,
                                              peer->datapath, false,
-                                             depth + 1, local_datapaths);
+                                             depth + 1, local_datapaths,
+                                             updated_dp_bindings);
                     }
                     ld->n_peer_ports++;
                     if (ld->n_peer_ports > ld->n_allocated_peer_ports) {
@@ -147,12 +160,14 @@  add_local_datapath(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
                    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
                    struct ovsdb_idl_index *sbrec_port_binding_by_name,
                    const struct sbrec_datapath_binding *datapath,
-                   bool has_local_l3gateway, struct hmap *local_datapaths)
+                   bool has_local_l3gateway, struct hmap *local_datapaths,
+                   struct hmap *updated_dp_bindings)
 {
     add_local_datapath__(sbrec_datapath_binding_by_key,
                          sbrec_port_binding_by_datapath,
                          sbrec_port_binding_by_name,
-                         datapath, has_local_l3gateway, 0, local_datapaths);
+                         datapath, has_local_l3gateway, 0, local_datapaths,
+                         updated_dp_bindings);
 }
 
 static void
@@ -623,6 +638,71 @@  is_lport_container(const struct sbrec_port_binding *pb)
     return !pb->type[0] && pb->parent_port && pb->parent_port[0];
 }
 
+static struct tracked_binding_datapath *
+tracked_binding_datapath_create(const struct sbrec_datapath_binding *dp,
+                                bool is_new,
+                                struct hmap *tracked_datapaths)
+{
+    struct tracked_binding_datapath *t_dp = xzalloc(sizeof *t_dp);
+    t_dp->dp = dp;
+    t_dp->is_new = is_new;
+    ovs_list_init(&t_dp->lports_head);
+    hmap_insert(tracked_datapaths, &t_dp->node, uuid_hash(&dp->header_.uuid));
+    return t_dp;
+}
+
+static struct tracked_binding_datapath *
+tracked_binding_datapath_find(struct hmap *tracked_datapaths,
+                              const struct sbrec_datapath_binding *dp)
+{
+    struct tracked_binding_datapath *t_dp;
+    size_t hash = uuid_hash(&dp->header_.uuid);
+    HMAP_FOR_EACH_WITH_HASH (t_dp, node, hash, tracked_datapaths) {
+        if (uuid_equals(&t_dp->dp->header_.uuid, &dp->header_.uuid)) {
+            return t_dp;
+        }
+    }
+
+    return NULL;
+}
+
+static void
+tracked_binding_datapath_lport_add(const struct sbrec_port_binding *pb,
+                                   bool deleted,
+                                   struct hmap *tracked_datapaths)
+{
+    if (!tracked_datapaths) {
+        return;
+    }
+
+    struct tracked_binding_datapath *tracked_dp =
+        tracked_binding_datapath_find(tracked_datapaths, pb->datapath);
+    if (!tracked_dp) {
+        tracked_dp = tracked_binding_datapath_create(pb->datapath, false,
+                                                     tracked_datapaths);
+    }
+    struct tracked_binding_lport *lport = xmalloc(sizeof *lport);
+    lport->pb = pb;
+    lport->deleted = deleted;
+    ovs_list_push_back(&tracked_dp->lports_head, &lport->list_node);
+}
+
+void
+binding_tracked_dp_destroy(struct hmap *tracked_datapaths)
+{
+    struct tracked_binding_datapath *t_dp;
+    HMAP_FOR_EACH_POP (t_dp, node, tracked_datapaths) {
+    struct tracked_binding_lport *lport, *next;
+        LIST_FOR_EACH_SAFE (lport, next, list_node, &t_dp->lports_head) {
+            ovs_list_remove(&lport->list_node);
+            free(lport);
+        }
+        free(t_dp);
+    }
+
+    hmap_destroy(tracked_datapaths);
+}
+
 /* Corresponds to each Port_Binding.type. */
 enum en_lport_type {
     LP_UNKNOWN,
@@ -770,7 +850,8 @@  can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec,
 static bool
 release_local_binding_children(const struct sbrec_chassis *chassis_rec,
                                struct local_binding *lbinding,
-                               bool sb_readonly)
+                               bool sb_readonly,
+                               struct hmap *tracked_dp_bindings)
 {
     struct shash_node *node;
     SHASH_FOR_EACH (node, &lbinding->children) {
@@ -780,6 +861,10 @@  release_local_binding_children(const struct sbrec_chassis *chassis_rec,
                 return false;
             }
         }
+        if (tracked_dp_bindings) {
+            tracked_binding_datapath_lport_add(l->pb, true,
+                                               tracked_dp_bindings);
+        }
     }
 
     return true;
@@ -787,10 +872,11 @@  release_local_binding_children(const struct sbrec_chassis *chassis_rec,
 
 static bool
 release_local_binding(const struct sbrec_chassis *chassis_rec,
-                      struct local_binding *lbinding, bool sb_readonly)
+                      struct local_binding *lbinding, bool sb_readonly,
+                      struct hmap *tracked_dp_bindings)
 {
     if (!release_local_binding_children(chassis_rec, lbinding,
-                                        sb_readonly)) {
+                                        sb_readonly, tracked_dp_bindings)) {
         return false;
     }
 
@@ -798,6 +884,10 @@  release_local_binding(const struct sbrec_chassis *chassis_rec,
         return release_lport(lbinding->pb, sb_readonly);
     }
 
+    if (tracked_dp_bindings) {
+        tracked_binding_datapath_lport_add(lbinding->pb, true,
+                                           tracked_dp_bindings);
+    }
     return true;
 }
 
@@ -821,7 +911,8 @@  consider_vif_lport_(const struct sbrec_port_binding *pb,
             add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
                             b_ctx_in->sbrec_port_binding_by_datapath,
                             b_ctx_in->sbrec_port_binding_by_name,
-                            pb->datapath, false, b_ctx_out->local_datapaths);
+                            pb->datapath, false, b_ctx_out->local_datapaths,
+                            b_ctx_out->tracked_dp_bindings);
             update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
             if (lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn) {
                 get_qos_params(pb, qos_map);
@@ -1003,7 +1094,8 @@  consider_nonvif_lport_(const struct sbrec_port_binding *pb,
                            b_ctx_in->sbrec_port_binding_by_datapath,
                            b_ctx_in->sbrec_port_binding_by_name,
                            pb->datapath, has_local_l3gateway,
-                           b_ctx_out->local_datapaths);
+                           b_ctx_out->local_datapaths,
+                           b_ctx_out->tracked_dp_bindings);
 
         update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
         return claim_lport(pb, b_ctx_in->chassis_rec, NULL,
@@ -1080,7 +1172,8 @@  consider_ha_lport(const struct sbrec_port_binding *pb,
                            b_ctx_in->sbrec_port_binding_by_datapath,
                            b_ctx_in->sbrec_port_binding_by_name,
                            pb->datapath, false,
-                           b_ctx_out->local_datapaths);
+                           b_ctx_out->local_datapaths,
+                           b_ctx_out->tracked_dp_bindings);
     }
 
     return consider_nonvif_lport_(pb, our_chassis, false, b_ctx_in, b_ctx_out);
@@ -1367,7 +1460,8 @@  add_local_datapath_peer_port(const struct sbrec_port_binding *pb,
                              b_ctx_in->sbrec_port_binding_by_datapath,
                              b_ctx_in->sbrec_port_binding_by_name,
                              peer->datapath, false,
-                             1, b_ctx_out->local_datapaths);
+                             1, b_ctx_out->local_datapaths,
+                             b_ctx_out->tracked_dp_bindings);
         return;
     }
 
@@ -1444,6 +1538,18 @@  remove_pb_from_local_datapath(const struct sbrec_port_binding *pb,
     }
 }
 
+static void
+update_lport_tracking(const struct sbrec_port_binding *pb,
+                      bool old_claim, bool new_claim,
+                      struct hmap *tracked_dp_bindings)
+{
+    if (!tracked_dp_bindings || !pb || (old_claim == new_claim)) {
+        return;
+    }
+
+    tracked_binding_datapath_lport_add(pb, old_claim, tracked_dp_bindings);
+}
+
 static bool
 handle_iface_add(const struct ovsrec_interface *iface_rec,
                  const char *iface_id,
@@ -1472,6 +1578,7 @@  handle_iface_add(const struct ovsrec_interface *iface_rec,
         }
     }
 
+    bool claimed = is_lbinding_this_chassis(lbinding, b_ctx_in->chassis_rec);
     if (lbinding->pb) {
         if (!consider_vif_lport(lbinding->pb, b_ctx_in, b_ctx_out,
                                 lbinding, qos_map)) {
@@ -1479,6 +1586,10 @@  handle_iface_add(const struct ovsrec_interface *iface_rec,
         }
     }
 
+    bool now_claimed =
+        is_lbinding_this_chassis(lbinding, b_ctx_in->chassis_rec);
+    update_lport_tracking(lbinding->pb, claimed, now_claimed,
+                          b_ctx_out->tracked_dp_bindings);
     /* Update the child local_binding's iface (if any children) and try to
      *  claim the container lbindings. */
     struct shash_node *node;
@@ -1486,10 +1597,15 @@  handle_iface_add(const struct ovsrec_interface *iface_rec,
         struct local_binding *child = node->data;
         child->iface = iface_rec;
         if (child->type == BT_CONTAINER) {
+            claimed = is_lbinding_this_chassis(child, b_ctx_in->chassis_rec);
             if (!consider_container_lport(child->pb, b_ctx_in, b_ctx_out,
                                           qos_map)) {
                 return false;
             }
+            now_claimed =
+                is_lbinding_this_chassis(child, b_ctx_in->chassis_rec);
+            update_lport_tracking(child->pb, claimed, now_claimed,
+                                  b_ctx_out->tracked_dp_bindings);
         }
     }
 
@@ -1508,7 +1624,8 @@  handle_iface_delete(const char *lport_name, const char *iface_name,
         lbinding->pb->chassis == b_ctx_in->chassis_rec) {
 
         if (!release_local_binding(b_ctx_in->chassis_rec, lbinding,
-                                   !b_ctx_in->ovnsb_idl_txn)) {
+                                   !b_ctx_in->ovnsb_idl_txn,
+                                   b_ctx_out->tracked_dp_bindings)) {
             return false;
         }
 
@@ -1556,6 +1673,8 @@  binding_handle_ovs_interface_changes(struct binding_ctx_in *b_ctx_in,
     bool handled = true;
     *changed = false;
 
+    *b_ctx_out->local_lports_changed = false;
+
     /* Run the tracked interfaces loop twice. One to handle deleted
      * changes. And another to handle add/update changes.
      * This will ensure correctness.
@@ -1698,9 +1817,10 @@  handle_deleted_vif_lport(const struct sbrec_port_binding *pb,
          * clear the 'chassis' column of 'pb'. But we need to do
          * for the local_binding's children. */
         if (lbinding->type == BT_VIF &&
-                !release_local_binding_children(b_ctx_in->chassis_rec,
-                                                lbinding,
-                                                !b_ctx_in->ovnsb_idl_txn)) {
+                !release_local_binding_children(
+                    b_ctx_in->chassis_rec, lbinding,
+                    !b_ctx_in->ovnsb_idl_txn,
+                    b_ctx_out->tracked_dp_bindings)) {
             return false;
         }
 
@@ -1746,6 +1866,9 @@  handle_updated_vif_lport(const struct sbrec_port_binding *pb,
         return true;
     }
 
+    update_lport_tracking(pb, claimed, now_claimed,
+                          b_ctx_out->tracked_dp_bindings);
+
     struct local_binding *lbinding =
         local_binding_find(b_ctx_out->local_bindings, pb->logical_port);
 
@@ -1755,11 +1878,17 @@  handle_updated_vif_lport(const struct sbrec_port_binding *pb,
     SHASH_FOR_EACH (node, &lbinding->children) {
         struct local_binding *child = node->data;
         if (child->type == BT_CONTAINER) {
+            claimed = is_lbinding_this_chassis(child, b_ctx_in->chassis_rec);
             handled = consider_container_lport(child->pb, b_ctx_in, b_ctx_out,
                                                qos_map);
             if (!handled) {
                 return false;
             }
+
+            now_claimed = is_lbinding_this_chassis(child,
+                                                   b_ctx_in->chassis_rec);
+            update_lport_tracking(child->pb, claimed, now_claimed,
+                                  b_ctx_out->tracked_dp_bindings);
         }
     }
 
diff --git a/controller/binding.h b/controller/binding.h
index 21118ecd4..fc2a673e5 100644
--- a/controller/binding.h
+++ b/controller/binding.h
@@ -19,6 +19,9 @@ 
 
 #include <stdbool.h>
 #include "openvswitch/shash.h"
+#include "openvswitch/hmap.h"
+#include "openvswitch/uuid.h"
+#include "openvswitch/list.h"
 
 struct hmap;
 struct ovsdb_idl;
@@ -58,6 +61,8 @@  struct binding_ctx_out {
     struct sset *local_lport_ids;
     struct sset *egress_ifaces;
     struct smap *local_iface_ids;
+    struct hmap *tracked_dp_bindings;
+    bool *local_lports_changed;
 };
 
 enum local_binding_type {
@@ -82,6 +87,21 @@  local_binding_find(struct shash *local_bindings, const char *name)
     return shash_find_data(local_bindings, name);
 }
 
+/* Represents a tracked binding logical port. */
+struct tracked_binding_lport {
+    const struct sbrec_port_binding *pb;
+    struct ovs_list list_node;
+    bool deleted;
+};
+
+/* Represent a tracked binding datapath. */
+struct tracked_binding_datapath {
+    struct hmap_node node;
+    const struct sbrec_datapath_binding *dp;
+    bool is_new;
+    struct ovs_list lports_head; /* List of struct tracked_binding_lport. */
+};
+
 void binding_register_ovs_idl(struct ovsdb_idl *);
 void binding_run(struct binding_ctx_in *, struct binding_ctx_out *);
 bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
@@ -96,4 +116,5 @@  bool binding_handle_ovs_interface_changes(struct binding_ctx_in *,
 bool binding_handle_port_binding_changes(struct binding_ctx_in *,
                                          struct binding_ctx_out *,
                                          bool *changed);
+void binding_tracked_dp_destroy(struct hmap *tracked_datapaths);
 #endif /* controller/binding.h */
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 8f95fff1f..dc790f0f7 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -978,6 +978,23 @@  struct ed_type_runtime_data {
     struct smap local_iface_ids;
 };
 
+struct ed_type_runtime_tracked_data {
+    struct hmap tracked_dp_bindings;
+    bool local_lports_changed;
+    bool tracked;
+};
+
+static void
+en_runtime_clear_tracked_data(void *tracked_data)
+{
+    struct ed_type_runtime_tracked_data *data = tracked_data;
+
+    binding_tracked_dp_destroy(&data->tracked_dp_bindings);
+    hmap_init(&data->tracked_dp_bindings);
+    data->local_lports_changed = false;
+    data->tracked = false;
+}
+
 static void *
 en_runtime_data_init(struct engine_node *node OVS_UNUSED,
                      struct engine_arg *arg OVS_UNUSED)
@@ -991,6 +1008,13 @@  en_runtime_data_init(struct engine_node *node OVS_UNUSED,
     sset_init(&data->egress_ifaces);
     smap_init(&data->local_iface_ids);
     local_bindings_init(&data->local_bindings);
+
+    struct ed_type_runtime_tracked_data *tracked_data =
+        xzalloc(sizeof *tracked_data);
+    hmap_init(&tracked_data->tracked_dp_bindings);
+    node->tracked_data = tracked_data;
+    node->clear_tracked_data = en_runtime_clear_tracked_data;
+
     return data;
 }
 
@@ -1093,6 +1117,8 @@  init_binding_ctx(struct engine_node *node,
     b_ctx_out->egress_ifaces = &rt_data->egress_ifaces;
     b_ctx_out->local_bindings = &rt_data->local_bindings;
     b_ctx_out->local_iface_ids = &rt_data->local_iface_ids;
+    b_ctx_out->tracked_dp_bindings = NULL;
+    b_ctx_out->local_lports_changed = NULL;
 }
 
 static void
@@ -1104,6 +1130,8 @@  en_runtime_data_run(struct engine_node *node, void *data)
     struct sset *local_lport_ids = &rt_data->local_lport_ids;
     struct sset *active_tunnels = &rt_data->active_tunnels;
 
+    en_runtime_clear_tracked_data(node->tracked_data);
+
     static bool first_run = true;
     if (first_run) {
         /* don't cleanup since there is no data yet */
@@ -1156,9 +1184,13 @@  static bool
 runtime_data_ovs_interface_handler(struct engine_node *node, void *data)
 {
     struct ed_type_runtime_data *rt_data = data;
+    struct ed_type_runtime_tracked_data *tracked_data = node->tracked_data;
     struct binding_ctx_in b_ctx_in;
     struct binding_ctx_out b_ctx_out;
     init_binding_ctx(node, rt_data, &b_ctx_in, &b_ctx_out);
+    tracked_data->tracked = true;
+    b_ctx_out.tracked_dp_bindings = &tracked_data->tracked_dp_bindings;
+    b_ctx_out.local_lports_changed = &tracked_data->local_lports_changed;
 
     bool changed = false;
     if (!binding_handle_ovs_interface_changes(&b_ctx_in, &b_ctx_out,
@@ -1191,6 +1223,10 @@  runtime_data_sb_port_binding_handler(struct engine_node *node, void *data)
         return false;
     }
 
+    struct ed_type_runtime_tracked_data *tracked_data = node->tracked_data;
+    tracked_data->tracked = true;
+    b_ctx_out.tracked_dp_bindings = &tracked_data->tracked_dp_bindings;
+
     bool changed = false;
     if (!binding_handle_port_binding_changes(&b_ctx_in, &b_ctx_out,
                                              &changed)) {
@@ -1862,6 +1898,29 @@  physical_flow_changes_ovs_iface_handler(struct engine_node *node OVS_UNUSED,
     return true;
 }
 
+static bool
+flow_output_runtime_data_handler(struct engine_node *node,
+                                 void *data OVS_UNUSED)
+{
+    struct ed_type_runtime_tracked_data *tracked_data =
+        engine_get_input_tracked_data("runtime_data", node);
+
+    if (!tracked_data || !tracked_data->tracked) {
+        return false;
+    }
+
+    if (!hmap_is_empty(&tracked_data->tracked_dp_bindings)) {
+        /* We are not yet handling the tracked datapath binding
+         * changes. Return false to trigger full recompute. */
+        return false;
+    }
+
+    if (tracked_data->local_lports_changed) {
+        engine_set_node_state(node, EN_UPDATED);
+    }
+    return true;
+}
+
 struct ovn_controller_exit_args {
     bool *exiting;
     bool *restart;
@@ -2014,7 +2073,8 @@  main(int argc, char *argv[])
                      flow_output_addr_sets_handler);
     engine_add_input(&en_flow_output, &en_port_groups,
                      flow_output_port_groups_handler);
-    engine_add_input(&en_flow_output, &en_runtime_data, NULL);
+    engine_add_input(&en_flow_output, &en_runtime_data,
+                     flow_output_runtime_data_handler);
     engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
     engine_add_input(&en_flow_output, &en_physical_flow_changes,
                      flow_output_physical_flow_changes_handler);
diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
index 5cc1960b6..6e064e64f 100644
--- a/tests/ovn-performance.at
+++ b/tests/ovn-performance.at
@@ -274,7 +274,7 @@  for i in 1 2; do
     )
 
     # Add router port to $ls
-    OVN_CONTROLLER_EXPECT_HIT(
+    OVN_CONTROLLER_EXPECT_NO_HIT(
         [hv1 hv2], [lflow_run],
         [ovn-nbctl --wait=hv lrp-add lr1 $lrp 02:00:00:00:0$i:01 10.0.$i.1/24]
     )
@@ -282,15 +282,15 @@  for i in 1 2; do
         [hv1 hv2], [lflow_run],
         [ovn-nbctl --wait=hv lsp-add $ls $lsp]
     )
-    OVN_CONTROLLER_EXPECT_HIT(
+    OVN_CONTROLLER_EXPECT_NO_HIT(
         [hv1 hv2], [lflow_run],
         [ovn-nbctl --wait=hv lsp-set-type $lsp router]
     )
-    OVN_CONTROLLER_EXPECT_HIT(
+    OVN_CONTROLLER_EXPECT_NO_HIT(
         [hv1 hv2], [lflow_run],
         [ovn-nbctl --wait=hv lsp-set-options $lsp router-port=$lrp]
     )
-    OVN_CONTROLLER_EXPECT_HIT(
+    OVN_CONTROLLER_EXPECT_NO_HIT(
         [hv1 hv2], [lflow_run],
         [ovn-nbctl --wait=hv lsp-set-addresses $lsp router]
     )
@@ -404,8 +404,8 @@  for i in 1 2; do
     lp=lp$i
 
     # Delete port $lp
-    OVN_CONTROLLER_EXPECT_HIT_COND(
-        [hv$i hv$j], [lflow_run], [>0 =0],
+    OVN_CONTROLLER_EXPECT_NO_HIT(
+        [hv$i hv$j], [lflow_run],
         [ovn-nbctl --wait=hv lsp-del $lp]
     )
 done
@@ -416,15 +416,15 @@  OVN_CONTROLLER_EXPECT_NO_HIT(
     [ovn-nbctl --wait=hv destroy Port_Group pg1]
 )
 
-for i in 1 2; do
-    ls=ls$i
+OVN_CONTROLLER_EXPECT_HIT(
+    [hv1 hv2], [lflow_run],
+    [ovn-nbctl --wait=hv ls-del ls1]
+)
 
-    # Delete switch $ls
-    OVN_CONTROLLER_EXPECT_HIT(
-        [hv1 hv2], [lflow_run],
-        [ovn-nbctl --wait=hv ls-del $ls]
-    )
-done
+OVN_CONTROLLER_EXPECT_NO_HIT(
+    [hv1 hv2], [lflow_run],
+    [ovn-nbctl --wait=hv ls-del ls2]
+)
 
 # Delete router lr1
 OVN_CONTROLLER_EXPECT_NO_HIT(