#include <rte_string_fns.h>
#include <rte_flow.h>
#include <rte_flow_driver.h>
+#include <rte_tailq.h>
#include "rte_eth_softnic_internals.h"
#include "rte_eth_softnic.h"
}
}
+static int
+flow_item_raw_preprocess(const struct rte_flow_item *item,
+ union flow_item *item_spec,
+ union flow_item *item_mask,
+ size_t *item_size,
+ int *item_disabled,
+ struct rte_flow_error *error)
+{
+ const struct rte_flow_item_raw *item_raw_spec = item->spec;
+ const struct rte_flow_item_raw *item_raw_mask = item->mask;
+ const uint8_t *pattern;
+ const uint8_t *pattern_mask;
+ uint8_t *spec = (uint8_t *)item_spec;
+ uint8_t *mask = (uint8_t *)item_mask;
+ size_t pattern_length, pattern_offset, i;
+ int disabled;
+
+ if (!item->spec)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ item,
+ "RAW: Null specification");
+
+ if (item->last)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ item,
+ "RAW: Range not allowed (last must be NULL)");
+
+ if (item_raw_spec->relative == 0)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ item,
+ "RAW: Absolute offset not supported");
+
+ if (item_raw_spec->search)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ item,
+ "RAW: Search not supported");
+
+ if (item_raw_spec->offset < 0)
+ return rte_flow_error_set(error,
+ ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM,
+ item,
+ "RAW: Negative offset not supported");
+
+ if (item_raw_spec->length == 0)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ item,
+ "RAW: Zero pattern length");
+
+ if (item_raw_spec->offset + item_raw_spec->length >
+ TABLE_RULE_MATCH_SIZE_MAX)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ item,
+ "RAW: Item too big");
+
+ if (!item_raw_spec->pattern && item_raw_mask && item_raw_mask->pattern)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ item,
+ "RAW: Non-NULL pattern mask not allowed with NULL pattern");
+
+ pattern = item_raw_spec->pattern;
+ pattern_mask = (item_raw_mask) ? item_raw_mask->pattern : NULL;
+ pattern_length = (size_t)item_raw_spec->length;
+ pattern_offset = (size_t)item_raw_spec->offset;
+
+ disabled = 0;
+ if (pattern_mask == NULL)
+ disabled = 1;
+ else
+ for (i = 0; i < pattern_length; i++)
+ if ((pattern)[i])
+ disabled = 1;
+
+ memset(spec, 0, TABLE_RULE_MATCH_SIZE_MAX);
+ if (pattern)
+ memcpy(&spec[pattern_offset], pattern, pattern_length);
+
+ memset(mask, 0, TABLE_RULE_MATCH_SIZE_MAX);
+ if (pattern_mask)
+ memcpy(&mask[pattern_offset], pattern_mask, pattern_length);
+
+ *item_size = pattern_offset + pattern_length;
+ *item_disabled = disabled;
+
+ return 0;
+}
+
static int
flow_item_proto_preprocess(const struct rte_flow_item *item,
union flow_item *item_spec,
item,
"Item type not supported");
+ if (item->type == RTE_FLOW_ITEM_TYPE_RAW)
+ return flow_item_raw_preprocess(item,
+ item_spec,
+ item_mask,
+ item_size,
+ item_disabled,
+ error);
+
/* spec */
if (!item->spec) {
/* If spec is NULL, then last and mask also have to be NULL. */
const struct rte_flow_attr *attr,
const struct rte_flow_action *action,
struct softnic_table_rule_action *rule_action,
- struct rte_flow_error *error __rte_unused)
+ struct rte_flow_error *error)
{
struct softnic_table_action_profile *profile;
struct softnic_table_action_profile_params *params;
int n_jump_queue_rss_drop = 0;
int n_count = 0;
+ int n_mark = 0;
+ int n_vxlan_decap = 0;
profile = softnic_table_action_profile_find(softnic,
table->params.action_profile_name);
break;
} /* RTE_FLOW_ACTION_TYPE_COUNT */
+ case RTE_FLOW_ACTION_TYPE_MARK:
+ {
+ const struct rte_flow_action_mark *conf = action->conf;
+
+ if (conf == NULL)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION,
+ action,
+ "MARK: Null configuration");
+
+ if (n_mark)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ACTION,
+ action,
+ "Only one MARK action per flow");
+
+ if ((params->action_mask &
+ (1LLU << RTE_TABLE_ACTION_TAG)) == 0)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "MARK action not supported by this table");
+
+ n_mark = 1;
+
+ /* RTE_TABLE_ACTION_TAG */
+ rule_action->tag.tag = conf->id;
+ rule_action->action_mask |= 1 << RTE_TABLE_ACTION_TAG;
+ break;
+ } /* RTE_FLOW_ACTION_TYPE_MARK */
+
+ case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:
+ {
+ const struct rte_flow_action_mark *conf = action->conf;
+
+ if (conf)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION,
+ action,
+ "VXLAN DECAP: Non-null configuration");
+
+ if (n_vxlan_decap)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ACTION,
+ action,
+ "Only one VXLAN DECAP action per flow");
+
+ if ((params->action_mask &
+ (1LLU << RTE_TABLE_ACTION_DECAP)) == 0)
+ return rte_flow_error_set(error,
+ ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "VXLAN DECAP action not supported by this table");
+
+ n_vxlan_decap = 1;
+
+ /* RTE_TABLE_ACTION_DECAP */
+ rule_action->decap.n = 50; /* Ether/IPv4/UDP/VXLAN */
+ rule_action->action_mask |= 1 << RTE_TABLE_ACTION_DECAP;
+ break;
+ } /* RTE_FLOW_ACTION_TYPE_VXLAN_DECAP */
+
+ case RTE_FLOW_ACTION_TYPE_METER:
+ {
+ const struct rte_flow_action_meter *conf = action->conf;
+ struct softnic_mtr_meter_profile *mp;
+ struct softnic_mtr *m;
+ uint32_t table_id = table - pipeline->table;
+ uint32_t meter_profile_id;
+ int status;
+
+ if ((params->action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) == 0)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "METER: Table action not supported");
+
+ if (params->mtr.n_tc != 1)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "METER: Multiple TCs not supported");
+
+ if (conf == NULL)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION,
+ action,
+ "METER: Null configuration");
+
+ m = softnic_mtr_find(softnic, conf->mtr_id);
+
+ if (m == NULL)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF,
+ NULL,
+ "METER: Invalid meter ID");
+
+ if (m->flow)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF,
+ NULL,
+ "METER: Meter already attached to a flow");
+
+ meter_profile_id = m->params.meter_profile_id;
+ mp = softnic_mtr_meter_profile_find(softnic, meter_profile_id);
+
+ /* Add meter profile to pipeline table */
+ if (!softnic_pipeline_table_meter_profile_find(table,
+ meter_profile_id)) {
+ struct rte_table_action_meter_profile profile;
+
+ memset(&profile, 0, sizeof(profile));
+ profile.alg = RTE_TABLE_ACTION_METER_TRTCM;
+ profile.trtcm.cir = mp->params.trtcm_rfc2698.cir;
+ profile.trtcm.pir = mp->params.trtcm_rfc2698.pir;
+ profile.trtcm.cbs = mp->params.trtcm_rfc2698.cbs;
+ profile.trtcm.pbs = mp->params.trtcm_rfc2698.pbs;
+
+ status = softnic_pipeline_table_mtr_profile_add(softnic,
+ pipeline->name,
+ table_id,
+ meter_profile_id,
+ &profile);
+ if (status) {
+ rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "METER: Table meter profile add failed");
+ return -1;
+ }
+ }
+
+ /* RTE_TABLE_ACTION_METER */
+ rule_action->mtr.mtr[0].meter_profile_id = meter_profile_id;
+ rule_action->mtr.mtr[0].policer[e_RTE_METER_GREEN] =
+ (enum rte_table_action_policer)m->params.action[RTE_MTR_GREEN];
+ rule_action->mtr.mtr[0].policer[e_RTE_METER_YELLOW] =
+ (enum rte_table_action_policer)m->params.action[RTE_MTR_YELLOW];
+ rule_action->mtr.mtr[0].policer[e_RTE_METER_RED] =
+ (enum rte_table_action_policer)m->params.action[RTE_MTR_RED];
+ rule_action->mtr.tc_mask = 1;
+ rule_action->action_mask |= 1 << RTE_TABLE_ACTION_MTR;
+ break;
+ } /* RTE_FLOW_ACTION_TYPE_METER */
+
default:
return -ENOTSUP;
}
return 0;
}
+static struct softnic_mtr *
+flow_action_meter_get(struct pmd_internals *softnic,
+ const struct rte_flow_action *action)
+{
+ for ( ; action->type != RTE_FLOW_ACTION_TYPE_END; action++)
+ if (action->type == RTE_FLOW_ACTION_TYPE_METER) {
+ const struct rte_flow_action_meter *conf = action->conf;
+
+ if (conf == NULL)
+ return NULL;
+
+ return softnic_mtr_find(softnic, conf->mtr_id);
+ }
+
+ return NULL;
+}
+
+static void
+flow_meter_owner_reset(struct pmd_internals *softnic,
+ struct rte_flow *flow)
+{
+ struct softnic_mtr_list *ml = &softnic->mtr.mtrs;
+ struct softnic_mtr *m;
+
+ TAILQ_FOREACH(m, ml, node)
+ if (m->flow == flow) {
+ m->flow = NULL;
+ break;
+ }
+}
+
+static void
+flow_meter_owner_set(struct pmd_internals *softnic,
+ struct rte_flow *flow,
+ struct softnic_mtr *mtr)
+{
+ /* Reset current flow meter */
+ flow_meter_owner_reset(softnic, flow);
+
+ /* Set new flow meter */
+ mtr->flow = flow;
+}
+
+static int
+is_meter_action_enable(struct pmd_internals *softnic,
+ struct softnic_table *table)
+{
+ struct softnic_table_action_profile *profile =
+ softnic_table_action_profile_find(softnic,
+ table->params.action_profile_name);
+ struct softnic_table_action_profile_params *params = &profile->params;
+
+ return (params->action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) ? 1 : 0;
+}
+
static struct rte_flow *
pmd_flow_create(struct rte_eth_dev *dev,
const struct rte_flow_attr *attr,
struct pipeline *pipeline;
struct softnic_table *table;
struct rte_flow *flow;
+ struct softnic_mtr *mtr;
const char *pipeline_name = NULL;
uint32_t table_id = 0;
int new_flow, status;
flow->pipeline = pipeline;
flow->table_id = table_id;
+ mtr = flow_action_meter_get(softnic, action);
+ if (mtr)
+ flow_meter_owner_set(softnic, flow, mtr);
+
/* Flow add to list. */
if (new_flow)
TAILQ_INSERT_TAIL(&table->flows, flow, node);
NULL,
"Pipeline table rule delete failed");
+ /* Update dependencies */
+ if (is_meter_action_enable(softnic, table))
+ flow_meter_owner_reset(softnic, flow);
+
/* Flow delete. */
TAILQ_REMOVE(&table->flows, flow, node);
free(flow);
return 0;
}
+static int
+pmd_flow_flush(struct rte_eth_dev *dev,
+ struct rte_flow_error *error)
+{
+ struct pmd_internals *softnic = dev->data->dev_private;
+ struct pipeline *pipeline;
+ int fail_to_del_rule = 0;
+ uint32_t i;
+
+ TAILQ_FOREACH(pipeline, &softnic->pipeline_list, node) {
+ /* Remove all the flows added to the tables. */
+ for (i = 0; i < pipeline->n_tables; i++) {
+ struct softnic_table *table = &pipeline->table[i];
+ struct rte_flow *flow;
+ void *temp;
+ int status;
+
+ TAILQ_FOREACH_SAFE(flow, &table->flows, node, temp) {
+ /* Rule delete. */
+ status = softnic_pipeline_table_rule_delete
+ (softnic,
+ pipeline->name,
+ i,
+ &flow->match);
+ if (status)
+ fail_to_del_rule = 1;
+ /* Update dependencies */
+ if (is_meter_action_enable(softnic, table))
+ flow_meter_owner_reset(softnic, flow);
+
+ /* Flow delete. */
+ TAILQ_REMOVE(&table->flows, flow, node);
+ free(flow);
+ }
+ }
+ }
+
+ if (fail_to_del_rule)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "Some of the rules could not be deleted");
+
+ return 0;
+}
+
+static int
+pmd_flow_query(struct rte_eth_dev *dev __rte_unused,
+ struct rte_flow *flow,
+ const struct rte_flow_action *action __rte_unused,
+ void *data,
+ struct rte_flow_error *error)
+{
+ struct rte_table_action_stats_counters stats;
+ struct softnic_table *table;
+ struct rte_flow_query_count *flow_stats = data;
+ int status;
+
+ /* Check input parameters. */
+ if (flow == NULL)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL,
+ "Null flow");
+
+ if (data == NULL)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "Null data");
+
+ table = &flow->pipeline->table[flow->table_id];
+
+ /* Rule stats read. */
+ status = rte_table_action_stats_read(table->a,
+ flow->data,
+ &stats,
+ flow_stats->reset);
+ if (status)
+ return rte_flow_error_set(error,
+ EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "Pipeline table rule stats read failed");
+
+ /* Fill in flow stats. */
+ flow_stats->hits_set =
+ (table->ap->params.stats.n_packets_enabled) ? 1 : 0;
+ flow_stats->bytes_set =
+ (table->ap->params.stats.n_bytes_enabled) ? 1 : 0;
+ flow_stats->hits = stats.n_packets;
+ flow_stats->bytes = stats.n_bytes;
+
+ return 0;
+}
+
const struct rte_flow_ops pmd_flow_ops = {
.validate = pmd_flow_validate,
.create = pmd_flow_create,
.destroy = pmd_flow_destroy,
+ .flush = pmd_flow_flush,
+ .query = pmd_flow_query,
+ .isolate = NULL,
};