pipeline: add traffic metering action
authorJasvinder Singh <jasvinder.singh@intel.com>
Thu, 29 Mar 2018 18:31:22 +0000 (19:31 +0100)
committerCristian Dumitrescu <cristian.dumitrescu@intel.com>
Wed, 4 Apr 2018 10:21:23 +0000 (12:21 +0200)
Add traffic metering action implementation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
lib/librte_pipeline/Makefile
lib/librte_pipeline/meson.build
lib/librte_pipeline/rte_pipeline_version.map
lib/librte_pipeline/rte_table_action.c
lib/librte_pipeline/rte_table_action.h

index e8c43c7..72e4c7c 100644 (file)
@@ -8,10 +8,11 @@ include $(RTE_SDK)/mk/rte.vars.mk
 #
 LIB = librte_pipeline.a
 
+CFLAGS += -DALLOW_EXPERIMENTAL_API
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS)
 LDLIBS += -lrte_eal -lrte_mempool -lrte_mbuf -lrte_table
-LDLIBS += -lrte_port
+LDLIBS += -lrte_port -lrte_meter
 
 EXPORT_MAP := rte_pipeline_version.map
 
index 4dda560..71da295 100644 (file)
@@ -5,4 +5,4 @@ version = 3
 allow_experimental_apis = true
 sources = files('rte_pipeline.c', 'rte_table_action.c')
 headers = files('rte_pipeline.h', 'rte_table_action.h')
