+ if (dev_info.tx_desc_lim.nb_min > allowed_min_txd) {
+ allowed_min_txd = dev_info.tx_desc_lim.nb_min;
+ *pid = pi;
+ }
+ }
+
+ return allowed_min_txd;
+}
+
+/*
+ * Check input txd is valid or not.
+ * If input txd is not greater than any of maximum number
+ * of TXDs of every Rx queues, it is valid.
+ * if valid, return 0, else return -1
+ */
+int
+check_nb_txd(queueid_t txd)
+{
+ uint16_t allowed_max_txd;
+ uint16_t allowed_min_txd;
+ portid_t pid = 0;
+
+ allowed_max_txd = get_allowed_max_nb_txd(&pid);
+ if (txd > allowed_max_txd) {
+ fprintf(stderr,
+ "Fail: input txd (%u) can't be greater than max_txds (%u) of port %u\n",
+ txd, allowed_max_txd, pid);
+ return -1;
+ }
+
+ allowed_min_txd = get_allowed_min_nb_txd(&pid);
+ if (txd < allowed_min_txd) {
+ fprintf(stderr,
+ "Fail: input txd (%u) can't be less than min_txds (%u) of port %u\n",
+ txd, allowed_min_txd, pid);
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * Get the allowed maximum number of hairpin queues.
+ * *pid return the port id which has minimal value of
+ * max_hairpin_queues in all ports.
+ */
+queueid_t
+get_allowed_max_nb_hairpinq(portid_t *pid)
+{
+ queueid_t allowed_max_hairpinq = RTE_MAX_QUEUES_PER_PORT;
+ portid_t pi;
+ struct rte_eth_hairpin_cap cap;
+
+ RTE_ETH_FOREACH_DEV(pi) {
+ if (rte_eth_dev_hairpin_capability_get(pi, &cap) != 0) {
+ *pid = pi;
+ return 0;
+ }
+ if (cap.max_nb_queues < allowed_max_hairpinq) {
+ allowed_max_hairpinq = cap.max_nb_queues;
+ *pid = pi;
+ }
+ }
+ return allowed_max_hairpinq;
+}
+
+/*
+ * Check input hairpin is valid or not.
+ * If input hairpin is not greater than any of maximum number
+ * of hairpin queues of all ports, it is valid.
+ * if valid, return 0, else return -1
+ */
+int
+check_nb_hairpinq(queueid_t hairpinq)
+{
+ queueid_t allowed_max_hairpinq;
+ portid_t pid = 0;
+
+ allowed_max_hairpinq = get_allowed_max_nb_hairpinq(&pid);
+ if (hairpinq > allowed_max_hairpinq) {
+ fprintf(stderr,
+ "Fail: input hairpin (%u) can't be greater than max_hairpin_queues (%u) of port %u\n",
+ hairpinq, allowed_max_hairpinq, pid);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get_eth_overhead(struct rte_eth_dev_info *dev_info)
+{
+ uint32_t eth_overhead;
+
+ if (dev_info->max_mtu != UINT16_MAX &&
+ dev_info->max_rx_pktlen > dev_info->max_mtu)
+ eth_overhead = dev_info->max_rx_pktlen - dev_info->max_mtu;
+ else
+ eth_overhead = RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN;
+
+ return eth_overhead;
+}
+
+static void
+init_config_port_offloads(portid_t pid, uint32_t socket_id)
+{
+ struct rte_port *port = &ports[pid];
+ int ret;
+ int i;
+
+ eth_rx_metadata_negotiate_mp(pid);
+
+ port->dev_conf.txmode = tx_mode;
+ port->dev_conf.rxmode = rx_mode;
+
+ ret = eth_dev_info_get_print_err(pid, &port->dev_info);
+ if (ret != 0)
+ rte_exit(EXIT_FAILURE, "rte_eth_dev_info_get() failed\n");
+
+ if (!(port->dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE))
+ port->dev_conf.txmode.offloads &=
+ ~RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE;
+
+ /* Apply Rx offloads configuration */
+ for (i = 0; i < port->dev_info.max_rx_queues; i++)
+ port->rx_conf[i].offloads = port->dev_conf.rxmode.offloads;
+ /* Apply Tx offloads configuration */
+ for (i = 0; i < port->dev_info.max_tx_queues; i++)
+ port->tx_conf[i].offloads = port->dev_conf.txmode.offloads;
+
+ if (eth_link_speed)
+ port->dev_conf.link_speeds = eth_link_speed;
+
+ if (max_rx_pkt_len)
+ port->dev_conf.rxmode.mtu = max_rx_pkt_len -
+ get_eth_overhead(&port->dev_info);
+
+ /* set flag to initialize port/queue */
+ port->need_reconfig = 1;
+ port->need_reconfig_queues = 1;
+ port->socket_id = socket_id;
+ port->tx_metadata = 0;
+
+ /*
+ * Check for maximum number of segments per MTU.
+ * Accordingly update the mbuf data size.
+ */
+ if (port->dev_info.rx_desc_lim.nb_mtu_seg_max != UINT16_MAX &&
+ port->dev_info.rx_desc_lim.nb_mtu_seg_max != 0) {
+ uint32_t eth_overhead = get_eth_overhead(&port->dev_info);
+ uint16_t mtu;
+
+ if (rte_eth_dev_get_mtu(pid, &mtu) == 0) {
+ uint16_t data_size = (mtu + eth_overhead) /
+ port->dev_info.rx_desc_lim.nb_mtu_seg_max;
+ uint16_t buffer_size = data_size + RTE_PKTMBUF_HEADROOM;
+
+ if (buffer_size > mbuf_data_size[0]) {
+ mbuf_data_size[0] = buffer_size;
+ TESTPMD_LOG(WARNING,
+ "Configured mbuf size of the first segment %hu\n",
+ mbuf_data_size[0]);
+ }
+ }
+ }
+}
+
+static void
+init_config(void)
+{
+ portid_t pid;
+ struct rte_mempool *mbp;
+ unsigned int nb_mbuf_per_pool;
+ lcoreid_t lc_id;
+#ifdef RTE_LIB_GRO
+ struct rte_gro_param gro_param;
+#endif
+#ifdef RTE_LIB_GSO
+ uint32_t gso_types;
+#endif
+
+ /* Configuration of logical cores. */
+ fwd_lcores = rte_zmalloc("testpmd: fwd_lcores",
+ sizeof(struct fwd_lcore *) * nb_lcores,
+ RTE_CACHE_LINE_SIZE);
+ if (fwd_lcores == NULL) {
+ rte_exit(EXIT_FAILURE, "rte_zmalloc(%d (struct fwd_lcore *)) "
+ "failed\n", nb_lcores);
+ }
+ for (lc_id = 0; lc_id < nb_lcores; lc_id++) {
+ fwd_lcores[lc_id] = rte_zmalloc("testpmd: struct fwd_lcore",
+ sizeof(struct fwd_lcore),
+ RTE_CACHE_LINE_SIZE);
+ if (fwd_lcores[lc_id] == NULL) {
+ rte_exit(EXIT_FAILURE, "rte_zmalloc(struct fwd_lcore) "
+ "failed\n");
+ }
+ fwd_lcores[lc_id]->cpuid_idx = lc_id;
+ }
+
+ RTE_ETH_FOREACH_DEV(pid) {
+ uint32_t socket_id;
+
+ if (numa_support) {
+ socket_id = port_numa[pid];
+ if (port_numa[pid] == NUMA_NO_CONFIG) {
+ socket_id = rte_eth_dev_socket_id(pid);
+
+ /*
+ * if socket_id is invalid,
+ * set to the first available socket.
+ */
+ if (check_socket_id(socket_id) < 0)
+ socket_id = socket_ids[0];
+ }
+ } else {
+ socket_id = (socket_num == UMA_NO_CONFIG) ?
+ 0 : socket_num;
+ }
+ /* Apply default TxRx configuration for all ports */
+ init_config_port_offloads(pid, socket_id);
+ }
+ /*
+ * Create pools of mbuf.
+ * If NUMA support is disabled, create a single pool of mbuf in
+ * socket 0 memory by default.
+ * Otherwise, create a pool of mbuf in the memory of sockets 0 and 1.
+ *
+ * Use the maximum value of nb_rxd and nb_txd here, then nb_rxd and
+ * nb_txd can be configured at run time.
+ */
+ if (param_total_num_mbufs)
+ nb_mbuf_per_pool = param_total_num_mbufs;
+ else {
+ nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX +
+ (nb_lcores * mb_mempool_cache) +
+ RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST;
+ nb_mbuf_per_pool *= RTE_MAX_ETHPORTS;
+ }
+
+ if (numa_support) {
+ uint8_t i, j;
+
+ for (i = 0; i < num_sockets; i++)
+ for (j = 0; j < mbuf_data_size_n; j++)
+ mempools[i * MAX_SEGS_BUFFER_SPLIT + j] =
+ mbuf_pool_create(mbuf_data_size[j],
+ nb_mbuf_per_pool,
+ socket_ids[i], j);
+ } else {
+ uint8_t i;
+
+ for (i = 0; i < mbuf_data_size_n; i++)
+ mempools[i] = mbuf_pool_create
+ (mbuf_data_size[i],
+ nb_mbuf_per_pool,
+ socket_num == UMA_NO_CONFIG ?
+ 0 : socket_num, i);
+ }
+
+ init_port_config();
+
+#ifdef RTE_LIB_GSO
+ gso_types = RTE_ETH_TX_OFFLOAD_TCP_TSO | RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO |
+ RTE_ETH_TX_OFFLOAD_GRE_TNL_TSO | RTE_ETH_TX_OFFLOAD_UDP_TSO;
+#endif
+ /*
+ * Records which Mbuf pool to use by each logical core, if needed.
+ */
+ for (lc_id = 0; lc_id < nb_lcores; lc_id++) {
+ mbp = mbuf_pool_find(
+ rte_lcore_to_socket_id(fwd_lcores_cpuids[lc_id]), 0);
+
+ if (mbp == NULL)
+ mbp = mbuf_pool_find(0, 0);
+ fwd_lcores[lc_id]->mbp = mbp;
+#ifdef RTE_LIB_GSO
+ /* initialize GSO context */
+ fwd_lcores[lc_id]->gso_ctx.direct_pool = mbp;
+ fwd_lcores[lc_id]->gso_ctx.indirect_pool = mbp;