diff mbox series

[ovs-dev,v4,4/8] netdev-linux: Add functions to manipulate tc police action

Message ID 20220503030836.28783-5-jianbol@nvidia.com
State Superseded
Headers show
Series Add support for ovs metering with tc offload | expand

Checks

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

Commit Message

Jianbo Liu May 3, 2022, 3:08 a.m. UTC
Add helpers to add, delete and get stats of police action with
the specified index.

Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
---
 lib/netdev-linux.c | 133 +++++++++++++++++++++++++++++++++++++++++++++
 lib/netdev-linux.h |   6 ++
 lib/tc.c           |  21 +++++++
 lib/tc.h           |   6 ++
 4 files changed, 166 insertions(+)

Comments

Eelco Chaudron May 13, 2022, 2:55 p.m. UTC | #1
On 3 May 2022, at 5:08, Jianbo Liu via dev wrote:

> Add helpers to add, delete and get stats of police action with
> the specified index.

See inline comments… This is the last patch for this week, I’ll continue the review sometime next week!


> Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
> ---
>  lib/netdev-linux.c | 133 +++++++++++++++++++++++++++++++++++++++++++++
>  lib/netdev-linux.h |   6 ++
>  lib/tc.c           |  21 +++++++
>  lib/tc.h           |   6 ++
>  4 files changed, 166 insertions(+)
>
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index eb05153c0..ef6c7312f 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -5664,6 +5664,139 @@ tc_add_policer(struct netdev *netdev, uint32_t kbits_rate,
>      return 0;
>  }
>
> +int
> +tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
> +                      uint32_t kbits_burst, uint32_t pkts_rate,
> +                      uint32_t pkts_burst, bool update)
> +{
> +    struct tc_police tc_police;
> +    struct ofpbuf request;
> +    struct tcamsg *tcamsg;
> +    size_t offset;
> +    int flags;
> +
> +    tc_policer_init(&tc_police, kbits_rate, kbits_burst);
> +    tc_police.index = index;
> +
> +    flags = (update ? NLM_F_REPLACE : NLM_F_EXCL) | NLM_F_CREATE;
> +    tcamsg = tc_make_action_request(RTM_NEWACTION, flags, &request);
> +    if (!tcamsg) {
> +        return ENODEV;
> +    }
> +
> +    offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
> +    nl_msg_put_act_police(&request, &tc_police, pkts_rate, pkts_burst);
> +    nl_msg_end_nested(&request, offset);
> +
> +    return tc_transact(&request, NULL);
> +}
> +
> +static int
> +tc_update_policer_action_stats(struct ofpbuf *msg,
> +                               struct ofputil_meter_stats *stats)
> +{
> +    const struct nlattr *act = NULL;
> +    struct tc_flower flower;
> +    struct nlattr *prio;
> +    struct tcamsg *tca;
> +    int error;
> +

If would also do the if (!stats) return check here so none of the APIs will crash if messed up.

> +    if (NLMSG_HDRLEN + sizeof *tca > msg->size) {
> +        return EPROTO;
> +    }
> +
> +    tca = ofpbuf_at_assert(msg, NLMSG_HDRLEN, sizeof *tca);
> +
> +    act = nl_attr_find(msg, NLMSG_HDRLEN + sizeof *tca, TCA_ACT_TAB);
> +    if (!act) {
> +        return EPROTO;
> +    }
> +
> +    prio = (struct nlattr *) act + 1;
> +    memset(&flower, 0, sizeof(struct tc_flower));
> +    error = tc_parse_single_action(prio, &flower, false);

I do not like this approach, we zero out a complex data structure pass into a general function, and hope it gives us a counter we need.
I think we should separate out the statistics handling from nl_parse_single_action() and make it available to use here.

> +    if (!error) {
> +        stats->packet_in_count +=
> +            get_32aligned_u64(&flower.stats_sw.n_packets);
> +        stats->byte_in_count += get_32aligned_u64(&flower.stats_sw.n_bytes);
> +        stats->packet_in_count +=
> +            get_32aligned_u64(&flower.stats_hw.n_packets);
> +        stats->byte_in_count += get_32aligned_u64(&flower.stats_hw.n_bytes);

What about the band stats on dropped packets? We need this to be in line with the kernel dp.

> +    }
> +
> +    return error;
> +}
> +
> +int
> +tc_get_policer_action(uint32_t index, struct ofputil_meter_stats *stats)
> +{
> +    struct ofpbuf *replyp = NULL;
> +    struct ofpbuf request;
> +    struct tcamsg *tcamsg;
> +    size_t root_offset;
> +    size_t prio_offset;
> +    int prio = 0;
> +    int error;

If you do not add an “if !stats” check in tc_update_policer_action_stats(), I would add it here to avoid crashes with invalid API callback arguments.

> +    tcamsg = tc_make_action_request(RTM_GETACTION, 0, &request);
> +    if (!tcamsg) {
> +        return ENODEV;
> +    }
> +
> +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
> +    prio_offset = nl_msg_start_nested(&request, ++prio);

Why do we need the ++prio variable? Can we just not call it with 1?

> +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
> +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
> +    nl_msg_end_nested(&request, prio_offset);
> +    nl_msg_end_nested(&request, root_offset);
> +
> +    error = tc_transact(&request, &replyp);
> +    if (error) {
> +        VLOG_ERR_RL(&rl, "failed to dump police action (index: %u), err=%d",

Capital F for Failed.

> +                    index, error);
> +        return error;
> +    }
> +
> +    error = tc_update_policer_action_stats(replyp, stats);
> +    if (error) {
> +        VLOG_ERR_RL(&rl, "failed to update police stats (index: %u), err=%d",
> +                    index, error);

Capital F for Failed.


Any reason for having log messages here, but not for add and delete?

> +    }
> +
> +    return error;
> +}
> +
> +int
> +tc_del_policer_action(uint32_t index, struct ofputil_meter_stats *stats)
> +{
> +    struct ofpbuf *replyp = NULL;
> +    struct ofpbuf request;
> +    struct tcamsg *tcamsg;
> +    size_t root_offset;
> +    size_t prio_offset;
> +    int prio = 0;
> +    int error;
> +
> +    tcamsg = tc_make_action_request(RTM_DELACTION, NLM_F_ACK, &request);
> +    if (!tcamsg) {
> +        return ENODEV;
> +    }
> +
> +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
> +    prio_offset = nl_msg_start_nested(&request, ++prio);

See above on prio.

> +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
> +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
> +    nl_msg_end_nested(&request, prio_offset);
> +    nl_msg_end_nested(&request, root_offset);
> +
> +    error = tc_transact(&request, &replyp);
> +    if (!error && stats) {
> +        error = tc_update_policer_action_stats(replyp, stats);
> +    }
	If the null check for stats get added to tc_update_policer_action_stats() the code can be changed to:

	if (error) {
	    return error;
    }
	return tc_update_policer_action_stats(replyp, stats);

> +
> +    return error;
> +}
> +
>  static void
>  read_psched(void)
>  {
> diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
> index e1e30f806..9a416ce50 100644
> --- a/lib/netdev-linux.h
> +++ b/lib/netdev-linux.h
> @@ -19,6 +19,7 @@
>
>  #include <stdint.h>
>  #include <stdbool.h>
> +#include "openvswitch/ofp-meter.h"
>
>  /* These functions are Linux specific, so they should be used directly only by
>   * Linux-specific code. */
> @@ -28,5 +29,10 @@ struct netdev;
>  int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
>                                    const char *flag_name, bool enable);
>  int linux_get_ifindex(const char *netdev_name);
> +int tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
> +                          uint32_t kbits_burst, uint32_t pkts_rate,
> +                          uint32_t pkts_burst, bool update);
> +int tc_del_policer_action(uint32_t index, struct ofputil_meter_stats *stats);
> +int tc_get_policer_action(uint32_t index, struct ofputil_meter_stats *stats);
>
>  #endif /* netdev-linux.h */
> diff --git a/lib/tc.c b/lib/tc.c
> index af7a7bc6d..ee16364ea 100644
> --- a/lib/tc.c
> +++ b/lib/tc.c
> @@ -199,6 +199,20 @@ tc_make_request(int ifindex, int type, unsigned int flags,
>      return tcmsg;
>  }
>
> +struct tcamsg *
> +tc_make_action_request(int type, unsigned int flags,
> +                       struct ofpbuf *request)
> +{
> +    struct tcamsg *tcamsg;
> +
> +    ofpbuf_init(request, 512);
> +    nl_msg_put_nlmsghdr(request, sizeof *tcamsg, type, NLM_F_REQUEST | flags);
> +    tcamsg = ofpbuf_put_zeros(request, sizeof *tcamsg);
> +    tcamsg->tca_family = AF_UNSPEC;
> +
> +    return tcamsg;
> +}
> +
>  static void request_from_tcf_id(struct tcf_id *id, uint16_t eth_type,
>                                  int type, unsigned int flags,
>                                  struct ofpbuf *request)
> @@ -1863,6 +1877,13 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
>      return 0;
>  }
>
> +int
> +tc_parse_single_action(struct nlattr *action, struct tc_flower *flower,
> +                       bool terse)
> +{
> +    return nl_parse_single_action(action, flower, terse);
> +}
> +

Why don’t we move all the functions in lib/netdev-linux.c above to tc.c where they belong? I know it’s a bit more work, but it avoids exposing internals like this.

