[{"id":3680576,"web_url":"http://patchwork.ozlabs.org/comment/3680576/","msgid":"<CACAUutOFjZ5gLB-8a83sc-1hj7Z9zHXmfopuNZLNRT_rofAfyw@mail.gmail.com>","list_archive_url":null,"date":"2026-04-22T11:59:59","subject":"Re: [ovs-dev] [PATCH ovn v2 2/2] northd: Incremental processing for\n static routes.","submitter":{"id":90169,"url":"http://patchwork.ozlabs.org/api/people/90169/","name":"Lucas Vargas Dias","email":"lucas.vdias@luizalabs.com"},"content":"Em ter., 21 de abr. de 2026 às 18:12, Lucas Vargas Dias <\nlucas.vdias@luizalabs.com> escreveu:\n\n> Create a handler for deleted and updated static routes,\n> and change the handler from engine route which check if\n> it has a new static route created.\n> Test with 2000 static routes created in the same logical router\n> and add a new one:\n> Without the incremental processing:\n> ovn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32\n> 192.168.20.2\n> Time spent on processing nb_cfg 4:\n>         ovn-northd delay before processing:     4ms\n>         ovn-northd completion:                  62ms\n>\n> With the incremental processing:\n> ovn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32\n> 192.168.20.2\n> Time spent on processing nb_cfg 6:\n>         ovn-northd delay before processing:     4ms\n>         ovn-northd completion:                  21ms\n>\n> Test with 2000 static routes created in the same logical router\n> and delete one:\n> Without the incremental processing:\n> ovn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32\n> 192.168.20.2\n> Time spent on processing nb_cfg 5:\n>         ovn-northd delay before processing:     3ms\n>         ovn-northd completion:                  62ms\n>\n> With the incremental processing:\n> ovn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32\n> 192.168.20.2\n> Time spent on processing nb_cfg 9:\n>         ovn-northd delay before processing:     2ms\n>         ovn-northd completion:                  32ms\n>\n> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>\n> ---\n>\n\nrecheck-request: github-robot\n\n\n>  northd/en-group-ecmp-route.c     |  57 ++++++++++\n>  northd/en-group-ecmp-route.h     |   4 +\n>  northd/en-lflow.c                |  20 ++++\n>  northd/en-lflow.h                |   2 +\n>  northd/en-northd.c               | 186 +++++++++++++++++++++++++++----\n>  northd/en-northd.h               |   5 +-\n>  northd/inc-proc-northd.c         |  13 ++-\n>  northd/northd.c                  | 107 +++++++++++++-----\n>  northd/northd.h                  |  38 ++++++-\n>  tests/ovn-inc-proc-graph-dump.at |   6 +-\n>  tests/ovn-northd.at              |  98 +++++++++++++---\n>  11 files changed, 466 insertions(+), 70 deletions(-)\n>\n> diff --git a/northd/en-group-ecmp-route.c b/northd/en-group-ecmp-route.c\n> index c4c93fd84..fcc76b076 100644\n> --- a/northd/en-group-ecmp-route.c\n> +++ b/northd/en-group-ecmp-route.c\n> @@ -519,3 +519,60 @@ group_ecmp_route_learned_route_change_handler(struct\n> engine_node *eng_node,\n>      }\n>      return EN_HANDLED_UNCHANGED;\n>  }\n> +\n> +enum engine_input_handler_result\n> +group_ecmp_static_route_change_handler(struct engine_node *eng_node,\n> +                                       void *_data)\n> +{\n> +    struct routes_data *routes_data\n> +        = engine_get_input_data(\"routes\", eng_node);\n> +    struct group_ecmp_route_data *data = _data;\n> +    if (!routes_data->tracked) {\n> +        data->tracked = false;\n> +        return EN_UNHANDLED;\n> +    }\n> +\n> +    struct parsed_route *pr;\n> +    struct hmapx updated_routes = HMAPX_INITIALIZER(&updated_routes);\n> +\n> +    const struct hmapx_node *hmapx_node;\n> +    HMAPX_FOR_EACH (hmapx_node,\n> +                    &routes_data->trk_data.trk_deleted_parsed_route) {\n> +        pr = hmapx_node->data;\n> +        if (!handle_deleted_route(data, pr, &updated_routes)) {\n> +            hmapx_destroy(&updated_routes);\n> +            return EN_UNHANDLED;\n> +        }\n> +\n> +        if (pr->is_in_parsed_routes) {\n> +            hmap_remove(&routes_data->parsed_routes, &pr->key_node);\n> +        }\n> +        parsed_route_free(pr);\n> +    }\n> +\n> +    HMAPX_FOR_EACH (hmapx_node,\n> +                    &routes_data->trk_data.trk_crupdated_parsed_route) {\n> +        pr = hmapx_node->data;\n> +        handle_added_route(data, pr, &updated_routes);\n> +    }\n> +\n> +    HMAPX_FOR_EACH (hmapx_node, &updated_routes) {\n> +        struct group_ecmp_datapath *node = hmapx_node->data;\n> +        if (hmap_is_empty(&node->unique_routes) &&\n> +                hmap_is_empty(&node->ecmp_groups)) {\n> +            hmapx_add(&data->trk_data.deleted_datapath_routes, node);\n> +            hmap_remove(&data->datapaths, &node->hmap_node);\n> +        } else {\n> +            hmapx_add(&data->trk_data.crupdated_datapath_routes, node);\n> +        }\n> +    }\n> +\n> +    hmapx_destroy(&updated_routes);\n> +\n> +    if (!hmapx_is_empty(&data->trk_data.crupdated_datapath_routes) ||\n> +        !hmapx_is_empty(&data->trk_data.deleted_datapath_routes)) {\n> +        data->tracked = true;\n> +        return EN_HANDLED_UPDATED;\n> +    }\n> +    return EN_HANDLED_UNCHANGED;\n> +}\n> diff --git a/northd/en-group-ecmp-route.h b/northd/en-group-ecmp-route.h\n> index d4a3248d0..246ca06bf 100644\n> --- a/northd/en-group-ecmp-route.h\n> +++ b/northd/en-group-ecmp-route.h\n> @@ -98,6 +98,10 @@ enum engine_input_handler_result\n>  group_ecmp_route_learned_route_change_handler(struct engine_node *,\n>                                                void *data);\n>\n> +enum engine_input_handler_result\n> +group_ecmp_static_route_change_handler(struct engine_node *,\n> +                                       void *data);\n> +\n>  struct group_ecmp_datapath *group_ecmp_datapath_lookup(\n>      const struct group_ecmp_route_data *data,\n>      const struct ovn_datapath *od);\n> diff --git a/northd/en-lflow.c b/northd/en-lflow.c\n> index d4351edb9..22cd8fe91 100644\n> --- a/northd/en-lflow.c\n> +++ b/northd/en-lflow.c\n> @@ -297,6 +297,21 @@ lflow_multicast_igmp_handler(struct engine_node\n> *node, void *data)\n>      return EN_HANDLED_UPDATED;\n>  }\n>\n> +enum engine_input_handler_result\n> +lflow_group_route_change_handler(struct engine_node *node,\n> +                                      void *data OVS_UNUSED)\n> +{\n> +    struct routes_data *route_data =\n> +        engine_get_input_data(\"routes\", node);\n> +\n> +    /* If we do not have tracked data we need to recompute. */\n> +    if (!route_data->tracked) {\n> +        return EN_UNHANDLED;\n> +    }\n> +\n> +    return EN_HANDLED_UNCHANGED;\n> +}\n> +\n>  enum engine_input_handler_result\n>  lflow_group_ecmp_route_change_handler(struct engine_node *node,\n>                                        void *data OVS_UNUSED)\n> @@ -346,6 +361,11 @@ lflow_group_ecmp_route_change_handler(struct\n> engine_node *node,\n>              route_node->od, lflow_data->lflow_table,\n>              route_node, lflow_input.bfd_ports);\n>\n> +        build_arp_request_flows_for_lrouter(route_node->od,\n> +                                            lflow_data->lflow_table,\n> +                                            lflow_input.meter_groups,\n> +                                            route_node->lflow_ref);\n> +\n>          bool handled = lflow_ref_sync_lflows(\n>              route_node->lflow_ref, lflow_data->lflow_table,\n>              eng_ctx->ovnsb_idl_txn, lflow_input.dps,\n> diff --git a/northd/en-lflow.h b/northd/en-lflow.h\n> index d2a92e49f..aa320615f 100644\n> --- a/northd/en-lflow.h\n> +++ b/northd/en-lflow.h\n> @@ -31,5 +31,7 @@ lflow_multicast_igmp_handler(struct engine_node *node,\n> void *data);\n>  enum engine_input_handler_result\n>  lflow_group_ecmp_route_change_handler(struct engine_node *node, void\n> *data);\n>  enum engine_input_handler_result\n> +lflow_group_route_change_handler(struct engine_node *node, void *data);\n> +enum engine_input_handler_result\n>  lflow_ic_learned_svc_mons_handler(struct engine_node *node, void *data);\n>  #endif /* EN_LFLOW_H */\n> diff --git a/northd/en-northd.c b/northd/en-northd.c\n> index c34818dba..c05939a1d 100644\n> --- a/northd/en-northd.c\n> +++ b/northd/en-northd.c\n> @@ -207,7 +207,8 @@ northd_nb_logical_router_handler(struct engine_node\n> *node,\n>      }\n>\n>      if (northd_has_lr_nats_in_tracked_data(&nd->trk_data) ||\n> -        northd_has_lrouters_in_tracked_data(&nd->trk_data)) {\n> +        northd_has_lrouters_in_tracked_data(&nd->trk_data) ||\n> +        northd_has_lr_route_in_tracked_data(&nd->trk_data)) {\n>          return EN_HANDLED_UPDATED;\n>      }\n>\n> @@ -329,32 +330,174 @@ en_route_policies_run(struct engine_node *node,\n> void *data)\n>\n>  enum engine_input_handler_result\n>  routes_northd_change_handler(struct engine_node *node,\n> -                                    void *data OVS_UNUSED)\n> +                                    void *data)\n>  {\n>      struct northd_data *northd_data = engine_get_input_data(\"northd\",\n> node);\n>      if (!northd_has_tracked_data(&northd_data->trk_data)) {\n>          return EN_UNHANDLED;\n>      }\n>\n> -    /* This node uses the below data from the en_northd engine node.\n> -     * See (lr_stateful_get_input_data())\n> -     *   1. northd_data->lr_datapaths\n> -     *   2. northd_data->lr_ports\n> -     *      This data gets updated when a logical router or logical\n> router port\n> -     *      is created or deleted.\n> -     *      Northd engine node presently falls back to full recompute when\n> -     *      this happens and so does this node.\n> -     *      Note: When we add I-P to the created/deleted logical routers\n> or\n> -     *      logical router ports, we need to revisit this handler.\n> -     *\n> -     *      This node also accesses the static routes of the logical\n> router.\n> -     *      When these static routes gets updated, en_northd engine\n> recomputes\n> -     *      and so does this node.\n> -     *      Note: When we add I-P to handle static routes changes, we need\n> -     *      to revisit this handler.\n> -     */\n> +    if (!northd_has_lr_route_in_tracked_data(&northd_data->trk_data)) {\n> +        return EN_HANDLED_UNCHANGED;\n> +    }\n> +\n> +    struct bfd_data *bfd_data = engine_get_input_data(\"bfd\", node);\n> +    struct routes_data *routes_data = data;\n> +    struct hmapx_node *hmapx_node;\n> +    struct ovn_datapath *od;\n> +    HMAPX_FOR_EACH (hmapx_node, &northd_data->trk_data.trk_lrs_routes) {\n> +        od = hmapx_node->data;\n> +        struct parsed_route *pr;\n> +\n> +        for (int i = 0; i < od->nbr->n_static_routes; i++) {\n> +            struct nbrec_logical_router_static_route *static_route =\n> +                od->nbr->static_routes[i];\n> +            pr = parsed_route_lookup_by_source(ROUTE_SOURCE_STATIC,\n> +                                               &static_route->header_,\n> +\n>  &routes_data->parsed_routes);\n> +            if (pr) {\n> +                pr->stale = false;\n> +                continue;\n> +            }\n> +            pr = parsed_routes_add_static(od, &northd_data->lr_ports,\n> +                                        static_route,\n> +                                        &bfd_data->bfd_connections,\n> +                                        &routes_data->parsed_routes,\n> +                                        &routes_data->route_tables,\n> +\n> &routes_data->bfd_active_connections);\n> +            if (!pr) {\n> +                continue;\n> +            }\n> +            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,\n> +                      pr);\n> +        }\n> +    }\n> +\n> +    if\n> (!hmapx_is_empty(&routes_data->trk_data.trk_crupdated_parsed_route)) {\n> +        routes_data->tracked = true;\n> +        return EN_HANDLED_UPDATED;\n> +    }\n> +\n>      return EN_HANDLED_UNCHANGED;\n>  }\n> +enum engine_input_handler_result\n> +routes_static_route_change_handler(struct engine_node *node,\n> +                                   void *data)\n> +{\n> +    struct routes_data *routes_data = data;\n> +    struct hmapx created_trk_parsed_route =\n> +        HMAPX_INITIALIZER(&created_trk_parsed_route);\n> +    const struct nbrec_logical_router_static_route_table *\n> +      nb_lr_static_route_table =\n> +    EN_OVSDB_GET(engine_get_input(\"NB_logical_router_static_route\",\n> node));\n> +\n> +    struct northd_data *northd_data = engine_get_input_data(\"northd\",\n> node);\n> +    struct bfd_data *bfd_data = engine_get_input_data(\"bfd\", node);\n> +\n> +    const struct nbrec_logical_router_static_route *changed_static_route;\n> +    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_TABLE_FOR_EACH_TRACKED (\n> +                            changed_static_route,\n> nb_lr_static_route_table) {\n> +\n> +        bool is_deleted = nbrec_logical_router_static_route_is_deleted(\n> +\n> changed_static_route);\n> +        bool is_new = nbrec_logical_router_static_route_is_new(\n> +\n> changed_static_route);\n> +\n> +        if (is_new && is_deleted) {\n> +            continue;\n> +        }\n> +\n> +        if (is_new) {\n> +            hmapx_add(&created_trk_parsed_route, &changed_static_route);\n> +            continue;\n> +        }\n> +\n> +        if (is_deleted) {\n> +            struct parsed_route *pr = parsed_route_lookup_by_source(\n> +                                            ROUTE_SOURCE_STATIC,\n> +\n> &changed_static_route->header_,\n> +                                            &routes_data->parsed_routes);\n> +            if (!pr) {\n> +                pr =\n> parsed_route_lookup_by_source(ROUTE_SOURCE_IC_DYNAMIC,\n> +\n> &changed_static_route->header_,\n> +                                            &routes_data->parsed_routes);\n> +            }\n> +            if (pr) {\n> +                pr->stale = true;\n> +\n> hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route, pr);\n> +            }\n> +            continue;\n> +        }\n> +\n> +        if (nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_NEXTHOP)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_IP_PREFIX)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OUTPUT_PORT)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_POLICY)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_ROUTE_TABLE)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +\n> NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_SELECTION_FIELDS)\n> +            ||  nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OPTIONS)) {\n> +            struct parsed_route *pr = parsed_route_lookup_by_source(\n> +                                            ROUTE_SOURCE_STATIC,\n> +\n> &changed_static_route->header_,\n> +                                            &routes_data->parsed_routes);\n> +            if (!pr) {\n> +                pr = parsed_route_lookup_by_source(\n> +                                                ROUTE_SOURCE_IC_DYNAMIC,\n> +\n> &changed_static_route->header_,\n> +\n> &routes_data->parsed_routes);\n> +            }\n> +\n> +            if (!pr || !pr->od || !northd_data || !bfd_data) {\n> +                continue;\n> +            }\n> +            struct parsed_route *old_pr = pr;\n> +            hmap_remove(&routes_data->parsed_routes, &old_pr->key_node);\n> +            pr = parsed_routes_add_static(old_pr->od,\n> &northd_data->lr_ports,\n> +                                        changed_static_route,\n> +                                        &bfd_data->bfd_connections,\n> +                                        &routes_data->parsed_routes,\n> +                                        &routes_data->route_tables,\n> +\n> &routes_data->bfd_active_connections);\n> +            old_pr->is_in_parsed_routes = false;\n> +            if (!pr) {\n> +                continue;\n> +            }\n> +\n> +            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,\n> +                       pr);\n> +            hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route,\n> +                      old_pr);\n> +        }\n> +    }\n> +    if\n> (!hmapx_is_empty(&routes_data->trk_data.trk_crupdated_parsed_route) ||\n> +        !hmapx_is_empty(&routes_data->trk_data.trk_deleted_parsed_route))\n> {\n> +        hmapx_destroy(&created_trk_parsed_route);\n> +        routes_data->tracked = true;\n> +        return EN_HANDLED_UPDATED;\n> +    }\n> +\n> +    if (!hmapx_is_empty(&created_trk_parsed_route)) {\n> +        hmapx_destroy(&created_trk_parsed_route);\n> +        return EN_HANDLED_UPDATED;\n> +    }\n> +\n> +    hmapx_destroy(&created_trk_parsed_route);\n> +    return EN_UNHANDLED;\n> +}\n>\n>  enum engine_node_state\n>  en_routes_run(struct engine_node *node, void *data)\n> @@ -590,6 +733,11 @@ en_routes_cleanup(void *data)\n>      routes_destroy(data);\n>  }\n>\n> +void\n> +en_routes_clear_tracked_data(void *data)\n> +{\n> +    routes_clear_tracked(data);\n> +}\n>  void\n>  en_bfd_cleanup(void *data)\n>  {\n> diff --git a/northd/en-northd.h b/northd/en-northd.h\n> index 7794739b9..5247f3e11 100644\n> --- a/northd/en-northd.h\n> +++ b/northd/en-northd.h\n> @@ -39,9 +39,12 @@ enum engine_node_state en_route_policies_run(struct\n> engine_node *node,\n>                                               void *data);\n>  void *en_route_policies_init(struct engine_node *node OVS_UNUSED,\n>                               struct engine_arg *arg OVS_UNUSED);\n> +void en_routes_clear_tracked_data(void *data);\n>  void en_routes_cleanup(void *data);\n>  enum engine_input_handler_result\n> -routes_northd_change_handler(struct engine_node *node, void *data\n> OVS_UNUSED);\n> +routes_northd_change_handler(struct engine_node *node, void *data);\n> +enum engine_input_handler_result\n> +routes_static_route_change_handler(struct engine_node *node, void *data);\n>  enum engine_node_state en_routes_run(struct engine_node *node, void\n> *data);\n>  void *en_bfd_init(struct engine_node *node OVS_UNUSED,\n>                    struct engine_arg *arg OVS_UNUSED);\n> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c\n> index ece388ce7..52f5dc57f 100644\n> --- a/northd/inc-proc-northd.c\n> +++ b/northd/inc-proc-northd.c\n> @@ -76,7 +76,9 @@ static unixctl_cb_func chassis_features_list;\n>      NB_NODE(sampling_app) \\\n>      NB_NODE(network_function) \\\n>      NB_NODE(network_function_group) \\\n> -    NB_NODE(logical_switch_port_health_check)\n> +    NB_NODE(logical_switch_port_health_check) \\\n> +    NB_NODE(logical_router_static_route)\n> +\n>\n>      enum nb_engine_node {\n>  #define NB_NODE(NAME) NB_##NAME,\n> @@ -179,7 +181,7 @@ static ENGINE_NODE(lr_stateful, CLEAR_TRACKED_DATA);\n>  static ENGINE_NODE(ls_stateful, CLEAR_TRACKED_DATA);\n>  static ENGINE_NODE(ls_arp, CLEAR_TRACKED_DATA);\n>  static ENGINE_NODE(route_policies);\n> -static ENGINE_NODE(routes);\n> +static ENGINE_NODE(routes, CLEAR_TRACKED_DATA);\n>  static ENGINE_NODE(bfd);\n>  static ENGINE_NODE(bfd_sync, SB_WRITE);\n>  static ENGINE_NODE(ecmp_nexthop, SB_WRITE);\n> @@ -341,6 +343,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n>      engine_add_input(&en_routes, &en_bfd, NULL);\n>      engine_add_input(&en_routes, &en_northd,\n>                       routes_northd_change_handler);\n> +    engine_add_input(&en_routes, &en_nb_logical_router_static_route,\n> +                     routes_static_route_change_handler);\n>\n>      engine_add_input(&en_bfd_sync, &en_bfd, NULL);\n>      engine_add_input(&en_bfd_sync, &en_nb_bfd, NULL);\n> @@ -380,7 +384,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n>      engine_add_input(&en_learned_route_sync, &en_northd,\n>                       learned_route_sync_northd_change_handler);\n>\n> -    engine_add_input(&en_group_ecmp_route, &en_routes, NULL);\n> +    engine_add_input(&en_group_ecmp_route, &en_routes,\n> +                     group_ecmp_static_route_change_handler);\n>      engine_add_input(&en_group_ecmp_route, &en_learned_route_sync,\n>                       group_ecmp_route_learned_route_change_handler);\n>\n> @@ -399,7 +404,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n>      engine_add_input(&en_lflow, &en_sb_logical_dp_group, NULL);\n>      engine_add_input(&en_lflow, &en_bfd_sync, NULL);\n>      engine_add_input(&en_lflow, &en_route_policies, NULL);\n> -    engine_add_input(&en_lflow, &en_routes, NULL);\n> +    engine_add_input(&en_lflow, &en_routes,\n> lflow_group_route_change_handler);\n>      /* XXX: The incremental processing only supports changes to learned\n> routes.\n>       * All other changes trigger a full recompute. */\n>      engine_add_input(&en_lflow, &en_group_ecmp_route,\n> diff --git a/northd/northd.c b/northd/northd.c\n> index 4fd4b9de9..ea48bc442 100644\n> --- a/northd/northd.c\n> +++ b/northd/northd.c\n> @@ -4517,6 +4517,7 @@ destroy_northd_data_tracked_changes(struct\n> northd_data *nd)\n>      destroy_tracked_ovn_ports(&trk_changes->trk_lsps);\n>      destroy_tracked_lbs(&trk_changes->trk_lbs);\n>      hmapx_clear(&trk_changes->trk_nat_lrs);\n> +    hmapx_clear(&trk_changes->trk_lrs_routes);\n>      hmapx_clear(&trk_changes->ls_with_changed_lbs);\n>      hmapx_clear(&trk_changes->ls_with_changed_acls);\n>      hmapx_clear(&trk_changes->ls_with_changed_ipam);\n> @@ -4540,6 +4541,7 @@ init_northd_tracked_data(struct northd_data *nd)\n>      hmapx_init(&trk_data->trk_lbs.crupdated);\n>      hmapx_init(&trk_data->trk_lbs.deleted);\n>      hmapx_init(&trk_data->trk_nat_lrs);\n> +    hmapx_init(&trk_data->trk_lrs_routes);\n>      hmapx_init(&trk_data->ls_with_changed_lbs);\n>      hmapx_init(&trk_data->ls_with_changed_acls);\n>      hmapx_init(&trk_data->ls_with_changed_ipam);\n> @@ -4558,6 +4560,7 @@ destroy_northd_tracked_data(struct northd_data *nd)\n>      hmapx_destroy(&trk_data->trk_lbs.crupdated);\n>      hmapx_destroy(&trk_data->trk_lbs.deleted);\n>      hmapx_destroy(&trk_data->trk_nat_lrs);\n> +    hmapx_destroy(&trk_data->trk_lrs_routes);\n>      hmapx_destroy(&trk_data->ls_with_changed_lbs);\n>      hmapx_destroy(&trk_data->ls_with_changed_acls);\n>      hmapx_destroy(&trk_data->ls_with_changed_ipam);\n> @@ -5379,7 +5382,8 @@ lr_changes_can_be_handled(const struct\n> nbrec_logical_router *lr)\n>          if (nbrec_logical_router_is_updated(lr, col)) {\n>              if (col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER\n>                  || col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER_GROUP\n> -                || col == NBREC_LOGICAL_ROUTER_COL_NAT) {\n> +                || col == NBREC_LOGICAL_ROUTER_COL_NAT\n> +                || col == NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES) {\n>                  continue;\n>              }\n>              return false;\n> @@ -5404,12 +5408,7 @@ lr_changes_can_be_handled(const struct\n> nbrec_logical_router *lr)\n>              return false;\n>          }\n>      }\n> -    for (size_t i = 0; i < lr->n_static_routes; i++) {\n> -        if (nbrec_logical_router_static_route_row_get_seqno(\n> -            lr->static_routes[i], OVSDB_IDL_CHANGE_MODIFY) > 0) {\n> -            return false;\n> -        }\n> -    }\n> +\n>      return true;\n>  }\n>\n> @@ -5435,6 +5434,13 @@ is_lr_nats_changed(const struct\n> nbrec_logical_router *nbr) {\n>              || is_lr_nats_seqno_changed(nbr));\n>  }\n>\n> +static bool\n> +is_lr_static_routes_changed(const struct nbrec_logical_router *nbr) {\n> +    return nbrec_logical_router_is_updated(nbr,\n> +\n>  NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES);\n> +}\n> +\n> +\n>  /* Return true if changes are handled incrementally, false otherwise.\n>   *\n>   * Note: Changes to load balancer and load balancer groups associated with\n> @@ -5503,6 +5509,22 @@ northd_handle_lr_changes(const struct northd_input\n> *ni,\n>\n>              hmapx_add(&nd->trk_data.trk_nat_lrs, od);\n>          }\n> +\n> +        /* Static Route was added or deleted. */\n> +        if (is_lr_static_routes_changed(changed_lr)) {\n> +            struct ovn_datapath *od = ovn_datapath_find_(\n> +                                    &nd->lr_datapaths.datapaths,\n> +                                    &changed_lr->header_.uuid);\n> +\n> +            if (!od) {\n> +                static struct vlog_rate_limit rl =\n> VLOG_RATE_LIMIT_INIT(1, 1);\n> +                VLOG_WARN_RL(&rl, \"Internal error: a tracked updated LR \"\n> +                            \"doesn't exist in lr_datapaths: \"UUID_FMT,\n> +                            UUID_ARGS(&changed_lr->header_.uuid));\n> +                goto fail;\n> +            }\n> +            hmapx_add(&nd->trk_data.trk_lrs_routes, od);\n> +        }\n>      }\n>\n>      HMAPX_FOR_EACH (node, &ni->synced_lrs->deleted) {\n> @@ -5543,6 +5565,9 @@ northd_handle_lr_changes(const struct northd_input\n> *ni,\n>      if (!hmapx_is_empty(&nd->trk_data.trk_nat_lrs)) {\n>          nd->trk_data.type |= NORTHD_TRACKED_LR_NATS;\n>      }\n> +    if (!hmapx_is_empty(&nd->trk_data.trk_lrs_routes)) {\n> +        nd->trk_data.type |= NORTHD_TRACKED_LR_ROUTES;\n> +    }\n>      if (!hmapx_is_empty(&nd->trk_data.trk_routers.crupdated) ||\n>          !hmapx_is_empty(&nd->trk_data.trk_routers.deleted)) {\n>          nd->trk_data.type |= NORTHD_TRACKED_ROUTERS;\n> @@ -12189,6 +12214,7 @@ parsed_route_init(const struct ovn_datapath *od,\n>      new_pr->route_table_id = route_table_id;\n>      new_pr->is_src_route = is_src_route;\n>      new_pr->od = od;\n> +    new_pr->is_in_parsed_routes = false;\n>      new_pr->ecmp_symmetric_reply = ecmp_symmetric_reply;\n>      new_pr->is_discard_route = is_discard_route;\n>      new_pr->lrp_addr_s = nullable_xstrdup(lrp_addr_s);\n> @@ -12296,6 +12322,7 @@ parsed_route_add(const struct ovn_datapath *od,\n>      struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);\n>      if (!pr) {\n>          hmap_insert(routes, &new_pr->key_node, hash);\n> +        new_pr->is_in_parsed_routes = true;\n>          return new_pr;\n>      } else {\n>          pr->stale = false;\n> @@ -12304,7 +12331,7 @@ parsed_route_add(const struct ovn_datapath *od,\n>      }\n>  }\n>\n> -static void\n> +struct parsed_route *\n>  parsed_routes_add_static(const struct ovn_datapath *od,\n>                           const struct hmap *lr_ports,\n>                           const struct nbrec_logical_router_static_route\n> *route,\n> @@ -12325,8 +12352,9 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                           UUID_FMT, route->nexthop,\n>                           UUID_ARGS(&route->header_.uuid));\n>              free(nexthop);\n> -            return;\n> +            return NULL;\n>          }\n> +\n>          if ((IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 32) ||\n>              (!IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 128)) {\n>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n> @@ -12334,7 +12362,7 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                           UUID_FMT, route->nexthop,\n>                           UUID_ARGS(&route->header_.uuid));\n>              free(nexthop);\n> -            return;\n> +            return NULL;\n>          }\n>      }\n>\n> @@ -12346,7 +12374,7 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                       UUID_FMT, route->ip_prefix,\n>                       UUID_ARGS(&route->header_.uuid));\n>          free(nexthop);\n> -        return;\n> +        return NULL;\n>      }\n>\n>      /* Verify that ip_prefix and nexthop are on the same network. */\n> @@ -12358,7 +12386,7 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                                     : IN6_IS_ADDR_V4MAPPED(&prefix),\n>                                     &lrp_addr_s, &out_port)) {\n>          free(nexthop);\n> -        return;\n> +        return NULL;\n>      }\n>\n>      const struct nbrec_bfd *nb_bt = route->bfd;\n> @@ -12368,7 +12396,7 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                                                    nb_bt->dst_ip);\n>          if (!bfd_e) {\n>              free(nexthop);\n> -            return;\n> +            return NULL;\n>          }\n>\n>          /* This static route is linked to an active bfd session. */\n> @@ -12385,10 +12413,9 @@ parsed_routes_add_static(const struct\n> ovn_datapath *od,\n>              bfd_set_status(bfd_sr, \"down\");\n>          }\n>\n> -\n>          if (!strcmp(bfd_sr->status, \"down\")) {\n> -            free(nexthop);\n> -            return;\n> +           free(nexthop);\n> +           return NULL;\n>          }\n>      }\n>\n> @@ -12427,11 +12454,15 @@ parsed_routes_add_static(const struct\n> ovn_datapath *od,\n>          source = ROUTE_SOURCE_STATIC;\n>      }\n>\n> -    parsed_route_add(od, nexthop, &prefix, plen, is_discard_route,\n> lrp_addr_s,\n> -                     out_port, route_table_id, is_src_route,\n> -                     ecmp_symmetric_reply, &ecmp_selection_fields, source,\n> -                     &route->header_, NULL, routes);\n> +    struct parsed_route *pr = parsed_route_add(od, nexthop, &prefix, plen,\n> +                                               is_discard_route,\n> lrp_addr_s,\n> +                                               out_port, route_table_id,\n> +                                               is_src_route,\n> +                                               ecmp_symmetric_reply,\n> +                                               &ecmp_selection_fields,\n> source,\n> +                                               &route->header_, NULL,\n> routes);\n>      sset_destroy(&ecmp_selection_fields);\n> +    return pr;\n>  }\n>\n>  static void\n> @@ -16309,13 +16340,14 @@ build_lr_gateway_redirect_flows_for_nats(\n>   * In the common case where the Ethernet destination has been resolved,\n>   * this table outputs the packet (priority 0).  Otherwise, it composes\n>   * and sends an ARP/IPv6 NA request (priority 100). */\n> -static void\n> +void\n>  build_arp_request_flows_for_lrouter(\n> -        struct ovn_datapath *od, struct lflow_table *lflows,\n> -        struct ds *match, struct ds *actions,\n> +        const struct ovn_datapath *od, struct lflow_table *lflows,\n>          const struct shash *meter_groups,\n>          struct lflow_ref *lflow_ref)\n>  {\n> +    struct ds match =  DS_EMPTY_INITIALIZER;\n> +    struct ds actions = DS_EMPTY_INITIALIZER;\n>      ovs_assert(od->nbr);\n>      for (int i = 0; i < od->nbr->n_static_routes; i++) {\n>          const struct nbrec_logical_router_static_route *route;\n> @@ -16329,8 +16361,8 @@ build_arp_request_flows_for_lrouter(\n>              continue;\n>          }\n>\n> -        ds_clear(match);\n> -        ds_put_format(match, \"eth.dst == 00:00:00:00:00:00 && \"\n> +        ds_clear(&match);\n> +        ds_put_format(&match, \"eth.dst == 00:00:00:00:00:00 && \"\n>                        REGBIT_NEXTHOP_IS_IPV4\" == 0 && \"\n>                        REG_NEXT_HOP_IPV6 \" == %s\",\n>                        route->nexthop);\n> @@ -16342,8 +16374,8 @@ build_arp_request_flows_for_lrouter(\n>          char sn_addr_s[INET6_ADDRSTRLEN + 1];\n>          ipv6_string_mapped(sn_addr_s, &sn_addr);\n>\n> -        ds_clear(actions);\n> -        ds_put_format(actions,\n> +        ds_clear(&actions);\n> +        ds_put_format(&actions,\n>                        \"nd_ns { \"\n>                        \"eth.dst = \"ETH_ADDR_FMT\"; \"\n>                        \"ip6.dst = %s; \"\n> @@ -16353,7 +16385,7 @@ build_arp_request_flows_for_lrouter(\n>                        route->nexthop);\n>\n>          ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,\n> -                      ds_cstr(match), ds_cstr(actions), lflow_ref,\n> +                      ds_cstr(&match), ds_cstr(&actions), lflow_ref,\n>                        WITH_CTRL_METER(copp_meter_get(COPP_ND_NS_RESOLVE,\n>                                                       od->nbr->copp,\n>                                                       meter_groups)),\n> @@ -16385,6 +16417,8 @@ build_arp_request_flows_for_lrouter(\n>\n>  meter_groups)));\n>      ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, \"1\", \"next;\",\n>                    lflow_ref);\n> +    ds_destroy(&match);\n> +    ds_destroy(&actions);\n>  }\n>\n>  static void\n> @@ -19508,8 +19542,7 @@ build_lswitch_and_lrouter_iterate_by_lr(struct\n> ovn_datapath *od,\n>      build_gateway_redirect_flows_for_lrouter(od, lsi->lflows, &lsi->match,\n>                                               &lsi->actions,\n>                                               od->datapath_lflows);\n> -    build_arp_request_flows_for_lrouter(od, lsi->lflows, &lsi->match,\n> -                                        &lsi->actions,\n> +    build_arp_request_flows_for_lrouter(od, lsi->lflows,\n>                                          lsi->meter_groups,\n>                                          od->datapath_lflows);\n>      build_ecmp_stateful_egr_flows_for_lrouter(od, lsi->lflows,\n> @@ -21067,6 +21100,9 @@ routes_init(struct routes_data *data)\n>      hmap_init(&data->parsed_routes);\n>      simap_init(&data->route_tables);\n>      hmap_init(&data->bfd_active_connections);\n> +    data->tracked = false;\n> +    hmapx_init(&data->trk_data.trk_deleted_parsed_route);\n> +    hmapx_init(&data->trk_data.trk_crupdated_parsed_route);\n>  }\n>\n>  void\n> @@ -21197,6 +21233,17 @@ routes_destroy(struct routes_data *data)\n>\n>      simap_destroy(&data->route_tables);\n>      __bfd_destroy(&data->bfd_active_connections);\n> +    data->tracked = false;\n> +    hmapx_destroy(&data->trk_data.trk_crupdated_parsed_route);\n> +    hmapx_destroy(&data->trk_data.trk_deleted_parsed_route);\n> +}\n> +\n> +void\n> +routes_clear_tracked(struct routes_data *data)\n> +{\n> +    data->tracked = false;\n> +    hmapx_clear(&data->trk_data.trk_crupdated_parsed_route);\n> +    hmapx_clear(&data->trk_data.trk_deleted_parsed_route);\n>  }\n>\n>  void\n> diff --git a/northd/northd.h b/northd/northd.h\n> index a9070d6f6..5b3461840 100644\n> --- a/northd/northd.h\n> +++ b/northd/northd.h\n> @@ -160,6 +160,7 @@ enum northd_tracked_data_type {\n>      NORTHD_TRACKED_LS_ACLS  = (1 << 4),\n>      NORTHD_TRACKED_SWITCHES = (1 << 5),\n>      NORTHD_TRACKED_ROUTERS  = (1 << 6),\n> +    NORTHD_TRACKED_LR_ROUTES  = (1 << 7),\n>  };\n>\n>  /* Track what's changed in the northd engine node.\n> @@ -177,6 +178,10 @@ struct northd_tracked_data {\n>       * hmapx node is 'struct ovn_datapath *'. */\n>      struct hmapx trk_nat_lrs;\n>\n> +    /* Tracked logical routers whose static routes have changed.\n> +     * hmapx node is 'struct ovn_datapath *'. */\n> +    struct hmapx trk_lrs_routes;\n> +\n>      /* Tracked logical switches whose load balancers have changed.\n>       * hmapx node is 'struct ovn_datapath *'. */\n>      struct hmapx ls_with_changed_lbs;\n> @@ -217,10 +222,23 @@ struct route_policy {\n>      uint32_t jump_chain_id;\n>  };\n>\n> +struct route_tracked_data {\n> +    /* Contains references to group_ecmp_route_node. Each of the\n> referenced\n> +     * datapaths contains at least one route. */\n> +    struct hmapx trk_crupdated_parsed_route;\n> +\n> +    /* Contains references to group_ecmp_route_node. Each of the\n> referenced\n> +     * datapath previously had some routes. The datapath now no longer\n> +     * contains any route.*/\n> +    struct hmapx trk_deleted_parsed_route;\n> +};\n> +\n>  struct routes_data {\n>      struct hmap parsed_routes; /* Stores struct parsed_route. */\n>      struct simap route_tables;\n>      struct hmap bfd_active_connections;\n> +    bool tracked;\n> +    struct route_tracked_data trk_data;\n>  };\n>\n>  struct route_policies_data {\n> @@ -855,6 +873,7 @@ struct parsed_route {\n>      char *lrp_addr_s;\n>      const struct ovn_port *out_port;\n>      const struct ovn_port *tracked_port; /* May be NULL. */\n> +    bool is_in_parsed_routes;\n>  };\n>\n>  struct parsed_route *parsed_route_clone(const struct parsed_route *);\n> @@ -881,6 +900,14 @@ struct parsed_route *parsed_route_add(\n>      const struct ovn_port *tracked_port,\n>      struct hmap *routes);\n>\n> +struct  parsed_route * parsed_routes_add_static(\n> +    const struct ovn_datapath *od,\n> +    const struct hmap *lr_ports,\n> +    const struct nbrec_logical_router_static_route *route,\n> +    const struct hmap *bfd_connections,\n> +    struct hmap *routes, struct simap *route_tables,\n> +    struct hmap *bfd_active_connections);\n> +\n>  struct svc_monitors_map_data {\n>      const struct hmap *local_svc_monitors_map;\n>      const struct hmap *ic_learned_svc_monitors_map;\n> @@ -925,7 +952,7 @@ void build_parsed_routes(const struct ovn_datapath *,\n> const struct hmap *,\n>  uint32_t get_route_table_id(struct simap *, const char *);\n>  void routes_init(struct routes_data *);\n>  void routes_destroy(struct routes_data *);\n> -\n> +void routes_clear_tracked(struct routes_data *);\n>  void bfd_init(struct bfd_data *);\n>  void bfd_destroy(struct bfd_data *);\n>\n> @@ -951,6 +978,10 @@ void build_route_data_flows_for_lrouter(\n>      const struct ovn_datapath *od, struct lflow_table *lflows,\n>      const struct group_ecmp_datapath *route_node,\n>      const struct sset *bfd_ports);\n> +void build_arp_request_flows_for_lrouter(\n> +    const struct ovn_datapath *od, struct lflow_table *lflows,\n> +    const struct shash *meter_groups,\n> +    struct lflow_ref *lflow_ref);\n>\n>  bool lflow_handle_northd_lr_changes(struct ovsdb_idl_txn *ovnsh_txn,\n>                                       struct tracked_dps *,\n> @@ -1041,6 +1072,11 @@ northd_has_lr_nats_in_tracked_data(struct\n> northd_tracked_data *trk_nd_changes)\n>  {\n>      return trk_nd_changes->type & NORTHD_TRACKED_LR_NATS;\n>  }\n> +static inline bool\n> +northd_has_lr_route_in_tracked_data(struct northd_tracked_data\n> *trk_nd_changes)\n> +{\n> +    return trk_nd_changes->type & NORTHD_TRACKED_LR_ROUTES;\n> +}\n>\n>  static inline bool\n>  northd_has_ls_lbs_in_tracked_data(struct northd_tracked_data\n> *trk_nd_changes)\n> diff --git a/tests/ovn-inc-proc-graph-dump.at b/tests/\n> ovn-inc-proc-graph-dump.at\n> index 178310978..fd05c20dc 100644\n> --- a/tests/ovn-inc-proc-graph-dump.at\n> +++ b/tests/ovn-inc-proc-graph-dump.at\n> @@ -151,9 +151,11 @@ digraph \"Incremental-Processing-Engine\" {\n>         bfd [[style=filled, shape=box, fillcolor=white, label=\"bfd\"]];\n>         NB_bfd -> bfd [[label=\"\"]];\n>         SB_bfd -> bfd [[label=\"\"]];\n> +       NB_logical_router_static_route [[style=filled, shape=box,\n> fillcolor=white, label=\"NB_logical_router_static_route\"]];\n>         routes [[style=filled, shape=box, fillcolor=white,\n> label=\"routes\"]];\n>         bfd -> routes [[label=\"\"]];\n>         northd -> routes [[label=\"routes_northd_change_handler\"]];\n> +       NB_logical_router_static_route -> routes\n> [[label=\"routes_static_route_change_handler\"]];\n>         route_policies [[style=filled, shape=box, fillcolor=white,\n> label=\"route_policies\"]];\n>         bfd -> route_policies [[label=\"\"]];\n>         northd -> route_policies\n> [[label=\"route_policies_northd_change_handler\"]];\n> @@ -168,7 +170,7 @@ digraph \"Incremental-Processing-Engine\" {\n>         SB_learned_route -> learned_route_sync\n> [[label=\"learned_route_sync_sb_learned_route_change_handler\"]];\n>         northd -> learned_route_sync\n> [[label=\"learned_route_sync_northd_change_handler\"]];\n>         group_ecmp_route [[style=filled, shape=box, fillcolor=white,\n> label=\"group_ecmp_route\"]];\n> -       routes -> group_ecmp_route [[label=\"\"]];\n> +       routes -> group_ecmp_route\n> [[label=\"group_ecmp_static_route_change_handler\"]];\n>         learned_route_sync -> group_ecmp_route\n> [[label=\"group_ecmp_route_learned_route_change_handler\"]];\n>         ls_stateful [[style=filled, shape=box, fillcolor=white,\n> label=\"ls_stateful\"]];\n>         northd -> ls_stateful [[label=\"ls_stateful_northd_handler\"]];\n> @@ -189,7 +191,7 @@ digraph \"Incremental-Processing-Engine\" {\n>         SB_logical_dp_group -> lflow [[label=\"\"]];\n>         bfd_sync -> lflow [[label=\"\"]];\n>         route_policies -> lflow [[label=\"\"]];\n> -       routes -> lflow [[label=\"\"]];\n> +       routes -> lflow [[label=\"lflow_group_route_change_handler\"]];\n>         group_ecmp_route -> lflow\n> [[label=\"lflow_group_ecmp_route_change_handler\"]];\n>         global_config -> lflow [[label=\"node_global_config_handler\"]];\n>         sampling_app -> lflow [[label=\"\"]];\n> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at\n> index 1d7bd6c28..eacccaf20 100644\n> --- a/tests/ovn-northd.at\n> +++ b/tests/ovn-northd.at\n> @@ -4272,9 +4272,9 @@ check ovn-nbctl --bfd=$uuid lr-route-add r0\n> 100.0.0.0/8 192.168.1.2\n>  wait_column down bfd status logical_port=r0-sw1\n>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],\n> [0], [], [ignore])\n>\n> -check_engine_stats northd recompute nocompute\n> +check_engine_stats northd norecompute compute\n>  check_engine_stats bfd recompute nocompute\n> -check_engine_stats routes recompute nocompute\n> +check_engine_stats routes recompute incremental\n>  check_engine_stats lflow recompute nocompute\n>  check_engine_stats northd_output norecompute compute\n>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n> @@ -4288,9 +4288,9 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8\n> 192.168.5.2 r0-sw5\n>  wait_column down bfd status logical_port=r0-sw5\n>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],\n> [0], [], [ignore])\n>\n> -check_engine_stats northd recompute nocompute\n> +check_engine_stats northd norecompute compute\n>  check_engine_stats bfd recompute nocompute\n> -check_engine_stats routes recompute nocompute\n> +check_engine_stats routes recompute incremental\n>  check_engine_stats lflow recompute nocompute\n>  check_engine_stats northd_output norecompute compute\n>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n> @@ -4300,7 +4300,7 @@ check ovn-nbctl --bfd --policy=src-ip lr-route-add\n> r0 192.168.6.1/32 192.168.10.\n>  wait_column down bfd status logical_port=r0-sw6\n>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],\n> [0], [], [ignore])\n>\n> -check_engine_stats northd recompute nocompute\n> +check_engine_stats northd norecompute compute\n>  check_engine_stats bfd recompute nocompute\n>  check_engine_stats route_policies recompute nocompute\n>  check_engine_stats lflow recompute nocompute\n> @@ -4335,10 +4335,10 @@ wait_column down bfd status logical_port=r0-sw8\n>  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)\n>  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q\n> $bfd_route_policy_uuid])\n>\n> -check_engine_stats northd recompute nocompute\n> +check_engine_stats northd recompute incremental\n>  check_engine_stats bfd recompute nocompute\n> -check_engine_stats routes recompute nocompute\n> -check_engine_stats lflow recompute nocompute\n> +check_engine_stats routes recompute incremental\n> +check_engine_stats lflow recompute incremental\n>  check_engine_stats northd_output norecompute compute\n>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> @@ -16437,12 +16437,12 @@ CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>\n>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>  check ovn-nbctl --wait=sb lr-route-add lr0 192.168.0.0/24 10.0.0.10\n> -check_engine_compute northd recompute\n> -check_engine_compute routes recompute\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n>  check_engine_compute advertised_route_sync recompute\n> -check_engine_compute learned_route_sync recompute\n> -check_engine_compute group_ecmp_route recompute\n> -check_engine_compute lflow recompute\n> +check_engine_compute learned_route_sync incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>\n>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> @@ -20672,3 +20672,75 @@ check_column \"$global_svc_mon_mac\"\n> sb:Service_Monitor src_mac port=2\n>  OVN_CLEANUP_NORTHD\n>  AT_CLEANUP\n>  ])\n> +\n> +OVN_FOR_EACH_NORTHD_NO_HV([\n> +AT_SETUP([Static Route incremental processing])\n> +ovn_start\n> +\n> +check ovn-nbctl lr-add r0\n> +\n> +check ovn-nbctl --wait=sb lrp-add r0 r0-lrp1 00:00:00:00:00:01\n> 192.168.1.1/24\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb lr-route-add r0 10.0.0.0/24 192.168.1.2\n> +\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +static_route_uuid=`ovn-nbctl --bare --columns _uuid find\n> Logical_Router_Static_Route nexthop=192.168.1.2`\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid nexthop=192.168.1.3\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid ip_prefix=10.0.1.0/24\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check ovn-nbctl --wait=sb lrp-add r0 r0-lrp2 00:00:00:00:00:02\n> 192.168.1.10/24\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid output_port=r0-lrp2\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid policy=src-ip\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid selection_fields=\"ip_proto,ip_src,ip_dst\"\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid options:ecmp_symmetric_reply=true\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl remove logical_router r0 static_routes $static_route_uuid\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +OVN_CLEANUP_NORTHD\n> +AT_CLEANUP\n> +])\n> --\n> 2.43.0\n>\n>","headers":{"Return-Path":"<ovs-dev-bounces@openvswitch.org>","X-Original-To":["incoming@patchwork.ozlabs.org","dev@openvswitch.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","ovs-dev@lists.linuxfoundation.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=KGByCm7O;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)","smtp3.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=luizalabs.com header.i=@luizalabs.com header.a=rsa-sha256\n header.s=google header.b=KGByCm7O","smtp3.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=luizalabs.com"],"Received":["from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g0yTk4HWFz1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 22 Apr 2026 22:00:26 +1000 (AEST)","from localhost (localhost [127.0.0.1])\n\tby smtp3.osuosl.org (Postfix) with ESMTP id 2A66D617A4;\n\tWed, 22 Apr 2026 12:00:25 +0000 (UTC)","from smtp3.osuosl.org ([127.0.0.1])\n by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id uxo1a9g1LfIL; Wed, 22 Apr 2026 12:00:21 +0000 (UTC)","from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])\n\tby smtp3.osuosl.org (Postfix) with ESMTPS id 1EF7D61797;\n\tWed, 22 Apr 2026 12:00:21 +0000 (UTC)","from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id D6264C0591;\n\tWed, 22 Apr 2026 12:00:20 +0000 (UTC)","from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 06557C058E\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 12:00:18 +0000 (UTC)","from localhost (localhost [127.0.0.1])\n by smtp3.osuosl.org (Postfix) with ESMTP id 4F1356179A\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 12:00:16 +0000 (UTC)","from smtp3.osuosl.org ([127.0.0.1])\n by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id VNG1uzsCTtnX for <dev@openvswitch.org>;\n Wed, 22 Apr 2026 12:00:13 +0000 (UTC)","from mail-oi1-x22b.google.com (mail-oi1-x22b.google.com\n [IPv6:2607:f8b0:4864:20::22b])\n by smtp3.osuosl.org (Postfix) with ESMTPS id 9FBBD61793\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 12:00:12 +0000 (UTC)","by mail-oi1-x22b.google.com with SMTP id\n 5614622812f47-479e6bc357eso1042254b6e.2\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 05:00:12 -0700 (PDT)"],"X-Virus-Scanned":["amavis at osuosl.org","amavis at osuosl.org"],"X-Comment":"SPF check N/A for local connections - client-ip=140.211.9.56;\n helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ","DKIM-Filter":["OpenDKIM Filter v2.11.0 smtp3.osuosl.org 1EF7D61797","OpenDKIM Filter v2.11.0 smtp3.osuosl.org 9FBBD61793"],"Received-SPF":"Pass (mailfrom) identity=mailfrom;\n client-ip=2607:f8b0:4864:20::22b; helo=mail-oi1-x22b.google.com;\n envelope-from=lucas.vdias@luizalabs.com; receiver=<UNKNOWN>","DMARC-Filter":"OpenDMARC Filter v1.4.2 smtp3.osuosl.org 9FBBD61793","ARC-Seal":"i=1; a=rsa-sha256; t=1776859211; cv=none;\n d=google.com; s=arc-20240605;\n b=XGUzyttfqW5AYK+EPUbDdRbZuRfhymAT42sxrXrxUu3C0sD7gVWU5zB5wqszHMAdV5\n 1kRCfZ1Lu8zSOXz6E2aZedOMfmTIRC4szGljdxA3yNI6Q7sAAVXJ9J41jsRussHz+TP+\n TJtUpPfgK2MY4ENlSB+/7as1V6+yuL0Q+kh6dnRyDgGodxIDaCkaCTf8YBW9tGQ6R2G1\n 9MSN0t19QI/wxTyjJgYzlxxPk44mV7tsCcoJoAfa6d//nDCvbAE4SqzHd5BXIAofiTz9\n 6qnAPcIc2G8cRoOZ1FigLEkGxFFPp/9KniyTv6vN/z4/M1F8WYMRiRawE6vjr7a6keM7\n 7lFw==","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com;\n s=arc-20240605;\n h=to:subject:message-id:date:from:in-reply-to:references:mime-version\n :dkim-signature;\n bh=gIFKp4yDrAbMYRONiLNvEpEjwyT3TGm57bsHqKahQYk=;\n fh=WnO6tfRXfzNkmVvpHWPU0Sn0nugAh6D8VsZG77w0RnY=;\n b=TJBOHx8VwakK5t0Itm0HjFEXCStJhQu9SttAVEc9ahim7YAziK6Mns9T6tvVNTVGSQ\n 5N/1VAu8q/MZxfKywp1NWqPCYPYom2gm2UNn2otLU7oTnKVnnul+gUkmwvYfUKaTocA7\n KT+bNu7oFki/FJC6zkT2l/Z87iqAKzwwLd2/agRLyj++hJoj32J/z4eQ0bH9wG6dmiUT\n 66tfzT1nVY7hg+PsKuHlCphlUrG98BZ9drrIu8wOD6T8PTmwqMS9rVh4kQ9PCU1xojfP\n N4I6CGf/7D/NfoaAdSY/uZKmyZrRVX6Us+hyP+VJc6abGPEIURquK00fpAP6lmlGPFGv\n frbg==; darn=openvswitch.org","ARC-Authentication-Results":"i=1; mx.google.com; arc=none","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=luizalabs.com; s=google; t=1776859211; x=1777464011; darn=openvswitch.org;\n h=to:subject:message-id:date:from:in-reply-to:references:mime-version\n :from:to:cc:subject:date:message-id:reply-to;\n bh=gIFKp4yDrAbMYRONiLNvEpEjwyT3TGm57bsHqKahQYk=;\n b=KGByCm7O3Iw/MEiKw7C2rMmXwjgebfL3FJc+3ODEiFGWkHR/HJMAZloPT1vpsASIxr\n tGS62TbkkmZA539JhSKu1QwlC5r3fBD9AeMPgoPD7ihE3Otwgl9yBHYj7AY4yRHubEgF\n wdrQaHNuqSodJNDTyP8rMXqvD1zXxmYlwBIpg=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776859211; x=1777464011;\n h=to:subject:message-id:date:from:in-reply-to:references:mime-version\n :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id\n :reply-to;\n bh=gIFKp4yDrAbMYRONiLNvEpEjwyT3TGm57bsHqKahQYk=;\n b=Nx3wjetJpjs4BQkB1T8m3ITfCc4lF/T+LH7CFnDMSUPzfYAgbGI3fOp54XiYok0LuM\n fcoXNS4b+8XV2TTbGvzbxJi8cdT0cUcsgpKMy+tXGxcH7OWgvFnP+ZSZwh8VSDXK2mQw\n PuC4UaRPY0maqNSsMpO9VXCmF0yiLEcCSSqibcs8V2goHYmIhd9INflT8AVgdDMOlSKs\n wu8bY2QDwtDWrzpKQQsSCke28YwUmuT8VlSMoYkSFXZ8Ls/flBLJNNbnscIr1PwVSB3/\n 2ZCORKJKllnYJBhtQIGwdcIxrtcZ4VI6kvGfdc7KMJJoLefGgorNCEXj5iwyZOD55jZq\n Yn2Q==","X-Gm-Message-State":"AOJu0Yxyu5v8MOHa7tKIDkYVtisT0LDR++yW96N/cMRnjznSyewYhiXY\n ABoh7x0+Wr3H/jPtdN0GFdGnlKyVjBCZOeVXO3wA8n5B5ReH3KDz8i9p7ufBK3auijQYjr1h6Wa\n ho/GiaTtu2L1fUxRU+4sSLqMdY/Y1kQ0bVoIuGo3t7tHFtEzRMO3Ig0FBqCj3njaaOhf+y75K2Y\n eqK89y9YkVhj0Imd969ec+H+8qxkyOOPPjwIo=","X-Gm-Gg":"AeBDietwmWUf1A2cUD/m7aZryBz+j9JHhs8yhkWWM87gdGuEOwDxVzsYe/e5lKg+nIl\n //+qsoQM9ikQgfvVN1YaIp04cw+Jy2HpgFq+hZwa2Z+4D/UnCEoCCDqbAzNVw5isO1pb8tctwVC\n f1sRtaUyMz50ET3tLxRfqSqkAY+lo1JnMv5fyWHIMk0d+sKWThI38wedUcOCT9ys739HvQtxcMg\n kcsVKEJoeGbxU8xaW7j0TgU8hGkkJdoH+MOyd5tgKgmks5hHGkRSGHyTeDYt9lHENg1o1ibwMNr\n tnUHJUDhFGye8g6UIewb","X-Received":"by 2002:a05:6808:19a6:b0:466:f57b:2ad2 with SMTP id\n 5614622812f47-4799c9a833amr11450553b6e.31.1776859210561; Wed, 22 Apr 2026\n 05:00:10 -0700 (PDT)","MIME-Version":"1.0","References":"<20260421211209.1974247-1-lucas.vdias@luizalabs.com>","In-Reply-To":"<20260421211209.1974247-1-lucas.vdias@luizalabs.com>","Date":"Wed, 22 Apr 2026 08:59:59 -0300","X-Gm-Features":"AQROBzAxEYbruDKGyUWRi9Pzb0NDapv05Sk4QkvEVb6GsghdVAtEhYmp1oSNQ4I","Message-ID":"\n <CACAUutOFjZ5gLB-8a83sc-1hj7Z9zHXmfopuNZLNRT_rofAfyw@mail.gmail.com>","To":"dev@openvswitch.org","X-Content-Filtered-By":"Mailman/MimeDel 2.1.30","Subject":"Re: [ovs-dev] [PATCH ovn v2 2/2] northd: Incremental processing for\n static routes.","X-BeenThere":"ovs-dev@openvswitch.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"<ovs-dev.openvswitch.org>","List-Unsubscribe":"<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>","List-Archive":"<http://mail.openvswitch.org/pipermail/ovs-dev/>","List-Post":"<mailto:ovs-dev@openvswitch.org>","List-Help":"<mailto:ovs-dev-request@openvswitch.org?subject=help>","List-Subscribe":"<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>","From":"Lucas Vargas Dias via dev <ovs-dev@openvswitch.org>","Reply-To":"Lucas Vargas Dias <lucas.vdias@luizalabs.com>","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"ovs-dev-bounces@openvswitch.org","Sender":"\"dev\" <ovs-dev-bounces@openvswitch.org>"}},{"id":3680643,"web_url":"http://patchwork.ozlabs.org/comment/3680643/","msgid":"<CACAUutNxtGY2qvZm45QwW+i1Y5HEcmicSag6dfPa+gK21MjoGw@mail.gmail.com>","list_archive_url":null,"date":"2026-04-22T14:04:20","subject":"Re: [ovs-dev] [PATCH ovn v2 2/2] northd: Incremental processing for\n static routes.","submitter":{"id":90169,"url":"http://patchwork.ozlabs.org/api/people/90169/","name":"Lucas Vargas Dias","email":"lucas.vdias@luizalabs.com"},"content":"Recheck-request: github-robot\n\nEm ter., 21 de abr. de 2026 às 18:12, Lucas Vargas Dias <\nlucas.vdias@luizalabs.com> escreveu:\n\n> Create a handler for deleted and updated static routes,\n> and change the handler from engine route which check if\n> it has a new static route created.\n> Test with 2000 static routes created in the same logical router\n> and add a new one:\n> Without the incremental processing:\n> ovn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32\n> 192.168.20.2\n> Time spent on processing nb_cfg 4:\n>         ovn-northd delay before processing:     4ms\n>         ovn-northd completion:                  62ms\n>\n> With the incremental processing:\n> ovn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32\n> 192.168.20.2\n> Time spent on processing nb_cfg 6:\n>         ovn-northd delay before processing:     4ms\n>         ovn-northd completion:                  21ms\n>\n> Test with 2000 static routes created in the same logical router\n> and delete one:\n> Without the incremental processing:\n> ovn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32\n> 192.168.20.2\n> Time spent on processing nb_cfg 5:\n>         ovn-northd delay before processing:     3ms\n>         ovn-northd completion:                  62ms\n>\n> With the incremental processing:\n> ovn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32\n> 192.168.20.2\n> Time spent on processing nb_cfg 9:\n>         ovn-northd delay before processing:     2ms\n>         ovn-northd completion:                  32ms\n>\n> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>\n> ---\n>  northd/en-group-ecmp-route.c     |  57 ++++++++++\n>  northd/en-group-ecmp-route.h     |   4 +\n>  northd/en-lflow.c                |  20 ++++\n>  northd/en-lflow.h                |   2 +\n>  northd/en-northd.c               | 186 +++++++++++++++++++++++++++----\n>  northd/en-northd.h               |   5 +-\n>  northd/inc-proc-northd.c         |  13 ++-\n>  northd/northd.c                  | 107 +++++++++++++-----\n>  northd/northd.h                  |  38 ++++++-\n>  tests/ovn-inc-proc-graph-dump.at |   6 +-\n>  tests/ovn-northd.at              |  98 +++++++++++++---\n>  11 files changed, 466 insertions(+), 70 deletions(-)\n>\n> diff --git a/northd/en-group-ecmp-route.c b/northd/en-group-ecmp-route.c\n> index c4c93fd84..fcc76b076 100644\n> --- a/northd/en-group-ecmp-route.c\n> +++ b/northd/en-group-ecmp-route.c\n> @@ -519,3 +519,60 @@ group_ecmp_route_learned_route_change_handler(struct\n> engine_node *eng_node,\n>      }\n>      return EN_HANDLED_UNCHANGED;\n>  }\n> +\n> +enum engine_input_handler_result\n> +group_ecmp_static_route_change_handler(struct engine_node *eng_node,\n> +                                       void *_data)\n> +{\n> +    struct routes_data *routes_data\n> +        = engine_get_input_data(\"routes\", eng_node);\n> +    struct group_ecmp_route_data *data = _data;\n> +    if (!routes_data->tracked) {\n> +        data->tracked = false;\n> +        return EN_UNHANDLED;\n> +    }\n> +\n> +    struct parsed_route *pr;\n> +    struct hmapx updated_routes = HMAPX_INITIALIZER(&updated_routes);\n> +\n> +    const struct hmapx_node *hmapx_node;\n> +    HMAPX_FOR_EACH (hmapx_node,\n> +                    &routes_data->trk_data.trk_deleted_parsed_route) {\n> +        pr = hmapx_node->data;\n> +        if (!handle_deleted_route(data, pr, &updated_routes)) {\n> +            hmapx_destroy(&updated_routes);\n> +            return EN_UNHANDLED;\n> +        }\n> +\n> +        if (pr->is_in_parsed_routes) {\n> +            hmap_remove(&routes_data->parsed_routes, &pr->key_node);\n> +        }\n> +        parsed_route_free(pr);\n> +    }\n> +\n> +    HMAPX_FOR_EACH (hmapx_node,\n> +                    &routes_data->trk_data.trk_crupdated_parsed_route) {\n> +        pr = hmapx_node->data;\n> +        handle_added_route(data, pr, &updated_routes);\n> +    }\n> +\n> +    HMAPX_FOR_EACH (hmapx_node, &updated_routes) {\n> +        struct group_ecmp_datapath *node = hmapx_node->data;\n> +        if (hmap_is_empty(&node->unique_routes) &&\n> +                hmap_is_empty(&node->ecmp_groups)) {\n> +            hmapx_add(&data->trk_data.deleted_datapath_routes, node);\n> +            hmap_remove(&data->datapaths, &node->hmap_node);\n> +        } else {\n> +            hmapx_add(&data->trk_data.crupdated_datapath_routes, node);\n> +        }\n> +    }\n> +\n> +    hmapx_destroy(&updated_routes);\n> +\n> +    if (!hmapx_is_empty(&data->trk_data.crupdated_datapath_routes) ||\n> +        !hmapx_is_empty(&data->trk_data.deleted_datapath_routes)) {\n> +        data->tracked = true;\n> +        return EN_HANDLED_UPDATED;\n> +    }\n> +    return EN_HANDLED_UNCHANGED;\n> +}\n> diff --git a/northd/en-group-ecmp-route.h b/northd/en-group-ecmp-route.h\n> index d4a3248d0..246ca06bf 100644\n> --- a/northd/en-group-ecmp-route.h\n> +++ b/northd/en-group-ecmp-route.h\n> @@ -98,6 +98,10 @@ enum engine_input_handler_result\n>  group_ecmp_route_learned_route_change_handler(struct engine_node *,\n>                                                void *data);\n>\n> +enum engine_input_handler_result\n> +group_ecmp_static_route_change_handler(struct engine_node *,\n> +                                       void *data);\n> +\n>  struct group_ecmp_datapath *group_ecmp_datapath_lookup(\n>      const struct group_ecmp_route_data *data,\n>      const struct ovn_datapath *od);\n> diff --git a/northd/en-lflow.c b/northd/en-lflow.c\n> index d4351edb9..22cd8fe91 100644\n> --- a/northd/en-lflow.c\n> +++ b/northd/en-lflow.c\n> @@ -297,6 +297,21 @@ lflow_multicast_igmp_handler(struct engine_node\n> *node, void *data)\n>      return EN_HANDLED_UPDATED;\n>  }\n>\n> +enum engine_input_handler_result\n> +lflow_group_route_change_handler(struct engine_node *node,\n> +                                      void *data OVS_UNUSED)\n> +{\n> +    struct routes_data *route_data =\n> +        engine_get_input_data(\"routes\", node);\n> +\n> +    /* If we do not have tracked data we need to recompute. */\n> +    if (!route_data->tracked) {\n> +        return EN_UNHANDLED;\n> +    }\n> +\n> +    return EN_HANDLED_UNCHANGED;\n> +}\n> +\n>  enum engine_input_handler_result\n>  lflow_group_ecmp_route_change_handler(struct engine_node *node,\n>                                        void *data OVS_UNUSED)\n> @@ -346,6 +361,11 @@ lflow_group_ecmp_route_change_handler(struct\n> engine_node *node,\n>              route_node->od, lflow_data->lflow_table,\n>              route_node, lflow_input.bfd_ports);\n>\n> +        build_arp_request_flows_for_lrouter(route_node->od,\n> +                                            lflow_data->lflow_table,\n> +                                            lflow_input.meter_groups,\n> +                                            route_node->lflow_ref);\n> +\n>          bool handled = lflow_ref_sync_lflows(\n>              route_node->lflow_ref, lflow_data->lflow_table,\n>              eng_ctx->ovnsb_idl_txn, lflow_input.dps,\n> diff --git a/northd/en-lflow.h b/northd/en-lflow.h\n> index d2a92e49f..aa320615f 100644\n> --- a/northd/en-lflow.h\n> +++ b/northd/en-lflow.h\n> @@ -31,5 +31,7 @@ lflow_multicast_igmp_handler(struct engine_node *node,\n> void *data);\n>  enum engine_input_handler_result\n>  lflow_group_ecmp_route_change_handler(struct engine_node *node, void\n> *data);\n>  enum engine_input_handler_result\n> +lflow_group_route_change_handler(struct engine_node *node, void *data);\n> +enum engine_input_handler_result\n>  lflow_ic_learned_svc_mons_handler(struct engine_node *node, void *data);\n>  #endif /* EN_LFLOW_H */\n> diff --git a/northd/en-northd.c b/northd/en-northd.c\n> index c34818dba..c05939a1d 100644\n> --- a/northd/en-northd.c\n> +++ b/northd/en-northd.c\n> @@ -207,7 +207,8 @@ northd_nb_logical_router_handler(struct engine_node\n> *node,\n>      }\n>\n>      if (northd_has_lr_nats_in_tracked_data(&nd->trk_data) ||\n> -        northd_has_lrouters_in_tracked_data(&nd->trk_data)) {\n> +        northd_has_lrouters_in_tracked_data(&nd->trk_data) ||\n> +        northd_has_lr_route_in_tracked_data(&nd->trk_data)) {\n>          return EN_HANDLED_UPDATED;\n>      }\n>\n> @@ -329,32 +330,174 @@ en_route_policies_run(struct engine_node *node,\n> void *data)\n>\n>  enum engine_input_handler_result\n>  routes_northd_change_handler(struct engine_node *node,\n> -                                    void *data OVS_UNUSED)\n> +                                    void *data)\n>  {\n>      struct northd_data *northd_data = engine_get_input_data(\"northd\",\n> node);\n>      if (!northd_has_tracked_data(&northd_data->trk_data)) {\n>          return EN_UNHANDLED;\n>      }\n>\n> -    /* This node uses the below data from the en_northd engine node.\n> -     * See (lr_stateful_get_input_data())\n> -     *   1. northd_data->lr_datapaths\n> -     *   2. northd_data->lr_ports\n> -     *      This data gets updated when a logical router or logical\n> router port\n> -     *      is created or deleted.\n> -     *      Northd engine node presently falls back to full recompute when\n> -     *      this happens and so does this node.\n> -     *      Note: When we add I-P to the created/deleted logical routers\n> or\n> -     *      logical router ports, we need to revisit this handler.\n> -     *\n> -     *      This node also accesses the static routes of the logical\n> router.\n> -     *      When these static routes gets updated, en_northd engine\n> recomputes\n> -     *      and so does this node.\n> -     *      Note: When we add I-P to handle static routes changes, we need\n> -     *      to revisit this handler.\n> -     */\n> +    if (!northd_has_lr_route_in_tracked_data(&northd_data->trk_data)) {\n> +        return EN_HANDLED_UNCHANGED;\n> +    }\n> +\n> +    struct bfd_data *bfd_data = engine_get_input_data(\"bfd\", node);\n> +    struct routes_data *routes_data = data;\n> +    struct hmapx_node *hmapx_node;\n> +    struct ovn_datapath *od;\n> +    HMAPX_FOR_EACH (hmapx_node, &northd_data->trk_data.trk_lrs_routes) {\n> +        od = hmapx_node->data;\n> +        struct parsed_route *pr;\n> +\n> +        for (int i = 0; i < od->nbr->n_static_routes; i++) {\n> +            struct nbrec_logical_router_static_route *static_route =\n> +                od->nbr->static_routes[i];\n> +            pr = parsed_route_lookup_by_source(ROUTE_SOURCE_STATIC,\n> +                                               &static_route->header_,\n> +\n>  &routes_data->parsed_routes);\n> +            if (pr) {\n> +                pr->stale = false;\n> +                continue;\n> +            }\n> +            pr = parsed_routes_add_static(od, &northd_data->lr_ports,\n> +                                        static_route,\n> +                                        &bfd_data->bfd_connections,\n> +                                        &routes_data->parsed_routes,\n> +                                        &routes_data->route_tables,\n> +\n> &routes_data->bfd_active_connections);\n> +            if (!pr) {\n> +                continue;\n> +            }\n> +            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,\n> +                      pr);\n> +        }\n> +    }\n> +\n> +    if\n> (!hmapx_is_empty(&routes_data->trk_data.trk_crupdated_parsed_route)) {\n> +        routes_data->tracked = true;\n> +        return EN_HANDLED_UPDATED;\n> +    }\n> +\n>      return EN_HANDLED_UNCHANGED;\n>  }\n> +enum engine_input_handler_result\n> +routes_static_route_change_handler(struct engine_node *node,\n> +                                   void *data)\n> +{\n> +    struct routes_data *routes_data = data;\n> +    struct hmapx created_trk_parsed_route =\n> +        HMAPX_INITIALIZER(&created_trk_parsed_route);\n> +    const struct nbrec_logical_router_static_route_table *\n> +      nb_lr_static_route_table =\n> +    EN_OVSDB_GET(engine_get_input(\"NB_logical_router_static_route\",\n> node));\n> +\n> +    struct northd_data *northd_data = engine_get_input_data(\"northd\",\n> node);\n> +    struct bfd_data *bfd_data = engine_get_input_data(\"bfd\", node);\n> +\n> +    const struct nbrec_logical_router_static_route *changed_static_route;\n> +    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_TABLE_FOR_EACH_TRACKED (\n> +                            changed_static_route,\n> nb_lr_static_route_table) {\n> +\n> +        bool is_deleted = nbrec_logical_router_static_route_is_deleted(\n> +\n> changed_static_route);\n> +        bool is_new = nbrec_logical_router_static_route_is_new(\n> +\n> changed_static_route);\n> +\n> +        if (is_new && is_deleted) {\n> +            continue;\n> +        }\n> +\n> +        if (is_new) {\n> +            hmapx_add(&created_trk_parsed_route, &changed_static_route);\n> +            continue;\n> +        }\n> +\n> +        if (is_deleted) {\n> +            struct parsed_route *pr = parsed_route_lookup_by_source(\n> +                                            ROUTE_SOURCE_STATIC,\n> +\n> &changed_static_route->header_,\n> +                                            &routes_data->parsed_routes);\n> +            if (!pr) {\n> +                pr =\n> parsed_route_lookup_by_source(ROUTE_SOURCE_IC_DYNAMIC,\n> +\n> &changed_static_route->header_,\n> +                                            &routes_data->parsed_routes);\n> +            }\n> +            if (pr) {\n> +                pr->stale = true;\n> +\n> hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route, pr);\n> +            }\n> +            continue;\n> +        }\n> +\n> +        if (nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_NEXTHOP)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_IP_PREFIX)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OUTPUT_PORT)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_POLICY)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_ROUTE_TABLE)\n> +            || nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +\n> NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_SELECTION_FIELDS)\n> +            ||  nbrec_logical_router_static_route_is_updated(\n> +                    changed_static_route,\n> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OPTIONS)) {\n> +            struct parsed_route *pr = parsed_route_lookup_by_source(\n> +                                            ROUTE_SOURCE_STATIC,\n> +\n> &changed_static_route->header_,\n> +                                            &routes_data->parsed_routes);\n> +            if (!pr) {\n> +                pr = parsed_route_lookup_by_source(\n> +                                                ROUTE_SOURCE_IC_DYNAMIC,\n> +\n> &changed_static_route->header_,\n> +\n> &routes_data->parsed_routes);\n> +            }\n> +\n> +            if (!pr || !pr->od || !northd_data || !bfd_data) {\n> +                continue;\n> +            }\n> +            struct parsed_route *old_pr = pr;\n> +            hmap_remove(&routes_data->parsed_routes, &old_pr->key_node);\n> +            pr = parsed_routes_add_static(old_pr->od,\n> &northd_data->lr_ports,\n> +                                        changed_static_route,\n> +                                        &bfd_data->bfd_connections,\n> +                                        &routes_data->parsed_routes,\n> +                                        &routes_data->route_tables,\n> +\n> &routes_data->bfd_active_connections);\n> +            old_pr->is_in_parsed_routes = false;\n> +            if (!pr) {\n> +                continue;\n> +            }\n> +\n> +            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,\n> +                       pr);\n> +            hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route,\n> +                      old_pr);\n> +        }\n> +    }\n> +    if\n> (!hmapx_is_empty(&routes_data->trk_data.trk_crupdated_parsed_route) ||\n> +        !hmapx_is_empty(&routes_data->trk_data.trk_deleted_parsed_route))\n> {\n> +        hmapx_destroy(&created_trk_parsed_route);\n> +        routes_data->tracked = true;\n> +        return EN_HANDLED_UPDATED;\n> +    }\n> +\n> +    if (!hmapx_is_empty(&created_trk_parsed_route)) {\n> +        hmapx_destroy(&created_trk_parsed_route);\n> +        return EN_HANDLED_UPDATED;\n> +    }\n> +\n> +    hmapx_destroy(&created_trk_parsed_route);\n> +    return EN_UNHANDLED;\n> +}\n>\n>  enum engine_node_state\n>  en_routes_run(struct engine_node *node, void *data)\n> @@ -590,6 +733,11 @@ en_routes_cleanup(void *data)\n>      routes_destroy(data);\n>  }\n>\n> +void\n> +en_routes_clear_tracked_data(void *data)\n> +{\n> +    routes_clear_tracked(data);\n> +}\n>  void\n>  en_bfd_cleanup(void *data)\n>  {\n> diff --git a/northd/en-northd.h b/northd/en-northd.h\n> index 7794739b9..5247f3e11 100644\n> --- a/northd/en-northd.h\n> +++ b/northd/en-northd.h\n> @@ -39,9 +39,12 @@ enum engine_node_state en_route_policies_run(struct\n> engine_node *node,\n>                                               void *data);\n>  void *en_route_policies_init(struct engine_node *node OVS_UNUSED,\n>                               struct engine_arg *arg OVS_UNUSED);\n> +void en_routes_clear_tracked_data(void *data);\n>  void en_routes_cleanup(void *data);\n>  enum engine_input_handler_result\n> -routes_northd_change_handler(struct engine_node *node, void *data\n> OVS_UNUSED);\n> +routes_northd_change_handler(struct engine_node *node, void *data);\n> +enum engine_input_handler_result\n> +routes_static_route_change_handler(struct engine_node *node, void *data);\n>  enum engine_node_state en_routes_run(struct engine_node *node, void\n> *data);\n>  void *en_bfd_init(struct engine_node *node OVS_UNUSED,\n>                    struct engine_arg *arg OVS_UNUSED);\n> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c\n> index ece388ce7..52f5dc57f 100644\n> --- a/northd/inc-proc-northd.c\n> +++ b/northd/inc-proc-northd.c\n> @@ -76,7 +76,9 @@ static unixctl_cb_func chassis_features_list;\n>      NB_NODE(sampling_app) \\\n>      NB_NODE(network_function) \\\n>      NB_NODE(network_function_group) \\\n> -    NB_NODE(logical_switch_port_health_check)\n> +    NB_NODE(logical_switch_port_health_check) \\\n> +    NB_NODE(logical_router_static_route)\n> +\n>\n>      enum nb_engine_node {\n>  #define NB_NODE(NAME) NB_##NAME,\n> @@ -179,7 +181,7 @@ static ENGINE_NODE(lr_stateful, CLEAR_TRACKED_DATA);\n>  static ENGINE_NODE(ls_stateful, CLEAR_TRACKED_DATA);\n>  static ENGINE_NODE(ls_arp, CLEAR_TRACKED_DATA);\n>  static ENGINE_NODE(route_policies);\n> -static ENGINE_NODE(routes);\n> +static ENGINE_NODE(routes, CLEAR_TRACKED_DATA);\n>  static ENGINE_NODE(bfd);\n>  static ENGINE_NODE(bfd_sync, SB_WRITE);\n>  static ENGINE_NODE(ecmp_nexthop, SB_WRITE);\n> @@ -341,6 +343,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n>      engine_add_input(&en_routes, &en_bfd, NULL);\n>      engine_add_input(&en_routes, &en_northd,\n>                       routes_northd_change_handler);\n> +    engine_add_input(&en_routes, &en_nb_logical_router_static_route,\n> +                     routes_static_route_change_handler);\n>\n>      engine_add_input(&en_bfd_sync, &en_bfd, NULL);\n>      engine_add_input(&en_bfd_sync, &en_nb_bfd, NULL);\n> @@ -380,7 +384,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n>      engine_add_input(&en_learned_route_sync, &en_northd,\n>                       learned_route_sync_northd_change_handler);\n>\n> -    engine_add_input(&en_group_ecmp_route, &en_routes, NULL);\n> +    engine_add_input(&en_group_ecmp_route, &en_routes,\n> +                     group_ecmp_static_route_change_handler);\n>      engine_add_input(&en_group_ecmp_route, &en_learned_route_sync,\n>                       group_ecmp_route_learned_route_change_handler);\n>\n> @@ -399,7 +404,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n>      engine_add_input(&en_lflow, &en_sb_logical_dp_group, NULL);\n>      engine_add_input(&en_lflow, &en_bfd_sync, NULL);\n>      engine_add_input(&en_lflow, &en_route_policies, NULL);\n> -    engine_add_input(&en_lflow, &en_routes, NULL);\n> +    engine_add_input(&en_lflow, &en_routes,\n> lflow_group_route_change_handler);\n>      /* XXX: The incremental processing only supports changes to learned\n> routes.\n>       * All other changes trigger a full recompute. */\n>      engine_add_input(&en_lflow, &en_group_ecmp_route,\n> diff --git a/northd/northd.c b/northd/northd.c\n> index 4fd4b9de9..ea48bc442 100644\n> --- a/northd/northd.c\n> +++ b/northd/northd.c\n> @@ -4517,6 +4517,7 @@ destroy_northd_data_tracked_changes(struct\n> northd_data *nd)\n>      destroy_tracked_ovn_ports(&trk_changes->trk_lsps);\n>      destroy_tracked_lbs(&trk_changes->trk_lbs);\n>      hmapx_clear(&trk_changes->trk_nat_lrs);\n> +    hmapx_clear(&trk_changes->trk_lrs_routes);\n>      hmapx_clear(&trk_changes->ls_with_changed_lbs);\n>      hmapx_clear(&trk_changes->ls_with_changed_acls);\n>      hmapx_clear(&trk_changes->ls_with_changed_ipam);\n> @@ -4540,6 +4541,7 @@ init_northd_tracked_data(struct northd_data *nd)\n>      hmapx_init(&trk_data->trk_lbs.crupdated);\n>      hmapx_init(&trk_data->trk_lbs.deleted);\n>      hmapx_init(&trk_data->trk_nat_lrs);\n> +    hmapx_init(&trk_data->trk_lrs_routes);\n>      hmapx_init(&trk_data->ls_with_changed_lbs);\n>      hmapx_init(&trk_data->ls_with_changed_acls);\n>      hmapx_init(&trk_data->ls_with_changed_ipam);\n> @@ -4558,6 +4560,7 @@ destroy_northd_tracked_data(struct northd_data *nd)\n>      hmapx_destroy(&trk_data->trk_lbs.crupdated);\n>      hmapx_destroy(&trk_data->trk_lbs.deleted);\n>      hmapx_destroy(&trk_data->trk_nat_lrs);\n> +    hmapx_destroy(&trk_data->trk_lrs_routes);\n>      hmapx_destroy(&trk_data->ls_with_changed_lbs);\n>      hmapx_destroy(&trk_data->ls_with_changed_acls);\n>      hmapx_destroy(&trk_data->ls_with_changed_ipam);\n> @@ -5379,7 +5382,8 @@ lr_changes_can_be_handled(const struct\n> nbrec_logical_router *lr)\n>          if (nbrec_logical_router_is_updated(lr, col)) {\n>              if (col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER\n>                  || col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER_GROUP\n> -                || col == NBREC_LOGICAL_ROUTER_COL_NAT) {\n> +                || col == NBREC_LOGICAL_ROUTER_COL_NAT\n> +                || col == NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES) {\n>                  continue;\n>              }\n>              return false;\n> @@ -5404,12 +5408,7 @@ lr_changes_can_be_handled(const struct\n> nbrec_logical_router *lr)\n>              return false;\n>          }\n>      }\n> -    for (size_t i = 0; i < lr->n_static_routes; i++) {\n> -        if (nbrec_logical_router_static_route_row_get_seqno(\n> -            lr->static_routes[i], OVSDB_IDL_CHANGE_MODIFY) > 0) {\n> -            return false;\n> -        }\n> -    }\n> +\n>      return true;\n>  }\n>\n> @@ -5435,6 +5434,13 @@ is_lr_nats_changed(const struct\n> nbrec_logical_router *nbr) {\n>              || is_lr_nats_seqno_changed(nbr));\n>  }\n>\n> +static bool\n> +is_lr_static_routes_changed(const struct nbrec_logical_router *nbr) {\n> +    return nbrec_logical_router_is_updated(nbr,\n> +\n>  NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES);\n> +}\n> +\n> +\n>  /* Return true if changes are handled incrementally, false otherwise.\n>   *\n>   * Note: Changes to load balancer and load balancer groups associated with\n> @@ -5503,6 +5509,22 @@ northd_handle_lr_changes(const struct northd_input\n> *ni,\n>\n>              hmapx_add(&nd->trk_data.trk_nat_lrs, od);\n>          }\n> +\n> +        /* Static Route was added or deleted. */\n> +        if (is_lr_static_routes_changed(changed_lr)) {\n> +            struct ovn_datapath *od = ovn_datapath_find_(\n> +                                    &nd->lr_datapaths.datapaths,\n> +                                    &changed_lr->header_.uuid);\n> +\n> +            if (!od) {\n> +                static struct vlog_rate_limit rl =\n> VLOG_RATE_LIMIT_INIT(1, 1);\n> +                VLOG_WARN_RL(&rl, \"Internal error: a tracked updated LR \"\n> +                            \"doesn't exist in lr_datapaths: \"UUID_FMT,\n> +                            UUID_ARGS(&changed_lr->header_.uuid));\n> +                goto fail;\n> +            }\n> +            hmapx_add(&nd->trk_data.trk_lrs_routes, od);\n> +        }\n>      }\n>\n>      HMAPX_FOR_EACH (node, &ni->synced_lrs->deleted) {\n> @@ -5543,6 +5565,9 @@ northd_handle_lr_changes(const struct northd_input\n> *ni,\n>      if (!hmapx_is_empty(&nd->trk_data.trk_nat_lrs)) {\n>          nd->trk_data.type |= NORTHD_TRACKED_LR_NATS;\n>      }\n> +    if (!hmapx_is_empty(&nd->trk_data.trk_lrs_routes)) {\n> +        nd->trk_data.type |= NORTHD_TRACKED_LR_ROUTES;\n> +    }\n>      if (!hmapx_is_empty(&nd->trk_data.trk_routers.crupdated) ||\n>          !hmapx_is_empty(&nd->trk_data.trk_routers.deleted)) {\n>          nd->trk_data.type |= NORTHD_TRACKED_ROUTERS;\n> @@ -12189,6 +12214,7 @@ parsed_route_init(const struct ovn_datapath *od,\n>      new_pr->route_table_id = route_table_id;\n>      new_pr->is_src_route = is_src_route;\n>      new_pr->od = od;\n> +    new_pr->is_in_parsed_routes = false;\n>      new_pr->ecmp_symmetric_reply = ecmp_symmetric_reply;\n>      new_pr->is_discard_route = is_discard_route;\n>      new_pr->lrp_addr_s = nullable_xstrdup(lrp_addr_s);\n> @@ -12296,6 +12322,7 @@ parsed_route_add(const struct ovn_datapath *od,\n>      struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);\n>      if (!pr) {\n>          hmap_insert(routes, &new_pr->key_node, hash);\n> +        new_pr->is_in_parsed_routes = true;\n>          return new_pr;\n>      } else {\n>          pr->stale = false;\n> @@ -12304,7 +12331,7 @@ parsed_route_add(const struct ovn_datapath *od,\n>      }\n>  }\n>\n> -static void\n> +struct parsed_route *\n>  parsed_routes_add_static(const struct ovn_datapath *od,\n>                           const struct hmap *lr_ports,\n>                           const struct nbrec_logical_router_static_route\n> *route,\n> @@ -12325,8 +12352,9 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                           UUID_FMT, route->nexthop,\n>                           UUID_ARGS(&route->header_.uuid));\n>              free(nexthop);\n> -            return;\n> +            return NULL;\n>          }\n> +\n>          if ((IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 32) ||\n>              (!IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 128)) {\n>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n> @@ -12334,7 +12362,7 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                           UUID_FMT, route->nexthop,\n>                           UUID_ARGS(&route->header_.uuid));\n>              free(nexthop);\n> -            return;\n> +            return NULL;\n>          }\n>      }\n>\n> @@ -12346,7 +12374,7 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                       UUID_FMT, route->ip_prefix,\n>                       UUID_ARGS(&route->header_.uuid));\n>          free(nexthop);\n> -        return;\n> +        return NULL;\n>      }\n>\n>      /* Verify that ip_prefix and nexthop are on the same network. */\n> @@ -12358,7 +12386,7 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                                     : IN6_IS_ADDR_V4MAPPED(&prefix),\n>                                     &lrp_addr_s, &out_port)) {\n>          free(nexthop);\n> -        return;\n> +        return NULL;\n>      }\n>\n>      const struct nbrec_bfd *nb_bt = route->bfd;\n> @@ -12368,7 +12396,7 @@ parsed_routes_add_static(const struct ovn_datapath\n> *od,\n>                                                    nb_bt->dst_ip);\n>          if (!bfd_e) {\n>              free(nexthop);\n> -            return;\n> +            return NULL;\n>          }\n>\n>          /* This static route is linked to an active bfd session. */\n> @@ -12385,10 +12413,9 @@ parsed_routes_add_static(const struct\n> ovn_datapath *od,\n>              bfd_set_status(bfd_sr, \"down\");\n>          }\n>\n> -\n>          if (!strcmp(bfd_sr->status, \"down\")) {\n> -            free(nexthop);\n> -            return;\n> +           free(nexthop);\n> +           return NULL;\n>          }\n>      }\n>\n> @@ -12427,11 +12454,15 @@ parsed_routes_add_static(const struct\n> ovn_datapath *od,\n>          source = ROUTE_SOURCE_STATIC;\n>      }\n>\n> -    parsed_route_add(od, nexthop, &prefix, plen, is_discard_route,\n> lrp_addr_s,\n> -                     out_port, route_table_id, is_src_route,\n> -                     ecmp_symmetric_reply, &ecmp_selection_fields, source,\n> -                     &route->header_, NULL, routes);\n> +    struct parsed_route *pr = parsed_route_add(od, nexthop, &prefix, plen,\n> +                                               is_discard_route,\n> lrp_addr_s,\n> +                                               out_port, route_table_id,\n> +                                               is_src_route,\n> +                                               ecmp_symmetric_reply,\n> +                                               &ecmp_selection_fields,\n> source,\n> +                                               &route->header_, NULL,\n> routes);\n>      sset_destroy(&ecmp_selection_fields);\n> +    return pr;\n>  }\n>\n>  static void\n> @@ -16309,13 +16340,14 @@ build_lr_gateway_redirect_flows_for_nats(\n>   * In the common case where the Ethernet destination has been resolved,\n>   * this table outputs the packet (priority 0).  Otherwise, it composes\n>   * and sends an ARP/IPv6 NA request (priority 100). */\n> -static void\n> +void\n>  build_arp_request_flows_for_lrouter(\n> -        struct ovn_datapath *od, struct lflow_table *lflows,\n> -        struct ds *match, struct ds *actions,\n> +        const struct ovn_datapath *od, struct lflow_table *lflows,\n>          const struct shash *meter_groups,\n>          struct lflow_ref *lflow_ref)\n>  {\n> +    struct ds match =  DS_EMPTY_INITIALIZER;\n> +    struct ds actions = DS_EMPTY_INITIALIZER;\n>      ovs_assert(od->nbr);\n>      for (int i = 0; i < od->nbr->n_static_routes; i++) {\n>          const struct nbrec_logical_router_static_route *route;\n> @@ -16329,8 +16361,8 @@ build_arp_request_flows_for_lrouter(\n>              continue;\n>          }\n>\n> -        ds_clear(match);\n> -        ds_put_format(match, \"eth.dst == 00:00:00:00:00:00 && \"\n> +        ds_clear(&match);\n> +        ds_put_format(&match, \"eth.dst == 00:00:00:00:00:00 && \"\n>                        REGBIT_NEXTHOP_IS_IPV4\" == 0 && \"\n>                        REG_NEXT_HOP_IPV6 \" == %s\",\n>                        route->nexthop);\n> @@ -16342,8 +16374,8 @@ build_arp_request_flows_for_lrouter(\n>          char sn_addr_s[INET6_ADDRSTRLEN + 1];\n>          ipv6_string_mapped(sn_addr_s, &sn_addr);\n>\n> -        ds_clear(actions);\n> -        ds_put_format(actions,\n> +        ds_clear(&actions);\n> +        ds_put_format(&actions,\n>                        \"nd_ns { \"\n>                        \"eth.dst = \"ETH_ADDR_FMT\"; \"\n>                        \"ip6.dst = %s; \"\n> @@ -16353,7 +16385,7 @@ build_arp_request_flows_for_lrouter(\n>                        route->nexthop);\n>\n>          ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,\n> -                      ds_cstr(match), ds_cstr(actions), lflow_ref,\n> +                      ds_cstr(&match), ds_cstr(&actions), lflow_ref,\n>                        WITH_CTRL_METER(copp_meter_get(COPP_ND_NS_RESOLVE,\n>                                                       od->nbr->copp,\n>                                                       meter_groups)),\n> @@ -16385,6 +16417,8 @@ build_arp_request_flows_for_lrouter(\n>\n>  meter_groups)));\n>      ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, \"1\", \"next;\",\n>                    lflow_ref);\n> +    ds_destroy(&match);\n> +    ds_destroy(&actions);\n>  }\n>\n>  static void\n> @@ -19508,8 +19542,7 @@ build_lswitch_and_lrouter_iterate_by_lr(struct\n> ovn_datapath *od,\n>      build_gateway_redirect_flows_for_lrouter(od, lsi->lflows, &lsi->match,\n>                                               &lsi->actions,\n>                                               od->datapath_lflows);\n> -    build_arp_request_flows_for_lrouter(od, lsi->lflows, &lsi->match,\n> -                                        &lsi->actions,\n> +    build_arp_request_flows_for_lrouter(od, lsi->lflows,\n>                                          lsi->meter_groups,\n>                                          od->datapath_lflows);\n>      build_ecmp_stateful_egr_flows_for_lrouter(od, lsi->lflows,\n> @@ -21067,6 +21100,9 @@ routes_init(struct routes_data *data)\n>      hmap_init(&data->parsed_routes);\n>      simap_init(&data->route_tables);\n>      hmap_init(&data->bfd_active_connections);\n> +    data->tracked = false;\n> +    hmapx_init(&data->trk_data.trk_deleted_parsed_route);\n> +    hmapx_init(&data->trk_data.trk_crupdated_parsed_route);\n>  }\n>\n>  void\n> @@ -21197,6 +21233,17 @@ routes_destroy(struct routes_data *data)\n>\n>      simap_destroy(&data->route_tables);\n>      __bfd_destroy(&data->bfd_active_connections);\n> +    data->tracked = false;\n> +    hmapx_destroy(&data->trk_data.trk_crupdated_parsed_route);\n> +    hmapx_destroy(&data->trk_data.trk_deleted_parsed_route);\n> +}\n> +\n> +void\n> +routes_clear_tracked(struct routes_data *data)\n> +{\n> +    data->tracked = false;\n> +    hmapx_clear(&data->trk_data.trk_crupdated_parsed_route);\n> +    hmapx_clear(&data->trk_data.trk_deleted_parsed_route);\n>  }\n>\n>  void\n> diff --git a/northd/northd.h b/northd/northd.h\n> index a9070d6f6..5b3461840 100644\n> --- a/northd/northd.h\n> +++ b/northd/northd.h\n> @@ -160,6 +160,7 @@ enum northd_tracked_data_type {\n>      NORTHD_TRACKED_LS_ACLS  = (1 << 4),\n>      NORTHD_TRACKED_SWITCHES = (1 << 5),\n>      NORTHD_TRACKED_ROUTERS  = (1 << 6),\n> +    NORTHD_TRACKED_LR_ROUTES  = (1 << 7),\n>  };\n>\n>  /* Track what's changed in the northd engine node.\n> @@ -177,6 +178,10 @@ struct northd_tracked_data {\n>       * hmapx node is 'struct ovn_datapath *'. */\n>      struct hmapx trk_nat_lrs;\n>\n> +    /* Tracked logical routers whose static routes have changed.\n> +     * hmapx node is 'struct ovn_datapath *'. */\n> +    struct hmapx trk_lrs_routes;\n> +\n>      /* Tracked logical switches whose load balancers have changed.\n>       * hmapx node is 'struct ovn_datapath *'. */\n>      struct hmapx ls_with_changed_lbs;\n> @@ -217,10 +222,23 @@ struct route_policy {\n>      uint32_t jump_chain_id;\n>  };\n>\n> +struct route_tracked_data {\n> +    /* Contains references to group_ecmp_route_node. Each of the\n> referenced\n> +     * datapaths contains at least one route. */\n> +    struct hmapx trk_crupdated_parsed_route;\n> +\n> +    /* Contains references to group_ecmp_route_node. Each of the\n> referenced\n> +     * datapath previously had some routes. The datapath now no longer\n> +     * contains any route.*/\n> +    struct hmapx trk_deleted_parsed_route;\n> +};\n> +\n>  struct routes_data {\n>      struct hmap parsed_routes; /* Stores struct parsed_route. */\n>      struct simap route_tables;\n>      struct hmap bfd_active_connections;\n> +    bool tracked;\n> +    struct route_tracked_data trk_data;\n>  };\n>\n>  struct route_policies_data {\n> @@ -855,6 +873,7 @@ struct parsed_route {\n>      char *lrp_addr_s;\n>      const struct ovn_port *out_port;\n>      const struct ovn_port *tracked_port; /* May be NULL. */\n> +    bool is_in_parsed_routes;\n>  };\n>\n>  struct parsed_route *parsed_route_clone(const struct parsed_route *);\n> @@ -881,6 +900,14 @@ struct parsed_route *parsed_route_add(\n>      const struct ovn_port *tracked_port,\n>      struct hmap *routes);\n>\n> +struct  parsed_route * parsed_routes_add_static(\n> +    const struct ovn_datapath *od,\n> +    const struct hmap *lr_ports,\n> +    const struct nbrec_logical_router_static_route *route,\n> +    const struct hmap *bfd_connections,\n> +    struct hmap *routes, struct simap *route_tables,\n> +    struct hmap *bfd_active_connections);\n> +\n>  struct svc_monitors_map_data {\n>      const struct hmap *local_svc_monitors_map;\n>      const struct hmap *ic_learned_svc_monitors_map;\n> @@ -925,7 +952,7 @@ void build_parsed_routes(const struct ovn_datapath *,\n> const struct hmap *,\n>  uint32_t get_route_table_id(struct simap *, const char *);\n>  void routes_init(struct routes_data *);\n>  void routes_destroy(struct routes_data *);\n> -\n> +void routes_clear_tracked(struct routes_data *);\n>  void bfd_init(struct bfd_data *);\n>  void bfd_destroy(struct bfd_data *);\n>\n> @@ -951,6 +978,10 @@ void build_route_data_flows_for_lrouter(\n>      const struct ovn_datapath *od, struct lflow_table *lflows,\n>      const struct group_ecmp_datapath *route_node,\n>      const struct sset *bfd_ports);\n> +void build_arp_request_flows_for_lrouter(\n> +    const struct ovn_datapath *od, struct lflow_table *lflows,\n> +    const struct shash *meter_groups,\n> +    struct lflow_ref *lflow_ref);\n>\n>  bool lflow_handle_northd_lr_changes(struct ovsdb_idl_txn *ovnsh_txn,\n>                                       struct tracked_dps *,\n> @@ -1041,6 +1072,11 @@ northd_has_lr_nats_in_tracked_data(struct\n> northd_tracked_data *trk_nd_changes)\n>  {\n>      return trk_nd_changes->type & NORTHD_TRACKED_LR_NATS;\n>  }\n> +static inline bool\n> +northd_has_lr_route_in_tracked_data(struct northd_tracked_data\n> *trk_nd_changes)\n> +{\n> +    return trk_nd_changes->type & NORTHD_TRACKED_LR_ROUTES;\n> +}\n>\n>  static inline bool\n>  northd_has_ls_lbs_in_tracked_data(struct northd_tracked_data\n> *trk_nd_changes)\n> diff --git a/tests/ovn-inc-proc-graph-dump.at b/tests/\n> ovn-inc-proc-graph-dump.at\n> index 178310978..fd05c20dc 100644\n> --- a/tests/ovn-inc-proc-graph-dump.at\n> +++ b/tests/ovn-inc-proc-graph-dump.at\n> @@ -151,9 +151,11 @@ digraph \"Incremental-Processing-Engine\" {\n>         bfd [[style=filled, shape=box, fillcolor=white, label=\"bfd\"]];\n>         NB_bfd -> bfd [[label=\"\"]];\n>         SB_bfd -> bfd [[label=\"\"]];\n> +       NB_logical_router_static_route [[style=filled, shape=box,\n> fillcolor=white, label=\"NB_logical_router_static_route\"]];\n>         routes [[style=filled, shape=box, fillcolor=white,\n> label=\"routes\"]];\n>         bfd -> routes [[label=\"\"]];\n>         northd -> routes [[label=\"routes_northd_change_handler\"]];\n> +       NB_logical_router_static_route -> routes\n> [[label=\"routes_static_route_change_handler\"]];\n>         route_policies [[style=filled, shape=box, fillcolor=white,\n> label=\"route_policies\"]];\n>         bfd -> route_policies [[label=\"\"]];\n>         northd -> route_policies\n> [[label=\"route_policies_northd_change_handler\"]];\n> @@ -168,7 +170,7 @@ digraph \"Incremental-Processing-Engine\" {\n>         SB_learned_route -> learned_route_sync\n> [[label=\"learned_route_sync_sb_learned_route_change_handler\"]];\n>         northd -> learned_route_sync\n> [[label=\"learned_route_sync_northd_change_handler\"]];\n>         group_ecmp_route [[style=filled, shape=box, fillcolor=white,\n> label=\"group_ecmp_route\"]];\n> -       routes -> group_ecmp_route [[label=\"\"]];\n> +       routes -> group_ecmp_route\n> [[label=\"group_ecmp_static_route_change_handler\"]];\n>         learned_route_sync -> group_ecmp_route\n> [[label=\"group_ecmp_route_learned_route_change_handler\"]];\n>         ls_stateful [[style=filled, shape=box, fillcolor=white,\n> label=\"ls_stateful\"]];\n>         northd -> ls_stateful [[label=\"ls_stateful_northd_handler\"]];\n> @@ -189,7 +191,7 @@ digraph \"Incremental-Processing-Engine\" {\n>         SB_logical_dp_group -> lflow [[label=\"\"]];\n>         bfd_sync -> lflow [[label=\"\"]];\n>         route_policies -> lflow [[label=\"\"]];\n> -       routes -> lflow [[label=\"\"]];\n> +       routes -> lflow [[label=\"lflow_group_route_change_handler\"]];\n>         group_ecmp_route -> lflow\n> [[label=\"lflow_group_ecmp_route_change_handler\"]];\n>         global_config -> lflow [[label=\"node_global_config_handler\"]];\n>         sampling_app -> lflow [[label=\"\"]];\n> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at\n> index 1d7bd6c28..eacccaf20 100644\n> --- a/tests/ovn-northd.at\n> +++ b/tests/ovn-northd.at\n> @@ -4272,9 +4272,9 @@ check ovn-nbctl --bfd=$uuid lr-route-add r0\n> 100.0.0.0/8 192.168.1.2\n>  wait_column down bfd status logical_port=r0-sw1\n>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],\n> [0], [], [ignore])\n>\n> -check_engine_stats northd recompute nocompute\n> +check_engine_stats northd norecompute compute\n>  check_engine_stats bfd recompute nocompute\n> -check_engine_stats routes recompute nocompute\n> +check_engine_stats routes recompute incremental\n>  check_engine_stats lflow recompute nocompute\n>  check_engine_stats northd_output norecompute compute\n>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n> @@ -4288,9 +4288,9 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8\n> 192.168.5.2 r0-sw5\n>  wait_column down bfd status logical_port=r0-sw5\n>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],\n> [0], [], [ignore])\n>\n> -check_engine_stats northd recompute nocompute\n> +check_engine_stats northd norecompute compute\n>  check_engine_stats bfd recompute nocompute\n> -check_engine_stats routes recompute nocompute\n> +check_engine_stats routes recompute incremental\n>  check_engine_stats lflow recompute nocompute\n>  check_engine_stats northd_output norecompute compute\n>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n> @@ -4300,7 +4300,7 @@ check ovn-nbctl --bfd --policy=src-ip lr-route-add\n> r0 192.168.6.1/32 192.168.10.\n>  wait_column down bfd status logical_port=r0-sw6\n>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],\n> [0], [], [ignore])\n>\n> -check_engine_stats northd recompute nocompute\n> +check_engine_stats northd norecompute compute\n>  check_engine_stats bfd recompute nocompute\n>  check_engine_stats route_policies recompute nocompute\n>  check_engine_stats lflow recompute nocompute\n> @@ -4335,10 +4335,10 @@ wait_column down bfd status logical_port=r0-sw8\n>  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)\n>  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q\n> $bfd_route_policy_uuid])\n>\n> -check_engine_stats northd recompute nocompute\n> +check_engine_stats northd recompute incremental\n>  check_engine_stats bfd recompute nocompute\n> -check_engine_stats routes recompute nocompute\n> -check_engine_stats lflow recompute nocompute\n> +check_engine_stats routes recompute incremental\n> +check_engine_stats lflow recompute incremental\n>  check_engine_stats northd_output norecompute compute\n>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> @@ -16437,12 +16437,12 @@ CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>\n>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>  check ovn-nbctl --wait=sb lr-route-add lr0 192.168.0.0/24 10.0.0.10\n> -check_engine_compute northd recompute\n> -check_engine_compute routes recompute\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n>  check_engine_compute advertised_route_sync recompute\n> -check_engine_compute learned_route_sync recompute\n> -check_engine_compute group_ecmp_route recompute\n> -check_engine_compute lflow recompute\n> +check_engine_compute learned_route_sync incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>\n>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> @@ -20672,3 +20672,75 @@ check_column \"$global_svc_mon_mac\"\n> sb:Service_Monitor src_mac port=2\n>  OVN_CLEANUP_NORTHD\n>  AT_CLEANUP\n>  ])\n> +\n> +OVN_FOR_EACH_NORTHD_NO_HV([\n> +AT_SETUP([Static Route incremental processing])\n> +ovn_start\n> +\n> +check ovn-nbctl lr-add r0\n> +\n> +check ovn-nbctl --wait=sb lrp-add r0 r0-lrp1 00:00:00:00:00:01\n> 192.168.1.1/24\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb lr-route-add r0 10.0.0.0/24 192.168.1.2\n> +\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +static_route_uuid=`ovn-nbctl --bare --columns _uuid find\n> Logical_Router_Static_Route nexthop=192.168.1.2`\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid nexthop=192.168.1.3\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid ip_prefix=10.0.1.0/24\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check ovn-nbctl --wait=sb lrp-add r0 r0-lrp2 00:00:00:00:00:02\n> 192.168.1.10/24\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid output_port=r0-lrp2\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid policy=src-ip\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid selection_fields=\"ip_proto,ip_src,ip_dst\"\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl --wait=sb set logical_router_static_route\n> $static_route_uuid options:ecmp_symmetric_reply=true\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n> +check ovn-nbctl remove logical_router r0 static_routes $static_route_uuid\n> +check_engine_compute northd incremental\n> +check_engine_compute routes incremental\n> +check_engine_compute group_ecmp_route incremental\n> +check_engine_compute lflow incremental\n> +\n> +OVN_CLEANUP_NORTHD\n> +AT_CLEANUP\n> +])\n> --\n> 2.43.0\n>\n>","headers":{"Return-Path":"<ovs-dev-bounces@openvswitch.org>","X-Original-To":["incoming@patchwork.ozlabs.org","dev@openvswitch.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","ovs-dev@lists.linuxfoundation.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=EktHI8a8;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=140.211.166.138; helo=smtp1.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)","smtp1.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=luizalabs.com header.i=@luizalabs.com header.a=rsa-sha256\n header.s=google header.b=EktHI8a8","smtp1.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=luizalabs.com"],"Received":["from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g11FC1Fc5z1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 23 Apr 2026 00:04:46 +1000 (AEST)","from localhost (localhost [127.0.0.1])\n\tby smtp1.osuosl.org (Postfix) with ESMTP id 90E9E84919;\n\tWed, 22 Apr 2026 14:04:44 +0000 (UTC)","from smtp1.osuosl.org ([127.0.0.1])\n by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id AR9dhq-CV8ft; Wed, 22 Apr 2026 14:04:40 +0000 (UTC)","from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])\n\tby smtp1.osuosl.org (Postfix) with ESMTPS id 4D8CF84797;\n\tWed, 22 Apr 2026 14:04:40 +0000 (UTC)","from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id E3FA6C058E;\n\tWed, 22 Apr 2026 14:04:39 +0000 (UTC)","from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138])\n by lists.linuxfoundation.org (Postfix) with ESMTP id B9A6BC058D\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 14:04:38 +0000 (UTC)","from localhost (localhost [127.0.0.1])\n by smtp1.osuosl.org (Postfix) with ESMTP id 98ECA84797\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 14:04:38 +0000 (UTC)","from smtp1.osuosl.org ([127.0.0.1])\n by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id No2fmo32S51y for <dev@openvswitch.org>;\n Wed, 22 Apr 2026 14:04:34 +0000 (UTC)","from mail-ot1-x336.google.com (mail-ot1-x336.google.com\n [IPv6:2607:f8b0:4864:20::336])\n by smtp1.osuosl.org (Postfix) with ESMTPS id 8ED15847AB\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 14:04:33 +0000 (UTC)","by mail-ot1-x336.google.com with SMTP id\n 46e09a7af769-7dbccf6a23dso4490178a34.2\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 07:04:33 -0700 (PDT)"],"X-Virus-Scanned":["amavis at osuosl.org","amavis at osuosl.org"],"X-Comment":"SPF check N/A for local connections - client-ip=140.211.9.56;\n helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ","DKIM-Filter":["OpenDKIM Filter v2.11.0 smtp1.osuosl.org 4D8CF84797","OpenDKIM Filter v2.11.0 smtp1.osuosl.org 8ED15847AB"],"Received-SPF":"Pass (mailfrom) identity=mailfrom;\n client-ip=2607:f8b0:4864:20::336; helo=mail-ot1-x336.google.com;\n envelope-from=lucas.vdias@luizalabs.com; receiver=<UNKNOWN>","DMARC-Filter":"OpenDMARC Filter v1.4.2 smtp1.osuosl.org 8ED15847AB","ARC-Seal":"i=1; a=rsa-sha256; t=1776866672; cv=none;\n d=google.com; s=arc-20240605;\n b=Nm8rkgFm0PJ7LhzK/jJu/5MjxeaObxNWG+f/q6XXDF1UIZJgOlbfuJKJuzhvIMOIyI\n x++sVUW23Ap94qHippxqUXIOv4ZdqNSsdtmNEn+OFBITlBcMKM+LmD1rkOoQTPYjVXdr\n 93QDTAvv10A4d9jHWzsCJsrxbZrSYPRMprAbia7MImJ4RU2+qrQn4Surlum9gXKnQ03P\n 1qckvnjPIpfXuyFtDkJNy1dzkRtcmdVuL9zLfP/7vY1w7zLMBrxmKWvzLPVRADHMtq2h\n CWsicu7Sntm0ZGjXfGnYwqQp39h8WOE3UVp+8Q9H+uWfgS6HKtChpvHX2QWV3NR04Rq7\n yteg==","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com;\n s=arc-20240605;\n h=to:subject:message-id:date:from:in-reply-to:references:mime-version\n :dkim-signature;\n bh=2V2Jg5q0VWFZ9obaFhPIVz/ma9PpEqyL+HBxF1OCmuY=;\n fh=WnO6tfRXfzNkmVvpHWPU0Sn0nugAh6D8VsZG77w0RnY=;\n b=QMT+HoEDppIslSqFNOeBQonnzwl0MhSUroLbNdiV7CxtlJvh62dLUakKQNJ64Eq/CR\n PNjvmyLJ+oqJagLAConY8I4IXwN62IW+yWZH1vBW+J5uUoXt7DwgDPIDfWtKAo30ZSab\n JF8AiEtWFEymbzfhhaK1AgUv/N5C2MtXfBcSQNqMjcB89HhA3hBuTpnNKDW0t136sIL0\n Uf2tOubF3Q53kGA4F3yAZsLWgaapfJsEohPwnQbCLflXmTKfrQeXd60ldICiB1ZzG+RR\n urN0a9ADWYSZ+ppKkVC3n4TFo34jLrmjaHWSQq1Gmiy3ZBoXdbdKDtuCCgiIKUbEUwEK\n baNg==; darn=openvswitch.org","ARC-Authentication-Results":"i=1; mx.google.com; arc=none","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=luizalabs.com; s=google; t=1776866672; x=1777471472; darn=openvswitch.org;\n h=to:subject:message-id:date:from:in-reply-to:references:mime-version\n :from:to:cc:subject:date:message-id:reply-to;\n bh=2V2Jg5q0VWFZ9obaFhPIVz/ma9PpEqyL+HBxF1OCmuY=;\n b=EktHI8a8zcR4i0XT20u9a8p5pYr4F4f1ris9DSEHDbZorug5kxd8aNBAqryzFAFDv2\n tEJgx1EYO/p38I2owaXzsQgcAceppcTiBl0D3NaS04S5WXexHNHCfYuwJNjDDftOMZ+W\n u1hhqe3Qsov/UwJnJdhWc2P9WTPF6wb2Qxa6Y=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776866672; x=1777471472;\n h=to:subject:message-id:date:from:in-reply-to:references:mime-version\n :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id\n :reply-to;\n bh=2V2Jg5q0VWFZ9obaFhPIVz/ma9PpEqyL+HBxF1OCmuY=;\n b=ZQyfF9m+IsznzYGsDgfRNff+TKSXgrWTw+r87Ji1ssZnMo/qSb+xBXuH712JPsMpIY\n iITgij74zeWPLgE62AoRks4N1gO6jrgYiLpzDD1X55LtCfnF8Zoa9LYaD1qZis9IvtUY\n SVVTBn88gCHfsSRlXUWrUXcF7/HW3sSRbXsh/YEZ+WimRSlAZxc6s3Lp6NHP1AzNR7Gz\n 5LJg6o0cPrLqOES8J6gponCX6emxxa0vqbJpbqNmQDUcClIhV12gcwqUY54IMHxtBVPh\n e2FYPM6ZPEjc8mllRMJqROaUsUu4gCuXjqXvdMCwhxifnPJbe2aM1zxa8eDXXVl3/+6l\n FSdA==","X-Gm-Message-State":"AOJu0Yysi5mGWJvbwS58V5kFhg1x7MJQKALVFG2VeHc23fm3j/ban7uO\n Ixg4iwHHFUTBsh8i5g+ioIyNeVRM8Vce/KisCQIKEZEWVPbNNEH1Q9j0GjosyyfFArMLPz8LEYj\n weYQKIw/Q/uW5j4shwchshGNT0HnQhYBsYgKseXtxWkhpjBwIEZkKMaFZMU1FFuWcSfcvrjRzgv\n vpdpNv+oS8PLDUd5x27YJfkLe4/U+u06nAA/4=","X-Gm-Gg":"AeBDieuciIMcjLUuTkCXqzX6d5wJ0PR4ea/cYQYmdwlXVZ8fkJjHZjPk9r6e6Jqczwk\n 4/qHf9cWjCoD/S9/ltcZOF/O9ToI/xmui1vhe3C9Gt9jn6hDlI+qiHaBDmCJbmjw/CqnNAxI3W2\n 79Ii7pG9fTPKLzNH5J6tQwvngKq7mgJuNDR7zYOy9XJSyTMwBi5jsLyolJTfl/SLTmEtHslwHtk\n xopS72emshi+nXMfwwQIXYSoIqwPCFt3VLYe02oSglndBECK4DfWCzhJCOnDkHibYbN6Lu0PWxO\n ck2WQswTCiG15HPUUcJW","X-Received":"by 2002:a05:6830:83b9:b0:7dd:9b19:a875 with SMTP id\n 46e09a7af769-7dd9b19ba4emr1327558a34.2.1776866671418; Wed, 22 Apr 2026\n 07:04:31 -0700 (PDT)","MIME-Version":"1.0","References":"<20260421211209.1974247-1-lucas.vdias@luizalabs.com>","In-Reply-To":"<20260421211209.1974247-1-lucas.vdias@luizalabs.com>","Date":"Wed, 22 Apr 2026 11:04:20 -0300","X-Gm-Features":"AQROBzDhbojDIIMYx-irOmMrdcG6lGk_1E-TCnSVACz6ewI5yaCpryeRZhavuuo","Message-ID":"\n <CACAUutNxtGY2qvZm45QwW+i1Y5HEcmicSag6dfPa+gK21MjoGw@mail.gmail.com>","To":"dev@openvswitch.org","X-Content-Filtered-By":"Mailman/MimeDel 2.1.30","Subject":"Re: [ovs-dev] [PATCH ovn v2 2/2] northd: Incremental processing for\n static routes.","X-BeenThere":"ovs-dev@openvswitch.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"<ovs-dev.openvswitch.org>","List-Unsubscribe":"<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>","List-Archive":"<http://mail.openvswitch.org/pipermail/ovs-dev/>","List-Post":"<mailto:ovs-dev@openvswitch.org>","List-Help":"<mailto:ovs-dev-request@openvswitch.org?subject=help>","List-Subscribe":"<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>","From":"Lucas Vargas Dias via dev <ovs-dev@openvswitch.org>","Reply-To":"Lucas Vargas Dias <lucas.vdias@luizalabs.com>","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"ovs-dev-bounces@openvswitch.org","Sender":"\"dev\" <ovs-dev-bounces@openvswitch.org>"}},{"id":3680707,"web_url":"http://patchwork.ozlabs.org/comment/3680707/","msgid":"<CACAUutN6eWpjfOxsk4J1NAXth+P9XT5PJkcXszEoj2_Or6ki=Q@mail.gmail.com>","list_archive_url":null,"date":"2026-04-22T16:54:39","subject":"Re: [ovs-dev] [PATCH ovn v2 2/2] northd: Incremental processing for\n static routes.","submitter":{"id":90169,"url":"http://patchwork.ozlabs.org/api/people/90169/","name":"Lucas Vargas Dias","email":"lucas.vdias@luizalabs.com"},"content":"Recheck-request: github-robot-_ovn-kubernetes\n\nEm qua., 22 de abr. de 2026 às 11:04, Lucas Vargas Dias <\nlucas.vdias@luizalabs.com> escreveu:\n\n> Recheck-request: github-robot\n>\n> Em ter., 21 de abr. de 2026 às 18:12, Lucas Vargas Dias <\n> lucas.vdias@luizalabs.com> escreveu:\n>\n>> Create a handler for deleted and updated static routes,\n>> and change the handler from engine route which check if\n>> it has a new static route created.\n>> Test with 2000 static routes created in the same logical router\n>> and add a new one:\n>> Without the incremental processing:\n>> ovn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32\n>> 192.168.20.2\n>> Time spent on processing nb_cfg 4:\n>>         ovn-northd delay before processing:     4ms\n>>         ovn-northd completion:                  62ms\n>>\n>> With the incremental processing:\n>> ovn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32\n>> 192.168.20.2\n>> Time spent on processing nb_cfg 6:\n>>         ovn-northd delay before processing:     4ms\n>>         ovn-northd completion:                  21ms\n>>\n>> Test with 2000 static routes created in the same logical router\n>> and delete one:\n>> Without the incremental processing:\n>> ovn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32\n>> 192.168.20.2\n>> Time spent on processing nb_cfg 5:\n>>         ovn-northd delay before processing:     3ms\n>>         ovn-northd completion:                  62ms\n>>\n>> With the incremental processing:\n>> ovn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32\n>> 192.168.20.2\n>> Time spent on processing nb_cfg 9:\n>>         ovn-northd delay before processing:     2ms\n>>         ovn-northd completion:                  32ms\n>>\n>> Signed-off-by: Lucas Vargas Dias <lucas.vdias@luizalabs.com>\n>> ---\n>>  northd/en-group-ecmp-route.c     |  57 ++++++++++\n>>  northd/en-group-ecmp-route.h     |   4 +\n>>  northd/en-lflow.c                |  20 ++++\n>>  northd/en-lflow.h                |   2 +\n>>  northd/en-northd.c               | 186 +++++++++++++++++++++++++++----\n>>  northd/en-northd.h               |   5 +-\n>>  northd/inc-proc-northd.c         |  13 ++-\n>>  northd/northd.c                  | 107 +++++++++++++-----\n>>  northd/northd.h                  |  38 ++++++-\n>>  tests/ovn-inc-proc-graph-dump.at |   6 +-\n>>  tests/ovn-northd.at              |  98 +++++++++++++---\n>>  11 files changed, 466 insertions(+), 70 deletions(-)\n>>\n>> diff --git a/northd/en-group-ecmp-route.c b/northd/en-group-ecmp-route.c\n>> index c4c93fd84..fcc76b076 100644\n>> --- a/northd/en-group-ecmp-route.c\n>> +++ b/northd/en-group-ecmp-route.c\n>> @@ -519,3 +519,60 @@ group_ecmp_route_learned_route_change_handler(struct\n>> engine_node *eng_node,\n>>      }\n>>      return EN_HANDLED_UNCHANGED;\n>>  }\n>> +\n>> +enum engine_input_handler_result\n>> +group_ecmp_static_route_change_handler(struct engine_node *eng_node,\n>> +                                       void *_data)\n>> +{\n>> +    struct routes_data *routes_data\n>> +        = engine_get_input_data(\"routes\", eng_node);\n>> +    struct group_ecmp_route_data *data = _data;\n>> +    if (!routes_data->tracked) {\n>> +        data->tracked = false;\n>> +        return EN_UNHANDLED;\n>> +    }\n>> +\n>> +    struct parsed_route *pr;\n>> +    struct hmapx updated_routes = HMAPX_INITIALIZER(&updated_routes);\n>> +\n>> +    const struct hmapx_node *hmapx_node;\n>> +    HMAPX_FOR_EACH (hmapx_node,\n>> +                    &routes_data->trk_data.trk_deleted_parsed_route) {\n>> +        pr = hmapx_node->data;\n>> +        if (!handle_deleted_route(data, pr, &updated_routes)) {\n>> +            hmapx_destroy(&updated_routes);\n>> +            return EN_UNHANDLED;\n>> +        }\n>> +\n>> +        if (pr->is_in_parsed_routes) {\n>> +            hmap_remove(&routes_data->parsed_routes, &pr->key_node);\n>> +        }\n>> +        parsed_route_free(pr);\n>> +    }\n>> +\n>> +    HMAPX_FOR_EACH (hmapx_node,\n>> +                    &routes_data->trk_data.trk_crupdated_parsed_route) {\n>> +        pr = hmapx_node->data;\n>> +        handle_added_route(data, pr, &updated_routes);\n>> +    }\n>> +\n>> +    HMAPX_FOR_EACH (hmapx_node, &updated_routes) {\n>> +        struct group_ecmp_datapath *node = hmapx_node->data;\n>> +        if (hmap_is_empty(&node->unique_routes) &&\n>> +                hmap_is_empty(&node->ecmp_groups)) {\n>> +            hmapx_add(&data->trk_data.deleted_datapath_routes, node);\n>> +            hmap_remove(&data->datapaths, &node->hmap_node);\n>> +        } else {\n>> +            hmapx_add(&data->trk_data.crupdated_datapath_routes, node);\n>> +        }\n>> +    }\n>> +\n>> +    hmapx_destroy(&updated_routes);\n>> +\n>> +    if (!hmapx_is_empty(&data->trk_data.crupdated_datapath_routes) ||\n>> +        !hmapx_is_empty(&data->trk_data.deleted_datapath_routes)) {\n>> +        data->tracked = true;\n>> +        return EN_HANDLED_UPDATED;\n>> +    }\n>> +    return EN_HANDLED_UNCHANGED;\n>> +}\n>> diff --git a/northd/en-group-ecmp-route.h b/northd/en-group-ecmp-route.h\n>> index d4a3248d0..246ca06bf 100644\n>> --- a/northd/en-group-ecmp-route.h\n>> +++ b/northd/en-group-ecmp-route.h\n>> @@ -98,6 +98,10 @@ enum engine_input_handler_result\n>>  group_ecmp_route_learned_route_change_handler(struct engine_node *,\n>>                                                void *data);\n>>\n>> +enum engine_input_handler_result\n>> +group_ecmp_static_route_change_handler(struct engine_node *,\n>> +                                       void *data);\n>> +\n>>  struct group_ecmp_datapath *group_ecmp_datapath_lookup(\n>>      const struct group_ecmp_route_data *data,\n>>      const struct ovn_datapath *od);\n>> diff --git a/northd/en-lflow.c b/northd/en-lflow.c\n>> index d4351edb9..22cd8fe91 100644\n>> --- a/northd/en-lflow.c\n>> +++ b/northd/en-lflow.c\n>> @@ -297,6 +297,21 @@ lflow_multicast_igmp_handler(struct engine_node\n>> *node, void *data)\n>>      return EN_HANDLED_UPDATED;\n>>  }\n>>\n>> +enum engine_input_handler_result\n>> +lflow_group_route_change_handler(struct engine_node *node,\n>> +                                      void *data OVS_UNUSED)\n>> +{\n>> +    struct routes_data *route_data =\n>> +        engine_get_input_data(\"routes\", node);\n>> +\n>> +    /* If we do not have tracked data we need to recompute. */\n>> +    if (!route_data->tracked) {\n>> +        return EN_UNHANDLED;\n>> +    }\n>> +\n>> +    return EN_HANDLED_UNCHANGED;\n>> +}\n>> +\n>>  enum engine_input_handler_result\n>>  lflow_group_ecmp_route_change_handler(struct engine_node *node,\n>>                                        void *data OVS_UNUSED)\n>> @@ -346,6 +361,11 @@ lflow_group_ecmp_route_change_handler(struct\n>> engine_node *node,\n>>              route_node->od, lflow_data->lflow_table,\n>>              route_node, lflow_input.bfd_ports);\n>>\n>> +        build_arp_request_flows_for_lrouter(route_node->od,\n>> +                                            lflow_data->lflow_table,\n>> +                                            lflow_input.meter_groups,\n>> +                                            route_node->lflow_ref);\n>> +\n>>          bool handled = lflow_ref_sync_lflows(\n>>              route_node->lflow_ref, lflow_data->lflow_table,\n>>              eng_ctx->ovnsb_idl_txn, lflow_input.dps,\n>> diff --git a/northd/en-lflow.h b/northd/en-lflow.h\n>> index d2a92e49f..aa320615f 100644\n>> --- a/northd/en-lflow.h\n>> +++ b/northd/en-lflow.h\n>> @@ -31,5 +31,7 @@ lflow_multicast_igmp_handler(struct engine_node *node,\n>> void *data);\n>>  enum engine_input_handler_result\n>>  lflow_group_ecmp_route_change_handler(struct engine_node *node, void\n>> *data);\n>>  enum engine_input_handler_result\n>> +lflow_group_route_change_handler(struct engine_node *node, void *data);\n>> +enum engine_input_handler_result\n>>  lflow_ic_learned_svc_mons_handler(struct engine_node *node, void *data);\n>>  #endif /* EN_LFLOW_H */\n>> diff --git a/northd/en-northd.c b/northd/en-northd.c\n>> index c34818dba..c05939a1d 100644\n>> --- a/northd/en-northd.c\n>> +++ b/northd/en-northd.c\n>> @@ -207,7 +207,8 @@ northd_nb_logical_router_handler(struct engine_node\n>> *node,\n>>      }\n>>\n>>      if (northd_has_lr_nats_in_tracked_data(&nd->trk_data) ||\n>> -        northd_has_lrouters_in_tracked_data(&nd->trk_data)) {\n>> +        northd_has_lrouters_in_tracked_data(&nd->trk_data) ||\n>> +        northd_has_lr_route_in_tracked_data(&nd->trk_data)) {\n>>          return EN_HANDLED_UPDATED;\n>>      }\n>>\n>> @@ -329,32 +330,174 @@ en_route_policies_run(struct engine_node *node,\n>> void *data)\n>>\n>>  enum engine_input_handler_result\n>>  routes_northd_change_handler(struct engine_node *node,\n>> -                                    void *data OVS_UNUSED)\n>> +                                    void *data)\n>>  {\n>>      struct northd_data *northd_data = engine_get_input_data(\"northd\",\n>> node);\n>>      if (!northd_has_tracked_data(&northd_data->trk_data)) {\n>>          return EN_UNHANDLED;\n>>      }\n>>\n>> -    /* This node uses the below data from the en_northd engine node.\n>> -     * See (lr_stateful_get_input_data())\n>> -     *   1. northd_data->lr_datapaths\n>> -     *   2. northd_data->lr_ports\n>> -     *      This data gets updated when a logical router or logical\n>> router port\n>> -     *      is created or deleted.\n>> -     *      Northd engine node presently falls back to full recompute\n>> when\n>> -     *      this happens and so does this node.\n>> -     *      Note: When we add I-P to the created/deleted logical routers\n>> or\n>> -     *      logical router ports, we need to revisit this handler.\n>> -     *\n>> -     *      This node also accesses the static routes of the logical\n>> router.\n>> -     *      When these static routes gets updated, en_northd engine\n>> recomputes\n>> -     *      and so does this node.\n>> -     *      Note: When we add I-P to handle static routes changes, we\n>> need\n>> -     *      to revisit this handler.\n>> -     */\n>> +    if (!northd_has_lr_route_in_tracked_data(&northd_data->trk_data)) {\n>> +        return EN_HANDLED_UNCHANGED;\n>> +    }\n>> +\n>> +    struct bfd_data *bfd_data = engine_get_input_data(\"bfd\", node);\n>> +    struct routes_data *routes_data = data;\n>> +    struct hmapx_node *hmapx_node;\n>> +    struct ovn_datapath *od;\n>> +    HMAPX_FOR_EACH (hmapx_node, &northd_data->trk_data.trk_lrs_routes) {\n>> +        od = hmapx_node->data;\n>> +        struct parsed_route *pr;\n>> +\n>> +        for (int i = 0; i < od->nbr->n_static_routes; i++) {\n>> +            struct nbrec_logical_router_static_route *static_route =\n>> +                od->nbr->static_routes[i];\n>> +            pr = parsed_route_lookup_by_source(ROUTE_SOURCE_STATIC,\n>> +                                               &static_route->header_,\n>> +\n>>  &routes_data->parsed_routes);\n>> +            if (pr) {\n>> +                pr->stale = false;\n>> +                continue;\n>> +            }\n>> +            pr = parsed_routes_add_static(od, &northd_data->lr_ports,\n>> +                                        static_route,\n>> +                                        &bfd_data->bfd_connections,\n>> +                                        &routes_data->parsed_routes,\n>> +                                        &routes_data->route_tables,\n>> +\n>> &routes_data->bfd_active_connections);\n>> +            if (!pr) {\n>> +                continue;\n>> +            }\n>> +            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,\n>> +                      pr);\n>> +        }\n>> +    }\n>> +\n>> +    if\n>> (!hmapx_is_empty(&routes_data->trk_data.trk_crupdated_parsed_route)) {\n>> +        routes_data->tracked = true;\n>> +        return EN_HANDLED_UPDATED;\n>> +    }\n>> +\n>>      return EN_HANDLED_UNCHANGED;\n>>  }\n>> +enum engine_input_handler_result\n>> +routes_static_route_change_handler(struct engine_node *node,\n>> +                                   void *data)\n>> +{\n>> +    struct routes_data *routes_data = data;\n>> +    struct hmapx created_trk_parsed_route =\n>> +        HMAPX_INITIALIZER(&created_trk_parsed_route);\n>> +    const struct nbrec_logical_router_static_route_table *\n>> +      nb_lr_static_route_table =\n>> +    EN_OVSDB_GET(engine_get_input(\"NB_logical_router_static_route\",\n>> node));\n>> +\n>> +    struct northd_data *northd_data = engine_get_input_data(\"northd\",\n>> node);\n>> +    struct bfd_data *bfd_data = engine_get_input_data(\"bfd\", node);\n>> +\n>> +    const struct nbrec_logical_router_static_route *changed_static_route;\n>> +    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_TABLE_FOR_EACH_TRACKED (\n>> +                            changed_static_route,\n>> nb_lr_static_route_table) {\n>> +\n>> +        bool is_deleted = nbrec_logical_router_static_route_is_deleted(\n>> +\n>> changed_static_route);\n>> +        bool is_new = nbrec_logical_router_static_route_is_new(\n>> +\n>> changed_static_route);\n>> +\n>> +        if (is_new && is_deleted) {\n>> +            continue;\n>> +        }\n>> +\n>> +        if (is_new) {\n>> +            hmapx_add(&created_trk_parsed_route, &changed_static_route);\n>> +            continue;\n>> +        }\n>> +\n>> +        if (is_deleted) {\n>> +            struct parsed_route *pr = parsed_route_lookup_by_source(\n>> +                                            ROUTE_SOURCE_STATIC,\n>> +\n>> &changed_static_route->header_,\n>> +                                            &routes_data->parsed_routes);\n>> +            if (!pr) {\n>> +                pr =\n>> parsed_route_lookup_by_source(ROUTE_SOURCE_IC_DYNAMIC,\n>> +\n>> &changed_static_route->header_,\n>> +                                            &routes_data->parsed_routes);\n>> +            }\n>> +            if (pr) {\n>> +                pr->stale = true;\n>> +\n>> hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route, pr);\n>> +            }\n>> +            continue;\n>> +        }\n>> +\n>> +        if (nbrec_logical_router_static_route_is_updated(\n>> +                    changed_static_route,\n>> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_NEXTHOP)\n>> +            || nbrec_logical_router_static_route_is_updated(\n>> +                    changed_static_route,\n>> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_IP_PREFIX)\n>> +            || nbrec_logical_router_static_route_is_updated(\n>> +                    changed_static_route,\n>> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OUTPUT_PORT)\n>> +            || nbrec_logical_router_static_route_is_updated(\n>> +                    changed_static_route,\n>> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_POLICY)\n>> +            || nbrec_logical_router_static_route_is_updated(\n>> +                    changed_static_route,\n>> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_ROUTE_TABLE)\n>> +            || nbrec_logical_router_static_route_is_updated(\n>> +                    changed_static_route,\n>> +\n>> NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_SELECTION_FIELDS)\n>> +            ||  nbrec_logical_router_static_route_is_updated(\n>> +                    changed_static_route,\n>> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OPTIONS)) {\n>> +            struct parsed_route *pr = parsed_route_lookup_by_source(\n>> +                                            ROUTE_SOURCE_STATIC,\n>> +\n>> &changed_static_route->header_,\n>> +                                            &routes_data->parsed_routes);\n>> +            if (!pr) {\n>> +                pr = parsed_route_lookup_by_source(\n>> +                                                ROUTE_SOURCE_IC_DYNAMIC,\n>> +\n>> &changed_static_route->header_,\n>> +\n>> &routes_data->parsed_routes);\n>> +            }\n>> +\n>> +            if (!pr || !pr->od || !northd_data || !bfd_data) {\n>> +                continue;\n>> +            }\n>> +            struct parsed_route *old_pr = pr;\n>> +            hmap_remove(&routes_data->parsed_routes, &old_pr->key_node);\n>> +            pr = parsed_routes_add_static(old_pr->od,\n>> &northd_data->lr_ports,\n>> +                                        changed_static_route,\n>> +                                        &bfd_data->bfd_connections,\n>> +                                        &routes_data->parsed_routes,\n>> +                                        &routes_data->route_tables,\n>> +\n>> &routes_data->bfd_active_connections);\n>> +            old_pr->is_in_parsed_routes = false;\n>> +            if (!pr) {\n>> +                continue;\n>> +            }\n>> +\n>> +            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,\n>> +                       pr);\n>> +            hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route,\n>> +                      old_pr);\n>> +        }\n>> +    }\n>> +    if\n>> (!hmapx_is_empty(&routes_data->trk_data.trk_crupdated_parsed_route) ||\n>> +\n>> !hmapx_is_empty(&routes_data->trk_data.trk_deleted_parsed_route)) {\n>> +        hmapx_destroy(&created_trk_parsed_route);\n>> +        routes_data->tracked = true;\n>> +        return EN_HANDLED_UPDATED;\n>> +    }\n>> +\n>> +    if (!hmapx_is_empty(&created_trk_parsed_route)) {\n>> +        hmapx_destroy(&created_trk_parsed_route);\n>> +        return EN_HANDLED_UPDATED;\n>> +    }\n>> +\n>> +    hmapx_destroy(&created_trk_parsed_route);\n>> +    return EN_UNHANDLED;\n>> +}\n>>\n>>  enum engine_node_state\n>>  en_routes_run(struct engine_node *node, void *data)\n>> @@ -590,6 +733,11 @@ en_routes_cleanup(void *data)\n>>      routes_destroy(data);\n>>  }\n>>\n>> +void\n>> +en_routes_clear_tracked_data(void *data)\n>> +{\n>> +    routes_clear_tracked(data);\n>> +}\n>>  void\n>>  en_bfd_cleanup(void *data)\n>>  {\n>> diff --git a/northd/en-northd.h b/northd/en-northd.h\n>> index 7794739b9..5247f3e11 100644\n>> --- a/northd/en-northd.h\n>> +++ b/northd/en-northd.h\n>> @@ -39,9 +39,12 @@ enum engine_node_state en_route_policies_run(struct\n>> engine_node *node,\n>>                                               void *data);\n>>  void *en_route_policies_init(struct engine_node *node OVS_UNUSED,\n>>                               struct engine_arg *arg OVS_UNUSED);\n>> +void en_routes_clear_tracked_data(void *data);\n>>  void en_routes_cleanup(void *data);\n>>  enum engine_input_handler_result\n>> -routes_northd_change_handler(struct engine_node *node, void *data\n>> OVS_UNUSED);\n>> +routes_northd_change_handler(struct engine_node *node, void *data);\n>> +enum engine_input_handler_result\n>> +routes_static_route_change_handler(struct engine_node *node, void *data);\n>>  enum engine_node_state en_routes_run(struct engine_node *node, void\n>> *data);\n>>  void *en_bfd_init(struct engine_node *node OVS_UNUSED,\n>>                    struct engine_arg *arg OVS_UNUSED);\n>> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c\n>> index ece388ce7..52f5dc57f 100644\n>> --- a/northd/inc-proc-northd.c\n>> +++ b/northd/inc-proc-northd.c\n>> @@ -76,7 +76,9 @@ static unixctl_cb_func chassis_features_list;\n>>      NB_NODE(sampling_app) \\\n>>      NB_NODE(network_function) \\\n>>      NB_NODE(network_function_group) \\\n>> -    NB_NODE(logical_switch_port_health_check)\n>> +    NB_NODE(logical_switch_port_health_check) \\\n>> +    NB_NODE(logical_router_static_route)\n>> +\n>>\n>>      enum nb_engine_node {\n>>  #define NB_NODE(NAME) NB_##NAME,\n>> @@ -179,7 +181,7 @@ static ENGINE_NODE(lr_stateful, CLEAR_TRACKED_DATA);\n>>  static ENGINE_NODE(ls_stateful, CLEAR_TRACKED_DATA);\n>>  static ENGINE_NODE(ls_arp, CLEAR_TRACKED_DATA);\n>>  static ENGINE_NODE(route_policies);\n>> -static ENGINE_NODE(routes);\n>> +static ENGINE_NODE(routes, CLEAR_TRACKED_DATA);\n>>  static ENGINE_NODE(bfd);\n>>  static ENGINE_NODE(bfd_sync, SB_WRITE);\n>>  static ENGINE_NODE(ecmp_nexthop, SB_WRITE);\n>> @@ -341,6 +343,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n>>      engine_add_input(&en_routes, &en_bfd, NULL);\n>>      engine_add_input(&en_routes, &en_northd,\n>>                       routes_northd_change_handler);\n>> +    engine_add_input(&en_routes, &en_nb_logical_router_static_route,\n>> +                     routes_static_route_change_handler);\n>>\n>>      engine_add_input(&en_bfd_sync, &en_bfd, NULL);\n>>      engine_add_input(&en_bfd_sync, &en_nb_bfd, NULL);\n>> @@ -380,7 +384,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n>>      engine_add_input(&en_learned_route_sync, &en_northd,\n>>                       learned_route_sync_northd_change_handler);\n>>\n>> -    engine_add_input(&en_group_ecmp_route, &en_routes, NULL);\n>> +    engine_add_input(&en_group_ecmp_route, &en_routes,\n>> +                     group_ecmp_static_route_change_handler);\n>>      engine_add_input(&en_group_ecmp_route, &en_learned_route_sync,\n>>                       group_ecmp_route_learned_route_change_handler);\n>>\n>> @@ -399,7 +404,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n>>      engine_add_input(&en_lflow, &en_sb_logical_dp_group, NULL);\n>>      engine_add_input(&en_lflow, &en_bfd_sync, NULL);\n>>      engine_add_input(&en_lflow, &en_route_policies, NULL);\n>> -    engine_add_input(&en_lflow, &en_routes, NULL);\n>> +    engine_add_input(&en_lflow, &en_routes,\n>> lflow_group_route_change_handler);\n>>      /* XXX: The incremental processing only supports changes to learned\n>> routes.\n>>       * All other changes trigger a full recompute. */\n>>      engine_add_input(&en_lflow, &en_group_ecmp_route,\n>> diff --git a/northd/northd.c b/northd/northd.c\n>> index 4fd4b9de9..ea48bc442 100644\n>> --- a/northd/northd.c\n>> +++ b/northd/northd.c\n>> @@ -4517,6 +4517,7 @@ destroy_northd_data_tracked_changes(struct\n>> northd_data *nd)\n>>      destroy_tracked_ovn_ports(&trk_changes->trk_lsps);\n>>      destroy_tracked_lbs(&trk_changes->trk_lbs);\n>>      hmapx_clear(&trk_changes->trk_nat_lrs);\n>> +    hmapx_clear(&trk_changes->trk_lrs_routes);\n>>      hmapx_clear(&trk_changes->ls_with_changed_lbs);\n>>      hmapx_clear(&trk_changes->ls_with_changed_acls);\n>>      hmapx_clear(&trk_changes->ls_with_changed_ipam);\n>> @@ -4540,6 +4541,7 @@ init_northd_tracked_data(struct northd_data *nd)\n>>      hmapx_init(&trk_data->trk_lbs.crupdated);\n>>      hmapx_init(&trk_data->trk_lbs.deleted);\n>>      hmapx_init(&trk_data->trk_nat_lrs);\n>> +    hmapx_init(&trk_data->trk_lrs_routes);\n>>      hmapx_init(&trk_data->ls_with_changed_lbs);\n>>      hmapx_init(&trk_data->ls_with_changed_acls);\n>>      hmapx_init(&trk_data->ls_with_changed_ipam);\n>> @@ -4558,6 +4560,7 @@ destroy_northd_tracked_data(struct northd_data *nd)\n>>      hmapx_destroy(&trk_data->trk_lbs.crupdated);\n>>      hmapx_destroy(&trk_data->trk_lbs.deleted);\n>>      hmapx_destroy(&trk_data->trk_nat_lrs);\n>> +    hmapx_destroy(&trk_data->trk_lrs_routes);\n>>      hmapx_destroy(&trk_data->ls_with_changed_lbs);\n>>      hmapx_destroy(&trk_data->ls_with_changed_acls);\n>>      hmapx_destroy(&trk_data->ls_with_changed_ipam);\n>> @@ -5379,7 +5382,8 @@ lr_changes_can_be_handled(const struct\n>> nbrec_logical_router *lr)\n>>          if (nbrec_logical_router_is_updated(lr, col)) {\n>>              if (col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER\n>>                  || col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER_GROUP\n>> -                || col == NBREC_LOGICAL_ROUTER_COL_NAT) {\n>> +                || col == NBREC_LOGICAL_ROUTER_COL_NAT\n>> +                || col == NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES) {\n>>                  continue;\n>>              }\n>>              return false;\n>> @@ -5404,12 +5408,7 @@ lr_changes_can_be_handled(const struct\n>> nbrec_logical_router *lr)\n>>              return false;\n>>          }\n>>      }\n>> -    for (size_t i = 0; i < lr->n_static_routes; i++) {\n>> -        if (nbrec_logical_router_static_route_row_get_seqno(\n>> -            lr->static_routes[i], OVSDB_IDL_CHANGE_MODIFY) > 0) {\n>> -            return false;\n>> -        }\n>> -    }\n>> +\n>>      return true;\n>>  }\n>>\n>> @@ -5435,6 +5434,13 @@ is_lr_nats_changed(const struct\n>> nbrec_logical_router *nbr) {\n>>              || is_lr_nats_seqno_changed(nbr));\n>>  }\n>>\n>> +static bool\n>> +is_lr_static_routes_changed(const struct nbrec_logical_router *nbr) {\n>> +    return nbrec_logical_router_is_updated(nbr,\n>> +\n>>  NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES);\n>> +}\n>> +\n>> +\n>>  /* Return true if changes are handled incrementally, false otherwise.\n>>   *\n>>   * Note: Changes to load balancer and load balancer groups associated\n>> with\n>> @@ -5503,6 +5509,22 @@ northd_handle_lr_changes(const struct northd_input\n>> *ni,\n>>\n>>              hmapx_add(&nd->trk_data.trk_nat_lrs, od);\n>>          }\n>> +\n>> +        /* Static Route was added or deleted. */\n>> +        if (is_lr_static_routes_changed(changed_lr)) {\n>> +            struct ovn_datapath *od = ovn_datapath_find_(\n>> +                                    &nd->lr_datapaths.datapaths,\n>> +                                    &changed_lr->header_.uuid);\n>> +\n>> +            if (!od) {\n>> +                static struct vlog_rate_limit rl =\n>> VLOG_RATE_LIMIT_INIT(1, 1);\n>> +                VLOG_WARN_RL(&rl, \"Internal error: a tracked updated LR \"\n>> +                            \"doesn't exist in lr_datapaths: \"UUID_FMT,\n>> +                            UUID_ARGS(&changed_lr->header_.uuid));\n>> +                goto fail;\n>> +            }\n>> +            hmapx_add(&nd->trk_data.trk_lrs_routes, od);\n>> +        }\n>>      }\n>>\n>>      HMAPX_FOR_EACH (node, &ni->synced_lrs->deleted) {\n>> @@ -5543,6 +5565,9 @@ northd_handle_lr_changes(const struct northd_input\n>> *ni,\n>>      if (!hmapx_is_empty(&nd->trk_data.trk_nat_lrs)) {\n>>          nd->trk_data.type |= NORTHD_TRACKED_LR_NATS;\n>>      }\n>> +    if (!hmapx_is_empty(&nd->trk_data.trk_lrs_routes)) {\n>> +        nd->trk_data.type |= NORTHD_TRACKED_LR_ROUTES;\n>> +    }\n>>      if (!hmapx_is_empty(&nd->trk_data.trk_routers.crupdated) ||\n>>          !hmapx_is_empty(&nd->trk_data.trk_routers.deleted)) {\n>>          nd->trk_data.type |= NORTHD_TRACKED_ROUTERS;\n>> @@ -12189,6 +12214,7 @@ parsed_route_init(const struct ovn_datapath *od,\n>>      new_pr->route_table_id = route_table_id;\n>>      new_pr->is_src_route = is_src_route;\n>>      new_pr->od = od;\n>> +    new_pr->is_in_parsed_routes = false;\n>>      new_pr->ecmp_symmetric_reply = ecmp_symmetric_reply;\n>>      new_pr->is_discard_route = is_discard_route;\n>>      new_pr->lrp_addr_s = nullable_xstrdup(lrp_addr_s);\n>> @@ -12296,6 +12322,7 @@ parsed_route_add(const struct ovn_datapath *od,\n>>      struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr);\n>>      if (!pr) {\n>>          hmap_insert(routes, &new_pr->key_node, hash);\n>> +        new_pr->is_in_parsed_routes = true;\n>>          return new_pr;\n>>      } else {\n>>          pr->stale = false;\n>> @@ -12304,7 +12331,7 @@ parsed_route_add(const struct ovn_datapath *od,\n>>      }\n>>  }\n>>\n>> -static void\n>> +struct parsed_route *\n>>  parsed_routes_add_static(const struct ovn_datapath *od,\n>>                           const struct hmap *lr_ports,\n>>                           const struct nbrec_logical_router_static_route\n>> *route,\n>> @@ -12325,8 +12352,9 @@ parsed_routes_add_static(const struct\n>> ovn_datapath *od,\n>>                           UUID_FMT, route->nexthop,\n>>                           UUID_ARGS(&route->header_.uuid));\n>>              free(nexthop);\n>> -            return;\n>> +            return NULL;\n>>          }\n>> +\n>>          if ((IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 32) ||\n>>              (!IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 128)) {\n>>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5,\n>> 1);\n>> @@ -12334,7 +12362,7 @@ parsed_routes_add_static(const struct\n>> ovn_datapath *od,\n>>                           UUID_FMT, route->nexthop,\n>>                           UUID_ARGS(&route->header_.uuid));\n>>              free(nexthop);\n>> -            return;\n>> +            return NULL;\n>>          }\n>>      }\n>>\n>> @@ -12346,7 +12374,7 @@ parsed_routes_add_static(const struct\n>> ovn_datapath *od,\n>>                       UUID_FMT, route->ip_prefix,\n>>                       UUID_ARGS(&route->header_.uuid));\n>>          free(nexthop);\n>> -        return;\n>> +        return NULL;\n>>      }\n>>\n>>      /* Verify that ip_prefix and nexthop are on the same network. */\n>> @@ -12358,7 +12386,7 @@ parsed_routes_add_static(const struct\n>> ovn_datapath *od,\n>>                                     : IN6_IS_ADDR_V4MAPPED(&prefix),\n>>                                     &lrp_addr_s, &out_port)) {\n>>          free(nexthop);\n>> -        return;\n>> +        return NULL;\n>>      }\n>>\n>>      const struct nbrec_bfd *nb_bt = route->bfd;\n>> @@ -12368,7 +12396,7 @@ parsed_routes_add_static(const struct\n>> ovn_datapath *od,\n>>                                                    nb_bt->dst_ip);\n>>          if (!bfd_e) {\n>>              free(nexthop);\n>> -            return;\n>> +            return NULL;\n>>          }\n>>\n>>          /* This static route is linked to an active bfd session. */\n>> @@ -12385,10 +12413,9 @@ parsed_routes_add_static(const struct\n>> ovn_datapath *od,\n>>              bfd_set_status(bfd_sr, \"down\");\n>>          }\n>>\n>> -\n>>          if (!strcmp(bfd_sr->status, \"down\")) {\n>> -            free(nexthop);\n>> -            return;\n>> +           free(nexthop);\n>> +           return NULL;\n>>          }\n>>      }\n>>\n>> @@ -12427,11 +12454,15 @@ parsed_routes_add_static(const struct\n>> ovn_datapath *od,\n>>          source = ROUTE_SOURCE_STATIC;\n>>      }\n>>\n>> -    parsed_route_add(od, nexthop, &prefix, plen, is_discard_route,\n>> lrp_addr_s,\n>> -                     out_port, route_table_id, is_src_route,\n>> -                     ecmp_symmetric_reply, &ecmp_selection_fields,\n>> source,\n>> -                     &route->header_, NULL, routes);\n>> +    struct parsed_route *pr = parsed_route_add(od, nexthop, &prefix,\n>> plen,\n>> +                                               is_discard_route,\n>> lrp_addr_s,\n>> +                                               out_port, route_table_id,\n>> +                                               is_src_route,\n>> +                                               ecmp_symmetric_reply,\n>> +                                               &ecmp_selection_fields,\n>> source,\n>> +                                               &route->header_, NULL,\n>> routes);\n>>      sset_destroy(&ecmp_selection_fields);\n>> +    return pr;\n>>  }\n>>\n>>  static void\n>> @@ -16309,13 +16340,14 @@ build_lr_gateway_redirect_flows_for_nats(\n>>   * In the common case where the Ethernet destination has been resolved,\n>>   * this table outputs the packet (priority 0).  Otherwise, it composes\n>>   * and sends an ARP/IPv6 NA request (priority 100). */\n>> -static void\n>> +void\n>>  build_arp_request_flows_for_lrouter(\n>> -        struct ovn_datapath *od, struct lflow_table *lflows,\n>> -        struct ds *match, struct ds *actions,\n>> +        const struct ovn_datapath *od, struct lflow_table *lflows,\n>>          const struct shash *meter_groups,\n>>          struct lflow_ref *lflow_ref)\n>>  {\n>> +    struct ds match =  DS_EMPTY_INITIALIZER;\n>> +    struct ds actions = DS_EMPTY_INITIALIZER;\n>>      ovs_assert(od->nbr);\n>>      for (int i = 0; i < od->nbr->n_static_routes; i++) {\n>>          const struct nbrec_logical_router_static_route *route;\n>> @@ -16329,8 +16361,8 @@ build_arp_request_flows_for_lrouter(\n>>              continue;\n>>          }\n>>\n>> -        ds_clear(match);\n>> -        ds_put_format(match, \"eth.dst == 00:00:00:00:00:00 && \"\n>> +        ds_clear(&match);\n>> +        ds_put_format(&match, \"eth.dst == 00:00:00:00:00:00 && \"\n>>                        REGBIT_NEXTHOP_IS_IPV4\" == 0 && \"\n>>                        REG_NEXT_HOP_IPV6 \" == %s\",\n>>                        route->nexthop);\n>> @@ -16342,8 +16374,8 @@ build_arp_request_flows_for_lrouter(\n>>          char sn_addr_s[INET6_ADDRSTRLEN + 1];\n>>          ipv6_string_mapped(sn_addr_s, &sn_addr);\n>>\n>> -        ds_clear(actions);\n>> -        ds_put_format(actions,\n>> +        ds_clear(&actions);\n>> +        ds_put_format(&actions,\n>>                        \"nd_ns { \"\n>>                        \"eth.dst = \"ETH_ADDR_FMT\"; \"\n>>                        \"ip6.dst = %s; \"\n>> @@ -16353,7 +16385,7 @@ build_arp_request_flows_for_lrouter(\n>>                        route->nexthop);\n>>\n>>          ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,\n>> -                      ds_cstr(match), ds_cstr(actions), lflow_ref,\n>> +                      ds_cstr(&match), ds_cstr(&actions), lflow_ref,\n>>                        WITH_CTRL_METER(copp_meter_get(COPP_ND_NS_RESOLVE,\n>>                                                       od->nbr->copp,\n>>                                                       meter_groups)),\n>> @@ -16385,6 +16417,8 @@ build_arp_request_flows_for_lrouter(\n>>\n>>  meter_groups)));\n>>      ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, \"1\", \"next;\",\n>>                    lflow_ref);\n>> +    ds_destroy(&match);\n>> +    ds_destroy(&actions);\n>>  }\n>>\n>>  static void\n>> @@ -19508,8 +19542,7 @@ build_lswitch_and_lrouter_iterate_by_lr(struct\n>> ovn_datapath *od,\n>>      build_gateway_redirect_flows_for_lrouter(od, lsi->lflows,\n>> &lsi->match,\n>>                                               &lsi->actions,\n>>                                               od->datapath_lflows);\n>> -    build_arp_request_flows_for_lrouter(od, lsi->lflows, &lsi->match,\n>> -                                        &lsi->actions,\n>> +    build_arp_request_flows_for_lrouter(od, lsi->lflows,\n>>                                          lsi->meter_groups,\n>>                                          od->datapath_lflows);\n>>      build_ecmp_stateful_egr_flows_for_lrouter(od, lsi->lflows,\n>> @@ -21067,6 +21100,9 @@ routes_init(struct routes_data *data)\n>>      hmap_init(&data->parsed_routes);\n>>      simap_init(&data->route_tables);\n>>      hmap_init(&data->bfd_active_connections);\n>> +    data->tracked = false;\n>> +    hmapx_init(&data->trk_data.trk_deleted_parsed_route);\n>> +    hmapx_init(&data->trk_data.trk_crupdated_parsed_route);\n>>  }\n>>\n>>  void\n>> @@ -21197,6 +21233,17 @@ routes_destroy(struct routes_data *data)\n>>\n>>      simap_destroy(&data->route_tables);\n>>      __bfd_destroy(&data->bfd_active_connections);\n>> +    data->tracked = false;\n>> +    hmapx_destroy(&data->trk_data.trk_crupdated_parsed_route);\n>> +    hmapx_destroy(&data->trk_data.trk_deleted_parsed_route);\n>> +}\n>> +\n>> +void\n>> +routes_clear_tracked(struct routes_data *data)\n>> +{\n>> +    data->tracked = false;\n>> +    hmapx_clear(&data->trk_data.trk_crupdated_parsed_route);\n>> +    hmapx_clear(&data->trk_data.trk_deleted_parsed_route);\n>>  }\n>>\n>>  void\n>> diff --git a/northd/northd.h b/northd/northd.h\n>> index a9070d6f6..5b3461840 100644\n>> --- a/northd/northd.h\n>> +++ b/northd/northd.h\n>> @@ -160,6 +160,7 @@ enum northd_tracked_data_type {\n>>      NORTHD_TRACKED_LS_ACLS  = (1 << 4),\n>>      NORTHD_TRACKED_SWITCHES = (1 << 5),\n>>      NORTHD_TRACKED_ROUTERS  = (1 << 6),\n>> +    NORTHD_TRACKED_LR_ROUTES  = (1 << 7),\n>>  };\n>>\n>>  /* Track what's changed in the northd engine node.\n>> @@ -177,6 +178,10 @@ struct northd_tracked_data {\n>>       * hmapx node is 'struct ovn_datapath *'. */\n>>      struct hmapx trk_nat_lrs;\n>>\n>> +    /* Tracked logical routers whose static routes have changed.\n>> +     * hmapx node is 'struct ovn_datapath *'. */\n>> +    struct hmapx trk_lrs_routes;\n>> +\n>>      /* Tracked logical switches whose load balancers have changed.\n>>       * hmapx node is 'struct ovn_datapath *'. */\n>>      struct hmapx ls_with_changed_lbs;\n>> @@ -217,10 +222,23 @@ struct route_policy {\n>>      uint32_t jump_chain_id;\n>>  };\n>>\n>> +struct route_tracked_data {\n>> +    /* Contains references to group_ecmp_route_node. Each of the\n>> referenced\n>> +     * datapaths contains at least one route. */\n>> +    struct hmapx trk_crupdated_parsed_route;\n>> +\n>> +    /* Contains references to group_ecmp_route_node. Each of the\n>> referenced\n>> +     * datapath previously had some routes. The datapath now no longer\n>> +     * contains any route.*/\n>> +    struct hmapx trk_deleted_parsed_route;\n>> +};\n>> +\n>>  struct routes_data {\n>>      struct hmap parsed_routes; /* Stores struct parsed_route. */\n>>      struct simap route_tables;\n>>      struct hmap bfd_active_connections;\n>> +    bool tracked;\n>> +    struct route_tracked_data trk_data;\n>>  };\n>>\n>>  struct route_policies_data {\n>> @@ -855,6 +873,7 @@ struct parsed_route {\n>>      char *lrp_addr_s;\n>>      const struct ovn_port *out_port;\n>>      const struct ovn_port *tracked_port; /* May be NULL. */\n>> +    bool is_in_parsed_routes;\n>>  };\n>>\n>>  struct parsed_route *parsed_route_clone(const struct parsed_route *);\n>> @@ -881,6 +900,14 @@ struct parsed_route *parsed_route_add(\n>>      const struct ovn_port *tracked_port,\n>>      struct hmap *routes);\n>>\n>> +struct  parsed_route * parsed_routes_add_static(\n>> +    const struct ovn_datapath *od,\n>> +    const struct hmap *lr_ports,\n>> +    const struct nbrec_logical_router_static_route *route,\n>> +    const struct hmap *bfd_connections,\n>> +    struct hmap *routes, struct simap *route_tables,\n>> +    struct hmap *bfd_active_connections);\n>> +\n>>  struct svc_monitors_map_data {\n>>      const struct hmap *local_svc_monitors_map;\n>>      const struct hmap *ic_learned_svc_monitors_map;\n>> @@ -925,7 +952,7 @@ void build_parsed_routes(const struct ovn_datapath *,\n>> const struct hmap *,\n>>  uint32_t get_route_table_id(struct simap *, const char *);\n>>  void routes_init(struct routes_data *);\n>>  void routes_destroy(struct routes_data *);\n>> -\n>> +void routes_clear_tracked(struct routes_data *);\n>>  void bfd_init(struct bfd_data *);\n>>  void bfd_destroy(struct bfd_data *);\n>>\n>> @@ -951,6 +978,10 @@ void build_route_data_flows_for_lrouter(\n>>      const struct ovn_datapath *od, struct lflow_table *lflows,\n>>      const struct group_ecmp_datapath *route_node,\n>>      const struct sset *bfd_ports);\n>> +void build_arp_request_flows_for_lrouter(\n>> +    const struct ovn_datapath *od, struct lflow_table *lflows,\n>> +    const struct shash *meter_groups,\n>> +    struct lflow_ref *lflow_ref);\n>>\n>>  bool lflow_handle_northd_lr_changes(struct ovsdb_idl_txn *ovnsh_txn,\n>>                                       struct tracked_dps *,\n>> @@ -1041,6 +1072,11 @@ northd_has_lr_nats_in_tracked_data(struct\n>> northd_tracked_data *trk_nd_changes)\n>>  {\n>>      return trk_nd_changes->type & NORTHD_TRACKED_LR_NATS;\n>>  }\n>> +static inline bool\n>> +northd_has_lr_route_in_tracked_data(struct northd_tracked_data\n>> *trk_nd_changes)\n>> +{\n>> +    return trk_nd_changes->type & NORTHD_TRACKED_LR_ROUTES;\n>> +}\n>>\n>>  static inline bool\n>>  northd_has_ls_lbs_in_tracked_data(struct northd_tracked_data\n>> *trk_nd_changes)\n>> diff --git a/tests/ovn-inc-proc-graph-dump.at b/tests/\n>> ovn-inc-proc-graph-dump.at\n>> index 178310978..fd05c20dc 100644\n>> --- a/tests/ovn-inc-proc-graph-dump.at\n>> +++ b/tests/ovn-inc-proc-graph-dump.at\n>> @@ -151,9 +151,11 @@ digraph \"Incremental-Processing-Engine\" {\n>>         bfd [[style=filled, shape=box, fillcolor=white, label=\"bfd\"]];\n>>         NB_bfd -> bfd [[label=\"\"]];\n>>         SB_bfd -> bfd [[label=\"\"]];\n>> +       NB_logical_router_static_route [[style=filled, shape=box,\n>> fillcolor=white, label=\"NB_logical_router_static_route\"]];\n>>         routes [[style=filled, shape=box, fillcolor=white,\n>> label=\"routes\"]];\n>>         bfd -> routes [[label=\"\"]];\n>>         northd -> routes [[label=\"routes_northd_change_handler\"]];\n>> +       NB_logical_router_static_route -> routes\n>> [[label=\"routes_static_route_change_handler\"]];\n>>         route_policies [[style=filled, shape=box, fillcolor=white,\n>> label=\"route_policies\"]];\n>>         bfd -> route_policies [[label=\"\"]];\n>>         northd -> route_policies\n>> [[label=\"route_policies_northd_change_handler\"]];\n>> @@ -168,7 +170,7 @@ digraph \"Incremental-Processing-Engine\" {\n>>         SB_learned_route -> learned_route_sync\n>> [[label=\"learned_route_sync_sb_learned_route_change_handler\"]];\n>>         northd -> learned_route_sync\n>> [[label=\"learned_route_sync_northd_change_handler\"]];\n>>         group_ecmp_route [[style=filled, shape=box, fillcolor=white,\n>> label=\"group_ecmp_route\"]];\n>> -       routes -> group_ecmp_route [[label=\"\"]];\n>> +       routes -> group_ecmp_route\n>> [[label=\"group_ecmp_static_route_change_handler\"]];\n>>         learned_route_sync -> group_ecmp_route\n>> [[label=\"group_ecmp_route_learned_route_change_handler\"]];\n>>         ls_stateful [[style=filled, shape=box, fillcolor=white,\n>> label=\"ls_stateful\"]];\n>>         northd -> ls_stateful [[label=\"ls_stateful_northd_handler\"]];\n>> @@ -189,7 +191,7 @@ digraph \"Incremental-Processing-Engine\" {\n>>         SB_logical_dp_group -> lflow [[label=\"\"]];\n>>         bfd_sync -> lflow [[label=\"\"]];\n>>         route_policies -> lflow [[label=\"\"]];\n>> -       routes -> lflow [[label=\"\"]];\n>> +       routes -> lflow [[label=\"lflow_group_route_change_handler\"]];\n>>         group_ecmp_route -> lflow\n>> [[label=\"lflow_group_ecmp_route_change_handler\"]];\n>>         global_config -> lflow [[label=\"node_global_config_handler\"]];\n>>         sampling_app -> lflow [[label=\"\"]];\n>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at\n>> index 1d7bd6c28..eacccaf20 100644\n>> --- a/tests/ovn-northd.at\n>> +++ b/tests/ovn-northd.at\n>> @@ -4272,9 +4272,9 @@ check ovn-nbctl --bfd=$uuid lr-route-add r0\n>> 100.0.0.0/8 192.168.1.2\n>>  wait_column down bfd status logical_port=r0-sw1\n>>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd],\n>> [0], [], [ignore])\n>>\n>> -check_engine_stats northd recompute nocompute\n>> +check_engine_stats northd norecompute compute\n>>  check_engine_stats bfd recompute nocompute\n>> -check_engine_stats routes recompute nocompute\n>> +check_engine_stats routes recompute incremental\n>>  check_engine_stats lflow recompute nocompute\n>>  check_engine_stats northd_output norecompute compute\n>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>> @@ -4288,9 +4288,9 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8\n>> 192.168.5.2 r0-sw5\n>>  wait_column down bfd status logical_port=r0-sw5\n>>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd],\n>> [0], [], [ignore])\n>>\n>> -check_engine_stats northd recompute nocompute\n>> +check_engine_stats northd norecompute compute\n>>  check_engine_stats bfd recompute nocompute\n>> -check_engine_stats routes recompute nocompute\n>> +check_engine_stats routes recompute incremental\n>>  check_engine_stats lflow recompute nocompute\n>>  check_engine_stats northd_output norecompute compute\n>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>> @@ -4300,7 +4300,7 @@ check ovn-nbctl --bfd --policy=src-ip lr-route-add\n>> r0 192.168.6.1/32 192.168.10.\n>>  wait_column down bfd status logical_port=r0-sw6\n>>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd],\n>> [0], [], [ignore])\n>>\n>> -check_engine_stats northd recompute nocompute\n>> +check_engine_stats northd norecompute compute\n>>  check_engine_stats bfd recompute nocompute\n>>  check_engine_stats route_policies recompute nocompute\n>>  check_engine_stats lflow recompute nocompute\n>> @@ -4335,10 +4335,10 @@ wait_column down bfd status logical_port=r0-sw8\n>>  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)\n>>  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q\n>> $bfd_route_policy_uuid])\n>>\n>> -check_engine_stats northd recompute nocompute\n>> +check_engine_stats northd recompute incremental\n>>  check_engine_stats bfd recompute nocompute\n>> -check_engine_stats routes recompute nocompute\n>> -check_engine_stats lflow recompute nocompute\n>> +check_engine_stats routes recompute incremental\n>> +check_engine_stats lflow recompute incremental\n>>  check_engine_stats northd_output norecompute compute\n>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> @@ -16437,12 +16437,12 @@ CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>>\n>>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>>  check ovn-nbctl --wait=sb lr-route-add lr0 192.168.0.0/24 10.0.0.10\n>> -check_engine_compute northd recompute\n>> -check_engine_compute routes recompute\n>> +check_engine_compute northd incremental\n>> +check_engine_compute routes incremental\n>>  check_engine_compute advertised_route_sync recompute\n>> -check_engine_compute learned_route_sync recompute\n>> -check_engine_compute group_ecmp_route recompute\n>> -check_engine_compute lflow recompute\n>> +check_engine_compute learned_route_sync incremental\n>> +check_engine_compute group_ecmp_route incremental\n>> +check_engine_compute lflow incremental\n>>  CHECK_NO_CHANGE_AFTER_RECOMPUTE\n>>\n>>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> @@ -20672,3 +20672,75 @@ check_column \"$global_svc_mon_mac\"\n>> sb:Service_Monitor src_mac port=2\n>>  OVN_CLEANUP_NORTHD\n>>  AT_CLEANUP\n>>  ])\n>> +\n>> +OVN_FOR_EACH_NORTHD_NO_HV([\n>> +AT_SETUP([Static Route incremental processing])\n>> +ovn_start\n>> +\n>> +check ovn-nbctl lr-add r0\n>> +\n>> +check ovn-nbctl --wait=sb lrp-add r0 r0-lrp1 00:00:00:00:00:01\n>> 192.168.1.1/24\n>> +\n>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> +check ovn-nbctl --wait=sb lr-route-add r0 10.0.0.0/24 192.168.1.2\n>> +\n>> +check_engine_compute northd incremental\n>> +check_engine_compute routes incremental\n>> +check_engine_compute group_ecmp_route incremental\n>> +check_engine_compute lflow incremental\n>> +\n>> +static_route_uuid=`ovn-nbctl --bare --columns _uuid find\n>> Logical_Router_Static_Route nexthop=192.168.1.2`\n>> +\n>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> +check ovn-nbctl --wait=sb set logical_router_static_route\n>> $static_route_uuid nexthop=192.168.1.3\n>> +check_engine_compute northd incremental\n>> +check_engine_compute routes incremental\n>> +check_engine_compute group_ecmp_route incremental\n>> +check_engine_compute lflow incremental\n>> +\n>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> +check ovn-nbctl --wait=sb set logical_router_static_route\n>> $static_route_uuid ip_prefix=10.0.1.0/24\n>> +check_engine_compute northd incremental\n>> +check_engine_compute routes incremental\n>> +check_engine_compute group_ecmp_route incremental\n>> +check_engine_compute lflow incremental\n>> +\n>> +check ovn-nbctl --wait=sb lrp-add r0 r0-lrp2 00:00:00:00:00:02\n>> 192.168.1.10/24\n>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> +check ovn-nbctl --wait=sb set logical_router_static_route\n>> $static_route_uuid output_port=r0-lrp2\n>> +check_engine_compute northd incremental\n>> +check_engine_compute routes incremental\n>> +check_engine_compute group_ecmp_route incremental\n>> +check_engine_compute lflow incremental\n>> +\n>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> +check ovn-nbctl --wait=sb set logical_router_static_route\n>> $static_route_uuid policy=src-ip\n>> +check_engine_compute northd incremental\n>> +check_engine_compute routes incremental\n>> +check_engine_compute group_ecmp_route incremental\n>> +check_engine_compute lflow incremental\n>> +\n>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> +check ovn-nbctl --wait=sb set logical_router_static_route\n>> $static_route_uuid selection_fields=\"ip_proto,ip_src,ip_dst\"\n>> +check_engine_compute northd incremental\n>> +check_engine_compute routes incremental\n>> +check_engine_compute group_ecmp_route incremental\n>> +check_engine_compute lflow incremental\n>> +\n>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> +check ovn-nbctl --wait=sb set logical_router_static_route\n>> $static_route_uuid options:ecmp_symmetric_reply=true\n>> +check_engine_compute northd incremental\n>> +check_engine_compute routes incremental\n>> +check_engine_compute group_ecmp_route incremental\n>> +check_engine_compute lflow incremental\n>> +\n>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats\n>> +check ovn-nbctl remove logical_router r0 static_routes $static_route_uuid\n>> +check_engine_compute northd incremental\n>> +check_engine_compute routes incremental\n>> +check_engine_compute group_ecmp_route incremental\n>> +check_engine_compute lflow incremental\n>> +\n>> +OVN_CLEANUP_NORTHD\n>> +AT_CLEANUP\n>> +])\n>> --\n>> 2.43.0\n>>\n>>","headers":{"Return-Path":"<ovs-dev-bounces@openvswitch.org>","X-Original-To":["incoming@patchwork.ozlabs.org","dev@openvswitch.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","ovs-dev@lists.linuxfoundation.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=OoqhKSsX;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)","smtp1.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key,\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=OoqhKSsX","smtp4.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=luizalabs.com","smtp4.osuosl.org; dkim=pass (1024-bit key,\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=OoqhKSsX"],"Received":["from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g151g1pqMz1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 23 Apr 2026 02:55:03 +1000 (AEST)","from localhost (localhost [127.0.0.1])\n\tby smtp1.osuosl.org (Postfix) with ESMTP id 91E9380B56;\n\tWed, 22 Apr 2026 16:55:01 +0000 (UTC)","from smtp1.osuosl.org ([127.0.0.1])\n by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id LAZ4es-9CQyi; Wed, 22 Apr 2026 16:54:59 +0000 (UTC)","from lists.linuxfoundation.org (lf-lists.osuosl.org\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby smtp1.osuosl.org (Postfix) with ESMTPS id 20BDC807D8;\n\tWed, 22 Apr 2026 16:54:59 +0000 (UTC)","from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id EF122C058E;\n\tWed, 22 Apr 2026 16:54:58 +0000 (UTC)","from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 5B9D5C058D\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 16:54:57 +0000 (UTC)","from localhost (localhost [127.0.0.1])\n by smtp4.osuosl.org (Postfix) with ESMTP id 3C1A2401E2\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 16:54:57 +0000 (UTC)","from smtp4.osuosl.org ([127.0.0.1])\n by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id Rl5LUpkW4t9X for <dev@openvswitch.org>;\n Wed, 22 Apr 2026 16:54:53 +0000 (UTC)","from mail-oi1-x232.google.com (mail-oi1-x232.google.com\n [IPv6:2607:f8b0:4864:20::232])\n by smtp4.osuosl.org (Postfix) with ESMTPS id 6D8FC427E2\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 16:54:53 +0000 (UTC)","by mail-oi1-x232.google.com with SMTP id\n 5614622812f47-479dc6d26e3so1653096b6e.0\n for <dev@openvswitch.org>; Wed, 22 Apr 2026 09:54:53 -0700 (PDT)"],"X-Virus-Scanned":["amavis at osuosl.org","amavis at osuosl.org"],"X-Comment":"SPF check N/A for local connections -\n client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ","DKIM-Filter":["OpenDKIM Filter v2.11.0 smtp1.osuosl.org 20BDC807D8","OpenDKIM Filter v2.11.0 smtp4.osuosl.org 6D8FC427E2"],"Received-SPF":"Pass (mailfrom) identity=mailfrom;\n client-ip=2607:f8b0:4864:20::232; helo=mail-oi1-x232.google.com;\n envelope-from=lucas.vdias@luizalabs.com; receiver=<UNKNOWN>","DMARC-Filter":"OpenDMARC Filter v1.4.2 smtp4.osuosl.org 6D8FC427E2","ARC-Seal":"i=1; a=rsa-sha256; t=1776876892; cv=none;\n d=google.com; s=arc-20240605;\n b=GOYegruAFJIdYVdbw2gWT5e37B1Z40frTR6aNu7DMki1ruWyFgdnFZcF0wrcJU+NYn\n NysPabVjvVozXh4YZXs6CMchHkDuc+VXPRKXOswH3vkiZUK2W+qqvJlRsG8M3t7RjDpi\n 947R7fczXSWDdsqxX1H+nWwnNI+MrazXg8xfhOH6SeDxe/oWLvAPX7f5yAGLYRBJwoNV\n byI3ZSV83rtfYgqgmyRuZG5u6cJp41Lm2p5AAOYWmb0XoQux6t/fqkjXJVf6r1vXvQaK\n QtHyF1YaW+SSO11fGyJmI37VgSICKjdcWFX7szFZOjhjxDn+O1d8W8DpSr/XpGyXVv0e\n JBbA==","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com;\n s=arc-20240605;\n h=to:subject:message-id:date:from:in-reply-to:references:mime-version\n :dkim-signature;\n bh=5agEOoYXxJ5arLwcfmdUGkKuSjumExSEW240uqKvpVQ=;\n fh=WnO6tfRXfzNkmVvpHWPU0Sn0nugAh6D8VsZG77w0RnY=;\n b=SRb3AggdLEVuokK+StHDtrOAgp6Tsi+y7ujrmYJFPaglkxb++BayOhLTTEY5xfPufX\n YQN0O4/y3nhiwbBj5UTMT04bZXrqYZIqDWfH36MtBqpSV8Xn9+K93CDa6g4R1ikLVX2v\n w87ZiaGCR5W2cmY+WjPw0SJQgS9oGjsSM0Gks5S7NQx7uqhJolUr10wBgReeT8gdbBX0\n HL6pB5xQewu64mRZFvqeQ8/tzz2v3dKioI39aTgjJJdrMNq2/bJssQtOY+fCGsJXcyCw\n FWxxwvK90qvKONNhI2/SaBDr3s0of/eh0IkpKcvkD43cR1A9ORHFEAEOOfV2cNx0PdyZ\n yNnw==; darn=openvswitch.org","ARC-Authentication-Results":"i=1; mx.google.com; arc=none","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=luizalabs.com; s=google; t=1776876892; x=1777481692; darn=openvswitch.org;\n h=to:subject:message-id:date:from:in-reply-to:references:mime-version\n :from:to:cc:subject:date:message-id:reply-to;\n bh=5agEOoYXxJ5arLwcfmdUGkKuSjumExSEW240uqKvpVQ=;\n b=OoqhKSsXe9y6fV7gFPQT4aGg4PHWhN3Zu0jgC8GPt6ErVD4PKr077GgyJSjF+PwKld\n NcAIq5KTSaTF1QZwUZBMSiFrbr34vEYR1bnASUg+3Q4qRLnZ/Qoa5+ZX/bKN9h6PQfkO\n ZNbaXQPtAOEuwlNNwOVpIcw1DcZJm5DVxzVfM=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776876892; x=1777481692;\n h=to:subject:message-id:date:from:in-reply-to:references:mime-version\n :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id\n :reply-to;\n bh=5agEOoYXxJ5arLwcfmdUGkKuSjumExSEW240uqKvpVQ=;\n b=NOBdYLvIHXaoTfEL26iudfdyLE0vA798+VlRmgtsoQm+1wi/2bpR7g1OvwSs8TD6QV\n WTHwBQKz9lE9xvtHLf779i+RZX35KhTkCjp10LWXWUkLCU3PXtT/e7rdKl8r2YZzdcmr\n vZwuBLWHAgDKKe4CaNpHTntwLZ7XFZOhAJcnSMOpjHl+6BfsSGM9GFs89kTtq33a9K7x\n SY+RylIBvUAiqkenjay1dQ3BYlOH1mQ6gAGCJMT0rxVJO4Q5G115gNXJWaatqWNYJGuQ\n F8lgLt9skQqkUoHaOBaM4FNCwaC42atjJGNT/AH41ucn/XOYXqRvbkKr9eSVw2OaJtps\n +M2w==","X-Gm-Message-State":"AOJu0Yxa5TBSRkGi1pysetvwBpqczjvagpWb1YFtsl0D8dBr5RwH8+JB\n LjSRV5K+w5Dr5IfY/P/iR7CG/55ckYFt9Tn/HmfatT7Xc1qlt8zyhnGy/mX5WLwGzw34yzGbioe\n Br1itgmDfS7Ne49hTmZrD6JBeilRB7dwgj895lro2/89SveKBaKrhqTo0GJuoXsjByVPAAEZEYI\n uNf671+0UxUfpMjFGO1zCkEh6qaq7xgU2T/Yk=","X-Gm-Gg":"AeBDietc3YcObiSCwYBee3hXvuEM4gg6sdRX5wTJTrzGq6+2fCPsRt5fa9ecaNNv8Ya\n MQynN/fNU6o/uGQntgd9ozrKAzHVF13SDVMROQoV9k92s00BmLYUlKjpjc6hqwB0GNR71b/iwR6\n HEVtTHcB/43zzmxATx5fVf1ZNkZVFZ4bn0ljFV3ahOIa5Qs4P3vKHKrXAjJmnaCQJ504GRXHo4+\n ZxPwlUdisWQkyiKLlQ4TrI6ZsKR2DbIGRDfk5Lxh6XuNs/nNb52Gtph9VVv++Ytt/uEv6NfMbeY\n lTnhmGLkH1Mmct4ZyAPz","X-Received":"by 2002:a05:6808:19a6:b0:46a:cd63:96ca with SMTP id\n 5614622812f47-4799c9b122emr12452157b6e.27.1776876891205; Wed, 22 Apr 2026\n 09:54:51 -0700 (PDT)","MIME-Version":"1.0","References":"<20260421211209.1974247-1-lucas.vdias@luizalabs.com>\n <CACAUutNxtGY2qvZm45QwW+i1Y5HEcmicSag6dfPa+gK21MjoGw@mail.gmail.com>","In-Reply-To":"\n <CACAUutNxtGY2qvZm45QwW+i1Y5HEcmicSag6dfPa+gK21MjoGw@mail.gmail.com>","Date":"Wed, 22 Apr 2026 13:54:39 -0300","X-Gm-Features":"AQROBzBI0EvUdkp0l6oqnChmxsobdqBzFbVzNLcWfsM53e-3GopKqysj8xBVKu8","Message-ID":"\n <CACAUutN6eWpjfOxsk4J1NAXth+P9XT5PJkcXszEoj2_Or6ki=Q@mail.gmail.com>","To":"dev@openvswitch.org","X-Content-Filtered-By":"Mailman/MimeDel 2.1.30","Subject":"Re: [ovs-dev] [PATCH ovn v2 2/2] northd: Incremental processing for\n static routes.","X-BeenThere":"ovs-dev@openvswitch.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"<ovs-dev.openvswitch.org>","List-Unsubscribe":"<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>","List-Archive":"<http://mail.openvswitch.org/pipermail/ovs-dev/>","List-Post":"<mailto:ovs-dev@openvswitch.org>","List-Help":"<mailto:ovs-dev-request@openvswitch.org?subject=help>","List-Subscribe":"<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>","From":"Lucas Vargas Dias via dev <ovs-dev@openvswitch.org>","Reply-To":"Lucas Vargas Dias <lucas.vdias@luizalabs.com>","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"ovs-dev-bounces@openvswitch.org","Sender":"\"dev\" <ovs-dev-bounces@openvswitch.org>"}}]