diff mbox series

[ovs-dev,v2,2/3] lib: extend-table: add pending_id map

Message ID 1212633d1a3d342e087416bd9f93b0cef572e011.1642182763.git.lorenzo.bianconi@redhat.com
State Changes Requested
Headers show
Series ovn metering incremental processing | expand

Checks

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

Commit Message

Lorenzo Bianconi Jan. 14, 2022, 6:01 p.m. UTC
Introduce pending_id map used to store meter_id allocated but not yet
inserted in the desired_table. This is a preliminary patch to add
metering IP engine.

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 controller/ofctrl.c |  9 +++++
 controller/ofctrl.h |  1 +
 lib/extend-table.c  | 82 +++++++++++++++++++++++++++++++++------------
 lib/extend-table.h  |  7 ++++
 4 files changed, 78 insertions(+), 21 deletions(-)

Comments

Mark Michelson Feb. 2, 2022, 9:27 p.m. UTC | #1
Hi Lorenzo,

I think this and patch 3 should be combined. See notes below for why.

On 1/14/22 13:01, Lorenzo Bianconi wrote:
> Introduce pending_id map used to store meter_id allocated but not yet
> inserted in the desired_table. This is a preliminary patch to add
> metering IP engine.
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> ---
>   controller/ofctrl.c |  9 +++++
>   controller/ofctrl.h |  1 +
>   lib/extend-table.c  | 82 +++++++++++++++++++++++++++++++++------------
>   lib/extend-table.h  |  7 ++++
>   4 files changed, 78 insertions(+), 21 deletions(-)
> 
> diff --git a/controller/ofctrl.c b/controller/ofctrl.c
> index 08fcfed8b..bf715787e 100644
> --- a/controller/ofctrl.c
> +++ b/controller/ofctrl.c
> @@ -1801,6 +1801,15 @@ add_meter_string(struct ovn_extend_table_info *m_desired,
>       free(meter_string);
>   }
>   
> +uint32_t ofctrl_get_meter_id(const char *name, bool new_id)

ofctrl_get_meter_id() is not used anywhere in this patch, so trying to 
analyze its purpose on its own is difficult (e.g. what are the 
circumstances for setting new_id to true or false?). To properly review 
this, I had to look at patch 3 as well.

> +{
> +    uint32_t id;
> +    bool val;
> +
> +    ovn_extend_table_get_id(meters, name, &id, NULL, &val, new_id);
> +    return id;
> +}
> +
>   static void
>   add_meter(struct ovn_extend_table_info *m_desired,
>             const struct sbrec_meter_table *meter_table,
> diff --git a/controller/ofctrl.h b/controller/ofctrl.h
> index 014de210d..0efcb2ef5 100644
> --- a/controller/ofctrl.h
> +++ b/controller/ofctrl.h
> @@ -129,5 +129,6 @@ void ofctrl_check_and_add_flow_metered(struct ovn_desired_flow_table *,
>   bool ofctrl_is_connected(void);
>   void ofctrl_set_probe_interval(int probe_interval);
>   void ofctrl_get_memory_usage(struct simap *usage);
> +uint32_t ofctrl_get_meter_id(const char *name, bool new_id);
>   
>   #endif /* controller/ofctrl.h */
> diff --git a/lib/extend-table.c b/lib/extend-table.c
> index c708e24b9..52c8cd62e 100644
> --- a/lib/extend-table.c
> +++ b/lib/extend-table.c
> @@ -37,6 +37,7 @@ ovn_extend_table_init(struct ovn_extend_table *table)
>       hmap_init(&table->desired);
>       hmap_init(&table->lflow_to_desired);
>       hmap_init(&table->existing);
> +    shash_init(&table->pending_ids);
>   }
>   
>   static struct ovn_extend_table_info *
> @@ -191,6 +192,14 @@ ovn_extend_table_clear(struct ovn_extend_table *table, bool existing)
>           }
>           ovn_extend_table_info_destroy(g);
>       }
> +
> +    struct shash_node *node, *tmp;
> +    SHASH_FOR_EACH_SAFE (node, tmp, &table->pending_ids) {
> +        uint32_t *id = node->data;
> +        shash_delete(&table->pending_ids, node);
> +        free(id);
> +    }
> +    shash_destroy(&table->pending_ids);

You can replace all of these lines with 
shash_destroy_free_data(&table->pending_ids)