>  #define TCA_ACT_MIN_PRIO 1
>
>  static int
> diff --git a/lib/tc.h b/lib/tc.h
> index 201345672..96b9e6ccc 100644
> --- a/lib/tc.h
> +++ b/lib/tc.h
> @@ -80,6 +80,8 @@ tc_get_minor(unsigned int handle)
>
>  struct tcmsg *tc_make_request(int ifindex, int type,
>                                unsigned int flags, struct ofpbuf *);
> +struct tcamsg *tc_make_action_request(int type, unsigned int flags,
> +                                      struct ofpbuf *request);
>  int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp);
>  int tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id,
>                       enum tc_qdisc_hook hook);
> @@ -365,6 +367,8 @@ struct tc_flower {
>      enum tc_offload_policy tc_policy;
>  };
>
> +struct nlattr;

Rather than this, would it make sense to include “netlink.h” at the top? But this is not needed if we move the function to tc.c

> +
>  int tc_replace_flower(struct tcf_id *id, struct tc_flower *flower);
>  int tc_del_filter(struct tcf_id *id);
>  int tc_get_flower(struct tcf_id *id, struct tc_flower *flower);
> @@ -376,5 +380,7 @@ int parse_netlink_to_tc_flower(struct ofpbuf *reply,
>                                 bool terse);
>  int parse_netlink_to_tc_chain(struct ofpbuf *reply, uint32_t *chain);
>  void tc_set_policy(const char *policy);
> +int tc_parse_single_action(struct nlattr *action, struct tc_flower *flower,
> +                           bool terse);
>
>  #endif /* tc.h */
> -- 
> 2.26.2
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Jianbo Liu May 17, 2022, 12:54 p.m. UTC | #2
On Fri, 2022-05-13 at 16:55 +0200, Eelco Chaudron wrote:
> 
> 
> On 3 May 2022, at 5:08, Jianbo Liu via dev wrote:
> 
> > Add helpers to add, delete and get stats of police action with
> > the specified index.
> 
> See inline comments… This is the last patch for this week, I’ll
> continue the review sometime next week!
> 
> 
> > Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
> > ---
> >  lib/netdev-linux.c | 133
> > +++++++++++++++++++++++++++++++++++++++++++++
> >  lib/netdev-linux.h |   6 ++
> >  lib/tc.c           |  21 +++++++
> >  lib/tc.h           |   6 ++
> >  4 files changed, 166 insertions(+)
> > 
> > diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> > index eb05153c0..ef6c7312f 100644
> > --- a/lib/netdev-linux.c
> > +++ b/lib/netdev-linux.c
> > @@ -5664,6 +5664,139 @@ tc_add_policer(struct netdev *netdev,
> > uint32_t kbits_rate,
> >      return 0;
> >  }
> > 
> > +int
> > +tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
> > +                      uint32_t kbits_burst, uint32_t pkts_rate,
> > +                      uint32_t pkts_burst, bool update)
> > +{
> > +    struct tc_police tc_police;
> > +    struct ofpbuf request;
> > +    struct tcamsg *tcamsg;
> > +    size_t offset;
> > +    int flags;
> > +
> > +    tc_policer_init(&tc_police, kbits_rate, kbits_burst);
> > +    tc_police.index = index;
> > +
> > +    flags = (update ? NLM_F_REPLACE : NLM_F_EXCL) | NLM_F_CREATE;
> > +    tcamsg = tc_make_action_request(RTM_NEWACTION, flags,
> > &request);
> > +    if (!tcamsg) {
> > +        return ENODEV;
> > +    }
> > +
> > +    offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
> > +    nl_msg_put_act_police(&request, &tc_police, pkts_rate,
> > pkts_burst);
> > +    nl_msg_end_nested(&request, offset);
> > +
> > +    return tc_transact(&request, NULL);
> > +}
> > +
> > +static int
> > +tc_update_policer_action_stats(struct ofpbuf *msg,
> > +                               struct ofputil_meter_stats *stats)
> > +{
> > +    const struct nlattr *act = NULL;
> > +    struct tc_flower flower;
> > +    struct nlattr *prio;
> > +    struct tcamsg *tca;
> > +    int error;
> > +
> 
> If would also do the if (!stats) return check here so none of the
> APIs will crash if messed up.
> 
> > +    if (NLMSG_HDRLEN + sizeof *tca > msg->size) {
> > +        return EPROTO;
> > +    }
> > +
> > +    tca = ofpbuf_at_assert(msg, NLMSG_HDRLEN, sizeof *tca);
> > +
> > +    act = nl_attr_find(msg, NLMSG_HDRLEN + sizeof *tca,
> > TCA_ACT_TAB);
> > +    if (!act) {
> > +        return EPROTO;
> > +    }
> > +
> > +    prio = (struct nlattr *) act + 1;
> > +    memset(&flower, 0, sizeof(struct tc_flower));
> > +    error = tc_parse_single_action(prio, &flower, false);
> 
> I do not like this approach, we zero out a complex data structure
> pass into a general function, and hope it gives us a counter we need.
> I think we should separate out the statistics handling from
> nl_parse_single_action() and make it available to use here.
> 
> > +    if (!error) {
> > +        stats->packet_in_count +=
> > +            get_32aligned_u64(&flower.stats_sw.n_packets);
> > +        stats->byte_in_count +=
> > get_32aligned_u64(&flower.stats_sw.n_bytes);
> > +        stats->packet_in_count +=
> > +            get_32aligned_u64(&flower.stats_hw.n_packets);
> > +        stats->byte_in_count +=
> > get_32aligned_u64(&flower.stats_hw.n_bytes);
> 
> What about the band stats on dropped packets? We need this to be in
> line with the kernel dp.
> 

It looks something wrong with tc police stats for the dropped packets,
and someone is working on the kernel. Maybe we have to ignore this
issue now?

> > +    }
> > +
> > +    return error;
> > +}
> > +
> > +int
> > +tc_get_policer_action(uint32_t index, struct ofputil_meter_stats
> > *stats)
> > +{
> > +    struct ofpbuf *replyp = NULL;
> > +    struct ofpbuf request;
> > +    struct tcamsg *tcamsg;
> > +    size_t root_offset;
> > +    size_t prio_offset;
> > +    int prio = 0;
> > +    int error;
> 
> If you do not add an “if !stats” check in
> tc_update_policer_action_stats(), I would add it here to avoid
> crashes with invalid API callback arguments.
> 
> > +    tcamsg = tc_make_action_request(RTM_GETACTION, 0, &request);
> > +    if (!tcamsg) {
> > +        return ENODEV;
> > +    }
> > +
> > +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
> > +    prio_offset = nl_msg_start_nested(&request, ++prio);
> 
> Why do we need the ++prio variable? Can we just not call it with 1?
> 
> > +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
> > +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
> > +    nl_msg_end_nested(&request, prio_offset);
> > +    nl_msg_end_nested(&request, root_offset);
> > +
> > +    error = tc_transact(&request, &replyp);
> > +    if (error) {
> > +        VLOG_ERR_RL(&rl, "failed to dump police action (index:
> > %u), err=%d",
> 
> Capital F for Failed.
> 
> > +                    index, error);
> > +        return error;
> > +    }
> > +
> > +    error = tc_update_policer_action_stats(replyp, stats);
> > +    if (error) {
> > +        VLOG_ERR_RL(&rl, "failed to update police stats (index:
> > %u), err=%d",
> > +                    index, error);
> 
> Capital F for Failed.
> 
> 
> Any reason for having log messages here, but not for add and delete?
> 
> > +    }
> > +
> > +    return error;
> > +}
> > +
> > +int
> > +tc_del_policer_action(uint32_t index, struct ofputil_meter_stats
> > *stats)
> > +{
> > +    struct ofpbuf *replyp = NULL;
> > +    struct ofpbuf request;
> > +    struct tcamsg *tcamsg;
> > +    size_t root_offset;
> > +    size_t prio_offset;
> > +    int prio = 0;
> > +    int error;
> > +
> > +    tcamsg = tc_make_action_request(RTM_DELACTION, NLM_F_ACK,
> > &request);
> > +    if (!tcamsg) {
> > +        return ENODEV;
> > +    }
> > +
> > +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
> > +    prio_offset = nl_msg_start_nested(&request, ++prio);
> 
> See above on prio.
> 
> > +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
> > +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
> > +    nl_msg_end_nested(&request, prio_offset);
> > +    nl_msg_end_nested(&request, root_offset);
> > +
> > +    error = tc_transact(&request, &replyp);
> > +    if (!error && stats) {
> > +        error = tc_update_policer_action_stats(replyp, stats);
> > +    }
>         If the null check for stats get added to
> tc_update_policer_action_stats() the code can be changed to:
> 
>         if (error) {
>             return error;
>     }
>         return tc_update_policer_action_stats(replyp, stats);
> 
> > +
> > +    return error;
> > +}
> > +
> >  static void
> >  read_psched(void)
> >  {
> > diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
> > index e1e30f806..9a416ce50 100644
> > --- a/lib/netdev-linux.h
> > +++ b/lib/netdev-linux.h
> > @@ -19,6 +19,7 @@
> > 
> >  #include <stdint.h>
> >  #include <stdbool.h>
> > +#include "openvswitch/ofp-meter.h"
> > 
> >  /* These functions are Linux specific, so they should be used
> > directly only by
> >   * Linux-specific code. */
> > @@ -28,5 +29,10 @@ struct netdev;
> >  int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t
> > flag,
> >                                    const char *flag_name, bool
> > enable);
> >  int linux_get_ifindex(const char *netdev_name);
> > +int tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
> > +                          uint32_t kbits_burst, uint32_t
> > pkts_rate,
> > +                          uint32_t pkts_burst, bool update);
> > +int tc_del_policer_action(uint32_t index, struct
> > ofputil_meter_stats *stats);
> > +int tc_get_policer_action(uint32_t index, struct
> > ofputil_meter_stats *stats);
> > 
> >  #endif /* netdev-linux.h */
> > diff --git a/lib/tc.c b/lib/tc.c
> > index af7a7bc6d..ee16364ea 100644
> > --- a/lib/tc.c
> > +++ b/lib/tc.c
> > @@ -199,6 +199,20 @@ tc_make_request(int ifindex, int type,
> > unsigned int flags,
> >      return tcmsg;
> >  }
> > 
> > +struct tcamsg *
> > +tc_make_action_request(int type, unsigned int flags,
> > +                       struct ofpbuf *request)
> > +{
> > +    struct tcamsg *tcamsg;
> > +
> > +    ofpbuf_init(request, 512);
> > +    nl_msg_put_nlmsghdr(request, sizeof *tcamsg, type,
> > NLM_F_REQUEST | flags);
> > +    tcamsg = ofpbuf_put_zeros(request, sizeof *tcamsg);
> > +    tcamsg->tca_family = AF_UNSPEC;
> > +
> > +    return tcamsg;
> > +}
> > +
> >  static void request_from_tcf_id(struct tcf_id *id, uint16_t
> > eth_type,
> >                                  int type, unsigned int flags,
> >                                  struct ofpbuf *request)
> > @@ -1863,6 +1877,13 @@ nl_parse_single_action(struct nlattr
> > *action, struct tc_flower *flower,
> >      return 0;
> >  }
> > 
> > +int
> > +tc_parse_single_action(struct nlattr *action, struct tc_flower
> > *flower,
> > +                       bool terse)
> > +{
> > +    return nl_parse_single_action(action, flower, terse);
> > +}
> > +
> 
> Why don’t we move all the functions in lib/netdev-linux.c above to
> tc.c where they belong? I know it’s a bit more work, but it avoids
> exposing internals like this.
> 

