[ovs-dev,v8,13/13] dpdk-tests: Add test coverage for multi-seg mbufs.

Message ID 1528734090-220990-15-git-send-email-tiago.lam@intel.com
State Superseded
Headers show
Series
  • Support multi-segment mbufs
Related show

Commit Message

Lam, Tiago June 11, 2018, 4:21 p.m.
In order to create a minimal environment that allows the tests to get
mbufs from an existing mempool, the following approach is taken:
- EAL is initialised (by using the main dpdk_init()) and a (very) small
  mempool is instantiated (mimicking the logic in dpdk_mp_create()).
  This mempool instance is global and used by all the tests;
- Packets are then allocated from the instantiated mempool, and tested
  on, by running some operations on them and manipulating data.

The tests introduced focus on testing DPDK dp_packets (where
source=DPBUF_DPDK), linked with a single or multiple mbufs, across
several operations, such as:
- dp_packet_put();
- dp_packet_shift();
- dp_packet_reserve();
- dp_packet_push_uninit();
- dp_packet_clear();
- And as a consequence of some of these, dp_packet_put_uninit() and
  dp_packet_resize__().

Finally, this has also been integrated with the new DPDK testsuite.
Thus, when running `$sudo make check-dpdk` one will also be running
these tests.

Signed-off-by: Tiago Lam <tiago.lam@intel.com>
---
 tests/automake.mk              |  10 +-
 tests/dpdk-packet-mbufs.at     |   7 +
 tests/system-dpdk-testsuite.at |   1 +
 tests/test-dpdk-mbufs.c        | 518 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 535 insertions(+), 1 deletion(-)
 create mode 100644 tests/dpdk-packet-mbufs.at
 create mode 100644 tests/test-dpdk-mbufs.c

Comments

Eelco Chaudron June 18, 2018, 1:17 p.m. | #1
I was running out of time, so did not review this patch. Will try to do 
it on the next iteration of this patch-set.


On 11 Jun 2018, at 18:21, Tiago Lam wrote:

