@@ -5667,6 +5667,150 @@ 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 = {0};
+ struct ovs_flow_stats stats_hw = {0};
+ struct ovs_flow_stats stats_sw = {0};
+ 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);
+ if (stats->n_bands >= 1) {
+ 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)
{
@@ -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 */
@@ -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)
@@ -1760,21 +1774,69 @@ 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 nlattr *stats_attrs[ARRAY_SIZE(stats_policy)];
+ struct gnet_stats_basic bs_all, bs_sw, bs_hw;
+ 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;
+ }
+
+ memcpy(&bs_all,
+ nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof bs_all),
+ sizeof bs_all);
+ if (stats_attrs[TCA_STATS_BASIC_HW]) {
+ memcpy(&bs_hw, nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC_HW],
+ sizeof bs_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 (stats_attrs[TCA_STATS_BASIC_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;
- struct gnet_stats_basic bs_all, bs_hw, bs_sw;
int err = 0;
if (!nl_parse_nested(action, act_policy, action_attrs,
@@ -1824,41 +1886,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;
- }
-
- memcpy(&bs_all,
- nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof bs_all),
- sizeof bs_all);
- if (stats_attrs[TCA_STATS_BASIC_HW]) {
- memcpy(&bs_hw, nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC_HW],
- sizeof bs_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 (stats_attrs[TCA_STATS_BASIC_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
@@ -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 */
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 | 144 +++++++++++++++++++++++++++++++++++++++++++++ lib/netdev-linux.h | 6 ++ lib/tc.c | 118 +++++++++++++++++++++++++------------ lib/tc.h | 7 +++ 4 files changed, 239 insertions(+), 36 deletions(-)