diff mbox series

[ovs-dev,v4,4/6] vswitchd, ofproto-dpif: Propagate the CT limit from database.

Message ID 20231010141224.638166-5-amusil@redhat.com
State Changes Requested
Delegated to: Ilya Maximets
Headers show
Series Expose CT limit via DB | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Ales Musil Oct. 10, 2023, 2:12 p.m. UTC
Propagate the CT limit that is present in the DB into
datapath. The limit is currently only propagated on change
and can be overwritten by the dpctl commands.

Signed-off-by: Ales Musil <amusil@redhat.com>
---
v4: Rebase on top of current master.
    Make sure that the values from DB are propagated only if set. That applies to both limit and policies.
---
 ofproto/ofproto-dpif.c     | 41 ++++++++++++++++++++++++++
 ofproto/ofproto-dpif.h     |  5 ++++
 ofproto/ofproto-provider.h |  4 +++
 ofproto/ofproto.c          | 16 ++++++++--
 ofproto/ofproto.h          |  4 +++
 tests/system-traffic.at    | 54 ++++++++++++++++++++++++++++++++++
 vswitchd/bridge.c          | 60 +++++++++++++++++++++++++++++++-------
 7 files changed, 171 insertions(+), 13 deletions(-)

Comments

Ilya Maximets Oct. 16, 2023, 7:11 p.m. UTC | #1
On 10/10/23 16:12, Ales Musil wrote:
> Propagate the CT limit that is present in the DB into
> datapath. The limit is currently only propagated on change
> and can be overwritten by the dpctl commands.
> 
> Signed-off-by: Ales Musil <amusil@redhat.com>
> ---
> v4: Rebase on top of current master.
>     Make sure that the values from DB are propagated only if set. That applies to both limit and policies.
> ---
>  ofproto/ofproto-dpif.c     | 41 ++++++++++++++++++++++++++
>  ofproto/ofproto-dpif.h     |  5 ++++
>  ofproto/ofproto-provider.h |  4 +++
>  ofproto/ofproto.c          | 16 ++++++++--
>  ofproto/ofproto.h          |  4 +++
>  tests/system-traffic.at    | 54 ++++++++++++++++++++++++++++++++++
>  vswitchd/bridge.c          | 60 +++++++++++++++++++++++++++++++-------
>  7 files changed, 171 insertions(+), 13 deletions(-)
> 
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index ba5706f6a..4fdbf0ef0 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -220,6 +220,7 @@ static void ofproto_unixctl_init(void);
>  static void ct_zone_config_init(struct dpif_backer *backer);
>  static void ct_zone_config_uninit(struct dpif_backer *backer);
>  static void ct_zone_timeout_policy_sweep(struct dpif_backer *backer);
> +static void ct_zone_limits_commit(struct dpif_backer *backer);
>  
>  static inline struct ofproto_dpif *
>  ofproto_dpif_cast(const struct ofproto *ofproto)
> @@ -513,6 +514,7 @@ type_run(const char *type)
>  
>      process_dpif_port_changes(backer);
>      ct_zone_timeout_policy_sweep(backer);
> +    ct_zone_limits_commit(backer);
>  
>      return 0;
>  }
> @@ -5522,6 +5524,8 @@ ct_zone_config_init(struct dpif_backer *backer)
>      cmap_init(&backer->ct_zones);
>      hmap_init(&backer->ct_tps);
>      ovs_list_init(&backer->ct_tp_kill_list);
> +    ovs_list_init(&backer->ct_zone_limits_to_add);
> +    ovs_list_init(&backer->ct_zone_limits_to_del);
>      clear_existing_ct_timeout_policies(backer);
>  }
>  
> @@ -5545,6 +5549,8 @@ ct_zone_config_uninit(struct dpif_backer *backer)
>      id_pool_destroy(backer->tp_ids);
>      cmap_destroy(&backer->ct_zones);
>      hmap_destroy(&backer->ct_tps);
> +    ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_add);
> +    ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_del);
>  }
>  
>  static void
> @@ -5625,6 +5631,40 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
>      }
>  }
>  
> +BUILD_ASSERT_DECL(OFPROTO_CT_DEFAULT_ZONE_ID == CT_DPIF_DEFAULT_ZONE);

Why this assertion added here?

> +
> +static void
> +ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
> +                     int64_t *limit)
> +{
> +    struct dpif_backer *backer = shash_find_data(&all_dpif_backers,
> +                                                 datapath_type);
> +    if (!backer) {
> +        return;
> +    }
> +
> +    if (limit) {
> +        ct_dpif_push_zone_limit(&backer->ct_zone_limits_to_add, zone_id,
> +                                *limit, 0);
> +    } else {
> +        ct_dpif_push_zone_limit(&backer->ct_zone_limits_to_del, zone_id, 0, 0);
> +    }
> +}
> +
> +static void
> +ct_zone_limits_commit(struct dpif_backer *backer)
> +{
> +    if (!ovs_list_is_empty(&backer->ct_zone_limits_to_add)) {
> +        ct_dpif_set_limits(backer->dpif, &backer->ct_zone_limits_to_add);
> +        ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_add);
> +    }
> +
> +    if (!ovs_list_is_empty(&backer->ct_zone_limits_to_del)) {
> +        ct_dpif_del_limits(backer->dpif, &backer->ct_zone_limits_to_del);
> +        ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_del);
> +    }
> +}
> +
>  static void
>  get_datapath_cap(const char *datapath_type, struct smap *cap)
>  {
> @@ -6914,4 +6954,5 @@ const struct ofproto_class ofproto_dpif_class = {
>      ct_flush,                   /* ct_flush */
>      ct_set_zone_timeout_policy,
>      ct_del_zone_timeout_policy,
> +    ct_zone_limit_update,
>  };
> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
> index d8e0cd37a..b863dd6fc 100644
> --- a/ofproto/ofproto-dpif.h
> +++ b/ofproto/ofproto-dpif.h
> @@ -284,6 +284,11 @@ struct dpif_backer {
>                                                  feature than 'bt_support'. */
>  
>      struct atomic_count tnl_count;
> +
> +    struct ovs_list ct_zone_limits_to_add;  /* CT zone limits queued for
> +                                             * addition into datapath. */
> +    struct ovs_list ct_zone_limits_to_del;  /* CT zone limt queued for
> +                                             * deletion from datapath. */
>  };
>  
>  /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
> index 9f7b8b6e8..33fb99280 100644
> --- a/ofproto/ofproto-provider.h
> +++ b/ofproto/ofproto-provider.h
> @@ -1921,6 +1921,10 @@ struct ofproto_class {
>      /* Deletes the timeout policy associated with 'zone' in datapath type
>       * 'dp_type'. */
>      void (*ct_del_zone_timeout_policy)(const char *dp_type, uint16_t zone);
> +
> +    /* Updates the CT zone limit for specified zone. */
> +    void (*ct_zone_limit_update)(const char *dp_type, int32_t zone,
> +                                 int64_t *limit);
>  };
>  
>  extern const struct ofproto_class ofproto_dpif_class;
> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> index e78c80d11..6df3f1b27 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -1026,6 +1026,18 @@ ofproto_ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
>  
>  }
>  
> +void
> +ofproto_ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
> +                             int64_t *limit)
> +{
> +    datapath_type = ofproto_normalize_type(datapath_type);
> +    const struct ofproto_class *class = ofproto_class_find__(datapath_type);
> +
> +    if (class && class->ct_zone_limit_update) {
> +        class->ct_zone_limit_update(datapath_type, zone_id, limit);
> +    }
> +}
> +
>  
>  /* Spanning Tree Protocol (STP) configuration. */
>  
> @@ -6366,7 +6378,7 @@ handle_flow_mod__(struct ofproto *ofproto, const struct ofputil_flow_mod *fm,
>      error = ofproto_flow_mod_start(ofproto, &ofm);
>      if (!error) {
>          ofproto_bump_tables_version(ofproto);
> -        error = ofproto_flow_mod_finish(ofproto, &ofm, req);        
> +        error = ofproto_flow_mod_finish(ofproto, &ofm, req);

The whitespace thing again.

>          ofmonitor_flush(ofproto->connmgr);
>      }
>      ovs_mutex_unlock(&ofproto_mutex);
> @@ -8437,7 +8449,7 @@ do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
>              /* Send error referring to the original message. */
>              ofconn_send_error(ofconn, be->msg, error);
>              error = OFPERR_OFPBFC_MSG_FAILED;
> - 
> +

And here.

>              /* 2. Revert.  Undo all the changes made above. */
>              LIST_FOR_EACH_REVERSE_CONTINUE(be, node, &bundle->msg_list) {
>                  if (be->type == OFPTYPE_FLOW_MOD) {
> diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
> index 8efdb20a0..bba4a9e0e 100644
> --- a/ofproto/ofproto.h
> +++ b/ofproto/ofproto.h
> @@ -53,6 +53,8 @@ struct lldp_status;
>  struct aa_settings;
>  struct aa_mapping_settings;
>  
> +#define OFPROTO_CT_DEFAULT_ZONE_ID -1
> +
>  /* Needed for the lock annotations. */
>  extern struct ovs_mutex ofproto_mutex;
>  
> @@ -384,6 +386,8 @@ void ofproto_ct_set_zone_timeout_policy(const char *datapath_type,
>                                          struct simap *timeout_policy);
>  void ofproto_ct_del_zone_timeout_policy(const char *datapath_type,
>                                          uint16_t zone);
> +void ofproto_ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
> +                                  int64_t *limit);
>  void ofproto_get_datapath_cap(const char *datapath_type,
>                                struct smap *dp_cap);
>  
> diff --git a/tests/system-traffic.at b/tests/system-traffic.at
> index f35cfaad9..d2897feb6 100644
> --- a/tests/system-traffic.at
> +++ b/tests/system-traffic.at
> @@ -5220,6 +5220,60 @@ default limit=0
>  zone=4,limit=0,count=0
>  ])
>  
> +dnl Test limit set via database.
> +VSCTL_ADD_DATAPATH_TABLE()
> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=0])
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=3])
> +
> +AT_CHECK([ovs-appctl dpctl/ct-set-limits default=10])
> +AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=3])
> +AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl
> +default limit=10
> +zone=0,limit=5,count=0
> +])
> +
> +AT_CHECK([ovs-vsctl add-zone-limit $DP_TYPE zone=0 limit=3])
> +AT_CHECK([ovs-vsctl add-zone-limit $DP_TYPE zone=3 limit=3])
> +
> +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
> +default limit=10
> +zone=0,limit=3,count=0
> +zone=3,limit=3,count=0])
> +
> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000200080000 actions=resubmit(,0)"])
> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000300080000 actions=resubmit(,0)"])
> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000400080000 actions=resubmit(,0)"])
> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000500080000 actions=resubmit(,0)"])
> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000600080000 actions=resubmit(,0)"])
> +
> +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.3," | sort ], [0], [dnl
> +udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=2),reply=(src=10.1.1.4,dst=10.1.1.3,sport=2,dport=1),zone=3
> +udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=3),reply=(src=10.1.1.4,dst=10.1.1.3,sport=3,dport=1),zone=3
> +udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=4),reply=(src=10.1.1.4,dst=10.1.1.3,sport=4,dport=1),zone=3
> +])
> +
> +AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl
> +default limit=10
> +zone=0,limit=3,count=0
> +zone=3,limit=3,count=3
> +])
> +
> +AT_CHECK([ovs-vsctl del-zone-limit $DP_TYPE zone=3])
> +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
> +default limit=10
> +zone=0,limit=3,count=0])