It's because tc_add_policer_action() calls tc_policer_init() and
nl_msg_put_act_police(), which uses much of code in netdev-linux.c.
And there also is a similar func tc_action_policer() in this file.

> >  #define TCA_ACT_MIN_PRIO 1
> > 
> >  static int
> > diff --git a/lib/tc.h b/lib/tc.h
> > index 201345672..96b9e6ccc 100644
> > --- a/lib/tc.h
> > +++ b/lib/tc.h
> > @@ -80,6 +80,8 @@ tc_get_minor(unsigned int handle)
> > 
> >  struct tcmsg *tc_make_request(int ifindex, int type,
> >                                unsigned int flags, struct ofpbuf
> > *);
> > +struct tcamsg *tc_make_action_request(int type, unsigned int
> > flags,
> > +                                      struct ofpbuf *request);
> >  int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp);
> >  int tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id,
> >                       enum tc_qdisc_hook hook);
> > @@ -365,6 +367,8 @@ struct tc_flower {
> >      enum tc_offload_policy tc_policy;
> >  };
> > 
> > +struct nlattr;
> 
> Rather than this, would it make sense to include “netlink.h” at the
> top? But this is not needed if we move the function to tc.c
> 
> > +
> >  int tc_replace_flower(struct tcf_id *id, struct tc_flower
> > *flower);
> >  int tc_del_filter(struct tcf_id *id);
> >  int tc_get_flower(struct tcf_id *id, struct tc_flower *flower);
> > @@ -376,5 +380,7 @@ int parse_netlink_to_tc_flower(struct ofpbuf
> > *reply,
> >                                 bool terse);
> >  int parse_netlink_to_tc_chain(struct ofpbuf *reply, uint32_t
> > *chain);
> >  void tc_set_policy(const char *policy);
> > +int tc_parse_single_action(struct nlattr *action, struct tc_flower
> > *flower,
> > +                           bool terse);
> > 
> >  #endif /* tc.h */
> > -- 
> > 2.26.2
> > 
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Eelco Chaudron May 23, 2022, 10:12 a.m. UTC | #3
On 17 May 2022, at 14:54, Jianbo Liu wrote:

