[ovs-dev] [PATCH v2] ovn: OVN Support QoS meter

Miguel Angel Ajo Pelayo majopela at redhat.com
Thu Sep 21 11:03:53 UTC 2017


I thought we didn't have meters yet in OvS switch implementation (beyond
openflow protocol support) as per:

http://docs.openvswitch.org/en/latest/faq/qos/

Has this changed in master?

On Wed, Sep 20, 2017 at 1:52 PM, Guoshuai Li <ligs at dtdream.com> wrote:

> ovn-northd modify:
> add bandwidth column in NB's QOS table.
> add QOS_METER stages in Logical switch ingress/egress.
> add set_meter() action in SB's LFlow table.
>
> ovn-controller modify:
> add meter_table for meter action process openflow meter table.
>
> This feature is only supported in DPDK.
> ---
>
> v2: Fix Ingress/Egress Table id error.
>
> ---
>  NEWS                            |   1 +
>  include/ovn/actions.h           |  31 +++++++-
>  ovn/controller/lflow.c          |   9 ++-
>  ovn/controller/lflow.h          |   2 +
>  ovn/controller/ofctrl.c         | 158 ++++++++++++++++++++++++++++++
> +++++++++-
>  ovn/controller/ofctrl.h         |   6 +-
>  ovn/controller/ovn-controller.c |  25 ++++++-
>  ovn/lib/actions.c               | 114 +++++++++++++++++++++++++++++
>  ovn/northd/ovn-northd.8.xml     |  54 ++++++++++----
>  ovn/northd/ovn-northd.c         | 108 +++++++++++++++++----------
>  ovn/ovn-nb.ovsschema            |  11 ++-
>  ovn/ovn-nb.xml                  |  16 ++++
>  ovn/ovn-sb.xml                  |  15 ++++
>  ovn/utilities/ovn-trace.c       |   4 +
>  tests/ovn.at                    |   7 +-
>  tests/test-ovn.c                |   8 ++
>  16 files changed, 504 insertions(+), 65 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 6a5d2bf98..b97e7bff7 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -59,6 +59,7 @@ v2.8.0 - xx xxx xxxx
>         gateway.
>       * Add support for ACL logging.
>       * ovn-northd now has native support for active-standby high
> availability.
> +     * Add support for QoS bandwidth limt with DPDK.
>     - Tracing with ofproto/trace now traces through recirculation.
>     - OVSDB:
>       * New support for role-based access control (see ovsdb-server(1)).
> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> index 0a04af7aa..8dbb895f3 100644
> --- a/include/ovn/actions.h
> +++ b/include/ovn/actions.h
> @@ -72,7 +72,8 @@ struct simap;
>      OVNACT(PUT_DHCPV6_OPTS,   ovnact_put_dhcp_opts)   \
>      OVNACT(SET_QUEUE,         ovnact_set_queue)       \
>      OVNACT(DNS_LOOKUP,        ovnact_dns_lookup)      \
> -    OVNACT(LOG,               ovnact_log)
> +    OVNACT(LOG,               ovnact_log)             \
> +    OVNACT(SET_METER,         ovnact_set_meter)
>
>  /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
>  enum OVS_PACKED_ENUM ovnact_type {
> @@ -274,6 +275,13 @@ struct ovnact_log {
>      char *name;
>  };
>
> +/* OVNACT_SET_METER. */
> +struct ovnact_set_meter {
> +    struct ovnact ovnact;
> +    uint32_t rate;              /* 32-bit rate field. */
> +    uint32_t burst;             /* 32-bit burst rate field. */
> +};
> +
>  /* Internal use by the helpers below. */
>  void ovnact_init(struct ovnact *, enum ovnact_type, size_t len);
>  void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len);
> @@ -350,6 +358,24 @@ struct group_info {
>                           * group_table's 'group_ids' bitmap. */
>  };
>
> +#define MAX_OVN_METERS 65535
> +
> +struct meter_table {
> +    unsigned long *meter_ids;  /* Used as a bitmap with value set
> +                                * for allocated meter ids in either
> +                                * desired_meters or existing_meters. */
> +    struct hmap desired_meters;
> +    struct hmap existing_meters;
> +};
> +
> +struct meter_info {
> +    struct hmap_node hmap_node;
> +    struct ds meter;
> +    uint32_t meter_id;
> +    bool new_meter_id;  /* 'True' if 'meter_id' was reserved from
> +                         * meter_table's 'meter_ids' bitmap. */
> +};
> +
>  enum action_opcode {
>      /* "arp { ...actions... }".
>       *
> @@ -484,6 +510,9 @@ struct ovnact_encode_params {
>      /* A struct to figure out the group_id for group actions. */
>      struct group_table *group_table;
>
> +    /* A struct to figure out the meter_id for meter actions. */
> +    struct meter_table *meter_table;
> +
>      /* OVN maps each logical flow table (ltable), one-to-one, onto a
> physical
>       * OpenFlow flow table (ptable).  A number of parameters describe this
>       * mapping and data related to flow tables:
> diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
> index 20a18c259..bf7a59f32 100644
> --- a/ovn/controller/lflow.c
> +++ b/ovn/controller/lflow.c
> @@ -62,6 +62,7 @@ static void consider_logical_flow(struct controller_ctx
> *ctx,
>                                    const struct sbrec_logical_flow *lflow,
>                                    const struct hmap *local_datapaths,
>                                    struct group_table *group_table,
> +                                  struct meter_table *meter_table,
>                                    const struct sbrec_chassis *chassis,
>                                    struct hmap *dhcp_opts,
>                                    struct hmap *dhcpv6_opts,
> @@ -143,6 +144,7 @@ add_logical_flows(struct controller_ctx *ctx,
>                    const struct chassis_index *chassis_index,
>                    const struct hmap *local_datapaths,
>                    struct group_table *group_table,
> +                  struct meter_table *meter_table,
>                    const struct sbrec_chassis *chassis,
>                    const struct shash *addr_sets,
>                    struct hmap *flow_table,
> @@ -170,7 +172,7 @@ add_logical_flows(struct controller_ctx *ctx,
>      SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
>          consider_logical_flow(ctx, chassis_index,
>                                lflow, local_datapaths,
> -                              group_table, chassis,
> +                              group_table, meter_table, chassis,
>                                &dhcp_opts, &dhcpv6_opts, &conj_id_ofs,
>                                addr_sets, flow_table, active_tunnels,
>                                local_lport_ids);
> @@ -186,6 +188,7 @@ consider_logical_flow(struct controller_ctx *ctx,
>                        const struct sbrec_logical_flow *lflow,
>                        const struct hmap *local_datapaths,
>                        struct group_table *group_table,
> +                      struct meter_table *meter_table,
>                        const struct sbrec_chassis *chassis,
>                        struct hmap *dhcp_opts,
>                        struct hmap *dhcpv6_opts,
> @@ -256,6 +259,7 @@ consider_logical_flow(struct controller_ctx *ctx,
>          .is_switch = is_switch(ldp),
>          .is_gateway_router = is_gateway_router(ldp, local_datapaths),
>          .group_table = group_table,
> +        .meter_table = meter_table,
>
>          .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
>          .ingress_ptable = OFTABLE_LOG_INGRESS_PIPELINE,
> @@ -428,13 +432,14 @@ lflow_run(struct controller_ctx *ctx,
>            const struct chassis_index *chassis_index,
>            const struct hmap *local_datapaths,
>            struct group_table *group_table,
> +          struct meter_table *meter_table,
>            const struct shash *addr_sets,
>            struct hmap *flow_table,
>            struct sset *active_tunnels,
>            struct sset *local_lport_ids)
>  {
>      add_logical_flows(ctx, chassis_index, local_datapaths,
> -                      group_table, chassis, addr_sets, flow_table,
> +                      group_table, meter_table, chassis, addr_sets,
> flow_table,
>                        active_tunnels, local_lport_ids);
>      add_neighbor_flows(ctx, flow_table);
>  }
> diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
> index bfb7415e2..ad8d0a4c7 100644
> --- a/ovn/controller/lflow.h
> +++ b/ovn/controller/lflow.h
> @@ -38,6 +38,7 @@
>  struct chassis_index;
>  struct controller_ctx;
>  struct group_table;
> +struct meter_table;
>  struct hmap;
>  struct sbrec_chassis;
>  struct simap;
> @@ -67,6 +68,7 @@ void lflow_run(struct controller_ctx *,
>                 const struct chassis_index *,
>                 const struct hmap *local_datapaths,
>                 struct group_table *group_table,
> +               struct meter_table *meter_table,
>                 const struct shash *addr_sets,
>                 struct hmap *flow_table,
>                 struct sset *active_tunnels,
> diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
> index fc88a410b..d92258558 100644
> --- a/ovn/controller/ofctrl.c
> +++ b/ovn/controller/ofctrl.c
> @@ -133,6 +133,9 @@ static struct hmap installed_flows;
>  /* A reference to the group_table. */
>  static struct group_table *groups;
>
> +/* A reference to the meter_table. */
> +static struct meter_table *meters;
> +
>  /* MFF_* field ID for our Geneve option.  In S_TLV_TABLE_MOD_SENT, this is
>   * the option we requested (we don't know whether we obtained it yet).  In
>   * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */
> @@ -144,13 +147,15 @@ static struct ofpbuf *encode_flow_mod(struct
> ofputil_flow_mod *);
>
>  static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *);
>
> +static struct ofpbuf *encode_meter_mod(const struct ofputil_meter_mod *);
> +
>  static void ovn_flow_table_clear(struct hmap *flow_table);
>  static void ovn_flow_table_destroy(struct hmap *flow_table);
>
>  static void ofctrl_recv(const struct ofp_header *, enum ofptype);
>
>  void
> -ofctrl_init(struct group_table *group_table)
> +ofctrl_init(struct group_table *group_table, struct meter_table
> *meter_table)
>  {
>      swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION);
>      tx_counter = rconn_packet_counter_create();
> @@ -158,6 +163,7 @@ ofctrl_init(struct group_table *group_table)
>      ovs_list_init(&flow_updates);
>      ovn_init_symtab(&symtab);
>      groups = group_table;
> +    meters = meter_table;
>  }
>
>  /* S_NEW, for a new connection.
> @@ -388,6 +394,18 @@ run_S_CLEAR_FLOWS(void)
>          ovn_group_table_clear(groups, true);
>      }
>
> +    /* Send a meter_mod to delete all meters. */
> +    struct ofputil_meter_mod mm;
> +    memset(&mm, 0, sizeof mm);
> +    mm.command = OFPMC13_DELETE;
> +    mm.meter.meter_id = OFPM13_ALL;
> +    queue_msg(encode_meter_mod(&mm));
> +
> +    /* Clear existing meters, to match the state of the switch. */
> +    if (meters) {
> +        ovn_meter_table_clear(meters, true);
> +    }
> +
>      /* All flow updates are irrelevant now. */
>      struct ofctrl_flow_update *fup, *next;
>      LIST_FOR_EACH_SAFE (fup, next, list_node, &flow_updates) {
> @@ -797,7 +815,60 @@ add_group_mod(const struct ofputil_group_mod *gm,
> struct ovs_list *msgs)
>      struct ofpbuf *msg = encode_group_mod(gm);
>      ovs_list_push_back(msgs, &msg->list_node);
>  }
> -
> +
> +/* meter_table. */
> +
> +/* Finds and returns a meter_info in 'existing_meters' whose key is
> identical
> + * to 'target''s key, or NULL if there is none. */
> +static struct meter_info *
> +ovn_meter_lookup(struct hmap *exisiting_meters,
> +                 const struct meter_info *target)
> +{
> +    struct meter_info *e;
> +
> +    HMAP_FOR_EACH_WITH_HASH(e, hmap_node, target->hmap_node.hash,
> +                            exisiting_meters) {
> +        if (e->meter_id == target->meter_id) {
> +            return e;
> +        }
> +   }
> +    return NULL;
> +}
> +
> +/* Clear either desired_meters or existing_meters in meter_table. */
> +void
> +ovn_meter_table_clear(struct meter_table *meter_table, bool existing)
> +{
> +    struct meter_info *m, *next;
> +    struct hmap *target_meter = existing
> +                                ? &meter_table->existing_meters
> +                                : &meter_table->desired_meters;
> +
> +    HMAP_FOR_EACH_SAFE (m, next, hmap_node, target_meter) {
> +        hmap_remove(target_meter, &m->hmap_node);
> +        /* Don't unset bitmap for desired meter_info if the meter_id
> +         * was not freshly reserved. */
> +        if (existing || m->new_meter_id) {
> +            bitmap_set0(meter_table->meter_ids, m->meter_id);
> +        }
> +        ds_destroy(&m->meter);
> +        free(m);
> +    }
> +}
> +
> +static struct ofpbuf *
> +encode_meter_mod(const struct ofputil_meter_mod *mm)
> +{
> +    return ofputil_encode_meter_mod(OFP13_VERSION, mm);
> +}
> +
> +static void
> +add_meter_mod(const struct ofputil_meter_mod *mm, struct ovs_list *msgs)
> +{
> +    struct ofpbuf *msg = encode_meter_mod(mm);
> +    ovs_list_push_back(msgs, &msg->list_node);
> +}
> +
>  static void
>  add_ct_flush_zone(uint16_t zone_id, struct ovs_list *msgs)
>  {
> @@ -833,6 +904,12 @@ ofctrl_can_put(void)
>   * 'groups->desired_groups' and frees them. (The hmap itself isn't
>   * destroyed.)
>   *
> + * Replaces the meter table on the switch, if possible, by the contents of
> + * 'meters->desired_meters'.  Regardless of whether the meter table
> + * is updated, this deletes all the meters from the
> + * 'meters->desired_meters' and frees them. (The hmap itself isn't
> + * destroyed.)
> + *
>   * Sends conntrack flush messages to each zone in 'pending_ct_zones' that
>   * is in the CT_ZONE_OF_QUEUED state and then moves the zone into the
>   * CT_ZONE_OF_SENT state.
> @@ -891,6 +968,35 @@ ofctrl_put(struct hmap *flow_table, struct shash
> *pending_ct_zones,
>          }
>      }
>
> +    /* Iterate through all the desired meters. If there are new ones,
> +     * add them to the switch. */
> +    struct meter_info *desired_meter;
> +    HMAP_FOR_EACH(desired_meter, hmap_node, &meters->desired_meters) {
> +        if (!ovn_meter_lookup(&meters->existing_meters, desired_meter)
> +            && desired_meter->meter_id) {
> +            /* Create and install new meter. */
> +            struct ofputil_meter_mod mm;
> +            enum ofputil_protocol usable_protocols;
> +            char *error;
> +            struct ds meter_string = DS_EMPTY_INITIALIZER;
> +            ds_put_format(&meter_string, "meter=%u,%s",
> +                          desired_meter->meter_id,
> +                          ds_cstr(&desired_meter->meter));
> +
> +            error = parse_ofp_meter_mod_str(&mm, ds_cstr(&meter_string),
> +                                            OFPMC13_ADD,
> &usable_protocols);
> +            if (!error) {
> +                add_meter_mod(&mm, &msgs);
> +            } else {
> +                static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(5, 1);
> +                VLOG_ERR_RL(&rl, "new meter %s %s", error,
> +                         ds_cstr(&meter_string));
> +                free(error);
> +            }
> +            ds_destroy(&meter_string);
> +        }
> +    }
> +
>      /* Iterate through all of the installed flows.  If any of them are no
>       * longer desired, delete them; if any of them should have different
>       * actions, update them. */
> @@ -1012,6 +1118,54 @@ ofctrl_put(struct hmap *flow_table, struct shash
> *pending_ct_zones,
>          }
>      }
>
> +    /* Iterate through the installed meters from previous runs. If they
> +     * are not needed delete them. */
> +    struct meter_info *installed_meter, *next_meter;
> +    HMAP_FOR_EACH_SAFE(installed_meter, next_meter, hmap_node,
> +                       &meters->existing_meters) {
> +        if (!ovn_meter_lookup(&meters->desired_meters, installed_meter))
> {
> +            /* Delete the meter. */
> +            struct ofputil_meter_mod mm;
> +            enum ofputil_protocol usable_protocols;
> +            char *error;
> +            struct ds meter_string = DS_EMPTY_INITIALIZER;
> +            ds_put_format(&meter_string, "meter=%u",
> installed_meter->meter_id);
> +
> +            error = parse_ofp_meter_mod_str(&mm, ds_cstr(&meter_string),
> +                                            OFPMC13_DELETE,
> &usable_protocols);
> +            if (!error) {
> +                add_meter_mod(&mm, &msgs);
> +            } else {
> +                static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(5, 1);
> +                VLOG_ERR_RL(&rl, "Error deleting meter %d: %s",
> +                         installed_meter->meter_id, error);
> +                free(error);
> +            }
> +            ds_destroy(&meter_string);
> +
> +            /* Remove 'installed_meter' from 'meters->existing_meters' */
> +            hmap_remove(&meters->existing_meters,
> &installed_meter->hmap_node);
> +            ds_destroy(&installed_meter->meter);
> +
> +            /* Dealloc meter_id. */
> +            bitmap_set0(meters->meter_ids, installed_meter->meter_id);
> +            free(installed_meter);
> +        }
> +    }
> +
> +    /* Move the contents of desired_meters to existing_meters. */
> +    HMAP_FOR_EACH_SAFE(desired_meter, next_meter, hmap_node,
> +                       &meters->desired_meters) {
> +        hmap_remove(&meters->desired_meters, &desired_meter->hmap_node);
> +        if (!ovn_meter_lookup(&meters->existing_meters, desired_meter)) {
> +            hmap_insert(&meters->existing_meters,
> &desired_meter->hmap_node,
> +                        desired_meter->hmap_node.hash);
> +        } else {
> +           ds_destroy(&desired_meter->meter);
> +           free(desired_meter);
> +        }
> +    }
> +
>      if (!ovs_list_is_empty(&msgs)) {
>          /* Add a barrier to the list of messages. */
>          struct ofpbuf *barrier = ofputil_encode_barrier_
> request(OFP13_VERSION);
> diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h
> index d83f6aec4..e680e2d61 100644
> --- a/ovn/controller/ofctrl.h
> +++ b/ovn/controller/ofctrl.h
> @@ -24,6 +24,7 @@
>
>  struct controller_ctx;
>  struct group_table;
> +struct meter_table;
>  struct hmap;
>  struct match;
>  struct ofpbuf;
> @@ -31,7 +32,7 @@ struct ovsrec_bridge;
>  struct shash;
>
>  /* Interface for OVN main loop. */
> -void ofctrl_init(struct group_table *group_table);
> +void ofctrl_init(struct group_table *group_table, struct meter_table
> *meter_table);
>  enum mf_field_id ofctrl_run(const struct ovsrec_bridge *br_int,
>                              struct shash *pending_ct_zones);
>  bool ofctrl_can_put(void);
> @@ -58,4 +59,7 @@ void ofctrl_flow_table_clear(void);
>  void ovn_group_table_clear(struct group_table *group_table,
>                             bool existing);
>
> +void ovn_meter_table_clear(struct meter_table *meter_table,
> +                           bool existing);
> +
>  #endif /* ovn/ofctrl.h */
> diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-
> controller.c
> index a935a791c..c5926bc83 100644
> --- a/ovn/controller/ovn-controller.c
> +++ b/ovn/controller/ovn-controller.c
> @@ -599,9 +599,16 @@ main(int argc, char *argv[])
>      hmap_init(&group_table.desired_groups);
>      hmap_init(&group_table.existing_groups);
>
> +    /* Initialize meter ids for QoS. */
> +    struct meter_table meter_table;
> +    meter_table.meter_ids = bitmap_allocate(MAX_OVN_METERS);
> +    bitmap_set1(meter_table.meter_ids, 0); /* Meter id 0 is invalid. */
> +    hmap_init(&meter_table.desired_meters);
> +    hmap_init(&meter_table.existing_meters);
> +
>      daemonize_complete();
>
> -    ofctrl_init(&group_table);
> +    ofctrl_init(&group_table, &meter_table);
>      pinctrl_init();
>      lflow_init();
>
> @@ -711,8 +718,8 @@ main(int argc, char *argv[])
>                      struct hmap flow_table =
> HMAP_INITIALIZER(&flow_table);
>                      lflow_run(&ctx, chassis,
>                                &chassis_index, &local_datapaths,
> &group_table,
> -                              &addr_sets, &flow_table, &active_tunnels,
> -                              &local_lport_ids);
> +                              &meter_table, &addr_sets, &flow_table,
> +                              &active_tunnels, &local_lport_ids);
>
>                      if (chassis_id) {
>                          bfd_run(&ctx, br_int, chassis, &local_datapaths,
> @@ -856,6 +863,18 @@ main(int argc, char *argv[])
>      }
>      hmap_destroy(&group_table.existing_groups);
>
> +    bitmap_free(meter_table.meter_ids);
> +    hmap_destroy(&meter_table.desired_meters);
> +
> +    struct meter_info *installed_meter, *next_meter;
> +    HMAP_FOR_EACH_SAFE(installed_meter, next_meter, hmap_node,
> +                       &meter_table.existing_meters) {
> +        hmap_remove(&meter_table.existing_meters,
> &installed_meter->hmap_node);
> +        ds_destroy(&installed_meter->meter);
> +        free(installed_meter);
> +    }
> +    hmap_destroy(&meter_table.existing_meters);
> +
>      ovsdb_idl_loop_destroy(&ovs_idl_loop);
>      ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
>
> diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
> index d0d73b69c..9c1f5f963 100644
> --- a/ovn/lib/actions.c
> +++ b/ovn/lib/actions.c
> @@ -1873,6 +1873,118 @@ ovnact_log_free(struct ovnact_log *log)
>      free(log->name);
>  }
>
> +static void
> +parse_set_meter_action(struct action_context *ctx)
> +{
> +    int rate;
> +    int burst = 0;
> +
> +    if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
> +        lexer_error(ctx->lexer,
> +                    "\"set_meter\" action not allowed in last table.");
> +        return;
> +    }
> +
> +    lexer_force_match(ctx->lexer, LEX_T_LPAREN);
> +    lexer_force_int(ctx->lexer, &rate);
> +    if (lexer_match(ctx->lexer, LEX_T_COMMA)) {
> +        lexer_force_int(ctx->lexer, &burst);
> +    }
> +    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
> +
> +    struct ovnact_set_meter *cl = ovnact_put_SET_METER(ctx->ovnacts);
> +    cl->rate = (uint32_t)rate;
> +    cl->burst = (uint32_t)burst;
> +}
> +
> +static void
> +format_SET_METER(const struct ovnact_set_meter *cl, struct ds *s)
> +{
> +    if (cl->burst) {
> +        ds_put_format(s, "set_meter(%d ,%d);", cl->rate, cl->burst);
> +    } else {
> +        ds_put_format(s, "set_meter(%d);", cl->rate);
> +    }
> +}
> +
> +static void
> +encode_SET_METER(const struct ovnact_set_meter *cl,
> +                 const struct ovnact_encode_params *ep,
> +                 struct ofpbuf *ofpacts)
> +{
> +    uint32_t meter_id = 0, hash;
> +    struct meter_info *meter_info;
> +    struct ofpact_meter *om;
> +
> +    struct ds ds = DS_EMPTY_INITIALIZER;
> +    if (cl->burst) {
> +        ds_put_format(&ds,
> +                      "kbps burst stats bands=type=drop rate=%d
> burst_size=%d",
> +                      cl->rate, cl->burst);
> +    } else {
> +        ds_put_format(&ds, "kbps stats bands=type=drop rate=%d",
> cl->rate);
> +    }
> +
> +    hash = hash_string(ds_cstr(&ds), 0);
> +
> +    /* Check whether we have non installed but allocated meter_id. */
> +    HMAP_FOR_EACH_WITH_HASH (meter_info, hmap_node, hash,
> +                             &ep->meter_table->desired_meters) {
> +        if (!strcmp(ds_cstr(&meter_info->meter), ds_cstr(&ds))) {
> +            meter_id = meter_info->meter_id;
> +            break;
> +        }
> +    }
> +
> +    if (!meter_id) {
> +        /* Check whether we already have an installed entry for this
> +         * combination. */
> +        HMAP_FOR_EACH_WITH_HASH (meter_info, hmap_node, hash,
> +                                 &ep->meter_table->existing_meters) {
> +            if (!strcmp(ds_cstr(&meter_info->meter), ds_cstr(&ds))) {
> +                meter_id = meter_info->meter_id;
> +            }
> +        }
> +
> +        bool new_meter_id = false;
> +        if (!meter_id) {
> +            /* Reserve a new meter_id. */
> +            meter_id = bitmap_scan(ep->meter_table->meter_ids, 0, 1,
> +                                   MAX_OVN_METERS + 1);
> +            new_meter_id = true;
> +        }
> +
> +        if (meter_id == MAX_OVN_METERS + 1) {
> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> +            VLOG_ERR_RL(&rl, "out of meter ids");
> +
> +            ds_destroy(&ds);
> +            return;
> +        }
> +        bitmap_set1(ep->meter_table->meter_ids, meter_id);
> +
> +        meter_info = xmalloc(sizeof *meter_info);
> +        meter_info->meter = ds;
> +        meter_info->meter_id = meter_id;
> +        meter_info->hmap_node.hash = hash;
> +        meter_info->new_meter_id = new_meter_id;
> +
> +        hmap_insert(&ep->meter_table->desired_meters,
> +                    &meter_info->hmap_node, meter_info->hmap_node.hash);
> +    } else {
> +        ds_destroy(&ds);
> +    }
> +
> +    /* Create an action to set the meter. */
> +    om = ofpact_put_METER(ofpacts);
> +    om->meter_id = meter_id;
> +}
> +
> +static void
> +ovnact_set_meter_free(struct ovnact_set_meter *ct OVS_UNUSED)
> +{
> +}
> +
>  /* Parses an assignment or exchange or put_dhcp_opts action. */
>  static void
>  parse_set_action(struct action_context *ctx)
> @@ -1954,6 +2066,8 @@ parse_action(struct action_context *ctx)
>          parse_SET_QUEUE(ctx);
>      } else if (lexer_match_id(ctx->lexer, "log")) {
>          parse_LOG(ctx);
> +    } else if (lexer_match_id(ctx->lexer, "set_meter")) {
> +        parse_set_meter_action(ctx);
>      } else {
>          lexer_syntax_error(ctx->lexer, "expecting action");
>      }
> diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
> index 0d85ec0d2..5a2febb27 100644
> --- a/ovn/northd/ovn-northd.8.xml
> +++ b/ovn/northd/ovn-northd.8.xml
> @@ -364,7 +364,28 @@
>        </li>
>      </ul>
>
> -    <h3>Ingress Table 8: LB</h3>
> +    <h3>Ingress Table 8: <code>from-lport</code> QoS meter</h3>
> +
> +    <p>
> +      Logical flows in this table closely reproduce those in the
> +      <code>QoS</code> table <code>bandwidth</code> column in the
> +      <code>OVN_Northbound</code> database for the <code>from-lport</code>
> +      direction.
> +    </p>
> +
> +    <ul>
> +      <li>
> +        For every qos_rules for every logical switch a flow will be added
> at
> +        priorities mentioned in the QoS table.
> +      </li>
> +
> +      <li>
> +        One priority-0 fallback flow that matches all packets and
> advances to
> +        the next table.
> +      </li>
> +    </ul>
> +
> +    <h3>Ingress Table 9: LB</h3>
>
>      <p>
>        It contains a priority-0 flow that simply moves traffic to the next
> @@ -377,7 +398,7 @@
>        connection.)
>      </p>
>
> -    <h3>Ingress Table 9: Stateful</h3>
> +    <h3>Ingress Table 10: Stateful</h3>
>
>      <ul>
>        <li>
> @@ -414,7 +435,7 @@
>        </li>
>      </ul>
>
> -    <h3>Ingress Table 10: ARP/ND responder</h3>
> +    <h3>Ingress Table 11: ARP/ND responder</h3>
>
>      <p>
>        This table implements ARP/ND responder in a logical switch for known
> @@ -564,7 +585,7 @@ nd_na {
>        </li>
>      </ul>
>
> -    <h3>Ingress Table 11: DHCP option processing</h3>
> +    <h3>Ingress Table 12: DHCP option processing</h3>
>
>      <p>
>        This table adds the DHCPv4 options to a DHCPv4 packet from the
> @@ -624,7 +645,7 @@ next;
>        </li>
>      </ul>
>
> -    <h3>Ingress Table 12: DHCP responses</h3>
> +    <h3>Ingress Table 13: DHCP responses</h3>
>
>      <p>
>        This table implements DHCP responder for the DHCP replies generated
> by
> @@ -706,7 +727,7 @@ output;
>        </li>
>      </ul>
>
> -    <h3>Ingress Table 13 DNS Lookup</h3>
> +    <h3>Ingress Table 14 DNS Lookup</h3>
>
>      <p>
>        This table looks up and resolves the DNS names to the corresponding
> @@ -735,7 +756,7 @@ reg0[4] = dns_lookup(); next;
>        </li>
>      </ul>
>
> -    <h3>Ingress Table 14 DNS Responses</h3>
> +    <h3>Ingress Table 15 DNS Responses</h3>
>
>      <p>
>        This table implements DNS responder for the DNS replies generated by
> @@ -770,7 +791,7 @@ output;
>        </li>
>      </ul>
>
> -    <h3>Ingress Table 15 Destination Lookup</h3>
> +    <h3>Ingress Table 16 Destination Lookup</h3>
>
>      <p>
>        This table implements switching behavior.  It contains these logical
> @@ -872,7 +893,14 @@ output;
>        <code>to-lport</code> qos rules.
>      </p>
>
> -    <h3>Egress Table 6: Stateful</h3>
> +    <h3>Egress Table 6: <code>to-lport</code> QoS meter</h3>
> +
> +    <p>
> +      This is similar to ingress table <code>QoS meter</code> except for
> +      <code>to-lport</code> qos rules.
> +    </p>
> +
> +    <h3>Egress Table 7: Stateful</h3>
>
>      <p>
>        This is similar to ingress table <code>Stateful</code> except that
> @@ -887,18 +915,18 @@ output;
>          A priority 34000 logical flow is added for each logical port which
>          has DHCPv4 options defined to allow the DHCPv4 reply packet and
> which has
>          DHCPv6 options defined to allow the DHCPv6 reply packet from the
> -        <code>Ingress Table 12: DHCP responses</code>.
> +        <code>Ingress Table 13: DHCP responses</code>.
>        </li>
>
>        <li>
>          A priority 34000 logical flow is added for each logical switch
> datapath
>          configured with DNS records with the match <code>udp.dst =
> 53</code>
>          to allow the DNS reply packet from the
> -        <code>Ingress Table 14:DNS responses</code>.
> +        <code>Ingress Table 15:DNS responses</code>.
>        </li>
>      </ul>
>
> -    <h3>Egress Table 7: Egress Port Security - IP</h3>
> +    <h3>Egress Table 8: Egress Port Security - IP</h3>
>
>      <p>
>        This is similar to the port security logic in table
> @@ -908,7 +936,7 @@ output;
>        <code>ip4.src</code> and <code>ip6.src</code>
>      </p>
>
> -    <h3>Egress Table 8: Egress Port Security - L2</h3>
> +    <h3>Egress Table 9: Egress Port Security - L2</h3>
>
>      <p>
>        This is similar to the ingress port security logic in ingress table
> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
> index 2db238073..4c0d6fcd0 100644
> --- a/ovn/northd/ovn-northd.c
> +++ b/ovn/northd/ovn-northd.c
> @@ -108,25 +108,27 @@ enum ovn_stage {
>      PIPELINE_STAGE(SWITCH, IN,  PRE_STATEFUL,   5, "ls_in_pre_stateful")
> \
>      PIPELINE_STAGE(SWITCH, IN,  ACL,            6, "ls_in_acl")
>  \
>      PIPELINE_STAGE(SWITCH, IN,  QOS_MARK,       7, "ls_in_qos_mark")
> \
> -    PIPELINE_STAGE(SWITCH, IN,  LB,             8, "ls_in_lb")
> \
> -    PIPELINE_STAGE(SWITCH, IN,  STATEFUL,       9, "ls_in_stateful")
> \
> -    PIPELINE_STAGE(SWITCH, IN,  ARP_ND_RSP,    10, "ls_in_arp_rsp")
>  \
> -    PIPELINE_STAGE(SWITCH, IN,  DHCP_OPTIONS,  11, "ls_in_dhcp_options")
> \
> -    PIPELINE_STAGE(SWITCH, IN,  DHCP_RESPONSE, 12, "ls_in_dhcp_response")
> \
> -    PIPELINE_STAGE(SWITCH, IN,  DNS_LOOKUP,      13, "ls_in_dns_lookup") \
> -    PIPELINE_STAGE(SWITCH, IN,  DNS_RESPONSE,  14, "ls_in_dns_response") \
> -    PIPELINE_STAGE(SWITCH, IN,  L2_LKUP,       15, "ls_in_l2_lkup")
>  \
> -                                                                      \
> -    /* Logical switch egress stages. */                               \
> -    PIPELINE_STAGE(SWITCH, OUT, PRE_LB,       0, "ls_out_pre_lb")     \
> -    PIPELINE_STAGE(SWITCH, OUT, PRE_ACL,      1, "ls_out_pre_acl")     \
> -    PIPELINE_STAGE(SWITCH, OUT, PRE_STATEFUL, 2, "ls_out_pre_stateful")  \
> -    PIPELINE_STAGE(SWITCH, OUT, LB,           3, "ls_out_lb")            \
> +    PIPELINE_STAGE(SWITCH, IN,  QOS_METER,      8, "ls_in_qos_meter")
>  \
> +    PIPELINE_STAGE(SWITCH, IN,  LB,             9, "ls_in_lb")
> \
> +    PIPELINE_STAGE(SWITCH, IN,  STATEFUL,      10, "ls_in_stateful")
> \
> +    PIPELINE_STAGE(SWITCH, IN,  ARP_ND_RSP,    11, "ls_in_arp_rsp")
>  \
> +    PIPELINE_STAGE(SWITCH, IN,  DHCP_OPTIONS,  12, "ls_in_dhcp_options")
> \
> +    PIPELINE_STAGE(SWITCH, IN,  DHCP_RESPONSE, 13, "ls_in_dhcp_response")
> \
> +    PIPELINE_STAGE(SWITCH, IN,  DNS_LOOKUP,    14, "ls_in_dns_lookup")
> \
> +    PIPELINE_STAGE(SWITCH, IN,  DNS_RESPONSE,  15, "ls_in_dns_response")
> \
> +    PIPELINE_STAGE(SWITCH, IN,  L2_LKUP,       16, "ls_in_l2_lkup")
>  \
> +
> \
> +    /* Logical switch egress stages. */
>  \
> +    PIPELINE_STAGE(SWITCH, OUT, PRE_LB,       0, "ls_out_pre_lb")
>  \
> +    PIPELINE_STAGE(SWITCH, OUT, PRE_ACL,      1, "ls_out_pre_acl")
> \
> +    PIPELINE_STAGE(SWITCH, OUT, PRE_STATEFUL, 2, "ls_out_pre_stateful")
>  \
> +    PIPELINE_STAGE(SWITCH, OUT, LB,           3, "ls_out_lb")
>  \
>      PIPELINE_STAGE(SWITCH, OUT, ACL,          4, "ls_out_acl")
> \
>      PIPELINE_STAGE(SWITCH, OUT, QOS_MARK,     5, "ls_out_qos_mark")
>  \
> -    PIPELINE_STAGE(SWITCH, OUT, STATEFUL,     6, "ls_out_stateful")
>  \
> -    PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_IP,  7, "ls_out_port_sec_ip")
> \
> -    PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2,  8, "ls_out_port_sec_l2")
> \
> +    PIPELINE_STAGE(SWITCH, OUT, QOS_METER,    6, "ls_out_qos_meter")
> \
> +    PIPELINE_STAGE(SWITCH, OUT, STATEFUL,     7, "ls_out_stateful")
>  \
> +    PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_IP,  8, "ls_out_port_sec_ip")
> \
> +    PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2,  9, "ls_out_port_sec_l2")
> \
>                                                                        \
>      /* Logical router ingress stages. */                              \
>      PIPELINE_STAGE(ROUTER, IN,  ADMISSION,   0, "lr_in_admission")    \
> @@ -3365,21 +3367,49 @@ static void
>  build_qos(struct ovn_datapath *od, struct hmap *lflows) {
>      ovn_lflow_add(lflows, od, S_SWITCH_IN_QOS_MARK, 0, "1", "next;");
>      ovn_lflow_add(lflows, od, S_SWITCH_OUT_QOS_MARK, 0, "1", "next;");
> +    ovn_lflow_add(lflows, od, S_SWITCH_IN_QOS_METER, 0, "1", "next;");
> +    ovn_lflow_add(lflows, od, S_SWITCH_OUT_QOS_METER, 0, "1", "next;");
>
>      for (size_t i = 0; i < od->nbs->n_qos_rules; i++) {
>          struct nbrec_qos *qos = od->nbs->qos_rules[i];
>          bool ingress = !strcmp(qos->direction, "from-lport") ? true
> :false;
>          enum ovn_stage stage = ingress ? S_SWITCH_IN_QOS_MARK :
> S_SWITCH_OUT_QOS_MARK;
> +        uint32_t rate = 0;
> +        uint32_t burst = 0;
> +
> +        for (size_t j = 0; j < qos->n_action; j++) {
> +            if (!strcmp(qos->key_action[j], "dscp")) {
> +                struct ds dscp_action = DS_EMPTY_INITIALIZER;
> +
> +                ds_put_format(&dscp_action, "ip.dscp = %d; next;",
> +                              (uint8_t)qos->value_action[j]);
> +                ovn_lflow_add(lflows, od, stage,
> +                              qos->priority,
> +                              qos->match, ds_cstr(&dscp_action));
> +                ds_destroy(&dscp_action);
> +            }
> +        }
>
> -        if (!strcmp(qos->key_action, "dscp")) {
> -            struct ds dscp_action = DS_EMPTY_INITIALIZER;
> -
> -            ds_put_format(&dscp_action, "ip.dscp = %d; next;",
> -                          (uint8_t)qos->value_action);
> +        for (size_t n = 0; n < qos->n_bandwidth; n++) {
> +            if (!strcmp(qos->key_bandwidth[n], "rate")) {
> +                rate = (uint32_t)qos->value_bandwidth[n];
> +            } else if (!strcmp(qos->key_bandwidth[n], "burst")) {
> +                burst = (uint32_t)qos->value_bandwidth[n];
> +            }
> +        }
> +        if (rate) {
> +            struct ds meter_action = DS_EMPTY_INITIALIZER;
> +            stage = ingress ? S_SWITCH_IN_QOS_METER :
> S_SWITCH_OUT_QOS_METER;
> +            if (burst) {
> +                ds_put_format(&meter_action, "set_meter(%d, %d); next;",
> +                              rate, burst);
> +            } else {
> +                ds_put_format(&meter_action, "set_meter(%d); next;",
> rate);
> +            }
>              ovn_lflow_add(lflows, od, stage,
>                            qos->priority,
> -                          qos->match, ds_cstr(&dscp_action));
> -            ds_destroy(&dscp_action);
> +                          qos->match, ds_cstr(&meter_action));
> +            ds_destroy(&meter_action);
>          }
>      }
>  }
> @@ -3489,7 +3519,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>      struct ds actions = DS_EMPTY_INITIALIZER;
>
>      /* Build pre-ACL and ACL tables for both ingress and egress.
> -     * Ingress tables 3 through 9.  Egress tables 0 through 6. */
> +     * Ingress tables 3 through 10.  Egress tables 0 through 7. */
>      struct ovn_datapath *od;
>      HMAP_FOR_EACH (od, key_node, datapaths) {
>          if (!od->nbs) {
> @@ -3572,7 +3602,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>          ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 0, "1",
> "next;");
>      }
>
> -    /* Ingress table 10: ARP/ND responder, skip requests coming from
> localnet
> +    /* Ingress table 11: ARP/ND responder, skip requests coming from
> localnet
>       * and vtep ports. (priority 100); see ovn-northd.8.xml for the
>       * rationale. */
>      HMAP_FOR_EACH (op, key_node, ports) {
> @@ -3589,7 +3619,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>          }
>      }
>
> -    /* Ingress table 10: ARP/ND responder, reply for known IPs.
> +    /* Ingress table 11: ARP/ND responder, reply for known IPs.
>       * (priority 50). */
>      HMAP_FOR_EACH (op, key_node, ports) {
>          if (!op->nbsp) {
> @@ -3684,7 +3714,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>          }
>      }
>
> -    /* Ingress table 10: ARP/ND responder, by default goto next.
> +    /* Ingress table 11: ARP/ND responder, by default goto next.
>       * (priority 0)*/
>      HMAP_FOR_EACH (od, key_node, datapaths) {
>          if (!od->nbs) {
> @@ -3694,7 +3724,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>          ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1",
> "next;");
>      }
>
> -    /* Logical switch ingress table 11 and 12: DHCP options and response
> +    /* Logical switch ingress table 12 and 13: DHCP options and response
>           * priority 100 flows. */
>      HMAP_FOR_EACH (op, key_node, ports) {
>          if (!op->nbsp) {
> @@ -3796,7 +3826,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>          }
>      }
>
> -    /* Logical switch ingress table 13 and 14: DNS lookup and response
> +    /* Logical switch ingress table 14 and 15: DNS lookup and response
>       * priority 100 flows.
>       */
>      HMAP_FOR_EACH (od, key_node, datapaths) {
> @@ -3828,9 +3858,9 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>          ds_destroy(&action);
>      }
>
> -    /* Ingress table 11 and 12: DHCP options and response, by default
> goto next.
> +    /* Ingress table 12 and 13: DHCP options and response, by default
> goto next.
>       * (priority 0).
> -     * Ingress table 13 and 14: DNS lookup and response, by default goto
> next.
> +     * Ingress table 14 and 15: DNS lookup and response, by default goto
> next.
>       * (priority 0).*/
>
>      HMAP_FOR_EACH (od, key_node, datapaths) {
> @@ -3844,7 +3874,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>          ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 0, "1",
> "next;");
>      }
>
> -    /* Ingress table 15: Destination lookup, broadcast and multicast
> handling
> +    /* Ingress table 16: Destination lookup, broadcast and multicast
> handling
>       * (priority 100). */
>      HMAP_FOR_EACH (op, key_node, ports) {
>          if (!op->nbsp) {
> @@ -3864,7 +3894,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>                        "outport = \""MC_FLOOD"\"; output;");
>      }
>
> -    /* Ingress table 13: Destination lookup, unicast handling (priority
> 50), */
> +    /* Ingress table 16: Destination lookup, unicast handling (priority
> 50), */
>      HMAP_FOR_EACH (op, key_node, ports) {
>          if (!op->nbsp) {
>              continue;
> @@ -3964,7 +3994,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>          }
>      }
>
> -    /* Ingress table 13: Destination lookup for unknown MACs (priority
> 0). */
> +    /* Ingress table 16: Destination lookup for unknown MACs (priority
> 0). */
>      HMAP_FOR_EACH (od, key_node, datapaths) {
>          if (!od->nbs) {
>              continue;
> @@ -3976,8 +4006,8 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>          }
>      }
>
> -    /* Egress tables 6: Egress port security - IP (priority 0)
> -     * Egress table 7: Egress port security L2 - multicast/broadcast
> +    /* Egress tables 8: Egress port security - IP (priority 0)
> +     * Egress table 9: Egress port security L2 - multicast/broadcast
>       *                 (priority 100). */
>      HMAP_FOR_EACH (od, key_node, datapaths) {
>          if (!od->nbs) {
> @@ -3989,10 +4019,10 @@ build_lswitch_flows(struct hmap *datapaths, struct
> hmap *ports,
>                        "output;");
>      }
>
> -    /* Egress table 6: Egress port security - IP (priorities 90 and 80)
> +    /* Egress table 8: Egress port security - IP (priorities 90 and 80)
>       * if port security enabled.
>       *
> -     * Egress table 7: Egress port security - L2 (priorities 50 and 150).
> +     * Egress table 9: Egress port security - L2 (priorities 50 and 150).
>       *
>       * Priority 50 rules implement port security for enabled logical port.
>       *
> diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
> index a077bfb81..23ab008cd 100644
> --- a/ovn/ovn-nb.ovsschema
> +++ b/ovn/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
>  {
>      "name": "OVN_Northbound",
>      "version": "5.8.0",
> -    "cksum": "2812300190 16766",
> +    "cksum": "3886656843 17257",
>      "tables": {
>          "NB_Global": {
>              "columns": {
> @@ -164,7 +164,14 @@
>                                              "enum": ["set", ["dscp"]]},
>                                      "value": {"type": "integer",
>                                                "minInteger": 0,
> -                                              "maxInteger": 63}}},
> +                                              "maxInteger": 63},
> +                                    "min": 0, "max": "unlimited"}},
> +                "bandwidth": {"type": {"key": {"type": "string",
> +                                               "enum": ["set", ["rate",
> "burst"]]},
> +                                       "value": {"type": "integer",
> +                                                 "minInteger": 1,
> +                                                 "maxInteger": 65535},
> +                                       "min": 0, "max": "unlimited"}},
>                  "external_ids": {
>                      "type": {"key": "string", "value": "string",
>                               "min": 0, "max": "unlimited"}}},
> diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
> index 9869d7ed7..c666ec2c4 100644
> --- a/ovn/ovn-nb.xml
> +++ b/ovn/ovn-nb.xml
> @@ -1250,6 +1250,22 @@
>        </ul>
>      </column>
>
> +    <column name="bandwidth">
> +      <p>
> +         The bandwidth limit to be performed on the matched packet.
> +         Currently only supported in the userspace by dpdk.
> +      </p>
> +      <ul>
> +        <li>
> +          <code>rate</code>: The value of rate limit.
> +        </li>
> +        <li>
> +          <code>burst</code>: The value of burst rate limit. This is
> optional
> +          and needs to specify the <code>rate</code> first.
> +        </li>
> +      </ul>
> +    </column>
> +
>      <column name="external_ids">
>        See <em>External IDs</em> at the beginning of this document.
>      </column>
> diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
> index 0a894f8cb..ee1db3f6f 100644
> --- a/ovn/ovn-sb.xml
> +++ b/ovn/ovn-sb.xml
> @@ -1516,6 +1516,21 @@
>              <b>Prerequisite:</b> <code>udp</code>
>            </p>
>          </dd>
> +
> +        <dt><code>set_meter(<var>rate</var>);</code></dt>
> +        <dt><code>set_meter(<var>rate</var>,
> <var>burst</var>);</code></dt>
> +        <dd>
> +          <p>
> +            <b>Parameters</b>: rate limit int field <var>rate</var>,
> burst rate limits
> +            int field <var>burst</var>.
> +          </p>
> +
> +          <p>
> +            This action sets the rate limit for a flow.
> +          </p>
> +
> +          <p><b>Example:</b> <code>set_meter(100, 1000);</code></p>
> +        </dd>
>        </dl>
>
>        <dl>
> diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
> index 59083eebe..bb5593876 100644
> --- a/ovn/utilities/ovn-trace.c
> +++ b/ovn/utilities/ovn-trace.c
> @@ -1833,6 +1833,10 @@ trace_actions(const struct ovnact *ovnacts, size_t
> ovnacts_len,
>          case OVNACT_LOG:
>              execute_log(ovnact_get_LOG(a), uflow, super);
>              break;
> +
> +        case OVNACT_SET_METER:
> +            /* Nothing to do. */
> +            break;
>          }
>
>      }
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 6c38b973f..0e34f7df4 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -5885,7 +5885,7 @@ OVN_CLEANUP([hv])
>  AT_CLEANUP
>
>
> -AT_SETUP([ovn -- DSCP marking check])
> +AT_SETUP([ovn -- DSCP marking and meter check])
>  AT_KEYWORDS([ovn])
>  ovn_start
>
> @@ -5952,13 +5952,16 @@ AT_CHECK([get_final_nw_tos], [0], [none
>  check_tos 0
>
>  # Mark DSCP with a valid value
> -qos_id=$(ovn-nbctl --wait=hv -- --id=@lp1-qos create QoS priority=100
> action=dscp=48 match="inport\=\=\"lp1\"" direction="from-lport" -- set
> Logical_Switch lsw0 qos_rules=@lp1-qos)
> +qos_id=$(ovn-nbctl --wait=hv -- --id=@lp1-qos create QoS priority=100
> action=dscp=48 bandwidth=rate=100,burst=1000 match="inport\=\=\"lp1\""
> direction="from-lport" -- set Logical_Switch lsw0 qos_rules=@lp1-qos)
>  check_tos 48
>
>  # Update the DSCP marking
>  ovn-nbctl --wait=hv set QoS $qos_id action=dscp=63
>  check_tos 63
>
> +# Update the meter rate
> +ovn-nbctl --wait=hv set QoS $qos_id bandwidth=rate=65535,burst=65535
> +
>  ovn-nbctl --wait=hv set QoS $qos_id match="outport\=\=\"lp2\""
> direction="to-lport"
>  check_tos 63
>
> diff --git a/tests/test-ovn.c b/tests/test-ovn.c
> index 4beb2b8d6..e9dcba231 100644
> --- a/tests/test-ovn.c
> +++ b/tests/test-ovn.c
> @@ -1206,6 +1206,13 @@ test_parse_actions(struct ovs_cmdl_context *ctx
> OVS_UNUSED)
>      hmap_init(&group_table.desired_groups);
>      hmap_init(&group_table.existing_groups);
>
> +    /* Initialize meter ids for QoS. */
> +    struct meter_table meter_table;
> +    meter_table.meter_ids = bitmap_allocate(MAX_OVN_METERS);
> +    bitmap_set1(meter_table.meter_ids, 0); /* Meter id 0 is invalid. */
> +    hmap_init(&meter_table.desired_meters);
> +    hmap_init(&meter_table.existing_meters);
> +
>      simap_init(&ports);
>      simap_put(&ports, "eth0", 5);
>      simap_put(&ports, "eth1", 6);
> @@ -1244,6 +1251,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx
> OVS_UNUSED)
>                  .aux = &ports,
>                  .is_switch = true,
>                  .group_table = &group_table,
> +                .meter_table = &meter_table,
>
>                  .pipeline = OVNACT_P_INGRESS,
>                  .ingress_ptable = 8,
> --
> 2.13.2.windows.1
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>


More information about the dev mailing list