aboutsummaryrefslogtreecommitdiff
path: root/test/validation/api/pktio
diff options
context:
space:
mode:
Diffstat (limited to 'test/validation/api/pktio')
-rw-r--r--test/validation/api/pktio/.gitignore1
-rw-r--r--test/validation/api/pktio/Makefile.am4
-rw-r--r--test/validation/api/pktio/lso.c938
-rw-r--r--test/validation/api/pktio/lso.h19
-rw-r--r--test/validation/api/pktio/parser.c609
-rw-r--r--test/validation/api/pktio/parser.h19
-rw-r--r--test/validation/api/pktio/pktio.c5517
7 files changed, 7107 insertions, 0 deletions
diff --git a/test/validation/api/pktio/.gitignore b/test/validation/api/pktio/.gitignore
new file mode 100644
index 000000000..1a5dd46e4
--- /dev/null
+++ b/test/validation/api/pktio/.gitignore
@@ -0,0 +1 @@
+pktio_main
diff --git a/test/validation/api/pktio/Makefile.am b/test/validation/api/pktio/Makefile.am
new file mode 100644
index 000000000..c63809f8c
--- /dev/null
+++ b/test/validation/api/pktio/Makefile.am
@@ -0,0 +1,4 @@
+include ../Makefile.inc
+
+test_PROGRAMS = pktio_main
+pktio_main_SOURCES = pktio.c parser.c parser.h lso.c lso.h
diff --git a/test/validation/api/pktio/lso.c b/test/validation/api/pktio/lso.c
new file mode 100644
index 000000000..832c08859
--- /dev/null
+++ b/test/validation/api/pktio/lso.c
@@ -0,0 +1,938 @@
+/* Copyright (c) 2020-2022, Nokia
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <test_packet_ipv4.h>
+#include <test_packet_custom.h>
+
+#include <odp/helper/odph_api.h>
+
+#include "lso.h"
+
+#define MAX_NUM_IFACES 2
+#define PKT_POOL_NUM 256
+#define PKT_POOL_LEN (2 * 1024)
+
+/* Maximum number of segments test is prepared to receive per outgoing packet */
+#define MAX_NUM_SEG 256
+
+/* Pktio interface info
+ */
+typedef struct {
+ const char *name;
+ odp_pktio_t hdl;
+ odp_pktout_queue_t pktout;
+ odp_pktin_queue_t pktin;
+ odp_pktio_capability_t capa;
+} pktio_info_t;
+
+/* Interface names used for testing */
+static const char *iface_name[MAX_NUM_IFACES];
+
+/* Test interfaces */
+static pktio_info_t pktios[MAX_NUM_IFACES];
+static pktio_info_t *pktio_a;
+static pktio_info_t *pktio_b;
+
+/* Number of interfaces being used (1=loopback, 2=pair) */
+static int num_ifaces;
+
+/* Some interface types cannot be restarted.
+ * These control test case execution in that case. */
+static int num_starts;
+static int disable_restart;
+
+/* While testing real-world interfaces additional time may be needed for
+ * external network to enable link to pktio interface that just become up. */
+static int wait_for_network;
+
+/* LSO test packet pool */
+odp_pool_t lso_pool = ODP_POOL_INVALID;
+
+/* Check test packet size */
+ODP_STATIC_ASSERT(sizeof(test_packet_ipv4_udp_1500) == 1500, "error: size is not 1500");
+ODP_STATIC_ASSERT(sizeof(test_packet_ipv4_udp_325) == 325, "error: size is not 325");
+ODP_STATIC_ASSERT(sizeof(test_packet_custom_eth_1) == 723, "error: size is not 723");
+
+static inline void wait_linkup(odp_pktio_t pktio)
+{
+ /* wait 1 second for link up */
+ uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
+ int wait_num = 100;
+ int i;
+ int ret = -1;
+
+ for (i = 0; i < wait_num; i++) {
+ ret = odp_pktio_link_status(pktio);
+ if (ret == ODP_PKTIO_LINK_STATUS_UNKNOWN || ret == ODP_PKTIO_LINK_STATUS_UP)
+ break;
+ /* link is down, call status again after delay */
+ odp_time_wait_ns(wait_ns);
+ }
+}
+
+static int pkt_pool_create(void)
+{
+ odp_pool_capability_t capa;
+ odp_pool_param_t params;
+
+ if (odp_pool_capability(&capa) != 0) {
+ ODPH_ERR("Pool capability failed\n");
+ return -1;
+ }
+
+ if (capa.pkt.max_num && capa.pkt.max_num < PKT_POOL_NUM) {
+ ODPH_ERR("Packet pool size not supported. Max %" PRIu32 "\n", capa.pkt.max_num);
+ return -1;
+ } else if (capa.pkt.max_len && capa.pkt.max_len < PKT_POOL_LEN) {
+ ODPH_ERR("Packet length not supported.\n");
+ return -1;
+ } else if (capa.pkt.max_seg_len &&
+ capa.pkt.max_seg_len < PKT_POOL_LEN) {
+ ODPH_ERR("Segment length not supported.\n");
+ return -1;
+ }
+
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = PKT_POOL_LEN;
+ params.pkt.len = PKT_POOL_LEN;
+ params.pkt.num = PKT_POOL_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ lso_pool = odp_pool_create("lso_pool", &params);
+ if (lso_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Packet pool create failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static odp_pktio_t create_pktio(int idx, const char *name, odp_pool_t pool)
+{
+ odp_pktio_t pktio;
+ odp_pktio_config_t config;
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t *capa;
+ int tx = (idx == 0) ? 1 : 0;
+ int rx = (idx == 0) ? 0 : 1;
+
+ if (num_ifaces == 1) {
+ tx = 1;
+ rx = 1;
+ }
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(name, pool, &pktio_param);
+ pktios[idx].hdl = pktio;
+ pktios[idx].name = name;
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Failed to open %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (odp_pktio_capability(pktio, &pktios[idx].capa)) {
+ ODPH_ERR("Pktio capa failed: %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+
+ capa = &pktios[idx].capa;
+
+ odp_pktio_config_init(&config);
+
+ if (tx) {
+ if (capa->config.enable_lso)
+ config.enable_lso = 1;
+ else
+ ODPH_DBG("LSO not supported\n");
+ }
+
+ if (rx) {
+ config.parser.layer = ODP_PROTO_LAYER_ALL;
+ if (capa->config.pktin.bit.ipv4_chksum)
+ config.pktin.bit.ipv4_chksum = 1;
+ else
+ ODPH_DBG("IPv4 checksum not verified\n");
+ }
+
+ if (odp_pktio_config(pktio, &config)) {
+ ODPH_ERR("Failed to configure %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+
+ /* By default, single input and output queue is used */
+ if (odp_pktin_queue_config(pktio, NULL)) {
+ ODPH_ERR("Failed to config input queue for %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Failed to config output queue for %s\n", name);
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (wait_for_network)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
+
+ return pktio;
+}
+
+static odp_packet_t create_packet(const uint8_t *data, uint32_t len)
+{
+ odp_packet_t pkt;
+
+ pkt = odp_packet_alloc(lso_pool, len);
+ if (pkt == ODP_PACKET_INVALID)
+ return ODP_PACKET_INVALID;
+
+ if (odp_packet_copy_from_mem(pkt, 0, len, data)) {
+ ODPH_ERR("Failed to copy test packet data\n");
+ odp_packet_free(pkt);
+ return ODP_PACKET_INVALID;
+ }
+
+ odp_packet_l2_offset_set(pkt, 0);
+
+ return pkt;
+}
+
+static void pktio_pkt_set_macs(odp_packet_t pkt, odp_pktio_t src, odp_pktio_t dst)
+{
+ uint32_t len;
+ odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, &len);
+ int ret;
+
+ ret = odp_pktio_mac_addr(src, &eth->src, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+
+ ret = odp_pktio_mac_addr(dst, &eth->dst, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+}
+
+static int send_packets(odp_lso_profile_t lso_profile, pktio_info_t *pktio_a, pktio_info_t *pktio_b,
+ const uint8_t *data, uint32_t len, uint32_t hdr_len, uint32_t max_payload,
+ uint32_t l3_offset, int use_opt)
+{
+ odp_packet_t pkt;
+ int ret;
+ odp_packet_lso_opt_t lso_opt;
+ odp_packet_lso_opt_t *opt_ptr = NULL;
+ int retries = 10;
+
+ pkt = create_packet(data, len);
+ if (pkt == ODP_PACKET_INVALID) {
+ CU_FAIL("failed to generate test packet");
+ return -1;
+ }
+
+ pktio_pkt_set_macs(pkt, pktio_a->hdl, pktio_b->hdl);
+ CU_ASSERT(odp_packet_has_lso_request(pkt) == 0);
+
+ memset(&lso_opt, 0, sizeof(odp_packet_lso_opt_t));
+ lso_opt.lso_profile = lso_profile;
+ lso_opt.payload_offset = hdr_len;
+ lso_opt.max_payload_len = max_payload;
+
+ if (use_opt) {
+ opt_ptr = &lso_opt;
+ } else {
+ if (odp_packet_lso_request(pkt, &lso_opt)) {
+ CU_FAIL("LSO request failed");
+ return -1;
+ }
+
+ CU_ASSERT(odp_packet_has_lso_request(pkt));
+ CU_ASSERT(odp_packet_payload_offset(pkt) == hdr_len);
+ }
+
+ if (l3_offset)
+ odp_packet_l3_offset_set(pkt, l3_offset);
+
+ while (retries) {
+ ret = odp_pktout_send_lso(pktio_a->pktout, &pkt, 1, opt_ptr);
+
+ CU_ASSERT_FATAL(ret < 2);
+
+ if (ret < 0) {
+ CU_FAIL("LSO send failed\n");
+ odp_packet_free(pkt);
+ return -1;
+ }
+ if (ret == 1)
+ break;
+
+ odp_time_wait_ns(10 * ODP_TIME_MSEC_IN_NS);
+ retries--;
+ }
+
+ if (ret < 1) {
+ CU_FAIL("LSO send timeout\n");
+ odp_packet_free(pkt);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int recv_packets(pktio_info_t *pktio_info, uint64_t timeout_ns,
+ odp_packet_t *pkt_out, int max_num)
+{
+ odp_packet_t pkt;
+ odp_time_t wait_time, end;
+ int ret;
+ odp_pktin_queue_t pktin = pktio_info->pktin;
+ int num = 0;
+
+ wait_time = odp_time_local_from_ns(timeout_ns);
+ end = odp_time_sum(odp_time_local(), wait_time);
+
+ do {
+ pkt = ODP_PACKET_INVALID;
+ ret = odp_pktin_recv(pktin, &pkt, 1);
+
+ CU_ASSERT_FATAL(ret < 2);
+ if (ret < 0) {
+ CU_FAIL("Packet receive failed\n");
+ if (num)
+ odp_packet_free_multi(pkt_out, num);
+ return -1;
+ }
+
+ if (ret == 1) {
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ pkt_out[num] = pkt;
+ num++;
+ if (num == max_num) {
+ CU_FAIL("Too many packets received\n");
+ return num;
+ }
+ }
+ } while (odp_time_cmp(end, odp_time_local()) > 0);
+
+ return num;
+}
+
+static int compare_data(odp_packet_t pkt, uint32_t offset, const uint8_t *data, uint32_t len)
+{
+ uint32_t i;
+ uint8_t *u8;
+
+ for (i = 0; i < len; i++) {
+ u8 = odp_packet_offset(pkt, offset + i, NULL, NULL);
+ if (*u8 != data[i])
+ return i;
+ }
+
+ return -1;
+}
+
+static int start_interfaces(void)
+{
+ int i;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ odp_pktio_t pktio = pktios[i].hdl;
+
+ if (odp_pktio_start(pktio)) {
+ ODPH_ERR("Failed to start interface: %s\n", pktios[i].name);
+ return -1;
+ }
+
+ wait_linkup(pktio);
+ }
+
+ return 0;
+}
+
+static int stop_interfaces(void)
+{
+ int i;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ odp_pktio_t pktio = pktios[i].hdl;
+
+ if (odp_pktio_stop(pktio)) {
+ ODPH_ERR("Failed to stop interface: %s\n", pktios[i].name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int lso_suite_init(void)
+{
+ int i;
+
+ if (getenv("ODP_PKTIO_TEST_DISABLE_START_STOP"))
+ disable_restart = 1;
+
+ if (getenv("ODP_WAIT_FOR_NETWORK"))
+ wait_for_network = 1;
+
+ iface_name[0] = getenv("ODP_PKTIO_IF0");
+ iface_name[1] = getenv("ODP_PKTIO_IF1");
+ num_ifaces = 1;
+
+ if (!iface_name[0]) {
+ printf("No interfaces specified, using default \"loop\".\n");
+ iface_name[0] = "loop";
+ } else if (!iface_name[1]) {
+ printf("Using loopback interface: %s\n", iface_name[0]);
+ } else {
+ num_ifaces = 2;
+ printf("Using paired interfaces: %s %s\n",
+ iface_name[0], iface_name[1]);
+ }
+
+ if (pkt_pool_create() != 0) {
+ ODPH_ERR("Failed to create pool\n");
+ return -1;
+ }
+
+ /* Create pktios and associate input/output queues */
+ for (i = 0; i < num_ifaces; ++i) {
+ odp_pktio_t pktio;
+ const char *name = iface_name[i];
+
+ pktio = create_pktio(i, name, lso_pool);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Failed to open interface: %s\n", name);
+ return -1;
+ }
+
+ if (odp_pktout_queue(pktio, &pktios[i].pktout, 1) != 1) {
+ ODPH_ERR("Failed to get pktout queue: %s\n", name);
+ return -1;
+ }
+
+ if (odp_pktin_queue(pktio, &pktios[i].pktin, 1) != 1) {
+ ODPH_ERR("Failed to get pktin queue: %s\n", name);
+ return -1;
+ }
+ }
+
+ pktio_a = &pktios[0];
+ pktio_b = &pktios[1];
+ if (num_ifaces == 1)
+ pktio_b = pktio_a;
+
+ return 0;
+}
+
+int lso_suite_term(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ if (odp_pktio_close(pktios[i].hdl)) {
+ ODPH_ERR("Failed to close pktio: %s\n", pktios[i].name);
+ ret = -1;
+ }
+ }
+
+ if (odp_pool_destroy(lso_pool) != 0) {
+ ODPH_ERR("Failed to destroy pool\n");
+ ret = -1;
+ }
+
+ if (odp_cunit_print_inactive())
+ ret = -1;
+
+ return ret;
+}
+
+static int check_lso_custom(void)
+{
+ if (pktio_a->capa.lso.max_profiles == 0 || pktio_a->capa.lso.max_profiles_per_pktio == 0)
+ return ODP_TEST_INACTIVE;
+
+ if (pktio_a->capa.lso.proto.custom == 0 || pktio_a->capa.lso.mod_op.add_segment_num == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_lso_custom_segs(uint32_t num)
+{
+ if (check_lso_custom() == ODP_TEST_INACTIVE)
+ return ODP_TEST_INACTIVE;
+
+ if (num > pktio_a->capa.lso.max_segments)
+ return ODP_TEST_INACTIVE;
+
+ if (disable_restart && num_starts > 0)
+ return ODP_TEST_INACTIVE;
+
+ /* Run only one packet IO test case when interface restart is disabled */
+ num_starts++;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_lso_custom_segs_1(void)
+{
+ return check_lso_custom_segs(1);
+}
+
+static int check_lso_custom_segs_2(void)
+{
+ return check_lso_custom_segs(2);
+}
+
+static int check_lso_custom_segs_3(void)
+{
+ return check_lso_custom_segs(3);
+}
+
+static int check_lso_ipv4(void)
+{
+ if (pktio_a->capa.lso.max_profiles == 0 || pktio_a->capa.lso.max_profiles_per_pktio == 0)
+ return ODP_TEST_INACTIVE;
+
+ if (pktio_a->capa.lso.proto.ipv4 == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_lso_ipv4_segs(uint32_t num)
+{
+ if (check_lso_ipv4() == ODP_TEST_INACTIVE)
+ return ODP_TEST_INACTIVE;
+
+ if (num > pktio_a->capa.lso.max_segments)
+ return ODP_TEST_INACTIVE;
+
+ if (disable_restart && num_starts > 0)
+ return ODP_TEST_INACTIVE;
+
+ num_starts++;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int check_lso_ipv4_segs_1(void)
+{
+ return check_lso_ipv4_segs(1);
+}
+
+static int check_lso_ipv4_segs_2(void)
+{
+ return check_lso_ipv4_segs(2);
+}
+
+static int check_lso_ipv4_segs_3(void)
+{
+ return check_lso_ipv4_segs(3);
+}
+
+static void lso_capability(void)
+{
+ /* LSO not supported when max_profiles is zero */
+ if (pktio_a->capa.lso.max_profiles == 0 || pktio_a->capa.lso.max_profiles_per_pktio == 0)
+ return;
+
+ CU_ASSERT(pktio_a->capa.lso.max_profiles >= pktio_a->capa.lso.max_profiles_per_pktio);
+ CU_ASSERT(pktio_a->capa.lso.max_packet_segments > 0);
+ /* At least 32 bytes of payload */
+ CU_ASSERT(pktio_a->capa.lso.max_payload_len >= 32);
+ /* LSO can create at least two segments */
+ CU_ASSERT(pktio_a->capa.lso.max_segments > 1);
+ /* LSO can copy at least Ethernet header to segments */
+ CU_ASSERT(pktio_a->capa.lso.max_payload_offset >= 14);
+
+ if (pktio_a->capa.lso.proto.custom) {
+ CU_ASSERT(pktio_a->capa.lso.max_num_custom > 0);
+
+ CU_ASSERT(pktio_a->capa.lso.mod_op.add_segment_num ||
+ pktio_a->capa.lso.mod_op.add_payload_len ||
+ pktio_a->capa.lso.mod_op.add_payload_offset)
+ }
+}
+
+static void lso_create_ipv4_profile(void)
+{
+ odp_lso_profile_param_t param;
+ odp_lso_profile_t profile;
+
+ odp_lso_profile_param_init(&param);
+ CU_ASSERT(param.lso_proto == ODP_LSO_PROTO_NONE);
+ CU_ASSERT(param.custom.num_custom == 0);
+
+ param.lso_proto = ODP_LSO_PROTO_IPV4;
+
+ profile = odp_lso_profile_create(pktio_a->hdl, &param);
+ CU_ASSERT_FATAL(profile != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile) == 0);
+}
+
+static void lso_create_custom_profile(void)
+{
+ odp_lso_profile_param_t param_0, param_1;
+ odp_lso_profile_t profile_0, profile_1;
+
+ odp_lso_profile_param_init(&param_0);
+ CU_ASSERT(param_0.lso_proto == ODP_LSO_PROTO_NONE);
+ CU_ASSERT(param_0.custom.num_custom == 0);
+
+ param_0.lso_proto = ODP_LSO_PROTO_CUSTOM;
+ param_0.custom.num_custom = 1;
+ param_0.custom.field[0].mod_op = ODP_LSO_ADD_SEGMENT_NUM;
+ param_0.custom.field[0].offset = 16;
+ param_0.custom.field[0].size = 2;
+
+ profile_0 = odp_lso_profile_create(pktio_a->hdl, &param_0);
+ CU_ASSERT_FATAL(profile_0 != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile_0) == 0);
+
+ if (pktio_a->capa.lso.max_profiles < 2 || pktio_a->capa.lso.max_num_custom < 3)
+ return;
+
+ if (pktio_a->capa.lso.mod_op.add_payload_len == 0 ||
+ pktio_a->capa.lso.mod_op.add_payload_offset == 0)
+ return;
+
+ odp_lso_profile_param_init(&param_1);
+ param_1.lso_proto = ODP_LSO_PROTO_CUSTOM;
+ param_1.custom.num_custom = 3;
+ param_1.custom.field[0].mod_op = ODP_LSO_ADD_PAYLOAD_LEN;
+ param_1.custom.field[0].offset = 14;
+ param_1.custom.field[0].size = 2;
+ param_1.custom.field[1].mod_op = ODP_LSO_ADD_SEGMENT_NUM;
+ param_1.custom.field[1].offset = 16;
+ param_1.custom.field[1].size = 2;
+ param_1.custom.field[2].mod_op = ODP_LSO_ADD_PAYLOAD_OFFSET;
+ param_1.custom.field[2].offset = 18;
+ param_1.custom.field[2].size = 2;
+
+ profile_0 = odp_lso_profile_create(pktio_a->hdl, &param_0);
+ CU_ASSERT_FATAL(profile_0 != ODP_LSO_PROFILE_INVALID);
+
+ profile_1 = odp_lso_profile_create(pktio_a->hdl, &param_1);
+ CU_ASSERT_FATAL(profile_1 != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile_1) == 0);
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile_0) == 0);
+}
+
+static void test_lso_request_clear(odp_lso_profile_t lso_profile, const uint8_t *data,
+ uint32_t len, uint32_t hdr_len, uint32_t max_payload)
+{
+ odp_packet_t pkt;
+ odp_packet_lso_opt_t lso_opt;
+
+ memset(&lso_opt, 0, sizeof(odp_packet_lso_opt_t));
+ lso_opt.lso_profile = lso_profile;
+ lso_opt.payload_offset = hdr_len;
+ lso_opt.max_payload_len = max_payload;
+
+ pkt = create_packet(data, len);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_lso_request(pkt) == 0);
+ CU_ASSERT(odp_packet_lso_request(pkt, &lso_opt) == 0);
+ CU_ASSERT(odp_packet_has_lso_request(pkt) != 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == hdr_len);
+ odp_packet_lso_request_clr(pkt);
+ CU_ASSERT(odp_packet_has_lso_request(pkt) == 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == hdr_len);
+ CU_ASSERT(odp_packet_payload_offset_set(pkt, ODP_PACKET_OFFSET_INVALID) == 0);
+ CU_ASSERT(odp_packet_payload_offset(pkt) == ODP_PACKET_OFFSET_INVALID);
+
+ odp_packet_free(pkt);
+}
+
+static void lso_send_custom_eth(const uint8_t *test_packet, uint32_t pkt_len, uint32_t max_payload,
+ int use_opt)
+{
+ int i, ret, num;
+ odp_lso_profile_param_t param;
+ odp_lso_profile_t profile;
+ uint32_t offset, len, payload_len, payload_sum;
+ uint16_t segnum;
+ odp_packet_t pkt_out[MAX_NUM_SEG];
+ /* Ethernet 14B + custom headers 8B */
+ uint32_t hdr_len = 22;
+ /* Offset to "segment number" field */
+ uint32_t segnum_offset = 16;
+ uint32_t sent_payload = pkt_len - hdr_len;
+
+ odp_lso_profile_param_init(&param);
+ param.lso_proto = ODP_LSO_PROTO_CUSTOM;
+ param.custom.num_custom = 1;
+ param.custom.field[0].mod_op = ODP_LSO_ADD_SEGMENT_NUM;
+ param.custom.field[0].offset = segnum_offset;
+ param.custom.field[0].size = 2;
+
+ profile = odp_lso_profile_create(pktio_a->hdl, &param);
+ CU_ASSERT_FATAL(profile != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(start_interfaces() == 0);
+
+ test_lso_request_clear(profile, test_packet, pkt_len, hdr_len, max_payload);
+
+ ret = send_packets(profile, pktio_a, pktio_b, test_packet, pkt_len, hdr_len,
+ max_payload, 0, use_opt);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ODPH_DBG("\n Sent payload length: %u bytes\n", sent_payload);
+
+ /* Wait a bit to receive all created segments. Timeout and MAX_NUM_SEG values should be
+ * large enough to ensure that we receive all created segments. */
+ num = recv_packets(pktio_b, 100 * ODP_TIME_MSEC_IN_NS, pkt_out, MAX_NUM_SEG);
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num < MAX_NUM_SEG);
+
+ offset = hdr_len;
+ payload_sum = 0;
+ segnum = 0xffff;
+ for (i = 0; i < num; i++) {
+ odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt_out[i], NULL);
+
+ /* Filter out possible non-test packets */
+ if (odp_be_to_cpu_16(eth->type) != 0x88B5)
+ continue;
+
+ len = odp_packet_len(pkt_out[i]);
+ payload_len = len - hdr_len;
+
+ ret = odp_packet_copy_to_mem(pkt_out[i], segnum_offset, 2, &segnum);
+
+ if (ret == 0) {
+ segnum = odp_be_to_cpu_16(segnum);
+ CU_ASSERT(segnum == i);
+ } else {
+ CU_FAIL("Seg num field read failed\n");
+ }
+
+ ODPH_DBG(" LSO segment[%u] payload: %u bytes\n", segnum, payload_len);
+
+ CU_ASSERT(payload_len <= max_payload);
+
+ if (compare_data(pkt_out[i], hdr_len,
+ test_packet_custom_eth_1 + offset, payload_len) >= 0) {
+ ODPH_ERR(" Payload compare failed at offset %u\n", offset);
+ CU_FAIL("Payload compare failed\n");
+ }
+
+ offset += payload_len;
+ payload_sum += payload_len;
+ }
+
+ ODPH_DBG(" Received payload length: %u bytes\n", payload_sum);
+
+ CU_ASSERT(payload_sum == sent_payload);
+
+ if (num > 0)
+ odp_packet_free_multi(pkt_out, num);
+
+ CU_ASSERT_FATAL(stop_interfaces() == 0);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile) == 0);
+}
+
+static void lso_send_custom_eth_723(uint32_t max_payload, int use_opt)
+{
+ uint32_t pkt_len = sizeof(test_packet_custom_eth_1);
+
+ if (max_payload > pktio_a->capa.lso.max_payload_len)
+ max_payload = pktio_a->capa.lso.max_payload_len;
+
+ lso_send_custom_eth(test_packet_custom_eth_1, pkt_len, max_payload, use_opt);
+}
+
+/* No segmentation needed: packet size 723 bytes, LSO segment payload 800 bytes */
+static void lso_send_custom_eth_723_800_pkt_meta(void)
+{
+ lso_send_custom_eth_723(800, 0);
+}
+
+static void lso_send_custom_eth_723_800_opt(void)
+{
+ lso_send_custom_eth_723(800, 1);
+}
+
+/* At least 2 segments: packet size 723 bytes, LSO segment payload 500 bytes */
+static void lso_send_custom_eth_723_500_pkt_meta(void)
+{
+ lso_send_custom_eth_723(500, 0);
+}
+
+static void lso_send_custom_eth_723_500_opt(void)
+{
+ lso_send_custom_eth_723(500, 1);
+}
+
+/* At least 3 segments: packet size 723 bytes, LSO segment payload 288 bytes */
+static void lso_send_custom_eth_723_288_pkt_meta(void)
+{
+ lso_send_custom_eth_723(288, 0);
+}
+
+static void lso_send_custom_eth_723_288_opt(void)
+{
+ lso_send_custom_eth_723(288, 1);
+}
+
+static void lso_send_ipv4(const uint8_t *test_packet, uint32_t pkt_len, uint32_t max_payload,
+ int use_opt)
+{
+ int i, ret, num;
+ odp_lso_profile_param_t param;
+ odp_lso_profile_t profile;
+ uint32_t offset, len, payload_len, payload_sum;
+ odp_packet_t packet[MAX_NUM_SEG];
+ /* Ethernet 14B + IPv4 header 20B */
+ uint32_t hdr_len = 34;
+ uint32_t sent_payload = pkt_len - hdr_len;
+
+ odp_lso_profile_param_init(&param);
+ param.lso_proto = ODP_LSO_PROTO_IPV4;
+
+ profile = odp_lso_profile_create(pktio_a->hdl, &param);
+ CU_ASSERT_FATAL(profile != ODP_LSO_PROFILE_INVALID);
+
+ CU_ASSERT_FATAL(start_interfaces() == 0);
+
+ test_lso_request_clear(profile, test_packet, pkt_len, hdr_len, max_payload);
+
+ ret = send_packets(profile, pktio_a, pktio_b, test_packet, pkt_len,
+ hdr_len, max_payload, 14, use_opt);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ODPH_DBG("\n Sent payload length: %u bytes\n", sent_payload);
+
+ /* Wait a bit to receive all created segments. Timeout and MAX_NUM_SEG values should be
+ * large enough to ensure that we receive all created segments. */
+ num = recv_packets(pktio_b, 100 * ODP_TIME_MSEC_IN_NS, packet, MAX_NUM_SEG);
+ CU_ASSERT(num > 0);
+ CU_ASSERT(num < MAX_NUM_SEG);
+
+ offset = hdr_len;
+ payload_sum = 0;
+ for (i = 0; i < num; i++) {
+ if (!odp_packet_has_ipv4(packet[i]))
+ continue;
+
+ odph_ipv4hdr_t *ip = odp_packet_l3_ptr(packet[i], NULL);
+
+ /* Filter out possible non-test packets */
+ if (odp_be_to_cpu_32(ip->dst_addr) != 0xc0a80101 ||
+ odp_be_to_cpu_32(ip->src_addr) != 0xc0a80102)
+ continue;
+
+ len = odp_packet_len(packet[i]);
+ payload_len = len - hdr_len;
+
+ ODPH_DBG(" LSO segment[%i] payload: %u bytes\n", i, payload_len);
+
+ CU_ASSERT(odp_packet_has_error(packet[i]) == 0);
+ CU_ASSERT(payload_len <= max_payload);
+
+ if (pkt_len > max_payload)
+ CU_ASSERT(odp_packet_has_ipfrag(packet[i]));
+
+ if (compare_data(packet[i], hdr_len, test_packet + offset, payload_len) >= 0) {
+ ODPH_ERR(" Payload compare failed at offset %u\n", offset);
+ CU_FAIL("Payload compare failed\n");
+ }
+
+ offset += payload_len;
+ payload_sum += payload_len;
+ }
+
+ ODPH_DBG(" Received payload length: %u bytes\n", payload_sum);
+
+ CU_ASSERT(payload_sum == sent_payload);
+
+ if (num > 0)
+ odp_packet_free_multi(packet, num);
+
+ CU_ASSERT_FATAL(stop_interfaces() == 0);
+
+ CU_ASSERT_FATAL(odp_lso_profile_destroy(profile) == 0);
+}
+
+static void lso_send_ipv4_udp_325(uint32_t max_payload, int use_opt)
+{
+ uint32_t pkt_len = sizeof(test_packet_ipv4_udp_325);
+
+ if (max_payload > pktio_a->capa.lso.max_payload_len)
+ max_payload = pktio_a->capa.lso.max_payload_len;
+
+ lso_send_ipv4(test_packet_ipv4_udp_325, pkt_len, max_payload, use_opt);
+}
+
+static void lso_send_ipv4_udp_1500(uint32_t max_payload, int use_opt)
+{
+ uint32_t pkt_len = sizeof(test_packet_ipv4_udp_1500);
+
+ if (max_payload > pktio_a->capa.lso.max_payload_len)
+ max_payload = pktio_a->capa.lso.max_payload_len;
+
+ lso_send_ipv4(test_packet_ipv4_udp_1500, pkt_len, max_payload, use_opt);
+}
+
+/* No segmentation needed: packet size 325 bytes, LSO segment payload 700 bytes */
+static void lso_send_ipv4_325_700_pkt_meta(void)
+{
+ lso_send_ipv4_udp_325(700, 0);
+}
+
+static void lso_send_ipv4_325_700_opt(void)
+{
+ lso_send_ipv4_udp_325(700, 1);
+}
+
+/* At least 2 segments: packet size 1500 bytes, LSO segment payload 1000 bytes */
+static void lso_send_ipv4_1500_1000_pkt_meta(void)
+{
+ lso_send_ipv4_udp_1500(1000, 0);
+}
+
+static void lso_send_ipv4_1500_1000_opt(void)
+{
+ lso_send_ipv4_udp_1500(1000, 1);
+}
+
+/* At least 3 segments: packet size 1500 bytes, LSO segment payload 700 bytes */
+static void lso_send_ipv4_1500_700_pkt_meta(void)
+{
+ lso_send_ipv4_udp_1500(700, 0);
+}
+
+static void lso_send_ipv4_1500_700_opt(void)
+{
+ lso_send_ipv4_udp_1500(700, 1);
+}
+
+odp_testinfo_t lso_suite[] = {
+ ODP_TEST_INFO(lso_capability),
+ ODP_TEST_INFO_CONDITIONAL(lso_create_ipv4_profile, check_lso_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(lso_create_custom_profile, check_lso_custom),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_325_700_pkt_meta, check_lso_ipv4_segs_1),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_325_700_opt, check_lso_ipv4_segs_1),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_1500_1000_pkt_meta, check_lso_ipv4_segs_2),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_1500_1000_opt, check_lso_ipv4_segs_2),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_1500_700_pkt_meta, check_lso_ipv4_segs_3),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_ipv4_1500_700_opt, check_lso_ipv4_segs_3),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_800_pkt_meta, check_lso_custom_segs_1),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_800_opt, check_lso_custom_segs_1),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_500_pkt_meta, check_lso_custom_segs_2),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_500_opt, check_lso_custom_segs_2),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_288_pkt_meta, check_lso_custom_segs_3),
+ ODP_TEST_INFO_CONDITIONAL(lso_send_custom_eth_723_288_opt, check_lso_custom_segs_3),
+ ODP_TEST_INFO_NULL
+};
diff --git a/test/validation/api/pktio/lso.h b/test/validation/api/pktio/lso.h
new file mode 100644
index 000000000..ce3dc7b64
--- /dev/null
+++ b/test/validation/api/pktio/lso.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2020, Nokia
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef _ODP_TEST_PKTIO_LSO_H_
+#define _ODP_TEST_PKTIO_LSO_H_
+
+#include <odp_cunit_common.h>
+
+/* test array init/term functions: */
+int lso_suite_term(void);
+int lso_suite_init(void);
+
+/* test arrays: */
+extern odp_testinfo_t lso_suite[];
+
+#endif
diff --git a/test/validation/api/pktio/parser.c b/test/validation/api/pktio/parser.c
new file mode 100644
index 000000000..7d243877c
--- /dev/null
+++ b/test/validation/api/pktio/parser.c
@@ -0,0 +1,609 @@
+/* Copyright (c) 2017-2018, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+#include <test_packet_ipv4.h>
+#include <test_packet_ipv6.h>
+
+#include <odp/helper/odph_api.h>
+
+#include <stdlib.h>
+#include "parser.h"
+
+#define MAX_NUM_IFACES 2
+#define PKT_POOL_NUM 256
+#define PKT_POOL_BUF_LEN (2 * 1024)
+
+/**
+ * local container for pktio attributes
+ */
+typedef struct {
+ const char *name;
+ odp_pktio_t hdl;
+ odp_pktout_queue_t pktout;
+ odp_pktin_queue_t pktin;
+} pktio_info_t;
+
+/** Interface names used for testing */
+static const char *iface_name[MAX_NUM_IFACES];
+
+/** Test interfaces */
+pktio_info_t pktios[MAX_NUM_IFACES];
+pktio_info_t *pktio_a;
+pktio_info_t *pktio_b;
+
+/** Number of interfaces being used (1=loopback, 2=pair) */
+static int num_ifaces;
+
+/** While testing real-world interfaces additional time may be needed for
+ * external network to enable link to pktio interface that just become up.
+ */
+static bool wait_for_network;
+
+/** Parser packet pool */
+odp_pool_t parser_pool = ODP_POOL_INVALID;
+
+static inline void wait_linkup(odp_pktio_t pktio)
+{
+ /* wait 1 second for link up */
+ uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
+ int wait_num = 100;
+ int i;
+ int ret = -1;
+
+ for (i = 0; i < wait_num; i++) {
+ ret = odp_pktio_link_status(pktio);
+ if (ret == ODP_PKTIO_LINK_STATUS_UNKNOWN || ret == ODP_PKTIO_LINK_STATUS_UP)
+ break;
+ /* link is down, call status again after delay */
+ odp_time_wait_ns(wait_ns);
+ }
+}
+
+static int pkt_pool_create(void)
+{
+ odp_pool_capability_t capa;
+ odp_pool_param_t params;
+
+ if (odp_pool_capability(&capa) != 0) {
+ ODPH_ERR("Unable to query pool capability\n");
+ return -1;
+ }
+
+ if (capa.pkt.max_num && capa.pkt.max_num < PKT_POOL_NUM) {
+ ODPH_ERR("Packet pool size not supported: MAX=%" PRIu32 "\n", capa.pkt.max_num);
+ return -1;
+ } else if (capa.pkt.max_len && capa.pkt.max_len < PKT_POOL_BUF_LEN) {
+ ODPH_ERR("Packet length not supported\n");
+ return -1;
+ } else if (capa.pkt.max_seg_len &&
+ capa.pkt.max_seg_len < PKT_POOL_BUF_LEN) {
+ ODPH_ERR("Segment length not supported\n");
+ return -1;
+ }
+
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = PKT_POOL_BUF_LEN;
+ params.pkt.len = PKT_POOL_BUF_LEN;
+ params.pkt.num = PKT_POOL_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ parser_pool = odp_pool_create("pkt_pool_default", &params);
+ if (parser_pool == ODP_POOL_INVALID) {
+ ODPH_ERR("Packet pool create failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static odp_pktio_t create_pktio(int iface_idx, odp_pool_t pool)
+{
+ odp_pktio_t pktio;
+ odp_pktio_config_t config;
+ odp_pktio_param_t pktio_param;
+ const char *iface = iface_name[iface_idx];
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface, pool, &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Failed to open %s\n", iface);
+ return ODP_PKTIO_INVALID;
+ }
+
+ odp_pktio_config_init(&config);
+ config.parser.layer = ODP_PROTO_LAYER_ALL;
+ if (odp_pktio_config(pktio, &config)) {
+ ODPH_ERR("Failed to configure %s\n", iface);
+ return ODP_PKTIO_INVALID;
+ }
+
+ /* By default, single input and output queue is used */
+ if (odp_pktin_queue_config(pktio, NULL)) {
+ ODPH_ERR("Failed to config input queue for %s\n", iface);
+ return ODP_PKTIO_INVALID;
+ }
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Failed to config output queue for %s\n", iface);
+ return ODP_PKTIO_INVALID;
+ }
+
+ if (wait_for_network)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
+
+ return pktio;
+}
+
+static odp_packet_t create_packet(const uint8_t *data, uint32_t len)
+{
+ odp_packet_t pkt;
+
+ pkt = odp_packet_alloc(parser_pool, len);
+ if (pkt == ODP_PACKET_INVALID)
+ return ODP_PACKET_INVALID;
+
+ if (odp_packet_copy_from_mem(pkt, 0, len, data)) {
+ ODPH_ERR("Failed to copy test packet data\n");
+ odp_packet_free(pkt);
+ return ODP_PACKET_INVALID;
+ }
+
+ odp_packet_l2_offset_set(pkt, 0);
+
+ return pkt;
+}
+
+/**
+ * Receive incoming packets and compare them to the original. Function returns
+ * a valid packet handle only when the received packet matches to the original
+ * packet.
+ */
+static odp_packet_t recv_and_cmp_packet(odp_pktin_queue_t pktin,
+ odp_packet_t orig_pkt, uint64_t ns)
+{
+ odp_packet_t pkt = ODP_PACKET_INVALID;
+ odp_time_t wait_time, end;
+ uint32_t orig_len;
+ uint8_t *orig_data;
+
+ orig_len = odp_packet_len(orig_pkt);
+ orig_data = odp_packet_data(orig_pkt);
+ wait_time = odp_time_local_from_ns(ns);
+ end = odp_time_sum(odp_time_local(), wait_time);
+
+ do {
+ int ret;
+ odp_packet_t tmp_pkt;
+
+ ret = odp_pktin_recv(pktin, &tmp_pkt, 1);
+ if (ret < 0)
+ break;
+
+ if (ret == 1) {
+ uint32_t len;
+ uint8_t *data;
+
+ len = odp_packet_len(tmp_pkt);
+ data = odp_packet_data(tmp_pkt);
+
+ if (len == orig_len &&
+ memcmp(data, orig_data, len) == 0) {
+ pkt = tmp_pkt;
+ break;
+ }
+ odp_packet_free(tmp_pkt);
+ }
+ } while (odp_time_cmp(end, odp_time_local()) > 0);
+
+ return pkt;
+}
+
+static void pktio_pkt_set_macs(odp_packet_t pkt, odp_pktio_t src, odp_pktio_t dst)
+{
+ uint32_t len;
+ odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, &len);
+ int ret;
+
+ ret = odp_pktio_mac_addr(src, &eth->src, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+
+ ret = odp_pktio_mac_addr(dst, &eth->dst, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+}
+
+/**
+ * Creates a test packet from data array and loops it through the test pktio
+ * interfaces forcing packet parsing.
+ */
+static odp_packet_t loopback_packet(pktio_info_t *pktio_a,
+ pktio_info_t *pktio_b, const uint8_t *data,
+ uint32_t len)
+{
+ odp_packet_t pkt;
+ odp_packet_t sent_pkt;
+
+ pkt = create_packet(data, len);
+ if (pkt == ODP_PACKET_INVALID) {
+ CU_FAIL("failed to generate test packet");
+ return ODP_PACKET_INVALID;
+ }
+
+ pktio_pkt_set_macs(pkt, pktio_a->hdl, pktio_b->hdl);
+
+ sent_pkt = odp_packet_copy(pkt, parser_pool);
+ if (sent_pkt == ODP_PACKET_INVALID) {
+ CU_FAIL_FATAL("failed to copy test packet");
+ odp_packet_free(pkt);
+ return ODP_PACKET_INVALID;
+ }
+
+ while (1) {
+ int ret = odp_pktout_send(pktio_a->pktout, &pkt, 1);
+
+ if (ret < 0) {
+ CU_FAIL_FATAL("failed to send test packet");
+ odp_packet_free(pkt);
+ odp_packet_free(sent_pkt);
+ return ODP_PACKET_INVALID;
+ }
+ if (ret == 1)
+ break;
+ }
+
+ /* and wait for them to arrive back */
+ pkt = recv_and_cmp_packet(pktio_b->pktin, sent_pkt, ODP_TIME_SEC_IN_NS);
+ odp_packet_free(sent_pkt);
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_input(pkt) == pktio_b->hdl);
+ CU_ASSERT(odp_packet_has_error(pkt) == 0);
+
+ return pkt;
+}
+
+static void parser_test_arp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_arp,
+ sizeof(test_packet_arp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_arp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv4_icmp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_icmp,
+ sizeof(test_packet_ipv4_icmp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_icmp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv4_tcp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_tcp,
+ sizeof(test_packet_ipv4_tcp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_tcp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv4_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_udp,
+ sizeof(test_packet_ipv4_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_vlan_ipv4_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_vlan_ipv4_udp,
+ sizeof(test_packet_vlan_ipv4_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_vlan(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_vlan_qinq_ipv4_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_vlan_qinq_ipv4_udp,
+ sizeof(test_packet_vlan_qinq_ipv4_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_vlan(pkt));
+ CU_ASSERT(odp_packet_has_vlan_qinq(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv4_sctp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv4_sctp,
+ sizeof(test_packet_ipv4_sctp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ CU_ASSERT(odp_packet_has_sctp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv6(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv6_icmp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_icmp,
+ sizeof(test_packet_ipv6_icmp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_icmp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv6_tcp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_tcp,
+ sizeof(test_packet_ipv6_tcp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_tcp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv6_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_udp,
+ sizeof(test_packet_ipv6_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_vlan_ipv6_udp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_vlan_ipv6_udp,
+ sizeof(test_packet_vlan_ipv6_udp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_vlan(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_sctp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+static void parser_test_ipv6_sctp(void)
+{
+ odp_packet_t pkt;
+
+ pkt = loopback_packet(pktio_a, pktio_b, test_packet_ipv6_sctp,
+ sizeof(test_packet_ipv6_sctp));
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ CU_ASSERT(odp_packet_has_ipv6(pkt));
+ CU_ASSERT(odp_packet_has_sctp(pkt));
+
+ CU_ASSERT(!odp_packet_has_ipv4(pkt));
+ CU_ASSERT(!odp_packet_has_tcp(pkt));
+ CU_ASSERT(!odp_packet_has_udp(pkt));
+
+ odp_packet_free(pkt);
+}
+
+int parser_suite_init(void)
+{
+ int i;
+
+ if (getenv("ODP_WAIT_FOR_NETWORK"))
+ wait_for_network = true;
+
+ iface_name[0] = getenv("ODP_PKTIO_IF0");
+ iface_name[1] = getenv("ODP_PKTIO_IF1");
+ num_ifaces = 1;
+
+ if (!iface_name[0]) {
+ printf("No interfaces specified, using default \"loop\".\n");
+ iface_name[0] = "loop";
+ } else if (!iface_name[1]) {
+ printf("Using loopback interface: %s\n", iface_name[0]);
+ } else {
+ num_ifaces = 2;
+ printf("Using paired interfaces: %s %s\n",
+ iface_name[0], iface_name[1]);
+ }
+
+ if (pkt_pool_create() != 0) {
+ ODPH_ERR("Failed to create parser pool\n");
+ return -1;
+ }
+
+ /* Create pktios and associate input/output queues */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio_info_t *io;
+
+ io = &pktios[i];
+ io->name = iface_name[i];
+ io->hdl = create_pktio(i, parser_pool);
+ if (io->hdl == ODP_PKTIO_INVALID) {
+ ODPH_ERR("Failed to open iface");
+ return -1;
+ }
+
+ if (odp_pktout_queue(io->hdl, &io->pktout, 1) != 1) {
+ ODPH_ERR("Failed to start iface: %s\n", io->name);
+ return -1;
+ }
+
+ if (odp_pktin_queue(io->hdl, &io->pktin, 1) != 1) {
+ ODPH_ERR("Failed to start iface: %s\n", io->name);
+ return -1;
+ }
+
+ if (odp_pktio_start(io->hdl)) {
+ ODPH_ERR("Failed to start iface: %s\n", io->name);
+ return -1;
+ }
+
+ wait_linkup(io->hdl);
+ }
+
+ pktio_a = &pktios[0];
+ pktio_b = &pktios[1];
+ if (num_ifaces == 1)
+ pktio_b = pktio_a;
+
+ return 0;
+}
+
+int parser_suite_term(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ if (odp_pktio_stop(pktios[i].hdl)) {
+ ODPH_ERR("Failed to stop pktio: %s\n", pktios[i].name);
+ ret = -1;
+ }
+ if (odp_pktio_close(pktios[i].hdl)) {
+ ODPH_ERR("Failed to close pktio: %s\n", pktios[i].name);
+ ret = -1;
+ }
+ }
+
+ if (odp_pool_destroy(parser_pool) != 0) {
+ ODPH_ERR("Failed to destroy packet pool\n");
+ ret = -1;
+ }
+
+ if (odp_cunit_print_inactive())
+ ret = -1;
+
+ return ret;
+}
+
+/**
+ * Certain tests can only be run with 'loop' pktio.
+ */
+static int loop_pktio(void)
+{
+ if (strcmp(iface_name[0], "loop") == 0)
+ return ODP_TEST_ACTIVE;
+ else
+ return ODP_TEST_INACTIVE;
+}
+
+odp_testinfo_t parser_suite[] = {
+ ODP_TEST_INFO(parser_test_arp),
+ ODP_TEST_INFO(parser_test_ipv4_icmp),
+ ODP_TEST_INFO(parser_test_ipv4_tcp),
+ ODP_TEST_INFO(parser_test_ipv4_udp),
+ ODP_TEST_INFO_CONDITIONAL(parser_test_vlan_ipv4_udp, loop_pktio),
+ ODP_TEST_INFO_CONDITIONAL(parser_test_vlan_qinq_ipv4_udp, loop_pktio),
+ ODP_TEST_INFO(parser_test_ipv4_sctp),
+ ODP_TEST_INFO(parser_test_ipv6_icmp),
+ ODP_TEST_INFO(parser_test_ipv6_tcp),
+ ODP_TEST_INFO(parser_test_ipv6_udp),
+ ODP_TEST_INFO_CONDITIONAL(parser_test_vlan_ipv6_udp, loop_pktio),
+ ODP_TEST_INFO(parser_test_ipv6_sctp),
+ ODP_TEST_INFO_NULL
+};
diff --git a/test/validation/api/pktio/parser.h b/test/validation/api/pktio/parser.h
new file mode 100644
index 000000000..4424737fd
--- /dev/null
+++ b/test/validation/api/pktio/parser.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2017-2018, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef _ODP_TEST_PARSER_H_
+#define _ODP_TEST_PARSER_H_
+
+#include <odp_cunit_common.h>
+
+/* test array init/term functions: */
+int parser_suite_term(void);
+int parser_suite_init(void);
+
+/* test arrays: */
+extern odp_testinfo_t parser_suite[];
+
+#endif
diff --git a/test/validation/api/pktio/pktio.c b/test/validation/api/pktio/pktio.c
new file mode 100644
index 000000000..deef4895a
--- /dev/null
+++ b/test/validation/api/pktio/pktio.c
@@ -0,0 +1,5517 @@
+/* Copyright (c) 2014-2018, Linaro Limited
+ * Copyright (c) 2020-2024, Nokia
+ * Copyright (c) 2020, Marvell
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <odp_api.h>
+#include <odp_cunit_common.h>
+
+#include <odp/helper/odph_api.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include "parser.h"
+#include "lso.h"
+
+#define PKT_BUF_NUM 128
+#define PKT_BUF_SIZE (9 * 1024)
+#define PKT_LEN_NORMAL 64
+#define PKT_LEN_MAX (PKT_BUF_SIZE - ODPH_ETHHDR_LEN - \
+ ODPH_IPV4HDR_LEN - ODPH_UDPHDR_LEN)
+
+#define USE_MTU 0
+#define MAX_NUM_IFACES 2
+#define TEST_SEQ_INVALID ((uint32_t)~0)
+#define TEST_SEQ_MAGIC 0x92749451
+#define TX_BATCH_LEN 4
+#define PKTV_TX_BATCH_LEN 32
+#define PKTV_DEFAULT_SIZE 8
+#define MAX_QUEUES 128
+
+#define PKTIO_TS_INTERVAL (50 * ODP_TIME_MSEC_IN_NS)
+#define PKTIO_TS_MIN_RES 1000
+#define PKTIO_TS_MAX_RES 10000000000
+
+#define PKTIO_SRC_MAC {1, 2, 3, 4, 5, 6}
+#define PKTIO_DST_MAC {6, 5, 4, 3, 2, 1}
+#undef DEBUG_STATS
+
+/** interface names used for testing */
+static const char *iface_name[MAX_NUM_IFACES];
+
+/** number of interfaces being used (1=loopback, 2=pair) */
+static int num_ifaces;
+
+/** while testing real-world interfaces additional time may be
+ needed for external network to enable link to pktio
+ interface that just become up.*/
+static bool wait_for_network;
+
+/* Dummy global variable to avoid compiler optimizing out API calls */
+static volatile uint64_t test_pktio_dummy_u64;
+
+/** local container for pktio attributes */
+typedef struct {
+ const char *name;
+ odp_pktio_t id;
+ odp_pktout_queue_t pktout;
+ odp_queue_t queue_out;
+ odp_queue_t inq;
+ odp_pktin_mode_t in_mode;
+} pktio_info_t;
+
+/** magic number and sequence at start of UDP payload */
+typedef struct ODP_PACKED {
+ odp_u32be_t magic;
+ odp_u32be_t seq;
+} pkt_head_t;
+
+/** magic number at end of UDP payload */
+typedef struct ODP_PACKED {
+ odp_u32be_t magic;
+} pkt_tail_t;
+
+/** Run mode */
+typedef enum {
+ PKT_POOL_UNSEGMENTED,
+ PKT_POOL_SEGMENTED,
+} pkt_segmented_e;
+
+typedef enum {
+ TXRX_MODE_SINGLE,
+ TXRX_MODE_MULTI,
+ TXRX_MODE_MULTI_EVENT
+} txrx_mode_e;
+
+typedef enum {
+ RECV_TMO,
+ RECV_MQ_TMO,
+ RECV_MQ_TMO_NO_IDX,
+} recv_tmo_mode_e;
+
+typedef enum {
+ ETH_UNICAST,
+ ETH_BROADCAST,
+} eth_addr_type_e;
+
+/** size of transmitted packets */
+static uint32_t packet_len = PKT_LEN_NORMAL;
+
+/** default packet pool */
+odp_pool_t default_pkt_pool = ODP_POOL_INVALID;
+
+/** default packet vector pool */
+odp_pool_t default_pktv_pool = ODP_POOL_INVALID;
+
+/** sequence number of IP packets */
+odp_atomic_u32_t ip_seq;
+
+/** Type of pool segmentation */
+pkt_segmented_e pool_segmentation = PKT_POOL_UNSEGMENTED;
+
+odp_pool_t pool[MAX_NUM_IFACES] = {ODP_POOL_INVALID, ODP_POOL_INVALID};
+
+odp_pool_t pktv_pool[MAX_NUM_IFACES] = {ODP_POOL_INVALID, ODP_POOL_INVALID};
+
+static inline void _pktio_wait_linkup(odp_pktio_t pktio)
+{
+ /* wait 1 second for link up */
+ uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS);
+ int wait_num = 100;
+ int i;
+ int ret = -1;
+
+ for (i = 0; i < wait_num; i++) {
+ ret = odp_pktio_link_status(pktio);
+ if (ret == ODP_PKTIO_LINK_STATUS_UNKNOWN || ret == ODP_PKTIO_LINK_STATUS_UP)
+ break;
+ /* link is down, call status again after delay */
+ odp_time_wait_ns(wait_ns);
+ }
+
+ if (ret != -1) {
+ /* assert only if link state supported and
+ * it's down. */
+ CU_ASSERT_FATAL(ret == 1);
+ }
+}
+
+static void set_pool_len(odp_pool_param_t *params, odp_pool_capability_t *capa)
+{
+ uint32_t len;
+ uint32_t seg_len;
+
+ len = (capa->pkt.max_len && capa->pkt.max_len < PKT_BUF_SIZE) ?
+ capa->pkt.max_len : PKT_BUF_SIZE;
+ seg_len = (capa->pkt.max_seg_len && capa->pkt.max_seg_len < PKT_BUF_SIZE) ?
+ capa->pkt.max_seg_len : PKT_BUF_SIZE;
+
+ switch (pool_segmentation) {
+ case PKT_POOL_SEGMENTED:
+ /* Force segment to minimum size */
+ params->pkt.seg_len = 0;
+ params->pkt.len = len;
+ break;
+ case PKT_POOL_UNSEGMENTED:
+ default:
+ params->pkt.seg_len = seg_len;
+ params->pkt.len = len;
+ break;
+ }
+}
+
+static void pktio_pkt_set_macs(odp_packet_t pkt, odp_pktio_t src, odp_pktio_t dst,
+ eth_addr_type_e dst_addr_type)
+{
+ uint32_t len;
+ odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, &len);
+ int ret;
+
+ ret = odp_pktio_mac_addr(src, &eth->src, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+
+ if (dst_addr_type == ETH_UNICAST) {
+ ret = odp_pktio_mac_addr(dst, &eth->dst, ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ret == ODPH_ETHADDR_LEN);
+ CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE);
+ } else {
+ CU_ASSERT(odph_eth_addr_parse(&eth->dst, "ff:ff:ff:ff:ff:ff") == 0);
+ }
+}
+
+static uint32_t pktio_pkt_set_seq(odp_packet_t pkt, size_t l4_hdr_len)
+{
+ static uint32_t tstseq;
+ size_t off;
+ pkt_head_t head;
+ pkt_tail_t tail;
+
+ off = odp_packet_l4_offset(pkt);
+ if (off == ODP_PACKET_OFFSET_INVALID) {
+ CU_FAIL("packet L4 offset not set");
+ return TEST_SEQ_INVALID;
+ }
+
+ head.magic = TEST_SEQ_MAGIC;
+ head.seq = tstseq;
+
+ off += l4_hdr_len;
+ if (odp_packet_copy_from_mem(pkt, off, sizeof(head), &head) != 0)
+ return TEST_SEQ_INVALID;
+
+ tail.magic = TEST_SEQ_MAGIC;
+ off = odp_packet_len(pkt) - sizeof(pkt_tail_t);
+ if (odp_packet_copy_from_mem(pkt, off, sizeof(tail), &tail) != 0)
+ return TEST_SEQ_INVALID;
+
+ tstseq++;
+
+ return head.seq;
+}
+
+static uint32_t pktio_pkt_seq_hdr(odp_packet_t pkt, size_t l4_hdr_len)
+{
+ size_t off;
+ uint32_t seq = TEST_SEQ_INVALID;
+ pkt_head_t head;
+ pkt_tail_t tail;
+
+ if (pkt == ODP_PACKET_INVALID) {
+ ODPH_ERR("pkt invalid\n");
+ return TEST_SEQ_INVALID;
+ }
+
+ off = odp_packet_l4_offset(pkt);
+ if (off == ODP_PACKET_OFFSET_INVALID) {
+ ODPH_ERR("offset invalid\n");
+ return TEST_SEQ_INVALID;
+ }
+
+ off += l4_hdr_len;
+ if (odp_packet_copy_to_mem(pkt, off, sizeof(head), &head) != 0) {
+ ODPH_ERR("header copy failed\n");
+ return TEST_SEQ_INVALID;
+ }
+
+ if (head.magic != TEST_SEQ_MAGIC) {
+ ODPH_ERR("header magic invalid 0x%" PRIx32 "\n", head.magic);
+ odp_packet_print(pkt);
+ return TEST_SEQ_INVALID;
+ }
+
+ if (odp_packet_len(pkt) == packet_len) {
+ off = packet_len - sizeof(tail);
+ if (odp_packet_copy_to_mem(pkt, off, sizeof(tail),
+ &tail) != 0) {
+ ODPH_ERR("header copy failed\n");
+ return TEST_SEQ_INVALID;
+ }
+
+ if (tail.magic == TEST_SEQ_MAGIC) {
+ seq = head.seq;
+ CU_ASSERT(seq != TEST_SEQ_INVALID);
+ } else {
+ ODPH_ERR("tail magic invalid 0x%" PRIx32 "\n", tail.magic);
+ }
+ } else {
+ ODPH_ERR("packet length invalid: %" PRIu32 "(%" PRIu32 ")\n",
+ odp_packet_len(pkt), packet_len);
+ }
+
+ return seq;
+}
+
+static uint32_t pktio_pkt_seq(odp_packet_t pkt)
+{
+ return pktio_pkt_seq_hdr(pkt, ODPH_UDPHDR_LEN);
+}
+
+static void pktio_init_packet_eth_ipv4(odp_packet_t pkt, uint8_t proto)
+{
+ odph_ethhdr_t *eth;
+ odph_ipv4hdr_t *ip;
+ char *buf;
+ uint16_t seq;
+ uint8_t src_mac[ODP_PKTIO_MACADDR_MAXSIZE] = PKTIO_SRC_MAC;
+ uint8_t dst_mac[ODP_PKTIO_MACADDR_MAXSIZE] = PKTIO_DST_MAC;
+ int pkt_len = odp_packet_len(pkt);
+
+ buf = odp_packet_data(pkt);
+
+ /* Ethernet */
+ odp_packet_l2_offset_set(pkt, 0);
+ eth = (odph_ethhdr_t *)buf;
+ memcpy(eth->src.addr, src_mac, ODPH_ETHADDR_LEN);
+ memcpy(eth->dst.addr, dst_mac, ODPH_ETHADDR_LEN);
+ eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+
+ /* IP */
+ odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
+ ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
+ ip->dst_addr = odp_cpu_to_be_32(0x0a000064);
+ ip->src_addr = odp_cpu_to_be_32(0x0a000001);
+ ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
+ ip->tot_len = odp_cpu_to_be_16(pkt_len - ODPH_ETHHDR_LEN);
+ ip->ttl = 128;
+ ip->proto = proto;
+ seq = odp_atomic_fetch_inc_u32(&ip_seq);
+ ip->id = odp_cpu_to_be_16(seq);
+ ip->chksum = 0;
+ ip->frag_offset = 0;
+ ip->tos = 0;
+ odph_ipv4_csum_update(pkt);
+}
+
+static uint32_t pktio_init_packet_udp(odp_packet_t pkt)
+{
+ odph_udphdr_t *udp;
+ char *buf;
+ int pkt_len = odp_packet_len(pkt);
+
+ buf = odp_packet_data(pkt);
+
+ pktio_init_packet_eth_ipv4(pkt, ODPH_IPPROTO_UDP);
+
+ /* UDP */
+ odp_packet_l4_offset_set(pkt, ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+ udp = (odph_udphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+ udp->src_port = odp_cpu_to_be_16(12049);
+ udp->dst_port = odp_cpu_to_be_16(12050);
+ udp->length = odp_cpu_to_be_16(pkt_len -
+ ODPH_ETHHDR_LEN - ODPH_IPV4HDR_LEN);
+ udp->chksum = 0;
+
+ return pktio_pkt_set_seq(pkt, ODPH_UDPHDR_LEN);
+}
+
+static uint32_t pktio_init_packet_sctp(odp_packet_t pkt)
+{
+ odph_sctphdr_t *sctp;
+ char *buf;
+
+ buf = odp_packet_data(pkt);
+
+ pktio_init_packet_eth_ipv4(pkt, ODPH_IPPROTO_SCTP);
+
+ /* SCTP */
+ odp_packet_l4_offset_set(pkt, ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+ sctp = (odph_sctphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+ sctp->src_port = odp_cpu_to_be_16(12049);
+ sctp->dst_port = odp_cpu_to_be_16(12050);
+ sctp->tag = 0;
+ sctp->chksum = 0;
+
+ return pktio_pkt_set_seq(pkt, ODPH_SCTPHDR_LEN);
+}
+
+static int pktio_zero_checksums(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip;
+ uint32_t len;
+
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, &len);
+
+ ip->chksum = 0;
+
+ if (ip->proto == ODPH_IPPROTO_UDP) {
+ odph_udphdr_t *udp;
+
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, &len);
+ udp->chksum = 0;
+ } else if (ip->proto == ODPH_IPPROTO_SCTP) {
+ odph_sctphdr_t *sctp;
+
+ sctp = (odph_sctphdr_t *)odp_packet_l4_ptr(pkt, &len);
+ sctp->chksum = 0;
+ } else {
+ CU_FAIL("unexpected L4 protocol");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pktio_fixup_checksums(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip;
+
+ pktio_zero_checksums(pkt);
+
+ odph_ipv4_csum_update(pkt);
+
+ ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ if (ip->proto == ODPH_IPPROTO_UDP) {
+ odph_udphdr_t *udp;
+
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ udp->chksum = odph_ipv4_udp_chksum(pkt);
+ } else if (ip->proto == ODPH_IPPROTO_SCTP) {
+ odph_sctp_chksum_set(pkt);
+ } else {
+ CU_FAIL("unexpected L4 protocol");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int default_pool_create(void)
+{
+ odp_pool_param_t params;
+ odp_pool_capability_t pool_capa;
+ char pool_name[ODP_POOL_NAME_LEN];
+
+ if (odp_pool_capability(&pool_capa) != 0)
+ return -1;
+
+ if (default_pkt_pool != ODP_POOL_INVALID)
+ return -1;
+
+ odp_pool_param_init(&params);
+ set_pool_len(&params, &pool_capa);
+ params.pkt.num = PKT_BUF_NUM;
+ params.type = ODP_POOL_PACKET;
+
+ snprintf(pool_name, sizeof(pool_name),
+ "pkt_pool_default_%d", pool_segmentation);
+ default_pkt_pool = odp_pool_create(pool_name, &params);
+ if (default_pkt_pool == ODP_POOL_INVALID)
+ return -1;
+
+ return 0;
+}
+
+static int default_pktv_pool_create(void)
+{
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t params;
+
+ if (odp_pool_capability(&pool_capa) != 0)
+ return -1;
+
+ if (pool_capa.vector.max_num < PKT_BUF_NUM)
+ return -1;
+
+ if (default_pktv_pool != ODP_POOL_INVALID)
+ return -1;
+
+ odp_pool_param_init(&params);
+ params.type = ODP_POOL_VECTOR;
+ params.vector.num = PKT_BUF_NUM;
+ params.vector.max_size = pool_capa.vector.max_size;
+
+ snprintf(pool_name, sizeof(pool_name),
+ "pktv_pool_default_%d", pool_segmentation);
+ default_pktv_pool = odp_pool_create(pool_name, &params);
+ if (default_pktv_pool == ODP_POOL_INVALID)
+ return -1;
+
+ return 0;
+}
+
+static odp_pktio_t create_pktio(int iface_idx, odp_pktin_mode_t imode,
+ odp_pktout_mode_t omode)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t pktin_param;
+ const char *iface = iface_name[iface_idx];
+
+ odp_pktio_param_init(&pktio_param);
+
+ pktio_param.in_mode = imode;
+ pktio_param.out_mode = omode;
+
+ pktio = odp_pktio_open(iface, pool[iface_idx], &pktio_param);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_pktio_to_u64(pktio) !=
+ odp_pktio_to_u64(ODP_PKTIO_INVALID));
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ /* Atomic queue when in scheduled mode */
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ /* By default, single input and output queue in all modes. Config can
+ * be overridden before starting the interface. */
+ CU_ASSERT(odp_pktin_queue_config(pktio, &pktin_param) == 0);
+ CU_ASSERT(odp_pktout_queue_config(pktio, NULL) == 0);
+
+ if (wait_for_network)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
+
+ return pktio;
+}
+
+static odp_pktio_t create_pktv_pktio(int iface_idx, odp_pktin_mode_t imode,
+ odp_pktout_mode_t omode, odp_schedule_sync_t sync_mode)
+{
+ const char *iface = iface_name[iface_idx];
+ odp_pktout_queue_param_t pktout_param;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+
+ odp_pktio_param_init(&pktio_param);
+
+ pktio_param.in_mode = imode;
+ pktio_param.out_mode = omode;
+
+ pktio = odp_pktio_open(iface, pool[iface_idx], &pktio_param);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT(odp_pktio_capability(pktio, &capa) == 0);
+ if (!capa.vector.supported) {
+ printf("Vector mode is not supported. Test Skipped.\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ if (imode == ODP_PKTIN_MODE_SCHED) {
+ pktin_param.queue_param.sched.prio = odp_schedule_default_prio();
+ pktin_param.queue_param.sched.sync = sync_mode;
+ pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ }
+
+ pktin_param.hash_enable = 0;
+ pktin_param.num_queues = 1;
+ pktin_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ pktin_param.vector.enable = 1;
+ pktin_param.vector.pool = pktv_pool[iface_idx];
+ pktin_param.vector.max_size = capa.vector.max_size < PKTV_DEFAULT_SIZE ?
+ capa.vector.max_size : PKTV_DEFAULT_SIZE;
+ pktin_param.vector.max_tmo_ns = capa.vector.min_tmo_ns;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &pktin_param) == 0);
+
+ odp_pktout_queue_param_init(&pktout_param);
+ pktout_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ pktout_param.num_queues = 1;
+ CU_ASSERT(odp_pktout_queue_config(pktio, &pktout_param) == 0);
+
+ if (wait_for_network)
+ odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4);
+
+ return pktio;
+}
+
+static int flush_input_queue(odp_pktio_t pktio, odp_pktin_mode_t imode)
+{
+ odp_event_t ev;
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+
+ if (imode == ODP_PKTIN_MODE_QUEUE) {
+ /* Assert breaks else-if without brackets */
+ CU_ASSERT_FATAL(odp_pktin_event_queue(pktio, &queue, 1) == 1);
+ } else if (imode == ODP_PKTIN_MODE_DIRECT) {
+ return 0;
+ }
+
+ /* flush any pending events */
+ while (1) {
+ if (queue != ODP_QUEUE_INVALID)
+ ev = odp_queue_deq(queue);
+ else
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ else
+ break;
+ }
+
+ return 0;
+}
+
+static int create_packets_udp(odp_packet_t pkt_tbl[],
+ uint32_t pkt_seq[],
+ int num,
+ odp_pktio_t pktio_src,
+ odp_pktio_t pktio_dst,
+ odp_bool_t fix_cs,
+ eth_addr_type_e dst_addr_type)
+{
+ int i, ret;
+
+ for (i = 0; i < num; i++) {
+ pkt_tbl[i] = odp_packet_alloc(default_pkt_pool, packet_len);
+ if (pkt_tbl[i] == ODP_PACKET_INVALID)
+ break;
+
+ pkt_seq[i] = pktio_init_packet_udp(pkt_tbl[i]);
+ if (pkt_seq[i] == TEST_SEQ_INVALID) {
+ odp_packet_free(pkt_tbl[i]);
+ break;
+ }
+
+ pktio_pkt_set_macs(pkt_tbl[i], pktio_src, pktio_dst, dst_addr_type);
+
+ /* Set user pointer. It should be NULL on receive side. */
+ odp_packet_user_ptr_set(pkt_tbl[i], (void *)1);
+
+ if (fix_cs)
+ ret = pktio_fixup_checksums(pkt_tbl[i]);
+ else
+ ret = pktio_zero_checksums(pkt_tbl[i]);
+ if (ret != 0) {
+ odp_packet_free(pkt_tbl[i]);
+ break;
+ }
+ }
+
+ return i;
+}
+
+static int create_packets_sctp(odp_packet_t pkt_tbl[],
+ uint32_t pkt_seq[],
+ int num,
+ odp_pktio_t pktio_src,
+ odp_pktio_t pktio_dst)
+{
+ int i, ret;
+
+ for (i = 0; i < num; i++) {
+ pkt_tbl[i] = odp_packet_alloc(default_pkt_pool, packet_len);
+ if (pkt_tbl[i] == ODP_PACKET_INVALID)
+ break;
+
+ pkt_seq[i] = pktio_init_packet_sctp(pkt_tbl[i]);
+ if (pkt_seq[i] == TEST_SEQ_INVALID) {
+ odp_packet_free(pkt_tbl[i]);
+ break;
+ }
+
+ pktio_pkt_set_macs(pkt_tbl[i], pktio_src, pktio_dst, ETH_UNICAST);
+
+ ret = pktio_zero_checksums(pkt_tbl[i]);
+ if (ret != 0) {
+ odp_packet_free(pkt_tbl[i]);
+ break;
+ }
+ }
+
+ return i;
+}
+
+static int create_packets(odp_packet_t pkt_tbl[], uint32_t pkt_seq[], int num,
+ odp_pktio_t pktio_src, odp_pktio_t pktio_dst)
+{
+ return create_packets_udp(pkt_tbl, pkt_seq, num, pktio_src, pktio_dst,
+ true, ETH_UNICAST);
+}
+
+static int get_packets(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[],
+ int num, txrx_mode_e mode, odp_bool_t vector_mode)
+{
+ odp_event_t evt_tbl[num];
+ int num_evts = 0;
+ int num_pkts = 0;
+ int i, ret;
+
+ if (pktio_rx->in_mode == ODP_PKTIN_MODE_DIRECT) {
+ odp_pktin_queue_t pktin;
+
+ ret = odp_pktin_queue(pktio_rx->id, &pktin, 1);
+
+ if (ret != 1) {
+ CU_FAIL_FATAL("No pktin queues");
+ return -1;
+ }
+
+ return odp_pktin_recv(pktin, pkt_tbl, num);
+ }
+
+ if (mode == TXRX_MODE_MULTI) {
+ if (pktio_rx->in_mode == ODP_PKTIN_MODE_QUEUE)
+ num_evts = odp_queue_deq_multi(pktio_rx->inq, evt_tbl,
+ num);
+ else
+ num_evts = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT,
+ evt_tbl, num);
+ } else {
+ odp_event_t evt_tmp = ODP_EVENT_INVALID;
+
+ if (pktio_rx->in_mode == ODP_PKTIN_MODE_QUEUE)
+ evt_tmp = odp_queue_deq(pktio_rx->inq);
+ else
+ evt_tmp = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (evt_tmp != ODP_EVENT_INVALID)
+ evt_tbl[num_evts++] = evt_tmp;
+ }
+
+ /* convert events to packets, discarding any non-packet events */
+ for (i = 0; i < num_evts; ++i) {
+ if (odp_event_type(evt_tbl[i]) == ODP_EVENT_PACKET) {
+ pkt_tbl[num_pkts++] = odp_packet_from_event(evt_tbl[i]);
+ } else if (vector_mode && odp_event_type(evt_tbl[i]) == ODP_EVENT_PACKET_VECTOR &&
+ num_pkts < num) {
+ odp_packet_vector_t pktv;
+ odp_packet_t *pkts;
+ int pktv_len;
+
+ pktv = odp_packet_vector_from_event(evt_tbl[i]);
+ pktv_len = odp_packet_vector_tbl(pktv, &pkts);
+ CU_ASSERT(odp_packet_vector_user_flag(pktv) == 0);
+
+ /* Make sure too many packets are not received */
+ if (num_pkts + pktv_len > num) {
+ int new_pkts = num - num_pkts;
+
+ memcpy(&pkt_tbl[num_pkts], pkts, new_pkts * sizeof(odp_packet_t));
+ odp_packet_free_multi(&pkts[new_pkts], pktv_len - new_pkts);
+ num_pkts += new_pkts;
+
+ } else {
+ memcpy(&pkt_tbl[num_pkts], pkts, pktv_len * sizeof(odp_packet_t));
+ num_pkts += pktv_len;
+ }
+ odp_packet_vector_free(pktv);
+ } else {
+ odp_event_free(evt_tbl[i]);
+ }
+ }
+
+ return num_pkts;
+}
+
+static int wait_for_packets_hdr(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[],
+ uint32_t seq_tbl[], int num, txrx_mode_e mode,
+ uint64_t ns, size_t l4_hdr_len, odp_bool_t vector_mode)
+{
+ odp_time_t wait_time, end, start;
+ int num_rx = 0;
+ int i;
+ odp_packet_t pkt_tmp[num];
+
+ wait_time = odp_time_local_from_ns(ns);
+ start = odp_time_local();
+ end = odp_time_sum(start, wait_time);
+
+ while (num_rx < num && odp_time_cmp(end, odp_time_local()) > 0) {
+ int n = get_packets(pktio_rx, pkt_tmp, num - num_rx, mode, vector_mode);
+
+ if (n < 0)
+ break;
+
+ if (n == 0)
+ continue;
+
+ for (i = 0; i < n; ++i) {
+ if (pktio_pkt_seq_hdr(pkt_tmp[i], l4_hdr_len) ==
+ seq_tbl[num_rx])
+ pkt_tbl[num_rx++] = pkt_tmp[i];
+ else
+ odp_packet_free(pkt_tmp[i]);
+ }
+ }
+
+ return num_rx;
+}
+
+static int wait_for_packets(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[],
+ uint32_t seq_tbl[], int num, txrx_mode_e mode,
+ uint64_t ns, odp_bool_t vector_mode)
+{
+ return wait_for_packets_hdr(pktio_rx, pkt_tbl, seq_tbl, num, mode, ns,
+ ODPH_UDPHDR_LEN, vector_mode);
+}
+
+static int recv_packets_tmo(odp_pktio_t pktio, odp_packet_t pkt_tbl[],
+ uint32_t seq_tbl[], int num, recv_tmo_mode_e mode,
+ uint64_t tmo, uint64_t ns, int no_pkt)
+{
+ odp_packet_t pkt_tmp[num];
+ odp_pktin_queue_t pktin[MAX_QUEUES];
+ odp_time_t ts1, ts2;
+ int num_rx = 0;
+ int num_q;
+ int i;
+ int n;
+ uint32_t from_val = 0;
+ uint32_t *from = NULL;
+
+ if (mode == RECV_MQ_TMO)
+ from = &from_val;
+
+ num_q = odp_pktin_queue(pktio, pktin, MAX_QUEUES);
+ CU_ASSERT_FATAL(num_q > 0);
+
+ /** Multiple odp_pktin_recv_tmo()/odp_pktin_recv_mq_tmo() calls may be
+ * required to discard possible non-test packets. */
+ do {
+ ts1 = odp_time_global();
+ if (mode == RECV_TMO)
+ n = odp_pktin_recv_tmo(pktin[0], pkt_tmp, num - num_rx,
+ tmo);
+ else
+ n = odp_pktin_recv_mq_tmo(pktin, (uint32_t)num_q, from, pkt_tmp,
+ num - num_rx, tmo);
+ ts2 = odp_time_global();
+
+ CU_ASSERT(n >= 0);
+
+ if (n <= 0)
+ break;
+
+ /* When we don't expect any packets, drop all packets and
+ * retry timeout test. */
+ if (no_pkt) {
+ printf(" drop %i dummy packets\n", n);
+ odp_packet_free_multi(pkt_tmp, n);
+ continue;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (pktio_pkt_seq(pkt_tmp[i]) == seq_tbl[num_rx])
+ pkt_tbl[num_rx++] = pkt_tmp[i];
+ else
+ odp_packet_free(pkt_tmp[i]);
+ }
+ if (mode == RECV_MQ_TMO)
+ CU_ASSERT(from_val < (uint32_t)num_q);
+ } while (num_rx < num);
+
+ if (num_rx < num) {
+ uint64_t diff = odp_time_diff_ns(ts2, ts1);
+
+ if (diff < ns)
+ printf(" diff %" PRIu64 ", ns %" PRIu64 "\n",
+ diff, ns);
+
+ CU_ASSERT(diff >= ns);
+ }
+
+ return num_rx;
+}
+
+static int send_packets(odp_pktout_queue_t pktout,
+ odp_packet_t *pkt_tbl, unsigned pkts)
+{
+ int ret;
+ unsigned sent = 0;
+
+ while (sent < pkts) {
+ ret = odp_pktout_send(pktout, &pkt_tbl[sent], pkts - sent);
+
+ if (ret < 0) {
+ CU_FAIL_FATAL("failed to send test packet");
+ return -1;
+ }
+
+ sent += ret;
+ }
+
+ return 0;
+}
+
+static int send_packet_events(odp_queue_t queue,
+ odp_packet_t *pkt_tbl, unsigned pkts)
+{
+ int ret;
+ unsigned i;
+ unsigned sent = 0;
+ odp_event_t ev_tbl[pkts];
+
+ for (i = 0; i < pkts; i++)
+ ev_tbl[i] = odp_packet_to_event(pkt_tbl[i]);
+
+ while (sent < pkts) {
+ ret = odp_queue_enq_multi(queue, &ev_tbl[sent], pkts - sent);
+
+ if (ret < 0) {
+ CU_FAIL_FATAL("failed to send test packet as events");
+ return -1;
+ }
+
+ sent += ret;
+ }
+
+ return 0;
+}
+
+static void check_parser_capa(odp_pktio_t pktio, int *l2, int *l3, int *l4)
+{
+ int ret;
+ odp_pktio_capability_t capa;
+
+ *l2 = 0;
+ *l3 = 0;
+ *l4 = 0;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ CU_ASSERT(ret == 0);
+
+ if (ret < 0)
+ return;
+
+ switch (capa.config.parser.layer) {
+ case ODP_PROTO_LAYER_ALL:
+ /* Fall through */
+ case ODP_PROTO_LAYER_L4:
+ *l2 = 1;
+ *l3 = 1;
+ *l4 = 1;
+ break;
+ case ODP_PROTO_LAYER_L3:
+ *l2 = 1;
+ *l3 = 1;
+ break;
+ case ODP_PROTO_LAYER_L2:
+ *l2 = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static void pktio_txrx_multi(pktio_info_t *pktio_info_a,
+ pktio_info_t *pktio_info_b,
+ int num_pkts, txrx_mode_e mode,
+ odp_bool_t vector_mode)
+{
+ odp_packet_t tx_pkt[num_pkts];
+ odp_packet_t rx_pkt[num_pkts];
+ uint32_t tx_seq[num_pkts];
+ int i, ret, num_rx;
+ int parser_l2, parser_l3, parser_l4;
+ odp_pktio_t pktio_a = pktio_info_a->id;
+ odp_pktio_t pktio_b = pktio_info_b->id;
+ int pktio_index_b = odp_pktio_index(pktio_b);
+
+ /* Check RX interface parser capability */
+ check_parser_capa(pktio_b, &parser_l2, &parser_l3, &parser_l4);
+
+ if (packet_len == USE_MTU) {
+ odp_pool_capability_t pool_capa;
+ uint32_t maxlen;
+
+ maxlen = odp_pktout_maxlen(pktio_a);
+ if (odp_pktout_maxlen(pktio_b) < maxlen)
+ maxlen = odp_pktout_maxlen(pktio_b);
+ CU_ASSERT_FATAL(maxlen > 0);
+ packet_len = maxlen;
+ if (packet_len > PKT_LEN_MAX)
+ packet_len = PKT_LEN_MAX;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&pool_capa) == 0);
+
+ if (pool_capa.pkt.max_len &&
+ packet_len > pool_capa.pkt.max_len)
+ packet_len = pool_capa.pkt.max_len;
+ }
+
+ /* generate test packets to send */
+ ret = create_packets(tx_pkt, tx_seq, num_pkts, pktio_a, pktio_b);
+ if (ret != num_pkts) {
+ CU_FAIL("failed to generate test packets");
+ return;
+ }
+
+ /* send packet(s) out */
+ if (mode == TXRX_MODE_SINGLE) {
+ for (i = 0; i < num_pkts; ++i) {
+ ret = odp_pktout_send(pktio_info_a->pktout,
+ &tx_pkt[i], 1);
+ if (ret != 1) {
+ CU_FAIL_FATAL("failed to send test packet");
+ odp_packet_free(tx_pkt[i]);
+ return;
+ }
+ }
+ } else if (mode == TXRX_MODE_MULTI) {
+ send_packets(pktio_info_a->pktout, tx_pkt, num_pkts);
+ } else {
+ send_packet_events(pktio_info_a->queue_out, tx_pkt, num_pkts);
+ }
+
+ /* and wait for them to arrive back */
+ num_rx = wait_for_packets(pktio_info_b, rx_pkt, tx_seq, num_pkts, mode,
+ ODP_TIME_SEC_IN_NS, vector_mode);
+ CU_ASSERT(num_rx == num_pkts);
+ if (num_rx != num_pkts)
+ ODPH_ERR("received %i, out of %i packets\n", num_rx, num_pkts);
+
+ for (i = 0; i < num_rx; ++i) {
+ odp_packet_data_range_t range;
+ uint16_t sum;
+ odp_packet_t pkt = rx_pkt[i];
+
+ CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID);
+ CU_ASSERT(odp_packet_input(pkt) == pktio_b);
+ CU_ASSERT(odp_packet_input_index(pkt) == pktio_index_b);
+ CU_ASSERT(odp_packet_has_error(pkt) == 0);
+ if (parser_l2) {
+ CU_ASSERT(odp_packet_has_l2(pkt));
+ CU_ASSERT(odp_packet_has_eth(pkt));
+ }
+ if (parser_l3) {
+ CU_ASSERT(odp_packet_has_l3(pkt));
+ CU_ASSERT(odp_packet_has_ipv4(pkt));
+ }
+ if (parser_l4) {
+ CU_ASSERT(odp_packet_has_l4(pkt));
+ CU_ASSERT(odp_packet_has_udp(pkt));
+ }
+
+ CU_ASSERT(odp_packet_user_flag(pkt) == 0);
+ CU_ASSERT(odp_packet_user_ptr(pkt) == NULL);
+ CU_ASSERT(odp_packet_cls_mark(pkt) == 0);
+
+ odp_packet_input_set(pkt, ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_packet_input(pkt) == ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_packet_input_index(pkt) < 0);
+
+ odp_packet_input_set(pkt, pktio_b);
+ CU_ASSERT(odp_packet_input(pkt) == pktio_b);
+ CU_ASSERT(odp_packet_input_index(pkt) == pktio_index_b);
+
+ /* Dummy read to ones complement in case pktio has set it */
+ sum = odp_packet_ones_comp(pkt, &range);
+ if (range.length > 0)
+ test_pktio_dummy_u64 += sum;
+
+ /* Dummy read to flow hash in case pktio has set it */
+ if (odp_packet_has_flow_hash(pkt))
+ test_pktio_dummy_u64 += odp_packet_flow_hash(pkt);
+
+ odp_packet_free(pkt);
+ }
+}
+
+static void test_txrx(odp_pktin_mode_t in_mode, int num_pkts,
+ txrx_mode_e mode, odp_schedule_sync_t sync_mode,
+ odp_bool_t vector_mode)
+{
+ int ret, i, if_b;
+ pktio_info_t pktios[MAX_NUM_IFACES];
+ pktio_info_t *io;
+
+ /* create pktios and associate input/output queues */
+ for (i = 0; i < num_ifaces; ++i) {
+ odp_pktout_queue_t pktout;
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+ odp_pktout_mode_t out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ if (mode == TXRX_MODE_MULTI_EVENT)
+ out_mode = ODP_PKTOUT_MODE_QUEUE;
+
+ io = &pktios[i];
+
+ io->name = iface_name[i];
+ if (vector_mode)
+ io->id = create_pktv_pktio(i, in_mode, out_mode, sync_mode);
+ else
+ io->id = create_pktio(i, in_mode, out_mode);
+ if (io->id == ODP_PKTIO_INVALID) {
+ CU_FAIL("failed to open iface");
+ return;
+ }
+
+ if (mode == TXRX_MODE_MULTI_EVENT) {
+ CU_ASSERT_FATAL(odp_pktout_event_queue(io->id,
+ &queue, 1) == 1);
+ } else {
+ CU_ASSERT_FATAL(odp_pktout_queue(io->id,
+ &pktout, 1) == 1);
+ io->pktout = pktout;
+ }
+
+ io->queue_out = queue;
+ io->in_mode = in_mode;
+
+ if (in_mode == ODP_PKTIN_MODE_QUEUE) {
+ CU_ASSERT_FATAL(odp_pktin_event_queue(io->id, &queue, 1)
+ == 1);
+ io->inq = queue;
+ } else {
+ io->inq = ODP_QUEUE_INVALID;
+ }
+
+ ret = odp_pktio_start(io->id);
+ CU_ASSERT(ret == 0);
+
+ _pktio_wait_linkup(io->id);
+ }
+
+ /* if we have two interfaces then send through one and receive on
+ * another but if there's only one assume it's a loopback */
+ if_b = (num_ifaces == 1) ? 0 : 1;
+ pktio_txrx_multi(&pktios[0], &pktios[if_b], num_pkts, mode, vector_mode);
+
+ for (i = 0; i < num_ifaces; ++i) {
+ ret = odp_pktio_stop(pktios[i].id);
+ CU_ASSERT_FATAL(ret == 0);
+ flush_input_queue(pktios[i].id, in_mode);
+ ret = odp_pktio_close(pktios[i].id);
+ CU_ASSERT(ret == 0);
+ }
+}
+
+static void pktio_test_plain_queue(void)
+{
+ test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_SINGLE, 0, false);
+ test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_SINGLE, 0, false);
+}
+
+static void pktio_test_plain_multi(void)
+{
+ test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_MULTI, 0, false);
+ test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_MULTI, 0, false);
+}
+
+static void pktio_test_plain_multi_event(void)
+{
+ test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_MULTI_EVENT, 0, false);
+ test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT, 0, false);
+}
+
+static void pktio_test_sched_queue(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_SINGLE, 0, false);
+ test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_SINGLE, 0, false);
+}
+
+static void pktio_test_sched_multi(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_MULTI, 0, false);
+ test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_MULTI, 0, false);
+}
+
+static void pktio_test_sched_multi_event(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_MULTI_EVENT, 0, false);
+ test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT, 0, false);
+}
+
+static void pktio_test_recv(void)
+{
+ test_txrx(ODP_PKTIN_MODE_DIRECT, 1, TXRX_MODE_SINGLE, 0, false);
+}
+
+static void pktio_test_recv_multi(void)
+{
+ test_txrx(ODP_PKTIN_MODE_DIRECT, TX_BATCH_LEN, TXRX_MODE_MULTI, 0, false);
+}
+
+static void pktio_test_recv_multi_event(void)
+{
+ test_txrx(ODP_PKTIN_MODE_DIRECT, 1, TXRX_MODE_MULTI_EVENT, 0, false);
+ test_txrx(ODP_PKTIN_MODE_DIRECT, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT, 0, false);
+}
+
+static void pktio_test_recv_queue(void)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t in_queue_param;
+ odp_pktout_queue_param_t out_queue_param;
+ odp_pktout_queue_t pktout_queue[MAX_QUEUES];
+ odp_pktin_queue_t pktin_queue[MAX_QUEUES];
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_packet_t tmp_pkt[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ odp_time_t wait_time, end;
+ int num_rx = 0;
+ int num_queues;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
+
+ odp_pktin_queue_param_init(&in_queue_param);
+ num_queues = capa.max_input_queues;
+ in_queue_param.num_queues = num_queues;
+ in_queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ in_queue_param.hash_proto.proto.ipv4_udp = 1;
+
+ ret = odp_pktin_queue_config(pktio[i], &in_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ odp_pktout_queue_param_init(&out_queue_param);
+ out_queue_param.num_queues = capa.max_output_queues;
+
+ ret = odp_pktout_queue_config(pktio[i], &out_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; ++i)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ if (num_ifaces > 1)
+ pktio_rx = pktio[1];
+ else
+ pktio_rx = pktio_tx;
+
+ /* Allocate and initialize test packets */
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ if (ret != TX_BATCH_LEN) {
+ CU_FAIL("Failed to generate test packets");
+ return;
+ }
+
+ /* Send packets */
+ num_queues = odp_pktout_queue(pktio_tx, pktout_queue, MAX_QUEUES);
+ CU_ASSERT_FATAL(num_queues > 0);
+ if (num_queues > MAX_QUEUES)
+ num_queues = MAX_QUEUES;
+
+ ret = odp_pktout_send(pktout_queue[num_queues - 1], pkt_tbl,
+ TX_BATCH_LEN);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ /* Receive packets */
+ num_queues = odp_pktin_queue(pktio_rx, pktin_queue, MAX_QUEUES);
+ CU_ASSERT_FATAL(num_queues > 0);
+ if (num_queues > MAX_QUEUES)
+ num_queues = MAX_QUEUES;
+
+ wait_time = odp_time_local_from_ns(ODP_TIME_SEC_IN_NS);
+ end = odp_time_sum(odp_time_local(), wait_time);
+ do {
+ int n = 0;
+
+ for (i = 0; i < num_queues; i++) {
+ n = odp_pktin_recv(pktin_queue[i], tmp_pkt,
+ TX_BATCH_LEN);
+ if (n != 0)
+ break;
+ }
+ if (n < 0)
+ break;
+ for (i = 0; i < n; i++) {
+ if (pktio_pkt_seq(tmp_pkt[i]) == pkt_seq[num_rx])
+ pkt_tbl[num_rx++] = tmp_pkt[i];
+ else
+ odp_packet_free(tmp_pkt[i]);
+ }
+ } while (num_rx < TX_BATCH_LEN &&
+ odp_time_cmp(end, odp_time_local()) > 0);
+
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void test_recv_tmo(recv_tmo_mode_e mode)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t in_queue_param;
+ odp_pktout_queue_t pktout_queue;
+ int test_pkt_count = 6;
+ odp_packet_t pkt_tbl[test_pkt_count];
+ uint32_t pkt_seq[test_pkt_count];
+ uint64_t ns;
+ uint32_t num_q;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
+
+ odp_pktin_queue_param_init(&in_queue_param);
+ if (mode == RECV_TMO)
+ num_q = 1;
+ else
+ num_q = (capa.max_input_queues < MAX_QUEUES) ?
+ capa.max_input_queues : MAX_QUEUES;
+ in_queue_param.num_queues = num_q;
+ in_queue_param.hash_enable = (num_q > 1) ? 1 : 0;
+ in_queue_param.hash_proto.proto.ipv4_udp = 1;
+
+ ret = odp_pktin_queue_config(pktio[i], &in_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ memset(pkt_seq, 0, sizeof(pkt_seq));
+
+ ns = 100 * ODP_TIME_MSEC_IN_NS;
+
+ ret = create_packets(pkt_tbl, pkt_seq, test_pkt_count, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == test_pkt_count);
+
+ ret = odp_pktout_send(pktout_queue, pkt_tbl, test_pkt_count);
+ CU_ASSERT_FATAL(ret == test_pkt_count);
+
+ ret = recv_packets_tmo(pktio_rx, &pkt_tbl[0], &pkt_seq[0], 1, mode,
+ odp_pktin_wait_time(10 * ODP_TIME_SEC_IN_NS),
+ 0, 0);
+ CU_ASSERT_FATAL(ret == 1);
+
+ ret = recv_packets_tmo(pktio_rx, &pkt_tbl[1], &pkt_seq[1], 1, mode,
+ ODP_PKTIN_NO_WAIT, 0, 0);
+ CU_ASSERT_FATAL(ret == 1);
+
+ ret = recv_packets_tmo(pktio_rx, &pkt_tbl[2], &pkt_seq[2], 1, mode,
+ odp_pktin_wait_time(0), 0, 0);
+ CU_ASSERT_FATAL(ret == 1);
+
+ ret = recv_packets_tmo(pktio_rx, &pkt_tbl[3], &pkt_seq[3], 3, mode,
+ odp_pktin_wait_time(ns), ns, 0);
+ CU_ASSERT_FATAL(ret == 3);
+
+ for (i = 0; i < test_pkt_count; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_recv_tmo(void)
+{
+ test_recv_tmo(RECV_TMO);
+}
+
+static void pktio_test_recv_mq_tmo(void)
+{
+ test_recv_tmo(RECV_MQ_TMO);
+ test_recv_tmo(RECV_MQ_TMO_NO_IDX);
+}
+
+static void pktio_test_recv_mtu(void)
+{
+ packet_len = USE_MTU;
+ pktio_test_sched_multi();
+ packet_len = PKT_LEN_NORMAL;
+}
+
+static void pktio_test_maxlen(void)
+{
+ int ret;
+ uint32_t maxlen;
+
+ odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ maxlen = odp_pktout_maxlen(pktio);
+ CU_ASSERT(maxlen > 0);
+
+ maxlen = odp_pktin_maxlen(pktio);
+ CU_ASSERT(maxlen > 0);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(ret == 0);
+}
+
+static int pktio_check_maxlen_set(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.set_op.op.maxlen)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_maxlen_set(void)
+{
+ odp_pktio_capability_t capa;
+ int ret;
+ uint32_t maxlen, input_orig, output_orig;
+
+ odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(!odp_pktio_capability(pktio, &capa));
+
+ input_orig = odp_pktin_maxlen(pktio);
+ CU_ASSERT(input_orig > 0);
+
+ output_orig = odp_pktout_maxlen(pktio);
+ CU_ASSERT(output_orig > 0);
+
+ if (capa.maxlen.equal) { /* Input and output values have to be equal */
+ CU_ASSERT(capa.maxlen.min_input == capa.maxlen.min_output);
+ CU_ASSERT(capa.maxlen.max_input == capa.maxlen.max_output);
+ CU_ASSERT(capa.maxlen.max_input > capa.maxlen.min_input);
+
+ maxlen = capa.maxlen.min_input;
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, maxlen, maxlen));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == maxlen);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == maxlen);
+
+ maxlen = capa.maxlen.max_input;
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, maxlen, maxlen));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == maxlen);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == maxlen);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, input_orig, input_orig));
+ } else {
+ CU_ASSERT(capa.maxlen.max_input || capa.maxlen.max_output);
+ if (capa.maxlen.max_output == 0) { /* Only input supported */
+ CU_ASSERT(capa.maxlen.min_output == 0);
+ CU_ASSERT(capa.maxlen.min_input < capa.maxlen.max_input);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.min_input, 0));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.min_input);
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.max_input, 0));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.max_input);
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, input_orig, 0));
+ } else if (capa.maxlen.max_input == 0) { /* Only output supported */
+ CU_ASSERT(capa.maxlen.min_input == 0);
+ CU_ASSERT(capa.maxlen.min_output < capa.maxlen.max_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, 0, capa.maxlen.min_output));
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.min_output);
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, 0, capa.maxlen.max_output));
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.max_output);
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, 0, output_orig));
+ } else { /* Both directions supported */
+ CU_ASSERT(capa.maxlen.min_input < capa.maxlen.max_input);
+ CU_ASSERT(capa.maxlen.min_output < capa.maxlen.max_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.min_input,
+ capa.maxlen.min_output));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.min_input);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.min_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.max_input,
+ capa.maxlen.max_output));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.max_input);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.max_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.max_input,
+ capa.maxlen.min_output));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.max_input);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.min_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, capa.maxlen.min_input,
+ capa.maxlen.max_output));
+ CU_ASSERT(odp_pktin_maxlen(pktio) == capa.maxlen.min_input);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == capa.maxlen.max_output);
+
+ CU_ASSERT(!odp_pktio_maxlen_set(pktio, input_orig, output_orig));
+ }
+ }
+ CU_ASSERT(odp_pktin_maxlen(pktio) == input_orig);
+ CU_ASSERT(odp_pktout_maxlen(pktio) == output_orig);
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(ret == 0);
+}
+
+static void pktio_test_promisc(void)
+{
+ int ret;
+ odp_pktio_capability_t capa;
+
+ odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
+
+ ret = odp_pktio_promisc_mode(pktio);
+ CU_ASSERT(ret >= 0);
+ CU_ASSERT(ret == 0 || ret == 1);
+
+ if (capa.set_op.op.promisc_mode) {
+ /* Disabled by default */
+ CU_ASSERT(ret == 0);
+ }
+
+ if (!capa.set_op.op.promisc_mode) {
+ printf("promiscuous mode not supported\n");
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(ret == 0);
+ return;
+ }
+
+ ret = odp_pktio_promisc_mode_set(pktio, 1);
+ CU_ASSERT(0 == ret);
+
+ /* Verify that promisc mode set */
+ ret = odp_pktio_promisc_mode(pktio);
+ CU_ASSERT(1 == ret);
+
+ ret = odp_pktio_promisc_mode_set(pktio, 0);
+ CU_ASSERT(0 == ret);
+
+ /* Verify that promisc mode is not set */
+ ret = odp_pktio_promisc_mode(pktio);
+ CU_ASSERT(0 == ret);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(ret == 0);
+}
+
+static void pktio_test_mac(void)
+{
+ unsigned char mac_addr[ODP_PKTIO_MACADDR_MAXSIZE];
+ unsigned char mac_addr_ref[ODP_PKTIO_MACADDR_MAXSIZE] = {
+ 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0};
+ int mac_len;
+ int ret;
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ printf("testing mac for %s\n", iface_name[0]);
+
+ mac_len = odp_pktio_mac_addr(pktio, mac_addr,
+ ODP_PKTIO_MACADDR_MAXSIZE);
+ CU_ASSERT(ODPH_ETHADDR_LEN == mac_len);
+ CU_ASSERT(ODP_PKTIO_MACADDR_MAXSIZE >= mac_len);
+
+ printf(" %X:%X:%X:%X:%X:%X ",
+ mac_addr[0], mac_addr[1], mac_addr[2],
+ mac_addr[3], mac_addr[4], mac_addr[5]);
+
+ /* Fail case: wrong addr_size. Expected <0. */
+ mac_len = odp_pktio_mac_addr(pktio, mac_addr, 2);
+ CU_ASSERT(mac_len < 0);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
+ if (capa.set_op.op.mac_addr) {
+ /* Fail case: wrong addr_size. Expected <0. */
+ ret = odp_pktio_mac_addr_set(pktio, mac_addr_ref, 2);
+ CU_ASSERT_FATAL(ret < 0);
+
+ ret = odp_pktio_mac_addr_set(pktio, mac_addr_ref,
+ ODPH_ETHADDR_LEN);
+ CU_ASSERT_FATAL(ret == 0);
+
+ mac_len = odp_pktio_mac_addr(pktio, mac_addr,
+ ODPH_ETHADDR_LEN);
+ CU_ASSERT(ODPH_ETHADDR_LEN == mac_len);
+
+ CU_ASSERT(odp_memcmp(mac_addr_ref, mac_addr,
+ ODPH_ETHADDR_LEN) == 0);
+ } else
+ printf("\n mac address set not supported for %s!\n",
+ iface_name[0]);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT(0 == ret);
+}
+
+static void test_defaults(uint8_t fill)
+{
+ odp_pktio_param_t pktio_p;
+ odp_pktin_queue_param_t qp_in;
+ odp_pktout_queue_param_t qp_out;
+ odp_pktio_config_t pktio_conf;
+
+ memset(&pktio_p, fill, sizeof(pktio_p));
+ odp_pktio_param_init(&pktio_p);
+ CU_ASSERT_EQUAL(pktio_p.in_mode, ODP_PKTIN_MODE_DIRECT);
+ CU_ASSERT_EQUAL(pktio_p.out_mode, ODP_PKTOUT_MODE_DIRECT);
+
+ memset(&qp_in, fill, sizeof(qp_in));
+ odp_pktin_queue_param_init(&qp_in);
+ CU_ASSERT_EQUAL(qp_in.op_mode, ODP_PKTIO_OP_MT);
+ CU_ASSERT_EQUAL(qp_in.classifier_enable, 0);
+ CU_ASSERT_EQUAL(qp_in.hash_enable, 0);
+ CU_ASSERT_EQUAL(qp_in.hash_proto.all_bits, 0);
+ CU_ASSERT_EQUAL(qp_in.num_queues, 1);
+ CU_ASSERT_EQUAL(qp_in.queue_size[0], 0);
+ CU_ASSERT_EQUAL(qp_in.queue_param.enq_mode, ODP_QUEUE_OP_MT);
+ CU_ASSERT_EQUAL(qp_in.queue_param.sched.prio, odp_schedule_default_prio());
+ CU_ASSERT_EQUAL(qp_in.queue_param.sched.sync, ODP_SCHED_SYNC_PARALLEL);
+ CU_ASSERT_EQUAL(qp_in.queue_param.sched.group, ODP_SCHED_GROUP_ALL);
+ CU_ASSERT_EQUAL(qp_in.queue_param.sched.lock_count, 0);
+ CU_ASSERT_EQUAL(qp_in.queue_param.order, ODP_QUEUE_ORDER_KEEP);
+ CU_ASSERT_EQUAL(qp_in.queue_param.nonblocking, ODP_BLOCKING);
+ CU_ASSERT_EQUAL(qp_in.queue_param.context, NULL);
+ CU_ASSERT_EQUAL(qp_in.queue_param.context_len, 0);
+ CU_ASSERT_EQUAL(qp_in.queue_param_ovr, NULL);
+ CU_ASSERT_EQUAL(qp_in.vector.enable, false);
+
+ memset(&qp_out, fill, sizeof(qp_out));
+ odp_pktout_queue_param_init(&qp_out);
+ CU_ASSERT_EQUAL(qp_out.op_mode, ODP_PKTIO_OP_MT);
+ CU_ASSERT_EQUAL(qp_out.num_queues, 1);
+ CU_ASSERT_EQUAL(qp_out.queue_size[0], 0);
+
+ memset(&pktio_conf, fill, sizeof(pktio_conf));
+ odp_pktio_config_init(&pktio_conf);
+ CU_ASSERT_EQUAL(pktio_conf.pktin.all_bits, 0);
+ CU_ASSERT_EQUAL(pktio_conf.pktout.all_bits, 0);
+ CU_ASSERT_EQUAL(pktio_conf.parser.layer, ODP_PROTO_LAYER_ALL);
+ CU_ASSERT_EQUAL(pktio_conf.enable_loop, false);
+ CU_ASSERT_EQUAL(pktio_conf.inbound_ipsec, false);
+ CU_ASSERT_EQUAL(pktio_conf.outbound_ipsec, false);
+ CU_ASSERT_EQUAL(pktio_conf.enable_lso, false);
+ CU_ASSERT_EQUAL(pktio_conf.reassembly.en_ipv4, false);
+ CU_ASSERT_EQUAL(pktio_conf.reassembly.en_ipv6, false);
+ CU_ASSERT_EQUAL(pktio_conf.reassembly.max_wait_time, 0);
+ CU_ASSERT_EQUAL(pktio_conf.reassembly.max_num_frags, 2);
+}
+
+static void pktio_test_default_values(void)
+{
+ test_defaults(0);
+ test_defaults(0xff);
+}
+
+static void pktio_test_open(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ int i;
+
+ /* test the sequence open->close->open->close() */
+ for (i = 0; i < 2; ++i) {
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ }
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open("nothere", default_pkt_pool, &pktio_param);
+ CU_ASSERT(pktio == ODP_PKTIO_INVALID);
+}
+
+static void pktio_test_lookup(void)
+{
+ odp_pktio_t pktio, pktio_inval;
+ odp_pktio_param_t pktio_param;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], default_pkt_pool, &pktio_param);
+ CU_ASSERT(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT(odp_pktio_lookup(iface_name[0]) == pktio);
+
+ pktio_inval = odp_pktio_open(iface_name[0], default_pkt_pool,
+ &pktio_param);
+ CU_ASSERT(pktio_inval == ODP_PKTIO_INVALID);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+
+ CU_ASSERT(odp_pktio_lookup(iface_name[0]) == ODP_PKTIO_INVALID);
+}
+
+static void pktio_test_index(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_param_t pktio_param;
+ int ndx;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], default_pkt_pool, &pktio_param);
+ CU_ASSERT(pktio != ODP_PKTIO_INVALID);
+
+ ndx = odp_pktio_index(pktio);
+ CU_ASSERT(ndx >= 0);
+
+ CU_ASSERT(ODP_PKTIO_MAX_INDEX >= odp_pktio_max_index());
+ CU_ASSERT(ODP_PKTIO_MAX_INDEX >= 0 && ODP_PKTIO_MAX_INDEX <= 1024);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_print(void)
+{
+ odp_pktio_t pktio;
+ int i;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ /* Print pktio debug info and test that the
+ * odp_pktio_print() function is implemented. */
+ odp_pktio_print(pktio);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ }
+}
+
+static void pktio_test_pktio_config(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+ const char *iface = iface_name[0];
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ memset(&config, 0xff, sizeof(config));
+ odp_pktio_config_init(&config);
+
+ /* Check default values */
+ CU_ASSERT(config.pktin.all_bits == 0);
+ CU_ASSERT(config.pktout.all_bits == 0);
+ CU_ASSERT(config.parser.layer == ODP_PROTO_LAYER_ALL);
+ CU_ASSERT(!config.enable_loop);
+ CU_ASSERT(!config.inbound_ipsec);
+ CU_ASSERT(!config.outbound_ipsec);
+ CU_ASSERT(!config.enable_lso);
+ CU_ASSERT(!config.reassembly.en_ipv4);
+ CU_ASSERT(!config.reassembly.en_ipv6);
+ CU_ASSERT(config.reassembly.max_wait_time == 0);
+ CU_ASSERT(config.reassembly.max_num_frags == 2);
+ CU_ASSERT(config.flow_control.pause_rx == ODP_PKTIO_LINK_PAUSE_OFF);
+ CU_ASSERT(config.flow_control.pause_tx == ODP_PKTIO_LINK_PAUSE_OFF);
+
+ /* Indicate packet refs might be used */
+ config.pktout.bit.no_packet_refs = 0;
+
+ CU_ASSERT(odp_pktio_config(pktio, NULL) == 0);
+
+ CU_ASSERT(odp_pktio_config(pktio, &config) == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
+
+ /* Loop interface supports loopback mode by definition */
+ if (!strcmp(iface, "loop"))
+ CU_ASSERT(capa.config.enable_loop);
+
+ config = capa.config;
+
+ /* Disable inbound_ipsec as it requires IPsec config to be done */
+ config.inbound_ipsec = 0;
+
+ CU_ASSERT(odp_pktio_config(pktio, &config) == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_info(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_info_t pktio_info;
+ int i;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_info(pktio, &pktio_info) == 0);
+
+ printf("pktio %d\n name %s\n driver %s\n", i,
+ pktio_info.name, pktio_info.drv_name);
+
+ CU_ASSERT(strcmp(pktio_info.name, iface_name[i]) == 0);
+ CU_ASSERT(pktio_info.pool == pool[i]);
+ CU_ASSERT(pktio_info.param.in_mode == ODP_PKTIN_MODE_QUEUE);
+ CU_ASSERT(pktio_info.param.out_mode == ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT(odp_pktio_info(ODP_PKTIO_INVALID, &pktio_info) < 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ }
+}
+
+static void pktio_test_link_info(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_link_info_t link_info;
+ int i;
+
+ for (i = 0; i < num_ifaces; i++) {
+ memset(&link_info, 0, sizeof(link_info));
+
+ pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_link_info(pktio, &link_info) == 0);
+
+ CU_ASSERT(link_info.autoneg == ODP_PKTIO_LINK_AUTONEG_UNKNOWN ||
+ link_info.autoneg == ODP_PKTIO_LINK_AUTONEG_ON ||
+ link_info.autoneg == ODP_PKTIO_LINK_AUTONEG_OFF);
+ CU_ASSERT(link_info.duplex == ODP_PKTIO_LINK_DUPLEX_UNKNOWN ||
+ link_info.duplex == ODP_PKTIO_LINK_DUPLEX_HALF ||
+ link_info.duplex == ODP_PKTIO_LINK_DUPLEX_FULL);
+ CU_ASSERT(link_info.pause_rx == ODP_PKTIO_LINK_PAUSE_UNKNOWN ||
+ link_info.pause_rx == ODP_PKTIO_LINK_PAUSE_OFF ||
+ link_info.pause_rx == ODP_PKTIO_LINK_PAUSE_ON ||
+ link_info.pause_rx == ODP_PKTIO_LINK_PFC_ON);
+ CU_ASSERT(link_info.pause_tx == ODP_PKTIO_LINK_PAUSE_UNKNOWN ||
+ link_info.pause_tx == ODP_PKTIO_LINK_PAUSE_OFF ||
+ link_info.pause_tx == ODP_PKTIO_LINK_PAUSE_ON ||
+ link_info.pause_tx == ODP_PKTIO_LINK_PFC_ON);
+ CU_ASSERT(link_info.status == ODP_PKTIO_LINK_STATUS_UNKNOWN ||
+ link_info.status == ODP_PKTIO_LINK_STATUS_UP ||
+ link_info.status == ODP_PKTIO_LINK_STATUS_DOWN);
+ CU_ASSERT(link_info.media != NULL);
+
+ CU_ASSERT(odp_pktio_link_info(ODP_PKTIO_INVALID, &link_info) < 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ }
+}
+
+static int pktio_check_flow_control(int pfc, int rx)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0)
+ return ODP_TEST_INACTIVE;
+
+ if (pfc == 0 && rx == 1 && capa.flow_control.pause_rx == 1)
+ return ODP_TEST_ACTIVE;
+
+ if (pfc == 1 && rx == 1 && capa.flow_control.pfc_rx == 1)
+ return ODP_TEST_ACTIVE;
+
+ if (pfc == 0 && rx == 0 && capa.flow_control.pause_tx == 1)
+ return ODP_TEST_ACTIVE;
+
+ if (pfc == 1 && rx == 0 && capa.flow_control.pfc_tx == 1)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int pktio_check_pause_rx(void)
+{
+ return pktio_check_flow_control(0, 1);
+}
+
+static int pktio_check_pause_tx(void)
+{
+ return pktio_check_flow_control(0, 0);
+}
+
+static int pktio_check_pause_both(void)
+{
+ int rx = pktio_check_pause_rx();
+ int tx = pktio_check_pause_tx();
+
+ if (rx == ODP_TEST_ACTIVE && tx == ODP_TEST_ACTIVE)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static int pktio_check_pfc_rx(void)
+{
+ return pktio_check_flow_control(1, 1);
+}
+
+static int pktio_check_pfc_tx(void)
+{
+ return pktio_check_flow_control(1, 0);
+}
+
+static int pktio_check_pfc_both(void)
+{
+ int rx = pktio_check_pfc_rx();
+ int tx = pktio_check_pfc_tx();
+
+ if (rx == ODP_TEST_ACTIVE && tx == ODP_TEST_ACTIVE)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static odp_cos_t set_default_cos(odp_pktio_t pktio, odp_queue_t queue)
+{
+ odp_cls_cos_param_t cos_param;
+ odp_cos_t cos;
+ int ret;
+
+ odp_cls_cos_param_init(&cos_param);
+ cos_param.queue = queue;
+ cos_param.pool = pool[0];
+
+ cos = odp_cls_cos_create("Default CoS", &cos_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ ret = odp_pktio_default_cos_set(pktio, cos);
+ CU_ASSERT_FATAL(ret == 0);
+
+ return cos;
+}
+
+static odp_cos_t create_pfc_cos(odp_cos_t default_cos, odp_queue_t queue, odp_pmr_t *pmr_out)
+{
+ odp_cls_cos_param_t cos_param;
+ odp_cos_t cos;
+ odp_pmr_param_t pmr_param;
+ odp_pmr_t pmr;
+ uint8_t pcp = 1;
+ uint8_t mask = 0x7;
+
+ /* Setup a CoS to control generation of PFC frame generation. PFC for the VLAN
+ * priority level is generated when queue/pool resource usage gets above 80%. */
+ odp_cls_cos_param_init(&cos_param);
+ cos_param.queue = queue;
+ cos_param.pool = pool[0];
+ cos_param.bp.enable = 1;
+ cos_param.bp.threshold.type = ODP_THRESHOLD_PERCENT;
+ cos_param.bp.threshold.percent.max = 80;
+ cos_param.bp.pfc_level = pcp;
+
+ cos = odp_cls_cos_create("PFC CoS", &cos_param);
+ CU_ASSERT_FATAL(cos != ODP_COS_INVALID);
+
+ odp_cls_pmr_param_init(&pmr_param);
+ pmr_param.term = ODP_PMR_VLAN_PCP_0;
+ pmr_param.match.value = &pcp;
+ pmr_param.match.mask = &mask;
+ pmr_param.val_sz = 1;
+
+ pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos);
+ CU_ASSERT_FATAL(pmr != ODP_PMR_INVALID);
+
+ *pmr_out = pmr;
+
+ return cos;
+}
+
+static void pktio_config_flow_control(int pfc, int rx, int tx)
+{
+ odp_pktio_t pktio;
+ odp_pktio_config_t config;
+ int ret;
+ odp_cos_t default_cos = ODP_COS_INVALID;
+ odp_cos_t cos = ODP_COS_INVALID;
+ odp_pmr_t pmr = ODP_PMR_INVALID;
+ odp_queue_t queue = ODP_QUEUE_INVALID;
+ odp_pktio_link_pause_t mode = ODP_PKTIO_LINK_PAUSE_ON;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ odp_pktio_config_init(&config);
+
+ if (pfc)
+ mode = ODP_PKTIO_LINK_PFC_ON;
+
+ if (rx)
+ config.flow_control.pause_rx = mode;
+
+ if (tx)
+ config.flow_control.pause_tx = mode;
+
+ ret = odp_pktio_config(pktio, &config);
+ CU_ASSERT_FATAL(ret == 0);
+
+ if (pfc && tx) {
+ /* Enable classifier for PFC backpressure configuration. Overrides previous
+ * pktin queue config. */
+ odp_pktin_queue_param_t pktin_param;
+
+ odp_pktin_queue_param_init(&pktin_param);
+
+ pktin_param.classifier_enable = 1;
+
+ ret = odp_pktin_queue_config(pktio, &pktin_param);
+ CU_ASSERT_FATAL(ret == 0);
+ }
+
+ ret = odp_pktio_start(pktio);
+ CU_ASSERT(ret == 0);
+
+ if (pfc && tx) {
+ odp_queue_param_t qparam;
+
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+
+ queue = odp_queue_create("CoS queue", &qparam);
+ CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID);
+
+ default_cos = set_default_cos(pktio, queue);
+
+ cos = create_pfc_cos(default_cos, queue, &pmr);
+ }
+
+ if (pmr != ODP_PMR_INVALID)
+ odp_cls_pmr_destroy(pmr);
+
+ if (cos != ODP_COS_INVALID)
+ odp_cos_destroy(cos);
+
+ if (default_cos != ODP_COS_INVALID) {
+ odp_pktio_default_cos_set(pktio, ODP_COS_INVALID);
+ odp_cos_destroy(default_cos);
+ }
+
+ if (queue != ODP_QUEUE_INVALID)
+ odp_queue_destroy(queue);
+
+ CU_ASSERT(odp_pktio_stop(pktio) == 0);
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_enable_pause_rx(void)
+{
+ pktio_config_flow_control(0, 1, 0);
+}
+
+static void pktio_test_enable_pause_tx(void)
+{
+ pktio_config_flow_control(0, 0, 1);
+}
+
+static void pktio_test_enable_pause_both(void)
+{
+ pktio_config_flow_control(0, 1, 1);
+}
+
+static void pktio_test_enable_pfc_rx(void)
+{
+ pktio_config_flow_control(1, 1, 0);
+}
+
+static void pktio_test_enable_pfc_tx(void)
+{
+ pktio_config_flow_control(1, 0, 1);
+}
+
+static void pktio_test_enable_pfc_both(void)
+{
+ pktio_config_flow_control(1, 1, 1);
+}
+
+static void pktio_test_pktin_queue_config_direct(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t queue_param;
+ odp_pktin_queue_t pktin_queues[MAX_QUEUES];
+ odp_queue_t in_queues[MAX_QUEUES];
+ int num_queues;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT(odp_pktio_capability(ODP_PKTIO_INVALID, &capa) < 0);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = capa.max_input_queues;
+
+ odp_pktin_queue_param_init(&queue_param);
+
+ queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ queue_param.hash_proto.proto.ipv4_udp = 1;
+ queue_param.num_queues = num_queues;
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES)
+ == num_queues);
+ CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES) < 0);
+
+ queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ queue_param.num_queues = 1;
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_queue_config(ODP_PKTIO_INVALID, &queue_param) < 0);
+
+ queue_param.num_queues = capa.max_input_queues + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_pktin_queue_config_sched(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t queue_param;
+ odp_pktin_queue_t pktin_queues[MAX_QUEUES];
+ odp_queue_t in_queues[MAX_QUEUES];
+ int num_queues;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = capa.max_input_queues;
+
+ odp_pktin_queue_param_init(&queue_param);
+
+ queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ queue_param.hash_proto.proto.ipv4_udp = 1;
+ queue_param.num_queues = num_queues;
+ queue_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ queue_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES)
+ == num_queues);
+ CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES) < 0);
+
+ queue_param.num_queues = 1;
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ queue_param.num_queues = capa.max_input_queues + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_pktin_queue_config_multi_sched(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t queue_param;
+ odp_queue_t in_queues[MAX_QUEUES];
+ odp_pktin_queue_param_ovr_t queue_param_ovr[MAX_QUEUES];
+ int num_queues, i;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = (capa.max_input_queues < MAX_QUEUES) ?
+ capa.max_input_queues : MAX_QUEUES;
+
+ odp_pktin_queue_param_init(&queue_param);
+
+ queue_param.hash_enable = 0;
+ queue_param.num_queues = num_queues;
+ queue_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ queue_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ for (i = 0; i < num_queues; i++)
+ queue_param_ovr[i].group = ODP_SCHED_GROUP_ALL;
+ queue_param.queue_param_ovr = queue_param_ovr;
+
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES)
+ == num_queues);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_pktin_queue_config_queue(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktin_queue_param_t queue_param;
+ odp_pktin_queue_t pktin_queues[MAX_QUEUES];
+ int num_queues;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = capa.max_input_queues;
+ CU_ASSERT_FATAL(num_queues <= ODP_PKTIN_MAX_QUEUES);
+
+ CU_ASSERT(capa.min_input_queue_size <= capa.max_input_queue_size);
+
+ odp_pktin_queue_param_init(&queue_param);
+
+ queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ queue_param.hash_proto.proto.ipv4_udp = 1;
+ queue_param.num_queues = num_queues;
+ for (int i = 0; i < num_queues; i++)
+ queue_param.queue_size[i] = capa.max_input_queue_size;
+
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES) == num_queues);
+
+ queue_param.num_queues = 1;
+ queue_param.queue_size[0] = capa.min_input_queue_size;
+
+ CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ queue_param.num_queues = capa.max_input_queues + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+static void pktio_test_pktout_queue_config(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktout_queue_param_t queue_param;
+ odp_pktout_queue_t pktout_queues[MAX_QUEUES];
+ int num_queues;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_output_queues > 0);
+ num_queues = capa.max_output_queues;
+ CU_ASSERT_FATAL(num_queues <= ODP_PKTOUT_MAX_QUEUES);
+
+ CU_ASSERT(capa.min_output_queue_size <= capa.max_output_queue_size);
+
+ odp_pktout_queue_param_init(&queue_param);
+
+ queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+ queue_param.num_queues = num_queues;
+ for (int i = 0; i < num_queues; i++)
+ queue_param.queue_size[i] = capa.max_output_queue_size;
+
+ CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktout_queue(pktio, pktout_queues, MAX_QUEUES)
+ == num_queues);
+
+ queue_param.op_mode = ODP_PKTIO_OP_MT;
+ queue_param.num_queues = 1;
+ queue_param.queue_size[0] = capa.min_output_queue_size;
+
+ CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) == 0);
+
+ CU_ASSERT(odp_pktout_queue_config(ODP_PKTIO_INVALID, &queue_param) < 0);
+
+ queue_param.num_queues = capa.max_output_queues + 1;
+ CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) < 0);
+
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+#ifdef DEBUG_STATS
+static void _print_pktio_stats(odp_pktio_stats_t *s, const char *name)
+{
+ ODPH_ERR("\n%s:\n"
+ " in_octets %" PRIu64 "\n"
+ " in_packets %" PRIu64 "\n"
+ " in_ucast_pkts %" PRIu64 "\n"
+ " in_mcast_pkts %" PRIu64 "\n"
+ " in_bcast_pkts %" PRIu64 "\n"
+ " in_discards %" PRIu64 "\n"
+ " in_errors %" PRIu64 "\n"
+ " out_octets %" PRIu64 "\n"
+ " out_packets %" PRIu64 "\n"
+ " out_ucast_pkts %" PRIu64 "\n"
+ " out_mcast_pkts %" PRIu64 "\n"
+ " out_bcast_pkts %" PRIu64 "\n"
+ " out_discards %" PRIu64 "\n"
+ " out_errors %" PRIu64 "\n",
+ name,
+ s->in_octets,
+ s->in_packets,
+ s->in_ucast_pkts,
+ s->in_mcast_pkts,
+ s->in_bcast_pkts,
+ s->in_discards,
+ s->in_errors,
+ s->out_octets,
+ s->out_packets,
+ s->out_ucast_pkts,
+ s->out_mcast_pkts,
+ s->out_bcast_pkts,
+ s->out_discards,
+ s->out_errors);
+}
+#endif
+
+static int pktio_check_statistics_counters(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || capa.stats.pktio.all_counters == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_statistics_counters(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t pkt;
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ odp_event_t ev;
+ int i, pkts, tx_pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ odp_pktio_stats_t stats[2];
+ odp_pktio_stats_t *rx_stats, *tx_stats;
+ odp_pktio_capability_t rx_capa, tx_capa;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &tx_capa) == 0);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &rx_capa) == 0);
+
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
+
+ ret = odp_pktio_start(pktio_tx);
+ CU_ASSERT(ret == 0);
+ if (num_ifaces > 1) {
+ ret = odp_pktio_start(pktio_rx);
+ CU_ASSERT(ret == 0);
+ }
+
+ alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx);
+
+ ret = odp_pktio_stats_reset(pktio_tx);
+ CU_ASSERT(ret == 0);
+ if (num_ifaces > 1) {
+ ret = odp_pktio_stats_reset(pktio_rx);
+ CU_ASSERT(ret == 0);
+ }
+
+ /* send */
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to send packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ tx_pkts = pkts;
+
+ /* get */
+ for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+
+ CU_ASSERT(pkts == tx_pkts);
+
+ ret = odp_pktio_stats(pktio_tx, &stats[0]);
+ CU_ASSERT(ret == 0);
+ tx_stats = &stats[0];
+
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_octets == 0) ||
+ (tx_stats->out_octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_packets == 0) ||
+ (tx_stats->out_packets >= (uint64_t)pkts));
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_ucast_pkts == 0) ||
+ (tx_stats->out_ucast_pkts >= (uint64_t)pkts));
+ CU_ASSERT(tx_stats->out_discards == 0);
+ CU_ASSERT(tx_stats->out_errors == 0);
+
+ rx_stats = &stats[0];
+ if (num_ifaces > 1) {
+ rx_stats = &stats[1];
+ ret = odp_pktio_stats(pktio_rx, rx_stats);
+ CU_ASSERT(ret == 0);
+ }
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_octets == 0) ||
+ (rx_stats->in_octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_packets == 0) ||
+ (rx_stats->in_packets >= (uint64_t)pkts));
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_ucast_pkts == 0) ||
+ (rx_stats->in_ucast_pkts >= (uint64_t)pkts));
+ CU_ASSERT(rx_stats->in_discards == 0);
+ CU_ASSERT(rx_stats->in_errors == 0);
+
+ /* Check that all unsupported counters are still zero */
+ if (!rx_capa.stats.pktio.counter.in_octets)
+ CU_ASSERT(rx_stats->in_octets == 0);
+ if (!rx_capa.stats.pktio.counter.in_packets)
+ CU_ASSERT(rx_stats->in_packets == 0);
+ if (!rx_capa.stats.pktio.counter.in_ucast_pkts)
+ CU_ASSERT(rx_stats->in_ucast_pkts == 0);
+ if (!rx_capa.stats.pktio.counter.in_mcast_pkts)
+ CU_ASSERT(rx_stats->in_mcast_pkts == 0);
+ if (!rx_capa.stats.pktio.counter.in_bcast_pkts)
+ CU_ASSERT(rx_stats->in_bcast_pkts == 0);
+ if (!rx_capa.stats.pktio.counter.in_discards)
+ CU_ASSERT(rx_stats->in_discards == 0);
+ if (!rx_capa.stats.pktio.counter.in_errors)
+ CU_ASSERT(rx_stats->in_errors == 0);
+
+ if (!tx_capa.stats.pktio.counter.out_octets)
+ CU_ASSERT(tx_stats->out_octets == 0);
+ if (!tx_capa.stats.pktio.counter.out_packets)
+ CU_ASSERT(tx_stats->out_packets == 0);
+ if (!tx_capa.stats.pktio.counter.out_ucast_pkts)
+ CU_ASSERT(tx_stats->out_ucast_pkts == 0);
+ if (!tx_capa.stats.pktio.counter.out_mcast_pkts)
+ CU_ASSERT(tx_stats->out_mcast_pkts == 0);
+ if (!tx_capa.stats.pktio.counter.out_bcast_pkts)
+ CU_ASSERT(tx_stats->out_bcast_pkts == 0);
+ if (!tx_capa.stats.pktio.counter.out_discards)
+ CU_ASSERT(tx_stats->out_discards == 0);
+ if (!tx_capa.stats.pktio.counter.out_errors)
+ CU_ASSERT(tx_stats->out_errors == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+#ifdef DEBUG_STATS
+ _print_pktio_stats(&stats[i], iface_name[i]);
+#endif
+ flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_statistics_counters_bcast(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || (capa.stats.pktio.counter.in_bcast_pkts == 0 &&
+ capa.stats.pktio.counter.out_bcast_pkts == 0))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_statistics_counters_bcast(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t pkt;
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ odp_event_t ev;
+ int i, pkts, tx_pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ odp_pktio_stats_t stats[2];
+ odp_pktio_stats_t *rx_stats, *tx_stats;
+ odp_pktio_capability_t rx_capa, tx_capa;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &tx_capa) == 0);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &rx_capa) == 0);
+
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_rx) == 0);
+
+ alloc = create_packets_udp(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx,
+ true, ETH_BROADCAST);
+
+ CU_ASSERT(odp_pktio_stats_reset(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT(odp_pktio_stats_reset(pktio_rx) == 0);
+
+ /* send */
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to send packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ tx_pkts = pkts;
+
+ /* get */
+ for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+
+ CU_ASSERT(pkts == tx_pkts);
+
+ CU_ASSERT(odp_pktio_stats(pktio_tx, &stats[0]) == 0);
+ tx_stats = &stats[0];
+
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_bcast_pkts == 0) ||
+ (tx_stats->out_bcast_pkts >= (uint64_t)pkts));
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_octets == 0) ||
+ (tx_stats->out_octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((tx_capa.stats.pktio.counter.out_packets == 0) ||
+ (tx_stats->out_packets >= (uint64_t)pkts));
+
+ rx_stats = &stats[0];
+ if (num_ifaces > 1) {
+ rx_stats = &stats[1];
+ CU_ASSERT(odp_pktio_stats(pktio_rx, rx_stats) == 0);
+ }
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_bcast_pkts == 0) ||
+ (rx_stats->in_bcast_pkts >= (uint64_t)pkts));
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_octets == 0) ||
+ (rx_stats->in_octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((rx_capa.stats.pktio.counter.in_packets == 0) ||
+ (rx_stats->in_packets >= (uint64_t)pkts));
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+#ifdef DEBUG_STATS
+ _print_pktio_stats(&stats[i], iface_name[i]);
+#endif
+ flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_queue_statistics_counters(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || (capa.stats.pktin_queue.all_counters == 0 &&
+ capa.stats.pktout_queue.all_counters == 0))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_queue_statistics_counters(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ int i, pkts, tx_pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ odp_pktin_queue_t pktin;
+ uint64_t wait = odp_pktin_wait_time(ODP_TIME_SEC_IN_NS);
+ odp_pktin_queue_stats_t rx_stats;
+ odp_pktout_queue_stats_t tx_stats;
+ odp_pktio_capability_t rx_capa, tx_capa;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &tx_capa) == 0);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &rx_capa) == 0);
+
+ CU_ASSERT_FATAL(odp_pktin_queue(pktio_rx, &pktin, 1) == 1);
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_rx) == 0);
+
+ alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx);
+
+ CU_ASSERT(odp_pktio_stats_reset(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT(odp_pktio_stats_reset(pktio_rx) == 0);
+
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to send packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ tx_pkts = pkts;
+
+ for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) {
+ odp_packet_t pkt;
+
+ if (odp_pktin_recv_tmo(pktin, &pkt, 1, wait) != 1)
+ break;
+
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+
+ odp_packet_free(pkt);
+ }
+
+ CU_ASSERT(pkts == tx_pkts);
+
+ CU_ASSERT_FATAL(odp_pktout_queue_stats(pktout, &tx_stats) == 0);
+ CU_ASSERT((!tx_capa.stats.pktout_queue.counter.octets) ||
+ (tx_stats.octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((!tx_capa.stats.pktout_queue.counter.packets) ||
+ (tx_stats.packets >= (uint64_t)pkts));
+ CU_ASSERT(tx_stats.discards == 0);
+ CU_ASSERT(tx_stats.errors == 0);
+
+ CU_ASSERT_FATAL(odp_pktin_queue_stats(pktin, &rx_stats) == 0);
+ CU_ASSERT((!rx_capa.stats.pktin_queue.counter.octets) ||
+ (rx_stats.octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((!rx_capa.stats.pktin_queue.counter.packets) ||
+ (rx_stats.packets >= (uint64_t)pkts));
+ CU_ASSERT(rx_stats.discards == 0);
+ CU_ASSERT(rx_stats.errors == 0);
+
+ /* Check that all unsupported counters are still zero */
+ if (!rx_capa.stats.pktin_queue.counter.octets)
+ CU_ASSERT(rx_stats.octets == 0);
+ if (!rx_capa.stats.pktin_queue.counter.packets)
+ CU_ASSERT(rx_stats.packets == 0);
+ if (!tx_capa.stats.pktout_queue.counter.octets)
+ CU_ASSERT(tx_stats.octets == 0);
+ if (!tx_capa.stats.pktout_queue.counter.packets)
+ CU_ASSERT(tx_stats.packets == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_event_queue_statistics_counters(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_QUEUE;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || (capa.stats.pktin_queue.all_counters == 0 &&
+ capa.stats.pktout_queue.all_counters == 0))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_event_queue_statistics_counters(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t pkt;
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ odp_event_t ev;
+ int i, pkts, tx_pkts;
+ odp_queue_t pktout;
+ odp_queue_t pktin;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ odp_pktin_queue_stats_t rx_stats;
+ odp_pktout_queue_stats_t tx_stats;
+ odp_pktio_capability_t rx_capa, tx_capa;
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_QUEUE);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &tx_capa) == 0);
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &rx_capa) == 0);
+
+ CU_ASSERT_FATAL(odp_pktin_event_queue(pktio_rx, &pktin, 1) == 1);
+ CU_ASSERT_FATAL(odp_pktout_event_queue(pktio_tx, &pktout, 1) == 1);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT_FATAL(odp_pktio_start(pktio_rx) == 0);
+
+ tx_pkts = create_packets(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx);
+
+ CU_ASSERT(odp_pktio_stats_reset(pktio_tx) == 0);
+ if (num_ifaces > 1)
+ CU_ASSERT(odp_pktio_stats_reset(pktio_rx) == 0);
+
+ CU_ASSERT_FATAL(send_packet_events(pktout, tx_pkt, tx_pkts) == 0);
+
+ /* Receive */
+ for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+ CU_ASSERT(pkts == tx_pkts);
+
+ CU_ASSERT_FATAL(odp_pktout_event_queue_stats(pktio_tx, pktout, &tx_stats) == 0);
+ CU_ASSERT((!tx_capa.stats.pktout_queue.counter.octets) ||
+ (tx_stats.octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((!tx_capa.stats.pktout_queue.counter.packets) ||
+ (tx_stats.packets >= (uint64_t)pkts));
+ CU_ASSERT(tx_stats.discards == 0);
+ CU_ASSERT(tx_stats.errors == 0);
+
+ CU_ASSERT_FATAL(odp_pktin_event_queue_stats(pktio_rx, pktin, &rx_stats) == 0);
+ CU_ASSERT((!rx_capa.stats.pktin_queue.counter.octets) ||
+ (rx_stats.octets >= (PKT_LEN_NORMAL * (uint64_t)pkts)));
+ CU_ASSERT((!rx_capa.stats.pktin_queue.counter.packets) ||
+ (rx_stats.packets >= (uint64_t)pkts));
+ CU_ASSERT(rx_stats.discards == 0);
+ CU_ASSERT(rx_stats.errors == 0);
+
+ /* Check that all unsupported counters are still zero */
+ if (!rx_capa.stats.pktin_queue.counter.octets)
+ CU_ASSERT(rx_stats.octets == 0);
+ if (!rx_capa.stats.pktin_queue.counter.packets)
+ CU_ASSERT(rx_stats.packets == 0);
+ if (!tx_capa.stats.pktout_queue.counter.octets)
+ CU_ASSERT(tx_stats.octets == 0);
+ if (!tx_capa.stats.pktout_queue.counter.packets)
+ CU_ASSERT(tx_stats.packets == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+ flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_extra_stats(void)
+{
+ odp_pktio_t pktio;
+ int num_info, num_stats, i, ret;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID)
+ CU_ASSERT_FATAL(odp_pktio_start(pktio) == 0);
+
+ num_info = odp_pktio_extra_stat_info(pktio, NULL, 0);
+ CU_ASSERT_FATAL(num_info >= 0);
+
+ num_stats = odp_pktio_extra_stats(pktio, NULL, 0);
+ CU_ASSERT_FATAL(num_stats >= 0);
+
+ CU_ASSERT_FATAL(num_info == num_stats);
+
+ /* No extra statistics supported */
+ if (num_stats == 0) {
+ CU_ASSERT(odp_pktio_stop(pktio) == 0);
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+ return;
+ }
+
+ odp_pktio_extra_stat_info_t stats_info[num_stats];
+ uint64_t extra_stats[num_stats];
+
+ ret = odp_pktio_extra_stat_info(pktio, stats_info, num_stats);
+ CU_ASSERT(ret == num_stats);
+ num_info = ret;
+
+ ret = odp_pktio_extra_stats(pktio, extra_stats, num_stats);
+ CU_ASSERT(ret == num_stats);
+ CU_ASSERT_FATAL(ret <= num_stats);
+ num_stats = ret;
+
+ CU_ASSERT_FATAL(num_info == num_stats);
+
+ printf("\nPktio extra statistics\n----------------------\n");
+ for (i = 0; i < num_stats; i++)
+ printf(" %s=%" PRIu64 "\n", stats_info[i].name, extra_stats[i]);
+
+ for (i = 0; i < num_stats; i++) {
+ uint64_t stat = 0;
+
+ CU_ASSERT(odp_pktio_extra_stat_counter(pktio, i, &stat) == 0);
+ }
+
+ odp_pktio_extra_stats_print(pktio);
+
+ CU_ASSERT(odp_pktio_stop(pktio) == 0);
+ CU_ASSERT(odp_pktio_close(pktio) == 0);
+}
+
+static int pktio_check_proto_statistics_counters(void)
+{
+ odp_proto_stats_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_proto_stats_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || capa.tx.counters.all_bits == 0)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void validate_proto_stats(odp_proto_stats_t stat, odp_packet_proto_stats_opt_t opt,
+ odp_proto_stats_capability_t capa, uint64_t pkts)
+{
+ odp_proto_stats_data_t data;
+ int ret;
+
+ ret = odp_proto_stats(stat, &data);
+ CU_ASSERT(ret == 0);
+
+ CU_ASSERT(!(capa.tx.counters.bit.tx_pkt_drops && (data.tx_pkt_drops > 0)));
+ CU_ASSERT(!(capa.tx.counters.bit.tx_oct_count0_drops && (data.tx_oct_count0_drops > 0)));
+ CU_ASSERT(!(capa.tx.counters.bit.tx_oct_count1_drops && (data.tx_oct_count1_drops > 0)));
+ CU_ASSERT(!(capa.tx.counters.bit.tx_pkts && (data.tx_pkts != pkts)));
+
+ if (capa.tx.counters.bit.tx_oct_count0) {
+ int64_t counted_bytes = PKT_LEN_NORMAL;
+
+ if (capa.tx.oct_count0_adj)
+ counted_bytes += opt.oct_count0_adj;
+ CU_ASSERT(data.tx_oct_count0 == counted_bytes * pkts);
+ }
+
+ if (capa.tx.counters.bit.tx_oct_count1) {
+ int64_t counted_bytes = PKT_LEN_NORMAL;
+
+ if (capa.tx.oct_count1_adj)
+ counted_bytes += opt.oct_count1_adj;
+ CU_ASSERT(data.tx_oct_count1 == counted_bytes * pkts);
+ }
+}
+
+static void pktio_test_proto_statistics_counters(void)
+{
+ odp_pktio_t pktio_rx, pktio_tx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {
+ ODP_PKTIO_INVALID, ODP_PKTIO_INVALID
+ };
+ odp_packet_t pkt;
+ const uint32_t num_pkts = 10;
+ odp_packet_t tx_pkt[num_pkts];
+ uint32_t pkt_seq[num_pkts];
+ odp_event_t ev;
+ int i, pkts, tx_pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+ uint64_t flow0_pkts = 0, flow1_pkts = 0;
+ odp_proto_stats_capability_t capa;
+ odp_packet_proto_stats_opt_t opt0;
+ odp_packet_proto_stats_opt_t opt1;
+ odp_proto_stats_param_t param;
+ odp_pktio_config_t config;
+ odp_proto_stats_t stat0;
+ odp_proto_stats_t stat1;
+
+ memset(&pktout, 0, sizeof(pktout));
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ /* Enable protocol stats on Tx interface */
+ odp_pktio_config_init(&config);
+ config.pktout.bit.proto_stats_ena = 1;
+ ret = odp_pktio_config(pktio_tx, &config);
+ CU_ASSERT(ret == 0);
+
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1);
+
+ ret = odp_pktio_start(pktio_tx);
+ CU_ASSERT(ret == 0);
+ if (num_ifaces > 1) {
+ ret = odp_pktio_start(pktio_rx);
+ CU_ASSERT(ret == 0);
+ }
+
+ odp_proto_stats_param_init(&param);
+ odp_proto_stats_capability(pktio_tx, &capa);
+ CU_ASSERT(capa.tx.counters.all_bits != 0);
+ param.counters.all_bits = capa.tx.counters.all_bits;
+ /* Create statistics object with all supported counters */
+ stat0 = odp_proto_stats_create("flow0_stat", &param);
+ CU_ASSERT_FATAL(stat0 != ODP_PROTO_STATS_INVALID);
+ stat1 = odp_proto_stats_create("flow1_stat", &param);
+ CU_ASSERT_FATAL(stat1 != ODP_PROTO_STATS_INVALID);
+
+ /* Flow-0 options */
+ opt0.stat = stat0;
+ opt0.oct_count0_adj = 0;
+ /* oct1 contains byte count of packets excluding Ethernet header */
+ opt0.oct_count1_adj = -14;
+
+ /* Flow-1 options */
+ opt1.stat = stat1;
+ opt1.oct_count0_adj = -8;
+ opt1.oct_count1_adj = 14;
+
+ alloc = create_packets(tx_pkt, pkt_seq, num_pkts, pktio_tx, pktio_rx);
+
+ /* Attach statistics object to all Tx packets */
+ for (pkts = 0; pkts < alloc; pkts++) {
+ if ((pkts % 2) == 0) {
+ odp_packet_proto_stats_request(tx_pkt[pkts], &opt0);
+ flow0_pkts++;
+ } else {
+ odp_packet_proto_stats_request(tx_pkt[pkts], &opt1);
+ flow1_pkts++;
+ }
+ }
+
+ /* send */
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to send packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ tx_pkts = pkts;
+
+ /* get */
+ for (i = 0, pkts = 0; i < (int)num_pkts && pkts != tx_pkts; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+
+ CU_ASSERT(pkts == tx_pkts);
+
+ /* Validate Flow-0 packet statistics */
+ validate_proto_stats(stat0, opt0, capa, flow0_pkts);
+
+ /* Validate Flow-1 packet statistics */
+ validate_proto_stats(stat1, opt1, capa, flow1_pkts);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+ flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+
+ /* Destroy proto statistics object */
+ CU_ASSERT(odp_proto_stats_destroy(stat0) == 0);
+ CU_ASSERT(odp_proto_stats_destroy(stat1) == 0);
+}
+
+static int pktio_check_start_stop(void)
+{
+ if (getenv("ODP_PKTIO_TEST_DISABLE_START_STOP"))
+ return ODP_TEST_INACTIVE;
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_start_stop(void)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES];
+ odp_pktio_t pktio_in;
+ odp_packet_t pkt;
+ odp_packet_t tx_pkt[1000];
+ uint32_t pkt_seq[1000];
+ odp_event_t ev;
+ int i, pkts, ret, alloc = 0;
+ odp_pktout_queue_t pktout;
+ uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio[0], &pktout, 1) == 1);
+
+ /* Interfaces are stopped by default,
+ * Check that stop when stopped generates an error */
+ ret = odp_pktio_stop(pktio[0]);
+ CU_ASSERT(ret < 0);
+
+ /* start first */
+ ret = odp_pktio_start(pktio[0]);
+ CU_ASSERT(ret == 0);
+ /* Check that start when started generates an error */
+ ret = odp_pktio_start(pktio[0]);
+ CU_ASSERT(ret < 0);
+
+ _pktio_wait_linkup(pktio[0]);
+
+ /* Test Rx on a stopped interface. Only works if there are 2 */
+ if (num_ifaces > 1) {
+ alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio[0],
+ pktio[1]);
+
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts],
+ alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to enqueue packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+ /* check that packets did not arrive */
+ for (i = 0, pkts = 0; i < 1000; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ if (pkts)
+ CU_FAIL("pktio stopped, received unexpected events");
+
+ /* start both, send and get packets */
+ /* 0 already started */
+ ret = odp_pktio_start(pktio[1]);
+ CU_ASSERT(ret == 0);
+
+ _pktio_wait_linkup(pktio[1]);
+
+ /* flush packets with magic number in pipes */
+ for (i = 0; i < 1000; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+ }
+ }
+
+ if (num_ifaces > 1)
+ pktio_in = pktio[1];
+ else
+ pktio_in = pktio[0];
+
+ alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio[0], pktio_in);
+
+ /* send */
+ for (pkts = 0; pkts != alloc; ) {
+ ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts);
+ if (ret < 0) {
+ CU_FAIL("unable to enqueue packet\n");
+ break;
+ }
+ pkts += ret;
+ }
+
+ /* get */
+ for (i = 0, pkts = 0; i < 1000; i++) {
+ ev = odp_schedule(NULL, wait);
+ if (ev != ODP_EVENT_INVALID) {
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ pkts++;
+ }
+ odp_event_free(ev);
+ }
+ }
+ CU_ASSERT(pkts == alloc);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
+ }
+
+ /* Verify that a schedule call after stop and close does not generate
+ errors. */
+ ev = odp_schedule(NULL, wait);
+ CU_ASSERT(ev == ODP_EVENT_INVALID);
+ if (ev != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+}
+
+static void pktio_test_recv_on_wonly(void)
+{
+ odp_pktio_t pktio;
+ int ret;
+ odp_pktin_queue_t pktin;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DISABLED,
+ ODP_PKTOUT_MODE_DIRECT);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ CU_FAIL("failed to open pktio");
+ return;
+ }
+
+ CU_ASSERT(odp_pktin_queue(pktio, &pktin, 1) == 0);
+
+ ret = odp_pktio_start(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+
+ _pktio_wait_linkup(pktio);
+
+ ret = odp_pktio_stop(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+}
+
+static void pktio_test_send_on_ronly(void)
+{
+ odp_pktio_t pktio;
+ int ret;
+ odp_pktout_queue_t pktout;
+
+ pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DISABLED);
+
+ if (pktio == ODP_PKTIO_INVALID) {
+ CU_FAIL("failed to open pktio");
+ return;
+ }
+
+ CU_ASSERT(odp_pktout_queue(pktio, &pktout, 1) == 0);
+
+ ret = odp_pktio_start(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+
+ _pktio_wait_linkup(pktio);
+
+ ret = odp_pktio_stop(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_pktio_close(pktio);
+ CU_ASSERT_FATAL(ret == 0);
+}
+
+static int pktio_check_pktin_ts(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.config.pktin.bit.ts_all)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_pktin_ts(void)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ pktio_info_t pktio_rx_info;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+ odp_pktout_queue_t pktout_queue;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ uint64_t ns1, ns2;
+ uint64_t res, res_ns, input_delay;
+ odp_time_t ts_prev;
+ odp_time_t ts;
+ int num_rx = 0;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.ts_all);
+
+ odp_pktio_config_init(&config);
+ config.pktin.bit.ts_all = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ /* Test odp_pktio_ts_res() and odp_pktio_ts_from_ns() */
+ res = odp_pktio_ts_res(pktio_tx);
+ CU_ASSERT(res > PKTIO_TS_MIN_RES);
+ CU_ASSERT(res < PKTIO_TS_MAX_RES);
+ ns1 = 100;
+ ts = odp_pktio_ts_from_ns(pktio_tx, ns1);
+ ns2 = odp_time_to_ns(ts);
+ CU_ASSERT_FATAL(res != 0);
+ res_ns = ODP_TIME_SEC_IN_NS / res;
+ if (ODP_TIME_SEC_IN_NS % res)
+ res_ns++;
+ /* Allow some arithmetic tolerance */
+ CU_ASSERT((ns2 <= (ns1 + res_ns)) && (ns2 >= (ns1 - res_ns)));
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Send packets one at a time and add delay between the packets */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue,
+ &pkt_tbl[i], 1) == 1);
+ ret = wait_for_packets(&pktio_rx_info, &pkt_tbl[i], &pkt_seq[i],
+ 1, TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS, false);
+ if (ret != 1)
+ break;
+
+ /* Compare to packet IO time to input timestamp */
+ ts = odp_pktio_time(pktio_rx_info.id, NULL);
+ CU_ASSERT_FATAL(odp_packet_has_ts(pkt_tbl[i]));
+ ts_prev = odp_packet_ts(pkt_tbl[i]);
+ CU_ASSERT(odp_time_cmp(ts, ts_prev) >= 0);
+ input_delay = odp_time_diff_ns(ts, ts_prev);
+ if (input_delay > 100 * ODP_TIME_MSEC_IN_NS) {
+ printf(" Test packet %d input delay: %" PRIu64 "ns\n", i, input_delay);
+ CU_FAIL("Packet input delay too long");
+ }
+
+ odp_time_wait_ns(PKTIO_TS_INTERVAL);
+ }
+ num_rx = i;
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+
+ ts_prev = ODP_TIME_NULL;
+ for (i = 0; i < num_rx; i++) {
+ ts = odp_packet_ts(pkt_tbl[i]);
+
+ CU_ASSERT(odp_time_cmp(ts, ts_prev) > 0);
+
+ ts_prev = ts;
+ odp_packet_free(pkt_tbl[i]);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_pktout_ts(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.config.pktout.bit.ts_ena)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_pktout_ts(void)
+{
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ odp_pktout_queue_t pktout_queue;
+ odp_pktio_t pktio_tx, pktio_rx;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ pktio_info_t pktio_rx_info;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+ odp_time_t ts_prev;
+ odp_time_t ts;
+ int num_rx = 0;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.ts_all);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.ts_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Start with current pktio time */
+ ts_prev = odp_pktio_time(pktio_tx, NULL);
+
+ odp_time_wait_ns(PKTIO_TS_INTERVAL);
+
+ /* Send packets one at a time and add delay between the packets */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ /* Enable ts capture on this pkt */
+ odp_packet_ts_request(pkt_tbl[i], 1);
+
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue,
+ &pkt_tbl[i], 1) == 1);
+ ret = wait_for_packets(&pktio_rx_info, &pkt_tbl[i], &pkt_seq[i],
+ 1, TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS,
+ false);
+ if (ret != 1)
+ break;
+
+ /* Since we got packet back, check for sent ts */
+ CU_ASSERT_FATAL(odp_pktout_ts_read(pktio_tx, &ts) == 0);
+
+ CU_ASSERT(odp_time_cmp(ts, ts_prev) > 0);
+ ts_prev = ts;
+
+ odp_time_wait_ns(PKTIO_TS_INTERVAL);
+ }
+ num_rx = i;
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_pktout_compl_event(bool use_plain_queue)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ odp_queue_t compl_queue[TX_BATCH_LEN];
+ odp_schedule_capability_t sched_capa;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ char queuename[ODP_QUEUE_NAME_LEN];
+ odp_pktio_capability_t pktio_capa;
+ odp_queue_capability_t queue_capa;
+ uint16_t seq_found[TX_BATCH_LEN];
+ odp_pktout_queue_t pktout_queue;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_packet_tx_compl_t tx_compl;
+ odp_packet_tx_compl_opt_t opt;
+ pktio_info_t pktio_rx_info;
+ odp_pktio_config_t config;
+ odp_queue_param_t qparam;
+ int flag, ret, i, num_rx = 0;
+ odp_event_t ev;
+ uint64_t wait;
+
+ /* Create queues to receive PKTIO Tx completion events */
+ CU_ASSERT_FATAL(!odp_schedule_capability(&sched_capa));
+ CU_ASSERT_FATAL(!odp_queue_capability(&queue_capa));
+
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ sprintf(queuename, "TxComplQueue%u", i);
+ odp_queue_param_init(&qparam);
+
+ if (use_plain_queue) {
+ qparam.type = ODP_QUEUE_TYPE_PLAIN;
+ } else {
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_default_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+ }
+ compl_queue[i] = odp_queue_create(queuename, &qparam);
+ CU_ASSERT_FATAL(compl_queue[i] != ODP_QUEUE_INVALID);
+ }
+
+ memset(&pktout_queue, 0, sizeof(pktout_queue));
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &pktio_capa) == 0);
+
+ /* Configure Tx completion offload for PKTIO Tx */
+ if (i == 0) {
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.mode_event == 1);
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.mode_all ==
+ pktio_capa.tx_compl.mode_event);
+ if (use_plain_queue) {
+ /* CU_ASSERT needs these extra braces */
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.queue_type_plain != 0);
+ } else {
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.queue_type_sched != 0);
+ }
+
+ odp_pktio_config_init(&config);
+ config.tx_compl.mode_event = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx, pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ memset(&opt, 0, sizeof(opt));
+
+ /* Disabled by default */
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+
+ /* Check that disable works. Also COMPL_ALL should be still supported. */
+ opt.queue = compl_queue[0];
+ opt.mode = ODP_PACKET_TX_COMPL_ALL;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) != 0);
+ opt.mode = ODP_PACKET_TX_COMPL_DISABLED;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+ opt.queue = compl_queue[0];
+ opt.mode = ODP_PACKET_TX_COMPL_EVENT;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) != 0);
+ opt.mode = ODP_PACKET_TX_COMPL_DISABLED;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+
+ /* Prepare batch of pkts with different tx completion queues */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[i]) == 0);
+ opt.queue = compl_queue[i];
+ opt.mode = ODP_PACKET_TX_COMPL_EVENT;
+ odp_packet_tx_compl_request(pkt_tbl[i], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[i]) != 0);
+ /* Set pkt sequence number as its user ptr */
+ odp_packet_user_ptr_set(pkt_tbl[i], (const void *)&pkt_seq[i]);
+ }
+
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue, pkt_tbl, TX_BATCH_LEN) == TX_BATCH_LEN);
+
+ num_rx = wait_for_packets(&pktio_rx_info, pkt_tbl, pkt_seq, TX_BATCH_LEN, TXRX_MODE_SINGLE,
+ ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ wait = odp_schedule_wait_time(ODP_TIME_SEC_IN_NS);
+ memset(seq_found, 0, sizeof(seq_found));
+
+ /* Receive Packet Tx completion events for all sent/dropped pkts */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ if (use_plain_queue) {
+ ev = odp_queue_deq(compl_queue[i]);
+
+ /* Event validation */
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT_FATAL(odp_event_is_valid(ev) == 1);
+ CU_ASSERT_FATAL(odp_event_type(ev) == ODP_EVENT_PACKET_TX_COMPL);
+ CU_ASSERT_FATAL(odp_packet_tx_compl_from_event(ev) !=
+ ODP_PACKET_TX_COMPL_INVALID);
+
+ tx_compl = odp_packet_tx_compl_from_event(ev);
+ CU_ASSERT_FATAL(odp_packet_tx_compl_to_event(tx_compl) == ev);
+
+ /* User ptr should be same as packet's user ptr */
+ CU_ASSERT(odp_packet_tx_compl_user_ptr(tx_compl) ==
+ (const void *)&pkt_seq[i]);
+
+ /* No user area or source pool for TX completion events */
+ CU_ASSERT(odp_event_user_area(ev) == NULL);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == NULL);
+ CU_ASSERT(flag < 0);
+
+ CU_ASSERT(odp_event_pool(ev) == ODP_POOL_INVALID);
+
+ /* Alternatively call event free / compl free */
+ if (i % 2)
+ odp_packet_tx_compl_free(tx_compl);
+ else
+ odp_event_free(ev);
+ } else {
+ odp_queue_t rcv_queue;
+ int j;
+
+ ev = odp_schedule(&rcv_queue, wait);
+
+ /* Event validation */
+ CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID);
+ CU_ASSERT_FATAL(odp_event_is_valid(ev) == 1);
+ CU_ASSERT_FATAL(odp_event_type(ev) == ODP_EVENT_PACKET_TX_COMPL);
+ CU_ASSERT_FATAL(odp_packet_tx_compl_from_event(ev) !=
+ ODP_PACKET_TX_COMPL_INVALID);
+
+ tx_compl = odp_packet_tx_compl_from_event(ev);
+ CU_ASSERT_FATAL(odp_packet_tx_compl_to_event(tx_compl) == ev);
+
+ /* User ptr should be same as packet's user ptr i.e seq array ptr */
+ for (j = 0; j < TX_BATCH_LEN; j++) {
+ if (!seq_found[j] &&
+ ((const void *)&pkt_seq[j] ==
+ odp_packet_tx_compl_user_ptr(tx_compl))) {
+ /* Mark that sequence number is found */
+ seq_found[j] = 1;
+
+ /* Receive queue validation */
+ CU_ASSERT(rcv_queue == compl_queue[j]);
+ break;
+ }
+ }
+
+ /* No user area or source pool for TX completion events */
+ CU_ASSERT(odp_event_user_area(ev) == NULL);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == NULL);
+ CU_ASSERT(flag < 0);
+
+ CU_ASSERT(odp_event_pool(ev) == ODP_POOL_INVALID);
+
+ /* Check that sequence number is found */
+ CU_ASSERT(j < TX_BATCH_LEN);
+
+ /* Alternatively call event free / compl free */
+ if (i % 2)
+ odp_packet_tx_compl_free(tx_compl);
+ else
+ odp_event_free(ev);
+ }
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+
+ odp_schedule_pause();
+
+ while (1) {
+ ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ odp_event_free(ev);
+ }
+
+ odp_schedule_resume();
+
+ for (i = 0; i < TX_BATCH_LEN; i++)
+ odp_queue_destroy(compl_queue[i]);
+}
+
+static void pktio_test_pktout_compl_poll(void)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_pktio_capability_t pktio_capa;
+ odp_pktout_queue_t pktout_queue;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_packet_tx_compl_opt_t opt;
+ pktio_info_t pktio_rx_info;
+ odp_pktio_config_t config;
+ int ret, i, num_rx = 0;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &pktio_capa) == 0);
+
+ /* Configure Tx completion offload for PKTIO Tx */
+ if (i == 0) {
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.mode_poll == 1);
+ CU_ASSERT_FATAL(pktio_capa.tx_compl.max_compl_id >= (TX_BATCH_LEN - 1));
+
+ odp_pktio_config_init(&config);
+ config.tx_compl.mode_poll = 1;
+ config.tx_compl.max_compl_id = TX_BATCH_LEN - 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ /* Completion status is initially zero */
+ CU_ASSERT(odp_packet_tx_compl_done(pktio_tx, i) == 0);
+ }
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx, pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ memset(&opt, 0, sizeof(opt));
+
+ /* Disabled by default */
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+
+ /* Check that disable works */
+ opt.compl_id = 0;
+ opt.mode = ODP_PACKET_TX_COMPL_POLL;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) != 0);
+ opt.mode = ODP_PACKET_TX_COMPL_DISABLED;
+ odp_packet_tx_compl_request(pkt_tbl[0], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[0]) == 0);
+
+ /* Prepare batch of pkts with different tx completion identifiers */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[i]) == 0);
+ opt.compl_id = i;
+ opt.mode = ODP_PACKET_TX_COMPL_POLL;
+ odp_packet_tx_compl_request(pkt_tbl[i], &opt);
+ CU_ASSERT(odp_packet_has_tx_compl_request(pkt_tbl[i]) != 0);
+ /* Set pkt sequence number as its user ptr */
+ odp_packet_user_ptr_set(pkt_tbl[i], (const void *)&pkt_seq[i]);
+
+ /* Completion status should be still zero after odp_packet_tx_compl_request() */
+ CU_ASSERT(odp_packet_tx_compl_done(pktio_tx, i) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue, pkt_tbl, TX_BATCH_LEN) == TX_BATCH_LEN);
+
+ num_rx = wait_for_packets(&pktio_rx_info, pkt_tbl, pkt_seq, TX_BATCH_LEN, TXRX_MODE_SINGLE,
+ ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_rx; i++) {
+ /* Transmits should be complete since we received the packets already */
+ CU_ASSERT(odp_packet_tx_compl_done(pktio_tx, i) > 0);
+
+ /* Check that the previous call did not clear the status */
+ CU_ASSERT(odp_packet_tx_compl_done(pktio_tx, i) > 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_pktout_compl_event(bool plain)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.tx_compl.mode_event ||
+ (plain && !capa.tx_compl.queue_type_plain) ||
+ (!plain && !capa.tx_compl.queue_type_sched))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pktio_check_pktout_compl_poll(void)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || capa.tx_compl.mode_poll == 0 ||
+ capa.tx_compl.max_compl_id < (TX_BATCH_LEN - 1))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pktio_check_pktout_compl_event_plain_queue(void)
+{
+ return pktio_check_pktout_compl_event(true);
+}
+
+static int pktio_check_pktout_compl_event_sched_queue(void)
+{
+ return pktio_check_pktout_compl_event(false);
+}
+
+static void pktio_test_pktout_compl_event_plain_queue(void)
+{
+ pktio_test_pktout_compl_event(true);
+}
+
+static void pktio_test_pktout_compl_event_sched_queue(void)
+{
+ pktio_test_pktout_compl_event(false);
+}
+
+static void pktio_test_pktout_dont_free(void)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ odp_packet_t pkt, rx_pkt;
+ odp_pktio_capability_t pktio_capa;
+ odp_pktout_queue_t pktout_queue;
+ odp_pktio_t pktio_tx, pktio_rx;
+ pktio_info_t pktio_rx_info;
+ uint32_t pkt_seq;
+ int ret, i;
+ const int num_pkt = 1;
+ int transmits = 5;
+ int num_rx = 0;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+
+ /* Check TX interface capa */
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &pktio_capa) == 0);
+ CU_ASSERT_FATAL(pktio_capa.free_ctrl.dont_free == 1);
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ ret = create_packets(&pkt, &pkt_seq, num_pkt, pktio_tx, pktio_rx);
+ CU_ASSERT_FATAL(ret == num_pkt);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Set don't free flag */
+ CU_ASSERT(odp_packet_free_ctrl(pkt) == ODP_PACKET_FREE_CTRL_DISABLED);
+ odp_packet_free_ctrl_set(pkt, ODP_PACKET_FREE_CTRL_DONT_FREE);
+ CU_ASSERT_FATAL(odp_packet_free_ctrl(pkt) == ODP_PACKET_FREE_CTRL_DONT_FREE);
+
+ while (transmits--) {
+ /* Retransmit the same packet after it has been received from the RX interface */
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue, &pkt, num_pkt) == num_pkt);
+
+ num_rx = wait_for_packets(&pktio_rx_info, &rx_pkt, &pkt_seq, num_pkt,
+ TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == num_pkt);
+
+ if (num_rx != num_pkt)
+ break;
+
+ CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(rx_pkt));
+ odp_packet_free(rx_pkt);
+ }
+
+ odp_packet_free(pkt);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_pktout_dont_free(void)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret == 0 && capa.free_ctrl.dont_free == 1)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void pktio_test_chksum(void (*config_fn)(odp_pktio_t, odp_pktio_t),
+ void (*prep_fn)(odp_packet_t pkt),
+ void (*test_fn)(odp_packet_t pkt))
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ pktio_info_t pktio_rx_info;
+ odp_pktout_queue_t pktout_queue;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ int ret;
+ int i, num_rx;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ config_fn(pktio_tx, pktio_rx);
+
+ for (i = 0; i < num_ifaces; ++i) {
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ _pktio_wait_linkup(pktio[i]);
+ }
+
+ ret = create_packets_udp(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx, false, ETH_UNICAST);
+ CU_ASSERT(ret == TX_BATCH_LEN);
+ if (ret != TX_BATCH_LEN) {
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+ return;
+ }
+
+ /* Provide L3 and L4 proto for pktout HW checksum generation */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ odp_packet_has_ipv4_set(pkt_tbl[i], true);
+ odp_packet_has_udp_set(pkt_tbl[i], true);
+ }
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ for (i = 0; i < TX_BATCH_LEN; i++)
+ if (prep_fn)
+ prep_fn(pkt_tbl[i]);
+
+ send_packets(pktout_queue, pkt_tbl, TX_BATCH_LEN);
+ num_rx = wait_for_packets(&pktio_rx_info, pkt_tbl, pkt_seq,
+ TX_BATCH_LEN, TXRX_MODE_MULTI,
+ ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++) {
+ test_fn(pkt_tbl[i]);
+ odp_packet_free(pkt_tbl[i]);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_chksum_sctp(void (*config_fn)(odp_pktio_t, odp_pktio_t),
+ void (*prep_fn)(odp_packet_t pkt),
+ void (*test_fn)(odp_packet_t pkt))
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ pktio_info_t pktio_rx_info;
+ odp_pktout_queue_t pktout_queue;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ int ret;
+ int i, num_rx;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+ }
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ config_fn(pktio_tx, pktio_rx);
+
+ for (i = 0; i < num_ifaces; ++i) {
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ _pktio_wait_linkup(pktio[i]);
+ }
+
+ ret = create_packets_sctp(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT(ret == TX_BATCH_LEN);
+ if (ret != TX_BATCH_LEN) {
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+ return;
+ }
+
+ /* Provide L3 and L4 proto for pktout HW checksum generation */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ odp_packet_has_ipv4_set(pkt_tbl[i], true);
+ odp_packet_has_sctp_set(pkt_tbl[i], true);
+ }
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ for (i = 0; i < TX_BATCH_LEN; i++)
+ if (prep_fn)
+ prep_fn(pkt_tbl[i]);
+
+ send_packets(pktout_queue, pkt_tbl, TX_BATCH_LEN);
+ num_rx = wait_for_packets_hdr(&pktio_rx_info, pkt_tbl, pkt_seq,
+ TX_BATCH_LEN, TXRX_MODE_MULTI,
+ ODP_TIME_SEC_IN_NS, ODPH_SCTPHDR_LEN, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++) {
+ test_fn(pkt_tbl[i]);
+ odp_packet_free(pkt_tbl[i]);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static int pktio_check_chksum_in_ipv4(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int idx = (num_ifaces == 1) ? 0 : 1;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[idx], pool[idx], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktin.bit.ipv4_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_in_ipv4_config(odp_pktio_t pktio_tx ODP_UNUSED,
+ odp_pktio_t pktio_rx)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.ipv4_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktin.bit.ipv4_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_rx, &config) == 0);
+}
+
+static void pktio_test_chksum_in_ipv4_prep(odp_packet_t pkt)
+{
+ odph_ipv4_csum_update(pkt);
+}
+
+static void pktio_test_chksum_in_ipv4_test(odp_packet_t pkt)
+{
+ CU_ASSERT(odp_packet_l3_chksum_status(pkt) == ODP_PACKET_CHKSUM_OK);
+}
+
+static void pktio_test_chksum_in_ipv4(void)
+{
+ pktio_test_chksum(pktio_test_chksum_in_ipv4_config,
+ pktio_test_chksum_in_ipv4_prep,
+ pktio_test_chksum_in_ipv4_test);
+}
+
+static int pktio_check_chksum_in_udp(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int idx = (num_ifaces == 1) ? 0 : 1;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[idx], pool[idx], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktin.bit.udp_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_in_udp_config(odp_pktio_t pktio_tx ODP_UNUSED,
+ odp_pktio_t pktio_rx)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.udp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktin.bit.udp_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_rx, &config) == 0);
+}
+
+static void pktio_test_chksum_in_udp_prep(odp_packet_t pkt)
+{
+ odp_packet_has_ipv4_set(pkt, 1);
+ odp_packet_has_udp_set(pkt, 1);
+ odph_ipv4_csum_update(pkt);
+ odph_udp_chksum_set(pkt);
+}
+
+static void pktio_test_chksum_in_udp_test(odp_packet_t pkt)
+{
+ CU_ASSERT(odp_packet_l4_chksum_status(pkt) == ODP_PACKET_CHKSUM_OK);
+}
+
+static void pktio_test_chksum_in_udp(void)
+{
+ pktio_test_chksum(pktio_test_chksum_in_udp_config,
+ pktio_test_chksum_in_udp_prep,
+ pktio_test_chksum_in_udp_test);
+}
+
+static int pktio_check_chksum_in_sctp(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int idx = (num_ifaces == 1) ? 0 : 1;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[idx], pool[idx], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktin.bit.sctp_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_in_sctp_config(odp_pktio_t pktio_tx ODP_UNUSED,
+ odp_pktio_t pktio_rx)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_rx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktin.bit.sctp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktin.bit.sctp_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_rx, &config) == 0);
+}
+
+static void pktio_test_chksum_in_sctp_prep(odp_packet_t pkt)
+{
+ odp_packet_has_ipv4_set(pkt, 1);
+ odp_packet_has_sctp_set(pkt, 1);
+ odph_ipv4_csum_update(pkt);
+ odph_sctp_chksum_set(pkt);
+}
+
+static void pktio_test_chksum_in_sctp_test(odp_packet_t pkt)
+{
+ CU_ASSERT(odp_packet_l4_chksum_status(pkt) == ODP_PACKET_CHKSUM_OK);
+}
+
+static void pktio_test_chksum_in_sctp(void)
+{
+ pktio_test_chksum_sctp(pktio_test_chksum_in_sctp_config,
+ pktio_test_chksum_in_sctp_prep,
+ pktio_test_chksum_in_sctp_test);
+}
+
+static int pktio_check_chksum_out_ipv4(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktout.bit.ipv4_chksum_ena ||
+ !capa.config.pktout.bit.ipv4_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_out_ipv4_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.ipv4_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.ipv4_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.ipv4_chksum_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_ipv4_test(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+
+ CU_ASSERT(ip != NULL);
+ if (ip != NULL)
+ CU_ASSERT(ip->chksum != 0);
+}
+
+static void pktio_test_chksum_out_ipv4_no_ovr_prep(odp_packet_t pkt)
+{
+ odp_packet_l3_chksum_insert(pkt, false);
+}
+
+static void pktio_test_chksum_out_ipv4_no_ovr_test(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+
+ CU_ASSERT(ip != NULL);
+ if (ip != NULL)
+ CU_ASSERT(ip->chksum == 0);
+}
+
+static void pktio_test_chksum_out_ipv4_no_ovr(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_ipv4_config,
+ pktio_test_chksum_out_ipv4_no_ovr_prep,
+ pktio_test_chksum_out_ipv4_no_ovr_test);
+}
+
+static void pktio_test_chksum_out_ipv4_ovr_prep(odp_packet_t pkt)
+{
+ odp_packet_l3_chksum_insert(pkt, true);
+}
+
+static void pktio_test_chksum_out_ipv4_ovr_test(odp_packet_t pkt)
+{
+ odph_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+
+ CU_ASSERT(ip != NULL);
+ if (ip != NULL)
+ CU_ASSERT(ip->chksum != 0);
+}
+
+static void pktio_test_chksum_out_ipv4_ovr(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_ipv4_config,
+ pktio_test_chksum_out_ipv4_ovr_prep,
+ pktio_test_chksum_out_ipv4_ovr_test);
+}
+
+static void pktio_test_chksum_out_ipv4_pktio_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx
+ ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.ipv4_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.ipv4_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.ipv4_chksum_ena = 1;
+ config.pktout.bit.ipv4_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_ipv4_pktio(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_ipv4_pktio_config,
+ NULL,
+ pktio_test_chksum_out_ipv4_test);
+}
+
+static int pktio_check_chksum_out_udp(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktout.bit.udp_chksum_ena ||
+ !capa.config.pktout.bit.udp_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_out_udp_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.udp_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.udp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.udp_chksum_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_udp_test(odp_packet_t pkt)
+{
+ odph_udphdr_t *udp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(udp != NULL);
+ if (udp != NULL) {
+ CU_ASSERT(udp->chksum != 0);
+ CU_ASSERT(!odph_udp_chksum_verify(pkt));
+ }
+}
+
+static void pktio_test_chksum_out_udp_no_ovr_prep(odp_packet_t pkt)
+{
+ odph_ipv4_csum_update(pkt);
+ odp_packet_l4_chksum_insert(pkt, false);
+}
+
+static void pktio_test_chksum_out_udp_no_ovr_test(odp_packet_t pkt)
+{
+ odph_udphdr_t *udp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(udp != NULL);
+ if (udp != NULL)
+ CU_ASSERT(udp->chksum == 0);
+}
+
+static void pktio_test_chksum_out_udp_no_ovr(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_udp_config,
+ pktio_test_chksum_out_udp_no_ovr_prep,
+ pktio_test_chksum_out_udp_no_ovr_test);
+}
+
+static void pktio_test_chksum_out_udp_ovr_prep(odp_packet_t pkt)
+{
+ odp_packet_l4_chksum_insert(pkt, true);
+}
+
+static void pktio_test_chksum_out_udp_ovr_test(odp_packet_t pkt)
+{
+ odph_udphdr_t *udp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(udp != NULL);
+ if (udp != NULL) {
+ CU_ASSERT(udp->chksum != 0);
+ CU_ASSERT(!odph_udp_chksum_verify(pkt));
+ }
+}
+
+static void pktio_test_chksum_out_udp_ovr(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_udp_config,
+ pktio_test_chksum_out_udp_ovr_prep,
+ pktio_test_chksum_out_udp_ovr_test);
+}
+
+static void pktio_test_chksum_out_udp_pktio_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx
+ ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.udp_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.udp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.udp_chksum_ena = 1;
+ config.pktout.bit.udp_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_udp_pktio(void)
+{
+ pktio_test_chksum(pktio_test_chksum_out_udp_pktio_config,
+ NULL,
+ pktio_test_chksum_out_udp_test);
+}
+
+static int pktio_check_chksum_out_sctp(void)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 ||
+ !capa.config.pktout.bit.sctp_chksum_ena ||
+ !capa.config.pktout.bit.sctp_chksum)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_chksum_out_sctp_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.sctp_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.sctp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.sctp_chksum_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_sctp_test(odp_packet_t pkt)
+{
+ odph_sctphdr_t *sctp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(sctp != NULL);
+ if (sctp != NULL) {
+ CU_ASSERT(sctp->chksum != 0);
+ CU_ASSERT(!odph_sctp_chksum_verify(pkt));
+ }
+}
+
+static void pktio_test_chksum_out_sctp_no_ovr_prep(odp_packet_t pkt)
+{
+ odph_ipv4_csum_update(pkt);
+ odp_packet_l4_chksum_insert(pkt, false);
+}
+
+static void pktio_test_chksum_out_sctp_no_ovr_test(odp_packet_t pkt)
+{
+ odph_sctphdr_t *sctp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(sctp != NULL);
+ if (sctp != NULL)
+ CU_ASSERT(sctp->chksum == 0);
+}
+
+static void pktio_test_chksum_out_sctp_no_ovr(void)
+{
+ pktio_test_chksum_sctp(pktio_test_chksum_out_sctp_config,
+ pktio_test_chksum_out_sctp_no_ovr_prep,
+ pktio_test_chksum_out_sctp_no_ovr_test);
+}
+
+static void pktio_test_chksum_out_sctp_ovr_prep(odp_packet_t pkt)
+{
+ odp_packet_l4_chksum_insert(pkt, true);
+}
+
+static void pktio_test_chksum_out_sctp_ovr_test(odp_packet_t pkt)
+{
+ odph_sctphdr_t *sctp = odp_packet_l4_ptr(pkt, NULL);
+
+ CU_ASSERT(sctp != NULL);
+ if (sctp != NULL) {
+ CU_ASSERT(sctp->chksum != 0);
+ CU_ASSERT(!odph_sctp_chksum_verify(pkt));
+ }
+}
+
+static void pktio_test_chksum_out_sctp_ovr(void)
+{
+ pktio_test_chksum_sctp(pktio_test_chksum_out_sctp_config,
+ pktio_test_chksum_out_sctp_ovr_prep,
+ pktio_test_chksum_out_sctp_ovr_test);
+}
+
+static void pktio_test_chksum_out_sctp_pktio_config(odp_pktio_t pktio_tx,
+ odp_pktio_t pktio_rx
+ ODP_UNUSED)
+{
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio_tx, &capa) == 0);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.sctp_chksum_ena);
+ CU_ASSERT_FATAL(capa.config.pktout.bit.sctp_chksum);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.sctp_chksum_ena = 1;
+ config.pktout.bit.sctp_chksum = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio_tx, &config) == 0);
+}
+
+static void pktio_test_chksum_out_sctp_pktio(void)
+{
+ pktio_test_chksum_sctp(pktio_test_chksum_out_sctp_pktio_config,
+ NULL,
+ pktio_test_chksum_out_sctp_test);
+}
+
+static int create_pool(const char *iface, int num)
+{
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_param_t params;
+ odp_pool_capability_t pool_capa;
+
+ if (odp_pool_capability(&pool_capa) != 0)
+ return -1;
+
+ odp_pool_param_init(&params);
+ set_pool_len(&params, &pool_capa);
+ /* Allocate enough buffers taking into consideration core starvation
+ * due to caching */
+ params.pkt.num = PKT_BUF_NUM + params.pkt.cache_size;
+ params.type = ODP_POOL_PACKET;
+
+ snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s_%d",
+ iface, pool_segmentation);
+
+ pool[num] = odp_pool_create(pool_name, &params);
+ if (ODP_POOL_INVALID == pool[num]) {
+ ODPH_ERR("failed to create pool: %s\n", pool_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int create_pktv_pool(const char *iface, int num)
+{
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_capability_t pool_capa;
+ odp_pool_param_t params;
+
+ if (odp_pool_capability(&pool_capa) != 0)
+ return -1;
+
+ if (pool_capa.vector.max_num < PKT_BUF_NUM)
+ return -1;
+
+ odp_pool_param_init(&params);
+ set_pool_len(&params, &pool_capa);
+ params.type = ODP_POOL_VECTOR;
+ params.vector.num = PKT_BUF_NUM;
+ params.vector.max_size = pool_capa.vector.max_size;
+
+ snprintf(pool_name, sizeof(pool_name), "pktv_pool_%s_%d",
+ iface, pool_segmentation);
+
+ pktv_pool[num] = odp_pool_create(pool_name, &params);
+ if (ODP_POOL_INVALID == pktv_pool[num]) {
+ ODPH_ERR("failed to create pool: %s\n", pool_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pktio_check_pktv(odp_pktin_mode_t in_mode)
+{
+ odp_pktio_t pktio;
+ odp_pktio_capability_t capa;
+ odp_pktio_param_t pktio_param;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = in_mode;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.vector.supported)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pktio_check_pktv_queue(void)
+{
+ return pktio_check_pktv(ODP_PKTIN_MODE_QUEUE);
+}
+
+static int pktio_check_pktv_sched(void)
+{
+ return pktio_check_pktv(ODP_PKTIN_MODE_SCHED);
+}
+
+static void pktio_test_pktv_recv_plain(void)
+{
+ test_txrx(ODP_PKTIN_MODE_QUEUE, PKTV_TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT, 0, true);
+}
+
+static void pktio_test_pktv_recv_parallel(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, PKTV_TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT,
+ ODP_SCHED_SYNC_PARALLEL, true);
+}
+
+static void pktio_test_pktv_recv_ordered(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, PKTV_TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT,
+ ODP_SCHED_SYNC_ORDERED, true);
+}
+
+static void pktio_test_pktv_recv_atomic(void)
+{
+ test_txrx(ODP_PKTIN_MODE_SCHED, PKTV_TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT,
+ ODP_SCHED_SYNC_ATOMIC, true);
+}
+
+static void pktio_test_pktv_pktin_queue_config(odp_pktin_mode_t in_mode)
+{
+ odp_pktin_queue_param_t queue_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int num_queues;
+ int i;
+
+ pktio = create_pktio(0, in_mode, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 &&
+ capa.max_input_queues > 0);
+ num_queues = capa.max_input_queues;
+
+ odp_pktin_queue_param_init(&queue_param);
+ queue_param.hash_enable = (num_queues > 1) ? 1 : 0;
+ queue_param.hash_proto.proto.ipv4_udp = 1;
+ queue_param.num_queues = num_queues;
+ queue_param.vector.enable = 1;
+ queue_param.vector.pool = default_pktv_pool;
+ queue_param.vector.max_size = capa.vector.min_size;
+ queue_param.vector.max_tmo_ns = capa.vector.min_tmo_ns;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ queue_param.vector.max_size = capa.vector.max_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ if (capa.vector.max_size != capa.vector.min_size) {
+ queue_param.vector.max_size = capa.vector.max_size - capa.vector.min_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+ }
+
+ queue_param.vector.max_size = capa.vector.min_size - 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) != 0);
+
+ queue_param.vector.max_size = capa.vector.max_size + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) != 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ pktio = create_pktio(i, in_mode, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0);
+
+ if (!capa.vector.supported) {
+ printf("Vector mode is not supported. Test Skipped\n");
+ return;
+ }
+
+ queue_param.vector.enable = 1;
+ queue_param.vector.pool = pktv_pool[i];
+ queue_param.vector.max_size = capa.vector.min_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ queue_param.vector.max_size = capa.vector.max_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+
+ if (capa.vector.max_size != capa.vector.min_size) {
+ queue_param.vector.max_size = capa.vector.max_size - capa.vector.min_size;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) == 0);
+ }
+
+ queue_param.vector.max_size = capa.vector.min_size - 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) != 0);
+
+ queue_param.vector.max_size = capa.vector.max_size + 1;
+ CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) != 0);
+
+ CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0);
+ }
+}
+
+static void pktio_test_pktv_pktin_queue_config_queue(void)
+{
+ pktio_test_pktv_pktin_queue_config(ODP_PKTIN_MODE_QUEUE);
+}
+
+static void pktio_test_pktv_pktin_queue_config_sched(void)
+{
+ pktio_test_pktv_pktin_queue_config(ODP_PKTIN_MODE_SCHED);
+}
+
+static void pktio_test_recv_maxlen_set(void)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ pktio_info_t pktio_rx_info;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+ odp_pktout_queue_t pktout_queue;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ uint32_t max_len = PKT_LEN_MAX;
+ int num_rx = 0;
+ int ret;
+ int i;
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; i++) {
+ uint32_t maxlen_tmp;
+
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(!odp_pktio_capability(pktio[i], &capa));
+ CU_ASSERT_FATAL(capa.set_op.op.maxlen);
+
+ odp_pktio_config_init(&config);
+ CU_ASSERT_FATAL(!odp_pktio_config(pktio[i], &config));
+
+ maxlen_tmp = capa.maxlen.max_input;
+ if (maxlen_tmp == 0)
+ maxlen_tmp = odp_pktin_maxlen(pktio[i]);
+ if (maxlen_tmp < max_len)
+ max_len = maxlen_tmp;
+
+ maxlen_tmp = capa.maxlen.max_output;
+ if (maxlen_tmp == 0)
+ maxlen_tmp = odp_pktout_maxlen(pktio[i]);
+ if (maxlen_tmp < max_len)
+ max_len = maxlen_tmp;
+
+ CU_ASSERT_FATAL(!odp_pktio_maxlen_set(pktio[i], capa.maxlen.max_input,
+ capa.maxlen.max_output));
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ packet_len = max_len;
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Send packets one at a time and add delay between the packets */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue,
+ &pkt_tbl[i], 1) == 1);
+ ret = wait_for_packets(&pktio_rx_info, &pkt_tbl[i], &pkt_seq[i],
+ 1, TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS, false);
+ if (ret != 1)
+ break;
+ }
+ num_rx = i;
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+
+ if (num_rx)
+ odp_packet_free_multi(pkt_tbl, num_rx);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(!odp_pktio_stop(pktio[i]));
+ CU_ASSERT_FATAL(!odp_pktio_close(pktio[i]));
+ }
+
+ /* Restore global variable */
+ packet_len = PKT_LEN_NORMAL;
+}
+
+static int pktio_check_pktout_aging_tmo(void)
+{
+ odp_pktio_param_t pktio_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_t pktio;
+ int ret;
+
+ odp_pktio_param_init(&pktio_param);
+ pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT;
+ pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
+
+ pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param);
+ if (pktio == ODP_PKTIO_INVALID)
+ return ODP_TEST_INACTIVE;
+
+ ret = odp_pktio_capability(pktio, &capa);
+ (void)odp_pktio_close(pktio);
+
+ if (ret < 0 || !capa.max_tx_aging_tmo_ns)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void pktio_test_pktout_aging_tmo(void)
+{
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {ODP_PKTIO_INVALID};
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_pktio_capability_t pktio_capa;
+ odp_pktout_queue_t pktout_queue;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ odp_pktio_t pktio_tx, pktio_rx;
+ pktio_info_t pktio_rx_info;
+ odp_pktio_config_t config;
+ int ret, i, num_rx = 0;
+ uint64_t tmo_0, tmo_1;
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT,
+ ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &pktio_capa) == 0);
+
+ /* Configure Tx aging for PKTIO Tx */
+ if (i == 0) {
+ CU_ASSERT_FATAL(pktio_capa.max_tx_aging_tmo_ns > 0);
+
+ odp_pktio_config_init(&config);
+ config.pktout.bit.aging_ena = 1;
+ CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; i++)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx;
+ pktio_rx_info.id = pktio_rx;
+ pktio_rx_info.inq = ODP_QUEUE_INVALID;
+ pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT;
+
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx,
+ pktio_rx);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1);
+ CU_ASSERT_FATAL(ret > 0);
+
+ /* Prepare packets with aging */
+ for (i = 0; i < TX_BATCH_LEN; i++) {
+ /* Aging disabled by default */
+ CU_ASSERT(odp_packet_aging_tmo(pkt_tbl[i]) == 0);
+
+ /* Test tmo set relatively since we don't know about supported resolution */
+ odp_packet_aging_tmo_set(pkt_tbl[i], pktio_capa.max_tx_aging_tmo_ns - 1);
+ tmo_0 = odp_packet_aging_tmo(pkt_tbl[i]);
+
+ odp_packet_aging_tmo_set(pkt_tbl[i], pktio_capa.max_tx_aging_tmo_ns / 2);
+ tmo_1 = odp_packet_aging_tmo(pkt_tbl[i]);
+ CU_ASSERT(tmo_0 > tmo_1);
+
+ /* Set max before transmitting */
+ odp_packet_aging_tmo_set(pkt_tbl[i], pktio_capa.max_tx_aging_tmo_ns);
+ CU_ASSERT(odp_packet_aging_tmo(pkt_tbl[i]) != 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pktout_send(pktout_queue, pkt_tbl, TX_BATCH_LEN) == TX_BATCH_LEN);
+
+ num_rx = wait_for_packets(&pktio_rx_info, pkt_tbl, pkt_seq, TX_BATCH_LEN, TXRX_MODE_SINGLE,
+ ODP_TIME_SEC_IN_NS, false);
+ CU_ASSERT(num_rx == TX_BATCH_LEN);
+ for (i = 0; i < num_rx; i++)
+ odp_packet_free(pkt_tbl[i]);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+}
+
+static void pktio_test_pktin_event_queue(odp_pktin_mode_t pktin_mode)
+{
+ odp_pktio_t pktio_tx, pktio_rx;
+ odp_pktin_queue_param_t in_queue_param;
+ odp_pktout_queue_param_t out_queue_param;
+ odp_pktout_queue_t pktout_queue;
+ odp_queue_t queue, from;
+ odp_pool_t buf_pool;
+ odp_pool_param_t pool_param;
+ odp_packet_t pkt_tbl[TX_BATCH_LEN];
+ odp_packet_t pkt;
+ odp_buffer_t buf;
+ odp_event_t ev;
+ uint32_t pkt_seq[TX_BATCH_LEN];
+ int ret, i;
+ odp_time_t t1, t2;
+ int inactive = 0;
+ int num_pkt = 0;
+ int num_buf = 0;
+ int num_bad = 0;
+ odp_pktio_t pktio[MAX_NUM_IFACES] = {0};
+ uint64_t wait_time = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
+
+ CU_ASSERT_FATAL(num_ifaces >= 1);
+
+ odp_pool_param_init(&pool_param);
+ pool_param.type = ODP_POOL_BUFFER;
+ pool_param.buf.num = 2 * TX_BATCH_LEN;
+ pool_param.buf.size = 100;
+
+ buf_pool = odp_pool_create("buffer pool", &pool_param);
+ CU_ASSERT_FATAL(buf_pool != ODP_POOL_INVALID);
+
+ buf = odp_buffer_alloc(buf_pool);
+ CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID);
+
+ odp_pktin_queue_param_init(&in_queue_param);
+ in_queue_param.num_queues = 1;
+ in_queue_param.hash_enable = 0;
+ in_queue_param.classifier_enable = 0;
+
+ if (pktin_mode == ODP_PKTIN_MODE_SCHED) {
+ in_queue_param.queue_param.type = ODP_QUEUE_TYPE_SCHED;
+ in_queue_param.queue_param.sched.prio = odp_schedule_default_prio();
+ in_queue_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ in_queue_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL;
+ }
+
+ odp_pktout_queue_param_init(&out_queue_param);
+ out_queue_param.num_queues = 1;
+
+ /* Open and configure interfaces */
+ for (i = 0; i < num_ifaces; ++i) {
+ pktio[i] = create_pktio(i, pktin_mode, ODP_PKTOUT_MODE_DIRECT);
+ CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
+
+ ret = odp_pktin_queue_config(pktio[i], &in_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ ret = odp_pktout_queue_config(pktio[i], &out_queue_param);
+ CU_ASSERT_FATAL(ret == 0);
+
+ CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0);
+ }
+
+ for (i = 0; i < num_ifaces; ++i)
+ _pktio_wait_linkup(pktio[i]);
+
+ pktio_tx = pktio[0];
+ if (num_ifaces > 1)
+ pktio_rx = pktio[1];
+ else
+ pktio_rx = pktio_tx;
+
+ CU_ASSERT_FATAL(odp_pktin_event_queue(pktio_rx, &queue, 1) == 1);
+ CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout_queue, 1) == 1);
+
+ /* Allocate and initialize test packets */
+ ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx, pktio_rx);
+ if (ret != TX_BATCH_LEN) {
+ CU_FAIL("Failed to generate test packets");
+ return;
+ }
+
+ /* Send packets */
+ ret = odp_pktout_send(pktout_queue, pkt_tbl, TX_BATCH_LEN);
+ CU_ASSERT_FATAL(ret == TX_BATCH_LEN);
+
+ /* Send buffer event */
+ ret = odp_queue_enq(queue, odp_buffer_to_event(buf));
+ CU_ASSERT_FATAL(ret == 0);
+
+ /* Receive events */
+ while (1) {
+ /* Break after a period of inactivity */
+ if (pktin_mode == ODP_PKTIN_MODE_SCHED) {
+ ev = odp_schedule(&from, wait_time);
+
+ if (ev == ODP_EVENT_INVALID)
+ break;
+
+ CU_ASSERT(from == queue);
+ } else {
+ ev = odp_queue_deq(queue);
+
+ if (ev == ODP_EVENT_INVALID) {
+ if (inactive == 0) {
+ inactive = 1;
+ t1 = odp_time_local();
+ continue;
+ } else {
+ t2 = odp_time_local();
+ if (odp_time_diff_ns(t2, t1) > ODP_TIME_SEC_IN_NS)
+ break;
+
+ continue;
+ }
+ }
+
+ inactive = 0;
+ }
+
+ if (odp_event_type(ev) == ODP_EVENT_PACKET) {
+ pkt = odp_packet_from_event(ev);
+
+ if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
+ num_pkt++;
+
+ } else if (odp_event_type(ev) == ODP_EVENT_BUFFER) {
+ num_buf++;
+ } else {
+ CU_FAIL("Bad event type");
+ num_bad++;
+ }
+
+ odp_event_free(ev);
+ }
+
+ CU_ASSERT(num_pkt == TX_BATCH_LEN);
+ CU_ASSERT(num_buf == 1);
+ CU_ASSERT(num_bad == 0);
+
+ for (i = 0; i < num_ifaces; i++) {
+ CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0);
+ CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0);
+ }
+
+ CU_ASSERT_FATAL(odp_pool_destroy(buf_pool) == 0);
+}
+
+static void pktio_test_pktin_event_sched(void)
+{
+ pktio_test_pktin_event_queue(ODP_PKTIN_MODE_SCHED);
+}
+
+static int pktio_check_pktin_event_sched(void)
+{
+ if (odp_cunit_ci_skip("pktio_test_pktin_event_sched"))
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static int pktio_suite_init(void)
+{
+ int i;
+
+ odp_atomic_init_u32(&ip_seq, 0);
+
+ if (getenv("ODP_WAIT_FOR_NETWORK"))
+ wait_for_network = true;
+
+ iface_name[0] = getenv("ODP_PKTIO_IF0");
+ iface_name[1] = getenv("ODP_PKTIO_IF1");
+ num_ifaces = 1;
+
+ if (!iface_name[0]) {
+ printf("No interfaces specified, using default \"loop\".\n");
+ iface_name[0] = "loop";
+ } else if (!iface_name[1]) {
+ printf("Using loopback interface: %s\n", iface_name[0]);
+ } else {
+ num_ifaces = 2;
+ printf("Using paired interfaces: %s %s\n",
+ iface_name[0], iface_name[1]);
+ }
+
+ for (i = 0; i < num_ifaces; i++) {
+ if (create_pool(iface_name[i], i) != 0)
+ return -1;
+
+ if (create_pktv_pool(iface_name[i], i) != 0)
+ return -1;
+ }
+
+ if (default_pool_create() != 0) {
+ ODPH_ERR("failed to create default pool\n");
+ return -1;
+ }
+
+ if (default_pktv_pool_create() != 0) {
+ ODPH_ERR("failed to create default pktv pool\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pktio_suite_init_unsegmented(void)
+{
+ pool_segmentation = PKT_POOL_UNSEGMENTED;
+ return pktio_suite_init();
+}
+
+static int pktio_suite_init_segmented(void)
+{
+ pool_segmentation = PKT_POOL_SEGMENTED;
+ return pktio_suite_init();
+}
+
+static int pktv_suite_init(void)
+{
+ pool_segmentation = PKT_POOL_UNSEGMENTED;
+ return pktio_suite_init();
+}
+
+static int pktio_suite_term(void)
+{
+ char pool_name[ODP_POOL_NAME_LEN];
+ odp_pool_t pool;
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < num_ifaces; ++i) {
+ snprintf(pool_name, sizeof(pool_name),
+ "pkt_pool_%s_%d", iface_name[i], pool_segmentation);
+ pool = odp_pool_lookup(pool_name);
+ if (pool == ODP_POOL_INVALID)
+ continue;
+
+ if (odp_pool_destroy(pool) != 0) {
+ ODPH_ERR("failed to destroy pool %s\n", pool_name);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < num_ifaces; ++i) {
+ snprintf(pool_name, sizeof(pool_name),
+ "pktv_pool_%s_%d", iface_name[i], pool_segmentation);
+ pool = odp_pool_lookup(pool_name);
+ if (pool == ODP_POOL_INVALID)
+ continue;
+
+ if (odp_pool_destroy(pool) != 0) {
+ ODPH_ERR("failed to destroy pool %s\n", pool_name);
+ ret = -1;
+ }
+ }
+
+ if (odp_pool_destroy(default_pkt_pool) != 0) {
+ ODPH_ERR("failed to destroy default pool\n");
+ ret = -1;
+ }
+ default_pkt_pool = ODP_POOL_INVALID;
+
+ if (odp_pool_destroy(default_pktv_pool) != 0) {
+ ODPH_ERR("failed to destroy default pktv pool\n");
+ ret = -1;
+ }
+ default_pktv_pool = ODP_POOL_INVALID;
+
+ if (odp_cunit_print_inactive())
+ ret = -1;
+
+ return ret;
+}
+
+static int pktv_suite_term(void)
+{
+ pool_segmentation = PKT_POOL_UNSEGMENTED;
+ return pktio_suite_term();
+}
+
+odp_testinfo_t pktio_suite_unsegmented[] = {
+ ODP_TEST_INFO(pktio_test_default_values),
+ ODP_TEST_INFO(pktio_test_open),
+ ODP_TEST_INFO(pktio_test_lookup),
+ ODP_TEST_INFO(pktio_test_index),
+ ODP_TEST_INFO(pktio_test_print),
+ ODP_TEST_INFO(pktio_test_pktio_config),
+ ODP_TEST_INFO(pktio_test_info),
+ ODP_TEST_INFO(pktio_test_link_info),
+ ODP_TEST_INFO(pktio_test_pktin_queue_config_direct),
+ ODP_TEST_INFO(pktio_test_pktin_queue_config_sched),
+ ODP_TEST_INFO(pktio_test_pktin_queue_config_multi_sched),
+ ODP_TEST_INFO(pktio_test_pktin_queue_config_queue),
+ ODP_TEST_INFO(pktio_test_pktout_queue_config),
+ ODP_TEST_INFO(pktio_test_plain_queue),
+ ODP_TEST_INFO(pktio_test_plain_multi),
+ ODP_TEST_INFO(pktio_test_sched_queue),
+ ODP_TEST_INFO(pktio_test_sched_multi),
+ ODP_TEST_INFO(pktio_test_recv),
+ ODP_TEST_INFO(pktio_test_recv_multi),
+ ODP_TEST_INFO(pktio_test_recv_queue),
+ ODP_TEST_INFO(pktio_test_recv_tmo),
+ ODP_TEST_INFO(pktio_test_recv_mq_tmo),
+ ODP_TEST_INFO(pktio_test_recv_mtu),
+ ODP_TEST_INFO(pktio_test_maxlen),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_maxlen_set,
+ pktio_check_maxlen_set),
+ ODP_TEST_INFO(pktio_test_promisc),
+ ODP_TEST_INFO(pktio_test_mac),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_start_stop,
+ pktio_check_start_stop),
+ ODP_TEST_INFO(pktio_test_recv_on_wonly),
+ ODP_TEST_INFO(pktio_test_send_on_ronly),
+ ODP_TEST_INFO(pktio_test_plain_multi_event),
+ ODP_TEST_INFO(pktio_test_sched_multi_event),
+ ODP_TEST_INFO(pktio_test_recv_multi_event),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktin_event_sched,
+ pktio_check_pktin_event_sched),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters,
+ pktio_check_statistics_counters),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters_bcast,
+ pktio_check_statistics_counters_bcast),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_queue_statistics_counters,
+ pktio_check_queue_statistics_counters),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_event_queue_statistics_counters,
+ pktio_check_event_queue_statistics_counters),
+ ODP_TEST_INFO(pktio_test_extra_stats),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_proto_statistics_counters,
+ pktio_check_proto_statistics_counters),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktin_ts,
+ pktio_check_pktin_ts),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_ts,
+ pktio_check_pktout_ts),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_in_ipv4,
+ pktio_check_chksum_in_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_in_udp,
+ pktio_check_chksum_in_udp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_in_sctp,
+ pktio_check_chksum_in_sctp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_ipv4_no_ovr,
+ pktio_check_chksum_out_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_ipv4_pktio,
+ pktio_check_chksum_out_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_ipv4_ovr,
+ pktio_check_chksum_out_ipv4),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_udp_no_ovr,
+ pktio_check_chksum_out_udp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_udp_pktio,
+ pktio_check_chksum_out_udp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_udp_ovr,
+ pktio_check_chksum_out_udp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_sctp_no_ovr,
+ pktio_check_chksum_out_sctp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_sctp_pktio,
+ pktio_check_chksum_out_sctp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_chksum_out_sctp_ovr,
+ pktio_check_chksum_out_sctp),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_recv_maxlen_set,
+ pktio_check_maxlen_set),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_aging_tmo,
+ pktio_check_pktout_aging_tmo),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_compl_event_plain_queue,
+ pktio_check_pktout_compl_event_plain_queue),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_compl_event_sched_queue,
+ pktio_check_pktout_compl_event_sched_queue),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_compl_poll, pktio_check_pktout_compl_poll),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktout_dont_free, pktio_check_pktout_dont_free),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pause_rx, pktio_check_pause_rx),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pause_tx, pktio_check_pause_tx),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pause_both, pktio_check_pause_both),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pfc_rx, pktio_check_pfc_rx),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pfc_tx, pktio_check_pfc_tx),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_enable_pfc_both, pktio_check_pfc_both),
+ ODP_TEST_INFO_NULL
+};
+
+odp_testinfo_t pktio_suite_segmented[] = {
+ ODP_TEST_INFO(pktio_test_plain_queue),
+ ODP_TEST_INFO(pktio_test_plain_multi),
+ ODP_TEST_INFO(pktio_test_sched_queue),
+ ODP_TEST_INFO(pktio_test_sched_multi),
+ ODP_TEST_INFO(pktio_test_recv),
+ ODP_TEST_INFO(pktio_test_recv_multi),
+ ODP_TEST_INFO(pktio_test_recv_mtu),
+ ODP_TEST_INFO_NULL
+};
+
+odp_testinfo_t pktv_suite[] = {
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_pktin_queue_config_queue, pktio_check_pktv_queue),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_pktin_queue_config_sched, pktio_check_pktv_sched),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_recv_plain, pktio_check_pktv_queue),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_recv_parallel, pktio_check_pktv_sched),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_recv_ordered, pktio_check_pktv_sched),
+ ODP_TEST_INFO_CONDITIONAL(pktio_test_pktv_recv_atomic, pktio_check_pktv_sched),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t pktio_suites[] = {
+ {"Packet I/O Unsegmented", pktio_suite_init_unsegmented,
+ pktio_suite_term, pktio_suite_unsegmented},
+ {"Packet I/O Segmented", pktio_suite_init_segmented,
+ pktio_suite_term, pktio_suite_segmented},
+ {"Packet parser", parser_suite_init, parser_suite_term, parser_suite},
+ {"Packet vector", pktv_suite_init, pktv_suite_term, pktv_suite},
+ {"Large Segment Offload", lso_suite_init, lso_suite_term, lso_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(pktio_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}