-deps += ['port', 'table']
+deps += ['port', 'table', 'meter']
index 13337de..c7106dc 100644 (file)
@@ -51,7 +51,11 @@ EXPERIMENTAL {
 
        rte_table_action_apply;
        rte_table_action_create;
+       rte_table_action_dscp_table_update;
        rte_table_action_free;
+       rte_table_action_meter_profile_add;
+       rte_table_action_meter_profile_delete;
+       rte_table_action_meter_read;
        rte_table_action_profile_action_register;
        rte_table_action_profile_create;
        rte_table_action_profile_free;
index 09f26f5..f3be28a 100644 (file)
@@ -5,13 +5,21 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <rte_malloc.h>
-
 #include <rte_common.h>
+#include <rte_byteorder.h>
+#include <rte_cycles.h>
 #include <rte_malloc.h>
+#include <rte_ip.h>
+
 
 #include "rte_table_action.h"
 
+#define rte_htons rte_cpu_to_be_16
+#define rte_htonl rte_cpu_to_be_32
+
+#define rte_ntohs rte_be_to_cpu_16
+#define rte_ntohl rte_be_to_cpu_32
+
 /**
  * RTE_TABLE_ACTION_FWD
  */
@@ -32,6 +40,264 @@ fwd_apply(struct fwd_data *data,
        return 0;
 }
 
+/**
+ * RTE_TABLE_ACTION_MTR
+ */
+static int
+mtr_cfg_check(struct rte_table_action_mtr_config *mtr)
+{
+       if ((mtr->alg == RTE_TABLE_ACTION_METER_SRTCM) ||
+               ((mtr->n_tc != 1) && (mtr->n_tc != 4)) ||
+               (mtr->n_bytes_enabled != 0))
+               return -ENOTSUP;
+       return 0;
+}
+
+#define MBUF_SCHED_QUEUE_TC_COLOR(queue, tc, color)        \
+       ((uint16_t)((((uint64_t)(queue)) & 0x3) |          \
+       ((((uint64_t)(tc)) & 0x3) << 2) |                  \
+       ((((uint64_t)(color)) & 0x3) << 4)))
+
+#define MBUF_SCHED_COLOR(sched, color)                     \
+       (((sched) & (~0x30LLU)) | ((color) << 4))
+
+struct mtr_trtcm_data {
+       struct rte_meter_trtcm trtcm;
+       uint64_t stats[e_RTE_METER_COLORS];
+} __attribute__((__packed__));
+
+#define MTR_TRTCM_DATA_METER_PROFILE_ID_GET(data)          \
+       (((data)->stats[e_RTE_METER_GREEN] & 0xF8LLU) >> 3)
+
+static void
+mtr_trtcm_data_meter_profile_id_set(struct mtr_trtcm_data *data,
+       uint32_t profile_id)
+{
+       data->stats[e_RTE_METER_GREEN] &= ~0xF8LLU;
+       data->stats[e_RTE_METER_GREEN] |= (profile_id % 32) << 3;
+}
+
+#define MTR_TRTCM_DATA_POLICER_ACTION_DROP_GET(data, color)\
+       (((data)->stats[(color)] & 4LLU) >> 2)
+
+#define MTR_TRTCM_DATA_POLICER_ACTION_COLOR_GET(data, color)\
+       ((enum rte_meter_color)((data)->stats[(color)] & 3LLU))
+
+static void
+mtr_trtcm_data_policer_action_set(struct mtr_trtcm_data *data,
+       enum rte_meter_color color,
+       enum rte_table_action_policer action)
+{
+       if (action == RTE_TABLE_ACTION_POLICER_DROP) {
+               data->stats[color] |= 4LLU;
+       } else {
+               data->stats[color] &= ~7LLU;
+               data->stats[color] |= color & 3LLU;
+       }
+}
+
+static uint64_t
+mtr_trtcm_data_stats_get(struct mtr_trtcm_data *data,
+       enum rte_meter_color color)
+{
+       return data->stats[color] >> 8;
+}
+
+static void
+mtr_trtcm_data_stats_reset(struct mtr_trtcm_data *data,
+       enum rte_meter_color color)
+{
+       data->stats[color] &= 0xFFLU;
+}
+
+#define MTR_TRTCM_DATA_STATS_INC(data, color)              \
+       ((data)->stats[(color)] += (1LLU << 8))
+
+static size_t
+mtr_data_size(struct rte_table_action_mtr_config *mtr)
+{
+       return mtr->n_tc * sizeof(struct mtr_trtcm_data);
+}
+
+struct dscp_table_entry_data {
+       enum rte_meter_color color;
+       uint16_t tc;
+       uint16_t queue_tc_color;
+};
+
+struct dscp_table_data {
+       struct dscp_table_entry_data entry[64];
+};
+
+struct meter_profile_data {
+       struct rte_meter_trtcm_profile profile;
+       uint32_t profile_id;
+       int valid;
+};
+
+static struct meter_profile_data *
+meter_profile_data_find(struct meter_profile_data *mp,
+       uint32_t mp_size,
+       uint32_t profile_id)
+{
+       uint32_t i;
+
+       for (i = 0; i < mp_size; i++) {
+               struct meter_profile_data *mp_data = &mp[i];
+
+               if (mp_data->valid && (mp_data->profile_id == profile_id))
+                       return mp_data;
+       }
+
+       return NULL;
+}
+
+static struct meter_profile_data *
+meter_profile_data_find_unused(struct meter_profile_data *mp,
+       uint32_t mp_size)
+{
+       uint32_t i;
+
+       for (i = 0; i < mp_size; i++) {
+               struct meter_profile_data *mp_data = &mp[i];
+
+               if (!mp_data->valid)
+                       return mp_data;
+       }
+
+       return NULL;
+}
+
+static int
+mtr_apply_check(struct rte_table_action_mtr_params *p,
+       struct rte_table_action_mtr_config *cfg,
+       struct meter_profile_data *mp,
+       uint32_t mp_size)
+{
+       uint32_t i;
+
+       if (p->tc_mask > RTE_LEN2MASK(cfg->n_tc, uint32_t))
+               return -EINVAL;
+
+       for (i = 0; i < RTE_TABLE_ACTION_TC_MAX; i++) {
+               struct rte_table_action_mtr_tc_params *p_tc = &p->mtr[i];
+               struct meter_profile_data *mp_data;
+
+               if ((p->tc_mask & (1LLU << i)) == 0)
+                       continue;
+
+               mp_data = meter_profile_data_find(mp,
+                       mp_size,
+                       p_tc->meter_profile_id);
+               if (!mp_data)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+mtr_apply(struct mtr_trtcm_data *data,
+       struct rte_table_action_mtr_params *p,
+       struct rte_table_action_mtr_config *cfg,
+       struct meter_profile_data *mp,
+       uint32_t mp_size)
+{
+       uint32_t i;
+       int status;
+
+       /* Check input arguments */
+       status = mtr_apply_check(p, cfg, mp, mp_size);
+       if (status)
+               return status;
+
+       /* Apply */
+       for (i = 0; i < RTE_TABLE_ACTION_TC_MAX; i++) {
+               struct rte_table_action_mtr_tc_params *p_tc = &p->mtr[i];
+               struct mtr_trtcm_data *data_tc = &data[i];
+               struct meter_profile_data *mp_data;
+
+               if ((p->tc_mask & (1LLU << i)) == 0)
+                       continue;
+
+               /* Find profile */
+               mp_data = meter_profile_data_find(mp,
+                       mp_size,
+                       p_tc->meter_profile_id);
+               if (!mp_data)
+                       return -EINVAL;
+
+               memset(data_tc, 0, sizeof(*data_tc));
+
+               /* Meter object */
+               status = rte_meter_trtcm_config(&data_tc->trtcm,
+                       &mp_data->profile);
+               if (status)
+                       return status;
+
+               /* Meter profile */
+               mtr_trtcm_data_meter_profile_id_set(data_tc,
+                       mp_data - mp);
+
+               /* Policer actions */
+               mtr_trtcm_data_policer_action_set(data_tc,
+                       e_RTE_METER_GREEN,
+                       p_tc->policer[e_RTE_METER_GREEN]);
+
+               mtr_trtcm_data_policer_action_set(data_tc,
+                       e_RTE_METER_YELLOW,
+                       p_tc->policer[e_RTE_METER_YELLOW]);
+
+               mtr_trtcm_data_policer_action_set(data_tc,
+                       e_RTE_METER_RED,
+                       p_tc->policer[e_RTE_METER_RED]);
+       }
+
+       return 0;
+}
+
+static __rte_always_inline uint64_t
+pkt_work_mtr(struct rte_mbuf *mbuf,
+       struct mtr_trtcm_data *data,
+       struct dscp_table_data *dscp_table,
+       struct meter_profile_data *mp,
+       uint64_t time,
+       uint32_t dscp,
+       uint16_t total_length)
+{
+       uint64_t drop_mask, sched;
+       uint64_t *sched_ptr = (uint64_t *) &mbuf->hash.sched;
+       struct dscp_table_entry_data *dscp_entry = &dscp_table->entry[dscp];
+       enum rte_meter_color color_in, color_meter, color_policer;
+       uint32_t tc, mp_id;
+
+       tc = dscp_entry->tc;
+       color_in = dscp_entry->color;
+       data += tc;
+       mp_id = MTR_TRTCM_DATA_METER_PROFILE_ID_GET(data);
+       sched = *sched_ptr;
+
+       /* Meter */
+       color_meter = rte_meter_trtcm_color_aware_check(
+               &data->trtcm,
+               &mp[mp_id].profile,
+               time,
+               total_length,
+               color_in);
+
+       /* Stats */
+       MTR_TRTCM_DATA_STATS_INC(data, color_meter);
+
+       /* Police */
+       drop_mask = MTR_TRTCM_DATA_POLICER_ACTION_DROP_GET(data, color_meter);
+       color_policer =
+               MTR_TRTCM_DATA_POLICER_ACTION_COLOR_GET(data, color_meter);
+       *sched_ptr = MBUF_SCHED_COLOR(sched, color_policer);
+
+       return drop_mask;
+}
+
+
 /**
  * Action profile
  */
@@ -40,6 +306,7 @@ action_valid(enum rte_table_action_type action)
 {
        switch (action) {
        case RTE_TABLE_ACTION_FWD:
+       case RTE_TABLE_ACTION_MTR:
                return 1;
        default:
                return 0;
@@ -52,22 +319,28 @@ action_valid(enum rte_table_action_type action)
 struct ap_config {
        uint64_t action_mask;
        struct rte_table_action_common_config common;
+       struct rte_table_action_mtr_config mtr;
 };
 
 static size_t
 action_cfg_size(enum rte_table_action_type action)
 {
        switch (action) {
+       case RTE_TABLE_ACTION_MTR:
+               return sizeof(struct rte_table_action_mtr_config);
        default:
                return 0;
        }
 }
 
 static void*
-action_cfg_get(struct ap_config *ap_config __rte_unused,
+action_cfg_get(struct ap_config *ap_config,
        enum rte_table_action_type type)
 {
        switch (type) {
+       case RTE_TABLE_ACTION_MTR:
+               return &ap_config->mtr;
+
        default:
                return NULL;
        }
@@ -93,12 +366,15 @@ struct ap_data {
 
 static size_t
 action_data_size(enum rte_table_action_type action,
-       struct ap_config *ap_config __rte_unused)
+       struct ap_config *ap_config)
 {
        switch (action) {
        case RTE_TABLE_ACTION_FWD:
                return sizeof(struct fwd_data);
 
+       case RTE_TABLE_ACTION_MTR:
+               return mtr_data_size(&ap_config->mtr);
+
        default:
                return 0;
        }
@@ -158,6 +434,8 @@ rte_table_action_profile_action_register(struct rte_table_action_profile *profil
        enum rte_table_action_type type,
        void *action_config)
 {
+       int status;
+
        /* Check input arguments */
        if ((profile == NULL) ||
                profile->frozen ||
@@ -167,6 +445,19 @@ rte_table_action_profile_action_register(struct rte_table_action_profile *profil
                (action_cfg_size(type) && (action_config == NULL)))
                return -EINVAL;
 
+       switch (type) {
+       case RTE_TABLE_ACTION_MTR:
+               status = mtr_cfg_check(action_config);
+               break;
+
+       default:
+               status = 0;
+               break;
+       }
+
+       if (status)
+               return status;
+
        /* Action enable */
        action_cfg_set(&profile->cfg, type, action_config);
 
@@ -196,9 +487,16 @@ rte_table_action_profile_free(struct rte_table_action_profile *profile)
        return 0;
 }
 
+/**
+ * Action
+ */
+#define METER_PROFILES_MAX                                 32
+
 struct rte_table_action {
        struct ap_config cfg;
        struct ap_data data;
+       struct dscp_table_data dscp_table;
+       struct meter_profile_data mp[METER_PROFILES_MAX];
 };
 
 struct rte_table_action *
@@ -262,31 +560,338 @@ rte_table_action_apply(struct rte_table_action *action,
                return fwd_apply(action_data,
                        action_params);
 
+       case RTE_TABLE_ACTION_MTR:
+               return mtr_apply(action_data,
+                       action_params,
+                       &action->cfg.mtr,
+                       action->mp,
+                       RTE_DIM(action->mp));
+
        default:
                return -EINVAL;
        }
 }
 
-static __rte_always_inline uint64_t
-pkt_work(struct rte_mbuf *mbuf __rte_unused,
-       struct rte_pipeline_table_entry *table_entry __rte_unused,
-       uint64_t time __rte_unused,
-       struct rte_table_action *action __rte_unused,
-       struct ap_config *cfg __rte_unused)
+int
+rte_table_action_dscp_table_update(struct rte_table_action *action,
+       uint64_t dscp_mask,
+       struct rte_table_action_dscp_table *table)
 {
+       uint32_t i;
+
+       /* Check input arguments */
+       if ((action == NULL) ||
+               (action->cfg.action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) ||
+               (dscp_mask == 0) ||
+               (table == NULL))
+               return -EINVAL;
+
+       for (i = 0; i < RTE_DIM(table->entry); i++) {
+               struct dscp_table_entry_data *data =
+                       &action->dscp_table.entry[i];
+               struct rte_table_action_dscp_table_entry *entry =
+                       &table->entry[i];
+               uint16_t queue_tc_color =
+                       MBUF_SCHED_QUEUE_TC_COLOR(entry->tc_queue_id,
+                               entry->tc_id,
+                               entry->color);
+
+               if ((dscp_mask & (1LLU << i)) == 0)
+                       continue;
+
+               data->color = entry->color;
+               data->tc = entry->tc_id;
+               data->queue_tc_color = queue_tc_color;
+       }
+
        return 0;
 }
 
-static __rte_always_inline uint64_t
-pkt4_work(struct rte_mbuf **mbufs __rte_unused,
-       struct rte_pipeline_table_entry **table_entries __rte_unused,
-       uint64_t time __rte_unused,
-       struct rte_table_action *action __rte_unused,
-       struct ap_config *cfg __rte_unused)
+int
+rte_table_action_meter_profile_add(struct rte_table_action *action,
+       uint32_t meter_profile_id,
+       struct rte_table_action_meter_profile *profile)
 {
+       struct meter_profile_data *mp_data;
+       uint32_t status;
+
+       /* Check input arguments */
+       if ((action == NULL) ||
+               ((action->cfg.action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) == 0) ||
+               (profile == NULL))
+               return -EINVAL;
+
+       if (profile->alg != RTE_TABLE_ACTION_METER_TRTCM)
+               return -ENOTSUP;
+
+       mp_data = meter_profile_data_find(action->mp,
+               RTE_DIM(action->mp),
+               meter_profile_id);
+       if (mp_data)
+               return -EEXIST;
+
+       mp_data = meter_profile_data_find_unused(action->mp,
+               RTE_DIM(action->mp));
+       if (!mp_data)
+               return -ENOSPC;
+
+       /* Install new profile */
+       status = rte_meter_trtcm_profile_config(&mp_data->profile,
+               &profile->trtcm);
+       if (status)
+               return status;
+
+       mp_data->profile_id = meter_profile_id;
+       mp_data->valid = 1;
+
        return 0;
 }
 
+int
+rte_table_action_meter_profile_delete(struct rte_table_action *action,
+       uint32_t meter_profile_id)
+{
+       struct meter_profile_data *mp_data;
+
+       /* Check input arguments */
+       if ((action == NULL) ||
+               ((action->cfg.action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) == 0))
+               return -EINVAL;
+
+       mp_data = meter_profile_data_find(action->mp,
+               RTE_DIM(action->mp),
+               meter_profile_id);
+       if (!mp_data)
+               return 0;
+
+       /* Uninstall profile */
+       mp_data->valid = 0;
+
+       return 0;
+}
+
+int
+rte_table_action_meter_read(struct rte_table_action *action,
+       void *data,
+       uint32_t tc_mask,
+       struct rte_table_action_mtr_counters *stats,
+       int clear)
+{
+       struct mtr_trtcm_data *mtr_data;
+       uint32_t i;
+
+       /* Check input arguments */
+       if ((action == NULL) ||
+               ((action->cfg.action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) == 0) ||
+               (data == NULL) ||
+               (tc_mask > RTE_LEN2MASK(action->cfg.mtr.n_tc, uint32_t)))
+               return -EINVAL;
+
+       mtr_data = action_data_get(data, action, RTE_TABLE_ACTION_MTR);
+
+       /* Read */
+       if (stats) {
+               for (i = 0; i < RTE_TABLE_ACTION_TC_MAX; i++) {
+                       struct rte_table_action_mtr_counters_tc *dst =
+                               &stats->stats[i];
+                       struct mtr_trtcm_data *src = &mtr_data[i];
+
+                       if ((tc_mask & (1 << i)) == 0)
+                               continue;
+
+                       dst->n_packets[e_RTE_METER_GREEN] =
+                               mtr_trtcm_data_stats_get(src, e_RTE_METER_GREEN);
+
+                       dst->n_packets[e_RTE_METER_YELLOW] =
+                               mtr_trtcm_data_stats_get(src, e_RTE_METER_YELLOW);
+
+                       dst->n_packets[e_RTE_METER_RED] =
+                               mtr_trtcm_data_stats_get(src, e_RTE_METER_RED);
+
+                       dst->n_packets_valid = 1;
+                       dst->n_bytes_valid = 0;
+               }
+
+               stats->tc_mask = tc_mask;
+       }
+
+       /* Clear */
+       if (clear)
+               for (i = 0; i < RTE_TABLE_ACTION_TC_MAX; i++) {
+                       struct mtr_trtcm_data *src = &mtr_data[i];
+
+                       if ((tc_mask & (1 << i)) == 0)
+                               continue;
+
+                       mtr_trtcm_data_stats_reset(src, e_RTE_METER_GREEN);
+                       mtr_trtcm_data_stats_reset(src, e_RTE_METER_YELLOW);
+                       mtr_trtcm_data_stats_reset(src, e_RTE_METER_RED);
+               }
+
+
+       return 0;
+}
+
+static __rte_always_inline uint64_t
+pkt_work(struct rte_mbuf *mbuf,
+       struct rte_pipeline_table_entry *table_entry,
+       uint64_t time,
+       struct rte_table_action *action,
+       struct ap_config *cfg)
+{
+       uint64_t drop_mask = 0;
+
+       uint32_t ip_offset = action->cfg.common.ip_offset;
+       void *ip = RTE_MBUF_METADATA_UINT32_PTR(mbuf, ip_offset);
+
+       uint32_t dscp;
+       uint16_t total_length;
+
+       if (cfg->common.ip_version) {
+               struct ipv4_hdr *hdr = ip;
+
+               dscp = hdr->type_of_service >> 2;
+               total_length = rte_ntohs(hdr->total_length);
+       } else {
+               struct ipv6_hdr *hdr = ip;
+
+               dscp = (rte_ntohl(hdr->vtc_flow) & 0x0F600000) >> 18;
+               total_length =
+                       rte_ntohs(hdr->payload_len) + sizeof(struct ipv6_hdr);
+       }
+
+       if (cfg->action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) {
+               void *data =
+                       action_data_get(table_entry, action, RTE_TABLE_ACTION_MTR);
+
+               drop_mask |= pkt_work_mtr(mbuf,
+                       data,
+                       &action->dscp_table,
+                       action->mp,
+                       time,
+                       dscp,
+                       total_length);
+       }
+
+       return drop_mask;
+}
+
+static __rte_always_inline uint64_t
+pkt4_work(struct rte_mbuf **mbufs,
+       struct rte_pipeline_table_entry **table_entries,
+       uint64_t time,
+       struct rte_table_action *action,
+       struct ap_config *cfg)
+{
+       uint64_t drop_mask0 = 0;
+       uint64_t drop_mask1 = 0;
+       uint64_t drop_mask2 = 0;
+       uint64_t drop_mask3 = 0;
+
+       struct rte_mbuf *mbuf0 = mbufs[0];
+       struct rte_mbuf *mbuf1 = mbufs[1];
+       struct rte_mbuf *mbuf2 = mbufs[2];
+       struct rte_mbuf *mbuf3 = mbufs[3];
+
+       struct rte_pipeline_table_entry *table_entry0 = table_entries[0];
+       struct rte_pipeline_table_entry *table_entry1 = table_entries[1];
+       struct rte_pipeline_table_entry *table_entry2 = table_entries[2];
+       struct rte_pipeline_table_entry *table_entry3 = table_entries[3];
+
+       uint32_t ip_offset = action->cfg.common.ip_offset;
+       void *ip0 = RTE_MBUF_METADATA_UINT32_PTR(mbuf0, ip_offset);
+       void *ip1 = RTE_MBUF_METADATA_UINT32_PTR(mbuf1, ip_offset);
+       void *ip2 = RTE_MBUF_METADATA_UINT32_PTR(mbuf2, ip_offset);
+       void *ip3 = RTE_MBUF_METADATA_UINT32_PTR(mbuf3, ip_offset);
+
+       uint32_t dscp0, dscp1, dscp2, dscp3;
+       uint16_t total_length0, total_length1, total_length2, total_length3;
+
+       if (cfg->common.ip_version) {
+               struct ipv4_hdr *hdr0 = ip0;
+               struct ipv4_hdr *hdr1 = ip1;
+               struct ipv4_hdr *hdr2 = ip2;
+               struct ipv4_hdr *hdr3 = ip3;
+
+               dscp0 = hdr0->type_of_service >> 2;
+               dscp1 = hdr1->type_of_service >> 2;
+               dscp2 = hdr2->type_of_service >> 2;
+               dscp3 = hdr3->type_of_service >> 2;
+
+               total_length0 = rte_ntohs(hdr0->total_length);
+               total_length1 = rte_ntohs(hdr1->total_length);
+               total_length2 = rte_ntohs(hdr2->total_length);
+               total_length3 = rte_ntohs(hdr3->total_length);
+       } else {
+               struct ipv6_hdr *hdr0 = ip0;
+               struct ipv6_hdr *hdr1 = ip1;
+               struct ipv6_hdr *hdr2 = ip2;
+               struct ipv6_hdr *hdr3 = ip3;
+
+               dscp0 = (rte_ntohl(hdr0->vtc_flow) & 0x0F600000) >> 18;
+               dscp1 = (rte_ntohl(hdr1->vtc_flow) & 0x0F600000) >> 18;
+               dscp2 = (rte_ntohl(hdr2->vtc_flow) & 0x0F600000) >> 18;
+               dscp3 = (rte_ntohl(hdr3->vtc_flow) & 0x0F600000) >> 18;
+
+               total_length0 =
+                       rte_ntohs(hdr0->payload_len) + sizeof(struct ipv6_hdr);
+               total_length1 =
+                       rte_ntohs(hdr1->payload_len) + sizeof(struct ipv6_hdr);
+               total_length2 =
+                       rte_ntohs(hdr2->payload_len) + sizeof(struct ipv6_hdr);
+               total_length3 =
+                       rte_ntohs(hdr3->payload_len) + sizeof(struct ipv6_hdr);
+       }
+
+       if (cfg->action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) {
+               void *data0 =
+                       action_data_get(table_entry0, action, RTE_TABLE_ACTION_MTR);
+               void *data1 =
+                       action_data_get(table_entry1, action, RTE_TABLE_ACTION_MTR);
+               void *data2 =
+                       action_data_get(table_entry2, action, RTE_TABLE_ACTION_MTR);
+               void *data3 =
+                       action_data_get(table_entry3, action, RTE_TABLE_ACTION_MTR);
+
+               drop_mask0 |= pkt_work_mtr(mbuf0,
+                       data0,
+                       &action->dscp_table,
+                       action->mp,
+                       time,
+                       dscp0,
+                       total_length0);
+
+               drop_mask1 |= pkt_work_mtr(mbuf1,
+                       data1,
+                       &action->dscp_table,
+                       action->mp,
+                       time,
+                       dscp1,
+                       total_length1);
+
+               drop_mask2 |= pkt_work_mtr(mbuf2,
+                       data2,
+                       &action->dscp_table,
+                       action->mp,
+                       time,
+                       dscp2,
+                       total_length2);
+
+               drop_mask3 |= pkt_work_mtr(mbuf3,
+                       data3,
+                       &action->dscp_table,
+                       action->mp,
+                       time,
+                       dscp3,
+                       total_length3);
+       }
+
+       return drop_mask0 |
+               (drop_mask1 << 1) |
+               (drop_mask2 << 2) |
+               (drop_mask3 << 3);
+}
+
 static __rte_always_inline int
 ah(struct rte_pipeline *p,
        struct rte_mbuf **pkts,
@@ -298,6 +903,9 @@ ah(struct rte_pipeline *p,
        uint64_t pkts_drop_mask = 0;
        uint64_t time = 0;
 
+       if (cfg->action_mask & (1LLU << RTE_TABLE_ACTION_MTR))
+               time = rte_rdtsc();
+
        if ((pkts_mask & (pkts_mask + 1)) == 0) {
                uint64_t n_pkts = __builtin_popcountll(pkts_mask);
                uint32_t i;
index 03b77ca..c2f4a55 100644 (file)
@@ -59,6 +59,7 @@ extern "C" {
 #include <stdint.h>
 
 #include <rte_compat.h>
+#include <rte_meter.h>
 
 #include "rte_pipeline.h"
 
@@ -66,6 +67,9 @@ extern "C" {
 enum rte_table_action_type {
        /** Forward to next pipeline table, output port or drop. */
        RTE_TABLE_ACTION_FWD = 0,
+
+       /**  Traffic Metering and Policing. */
+       RTE_TABLE_ACTION_MTR,
 };
 
 /** Common action configuration (per table action profile). */
@@ -93,6 +97,164 @@ struct rte_table_action_fwd_params {
        uint32_t id;
 };
 
+/**
+ * RTE_TABLE_ACTION_MTR
+ */
+/** Max number of traffic classes (TCs). */
+#define RTE_TABLE_ACTION_TC_MAX                                  4
+
+/** Max number of queues per traffic class. */
+#define RTE_TABLE_ACTION_TC_QUEUE_MAX                            4
+
+/** Differentiated Services Code Point (DSCP) translation table entry. */
+struct rte_table_action_dscp_table_entry {
+       /** Traffic class. Used by the meter or the traffic management actions.
+        * Has to be strictly smaller than *RTE_TABLE_ACTION_TC_MAX*. Traffic
+        * class 0 is the highest priority.
+        */
+       uint32_t tc_id;
+
+       /** Traffic class queue. Used by the traffic management action. Has to
+        * be strictly smaller than *RTE_TABLE_ACTION_TC_QUEUE_MAX*.
+        */
+       uint32_t tc_queue_id;
+
+       /** Packet color. Used by the meter action as the packet input color
+        * for the color aware mode of the traffic metering algorithm.
+        */
+       enum rte_meter_color color;
+};
+
+/** DSCP translation table. */
+struct rte_table_action_dscp_table {
+       /** Array of DSCP table entries */
+       struct rte_table_action_dscp_table_entry entry[64];
+};
+
+/** Supported traffic metering algorithms. */
+enum rte_table_action_meter_algorithm {
+       /** Single Rate Three Color Marker (srTCM) - IETF RFC 2697. */
+       RTE_TABLE_ACTION_METER_SRTCM,
+
+       /** Two Rate Three Color Marker (trTCM) - IETF RFC 2698. */
+       RTE_TABLE_ACTION_METER_TRTCM,
+};
+
+/** Traffic metering profile (configuration template). */
+struct rte_table_action_meter_profile {
+       /** Traffic metering algorithm. */
+       enum rte_table_action_meter_algorithm alg;
+
+       RTE_STD_C11
+       union {
+               /** Only valid when *alg* is set to srTCM - IETF RFC 2697. */
+               struct rte_meter_srtcm_params srtcm;
+
+               /** Only valid when *alg* is set to trTCM - IETF RFC 2698. */
+               struct rte_meter_trtcm_params trtcm;
+       };
+};
+
+/** Policer actions. */
+enum rte_table_action_policer {
+       /** Recolor the packet as green. */
+       RTE_TABLE_ACTION_POLICER_COLOR_GREEN = 0,
+
+       /** Recolor the packet as yellow. */
+       RTE_TABLE_ACTION_POLICER_COLOR_YELLOW,
+
+       /** Recolor the packet as red. */
+       RTE_TABLE_ACTION_POLICER_COLOR_RED,
+
+       /** Drop the packet. */
+       RTE_TABLE_ACTION_POLICER_DROP,
+
+       /** Number of policer actions. */
+       RTE_TABLE_ACTION_POLICER_MAX
+};
+
+/** Meter action configuration per traffic class. */
+struct rte_table_action_mtr_tc_params {
+       /** Meter profile ID. */
+       uint32_t meter_profile_id;
+
+       /** Policer actions. */
+       enum rte_table_action_policer policer[e_RTE_METER_COLORS];
+};
+
+/** Meter action statistics counters per traffic class. */
+struct rte_table_action_mtr_counters_tc {
+       /** Number of packets per color at the output of the traffic metering
+        * and before the policer actions are executed. Only valid when
+        * *n_packets_valid* is non-zero.
+        */
+       uint64_t n_packets[e_RTE_METER_COLORS];
+
+       /** Number of packet bytes per color at the output of the traffic
+        * metering and before the policer actions are executed. Only valid when
+        * *n_bytes_valid* is non-zero.
+        */
+       uint64_t n_bytes[e_RTE_METER_COLORS];
+
+       /** When non-zero, the *n_packets* field is valid. */
+       int n_packets_valid;
+
+       /** When non-zero, the *n_bytes* field is valid. */
+       int n_bytes_valid;
+};
+
+/** Meter action configuration (per table action profile). */
+struct rte_table_action_mtr_config {
+       /** Meter algorithm. */
+       enum rte_table_action_meter_algorithm alg;
+
+       /** Number of traffic classes. Each traffic class has its own traffic
+        * meter and policer instances. Needs to be equal to either 1 or to
+        * *RTE_TABLE_ACTION_TC_MAX*.
+        */
+       uint32_t n_tc;
+
+       /** When non-zero, the *n_packets* meter stats counter is enabled,
+        * otherwise it is disabled.
+        *
+        * @see struct rte_table_action_mtr_counters_tc
+        */
+       int n_packets_enabled;
+
+       /** When non-zero, the *n_bytes* meter stats counter is enabled,
+        * otherwise it is disabled.
+        *
+        * @see struct rte_table_action_mtr_counters_tc
+        */
+       int n_bytes_enabled;
+};
+
+/** Meter action parameters (per table rule). */
+struct rte_table_action_mtr_params {
+       /** Traffic meter and policer parameters for each of the *tc_mask*
+        * traffic classes.
+        */
+       struct rte_table_action_mtr_tc_params mtr[RTE_TABLE_ACTION_TC_MAX];
+
+       /** Bit mask defining which traffic class parameters are valid in *mtr*.
+        * If bit N is set in *tc_mask*, then parameters for traffic class N are
+        * valid in *mtr*.
+        */
+       uint32_t tc_mask;
+};
+
+/** Meter action statistics counters (per table rule). */
+struct rte_table_action_mtr_counters {
+       /** Stats counters for each of the *tc_mask* traffic classes. */
+       struct rte_table_action_mtr_counters_tc stats[RTE_TABLE_ACTION_TC_MAX];
+
+       /** Bit mask defining which traffic class parameters are valid in *mtr*.
+        * If bit N is set in *tc_mask*, then parameters for traffic class N are
+        * valid in *mtr*.
+        */
+       uint32_t tc_mask;
+};
+
 /**
  * Table action profile.
  */
@@ -231,6 +393,92 @@ rte_table_action_apply(struct rte_table_action *action,
        enum rte_table_action_type type,
        void *action_params);
 
+/**
+ * Table action DSCP table update.
+ *
+ * @param[in] action
+ *   Handle to table action object (needs to be valid).
+ * @param[in] dscp_mask
+ *   64-bit mask defining the DSCP table entries to be updated. If bit N is set
+ *   in this bit mask, then DSCP table entry N is to be updated, otherwise not.
+ * @param[in] table
+ *   DSCP table.
+ * @return
+ *   Zero on success, non-zero error code otherwise.
+ */
+int __rte_experimental
+rte_table_action_dscp_table_update(struct rte_table_action *action,
+       uint64_t dscp_mask,
+       struct rte_table_action_dscp_table *table);
+
+/**
+ * Table action meter profile add.
+ *
+ * @param[in] action
+ *   Handle to table action object (needs to be valid).
+ * @param[in] meter_profile_id
+ *   Meter profile ID to be used for the *profile* once it is successfully added
+ *   to the *action* object (needs to be unused by the set of meter profiles
+ *   currently registered for the *action* object).
+ * @param[in] profile
+ *   Meter profile to be added.
+ * @return
+ *   Zero on success, non-zero error code otherwise.
+ */
+int __rte_experimental
+rte_table_action_meter_profile_add(struct rte_table_action *action,
+       uint32_t meter_profile_id,
+       struct rte_table_action_meter_profile *profile);
+
+/**
+ * Table action meter profile delete.
+ *
+ * @param[in] action
+ *   Handle to table action object (needs to be valid).
+ * @param[in] meter_profile_id
+ *   Meter profile ID of the meter profile to be deleted from the *action*
+ *   object (needs to be valid for the *action* object).
+ * @return
+ *   Zero on success, non-zero error code otherwise.
+ */
+int __rte_experimental
+rte_table_action_meter_profile_delete(struct rte_table_action *action,
+       uint32_t meter_profile_id);
+
+/**
+ * Table action meter read.
+ *
+ * @param[in] action
+ *   Handle to table action object (needs to be valid).
+ * @param[in] data
+ *   Data byte array (typically table rule data) with meter action previously
+ *   applied on it.
+ * @param[in] tc_mask
+ *   Bit mask defining which traffic classes should have the meter stats
+ *   counters read from *data* and stored into *stats*. If bit N is set in this
+ *   bit mask, then traffic class N is part of this operation, otherwise it is
+ *   not. If bit N is set in this bit mask, then traffic class N must be one of
+ *   the traffic classes that are enabled for the meter action in the table
+ *   action profile used by the *action* object.
+ * @param[inout] stats
+ *   When non-NULL, it points to the area where the meter stats counters read
+ *   from *data* are saved. Only the meter stats counters for the *tc_mask*
+ *   traffic classes are read and stored to *stats*.
+ * @param[in] clear
+ *   When non-zero, the meter stats counters are cleared (i.e. set to zero),
+ *   otherwise the counters are not modified. When the read operation is enabled
+ *   (*stats* is non-NULL), the clear operation is performed after the read
+ *   operation is completed.
+ * @return
+ *   Zero on success, non-zero error code otherwise.
+ */
+int __rte_experimental
+rte_table_action_meter_read(struct rte_table_action *action,
+       void *data,
+       uint32_t tc_mask,
+       struct rte_table_action_mtr_counters *stats,
+       int clear);
+
 #ifdef __cplusplus
 }
 #endif