>   }
>   
>   void
> @@ -282,54 +291,85 @@ ovn_extend_table_sync(struct ovn_extend_table *table)
>       }
>   }
>   
> -/* Assign a new table ID for the table information from the bitmap.
> - * If it already exists, return the old ID. */
> -uint32_t
> -ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name,
> -                           struct uuid lflow_uuid)
> +bool
> +ovn_extend_table_get_id(struct ovn_extend_table *table, const char *name,
> +                        uint32_t *meter_id, struct uuid *lflow_uuid,
> +                        bool *new_id, bool pending)

The return value of this function needs to be documented. It's not clear 
at all what a true or false return means. I *think* that a true return 
means that a new ID was allocated for

I think the meter_id parameter should have its name changed. Extend 
tables are used for groups, meters, and other purposes. "id" is a better 
choice.

>   {
> -    uint32_t table_id = 0, hash;
>       struct ovn_extend_table_info *table_info;
> -
> -    hash = hash_string(name, 0);
> +    uint32_t hash = hash_string(name, 0);
>   
>       /* Check whether we have non installed but allocated group_id. */
>       HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->desired) {
>           if (!strcmp(table_info->name, name)) {
>               VLOG_DBG("ovn_externd_table_assign_id: reuse old id %"PRIu32

Nit: This is in a different function now, so it should be 
"ovn_extend_table_get_id" in this debug message.

> -                     " for %s, used by lflow "UUID_FMT,
> -                     table_info->table_id, table_info->name,
> -                     UUID_ARGS(&lflow_uuid));
> -            ovn_extend_info_add_lflow_ref(table, table_info, &lflow_uuid);
> -            return table_info->table_id;
> +                     " for %s", table_info->table_id, table_info->name);
> +            if (lflow_uuid) {
> +                ovn_extend_info_add_lflow_ref(table, table_info, lflow_uuid);
> +            }
> +            *meter_id = table_info->table_id;
> +            return false;
>           }
>       }
>   
> +    *new_id = false;
>       /* Check whether we already have an installed entry for this
>        * combination. */
>       HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->existing) {
>           if (!strcmp(table_info->name, name)) {
> -            table_id = table_info->table_id;
> +            *meter_id = table_info->table_id;
> +            return true;
>           }
>       }
>   
> -    bool new_table_id = false;
> -    if (!table_id) {
> -        /* Reserve a new group_id. */
> -        table_id = bitmap_scan(table->table_ids, 0, 1, MAX_EXT_TABLE_ID + 1);
> -        new_table_id = true;
> +    /* Check if a ids has been already allocated for this flow. */
> +    uint32_t *id = shash_find_and_delete(&table->pending_ids, name);

It took me quite a while to understand what the pending_ids were being 
used for. I *think* the idea is that the incremental engine is 
generating IDs but storing them in table->pending_ids instead of 
creating extend_table_info structures. Then later, when 
ovn_extend_table_assign_id() is called by the flow_engine, we can pull 
the ID from here instead of using another bit in table->table_ids. This 
way, the table info is generated by lflow processing like it always has, 
and the lflow_uuid can be assigned at that point.

The problem is, this is an unorthodox use of the incremental engine, and 
it's the reason why it took me so long to understand. The IDs created by 
the meter engine node are directly required by the lflow processing 
engine node. So presumably, the meter engine node should be an input to 
the lflow processing engine node. This way, the lflow change handler can 
be called in the case the meter information has changed. As it stands, 
the two separate engines are using a third-party structure for their 
data, and it just happens to work out because the meter_engine is run 
before the flow_engine.

I also think this was harder to analyze since it required me to look at 
patch 3 to understand the concept fully. This is another reason I think 
patch 2 and patch 3 should be combined.

> +    if (id) {
> +        *meter_id = *id;
> +        free(id);
> +        return true;
>       }
>   
> +    /* Reserve a new group_id. */
> +    uint32_t table_id = bitmap_scan(table->table_ids, 0, 1,
> +                                    MAX_EXT_TABLE_ID + 1);
>       if (table_id == MAX_EXT_TABLE_ID + 1) {
>           static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
>           VLOG_ERR_RL(&rl, "%"PRIu32" out of table ids.", table_id);
> -        return EXT_TABLE_ID_INVALID;
> +        *meter_id = EXT_TABLE_ID_INVALID;
> +        return false;
>       }
>       bitmap_set1(table->table_ids, table_id);
> +    *meter_id = table_id;
> +
> +    *new_id = true;
> +    if (pending) {
> +        /* Add the id to pedning map. */
> +        id = xmalloc(sizeof *id);
> +        *id = table_id;
> +        shash_add(&table->pending_ids, name, id);
> +    }
> +
> +    return true;
> +}
> +
> +/* Assign a new table ID for the table information from the bitmap.
> + * If it already exists, return the old ID. */
> +uint32_t
> +ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name,
> +                           struct uuid lflow_uuid)
> +{
> +    uint32_t table_id, hash = hash_string(name, 0);
> +    struct ovn_extend_table_info *table_info;
> +    bool new_table_id;
> +
> +    if (!ovn_extend_table_get_id(table, name, &table_id,
> +                                 &lflow_uuid, &new_table_id, false)) {
> +        return table_id;
> +    }
>   
>       table_info = ovn_extend_table_info_alloc(name, table_id, new_table_id,
>                                                hash);
> -
>       hmap_insert(&table->desired,
>                   &table_info->hmap_node, table_info->hmap_node.hash);
>   
> diff --git a/lib/extend-table.h b/lib/extend-table.h
> index 4d80cfd80..5c5f65695 100644
> --- a/lib/extend-table.h
> +++ b/lib/extend-table.h
> @@ -21,6 +21,7 @@
>   #define EXT_TABLE_ID_INVALID 0
>   
>   #include "openvswitch/hmap.h"
> +#include "openvswitch/shash.h"
>   #include "openvswitch/list.h"
>   #include "openvswitch/uuid.h"
>   
> @@ -30,6 +31,8 @@ struct ovn_extend_table {
>       unsigned long *table_ids;  /* Used as a bitmap with value set
>                                   * for allocated group ids in either
>                                   * desired or existing. */
> +    struct shash pending_ids;
> +


>       struct hmap desired;
>       struct hmap lflow_to_desired; /* Index for looking up desired table
>                                      * items from given lflow uuid, with
> @@ -93,6 +96,10 @@ void ovn_extend_table_sync(struct ovn_extend_table *);
>   uint32_t ovn_extend_table_assign_id(struct ovn_extend_table *,
>                                       const char *name,
>                                       struct uuid lflow_uuid);
> +bool
> +ovn_extend_table_get_id(struct ovn_extend_table *table, const char *name,
> +                        uint32_t *meter_id, struct uuid *lflow_uuid,
> +                        bool *new_id, bool pending);
>   
>   /* Iterates 'DESIRED' through all of the 'ovn_extend_table_info's in
>    * 'TABLE'->desired that are not in 'TABLE'->existing.  (The loop body
>
Lorenzo Bianconi Feb. 3, 2022, 5:05 p.m. UTC | #2
> Hi Lorenzo,
> 
> I think this and patch 3 should be combined. See notes below for why.
> 
> On 1/14/22 13:01, Lorenzo Bianconi wrote:
> > Introduce pending_id map used to store meter_id allocated but not yet
> > inserted in the desired_table. This is a preliminary patch to add
> > metering IP engine.
> > 
> > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> > ---
> >   controller/ofctrl.c |  9 +++++
> >   controller/ofctrl.h |  1 +
> >   lib/extend-table.c  | 82 +++++++++++++++++++++++++++++++++------------
> >   lib/extend-table.h  |  7 ++++
> >   4 files changed, 78 insertions(+), 21 deletions(-)
> > 
> > diff --git a/controller/ofctrl.c b/controller/ofctrl.c
> > index 08fcfed8b..bf715787e 100644
> > --- a/controller/ofctrl.c
> > +++ b/controller/ofctrl.c
> > @@ -1801,6 +1801,15 @@ add_meter_string(struct ovn_extend_table_info *m_desired,
> >       free(meter_string);
> >   }
> > +uint32_t ofctrl_get_meter_id(const char *name, bool new_id)
> 
> ofctrl_get_meter_id() is not used anywhere in this patch, so trying to
> analyze its purpose on its own is difficult (e.g. what are the circumstances
> for setting new_id to true or false?). To properly review this, I had to
> look at patch 3 as well.

ack, I am fine with it.

> 
> > +{
> > +    uint32_t id;
> > +    bool val;
> > +
> > +    ovn_extend_table_get_id(meters, name, &id, NULL, &val, new_id);
> > +    return id;
> > +}
> > +
> >   static void
> >   add_meter(struct ovn_extend_table_info *m_desired,
> >             const struct sbrec_meter_table *meter_table,
> > diff --git a/controller/ofctrl.h b/controller/ofctrl.h
> > index 014de210d..0efcb2ef5 100644
> > --- a/controller/ofctrl.h
> > +++ b/controller/ofctrl.h
> > @@ -129,5 +129,6 @@ void ofctrl_check_and_add_flow_metered(struct ovn_desired_flow_table *,
> >   bool ofctrl_is_connected(void);
> >   void ofctrl_set_probe_interval(int probe_interval);
> >   void ofctrl_get_memory_usage(struct simap *usage);
> > +uint32_t ofctrl_get_meter_id(const char *name, bool new_id);
> >   #endif /* controller/ofctrl.h */
> > diff --git a/lib/extend-table.c b/lib/extend-table.c
> > index c708e24b9..52c8cd62e 100644
> > --- a/lib/extend-table.c
> > +++ b/lib/extend-table.c
> > @@ -37,6 +37,7 @@ ovn_extend_table_init(struct ovn_extend_table *table)
> >       hmap_init(&table->desired);
> >       hmap_init(&table->lflow_to_desired);
> >       hmap_init(&table->existing);
> > +    shash_init(&table->pending_ids);
> >   }
> >   static struct ovn_extend_table_info *
> > @@ -191,6 +192,14 @@ ovn_extend_table_clear(struct ovn_extend_table *table, bool existing)
> >           }
> >           ovn_extend_table_info_destroy(g);
> >       }
> > +
> > +    struct shash_node *node, *tmp;
> > +    SHASH_FOR_EACH_SAFE (node, tmp, &table->pending_ids) {
> > +        uint32_t *id = node->data;
> > +        shash_delete(&table->pending_ids, node);
> > +        free(id);
> > +    }
> > +    shash_destroy(&table->pending_ids);
> 
> You can replace all of these lines with
> shash_destroy_free_data(&table->pending_ids)