Are we waiting for all the ct entries to expire here?  Might take
a lot of time unnecessarily.

> +
> +AT_CHECK([ovs-vsctl set-zone-default-limit $DP_TYPE limit=5])
> +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
> +default limit=5
> +zone=0,limit=3,count=0])
> +
> +AT_CHECK([ovs-vsctl del-zone-default-limit $DP_TYPE])
> +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
> +default limit=0
> +zone=0,limit=3,count=0])
> +
>  OVS_TRAFFIC_VSWITCHD_STOP(["dnl
>  /could not create datapath/d
>  /(Cannot allocate memory) on packet/d"])
> diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
> index e9110c1d8..1e02cc8df 100644
> --- a/vswitchd/bridge.c
> +++ b/vswitchd/bridge.c
> @@ -157,6 +157,7 @@ struct aa_mapping {
>  /* Internal representation of conntrack zone configuration table in OVSDB. */
>  struct ct_zone {
>      uint16_t zone_id;
> +    int64_t limit;              /* Limit of allowed entries. */
>      struct simap tp;            /* A map from timeout policy attribute to
>                                   * timeout value. */
>      struct hmap_node node;      /* Node in 'struct datapath' 'ct_zones'
> @@ -176,6 +177,7 @@ struct datapath {
>      unsigned int last_used;     /* The last idl_seqno that this 'datapath'
>                                   * used in OVSDB. This number is used for
>                                   * garbage collection. */
> +    int64_t ct_default_limit;   /* Default limit for CT zones. */
>  };
>  
>  /* All bridges, indexed by name. */
> @@ -722,6 +724,11 @@ datapath_destroy(struct datapath *dp)
>              ct_zone_remove_and_destroy(dp, ct_zone);

Missed updating zone limits here?

>          }
>  
> +        if (dp->ct_default_limit > -1) {
> +            ofproto_ct_zone_limit_update(dp->type, OFPROTO_CT_DEFAULT_ZONE_ID,
> +                                         NULL);
> +        }
> +
>          hmap_remove(&all_datapaths, &dp->node);
>          hmap_destroy(&dp->ct_zones);
>          free(dp->type);
> @@ -743,29 +750,60 @@ ct_zones_reconfigure(struct datapath *dp, struct ovsrec_datapath *dp_cfg)
>          struct ovsrec_ct_timeout_policy *tp_cfg = zone_cfg->timeout_policy;
>  
>          ct_zone = ct_zone_lookup(&dp->ct_zones, zone_id);
> -        if (ct_zone) {
> -            struct simap new_tp = SIMAP_INITIALIZER(&new_tp);
> -            get_timeout_policy_from_ovsrec(&new_tp, tp_cfg);
> -            if (update_timeout_policy(&ct_zone->tp, &new_tp)) {
> +        if (!ct_zone) {
> +            ct_zone = ct_zone_alloc(zone_id, tp_cfg);
> +            hmap_insert(&dp->ct_zones, &ct_zone->node, hash_int(zone_id, 0));
> +        }
> +
> +        struct simap new_tp = SIMAP_INITIALIZER(&new_tp);
> +        get_timeout_policy_from_ovsrec(&new_tp, tp_cfg);
> +
> +        if (update_timeout_policy(&ct_zone->tp, &new_tp)) {
> +            if (simap_count(&ct_zone->tp)) {

Use simap_is_empty() instead.

>                  ofproto_ct_set_zone_timeout_policy(dp->type, ct_zone->zone_id,
>                                                     &ct_zone->tp);
> +            } else {
> +                ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
>              }
> -        } else {
> -            ct_zone = ct_zone_alloc(zone_id, tp_cfg);
> -            hmap_insert(&dp->ct_zones, &ct_zone->node, hash_int(zone_id, 0));
> -            ofproto_ct_set_zone_timeout_policy(dp->type, ct_zone->zone_id,
> -                                               &ct_zone->tp);
>          }
> +
> +        int64_t desired_limit = zone_cfg->limit ? *zone_cfg->limit : -1;
> +        if (ct_zone->limit != desired_limit) {

The 'limit' is not initialiezd to -1 by ct_zone_alloc().

> +            ofproto_ct_zone_limit_update(dp->type, zone_id, zone_cfg->limit);
> +        }
> +
> +        ct_zone->limit = desired_limit;

This can be moved into the 'if' statement.

>          ct_zone->last_used = idl_seqno;
>      }
>  
>      /* Purge 'ct_zone's no longer found in the database. */
>      HMAP_FOR_EACH_SAFE (ct_zone, node, &dp->ct_zones) {
> -        if (ct_zone->last_used != idl_seqno) {
> +        if (ct_zone->last_used == idl_seqno) {
> +            continue;
> +        }
> +
> +        if (simap_count(&ct_zone->tp)) {
>              ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
> -            ct_zone_remove_and_destroy(dp, ct_zone);
>          }
> +
> +        if (ct_zone->limit > -1) {
> +            ofproto_ct_zone_limit_update(dp->type, ct_zone->zone_id, NULL);
> +        }

Can we move the checks above into ct_zone_remove_and_destroy() ?
This will also help with the missed zone limit updates on datapath
destruction.

> +
> +        ct_zone_remove_and_destroy(dp, ct_zone);
> +    }
> +
> +    /* Reconfigure default CT zone limit if needed. */
> +    int64_t default_limit = dp_cfg->ct_zone_default_limit
> +                            ? *dp_cfg->ct_zone_default_limit
> +                            : -1;
> +
> +    if (dp->ct_default_limit != default_limit) {

'ct_default_limit' is used uninitialized here?  It is initialized
with xzalloc, but it's not -1, i.e. we will issue an unnecessary
update command.

> +        ofproto_ct_zone_limit_update(dp->type, OFPROTO_CT_DEFAULT_ZONE_ID,
> +                                     dp_cfg->ct_zone_default_limit);
>      }
> +
> +    dp->ct_default_limit = default_limit;

Can be moved into the 'if'.

>  }
>  
>  static void
Ales Musil Oct. 18, 2023, 7:56 a.m. UTC | #2
On Mon, Oct 16, 2023 at 9:10 PM Ilya Maximets <i.maximets@ovn.org> wrote:
>
> On 10/10/23 16:12, Ales Musil wrote:
> > Propagate the CT limit that is present in the DB into
> > datapath. The limit is currently only propagated on change
> > and can be overwritten by the dpctl commands.
> >
> > Signed-off-by: Ales Musil <amusil@redhat.com>
> > ---
> > v4: Rebase on top of current master.
> >     Make sure that the values from DB are propagated only if set. That applies to both limit and policies.
> > ---
> >  ofproto/ofproto-dpif.c     | 41 ++++++++++++++++++++++++++
> >  ofproto/ofproto-dpif.h     |  5 ++++
> >  ofproto/ofproto-provider.h |  4 +++
> >  ofproto/ofproto.c          | 16 ++++++++--
> >  ofproto/ofproto.h          |  4 +++
> >  tests/system-traffic.at    | 54 ++++++++++++++++++++++++++++++++++
> >  vswitchd/bridge.c          | 60 +++++++++++++++++++++++++++++++-------
> >  7 files changed, 171 insertions(+), 13 deletions(-)
> >
> > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> > index ba5706f6a..4fdbf0ef0 100644
> > --- a/ofproto/ofproto-dpif.c
> > +++ b/ofproto/ofproto-dpif.c
> > @@ -220,6 +220,7 @@ static void ofproto_unixctl_init(void);
> >  static void ct_zone_config_init(struct dpif_backer *backer);
> >  static void ct_zone_config_uninit(struct dpif_backer *backer);
> >  static void ct_zone_timeout_policy_sweep(struct dpif_backer *backer);
> > +static void ct_zone_limits_commit(struct dpif_backer *backer);
> >
> >  static inline struct ofproto_dpif *
> >  ofproto_dpif_cast(const struct ofproto *ofproto)
> > @@ -513,6 +514,7 @@ type_run(const char *type)
> >
> >      process_dpif_port_changes(backer);
> >      ct_zone_timeout_policy_sweep(backer);
> > +    ct_zone_limits_commit(backer);
> >
> >      return 0;
> >  }
> > @@ -5522,6 +5524,8 @@ ct_zone_config_init(struct dpif_backer *backer)
> >      cmap_init(&backer->ct_zones);
> >      hmap_init(&backer->ct_tps);
> >      ovs_list_init(&backer->ct_tp_kill_list);
> > +    ovs_list_init(&backer->ct_zone_limits_to_add);
> > +    ovs_list_init(&backer->ct_zone_limits_to_del);
> >      clear_existing_ct_timeout_policies(backer);
> >  }
> >
> > @@ -5545,6 +5549,8 @@ ct_zone_config_uninit(struct dpif_backer *backer)
> >      id_pool_destroy(backer->tp_ids);
> >      cmap_destroy(&backer->ct_zones);
> >      hmap_destroy(&backer->ct_tps);
> > +    ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_add);
> > +    ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_del);
> >  }
> >
> >  static void
> > @@ -5625,6 +5631,40 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
> >      }
> >  }
> >
> > +BUILD_ASSERT_DECL(OFPROTO_CT_DEFAULT_ZONE_ID == CT_DPIF_DEFAULT_ZONE);
>
> Why this assertion added here?
>
> > +
> > +static void
> > +ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
> > +                     int64_t *limit)
> > +{
> > +    struct dpif_backer *backer = shash_find_data(&all_dpif_backers,
> > +                                                 datapath_type);
> > +    if (!backer) {
> > +        return;
> > +    }
> > +
> > +    if (limit) {
> > +        ct_dpif_push_zone_limit(&backer->ct_zone_limits_to_add, zone_id,
> > +                                *limit, 0);
> > +    } else {
> > +        ct_dpif_push_zone_limit(&backer->ct_zone_limits_to_del, zone_id, 0, 0);
> > +    }
> > +}
> > +
> > +static void
> > +ct_zone_limits_commit(struct dpif_backer *backer)
> > +{
> > +    if (!ovs_list_is_empty(&backer->ct_zone_limits_to_add)) {
> > +        ct_dpif_set_limits(backer->dpif, &backer->ct_zone_limits_to_add);
> > +        ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_add);
> > +    }
> > +
> > +    if (!ovs_list_is_empty(&backer->ct_zone_limits_to_del)) {
> > +        ct_dpif_del_limits(backer->dpif, &backer->ct_zone_limits_to_del);
> > +        ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_del);
> > +    }
> > +}
> > +
> >  static void
> >  get_datapath_cap(const char *datapath_type, struct smap *cap)
> >  {
> > @@ -6914,4 +6954,5 @@ const struct ofproto_class ofproto_dpif_class = {
> >      ct_flush,                   /* ct_flush */
> >      ct_set_zone_timeout_policy,
> >      ct_del_zone_timeout_policy,
> > +    ct_zone_limit_update,
> >  };
> > diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
> > index d8e0cd37a..b863dd6fc 100644
> > --- a/ofproto/ofproto-dpif.h
> > +++ b/ofproto/ofproto-dpif.h
> > @@ -284,6 +284,11 @@ struct dpif_backer {
> >                                                  feature than 'bt_support'. */
> >
> >      struct atomic_count tnl_count;
> > +
> > +    struct ovs_list ct_zone_limits_to_add;  /* CT zone limits queued for
> > +                                             * addition into datapath. */
> > +    struct ovs_list ct_zone_limits_to_del;  /* CT zone limt queued for
> > +                                             * deletion from datapath. */
> >  };
> >
> >  /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
> > diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
> > index 9f7b8b6e8..33fb99280 100644
> > --- a/ofproto/ofproto-provider.h
> > +++ b/ofproto/ofproto-provider.h
> > @@ -1921,6 +1921,10 @@ struct ofproto_class {
> >      /* Deletes the timeout policy associated with 'zone' in datapath type
> >       * 'dp_type'. */
> >      void (*ct_del_zone_timeout_policy)(const char *dp_type, uint16_t zone);
> > +
> > +    /* Updates the CT zone limit for specified zone. */
> > +    void (*ct_zone_limit_update)(const char *dp_type, int32_t zone,
> > +                                 int64_t *limit);
> >  };
> >
> >  extern const struct ofproto_class ofproto_dpif_class;
> > diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> > index e78c80d11..6df3f1b27 100644
> > --- a/ofproto/ofproto.c
> > +++ b/ofproto/ofproto.c
> > @@ -1026,6 +1026,18 @@ ofproto_ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
> >
> >  }
> >
> > +void
> > +ofproto_ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
> > +                             int64_t *limit)
> > +{
> > +    datapath_type = ofproto_normalize_type(datapath_type);
> > +    const struct ofproto_class *class = ofproto_class_find__(datapath_type);
> > +
> > +    if (class && class->ct_zone_limit_update) {
> > +        class->ct_zone_limit_update(datapath_type, zone_id, limit);
> > +    }
> > +}
> > +
> >
> >  /* Spanning Tree Protocol (STP) configuration. */
> >
> > @@ -6366,7 +6378,7 @@ handle_flow_mod__(struct ofproto *ofproto, const struct ofputil_flow_mod *fm,
> >      error = ofproto_flow_mod_start(ofproto, &ofm);
> >      if (!error) {
> >          ofproto_bump_tables_version(ofproto);
> > -        error = ofproto_flow_mod_finish(ofproto, &ofm, req);
> > +        error = ofproto_flow_mod_finish(ofproto, &ofm, req);
>
> The whitespace thing again.
>
> >          ofmonitor_flush(ofproto->connmgr);
> >      }
> >      ovs_mutex_unlock(&ofproto_mutex);
> > @@ -8437,7 +8449,7 @@ do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
> >              /* Send error referring to the original message. */
> >              ofconn_send_error(ofconn, be->msg, error);
> >              error = OFPERR_OFPBFC_MSG_FAILED;
> > -
> > +
>
> And here.
>
> >              /* 2. Revert.  Undo all the changes made above. */
> >              LIST_FOR_EACH_REVERSE_CONTINUE(be, node, &bundle->msg_list) {
> >                  if (be->type == OFPTYPE_FLOW_MOD) {
> > diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
> > index 8efdb20a0..bba4a9e0e 100644
> > --- a/ofproto/ofproto.h
> > +++ b/ofproto/ofproto.h
> > @@ -53,6 +53,8 @@ struct lldp_status;
> >  struct aa_settings;
> >  struct aa_mapping_settings;
> >
> > +#define OFPROTO_CT_DEFAULT_ZONE_ID -1
> > +
> >  /* Needed for the lock annotations. */
> >  extern struct ovs_mutex ofproto_mutex;
> >
> > @@ -384,6 +386,8 @@ void ofproto_ct_set_zone_timeout_policy(const char *datapath_type,
> >                                          struct simap *timeout_policy);
> >  void ofproto_ct_del_zone_timeout_policy(const char *datapath_type,
> >                                          uint16_t zone);
> > +void ofproto_ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
> > +                                  int64_t *limit);
> >  void ofproto_get_datapath_cap(const char *datapath_type,
> >                                struct smap *dp_cap);
> >
> > diff --git a/tests/system-traffic.at b/tests/system-traffic.at
> > index f35cfaad9..d2897feb6 100644
> > --- a/tests/system-traffic.at
> > +++ b/tests/system-traffic.at
> > @@ -5220,6 +5220,60 @@ default limit=0
> >  zone=4,limit=0,count=0
> >  ])
> >
> > +dnl Test limit set via database.
> > +VSCTL_ADD_DATAPATH_TABLE()
> > +
> > +AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=0])
> > +AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=3])
> > +
> > +AT_CHECK([ovs-appctl dpctl/ct-set-limits default=10])
> > +AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=3])
> > +AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl
> > +default limit=10
> > +zone=0,limit=5,count=0
> > +])
> > +
> > +AT_CHECK([ovs-vsctl add-zone-limit $DP_TYPE zone=0 limit=3])
> > +AT_CHECK([ovs-vsctl add-zone-limit $DP_TYPE zone=3 limit=3])
> > +
> > +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
> > +default limit=10
> > +zone=0,limit=3,count=0
> > +zone=3,limit=3,count=0])
> > +
> > +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000200080000 actions=resubmit(,0)"])
> > +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000300080000 actions=resubmit(,0)"])
> > +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000400080000 actions=resubmit(,0)"])
> > +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000500080000 actions=resubmit(,0)"])
> > +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000600080000 actions=resubmit(,0)"])
> > +
> > +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.3," | sort ], [0], [dnl
> > +udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=2),reply=(src=10.1.1.4,dst=10.1.1.3,sport=2,dport=1),zone=3
> > +udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=3),reply=(src=10.1.1.4,dst=10.1.1.3,sport=3,dport=1),zone=3
> > +udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=4),reply=(src=10.1.1.4,dst=10.1.1.3,sport=4,dport=1),zone=3
> > +])
> > +
> > +AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl
> > +default limit=10
> > +zone=0,limit=3,count=0
> > +zone=3,limit=3,count=3
> > +])
> > +
> > +AT_CHECK([ovs-vsctl del-zone-limit $DP_TYPE zone=3])
> > +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
> > +default limit=10
> > +zone=0,limit=3,count=0])
>
> Are we waiting for all the ct entries to expire here?  Might take
> a lot of time unnecessarily.


