app/testpmd: add flow queue pull operation
[dpdk.git] / app / test-pmd / config.c
index de1ec14..158d1b3 100644 (file)
@@ -1610,6 +1610,153 @@ action_alloc(portid_t port_id, uint32_t id,
        return 0;
 }
 
+static int
+template_alloc(uint32_t id, struct port_template **template,
+              struct port_template **list)
+{
+       struct port_template *lst = *list;
+       struct port_template **ppt;
+       struct port_template *pt = NULL;
+
+       *template = NULL;
+       if (id == UINT32_MAX) {
+               /* taking first available ID */
+               if (lst) {
+                       if (lst->id == UINT32_MAX - 1) {
+                               printf("Highest template ID is already"
+                               " assigned, delete it first\n");
+                               return -ENOMEM;
+                       }
+                       id = lst->id + 1;
+               } else {
+                       id = 0;
+               }
+       }
+       pt = calloc(1, sizeof(*pt));
+       if (!pt) {
+               printf("Allocation of port template failed\n");
+               return -ENOMEM;
+       }
+       ppt = list;
+       while (*ppt && (*ppt)->id > id)
+               ppt = &(*ppt)->next;
+       if (*ppt && (*ppt)->id == id) {
+               printf("Template #%u is already assigned,"
+                       " delete it first\n", id);
+               free(pt);
+               return -EINVAL;
+       }
+       pt->next = *ppt;
+       pt->id = id;
+       *ppt = pt;
+       *template = pt;
+       return 0;
+}
+
+static int
+table_alloc(uint32_t id, struct port_table **table,
+           struct port_table **list)
+{
+       struct port_table *lst = *list;
+       struct port_table **ppt;
+       struct port_table *pt = NULL;
+
+       *table = NULL;
+       if (id == UINT32_MAX) {
+               /* taking first available ID */
+               if (lst) {
+                       if (lst->id == UINT32_MAX - 1) {
+                               printf("Highest table ID is already"
+                               " assigned, delete it first\n");
+                               return -ENOMEM;
+                       }
+                       id = lst->id + 1;
+               } else {
+                       id = 0;
+               }
+       }
+       pt = calloc(1, sizeof(*pt));
+       if (!pt) {
+               printf("Allocation of table failed\n");
+               return -ENOMEM;
+       }
+       ppt = list;
+       while (*ppt && (*ppt)->id > id)
+               ppt = &(*ppt)->next;
+       if (*ppt && (*ppt)->id == id) {
+               printf("Table #%u is already assigned,"
+                       " delete it first\n", id);
+               free(pt);
+               return -EINVAL;
+       }
+       pt->next = *ppt;
+       pt->id = id;
+       *ppt = pt;
+       *table = pt;
+       return 0;
+}
+
+/** Get info about flow management resources. */
+int
+port_flow_get_info(portid_t port_id)
+{
+       struct rte_flow_port_info port_info;
+       struct rte_flow_queue_info queue_info;
+       struct rte_flow_error error;
+
+       if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+           port_id == (portid_t)RTE_PORT_ALL)
+               return -EINVAL;
+       /* Poisoning to make sure PMDs update it in case of error. */
+       memset(&error, 0x99, sizeof(error));
+       memset(&port_info, 0, sizeof(port_info));
+       memset(&queue_info, 0, sizeof(queue_info));
+       if (rte_flow_info_get(port_id, &port_info, &queue_info, &error))
+               return port_flow_complain(&error);
+       printf("Flow engine resources on port %u:\n"
+              "Number of queues: %d\n"
+                  "Size of queues: %d\n"
+              "Number of counters: %d\n"
+              "Number of aging objects: %d\n"
+              "Number of meter actions: %d\n",
+              port_id, port_info.max_nb_queues,
+                  queue_info.max_size,
+              port_info.max_nb_counters,
+              port_info.max_nb_aging_objects,
+              port_info.max_nb_meters);
+       return 0;
+}
+
+/** Configure flow management resources. */
+int
+port_flow_configure(portid_t port_id,
+       const struct rte_flow_port_attr *port_attr,
+       uint16_t nb_queue,
+       const struct rte_flow_queue_attr *queue_attr)
+{
+       struct rte_port *port;
+       struct rte_flow_error error;
+       const struct rte_flow_queue_attr *attr_list[nb_queue];
+       int std_queue;
+
+       if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+           port_id == (portid_t)RTE_PORT_ALL)
+               return -EINVAL;
+       port = &ports[port_id];
+       port->queue_nb = nb_queue;
+       port->queue_sz = queue_attr->size;
+       for (std_queue = 0; std_queue < nb_queue; std_queue++)
+               attr_list[std_queue] = queue_attr;
+       /* Poisoning to make sure PMDs update it in case of error. */
+       memset(&error, 0x66, sizeof(error));
+       if (rte_flow_configure(port_id, port_attr, nb_queue, attr_list, &error))
+               return port_flow_complain(&error);
+       printf("Configure flows on port %u: "
+              "number of queues %d with %d elements\n",
+              port_id, nb_queue, queue_attr->size);
+       return 0;
+}
+
 /** Create indirect action */
 int
 port_action_handle_create(portid_t port_id, uint32_t id,
@@ -2025,6 +2172,506 @@ age_action_get(const struct rte_flow_action *actions)
        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;
+}
+
+/** 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;
+       }
+
+       memset(&error, 0x55, sizeof(error));
+       ret = rte_flow_push(port_id, queue_id, &error);
+       if (ret < 0) {
+               printf("Failed to push operations in the queue\n");
+               return -EINVAL;
+       }
+       printf("Queue #%u operations pushed\n", queue_id);
+       return ret;
+}
+
+/** Pull queue operation results from the queue. */
+int
+port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
+{
+       struct rte_port *port;
+       struct rte_flow_op_result *res;
+       struct rte_flow_error error;
+       int ret = 0;
+       int success = 0;
+       int i;
+
+       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;
+       }
+
+       res = calloc(port->queue_sz, sizeof(struct rte_flow_op_result));
+       if (!res) {
+               printf("Failed to allocate memory for pulled results\n");
+               return -ENOMEM;
+       }
+
+       memset(&error, 0x66, sizeof(error));
+       ret = rte_flow_pull(port_id, queue_id, res,
+                                port->queue_sz, &error);
+       if (ret < 0) {
+               printf("Failed to pull a operation results\n");
+               free(res);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < ret; i++) {
+               if (res[i].status == RTE_FLOW_OP_SUCCESS)
+                       success++;
+       }
+       printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
+              queue_id, ret, ret - success, success);
+       free(res);
+       return ret;
+}
+
 /** Create flow rule. */
 int
 port_flow_create(portid_t port_id,