ack, I will fix it

> 
> >   }
> >   void
> > @@ -282,54 +291,85 @@ ovn_extend_table_sync(struct ovn_extend_table *table)
> >       }
> >   }
> > -/* Assign a new table ID for the table information from the bitmap.
> > - * If it already exists, return the old ID. */
> > -uint32_t
> > -ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name,
> > -                           struct uuid lflow_uuid)
> > +bool
> > +ovn_extend_table_get_id(struct ovn_extend_table *table, const char *name,
> > +                        uint32_t *meter_id, struct uuid *lflow_uuid,
> > +                        bool *new_id, bool pending)
> 
> The return value of this function needs to be documented. It's not clear at
> all what a true or false return means. I *think* that a true return means
> that a new ID was allocated for
> 
> I think the meter_id parameter should have its name changed. Extend tables
> are used for groups, meters, and other purposes. "id" is a better choice.

ack, I will fix it
> 
> >   {
> > -    uint32_t table_id = 0, hash;
> >       struct ovn_extend_table_info *table_info;
> > -
> > -    hash = hash_string(name, 0);
> > +    uint32_t hash = hash_string(name, 0);
> >       /* Check whether we have non installed but allocated group_id. */
> >       HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->desired) {
> >           if (!strcmp(table_info->name, name)) {
> >               VLOG_DBG("ovn_externd_table_assign_id: reuse old id %"PRIu32
> 
> Nit: This is in a different function now, so it should be
> "ovn_extend_table_get_id" in this debug message.
> 
> > -                     " for %s, used by lflow "UUID_FMT,
> > -                     table_info->table_id, table_info->name,
> > -                     UUID_ARGS(&lflow_uuid));
> > -            ovn_extend_info_add_lflow_ref(table, table_info, &lflow_uuid);
> > -            return table_info->table_id;
> > +                     " for %s", table_info->table_id, table_info->name);
> > +            if (lflow_uuid) {
> > +                ovn_extend_info_add_lflow_ref(table, table_info, lflow_uuid);
> > +            }
> > +            *meter_id = table_info->table_id;
> > +            return false;
> >           }
> >       }
> > +    *new_id = false;
> >       /* Check whether we already have an installed entry for this
> >        * combination. */
> >       HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->existing) {
> >           if (!strcmp(table_info->name, name)) {
> > -            table_id = table_info->table_id;
> > +            *meter_id = table_info->table_id;
> > +            return true;
> >           }
> >       }
> > -    bool new_table_id = false;
> > -    if (!table_id) {
> > -        /* Reserve a new group_id. */
> > -        table_id = bitmap_scan(table->table_ids, 0, 1, MAX_EXT_TABLE_ID + 1);
> > -        new_table_id = true;
> > +    /* Check if a ids has been already allocated for this flow. */
> > +    uint32_t *id = shash_find_and_delete(&table->pending_ids, name);
> 
> It took me quite a while to understand what the pending_ids were being used
> for. I *think* the idea is that the incremental engine is generating IDs but
> storing them in table->pending_ids instead of creating extend_table_info
> structures. Then later, when ovn_extend_table_assign_id() is called by the
> flow_engine, we can pull the ID from here instead of using another bit in
> table->table_ids. This way, the table info is generated by lflow processing
> like it always has, and the lflow_uuid can be assigned at that point.

ack, correct.

> 
> The problem is, this is an unorthodox use of the incremental engine, and
> it's the reason why it took me so long to understand. The IDs created by the
> meter engine node are directly required by the lflow processing engine node.
> So presumably, the meter engine node should be an input to the lflow
> processing engine node. This way, the lflow change handler can be called in
> the case the meter information has changed. As it stands, the two separate
> engines are using a third-party structure for their data, and it just
> happens to work out because the meter_engine is run before the flow_engine.

I think for meter-band table is not 1:1 with all other SB tables since changing
a meter band will not change the related lflow since in logical_flow table we
just reference the UUID not the band value.
I guess we can try to hook it into the lflow engine, but I guess we need to
understand how to manage the full-recompute and how to send configuration
changes to OVS.

> 
> I also think this was harder to analyze since it required me to look at
> patch 3 to understand the concept fully. This is another reason I think
> patch 2 and patch 3 should be combined.

ack, fine with it.

Regards,
Lorenzo

> 
> > +    if (id) {
> > +        *meter_id = *id;
> > +        free(id);
> > +        return true;
> >       }
> > +    /* Reserve a new group_id. */
> > +    uint32_t table_id = bitmap_scan(table->table_ids, 0, 1,
> > +                                    MAX_EXT_TABLE_ID + 1);
> >       if (table_id == MAX_EXT_TABLE_ID + 1) {
> >           static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> >           VLOG_ERR_RL(&rl, "%"PRIu32" out of table ids.", table_id);
> > -        return EXT_TABLE_ID_INVALID;
> > +        *meter_id = EXT_TABLE_ID_INVALID;
> > +        return false;
> >       }
> >       bitmap_set1(table->table_ids, table_id);
> > +    *meter_id = table_id;
> > +
> > +    *new_id = true;
> > +    if (pending) {
> > +        /* Add the id to pedning map. */
> > +        id = xmalloc(sizeof *id);
> > +        *id = table_id;
> > +        shash_add(&table->pending_ids, name, id);
> > +    }
> > +
> > +    return true;
> > +}
> > +
> > +/* Assign a new table ID for the table information from the bitmap.
> > + * If it already exists, return the old ID. */
> > +uint32_t
> > +ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name,
> > +                           struct uuid lflow_uuid)
> > +{
> > +    uint32_t table_id, hash = hash_string(name, 0);
> > +    struct ovn_extend_table_info *table_info;
> > +    bool new_table_id;
> > +
> > +    if (!ovn_extend_table_get_id(table, name, &table_id,
> > +                                 &lflow_uuid, &new_table_id, false)) {
> > +        return table_id;
> > +    }
> >       table_info = ovn_extend_table_info_alloc(name, table_id, new_table_id,
> >                                                hash);
> > -
> >       hmap_insert(&table->desired,
> >                   &table_info->hmap_node, table_info->hmap_node.hash);
> > diff --git a/lib/extend-table.h b/lib/extend-table.h
> > index 4d80cfd80..5c5f65695 100644
> > --- a/lib/extend-table.h
> > +++ b/lib/extend-table.h
> > @@ -21,6 +21,7 @@
> >   #define EXT_TABLE_ID_INVALID 0
> >   #include "openvswitch/hmap.h"
> > +#include "openvswitch/shash.h"
> >   #include "openvswitch/list.h"
> >   #include "openvswitch/uuid.h"
> > @@ -30,6 +31,8 @@ struct ovn_extend_table {
> >       unsigned long *table_ids;  /* Used as a bitmap with value set
> >                                   * for allocated group ids in either
> >                                   * desired or existing. */
> > +    struct shash pending_ids;
> > +
> 
> 
> >       struct hmap desired;
> >       struct hmap lflow_to_desired; /* Index for looking up desired table
> >                                      * items from given lflow uuid, with
> > @@ -93,6 +96,10 @@ void ovn_extend_table_sync(struct ovn_extend_table *);
> >   uint32_t ovn_extend_table_assign_id(struct ovn_extend_table *,
> >                                       const char *name,
> >                                       struct uuid lflow_uuid);
> > +bool
> > +ovn_extend_table_get_id(struct ovn_extend_table *table, const char *name,
> > +                        uint32_t *meter_id, struct uuid *lflow_uuid,
> > +                        bool *new_id, bool pending);
> >   /* Iterates 'DESIRED' through all of the 'ovn_extend_table_info's in
> >    * 'TABLE'->desired that are not in 'TABLE'->existing.  (The loop body
> > 
>
diff mbox series

Patch

diff --git a/controller/ofctrl.c b/controller/ofctrl.c
index 08fcfed8b..bf715787e 100644
--- a/controller/ofctrl.c
+++ b/controller/ofctrl.c
@@ -1801,6 +1801,15 @@  add_meter_string(struct ovn_extend_table_info *m_desired,
     free(meter_string);
 }
 
+uint32_t ofctrl_get_meter_id(const char *name, bool new_id)
+{
+    uint32_t id;
+    bool val;
+
+    ovn_extend_table_get_id(meters, name, &id, NULL, &val, new_id);
+    return id;
+}
+
 static void
 add_meter(struct ovn_extend_table_info *m_desired,
           const struct sbrec_meter_table *meter_table,
diff --git a/controller/ofctrl.h b/controller/ofctrl.h
index 014de210d..0efcb2ef5 100644
--- a/controller/ofctrl.h
+++ b/controller/ofctrl.h
@@ -129,5 +129,6 @@  void ofctrl_check_and_add_flow_metered(struct ovn_desired_flow_table *,
 bool ofctrl_is_connected(void);
 void ofctrl_set_probe_interval(int probe_interval);
 void ofctrl_get_memory_usage(struct simap *usage);
+uint32_t ofctrl_get_meter_id(const char *name, bool new_id);
 
 #endif /* controller/ofctrl.h */
diff --git a/lib/extend-table.c b/lib/extend-table.c
index c708e24b9..52c8cd62e 100644
--- a/lib/extend-table.c
+++ b/lib/extend-table.c
@@ -37,6 +37,7 @@  ovn_extend_table_init(struct ovn_extend_table *table)
     hmap_init(&table->desired);
     hmap_init(&table->lflow_to_desired);
     hmap_init(&table->existing);
+    shash_init(&table->pending_ids);
 }
 
 static struct ovn_extend_table_info *
@@ -191,6 +192,14 @@  ovn_extend_table_clear(struct ovn_extend_table *table, bool existing)
         }
         ovn_extend_table_info_destroy(g);
     }