The 3 entries are in zone 3, we were removing the limit from zone 3,
this just waits for this deletion to take effect.

>
> > +
> > +AT_CHECK([ovs-vsctl set-zone-default-limit $DP_TYPE limit=5])
> > +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
> > +default limit=5
> > +zone=0,limit=3,count=0])
> > +
> > +AT_CHECK([ovs-vsctl del-zone-default-limit $DP_TYPE])
> > +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
> > +default limit=0
> > +zone=0,limit=3,count=0])
> > +
> >  OVS_TRAFFIC_VSWITCHD_STOP(["dnl
> >  /could not create datapath/d
> >  /(Cannot allocate memory) on packet/d"])
> > diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
> > index e9110c1d8..1e02cc8df 100644
> > --- a/vswitchd/bridge.c
> > +++ b/vswitchd/bridge.c
> > @@ -157,6 +157,7 @@ struct aa_mapping {
> >  /* Internal representation of conntrack zone configuration table in OVSDB. */
> >  struct ct_zone {
> >      uint16_t zone_id;
> > +    int64_t limit;              /* Limit of allowed entries. */
> >      struct simap tp;            /* A map from timeout policy attribute to
> >                                   * timeout value. */
> >      struct hmap_node node;      /* Node in 'struct datapath' 'ct_zones'
> > @@ -176,6 +177,7 @@ struct datapath {
> >      unsigned int last_used;     /* The last idl_seqno that this 'datapath'
> >                                   * used in OVSDB. This number is used for
> >                                   * garbage collection. */
> > +    int64_t ct_default_limit;   /* Default limit for CT zones. */
> >  };
> >
> >  /* All bridges, indexed by name. */
> > @@ -722,6 +724,11 @@ datapath_destroy(struct datapath *dp)
> >              ct_zone_remove_and_destroy(dp, ct_zone);
>
> Missed updating zone limits here?
>
> >          }
> >
> > +        if (dp->ct_default_limit > -1) {
> > +            ofproto_ct_zone_limit_update(dp->type, OFPROTO_CT_DEFAULT_ZONE_ID,
> > +                                         NULL);
> > +        }
> > +
> >          hmap_remove(&all_datapaths, &dp->node);
> >          hmap_destroy(&dp->ct_zones);
> >          free(dp->type);
> > @@ -743,29 +750,60 @@ ct_zones_reconfigure(struct datapath *dp, struct ovsrec_datapath *dp_cfg)
> >          struct ovsrec_ct_timeout_policy *tp_cfg = zone_cfg->timeout_policy;
> >
> >          ct_zone = ct_zone_lookup(&dp->ct_zones, zone_id);
> > -        if (ct_zone) {
> > -            struct simap new_tp = SIMAP_INITIALIZER(&new_tp);
> > -            get_timeout_policy_from_ovsrec(&new_tp, tp_cfg);
> > -            if (update_timeout_policy(&ct_zone->tp, &new_tp)) {
> > +        if (!ct_zone) {
> > +            ct_zone = ct_zone_alloc(zone_id, tp_cfg);
> > +            hmap_insert(&dp->ct_zones, &ct_zone->node, hash_int(zone_id, 0));
> > +        }
> > +
> > +        struct simap new_tp = SIMAP_INITIALIZER(&new_tp);
> > +        get_timeout_policy_from_ovsrec(&new_tp, tp_cfg);
> > +
> > +        if (update_timeout_policy(&ct_zone->tp, &new_tp)) {
> > +            if (simap_count(&ct_zone->tp)) {
>
> Use simap_is_empty() instead.
>
> >                  ofproto_ct_set_zone_timeout_policy(dp->type, ct_zone->zone_id,
> >                                                     &ct_zone->tp);
> > +            } else {
> > +                ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
> >              }
> > -        } else {
> > -            ct_zone = ct_zone_alloc(zone_id, tp_cfg);
> > -            hmap_insert(&dp->ct_zones, &ct_zone->node, hash_int(zone_id, 0));
> > -            ofproto_ct_set_zone_timeout_policy(dp->type, ct_zone->zone_id,
> > -                                               &ct_zone->tp);
> >          }
> > +
> > +        int64_t desired_limit = zone_cfg->limit ? *zone_cfg->limit : -1;
> > +        if (ct_zone->limit != desired_limit) {
>
> The 'limit' is not initialiezd to -1 by ct_zone_alloc().
>
> > +            ofproto_ct_zone_limit_update(dp->type, zone_id, zone_cfg->limit);
> > +        }
> > +
> > +        ct_zone->limit = desired_limit;
>
> This can be moved into the 'if' statement.
>
> >          ct_zone->last_used = idl_seqno;
> >      }
> >
> >      /* Purge 'ct_zone's no longer found in the database. */
> >      HMAP_FOR_EACH_SAFE (ct_zone, node, &dp->ct_zones) {
> > -        if (ct_zone->last_used != idl_seqno) {
> > +        if (ct_zone->last_used == idl_seqno) {
> > +            continue;
> > +        }
> > +
> > +        if (simap_count(&ct_zone->tp)) {
> >              ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
> > -            ct_zone_remove_and_destroy(dp, ct_zone);
> >          }
> > +
> > +        if (ct_zone->limit > -1) {
> > +            ofproto_ct_zone_limit_update(dp->type, ct_zone->zone_id, NULL);
> > +        }
>
> Can we move the checks above into ct_zone_remove_and_destroy() ?
> This will also help with the missed zone limit updates on datapath
> destruction.
>
> > +
> > +        ct_zone_remove_and_destroy(dp, ct_zone);
> > +    }
> > +
> > +    /* Reconfigure default CT zone limit if needed. */
> > +    int64_t default_limit = dp_cfg->ct_zone_default_limit
> > +                            ? *dp_cfg->ct_zone_default_limit
> > +                            : -1;
> > +
> > +    if (dp->ct_default_limit != default_limit) {
>
> 'ct_default_limit' is used uninitialized here?  It is initialized
> with xzalloc, but it's not -1, i.e. we will issue an unnecessary
> update command.
>
> > +        ofproto_ct_zone_limit_update(dp->type, OFPROTO_CT_DEFAULT_ZONE_ID,
> > +                                     dp_cfg->ct_zone_default_limit);
> >      }
> > +
> > +    dp->ct_default_limit = default_limit;
>
> Can be moved into the 'if'.
>
> >  }
> >
> >  static void
>

Everything else will be addressed in v5.

Thanks,
Ales


--

Ales Musil

Senior Software Engineer - OVN Core

Red Hat EMEA

amusil@redhat.com
Ilya Maximets Oct. 18, 2023, 1:36 p.m. UTC | #3
On 10/18/23 09:56, Ales Musil wrote:
> On Mon, Oct 16, 2023 at 9:10 PM Ilya Maximets <i.maximets@ovn.org> wrote:
>>
>> On 10/10/23 16:12, Ales Musil wrote:
>>> Propagate the CT limit that is present in the DB into
>>> datapath. The limit is currently only propagated on change
>>> and can be overwritten by the dpctl commands.
>>>
>>> Signed-off-by: Ales Musil <amusil@redhat.com>
>>> ---
>>> v4: Rebase on top of current master.
>>>     Make sure that the values from DB are propagated only if set. That applies to both limit and policies.
>>> ---
>>>  ofproto/ofproto-dpif.c     | 41 ++++++++++++++++++++++++++
>>>  ofproto/ofproto-dpif.h     |  5 ++++
>>>  ofproto/ofproto-provider.h |  4 +++
>>>  ofproto/ofproto.c          | 16 ++++++++--
>>>  ofproto/ofproto.h          |  4 +++
>>>  tests/system-traffic.at    | 54 ++++++++++++++++++++++++++++++++++
>>>  vswitchd/bridge.c          | 60 +++++++++++++++++++++++++++++++-------
>>>  7 files changed, 171 insertions(+), 13 deletions(-)
>>>
>>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>>> index ba5706f6a..4fdbf0ef0 100644
>>> --- a/ofproto/ofproto-dpif.c
>>> +++ b/ofproto/ofproto-dpif.c
>>> @@ -220,6 +220,7 @@ static void ofproto_unixctl_init(void);
>>>  static void ct_zone_config_init(struct dpif_backer *backer);
>>>  static void ct_zone_config_uninit(struct dpif_backer *backer);
>>>  static void ct_zone_timeout_policy_sweep(struct dpif_backer *backer);
>>> +static void ct_zone_limits_commit(struct dpif_backer *backer);
>>>
>>>  static inline struct ofproto_dpif *
>>>  ofproto_dpif_cast(const struct ofproto *ofproto)
>>> @@ -513,6 +514,7 @@ type_run(const char *type)
>>>
>>>      process_dpif_port_changes(backer);
>>>      ct_zone_timeout_policy_sweep(backer);
>>> +    ct_zone_limits_commit(backer);
>>>
>>>      return 0;
>>>  }
>>> @@ -5522,6 +5524,8 @@ ct_zone_config_init(struct dpif_backer *backer)
>>>      cmap_init(&backer->ct_zones);
>>>      hmap_init(&backer->ct_tps);
>>>      ovs_list_init(&backer->ct_tp_kill_list);
>>> +    ovs_list_init(&backer->ct_zone_limits_to_add);
>>> +    ovs_list_init(&backer->ct_zone_limits_to_del);
>>>      clear_existing_ct_timeout_policies(backer);
>>>  }
>>>
>>> @@ -5545,6 +5549,8 @@ ct_zone_config_uninit(struct dpif_backer *backer)
>>>      id_pool_destroy(backer->tp_ids);
>>>      cmap_destroy(&backer->ct_zones);
>>>      hmap_destroy(&backer->ct_tps);
>>> +    ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_add);
>>> +    ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_del);
>>>  }
>>>
>>>  static void
>>> @@ -5625,6 +5631,40 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
>>>      }
>>>  }
>>>
>>> +BUILD_ASSERT_DECL(OFPROTO_CT_DEFAULT_ZONE_ID == CT_DPIF_DEFAULT_ZONE);
>>
>> Why this assertion added here?
>>
>>> +
>>> +static void
>>> +ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
>>> +                     int64_t *limit)
>>> +{
>>> +    struct dpif_backer *backer = shash_find_data(&all_dpif_backers,
>>> +                                                 datapath_type);
>>> +    if (!backer) {
>>> +        return;
>>> +    }
>>> +
>>> +    if (limit) {
>>> +        ct_dpif_push_zone_limit(&backer->ct_zone_limits_to_add, zone_id,
>>> +                                *limit, 0);
>>> +    } else {
>>> +        ct_dpif_push_zone_limit(&backer->ct_zone_limits_to_del, zone_id, 0, 0);
>>> +    }
>>> +}
>>> +
>>> +static void
>>> +ct_zone_limits_commit(struct dpif_backer *backer)
>>> +{
>>> +    if (!ovs_list_is_empty(&backer->ct_zone_limits_to_add)) {
>>> +        ct_dpif_set_limits(backer->dpif, &backer->ct_zone_limits_to_add);
>>> +        ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_add);
>>> +    }
>>> +
>>> +    if (!ovs_list_is_empty(&backer->ct_zone_limits_to_del)) {
>>> +        ct_dpif_del_limits(backer->dpif, &backer->ct_zone_limits_to_del);
>>> +        ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_del);
>>> +    }
>>> +}
>>> +
>>>  static void
>>>  get_datapath_cap(const char *datapath_type, struct smap *cap)
>>>  {
>>> @@ -6914,4 +6954,5 @@ const struct ofproto_class ofproto_dpif_class = {
>>>      ct_flush,                   /* ct_flush */
>>>      ct_set_zone_timeout_policy,
>>>      ct_del_zone_timeout_policy,
>>> +    ct_zone_limit_update,
>>>  };
>>> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
>>> index d8e0cd37a..b863dd6fc 100644
>>> --- a/ofproto/ofproto-dpif.h
>>> +++ b/ofproto/ofproto-dpif.h
>>> @@ -284,6 +284,11 @@ struct dpif_backer {
>>>                                                  feature than 'bt_support'. */
>>>
>>>      struct atomic_count tnl_count;
>>> +
>>> +    struct ovs_list ct_zone_limits_to_add;  /* CT zone limits queued for
>>> +                                             * addition into datapath. */
>>> +    struct ovs_list ct_zone_limits_to_del;  /* CT zone limt queued for
>>> +                                             * deletion from datapath. */
>>>  };
>>>
>>>  /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
>>> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
>>> index 9f7b8b6e8..33fb99280 100644
>>> --- a/ofproto/ofproto-provider.h
>>> +++ b/ofproto/ofproto-provider.h
>>> @@ -1921,6 +1921,10 @@ struct ofproto_class {
>>>      /* Deletes the timeout policy associated with 'zone' in datapath type
>>>       * 'dp_type'. */
>>>      void (*ct_del_zone_timeout_policy)(const char *dp_type, uint16_t zone);
>>> +
>>> +    /* Updates the CT zone limit for specified zone. */
>>> +    void (*ct_zone_limit_update)(const char *dp_type, int32_t zone,
>>> +                                 int64_t *limit);
>>>  };
>>>
>>>  extern const struct ofproto_class ofproto_dpif_class;
>>> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
>>> index e78c80d11..6df3f1b27 100644
>>> --- a/ofproto/ofproto.c
>>> +++ b/ofproto/ofproto.c
>>> @@ -1026,6 +1026,18 @@ ofproto_ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
>>>
>>>  }
>>>
>>> +void
>>> +ofproto_ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
>>> +                             int64_t *limit)
>>> +{
>>> +    datapath_type = ofproto_normalize_type(datapath_type);
>>> +    const struct ofproto_class *class = ofproto_class_find__(datapath_type);
>>> +
>>> +    if (class && class->ct_zone_limit_update) {
>>> +        class->ct_zone_limit_update(datapath_type, zone_id, limit);
>>> +    }
>>> +}
>>> +
>>>
>>>  /* Spanning Tree Protocol (STP) configuration. */
>>>
>>> @@ -6366,7 +6378,7 @@ handle_flow_mod__(struct ofproto *ofproto, const struct ofputil_flow_mod *fm,
>>>      error = ofproto_flow_mod_start(ofproto, &ofm);
>>>      if (!error) {
>>>          ofproto_bump_tables_version(ofproto);
>>> -        error = ofproto_flow_mod_finish(ofproto, &ofm, req);
>>> +        error = ofproto_flow_mod_finish(ofproto, &ofm, req);
>>
>> The whitespace thing again.
>>
>>>          ofmonitor_flush(ofproto->connmgr);
>>>      }
>>>      ovs_mutex_unlock(&ofproto_mutex);
>>> @@ -8437,7 +8449,7 @@ do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
>>>              /* Send error referring to the original message. */
>>>              ofconn_send_error(ofconn, be->msg, error);
>>>              error = OFPERR_OFPBFC_MSG_FAILED;
>>> -
>>> +
>>
>> And here.
>>
>>>              /* 2. Revert.  Undo all the changes made above. */
>>>              LIST_FOR_EACH_REVERSE_CONTINUE(be, node, &bundle->msg_list) {
>>>                  if (be->type == OFPTYPE_FLOW_MOD) {
>>> diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
>>> index 8efdb20a0..bba4a9e0e 100644
>>> --- a/ofproto/ofproto.h
>>> +++ b/ofproto/ofproto.h
>>> @@ -53,6 +53,8 @@ struct lldp_status;
>>>  struct aa_settings;
>>>  struct aa_mapping_settings;
>>>
>>> +#define OFPROTO_CT_DEFAULT_ZONE_ID -1
>>> +
>>>  /* Needed for the lock annotations. */
>>>  extern struct ovs_mutex ofproto_mutex;
>>>
>>> @@ -384,6 +386,8 @@ void ofproto_ct_set_zone_timeout_policy(const char *datapath_type,
>>>                                          struct simap *timeout_policy);
>>>  void ofproto_ct_del_zone_timeout_policy(const char *datapath_type,
>>>                                          uint16_t zone);
>>> +void ofproto_ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
>>> +                                  int64_t *limit);
>>>  void ofproto_get_datapath_cap(const char *datapath_type,
>>>                                struct smap *dp_cap);
>>>
>>> diff --git a/tests/system-traffic.at b/tests/system-traffic.at
>>> index f35cfaad9..d2897feb6 100644
>>> --- a/tests/system-traffic.at
>>> +++ b/tests/system-traffic.at
>>> @@ -5220,6 +5220,60 @@ default limit=0
>>>  zone=4,limit=0,count=0
>>>  ])
>>>
>>> +dnl Test limit set via database.
>>> +VSCTL_ADD_DATAPATH_TABLE()
>>> +
>>> +AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=0])
>>> +AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=3])
>>> +
>>> +AT_CHECK([ovs-appctl dpctl/ct-set-limits default=10])
>>> +AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=3])
>>> +AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl
>>> +default limit=10
>>> +zone=0,limit=5,count=0
>>> +])
>>> +
>>> +AT_CHECK([ovs-vsctl add-zone-limit $DP_TYPE zone=0 limit=3])
>>> +AT_CHECK([ovs-vsctl add-zone-limit $DP_TYPE zone=3 limit=3])
>>> +
>>> +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
>>> +default limit=10
>>> +zone=0,limit=3,count=0
>>> +zone=3,limit=3,count=0])
>>> +
>>> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000200080000 actions=resubmit(,0)"])
>>> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000300080000 actions=resubmit(,0)"])
>>> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000400080000 actions=resubmit(,0)"])
>>> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000500080000 actions=resubmit(,0)"])
>>> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000600080000 actions=resubmit(,0)"])
>>> +
>>> +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.3," | sort ], [0], [dnl
>>> +udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=2),reply=(src=10.1.1.4,dst=10.1.1.3,sport=2,dport=1),zone=3
>>> +udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=3),reply=(src=10.1.1.4,dst=10.1.1.3,sport=3,dport=1),zone=3
>>> +udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=4),reply=(src=10.1.1.4,dst=10.1.1.3,sport=4,dport=1),zone=3
>>> +])
>>> +
>>> +AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl
>>> +default limit=10
>>> +zone=0,limit=3,count=0
>>> +zone=3,limit=3,count=3
>>> +])
>>> +
>>> +AT_CHECK([ovs-vsctl del-zone-limit $DP_TYPE zone=3])
>>> +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
>>> +default limit=10
>>> +zone=0,limit=3,count=0])
>>
>> Are we waiting for all the ct entries to expire here?  Might take
>> a lot of time unnecessarily.
> 
> 
> The 3 entries are in zone 3, we were removing the limit from zone 3,
> this just waits for this deletion to take effect.