> On Fri, 2022-05-13 at 16:55 +0200, Eelco Chaudron wrote:
>>
>>
>> On 3 May 2022, at 5:08, Jianbo Liu via dev wrote:
>>
>>> Add helpers to add, delete and get stats of police action with
>>> the specified index.
>>
>> See inline comments… This is the last patch for this week, I’ll
>> continue the review sometime next week!
>>
>>
>>> Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
>>> ---
>>>  lib/netdev-linux.c | 133
>>> +++++++++++++++++++++++++++++++++++++++++++++
>>>  lib/netdev-linux.h |   6 ++
>>>  lib/tc.c           |  21 +++++++
>>>  lib/tc.h           |   6 ++
>>>  4 files changed, 166 insertions(+)
>>>
>>> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
>>> index eb05153c0..ef6c7312f 100644
>>> --- a/lib/netdev-linux.c
>>> +++ b/lib/netdev-linux.c
>>> @@ -5664,6 +5664,139 @@ tc_add_policer(struct netdev *netdev,
>>> uint32_t kbits_rate,
>>>      return 0;
>>>  }
>>>
>>> +int
>>> +tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
>>> +                      uint32_t kbits_burst, uint32_t pkts_rate,
>>> +                      uint32_t pkts_burst, bool update)
>>> +{
>>> +    struct tc_police tc_police;
>>> +    struct ofpbuf request;
>>> +    struct tcamsg *tcamsg;
>>> +    size_t offset;
>>> +    int flags;
>>> +
>>> +    tc_policer_init(&tc_police, kbits_rate, kbits_burst);
>>> +    tc_police.index = index;
>>> +
>>> +    flags = (update ? NLM_F_REPLACE : NLM_F_EXCL) | NLM_F_CREATE;
>>> +    tcamsg = tc_make_action_request(RTM_NEWACTION, flags,
>>> &request);
>>> +    if (!tcamsg) {
>>> +        return ENODEV;
>>> +    }
>>> +
>>> +    offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
>>> +    nl_msg_put_act_police(&request, &tc_police, pkts_rate,
>>> pkts_burst);
>>> +    nl_msg_end_nested(&request, offset);
>>> +
>>> +    return tc_transact(&request, NULL);
>>> +}
>>> +
>>> +static int
>>> +tc_update_policer_action_stats(struct ofpbuf *msg,
>>> +                               struct ofputil_meter_stats *stats)
>>> +{
>>> +    const struct nlattr *act = NULL;
>>> +    struct tc_flower flower;
>>> +    struct nlattr *prio;
>>> +    struct tcamsg *tca;
>>> +    int error;
>>> +
>>
>> If would also do the if (!stats) return check here so none of the
>> APIs will crash if messed up.
>>
>>> +    if (NLMSG_HDRLEN + sizeof *tca > msg->size) {
>>> +        return EPROTO;
>>> +    }
>>> +
>>> +    tca = ofpbuf_at_assert(msg, NLMSG_HDRLEN, sizeof *tca);
>>> +
>>> +    act = nl_attr_find(msg, NLMSG_HDRLEN + sizeof *tca,
>>> TCA_ACT_TAB);
>>> +    if (!act) {
>>> +        return EPROTO;
>>> +    }
>>> +
>>> +    prio = (struct nlattr *) act + 1;
>>> +    memset(&flower, 0, sizeof(struct tc_flower));
>>> +    error = tc_parse_single_action(prio, &flower, false);
>>
>> I do not like this approach, we zero out a complex data structure
>> pass into a general function, and hope it gives us a counter we need.
>> I think we should separate out the statistics handling from
>> nl_parse_single_action() and make it available to use here.
>>
>>> +    if (!error) {
>>> +        stats->packet_in_count +=
>>> +            get_32aligned_u64(&flower.stats_sw.n_packets);
>>> +        stats->byte_in_count +=
>>> get_32aligned_u64(&flower.stats_sw.n_bytes);
>>> +        stats->packet_in_count +=
>>> +            get_32aligned_u64(&flower.stats_hw.n_packets);
>>> +        stats->byte_in_count +=
>>> get_32aligned_u64(&flower.stats_hw.n_bytes);
>>
>> What about the band stats on dropped packets? We need this to be in
>> line with the kernel dp.
>>
>
> It looks something wrong with tc police stats for the dropped packets,
> and someone is working on the kernel. Maybe we have to ignore this
> issue now?

I think this is very important for debugging. Maybe we can add the code to support this, so it will be there when it’s fixed in the kernel?

>>> +    }
>>> +
>>> +    return error;
>>> +}
>>> +
>>> +int
>>> +tc_get_policer_action(uint32_t index, struct ofputil_meter_stats
>>> *stats)
>>> +{
>>> +    struct ofpbuf *replyp = NULL;
>>> +    struct ofpbuf request;
>>> +    struct tcamsg *tcamsg;
>>> +    size_t root_offset;
>>> +    size_t prio_offset;
>>> +    int prio = 0;
>>> +    int error;
>>
>> If you do not add an “if !stats” check in
>> tc_update_policer_action_stats(), I would add it here to avoid
>> crashes with invalid API callback arguments.
>>
>>> +    tcamsg = tc_make_action_request(RTM_GETACTION, 0, &request);
>>> +    if (!tcamsg) {
>>> +        return ENODEV;
>>> +    }
>>> +
>>> +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
>>> +    prio_offset = nl_msg_start_nested(&request, ++prio);
>>
>> Why do we need the ++prio variable? Can we just not call it with 1?
>>
>>> +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
>>> +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
>>> +    nl_msg_end_nested(&request, prio_offset);
>>> +    nl_msg_end_nested(&request, root_offset);
>>> +
>>> +    error = tc_transact(&request, &replyp);
>>> +    if (error) {
>>> +        VLOG_ERR_RL(&rl, "failed to dump police action (index:
>>> %u), err=%d",
>>
>> Capital F for Failed.
>>
>>> +                    index, error);
>>> +        return error;
>>> +    }
>>> +
>>> +    error = tc_update_policer_action_stats(replyp, stats);
>>> +    if (error) {
>>> +        VLOG_ERR_RL(&rl, "failed to update police stats (index:
>>> %u), err=%d",
>>> +                    index, error);
>>
>> Capital F for Failed.
>>
>>
>> Any reason for having log messages here, but not for add and delete?
>>
>>> +    }
>>> +
>>> +    return error;
>>> +}
>>> +
>>> +int
>>> +tc_del_policer_action(uint32_t index, struct ofputil_meter_stats
>>> *stats)
>>> +{
>>> +    struct ofpbuf *replyp = NULL;
>>> +    struct ofpbuf request;
>>> +    struct tcamsg *tcamsg;
>>> +    size_t root_offset;
>>> +    size_t prio_offset;
>>> +    int prio = 0;
>>> +    int error;
>>> +
>>> +    tcamsg = tc_make_action_request(RTM_DELACTION, NLM_F_ACK,
>>> &request);
>>> +    if (!tcamsg) {
>>> +        return ENODEV;
>>> +    }
>>> +
>>> +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
>>> +    prio_offset = nl_msg_start_nested(&request, ++prio);
>>
>> See above on prio.
>>
>>> +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
>>> +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
>>> +    nl_msg_end_nested(&request, prio_offset);
>>> +    nl_msg_end_nested(&request, root_offset);
>>> +
>>> +    error = tc_transact(&request, &replyp);
>>> +    if (!error && stats) {
>>> +        error = tc_update_policer_action_stats(replyp, stats);
>>> +    }
>>         If the null check for stats get added to
>> tc_update_policer_action_stats() the code can be changed to:
>>
>>         if (error) {
>>             return error;
>>     }
>>         return tc_update_policer_action_stats(replyp, stats);
>>
>>> +
>>> +    return error;
>>> +}
>>> +
>>>  static void
>>>  read_psched(void)
>>>  {
>>> diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
>>> index e1e30f806..9a416ce50 100644
>>> --- a/lib/netdev-linux.h
>>> +++ b/lib/netdev-linux.h
>>> @@ -19,6 +19,7 @@
>>>
>>>  #include <stdint.h>
>>>  #include <stdbool.h>
>>> +#include "openvswitch/ofp-meter.h"
>>>
>>>  /* These functions are Linux specific, so they should be used
>>> directly only by
>>>   * Linux-specific code. */
>>> @@ -28,5 +29,10 @@ struct netdev;
>>>  int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t
>>> flag,
>>>                                    const char *flag_name, bool
>>> enable);
>>>  int linux_get_ifindex(const char *netdev_name);
>>> +int tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
>>> +                          uint32_t kbits_burst, uint32_t
>>> pkts_rate,
>>> +                          uint32_t pkts_burst, bool update);
>>> +int tc_del_policer_action(uint32_t index, struct
>>> ofputil_meter_stats *stats);
>>> +int tc_get_policer_action(uint32_t index, struct
>>> ofputil_meter_stats *stats);
>>>
>>>  #endif /* netdev-linux.h */
>>> diff --git a/lib/tc.c b/lib/tc.c
>>> index af7a7bc6d..ee16364ea 100644
>>> --- a/lib/tc.c
>>> +++ b/lib/tc.c
>>> @@ -199,6 +199,20 @@ tc_make_request(int ifindex, int type,
>>> unsigned int flags,
>>>      return tcmsg;
>>>  }
>>>
>>> +struct tcamsg *
>>> +tc_make_action_request(int type, unsigned int flags,
>>> +                       struct ofpbuf *request)
>>> +{
>>> +    struct tcamsg *tcamsg;
>>> +
>>> +    ofpbuf_init(request, 512);
>>> +    nl_msg_put_nlmsghdr(request, sizeof *tcamsg, type,
>>> NLM_F_REQUEST | flags);
>>> +    tcamsg = ofpbuf_put_zeros(request, sizeof *tcamsg);
>>> +    tcamsg->tca_family = AF_UNSPEC;
>>> +
>>> +    return tcamsg;
>>> +}
>>> +
>>>  static void request_from_tcf_id(struct tcf_id *id, uint16_t
>>> eth_type,
>>>                                  int type, unsigned int flags,
>>>                                  struct ofpbuf *request)
>>> @@ -1863,6 +1877,13 @@ nl_parse_single_action(struct nlattr
>>> *action, struct tc_flower *flower,
>>>      return 0;
>>>  }
>>>
>>> +int
>>> +tc_parse_single_action(struct nlattr *action, struct tc_flower
>>> *flower,
>>> +                       bool terse)
>>> +{
>>> +    return nl_parse_single_action(action, flower, terse);
>>> +}
>>> +
>>
>> Why don’t we move all the functions in lib/netdev-linux.c above to
>> tc.c where they belong? I know it’s a bit more work, but it avoids
>> exposing internals like this.
>>
>
> It's because tc_add_policer_action() calls tc_policer_init() and
> nl_msg_put_act_police(), which uses much of code in netdev-linux.c.
> And there also is a similar func tc_action_policer() in this file.

I know it’s a mess right now, maybe you can do this as a follow-up patch, i.e. moving all TC to tc.c and only call a single function from this file to install the TC rule.

