Add helpers to add, delete and get stats of police action with the specified index.
Signed-off-by: Jianbo Liu <[email protected]> --- lib/netdev-linux.c | 142 +++++++++++++++++++++++++++++++++++++++++++++ lib/netdev-linux.h | 6 ++ lib/tc.c | 114 +++++++++++++++++++++++++----------- lib/tc.h | 7 +++ 4 files changed, 235 insertions(+), 34 deletions(-) diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 4956e2fd1..94422cbc8 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -5667,6 +5667,148 @@ 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; + int error; + + 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); + + error = tc_transact(&request, NULL); + if (error) { + VLOG_ERR_RL(&rl, "Failed to %s police action, err=%d", + update ? "update" : "add", error); + } + + return error; +} + +static int +tc_update_policer_action_stats(struct ofpbuf *msg, + struct ofputil_meter_stats *stats) +{ + struct ovs_flow_stats stats_dropped = {}; + struct ovs_flow_stats stats_hw = {}; + struct ovs_flow_stats stats_sw = {}; + const struct nlattr *act = NULL; + struct nlattr *prio; + struct tcamsg *tca; + int error; + + if (!stats) { + return 0; + } + + if (NLMSG_HDRLEN + sizeof *tca > msg->size) { + VLOG_ERR_RL(&rl, "Failed to get action stats, size error"); + 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) { + VLOG_ERR_RL(&rl, "Failed to get action stats, can't find attr"); + return EPROTO; + } + + prio = (struct nlattr *) act + 1; + error = tc_parse_action_stats(prio, &stats_sw, &stats_hw, &stats_dropped); + if (!error) { + stats->packet_in_count += + get_32aligned_u64(&stats_sw.n_packets); + stats->byte_in_count += get_32aligned_u64(&stats_sw.n_bytes); + stats->packet_in_count += + get_32aligned_u64(&stats_hw.n_packets); + stats->byte_in_count += get_32aligned_u64(&stats_hw.n_bytes); + stats->bands[0].packet_count += + get_32aligned_u64(&stats_dropped.n_packets); + } + + 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 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, 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", + index, error); + return error; + } + + return tc_update_policer_action_stats(replyp, stats); +} + +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 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, 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 delete police action (index: %u), err=%d", + index, error); + return error; + } + + return tc_update_policer_action_stats(replyp, stats); +} + 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 5d96567e0..77d0c9de7 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) @@ -1759,23 +1773,67 @@ static const struct nl_policy stats_policy[] = { [TCA_STATS_BASIC_HW] = { .type = NL_A_UNSPEC, .min_len = sizeof(struct gnet_stats_basic), .optional = true, }, + [TCA_STATS_QUEUE] = { .type = NL_A_UNSPEC, + .min_len = sizeof(struct gnet_stats_queue), + .optional = true, }, }; +static int +nl_parse_action_stats(struct nlattr *act_stats, + struct ovs_flow_stats *stats_sw, + struct ovs_flow_stats *stats_hw, + struct ovs_flow_stats *stats_dropped) +{ + struct gnet_stats_basic bs_sw = { .packets = 0, .bytes = 0, }; + struct nlattr *stats_attrs[ARRAY_SIZE(stats_policy)]; + const struct gnet_stats_basic *bs_all = NULL; + const struct gnet_stats_basic *bs_hw = NULL; + const struct gnet_stats_queue *qs; + + if (!nl_parse_nested(act_stats, stats_policy, stats_attrs, + ARRAY_SIZE(stats_policy))) { + VLOG_ERR_RL(&error_rl, "failed to parse action stats policy"); + return EPROTO; + } + + bs_all = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs_all); + if (stats_attrs[TCA_STATS_BASIC_HW]) { + bs_hw = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC_HW], + sizeof *bs_hw); + + bs_sw.packets = bs_all->packets - bs_hw->packets; + bs_sw.bytes = bs_all->bytes - bs_hw->bytes; + } else { + bs_sw.packets = bs_all->packets; + bs_sw.bytes = bs_all->bytes; + } + + if (bs_sw.packets > get_32aligned_u64(&stats_sw->n_packets)) { + put_32aligned_u64(&stats_sw->n_packets, bs_sw.packets); + put_32aligned_u64(&stats_sw->n_bytes, bs_sw.bytes); + } + + if (bs_hw && bs_hw->packets > get_32aligned_u64(&stats_hw->n_packets)) { + put_32aligned_u64(&stats_hw->n_packets, bs_hw->packets); + put_32aligned_u64(&stats_hw->n_bytes, bs_hw->bytes); + } + + if (stats_dropped && stats_attrs[TCA_STATS_QUEUE]) { + qs = nl_attr_get_unspec(stats_attrs[TCA_STATS_QUEUE], sizeof *qs); + put_32aligned_u64(&stats_dropped->n_packets, qs->drops); + } + + return 0; +} + static int nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, bool terse) { struct nlattr *act_options; - struct nlattr *act_stats; struct nlattr *act_cookie; const char *act_kind; struct nlattr *action_attrs[ARRAY_SIZE(act_policy)]; - struct nlattr *stats_attrs[ARRAY_SIZE(stats_policy)]; - struct ovs_flow_stats *stats_sw = &flower->stats_sw; - struct ovs_flow_stats *stats_hw = &flower->stats_hw; - const struct gnet_stats_basic *bs_all = NULL; - const struct gnet_stats_basic *bs_hw = NULL; - struct gnet_stats_basic bs_sw = { .packets = 0, .bytes = 0, }; int err = 0; if (!nl_parse_nested(action, act_policy, action_attrs, @@ -1825,37 +1883,25 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, flower->act_cookie.len = nl_attr_get_size(act_cookie); } - act_stats = action_attrs[TCA_ACT_STATS]; - - if (!nl_parse_nested(act_stats, stats_policy, stats_attrs, - ARRAY_SIZE(stats_policy))) { - VLOG_ERR_RL(&error_rl, "failed to parse action stats policy"); - return EPROTO; - } - - bs_all = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs_all); - if (stats_attrs[TCA_STATS_BASIC_HW]) { - bs_hw = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC_HW], - sizeof *bs_hw); - - bs_sw.packets = bs_all->packets - bs_hw->packets; - bs_sw.bytes = bs_all->bytes - bs_hw->bytes; - } else { - bs_sw.packets = bs_all->packets; - bs_sw.bytes = bs_all->bytes; - } + return nl_parse_action_stats(action_attrs[TCA_ACT_STATS], + &flower->stats_sw, &flower->stats_hw, NULL); +} - if (bs_sw.packets > get_32aligned_u64(&stats_sw->n_packets)) { - put_32aligned_u64(&stats_sw->n_packets, bs_sw.packets); - put_32aligned_u64(&stats_sw->n_bytes, bs_sw.bytes); - } +int +tc_parse_action_stats(struct nlattr *action, struct ovs_flow_stats *stats_sw, + struct ovs_flow_stats *stats_hw, + struct ovs_flow_stats *stats_dropped) +{ + struct nlattr *action_attrs[ARRAY_SIZE(act_policy)]; - if (bs_hw && bs_hw->packets > get_32aligned_u64(&stats_hw->n_packets)) { - put_32aligned_u64(&stats_hw->n_packets, bs_hw->packets); - put_32aligned_u64(&stats_hw->n_bytes, bs_hw->bytes); + if (!nl_parse_nested(action, act_policy, action_attrs, + ARRAY_SIZE(act_policy))) { + VLOG_ERR_RL(&error_rl, "failed to parse single action options"); + return EPROTO; } - return 0; + return nl_parse_action_stats(action_attrs[TCA_ACT_STATS], stats_sw, + stats_hw, stats_dropped); } #define TCA_ACT_MIN_PRIO 1 diff --git a/lib/tc.h b/lib/tc.h index 751bec891..30cd2f7ff 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -23,6 +23,7 @@ #include <linux/pkt_cls.h> #include <linux/pkt_sched.h> +#include "netlink.h" #include "netlink-socket.h" #include "odp-netlink.h" #include "openvswitch/ofpbuf.h" @@ -80,6 +81,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); @@ -375,5 +378,9 @@ 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_action_stats(struct nlattr *action, + struct ovs_flow_stats *stats_sw, + struct ovs_flow_stats *stats_hw, + struct ovs_flow_stats *stats_dropped); #endif /* tc.h */ -- 2.26.2 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