Oh, you're right.  I looked at the wrong place.  :)

> 
>>
>>> +
>>> +AT_CHECK([ovs-vsctl set-zone-default-limit $DP_TYPE limit=5])
>>> +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
>>> +default limit=5
>>> +zone=0,limit=3,count=0])
>>> +
>>> +AT_CHECK([ovs-vsctl del-zone-default-limit $DP_TYPE])
>>> +OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
>>> +default limit=0
>>> +zone=0,limit=3,count=0])
>>> +
>>>  OVS_TRAFFIC_VSWITCHD_STOP(["dnl
>>>  /could not create datapath/d
>>>  /(Cannot allocate memory) on packet/d"])
>>> diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
>>> index e9110c1d8..1e02cc8df 100644
>>> --- a/vswitchd/bridge.c
>>> +++ b/vswitchd/bridge.c
>>> @@ -157,6 +157,7 @@ struct aa_mapping {
>>>  /* Internal representation of conntrack zone configuration table in OVSDB. */
>>>  struct ct_zone {
>>>      uint16_t zone_id;
>>> +    int64_t limit;              /* Limit of allowed entries. */
>>>      struct simap tp;            /* A map from timeout policy attribute to
>>>                                   * timeout value. */
>>>      struct hmap_node node;      /* Node in 'struct datapath' 'ct_zones'
>>> @@ -176,6 +177,7 @@ struct datapath {
>>>      unsigned int last_used;     /* The last idl_seqno that this 'datapath'
>>>                                   * used in OVSDB. This number is used for
>>>                                   * garbage collection. */
>>> +    int64_t ct_default_limit;   /* Default limit for CT zones. */
>>>  };
>>>
>>>  /* All bridges, indexed by name. */
>>> @@ -722,6 +724,11 @@ datapath_destroy(struct datapath *dp)
>>>              ct_zone_remove_and_destroy(dp, ct_zone);
>>
>> Missed updating zone limits here?
>>
>>>          }
>>>
>>> +        if (dp->ct_default_limit > -1) {
>>> +            ofproto_ct_zone_limit_update(dp->type, OFPROTO_CT_DEFAULT_ZONE_ID,
>>> +                                         NULL);
>>> +        }
>>> +
>>>          hmap_remove(&all_datapaths, &dp->node);
>>>          hmap_destroy(&dp->ct_zones);
>>>          free(dp->type);
>>> @@ -743,29 +750,60 @@ ct_zones_reconfigure(struct datapath *dp, struct ovsrec_datapath *dp_cfg)
>>>          struct ovsrec_ct_timeout_policy *tp_cfg = zone_cfg->timeout_policy;
>>>
>>>          ct_zone = ct_zone_lookup(&dp->ct_zones, zone_id);
>>> -        if (ct_zone) {
>>> -            struct simap new_tp = SIMAP_INITIALIZER(&new_tp);
>>> -            get_timeout_policy_from_ovsrec(&new_tp, tp_cfg);
>>> -            if (update_timeout_policy(&ct_zone->tp, &new_tp)) {
>>> +        if (!ct_zone) {
>>> +            ct_zone = ct_zone_alloc(zone_id, tp_cfg);
>>> +            hmap_insert(&dp->ct_zones, &ct_zone->node, hash_int(zone_id, 0));
>>> +        }
>>> +
>>> +        struct simap new_tp = SIMAP_INITIALIZER(&new_tp);
>>> +        get_timeout_policy_from_ovsrec(&new_tp, tp_cfg);
>>> +
>>> +        if (update_timeout_policy(&ct_zone->tp, &new_tp)) {
>>> +            if (simap_count(&ct_zone->tp)) {
>>
>> Use simap_is_empty() instead.
>>
>>>                  ofproto_ct_set_zone_timeout_policy(dp->type, ct_zone->zone_id,
>>>                                                     &ct_zone->tp);
>>> +            } else {
>>> +                ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
>>>              }
>>> -        } else {
>>> -            ct_zone = ct_zone_alloc(zone_id, tp_cfg);
>>> -            hmap_insert(&dp->ct_zones, &ct_zone->node, hash_int(zone_id, 0));
>>> -            ofproto_ct_set_zone_timeout_policy(dp->type, ct_zone->zone_id,
>>> -                                               &ct_zone->tp);
>>>          }
>>> +
>>> +        int64_t desired_limit = zone_cfg->limit ? *zone_cfg->limit : -1;
>>> +        if (ct_zone->limit != desired_limit) {
>>
>> The 'limit' is not initialiezd to -1 by ct_zone_alloc().
>>
>>> +            ofproto_ct_zone_limit_update(dp->type, zone_id, zone_cfg->limit);
>>> +        }
>>> +
>>> +        ct_zone->limit = desired_limit;
>>
>> This can be moved into the 'if' statement.
>>
>>>          ct_zone->last_used = idl_seqno;
>>>      }
>>>
>>>      /* Purge 'ct_zone's no longer found in the database. */
>>>      HMAP_FOR_EACH_SAFE (ct_zone, node, &dp->ct_zones) {
>>> -        if (ct_zone->last_used != idl_seqno) {
>>> +        if (ct_zone->last_used == idl_seqno) {
>>> +            continue;
>>> +        }
>>> +
>>> +        if (simap_count(&ct_zone->tp)) {
>>>              ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
>>> -            ct_zone_remove_and_destroy(dp, ct_zone);
>>>          }
>>> +
>>> +        if (ct_zone->limit > -1) {
>>> +            ofproto_ct_zone_limit_update(dp->type, ct_zone->zone_id, NULL);
>>> +        }
>>
>> Can we move the checks above into ct_zone_remove_and_destroy() ?
>> This will also help with the missed zone limit updates on datapath
>> destruction.
>>
>>> +
>>> +        ct_zone_remove_and_destroy(dp, ct_zone);
>>> +    }
>>> +
>>> +    /* Reconfigure default CT zone limit if needed. */
>>> +    int64_t default_limit = dp_cfg->ct_zone_default_limit
>>> +                            ? *dp_cfg->ct_zone_default_limit
>>> +                            : -1;
>>> +
>>> +    if (dp->ct_default_limit != default_limit) {
>>
>> 'ct_default_limit' is used uninitialized here?  It is initialized
>> with xzalloc, but it's not -1, i.e. we will issue an unnecessary
>> update command.
>>
>>> +        ofproto_ct_zone_limit_update(dp->type, OFPROTO_CT_DEFAULT_ZONE_ID,
>>> +                                     dp_cfg->ct_zone_default_limit);
>>>      }
>>> +
>>> +    dp->ct_default_limit = default_limit;
>>
>> Can be moved into the 'if'.
>>
>>>  }
>>>
>>>  static void
>>
> 
> Everything else will be addressed in v5.
> 
> Thanks,
> Ales
> 
> 
> --
> 
> Ales Musil
> 
> Senior Software Engineer - OVN Core
> 
> Red Hat EMEA
> 
> amusil@redhat.com
>
diff mbox series

Patch

diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index ba5706f6a..4fdbf0ef0 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -220,6 +220,7 @@  static void ofproto_unixctl_init(void);
 static void ct_zone_config_init(struct dpif_backer *backer);
 static void ct_zone_config_uninit(struct dpif_backer *backer);
 static void ct_zone_timeout_policy_sweep(struct dpif_backer *backer);
+static void ct_zone_limits_commit(struct dpif_backer *backer);
 
 static inline struct ofproto_dpif *
 ofproto_dpif_cast(const struct ofproto *ofproto)
@@ -513,6 +514,7 @@  type_run(const char *type)
 
     process_dpif_port_changes(backer);
     ct_zone_timeout_policy_sweep(backer);
+    ct_zone_limits_commit(backer);
 
     return 0;
 }
@@ -5522,6 +5524,8 @@  ct_zone_config_init(struct dpif_backer *backer)
     cmap_init(&backer->ct_zones);
     hmap_init(&backer->ct_tps);
     ovs_list_init(&backer->ct_tp_kill_list);
+    ovs_list_init(&backer->ct_zone_limits_to_add);
+    ovs_list_init(&backer->ct_zone_limits_to_del);
     clear_existing_ct_timeout_policies(backer);
 }
 
@@ -5545,6 +5549,8 @@  ct_zone_config_uninit(struct dpif_backer *backer)
     id_pool_destroy(backer->tp_ids);
     cmap_destroy(&backer->ct_zones);
     hmap_destroy(&backer->ct_tps);
+    ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_add);
+    ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_del);
 }
 
 static void
