@@ -5654,6 +5654,135 @@ 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.n_packets);
+ stats->byte_in_count += get_32aligned_u64(&flower.stats.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)
{
@@ -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)
@@ -1835,6 +1849,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
@@ -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);
@@ -357,6 +359,8 @@ struct tc_flower {
enum tc_offload_policy tc_policy;
};
+struct nlattr;
+
/* assert that if we overflow with a masked write of uint32_t to the last byte
* of flower.rewrite we overflow inside struct flower.
* shouldn't happen unless someone moves rewrite to the end of flower */
@@ -375,5 +379,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 */