diff mbox series

[ovs-dev,v2,1/5] lflow: Factor out the lflow reference handling code into a new module.

Message ID 166759987050.515460.12406491945779417131.stgit@dceara.remote.csb
State Changes Requested
Delegated to: Mark Michelson
Headers show
Series Add OVN component templates. | expand

Checks

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

Commit Message

Dumitru Ceara Nov. 4, 2022, 10:11 p.m. UTC
This makes it easier to have an overview of what the code does and at the
same time it allows multiple users to define and manage
"resource <-> object" dependencies.

Acked-by: Han Zhou <hzhou@ovn.org>
Signed-off-by: Dumitru Ceara <dceara@redhat.com>
---
V2:
- Addressed Mark's comments:
  - Fixed typos in comments in objdep.h.
  - Made objdep_change_handler return bool (handled successfully or not).
  - Reverted some unrelated style changes.
- Fixed cast style.
- Addressed Han's comments:
  - Removed superfluous 'type' argument name in prototypes.
- Added Han's ack.
---
 controller/lflow.c          |  330 ++++++++++---------------------------------
 controller/lflow.h          |   67 +--------
 controller/ovn-controller.c |   55 +++++--
 lib/automake.mk             |    2 
 lib/objdep.c                |  260 ++++++++++++++++++++++++++++++++++
 lib/objdep.h                |  122 ++++++++++++++++
 6 files changed, 508 insertions(+), 328 deletions(-)
 create mode 100644 lib/objdep.c
 create mode 100644 lib/objdep.h

Comments

0-day Robot Nov. 4, 2022, 11:04 p.m. UTC | #1
Bleep bloop.  Greetings Dumitru Ceara, I am a robot and I have tried out your patch.
Thanks for your contribution.

I encountered some error that I wasn't expecting.  See the details below.


checkpatch:
ERROR: Inappropriate bracing around statement
#1187 FILE: lib/objdep.h:52:
    HMAP_FOR_EACH (NODE, hmap_node, &(MAP)->objs)

Lines checked: 1261, Warnings: 0, Errors: 1


Please check this out.  If you feel there has been an error, please email aconole@redhat.com

Thanks,
0-day Robot
Mark Michelson Nov. 16, 2022, 9:32 p.m. UTC | #2
Acked-by: Mark Michelson <mmichels@redhat.com>

