+ if (tunnel_ops->actions) {
+ free(pft->actions);
+ rte_flow_tunnel_action_decap_release(
+ port_id, pft->pmd_actions,
+ pft->num_pmd_actions, &error);
+ pft->actions = NULL;
+ pft->pmd_actions = NULL;
+ }
+ if (tunnel_ops->items) {
+ free(pft->items);
+ rte_flow_tunnel_item_release(port_id, pft->pmd_items,
+ pft->num_pmd_items,
+ &error);
+ pft->items = NULL;
+ pft->pmd_items = NULL;
+ }
+}
+
+/** Add port meter policy */
+int
+port_meter_policy_add(portid_t port_id, uint32_t policy_id,
+ const struct rte_flow_action *actions)
+{
+ struct rte_mtr_error error;
+ const struct rte_flow_action *act = actions;
+ const struct rte_flow_action *start;
+ struct rte_mtr_meter_policy_params policy;
+ uint32_t i = 0, act_n;
+ int ret;
+
+ for (i = 0; i < RTE_COLORS; i++) {
+ for (act_n = 0, start = act;
+ act->type != RTE_FLOW_ACTION_TYPE_END; act++)
+ act_n++;
+ if (act_n && act->type == RTE_FLOW_ACTION_TYPE_END)
+ policy.actions[i] = start;
+ else
+ policy.actions[i] = NULL;
+ act++;
+ }
+ ret = rte_mtr_meter_policy_add(port_id,
+ policy_id,
+ &policy, &error);
+ if (ret)
+ print_mtr_err_msg(&error);
+ return ret;
+}
+
+/** Validate flow rule. */
+int
+port_flow_validate(portid_t port_id,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item *pattern,
+ const struct rte_flow_action *actions,
+ const struct tunnel_ops *tunnel_ops)
+{
+ struct rte_flow_error error;
+ struct port_flow_tunnel *pft = NULL;
+ int ret;
+
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x11, sizeof(error));
+ if (tunnel_ops->enabled) {
+ pft = port_flow_tunnel_offload_cmd_prep(port_id, pattern,
+ actions, tunnel_ops);
+ if (!pft)
+ return -ENOENT;
+ if (pft->items)
+ pattern = pft->items;
+ if (pft->actions)
+ actions = pft->actions;
+ }
+ ret = rte_flow_validate(port_id, attr, pattern, actions, &error);
+ if (tunnel_ops->enabled)
+ port_flow_tunnel_offload_cmd_release(port_id, tunnel_ops, pft);
+ if (ret)
+ return port_flow_complain(&error);
+ printf("Flow rule validated\n");
+ return 0;
+}
+
+/** Return age action structure if exists, otherwise NULL. */
+static struct rte_flow_action_age *
+age_action_get(const struct rte_flow_action *actions)
+{
+ for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+ switch (actions->type) {
+ case RTE_FLOW_ACTION_TYPE_AGE:
+ return (struct rte_flow_action_age *)
+ (uintptr_t)actions->conf;
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+/** Create pattern template */
+int
+port_flow_pattern_template_create(portid_t port_id, uint32_t id,
+ const struct rte_flow_pattern_template_attr *attr,
+ const struct rte_flow_item *pattern)
+{
+ struct rte_port *port;
+ struct port_template *pit;
+ int ret;
+ struct rte_flow_error error;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ ret = template_alloc(id, &pit, &port->pattern_templ_list);
+ if (ret)
+ return ret;
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x22, sizeof(error));
+ pit->template.pattern_template = rte_flow_pattern_template_create(port_id,
+ attr, pattern, &error);
+ if (!pit->template.pattern_template) {
+ uint32_t destroy_id = pit->id;
+ port_flow_pattern_template_destroy(port_id, 1, &destroy_id);
+ return port_flow_complain(&error);
+ }
+ printf("Pattern template #%u created\n", pit->id);
+ return 0;
+}
+
+/** Destroy pattern template */
+int
+port_flow_pattern_template_destroy(portid_t port_id, uint32_t n,
+ const uint32_t *template)
+{
+ struct rte_port *port;
+ struct port_template **tmp;
+ uint32_t c = 0;
+ int ret = 0;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ tmp = &port->pattern_templ_list;
+ while (*tmp) {
+ uint32_t i;
+
+ for (i = 0; i != n; ++i) {
+ struct rte_flow_error error;
+ struct port_template *pit = *tmp;
+
+ if (template[i] != pit->id)
+ continue;
+ /*
+ * Poisoning to make sure PMDs update it in case
+ * of error.
+ */
+ memset(&error, 0x33, sizeof(error));
+
+ if (pit->template.pattern_template &&
+ rte_flow_pattern_template_destroy(port_id,
+ pit->template.pattern_template,
+ &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ *tmp = pit->next;
+ printf("Pattern template #%u destroyed\n", pit->id);
+ free(pit);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+/** Create actions template */
+int
+port_flow_actions_template_create(portid_t port_id, uint32_t id,
+ const struct rte_flow_actions_template_attr *attr,
+ const struct rte_flow_action *actions,
+ const struct rte_flow_action *masks)
+{
+ struct rte_port *port;
+ struct port_template *pat;
+ int ret;
+ struct rte_flow_error error;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ ret = template_alloc(id, &pat, &port->actions_templ_list);
+ if (ret)
+ return ret;
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x22, sizeof(error));
+ pat->template.actions_template = rte_flow_actions_template_create(port_id,
+ attr, actions, masks, &error);
+ if (!pat->template.actions_template) {
+ uint32_t destroy_id = pat->id;
+ port_flow_actions_template_destroy(port_id, 1, &destroy_id);
+ return port_flow_complain(&error);
+ }
+ printf("Actions template #%u created\n", pat->id);
+ return 0;
+}
+
+/** Destroy actions template */
+int
+port_flow_actions_template_destroy(portid_t port_id, uint32_t n,
+ const uint32_t *template)
+{
+ struct rte_port *port;
+ struct port_template **tmp;
+ uint32_t c = 0;
+ int ret = 0;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ tmp = &port->actions_templ_list;
+ while (*tmp) {
+ uint32_t i;
+
+ for (i = 0; i != n; ++i) {
+ struct rte_flow_error error;
+ struct port_template *pat = *tmp;
+
+ if (template[i] != pat->id)
+ continue;
+ /*
+ * Poisoning to make sure PMDs update it in case
+ * of error.
+ */
+ memset(&error, 0x33, sizeof(error));
+
+ if (pat->template.actions_template &&
+ rte_flow_actions_template_destroy(port_id,
+ pat->template.actions_template, &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ *tmp = pat->next;
+ printf("Actions template #%u destroyed\n", pat->id);
+ free(pat);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+/** Create table */
+int
+port_flow_template_table_create(portid_t port_id, uint32_t id,
+ const struct rte_flow_template_table_attr *table_attr,
+ uint32_t nb_pattern_templates, uint32_t *pattern_templates,
+ uint32_t nb_actions_templates, uint32_t *actions_templates)
+{
+ struct rte_port *port;
+ struct port_table *pt;
+ struct port_template *temp = NULL;
+ int ret;
+ uint32_t i;
+ struct rte_flow_error error;
+ struct rte_flow_pattern_template
+ *flow_pattern_templates[nb_pattern_templates];
+ struct rte_flow_actions_template
+ *flow_actions_templates[nb_actions_templates];
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ for (i = 0; i < nb_pattern_templates; ++i) {
+ bool found = false;
+ temp = port->pattern_templ_list;
+ while (temp) {
+ if (pattern_templates[i] == temp->id) {
+ flow_pattern_templates[i] =
+ temp->template.pattern_template;
+ found = true;
+ break;
+ }
+ temp = temp->next;
+ }
+ if (!found) {
+ printf("Pattern template #%u is invalid\n",
+ pattern_templates[i]);
+ return -EINVAL;
+ }
+ }
+ for (i = 0; i < nb_actions_templates; ++i) {
+ bool found = false;
+ temp = port->actions_templ_list;
+ while (temp) {
+ if (actions_templates[i] == temp->id) {
+ flow_actions_templates[i] =
+ temp->template.actions_template;
+ found = true;
+ break;
+ }
+ temp = temp->next;
+ }
+ if (!found) {
+ printf("Actions template #%u is invalid\n",
+ actions_templates[i]);
+ return -EINVAL;
+ }
+ }
+ ret = table_alloc(id, &pt, &port->table_list);
+ if (ret)
+ return ret;
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x22, sizeof(error));
+ pt->table = rte_flow_template_table_create(port_id, table_attr,
+ flow_pattern_templates, nb_pattern_templates,
+ flow_actions_templates, nb_actions_templates,
+ &error);
+
+ if (!pt->table) {
+ uint32_t destroy_id = pt->id;
+ port_flow_template_table_destroy(port_id, 1, &destroy_id);
+ return port_flow_complain(&error);
+ }
+ pt->nb_pattern_templates = nb_pattern_templates;
+ pt->nb_actions_templates = nb_actions_templates;
+ printf("Template table #%u created\n", pt->id);
+ return 0;
+}
+
+/** Destroy table */
+int
+port_flow_template_table_destroy(portid_t port_id,
+ uint32_t n, const uint32_t *table)
+{
+ struct rte_port *port;
+ struct port_table **tmp;
+ uint32_t c = 0;
+ int ret = 0;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ tmp = &port->table_list;
+ while (*tmp) {
+ uint32_t i;
+
+ for (i = 0; i != n; ++i) {
+ struct rte_flow_error error;
+ struct port_table *pt = *tmp;
+
+ if (table[i] != pt->id)
+ continue;
+ /*
+ * Poisoning to make sure PMDs update it in case
+ * of error.
+ */
+ memset(&error, 0x33, sizeof(error));
+
+ if (pt->table &&
+ rte_flow_template_table_destroy(port_id,
+ pt->table,
+ &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ *tmp = pt->next;
+ printf("Template table #%u destroyed\n", pt->id);
+ free(pt);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+/** Enqueue create flow rule operation. */
+int
+port_queue_flow_create(portid_t port_id, queueid_t queue_id,
+ bool postpone, uint32_t table_id,
+ uint32_t pattern_idx, uint32_t actions_idx,
+ const struct rte_flow_item *pattern,
+ const struct rte_flow_action *actions)
+{
+ struct rte_flow_op_attr op_attr = { .postpone = postpone };
+ struct rte_flow *flow;
+ struct rte_port *port;
+ struct port_flow *pf;
+ struct port_table *pt;
+ uint32_t id = 0;
+ bool found;
+ struct rte_flow_error error = { RTE_FLOW_ERROR_TYPE_NONE, NULL, NULL };
+ struct rte_flow_action_age *age = age_action_get(actions);
+
+ port = &ports[port_id];
+ if (port->flow_list) {
+ if (port->flow_list->id == UINT32_MAX) {
+ printf("Highest rule ID is already assigned,"
+ " delete it first");
+ return -ENOMEM;
+ }
+ id = port->flow_list->id + 1;
+ }
+
+ if (queue_id >= port->queue_nb) {
+ printf("Queue #%u is invalid\n", queue_id);
+ return -EINVAL;
+ }
+
+ found = false;
+ pt = port->table_list;
+ while (pt) {
+ if (table_id == pt->id) {
+ found = true;
+ break;
+ }
+ pt = pt->next;
+ }
+ if (!found) {
+ printf("Table #%u is invalid\n", table_id);
+ return -EINVAL;
+ }
+
+ if (pattern_idx >= pt->nb_pattern_templates) {
+ printf("Pattern template index #%u is invalid,"
+ " %u templates present in the table\n",
+ pattern_idx, pt->nb_pattern_templates);
+ return -EINVAL;
+ }
+ if (actions_idx >= pt->nb_actions_templates) {
+ printf("Actions template index #%u is invalid,"
+ " %u templates present in the table\n",
+ actions_idx, pt->nb_actions_templates);
+ return -EINVAL;
+ }
+
+ pf = port_flow_new(NULL, pattern, actions, &error);
+ if (!pf)
+ return port_flow_complain(&error);
+ if (age) {
+ pf->age_type = ACTION_AGE_CONTEXT_TYPE_FLOW;
+ age->context = &pf->age_type;
+ }
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x11, sizeof(error));
+ flow = rte_flow_async_create(port_id, queue_id, &op_attr, pt->table,
+ pattern, pattern_idx, actions, actions_idx, NULL, &error);
+ if (!flow) {
+ uint32_t flow_id = pf->id;
+ port_queue_flow_destroy(port_id, queue_id, true, 1, &flow_id);
+ return port_flow_complain(&error);
+ }
+
+ pf->next = port->flow_list;
+ pf->id = id;
+ pf->flow = flow;
+ port->flow_list = pf;
+ printf("Flow rule #%u creation enqueued\n", pf->id);
+ return 0;
+}
+
+/** Enqueue number of destroy flow rules operations. */
+int
+port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
+ bool postpone, uint32_t n, const uint32_t *rule)
+{
+ struct rte_flow_op_attr op_attr = { .postpone = postpone };
+ struct rte_port *port;
+ struct port_flow **tmp;
+ uint32_t c = 0;
+ int ret = 0;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+
+ if (queue_id >= port->queue_nb) {
+ printf("Queue #%u is invalid\n", queue_id);
+ return -EINVAL;
+ }
+
+ tmp = &port->flow_list;
+ while (*tmp) {
+ uint32_t i;
+
+ for (i = 0; i != n; ++i) {
+ struct rte_flow_error error;
+ struct port_flow *pf = *tmp;
+
+ if (rule[i] != pf->id)
+ continue;
+ /*
+ * Poisoning to make sure PMD
+ * update it in case of error.
+ */
+ memset(&error, 0x33, sizeof(error));
+ if (rte_flow_async_destroy(port_id, queue_id, &op_attr,
+ pf->flow, NULL, &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ printf("Flow rule #%u destruction enqueued\n", pf->id);
+ *tmp = pf->next;
+ free(pf);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+/** Enqueue indirect action create operation. */
+int
+port_queue_action_handle_create(portid_t port_id, uint32_t queue_id,
+ bool postpone, uint32_t id,
+ const struct rte_flow_indir_action_conf *conf,
+ const struct rte_flow_action *action)
+{
+ const struct rte_flow_op_attr attr = { .postpone = postpone};
+ struct rte_port *port;
+ struct port_indirect_action *pia;
+ int ret;
+ struct rte_flow_error error;
+
+ ret = action_alloc(port_id, id, &pia);
+ if (ret)
+ return ret;
+
+ port = &ports[port_id];
+ if (queue_id >= port->queue_nb) {
+ printf("Queue #%u is invalid\n", queue_id);
+ return -EINVAL;
+ }
+
+ if (action->type == RTE_FLOW_ACTION_TYPE_AGE) {
+ struct rte_flow_action_age *age =
+ (struct rte_flow_action_age *)(uintptr_t)(action->conf);
+
+ pia->age_type = ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION;
+ age->context = &pia->age_type;
+ }
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x88, sizeof(error));
+ pia->handle = rte_flow_async_action_handle_create(port_id, queue_id,
+ &attr, conf, action, NULL, &error);
+ if (!pia->handle) {
+ uint32_t destroy_id = pia->id;
+ port_queue_action_handle_destroy(port_id, queue_id,
+ postpone, 1, &destroy_id);
+ return port_flow_complain(&error);
+ }
+ pia->type = action->type;
+ printf("Indirect action #%u creation queued\n", pia->id);
+ return 0;
+}
+
+/** Enqueue indirect action destroy operation. */
+int
+port_queue_action_handle_destroy(portid_t port_id,
+ uint32_t queue_id, bool postpone,
+ uint32_t n, const uint32_t *actions)
+{
+ const struct rte_flow_op_attr attr = { .postpone = postpone};
+ struct rte_port *port;
+ struct port_indirect_action **tmp;
+ uint32_t c = 0;
+ int ret = 0;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+
+ if (queue_id >= port->queue_nb) {
+ printf("Queue #%u is invalid\n", queue_id);
+ return -EINVAL;
+ }
+
+ tmp = &port->actions_list;
+ while (*tmp) {
+ uint32_t i;
+
+ for (i = 0; i != n; ++i) {
+ struct rte_flow_error error;
+ struct port_indirect_action *pia = *tmp;
+
+ if (actions[i] != pia->id)
+ continue;
+ /*
+ * Poisoning to make sure PMDs update it in case
+ * of error.
+ */
+ memset(&error, 0x99, sizeof(error));
+
+ if (pia->handle &&
+ rte_flow_async_action_handle_destroy(port_id,
+ queue_id, &attr, pia->handle, NULL, &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ *tmp = pia->next;
+ printf("Indirect action #%u destruction queued\n",
+ pia->id);
+ free(pia);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+/** Enqueue indirect action update operation. */
+int
+port_queue_action_handle_update(portid_t port_id,
+ uint32_t queue_id, bool postpone, uint32_t id,
+ const struct rte_flow_action *action)
+{
+ const struct rte_flow_op_attr attr = { .postpone = postpone};
+ struct rte_port *port;
+ struct rte_flow_error error;
+ struct rte_flow_action_handle *action_handle;
+
+ action_handle = port_action_handle_get_by_id(port_id, id);
+ if (!action_handle)
+ return -EINVAL;
+
+ port = &ports[port_id];
+ if (queue_id >= port->queue_nb) {
+ printf("Queue #%u is invalid\n", queue_id);
+ return -EINVAL;
+ }
+
+ if (rte_flow_async_action_handle_update(port_id, queue_id, &attr,
+ action_handle, action, NULL, &error)) {
+ return port_flow_complain(&error);
+ }
+ printf("Indirect action #%u update queued\n", id);
+ return 0;
+}
+
+/** Push all the queue operations in the queue to the NIC. */
+int
+port_queue_flow_push(portid_t port_id, queueid_t queue_id)
+{
+ struct rte_port *port;
+ struct rte_flow_error error;
+ int ret = 0;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+
+ if (queue_id >= port->queue_nb) {
+ printf("Queue #%u is invalid\n", queue_id);
+ return -EINVAL;