+
+    struct shash_node *node, *tmp;
+    SHASH_FOR_EACH_SAFE (node, tmp, &table->pending_ids) {
+        uint32_t *id = node->data;
+        shash_delete(&table->pending_ids, node);
+        free(id);
+    }
+    shash_destroy(&table->pending_ids);
 }
 
 void
@@ -282,54 +291,85 @@  ovn_extend_table_sync(struct ovn_extend_table *table)
     }
 }
 
-/* Assign a new table ID for the table information from the bitmap.
- * If it already exists, return the old ID. */
-uint32_t
-ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name,
-                           struct uuid lflow_uuid)
+bool
+ovn_extend_table_get_id(struct ovn_extend_table *table, const char *name,
+                        uint32_t *meter_id, struct uuid *lflow_uuid,
+                        bool *new_id, bool pending)
 {
-    uint32_t table_id = 0, hash;
     struct ovn_extend_table_info *table_info;
-
-    hash = hash_string(name, 0);
+    uint32_t hash = hash_string(name, 0);
 
     /* Check whether we have non installed but allocated group_id. */
     HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->desired) {
         if (!strcmp(table_info->name, name)) {
             VLOG_DBG("ovn_externd_table_assign_id: reuse old id %"PRIu32
-                     " for %s, used by lflow "UUID_FMT,
-                     table_info->table_id, table_info->name,
-                     UUID_ARGS(&lflow_uuid));
-            ovn_extend_info_add_lflow_ref(table, table_info, &lflow_uuid);
-            return table_info->table_id;
+                     " for %s", table_info->table_id, table_info->name);
+            if (lflow_uuid) {
+                ovn_extend_info_add_lflow_ref(table, table_info, lflow_uuid);
+            }
+            *meter_id = table_info->table_id;
+            return false;
         }
     }
 