On 11/4/22 18:11, Dumitru Ceara wrote:
> This makes it easier to have an overview of what the code does and at the
> same time it allows multiple users to define and manage
> "resource <-> object" dependencies.
> 
> Acked-by: Han Zhou <hzhou@ovn.org>
> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
> ---
> V2:
> - Addressed Mark's comments:
>    - Fixed typos in comments in objdep.h.
>    - Made objdep_change_handler return bool (handled successfully or not).
>    - Reverted some unrelated style changes.
> - Fixed cast style.
> - Addressed Han's comments:
>    - Removed superfluous 'type' argument name in prototypes.
> - Added Han's ack.
> ---
>   controller/lflow.c          |  330 ++++++++++---------------------------------
>   controller/lflow.h          |   67 +--------
>   controller/ovn-controller.c |   55 +++++--
>   lib/automake.mk             |    2
>   lib/objdep.c                |  260 ++++++++++++++++++++++++++++++++++
>   lib/objdep.h                |  122 ++++++++++++++++
>   6 files changed, 508 insertions(+), 328 deletions(-)
>   create mode 100644 lib/objdep.c
>   create mode 100644 lib/objdep.h
> 
> diff --git a/controller/lflow.c b/controller/lflow.c
> index cc0f31db06..d4434bdee8 100644
> --- a/controller/lflow.c
> +++ b/controller/lflow.c
> @@ -61,7 +61,7 @@ struct lookup_port_aux {
>       struct ovsdb_idl_index *sbrec_port_binding_by_name;
>       const struct sbrec_datapath_binding *dp;
>       const struct sbrec_logical_flow *lflow;
> -    struct lflow_resource_ref *lfrr;
> +    struct objdep_mgr *deps_mgr;
>       const struct hmap *chassis_tunnels;
>   };
>   
> @@ -72,8 +72,8 @@ struct condition_aux {
>       const struct sset *active_tunnels;
>       const struct sbrec_logical_flow *lflow;
>       /* Resource reference to store the port name referenced
> -     * in is_chassis_resident() to the logical flow. */
> -    struct lflow_resource_ref *lfrr;
> +     * in is_chassis_resident() to the object (logical flow). */
> +    struct objdep_mgr *deps_mgr;
>   };
>   
>   static struct expr *
> @@ -81,7 +81,7 @@ convert_match_to_expr(const struct sbrec_logical_flow *,
>                         const struct local_datapath *ldp,
>                         struct expr **prereqs, const struct shash *addr_sets,
>                         const struct shash *port_groups,
> -                      struct lflow_resource_ref *, bool *pg_addr_set_ref);
> +                      struct objdep_mgr *, bool *pg_addr_set_ref);
>   static void
>   add_matches_to_flow_table(const struct sbrec_logical_flow *,
>                             const struct local_datapath *,
> @@ -94,17 +94,6 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow,
>                         bool is_recompute,
>                         struct lflow_ctx_in *l_ctx_in,
>                         struct lflow_ctx_out *l_ctx_out);
> -static void lflow_resource_add(struct lflow_resource_ref *, enum ref_type,
> -                               const char *ref_name, const struct uuid *,
> -                               size_t ref_count);
> -static struct ref_lflow_node *ref_lflow_lookup(struct hmap *ref_lflow_table,
> -                                               enum ref_type,
> -                                               const char *ref_name);
> -static struct lflow_ref_node *lflow_ref_lookup(struct hmap *lflow_ref_table,
> -                                               const struct uuid *lflow_uuid);
> -static void ref_lflow_node_destroy(struct ref_lflow_node *);
> -static void lflow_resource_destroy_lflow(struct lflow_resource_ref *,
> -                                         const struct uuid *lflow_uuid);
>   
>   static void add_port_sec_flows(const struct shash *binding_lports,
>                                  const struct sbrec_chassis *,
> @@ -125,8 +114,8 @@ lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
>       /* Store the name that used to lookup the lport to lflow reference, so that
>        * in the future when the lport's port binding changes, the logical flow
>        * that references this lport can be reprocessed. */
> -    lflow_resource_add(aux->lfrr, REF_TYPE_PORTBINDING, port_name,
> -                       &aux->lflow->header_.uuid, 0);
> +    objdep_mgr_add(aux->deps_mgr, OBJDEP_TYPE_PORTBINDING, port_name,
> +                   &aux->lflow->header_.uuid);
>   
>       const struct sbrec_port_binding *pb
>           = lport_lookup_by_name(aux->sbrec_port_binding_by_name, port_name);
> @@ -141,8 +130,8 @@ lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
>        * this multicast group can be reprocessed. */
>       struct ds mg_key = DS_EMPTY_INITIALIZER;
>       get_mc_group_key(port_name, aux->dp->tunnel_key, &mg_key);
> -    lflow_resource_add(aux->lfrr, REF_TYPE_MC_GROUP, ds_cstr(&mg_key),
> -                       &aux->lflow->header_.uuid, 0);
> +    objdep_mgr_add(aux->deps_mgr, OBJDEP_TYPE_MC_GROUP, ds_cstr(&mg_key),
> +                   &aux->lflow->header_.uuid);
>       ds_destroy(&mg_key);
>   
>       const struct sbrec_multicast_group *mg = mcgroup_lookup_by_dp_name(
> @@ -180,11 +169,11 @@ is_chassis_resident_cb(const void *c_aux_, const char *port_name)
>   {
>       const struct condition_aux *c_aux = c_aux_;
>   
> -    /* Store the port name that used to lookup the lport to lflow reference, so
> -     * that in the future when the lport's port-binding changes the logical
> +    /* Store the port name that used to lookup the lport to object reference,
> +     * so that in the future when the lport's port-binding changes the logical
>        * flow that references this lport can be reprocessed. */
> -    lflow_resource_add(c_aux->lfrr, REF_TYPE_PORTBINDING, port_name,
> -                       &c_aux->lflow->header_.uuid, 0);
> +    objdep_mgr_add(c_aux->deps_mgr, OBJDEP_TYPE_PORTBINDING, port_name,
> +                   &c_aux->lflow->header_.uuid);
>   
>       const struct sbrec_port_binding *pb
>           = lport_lookup_by_name(c_aux->sbrec_port_binding_by_name, port_name);
> @@ -207,155 +196,6 @@ is_chassis_resident_cb(const void *c_aux_, const char *port_name)
>       }
>   }
>   
> -void
> -lflow_resource_init(struct lflow_resource_ref *lfrr)
> -{
> -    hmap_init(&lfrr->ref_lflow_table);
> -    hmap_init(&lfrr->lflow_ref_table);
> -}
> -
> -void
> -lflow_resource_destroy(struct lflow_resource_ref *lfrr)
> -{
> -    struct ref_lflow_node *rlfn;
> -    HMAP_FOR_EACH_SAFE (rlfn, node, &lfrr->ref_lflow_table) {
> -        struct lflow_ref_list_node *lrln;
> -        HMAP_FOR_EACH_SAFE (lrln, hmap_node, &rlfn->lflow_uuids) {
> -            ovs_list_remove(&lrln->list_node);
> -            hmap_remove(&rlfn->lflow_uuids, &lrln->hmap_node);
> -            free(lrln);
> -        }
> -        hmap_remove(&lfrr->ref_lflow_table, &rlfn->node);
> -        ref_lflow_node_destroy(rlfn);
> -    }
> -    hmap_destroy(&lfrr->ref_lflow_table);
> -
> -    struct lflow_ref_node *lfrn;
> -    HMAP_FOR_EACH_SAFE (lfrn, node, &lfrr->lflow_ref_table) {
> -        hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
> -        free(lfrn);
> -    }
> -    hmap_destroy(&lfrr->lflow_ref_table);
> -}
> -
> -void
> -lflow_resource_clear(struct lflow_resource_ref *lfrr)
> -{
> -    lflow_resource_destroy(lfrr);
> -    lflow_resource_init(lfrr);
> -}
> -
> -static struct ref_lflow_node*
> -ref_lflow_lookup(struct hmap *ref_lflow_table,
> -                 enum ref_type type, const char *ref_name)
> -{
> -    struct ref_lflow_node *rlfn;
> -
> -    HMAP_FOR_EACH_WITH_HASH (rlfn, node, hash_string(ref_name, type),
> -                             ref_lflow_table) {
> -        if (rlfn->type == type && !strcmp(rlfn->ref_name, ref_name)) {
> -            return rlfn;
> -        }
> -    }
> -    return NULL;
> -}
> -
> -static struct lflow_ref_node*
> -lflow_ref_lookup(struct hmap *lflow_ref_table,
> -                 const struct uuid *lflow_uuid)
> -{
> -    struct lflow_ref_node *lfrn;
> -
> -    HMAP_FOR_EACH_WITH_HASH (lfrn, node, uuid_hash(lflow_uuid),
> -                             lflow_ref_table) {
> -        if (uuid_equals(&lfrn->lflow_uuid, lflow_uuid)) {
> -            return lfrn;
> -        }
> -    }
> -    return NULL;
> -}
> -
> -static void
> -lflow_resource_add(struct lflow_resource_ref *lfrr, enum ref_type type,
> -                   const char *ref_name, const struct uuid *lflow_uuid,
> -                   size_t ref_count)
> -{
> -    struct ref_lflow_node *rlfn = ref_lflow_lookup(&lfrr->ref_lflow_table,
> -                                                   type, ref_name);
> -    struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
> -                                                   lflow_uuid);
> -    if (rlfn && lfrn) {
> -        /* Check if the mapping already existed before adding a new one. */
> -        struct lflow_ref_list_node *n;
> -        HMAP_FOR_EACH_WITH_HASH (n, hmap_node, uuid_hash(lflow_uuid),
> -                                 &rlfn->lflow_uuids) {
> -            if (uuid_equals(&n->lflow_uuid, lflow_uuid)) {
> -                return;
> -            }
> -        }
> -    }
> -
> -    if (!rlfn) {
> -        rlfn = xzalloc(sizeof *rlfn);
> -        rlfn->node.hash = hash_string(ref_name, type);
> -        rlfn->type = type;
> -        rlfn->ref_name = xstrdup(ref_name);
> -        hmap_init(&rlfn->lflow_uuids);
> -        hmap_insert(&lfrr->ref_lflow_table, &rlfn->node, rlfn->node.hash);
> -    }
> -
> -    if (!lfrn) {
> -        lfrn = xzalloc(sizeof *lfrn);
> -        lfrn->node.hash = uuid_hash(lflow_uuid);
> -        lfrn->lflow_uuid = *lflow_uuid;
> -        ovs_list_init(&lfrn->lflow_ref_head);
> -        hmap_insert(&lfrr->lflow_ref_table, &lfrn->node, lfrn->node.hash);
> -    }
> -
> -    struct lflow_ref_list_node *lrln = xzalloc(sizeof *lrln);
> -    lrln->lflow_uuid = *lflow_uuid;
> -    lrln->ref_count = ref_count;
> -    lrln->rlfn = rlfn;
> -    hmap_insert(&rlfn->lflow_uuids, &lrln->hmap_node, uuid_hash(lflow_uuid));
> -    ovs_list_push_back(&lfrn->lflow_ref_head, &lrln->list_node);
> -}
> -
> -static void
> -ref_lflow_node_destroy(struct ref_lflow_node *rlfn)
> -{
> -    free(rlfn->ref_name);
> -    hmap_destroy(&rlfn->lflow_uuids);
> -    free(rlfn);
> -}
> -
> -static void
> -lflow_resource_destroy_lflow(struct lflow_resource_ref *lfrr,
> -                            const struct uuid *lflow_uuid)
> -{
> -    struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
> -                                                   lflow_uuid);
> -    if (!lfrn) {
> -        return;
> -    }
> -
> -    hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
> -    struct lflow_ref_list_node *lrln;
> -    LIST_FOR_EACH_SAFE (lrln, list_node, &lfrn->lflow_ref_head) {
> -        ovs_list_remove(&lrln->list_node);
> -        hmap_remove(&lrln->rlfn->lflow_uuids, &lrln->hmap_node);
> -
> -        /* Clean up the node in ref_lflow_table if the resource is not
> -         * referred by any logical flows. */
> -        if (hmap_is_empty(&lrln->rlfn->lflow_uuids)) {
> -            hmap_remove(&lfrr->ref_lflow_table, &lrln->rlfn->node);
> -            ref_lflow_node_destroy(lrln->rlfn);
> -        }
> -
> -        free(lrln);
> -    }
> -    free(lfrn);
> -}
> -
>   /* Adds the logical flows from the Logical_Flow table to flow tables. */
>   static void
>   add_logical_flows(struct lflow_ctx_in *l_ctx_in,
> @@ -400,7 +240,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in *l_ctx_in,
>       struct uuidset_node *ofrn;
>       UUIDSET_FOR_EACH (ofrn, &flood_remove_nodes) {
>           /* Delete entries from lflow resource reference. */
> -        lflow_resource_destroy_lflow(l_ctx_out->lfrr, &ofrn->uuid);
> +        objdep_mgr_remove_obj(l_ctx_out->lflow_deps_mgr, &ofrn->uuid);
>           /* Delete conj_ids owned by the lflow. */
>           lflow_conj_ids_free(l_ctx_out->conj_ids, &ofrn->uuid);
>           /* Reprocessing the lflow if the sb record is not deleted. */
> @@ -537,7 +377,7 @@ consider_lflow_for_added_as_ips__(
>           .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
>           .dp = dp,
>           .lflow = lflow,
> -        .lfrr = l_ctx_out->lfrr,
> +        .deps_mgr = l_ctx_out->lflow_deps_mgr,
>       };
>       struct condition_aux cond_aux = {
>           .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
> @@ -545,7 +385,7 @@ consider_lflow_for_added_as_ips__(
>           .chassis = l_ctx_in->chassis,
>           .active_tunnels = l_ctx_in->active_tunnels,
>           .lflow = lflow,
> -        .lfrr = l_ctx_out->lfrr,
> +        .deps_mgr = l_ctx_out->lflow_deps_mgr,
>       };
>   
>       struct hmap matches = HMAP_INITIALIZER(&matches);
> @@ -591,7 +431,7 @@ consider_lflow_for_added_as_ips__(
>       struct expr *expr = convert_match_to_expr(lflow, ldp, &prereqs,
>                                                 l_ctx_in->addr_sets,
>                                                 l_ctx_in->port_groups,
> -                                              l_ctx_out->lfrr, NULL);
> +                                              l_ctx_out->lflow_deps_mgr, NULL);
>       shash_replace((struct shash *)l_ctx_in->addr_sets, as_name, real_as);
>       if (new_fake_as) {
>           expr_constant_set_destroy(new_fake_as);
> @@ -787,10 +627,10 @@ lflow_handle_addr_set_update(const char *as_name,
>           return false;
>       }
>   
> -    struct ref_lflow_node *rlfn =
> -        ref_lflow_lookup(&l_ctx_out->lfrr->ref_lflow_table, REF_TYPE_ADDRSET,
> -                         as_name);
> -    if (!rlfn) {
> +    struct resource_to_objects_node *resource_node =
> +        objdep_mgr_find_objs(l_ctx_out->lflow_deps_mgr, OBJDEP_TYPE_ADDRSET,
> +                             as_name);
> +    if (!resource_node) {
>           *changed = false;
>           return true;
>       }
> @@ -798,22 +638,23 @@ lflow_handle_addr_set_update(const char *as_name,
>       *changed = false;
>   
>       bool ret = true;
> -    struct lflow_ref_list_node *lrln;
> -    HMAP_FOR_EACH (lrln, hmap_node, &rlfn->lflow_uuids) {
> -        if (uuidset_find(l_ctx_out->lflows_processed, &lrln->lflow_uuid)) {
> +    struct object_to_resources_list_node *resource_list_node;
> +    RESOURCE_FOR_EACH_OBJ (resource_list_node, resource_node) {
> +        const struct uuid *obj_uuid = &resource_list_node->obj_uuid;
> +        if (uuidset_find(l_ctx_out->lflows_processed, obj_uuid)) {
>               VLOG_DBG("lflow "UUID_FMT"has been processed, skip.",
> -                     UUID_ARGS(&lrln->lflow_uuid));
> +                     UUID_ARGS(obj_uuid));
>               continue;
>           }
>           const struct sbrec_logical_flow *lflow =
>               sbrec_logical_flow_table_get_for_uuid(l_ctx_in->logical_flow_table,
> -                                                  &lrln->lflow_uuid);
> +                                                  obj_uuid);
>           if (!lflow) {
>               /* lflow deletion should be handled in the corresponding input
>                * handler, so we can skip here. */
>               VLOG_DBG("lflow "UUID_FMT" not found while handling updates of "
>                        "address set %s, skip.",
> -                     UUID_ARGS(&lrln->lflow_uuid), as_name);
> +                     UUID_ARGS(obj_uuid), as_name);
>               continue;
>           }
>           *changed = true;
> @@ -825,9 +666,9 @@ lflow_handle_addr_set_update(const char *as_name,
>                   if (!as_info_from_expr_const(as_name, c, &as_info)) {
>                       continue;
>                   }
> -                if (!ofctrl_remove_flows_for_as_ip(l_ctx_out->flow_table,
> -                                                   &lrln->lflow_uuid, &as_info,
> -                                                   lrln->ref_count)) {
> +                if (!ofctrl_remove_flows_for_as_ip(
> +                        l_ctx_out->flow_table, obj_uuid, &as_info,
> +                        resource_list_node->ref_count)) {
>                       ret = false;
>                       goto done;
>                   }
> @@ -836,7 +677,7 @@ lflow_handle_addr_set_update(const char *as_name,
>   
>           if (as_diff->added) {
>               if (!consider_lflow_for_added_as_ips(lflow, as_name,
> -                                                 lrln->ref_count,
> +                                                 resource_list_node->ref_count,
>                                                    as_diff->added,
>                                                    l_ctx_in, l_ctx_out)) {
>                   ret = false;
> @@ -850,59 +691,32 @@ done:
>   }
>   
>   bool
> -lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
> -                         struct lflow_ctx_in *l_ctx_in,
> -                         struct lflow_ctx_out *l_ctx_out,
> -                         bool *changed)
> +lflow_handle_changed_ref(enum objdep_type type, const char *res_name,
> +                         struct ovs_list *objs_todo,
> +                         const void *in_arg, void *out_arg)
>   {
> -    struct ref_lflow_node *rlfn =
> -        ref_lflow_lookup(&l_ctx_out->lfrr->ref_lflow_table, ref_type,
> -                         ref_name);
> -    if (!rlfn) {
> -        *changed = false;
> -        return true;
> -    }
> -    VLOG_DBG("Handle changed lflow reference for resource type: %d,"
> -             " name: %s.", ref_type, ref_name);
> -    *changed = false;
> -    bool ret = true;
> -
> -    struct ovs_list lflows_todo = OVS_LIST_INITIALIZER(&lflows_todo);
> -
> -    struct lflow_ref_list_node *lrln, *lrln_uuid;
> -    HMAP_FOR_EACH (lrln, hmap_node, &rlfn->lflow_uuids) {
> -        if (uuidset_find(l_ctx_out->lflows_processed, &lrln->lflow_uuid)) {
> -            continue;
> -        }
> -        /* Use lflow_ref_list_node as list node to store the uuid.
> -         * Other fields are not used here. */
> -        lrln_uuid = xmalloc(sizeof *lrln_uuid);
> -        lrln_uuid->lflow_uuid = lrln->lflow_uuid;
> -        ovs_list_push_back(&lflows_todo, &lrln_uuid->list_node);
> -    }
> -    if (ovs_list_is_empty(&lflows_todo)) {
> -        return true;
> -    }
> -    *changed = true;
> +    struct lflow_ctx_in *l_ctx_in = CONST_CAST(struct lflow_ctx_in *, in_arg);
> +    struct lflow_ctx_out *l_ctx_out = out_arg;
>   
>       /* Re-parse the related lflows. */
>       /* Firstly, flood remove the flows from desired flow table. */
> +    struct object_to_resources_list_node *resource_list_node_uuid;
>       struct uuidset flood_remove_nodes =
>           UUIDSET_INITIALIZER(&flood_remove_nodes);
> -    LIST_FOR_EACH_SAFE (lrln_uuid, list_node, &lflows_todo) {
> -        VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d,"
> +    LIST_FOR_EACH_SAFE (resource_list_node_uuid, list_node, objs_todo) {
> +        const struct uuid *obj_uuid = &resource_list_node_uuid->obj_uuid;
> +        VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %s,"
>                    " name: %s.",
> -                 UUID_ARGS(&lrln_uuid->lflow_uuid),
> -                 ref_type, ref_name);
> -        uuidset_insert(&flood_remove_nodes, &lrln_uuid->lflow_uuid);
> -        free(lrln_uuid);
> +                 UUID_ARGS(obj_uuid), objdep_type_name(type), res_name);
> +        uuidset_insert(&flood_remove_nodes, obj_uuid);
> +        free(resource_list_node_uuid);
>       }
>       ofctrl_flood_remove_flows(l_ctx_out->flow_table, &flood_remove_nodes);
>   
>       /* Secondly, for each lflow that is actually removed, reprocessing it. */
>       struct uuidset_node *ofrn;
>       UUIDSET_FOR_EACH (ofrn, &flood_remove_nodes) {
> -        lflow_resource_destroy_lflow(l_ctx_out->lfrr, &ofrn->uuid);
> +        objdep_mgr_remove_obj(l_ctx_out->lflow_deps_mgr, &ofrn->uuid);
>           lflow_conj_ids_free(l_ctx_out->conj_ids, &ofrn->uuid);
>   
>           const struct sbrec_logical_flow *lflow =
> @@ -910,9 +724,9 @@ lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
>                                                     &ofrn->uuid);
>           if (!lflow) {
>               VLOG_DBG("lflow "UUID_FMT" not found while reprocessing for"
> -                     " resource type: %d, name: %s.",
> +                     " resource type: %s, name: %s.",
>                        UUID_ARGS(&ofrn->uuid),
> -                     ref_type, ref_name);
> +                     objdep_type_name(type), res_name);
>               continue;
>           }
>   
> @@ -929,8 +743,7 @@ lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
>           consider_logical_flow(lflow, false, l_ctx_in, l_ctx_out);
>       }
>       uuidset_destroy(&flood_remove_nodes);
> -
> -    return ret;
> +    return true;
>   }
>   
>   static void
> @@ -985,7 +798,7 @@ add_matches_to_flow_table(const struct sbrec_logical_flow *lflow,
>           .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
>           .dp = ldp->datapath,
>           .lflow = lflow,
> -        .lfrr = l_ctx_out->lfrr,
> +        .deps_mgr = l_ctx_out->lflow_deps_mgr,
>           .chassis_tunnels = l_ctx_in->chassis_tunnels,
>       };
>   
> @@ -1100,7 +913,7 @@ convert_match_to_expr(const struct sbrec_logical_flow *lflow,
>                         struct expr **prereqs,
>                         const struct shash *addr_sets,
>                         const struct shash *port_groups,
> -                      struct lflow_resource_ref *lfrr,
> +                      struct objdep_mgr *mgr,
>                         bool *pg_addr_set_ref)
>   {
>       struct shash addr_sets_ref = SHASH_INITIALIZER(&addr_sets_ref);
> @@ -1114,14 +927,15 @@ convert_match_to_expr(const struct sbrec_logical_flow *lflow,
>                                          &error);
>       struct shash_node *addr_sets_ref_node;
>       SHASH_FOR_EACH (addr_sets_ref_node, &addr_sets_ref) {
> -        lflow_resource_add(lfrr, REF_TYPE_ADDRSET, addr_sets_ref_node->name,
> -                           &lflow->header_.uuid,
> -                           *(size_t *)addr_sets_ref_node->data);
> +        objdep_mgr_add_with_refcount(mgr, OBJDEP_TYPE_ADDRSET,
> +                                     addr_sets_ref_node->name,
> +                                     &lflow->header_.uuid,
> +                                     *(size_t *) addr_sets_ref_node->data);
>       }
>       const char *port_group_name;
>       SSET_FOR_EACH (port_group_name, &port_groups_ref) {
> -        lflow_resource_add(lfrr, REF_TYPE_PORTGROUP, port_group_name,
> -                           &lflow->header_.uuid, 0);
> +        objdep_mgr_add(mgr, OBJDEP_TYPE_PORTGROUP, port_group_name,
> +                       &lflow->header_.uuid);
>       }
>   
>       if (pg_addr_set_ref) {
> @@ -1165,8 +979,8 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
>   
>       const char *io_port = smap_get(&lflow->tags, "in_out_port");
>       if (io_port) {
> -        lflow_resource_add(l_ctx_out->lfrr, REF_TYPE_PORTBINDING, io_port,
> -                           &lflow->header_.uuid, 0);
> +        objdep_mgr_add(l_ctx_out->lflow_deps_mgr, OBJDEP_TYPE_PORTBINDING,
> +                       io_port, &lflow->header_.uuid);
>           const struct sbrec_port_binding *pb
>               = lport_lookup_by_name(l_ctx_in->sbrec_port_binding_by_name,
>                                      io_port);
> @@ -1232,7 +1046,7 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
>           .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
>           .dp = dp,
>           .lflow = lflow,
> -        .lfrr = l_ctx_out->lfrr,
> +        .deps_mgr = l_ctx_out->lflow_deps_mgr,
>       };
>       struct condition_aux cond_aux = {
>           .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
> @@ -1240,7 +1054,7 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
>           .chassis = l_ctx_in->chassis,
>           .active_tunnels = l_ctx_in->active_tunnels,
>           .lflow = lflow,
> -        .lfrr = l_ctx_out->lfrr,
> +        .deps_mgr = l_ctx_out->lflow_deps_mgr,
>       };
>   
>       struct lflow_cache_value *lcv =
> @@ -1272,7 +1086,8 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
>       switch (lcv_type) {
>       case LCACHE_T_NONE:
>           expr = convert_match_to_expr(lflow, ldp, &prereqs, l_ctx_in->addr_sets,
> -                                     l_ctx_in->port_groups, l_ctx_out->lfrr,
> +                                     l_ctx_in->port_groups,
> +                                     l_ctx_out->lflow_deps_mgr,
>                                        &pg_addr_set_ref);
>           if (!expr) {
>               goto done;
> @@ -1345,8 +1160,8 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
>           /* Cache new entry if caching is enabled. */
>           if (lflow_cache_is_enabled(l_ctx_out->lflow_cache)) {
>               if (cached_expr
> -                && !lflow_ref_lookup(&l_ctx_out->lfrr->lflow_ref_table,
> -                                     &lflow->header_.uuid)) {
> +                && !objdep_mgr_contains_obj(l_ctx_out->lflow_deps_mgr,
> +                                            &lflow->header_.uuid)) {
>                   lflow_cache_add_matches(l_ctx_out->lflow_cache,
>                                           &lflow->header_.uuid, start_conj_id,
>                                           n_conjs, matches, matches_size);
> @@ -2448,7 +2263,7 @@ lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *dp,
>                                &lflow->header_.uuid)) {
>                   continue;
>               }
> -            /* Don't call lflows_processed_add() because here we process the
> +            /* Don't call uuidset_insert() because here we process the
>                * lflow only for one of the DPs in the DP group, which may be
>                * incomplete. */
>               consider_logical_flow__(lflow, dp, l_ctx_in, l_ctx_out);
> @@ -2514,7 +2329,11 @@ lflow_handle_flows_for_lport(const struct sbrec_port_binding *pb,
>   {
>       bool changed;
>   
> -    if (!lflow_handle_changed_ref(REF_TYPE_PORTBINDING, pb->logical_port,
> +    if (!objdep_mgr_handle_change(l_ctx_out->lflow_deps_mgr,
> +                                  OBJDEP_TYPE_PORTBINDING,
> +                                  pb->logical_port,
> +                                  lflow_handle_changed_ref,
> +                                  l_ctx_out->lflows_processed,
>                                     l_ctx_in, l_ctx_out, &changed)) {
>           return false;
>       }
> @@ -2549,7 +2368,11 @@ lflow_handle_changed_port_bindings(struct lflow_ctx_in *l_ctx_in,
>               && !sbrec_port_binding_is_deleted(pb)) {
>               continue;
>           }
> -        if (!lflow_handle_changed_ref(REF_TYPE_PORTBINDING, pb->logical_port,
> +        if (!objdep_mgr_handle_change(l_ctx_out->lflow_deps_mgr,
> +                                      OBJDEP_TYPE_PORTBINDING,
> +                                      pb->logical_port,
> +                                      lflow_handle_changed_ref,
> +                                      l_ctx_out->lflows_processed,
>                                         l_ctx_in, l_ctx_out, &changed)) {
>               ret = false;
>               break;
> @@ -2573,7 +2396,10 @@ lflow_handle_changed_mc_groups(struct lflow_ctx_in *l_ctx_in,
>               && !sbrec_multicast_group_is_deleted(mg)) {
>               continue;
>           }
> -        if (!lflow_handle_changed_ref(REF_TYPE_MC_GROUP, ds_cstr(&mg_key),
> +        if (!objdep_mgr_handle_change(l_ctx_out->lflow_deps_mgr,
> +                                      OBJDEP_TYPE_MC_GROUP, ds_cstr(&mg_key),
> +                                      lflow_handle_changed_ref,
> +                                      l_ctx_out->lflows_processed,
>                                         l_ctx_in, l_ctx_out, &changed)) {
>               ret = false;
>               break;
> diff --git a/controller/lflow.h b/controller/lflow.h
> index 8cbe312cac..3f369ebfb6 100644
> --- a/controller/lflow.h
> +++ b/controller/lflow.h
> @@ -16,6 +16,9 @@
>   #ifndef OVN_LFLOW_H
>   #define OVN_LFLOW_H 1
>   
> +#include "lib/ovn-util.h"
> +#include "lib/objdep.h"
> +#include "lib/uuidset.h"
>   #include "ovn/logical-fields.h"
>   
>   /* Logical_Flow table translation to OpenFlow
> @@ -80,59 +83,6 @@ struct uuid;
>   #define OFTABLE_ECMP_NH_MAC          76
>   #define OFTABLE_ECMP_NH              77
>   
> -enum ref_type {
> -    REF_TYPE_ADDRSET,
> -    REF_TYPE_PORTGROUP,
> -    REF_TYPE_PORTBINDING,
> -    REF_TYPE_MC_GROUP
> -};
> -
> -struct ref_lflow_node {
> -    struct hmap_node node; /* node in lflow_resource_ref.ref_lflow_table. */
> -    enum ref_type type; /* key */
> -    char *ref_name; /* key */
> -    struct hmap lflow_uuids; /* Contains lflow_ref_list_node. Use hmap instead
> -                                of list so that lflow_resource_add() can check
> -                                and avoid adding redundant entires in O(1). */
> -};
> -
> -struct lflow_ref_node {
> -    struct hmap_node node; /* node in lflow_resource_ref.lflow_ref_table. */
> -    struct uuid lflow_uuid; /* key */
> -    struct ovs_list lflow_ref_head; /* Contains lflow_ref_list_node. */
> -};
> -
> -/* Maintains the relationship for a pair of named resource and
> - * a lflow, indexed by both ref_lflow_table and lflow_ref_table. */
> -struct lflow_ref_list_node {
> -    struct ovs_list list_node; /* node in lflow_ref_node.lflow_ref_head. */
> -    struct hmap_node hmap_node; /* node in ref_lflow_node.lflow_uuids. */
> -    struct uuid lflow_uuid;
> -    size_t ref_count; /* Reference count of the resource by this lflow.
> -                         Currently used for the resource type REF_TYPE_ADDRSET
> -                         only, and for other types it is always 0. */
> -    struct ref_lflow_node *rlfn;
> -};
> -
> -struct lflow_resource_ref {
> -    /* A map from a referenced resource type & name (e.g. address_set AS1)
> -     * to a list of lflows that are referencing the named resource. Data
> -     * type of each node in this hmap is struct ref_lflow_node. The
> -     * ref_lflow_head in each node points to a list of
> -     * lflow_ref_list_node.ref_list. */
> -    struct hmap ref_lflow_table;
> -
> -    /* A map from a lflow uuid to a list of named resources that are
> -     * referenced by the lflow. Data type of each node in this hmap is
> -     * struct lflow_ref_node. The lflow_ref_head in each node points to
> -     * a list of lflow_ref_list_node.lflow_list. */
> -    struct hmap lflow_ref_table;
> -};
> -
> -void lflow_resource_init(struct lflow_resource_ref *);
> -void lflow_resource_destroy(struct lflow_resource_ref *);
> -void lflow_resource_clear(struct lflow_resource_ref *);
> -
>   struct lflow_ctx_in {
>       struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
>       struct ovsdb_idl_index *sbrec_logical_flow_by_logical_datapath;
> @@ -169,7 +119,7 @@ struct lflow_ctx_out {
>       struct ovn_desired_flow_table *flow_table;
>       struct ovn_extend_table *group_table;
>       struct ovn_extend_table *meter_table;
> -    struct lflow_resource_ref *lfrr;
> +    struct objdep_mgr *lflow_deps_mgr;
>       struct lflow_cache *lflow_cache;
>       struct conj_ids *conj_ids;
>       struct uuidset *lflows_processed;
> @@ -181,10 +131,8 @@ void lflow_init(void);
>   void lflow_run(struct lflow_ctx_in *, struct lflow_ctx_out *);
>   void lflow_handle_cached_flows(struct lflow_cache *,
>                                  const struct sbrec_logical_flow_table *);
> -bool lflow_handle_changed_flows(struct lflow_ctx_in *, struct lflow_ctx_out *);
> -bool lflow_handle_changed_ref(enum ref_type, const char *ref_name,
> -                              struct lflow_ctx_in *, struct lflow_ctx_out *,
> -                              bool *changed);
> +bool lflow_handle_changed_flows(struct lflow_ctx_in *,
> +                                struct lflow_ctx_out *);
>   
>   struct addr_set_diff {
>       struct expr_constant_set *added;
> @@ -194,6 +142,9 @@ bool lflow_handle_addr_set_update(const char *as_name, struct addr_set_diff *,
>                                     struct lflow_ctx_in *,
>                                     struct lflow_ctx_out *,
>                                     bool *changed);
> +bool lflow_handle_changed_ref(enum objdep_type, const char *res_name,
> +                              struct ovs_list *objs_todo,
> +                              const void *in_arg, void *out_arg);
>   
>   void lflow_handle_changed_mac_bindings(
>       struct ovsdb_idl_index *sbrec_port_binding_by_name,
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 8895c7a2bd..f62303e3cb 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -2578,8 +2578,8 @@ struct ed_type_lflow_output {
>       struct ovn_extend_table group_table;
>       /* meter ids for QoS */
>       struct ovn_extend_table meter_table;
> -    /* lflow resource cross reference */
> -    struct lflow_resource_ref lflow_resource_ref;
> +    /* lflow <-> resource cross reference */
> +    struct objdep_mgr lflow_deps_mgr;;
>       /* conjunciton ID usage information of lflows */
>       struct conj_ids conj_ids;
>   
> @@ -2738,7 +2738,7 @@ init_lflow_ctx(struct engine_node *node,
>       l_ctx_out->flow_table = &fo->flow_table;
>       l_ctx_out->group_table = &fo->group_table;
>       l_ctx_out->meter_table = &fo->meter_table;
> -    l_ctx_out->lfrr = &fo->lflow_resource_ref;
> +    l_ctx_out->lflow_deps_mgr = &fo->lflow_deps_mgr;
>       l_ctx_out->conj_ids = &fo->conj_ids;
>       l_ctx_out->lflows_processed = &fo->lflows_processed;
>       l_ctx_out->lflow_cache = fo->pd.lflow_cache;
> @@ -2754,7 +2754,7 @@ en_lflow_output_init(struct engine_node *node OVS_UNUSED,
>       ovn_desired_flow_table_init(&data->flow_table);
>       ovn_extend_table_init(&data->group_table);
>       ovn_extend_table_init(&data->meter_table);
> -    lflow_resource_init(&data->lflow_resource_ref);
> +    objdep_mgr_init(&data->lflow_deps_mgr);
>       lflow_conj_ids_init(&data->conj_ids);
>       uuidset_init(&data->lflows_processed);
>       simap_init(&data->hd.ids);
> @@ -2778,7 +2778,7 @@ en_lflow_output_cleanup(void *data)
>       ovn_desired_flow_table_destroy(&flow_output_data->flow_table);
>       ovn_extend_table_destroy(&flow_output_data->group_table);
>       ovn_extend_table_destroy(&flow_output_data->meter_table);
> -    lflow_resource_destroy(&flow_output_data->lflow_resource_ref);
> +    objdep_mgr_destroy(&flow_output_data->lflow_deps_mgr);
>       lflow_conj_ids_destroy(&flow_output_data->conj_ids);
>       uuidset_destroy(&flow_output_data->lflows_processed);
>       lflow_cache_destroy(flow_output_data->pd.lflow_cache);
> @@ -2814,7 +2814,7 @@ en_lflow_output_run(struct engine_node *node, void *data)
>       struct ovn_desired_flow_table *lflow_table = &fo->flow_table;
>       struct ovn_extend_table *group_table = &fo->group_table;
>       struct ovn_extend_table *meter_table = &fo->meter_table;
> -    struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref;
> +    struct objdep_mgr *lflow_deps_mgr = &fo->lflow_deps_mgr;
>   
>       static bool first_run = true;
>       if (first_run) {
> @@ -2823,7 +2823,7 @@ en_lflow_output_run(struct engine_node *node, void *data)
>           ovn_desired_flow_table_clear(lflow_table);
>           ovn_extend_table_clear(group_table, false /* desired */);
>           ovn_extend_table_clear(meter_table, false /* desired */);
> -        lflow_resource_clear(lfrr);
> +        objdep_mgr_clear(lflow_deps_mgr);
>           lflow_conj_ids_clear(&fo->conj_ids);
>       }
>   
> @@ -2954,8 +2954,11 @@ lflow_output_addr_sets_handler(struct engine_node *node, void *data)
>       }
>   
>       SSET_FOR_EACH (ref_name, &as_data->deleted) {
> -        if (!lflow_handle_changed_ref(REF_TYPE_ADDRSET, ref_name, &l_ctx_in,
> -                                      &l_ctx_out, &changed)) {
> +        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
> +                                      OBJDEP_TYPE_ADDRSET, ref_name,
> +                                      lflow_handle_changed_ref,
> +                                      l_ctx_out.lflows_processed,
> +                                      &l_ctx_in, &l_ctx_out, &changed)) {
>               return false;
>           }
>           if (changed) {
> @@ -2969,7 +2972,11 @@ lflow_output_addr_sets_handler(struct engine_node *node, void *data)
>                                             &l_ctx_out, &changed)) {
>               VLOG_DBG("Can't incrementally handle the change of address set %s."
>                        " Reprocess related lflows.", shash_node->name);
> -            if (!lflow_handle_changed_ref(REF_TYPE_ADDRSET, shash_node->name,
> +            if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
> +                                          OBJDEP_TYPE_ADDRSET,
> +                                          shash_node->name,
> +                                          lflow_handle_changed_ref,
> +                                          l_ctx_out.lflows_processed,
>                                             &l_ctx_in, &l_ctx_out, &changed)) {
>                   return false;
>               }
> @@ -2979,8 +2986,11 @@ lflow_output_addr_sets_handler(struct engine_node *node, void *data)
>           }
>       }
>       SSET_FOR_EACH (ref_name, &as_data->new) {
> -        if (!lflow_handle_changed_ref(REF_TYPE_ADDRSET, ref_name, &l_ctx_in,
> -                                      &l_ctx_out, &changed)) {
> +        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
> +                                      OBJDEP_TYPE_ADDRSET, ref_name,
> +                                      lflow_handle_changed_ref,
> +                                      l_ctx_out.lflows_processed,
> +                                      &l_ctx_in, &l_ctx_out, &changed)) {
>               return false;
>           }
>           if (changed) {
> @@ -3011,8 +3021,11 @@ lflow_output_port_groups_handler(struct engine_node *node, void *data)
>       }
>   
>       SSET_FOR_EACH (ref_name, &pg_data->deleted) {
> -        if (!lflow_handle_changed_ref(REF_TYPE_PORTGROUP, ref_name, &l_ctx_in,
> -                                      &l_ctx_out, &changed)) {
> +        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
> +                                      OBJDEP_TYPE_PORTGROUP, ref_name,
> +                                      lflow_handle_changed_ref,
> +                                      l_ctx_out.lflows_processed,
> +                                      &l_ctx_in, &l_ctx_out, &changed)) {
>               return false;
>           }
>           if (changed) {
> @@ -3020,8 +3033,11 @@ lflow_output_port_groups_handler(struct engine_node *node, void *data)
>           }
>       }
>       SSET_FOR_EACH (ref_name, &pg_data->updated) {
> -        if (!lflow_handle_changed_ref(REF_TYPE_PORTGROUP, ref_name, &l_ctx_in,
> -                                      &l_ctx_out, &changed)) {
> +        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
> +                                      OBJDEP_TYPE_PORTGROUP, ref_name,
> +                                      lflow_handle_changed_ref,
> +                                      l_ctx_out.lflows_processed,
> +                                      &l_ctx_in, &l_ctx_out, &changed)) {
>               return false;
>           }
>           if (changed) {
> @@ -3029,8 +3045,11 @@ lflow_output_port_groups_handler(struct engine_node *node, void *data)
>           }
>       }
>       SSET_FOR_EACH (ref_name, &pg_data->new) {
> -        if (!lflow_handle_changed_ref(REF_TYPE_PORTGROUP, ref_name, &l_ctx_in,
> -                                      &l_ctx_out, &changed)) {
> +        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
> +                                      OBJDEP_TYPE_PORTGROUP, ref_name,
> +                                      lflow_handle_changed_ref,
> +                                      l_ctx_out.lflows_processed,
> +                                      &l_ctx_in, &l_ctx_out, &changed)) {
>               return false;
>           }
>           if (changed) {
> diff --git a/lib/automake.mk b/lib/automake.mk
> index 60bead6a6a..15d4f84bbb 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -31,6 +31,8 @@ lib_libovn_la_SOURCES = \
>   	lib/mcast-group-index.c \
>   	lib/mcast-group-index.h \
>   	lib/lex.c \
> +	lib/objdep.c \
> +	lib/objdep.h \
>   	lib/ovn-l7.h \
>   	lib/ovn-l7.c \
>   	lib/ovn-util.c \
> diff --git a/lib/objdep.c b/lib/objdep.c
> new file mode 100644
> index 0000000000..092d4af261
> --- /dev/null
> +++ b/lib/objdep.c
> @@ -0,0 +1,260 @@
> +/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> + * Copyright (c) 2022, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include "lib/objdep.h"
> +#include "lib/hash.h"
> +#include "lib/util.h"
> +#include "openvswitch/vlog.h"
> +
> +VLOG_DEFINE_THIS_MODULE(resource_dep);
> +
> +static void resource_node_destroy(struct resource_to_objects_node *);
> +
> +void
> +objdep_mgr_init(struct objdep_mgr *mgr)
> +{
> +    hmap_init(&mgr->resource_to_objects_table);
> +    hmap_init(&mgr->object_to_resources_table);
> +}
> +
> +void
> +objdep_mgr_destroy(struct objdep_mgr *mgr)
> +{
> +    objdep_mgr_clear(mgr);
> +
> +    hmap_destroy(&mgr->resource_to_objects_table);
> +    hmap_destroy(&mgr->object_to_resources_table);
> +}
> +
> +void
> +objdep_mgr_clear(struct objdep_mgr *mgr)
> +{
> +    struct resource_to_objects_node *resource_node;
> +    HMAP_FOR_EACH_SAFE (resource_node, node, &mgr->resource_to_objects_table) {
> +        struct object_to_resources_list_node *object_list_node;
> +        HMAP_FOR_EACH_SAFE (object_list_node, hmap_node,
> +                            &resource_node->objs) {
> +            ovs_list_remove(&object_list_node->list_node);
> +            hmap_remove(&resource_node->objs, &object_list_node->hmap_node);
> +            free(object_list_node);
> +        }
> +        hmap_remove(&mgr->resource_to_objects_table, &resource_node->node);
> +        resource_node_destroy(resource_node);
> +    }
> +
> +    struct object_to_resources_node *object_node;
> +    HMAP_FOR_EACH_SAFE (object_node, node, &mgr->object_to_resources_table) {
> +        hmap_remove(&mgr->object_to_resources_table, &object_node->node);
> +        free(object_node);
> +    }
> +}
> +
> +void
> +objdep_mgr_add(struct objdep_mgr *mgr, enum objdep_type type,
> +               const char *res_name, const struct uuid *obj_uuid)
> +{
> +    objdep_mgr_add_with_refcount(mgr, type, res_name, obj_uuid, 0);
> +}
> +
> +void
> +objdep_mgr_add_with_refcount(struct objdep_mgr *mgr, enum objdep_type type,
> +                             const char *res_name, const struct uuid *obj_uuid,
> +                             size_t ref_count)
> +{
> +    struct resource_to_objects_node *resource_node =
> +        objdep_mgr_find_objs(mgr, type, res_name);
> +    struct object_to_resources_node *object_node =
> +        objdep_mgr_find_resources(mgr, obj_uuid);
> +    if (resource_node && object_node) {
> +        /* Check if the mapping already existed before adding a new one. */
> +        struct object_to_resources_list_node *n;
> +        HMAP_FOR_EACH_WITH_HASH (n, hmap_node, uuid_hash(obj_uuid),
> +                                 &resource_node->objs) {
> +            if (uuid_equals(&n->obj_uuid, obj_uuid)) {
> +                return;
> +            }
> +        }
> +    }
> +
> +    /* Create the resource node if we didn't have one already (for a
> +     * different object). */
> +    if (!resource_node) {
> +        resource_node = xzalloc(sizeof *resource_node);
> +        resource_node->node.hash = hash_string(res_name, type);
> +        resource_node->type = type;
> +        resource_node->res_name = xstrdup(res_name);
> +        hmap_init(&resource_node->objs);
> +        hmap_insert(&mgr->resource_to_objects_table,
> +                    &resource_node->node,
> +                    resource_node->node.hash);
> +    }
> +
> +    /* Create the object node if we didn't have one already (for a
> +     * different resource). */
> +    if (!object_node) {
> +        object_node = xzalloc(sizeof *object_node);
> +        object_node->node.hash = uuid_hash(obj_uuid);
> +        object_node->obj_uuid = *obj_uuid;
> +        ovs_list_init(&object_node->resources_head);
> +        hmap_insert(&mgr->object_to_resources_table,
> +                    &object_node->node,
> +                    object_node->node.hash);
> +    }
> +
> +    struct object_to_resources_list_node *resource_list_node =
> +        xzalloc(sizeof *resource_list_node);
> +    resource_list_node->obj_uuid = *obj_uuid;
> +    resource_list_node->ref_count = ref_count;
> +    resource_list_node->resource_node = resource_node;
> +    hmap_insert(&resource_node->objs, &resource_list_node->hmap_node,
> +                uuid_hash(obj_uuid));
> +    ovs_list_push_back(&object_node->resources_head,
> +                       &resource_list_node->list_node);
> +}
> +
> +void
> +objdep_mgr_remove_obj(struct objdep_mgr *mgr, const struct uuid *obj_uuid)
> +{
> +    struct object_to_resources_node *object_node =
> +        objdep_mgr_find_resources(mgr, obj_uuid);
> +    if (!object_node) {
> +        return;
> +    }
> +
> +    hmap_remove(&mgr->object_to_resources_table, &object_node->node);
> +
> +    struct object_to_resources_list_node *resource_list_node;
> +    LIST_FOR_EACH_SAFE (resource_list_node, list_node,
> +                        &object_node->resources_head) {
> +        struct resource_to_objects_node *resource_node =
> +            resource_list_node->resource_node;
> +        ovs_list_remove(&resource_list_node->list_node);
> +        hmap_remove(&resource_node->objs, &resource_list_node->hmap_node);
> +
> +        /* Clean up the node in ref_obj_table if the resource is not
> +         * referred by any logical flows. */
> +        if (hmap_is_empty(&resource_node->objs)) {
> +            hmap_remove(&mgr->resource_to_objects_table, &resource_node->node);
> +            resource_node_destroy(resource_list_node->resource_node);
> +        }
> +
> +        free(resource_list_node);
> +    }
> +    free(object_node);
> +}
> +
> +struct resource_to_objects_node *
> +objdep_mgr_find_objs(struct objdep_mgr *mgr, enum objdep_type type,
> +                     const char *res_name)
> +{
> +    struct resource_to_objects_node *resource_node;
> +
> +    HMAP_FOR_EACH_WITH_HASH (resource_node, node, hash_string(res_name, type),
> +                             &mgr->resource_to_objects_table) {
> +        if (resource_node->type == type &&
> +                !strcmp(resource_node->res_name, res_name)) {
> +            return resource_node;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +struct object_to_resources_node *
> +objdep_mgr_find_resources(struct objdep_mgr *mgr,
> +                          const struct uuid *obj_uuid)
> +{
> +    struct object_to_resources_node *object_node;
> +
> +    HMAP_FOR_EACH_WITH_HASH (object_node, node, uuid_hash(obj_uuid),
> +                             &mgr->object_to_resources_table) {
> +        if (uuid_equals(&object_node->obj_uuid, obj_uuid)) {
> +            return object_node;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +bool
> +objdep_mgr_contains_obj(struct objdep_mgr *mgr, const struct uuid *obj_uuid)
> +{
> +    return !!objdep_mgr_find_resources(mgr, obj_uuid);
> +}
> +
> +bool
> +objdep_mgr_handle_change(struct objdep_mgr *mgr,
> +                         enum objdep_type type,
> +                         const char *res_name,
> +                         objdep_change_handler handler,
> +                         struct uuidset *objs_processed,
> +                         const void *in_arg, void *out_arg,
> +                         bool *changed)
> +{
> +    struct resource_to_objects_node *resource_node =
> +        objdep_mgr_find_objs(mgr, type, res_name);
> +    if (!resource_node) {
> +        *changed = false;
> +        return true;
> +    }
> +    VLOG_DBG("Handle changed object reference for resource type: %s,"
> +             " name: %s.", objdep_type_name(type), res_name);
> +    *changed = false;
> +
> +    struct ovs_list objs_todo = OVS_LIST_INITIALIZER(&objs_todo);
> +
> +    struct object_to_resources_list_node *resource_list_node;
> +    HMAP_FOR_EACH (resource_list_node, hmap_node, &resource_node->objs) {
> +        if (uuidset_find(objs_processed, &resource_list_node->obj_uuid)) {
> +            continue;
> +        }
> +        /* Use object_to_resources_list_node as list node to store the uuid.
> +         * Other fields are not used here. */
> +        struct object_to_resources_list_node *resource_list_node_uuid =
> +            xmalloc(sizeof *resource_list_node_uuid);
> +        resource_list_node_uuid->obj_uuid = resource_list_node->obj_uuid;
> +        ovs_list_push_back(&objs_todo, &resource_list_node_uuid->list_node);
> +    }
> +    if (ovs_list_is_empty(&objs_todo)) {
> +        return true;
> +    }
> +    *changed = true;
> +
> +    /* This takes ownership of objs_todo. */
> +    return handler(type, res_name, &objs_todo, in_arg, out_arg);
> +}
> +
> +const char *
> +objdep_type_name(enum objdep_type type)
> +{
> +    static const char *type_names[OBJDEP_TYPE_MAX] = {
> +        [OBJDEP_TYPE_ADDRSET] = "Address_Set",
> +        [OBJDEP_TYPE_PORTGROUP] = "Port_Group",
> +        [OBJDEP_TYPE_PORTBINDING] = "Port_Binding",
> +        [OBJDEP_TYPE_MC_GROUP] = "Multicast_Group",
> +    };
> +
> +    ovs_assert(type < OBJDEP_TYPE_MAX);
> +    return type_names[type];
> +}
> +
> +static void
> +resource_node_destroy(struct resource_to_objects_node *resource_node)
> +{
> +    free(resource_node->res_name);
> +    hmap_destroy(&resource_node->objs);
> +    free(resource_node);
> +}
> diff --git a/lib/objdep.h b/lib/objdep.h
> new file mode 100644
> index 0000000000..50c7b01ef1
> --- /dev/null
> +++ b/lib/objdep.h
> @@ -0,0 +1,122 @@
> +/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> + * Copyright (c) 2022, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef OVN_OBJDEP_H
> +#define OVN_OBJDEP_H 1
> +
> +#include "lib/uuidset.h"
> +#include "openvswitch/hmap.h"
> +#include "openvswitch/list.h"
> +
> +enum objdep_type {
> +    OBJDEP_TYPE_ADDRSET,
> +    OBJDEP_TYPE_PORTGROUP,
> +    OBJDEP_TYPE_PORTBINDING,
> +    OBJDEP_TYPE_MC_GROUP,
> +    OBJDEP_TYPE_MAX,
> +};
> +
> +/* Callbacks provided by users to process changes to resources referred by
> + * various objects.  They should return true if the change has been
> + * handled successfully. */
> +typedef bool (*objdep_change_handler)(enum objdep_type,
> +                                      const char *res_name,
> +                                      struct ovs_list *ref_nodes,
> +                                      const void *in_arg, void *out_arg);
> +
> +/* A node pointing to all objects that refer to a given resource. */
> +struct resource_to_objects_node {
> +    struct hmap_node node; /* node in objdep_mgr.resource_to_objects_table. */
> +    enum objdep_type type; /* key */
> +    char *res_name;        /* key */
> +    struct hmap objs;      /* Contains object_to_resources_list_node.
> +                            * Use hmap instead of list so
> +                            * that obj_resource_add() can check and avoid
> +                            * and redundant entries in O(1). */
> +};
> +
> +#define RESOURCE_FOR_EACH_OBJ(NODE, MAP) \
> +    HMAP_FOR_EACH (NODE, hmap_node, &(MAP)->objs)
> +
> +/* A node pointing to all resources used by a given object (specified by
> + * uuid).
> + */
> +struct object_to_resources_node {
> +    struct hmap_node node; /* node in objdep_mgr.object_to_resources_table. */
> +    struct uuid obj_uuid;  /* key */
> +    struct ovs_list resources_head; /* Contains elements of type
> +                                     * object_to_resources_list_node. */
> +};
> +
> +/* Maintains the relationship for a pair of named resource and
> + * an object, indexed by both resource_to_object_table and
> + * object_to_resources_table. */
> +struct object_to_resources_list_node {
> +    /* node in object_to_resources_node.resources_head. */
> +    struct ovs_list list_node;
> +    struct hmap_node hmap_node; /* node in resource_to_objects_node.objs. */
> +    struct uuid obj_uuid;
> +    size_t ref_count; /* Reference count of the resource by this object.
> +                       * Currently only used for the resource type
> +                       * OBJDEP_TYPE_ADDRSET and for other types always
> +                       * set to 0. */
> +    struct resource_to_objects_node *resource_node;
> +};
> +
> +struct objdep_mgr {
> +    /* A map from a referenced resource type & name (e.g. address_set AS1)
> +     * to a list of object UUIDs (e.g., lflow) that are referencing the named
> +     * resource. Data type of each node in this hmap is struct
> +     * resource_to_objects_node.  The objs in each node point
> +     * to a map of object_to_resources_list_node.ref_list. */
> +    struct hmap resource_to_objects_table;
> +
> +    /* A map from a obj uuid to a list of named resources that are
> +     * referenced by the object. Data type of each node in this hmap is
> +     * struct object_to_resources_node. The resources_head in each node
> +     * points to a list of object_to_resources_list_node.obj_list. */
> +    struct hmap object_to_resources_table;
> +};
> +
> +void objdep_mgr_init(struct objdep_mgr *);
> +void objdep_mgr_destroy(struct objdep_mgr *);
> +void objdep_mgr_clear(struct objdep_mgr *);
> +
> +void objdep_mgr_add(struct objdep_mgr *, enum objdep_type,
> +                    const char *res_name, const struct uuid *);
> +void objdep_mgr_add_with_refcount(struct objdep_mgr *,
> +                                  enum objdep_type,
> +                                  const char *res_name,
> +                                  const struct uuid *,
> +                                  size_t ref_count);
> +void objdep_mgr_remove_obj(struct objdep_mgr *, const struct uuid *);
> +
> +struct resource_to_objects_node *objdep_mgr_find_objs(
> +    struct objdep_mgr *, enum objdep_type, const char *res_name);
> +struct object_to_resources_node *objdep_mgr_find_resources(
> +    struct objdep_mgr *, const struct uuid *);
> +bool objdep_mgr_contains_obj(struct objdep_mgr *, const struct uuid *);
> +
> +bool objdep_mgr_handle_change(struct objdep_mgr *, enum objdep_type,
> +                              const char *res_name,
> +                              objdep_change_handler handler,
> +                              struct uuidset *objs_processed,
> +                              const void *in_arg, void *out_arg,
> +                              bool *changed);
> +
> +const char *objdep_type_name(enum objdep_type);
> +
> +#endif /* lib/objdep.h */
>
diff mbox series

Patch

diff --git a/controller/lflow.c b/controller/lflow.c
index cc0f31db06..d4434bdee8 100644
--- a/controller/lflow.c
+++ b/controller/lflow.c
@@ -61,7 +61,7 @@  struct lookup_port_aux {
     struct ovsdb_idl_index *sbrec_port_binding_by_name;
     const struct sbrec_datapath_binding *dp;
     const struct sbrec_logical_flow *lflow;
-    struct lflow_resource_ref *lfrr;
+    struct objdep_mgr *deps_mgr;
     const struct hmap *chassis_tunnels;
 };
 
@@ -72,8 +72,8 @@  struct condition_aux {
     const struct sset *active_tunnels;
     const struct sbrec_logical_flow *lflow;
     /* Resource reference to store the port name referenced
-     * in is_chassis_resident() to the logical flow. */
-    struct lflow_resource_ref *lfrr;
+     * in is_chassis_resident() to the object (logical flow). */
+    struct objdep_mgr *deps_mgr;
 };
 
 static struct expr *
@@ -81,7 +81,7 @@  convert_match_to_expr(const struct sbrec_logical_flow *,
                       const struct local_datapath *ldp,
                       struct expr **prereqs, const struct shash *addr_sets,
                       const struct shash *port_groups,
-                      struct lflow_resource_ref *, bool *pg_addr_set_ref);
+                      struct objdep_mgr *, bool *pg_addr_set_ref);
 static void
 add_matches_to_flow_table(const struct sbrec_logical_flow *,
                           const struct local_datapath *,
@@ -94,17 +94,6 @@  consider_logical_flow(const struct sbrec_logical_flow *lflow,
                       bool is_recompute,
                       struct lflow_ctx_in *l_ctx_in,
                       struct lflow_ctx_out *l_ctx_out);
-static void lflow_resource_add(struct lflow_resource_ref *, enum ref_type,
-                               const char *ref_name, const struct uuid *,
-                               size_t ref_count);
-static struct ref_lflow_node *ref_lflow_lookup(struct hmap *ref_lflow_table,
-                                               enum ref_type,
-                                               const char *ref_name);
-static struct lflow_ref_node *lflow_ref_lookup(struct hmap *lflow_ref_table,
-                                               const struct uuid *lflow_uuid);
-static void ref_lflow_node_destroy(struct ref_lflow_node *);
-static void lflow_resource_destroy_lflow(struct lflow_resource_ref *,
-                                         const struct uuid *lflow_uuid);
 
 static void add_port_sec_flows(const struct shash *binding_lports,
                                const struct sbrec_chassis *,
@@ -125,8 +114,8 @@  lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
     /* Store the name that used to lookup the lport to lflow reference, so that
      * in the future when the lport's port binding changes, the logical flow
      * that references this lport can be reprocessed. */
-    lflow_resource_add(aux->lfrr, REF_TYPE_PORTBINDING, port_name,
-                       &aux->lflow->header_.uuid, 0);
+    objdep_mgr_add(aux->deps_mgr, OBJDEP_TYPE_PORTBINDING, port_name,
+                   &aux->lflow->header_.uuid);
 
     const struct sbrec_port_binding *pb
         = lport_lookup_by_name(aux->sbrec_port_binding_by_name, port_name);
@@ -141,8 +130,8 @@  lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
      * this multicast group can be reprocessed. */
     struct ds mg_key = DS_EMPTY_INITIALIZER;
     get_mc_group_key(port_name, aux->dp->tunnel_key, &mg_key);
-    lflow_resource_add(aux->lfrr, REF_TYPE_MC_GROUP, ds_cstr(&mg_key),
-                       &aux->lflow->header_.uuid, 0);
+    objdep_mgr_add(aux->deps_mgr, OBJDEP_TYPE_MC_GROUP, ds_cstr(&mg_key),
+                   &aux->lflow->header_.uuid);
     ds_destroy(&mg_key);
 
     const struct sbrec_multicast_group *mg = mcgroup_lookup_by_dp_name(
@@ -180,11 +169,11 @@  is_chassis_resident_cb(const void *c_aux_, const char *port_name)
 {
     const struct condition_aux *c_aux = c_aux_;
 
-    /* Store the port name that used to lookup the lport to lflow reference, so
-     * that in the future when the lport's port-binding changes the logical
+    /* Store the port name that used to lookup the lport to object reference,
+     * so that in the future when the lport's port-binding changes the logical
      * flow that references this lport can be reprocessed. */
-    lflow_resource_add(c_aux->lfrr, REF_TYPE_PORTBINDING, port_name,
-                       &c_aux->lflow->header_.uuid, 0);
+    objdep_mgr_add(c_aux->deps_mgr, OBJDEP_TYPE_PORTBINDING, port_name,
+                   &c_aux->lflow->header_.uuid);
 
     const struct sbrec_port_binding *pb
         = lport_lookup_by_name(c_aux->sbrec_port_binding_by_name, port_name);
@@ -207,155 +196,6 @@  is_chassis_resident_cb(const void *c_aux_, const char *port_name)
     }
 }
 
-void
-lflow_resource_init(struct lflow_resource_ref *lfrr)
-{
-    hmap_init(&lfrr->ref_lflow_table);
-    hmap_init(&lfrr->lflow_ref_table);
-}
-
-void
-lflow_resource_destroy(struct lflow_resource_ref *lfrr)
-{
-    struct ref_lflow_node *rlfn;
-    HMAP_FOR_EACH_SAFE (rlfn, node, &lfrr->ref_lflow_table) {
-        struct lflow_ref_list_node *lrln;
-        HMAP_FOR_EACH_SAFE (lrln, hmap_node, &rlfn->lflow_uuids) {
-            ovs_list_remove(&lrln->list_node);
-            hmap_remove(&rlfn->lflow_uuids, &lrln->hmap_node);
-            free(lrln);
-        }
-        hmap_remove(&lfrr->ref_lflow_table, &rlfn->node);
-        ref_lflow_node_destroy(rlfn);
-    }
-    hmap_destroy(&lfrr->ref_lflow_table);
-
-    struct lflow_ref_node *lfrn;
-    HMAP_FOR_EACH_SAFE (lfrn, node, &lfrr->lflow_ref_table) {
-        hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
-        free(lfrn);
-    }
-    hmap_destroy(&lfrr->lflow_ref_table);
-}
-
-void
-lflow_resource_clear(struct lflow_resource_ref *lfrr)
-{
-    lflow_resource_destroy(lfrr);
-    lflow_resource_init(lfrr);
-}
-
-static struct ref_lflow_node*
-ref_lflow_lookup(struct hmap *ref_lflow_table,
-                 enum ref_type type, const char *ref_name)
-{
-    struct ref_lflow_node *rlfn;
-
-    HMAP_FOR_EACH_WITH_HASH (rlfn, node, hash_string(ref_name, type),
-                             ref_lflow_table) {
-        if (rlfn->type == type && !strcmp(rlfn->ref_name, ref_name)) {
-            return rlfn;
-        }
-    }
-    return NULL;
-}
-
-static struct lflow_ref_node*
-lflow_ref_lookup(struct hmap *lflow_ref_table,
-                 const struct uuid *lflow_uuid)
-{
-    struct lflow_ref_node *lfrn;
-
-    HMAP_FOR_EACH_WITH_HASH (lfrn, node, uuid_hash(lflow_uuid),
-                             lflow_ref_table) {
-        if (uuid_equals(&lfrn->lflow_uuid, lflow_uuid)) {
-            return lfrn;
-        }
-    }
-    return NULL;
-}
-
-static void
-lflow_resource_add(struct lflow_resource_ref *lfrr, enum ref_type type,
-                   const char *ref_name, const struct uuid *lflow_uuid,
-                   size_t ref_count)
-{
-    struct ref_lflow_node *rlfn = ref_lflow_lookup(&lfrr->ref_lflow_table,
-                                                   type, ref_name);
-    struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
-                                                   lflow_uuid);
-    if (rlfn && lfrn) {
-        /* Check if the mapping already existed before adding a new one. */
-        struct lflow_ref_list_node *n;
-        HMAP_FOR_EACH_WITH_HASH (n, hmap_node, uuid_hash(lflow_uuid),
-                                 &rlfn->lflow_uuids) {
-            if (uuid_equals(&n->lflow_uuid, lflow_uuid)) {
-                return;
-            }
-        }
-    }
-
-    if (!rlfn) {
-        rlfn = xzalloc(sizeof *rlfn);
-        rlfn->node.hash = hash_string(ref_name, type);
-        rlfn->type = type;
-        rlfn->ref_name = xstrdup(ref_name);
-        hmap_init(&rlfn->lflow_uuids);
-        hmap_insert(&lfrr->ref_lflow_table, &rlfn->node, rlfn->node.hash);
-    }
-
-    if (!lfrn) {
-        lfrn = xzalloc(sizeof *lfrn);
-        lfrn->node.hash = uuid_hash(lflow_uuid);
-        lfrn->lflow_uuid = *lflow_uuid;
-        ovs_list_init(&lfrn->lflow_ref_head);
-        hmap_insert(&lfrr->lflow_ref_table, &lfrn->node, lfrn->node.hash);
-    }
-
-    struct lflow_ref_list_node *lrln = xzalloc(sizeof *lrln);
-    lrln->lflow_uuid = *lflow_uuid;
-    lrln->ref_count = ref_count;
-    lrln->rlfn = rlfn;
-    hmap_insert(&rlfn->lflow_uuids, &lrln->hmap_node, uuid_hash(lflow_uuid));
-    ovs_list_push_back(&lfrn->lflow_ref_head, &lrln->list_node);
-}
-
-static void
-ref_lflow_node_destroy(struct ref_lflow_node *rlfn)
-{
-    free(rlfn->ref_name);
-    hmap_destroy(&rlfn->lflow_uuids);
-    free(rlfn);
-}
-
-static void
-lflow_resource_destroy_lflow(struct lflow_resource_ref *lfrr,
-                            const struct uuid *lflow_uuid)
-{
-    struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
-                                                   lflow_uuid);
-    if (!lfrn) {
-        return;
-    }
-
-    hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
-    struct lflow_ref_list_node *lrln;
-    LIST_FOR_EACH_SAFE (lrln, list_node, &lfrn->lflow_ref_head) {
-        ovs_list_remove(&lrln->list_node);
-        hmap_remove(&lrln->rlfn->lflow_uuids, &lrln->hmap_node);
-
-        /* Clean up the node in ref_lflow_table if the resource is not
-         * referred by any logical flows. */
-        if (hmap_is_empty(&lrln->rlfn->lflow_uuids)) {
-            hmap_remove(&lfrr->ref_lflow_table, &lrln->rlfn->node);
-            ref_lflow_node_destroy(lrln->rlfn);
-        }
-
-        free(lrln);
-    }
-    free(lfrn);
-}
-
 /* Adds the logical flows from the Logical_Flow table to flow tables. */
 static void
 add_logical_flows(struct lflow_ctx_in *l_ctx_in,
@@ -400,7 +240,7 @@  lflow_handle_changed_flows(struct lflow_ctx_in *l_ctx_in,
     struct uuidset_node *ofrn;
     UUIDSET_FOR_EACH (ofrn, &flood_remove_nodes) {
         /* Delete entries from lflow resource reference. */
-        lflow_resource_destroy_lflow(l_ctx_out->lfrr, &ofrn->uuid);
+        objdep_mgr_remove_obj(l_ctx_out->lflow_deps_mgr, &ofrn->uuid);
         /* Delete conj_ids owned by the lflow. */
         lflow_conj_ids_free(l_ctx_out->conj_ids, &ofrn->uuid);
         /* Reprocessing the lflow if the sb record is not deleted. */
@@ -537,7 +377,7 @@  consider_lflow_for_added_as_ips__(
         .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
         .dp = dp,
         .lflow = lflow,
-        .lfrr = l_ctx_out->lfrr,
+        .deps_mgr = l_ctx_out->lflow_deps_mgr,
     };
     struct condition_aux cond_aux = {
         .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
@@ -545,7 +385,7 @@  consider_lflow_for_added_as_ips__(
         .chassis = l_ctx_in->chassis,
         .active_tunnels = l_ctx_in->active_tunnels,
         .lflow = lflow,
-        .lfrr = l_ctx_out->lfrr,
+        .deps_mgr = l_ctx_out->lflow_deps_mgr,
     };
 
     struct hmap matches = HMAP_INITIALIZER(&matches);
@@ -591,7 +431,7 @@  consider_lflow_for_added_as_ips__(
     struct expr *expr = convert_match_to_expr(lflow, ldp, &prereqs,
                                               l_ctx_in->addr_sets,
                                               l_ctx_in->port_groups,
-                                              l_ctx_out->lfrr, NULL);
+                                              l_ctx_out->lflow_deps_mgr, NULL);
     shash_replace((struct shash *)l_ctx_in->addr_sets, as_name, real_as);
     if (new_fake_as) {
         expr_constant_set_destroy(new_fake_as);
@@ -787,10 +627,10 @@  lflow_handle_addr_set_update(const char *as_name,
         return false;
     }
 
-    struct ref_lflow_node *rlfn =
-        ref_lflow_lookup(&l_ctx_out->lfrr->ref_lflow_table, REF_TYPE_ADDRSET,
-                         as_name);
-    if (!rlfn) {
+    struct resource_to_objects_node *resource_node =
+        objdep_mgr_find_objs(l_ctx_out->lflow_deps_mgr, OBJDEP_TYPE_ADDRSET,
+                             as_name);
+    if (!resource_node) {
         *changed = false;
         return true;
     }
@@ -798,22 +638,23 @@  lflow_handle_addr_set_update(const char *as_name,
     *changed = false;
 
     bool ret = true;
-    struct lflow_ref_list_node *lrln;
-    HMAP_FOR_EACH (lrln, hmap_node, &rlfn->lflow_uuids) {
-        if (uuidset_find(l_ctx_out->lflows_processed, &lrln->lflow_uuid)) {
+    struct object_to_resources_list_node *resource_list_node;
+    RESOURCE_FOR_EACH_OBJ (resource_list_node, resource_node) {
+        const struct uuid *obj_uuid = &resource_list_node->obj_uuid;
+        if (uuidset_find(l_ctx_out->lflows_processed, obj_uuid)) {
             VLOG_DBG("lflow "UUID_FMT"has been processed, skip.",
-                     UUID_ARGS(&lrln->lflow_uuid));
+                     UUID_ARGS(obj_uuid));
             continue;
         }
         const struct sbrec_logical_flow *lflow =
             sbrec_logical_flow_table_get_for_uuid(l_ctx_in->logical_flow_table,
-                                                  &lrln->lflow_uuid);
+                                                  obj_uuid);
         if (!lflow) {
             /* lflow deletion should be handled in the corresponding input
              * handler, so we can skip here. */
             VLOG_DBG("lflow "UUID_FMT" not found while handling updates of "
                      "address set %s, skip.",
-                     UUID_ARGS(&lrln->lflow_uuid), as_name);
+                     UUID_ARGS(obj_uuid), as_name);
             continue;
         }
         *changed = true;
@@ -825,9 +666,9 @@  lflow_handle_addr_set_update(const char *as_name,
                 if (!as_info_from_expr_const(as_name, c, &as_info)) {
                     continue;
                 }
-                if (!ofctrl_remove_flows_for_as_ip(l_ctx_out->flow_table,
-                                                   &lrln->lflow_uuid, &as_info,
-                                                   lrln->ref_count)) {
+                if (!ofctrl_remove_flows_for_as_ip(
+                        l_ctx_out->flow_table, obj_uuid, &as_info,
+                        resource_list_node->ref_count)) {
                     ret = false;
                     goto done;
                 }
@@ -836,7 +677,7 @@  lflow_handle_addr_set_update(const char *as_name,
 
         if (as_diff->added) {
             if (!consider_lflow_for_added_as_ips(lflow, as_name,
-                                                 lrln->ref_count,
+                                                 resource_list_node->ref_count,
                                                  as_diff->added,
                                                  l_ctx_in, l_ctx_out)) {
                 ret = false;
@@ -850,59 +691,32 @@  done:
 }
 
 bool
-lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
-                         struct lflow_ctx_in *l_ctx_in,
-                         struct lflow_ctx_out *l_ctx_out,
-                         bool *changed)
+lflow_handle_changed_ref(enum objdep_type type, const char *res_name,
+                         struct ovs_list *objs_todo,
+                         const void *in_arg, void *out_arg)
 {
-    struct ref_lflow_node *rlfn =
-        ref_lflow_lookup(&l_ctx_out->lfrr->ref_lflow_table, ref_type,
-                         ref_name);
-    if (!rlfn) {
-        *changed = false;
-        return true;
-    }
-    VLOG_DBG("Handle changed lflow reference for resource type: %d,"
-             " name: %s.", ref_type, ref_name);
-    *changed = false;
-    bool ret = true;
-
-    struct ovs_list lflows_todo = OVS_LIST_INITIALIZER(&lflows_todo);
-
-    struct lflow_ref_list_node *lrln, *lrln_uuid;
-    HMAP_FOR_EACH (lrln, hmap_node, &rlfn->lflow_uuids) {
-        if (uuidset_find(l_ctx_out->lflows_processed, &lrln->lflow_uuid)) {
-            continue;
-        }
-        /* Use lflow_ref_list_node as list node to store the uuid.
-         * Other fields are not used here. */
-        lrln_uuid = xmalloc(sizeof *lrln_uuid);
-        lrln_uuid->lflow_uuid = lrln->lflow_uuid;
-        ovs_list_push_back(&lflows_todo, &lrln_uuid->list_node);
-    }
-    if (ovs_list_is_empty(&lflows_todo)) {
-        return true;
-    }
-    *changed = true;
+    struct lflow_ctx_in *l_ctx_in = CONST_CAST(struct lflow_ctx_in *, in_arg);
+    struct lflow_ctx_out *l_ctx_out = out_arg;
 
     /* Re-parse the related lflows. */
     /* Firstly, flood remove the flows from desired flow table. */
+    struct object_to_resources_list_node *resource_list_node_uuid;
     struct uuidset flood_remove_nodes =
         UUIDSET_INITIALIZER(&flood_remove_nodes);
-    LIST_FOR_EACH_SAFE (lrln_uuid, list_node, &lflows_todo) {
-        VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d,"
+    LIST_FOR_EACH_SAFE (resource_list_node_uuid, list_node, objs_todo) {
+        const struct uuid *obj_uuid = &resource_list_node_uuid->obj_uuid;
+        VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %s,"
                  " name: %s.",
-                 UUID_ARGS(&lrln_uuid->lflow_uuid),
-                 ref_type, ref_name);
-        uuidset_insert(&flood_remove_nodes, &lrln_uuid->lflow_uuid);
-        free(lrln_uuid);
+                 UUID_ARGS(obj_uuid), objdep_type_name(type), res_name);
+        uuidset_insert(&flood_remove_nodes, obj_uuid);
+        free(resource_list_node_uuid);
     }
     ofctrl_flood_remove_flows(l_ctx_out->flow_table, &flood_remove_nodes);
 
     /* Secondly, for each lflow that is actually removed, reprocessing it. */
     struct uuidset_node *ofrn;
     UUIDSET_FOR_EACH (ofrn, &flood_remove_nodes) {
-        lflow_resource_destroy_lflow(l_ctx_out->lfrr, &ofrn->uuid);
+        objdep_mgr_remove_obj(l_ctx_out->lflow_deps_mgr, &ofrn->uuid);
         lflow_conj_ids_free(l_ctx_out->conj_ids, &ofrn->uuid);
 
         const struct sbrec_logical_flow *lflow =
@@ -910,9 +724,9 @@  lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
                                                   &ofrn->uuid);
         if (!lflow) {
             VLOG_DBG("lflow "UUID_FMT" not found while reprocessing for"
-                     " resource type: %d, name: %s.",
+                     " resource type: %s, name: %s.",
                      UUID_ARGS(&ofrn->uuid),
-                     ref_type, ref_name);
+                     objdep_type_name(type), res_name);
             continue;
         }
 
@@ -929,8 +743,7 @@  lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
         consider_logical_flow(lflow, false, l_ctx_in, l_ctx_out);
     }
     uuidset_destroy(&flood_remove_nodes);
-
-    return ret;
+    return true;
 }
 
 static void
@@ -985,7 +798,7 @@  add_matches_to_flow_table(const struct sbrec_logical_flow *lflow,
         .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
         .dp = ldp->datapath,
         .lflow = lflow,
-        .lfrr = l_ctx_out->lfrr,
+        .deps_mgr = l_ctx_out->lflow_deps_mgr,
         .chassis_tunnels = l_ctx_in->chassis_tunnels,
     };
 
@@ -1100,7 +913,7 @@  convert_match_to_expr(const struct sbrec_logical_flow *lflow,
                       struct expr **prereqs,
                       const struct shash *addr_sets,
                       const struct shash *port_groups,
-                      struct lflow_resource_ref *lfrr,
+                      struct objdep_mgr *mgr,
                       bool *pg_addr_set_ref)
 {
     struct shash addr_sets_ref = SHASH_INITIALIZER(&addr_sets_ref);
@@ -1114,14 +927,15 @@  convert_match_to_expr(const struct sbrec_logical_flow *lflow,
                                        &error);
     struct shash_node *addr_sets_ref_node;
     SHASH_FOR_EACH (addr_sets_ref_node, &addr_sets_ref) {
-        lflow_resource_add(lfrr, REF_TYPE_ADDRSET, addr_sets_ref_node->name,
-                           &lflow->header_.uuid,
-                           *(size_t *)addr_sets_ref_node->data);
+        objdep_mgr_add_with_refcount(mgr, OBJDEP_TYPE_ADDRSET,
+                                     addr_sets_ref_node->name,
+                                     &lflow->header_.uuid,
+                                     *(size_t *) addr_sets_ref_node->data);
     }
     const char *port_group_name;
     SSET_FOR_EACH (port_group_name, &port_groups_ref) {
-        lflow_resource_add(lfrr, REF_TYPE_PORTGROUP, port_group_name,
-                           &lflow->header_.uuid, 0);
+        objdep_mgr_add(mgr, OBJDEP_TYPE_PORTGROUP, port_group_name,
+                       &lflow->header_.uuid);
     }
 
     if (pg_addr_set_ref) {
@@ -1165,8 +979,8 @@  consider_logical_flow__(const struct sbrec_logical_flow *lflow,
 
     const char *io_port = smap_get(&lflow->tags, "in_out_port");
     if (io_port) {
-        lflow_resource_add(l_ctx_out->lfrr, REF_TYPE_PORTBINDING, io_port,
-                           &lflow->header_.uuid, 0);
+        objdep_mgr_add(l_ctx_out->lflow_deps_mgr, OBJDEP_TYPE_PORTBINDING,
+                       io_port, &lflow->header_.uuid);
         const struct sbrec_port_binding *pb
             = lport_lookup_by_name(l_ctx_in->sbrec_port_binding_by_name,
                                    io_port);
@@ -1232,7 +1046,7 @@  consider_logical_flow__(const struct sbrec_logical_flow *lflow,
         .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
         .dp = dp,
         .lflow = lflow,
-        .lfrr = l_ctx_out->lfrr,
+        .deps_mgr = l_ctx_out->lflow_deps_mgr,
     };
     struct condition_aux cond_aux = {
         .sbrec_port_binding_by_name = l_ctx_in->sbrec_port_binding_by_name,
@@ -1240,7 +1054,7 @@  consider_logical_flow__(const struct sbrec_logical_flow *lflow,
         .chassis = l_ctx_in->chassis,
         .active_tunnels = l_ctx_in->active_tunnels,
         .lflow = lflow,
-        .lfrr = l_ctx_out->lfrr,
+        .deps_mgr = l_ctx_out->lflow_deps_mgr,
     };
 
     struct lflow_cache_value *lcv =
@@ -1272,7 +1086,8 @@  consider_logical_flow__(const struct sbrec_logical_flow *lflow,
     switch (lcv_type) {
     case LCACHE_T_NONE:
         expr = convert_match_to_expr(lflow, ldp, &prereqs, l_ctx_in->addr_sets,
-                                     l_ctx_in->port_groups, l_ctx_out->lfrr,
+                                     l_ctx_in->port_groups,
+                                     l_ctx_out->lflow_deps_mgr,
                                      &pg_addr_set_ref);
         if (!expr) {
             goto done;
@@ -1345,8 +1160,8 @@  consider_logical_flow__(const struct sbrec_logical_flow *lflow,
         /* Cache new entry if caching is enabled. */
         if (lflow_cache_is_enabled(l_ctx_out->lflow_cache)) {
             if (cached_expr
-                && !lflow_ref_lookup(&l_ctx_out->lfrr->lflow_ref_table,
-                                     &lflow->header_.uuid)) {
+                && !objdep_mgr_contains_obj(l_ctx_out->lflow_deps_mgr,
+                                            &lflow->header_.uuid)) {
                 lflow_cache_add_matches(l_ctx_out->lflow_cache,
                                         &lflow->header_.uuid, start_conj_id,
                                         n_conjs, matches, matches_size);
@@ -2448,7 +2263,7 @@  lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *dp,
                              &lflow->header_.uuid)) {
                 continue;
             }
-            /* Don't call lflows_processed_add() because here we process the
+            /* Don't call uuidset_insert() because here we process the
              * lflow only for one of the DPs in the DP group, which may be
              * incomplete. */
             consider_logical_flow__(lflow, dp, l_ctx_in, l_ctx_out);
@@ -2514,7 +2329,11 @@  lflow_handle_flows_for_lport(const struct sbrec_port_binding *pb,
 {
     bool changed;
 
-    if (!lflow_handle_changed_ref(REF_TYPE_PORTBINDING, pb->logical_port,
+    if (!objdep_mgr_handle_change(l_ctx_out->lflow_deps_mgr,
+                                  OBJDEP_TYPE_PORTBINDING,
+                                  pb->logical_port,
+                                  lflow_handle_changed_ref,
+                                  l_ctx_out->lflows_processed,
                                   l_ctx_in, l_ctx_out, &changed)) {
         return false;
     }
@@ -2549,7 +2368,11 @@  lflow_handle_changed_port_bindings(struct lflow_ctx_in *l_ctx_in,
             && !sbrec_port_binding_is_deleted(pb)) {
             continue;
         }
-        if (!lflow_handle_changed_ref(REF_TYPE_PORTBINDING, pb->logical_port,
+        if (!objdep_mgr_handle_change(l_ctx_out->lflow_deps_mgr,
+                                      OBJDEP_TYPE_PORTBINDING,
+                                      pb->logical_port,
+                                      lflow_handle_changed_ref,
+                                      l_ctx_out->lflows_processed,
                                       l_ctx_in, l_ctx_out, &changed)) {
             ret = false;
             break;
@@ -2573,7 +2396,10 @@  lflow_handle_changed_mc_groups(struct lflow_ctx_in *l_ctx_in,
             && !sbrec_multicast_group_is_deleted(mg)) {
             continue;
         }
-        if (!lflow_handle_changed_ref(REF_TYPE_MC_GROUP, ds_cstr(&mg_key),
+        if (!objdep_mgr_handle_change(l_ctx_out->lflow_deps_mgr,
+                                      OBJDEP_TYPE_MC_GROUP, ds_cstr(&mg_key),
+                                      lflow_handle_changed_ref,
+                                      l_ctx_out->lflows_processed,
                                       l_ctx_in, l_ctx_out, &changed)) {
             ret = false;
             break;
diff --git a/controller/lflow.h b/controller/lflow.h
index 8cbe312cac..3f369ebfb6 100644
--- a/controller/lflow.h
+++ b/controller/lflow.h
@@ -16,6 +16,9 @@ 
 #ifndef OVN_LFLOW_H
 #define OVN_LFLOW_H 1
 
+#include "lib/ovn-util.h"
+#include "lib/objdep.h"
+#include "lib/uuidset.h"
 #include "ovn/logical-fields.h"
 
 /* Logical_Flow table translation to OpenFlow
@@ -80,59 +83,6 @@  struct uuid;
 #define OFTABLE_ECMP_NH_MAC          76
 #define OFTABLE_ECMP_NH              77
 
-enum ref_type {
-    REF_TYPE_ADDRSET,
-    REF_TYPE_PORTGROUP,
-    REF_TYPE_PORTBINDING,
-    REF_TYPE_MC_GROUP
-};
-
-struct ref_lflow_node {
-    struct hmap_node node; /* node in lflow_resource_ref.ref_lflow_table. */
-    enum ref_type type; /* key */
-    char *ref_name; /* key */
-    struct hmap lflow_uuids; /* Contains lflow_ref_list_node. Use hmap instead
-                                of list so that lflow_resource_add() can check
-                                and avoid adding redundant entires in O(1). */
-};
-
-struct lflow_ref_node {
-    struct hmap_node node; /* node in lflow_resource_ref.lflow_ref_table. */
-    struct uuid lflow_uuid; /* key */
-    struct ovs_list lflow_ref_head; /* Contains lflow_ref_list_node. */
-};
-
-/* Maintains the relationship for a pair of named resource and
- * a lflow, indexed by both ref_lflow_table and lflow_ref_table. */
-struct lflow_ref_list_node {
-    struct ovs_list list_node; /* node in lflow_ref_node.lflow_ref_head. */
-    struct hmap_node hmap_node; /* node in ref_lflow_node.lflow_uuids. */
-    struct uuid lflow_uuid;
-    size_t ref_count; /* Reference count of the resource by this lflow.
-                         Currently used for the resource type REF_TYPE_ADDRSET
-                         only, and for other types it is always 0. */
-    struct ref_lflow_node *rlfn;
-};
-
-struct lflow_resource_ref {
-    /* A map from a referenced resource type & name (e.g. address_set AS1)
-     * to a list of lflows that are referencing the named resource. Data
-     * type of each node in this hmap is struct ref_lflow_node. The
-     * ref_lflow_head in each node points to a list of
-     * lflow_ref_list_node.ref_list. */
-    struct hmap ref_lflow_table;
-
-    /* A map from a lflow uuid to a list of named resources that are
-     * referenced by the lflow. Data type of each node in this hmap is
-     * struct lflow_ref_node. The lflow_ref_head in each node points to
-     * a list of lflow_ref_list_node.lflow_list. */
-    struct hmap lflow_ref_table;
-};
-
-void lflow_resource_init(struct lflow_resource_ref *);
-void lflow_resource_destroy(struct lflow_resource_ref *);
-void lflow_resource_clear(struct lflow_resource_ref *);
-
 struct lflow_ctx_in {
     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
     struct ovsdb_idl_index *sbrec_logical_flow_by_logical_datapath;
@@ -169,7 +119,7 @@  struct lflow_ctx_out {
     struct ovn_desired_flow_table *flow_table;
     struct ovn_extend_table *group_table;
     struct ovn_extend_table *meter_table;
-    struct lflow_resource_ref *lfrr;
+    struct objdep_mgr *lflow_deps_mgr;
     struct lflow_cache *lflow_cache;
     struct conj_ids *conj_ids;
     struct uuidset *lflows_processed;
@@ -181,10 +131,8 @@  void lflow_init(void);
 void lflow_run(struct lflow_ctx_in *, struct lflow_ctx_out *);
 void lflow_handle_cached_flows(struct lflow_cache *,
                                const struct sbrec_logical_flow_table *);
-bool lflow_handle_changed_flows(struct lflow_ctx_in *, struct lflow_ctx_out *);
-bool lflow_handle_changed_ref(enum ref_type, const char *ref_name,
-                              struct lflow_ctx_in *, struct lflow_ctx_out *,
-                              bool *changed);
+bool lflow_handle_changed_flows(struct lflow_ctx_in *,
+                                struct lflow_ctx_out *);
 
 struct addr_set_diff {
     struct expr_constant_set *added;
@@ -194,6 +142,9 @@  bool lflow_handle_addr_set_update(const char *as_name, struct addr_set_diff *,
                                   struct lflow_ctx_in *,
                                   struct lflow_ctx_out *,
                                   bool *changed);
+bool lflow_handle_changed_ref(enum objdep_type, const char *res_name,
+                              struct ovs_list *objs_todo,
+                              const void *in_arg, void *out_arg);
 
 void lflow_handle_changed_mac_bindings(
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 8895c7a2bd..f62303e3cb 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -2578,8 +2578,8 @@  struct ed_type_lflow_output {
     struct ovn_extend_table group_table;
     /* meter ids for QoS */
     struct ovn_extend_table meter_table;
-    /* lflow resource cross reference */
-    struct lflow_resource_ref lflow_resource_ref;
+    /* lflow <-> resource cross reference */
+    struct objdep_mgr lflow_deps_mgr;;
     /* conjunciton ID usage information of lflows */
     struct conj_ids conj_ids;
 
@@ -2738,7 +2738,7 @@  init_lflow_ctx(struct engine_node *node,
     l_ctx_out->flow_table = &fo->flow_table;
     l_ctx_out->group_table = &fo->group_table;
     l_ctx_out->meter_table = &fo->meter_table;
-    l_ctx_out->lfrr = &fo->lflow_resource_ref;
+    l_ctx_out->lflow_deps_mgr = &fo->lflow_deps_mgr;
     l_ctx_out->conj_ids = &fo->conj_ids;
     l_ctx_out->lflows_processed = &fo->lflows_processed;
     l_ctx_out->lflow_cache = fo->pd.lflow_cache;
@@ -2754,7 +2754,7 @@  en_lflow_output_init(struct engine_node *node OVS_UNUSED,
     ovn_desired_flow_table_init(&data->flow_table);
     ovn_extend_table_init(&data->group_table);
     ovn_extend_table_init(&data->meter_table);
-    lflow_resource_init(&data->lflow_resource_ref);
+    objdep_mgr_init(&data->lflow_deps_mgr);
     lflow_conj_ids_init(&data->conj_ids);
     uuidset_init(&data->lflows_processed);
     simap_init(&data->hd.ids);
@@ -2778,7 +2778,7 @@  en_lflow_output_cleanup(void *data)
     ovn_desired_flow_table_destroy(&flow_output_data->flow_table);
     ovn_extend_table_destroy(&flow_output_data->group_table);
     ovn_extend_table_destroy(&flow_output_data->meter_table);
-    lflow_resource_destroy(&flow_output_data->lflow_resource_ref);
+    objdep_mgr_destroy(&flow_output_data->lflow_deps_mgr);
     lflow_conj_ids_destroy(&flow_output_data->conj_ids);
     uuidset_destroy(&flow_output_data->lflows_processed);
     lflow_cache_destroy(flow_output_data->pd.lflow_cache);
@@ -2814,7 +2814,7 @@  en_lflow_output_run(struct engine_node *node, void *data)
     struct ovn_desired_flow_table *lflow_table = &fo->flow_table;
     struct ovn_extend_table *group_table = &fo->group_table;
     struct ovn_extend_table *meter_table = &fo->meter_table;
-    struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref;
+    struct objdep_mgr *lflow_deps_mgr = &fo->lflow_deps_mgr;
 
     static bool first_run = true;
     if (first_run) {
@@ -2823,7 +2823,7 @@  en_lflow_output_run(struct engine_node *node, void *data)
         ovn_desired_flow_table_clear(lflow_table);
         ovn_extend_table_clear(group_table, false /* desired */);
         ovn_extend_table_clear(meter_table, false /* desired */);
-        lflow_resource_clear(lfrr);
+        objdep_mgr_clear(lflow_deps_mgr);
         lflow_conj_ids_clear(&fo->conj_ids);
     }
 
@@ -2954,8 +2954,11 @@  lflow_output_addr_sets_handler(struct engine_node *node, void *data)
     }
 
     SSET_FOR_EACH (ref_name, &as_data->deleted) {
-        if (!lflow_handle_changed_ref(REF_TYPE_ADDRSET, ref_name, &l_ctx_in,
-                                      &l_ctx_out, &changed)) {
+        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
+                                      OBJDEP_TYPE_ADDRSET, ref_name,
+                                      lflow_handle_changed_ref,
+                                      l_ctx_out.lflows_processed,
+                                      &l_ctx_in, &l_ctx_out, &changed)) {
             return false;
         }
         if (changed) {
@@ -2969,7 +2972,11 @@  lflow_output_addr_sets_handler(struct engine_node *node, void *data)
                                           &l_ctx_out, &changed)) {
             VLOG_DBG("Can't incrementally handle the change of address set %s."
                      " Reprocess related lflows.", shash_node->name);
-            if (!lflow_handle_changed_ref(REF_TYPE_ADDRSET, shash_node->name,
+            if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
+                                          OBJDEP_TYPE_ADDRSET,
+                                          shash_node->name,
+                                          lflow_handle_changed_ref,
+                                          l_ctx_out.lflows_processed,
                                           &l_ctx_in, &l_ctx_out, &changed)) {
                 return false;
             }
@@ -2979,8 +2986,11 @@  lflow_output_addr_sets_handler(struct engine_node *node, void *data)
         }
     }
     SSET_FOR_EACH (ref_name, &as_data->new) {
-        if (!lflow_handle_changed_ref(REF_TYPE_ADDRSET, ref_name, &l_ctx_in,
-                                      &l_ctx_out, &changed)) {
+        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
+                                      OBJDEP_TYPE_ADDRSET, ref_name,
+                                      lflow_handle_changed_ref,
+                                      l_ctx_out.lflows_processed,
+                                      &l_ctx_in, &l_ctx_out, &changed)) {
             return false;
         }
         if (changed) {
@@ -3011,8 +3021,11 @@  lflow_output_port_groups_handler(struct engine_node *node, void *data)
     }
 
     SSET_FOR_EACH (ref_name, &pg_data->deleted) {
-        if (!lflow_handle_changed_ref(REF_TYPE_PORTGROUP, ref_name, &l_ctx_in,
-                                      &l_ctx_out, &changed)) {
+        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
+                                      OBJDEP_TYPE_PORTGROUP, ref_name,
+                                      lflow_handle_changed_ref,
+                                      l_ctx_out.lflows_processed,
+                                      &l_ctx_in, &l_ctx_out, &changed)) {
             return false;
         }
         if (changed) {
@@ -3020,8 +3033,11 @@  lflow_output_port_groups_handler(struct engine_node *node, void *data)
         }
     }
     SSET_FOR_EACH (ref_name, &pg_data->updated) {
-        if (!lflow_handle_changed_ref(REF_TYPE_PORTGROUP, ref_name, &l_ctx_in,
-                                      &l_ctx_out, &changed)) {
+        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
+                                      OBJDEP_TYPE_PORTGROUP, ref_name,
+                                      lflow_handle_changed_ref,
+                                      l_ctx_out.lflows_processed,
+                                      &l_ctx_in, &l_ctx_out, &changed)) {
             return false;
         }
         if (changed) {
@@ -3029,8 +3045,11 @@  lflow_output_port_groups_handler(struct engine_node *node, void *data)
         }
     }
     SSET_FOR_EACH (ref_name, &pg_data->new) {
-        if (!lflow_handle_changed_ref(REF_TYPE_PORTGROUP, ref_name, &l_ctx_in,
-                                      &l_ctx_out, &changed)) {
+        if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
+                                      OBJDEP_TYPE_PORTGROUP, ref_name,
+                                      lflow_handle_changed_ref,
+                                      l_ctx_out.lflows_processed,
+                                      &l_ctx_in, &l_ctx_out, &changed)) {
             return false;
         }
         if (changed) {
diff --git a/lib/automake.mk b/lib/automake.mk
index 60bead6a6a..15d4f84bbb 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -31,6 +31,8 @@  lib_libovn_la_SOURCES = \
 	lib/mcast-group-index.c \
 	lib/mcast-group-index.h \
 	lib/lex.c \
+	lib/objdep.c \
+	lib/objdep.h \
 	lib/ovn-l7.h \
 	lib/ovn-l7.c \
 	lib/ovn-util.c \
diff --git a/lib/objdep.c b/lib/objdep.c
new file mode 100644
index 0000000000..092d4af261
--- /dev/null
+++ b/lib/objdep.c
@@ -0,0 +1,260 @@ 
+/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
+ * Copyright (c) 2022, Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "lib/objdep.h"
+#include "lib/hash.h"
+#include "lib/util.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(resource_dep);
+
+static void resource_node_destroy(struct resource_to_objects_node *);
+
+void
+objdep_mgr_init(struct objdep_mgr *mgr)
+{
+    hmap_init(&mgr->resource_to_objects_table);
+    hmap_init(&mgr->object_to_resources_table);
+}
+
+void
+objdep_mgr_destroy(struct objdep_mgr *mgr)
+{
+    objdep_mgr_clear(mgr);
+
+    hmap_destroy(&mgr->resource_to_objects_table);
+    hmap_destroy(&mgr->object_to_resources_table);
+}
+
+void
+objdep_mgr_clear(struct objdep_mgr *mgr)
+{
+    struct resource_to_objects_node *resource_node;
+    HMAP_FOR_EACH_SAFE (resource_node, node, &mgr->resource_to_objects_table) {
+        struct object_to_resources_list_node *object_list_node;
+        HMAP_FOR_EACH_SAFE (object_list_node, hmap_node,
+                            &resource_node->objs) {
+            ovs_list_remove(&object_list_node->list_node);
+            hmap_remove(&resource_node->objs, &object_list_node->hmap_node);
+            free(object_list_node);
+        }
+        hmap_remove(&mgr->resource_to_objects_table, &resource_node->node);
+        resource_node_destroy(resource_node);
+    }
+
+    struct object_to_resources_node *object_node;
+    HMAP_FOR_EACH_SAFE (object_node, node, &mgr->object_to_resources_table) {
+        hmap_remove(&mgr->object_to_resources_table, &object_node->node);
+        free(object_node);
+    }
+}
+
+void
+objdep_mgr_add(struct objdep_mgr *mgr, enum objdep_type type,
+               const char *res_name, const struct uuid *obj_uuid)
+{
+    objdep_mgr_add_with_refcount(mgr, type, res_name, obj_uuid, 0);
+}
+
+void
+objdep_mgr_add_with_refcount(struct objdep_mgr *mgr, enum objdep_type type,
+                             const char *res_name, const struct uuid *obj_uuid,
+                             size_t ref_count)
+{
+    struct resource_to_objects_node *resource_node =
+        objdep_mgr_find_objs(mgr, type, res_name);
+    struct object_to_resources_node *object_node =
+        objdep_mgr_find_resources(mgr, obj_uuid);
+    if (resource_node && object_node) {
+        /* Check if the mapping already existed before adding a new one. */
+        struct object_to_resources_list_node *n;
+        HMAP_FOR_EACH_WITH_HASH (n, hmap_node, uuid_hash(obj_uuid),
+                                 &resource_node->objs) {
+            if (uuid_equals(&n->obj_uuid, obj_uuid)) {
+                return;
+            }
+        }
+    }
+
+    /* Create the resource node if we didn't have one already (for a
+     * different object). */
+    if (!resource_node) {
+        resource_node = xzalloc(sizeof *resource_node);
+        resource_node->node.hash = hash_string(res_name, type);
+        resource_node->type = type;
+        resource_node->res_name = xstrdup(res_name);
+        hmap_init(&resource_node->objs);
+        hmap_insert(&mgr->resource_to_objects_table,
+                    &resource_node->node,
+                    resource_node->node.hash);
+    }
+
+    /* Create the object node if we didn't have one already (for a
+     * different resource). */
+    if (!object_node) {
+        object_node = xzalloc(sizeof *object_node);
+        object_node->node.hash = uuid_hash(obj_uuid);
+        object_node->obj_uuid = *obj_uuid;
+        ovs_list_init(&object_node->resources_head);
+        hmap_insert(&mgr->object_to_resources_table,
+                    &object_node->node,
+                    object_node->node.hash);
+    }
+
+    struct object_to_resources_list_node *resource_list_node =
+        xzalloc(sizeof *resource_list_node);
+    resource_list_node->obj_uuid = *obj_uuid;
+    resource_list_node->ref_count = ref_count;
+    resource_list_node->resource_node = resource_node;
+    hmap_insert(&resource_node->objs, &resource_list_node->hmap_node,
+                uuid_hash(obj_uuid));
+    ovs_list_push_back(&object_node->resources_head,
+                       &resource_list_node->list_node);
+}
+
+void
+objdep_mgr_remove_obj(struct objdep_mgr *mgr, const struct uuid *obj_uuid)
+{
+    struct object_to_resources_node *object_node =
+        objdep_mgr_find_resources(mgr, obj_uuid);
+    if (!object_node) {
+        return;
+    }
+
+    hmap_remove(&mgr->object_to_resources_table, &object_node->node);
+
+    struct object_to_resources_list_node *resource_list_node;
+    LIST_FOR_EACH_SAFE (resource_list_node, list_node,
+                        &object_node->resources_head) {
+        struct resource_to_objects_node *resource_node =
+            resource_list_node->resource_node;
+        ovs_list_remove(&resource_list_node->list_node);
+        hmap_remove(&resource_node->objs, &resource_list_node->hmap_node);
+
+        /* Clean up the node in ref_obj_table if the resource is not
+         * referred by any logical flows. */
+        if (hmap_is_empty(&resource_node->objs)) {
+            hmap_remove(&mgr->resource_to_objects_table, &resource_node->node);
+            resource_node_destroy(resource_list_node->resource_node);
+        }
+
+        free(resource_list_node);
+    }
+    free(object_node);
+}
+
+struct resource_to_objects_node *
+objdep_mgr_find_objs(struct objdep_mgr *mgr, enum objdep_type type,
+                     const char *res_name)
+{
+    struct resource_to_objects_node *resource_node;
+
+    HMAP_FOR_EACH_WITH_HASH (resource_node, node, hash_string(res_name, type),
+                             &mgr->resource_to_objects_table) {
+        if (resource_node->type == type &&
+                !strcmp(resource_node->res_name, res_name)) {
+            return resource_node;
+        }
+    }
+    return NULL;
+}
+
+struct object_to_resources_node *
+objdep_mgr_find_resources(struct objdep_mgr *mgr,
+                          const struct uuid *obj_uuid)
+{
+    struct object_to_resources_node *object_node;
+
+    HMAP_FOR_EACH_WITH_HASH (object_node, node, uuid_hash(obj_uuid),
+                             &mgr->object_to_resources_table) {
+        if (uuid_equals(&object_node->obj_uuid, obj_uuid)) {
+            return object_node;
+        }
+    }
+    return NULL;
+}
+
+bool
+objdep_mgr_contains_obj(struct objdep_mgr *mgr, const struct uuid *obj_uuid)
+{
+    return !!objdep_mgr_find_resources(mgr, obj_uuid);
+}
+
+bool
+objdep_mgr_handle_change(struct objdep_mgr *mgr,
+                         enum objdep_type type,
+                         const char *res_name,
+                         objdep_change_handler handler,
+                         struct uuidset *objs_processed,
+                         const void *in_arg, void *out_arg,
+                         bool *changed)
+{
+    struct resource_to_objects_node *resource_node =
+        objdep_mgr_find_objs(mgr, type, res_name);
+    if (!resource_node) {
+        *changed = false;
+        return true;
+    }
+    VLOG_DBG("Handle changed object reference for resource type: %s,"
+             " name: %s.", objdep_type_name(type), res_name);
+    *changed = false;
+
+    struct ovs_list objs_todo = OVS_LIST_INITIALIZER(&objs_todo);
+
+    struct object_to_resources_list_node *resource_list_node;
+    HMAP_FOR_EACH (resource_list_node, hmap_node, &resource_node->objs) {
+        if (uuidset_find(objs_processed, &resource_list_node->obj_uuid)) {
+            continue;
+        }
+        /* Use object_to_resources_list_node as list node to store the uuid.
+         * Other fields are not used here. */
+        struct object_to_resources_list_node *resource_list_node_uuid =
+            xmalloc(sizeof *resource_list_node_uuid);
+        resource_list_node_uuid->obj_uuid = resource_list_node->obj_uuid;
+        ovs_list_push_back(&objs_todo, &resource_list_node_uuid->list_node);
+    }
+    if (ovs_list_is_empty(&objs_todo)) {
+        return true;
+    }
+    *changed = true;
+
+    /* This takes ownership of objs_todo. */
+    return handler(type, res_name, &objs_todo, in_arg, out_arg);
+}
+
+const char *
+objdep_type_name(enum objdep_type type)
+{
+    static const char *type_names[OBJDEP_TYPE_MAX] = {
+        [OBJDEP_TYPE_ADDRSET] = "Address_Set",
+        [OBJDEP_TYPE_PORTGROUP] = "Port_Group",
+        [OBJDEP_TYPE_PORTBINDING] = "Port_Binding",
+        [OBJDEP_TYPE_MC_GROUP] = "Multicast_Group",
+    };
+
+    ovs_assert(type < OBJDEP_TYPE_MAX);
+    return type_names[type];
+}
+
+static void
+resource_node_destroy(struct resource_to_objects_node *resource_node)
+{
+    free(resource_node->res_name);
+    hmap_destroy(&resource_node->objs);
+    free(resource_node);
+}
diff --git a/lib/objdep.h b/lib/objdep.h
new file mode 100644
index 0000000000..50c7b01ef1
--- /dev/null
+++ b/lib/objdep.h
@@ -0,0 +1,122 @@ 
+/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
+ * Copyright (c) 2022, Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OVN_OBJDEP_H
+#define OVN_OBJDEP_H 1
+
+#include "lib/uuidset.h"
+#include "openvswitch/hmap.h"
+#include "openvswitch/list.h"
+
+enum objdep_type {
+    OBJDEP_TYPE_ADDRSET,
+    OBJDEP_TYPE_PORTGROUP,
+    OBJDEP_TYPE_PORTBINDING,
+    OBJDEP_TYPE_MC_GROUP,
+    OBJDEP_TYPE_MAX,
+};
+
+/* Callbacks provided by users to process changes to resources referred by
+ * various objects.  They should return true if the change has been
+ * handled successfully. */
+typedef bool (*objdep_change_handler)(enum objdep_type,
+                                      const char *res_name,
+                                      struct ovs_list *ref_nodes,
+                                      const void *in_arg, void *out_arg);
+
+/* A node pointing to all objects that refer to a given resource. */
+struct resource_to_objects_node {
+    struct hmap_node node; /* node in objdep_mgr.resource_to_objects_table. */
+    enum objdep_type type; /* key */
+    char *res_name;        /* key */
+    struct hmap objs;      /* Contains object_to_resources_list_node.
+                            * Use hmap instead of list so
+                            * that obj_resource_add() can check and avoid
+                            * and redundant entries in O(1). */
+};
+
+#define RESOURCE_FOR_EACH_OBJ(NODE, MAP) \
+    HMAP_FOR_EACH (NODE, hmap_node, &(MAP)->objs)
+
+/* A node pointing to all resources used by a given object (specified by
+ * uuid).
+ */
+struct object_to_resources_node {
+    struct hmap_node node; /* node in objdep_mgr.object_to_resources_table. */
+    struct uuid obj_uuid;  /* key */
+    struct ovs_list resources_head; /* Contains elements of type
+                                     * object_to_resources_list_node. */
+};
+
+/* Maintains the relationship for a pair of named resource and
+ * an object, indexed by both resource_to_object_table and
+ * object_to_resources_table. */
+struct object_to_resources_list_node {
+    /* node in object_to_resources_node.resources_head. */
+    struct ovs_list list_node;
+    struct hmap_node hmap_node; /* node in resource_to_objects_node.objs. */
+    struct uuid obj_uuid;
+    size_t ref_count; /* Reference count of the resource by this object.
+                       * Currently only used for the resource type
+                       * OBJDEP_TYPE_ADDRSET and for other types always
+                       * set to 0. */
+    struct resource_to_objects_node *resource_node;
+};
+
+struct objdep_mgr {
+    /* A map from a referenced resource type & name (e.g. address_set AS1)
+     * to a list of object UUIDs (e.g., lflow) that are referencing the named
+     * resource. Data type of each node in this hmap is struct
+     * resource_to_objects_node.  The objs in each node point
+     * to a map of object_to_resources_list_node.ref_list. */
+    struct hmap resource_to_objects_table;
+
+    /* A map from a obj uuid to a list of named resources that are
+     * referenced by the object. Data type of each node in this hmap is
+     * struct object_to_resources_node. The resources_head in each node
+     * points to a list of object_to_resources_list_node.obj_list. */
+    struct hmap object_to_resources_table;
+};
+
+void objdep_mgr_init(struct objdep_mgr *);
+void objdep_mgr_destroy(struct objdep_mgr *);
+void objdep_mgr_clear(struct objdep_mgr *);
+
+void objdep_mgr_add(struct objdep_mgr *, enum objdep_type,
+                    const char *res_name, const struct uuid *);
+void objdep_mgr_add_with_refcount(struct objdep_mgr *,
+                                  enum objdep_type,
+                                  const char *res_name,
+                                  const struct uuid *,
+                                  size_t ref_count);
+void objdep_mgr_remove_obj(struct objdep_mgr *, const struct uuid *);
+
+struct resource_to_objects_node *objdep_mgr_find_objs(
+    struct objdep_mgr *, enum objdep_type, const char *res_name);
+struct object_to_resources_node *objdep_mgr_find_resources(
+    struct objdep_mgr *, const struct uuid *);
+bool objdep_mgr_contains_obj(struct objdep_mgr *, const struct uuid *);
+
+bool objdep_mgr_handle_change(struct objdep_mgr *, enum objdep_type,
+                              const char *res_name,
+                              objdep_change_handler handler,
+                              struct uuidset *objs_processed,
+                              const void *in_arg, void *out_arg,
+                              bool *changed);
+
+const char *objdep_type_name(enum objdep_type);
+
+#endif /* lib/objdep.h */