@@ -5625,6 +5631,40 @@  ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
     }
 }
 
+BUILD_ASSERT_DECL(OFPROTO_CT_DEFAULT_ZONE_ID == CT_DPIF_DEFAULT_ZONE);
+
+static void
+ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
+                     int64_t *limit)
+{
+    struct dpif_backer *backer = shash_find_data(&all_dpif_backers,
+                                                 datapath_type);
+    if (!backer) {
+        return;
+    }
+
+    if (limit) {
+        ct_dpif_push_zone_limit(&backer->ct_zone_limits_to_add, zone_id,
+                                *limit, 0);
+    } else {
+        ct_dpif_push_zone_limit(&backer->ct_zone_limits_to_del, zone_id, 0, 0);
+    }
+}
+
+static void
+ct_zone_limits_commit(struct dpif_backer *backer)
+{
+    if (!ovs_list_is_empty(&backer->ct_zone_limits_to_add)) {
+        ct_dpif_set_limits(backer->dpif, &backer->ct_zone_limits_to_add);
+        ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_add);
+    }
+
+    if (!ovs_list_is_empty(&backer->ct_zone_limits_to_del)) {
+        ct_dpif_del_limits(backer->dpif, &backer->ct_zone_limits_to_del);
+        ct_dpif_free_zone_limits(&backer->ct_zone_limits_to_del);
+    }
+}
+
 static void
 get_datapath_cap(const char *datapath_type, struct smap *cap)
 {
@@ -6914,4 +6954,5 @@  const struct ofproto_class ofproto_dpif_class = {
     ct_flush,                   /* ct_flush */
     ct_set_zone_timeout_policy,
     ct_del_zone_timeout_policy,
+    ct_zone_limit_update,
 };
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
index d8e0cd37a..b863dd6fc 100644
--- a/ofproto/ofproto-dpif.h
+++ b/ofproto/ofproto-dpif.h
@@ -284,6 +284,11 @@  struct dpif_backer {
                                                 feature than 'bt_support'. */
 
     struct atomic_count tnl_count;
+
+    struct ovs_list ct_zone_limits_to_add;  /* CT zone limits queued for
+                                             * addition into datapath. */
+    struct ovs_list ct_zone_limits_to_del;  /* CT zone limt queued for
+                                             * deletion from datapath. */
 };
 
 /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 9f7b8b6e8..33fb99280 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1921,6 +1921,10 @@  struct ofproto_class {
     /* Deletes the timeout policy associated with 'zone' in datapath type
      * 'dp_type'. */
     void (*ct_del_zone_timeout_policy)(const char *dp_type, uint16_t zone);
+
+    /* Updates the CT zone limit for specified zone. */
+    void (*ct_zone_limit_update)(const char *dp_type, int32_t zone,
+                                 int64_t *limit);
 };
 
 extern const struct ofproto_class ofproto_dpif_class;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index e78c80d11..6df3f1b27 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -1026,6 +1026,18 @@  ofproto_ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
 
 }
 