>>>  #define TCA_ACT_MIN_PRIO 1
>>>
>>>  static int
>>> diff --git a/lib/tc.h b/lib/tc.h
>>> index 201345672..96b9e6ccc 100644
>>> --- a/lib/tc.h
>>> +++ b/lib/tc.h
>>> @@ -80,6 +80,8 @@ tc_get_minor(unsigned int handle)
>>>
>>>  struct tcmsg *tc_make_request(int ifindex, int type,
>>>                                unsigned int flags, struct ofpbuf
>>> *);
>>> +struct tcamsg *tc_make_action_request(int type, unsigned int
>>> flags,
>>> +                                      struct ofpbuf *request);
>>>  int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp);
>>>  int tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id,
>>>                       enum tc_qdisc_hook hook);
>>> @@ -365,6 +367,8 @@ struct tc_flower {
>>>      enum tc_offload_policy tc_policy;
>>>  };
>>>
>>> +struct nlattr;
>>
>> Rather than this, would it make sense to include “netlink.h” at the
>> top? But this is not needed if we move the function to tc.c
>>
>>> +
>>>  int tc_replace_flower(struct tcf_id *id, struct tc_flower
>>> *flower);
>>>  int tc_del_filter(struct tcf_id *id);
>>>  int tc_get_flower(struct tcf_id *id, struct tc_flower *flower);
>>> @@ -376,5 +380,7 @@ int parse_netlink_to_tc_flower(struct ofpbuf
>>> *reply,
>>>                                 bool terse);
>>>  int parse_netlink_to_tc_chain(struct ofpbuf *reply, uint32_t
>>> *chain);
>>>  void tc_set_policy(const char *policy);
>>> +int tc_parse_single_action(struct nlattr *action, struct tc_flower
>>> *flower,
>>> +                           bool terse);
>>>
>>>  #endif /* tc.h */
>>> -- 
>>> 2.26.2
>>>
>>> _______________________________________________
>>> dev mailing list
>>> dev@openvswitch.org
>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
Jianbo Liu May 24, 2022, 2:53 a.m. UTC | #4
On Mon, 2022-05-23 at 12:12 +0200, Eelco Chaudron wrote:
> 
> 
> On 17 May 2022, at 14:54, Jianbo Liu wrote:
> 
> > On Fri, 2022-05-13 at 16:55 +0200, Eelco Chaudron wrote:
> > > 
> > > 
> > > On 3 May 2022, at 5:08, Jianbo Liu via dev wrote:
> > > 
> > > > Add helpers to add, delete and get stats of police action with
> > > > the specified index.
> > > 
> > > See inline comments… This is the last patch for this week, I’ll
> > > continue the review sometime next week!
> > > 
> > > 
> > > > Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
> > > > ---
> > > >  lib/netdev-linux.c | 133
> > > > +++++++++++++++++++++++++++++++++++++++++++++
> > > >  lib/netdev-linux.h |   6 ++
> > > >  lib/tc.c           |  21 +++++++
> > > >  lib/tc.h           |   6 ++
> > > >  4 files changed, 166 insertions(+)
> > > > 
> > > > diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> > > > index eb05153c0..ef6c7312f 100644
> > > > --- a/lib/netdev-linux.c
> > > > +++ b/lib/netdev-linux.c
> > > > @@ -5664,6 +5664,139 @@ tc_add_policer(struct netdev *netdev,
> > > > uint32_t kbits_rate,
> > > >      return 0;
> > > >  }
> > > > 
> > > > +int
> > > > +tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
> > > > +                      uint32_t kbits_burst, uint32_t
> > > > pkts_rate,
> > > > +                      uint32_t pkts_burst, bool update)
> > > > +{
> > > > +    struct tc_police tc_police;
> > > > +    struct ofpbuf request;
> > > > +    struct tcamsg *tcamsg;
> > > > +    size_t offset;
> > > > +    int flags;
> > > > +
> > > > +    tc_policer_init(&tc_police, kbits_rate, kbits_burst);
> > > > +    tc_police.index = index;
> > > > +
> > > > +    flags = (update ? NLM_F_REPLACE : NLM_F_EXCL) |
> > > > NLM_F_CREATE;
> > > > +    tcamsg = tc_make_action_request(RTM_NEWACTION, flags,
> > > > &request);
> > > > +    if (!tcamsg) {
> > > > +        return ENODEV;
> > > > +    }
> > > > +
> > > > +    offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
> > > > +    nl_msg_put_act_police(&request, &tc_police, pkts_rate,
> > > > pkts_burst);
> > > > +    nl_msg_end_nested(&request, offset);
> > > > +
> > > > +    return tc_transact(&request, NULL);
> > > > +}
> > > > +
> > > > +static int
> > > > +tc_update_policer_action_stats(struct ofpbuf *msg,
> > > > +                               struct ofputil_meter_stats
> > > > *stats)
> > > > +{
> > > > +    const struct nlattr *act = NULL;
> > > > +    struct tc_flower flower;
> > > > +    struct nlattr *prio;
> > > > +    struct tcamsg *tca;
> > > > +    int error;
> > > > +
> > > 
> > > If would also do the if (!stats) return check here so none of the
> > > APIs will crash if messed up.
> > > 
> > > > +    if (NLMSG_HDRLEN + sizeof *tca > msg->size) {
> > > > +        return EPROTO;
> > > > +    }
> > > > +
> > > > +    tca = ofpbuf_at_assert(msg, NLMSG_HDRLEN, sizeof *tca);
> > > > +
> > > > +    act = nl_attr_find(msg, NLMSG_HDRLEN + sizeof *tca,
> > > > TCA_ACT_TAB);
> > > > +    if (!act) {
> > > > +        return EPROTO;
> > > > +    }
> > > > +
> > > > +    prio = (struct nlattr *) act + 1;
> > > > +    memset(&flower, 0, sizeof(struct tc_flower));
> > > > +    error = tc_parse_single_action(prio, &flower, false);
> > > 
> > > I do not like this approach, we zero out a complex data structure
> > > pass into a general function, and hope it gives us a counter we
> > > need.
> > > I think we should separate out the statistics handling from
> > > nl_parse_single_action() and make it available to use here.
> > > 
> > > > +    if (!error) {
> > > > +        stats->packet_in_count +=
> > > > +            get_32aligned_u64(&flower.stats_sw.n_packets);
> > > > +        stats->byte_in_count +=
> > > > get_32aligned_u64(&flower.stats_sw.n_bytes);
> > > > +        stats->packet_in_count +=
> > > > +            get_32aligned_u64(&flower.stats_hw.n_packets);
> > > > +        stats->byte_in_count +=
> > > > get_32aligned_u64(&flower.stats_hw.n_bytes);
> > > 
> > > What about the band stats on dropped packets? We need this to be
> > > in
> > > line with the kernel dp.
> > > 
> > 
> > It looks something wrong with tc police stats for the dropped
> > packets,
> > and someone is working on the kernel. Maybe we have to ignore this
> > issue now?
> 
> I think this is very important for debugging. Maybe we can add the
> code to support this, so it will be there when it’s fixed in the
> kernel?
> 
> > > > +    }
> > > > +
> > > > +    return error;
> > > > +}
> > > > +
> > > > +int
> > > > +tc_get_policer_action(uint32_t index, struct
> > > > ofputil_meter_stats
> > > > *stats)
> > > > +{
> > > > +    struct ofpbuf *replyp = NULL;
> > > > +    struct ofpbuf request;
> > > > +    struct tcamsg *tcamsg;
> > > > +    size_t root_offset;
> > > > +    size_t prio_offset;
> > > > +    int prio = 0;
> > > > +    int error;
> > > 
> > > If you do not add an “if !stats” check in
> > > tc_update_policer_action_stats(), I would add it here to avoid
> > > crashes with invalid API callback arguments.
> > > 
> > > > +    tcamsg = tc_make_action_request(RTM_GETACTION, 0,
> > > > &request);
> > > > +    if (!tcamsg) {
> > > > +        return ENODEV;
> > > > +    }
> > > > +
> > > > +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
> > > > +    prio_offset = nl_msg_start_nested(&request, ++prio);
> > > 
> > > Why do we need the ++prio variable? Can we just not call it with
> > > 1?
> > > 
> > > > +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
> > > > +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
> > > > +    nl_msg_end_nested(&request, prio_offset);
> > > > +    nl_msg_end_nested(&request, root_offset);
> > > > +
> > > > +    error = tc_transact(&request, &replyp);
> > > > +    if (error) {
> > > > +        VLOG_ERR_RL(&rl, "failed to dump police action (index:
> > > > %u), err=%d",
> > > 
> > > Capital F for Failed.
> > > 
> > > > +                    index, error);
> > > > +        return error;
> > > > +    }
> > > > +
> > > > +    error = tc_update_policer_action_stats(replyp, stats);
> > > > +    if (error) {
> > > > +        VLOG_ERR_RL(&rl, "failed to update police stats
> > > > (index:
> > > > %u), err=%d",
> > > > +                    index, error);
> > > 
> > > Capital F for Failed.
> > > 
> > > 
> > > Any reason for having log messages here, but not for add and
> > > delete?
> > > 
> > > > +    }
> > > > +
> > > > +    return error;
> > > > +}
> > > > +
> > > > +int
> > > > +tc_del_policer_action(uint32_t index, struct
> > > > ofputil_meter_stats
> > > > *stats)
> > > > +{
> > > > +    struct ofpbuf *replyp = NULL;
> > > > +    struct ofpbuf request;
> > > > +    struct tcamsg *tcamsg;
> > > > +    size_t root_offset;
> > > > +    size_t prio_offset;
> > > > +    int prio = 0;
> > > > +    int error;
> > > > +
> > > > +    tcamsg = tc_make_action_request(RTM_DELACTION, NLM_F_ACK,
> > > > &request);
> > > > +    if (!tcamsg) {
> > > > +        return ENODEV;
> > > > +    }
> > > > +
> > > > +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
> > > > +    prio_offset = nl_msg_start_nested(&request, ++prio);
> > > 
> > > See above on prio.
> > > 
> > > > +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
> > > > +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
> > > > +    nl_msg_end_nested(&request, prio_offset);
> > > > +    nl_msg_end_nested(&request, root_offset);
> > > > +
> > > > +    error = tc_transact(&request, &replyp);
> > > > +    if (!error && stats) {
> > > > +        error = tc_update_policer_action_stats(replyp, stats);
> > > > +    }
> > >         If the null check for stats get added to
> > > tc_update_policer_action_stats() the code can be changed to:
> > > 
> > >         if (error) {
> > >             return error;
> > >     }
> > >         return tc_update_policer_action_stats(replyp, stats);
> > > 
> > > > +
> > > > +    return error;
> > > > +}
> > > > +
> > > >  static void
> > > >  read_psched(void)
> > > >  {
> > > > diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
> > > > index e1e30f806..9a416ce50 100644
> > > > --- a/lib/netdev-linux.h
> > > > +++ b/lib/netdev-linux.h
> > > > @@ -19,6 +19,7 @@
> > > > 
> > > >  #include <stdint.h>
> > > >  #include <stdbool.h>
> > > > +#include "openvswitch/ofp-meter.h"
> > > > 
> > > >  /* These functions are Linux specific, so they should be used
> > > > directly only by
> > > >   * Linux-specific code. */
> > > > @@ -28,5 +29,10 @@ struct netdev;
> > > >  int netdev_linux_ethtool_set_flag(struct netdev *netdev,
> > > > uint32_t
> > > > flag,
> > > >                                    const char *flag_name, bool
> > > > enable);
> > > >  int linux_get_ifindex(const char *netdev_name);
> > > > +int tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
> > > > +                          uint32_t kbits_burst, uint32_t
> > > > pkts_rate,
> > > > +                          uint32_t pkts_burst, bool update);
> > > > +int tc_del_policer_action(uint32_t index, struct
> > > > ofputil_meter_stats *stats);
> > > > +int tc_get_policer_action(uint32_t index, struct
> > > > ofputil_meter_stats *stats);
> > > > 
> > > >  #endif /* netdev-linux.h */
> > > > diff --git a/lib/tc.c b/lib/tc.c
> > > > index af7a7bc6d..ee16364ea 100644
> > > > --- a/lib/tc.c
> > > > +++ b/lib/tc.c
> > > > @@ -199,6 +199,20 @@ tc_make_request(int ifindex, int type,
> > > > unsigned int flags,
> > > >      return tcmsg;
> > > >  }
> > > > 
> > > > +struct tcamsg *
> > > > +tc_make_action_request(int type, unsigned int flags,
> > > > +                       struct ofpbuf *request)
> > > > +{
> > > > +    struct tcamsg *tcamsg;
> > > > +
> > > > +    ofpbuf_init(request, 512);
> > > > +    nl_msg_put_nlmsghdr(request, sizeof *tcamsg, type,
> > > > NLM_F_REQUEST | flags);
> > > > +    tcamsg = ofpbuf_put_zeros(request, sizeof *tcamsg);
> > > > +    tcamsg->tca_family = AF_UNSPEC;
> > > > +
> > > > +    return tcamsg;
> > > > +}
> > > > +
> > > >  static void request_from_tcf_id(struct tcf_id *id, uint16_t
> > > > eth_type,
> > > >                                  int type, unsigned int flags,
> > > >                                  struct ofpbuf *request)
> > > > @@ -1863,6 +1877,13 @@ nl_parse_single_action(struct nlattr
> > > > *action, struct tc_flower *flower,
> > > >      return 0;
> > > >  }
> > > > 
> > > > +int
> > > > +tc_parse_single_action(struct nlattr *action, struct tc_flower
> > > > *flower,
> > > > +                       bool terse)
> > > > +{
> > > > +    return nl_parse_single_action(action, flower, terse);
> > > > +}
> > > > +
> > > 
> > > Why don’t we move all the functions in lib/netdev-linux.c above
> > > to
> > > tc.c where they belong? I know it’s a bit more work, but it
> > > avoids
> > > exposing internals like this.
> > > 
> > 
> > It's because tc_add_policer_action() calls tc_policer_init() and
> > nl_msg_put_act_police(), which uses much of code in netdev-linux.c.
> > And there also is a similar func tc_action_policer() in this file.
> 
> I know it’s a mess right now, maybe you can do this as a follow-up
> patch, i.e. moving all TC to tc.c and only call a single function
> from this file to install the TC rule.