+    *new_id = false;
     /* Check whether we already have an installed entry for this
      * combination. */
     HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->existing) {
         if (!strcmp(table_info->name, name)) {
-            table_id = table_info->table_id;
+            *meter_id = table_info->table_id;
+            return true;
         }
     }
 
-    bool new_table_id = false;
-    if (!table_id) {
-        /* Reserve a new group_id. */
-        table_id = bitmap_scan(table->table_ids, 0, 1, MAX_EXT_TABLE_ID + 1);
-        new_table_id = true;
+    /* Check if a ids has been already allocated for this flow. */
+    uint32_t *id = shash_find_and_delete(&table->pending_ids, name);
+    if (id) {
+        *meter_id = *id;
+        free(id);
+        return true;
     }
 
+    /* Reserve a new group_id. */
+    uint32_t table_id = bitmap_scan(table->table_ids, 0, 1,
+                                    MAX_EXT_TABLE_ID + 1);
     if (table_id == MAX_EXT_TABLE_ID + 1) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
         VLOG_ERR_RL(&rl, "%"PRIu32" out of table ids.", table_id);
-        return EXT_TABLE_ID_INVALID;
+        *meter_id = EXT_TABLE_ID_INVALID;
+        return false;
     }
     bitmap_set1(table->table_ids, table_id);