> In order to create a minimal environment that allows the tests to get
> mbufs from an existing mempool, the following approach is taken:
> - EAL is initialised (by using the main dpdk_init()) and a (very) 
> small
>   mempool is instantiated (mimicking the logic in dpdk_mp_create()).
>   This mempool instance is global and used by all the tests;
> - Packets are then allocated from the instantiated mempool, and tested
>   on, by running some operations on them and manipulating data.
>
> The tests introduced focus on testing DPDK dp_packets (where
> source=DPBUF_DPDK), linked with a single or multiple mbufs, across
> several operations, such as:
> - dp_packet_put();
> - dp_packet_shift();
> - dp_packet_reserve();
> - dp_packet_push_uninit();
> - dp_packet_clear();
> - And as a consequence of some of these, dp_packet_put_uninit() and
>   dp_packet_resize__().
>
> Finally, this has also been integrated with the new DPDK testsuite.
> Thus, when running `$sudo make check-dpdk` one will also be running
> these tests.
>
> Signed-off-by: Tiago Lam <tiago.lam@intel.com>
> ---
>  tests/automake.mk              |  10 +-
>  tests/dpdk-packet-mbufs.at     |   7 +
>  tests/system-dpdk-testsuite.at |   1 +
>  tests/test-dpdk-mbufs.c        | 518 
> +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 535 insertions(+), 1 deletion(-)
>  create mode 100644 tests/dpdk-packet-mbufs.at
>  create mode 100644 tests/test-dpdk-mbufs.c
>
> diff --git a/tests/automake.mk b/tests/automake.mk
> index c420b29..74841a8 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -134,7 +134,8 @@ SYSTEM_DPDK_TESTSUITE_AT = \
>  	tests/system-common-macros.at \
>  	tests/system-dpdk-macros.at \
>  	tests/system-dpdk-testsuite.at \
> -	tests/system-dpdk.at
> +	tests/system-dpdk.at \
> +	tests/dpdk-packet-mbufs.at
>
>  check_SCRIPTS += tests/atlocal
>
> @@ -384,6 +385,10 @@ tests_ovstest_SOURCES = \
>  	tests/test-vconn.c \
>  	tests/test-aa.c \
>  	tests/test-stopwatch.c
> +if DPDK_NETDEV
> +tests_ovstest_SOURCES = \
> +	tests/test-dpdk-mbufs.c
> +endif
>
>  if !WIN32
>  tests_ovstest_SOURCES += \
> @@ -396,6 +401,9 @@ tests_ovstest_SOURCES += \
>  endif
>
>  tests_ovstest_LDADD = lib/libopenvswitch.la ovn/lib/libovn.la
> +if DPDK_NETDEV
> +tests_ovstest_LDFLAGS = $(AM_LDFLAGS) $(DPDK_vswitchd_LDFLAGS)
> +endif
>
>  noinst_PROGRAMS += tests/test-strtok_r
>  tests_test_strtok_r_SOURCES = tests/test-strtok_r.c
> diff --git a/tests/dpdk-packet-mbufs.at b/tests/dpdk-packet-mbufs.at
> new file mode 100644
> index 0000000..f28e4fc
> --- /dev/null
> +++ b/tests/dpdk-packet-mbufs.at
> @@ -0,0 +1,7 @@
> +AT_BANNER([OVS-DPDK dp_packet unit tests])
> +
> +AT_SETUP([OVS-DPDK dp_packet - mbufs allocation])
> +AT_KEYWORDS([dp_packet, multi-seg, mbufs])
> +AT_CHECK(ovstest test-dpdk-packet, [], [ignore], [ignore])
> +
> +AT_CLEANUP
> diff --git a/tests/system-dpdk-testsuite.at 
> b/tests/system-dpdk-testsuite.at
> index 382f09e..f5edf58 100644
> --- a/tests/system-dpdk-testsuite.at
> +++ b/tests/system-dpdk-testsuite.at
> @@ -23,3 +23,4 @@ m4_include([tests/system-common-macros.at])
>  m4_include([tests/system-dpdk-macros.at])
>
>  m4_include([tests/system-dpdk.at])
> +m4_include([tests/dpdk-packet-mbufs.at])
> diff --git a/tests/test-dpdk-mbufs.c b/tests/test-dpdk-mbufs.c
> new file mode 100644
> index 0000000..6c3bfdc
> --- /dev/null
> +++ b/tests/test-dpdk-mbufs.c
> @@ -0,0 +1,518 @@
> +/*
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, 
> software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
> implied.
> + * See the License for the specific language governing permissions 
> and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <rte_config.h>
> +#include <rte_mbuf.h>
> +#include <rte_mempool.h>
> +#include <rte_errno.h>
> +#include "dp-packet.h"
> +#include "ovstest.h"
> +#include "dpdk.h"
> +#include "smap.h"
> +
> +#define N_MBUFS 1024
> +#define MBUF_DATA_LEN 2048
> +
> +int num_tests = 0;
> +
> +/* Global var to hold a mempool instance, "test-mp", used in all of 
> the tests
> + * below. This instance is instantiated in dpdk_setup_eal_with_mp(). 
> */
> +static struct rte_mempool *mp;
> +
> +/* Test data used to fill the packets with data. Note that this isn't 
> a string
> + * that repsents a valid packet, by any means. The pattern is 
> generated in set_
> + * testing_pattern_str() and the sole purpose is to verify the data 
> remains the
> + * same after inserting and operating on multi-segment mbufs. */
> +static char *test_str;
> +
> +/* Asserts a dp_packet that holds a single mbuf, where:
> + * - nb_segs must be 1;
> + * - pkt_len must be equal to data_len which in turn must equal the 
> provided
> + *   'pkt_len';
> + * - data_off must start at the provided 'data_ofs';
> + * - next must be NULL. */
> +static void
> +assert_single_mbuf(struct dp_packet *pkt, uint16_t data_ofs,
> +                   uint32_t pkt_len) {
> +    struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, 
> &pkt->mbuf);
> +    ovs_assert(mbuf->nb_segs == 1);
> +    ovs_assert(mbuf->data_off == data_ofs);
> +    ovs_assert(mbuf->pkt_len == mbuf->data_len);
> +    ovs_assert(mbuf->pkt_len == pkt_len);
> +    ovs_assert(mbuf->next == NULL);
> +}
> +
> +/* Asserts a dp_packet that holds multiple mbufs, where:
> + * - nb_segs must be > 1 and equal to the provided 'nb_segs';
> + * - data_off must start at the provided 'data_ofs';
> + * - pkt_len must be equal to the provided 'pkt_len' and the some of 
> each
> + *   mbufs' 'data_len' must equal the pky_len;
> + * - next must not be NULL. */
> +static void
> +assert_multiple_mbufs(struct dp_packet *pkt, uint16_t data_ofs,
> +                      uint32_t pkt_len, uint16_t nb_segs) {
> +    struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, 
> &pkt->mbuf);
> +    ovs_assert(mbuf->nb_segs > 1 && mbuf->nb_segs == nb_segs);
> +    ovs_assert(mbuf->data_off == data_ofs);
> +    ovs_assert(mbuf->pkt_len != mbuf->data_len);
> +    ovs_assert(mbuf->next != NULL);
> +    ovs_assert(mbuf->pkt_len == pkt_len);
> +    /* Make sure pkt_len equals the sum of all segments data_len */
> +    while (mbuf) {
> +        pkt_len -= rte_pktmbuf_data_len(mbuf);
> +        mbuf = mbuf->next;
> +    }
> +    ovs_assert(pkt_len == 0);
> +}
> +
> +/* Asserts that the data existing in a packet, starting at 'data_ofs' 
> of the
> + * first mbuf and of length 'data_len' matches the global test_str 
> used,
> + * starting at index 0 and of the same length. */
> +static void
> +assert_data(struct dp_packet *pkt, uint16_t data_ofs, uint16_t 
> data_len) {
> +    struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, 
> &pkt->mbuf);
> +
> +    char data[data_len];
> +    const char *rd = rte_pktmbuf_read(mbuf, data_ofs, data_len, 
> data);
> +
> +    ovs_assert(rd != NULL);
> +    ovs_assert(memcmp(rd, test_str, data_len) == 0);
> +}
> +
> +static void
> +set_testing_pattern_str(void) {
> +    static const char *pattern = "1234567890";
> +
> +    /* Pattern will be of size 5000B */
> +    size_t test_str_len = 5000;
> +    test_str = xmalloc(test_str_len * sizeof(*test_str) + 1);
> +
> +    for (int i = 0; i < test_str_len; i += strlen(pattern)) {
> +        memcpy(test_str + i, pattern, strlen(pattern));
> +    }
> +
> +    test_str[test_str_len] = 0;
> +}
> +
> +static void
> +dpdk_eal_init(void) {
> +    struct smap other_config;
> +    smap_init(&other_config);
> +
> +    printf("Initialising EAL...\n");
> +    smap_add(&other_config, "dpdk-init", "true");
> +    smap_add(&other_config, "dpdk-lcore-mask", "10");
> +    smap_add(&other_config, "dpdk-socket-mem", "2048,0");
> +    smap_add(&other_config, "dpdk-multi-seg-mbufs", "true");
> +
> +    dpdk_init(&other_config);
> +}
> +
> +/* The allocation of mbufs here mimics the logic in dpdk_mp_create in
> + * netdev-dpdk.c. */
> +static struct rte_mempool *
> +dpdk_mp_create(char *mp_name) {
> +    uint16_t mbuf_size, aligned_mbuf_size, mbuf_priv_data_len;
> +
> +    mbuf_size = sizeof (struct dp_packet) +
> +                            MBUF_DATA_LEN + RTE_PKTMBUF_HEADROOM;
> +    aligned_mbuf_size = ROUND_UP(mbuf_size, RTE_CACHE_LINE_SIZE);
> +    mbuf_priv_data_len = sizeof(struct dp_packet) - sizeof(struct 
> rte_mbuf) +
> +                         (aligned_mbuf_size - mbuf_size);
> +
> +    struct rte_mempool *mpool = rte_pktmbuf_pool_create(
> +                                    mp_name, N_MBUFS,
> +                                    RTE_MEMPOOL_CACHE_MAX_SIZE,
> +                                    mbuf_priv_data_len,
> +                                    MBUF_DATA_LEN +
> +                                    RTE_PKTMBUF_HEADROOM /* defaults 
> 128B */,
> +                                    SOCKET_ID_ANY);
> +    if (mpool) {
> +        printf("Allocated \"%s\" mempool with %u mbufs\n", mp_name, 
> N_MBUFS);
> +    } else {
> +        printf("Failed mempool \"%s\" create request of %u mbufs: 
> %s.\n",
> +               mp_name, N_MBUFS, rte_strerror(rte_errno));
> +
> +        ovs_assert(mpool != NULL);
> +    }
> +
> +    return mpool;
> +}
> +
> +static void
> +dpdk_setup_eal_with_mp(void) {
> +    dpdk_eal_init();
> +
> +    mp = dpdk_mp_create("test-mp");
> +    ovs_assert(mp != NULL);
> +}
> +
> +static struct dp_packet *
> +dpdk_mp_alloc_pkt(struct rte_mempool *mpool) {
> +    struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mpool);
> +
> +    struct dp_packet *pkt = (struct dp_packet *) mbuf;
> +    pkt->source = DPBUF_DPDK;
> +
> +    return pkt;
> +}
> +
> +/* Similar to dp_packet_put() in dp-packet.c, appends the 'size' 
> bytes of data
> + * in 'p' to the tail end of 'pkt', allocating new mbufs if needed. 
> */
> +static struct dp_packet *
> +dpdk_pkt_put(struct dp_packet *pkt, void *p, size_t size) {
> +    uint16_t max_data_len, nb_segs;
> +    struct rte_mbuf *mbuf, *fmbuf;
> +
> +    mbuf = CONST_CAST(struct rte_mbuf *, &pkt->mbuf);
> +
> +    /* All new allocated mbuf's max data len is the same */
> +    max_data_len = mbuf->buf_len - mbuf->data_off;
> +
> +    /* Calculate # of needed mbufs to accomodate 'miss_len' */
> +    nb_segs = size / max_data_len;
> +    if (size % max_data_len) {
> +        nb_segs += 1;
> +    }
> +
> +    /* Proceed with the allocation of new mbufs */
> +    mp = mbuf->pool;
> +    fmbuf = mbuf;
> +    mbuf = rte_pktmbuf_lastseg(mbuf);
> +
> +    for (int i = 0; i < nb_segs; i++) {
> +        /* This takes care of initialising buf_len, data_len and 
> other
> +         * fields properly */
> +        mbuf->next = rte_pktmbuf_alloc(mp);
> +        if (!mbuf->next) {
> +            printf("Problem allocating more mbufs for tests.\n");
> +            rte_pktmbuf_free(mbuf);
> +            fmbuf = NULL;
> +            return NULL;
> +        }
> +
> +        fmbuf->nb_segs += 1;
> +
> +        mbuf = mbuf->next;
> +    }
> +
> +    dp_packet_mbuf_write(fmbuf, 0, size, p);
> +
> +    /* Adjust size of intermediate mbufs from current tail to end */
> +    size_t pkt_len = size;
> +    while (fmbuf && pkt_len > 0) {
> +        fmbuf->data_len = MIN(pkt_len, fmbuf->buf_len - 
> fmbuf->data_off);
> +        pkt_len -= fmbuf->data_len;
> +
> +        fmbuf = fmbuf->next;
> +    }
> +
> +    dp_packet_set_size(pkt, size);
> +
> +    return pkt;
> +}
> +
> +static int
> +test_dpdk_packet_insert_headroom(void) {
> +    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Reserve 256B of header */
> +    size_t str_len = 512;
> +    dp_packet_reserve(pkt, str_len);
> +    char *p = dp_packet_push_uninit(pkt, str_len);
> +    ovs_assert(p != NULL);
> +    /* Put the first 512B of "test_str" in the allocated header */
> +    memcpy(p, test_str, str_len);
> +
> +    /* Check properties and data are as expected */
> +    assert_single_mbuf(pkt, RTE_PKTMBUF_HEADROOM, str_len);
> +    assert_data(pkt, 0, str_len);
> +
> +    dp_packet_uninit(pkt);
> +
> +    return 0;
> +}
> +
> +static int
> +test_dpdk_packet_insert_tailroom_and_headroom(void) {
> +    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put the first 512B of "test_str" in the packet's header */
> +    size_t str_len = 512;
> +    char *p = dp_packet_put(pkt, test_str, str_len);
> +    ovs_assert(p != NULL);
> +    /* Allocate extra 256B of header */
> +    size_t head_len = 256;
> +    p = dp_packet_push_uninit(pkt, head_len);
> +    ovs_assert(p != NULL);
> +
> +    /* Check properties and data are as expected */
> +    assert_single_mbuf(pkt, 0, str_len + head_len);
> +
> +    /* Check the data inserted in the packet is correct */
> +    char data[str_len + head_len];
> +    const char *rd = rte_pktmbuf_read(&pkt->mbuf, 0, str_len + 
> head_len, data);
> +    ovs_assert(rd != NULL);
> +    /* Because of the headroom inserted, the data now begin at offset 
> 256 */
> +    ovs_assert(memcmp(rd + head_len, test_str, str_len) == 0);
> +
> +    dp_packet_uninit(pkt);
> +
> +    return 0;
> +}
> +
> +static int
> +test_dpdk_packet_insert_tailroom_and_headroom_multiple_mbufs(void) {
> +    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put the first 2050B of "test_str" in the packet, just enought 
> to
> +     * allocate two mbufs */
> +    size_t str_len = MBUF_DATA_LEN + 2;
> +    pkt = dpdk_pkt_put(pkt, test_str, str_len);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put the first 512B of "test_str" in the packet's header */
> +    size_t tail_len = 512;
> +    char *p = dp_packet_put(pkt, test_str, tail_len);
> +    ovs_assert(p != NULL);
> +
> +    /* Allocate extra 256B of header */
> +    size_t head_len = 256;
> +    p = dp_packet_push_uninit(pkt, head_len);
> +    ovs_assert(p != NULL);
> +    /* Copy the data to the reserved headroom */
> +    memcpy(p, test_str, head_len);
> +
> +    /* Check properties and data are as expected */
> +    size_t pkt_len = head_len + str_len + tail_len;
> +    uint16_t nb_segs = 2;
> +    assert_multiple_mbufs(pkt, 0, pkt_len, nb_segs);
> +
> +    /* Check the data inserted in the packet is correct */
> +    char data[pkt_len];
> +    const char *rd = rte_pktmbuf_read(&pkt->mbuf, 0, pkt_len, data);
> +    ovs_assert(rd != NULL);
> +    ovs_assert(memcmp(rd, test_str, head_len) == 0);
> +    ovs_assert(memcmp(rd + head_len + str_len, test_str, tail_len) == 
> 0);
> +
> +    dp_packet_uninit(pkt);
> +
> +    return 0;
> +}
> +
> +static int
> +test_dpdk_packet_insert_tailroom_multiple_mbufs(void) {
> +    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put the first 2050B of "test_str" in the packet, just enought 
> to
> +     * allocate two mbufs */
> +    size_t str_len = MBUF_DATA_LEN + 2;
> +    pkt = dpdk_pkt_put(pkt, test_str, str_len);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put the first 2000B of "test_str" in the packet's end */
> +    size_t tail_len = 2000;
> +    char *p = dp_packet_put(pkt, test_str, tail_len);
> +    ovs_assert(p != NULL);
> +
> +    /* Check properties and data are as expected */
> +    char data[str_len + tail_len];
> +    const char *rd = rte_pktmbuf_read(&pkt->mbuf, 0, str_len + 
> tail_len, data);
> +    ovs_assert(rd != NULL);
> +    /* Because of the headroom inserted, the data now begin at offset 
> 256 */
> +    ovs_assert(memcmp(rd + str_len, test_str, tail_len) == 0);
> +
> +    dp_packet_uninit(pkt);
> +
> +    return 0;
> +}
> +
> +static int
> +test_dpdk_packet_insert_headroom_multiple_mbufs(void) {
> +    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put the first 2050B of "test_str" in the packet, just enought 
> to
> +     * allocate two mbufs */
> +    size_t str_len = MBUF_DATA_LEN + 2;
> +    pkt = dpdk_pkt_put(pkt, test_str, str_len);
> +
> +    /* Push the first 2000B of "test_str" in the packet's header */
> +    size_t head_len = 2000;
> +    char *p = dp_packet_push_uninit(pkt, head_len);
> +    ovs_assert(p != NULL);
> +
> +    /* Check properties and data are as expected */
> +    char data[str_len + head_len];
> +    const char *rd = rte_pktmbuf_read(&pkt->mbuf, 0, str_len + 
> head_len, data);
> +    ovs_assert(rd != NULL);
> +    /* Because of the headroom inserted, the data is at offset 
> 'head_len' */
> +    ovs_assert(memcmp(rd + head_len, test_str, str_len) == 0);
> +
> +    dp_packet_uninit(pkt);
> +
> +    return 0;
> +}
> +
> +static int
> +test_dpdk_packet_change_size(void) {
> +    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put enough data in the packet that spans three mbufs (5120B) 
> */
> +    size_t str_len = MBUF_DATA_LEN * 2 + 1024;
> +    pkt = dpdk_pkt_put(pkt, test_str, str_len);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Check properties and data are as expected */
> +    uint16_t nb_segs = 3;
> +    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM, str_len, 
> nb_segs);
> +
> +    /* Change the size of the packet to fit in a single mbuf */
> +    dp_packet_clear(pkt);
> +
> +    assert_single_mbuf(pkt, RTE_PKTMBUF_HEADROOM, 0);
> +
> +    dp_packet_uninit(pkt);
> +
> +    return 0;
> +}
> +
> +/* Shift() tests */
> +
> +static int
> +test_dpdk_packet_shift_single_mbuf(void) {
> +    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put the first 1024B of "test_str" in the packet */
> +    size_t str_len = 1024;
> +    char *p = dp_packet_put(pkt, test_str, str_len);
> +    ovs_assert(p != NULL);
> +
> +    /* Shift data right by 512B */
> +    uint16_t shift_len = 512;
> +    dp_packet_shift(pkt, shift_len);
> +
> +    /* Check properties and data are as expected */
> +    assert_single_mbuf(pkt, RTE_PKTMBUF_HEADROOM + shift_len, 
> str_len);
> +    assert_data(pkt, 0, str_len);
> +
> +    dp_packet_uninit(pkt);
> +
> +    return 0;
> +}
> +
> +static int
> +test_dpdk_packet_shift_multiple_mbufs(void) {
> +    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put the data in "test_str" in the packet */
> +    size_t str_len = strlen(test_str);
> +    pkt = dpdk_pkt_put(pkt, test_str, str_len);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Check properties and data are as expected */
> +    uint16_t nb_segs = 3;
> +    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM, str_len, 
> nb_segs);
> +
> +    /* Shift data right by 1024B */
> +    uint16_t shift_len = 1024;
> +    dp_packet_shift(pkt, shift_len);
> +
> +    /* Check the data has been inserted correctly */
> +    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM + shift_len, 
> str_len,
> +                          nb_segs);
> +    assert_data(pkt, 0, str_len);
> +
> +    dp_packet_uninit(pkt);
> +
> +    return 0;
> +}
> +
> +static int
> +test_dpdk_packet_shift_right_then_left(void) {
> +    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Put the first 1024B of "test_str" in the packet */
> +    size_t str_len = strlen(test_str);
> +    pkt = dpdk_pkt_put(pkt, test_str, str_len);
> +    ovs_assert(pkt != NULL);
> +
> +    /* Shift data right by 1024B */
> +    int16_t shift_len = 1024;
> +    dp_packet_shift(pkt, 1024);
> +
> +    /* Check properties and data are as expected */
> +    uint16_t nb_segs = 3;
> +    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM + shift_len, 
> str_len,
> +                          nb_segs);
> +
> +    /* Shift data left by 512B */
> +    dp_packet_shift(pkt, -shift_len);
> +
> +    /* We negative shift_len (-shift_len) since  */
> +    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM, str_len,
> +                          nb_segs);
> +    assert_data(pkt, 0, str_len);
> +
> +    dp_packet_uninit(pkt);
> +
> +    return 0;
> +}
> +
> +static int
> +test_dpdk_packet(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
> +{
> +    /* Setup environment for tests */
> +    dpdk_setup_eal_with_mp();
> +    set_testing_pattern_str();
> +
> +    test_dpdk_packet_insert_headroom();
> +    num_tests++;
> +    test_dpdk_packet_insert_tailroom_and_headroom();
> +    num_tests++;
> +    test_dpdk_packet_insert_tailroom_multiple_mbufs();
> +    num_tests++;
> +    test_dpdk_packet_insert_headroom_multiple_mbufs();
> +    num_tests++;
> +    test_dpdk_packet_insert_tailroom_and_headroom_multiple_mbufs();
> +    num_tests++;
> +    test_dpdk_packet_change_size();
> +    num_tests++;
> +    test_dpdk_packet_shift_single_mbuf();
> +    num_tests++;
> +    test_dpdk_packet_shift_multiple_mbufs();
> +    num_tests++;
> +    test_dpdk_packet_shift_right_then_left();
> +    num_tests++;
> +
> +    printf("Executed %d tests\n", num_tests);
> +
> +    exit(0);
> +}
> +
> +OVSTEST_REGISTER("test-dpdk-packet", test_dpdk_packet);
> -- 
> 2.7.4

Patch

diff --git a/tests/automake.mk b/tests/automake.mk
index c420b29..74841a8 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -134,7 +134,8 @@  SYSTEM_DPDK_TESTSUITE_AT = \
 	tests/system-common-macros.at \
 	tests/system-dpdk-macros.at \
 	tests/system-dpdk-testsuite.at \
-	tests/system-dpdk.at
+	tests/system-dpdk.at \
+	tests/dpdk-packet-mbufs.at
 
 check_SCRIPTS += tests/atlocal
 
@@ -384,6 +385,10 @@  tests_ovstest_SOURCES = \
 	tests/test-vconn.c \
 	tests/test-aa.c \
 	tests/test-stopwatch.c
+if DPDK_NETDEV
+tests_ovstest_SOURCES = \
+	tests/test-dpdk-mbufs.c
+endif
 
 if !WIN32
 tests_ovstest_SOURCES += \
@@ -396,6 +401,9 @@  tests_ovstest_SOURCES += \
 endif
 
 tests_ovstest_LDADD = lib/libopenvswitch.la ovn/lib/libovn.la
+if DPDK_NETDEV
+tests_ovstest_LDFLAGS = $(AM_LDFLAGS) $(DPDK_vswitchd_LDFLAGS)
+endif
 
 noinst_PROGRAMS += tests/test-strtok_r
 tests_test_strtok_r_SOURCES = tests/test-strtok_r.c
diff --git a/tests/dpdk-packet-mbufs.at b/tests/dpdk-packet-mbufs.at
new file mode 100644
index 0000000..f28e4fc
--- /dev/null
+++ b/tests/dpdk-packet-mbufs.at
@@ -0,0 +1,7 @@ 
+AT_BANNER([OVS-DPDK dp_packet unit tests])
+
+AT_SETUP([OVS-DPDK dp_packet - mbufs allocation])
+AT_KEYWORDS([dp_packet, multi-seg, mbufs])
+AT_CHECK(ovstest test-dpdk-packet, [], [ignore], [ignore])
+
+AT_CLEANUP
diff --git a/tests/system-dpdk-testsuite.at b/tests/system-dpdk-testsuite.at
index 382f09e..f5edf58 100644
--- a/tests/system-dpdk-testsuite.at
+++ b/tests/system-dpdk-testsuite.at
@@ -23,3 +23,4 @@  m4_include([tests/system-common-macros.at])
 m4_include([tests/system-dpdk-macros.at])
 
 m4_include([tests/system-dpdk.at])
+m4_include([tests/dpdk-packet-mbufs.at])
diff --git a/tests/test-dpdk-mbufs.c b/tests/test-dpdk-mbufs.c
new file mode 100644
index 0000000..6c3bfdc
--- /dev/null
+++ b/tests/test-dpdk-mbufs.c
@@ -0,0 +1,518 @@ 
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rte_config.h>
+#include <rte_mbuf.h>
+#include <rte_mempool.h>
+#include <rte_errno.h>
+#include "dp-packet.h"
+#include "ovstest.h"
+#include "dpdk.h"
+#include "smap.h"
+
+#define N_MBUFS 1024
+#define MBUF_DATA_LEN 2048
+
+int num_tests = 0;
+
+/* Global var to hold a mempool instance, "test-mp", used in all of the tests
+ * below. This instance is instantiated in dpdk_setup_eal_with_mp(). */
+static struct rte_mempool *mp;
+
+/* Test data used to fill the packets with data. Note that this isn't a string
+ * that repsents a valid packet, by any means. The pattern is generated in set_
+ * testing_pattern_str() and the sole purpose is to verify the data remains the
+ * same after inserting and operating on multi-segment mbufs. */
+static char *test_str;
+
+/* Asserts a dp_packet that holds a single mbuf, where:
+ * - nb_segs must be 1;
+ * - pkt_len must be equal to data_len which in turn must equal the provided
+ *   'pkt_len';
+ * - data_off must start at the provided 'data_ofs';
+ * - next must be NULL. */
+static void
+assert_single_mbuf(struct dp_packet *pkt, uint16_t data_ofs,
+                   uint32_t pkt_len) {
+    struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &pkt->mbuf);
+    ovs_assert(mbuf->nb_segs == 1);
+    ovs_assert(mbuf->data_off == data_ofs);
+    ovs_assert(mbuf->pkt_len == mbuf->data_len);
+    ovs_assert(mbuf->pkt_len == pkt_len);
+    ovs_assert(mbuf->next == NULL);
+}
+
+/* Asserts a dp_packet that holds multiple mbufs, where:
+ * - nb_segs must be > 1 and equal to the provided 'nb_segs';
+ * - data_off must start at the provided 'data_ofs';
+ * - pkt_len must be equal to the provided 'pkt_len' and the some of each
+ *   mbufs' 'data_len' must equal the pky_len;
+ * - next must not be NULL. */
+static void
+assert_multiple_mbufs(struct dp_packet *pkt, uint16_t data_ofs,
+                      uint32_t pkt_len, uint16_t nb_segs) {
+    struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &pkt->mbuf);
+    ovs_assert(mbuf->nb_segs > 1 && mbuf->nb_segs == nb_segs);
+    ovs_assert(mbuf->data_off == data_ofs);
+    ovs_assert(mbuf->pkt_len != mbuf->data_len);
+    ovs_assert(mbuf->next != NULL);
+    ovs_assert(mbuf->pkt_len == pkt_len);
+    /* Make sure pkt_len equals the sum of all segments data_len */
+    while (mbuf) {
+        pkt_len -= rte_pktmbuf_data_len(mbuf);
+        mbuf = mbuf->next;
+    }
+    ovs_assert(pkt_len == 0);
+}
+
+/* Asserts that the data existing in a packet, starting at 'data_ofs' of the
+ * first mbuf and of length 'data_len' matches the global test_str used,
+ * starting at index 0 and of the same length. */
+static void
+assert_data(struct dp_packet *pkt, uint16_t data_ofs, uint16_t data_len) {
+    struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &pkt->mbuf);
+
+    char data[data_len];
+    const char *rd = rte_pktmbuf_read(mbuf, data_ofs, data_len, data);
+
+    ovs_assert(rd != NULL);
+    ovs_assert(memcmp(rd, test_str, data_len) == 0);
+}
+
+static void
+set_testing_pattern_str(void) {
+    static const char *pattern = "1234567890";
+
+    /* Pattern will be of size 5000B */
+    size_t test_str_len = 5000;
+    test_str = xmalloc(test_str_len * sizeof(*test_str) + 1);
+
+    for (int i = 0; i < test_str_len; i += strlen(pattern)) {
+        memcpy(test_str + i, pattern, strlen(pattern));
+    }
+
+    test_str[test_str_len] = 0;
+}
+
+static void
+dpdk_eal_init(void) {
+    struct smap other_config;
+    smap_init(&other_config);
+
+    printf("Initialising EAL...\n");
+    smap_add(&other_config, "dpdk-init", "true");
+    smap_add(&other_config, "dpdk-lcore-mask", "10");
+    smap_add(&other_config, "dpdk-socket-mem", "2048,0");
+    smap_add(&other_config, "dpdk-multi-seg-mbufs", "true");
+
+    dpdk_init(&other_config);
+}
+
+/* The allocation of mbufs here mimics the logic in dpdk_mp_create in
+ * netdev-dpdk.c. */
+static struct rte_mempool *
+dpdk_mp_create(char *mp_name) {
+    uint16_t mbuf_size, aligned_mbuf_size, mbuf_priv_data_len;
+
+    mbuf_size = sizeof (struct dp_packet) +
+                            MBUF_DATA_LEN + RTE_PKTMBUF_HEADROOM;
+    aligned_mbuf_size = ROUND_UP(mbuf_size, RTE_CACHE_LINE_SIZE);
+    mbuf_priv_data_len = sizeof(struct dp_packet) - sizeof(struct rte_mbuf) +
+                         (aligned_mbuf_size - mbuf_size);
+
+    struct rte_mempool *mpool = rte_pktmbuf_pool_create(
+                                    mp_name, N_MBUFS,
+                                    RTE_MEMPOOL_CACHE_MAX_SIZE,
+                                    mbuf_priv_data_len,
+                                    MBUF_DATA_LEN +
+                                    RTE_PKTMBUF_HEADROOM /* defaults 128B */,
+                                    SOCKET_ID_ANY);
+    if (mpool) {
+        printf("Allocated \"%s\" mempool with %u mbufs\n", mp_name, N_MBUFS);
+    } else {
+        printf("Failed mempool \"%s\" create request of %u mbufs: %s.\n",
+               mp_name, N_MBUFS, rte_strerror(rte_errno));
+
+        ovs_assert(mpool != NULL);
+    }
+
+    return mpool;
+}
+
+static void
+dpdk_setup_eal_with_mp(void) {
+    dpdk_eal_init();
+
+    mp = dpdk_mp_create("test-mp");
+    ovs_assert(mp != NULL);
+}
+
+static struct dp_packet *
+dpdk_mp_alloc_pkt(struct rte_mempool *mpool) {
+    struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mpool);
+
+    struct dp_packet *pkt = (struct dp_packet *) mbuf;
+    pkt->source = DPBUF_DPDK;
+
+    return pkt;
+}
+
+/* Similar to dp_packet_put() in dp-packet.c, appends the 'size' bytes of data
+ * in 'p' to the tail end of 'pkt', allocating new mbufs if needed. */
+static struct dp_packet *
+dpdk_pkt_put(struct dp_packet *pkt, void *p, size_t size) {
+    uint16_t max_data_len, nb_segs;
+    struct rte_mbuf *mbuf, *fmbuf;
+
+    mbuf = CONST_CAST(struct rte_mbuf *, &pkt->mbuf);
+
+    /* All new allocated mbuf's max data len is the same */
+    max_data_len = mbuf->buf_len - mbuf->data_off;
+
+    /* Calculate # of needed mbufs to accomodate 'miss_len' */
+    nb_segs = size / max_data_len;
+    if (size % max_data_len) {
+        nb_segs += 1;
+    }
+
+    /* Proceed with the allocation of new mbufs */
+    mp = mbuf->pool;
+    fmbuf = mbuf;
+    mbuf = rte_pktmbuf_lastseg(mbuf);
+
+    for (int i = 0; i < nb_segs; i++) {
+        /* This takes care of initialising buf_len, data_len and other
+         * fields properly */
+        mbuf->next = rte_pktmbuf_alloc(mp);
+        if (!mbuf->next) {
+            printf("Problem allocating more mbufs for tests.\n");
+            rte_pktmbuf_free(mbuf);
+            fmbuf = NULL;
+            return NULL;
+        }
+
+        fmbuf->nb_segs += 1;
+
+        mbuf = mbuf->next;
+    }
+
+    dp_packet_mbuf_write(fmbuf, 0, size, p);
+
+    /* Adjust size of intermediate mbufs from current tail to end */
+    size_t pkt_len = size;
+    while (fmbuf && pkt_len > 0) {
+        fmbuf->data_len = MIN(pkt_len, fmbuf->buf_len - fmbuf->data_off);
+        pkt_len -= fmbuf->data_len;
+
+        fmbuf = fmbuf->next;
+    }
+
+    dp_packet_set_size(pkt, size);
+
+    return pkt;
+}
+
+static int
+test_dpdk_packet_insert_headroom(void) {
+    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
+    ovs_assert(pkt != NULL);
+
+    /* Reserve 256B of header */
+    size_t str_len = 512;
+    dp_packet_reserve(pkt, str_len);
+    char *p = dp_packet_push_uninit(pkt, str_len);
+    ovs_assert(p != NULL);
+    /* Put the first 512B of "test_str" in the allocated header */
+    memcpy(p, test_str, str_len);
+
+    /* Check properties and data are as expected */
+    assert_single_mbuf(pkt, RTE_PKTMBUF_HEADROOM, str_len);
+    assert_data(pkt, 0, str_len);
+
+    dp_packet_uninit(pkt);
+
+    return 0;
+}
+
+static int
+test_dpdk_packet_insert_tailroom_and_headroom(void) {
+    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
+    ovs_assert(pkt != NULL);
+
+    /* Put the first 512B of "test_str" in the packet's header */
+    size_t str_len = 512;
+    char *p = dp_packet_put(pkt, test_str, str_len);
+    ovs_assert(p != NULL);
+    /* Allocate extra 256B of header */
+    size_t head_len = 256;
+    p = dp_packet_push_uninit(pkt, head_len);
+    ovs_assert(p != NULL);
+
+    /* Check properties and data are as expected */
+    assert_single_mbuf(pkt, 0, str_len + head_len);
+
+    /* Check the data inserted in the packet is correct */
+    char data[str_len + head_len];
+    const char *rd = rte_pktmbuf_read(&pkt->mbuf, 0, str_len + head_len, data);
+    ovs_assert(rd != NULL);
+    /* Because of the headroom inserted, the data now begin at offset 256 */
+    ovs_assert(memcmp(rd + head_len, test_str, str_len) == 0);
+
+    dp_packet_uninit(pkt);
+
+    return 0;
+}
+
+static int
+test_dpdk_packet_insert_tailroom_and_headroom_multiple_mbufs(void) {
+    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
+    ovs_assert(pkt != NULL);
+
+    /* Put the first 2050B of "test_str" in the packet, just enought to
+     * allocate two mbufs */
+    size_t str_len = MBUF_DATA_LEN + 2;
+    pkt = dpdk_pkt_put(pkt, test_str, str_len);
+    ovs_assert(pkt != NULL);
+
+    /* Put the first 512B of "test_str" in the packet's header */
+    size_t tail_len = 512;
+    char *p = dp_packet_put(pkt, test_str, tail_len);
+    ovs_assert(p != NULL);
+
+    /* Allocate extra 256B of header */
+    size_t head_len = 256;
+    p = dp_packet_push_uninit(pkt, head_len);
+    ovs_assert(p != NULL);
+    /* Copy the data to the reserved headroom */
+    memcpy(p, test_str, head_len);
+
+    /* Check properties and data are as expected */
+    size_t pkt_len = head_len + str_len + tail_len;
+    uint16_t nb_segs = 2;
+    assert_multiple_mbufs(pkt, 0, pkt_len, nb_segs);
+
+    /* Check the data inserted in the packet is correct */
+    char data[pkt_len];
+    const char *rd = rte_pktmbuf_read(&pkt->mbuf, 0, pkt_len, data);
+    ovs_assert(rd != NULL);
+    ovs_assert(memcmp(rd, test_str, head_len) == 0);
+    ovs_assert(memcmp(rd + head_len + str_len, test_str, tail_len) == 0);
+
+    dp_packet_uninit(pkt);
+
+    return 0;
+}
+
+static int
+test_dpdk_packet_insert_tailroom_multiple_mbufs(void) {
+    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
+    ovs_assert(pkt != NULL);
+
+    /* Put the first 2050B of "test_str" in the packet, just enought to
+     * allocate two mbufs */
+    size_t str_len = MBUF_DATA_LEN + 2;
+    pkt = dpdk_pkt_put(pkt, test_str, str_len);
+    ovs_assert(pkt != NULL);
+
+    /* Put the first 2000B of "test_str" in the packet's end */
+    size_t tail_len = 2000;
+    char *p = dp_packet_put(pkt, test_str, tail_len);
+    ovs_assert(p != NULL);
+
+    /* Check properties and data are as expected */
+    char data[str_len + tail_len];
+    const char *rd = rte_pktmbuf_read(&pkt->mbuf, 0, str_len + tail_len, data);
+    ovs_assert(rd != NULL);
+    /* Because of the headroom inserted, the data now begin at offset 256 */
+    ovs_assert(memcmp(rd + str_len, test_str, tail_len) == 0);
+
+    dp_packet_uninit(pkt);
+
+    return 0;
+}
+
+static int
+test_dpdk_packet_insert_headroom_multiple_mbufs(void) {
+    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
+    ovs_assert(pkt != NULL);
+
+    /* Put the first 2050B of "test_str" in the packet, just enought to
+     * allocate two mbufs */
+    size_t str_len = MBUF_DATA_LEN + 2;
+    pkt = dpdk_pkt_put(pkt, test_str, str_len);
+
+    /* Push the first 2000B of "test_str" in the packet's header */
+    size_t head_len = 2000;
+    char *p = dp_packet_push_uninit(pkt, head_len);
+    ovs_assert(p != NULL);
+
+    /* Check properties and data are as expected */
+    char data[str_len + head_len];
+    const char *rd = rte_pktmbuf_read(&pkt->mbuf, 0, str_len + head_len, data);
+    ovs_assert(rd != NULL);
+    /* Because of the headroom inserted, the data is at offset 'head_len' */
+    ovs_assert(memcmp(rd + head_len, test_str, str_len) == 0);
+
+    dp_packet_uninit(pkt);
+
+    return 0;
+}
+
+static int
+test_dpdk_packet_change_size(void) {
+    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
+    ovs_assert(pkt != NULL);
+
+    /* Put enough data in the packet that spans three mbufs (5120B) */
+    size_t str_len = MBUF_DATA_LEN * 2 + 1024;
+    pkt = dpdk_pkt_put(pkt, test_str, str_len);
+    ovs_assert(pkt != NULL);
+
+    /* Check properties and data are as expected */
+    uint16_t nb_segs = 3;
+    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM, str_len, nb_segs);
+
+    /* Change the size of the packet to fit in a single mbuf */
+    dp_packet_clear(pkt);
+
+    assert_single_mbuf(pkt, RTE_PKTMBUF_HEADROOM, 0);
+
+    dp_packet_uninit(pkt);
+
+    return 0;
+}
+
+/* Shift() tests */
+
+static int
+test_dpdk_packet_shift_single_mbuf(void) {
+    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
+    ovs_assert(pkt != NULL);
+
+    /* Put the first 1024B of "test_str" in the packet */
+    size_t str_len = 1024;
+    char *p = dp_packet_put(pkt, test_str, str_len);
+    ovs_assert(p != NULL);
+
+    /* Shift data right by 512B */
+    uint16_t shift_len = 512;
+    dp_packet_shift(pkt, shift_len);
+
+    /* Check properties and data are as expected */
+    assert_single_mbuf(pkt, RTE_PKTMBUF_HEADROOM + shift_len, str_len);
+    assert_data(pkt, 0, str_len);
+
+    dp_packet_uninit(pkt);
+
+    return 0;
+}
+
+static int
+test_dpdk_packet_shift_multiple_mbufs(void) {
+    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
+    ovs_assert(pkt != NULL);
+
+    /* Put the data in "test_str" in the packet */
+    size_t str_len = strlen(test_str);
+    pkt = dpdk_pkt_put(pkt, test_str, str_len);
+    ovs_assert(pkt != NULL);
+
+    /* Check properties and data are as expected */
+    uint16_t nb_segs = 3;
+    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM, str_len, nb_segs);
+
+    /* Shift data right by 1024B */
+    uint16_t shift_len = 1024;
+    dp_packet_shift(pkt, shift_len);
+
+    /* Check the data has been inserted correctly */
+    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM + shift_len, str_len,
+                          nb_segs);
+    assert_data(pkt, 0, str_len);
+
+    dp_packet_uninit(pkt);
+
+    return 0;
+}
+
+static int
+test_dpdk_packet_shift_right_then_left(void) {
+    struct dp_packet *pkt = dpdk_mp_alloc_pkt(mp);
+    ovs_assert(pkt != NULL);
+
+    /* Put the first 1024B of "test_str" in the packet */
+    size_t str_len = strlen(test_str);
+    pkt = dpdk_pkt_put(pkt, test_str, str_len);
+    ovs_assert(pkt != NULL);
+
+    /* Shift data right by 1024B */
+    int16_t shift_len = 1024;
+    dp_packet_shift(pkt, 1024);
+
+    /* Check properties and data are as expected */
+    uint16_t nb_segs = 3;
+    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM + shift_len, str_len,
+                          nb_segs);
+
+    /* Shift data left by 512B */
+    dp_packet_shift(pkt, -shift_len);
+
+    /* We negative shift_len (-shift_len) since  */
+    assert_multiple_mbufs(pkt, RTE_PKTMBUF_HEADROOM, str_len,
+                          nb_segs);
+    assert_data(pkt, 0, str_len);
+
+    dp_packet_uninit(pkt);
+
+    return 0;
+}
+
+static int
+test_dpdk_packet(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    /* Setup environment for tests */
+    dpdk_setup_eal_with_mp();
+    set_testing_pattern_str();
+
+    test_dpdk_packet_insert_headroom();
+    num_tests++;
+    test_dpdk_packet_insert_tailroom_and_headroom();
+    num_tests++;
+    test_dpdk_packet_insert_tailroom_multiple_mbufs();
+    num_tests++;
+    test_dpdk_packet_insert_headroom_multiple_mbufs();
+    num_tests++;
+    test_dpdk_packet_insert_tailroom_and_headroom_multiple_mbufs();
+    num_tests++;
+    test_dpdk_packet_change_size();
+    num_tests++;
+    test_dpdk_packet_shift_single_mbuf();
+    num_tests++;
+    test_dpdk_packet_shift_multiple_mbufs();
+    num_tests++;
+    test_dpdk_packet_shift_right_then_left();
+    num_tests++;
+
+    printf("Executed %d tests\n", num_tests);
+
+    exit(0);
+}
+
+OVSTEST_REGISTER("test-dpdk-packet", test_dpdk_packet);