It doesn't look like a small task. Can we do the refactoring later?

> 
> > > >  #define TCA_ACT_MIN_PRIO 1
> > > > 
> > > >  static int
> > > > diff --git a/lib/tc.h b/lib/tc.h
> > > > index 201345672..96b9e6ccc 100644
> > > > --- a/lib/tc.h
> > > > +++ b/lib/tc.h
> > > > @@ -80,6 +80,8 @@ tc_get_minor(unsigned int handle)
> > > > 
> > > >  struct tcmsg *tc_make_request(int ifindex, int type,
> > > >                                unsigned int flags, struct
> > > > ofpbuf
> > > > *);
> > > > +struct tcamsg *tc_make_action_request(int type, unsigned int
> > > > flags,
> > > > +                                      struct ofpbuf *request);
> > > >  int tc_transact(struct ofpbuf *request, struct ofpbuf
> > > > **replyp);
> > > >  int tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id,
> > > >                       enum tc_qdisc_hook hook);
> > > > @@ -365,6 +367,8 @@ struct tc_flower {
> > > >      enum tc_offload_policy tc_policy;
> > > >  };
> > > > 
> > > > +struct nlattr;
> > > 
> > > Rather than this, would it make sense to include “netlink.h” at
> > > the
> > > top? But this is not needed if we move the function to tc.c
> > > 
> > > > +
> > > >  int tc_replace_flower(struct tcf_id *id, struct tc_flower
> > > > *flower);
> > > >  int tc_del_filter(struct tcf_id *id);
> > > >  int tc_get_flower(struct tcf_id *id, struct tc_flower
> > > > *flower);
> > > > @@ -376,5 +380,7 @@ int parse_netlink_to_tc_flower(struct
> > > > ofpbuf
> > > > *reply,
> > > >                                 bool terse);
> > > >  int parse_netlink_to_tc_chain(struct ofpbuf *reply, uint32_t
> > > > *chain);
> > > >  void tc_set_policy(const char *policy);
> > > > +int tc_parse_single_action(struct nlattr *action, struct
> > > > tc_flower
> > > > *flower,
> > > > +                           bool terse);
> > > > 
> > > >  #endif /* tc.h */
> > > > -- 
> > > > 2.26.2
> > > > 
> > > > _______________________________________________
> > > > dev mailing list
> > > > dev@openvswitch.org
> > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > 
>
Eelco Chaudron May 24, 2022, 8:56 a.m. UTC | #5
On 24 May 2022, at 4:53, Jianbo Liu wrote:

> On Mon, 2022-05-23 at 12:12 +0200, Eelco Chaudron wrote:
>>
>>
>> On 17 May 2022, at 14:54, Jianbo Liu wrote:
>>
>>> On Fri, 2022-05-13 at 16:55 +0200, Eelco Chaudron wrote:
>>>>
>>>>
>>>> On 3 May 2022, at 5:08, Jianbo Liu via dev wrote:
>>>>
>>>>> Add helpers to add, delete and get stats of police action with
>>>>> the specified index.
>>>>
>>>> See inline comments… This is the last patch for this week, I’ll
>>>> continue the review sometime next week!
>>>>
>>>>
>>>>> Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
>>>>> ---
>>>>>  lib/netdev-linux.c | 133
>>>>> +++++++++++++++++++++++++++++++++++++++++++++
>>>>>  lib/netdev-linux.h |   6 ++
>>>>>  lib/tc.c           |  21 +++++++
>>>>>  lib/tc.h           |   6 ++
>>>>>  4 files changed, 166 insertions(+)
>>>>>
>>>>> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
>>>>> index eb05153c0..ef6c7312f 100644
>>>>> --- a/lib/netdev-linux.c
>>>>> +++ b/lib/netdev-linux.c
>>>>> @@ -5664,6 +5664,139 @@ tc_add_policer(struct netdev *netdev,
>>>>> uint32_t kbits_rate,
>>>>>      return 0;
>>>>>  }
>>>>>
>>>>> +int
>>>>> +tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
>>>>> +                      uint32_t kbits_burst, uint32_t
>>>>> pkts_rate,
>>>>> +                      uint32_t pkts_burst, bool update)
>>>>> +{
>>>>> +    struct tc_police tc_police;
>>>>> +    struct ofpbuf request;
>>>>> +    struct tcamsg *tcamsg;
>>>>> +    size_t offset;
>>>>> +    int flags;
>>>>> +
>>>>> +    tc_policer_init(&tc_police, kbits_rate, kbits_burst);
>>>>> +    tc_police.index = index;
>>>>> +
>>>>> +    flags = (update ? NLM_F_REPLACE : NLM_F_EXCL) |
>>>>> NLM_F_CREATE;
>>>>> +    tcamsg = tc_make_action_request(RTM_NEWACTION, flags,
>>>>> &request);
>>>>> +    if (!tcamsg) {
>>>>> +        return ENODEV;
>>>>> +    }
>>>>> +
>>>>> +    offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
>>>>> +    nl_msg_put_act_police(&request, &tc_police, pkts_rate,
>>>>> pkts_burst);
>>>>> +    nl_msg_end_nested(&request, offset);
>>>>> +
>>>>> +    return tc_transact(&request, NULL);
>>>>> +}
>>>>> +
>>>>> +static int
>>>>> +tc_update_policer_action_stats(struct ofpbuf *msg,
>>>>> +                               struct ofputil_meter_stats
>>>>> *stats)
>>>>> +{
>>>>> +    const struct nlattr *act = NULL;
>>>>> +    struct tc_flower flower;
>>>>> +    struct nlattr *prio;
>>>>> +    struct tcamsg *tca;
>>>>> +    int error;
>>>>> +
>>>>
>>>> If would also do the if (!stats) return check here so none of the
>>>> APIs will crash if messed up.
>>>>
>>>>> +    if (NLMSG_HDRLEN + sizeof *tca > msg->size) {
>>>>> +        return EPROTO;
>>>>> +    }
>>>>> +
>>>>> +    tca = ofpbuf_at_assert(msg, NLMSG_HDRLEN, sizeof *tca);
>>>>> +
>>>>> +    act = nl_attr_find(msg, NLMSG_HDRLEN + sizeof *tca,
>>>>> TCA_ACT_TAB);
>>>>> +    if (!act) {
>>>>> +        return EPROTO;
>>>>> +    }
>>>>> +
>>>>> +    prio = (struct nlattr *) act + 1;
>>>>> +    memset(&flower, 0, sizeof(struct tc_flower));
>>>>> +    error = tc_parse_single_action(prio, &flower, false);
>>>>
>>>> I do not like this approach, we zero out a complex data structure
>>>> pass into a general function, and hope it gives us a counter we
>>>> need.
>>>> I think we should separate out the statistics handling from
>>>> nl_parse_single_action() and make it available to use here.
>>>>
>>>>> +    if (!error) {
>>>>> +        stats->packet_in_count +=
>>>>> +            get_32aligned_u64(&flower.stats_sw.n_packets);
>>>>> +        stats->byte_in_count +=
>>>>> get_32aligned_u64(&flower.stats_sw.n_bytes);
>>>>> +        stats->packet_in_count +=
>>>>> +            get_32aligned_u64(&flower.stats_hw.n_packets);
>>>>> +        stats->byte_in_count +=
>>>>> get_32aligned_u64(&flower.stats_hw.n_bytes);
>>>>
>>>> What about the band stats on dropped packets? We need this to be
>>>> in
>>>> line with the kernel dp.
>>>>
>>>
>>> It looks something wrong with tc police stats for the dropped
>>> packets,
>>> and someone is working on the kernel. Maybe we have to ignore this
>>> issue now?
>>
>> I think this is very important for debugging. Maybe we can add the
>> code to support this, so it will be there when it’s fixed in the
>> kernel?
>>
>>>>> +    }
>>>>> +
>>>>> +    return error;
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +tc_get_policer_action(uint32_t index, struct
>>>>> ofputil_meter_stats
>>>>> *stats)
>>>>> +{
>>>>> +    struct ofpbuf *replyp = NULL;
>>>>> +    struct ofpbuf request;
>>>>> +    struct tcamsg *tcamsg;
>>>>> +    size_t root_offset;
>>>>> +    size_t prio_offset;
>>>>> +    int prio = 0;
>>>>> +    int error;
>>>>
>>>> If you do not add an “if !stats” check in
>>>> tc_update_policer_action_stats(), I would add it here to avoid
>>>> crashes with invalid API callback arguments.
>>>>
>>>>> +    tcamsg = tc_make_action_request(RTM_GETACTION, 0,
>>>>> &request);
>>>>> +    if (!tcamsg) {
>>>>> +        return ENODEV;
>>>>> +    }
>>>>> +
>>>>> +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
>>>>> +    prio_offset = nl_msg_start_nested(&request, ++prio);
>>>>
>>>> Why do we need the ++prio variable? Can we just not call it with
>>>> 1?
>>>>
>>>>> +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
>>>>> +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
>>>>> +    nl_msg_end_nested(&request, prio_offset);
>>>>> +    nl_msg_end_nested(&request, root_offset);
>>>>> +
>>>>> +    error = tc_transact(&request, &replyp);
>>>>> +    if (error) {
>>>>> +        VLOG_ERR_RL(&rl, "failed to dump police action (index:
>>>>> %u), err=%d",
>>>>
>>>> Capital F for Failed.
>>>>
>>>>> +                    index, error);
>>>>> +        return error;
>>>>> +    }
>>>>> +
>>>>> +    error = tc_update_policer_action_stats(replyp, stats);
>>>>> +    if (error) {
>>>>> +        VLOG_ERR_RL(&rl, "failed to update police stats
>>>>> (index:
>>>>> %u), err=%d",
>>>>> +                    index, error);
>>>>
>>>> Capital F for Failed.
>>>>
>>>>
>>>> Any reason for having log messages here, but not for add and
>>>> delete?
>>>>
>>>>> +    }
>>>>> +
>>>>> +    return error;
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +tc_del_policer_action(uint32_t index, struct
>>>>> ofputil_meter_stats
>>>>> *stats)
>>>>> +{
>>>>> +    struct ofpbuf *replyp = NULL;
>>>>> +    struct ofpbuf request;
>>>>> +    struct tcamsg *tcamsg;
>>>>> +    size_t root_offset;
>>>>> +    size_t prio_offset;
>>>>> +    int prio = 0;
>>>>> +    int error;
>>>>> +
>>>>> +    tcamsg = tc_make_action_request(RTM_DELACTION, NLM_F_ACK,
>>>>> &request);
>>>>> +    if (!tcamsg) {
>>>>> +        return ENODEV;
>>>>> +    }
>>>>> +
>>>>> +    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
>>>>> +    prio_offset = nl_msg_start_nested(&request, ++prio);
>>>>
>>>> See above on prio.
>>>>
>>>>> +    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
>>>>> +    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
>>>>> +    nl_msg_end_nested(&request, prio_offset);
>>>>> +    nl_msg_end_nested(&request, root_offset);
>>>>> +
>>>>> +    error = tc_transact(&request, &replyp);
>>>>> +    if (!error && stats) {
>>>>> +        error = tc_update_policer_action_stats(replyp, stats);
>>>>> +    }
>>>>         If the null check for stats get added to
>>>> tc_update_policer_action_stats() the code can be changed to:
>>>>
>>>>         if (error) {
>>>>             return error;
>>>>     }
>>>>         return tc_update_policer_action_stats(replyp, stats);
>>>>
>>>>> +
>>>>> +    return error;
>>>>> +}
>>>>> +
>>>>>  static void
>>>>>  read_psched(void)
>>>>>  {
>>>>> diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
>>>>> index e1e30f806..9a416ce50 100644
>>>>> --- a/lib/netdev-linux.h
>>>>> +++ b/lib/netdev-linux.h
>>>>> @@ -19,6 +19,7 @@
>>>>>
>>>>>  #include <stdint.h>
>>>>>  #include <stdbool.h>
>>>>> +#include "openvswitch/ofp-meter.h"
>>>>>
>>>>>  /* These functions are Linux specific, so they should be used
>>>>> directly only by
>>>>>   * Linux-specific code. */
>>>>> @@ -28,5 +29,10 @@ struct netdev;
>>>>>  int netdev_linux_ethtool_set_flag(struct netdev *netdev,
>>>>> uint32_t
>>>>> flag,
>>>>>                                    const char *flag_name, bool
>>>>> enable);
>>>>>  int linux_get_ifindex(const char *netdev_name);
>>>>> +int tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
>>>>> +                          uint32_t kbits_burst, uint32_t
>>>>> pkts_rate,
>>>>> +                          uint32_t pkts_burst, bool update);
>>>>> +int tc_del_policer_action(uint32_t index, struct
>>>>> ofputil_meter_stats *stats);
>>>>> +int tc_get_policer_action(uint32_t index, struct
>>>>> ofputil_meter_stats *stats);
>>>>>
>>>>>  #endif /* netdev-linux.h */
>>>>> diff --git a/lib/tc.c b/lib/tc.c
>>>>> index af7a7bc6d..ee16364ea 100644
>>>>> --- a/lib/tc.c
>>>>> +++ b/lib/tc.c
>>>>> @@ -199,6 +199,20 @@ tc_make_request(int ifindex, int type,
>>>>> unsigned int flags,
>>>>>      return tcmsg;
>>>>>  }
>>>>>
>>>>> +struct tcamsg *
>>>>> +tc_make_action_request(int type, unsigned int flags,
>>>>> +                       struct ofpbuf *request)
>>>>> +{
>>>>> +    struct tcamsg *tcamsg;
>>>>> +
>>>>> +    ofpbuf_init(request, 512);
>>>>> +    nl_msg_put_nlmsghdr(request, sizeof *tcamsg, type,
>>>>> NLM_F_REQUEST | flags);
>>>>> +    tcamsg = ofpbuf_put_zeros(request, sizeof *tcamsg);
>>>>> +    tcamsg->tca_family = AF_UNSPEC;
>>>>> +
>>>>> +    return tcamsg;
>>>>> +}
>>>>> +
>>>>>  static void request_from_tcf_id(struct tcf_id *id, uint16_t
>>>>> eth_type,
>>>>>                                  int type, unsigned int flags,
>>>>>                                  struct ofpbuf *request)
>>>>> @@ -1863,6 +1877,13 @@ nl_parse_single_action(struct nlattr
>>>>> *action, struct tc_flower *flower,
>>>>>      return 0;
>>>>>  }
>>>>>
>>>>> +int
>>>>> +tc_parse_single_action(struct nlattr *action, struct tc_flower
>>>>> *flower,
>>>>> +                       bool terse)
>>>>> +{
>>>>> +    return nl_parse_single_action(action, flower, terse);
>>>>> +}
>>>>> +
>>>>
>>>> Why don’t we move all the functions in lib/netdev-linux.c above
>>>> to
>>>> tc.c where they belong? I know it’s a bit more work, but it
>>>> avoids
>>>> exposing internals like this.
>>>>
>>>
>>> It's because tc_add_policer_action() calls tc_policer_init() and
>>> nl_msg_put_act_police(), which uses much of code in netdev-linux.c.
>>> And there also is a similar func tc_action_policer() in this file.
>>
>> I know it’s a mess right now, maybe you can do this as a follow-up
>> patch, i.e. moving all TC to tc.c and only call a single function
>> from this file to install the TC rule.
>
> It doesn't look like a small task. Can we do the refactoring later?


Sorry, I was not clear, that was what I suggested, we do this in a follow-up patch later.

>>
>>>>>  #define TCA_ACT_MIN_PRIO 1
>>>>>
>>>>>  static int
>>>>> diff --git a/lib/tc.h b/lib/tc.h
>>>>> index 201345672..96b9e6ccc 100644
>>>>> --- a/lib/tc.h
>>>>> +++ b/lib/tc.h
>>>>> @@ -80,6 +80,8 @@ tc_get_minor(unsigned int handle)
>>>>>
>>>>>  struct tcmsg *tc_make_request(int ifindex, int type,
>>>>>                                unsigned int flags, struct
>>>>> ofpbuf
>>>>> *);
>>>>> +struct tcamsg *tc_make_action_request(int type, unsigned int
>>>>> flags,
>>>>> +                                      struct ofpbuf *request);
>>>>>  int tc_transact(struct ofpbuf *request, struct ofpbuf
>>>>> **replyp);
>>>>>  int tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id,
>>>>>                       enum tc_qdisc_hook hook);
>>>>> @@ -365,6 +367,8 @@ struct tc_flower {
>>>>>      enum tc_offload_policy tc_policy;
>>>>>  };
>>>>>
>>>>> +struct nlattr;
>>>>
>>>> Rather than this, would it make sense to include “netlink.h” at
>>>> the
>>>> top? But this is not needed if we move the function to tc.c
>>>>
>>>>> +
>>>>>  int tc_replace_flower(struct tcf_id *id, struct tc_flower
>>>>> *flower);
>>>>>  int tc_del_filter(struct tcf_id *id);
>>>>>  int tc_get_flower(struct tcf_id *id, struct tc_flower
>>>>> *flower);
>>>>> @@ -376,5 +380,7 @@ int parse_netlink_to_tc_flower(struct
>>>>> ofpbuf
>>>>> *reply,
>>>>>                                 bool terse);
>>>>>  int parse_netlink_to_tc_chain(struct ofpbuf *reply, uint32_t
>>>>> *chain);
>>>>>  void tc_set_policy(const char *policy);
>>>>> +int tc_parse_single_action(struct nlattr *action, struct
>>>>> tc_flower
>>>>> *flower,
>>>>> +                           bool terse);
>>>>>
>>>>>  #endif /* tc.h */
>>>>> -- 
>>>>> 2.26.2
>>>>>
>>>>> _______________________________________________
>>>>> dev mailing list
>>>>> dev@openvswitch.org
>>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>>
>>
diff mbox series