+    *meter_id = table_id;
+
+    *new_id = true;
+    if (pending) {
+        /* Add the id to pedning map. */
+        id = xmalloc(sizeof *id);
+        *id = table_id;
+        shash_add(&table->pending_ids, name, id);
+    }
+
+    return true;
+}
+
+/* Assign a new table ID for the table information from the bitmap.
+ * If it already exists, return the old ID. */
+uint32_t
+ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name,
+                           struct uuid lflow_uuid)
+{
+    uint32_t table_id, hash = hash_string(name, 0);
+    struct ovn_extend_table_info *table_info;
+    bool new_table_id;
+
+    if (!ovn_extend_table_get_id(table, name, &table_id,
+                                 &lflow_uuid, &new_table_id, false)) {
+        return table_id;
+    }
 
     table_info = ovn_extend_table_info_alloc(name, table_id, new_table_id,
                                              hash);
-
     hmap_insert(&table->desired,
                 &table_info->hmap_node, table_info->hmap_node.hash);
 
diff --git a/lib/extend-table.h b/lib/extend-table.h
index 4d80cfd80..5c5f65695 100644
--- a/lib/extend-table.h
+++ b/lib/extend-table.h
@@ -21,6 +21,7 @@ 
 #define EXT_TABLE_ID_INVALID 0
 
 #include "openvswitch/hmap.h"
+#include "openvswitch/shash.h"
 #include "openvswitch/list.h"
 #include "openvswitch/uuid.h"
 
@@ -30,6 +31,8 @@  struct ovn_extend_table {
     unsigned long *table_ids;  /* Used as a bitmap with value set
                                 * for allocated group ids in either
                                 * desired or existing. */
+    struct shash pending_ids;
+
     struct hmap desired;
     struct hmap lflow_to_desired; /* Index for looking up desired table
                                    * items from given lflow uuid, with
@@ -93,6 +96,10 @@  void ovn_extend_table_sync(struct ovn_extend_table *);
 uint32_t ovn_extend_table_assign_id(struct ovn_extend_table *,
                                     const char *name,
                                     struct uuid lflow_uuid);
+bool
+ovn_extend_table_get_id(struct ovn_extend_table *table, const char *name,
+                        uint32_t *meter_id, struct uuid *lflow_uuid,
+                        bool *new_id, bool pending);
 
 /* Iterates 'DESIRED' through all of the 'ovn_extend_table_info's in
  * 'TABLE'->desired that are not in 'TABLE'->existing.  (The loop body