+void
+ofproto_ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
+                             int64_t *limit)
+{
+    datapath_type = ofproto_normalize_type(datapath_type);
+    const struct ofproto_class *class = ofproto_class_find__(datapath_type);
+
+    if (class && class->ct_zone_limit_update) {
+        class->ct_zone_limit_update(datapath_type, zone_id, limit);
+    }
+}
+
 
 /* Spanning Tree Protocol (STP) configuration. */
 
@@ -6366,7 +6378,7 @@  handle_flow_mod__(struct ofproto *ofproto, const struct ofputil_flow_mod *fm,
     error = ofproto_flow_mod_start(ofproto, &ofm);
     if (!error) {
         ofproto_bump_tables_version(ofproto);
-        error = ofproto_flow_mod_finish(ofproto, &ofm, req);        
+        error = ofproto_flow_mod_finish(ofproto, &ofm, req);
         ofmonitor_flush(ofproto->connmgr);
     }
     ovs_mutex_unlock(&ofproto_mutex);
@@ -8437,7 +8449,7 @@  do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
             /* Send error referring to the original message. */
             ofconn_send_error(ofconn, be->msg, error);
             error = OFPERR_OFPBFC_MSG_FAILED;
- 
+
             /* 2. Revert.  Undo all the changes made above. */
             LIST_FOR_EACH_REVERSE_CONTINUE(be, node, &bundle->msg_list) {
                 if (be->type == OFPTYPE_FLOW_MOD) {
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 8efdb20a0..bba4a9e0e 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -53,6 +53,8 @@  struct lldp_status;
 struct aa_settings;
 struct aa_mapping_settings;
 
+#define OFPROTO_CT_DEFAULT_ZONE_ID -1
+
 /* Needed for the lock annotations. */
 extern struct ovs_mutex ofproto_mutex;
 
@@ -384,6 +386,8 @@  void ofproto_ct_set_zone_timeout_policy(const char *datapath_type,
                                         struct simap *timeout_policy);
 void ofproto_ct_del_zone_timeout_policy(const char *datapath_type,
                                         uint16_t zone);
+void ofproto_ct_zone_limit_update(const char *datapath_type, int32_t zone_id,
+                                  int64_t *limit);
 void ofproto_get_datapath_cap(const char *datapath_type,
                               struct smap *dp_cap);
 
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index f35cfaad9..d2897feb6 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -5220,6 +5220,60 @@  default limit=0
 zone=4,limit=0,count=0
 ])
 
+dnl Test limit set via database.
+VSCTL_ADD_DATAPATH_TABLE()
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=0])
+AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=3])
+
+AT_CHECK([ovs-appctl dpctl/ct-set-limits default=10])
+AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=3])
+AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl
+default limit=10
+zone=0,limit=5,count=0
+])
+
+AT_CHECK([ovs-vsctl add-zone-limit $DP_TYPE zone=0 limit=3])
+AT_CHECK([ovs-vsctl add-zone-limit $DP_TYPE zone=3 limit=3])
+
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
+default limit=10
+zone=0,limit=3,count=0
+zone=3,limit=3,count=0])
+
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000200080000 actions=resubmit(,0)"])
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000300080000 actions=resubmit(,0)"])
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000400080000 actions=resubmit(,0)"])
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000500080000 actions=resubmit(,0)"])
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4c90a0101030a0101040001000600080000 actions=resubmit(,0)"])
+
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.3," | sort ], [0], [dnl
+udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=2),reply=(src=10.1.1.4,dst=10.1.1.3,sport=2,dport=1),zone=3
+udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=3),reply=(src=10.1.1.4,dst=10.1.1.3,sport=3,dport=1),zone=3
+udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=4),reply=(src=10.1.1.4,dst=10.1.1.3,sport=4,dport=1),zone=3
+])
+
+AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl
+default limit=10
+zone=0,limit=3,count=0
+zone=3,limit=3,count=3
+])
+
+AT_CHECK([ovs-vsctl del-zone-limit $DP_TYPE zone=3])
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
+default limit=10
+zone=0,limit=3,count=0])
+
+AT_CHECK([ovs-vsctl set-zone-default-limit $DP_TYPE limit=5])
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
+default limit=5
+zone=0,limit=3,count=0])
+
+AT_CHECK([ovs-vsctl del-zone-default-limit $DP_TYPE])
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl
+default limit=0
+zone=0,limit=3,count=0])
+
 OVS_TRAFFIC_VSWITCHD_STOP(["dnl
 /could not create datapath/d
 /(Cannot allocate memory) on packet/d"])
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index e9110c1d8..1e02cc8df 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -157,6 +157,7 @@  struct aa_mapping {
 /* Internal representation of conntrack zone configuration table in OVSDB. */
 struct ct_zone {
     uint16_t zone_id;
+    int64_t limit;              /* Limit of allowed entries. */
     struct simap tp;            /* A map from timeout policy attribute to
                                  * timeout value. */
     struct hmap_node node;      /* Node in 'struct datapath' 'ct_zones'
@@ -176,6 +177,7 @@  struct datapath {
     unsigned int last_used;     /* The last idl_seqno that this 'datapath'
                                  * used in OVSDB. This number is used for
                                  * garbage collection. */
+    int64_t ct_default_limit;   /* Default limit for CT zones. */
 };
 
 /* All bridges, indexed by name. */
@@ -722,6 +724,11 @@  datapath_destroy(struct datapath *dp)
             ct_zone_remove_and_destroy(dp, ct_zone);
         }
 
+        if (dp->ct_default_limit > -1) {
+            ofproto_ct_zone_limit_update(dp->type, OFPROTO_CT_DEFAULT_ZONE_ID,
+                                         NULL);
+        }
+
         hmap_remove(&all_datapaths, &dp->node);
         hmap_destroy(&dp->ct_zones);
         free(dp->type);
@@ -743,29 +750,60 @@  ct_zones_reconfigure(struct datapath *dp, struct ovsrec_datapath *dp_cfg)
         struct ovsrec_ct_timeout_policy *tp_cfg = zone_cfg->timeout_policy;
 
         ct_zone = ct_zone_lookup(&dp->ct_zones, zone_id);
-        if (ct_zone) {
-            struct simap new_tp = SIMAP_INITIALIZER(&new_tp);
-            get_timeout_policy_from_ovsrec(&new_tp, tp_cfg);
-            if (update_timeout_policy(&ct_zone->tp, &new_tp)) {
+        if (!ct_zone) {
+            ct_zone = ct_zone_alloc(zone_id, tp_cfg);
+            hmap_insert(&dp->ct_zones, &ct_zone->node, hash_int(zone_id, 0));
+        }
+
+        struct simap new_tp = SIMAP_INITIALIZER(&new_tp);
+        get_timeout_policy_from_ovsrec(&new_tp, tp_cfg);
+
+        if (update_timeout_policy(&ct_zone->tp, &new_tp)) {
+            if (simap_count(&ct_zone->tp)) {
                 ofproto_ct_set_zone_timeout_policy(dp->type, ct_zone->zone_id,
                                                    &ct_zone->tp);
+            } else {
+                ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
             }
-        } else {
-            ct_zone = ct_zone_alloc(zone_id, tp_cfg);
-            hmap_insert(&dp->ct_zones, &ct_zone->node, hash_int(zone_id, 0));
-            ofproto_ct_set_zone_timeout_policy(dp->type, ct_zone->zone_id,
-                                               &ct_zone->tp);
         }
+
+        int64_t desired_limit = zone_cfg->limit ? *zone_cfg->limit : -1;
+        if (ct_zone->limit != desired_limit) {
+            ofproto_ct_zone_limit_update(dp->type, zone_id, zone_cfg->limit);
+        }
+
+        ct_zone->limit = desired_limit;
         ct_zone->last_used = idl_seqno;
     }
 
     /* Purge 'ct_zone's no longer found in the database. */
     HMAP_FOR_EACH_SAFE (ct_zone, node, &dp->ct_zones) {
-        if (ct_zone->last_used != idl_seqno) {
+        if (ct_zone->last_used == idl_seqno) {
+            continue;
+        }
+
+        if (simap_count(&ct_zone->tp)) {
             ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
-            ct_zone_remove_and_destroy(dp, ct_zone);
         }
+
+        if (ct_zone->limit > -1) {
+            ofproto_ct_zone_limit_update(dp->type, ct_zone->zone_id, NULL);
+        }
+
+        ct_zone_remove_and_destroy(dp, ct_zone);
+    }
+
+    /* Reconfigure default CT zone limit if needed. */
+    int64_t default_limit = dp_cfg->ct_zone_default_limit
+                            ? *dp_cfg->ct_zone_default_limit
+                            : -1;
+
+    if (dp->ct_default_limit != default_limit) {
+        ofproto_ct_zone_limit_update(dp->type, OFPROTO_CT_DEFAULT_ZONE_ID,
+                                     dp_cfg->ct_zone_default_limit);
     }
+
+    dp->ct_default_limit = default_limit;
 }
 
 static void