Patch

diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index eb05153c0..ef6c7312f 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -5664,6 +5664,139 @@  tc_add_policer(struct netdev *netdev, uint32_t kbits_rate,
     return 0;
 }
 
+int
+tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
+                      uint32_t kbits_burst, uint32_t pkts_rate,
+                      uint32_t pkts_burst, bool update)
+{
+    struct tc_police tc_police;
+    struct ofpbuf request;
+    struct tcamsg *tcamsg;
+    size_t offset;
+    int flags;
+
+    tc_policer_init(&tc_police, kbits_rate, kbits_burst);
+    tc_police.index = index;
+
+    flags = (update ? NLM_F_REPLACE : NLM_F_EXCL) | NLM_F_CREATE;
+    tcamsg = tc_make_action_request(RTM_NEWACTION, flags, &request);
+    if (!tcamsg) {
+        return ENODEV;
+    }
+
+    offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
+    nl_msg_put_act_police(&request, &tc_police, pkts_rate, pkts_burst);
+    nl_msg_end_nested(&request, offset);
+
+    return tc_transact(&request, NULL);
+}
+
+static int
+tc_update_policer_action_stats(struct ofpbuf *msg,
+                               struct ofputil_meter_stats *stats)
+{
+    const struct nlattr *act = NULL;
+    struct tc_flower flower;
+    struct nlattr *prio;
+    struct tcamsg *tca;
+    int error;
+
+    if (NLMSG_HDRLEN + sizeof *tca > msg->size) {
+        return EPROTO;
+    }
+
+    tca = ofpbuf_at_assert(msg, NLMSG_HDRLEN, sizeof *tca);
+
+    act = nl_attr_find(msg, NLMSG_HDRLEN + sizeof *tca, TCA_ACT_TAB);
+    if (!act) {
+        return EPROTO;
+    }
+
+    prio = (struct nlattr *) act + 1;
+    memset(&flower, 0, sizeof(struct tc_flower));
+    error = tc_parse_single_action(prio, &flower, false);
+    if (!error) {
+        stats->packet_in_count +=
+            get_32aligned_u64(&flower.stats_sw.n_packets);
+        stats->byte_in_count += get_32aligned_u64(&flower.stats_sw.n_bytes);
+        stats->packet_in_count +=
+            get_32aligned_u64(&flower.stats_hw.n_packets);
+        stats->byte_in_count += get_32aligned_u64(&flower.stats_hw.n_bytes);
+    }
+
+    return error;
+}
+
+int
+tc_get_policer_action(uint32_t index, struct ofputil_meter_stats *stats)
+{
+    struct ofpbuf *replyp = NULL;
+    struct ofpbuf request;
+    struct tcamsg *tcamsg;
+    size_t root_offset;
+    size_t prio_offset;
+    int prio = 0;
+    int error;
+
+    tcamsg = tc_make_action_request(RTM_GETACTION, 0, &request);
+    if (!tcamsg) {
+        return ENODEV;
+    }
+
+    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
+    prio_offset = nl_msg_start_nested(&request, ++prio);
+    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
+    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
+    nl_msg_end_nested(&request, prio_offset);
+    nl_msg_end_nested(&request, root_offset);
+
+    error = tc_transact(&request, &replyp);
+    if (error) {
+        VLOG_ERR_RL(&rl, "failed to dump police action (index: %u), err=%d",
+                    index, error);
+        return error;
+    }
+
+    error = tc_update_policer_action_stats(replyp, stats);
+    if (error) {
+        VLOG_ERR_RL(&rl, "failed to update police stats (index: %u), err=%d",
+                    index, error);
+    }
+
+    return error;
+}
+
+int
+tc_del_policer_action(uint32_t index, struct ofputil_meter_stats *stats)
+{
+    struct ofpbuf *replyp = NULL;
+    struct ofpbuf request;
+    struct tcamsg *tcamsg;
+    size_t root_offset;
+    size_t prio_offset;
+    int prio = 0;
+    int error;
+
+    tcamsg = tc_make_action_request(RTM_DELACTION, NLM_F_ACK, &request);
+    if (!tcamsg) {
+        return ENODEV;
+    }
+
+    root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
+    prio_offset = nl_msg_start_nested(&request, ++prio);
+    nl_msg_put_string(&request, TCA_ACT_KIND, "police");
+    nl_msg_put_u32(&request, TCA_ACT_INDEX, index);
+    nl_msg_end_nested(&request, prio_offset);
+    nl_msg_end_nested(&request, root_offset);
+
+    error = tc_transact(&request, &replyp);
+    if (!error && stats) {
+        error = tc_update_policer_action_stats(replyp, stats);
+    }
+
+    return error;
+}
+
 static void
 read_psched(void)
 {
diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
index e1e30f806..9a416ce50 100644
--- a/lib/netdev-linux.h
+++ b/lib/netdev-linux.h
@@ -19,6 +19,7 @@ 
 
 #include <stdint.h>
 #include <stdbool.h>
+#include "openvswitch/ofp-meter.h"
 
 /* These functions are Linux specific, so they should be used directly only by
  * Linux-specific code. */
@@ -28,5 +29,10 @@  struct netdev;
 int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
                                   const char *flag_name, bool enable);
 int linux_get_ifindex(const char *netdev_name);
+int tc_add_policer_action(uint32_t index, uint32_t kbits_rate,
+                          uint32_t kbits_burst, uint32_t pkts_rate,
+                          uint32_t pkts_burst, bool update);
+int tc_del_policer_action(uint32_t index, struct ofputil_meter_stats *stats);
+int tc_get_policer_action(uint32_t index, struct ofputil_meter_stats *stats);
 
 #endif /* netdev-linux.h */
diff --git a/lib/tc.c b/lib/tc.c
index af7a7bc6d..ee16364ea 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -199,6 +199,20 @@  tc_make_request(int ifindex, int type, unsigned int flags,
     return tcmsg;
 }
 
+struct tcamsg *
+tc_make_action_request(int type, unsigned int flags,
+                       struct ofpbuf *request)
+{
+    struct tcamsg *tcamsg;
+
+    ofpbuf_init(request, 512);
+    nl_msg_put_nlmsghdr(request, sizeof *tcamsg, type, NLM_F_REQUEST | flags);
+    tcamsg = ofpbuf_put_zeros(request, sizeof *tcamsg);
+    tcamsg->tca_family = AF_UNSPEC;
+
+    return tcamsg;
+}
+
 static void request_from_tcf_id(struct tcf_id *id, uint16_t eth_type,
                                 int type, unsigned int flags,
                                 struct ofpbuf *request)
@@ -1863,6 +1877,13 @@  nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
     return 0;
 }
 
+int
+tc_parse_single_action(struct nlattr *action, struct tc_flower *flower,
+                       bool terse)
+{
+    return nl_parse_single_action(action, flower, terse);
+}
+
 #define TCA_ACT_MIN_PRIO 1
 
 static int
diff --git a/lib/tc.h b/lib/tc.h
index 201345672..96b9e6ccc 100644
--- a/lib/tc.h
+++ b/lib/tc.h
@@ -80,6 +80,8 @@  tc_get_minor(unsigned int handle)
 
 struct tcmsg *tc_make_request(int ifindex, int type,
                               unsigned int flags, struct ofpbuf *);
+struct tcamsg *tc_make_action_request(int type, unsigned int flags,
+                                      struct ofpbuf *request);
 int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp);
 int tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id,
                      enum tc_qdisc_hook hook);
@@ -365,6 +367,8 @@  struct tc_flower {
     enum tc_offload_policy tc_policy;
 };
 
+struct nlattr;
+
 int tc_replace_flower(struct tcf_id *id, struct tc_flower *flower);
 int tc_del_filter(struct tcf_id *id);
 int tc_get_flower(struct tcf_id *id, struct tc_flower *flower);
@@ -376,5 +380,7 @@  int parse_netlink_to_tc_flower(struct ofpbuf *reply,
                                bool terse);
 int parse_netlink_to_tc_chain(struct ofpbuf *reply, uint32_t *chain);
 void tc_set_policy(const char *policy);
+int tc_parse_single_action(struct nlattr *action, struct tc_flower *flower,
+                           bool terse);
 
 #endif /* tc.h */