X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=app%2Ftest-pmd%2Fconfig.c;h=cc8e7aa13822efbdb66856fa7c41e1c6ee3909f8;hb=d906fff51878cf8dedb7f6f0aa24564d4f6d3597;hp=f87d9d5b820f74b4b1dbbc7c475576f6eef18525;hpb=2490bb897182f57de80fd924dd3ae48dda819b8c;p=dpdk.git diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index f87d9d5b82..cc8e7aa138 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -90,7 +90,7 @@ const struct rss_type_info rss_type_table[] = { { "all", RTE_ETH_RSS_ETH | RTE_ETH_RSS_VLAN | RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP | RTE_ETH_RSS_SCTP | RTE_ETH_RSS_L2_PAYLOAD | RTE_ETH_RSS_L2TPV3 | RTE_ETH_RSS_ESP | RTE_ETH_RSS_AH | RTE_ETH_RSS_PFCP | - RTE_ETH_RSS_GTPU | RTE_ETH_RSS_ECPRI | RTE_ETH_RSS_MPLS}, + RTE_ETH_RSS_GTPU | RTE_ETH_RSS_ECPRI | RTE_ETH_RSS_MPLS | RTE_ETH_RSS_L2TPV2}, { "none", 0 }, { "eth", RTE_ETH_RSS_ETH }, { "l2-src-only", RTE_ETH_RSS_L2_SRC_ONLY }, @@ -143,6 +143,7 @@ const struct rss_type_info rss_type_table[] = { { "mpls", RTE_ETH_RSS_MPLS }, { "ipv4-chksum", RTE_ETH_RSS_IPV4_CHKSUM }, { "l4-chksum", RTE_ETH_RSS_L4_CHKSUM }, + { "l2tpv2", RTE_ETH_RSS_L2TPV2 }, { NULL, 0 }, }; @@ -912,10 +913,15 @@ port_eeprom_display(portid_t port_id) return; } - char buf[len_eeprom]; einfo.offset = 0; einfo.length = len_eeprom; - einfo.data = buf; + einfo.data = calloc(1, len_eeprom); + if (!einfo.data) { + fprintf(stderr, + "Allocation of port %u eeprom data failed\n", + port_id); + return; + } ret = rte_eth_dev_get_eeprom(port_id, &einfo); if (ret != 0) { @@ -933,10 +939,12 @@ port_eeprom_display(portid_t port_id) fprintf(stderr, "Unable to get EEPROM: %d\n", ret); break; } + free(einfo.data); return; } rte_hexdump(stdout, "hexdump", einfo.data, einfo.length); printf("Finish -- Port: %d EEPROM length: %d bytes\n", port_id, len_eeprom); + free(einfo.data); } void @@ -972,10 +980,15 @@ port_module_eeprom_display(portid_t port_id) return; } - char buf[minfo.eeprom_len]; einfo.offset = 0; einfo.length = minfo.eeprom_len; - einfo.data = buf; + einfo.data = calloc(1, minfo.eeprom_len); + if (!einfo.data) { + fprintf(stderr, + "Allocation of port %u eeprom data failed\n", + port_id); + return; + } ret = rte_eth_dev_get_module_eeprom(port_id, &einfo); if (ret != 0) { @@ -994,11 +1007,13 @@ port_module_eeprom_display(portid_t port_id) ret); break; } + free(einfo.data); return; } rte_hexdump(stdout, "hexdump", einfo.data, einfo.length); printf("Finish -- Port: %d MODULE EEPROM length: %d bytes\n", port_id, einfo.length); + free(einfo.data); } int @@ -1595,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, @@ -1971,6 +2133,7 @@ port_flow_validate(portid_t port_id, { 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)); @@ -1984,10 +2147,11 @@ port_flow_validate(portid_t port_id, if (pft->actions) actions = pft->actions; } - if (rte_flow_validate(port_id, attr, pattern, actions, &error)) - return port_flow_complain(&error); + 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; } @@ -2008,6 +2172,637 @@ 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; +} + +/** 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; + } + + 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,