X-Git-Url: http://git.droids-corp.org/?p=dpdk.git;a=blobdiff_plain;f=app%2Ftest%2Ftest_mbuf.c;h=a96ba3fa11ef85d03062bb4d4c955d5566cb6165;hp=2a97afe2044a3e0ff490c3e6540593d7fb234e37;hb=a617494eeb01ff;hpb=e0f4a0ed4237e273b78728b7c7a3bf71d2d5218f diff --git a/app/test/test_mbuf.c b/app/test/test_mbuf.c index 2a97afe204..a96ba3fa11 100644 --- a/app/test/test_mbuf.c +++ b/app/test/test_mbuf.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -28,16 +29,33 @@ #include #include #include +#include +#include +#include +#include #include "test.h" +#define MEMPOOL_CACHE_SIZE 32 #define MBUF_DATA_SIZE 2048 #define NB_MBUF 128 #define MBUF_TEST_DATA_LEN 1464 #define MBUF_TEST_DATA_LEN2 50 +#define MBUF_TEST_DATA_LEN3 256 #define MBUF_TEST_HDR1_LEN 20 #define MBUF_TEST_HDR2_LEN 30 #define MBUF_TEST_ALL_HDRS_LEN (MBUF_TEST_HDR1_LEN+MBUF_TEST_HDR2_LEN) +#define MBUF_TEST_SEG_SIZE 64 +#define MBUF_TEST_BURST 8 +#define EXT_BUF_TEST_DATA_LEN 1024 +#define MBUF_MAX_SEG 16 +#define MBUF_NO_HEADER 0 +#define MBUF_HEADER 1 +#define MBUF_NEG_TEST_READ 2 +#define VAL_NAME(flag) { flag, #flag } + +/* chain length in bulk test */ +#define CHAIN_LEN 16 /* size of private data for mbuf in pktmbuf_pool2 */ #define MBUF2_PRIV_SIZE 128 @@ -292,8 +310,17 @@ fail: return -1; } +static uint16_t +testclone_refcnt_read(struct rte_mbuf *m) +{ + return RTE_MBUF_HAS_PINNED_EXTBUF(m) ? + rte_mbuf_ext_refcnt_read(m->shinfo) : + rte_mbuf_refcnt_read(m); +} + static int -testclone_testupdate_testdetach(struct rte_mempool *pktmbuf_pool) +testclone_testupdate_testdetach(struct rte_mempool *pktmbuf_pool, + struct rte_mempool *clone_pool) { struct rte_mbuf *m = NULL; struct rte_mbuf *clone = NULL; @@ -313,7 +340,7 @@ testclone_testupdate_testdetach(struct rte_mempool *pktmbuf_pool) *data = MAGIC_DATA; /* clone the allocated mbuf */ - clone = rte_pktmbuf_clone(m, pktmbuf_pool); + clone = rte_pktmbuf_clone(m, clone_pool); if (clone == NULL) GOTO_FAIL("cannot clone data\n"); @@ -321,7 +348,7 @@ testclone_testupdate_testdetach(struct rte_mempool *pktmbuf_pool) if (*data != MAGIC_DATA) GOTO_FAIL("invalid data in clone\n"); - if (rte_mbuf_refcnt_read(m) != 2) + if (testclone_refcnt_read(m) != 2) GOTO_FAIL("invalid refcnt in m\n"); /* free the clone */ @@ -332,12 +359,15 @@ testclone_testupdate_testdetach(struct rte_mempool *pktmbuf_pool) m->next = rte_pktmbuf_alloc(pktmbuf_pool); if (m->next == NULL) GOTO_FAIL("Next Pkt Null\n"); + m->nb_segs = 2; rte_pktmbuf_append(m->next, sizeof(uint32_t)); + m->pkt_len = 2 * sizeof(uint32_t); + data = rte_pktmbuf_mtod(m->next, unaligned_uint32_t *); *data = MAGIC_DATA; - clone = rte_pktmbuf_clone(m, pktmbuf_pool); + clone = rte_pktmbuf_clone(m, clone_pool); if (clone == NULL) GOTO_FAIL("cannot clone data\n"); @@ -349,15 +379,15 @@ testclone_testupdate_testdetach(struct rte_mempool *pktmbuf_pool) if (*data != MAGIC_DATA) GOTO_FAIL("invalid data in clone->next\n"); - if (rte_mbuf_refcnt_read(m) != 2) + if (testclone_refcnt_read(m) != 2) GOTO_FAIL("invalid refcnt in m\n"); - if (rte_mbuf_refcnt_read(m->next) != 2) + if (testclone_refcnt_read(m->next) != 2) GOTO_FAIL("invalid refcnt in m->next\n"); /* try to clone the clone */ - clone2 = rte_pktmbuf_clone(clone, pktmbuf_pool); + clone2 = rte_pktmbuf_clone(clone, clone_pool); if (clone2 == NULL) GOTO_FAIL("cannot clone the clone\n"); @@ -369,10 +399,10 @@ testclone_testupdate_testdetach(struct rte_mempool *pktmbuf_pool) if (*data != MAGIC_DATA) GOTO_FAIL("invalid data in clone2->next\n"); - if (rte_mbuf_refcnt_read(m) != 3) + if (testclone_refcnt_read(m) != 3) GOTO_FAIL("invalid refcnt in m\n"); - if (rte_mbuf_refcnt_read(m->next) != 3) + if (testclone_refcnt_read(m->next) != 3) GOTO_FAIL("invalid refcnt in m->next\n"); /* free mbuf */ @@ -396,6 +426,162 @@ fail: return -1; } +static int +test_pktmbuf_copy(struct rte_mempool *pktmbuf_pool, + struct rte_mempool *clone_pool) +{ + struct rte_mbuf *m = NULL; + struct rte_mbuf *copy = NULL; + struct rte_mbuf *copy2 = NULL; + struct rte_mbuf *clone = NULL; + unaligned_uint32_t *data; + + /* alloc a mbuf */ + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("ooops not allocating mbuf"); + + if (rte_pktmbuf_pkt_len(m) != 0) + GOTO_FAIL("Bad length"); + + rte_pktmbuf_append(m, sizeof(uint32_t)); + data = rte_pktmbuf_mtod(m, unaligned_uint32_t *); + *data = MAGIC_DATA; + + /* copy the allocated mbuf */ + copy = rte_pktmbuf_copy(m, pktmbuf_pool, 0, UINT32_MAX); + if (copy == NULL) + GOTO_FAIL("cannot copy data\n"); + + if (rte_pktmbuf_pkt_len(copy) != sizeof(uint32_t)) + GOTO_FAIL("copy length incorrect\n"); + + if (rte_pktmbuf_data_len(copy) != sizeof(uint32_t)) + GOTO_FAIL("copy data length incorrect\n"); + + data = rte_pktmbuf_mtod(copy, unaligned_uint32_t *); + if (*data != MAGIC_DATA) + GOTO_FAIL("invalid data in copy\n"); + + /* free the copy */ + rte_pktmbuf_free(copy); + copy = NULL; + + /* same test with a cloned mbuf */ + clone = rte_pktmbuf_clone(m, clone_pool); + if (clone == NULL) + GOTO_FAIL("cannot clone data\n"); + + if ((!RTE_MBUF_HAS_PINNED_EXTBUF(m) && + !RTE_MBUF_CLONED(clone)) || + (RTE_MBUF_HAS_PINNED_EXTBUF(m) && + !RTE_MBUF_HAS_EXTBUF(clone))) + GOTO_FAIL("clone did not give a cloned mbuf\n"); + + copy = rte_pktmbuf_copy(clone, pktmbuf_pool, 0, UINT32_MAX); + if (copy == NULL) + GOTO_FAIL("cannot copy cloned mbuf\n"); + + if (RTE_MBUF_CLONED(copy)) + GOTO_FAIL("copy of clone is cloned?\n"); + + if (rte_pktmbuf_pkt_len(copy) != sizeof(uint32_t)) + GOTO_FAIL("copy clone length incorrect\n"); + + if (rte_pktmbuf_data_len(copy) != sizeof(uint32_t)) + GOTO_FAIL("copy clone data length incorrect\n"); + + data = rte_pktmbuf_mtod(copy, unaligned_uint32_t *); + if (*data != MAGIC_DATA) + GOTO_FAIL("invalid data in clone copy\n"); + rte_pktmbuf_free(clone); + rte_pktmbuf_free(copy); + copy = NULL; + clone = NULL; + + + /* same test with a chained mbuf */ + m->next = rte_pktmbuf_alloc(pktmbuf_pool); + if (m->next == NULL) + GOTO_FAIL("Next Pkt Null\n"); + m->nb_segs = 2; + + rte_pktmbuf_append(m->next, sizeof(uint32_t)); + m->pkt_len = 2 * sizeof(uint32_t); + data = rte_pktmbuf_mtod(m->next, unaligned_uint32_t *); + *data = MAGIC_DATA + 1; + + copy = rte_pktmbuf_copy(m, pktmbuf_pool, 0, UINT32_MAX); + if (copy == NULL) + GOTO_FAIL("cannot copy data\n"); + + if (rte_pktmbuf_pkt_len(copy) != 2 * sizeof(uint32_t)) + GOTO_FAIL("chain copy length incorrect\n"); + + if (rte_pktmbuf_data_len(copy) != 2 * sizeof(uint32_t)) + GOTO_FAIL("chain copy data length incorrect\n"); + + data = rte_pktmbuf_mtod(copy, unaligned_uint32_t *); + if (data[0] != MAGIC_DATA || data[1] != MAGIC_DATA + 1) + GOTO_FAIL("invalid data in copy\n"); + + rte_pktmbuf_free(copy2); + + /* test offset copy */ + copy2 = rte_pktmbuf_copy(copy, pktmbuf_pool, + sizeof(uint32_t), UINT32_MAX); + if (copy2 == NULL) + GOTO_FAIL("cannot copy the copy\n"); + + if (rte_pktmbuf_pkt_len(copy2) != sizeof(uint32_t)) + GOTO_FAIL("copy with offset, length incorrect\n"); + + if (rte_pktmbuf_data_len(copy2) != sizeof(uint32_t)) + GOTO_FAIL("copy with offset, data length incorrect\n"); + + data = rte_pktmbuf_mtod(copy2, unaligned_uint32_t *); + if (data[0] != MAGIC_DATA + 1) + GOTO_FAIL("copy with offset, invalid data\n"); + + rte_pktmbuf_free(copy2); + + /* test truncation copy */ + copy2 = rte_pktmbuf_copy(copy, pktmbuf_pool, + 0, sizeof(uint32_t)); + if (copy2 == NULL) + GOTO_FAIL("cannot copy the copy\n"); + + if (rte_pktmbuf_pkt_len(copy2) != sizeof(uint32_t)) + GOTO_FAIL("copy with truncate, length incorrect\n"); + + if (rte_pktmbuf_data_len(copy2) != sizeof(uint32_t)) + GOTO_FAIL("copy with truncate, data length incorrect\n"); + + data = rte_pktmbuf_mtod(copy2, unaligned_uint32_t *); + if (data[0] != MAGIC_DATA) + GOTO_FAIL("copy with truncate, invalid data\n"); + + /* free mbuf */ + rte_pktmbuf_free(m); + rte_pktmbuf_free(copy); + rte_pktmbuf_free(copy2); + + m = NULL; + copy = NULL; + copy2 = NULL; + printf("%s ok\n", __func__); + return 0; + +fail: + if (m) + rte_pktmbuf_free(m); + if (copy) + rte_pktmbuf_free(copy); + if (copy2) + rte_pktmbuf_free(copy2); + return -1; +} + static int test_attach_from_different_pool(struct rte_mempool *pktmbuf_pool, struct rte_mempool *pktmbuf_pool2) @@ -502,7 +688,6 @@ fail: rte_pktmbuf_free(clone2); return -1; } -#undef GOTO_FAIL /* * test allocation and free of mbufs @@ -545,6 +730,175 @@ test_pktmbuf_pool(struct rte_mempool *pktmbuf_pool) return ret; } +/* + * test bulk allocation and bulk free of mbufs + */ +static int +test_pktmbuf_pool_bulk(void) +{ + struct rte_mempool *pool = NULL; + struct rte_mempool *pool2 = NULL; + unsigned int i; + struct rte_mbuf *m; + struct rte_mbuf *mbufs[NB_MBUF]; + int ret = 0; + + /* We cannot use the preallocated mbuf pools because their caches + * prevent us from bulk allocating all objects in them. + * So we create our own mbuf pools without caches. + */ + printf("Create mbuf pools for bulk allocation.\n"); + pool = rte_pktmbuf_pool_create("test_pktmbuf_bulk", + NB_MBUF, 0, 0, MBUF_DATA_SIZE, SOCKET_ID_ANY); + if (pool == NULL) { + printf("rte_pktmbuf_pool_create() failed. rte_errno %d\n", + rte_errno); + goto err; + } + pool2 = rte_pktmbuf_pool_create("test_pktmbuf_bulk2", + NB_MBUF, 0, 0, MBUF_DATA_SIZE, SOCKET_ID_ANY); + if (pool2 == NULL) { + printf("rte_pktmbuf_pool_create() failed. rte_errno %d\n", + rte_errno); + goto err; + } + + /* Preconditions: Mempools must be full. */ + if (!(rte_mempool_full(pool) && rte_mempool_full(pool2))) { + printf("Test precondition failed: mempools not full\n"); + goto err; + } + if (!(rte_mempool_avail_count(pool) == NB_MBUF && + rte_mempool_avail_count(pool2) == NB_MBUF)) { + printf("Test precondition failed: mempools: %u+%u != %u+%u", + rte_mempool_avail_count(pool), + rte_mempool_avail_count(pool2), + NB_MBUF, NB_MBUF); + goto err; + } + + printf("Test single bulk alloc, followed by multiple bulk free.\n"); + + /* Bulk allocate all mbufs in the pool, in one go. */ + ret = rte_pktmbuf_alloc_bulk(pool, mbufs, NB_MBUF); + if (ret != 0) { + printf("rte_pktmbuf_alloc_bulk() failed: %d\n", ret); + goto err; + } + /* Test that they have been removed from the pool. */ + if (!rte_mempool_empty(pool)) { + printf("mempool not empty\n"); + goto err; + } + /* Bulk free all mbufs, in four steps. */ + RTE_BUILD_BUG_ON(NB_MBUF % 4 != 0); + for (i = 0; i < NB_MBUF; i += NB_MBUF / 4) { + rte_pktmbuf_free_bulk(&mbufs[i], NB_MBUF / 4); + /* Test that they have been returned to the pool. */ + if (rte_mempool_avail_count(pool) != i + NB_MBUF / 4) { + printf("mempool avail count incorrect\n"); + goto err; + } + } + + printf("Test multiple bulk alloc, followed by single bulk free.\n"); + + /* Bulk allocate all mbufs in the pool, in four steps. */ + for (i = 0; i < NB_MBUF; i += NB_MBUF / 4) { + ret = rte_pktmbuf_alloc_bulk(pool, &mbufs[i], NB_MBUF / 4); + if (ret != 0) { + printf("rte_pktmbuf_alloc_bulk() failed: %d\n", ret); + goto err; + } + } + /* Test that they have been removed from the pool. */ + if (!rte_mempool_empty(pool)) { + printf("mempool not empty\n"); + goto err; + } + /* Bulk free all mbufs, in one go. */ + rte_pktmbuf_free_bulk(mbufs, NB_MBUF); + /* Test that they have been returned to the pool. */ + if (!rte_mempool_full(pool)) { + printf("mempool not full\n"); + goto err; + } + + printf("Test bulk free of single long chain.\n"); + + /* Bulk allocate all mbufs in the pool, in one go. */ + ret = rte_pktmbuf_alloc_bulk(pool, mbufs, NB_MBUF); + if (ret != 0) { + printf("rte_pktmbuf_alloc_bulk() failed: %d\n", ret); + goto err; + } + /* Create a long mbuf chain. */ + for (i = 1; i < NB_MBUF; i++) { + ret = rte_pktmbuf_chain(mbufs[0], mbufs[i]); + if (ret != 0) { + printf("rte_pktmbuf_chain() failed: %d\n", ret); + goto err; + } + mbufs[i] = NULL; + } + /* Free the mbuf chain containing all the mbufs. */ + rte_pktmbuf_free_bulk(mbufs, 1); + /* Test that they have been returned to the pool. */ + if (!rte_mempool_full(pool)) { + printf("mempool not full\n"); + goto err; + } + + printf("Test bulk free of multiple chains using multiple pools.\n"); + + /* Create mbuf chains containing mbufs from different pools. */ + RTE_BUILD_BUG_ON(CHAIN_LEN % 2 != 0); + RTE_BUILD_BUG_ON(NB_MBUF % (CHAIN_LEN / 2) != 0); + for (i = 0; i < NB_MBUF * 2; i++) { + m = rte_pktmbuf_alloc((i & 4) ? pool2 : pool); + if (m == NULL) { + printf("rte_pktmbuf_alloc() failed (%u)\n", i); + goto err; + } + if ((i % CHAIN_LEN) == 0) + mbufs[i / CHAIN_LEN] = m; + else + rte_pktmbuf_chain(mbufs[i / CHAIN_LEN], m); + } + /* Test that both pools have been emptied. */ + if (!(rte_mempool_empty(pool) && rte_mempool_empty(pool2))) { + printf("mempools not empty\n"); + goto err; + } + /* Free one mbuf chain. */ + rte_pktmbuf_free_bulk(mbufs, 1); + /* Test that the segments have been returned to the pools. */ + if (!(rte_mempool_avail_count(pool) == CHAIN_LEN / 2 && + rte_mempool_avail_count(pool2) == CHAIN_LEN / 2)) { + printf("all segments of first mbuf have not been returned\n"); + goto err; + } + /* Free the remaining mbuf chains. */ + rte_pktmbuf_free_bulk(&mbufs[1], NB_MBUF * 2 / CHAIN_LEN - 1); + /* Test that they have been returned to the pools. */ + if (!(rte_mempool_full(pool) && rte_mempool_full(pool2))) { + printf("mempools not full\n"); + goto err; + } + + ret = 0; + goto done; + +err: + ret = -1; + +done: + printf("Free mbuf pools for bulk allocation.\n"); + rte_mempool_free(pool); + rte_mempool_free(pool2); + return ret; +} + /* * test that the pointer to the data on a packet mbuf is set properly */ @@ -803,7 +1157,7 @@ test_refcnt_mbuf(void) tref += refcnt_lcore[slave]; if (tref != refcnt_lcore[master]) - rte_panic("refernced mbufs: %u, freed mbufs: %u\n", + rte_panic("referenced mbufs: %u, freed mbufs: %u\n", tref, refcnt_lcore[master]); rte_mempool_dump(stdout, refcnt_pool); @@ -858,6 +1212,7 @@ test_failing_mbuf_sanity_check(struct rte_mempool *pktmbuf_pool) buf = rte_pktmbuf_alloc(pktmbuf_pool); if (buf == NULL) return -1; + printf("Checking good mbuf initially\n"); if (verify_mbuf_check_panics(buf) != -1) return -1; @@ -1121,6 +1476,1281 @@ test_tx_offload(void) return (v1 == v2) ? 0 : -EINVAL; } +static int +test_get_rx_ol_flag_list(void) +{ + int len = 6, ret = 0; + char buf[256] = ""; + int buflen = 0; + + /* Test case to check with null buffer */ + ret = rte_get_rx_ol_flag_list(0, NULL, 0); + if (ret != -1) + GOTO_FAIL("%s expected: -1, received = %d\n", __func__, ret); + + /* Test case to check with zero buffer len */ + ret = rte_get_rx_ol_flag_list(PKT_RX_L4_CKSUM_MASK, buf, 0); + if (ret != -1) + GOTO_FAIL("%s expected: -1, received = %d\n", __func__, ret); + + buflen = strlen(buf); + if (buflen != 0) + GOTO_FAIL("%s buffer should be empty, received = %d\n", + __func__, buflen); + + /* Test case to check with reduced buffer len */ + ret = rte_get_rx_ol_flag_list(0, buf, len); + if (ret != -1) + GOTO_FAIL("%s expected: -1, received = %d\n", __func__, ret); + + buflen = strlen(buf); + if (buflen != (len - 1)) + GOTO_FAIL("%s invalid buffer length retrieved, expected: %d," + "received = %d\n", __func__, + (len - 1), buflen); + + /* Test case to check with zero mask value */ + ret = rte_get_rx_ol_flag_list(0, buf, sizeof(buf)); + if (ret != 0) + GOTO_FAIL("%s expected: 0, received = %d\n", __func__, ret); + + buflen = strlen(buf); + if (buflen == 0) + GOTO_FAIL("%s expected: %s, received length = 0\n", __func__, + "non-zero, buffer should not be empty"); + + /* Test case to check with valid mask value */ + ret = rte_get_rx_ol_flag_list(PKT_RX_SEC_OFFLOAD, buf, sizeof(buf)); + if (ret != 0) + GOTO_FAIL("%s expected: 0, received = %d\n", __func__, ret); + + buflen = strlen(buf); + if (buflen == 0) + GOTO_FAIL("%s expected: %s, received length = 0\n", __func__, + "non-zero, buffer should not be empty"); + + return 0; +fail: + return -1; +} + +static int +test_get_tx_ol_flag_list(void) +{ + int len = 6, ret = 0; + char buf[256] = ""; + int buflen = 0; + + /* Test case to check with null buffer */ + ret = rte_get_tx_ol_flag_list(0, NULL, 0); + if (ret != -1) + GOTO_FAIL("%s expected: -1, received = %d\n", __func__, ret); + + /* Test case to check with zero buffer len */ + ret = rte_get_tx_ol_flag_list(PKT_TX_IP_CKSUM, buf, 0); + if (ret != -1) + GOTO_FAIL("%s expected: -1, received = %d\n", __func__, ret); + + buflen = strlen(buf); + if (buflen != 0) { + GOTO_FAIL("%s buffer should be empty, received = %d\n", + __func__, buflen); + } + + /* Test case to check with reduced buffer len */ + ret = rte_get_tx_ol_flag_list(0, buf, len); + if (ret != -1) + GOTO_FAIL("%s expected: -1, received = %d\n", __func__, ret); + + buflen = strlen(buf); + if (buflen != (len - 1)) + GOTO_FAIL("%s invalid buffer length retrieved, expected: %d," + "received = %d\n", __func__, + (len - 1), buflen); + + /* Test case to check with zero mask value */ + ret = rte_get_tx_ol_flag_list(0, buf, sizeof(buf)); + if (ret != 0) + GOTO_FAIL("%s expected: 0, received = %d\n", __func__, ret); + + buflen = strlen(buf); + if (buflen == 0) + GOTO_FAIL("%s expected: %s, received length = 0\n", __func__, + "non-zero, buffer should not be empty"); + + /* Test case to check with valid mask value */ + ret = rte_get_tx_ol_flag_list(PKT_TX_UDP_CKSUM, buf, sizeof(buf)); + if (ret != 0) + GOTO_FAIL("%s expected: 0, received = %d\n", __func__, ret); + + buflen = strlen(buf); + if (buflen == 0) + GOTO_FAIL("%s expected: %s, received length = 0\n", __func__, + "non-zero, buffer should not be empty"); + + return 0; +fail: + return -1; + +} + +struct flag_name { + uint64_t flag; + const char *name; +}; + +static int +test_get_rx_ol_flag_name(void) +{ + uint16_t i; + const char *flag_str = NULL; + const struct flag_name rx_flags[] = { + VAL_NAME(PKT_RX_VLAN), + VAL_NAME(PKT_RX_RSS_HASH), + VAL_NAME(PKT_RX_FDIR), + VAL_NAME(PKT_RX_L4_CKSUM_BAD), + VAL_NAME(PKT_RX_L4_CKSUM_GOOD), + VAL_NAME(PKT_RX_L4_CKSUM_NONE), + VAL_NAME(PKT_RX_IP_CKSUM_BAD), + VAL_NAME(PKT_RX_IP_CKSUM_GOOD), + VAL_NAME(PKT_RX_IP_CKSUM_NONE), + VAL_NAME(PKT_RX_EIP_CKSUM_BAD), + VAL_NAME(PKT_RX_VLAN_STRIPPED), + VAL_NAME(PKT_RX_IEEE1588_PTP), + VAL_NAME(PKT_RX_IEEE1588_TMST), + VAL_NAME(PKT_RX_FDIR_ID), + VAL_NAME(PKT_RX_FDIR_FLX), + VAL_NAME(PKT_RX_QINQ_STRIPPED), + VAL_NAME(PKT_RX_LRO), + VAL_NAME(PKT_RX_TIMESTAMP), + VAL_NAME(PKT_RX_SEC_OFFLOAD), + VAL_NAME(PKT_RX_SEC_OFFLOAD_FAILED), + VAL_NAME(PKT_RX_OUTER_L4_CKSUM_BAD), + VAL_NAME(PKT_RX_OUTER_L4_CKSUM_GOOD), + VAL_NAME(PKT_RX_OUTER_L4_CKSUM_INVALID), + }; + + /* Test case to check with valid flag */ + for (i = 0; i < RTE_DIM(rx_flags); i++) { + flag_str = rte_get_rx_ol_flag_name(rx_flags[i].flag); + if (flag_str == NULL) + GOTO_FAIL("%s: Expected flagname = %s; received null\n", + __func__, rx_flags[i].name); + if (strcmp(flag_str, rx_flags[i].name) != 0) + GOTO_FAIL("%s: Expected flagname = %s; received = %s\n", + __func__, rx_flags[i].name, flag_str); + } + /* Test case to check with invalid flag */ + flag_str = rte_get_rx_ol_flag_name(0); + if (flag_str != NULL) { + GOTO_FAIL("%s: Expected flag name = null; received = %s\n", + __func__, flag_str); + } + + return 0; +fail: + return -1; +} + +static int +test_get_tx_ol_flag_name(void) +{ + uint16_t i; + const char *flag_str = NULL; + const struct flag_name tx_flags[] = { + VAL_NAME(PKT_TX_VLAN), + VAL_NAME(PKT_TX_IP_CKSUM), + VAL_NAME(PKT_TX_TCP_CKSUM), + VAL_NAME(PKT_TX_SCTP_CKSUM), + VAL_NAME(PKT_TX_UDP_CKSUM), + VAL_NAME(PKT_TX_IEEE1588_TMST), + VAL_NAME(PKT_TX_TCP_SEG), + VAL_NAME(PKT_TX_IPV4), + VAL_NAME(PKT_TX_IPV6), + VAL_NAME(PKT_TX_OUTER_IP_CKSUM), + VAL_NAME(PKT_TX_OUTER_IPV4), + VAL_NAME(PKT_TX_OUTER_IPV6), + VAL_NAME(PKT_TX_TUNNEL_VXLAN), + VAL_NAME(PKT_TX_TUNNEL_GRE), + VAL_NAME(PKT_TX_TUNNEL_IPIP), + VAL_NAME(PKT_TX_TUNNEL_GENEVE), + VAL_NAME(PKT_TX_TUNNEL_MPLSINUDP), + VAL_NAME(PKT_TX_TUNNEL_VXLAN_GPE), + VAL_NAME(PKT_TX_TUNNEL_IP), + VAL_NAME(PKT_TX_TUNNEL_UDP), + VAL_NAME(PKT_TX_QINQ), + VAL_NAME(PKT_TX_MACSEC), + VAL_NAME(PKT_TX_SEC_OFFLOAD), + VAL_NAME(PKT_TX_UDP_SEG), + VAL_NAME(PKT_TX_OUTER_UDP_CKSUM), + }; + + /* Test case to check with valid flag */ + for (i = 0; i < RTE_DIM(tx_flags); i++) { + flag_str = rte_get_tx_ol_flag_name(tx_flags[i].flag); + if (flag_str == NULL) + GOTO_FAIL("%s: Expected flagname = %s; received null\n", + __func__, tx_flags[i].name); + if (strcmp(flag_str, tx_flags[i].name) != 0) + GOTO_FAIL("%s: Expected flagname = %s; received = %s\n", + __func__, tx_flags[i].name, flag_str); + } + /* Test case to check with invalid flag */ + flag_str = rte_get_tx_ol_flag_name(0); + if (flag_str != NULL) { + GOTO_FAIL("%s: Expected flag name = null; received = %s\n", + __func__, flag_str); + } + + return 0; +fail: + return -1; + +} + +static int +test_mbuf_validate_tx_offload(const char *test_name, + struct rte_mempool *pktmbuf_pool, + uint64_t ol_flags, + uint16_t segsize, + int expected_retval) +{ + struct rte_mbuf *m = NULL; + int ret = 0; + + /* alloc a mbuf and do sanity check */ + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("%s: mbuf allocation failed!\n", __func__); + if (rte_pktmbuf_pkt_len(m) != 0) + GOTO_FAIL("%s: Bad packet length\n", __func__); + rte_mbuf_sanity_check(m, 0); + m->ol_flags = ol_flags; + m->tso_segsz = segsize; + ret = rte_validate_tx_offload(m); + if (ret != expected_retval) + GOTO_FAIL("%s(%s): expected ret val: %d; received: %d\n", + __func__, test_name, expected_retval, ret); + rte_pktmbuf_free(m); + m = NULL; + return 0; +fail: + if (m) { + rte_pktmbuf_free(m); + m = NULL; + } + return -1; +} + +static int +test_mbuf_validate_tx_offload_one(struct rte_mempool *pktmbuf_pool) +{ + /* test to validate tx offload flags */ + uint64_t ol_flags = 0; + + /* test to validate if IP checksum is counted only for IPV4 packet */ + /* set both IP checksum and IPV6 flags */ + ol_flags |= PKT_TX_IP_CKSUM; + ol_flags |= PKT_TX_IPV6; + if (test_mbuf_validate_tx_offload("MBUF_TEST_IP_CKSUM_IPV6_SET", + pktmbuf_pool, + ol_flags, 0, -EINVAL) < 0) + GOTO_FAIL("%s failed: IP cksum is set incorrect.\n", __func__); + /* resetting ol_flags for next testcase */ + ol_flags = 0; + + /* test to validate if IP type is set when required */ + ol_flags |= PKT_TX_L4_MASK; + if (test_mbuf_validate_tx_offload("MBUF_TEST_IP_TYPE_NOT_SET", + pktmbuf_pool, + ol_flags, 0, -EINVAL) < 0) + GOTO_FAIL("%s failed: IP type is not set.\n", __func__); + + /* test if IP type is set when TCP SEG is on */ + ol_flags |= PKT_TX_TCP_SEG; + if (test_mbuf_validate_tx_offload("MBUF_TEST_IP_TYPE_NOT_SET", + pktmbuf_pool, + ol_flags, 0, -EINVAL) < 0) + GOTO_FAIL("%s failed: IP type is not set.\n", __func__); + + ol_flags = 0; + /* test to confirm IP type (IPV4/IPV6) is set */ + ol_flags = PKT_TX_L4_MASK; + ol_flags |= PKT_TX_IPV6; + if (test_mbuf_validate_tx_offload("MBUF_TEST_IP_TYPE_SET", + pktmbuf_pool, + ol_flags, 0, 0) < 0) + GOTO_FAIL("%s failed: tx offload flag error.\n", __func__); + + ol_flags = 0; + /* test to check TSO segment size is non-zero */ + ol_flags |= PKT_TX_IPV4; + ol_flags |= PKT_TX_TCP_SEG; + /* set 0 tso segment size */ + if (test_mbuf_validate_tx_offload("MBUF_TEST_NULL_TSO_SEGSZ", + pktmbuf_pool, + ol_flags, 0, -EINVAL) < 0) + GOTO_FAIL("%s failed: tso segment size is null.\n", __func__); + + /* retain IPV4 and PKT_TX_TCP_SEG mask */ + /* set valid tso segment size but IP CKSUM not set */ + if (test_mbuf_validate_tx_offload("MBUF_TEST_TSO_IP_CKSUM_NOT_SET", + pktmbuf_pool, + ol_flags, 512, -EINVAL) < 0) + GOTO_FAIL("%s failed: IP CKSUM is not set.\n", __func__); + + /* test to validate if IP checksum is set for TSO capability */ + /* retain IPV4, TCP_SEG, tso_seg size */ + ol_flags |= PKT_TX_IP_CKSUM; + if (test_mbuf_validate_tx_offload("MBUF_TEST_TSO_IP_CKSUM_SET", + pktmbuf_pool, + ol_flags, 512, 0) < 0) + GOTO_FAIL("%s failed: tx offload flag error.\n", __func__); + + /* test to confirm TSO for IPV6 type */ + ol_flags = 0; + ol_flags |= PKT_TX_IPV6; + ol_flags |= PKT_TX_TCP_SEG; + if (test_mbuf_validate_tx_offload("MBUF_TEST_TSO_IPV6_SET", + pktmbuf_pool, + ol_flags, 512, 0) < 0) + GOTO_FAIL("%s failed: TSO req not met.\n", __func__); + + ol_flags = 0; + /* test if outer IP checksum set for non outer IPv4 packet */ + ol_flags |= PKT_TX_IPV6; + ol_flags |= PKT_TX_OUTER_IP_CKSUM; + if (test_mbuf_validate_tx_offload("MBUF_TEST_OUTER_IPV4_NOT_SET", + pktmbuf_pool, + ol_flags, 512, -EINVAL) < 0) + GOTO_FAIL("%s failed: Outer IP cksum set.\n", __func__); + + ol_flags = 0; + /* test to confirm outer IP checksum is set for outer IPV4 packet */ + ol_flags |= PKT_TX_OUTER_IP_CKSUM; + ol_flags |= PKT_TX_OUTER_IPV4; + if (test_mbuf_validate_tx_offload("MBUF_TEST_OUTER_IPV4_SET", + pktmbuf_pool, + ol_flags, 512, 0) < 0) + GOTO_FAIL("%s failed: tx offload flag error.\n", __func__); + + ol_flags = 0; + /* test to confirm if packets with no TX_OFFLOAD_MASK are skipped */ + if (test_mbuf_validate_tx_offload("MBUF_TEST_OL_MASK_NOT_SET", + pktmbuf_pool, + ol_flags, 512, 0) < 0) + GOTO_FAIL("%s failed: tx offload flag error.\n", __func__); + return 0; +fail: + return -1; +} + +/* + * Test for allocating a bulk of mbufs + * define an array with positive sizes for mbufs allocations. + */ +static int +test_pktmbuf_alloc_bulk(struct rte_mempool *pktmbuf_pool) +{ + int ret = 0; + unsigned int idx, loop; + unsigned int alloc_counts[] = { + 0, + MEMPOOL_CACHE_SIZE - 1, + MEMPOOL_CACHE_SIZE + 1, + MEMPOOL_CACHE_SIZE * 1.5, + MEMPOOL_CACHE_SIZE * 2, + MEMPOOL_CACHE_SIZE * 2 - 1, + MEMPOOL_CACHE_SIZE * 2 + 1, + MEMPOOL_CACHE_SIZE, + }; + + /* allocate a large array of mbuf pointers */ + struct rte_mbuf *mbufs[NB_MBUF] = { 0 }; + for (idx = 0; idx < RTE_DIM(alloc_counts); idx++) { + ret = rte_pktmbuf_alloc_bulk(pktmbuf_pool, mbufs, + alloc_counts[idx]); + if (ret == 0) { + for (loop = 0; loop < alloc_counts[idx] && + mbufs[loop] != NULL; loop++) + rte_pktmbuf_free(mbufs[loop]); + } else if (ret != 0) { + printf("%s: Bulk alloc failed count(%u); ret val(%d)\n", + __func__, alloc_counts[idx], ret); + return -1; + } + } + return 0; +} + +/* + * Negative testing for allocating a bulk of mbufs + */ +static int +test_neg_pktmbuf_alloc_bulk(struct rte_mempool *pktmbuf_pool) +{ + int ret = 0; + unsigned int idx, loop; + unsigned int neg_alloc_counts[] = { + MEMPOOL_CACHE_SIZE - NB_MBUF, + NB_MBUF + 1, + NB_MBUF * 8, + UINT_MAX + }; + struct rte_mbuf *mbufs[NB_MBUF * 8] = { 0 }; + + for (idx = 0; idx < RTE_DIM(neg_alloc_counts); idx++) { + ret = rte_pktmbuf_alloc_bulk(pktmbuf_pool, mbufs, + neg_alloc_counts[idx]); + if (ret == 0) { + printf("%s: Bulk alloc must fail! count(%u); ret(%d)\n", + __func__, neg_alloc_counts[idx], ret); + for (loop = 0; loop < neg_alloc_counts[idx] && + mbufs[loop] != NULL; loop++) + rte_pktmbuf_free(mbufs[loop]); + return -1; + } + } + return 0; +} + +/* + * Test to read mbuf packet using rte_pktmbuf_read + */ +static int +test_pktmbuf_read(struct rte_mempool *pktmbuf_pool) +{ + struct rte_mbuf *m = NULL; + char *data = NULL; + const char *data_copy = NULL; + int off; + + /* alloc a mbuf */ + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("%s: mbuf allocation failed!\n", __func__); + if (rte_pktmbuf_pkt_len(m) != 0) + GOTO_FAIL("%s: Bad packet length\n", __func__); + rte_mbuf_sanity_check(m, 0); + + data = rte_pktmbuf_append(m, MBUF_TEST_DATA_LEN2); + if (data == NULL) + GOTO_FAIL("%s: Cannot append data\n", __func__); + if (rte_pktmbuf_pkt_len(m) != MBUF_TEST_DATA_LEN2) + GOTO_FAIL("%s: Bad packet length\n", __func__); + memset(data, 0xfe, MBUF_TEST_DATA_LEN2); + + /* read the data from mbuf */ + data_copy = rte_pktmbuf_read(m, 0, MBUF_TEST_DATA_LEN2, NULL); + if (data_copy == NULL) + GOTO_FAIL("%s: Error in reading data!\n", __func__); + for (off = 0; off < MBUF_TEST_DATA_LEN2; off++) { + if (data_copy[off] != (char)0xfe) + GOTO_FAIL("Data corrupted at offset %u", off); + } + rte_pktmbuf_free(m); + m = NULL; + + return 0; +fail: + if (m) { + rte_pktmbuf_free(m); + m = NULL; + } + return -1; +} + +/* + * Test to read mbuf packet data from offset + */ +static int +test_pktmbuf_read_from_offset(struct rte_mempool *pktmbuf_pool) +{ + struct rte_mbuf *m = NULL; + struct ether_hdr *hdr = NULL; + char *data = NULL; + const char *data_copy = NULL; + unsigned int off; + unsigned int hdr_len = sizeof(struct rte_ether_hdr); + + /* alloc a mbuf */ + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("%s: mbuf allocation failed!\n", __func__); + + if (rte_pktmbuf_pkt_len(m) != 0) + GOTO_FAIL("%s: Bad packet length\n", __func__); + rte_mbuf_sanity_check(m, 0); + + /* prepend an ethernet header */ + hdr = (struct ether_hdr *)rte_pktmbuf_prepend(m, hdr_len); + if (hdr == NULL) + GOTO_FAIL("%s: Cannot prepend header\n", __func__); + if (rte_pktmbuf_pkt_len(m) != hdr_len) + GOTO_FAIL("%s: Bad pkt length", __func__); + if (rte_pktmbuf_data_len(m) != hdr_len) + GOTO_FAIL("%s: Bad data length", __func__); + memset(hdr, 0xde, hdr_len); + + /* read mbuf header info from 0 offset */ + data_copy = rte_pktmbuf_read(m, 0, hdr_len, NULL); + if (data_copy == NULL) + GOTO_FAIL("%s: Error in reading header!\n", __func__); + for (off = 0; off < hdr_len; off++) { + if (data_copy[off] != (char)0xde) + GOTO_FAIL("Header info corrupted at offset %u", off); + } + + /* append sample data after ethernet header */ + data = rte_pktmbuf_append(m, MBUF_TEST_DATA_LEN2); + if (data == NULL) + GOTO_FAIL("%s: Cannot append data\n", __func__); + if (rte_pktmbuf_pkt_len(m) != hdr_len + MBUF_TEST_DATA_LEN2) + GOTO_FAIL("%s: Bad packet length\n", __func__); + if (rte_pktmbuf_data_len(m) != hdr_len + MBUF_TEST_DATA_LEN2) + GOTO_FAIL("%s: Bad data length\n", __func__); + memset(data, 0xcc, MBUF_TEST_DATA_LEN2); + + /* read mbuf data after header info */ + data_copy = rte_pktmbuf_read(m, hdr_len, MBUF_TEST_DATA_LEN2, NULL); + if (data_copy == NULL) + GOTO_FAIL("%s: Error in reading header data!\n", __func__); + for (off = 0; off < MBUF_TEST_DATA_LEN2; off++) { + if (data_copy[off] != (char)0xcc) + GOTO_FAIL("Data corrupted at offset %u", off); + } + + /* partial reading of mbuf data */ + data_copy = rte_pktmbuf_read(m, hdr_len + 5, MBUF_TEST_DATA_LEN2 - 5, + NULL); + if (data_copy == NULL) + GOTO_FAIL("%s: Error in reading packet data!\n", __func__); + if (strlen(data_copy) != MBUF_TEST_DATA_LEN2 - 5) + GOTO_FAIL("%s: Incorrect data length!\n", __func__); + for (off = 0; off < MBUF_TEST_DATA_LEN2 - 5; off++) { + if (data_copy[off] != (char)0xcc) + GOTO_FAIL("Data corrupted at offset %u", off); + } + + /* read length greater than mbuf data_len */ + if (rte_pktmbuf_read(m, hdr_len, rte_pktmbuf_data_len(m) + 1, + NULL) != NULL) + GOTO_FAIL("%s: Requested len is larger than mbuf data len!\n", + __func__); + + /* read length greater than mbuf pkt_len */ + if (rte_pktmbuf_read(m, hdr_len, rte_pktmbuf_pkt_len(m) + 1, + NULL) != NULL) + GOTO_FAIL("%s: Requested len is larger than mbuf pkt len!\n", + __func__); + + /* read data of zero len from valid offset */ + data_copy = rte_pktmbuf_read(m, hdr_len, 0, NULL); + if (data_copy == NULL) + GOTO_FAIL("%s: Error in reading packet data!\n", __func__); + if (strlen(data_copy) != MBUF_TEST_DATA_LEN2) + GOTO_FAIL("%s: Corrupted data content!\n", __func__); + for (off = 0; off < MBUF_TEST_DATA_LEN2; off++) { + if (data_copy[off] != (char)0xcc) + GOTO_FAIL("Data corrupted at offset %u", off); + } + + /* read data of zero length from zero offset */ + data_copy = rte_pktmbuf_read(m, 0, 0, NULL); + if (data_copy == NULL) + GOTO_FAIL("%s: Error in reading packet data!\n", __func__); + /* check if the received address is the beginning of header info */ + if (hdr != (const struct ether_hdr *)data_copy) + GOTO_FAIL("%s: Corrupted data address!\n", __func__); + + /* read data of max length from valid offset */ + data_copy = rte_pktmbuf_read(m, hdr_len, UINT_MAX, NULL); + if (data_copy == NULL) + GOTO_FAIL("%s: Error in reading packet data!\n", __func__); + /* check if the received address is the beginning of data segment */ + if (data_copy != data) + GOTO_FAIL("%s: Corrupted data address!\n", __func__); + + /* try to read from mbuf with max size offset */ + data_copy = rte_pktmbuf_read(m, UINT_MAX, 0, NULL); + if (data_copy != NULL) + GOTO_FAIL("%s: Error in reading packet data!\n", __func__); + + /* try to read from mbuf with max size offset and len */ + data_copy = rte_pktmbuf_read(m, UINT_MAX, UINT_MAX, NULL); + if (data_copy != NULL) + GOTO_FAIL("%s: Error in reading packet data!\n", __func__); + + rte_pktmbuf_dump(stdout, m, rte_pktmbuf_pkt_len(m)); + + rte_pktmbuf_free(m); + m = NULL; + + return 0; +fail: + if (m) { + rte_pktmbuf_free(m); + m = NULL; + } + return -1; +} + +struct test_case { + unsigned int seg_count; + unsigned int flags; + uint32_t read_off; + uint32_t read_len; + unsigned int seg_lengths[MBUF_MAX_SEG]; +}; + +/* create a mbuf with different sized segments + * and fill with data [0x00 0x01 0x02 ...] + */ +static struct rte_mbuf * +create_packet(struct rte_mempool *pktmbuf_pool, + struct test_case *test_data) +{ + uint16_t i, ret, seg, seg_len = 0; + uint32_t last_index = 0; + unsigned int seg_lengths[MBUF_MAX_SEG]; + unsigned int hdr_len; + struct rte_mbuf *pkt = NULL; + struct rte_mbuf *pkt_seg = NULL; + char *hdr = NULL; + char *data = NULL; + + memcpy(seg_lengths, test_data->seg_lengths, + sizeof(unsigned int)*test_data->seg_count); + for (seg = 0; seg < test_data->seg_count; seg++) { + hdr_len = 0; + seg_len = seg_lengths[seg]; + pkt_seg = rte_pktmbuf_alloc(pktmbuf_pool); + if (pkt_seg == NULL) + GOTO_FAIL("%s: mbuf allocation failed!\n", __func__); + if (rte_pktmbuf_pkt_len(pkt_seg) != 0) + GOTO_FAIL("%s: Bad packet length\n", __func__); + rte_mbuf_sanity_check(pkt_seg, 0); + /* Add header only for the first segment */ + if (test_data->flags == MBUF_HEADER && seg == 0) { + hdr_len = sizeof(struct rte_ether_hdr); + /* prepend a header and fill with dummy data */ + hdr = (char *)rte_pktmbuf_prepend(pkt_seg, hdr_len); + if (hdr == NULL) + GOTO_FAIL("%s: Cannot prepend header\n", + __func__); + if (rte_pktmbuf_pkt_len(pkt_seg) != hdr_len) + GOTO_FAIL("%s: Bad pkt length", __func__); + if (rte_pktmbuf_data_len(pkt_seg) != hdr_len) + GOTO_FAIL("%s: Bad data length", __func__); + for (i = 0; i < hdr_len; i++) + hdr[i] = (last_index + i) % 0xffff; + last_index += hdr_len; + } + /* skip appending segment with 0 length */ + if (seg_len == 0) + continue; + data = rte_pktmbuf_append(pkt_seg, seg_len); + if (data == NULL) + GOTO_FAIL("%s: Cannot append data segment\n", __func__); + if (rte_pktmbuf_pkt_len(pkt_seg) != hdr_len + seg_len) + GOTO_FAIL("%s: Bad packet segment length: %d\n", + __func__, rte_pktmbuf_pkt_len(pkt_seg)); + if (rte_pktmbuf_data_len(pkt_seg) != hdr_len + seg_len) + GOTO_FAIL("%s: Bad data length\n", __func__); + for (i = 0; i < seg_len; i++) + data[i] = (last_index + i) % 0xffff; + /* to fill continuous data from one seg to another */ + last_index += i; + /* create chained mbufs */ + if (seg == 0) + pkt = pkt_seg; + else { + ret = rte_pktmbuf_chain(pkt, pkt_seg); + if (ret != 0) + GOTO_FAIL("%s:FAIL: Chained mbuf creation %d\n", + __func__, ret); + } + + pkt_seg = pkt_seg->next; + } + return pkt; +fail: + if (pkt != NULL) { + rte_pktmbuf_free(pkt); + pkt = NULL; + } + if (pkt_seg != NULL) { + rte_pktmbuf_free(pkt_seg); + pkt_seg = NULL; + } + return NULL; +} + +static int +test_pktmbuf_read_from_chain(struct rte_mempool *pktmbuf_pool) +{ + struct rte_mbuf *m; + struct test_case test_cases[] = { + { + .seg_lengths = { 100, 100, 100 }, + .seg_count = 3, + .flags = MBUF_NO_HEADER, + .read_off = 0, + .read_len = 300 + }, + { + .seg_lengths = { 100, 125, 150 }, + .seg_count = 3, + .flags = MBUF_NO_HEADER, + .read_off = 99, + .read_len = 201 + }, + { + .seg_lengths = { 100, 100 }, + .seg_count = 2, + .flags = MBUF_NO_HEADER, + .read_off = 0, + .read_len = 100 + }, + { + .seg_lengths = { 100, 200 }, + .seg_count = 2, + .flags = MBUF_HEADER, + .read_off = sizeof(struct rte_ether_hdr), + .read_len = 150 + }, + { + .seg_lengths = { 1000, 100 }, + .seg_count = 2, + .flags = MBUF_NO_HEADER, + .read_off = 0, + .read_len = 1000 + }, + { + .seg_lengths = { 1024, 0, 100 }, + .seg_count = 3, + .flags = MBUF_NO_HEADER, + .read_off = 100, + .read_len = 1001 + }, + { + .seg_lengths = { 1000, 1, 1000 }, + .seg_count = 3, + .flags = MBUF_NO_HEADER, + .read_off = 1000, + .read_len = 2 + }, + { + .seg_lengths = { MBUF_TEST_DATA_LEN, + MBUF_TEST_DATA_LEN2, + MBUF_TEST_DATA_LEN3, 800, 10 }, + .seg_count = 5, + .flags = MBUF_NEG_TEST_READ, + .read_off = 1000, + .read_len = MBUF_DATA_SIZE + }, + }; + + uint32_t i, pos; + const char *data_copy = NULL; + char data_buf[MBUF_DATA_SIZE]; + + memset(data_buf, 0, MBUF_DATA_SIZE); + + for (i = 0; i < RTE_DIM(test_cases); i++) { + m = create_packet(pktmbuf_pool, &test_cases[i]); + if (m == NULL) + GOTO_FAIL("%s: mbuf allocation failed!\n", __func__); + + data_copy = rte_pktmbuf_read(m, test_cases[i].read_off, + test_cases[i].read_len, data_buf); + if (test_cases[i].flags == MBUF_NEG_TEST_READ) { + if (data_copy != NULL) + GOTO_FAIL("%s: mbuf data read should fail!\n", + __func__); + else { + rte_pktmbuf_free(m); + m = NULL; + continue; + } + } + if (data_copy == NULL) + GOTO_FAIL("%s: Error in reading packet data!\n", + __func__); + for (pos = 0; pos < test_cases[i].read_len; pos++) { + if (data_copy[pos] != + (char)((test_cases[i].read_off + pos) + % 0xffff)) + GOTO_FAIL("Data corrupted at offset %u is %2X", + pos, data_copy[pos]); + } + rte_pktmbuf_dump(stdout, m, rte_pktmbuf_pkt_len(m)); + rte_pktmbuf_free(m); + m = NULL; + } + return 0; + +fail: + if (m != NULL) { + rte_pktmbuf_free(m); + m = NULL; + } + return -1; +} + +/* Define a free call back function to be used for external buffer */ +static void +ext_buf_free_callback_fn(void *addr __rte_unused, void *opaque) +{ + void *ext_buf_addr = opaque; + + if (ext_buf_addr == NULL) { + printf("External buffer address is invalid\n"); + return; + } + rte_free(ext_buf_addr); + ext_buf_addr = NULL; + printf("External buffer freed via callback\n"); +} + +/* + * Test to initialize shared data in external buffer before attaching to mbuf + * - Allocate mbuf with no data. + * - Allocate external buffer with size should be large enough to accommodate + * rte_mbuf_ext_shared_info. + * - Invoke pktmbuf_ext_shinfo_init_helper to initialize shared data. + * - Invoke rte_pktmbuf_attach_extbuf to attach external buffer to the mbuf. + * - Clone another mbuf and attach the same external buffer to it. + * - Invoke rte_pktmbuf_detach_extbuf to detach the external buffer from mbuf. + */ +static int +test_pktmbuf_ext_shinfo_init_helper(struct rte_mempool *pktmbuf_pool) +{ + struct rte_mbuf *m = NULL; + struct rte_mbuf *clone = NULL; + struct rte_mbuf_ext_shared_info *ret_shinfo = NULL; + rte_iova_t buf_iova; + void *ext_buf_addr = NULL; + uint16_t buf_len = EXT_BUF_TEST_DATA_LEN + + sizeof(struct rte_mbuf_ext_shared_info); + + /* alloc a mbuf */ + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("%s: mbuf allocation failed!\n", __func__); + if (rte_pktmbuf_pkt_len(m) != 0) + GOTO_FAIL("%s: Bad packet length\n", __func__); + rte_mbuf_sanity_check(m, 0); + + ext_buf_addr = rte_malloc("External buffer", buf_len, + RTE_CACHE_LINE_SIZE); + if (ext_buf_addr == NULL) + GOTO_FAIL("%s: External buffer allocation failed\n", __func__); + + ret_shinfo = rte_pktmbuf_ext_shinfo_init_helper(ext_buf_addr, &buf_len, + ext_buf_free_callback_fn, ext_buf_addr); + if (ret_shinfo == NULL) + GOTO_FAIL("%s: Shared info initialization failed!\n", __func__); + + if (rte_mbuf_ext_refcnt_read(ret_shinfo) != 1) + GOTO_FAIL("%s: External refcount is not 1\n", __func__); + + if (rte_mbuf_refcnt_read(m) != 1) + GOTO_FAIL("%s: Invalid refcnt in mbuf\n", __func__); + + buf_iova = rte_mempool_virt2iova(ext_buf_addr); + rte_pktmbuf_attach_extbuf(m, ext_buf_addr, buf_iova, buf_len, + ret_shinfo); + if (m->ol_flags != EXT_ATTACHED_MBUF) + GOTO_FAIL("%s: External buffer is not attached to mbuf\n", + __func__); + + /* allocate one more mbuf */ + clone = rte_pktmbuf_clone(m, pktmbuf_pool); + if (clone == NULL) + GOTO_FAIL("%s: mbuf clone allocation failed!\n", __func__); + if (rte_pktmbuf_pkt_len(clone) != 0) + GOTO_FAIL("%s: Bad packet length\n", __func__); + + /* attach the same external buffer to the cloned mbuf */ + rte_pktmbuf_attach_extbuf(clone, ext_buf_addr, buf_iova, buf_len, + ret_shinfo); + if (clone->ol_flags != EXT_ATTACHED_MBUF) + GOTO_FAIL("%s: External buffer is not attached to mbuf\n", + __func__); + + if (rte_mbuf_ext_refcnt_read(ret_shinfo) != 2) + GOTO_FAIL("%s: Invalid ext_buf ref_cnt\n", __func__); + + /* test to manually update ext_buf_ref_cnt from 2 to 3*/ + rte_mbuf_ext_refcnt_update(ret_shinfo, 1); + if (rte_mbuf_ext_refcnt_read(ret_shinfo) != 3) + GOTO_FAIL("%s: Update ext_buf ref_cnt failed\n", __func__); + + /* reset the ext_refcnt before freeing the external buffer */ + rte_mbuf_ext_refcnt_set(ret_shinfo, 2); + if (rte_mbuf_ext_refcnt_read(ret_shinfo) != 2) + GOTO_FAIL("%s: set ext_buf ref_cnt failed\n", __func__); + + /* detach the external buffer from mbufs */ + rte_pktmbuf_detach_extbuf(m); + /* check if ref cnt is decremented */ + if (rte_mbuf_ext_refcnt_read(ret_shinfo) != 1) + GOTO_FAIL("%s: Invalid ext_buf ref_cnt\n", __func__); + + rte_pktmbuf_detach_extbuf(clone); + if (rte_mbuf_ext_refcnt_read(ret_shinfo) != 0) + GOTO_FAIL("%s: Invalid ext_buf ref_cnt\n", __func__); + + rte_pktmbuf_free(m); + m = NULL; + rte_pktmbuf_free(clone); + clone = NULL; + + return 0; + +fail: + if (m) { + rte_pktmbuf_free(m); + m = NULL; + } + if (clone) { + rte_pktmbuf_free(clone); + clone = NULL; + } + if (ext_buf_addr != NULL) { + rte_free(ext_buf_addr); + ext_buf_addr = NULL; + } + return -1; +} + +/* + * Test the mbuf pool with pinned external data buffers + * - Allocate memory zone for external buffer + * - Create the mbuf pool with pinned external buffer + * - Check the created pool with relevant mbuf pool unit tests + */ +static int +test_pktmbuf_ext_pinned_buffer(struct rte_mempool *std_pool) +{ + + struct rte_pktmbuf_extmem ext_mem; + struct rte_mempool *pinned_pool = NULL; + const struct rte_memzone *mz = NULL; + + printf("Test mbuf pool with external pinned data buffers\n"); + + /* Allocate memzone for the external data buffer */ + mz = rte_memzone_reserve("pinned_pool", + NB_MBUF * MBUF_DATA_SIZE, + SOCKET_ID_ANY, + RTE_MEMZONE_2MB | RTE_MEMZONE_SIZE_HINT_ONLY); + if (mz == NULL) + GOTO_FAIL("%s: Memzone allocation failed\n", __func__); + + /* Create the mbuf pool with pinned external data buffer */ + ext_mem.buf_ptr = mz->addr; + ext_mem.buf_iova = mz->iova; + ext_mem.buf_len = mz->len; + ext_mem.elt_size = MBUF_DATA_SIZE; + + pinned_pool = rte_pktmbuf_pool_create_extbuf("test_pinned_pool", + NB_MBUF, MEMPOOL_CACHE_SIZE, 0, + MBUF_DATA_SIZE, SOCKET_ID_ANY, + &ext_mem, 1); + if (pinned_pool == NULL) + GOTO_FAIL("%s: Mbuf pool with pinned external" + " buffer creation failed\n", __func__); + /* test multiple mbuf alloc */ + if (test_pktmbuf_pool(pinned_pool) < 0) + GOTO_FAIL("%s: test_mbuf_pool(pinned) failed\n", + __func__); + + /* do it another time to check that all mbufs were freed */ + if (test_pktmbuf_pool(pinned_pool) < 0) + GOTO_FAIL("%s: test_mbuf_pool(pinned) failed (2)\n", + __func__); + + /* test that the data pointer on a packet mbuf is set properly */ + if (test_pktmbuf_pool_ptr(pinned_pool) < 0) + GOTO_FAIL("%s: test_pktmbuf_pool_ptr(pinned) failed\n", + __func__); + + /* test data manipulation in mbuf with non-ascii data */ + if (test_pktmbuf_with_non_ascii_data(pinned_pool) < 0) + GOTO_FAIL("%s: test_pktmbuf_with_non_ascii_data(pinned)" + " failed\n", __func__); + + /* test free pktmbuf segment one by one */ + if (test_pktmbuf_free_segment(pinned_pool) < 0) + GOTO_FAIL("%s: test_pktmbuf_free_segment(pinned) failed\n", + __func__); + + if (testclone_testupdate_testdetach(pinned_pool, std_pool) < 0) + GOTO_FAIL("%s: testclone_and_testupdate(pinned) failed\n", + __func__); + + if (test_pktmbuf_copy(pinned_pool, std_pool) < 0) + GOTO_FAIL("%s: test_pktmbuf_copy(pinned) failed\n", + __func__); + + if (test_failing_mbuf_sanity_check(pinned_pool) < 0) + GOTO_FAIL("%s: test_failing_mbuf_sanity_check(pinned)" + " failed\n", __func__); + + if (test_mbuf_linearize_check(pinned_pool) < 0) + GOTO_FAIL("%s: test_mbuf_linearize_check(pinned) failed\n", + __func__); + + /* test for allocating a bulk of mbufs with various sizes */ + if (test_pktmbuf_alloc_bulk(pinned_pool) < 0) + GOTO_FAIL("%s: test_rte_pktmbuf_alloc_bulk(pinned) failed\n", + __func__); + + /* test for allocating a bulk of mbufs with various sizes */ + if (test_neg_pktmbuf_alloc_bulk(pinned_pool) < 0) + GOTO_FAIL("%s: test_neg_rte_pktmbuf_alloc_bulk(pinned)" + " failed\n", __func__); + + /* test to read mbuf packet */ + if (test_pktmbuf_read(pinned_pool) < 0) + GOTO_FAIL("%s: test_rte_pktmbuf_read(pinned) failed\n", + __func__); + + /* test to read mbuf packet from offset */ + if (test_pktmbuf_read_from_offset(pinned_pool) < 0) + GOTO_FAIL("%s: test_rte_pktmbuf_read_from_offset(pinned)" + " failed\n", __func__); + + /* test to read data from chain of mbufs with data segments */ + if (test_pktmbuf_read_from_chain(pinned_pool) < 0) + GOTO_FAIL("%s: test_rte_pktmbuf_read_from_chain(pinned)" + " failed\n", __func__); + + RTE_SET_USED(std_pool); + rte_mempool_free(pinned_pool); + rte_memzone_free(mz); + return 0; + +fail: + rte_mempool_free(pinned_pool); + rte_memzone_free(mz); + return -1; +} + +static int +test_mbuf_dyn(struct rte_mempool *pktmbuf_pool) +{ + const struct rte_mbuf_dynfield dynfield = { + .name = "test-dynfield", + .size = sizeof(uint8_t), + .align = __alignof__(uint8_t), + .flags = 0, + }; + const struct rte_mbuf_dynfield dynfield2 = { + .name = "test-dynfield2", + .size = sizeof(uint16_t), + .align = __alignof__(uint16_t), + .flags = 0, + }; + const struct rte_mbuf_dynfield dynfield3 = { + .name = "test-dynfield3", + .size = sizeof(uint8_t), + .align = __alignof__(uint8_t), + .flags = 0, + }; + const struct rte_mbuf_dynfield dynfield_fail_big = { + .name = "test-dynfield-fail-big", + .size = 256, + .align = 1, + .flags = 0, + }; + const struct rte_mbuf_dynfield dynfield_fail_align = { + .name = "test-dynfield-fail-align", + .size = 1, + .align = 3, + .flags = 0, + }; + const struct rte_mbuf_dynflag dynflag = { + .name = "test-dynflag", + .flags = 0, + }; + const struct rte_mbuf_dynflag dynflag2 = { + .name = "test-dynflag2", + .flags = 0, + }; + const struct rte_mbuf_dynflag dynflag3 = { + .name = "test-dynflag3", + .flags = 0, + }; + struct rte_mbuf *m = NULL; + int offset, offset2, offset3; + int flag, flag2, flag3; + int ret; + + printf("Test mbuf dynamic fields and flags\n"); + rte_mbuf_dyn_dump(stdout); + + offset = rte_mbuf_dynfield_register(&dynfield); + if (offset == -1) + GOTO_FAIL("failed to register dynamic field, offset=%d: %s", + offset, strerror(errno)); + + ret = rte_mbuf_dynfield_register(&dynfield); + if (ret != offset) + GOTO_FAIL("failed to lookup dynamic field, ret=%d: %s", + ret, strerror(errno)); + + offset2 = rte_mbuf_dynfield_register(&dynfield2); + if (offset2 == -1 || offset2 == offset || (offset2 & 1)) + GOTO_FAIL("failed to register dynamic field 2, offset2=%d: %s", + offset2, strerror(errno)); + + offset3 = rte_mbuf_dynfield_register_offset(&dynfield3, + offsetof(struct rte_mbuf, dynfield1[1])); + if (offset3 != offsetof(struct rte_mbuf, dynfield1[1])) + GOTO_FAIL("failed to register dynamic field 3, offset=%d: %s", + offset3, strerror(errno)); + + printf("dynfield: offset=%d, offset2=%d, offset3=%d\n", + offset, offset2, offset3); + + ret = rte_mbuf_dynfield_register(&dynfield_fail_big); + if (ret != -1) + GOTO_FAIL("dynamic field creation should fail (too big)"); + + ret = rte_mbuf_dynfield_register(&dynfield_fail_align); + if (ret != -1) + GOTO_FAIL("dynamic field creation should fail (bad alignment)"); + + ret = rte_mbuf_dynfield_register_offset(&dynfield_fail_align, + offsetof(struct rte_mbuf, ol_flags)); + if (ret != -1) + GOTO_FAIL("dynamic field creation should fail (not avail)"); + + flag = rte_mbuf_dynflag_register(&dynflag); + if (flag == -1) + GOTO_FAIL("failed to register dynamic flag, flag=%d: %s", + flag, strerror(errno)); + + ret = rte_mbuf_dynflag_register(&dynflag); + if (ret != flag) + GOTO_FAIL("failed to lookup dynamic flag, ret=%d: %s", + ret, strerror(errno)); + + flag2 = rte_mbuf_dynflag_register(&dynflag2); + if (flag2 == -1 || flag2 == flag) + GOTO_FAIL("failed to register dynamic flag 2, flag2=%d: %s", + flag2, strerror(errno)); + + flag3 = rte_mbuf_dynflag_register_bitnum(&dynflag3, + rte_bsf64(PKT_LAST_FREE)); + if (flag3 != rte_bsf64(PKT_LAST_FREE)) + GOTO_FAIL("failed to register dynamic flag 3, flag3=%d: %s", + flag3, strerror(errno)); + + printf("dynflag: flag=%d, flag2=%d, flag3=%d\n", flag, flag2, flag3); + + /* set, get dynamic field */ + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("Cannot allocate mbuf"); + + *RTE_MBUF_DYNFIELD(m, offset, uint8_t *) = 1; + if (*RTE_MBUF_DYNFIELD(m, offset, uint8_t *) != 1) + GOTO_FAIL("failed to read dynamic field"); + *RTE_MBUF_DYNFIELD(m, offset2, uint16_t *) = 1000; + if (*RTE_MBUF_DYNFIELD(m, offset2, uint16_t *) != 1000) + GOTO_FAIL("failed to read dynamic field"); + + /* set a dynamic flag */ + m->ol_flags |= (1ULL << flag); + + rte_mbuf_dyn_dump(stdout); + rte_pktmbuf_free(m); + return 0; +fail: + rte_pktmbuf_free(m); + return -1; +} + +static void +my_free_cb(void *addr, void *opaque __rte_unused) +{ + rte_free(addr); +} + +static int +test_shinfo_in_mbuf(struct rte_mempool *pktmbuf_pool) +{ + struct rte_mbuf_ext_shared_info *shinfo = NULL; + struct rte_mbuf *m2 = NULL; + struct rte_mbuf *m = NULL; + size_t buf_len = 256; + rte_iova_t iova; + char *buf = NULL; + + m = rte_pktmbuf_alloc(pktmbuf_pool); + printf("%s() m=%p\n", __func__, m); + if (m == NULL) + GOTO_FAIL("cannot allocate mbuf m"); + rte_pktmbuf_dump(stdout, m, 0); + + if (rte_pktmbuf_tailroom(m) < sizeof(*shinfo)) + GOTO_FAIL("tailroom too small"); + + buf = rte_malloc(NULL, buf_len, RTE_CACHE_LINE_SIZE); + if (buf == NULL) + GOTO_FAIL("cannot allocate buffer"); + + shinfo = rte_pktmbuf_mtod(m, struct rte_mbuf_ext_shared_info *); + shinfo->free_cb = my_free_cb; + shinfo->fcb_opaque = NULL; + rte_mbuf_ext_refcnt_set(shinfo, 1); + iova = rte_malloc_virt2iova(buf); + rte_pktmbuf_attach_extbuf(m, buf, iova, buf_len, shinfo); + printf("%s() m is attached to the ext buf\n", __func__); + rte_pktmbuf_dump(stdout, m, 0); + + m2 = rte_pktmbuf_alloc(pktmbuf_pool); + printf("%s() m2=%p\n", __func__, m2); + if (m2 == NULL) + GOTO_FAIL("cannot allocate mbuf m2"); + rte_pktmbuf_dump(stdout, m2, 0); + + rte_pktmbuf_attach(m2, m); + rte_pktmbuf_dump(stdout, m2, 0); + rte_pktmbuf_dump(stdout, m, 0); + rte_pktmbuf_free(m); + m = NULL; + + m = rte_pktmbuf_alloc(pktmbuf_pool); + printf("%s() m=%p\n", __func__, m); + if (m == NULL) + GOTO_FAIL("cannot allocate mbuf m"); + + /* clobber data in the mbuf we just allocated */ + shinfo = rte_pktmbuf_mtod(m, struct rte_mbuf_ext_shared_info *); + shinfo->free_cb = NULL; + + rte_pktmbuf_free(m); + m = NULL; + + rte_pktmbuf_free(m2); + m2 = NULL; + + printf("done\n"); + return 0; + + +fail: + rte_pktmbuf_free(m2); + rte_pktmbuf_free(m); + rte_free(buf); + return -1; +} + static int test_mbuf(void) { @@ -1133,17 +2763,35 @@ test_mbuf(void) /* create pktmbuf pool if it does not exist */ pktmbuf_pool = rte_pktmbuf_pool_create("test_pktmbuf_pool", - NB_MBUF, 32, 0, MBUF_DATA_SIZE, SOCKET_ID_ANY); + NB_MBUF, MEMPOOL_CACHE_SIZE, 0, MBUF_DATA_SIZE, + SOCKET_ID_ANY); if (pktmbuf_pool == NULL) { printf("cannot allocate mbuf pool\n"); goto err; } + if (test_shinfo_in_mbuf(pktmbuf_pool) < 0) { + printf("mbuf shinfo in mbuf failed\n"); + goto err; + } + + if (1) { + ret = 0; + goto err; + } + + /* test registration of dynamic fields and flags */ + if (test_mbuf_dyn(pktmbuf_pool) < 0) { + printf("mbuf dynflag test failed\n"); + goto err; + } + /* create a specific pktmbuf pool with a priv_size != 0 and no data * room size */ pktmbuf_pool2 = rte_pktmbuf_pool_create("test_pktmbuf_pool2", - NB_MBUF, 32, MBUF2_PRIV_SIZE, 0, SOCKET_ID_ANY); + NB_MBUF, MEMPOOL_CACHE_SIZE, MBUF2_PRIV_SIZE, 0, + SOCKET_ID_ANY); if (pktmbuf_pool2 == NULL) { printf("cannot allocate mbuf pool\n"); @@ -1162,6 +2810,12 @@ test_mbuf(void) goto err; } + /* test bulk mbuf alloc and free */ + if (test_pktmbuf_pool_bulk() < 0) { + printf("test_pktmbuf_pool_bulk() failed\n"); + goto err; + } + /* test that the pointer to the data on a packet mbuf is set properly */ if (test_pktmbuf_pool_ptr(pktmbuf_pool) < 0) { printf("test_pktmbuf_pool_ptr() failed\n"); @@ -1195,11 +2849,16 @@ test_mbuf(void) goto err; } - if (testclone_testupdate_testdetach(pktmbuf_pool) < 0) { + if (testclone_testupdate_testdetach(pktmbuf_pool, pktmbuf_pool) < 0) { printf("testclone_and_testupdate() failed \n"); goto err; } + if (test_pktmbuf_copy(pktmbuf_pool, pktmbuf_pool) < 0) { + printf("test_pktmbuf_copy() failed\n"); + goto err; + } + if (test_attach_from_different_pool(pktmbuf_pool, pktmbuf_pool2) < 0) { printf("test_attach_from_different_pool() failed\n"); goto err; @@ -1225,11 +2884,80 @@ test_mbuf(void) goto err; } + if (test_get_rx_ol_flag_list() < 0) { + printf("test_rte_get_rx_ol_flag_list() failed\n"); + goto err; + } + + if (test_get_tx_ol_flag_list() < 0) { + printf("test_rte_get_tx_ol_flag_list() failed\n"); + goto err; + } + + if (test_get_rx_ol_flag_name() < 0) { + printf("test_rte_get_rx_ol_flag_name() failed\n"); + goto err; + } + + if (test_get_tx_ol_flag_name() < 0) { + printf("test_rte_get_tx_ol_flag_name() failed\n"); + goto err; + } + + if (test_mbuf_validate_tx_offload_one(pktmbuf_pool) < 0) { + printf("test_mbuf_validate_tx_offload_one() failed\n"); + goto err; + } + + /* test for allocating a bulk of mbufs with various sizes */ + if (test_pktmbuf_alloc_bulk(pktmbuf_pool) < 0) { + printf("test_rte_pktmbuf_alloc_bulk() failed\n"); + goto err; + } + + /* test for allocating a bulk of mbufs with various sizes */ + if (test_neg_pktmbuf_alloc_bulk(pktmbuf_pool) < 0) { + printf("test_neg_rte_pktmbuf_alloc_bulk() failed\n"); + goto err; + } + + /* test to read mbuf packet */ + if (test_pktmbuf_read(pktmbuf_pool) < 0) { + printf("test_rte_pktmbuf_read() failed\n"); + goto err; + } + + /* test to read mbuf packet from offset */ + if (test_pktmbuf_read_from_offset(pktmbuf_pool) < 0) { + printf("test_rte_pktmbuf_read_from_offset() failed\n"); + goto err; + } + + /* test to read data from chain of mbufs with data segments */ + if (test_pktmbuf_read_from_chain(pktmbuf_pool) < 0) { + printf("test_rte_pktmbuf_read_from_chain() failed\n"); + goto err; + } + + /* test to initialize shared info. at the end of external buffer */ + if (test_pktmbuf_ext_shinfo_init_helper(pktmbuf_pool) < 0) { + printf("test_pktmbuf_ext_shinfo_init_helper() failed\n"); + goto err; + } + + /* test the mbuf pool with pinned external data buffers */ + if (test_pktmbuf_ext_pinned_buffer(pktmbuf_pool) < 0) { + printf("test_pktmbuf_ext_pinned_buffer() failed\n"); + goto err; + } + + ret = 0; err: rte_mempool_free(pktmbuf_pool); rte_mempool_free(pktmbuf_pool2); return ret; } +#undef GOTO_FAIL REGISTER_TEST_COMMAND(mbuf_autotest, test_mbuf);