app/testpmd: fix multicast address pool leak
[dpdk.git] / app / test-pmd / config.c
index d7ab57b..d6caa1f 100644 (file)
@@ -249,14 +249,20 @@ nic_stats_display(portid_t port_id)
                                                                diff_ns;
        uint64_t mpps_rx, mpps_tx, mbps_rx, mbps_tx;
        struct rte_eth_stats stats;
-
        static const char *nic_stats_border = "########################";
+       int ret;
 
        if (port_id_is_invalid(port_id, ENABLED_WARN)) {
                print_valid_ports();
                return;
        }
-       rte_eth_stats_get(port_id, &stats);
+       ret = rte_eth_stats_get(port_id, &stats);
+       if (ret != 0) {
+               fprintf(stderr,
+                       "%s: Error: failed to get stats (port %u): %d",
+                       __func__, port_id, ret);
+               return;
+       }
        printf("\n  %s NIC statistics for port %-2d %s\n",
               nic_stats_border, port_id, nic_stats_border);
 
@@ -1248,6 +1254,57 @@ port_reg_set(portid_t port_id, uint32_t reg_off, uint32_t reg_v)
        display_port_reg_value(port_id, reg_off, reg_v);
 }
 
+static uint32_t
+eth_dev_get_overhead_len(uint32_t max_rx_pktlen, uint16_t max_mtu)
+{
+       uint32_t overhead_len;
+
+       if (max_mtu != UINT16_MAX && max_rx_pktlen > max_mtu)
+               overhead_len = max_rx_pktlen - max_mtu;
+       else
+               overhead_len = RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN;
+
+       return overhead_len;
+}
+
+static int
+eth_dev_validate_mtu(uint16_t port_id, uint16_t mtu)
+{
+       struct rte_eth_dev_info dev_info;
+       uint32_t overhead_len;
+       uint32_t frame_size;
+       int ret;
+
+       ret = rte_eth_dev_info_get(port_id, &dev_info);
+       if (ret != 0)
+               return ret;
+
+       if (mtu < dev_info.min_mtu) {
+               fprintf(stderr,
+                       "MTU (%u) < device min MTU (%u) for port_id %u\n",
+                       mtu, dev_info.min_mtu, port_id);
+               return -EINVAL;
+       }
+       if (mtu > dev_info.max_mtu) {
+               fprintf(stderr,
+                       "MTU (%u) > device max MTU (%u) for port_id %u\n",
+                       mtu, dev_info.max_mtu, port_id);
+               return -EINVAL;
+       }
+
+       overhead_len = eth_dev_get_overhead_len(dev_info.max_rx_pktlen,
+                       dev_info.max_mtu);
+       frame_size = mtu + overhead_len;
+       if (frame_size > dev_info.max_rx_pktlen) {
+               fprintf(stderr,
+                       "Frame size (%u) > device max frame size (%u) for port_id %u\n",
+                       frame_size, dev_info.max_rx_pktlen, port_id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 void
 port_mtu_set(portid_t port_id, uint16_t mtu)
 {
@@ -1257,6 +1314,10 @@ port_mtu_set(portid_t port_id, uint16_t mtu)
        if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
 
+       diag = eth_dev_validate_mtu(port_id, mtu);
+       if (diag != 0)
+               return;
+
        if (port->need_reconfig == 0) {
                diag = rte_eth_dev_set_mtu(port_id, mtu);
                if (diag != 0) {
@@ -1844,6 +1905,37 @@ port_action_handle_destroy(portid_t port_id,
        return ret;
 }
 
+int
+port_action_handle_flush(portid_t port_id)
+{
+       struct rte_port *port;
+       struct port_indirect_action **tmp;
+       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_list;
+       while (*tmp != NULL) {
+               struct rte_flow_error error;
+               struct port_indirect_action *pia = *tmp;
+
+               /* Poisoning to make sure PMDs update it in case of error. */
+               memset(&error, 0x44, sizeof(error));
+               if (pia->handle != NULL &&
+                   rte_flow_action_handle_destroy
+                                       (port_id, pia->handle, &error) != 0) {
+                       printf("Indirect action #%u not destroyed\n", pia->id);
+                       ret = port_flow_complain(&error);
+                       tmp = &pia->next;
+               } else {
+                       *tmp = pia->next;
+                       free(pia);
+               }
+       }
+       return ret;
+}
 
 /** Get indirect action by port + id */
 struct rte_flow_action_handle *
@@ -2469,14 +2561,12 @@ port_queue_flow_create(portid_t port_id, queueid_t queue_id,
                       const struct rte_flow_action *actions)
 {
        struct rte_flow_op_attr op_attr = { .postpone = postpone };
-       struct rte_flow_op_result comp = { 0 };
        struct rte_flow *flow;
        struct rte_port *port;
        struct port_flow *pf;
        struct port_table *pt;
        uint32_t id = 0;
        bool found;
-       int ret = 0;
        struct rte_flow_error error = { RTE_FLOW_ERROR_TYPE_NONE, NULL, NULL };
        struct rte_flow_action_age *age = age_action_get(actions);
 
@@ -2539,16 +2629,6 @@ port_queue_flow_create(portid_t port_id, queueid_t queue_id,
                return port_flow_complain(&error);
        }
 
-       while (ret == 0) {
-               /* Poisoning to make sure PMDs update it in case of error. */
-               memset(&error, 0x22, sizeof(error));
-               ret = rte_flow_pull(port_id, queue_id, &comp, 1, &error);
-               if (ret < 0) {
-                       printf("Failed to pull queue\n");
-                       return -EINVAL;
-               }
-       }
-
        pf->next = port->flow_list;
        pf->id = id;
        pf->flow = flow;
@@ -2563,7 +2643,6 @@ 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_flow_op_result comp = { 0 };
        struct rte_port *port;
        struct port_flow **tmp;
        uint32_t c = 0;
@@ -2599,21 +2678,6 @@ port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
                                ret = port_flow_complain(&error);
                                continue;
                        }
-
-                       while (ret == 0) {
-                               /*
-                                * Poisoning to make sure PMD
-                                * update it in case of error.
-                                */
-                               memset(&error, 0x44, sizeof(error));
-                               ret = rte_flow_pull(port_id, queue_id,
-                                                   &comp, 1, &error);
-                               if (ret < 0) {
-                                       printf("Failed to pull queue\n");
-                                       return -EINVAL;
-                               }
-                       }
-
                        printf("Flow rule #%u destruction enqueued\n", pf->id);
                        *tmp = pf->next;
                        free(pf);
@@ -2626,6 +2690,211 @@ port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
        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,
@@ -3374,8 +3643,8 @@ rxtx_config_display(void)
               nb_fwd_lcores, nb_fwd_ports);
 
        RTE_ETH_FOREACH_DEV(pid) {
-               struct rte_eth_rxconf *rx_conf = &ports[pid].rx_conf[0];
-               struct rte_eth_txconf *tx_conf = &ports[pid].tx_conf[0];
+               struct rte_eth_rxconf *rx_conf = &ports[pid].rxq[0].conf;
+               struct rte_eth_txconf *tx_conf = &ports[pid].txq[0].conf;
                uint16_t *nb_rx_desc = &ports[pid].nb_rx_desc[0];
                uint16_t *nb_tx_desc = &ports[pid].nb_tx_desc[0];
                struct rte_eth_rxq_info rx_qinfo;
@@ -3633,7 +3902,7 @@ fwd_stream_on_other_lcores(uint16_t domain_id, lcoreid_t src_lc,
                        fs = fwd_streams[sm_id];
                        port = &ports[fs->rx_port];
                        dev_info = &port->dev_info;
-                       rxq_conf = &port->rx_conf[fs->rx_queue];
+                       rxq_conf = &port->rxq[fs->rx_queue].conf;
                        if ((dev_info->dev_capa & RTE_ETH_DEV_CAPA_RXQ_SHARE)
                            == 0 || rxq_conf->share_group == 0)
                                /* Not shared rxq. */
@@ -3693,7 +3962,7 @@ pkt_fwd_shared_rxq_check(void)
                        fs->lcore = fwd_lcores[lc_id];
                        port = &ports[fs->rx_port];
                        dev_info = &port->dev_info;
-                       rxq_conf = &port->rx_conf[fs->rx_queue];
+                       rxq_conf = &port->rxq[fs->rx_queue].conf;
                        if ((dev_info->dev_capa & RTE_ETH_DEV_CAPA_RXQ_SHARE)
                            == 0 || rxq_conf->share_group == 0)
                                /* Not shared rxq. */
@@ -5801,6 +6070,25 @@ mcast_addr_pool_remove(struct rte_port *port, uint32_t addr_idx)
                sizeof(struct rte_ether_addr) * (port->mc_addr_nb - addr_idx));
 }
 
+int
+mcast_addr_pool_destroy(portid_t port_id)
+{
+       struct rte_port *port;
+
+       if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+           port_id == (portid_t)RTE_PORT_ALL)
+               return -EINVAL;
+       port = &ports[port_id];
+
+       if (port->mc_addr_nb != 0) {
+               /* free the pool of multicast addresses. */
+               free(port->mc_addr_pool);
+               port->mc_addr_pool = NULL;
+               port->mc_addr_nb = 0;
+       }
+       return 0;
+}
+
 static int
 eth_port_multicast_addr_list_set(portid_t port_id)
 {