From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/spdk/dpdk/app/test/test_mbuf.c | 2877 ++++++++++++++++++++++++++++++++++++ 1 file changed, 2877 insertions(+) create mode 100644 src/spdk/dpdk/app/test/test_mbuf.c (limited to 'src/spdk/dpdk/app/test/test_mbuf.c') diff --git a/src/spdk/dpdk/app/test/test_mbuf.c b/src/spdk/dpdk/app/test/test_mbuf.c new file mode 100644 index 000000000..71bdab691 --- /dev/null +++ b/src/spdk/dpdk/app/test/test_mbuf.c @@ -0,0 +1,2877 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + +#define REFCNT_MAX_ITER 64 +#define REFCNT_MAX_TIMEOUT 10 +#define REFCNT_MAX_REF (RTE_MAX_LCORE) +#define REFCNT_MBUF_NUM 64 +#define REFCNT_RING_SIZE (REFCNT_MBUF_NUM * REFCNT_MAX_REF) + +#define MAGIC_DATA 0x42424242 + +#define MAKE_STRING(x) # x + +#ifdef RTE_MBUF_REFCNT_ATOMIC + +static volatile uint32_t refcnt_stop_slaves; +static unsigned refcnt_lcore[RTE_MAX_LCORE]; + +#endif + +/* + * MBUF + * ==== + * + * #. Allocate a mbuf pool. + * + * - The pool contains NB_MBUF elements, where each mbuf is MBUF_SIZE + * bytes long. + * + * #. Test multiple allocations of mbufs from this pool. + * + * - Allocate NB_MBUF and store pointers in a table. + * - If an allocation fails, return an error. + * - Free all these mbufs. + * - Repeat the same test to check that mbufs were freed correctly. + * + * #. Test data manipulation in pktmbuf. + * + * - Alloc an mbuf. + * - Append data using rte_pktmbuf_append(). + * - Test for error in rte_pktmbuf_append() when len is too large. + * - Trim data at the end of mbuf using rte_pktmbuf_trim(). + * - Test for error in rte_pktmbuf_trim() when len is too large. + * - Prepend a header using rte_pktmbuf_prepend(). + * - Test for error in rte_pktmbuf_prepend() when len is too large. + * - Remove data at the beginning of mbuf using rte_pktmbuf_adj(). + * - Test for error in rte_pktmbuf_adj() when len is too large. + * - Check that appended data is not corrupt. + * - Free the mbuf. + * - Between all these tests, check data_len and pkt_len, and + * that the mbuf is contiguous. + * - Repeat the test to check that allocation operations + * reinitialize the mbuf correctly. + * + * #. Test packet cloning + * - Clone a mbuf and verify the data + * - Clone the cloned mbuf and verify the data + * - Attach a mbuf to another that does not have the same priv_size. + */ + +#define GOTO_FAIL(str, ...) do { \ + printf("mbuf test FAILED (l.%d): <" str ">\n", \ + __LINE__, ##__VA_ARGS__); \ + goto fail; \ +} while(0) + +/* + * test data manipulation in mbuf with non-ascii data + */ +static int +test_pktmbuf_with_non_ascii_data(struct rte_mempool *pktmbuf_pool) +{ + struct rte_mbuf *m = NULL; + char *data; + + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("Cannot allocate mbuf"); + if (rte_pktmbuf_pkt_len(m) != 0) + GOTO_FAIL("Bad length"); + + data = rte_pktmbuf_append(m, MBUF_TEST_DATA_LEN); + if (data == NULL) + GOTO_FAIL("Cannot append data"); + if (rte_pktmbuf_pkt_len(m) != MBUF_TEST_DATA_LEN) + GOTO_FAIL("Bad pkt length"); + if (rte_pktmbuf_data_len(m) != MBUF_TEST_DATA_LEN) + GOTO_FAIL("Bad data length"); + memset(data, 0xff, rte_pktmbuf_pkt_len(m)); + if (!rte_pktmbuf_is_contiguous(m)) + GOTO_FAIL("Buffer should be continuous"); + rte_pktmbuf_dump(stdout, m, MBUF_TEST_DATA_LEN); + + rte_pktmbuf_free(m); + + return 0; + +fail: + if(m) { + rte_pktmbuf_free(m); + } + return -1; +} + +/* + * test data manipulation in mbuf + */ +static int +test_one_pktmbuf(struct rte_mempool *pktmbuf_pool) +{ + struct rte_mbuf *m = NULL; + char *data, *data2, *hdr; + unsigned i; + + printf("Test pktmbuf API\n"); + + /* alloc a mbuf */ + + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("Cannot allocate mbuf"); + if (rte_pktmbuf_pkt_len(m) != 0) + GOTO_FAIL("Bad length"); + + rte_pktmbuf_dump(stdout, m, 0); + + /* append data */ + + data = rte_pktmbuf_append(m, MBUF_TEST_DATA_LEN); + if (data == NULL) + GOTO_FAIL("Cannot append data"); + if (rte_pktmbuf_pkt_len(m) != MBUF_TEST_DATA_LEN) + GOTO_FAIL("Bad pkt length"); + if (rte_pktmbuf_data_len(m) != MBUF_TEST_DATA_LEN) + GOTO_FAIL("Bad data length"); + memset(data, 0x66, rte_pktmbuf_pkt_len(m)); + if (!rte_pktmbuf_is_contiguous(m)) + GOTO_FAIL("Buffer should be continuous"); + rte_pktmbuf_dump(stdout, m, MBUF_TEST_DATA_LEN); + rte_pktmbuf_dump(stdout, m, 2*MBUF_TEST_DATA_LEN); + + /* this append should fail */ + + data2 = rte_pktmbuf_append(m, (uint16_t)(rte_pktmbuf_tailroom(m) + 1)); + if (data2 != NULL) + GOTO_FAIL("Append should not succeed"); + + /* append some more data */ + + data2 = rte_pktmbuf_append(m, MBUF_TEST_DATA_LEN2); + if (data2 == NULL) + GOTO_FAIL("Cannot append data"); + if (rte_pktmbuf_pkt_len(m) != MBUF_TEST_DATA_LEN + MBUF_TEST_DATA_LEN2) + GOTO_FAIL("Bad pkt length"); + if (rte_pktmbuf_data_len(m) != MBUF_TEST_DATA_LEN + MBUF_TEST_DATA_LEN2) + GOTO_FAIL("Bad data length"); + if (!rte_pktmbuf_is_contiguous(m)) + GOTO_FAIL("Buffer should be continuous"); + + /* trim data at the end of mbuf */ + + if (rte_pktmbuf_trim(m, MBUF_TEST_DATA_LEN2) < 0) + GOTO_FAIL("Cannot trim data"); + if (rte_pktmbuf_pkt_len(m) != MBUF_TEST_DATA_LEN) + GOTO_FAIL("Bad pkt length"); + if (rte_pktmbuf_data_len(m) != MBUF_TEST_DATA_LEN) + GOTO_FAIL("Bad data length"); + if (!rte_pktmbuf_is_contiguous(m)) + GOTO_FAIL("Buffer should be continuous"); + + /* this trim should fail */ + + if (rte_pktmbuf_trim(m, (uint16_t)(rte_pktmbuf_data_len(m) + 1)) == 0) + GOTO_FAIL("trim should not succeed"); + + /* prepend one header */ + + hdr = rte_pktmbuf_prepend(m, MBUF_TEST_HDR1_LEN); + if (hdr == NULL) + GOTO_FAIL("Cannot prepend"); + if (data - hdr != MBUF_TEST_HDR1_LEN) + GOTO_FAIL("Prepend failed"); + if (rte_pktmbuf_pkt_len(m) != MBUF_TEST_DATA_LEN + MBUF_TEST_HDR1_LEN) + GOTO_FAIL("Bad pkt length"); + if (rte_pktmbuf_data_len(m) != MBUF_TEST_DATA_LEN + MBUF_TEST_HDR1_LEN) + GOTO_FAIL("Bad data length"); + if (!rte_pktmbuf_is_contiguous(m)) + GOTO_FAIL("Buffer should be continuous"); + memset(hdr, 0x55, MBUF_TEST_HDR1_LEN); + + /* prepend another header */ + + hdr = rte_pktmbuf_prepend(m, MBUF_TEST_HDR2_LEN); + if (hdr == NULL) + GOTO_FAIL("Cannot prepend"); + if (data - hdr != MBUF_TEST_ALL_HDRS_LEN) + GOTO_FAIL("Prepend failed"); + if (rte_pktmbuf_pkt_len(m) != MBUF_TEST_DATA_LEN + MBUF_TEST_ALL_HDRS_LEN) + GOTO_FAIL("Bad pkt length"); + if (rte_pktmbuf_data_len(m) != MBUF_TEST_DATA_LEN + MBUF_TEST_ALL_HDRS_LEN) + GOTO_FAIL("Bad data length"); + if (!rte_pktmbuf_is_contiguous(m)) + GOTO_FAIL("Buffer should be continuous"); + memset(hdr, 0x55, MBUF_TEST_HDR2_LEN); + + rte_mbuf_sanity_check(m, 1); + rte_mbuf_sanity_check(m, 0); + rte_pktmbuf_dump(stdout, m, 0); + + /* this prepend should fail */ + + hdr = rte_pktmbuf_prepend(m, (uint16_t)(rte_pktmbuf_headroom(m) + 1)); + if (hdr != NULL) + GOTO_FAIL("prepend should not succeed"); + + /* remove data at beginning of mbuf (adj) */ + + if (data != rte_pktmbuf_adj(m, MBUF_TEST_ALL_HDRS_LEN)) + GOTO_FAIL("rte_pktmbuf_adj failed"); + if (rte_pktmbuf_pkt_len(m) != MBUF_TEST_DATA_LEN) + GOTO_FAIL("Bad pkt length"); + if (rte_pktmbuf_data_len(m) != MBUF_TEST_DATA_LEN) + GOTO_FAIL("Bad data length"); + if (!rte_pktmbuf_is_contiguous(m)) + GOTO_FAIL("Buffer should be continuous"); + + /* this adj should fail */ + + if (rte_pktmbuf_adj(m, (uint16_t)(rte_pktmbuf_data_len(m) + 1)) != NULL) + GOTO_FAIL("rte_pktmbuf_adj should not succeed"); + + /* check data */ + + if (!rte_pktmbuf_is_contiguous(m)) + GOTO_FAIL("Buffer should be continuous"); + + for (i=0; ishinfo) : + rte_mbuf_refcnt_read(m); +} + +static int +testclone_testupdate_testdetach(struct rte_mempool *pktmbuf_pool, + struct rte_mempool *clone_pool) +{ + struct rte_mbuf *m = NULL; + struct rte_mbuf *clone = NULL; + struct rte_mbuf *clone2 = 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; + + /* clone the allocated mbuf */ + clone = rte_pktmbuf_clone(m, clone_pool); + if (clone == NULL) + GOTO_FAIL("cannot clone data\n"); + + data = rte_pktmbuf_mtod(clone, unaligned_uint32_t *); + if (*data != MAGIC_DATA) + GOTO_FAIL("invalid data in clone\n"); + + if (testclone_refcnt_read(m) != 2) + GOTO_FAIL("invalid refcnt in m\n"); + + /* free the clone */ + rte_pktmbuf_free(clone); + 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; + + clone = rte_pktmbuf_clone(m, clone_pool); + if (clone == NULL) + GOTO_FAIL("cannot clone data\n"); + + data = rte_pktmbuf_mtod(clone, unaligned_uint32_t *); + if (*data != MAGIC_DATA) + GOTO_FAIL("invalid data in clone\n"); + + data = rte_pktmbuf_mtod(clone->next, unaligned_uint32_t *); + if (*data != MAGIC_DATA) + GOTO_FAIL("invalid data in clone->next\n"); + + if (testclone_refcnt_read(m) != 2) + GOTO_FAIL("invalid refcnt in m\n"); + + 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, clone_pool); + if (clone2 == NULL) + GOTO_FAIL("cannot clone the clone\n"); + + data = rte_pktmbuf_mtod(clone2, unaligned_uint32_t *); + if (*data != MAGIC_DATA) + GOTO_FAIL("invalid data in clone2\n"); + + data = rte_pktmbuf_mtod(clone2->next, unaligned_uint32_t *); + if (*data != MAGIC_DATA) + GOTO_FAIL("invalid data in clone2->next\n"); + + if (testclone_refcnt_read(m) != 3) + GOTO_FAIL("invalid refcnt in m\n"); + + if (testclone_refcnt_read(m->next) != 3) + GOTO_FAIL("invalid refcnt in m->next\n"); + + /* free mbuf */ + rte_pktmbuf_free(m); + rte_pktmbuf_free(clone); + rte_pktmbuf_free(clone2); + + m = NULL; + clone = NULL; + clone2 = NULL; + printf("%s ok\n", __func__); + return 0; + +fail: + if (m) + rte_pktmbuf_free(m); + if (clone) + rte_pktmbuf_free(clone); + if (clone2) + rte_pktmbuf_free(clone2); + 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) +{ + struct rte_mbuf *m = NULL; + struct rte_mbuf *clone = NULL; + struct rte_mbuf *clone2 = NULL; + char *data, *c_data, *c_data2; + + /* alloc a mbuf */ + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("cannot allocate mbuf"); + + if (rte_pktmbuf_pkt_len(m) != 0) + GOTO_FAIL("Bad length"); + + data = rte_pktmbuf_mtod(m, char *); + + /* allocate a new mbuf from the second pool, and attach it to the first + * mbuf */ + clone = rte_pktmbuf_alloc(pktmbuf_pool2); + if (clone == NULL) + GOTO_FAIL("cannot allocate mbuf from second pool\n"); + + /* check data room size and priv size, and erase priv */ + if (rte_pktmbuf_data_room_size(clone->pool) != 0) + GOTO_FAIL("data room size should be 0\n"); + if (rte_pktmbuf_priv_size(clone->pool) != MBUF2_PRIV_SIZE) + GOTO_FAIL("data room size should be %d\n", MBUF2_PRIV_SIZE); + memset(clone + 1, 0, MBUF2_PRIV_SIZE); + + /* save data pointer to compare it after detach() */ + c_data = rte_pktmbuf_mtod(clone, char *); + if (c_data != (char *)clone + sizeof(*clone) + MBUF2_PRIV_SIZE) + GOTO_FAIL("bad data pointer in clone"); + if (rte_pktmbuf_headroom(clone) != 0) + GOTO_FAIL("bad headroom in clone"); + + rte_pktmbuf_attach(clone, m); + + if (rte_pktmbuf_mtod(clone, char *) != data) + GOTO_FAIL("clone was not attached properly\n"); + if (rte_pktmbuf_headroom(clone) != RTE_PKTMBUF_HEADROOM) + GOTO_FAIL("bad headroom in clone after attach"); + if (rte_mbuf_refcnt_read(m) != 2) + GOTO_FAIL("invalid refcnt in m\n"); + + /* allocate a new mbuf from the second pool, and attach it to the first + * cloned mbuf */ + clone2 = rte_pktmbuf_alloc(pktmbuf_pool2); + if (clone2 == NULL) + GOTO_FAIL("cannot allocate clone2 from second pool\n"); + + /* check data room size and priv size, and erase priv */ + if (rte_pktmbuf_data_room_size(clone2->pool) != 0) + GOTO_FAIL("data room size should be 0\n"); + if (rte_pktmbuf_priv_size(clone2->pool) != MBUF2_PRIV_SIZE) + GOTO_FAIL("data room size should be %d\n", MBUF2_PRIV_SIZE); + memset(clone2 + 1, 0, MBUF2_PRIV_SIZE); + + /* save data pointer to compare it after detach() */ + c_data2 = rte_pktmbuf_mtod(clone2, char *); + if (c_data2 != (char *)clone2 + sizeof(*clone2) + MBUF2_PRIV_SIZE) + GOTO_FAIL("bad data pointer in clone2"); + if (rte_pktmbuf_headroom(clone2) != 0) + GOTO_FAIL("bad headroom in clone2"); + + rte_pktmbuf_attach(clone2, clone); + + if (rte_pktmbuf_mtod(clone2, char *) != data) + GOTO_FAIL("clone2 was not attached properly\n"); + if (rte_pktmbuf_headroom(clone2) != RTE_PKTMBUF_HEADROOM) + GOTO_FAIL("bad headroom in clone2 after attach"); + if (rte_mbuf_refcnt_read(m) != 3) + GOTO_FAIL("invalid refcnt in m\n"); + + /* detach the clones */ + rte_pktmbuf_detach(clone); + if (c_data != rte_pktmbuf_mtod(clone, char *)) + GOTO_FAIL("clone was not detached properly\n"); + if (rte_mbuf_refcnt_read(m) != 2) + GOTO_FAIL("invalid refcnt in m\n"); + + rte_pktmbuf_detach(clone2); + if (c_data2 != rte_pktmbuf_mtod(clone2, char *)) + GOTO_FAIL("clone2 was not detached properly\n"); + if (rte_mbuf_refcnt_read(m) != 1) + GOTO_FAIL("invalid refcnt in m\n"); + + /* free the clones and the initial mbuf */ + rte_pktmbuf_free(clone2); + rte_pktmbuf_free(clone); + rte_pktmbuf_free(m); + printf("%s ok\n", __func__); + return 0; + +fail: + if (m) + rte_pktmbuf_free(m); + if (clone) + rte_pktmbuf_free(clone); + if (clone2) + rte_pktmbuf_free(clone2); + return -1; +} + +/* + * test allocation and free of mbufs + */ +static int +test_pktmbuf_pool(struct rte_mempool *pktmbuf_pool) +{ + unsigned i; + struct rte_mbuf *m[NB_MBUF]; + int ret = 0; + + for (i=0; idata_off += 64; + } + + /* free them */ + for (i=0; idata_off != RTE_PKTMBUF_HEADROOM) { + printf("invalid data_off\n"); + ret = -1; + } + } + + /* free them */ + for (i=0; inext; + rte_pktmbuf_free_seg(mt); + } + } + } + + return ret; +} + +/* + * Stress test for rte_mbuf atomic refcnt. + * Implies that RTE_MBUF_REFCNT_ATOMIC is defined. + * For more efficiency, recommended to run with RTE_LIBRTE_MBUF_DEBUG defined. + */ + +#ifdef RTE_MBUF_REFCNT_ATOMIC + +static int +test_refcnt_slave(void *arg) +{ + unsigned lcore, free; + void *mp = 0; + struct rte_ring *refcnt_mbuf_ring = arg; + + lcore = rte_lcore_id(); + printf("%s started at lcore %u\n", __func__, lcore); + + free = 0; + while (refcnt_stop_slaves == 0) { + if (rte_ring_dequeue(refcnt_mbuf_ring, &mp) == 0) { + free++; + rte_pktmbuf_free(mp); + } + } + + refcnt_lcore[lcore] += free; + printf("%s finished at lcore %u, " + "number of freed mbufs: %u\n", + __func__, lcore, free); + return 0; +} + +static void +test_refcnt_iter(unsigned int lcore, unsigned int iter, + struct rte_mempool *refcnt_pool, + struct rte_ring *refcnt_mbuf_ring) +{ + uint16_t ref; + unsigned i, n, tref, wn; + struct rte_mbuf *m; + + tref = 0; + + /* For each mbuf in the pool: + * - allocate mbuf, + * - increment it's reference up to N+1, + * - enqueue it N times into the ring for slave cores to free. + */ + for (i = 0, n = rte_mempool_avail_count(refcnt_pool); + i != n && (m = rte_pktmbuf_alloc(refcnt_pool)) != NULL; + i++) { + ref = RTE_MAX(rte_rand() % REFCNT_MAX_REF, 1UL); + tref += ref; + if ((ref & 1) != 0) { + rte_pktmbuf_refcnt_update(m, ref); + while (ref-- != 0) + rte_ring_enqueue(refcnt_mbuf_ring, m); + } else { + while (ref-- != 0) { + rte_pktmbuf_refcnt_update(m, 1); + rte_ring_enqueue(refcnt_mbuf_ring, m); + } + } + rte_pktmbuf_free(m); + } + + if (i != n) + rte_panic("(lcore=%u, iter=%u): was able to allocate only " + "%u from %u mbufs\n", lcore, iter, i, n); + + /* wait till slave lcores will consume all mbufs */ + while (!rte_ring_empty(refcnt_mbuf_ring)) + ; + + /* check that all mbufs are back into mempool by now */ + for (wn = 0; wn != REFCNT_MAX_TIMEOUT; wn++) { + if ((i = rte_mempool_avail_count(refcnt_pool)) == n) { + refcnt_lcore[lcore] += tref; + printf("%s(lcore=%u, iter=%u) completed, " + "%u references processed\n", + __func__, lcore, iter, tref); + return; + } + rte_delay_ms(100); + } + + rte_panic("(lcore=%u, iter=%u): after %us only " + "%u of %u mbufs left free\n", lcore, iter, wn, i, n); +} + +static int +test_refcnt_master(struct rte_mempool *refcnt_pool, + struct rte_ring *refcnt_mbuf_ring) +{ + unsigned i, lcore; + + lcore = rte_lcore_id(); + printf("%s started at lcore %u\n", __func__, lcore); + + for (i = 0; i != REFCNT_MAX_ITER; i++) + test_refcnt_iter(lcore, i, refcnt_pool, refcnt_mbuf_ring); + + refcnt_stop_slaves = 1; + rte_wmb(); + + printf("%s finished at lcore %u\n", __func__, lcore); + return 0; +} + +#endif + +static int +test_refcnt_mbuf(void) +{ +#ifdef RTE_MBUF_REFCNT_ATOMIC + unsigned int master, slave, tref; + int ret = -1; + struct rte_mempool *refcnt_pool = NULL; + struct rte_ring *refcnt_mbuf_ring = NULL; + + if (rte_lcore_count() < 2) { + printf("Not enough cores for test_refcnt_mbuf, expecting at least 2\n"); + return TEST_SKIPPED; + } + + printf("starting %s, at %u lcores\n", __func__, rte_lcore_count()); + + /* create refcnt pool & ring if they don't exist */ + + refcnt_pool = rte_pktmbuf_pool_create(MAKE_STRING(refcnt_pool), + REFCNT_MBUF_NUM, 0, 0, 0, + SOCKET_ID_ANY); + if (refcnt_pool == NULL) { + printf("%s: cannot allocate " MAKE_STRING(refcnt_pool) "\n", + __func__); + return -1; + } + + refcnt_mbuf_ring = rte_ring_create("refcnt_mbuf_ring", + rte_align32pow2(REFCNT_RING_SIZE), SOCKET_ID_ANY, + RING_F_SP_ENQ); + if (refcnt_mbuf_ring == NULL) { + printf("%s: cannot allocate " MAKE_STRING(refcnt_mbuf_ring) + "\n", __func__); + goto err; + } + + refcnt_stop_slaves = 0; + memset(refcnt_lcore, 0, sizeof (refcnt_lcore)); + + rte_eal_mp_remote_launch(test_refcnt_slave, refcnt_mbuf_ring, + SKIP_MASTER); + + test_refcnt_master(refcnt_pool, refcnt_mbuf_ring); + + rte_eal_mp_wait_lcore(); + + /* check that we porcessed all references */ + tref = 0; + master = rte_get_master_lcore(); + + RTE_LCORE_FOREACH_SLAVE(slave) + tref += refcnt_lcore[slave]; + + if (tref != refcnt_lcore[master]) + rte_panic("referenced mbufs: %u, freed mbufs: %u\n", + tref, refcnt_lcore[master]); + + rte_mempool_dump(stdout, refcnt_pool); + rte_ring_dump(stdout, refcnt_mbuf_ring); + + ret = 0; + +err: + rte_mempool_free(refcnt_pool); + rte_ring_free(refcnt_mbuf_ring); + return ret; +#else + return 0; +#endif +} + +#include +#include + +/* use fork() to test mbuf errors panic */ +static int +verify_mbuf_check_panics(struct rte_mbuf *buf) +{ + int pid; + int status; + + pid = fork(); + + if (pid == 0) { + rte_mbuf_sanity_check(buf, 1); /* should panic */ + exit(0); /* return normally if it doesn't panic */ + } else if (pid < 0){ + printf("Fork Failed\n"); + return -1; + } + wait(&status); + if(status == 0) + return -1; + + return 0; +} + +static int +test_failing_mbuf_sanity_check(struct rte_mempool *pktmbuf_pool) +{ + struct rte_mbuf *buf; + struct rte_mbuf badbuf; + + printf("Checking rte_mbuf_sanity_check for failure conditions\n"); + + /* get a good mbuf to use to make copies */ + 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; + + printf("Now checking for error conditions\n"); + + if (verify_mbuf_check_panics(NULL)) { + printf("Error with NULL mbuf test\n"); + return -1; + } + + badbuf = *buf; + badbuf.pool = NULL; + if (verify_mbuf_check_panics(&badbuf)) { + printf("Error with bad-pool mbuf test\n"); + return -1; + } + + badbuf = *buf; + badbuf.buf_iova = 0; + if (verify_mbuf_check_panics(&badbuf)) { + printf("Error with bad-physaddr mbuf test\n"); + return -1; + } + + badbuf = *buf; + badbuf.buf_addr = NULL; + if (verify_mbuf_check_panics(&badbuf)) { + printf("Error with bad-addr mbuf test\n"); + return -1; + } + + badbuf = *buf; + badbuf.refcnt = 0; + if (verify_mbuf_check_panics(&badbuf)) { + printf("Error with bad-refcnt(0) mbuf test\n"); + return -1; + } + + badbuf = *buf; + badbuf.refcnt = UINT16_MAX; + if (verify_mbuf_check_panics(&badbuf)) { + printf("Error with bad-refcnt(MAX) mbuf test\n"); + return -1; + } + + return 0; +} + +static int +test_mbuf_linearize(struct rte_mempool *pktmbuf_pool, int pkt_len, + int nb_segs) +{ + + struct rte_mbuf *m = NULL, *mbuf = NULL; + uint8_t *data; + int data_len = 0; + int remain; + int seg, seg_len; + int i; + + if (pkt_len < 1) { + printf("Packet size must be 1 or more (is %d)\n", pkt_len); + return -1; + } + + if (nb_segs < 1) { + printf("Number of segments must be 1 or more (is %d)\n", + nb_segs); + return -1; + } + + seg_len = pkt_len / nb_segs; + if (seg_len == 0) + seg_len = 1; + + remain = pkt_len; + + /* Create chained mbuf_src and fill it generated data */ + for (seg = 0; remain > 0; seg++) { + + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) { + printf("Cannot create segment for source mbuf"); + goto fail; + } + + /* Make sure if tailroom is zeroed */ + memset(rte_pktmbuf_mtod(m, uint8_t *), 0, + rte_pktmbuf_tailroom(m)); + + data_len = remain; + if (data_len > seg_len) + data_len = seg_len; + + data = (uint8_t *)rte_pktmbuf_append(m, data_len); + if (data == NULL) { + printf("Cannot append %d bytes to the mbuf\n", + data_len); + goto fail; + } + + for (i = 0; i < data_len; i++) + data[i] = (seg * seg_len + i) % 0x0ff; + + if (seg == 0) + mbuf = m; + else + rte_pktmbuf_chain(mbuf, m); + + remain -= data_len; + } + + /* Create destination buffer to store coalesced data */ + if (rte_pktmbuf_linearize(mbuf)) { + printf("Mbuf linearization failed\n"); + goto fail; + } + + if (!rte_pktmbuf_is_contiguous(mbuf)) { + printf("Source buffer should be contiguous after " + "linearization\n"); + goto fail; + } + + data = rte_pktmbuf_mtod(mbuf, uint8_t *); + + for (i = 0; i < pkt_len; i++) + if (data[i] != (i % 0x0ff)) { + printf("Incorrect data in linearized mbuf\n"); + goto fail; + } + + rte_pktmbuf_free(mbuf); + return 0; + +fail: + if (mbuf) + rte_pktmbuf_free(mbuf); + return -1; +} + +static int +test_mbuf_linearize_check(struct rte_mempool *pktmbuf_pool) +{ + struct test_mbuf_array { + int size; + int nb_segs; + } mbuf_array[] = { + { 128, 1 }, + { 64, 64 }, + { 512, 10 }, + { 250, 11 }, + { 123, 8 }, + }; + unsigned int i; + + printf("Test mbuf linearize API\n"); + + for (i = 0; i < RTE_DIM(mbuf_array); i++) + if (test_mbuf_linearize(pktmbuf_pool, mbuf_array[i].size, + mbuf_array[i].nb_segs)) { + printf("Test failed for %d, %d\n", mbuf_array[i].size, + mbuf_array[i].nb_segs); + return -1; + } + + return 0; +} + +/* + * Helper function for test_tx_ofload + */ +static inline void +set_tx_offload(struct rte_mbuf *mb, uint64_t il2, uint64_t il3, uint64_t il4, + uint64_t tso, uint64_t ol3, uint64_t ol2) +{ + mb->l2_len = il2; + mb->l3_len = il3; + mb->l4_len = il4; + mb->tso_segsz = tso; + mb->outer_l3_len = ol3; + mb->outer_l2_len = ol2; +} + +static int +test_tx_offload(void) +{ + struct rte_mbuf *mb; + uint64_t tm, v1, v2; + size_t sz; + uint32_t i; + + static volatile struct { + uint16_t l2; + uint16_t l3; + uint16_t l4; + uint16_t tso; + } txof; + + const uint32_t num = 0x10000; + + txof.l2 = rte_rand() % (1 << RTE_MBUF_L2_LEN_BITS); + txof.l3 = rte_rand() % (1 << RTE_MBUF_L3_LEN_BITS); + txof.l4 = rte_rand() % (1 << RTE_MBUF_L4_LEN_BITS); + txof.tso = rte_rand() % (1 << RTE_MBUF_TSO_SEGSZ_BITS); + + printf("%s started, tx_offload = {\n" + "\tl2_len=%#hx,\n" + "\tl3_len=%#hx,\n" + "\tl4_len=%#hx,\n" + "\ttso_segsz=%#hx,\n" + "\touter_l3_len=%#x,\n" + "\touter_l2_len=%#x,\n" + "};\n", + __func__, + txof.l2, txof.l3, txof.l4, txof.tso, txof.l3, txof.l2); + + sz = sizeof(*mb) * num; + mb = rte_zmalloc(NULL, sz, RTE_CACHE_LINE_SIZE); + if (mb == NULL) { + printf("%s failed, out of memory\n", __func__); + return -ENOMEM; + } + + memset(mb, 0, sz); + tm = rte_rdtsc_precise(); + + for (i = 0; i != num; i++) + set_tx_offload(mb + i, txof.l2, txof.l3, txof.l4, + txof.tso, txof.l3, txof.l2); + + tm = rte_rdtsc_precise() - tm; + printf("%s set tx_offload by bit-fields: %u iterations, %" + PRIu64 " cycles, %#Lf cycles/iter\n", + __func__, num, tm, (long double)tm / num); + + v1 = mb[rte_rand() % num].tx_offload; + + memset(mb, 0, sz); + tm = rte_rdtsc_precise(); + + for (i = 0; i != num; i++) + mb[i].tx_offload = rte_mbuf_tx_offload(txof.l2, txof.l3, + txof.l4, txof.tso, txof.l3, txof.l2, 0); + + tm = rte_rdtsc_precise() - tm; + printf("%s set raw tx_offload: %u iterations, %" + PRIu64 " cycles, %#Lf cycles/iter\n", + __func__, num, tm, (long double)tm / num); + + v2 = mb[rte_rand() % num].tx_offload; + + rte_free(mb); + + printf("%s finished\n" + "expected tx_offload value: 0x%" PRIx64 ";\n" + "rte_mbuf_tx_offload value: 0x%" PRIx64 ";\n", + __func__, v1, v2); + + 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, flag2=%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 int +test_mbuf(void) +{ + int ret = -1; + struct rte_mempool *pktmbuf_pool = NULL; + struct rte_mempool *pktmbuf_pool2 = NULL; + + + RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) != RTE_CACHE_LINE_MIN_SIZE * 2); + + /* create pktmbuf pool if it does not exist */ + pktmbuf_pool = rte_pktmbuf_pool_create("test_pktmbuf_pool", + NB_MBUF, MEMPOOL_CACHE_SIZE, 0, MBUF_DATA_SIZE, + SOCKET_ID_ANY); + + if (pktmbuf_pool == NULL) { + printf("cannot allocate mbuf pool\n"); + 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, MEMPOOL_CACHE_SIZE, MBUF2_PRIV_SIZE, 0, + SOCKET_ID_ANY); + + if (pktmbuf_pool2 == NULL) { + printf("cannot allocate mbuf pool\n"); + goto err; + } + + /* test multiple mbuf alloc */ + if (test_pktmbuf_pool(pktmbuf_pool) < 0) { + printf("test_mbuf_pool() failed\n"); + goto err; + } + + /* do it another time to check that all mbufs were freed */ + if (test_pktmbuf_pool(pktmbuf_pool) < 0) { + printf("test_mbuf_pool() failed (2)\n"); + 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"); + goto err; + } + + /* test data manipulation in mbuf */ + if (test_one_pktmbuf(pktmbuf_pool) < 0) { + printf("test_one_mbuf() failed\n"); + goto err; + } + + + /* + * do it another time, to check that allocation reinitialize + * the mbuf correctly + */ + if (test_one_pktmbuf(pktmbuf_pool) < 0) { + printf("test_one_mbuf() failed (2)\n"); + goto err; + } + + if (test_pktmbuf_with_non_ascii_data(pktmbuf_pool) < 0) { + printf("test_pktmbuf_with_non_ascii_data() failed\n"); + goto err; + } + + /* test free pktmbuf segment one by one */ + if (test_pktmbuf_free_segment(pktmbuf_pool) < 0) { + printf("test_pktmbuf_free_segment() failed.\n"); + goto err; + } + + 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; + } + + if (test_refcnt_mbuf() < 0) { + printf("test_refcnt_mbuf() failed \n"); + goto err; + } + + if (test_failing_mbuf_sanity_check(pktmbuf_pool) < 0) { + printf("test_failing_mbuf_sanity_check() failed\n"); + goto err; + } + + if (test_mbuf_linearize_check(pktmbuf_pool) < 0) { + printf("test_mbuf_linearize_check() failed\n"); + goto err; + } + + if (test_tx_offload() < 0) { + printf("test_tx_offload() failed\n"); + 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); -- cgit v1.2.3