* [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library @ 2015-01-07 16:39 Reshma Pattan 2015-01-07 16:39 ` [dpdk-dev] [PATCH 2/3] librte_reorder: New unit test cases added Reshma Pattan ` (5 more replies) 0 siblings, 6 replies; 44+ messages in thread From: Reshma Pattan @ 2015-01-07 16:39 UTC (permalink / raw) To: dev From: Reshma Pattan <reshma.pattan@intel.com> 1)New library to provide reordering of out of ordered mbufs based on sequence number of mbuf. Library uses reorder buffer structure which in tern uses two circular buffers called ready and order buffers. *rte_reorder_create API creates instance of reorder buffer. *rte_reorder_init API initializes given reorder buffer instance. *rte_reorder_reset API resets given reorder buffer instance. *rte_reorder_insert API inserts the mbuf into order circular buffer. *rte_reorder_fill_overflow moves mbufs from order buffer to ready buffer to accomodate early packets in order buffer. *rte_reorder_drain API provides draining facility to fetch out reordered mbufs from order and ready buffers. Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_eal/common/include/rte_tailq_elem.h | 2 + lib/librte_mbuf/rte_mbuf.h | 3 + lib/librte_reorder/Makefile | 50 +++ lib/librte_reorder/rte_reorder.c | 464 +++++++++++++++++++++++++ lib/librte_reorder/rte_reorder.h | 184 ++++++++++ 8 files changed, 714 insertions(+) create mode 100644 lib/librte_reorder/Makefile create mode 100644 lib/librte_reorder/rte_reorder.c create mode 100644 lib/librte_reorder/rte_reorder.h diff --git a/config/common_bsdapp b/config/common_bsdapp index 9177db1..e3e0e94 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -334,6 +334,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 2f9643b..b5ec730 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -342,6 +342,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/lib/Makefile b/lib/Makefile index 0ffc982..5919d32 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -65,6 +65,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += librte_distributor DIRS-$(CONFIG_RTE_LIBRTE_PORT) += librte_port DIRS-$(CONFIG_RTE_LIBRTE_TABLE) += librte_table DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += librte_pipeline +DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += librte_reorder ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni diff --git a/lib/librte_eal/common/include/rte_tailq_elem.h b/lib/librte_eal/common/include/rte_tailq_elem.h index f74fc7c..3013869 100644 --- a/lib/librte_eal/common/include/rte_tailq_elem.h +++ b/lib/librte_eal/common/include/rte_tailq_elem.h @@ -84,6 +84,8 @@ rte_tailq_elem(RTE_TAILQ_ACL, "RTE_ACL") rte_tailq_elem(RTE_TAILQ_DISTRIBUTOR, "RTE_DISTRIBUTOR") +rte_tailq_elem(RTE_TAILQ_REORDER, "RTE_REORDER") + rte_tailq_end(RTE_TAILQ_NUM) #undef rte_tailq_elem diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h index 16059c6..ed27eb8 100644 --- a/lib/librte_mbuf/rte_mbuf.h +++ b/lib/librte_mbuf/rte_mbuf.h @@ -262,6 +262,9 @@ struct rte_mbuf { uint32_t usr; /**< User defined tags. See @rte_distributor_process */ } hash; /**< hash information */ + /* sequence number - field used in distributor and reorder library */ + uint32_t seqn; + /* second cache line - fields only used in slow path or on TX */ MARKER cacheline1 __rte_cache_aligned; diff --git a/lib/librte_reorder/Makefile b/lib/librte_reorder/Makefile new file mode 100644 index 0000000..12b916f --- /dev/null +++ b/lib/librte_reorder/Makefile @@ -0,0 +1,50 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(RTE_SDK)/mk/rte.vars.mk + +# library name +LIB = librte_reorder.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) + +# all source are stored in SRCS-y +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) := rte_reorder.c + +# install this header file +SYMLINK-$(CONFIG_RTE_LIBRTE_REORDER)-include := rte_reorder.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_eal + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_reorder/rte_reorder.c b/lib/librte_reorder/rte_reorder.c new file mode 100644 index 0000000..fb3e986 --- /dev/null +++ b/lib/librte_reorder/rte_reorder.c @@ -0,0 +1,464 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <inttypes.h> +#include <string.h> + +#include <rte_log.h> +#include <rte_mbuf.h> +#include <rte_memzone.h> +#include <rte_eal_memconfig.h> +#include <rte_errno.h> +#include <rte_tailq.h> +#include <rte_malloc.h> + +#include "rte_reorder.h" + +TAILQ_HEAD(rte_reorder_list, rte_tailq_entry); + +#define NO_FLAGS 0 +#define RTE_REORDER_PREFIX "RO_" +#define RTE_REORDER_NAMESIZE 32 + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDER RTE_LOGTYPE_USER1 + +/* A generic circular buffer */ +struct cir_buffer { + unsigned int size; /**< Number of entries that can be stored */ + unsigned int mask; /**< [buffer_size - 1]: used for wrap-around */ + unsigned int head; /**< insertion point in buffer */ + unsigned int tail; /**< extraction point in buffer */ + struct rte_mbuf **entries; +} __rte_cache_aligned; + +/* The reorder buffer data structure itself */ +struct rte_reorder_buffer { + char name[RTE_REORDER_NAMESIZE]; + uint32_t min_seqn; /**< Lowest seq. number that can be in the buffer */ + unsigned int memsize; /**< memory area size of reorder buffer */ + struct cir_buffer ready_buf; /**< temp buffer for dequeued entries */ + struct cir_buffer order_buf; /**< buffer used to reorder entries */ +} __rte_cache_aligned; + +struct rte_reorder_buffer * +rte_reorder_init(void *buf, unsigned int bufsize, + const char *name, unsigned int size) +{ + struct rte_reorder_buffer *b = (struct rte_reorder_buffer *)buf; + const unsigned int min_bufsize = sizeof(*b) + + (2 * size * sizeof(struct rte_mbuf *)); + + struct rte_reorder_buffer *be; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (b == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer parameter:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (bufsize < min_bufsize) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size:%u, " + "should be minimum:%u\n", bufsize, min_bufsize); + rte_errno = ENOMEM; + return NULL; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* guarantee there's no existing */ + TAILQ_FOREACH(te, reorder_list, next) { + be = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, be->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + if (te != NULL) { + b = be; + memset(b, 0, bufsize); + snprintf(b->name, sizeof(b->name), "%s", name); + b->memsize = bufsize; + b->order_buf.size = b->ready_buf.size = size; + b->order_buf.mask = b->ready_buf.mask = size - 1; + b->ready_buf.entries = (void *)&b[1]; + b->order_buf.entries = RTE_PTR_ADD(&b[1], + size * sizeof(b->ready_buf.entries[0])); + goto exit; + } + + /* allocate tailq entry */ + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); + if (te == NULL) { + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); + goto exit; + } + + memset(b, 0, bufsize); + snprintf(b->name, sizeof(b->name), "%s", name); + b->memsize = bufsize; + b->order_buf.size = b->ready_buf.size = size; + b->order_buf.mask = b->ready_buf.mask = size - 1; + b->ready_buf.entries = (void *)&b[1]; + b->order_buf.entries = RTE_PTR_ADD(&b[1], + size * sizeof(b->ready_buf.entries[0])); + + te->data = (void *) b; + + TAILQ_INSERT_TAIL(reorder_list, te, next); + +exit: + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return b; +} + +void rte_reorder_reset(struct rte_reorder_buffer *b) +{ + unsigned int i = 0; + char name[RTE_REORDER_NAMESIZE]; + /* Free up the mbufs of order buffer & ready buffer */ + for (i = 0; i < b->order_buf.size; i++) { + if (b->order_buf.entries[i]) + rte_pktmbuf_free(b->order_buf.entries[i]); + if (b->ready_buf.entries[i]) + rte_pktmbuf_free(b->ready_buf.entries[i]); + } + snprintf(name, sizeof(name), "%s", b->name); + rte_reorder_init(b, b->memsize, name, b->order_buf.size); +} + +struct rte_reorder_buffer* +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size) +{ + const struct rte_memzone *mz; + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + char mz_name[RTE_MEMZONE_NAMESIZE]; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + /* Check user arguments. */ + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* guarantee there's no existing */ + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + if (te != NULL) + goto exit; + + /* allocate tailq entry */ + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); + if (te == NULL) { + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); + goto exit; + } + + /* Allocate memory to store the reorder buffer structure. */ + const unsigned int bufsize = sizeof(struct rte_reorder_buffer) + + (2 * size * sizeof(struct rte_mbuf *)); + snprintf(mz_name, sizeof(mz_name), RTE_REORDER_PREFIX"%s", name); + mz = rte_memzone_reserve(mz_name, bufsize, + socket_id, NO_FLAGS); + if (mz == NULL) { + RTE_LOG(ERR, REORDER, "Memzone allocation failed\n"); + rte_errno = ENOMEM; + return NULL; + } + b = mz->addr; + memset(b, 0, bufsize); + snprintf(b->name, sizeof(b->name), "%s", name); + b->memsize = bufsize; + b->order_buf.size = b->ready_buf.size = size; + b->order_buf.mask = b->ready_buf.mask = size - 1; + b->ready_buf.entries = (void *)&b[1]; + b->order_buf.entries = RTE_PTR_ADD(&b[1], + size * sizeof(b->ready_buf.entries[0])); + + te->data = (void *) b; + + TAILQ_INSERT_TAIL(reorder_list, te, next); + +exit: + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return b; +} + +void +rte_reorder_free(struct rte_reorder_buffer *b) +{ + struct rte_reorder_list *reorder_list; + struct rte_tailq_entry *te; + + /* Check user arguments. */ + if (b == NULL) + return; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* find our tailq entry */ + TAILQ_FOREACH(te, reorder_list, next) { + if (te->data == (void *) b) + break; + } + if (te == NULL) { + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return; + } + + TAILQ_REMOVE(reorder_list, te, next); + + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + + rte_free(b); + rte_free(te); +} + +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); + + if (te == NULL) { + rte_errno = ENOENT; + return NULL; + } + + return b; +} + +static unsigned +rte_reorder_fill_overflow(struct rte_reorder_buffer *b, unsigned n) +{ + /* + * 1. Move all ready entries that fit to the ready_buf + * 2. check if we meet the minimum needed (n). + * 3. If not, then skip any gaps and keep moving. + * 4. If at any point the ready buffer is full, stop + * 5. Return the number of positions the order_buf head has moved + */ + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + unsigned int order_head_adv = 0; + + /* + * move at least n packets to ready buffer, assuming ready buffer + * has room for those packets. + */ + while (order_head_adv < n && + ((ready_buf->head + 1) & ready_buf->mask) != ready_buf->tail) { + + /* if we are blocked waiting on a packet, skip it */ + if (order_buf->entries[order_buf->head] == NULL) { + order_buf->head++, order_head_adv++; + + if (order_buf->head == order_buf->size) + order_buf->head = 0; + } + + /* Move all ready entries that fit to the ready_buf */ + while (order_buf->entries[order_buf->head] != NULL) { + ready_buf->entries[ready_buf->head++] = + order_buf->entries[order_buf->head]; + + order_buf->entries[order_buf->head++] = NULL; + order_head_adv++; + + if (ready_buf->head == ready_buf->size) + ready_buf->head = 0; + if (order_buf->head == order_buf->size) + order_buf->head = 0; + + if (((ready_buf->head+1) & ready_buf->mask) == ready_buf->tail) + break; + } + } + + b->min_seqn += order_head_adv; + /* Return the number of positions the order_buf head has moved */ + return order_head_adv; +} + +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf) +{ + uint32_t offset, position; + struct cir_buffer *order_buf = &b->order_buf; + + /* + * calculate the offset from the head pointer we need to go. + * The subtraction takes care of the sequence number wrapping. + * For example (using 16-bit for brevity): + * min_seqn = 0xFFFD + * mbuf_seqn = 0x0010 + * offset = 0x0010 - 0xFFFD = 0x13 + */ + offset = mbuf->seqn - b->min_seqn; + + /* + * action to take depends on offset. + * offset < buffer->size: the mbuf fits within the current window of + * sequence numbers we can reorder. EXPECTED CASE. + * offset > buffer->size: the mbuf is outside the current window. There + * are a number of cases to consider: + * 1. The packet sequence is just outside the window, then we need + * to see about shifting the head pointer and taking any ready + * to return packets out of the ring. If there was a delayed + * or dropped packet preventing drains from shifting the window + * this case will skip over the dropped packet instead, and any + * packets dequeued here will be returned on the next drain call. + * 2. The packet sequence number is vastly outside our window, taken + * here as having offset greater than twice the buffer size. In + * this case, the packet is probably an old or late packet that + * was previously skipped, so just enqueue the packet for + * immediate return on the next drain call, or else return error. + */ + if (offset < b->order_buf.size) { + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else if (offset < 2 * b->order_buf.size) { + if (rte_reorder_fill_overflow(b, offset - order_buf->size) < + offset - order_buf->size) { + /* Put in handling for enqueue straight to output */ + rte_errno = ENOSPC; + return -1; + } + offset = mbuf->seqn - b->min_seqn; + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else { + /* Put in handling for enqueue straight to output */ + rte_errno = ERANGE; + return -1; + } + return 0; +} + +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs) +{ + unsigned int drain_cnt = 0; + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + /* Try to fetch requested number of mbufs from ready buffer */ + while ((drain_cnt < max_mbufs) && (ready_buf->tail != ready_buf->head)) { + mbufs[drain_cnt++] = ready_buf->entries[ready_buf->tail++]; + if (ready_buf->tail == ready_buf->size) + ready_buf->tail = 0; + } + + /* + * If requested number of buffers not fetched from ready buffer, fetch + * remaining buffers from order buffer + */ + while ((drain_cnt < max_mbufs) && + (order_buf->entries[order_buf->head] != NULL)) { + mbufs[drain_cnt++] = order_buf->entries[order_buf->head]; + order_buf->entries[order_buf->head] = NULL; + b->min_seqn++; + order_buf->head++; + if (order_buf->head == order_buf->size) + order_buf->head = 0; + } + + return drain_cnt; +} diff --git a/lib/librte_reorder/rte_reorder.h b/lib/librte_reorder/rte_reorder.h new file mode 100644 index 0000000..3ec7011 --- /dev/null +++ b/lib/librte_reorder/rte_reorder.h @@ -0,0 +1,184 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_REORDER_H_ +#define _RTE_REORDER_H_ + +/** + * @file + * RTE reorder + * + * Reorder library is a component which is designed to + * provide ordering of out of ordered packets based on + * sequence number present in mbuf. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rte_reorder_buffer; + +/** + * Create a new reorder buffer instance + * + * Allocate memory and initialize a new reorder buffer in that + * memory, returning the reorder buffer pointer to the user + * + * @param name + * The name to be given to the reorder buffer instance. + * @param socket_id + * The NUMA node on which the memory for the reorder buffer + * instance is to be reserved. + * @param size + * Max number of elements that can be stored in the reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - ENOMEM - no appropriate memory area found in which to create memzone + * - EINVAL - invalid parameters + */ +struct rte_reorder_buffer * +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size); + +/** + * Initializes given reorder buffer instance + * + * @param buf + * Pointer to memory area where reorder buffer instance + * should be initialized + * @param bufsize + * Size of the memory area to be used for reorder buffer instance + * initialization + * @param name + * The name to be given to the reorder buffer instance + * @param size + * Number of elements that can be stored in reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - EINVAL - invalid parameters + * - ENOMEM - not enough memory for reorder buffer instance + * initialization + */ +struct rte_reorder_buffer * +rte_reorder_init(void *buf, unsigned int bufsize, + const char *name, unsigned int size); + +/** + * Reset the given reorder buffer instance with initial values. + * + * @param b + * Reorder buffer instance which has to be reset + */ +void rte_reorder_reset(struct rte_reorder_buffer *b); + +/** + * Find an existing reorder buffer instance + * and return a pointer to it. + * + * @param name + * Name of the reorder buffer instacne as passed to rte_reorder_create() + * @return + * Pointer to reorder buffer instance or NULL if object not found with rte_errno + * set appropriately. Possible rte_errno values include: + * - ENOENT - required entry not available to return. + * - E_RTE_NO_TAILQ - no tailq list could be got for the + * reorder instance list + */ +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name); + +/** + * Free reorder buffer instance. + * + * @param b + * reorder buffer instance + * @return + * None + */ +void +rte_reorder_free(struct rte_reorder_buffer *b); + +/** + * Insert given mbuf in reorder buffer in its correct position + * + * The given mbuf is to be reordered relative to other mbufs in the system. + * The mbuf must contain a sequence number which is then used to place + * the buffer in the correct position in the reorder buffer. Reordered + * packets can later be taken from the buffer using the rte_reorder_drain() + * API. + * + * @param b + * Reorder buffer where the mbuf has to be inserted. + * @param mbuf + * mbuf of packet that needs to be inserted in reorder buffer. + * @return + * 0 on success + * -1 on error + * On error case, rte_errno will be set appropriately: + * - ENOSPC - Cannot move existing mbufs from reorder buffer to accomodate ealry mbuf. + * But mbuf can be accomodated by performing drain and then insert. + * - ERANGE - Too early or late mbuf which is vastly out of + * range of expected window should be ingnored without any handling. + */ +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf); + +/** + * Fetch reordered buffers + * + * Returns a set of in-order buffers from the reorder buffer structure. Gaps + * may be present in the sequence numbers of the mbuf if packets have been + * delayed too long before reaching the reorder window, or have been previously + * dropped by the system. + * + * @param b + * Reorder buffer instance from which packets are to be drained + * @param mbufs + * array of mbufs where reordered packets will be inserted from reorder buffer + * @param max_mbufs + * the number of elements in the mbufs array. + * @return + * number of mbuf pointers written to mbufs. 0 <= N < max_mbufs. + */ +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs); + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_REORDER_H_ */ -- 1.8.3.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH 2/3] librte_reorder: New unit test cases added 2015-01-07 16:39 [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library Reshma Pattan @ 2015-01-07 16:39 ` Reshma Pattan 2015-01-07 16:39 ` [dpdk-dev] [PATCH 3/3] librte_reorder: New sample app for reorder library Reshma Pattan ` (4 subsequent siblings) 5 siblings, 0 replies; 44+ messages in thread From: Reshma Pattan @ 2015-01-07 16:39 UTC (permalink / raw) To: dev From: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> --- app/test/Makefile | 2 + app/test/test_reorder.c | 452 ++++++++++++++++++++++++++++++++++++++++++++++++ mk/rte.app.mk | 4 + 3 files changed, 458 insertions(+) create mode 100644 app/test/test_reorder.c diff --git a/app/test/Makefile b/app/test/Makefile index 4311f96..24b27d7 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -124,6 +124,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += test_ivshmem.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor_perf.c +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) += test_reorder.c + SRCS-y += test_devargs.c SRCS-y += virtual_pmd.c SRCS-y += packet_burst_generator.c diff --git a/app/test/test_reorder.c b/app/test/test_reorder.c new file mode 100644 index 0000000..6a673e2 --- /dev/null +++ b/app/test/test_reorder.c @@ -0,0 +1,452 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test.h" +#include "stdio.h" + +#include <unistd.h> +#include <string.h> + +#include <rte_cycles.h> +#include <rte_errno.h> +#include <rte_mbuf.h> +#include <rte_reorder.h> +#include <rte_lcore.h> +#include <rte_malloc.h> + +#include "test.h" + +#define BURST 32 +#define REORDER_BUFFER_SIZE 16384 +#define NUM_MBUFS (2*REORDER_BUFFER_SIZE) +#define REORDER_BUFFER_SIZE_INVALID 2049 +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) + +struct reorder_unittest_params { + struct rte_mempool *p; + struct rte_reorder_buffer *b; +}; + +static struct reorder_unittest_params default_params = { + .p = NULL, + .b = NULL +}; + +static struct reorder_unittest_params *test_params = &default_params; + +static int +test_reorder_create_inval_name(void) +{ + struct rte_reorder_buffer *b = NULL; + char *name = NULL; + + b = rte_reorder_create(name, rte_socket_id(), REORDER_BUFFER_SIZE); + TEST_ASSERT_EQUAL(b, NULL, "No error on create() with invalid name param."); + TEST_ASSERT_EQUAL(rte_errno, EINVAL, + "No error on create() with invalid name param."); + return 0; +} + +static int +test_reorder_create_inval_size(void) +{ + struct rte_reorder_buffer *b = NULL; + + b = rte_reorder_create("PKT", rte_socket_id(), REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT_EQUAL(b, NULL, + "No error on create() with invalid buffer size param."); + TEST_ASSERT_EQUAL(rte_errno, EINVAL, + "No error on create() with invalid buffer size param."); + return 0; +} + +static int +test_reorder_init_null_buffer(void) +{ + struct rte_reorder_buffer *b = NULL; + /* + * The minimum memory area size that should be passed to library is, + * sizeof(struct rte_reorder_buffer) + (2 * size * sizeof(struct rte_mbuf *)); + * Otherwise error will be thrown + */ + unsigned int mzsize = 262336; + b = rte_reorder_init(b, mzsize, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT_EQUAL(b, NULL, "No error on init with NULL buffer."); + TEST_ASSERT_EQUAL(rte_errno, EINVAL, "No error on init with NULL buffer."); + return 0; +} + +static int +test_reorder_init_inval_mzsize(void) +{ + struct rte_reorder_buffer *b = NULL; + unsigned int mzsize = 100; + b = rte_malloc(NULL, mzsize, 0); + b = rte_reorder_init(b, mzsize, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT_EQUAL(b, NULL, "No error on init with invalid mem zone size."); + TEST_ASSERT_EQUAL(rte_errno, ENOMEM, + "No error on init with invalid mem zone size."); + rte_free(b); + return 0; +} + +static int +test_reorder_init_inval_size(void) +{ + struct rte_reorder_buffer *b = NULL; + unsigned int mzsize = 262336; + b = rte_malloc(NULL, mzsize, 0); + b = rte_reorder_init(b, mzsize, "PKT1", REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT_EQUAL(b, NULL, "No error on init with invalid buffer size param."); + TEST_ASSERT_EQUAL(rte_errno, EINVAL, + "No error on init with invalid buffer size param."); + rte_free(b); + return 0; +} + +static int +test_reorder_init_inval_name(void) +{ + struct rte_reorder_buffer *b = NULL; + char *name = NULL; + unsigned int mzsize = 262336; + b = rte_malloc(NULL, mzsize, 0); + b = rte_reorder_init(b, mzsize, name, REORDER_BUFFER_SIZE); + TEST_ASSERT_EQUAL(b, NULL, "No error on init with invalid name."); + TEST_ASSERT_EQUAL(rte_errno, EINVAL, "No error on init with invalid name."); + rte_free(b); + return 0; +} + +static int +test_reorder_buf_instance_existance(void) +{ + struct rte_reorder_buffer *result = NULL; + struct rte_reorder_buffer *b1 = NULL; + struct rte_reorder_buffer *b2 = NULL; + unsigned int mzsize = 262336; + + /* Try to find existing reorder buffer instance */ + result = rte_reorder_find_existing("PKT_RO1"); + TEST_ASSERT_EQUAL(test_params->b, result, + "existing reorder buffer instance not found"); + + /* Try to find non existing reorder buffer instance */ + result = rte_reorder_find_existing("ro_find_non_existing"); + TEST_ASSERT_EQUAL(result, NULL, + "non existing reorder buffer instance found"); + TEST_ASSERT_EQUAL(rte_errno, ENOENT, + "non existing reorder buffer instance found"); + + b1 = rte_malloc(NULL, mzsize, 0); + b2 = rte_reorder_init(b1, mzsize, "PKT_RO1", REORDER_BUFFER_SIZE); + TEST_ASSERT_EQUAL(b2, test_params->b, + "no error on init with existing reorder instance name"); + rte_free(b1); + + b1 = rte_malloc(NULL, mzsize, 0); + b2 = rte_reorder_init(b1, mzsize, "ro_find_nonexisting1", REORDER_BUFFER_SIZE); + TEST_ASSERT_EQUAL(b2, b1, + "error on init with non existing reorder instance name"); + rte_reorder_free(b1); + + return 0; +} + +static int +test_reorder_insert(void) +{ + struct rte_reorder_buffer *b = test_params->b; + struct rte_mempool *p = test_params->p; + rte_reorder_reset(b); + int num_bufs = 4; + struct rte_mbuf *bufs[num_bufs]; + int ret = 0; + if (rte_mempool_get_bulk(p, (void *)bufs, num_bufs) != 0) { + printf("%s: Error getting mbuf from pool\n", __func__); + return -1; + } + + /* too early packet */ + bufs[0]->seqn = (3*REORDER_BUFFER_SIZE); + ret = rte_reorder_insert(b, bufs[0]); + if (ret != -1 || rte_errno != ERANGE) { + printf("%s:%d: No error on insert() of too early packet with seqn:" + " (3*REORDER_BUFFER_SIZE)\n", __func__, __LINE__); + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + return -1; + } + + /* early packet */ + bufs[1]->seqn = (2*REORDER_BUFFER_SIZE)-2; + ret = rte_reorder_insert(b, bufs[1]); + if (ret == -1 || rte_errno == ENOSPC) { + printf("%s:%d: Error on insert of early packet with seqn:" + " (2*REORDER_BUFFER_SIZE)-2\n", __func__ , __LINE__); + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + return -1; + } + + bufs[2]->seqn = (3*REORDER_BUFFER_SIZE)-1; + ret = rte_reorder_insert(b, bufs[2]); + if (ret != -1 && rte_errno != ENOSPC) { + printf("%s:%d: Error on insert of early packet with seqn:" + " (3*REORDER_BUFFER_SIZE)-3\n", __func__ , __LINE__); + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + return -1; + } + + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + return 0; +} + +/* Test case covers draining conditions on order buffer */ +static int +test_reorder_drain_order_buf(void) +{ + + struct rte_reorder_buffer *b = test_params->b; + struct rte_mempool *p = test_params->p; + rte_reorder_reset(b); + struct rte_mbuf *bufs[REORDER_BUFFER_SIZE+10] = {NULL}; + struct rte_mbuf *robufs[REORDER_BUFFER_SIZE+10] = {NULL}; + int cnt; + int i = 0; + + if (rte_mempool_get_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10) != 0) { + printf("%s: Error getting mbuf from pool\n", __func__); + return -1; + } + + /* insert mbufs in order buffer with gaps i.e seqn 0 to 5 and 8,9 inserted */ + for (i = 0; i < 10; ) { + bufs[i]->seqn = i; + rte_reorder_insert(b, bufs[i]); + if (i == 5) + i += 3; + else + i++; + } + + /* should drain till first gap */ + cnt = rte_reorder_drain(b, robufs, BURST); + if (cnt != 6) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + rte_mempool_put_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10); + return -1; + } + + /* now add missing entries and remaining entries till end of order buf */ + bufs[6]->seqn = 6; + bufs[7]->seqn = 7; + rte_reorder_insert(b, bufs[6]); + rte_reorder_insert(b, bufs[7]); + for (i = 10; i < REORDER_BUFFER_SIZE; i++) { + bufs[i]->seqn = i; + rte_reorder_insert(b, bufs[i]); + } + + /* + * hence gaps are filled now, drain should return entries + * from last gap to till end + */ + cnt = rte_reorder_drain(b, robufs, REORDER_BUFFER_SIZE+1); + if (cnt != REORDER_BUFFER_SIZE-6) { + printf("%s:%d: number of expected packets not drained\n", + __func__, __LINE__); + rte_mempool_put_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10); + return -1; + } + rte_mempool_put_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10); + return 0; +} + +/* Test case covers draining conditions on ready buffer */ +static int +test_reorder_drain_ready_buf(void) +{ + + struct rte_reorder_buffer *b = test_params->b; + struct rte_mempool *p = test_params->p; + rte_reorder_reset(b); + + struct rte_mbuf *bufs[REORDER_BUFFER_SIZE+10] = {NULL}; + struct rte_mbuf *robufs[REORDER_BUFFER_SIZE+10] = {NULL}; + int cnt = 0; + int i; + int ret = 0; + + if (rte_mempool_get_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10) != 0) { + printf("%s: Error getting mbuf from pool\n", __func__); + return -1; + } + + /*1: draining of ready buffer with tail == 0 */ + for (i = 0; i < REORDER_BUFFER_SIZE; i++) { + bufs[i]->seqn = i; + ret = rte_reorder_insert(b, bufs[i]); + if (ret) { + printf("%s: Error on insert of bufs[%u]\n", + __func__, i); + rte_mempool_put_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10); + return -1; + } + } + + /* + * insert early packet, this moves entries from order buffer + * to ready buffer + */ + bufs[REORDER_BUFFER_SIZE]->seqn = (2*REORDER_BUFFER_SIZE)-1; + rte_reorder_insert(b, bufs[REORDER_BUFFER_SIZE]); + + /* + * since ready buffer is full, could drain REORDER_BUFFER_SIZE + * entries from ready buffer + */ + cnt = rte_reorder_drain(b, robufs, REORDER_BUFFER_SIZE); + if (cnt != REORDER_BUFFER_SIZE) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + rte_mempool_put_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10); + return -1; + } + + /*2: draining of ready buffer with tail != 0 */ + + /* insert mbufs with seqn:REORDER_BUFFER_SIZE to 2*REORDER_BUFFER_SIZE */ + for (i = 0; i < REORDER_BUFFER_SIZE; i++) { + bufs[i]->seqn = REORDER_BUFFER_SIZE+1+i; + ret = rte_reorder_insert(b, bufs[i]); + if (ret) { + printf("%s: Error on insert of bufs[%u]\n", + __func__, i); + rte_mempool_put_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10); + return -1; + } + } + + /* + * insert early packet, this will move entries + * from order buffer to ready buffer + */ + bufs[REORDER_BUFFER_SIZE]->seqn = (3*REORDER_BUFFER_SIZE)-5; + rte_reorder_insert(b, bufs[REORDER_BUFFER_SIZE]); + + /* + * drain only 3 mbufs, this will drain ready buffer + * and advances tail by 3 + */ + cnt = rte_reorder_drain(b, robufs, 3); + if (cnt != 3) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + rte_mempool_put_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10); + return -1; + } + + /* insert early packet */ + bufs[REORDER_BUFFER_SIZE]->seqn = (3*REORDER_BUFFER_SIZE)+2; + rte_reorder_insert(b, bufs[REORDER_BUFFER_SIZE]); + + /* + * perform drain on ready buffer with advanced tail, + * validates if(tail == size) in drain + */ + rte_reorder_drain(b, robufs, REORDER_BUFFER_SIZE); + rte_mempool_put_bulk(p, (void *)bufs, REORDER_BUFFER_SIZE+10); + return 0; +} + +static int +test_setup(void) +{ + /* reorder buffer instance creation */ + if (test_params->b == NULL) { + test_params->b = rte_reorder_create("PKT_RO1", rte_socket_id(), + REORDER_BUFFER_SIZE); + if (test_params->b == NULL) { + printf("%s: Error creating reorder buffer instance b\n", + __func__); + return -1; + } + } else + rte_reorder_reset(test_params->b); + + /* mempool creation */ + if (test_params->p == NULL) { + test_params->p = rte_mempool_create("RO_MBUF_POOL", NUM_MBUFS, + MBUF_SIZE, BURST, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->p == NULL) { + printf("%s: Error creating mempool\n", __func__); + return -1; + } + } + return 0; +} + +static struct unit_test_suite reorder_test_suite = { + + .setup = test_setup, + .suite_name = "Reorder Unit Test Suite", + .unit_test_cases = { + TEST_CASE(test_reorder_create_inval_name), + TEST_CASE(test_reorder_create_inval_size), + TEST_CASE(test_reorder_init_null_buffer), + TEST_CASE(test_reorder_init_inval_mzsize), + TEST_CASE(test_reorder_init_inval_size), + TEST_CASE(test_reorder_init_inval_name), + TEST_CASE(test_reorder_buf_instance_existance), + TEST_CASE(test_reorder_insert), + TEST_CASE(test_reorder_drain_order_buf), + TEST_CASE(test_reorder_drain_ready_buf), + TEST_CASES_END() + } +}; + +static int +test_reorder(void) +{ + return unit_test_suite_runner(&reorder_test_suite); +} + +static struct test_command reorder_cmd = { + .command = "reorder_autotest", + .callback = test_reorder, +}; +REGISTER_TEST_COMMAND(reorder_cmd); diff --git a/mk/rte.app.mk b/mk/rte.app.mk index e1a0dbf..2a08acb 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -67,6 +67,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_DISTRIBUTOR),y) LDLIBS += -lrte_distributor endif +ifeq ($(CONFIG_RTE_LIBRTE_REORDER),y) +LDLIBS += -lrte_reorder +endif + ifeq ($(CONFIG_RTE_LIBRTE_KNI),y) ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) LDLIBS += -lrte_kni -- 1.8.3.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH 3/3] librte_reorder: New sample app for reorder library 2015-01-07 16:39 [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library Reshma Pattan 2015-01-07 16:39 ` [dpdk-dev] [PATCH 2/3] librte_reorder: New unit test cases added Reshma Pattan @ 2015-01-07 16:39 ` Reshma Pattan 2015-01-07 17:45 ` [dpdk-dev] [PATCH 1/3] librte_reorder: New " Neil Horman ` (3 subsequent siblings) 5 siblings, 0 replies; 44+ messages in thread From: Reshma Pattan @ 2015-01-07 16:39 UTC (permalink / raw) To: dev From: Reshma Pattan <reshma.pattan@intel.com> *Sample application consists of RX, Worker and TX threads. *RX thread marks the seqn field of mbufs upon receiving mbufs from driver. Marked mbufs will be enqueued in multi consumer ring. *Worker threads will dequeue mbufs from multi consumer ring and performs XOR on input port value of mbufs. Operated mbufs will be enqueued to another ring for TX. *TX thread will dequeue the mbufs from ring and hand it over to reorder lib for reordering before sending them out. Signed-of-by: Reshma Pattan <reshma.pattan@intel.com> --- examples/packet_ordering/Makefile | 50 +++ examples/packet_ordering/main.c | 637 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 687 insertions(+) create mode 100644 examples/packet_ordering/Makefile create mode 100644 examples/packet_ordering/main.c diff --git a/examples/packet_ordering/Makefile b/examples/packet_ordering/Makefile new file mode 100644 index 0000000..44bd2e1 --- /dev/null +++ b/examples/packet_ordering/Makefile @@ -0,0 +1,50 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overriden by command line or environment +RTE_TARGET ?= x86_64-ivshmem-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +# binary name +APP = packet_ordering + +# all source are stored in SRCS-y +SRCS-y := main.c + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/packet_ordering/main.c b/examples/packet_ordering/main.c new file mode 100644 index 0000000..8b65275 --- /dev/null +++ b/examples/packet_ordering/main.c @@ -0,0 +1,637 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <signal.h> +#include <getopt.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_errno.h> +#include <rte_ethdev.h> +#include <rte_lcore.h> +#include <rte_mbuf.h> +#include <rte_mempool.h> +#include <rte_ring.h> +#include <rte_reorder.h> + +#define RX_DESC_PER_QUEUE 128 +#define TX_DESC_PER_QUEUE 512 + +#define MAX_PKTS_BURST 32 +#define REORDER_BUFFER_SIZE 8192 +#define MBUF_PER_POOL 65535 +#define MBUF_SIZE (1600 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_POOL_CACHE_SIZE 250 + +#define RING_SIZE 16384 + +/* uncommnet below line to enable debug logs */ +/* #define DEBUG */ + +#ifdef DEBUG +#define LOG_LEVEL RTE_LOG_DEBUG +#define LOG_DEBUG(log_type, fmt, args...) RTE_LOG(DEBUG, log_type, fmt, ##args) +#else +#define LOG_LEVEL RTE_LOG_INFO +#define LOG_DEBUG(log_type, fmt, args...) do {} while (0) +#endif + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDERAPP RTE_LOGTYPE_USER1 + +unsigned int portmask; +volatile uint8_t quit_signal; + +static struct rte_mempool *mbuf_pool; + +static struct rte_eth_conf port_conf_default; + +struct worker_thread_args { + struct rte_ring *ring_in; + struct rte_ring *ring_out; +}; + +struct output_buffer { + unsigned count; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; +}; + +volatile struct app_stats { + struct { + uint64_t rx_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } rx __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } wkr __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + /* Too early pkts transmitted directly w/o reordering */ + uint64_t early_pkts_txtd_woro; + /* Too early pkts failed from direct transmit */ + uint64_t early_pkts_tx_failed_woro; + uint64_t ro_tx_pkts; + uint64_t ro_tx_failed_pkts; + } tx __rte_cache_aligned; +} app_stats; + +/** + * Get the last enabled lcore ID + * + * @return + * The last enabled lcore ID. + */ +static unsigned int +get_last_lcore_id(void) +{ + int i; + + for (i = RTE_MAX_LCORE - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return 0; +} + +/** + * Get the previous enabled lcore ID + * @param id + * The current lcore ID + * @return + * The previous enabled lcore ID or the current lcore + * ID if it is the first available core. + */ +static unsigned int +get_previous_lcore_id(unsigned int id) +{ + int i; + + for (i = id - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return id; +} + +static inline void +pktmbuf_free_bulk(struct rte_mbuf *mbuf_table[], unsigned n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + rte_pktmbuf_free(mbuf_table[i]); +} + +/* display usage */ +static void +print_usage(const char *prgname) +{ + printf("%s [EAL options] -- -p PORTMASK\n" + " -p PORTMASK: hexadecimal bitmask of ports to configure\n", + prgname); +} + +static int +parse_portmask(const char *portmask) +{ + unsigned long pm; + char *end = NULL; + + /* parse hexadecimal string */ + pm = strtoul(portmask, &end, 16); + if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) + return -1; + + if (pm == 0) + return -1; + + return pm; +} + +/* Parse the argument given in the command line of the application */ +static int +parse_args(int argc, char **argv) +{ + int opt; + int option_index; + char **argvopt; + char *prgname = argv[0]; + static struct option lgopts[] = { + {NULL, 0, 0, 0} + }; + + argvopt = argv; + + while ((opt = getopt_long(argc, argvopt, "p:", + lgopts, &option_index)) != EOF) { + switch (opt) { + /* portmask */ + case 'p': + portmask = parse_portmask(optarg); + if (portmask == 0) { + printf("invalid portmask\n"); + print_usage(prgname); + return -1; + } + break; + default: + print_usage(prgname); + return -1; + } + } + if (optind <= 1) { + print_usage(prgname); + return -1; + } + + argv[optind-1] = prgname; + optind = 0; /* reset getopt lib */ + return 0; +} + +static inline int +configure_eth_port(uint8_t port_id) +{ + const uint16_t rxRings = 1, txRings = 1; + const uint8_t nb_ports = rte_eth_dev_count(); + int ret; + uint16_t q; + + if (port_id > nb_ports) + return -1; + + ret = rte_eth_dev_configure(port_id, rxRings, txRings , &port_conf_default); + if (ret != 0) + return ret; + + for (q = 0; q < rxRings; q++) { + ret = rte_eth_rx_queue_setup(port_id, q, RX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL, + mbuf_pool); + if (ret < 0) + return ret; + } + + for (q = 0; q < txRings; q++) { + ret = rte_eth_tx_queue_setup(port_id, q, TX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL); + if (ret < 0) + return ret; + } + + ret = rte_eth_dev_start(port_id); + if (ret < 0) + return ret; + + struct ether_addr addr; + rte_eth_macaddr_get(port_id, &addr); + printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 + " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", + (unsigned)port_id, + addr.addr_bytes[0], addr.addr_bytes[1], + addr.addr_bytes[2], addr.addr_bytes[3], + addr.addr_bytes[4], addr.addr_bytes[5]); + + rte_eth_promiscuous_enable(port_id); + + return 0; +} + +static void +print_stats(void) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + unsigned i; + struct rte_eth_stats eth_stats; + + printf("\nRX thread stats:\n"); + printf(" - Pkts rxd: %"PRIu64"\n", + app_stats.rx.rx_pkts); + printf(" - Pkts enqd to workers ring: %"PRIu64"\n", + app_stats.rx.enqueue_pkts); + + printf("\nWorker thread stats:\n"); + printf(" - Pkts deqd from workers ring: %"PRIu64"\n", + app_stats.wkr.dequeue_pkts); + printf(" - Pkts enqd to tx ring: %"PRIu64"\n", + app_stats.wkr.enqueue_pkts); + printf(" - Pkts enq to tx failed: %"PRIu64"\n", + app_stats.wkr.enqueue_failed_pkts); + + printf("\nTX stats:\n"); + printf(" - Pkts deqd from tx ring: %"PRIu64"\n", + app_stats.tx.dequeue_pkts); + printf(" - Ro Pkts transmitted: %"PRIu64"\n", + app_stats.tx.ro_tx_pkts); + printf(" - Ro Pkts tx failed: %"PRIu64"\n", + app_stats.tx.ro_tx_failed_pkts); + printf(" - Pkts transmitted w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_txtd_woro); + printf(" - Pkts tx failed w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_tx_failed_woro); + + for (i = 0; i < nb_ports; i++) { + rte_eth_stats_get(i, ð_stats); + printf("\nPort %u stats:\n", i); + printf(" - Pkts in: %"PRIu64"\n", eth_stats.ipackets); + printf(" - Pkts out: %"PRIu64"\n", eth_stats.opackets); + printf(" - In Errs: %"PRIu64"\n", eth_stats.ierrors); + printf(" - Out Errs: %"PRIu64"\n", eth_stats.oerrors); + printf(" - Mbuf Errs: %"PRIu64"\n", eth_stats.rx_nombuf); + } +} + +static void +int_handler(int sig_num) +{ + printf("Exiting on signal %d\n", sig_num); + quit_signal = 1; +} + +/** + * This thread receives mbufs from the port and affects them an internal + * sequence number to keep track of their order of arrival through an + * mbuf structure. + * The mbufs are then passed to the worker threads via the rx_to_workers + * ring. + */ +static int +rx_thread(struct rte_ring *ring_out) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint32_t seqn = 0; + uint16_t i, ret = 0; + uint16_t nb_rx_pkts; + uint8_t port_id; + struct rte_mbuf *pkts[MAX_PKTS_BURST]; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + + while (!quit_signal) { + + for (port_id = 0; port_id < nb_ports; port_id++) { + if ((portmask & (1 << port_id)) != 0) { + + /* receive packets */ + nb_rx_pkts = rte_eth_rx_burst(port_id, 0, + pkts, MAX_PKTS_BURST); + if (nb_rx_pkts == 0) { + LOG_DEBUG(REORDERAPP, + "%s():Received zero packets\n", __func__); + continue; + } + app_stats.rx.rx_pkts += nb_rx_pkts; + + /* mark sequence number */ + for (i = 0; i < nb_rx_pkts; ) + pkts[i++]->seqn = seqn++; + + /* enqueue to rx_to_workers ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *) pkts, + nb_rx_pkts); + app_stats.rx.enqueue_pkts += ret; + if (unlikely(ret < nb_rx_pkts)) { + app_stats.rx.enqueue_failed_pkts += + (nb_rx_pkts-ret); + pktmbuf_free_bulk(&pkts[ret], nb_rx_pkts - ret); + } + } + } + } + return 0; +} + +/** + * This thread takes bursts of packets from the rx_to_workers ring and + * Changes the input port value to output port value. And feds it to + * workers_to_tx + */ +static int +worker_thread(void *args_ptr) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint16_t i, ret = 0; + uint16_t burst_size = 0; + struct worker_thread_args *args; + struct rte_mbuf *burst_buffer[MAX_PKTS_BURST] = { NULL }; + struct rte_ring *ring_in, *ring_out; + + args = (struct worker_thread_args *) args_ptr; + ring_in = args->ring_in; + ring_out = args->ring_out; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + const unsigned xor_val = (nb_ports > 1); + while (!quit_signal) { + + /* dequeue the mbufs from rx_to_workers ring */ + burst_size = rte_ring_dequeue_burst(ring_in, + (void *)burst_buffer, MAX_PKTS_BURST); + if (unlikely(burst_size == 0)) + continue; + + __sync_fetch_and_add(&app_stats.wkr.dequeue_pkts, burst_size); + + /* just do some operation on mbuf */ + for (i = 0; i < burst_size;) + burst_buffer[i++]->port ^= xor_val; + + /* enqueue the modified mbufs to workers_to_tx ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *)burst_buffer, burst_size); + __sync_fetch_and_add(&app_stats.wkr.enqueue_pkts, ret); + if (unlikely(ret < burst_size)) { + /* Return the mbufs to their respective pool, dropping packets */ + __sync_fetch_and_add(&app_stats.wkr.enqueue_failed_pkts, + (int)burst_size - ret); + pktmbuf_free_bulk(&burst_buffer[ret], burst_size - ret); + } + } + return 0; +} + +static inline void +flush_one_port(struct output_buffer *outbuf, uint8_t outp) +{ + unsigned nb_tx = rte_eth_tx_burst(outp, 0, outbuf->mbufs, + outbuf->count); + app_stats.tx.ro_tx_pkts += nb_tx; + + if (unlikely(nb_tx < outbuf->count)) { + /* free the mbufs which failed from transmit */ + app_stats.tx.ro_tx_failed_pkts += (outbuf->count - nb_tx); + LOG_DEBUG(REORDERAPP, "%s:Packet loss with tx_burst\n", __func__); + pktmbuf_free_bulk(&outbuf->mbufs[nb_tx], outbuf->count - nb_tx); + } + outbuf->count = 0; +} + +/** + * Dequeue mbufs from the workers_to_tx ring and reorder them before + * transmitting. + */ +static int +send_thread(struct rte_ring *ring_in) +{ + int ret; + unsigned int i, dret; + uint16_t nb_dq_mbufs; + uint8_t outp; + static struct output_buffer tx_buffers[RTE_MAX_ETHPORTS]; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; + struct rte_mbuf *rombufs[MAX_PKTS_BURST] = {NULL}; + struct rte_reorder_buffer *buffer; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + buffer = rte_reorder_create("PKT_RO", rte_socket_id(), REORDER_BUFFER_SIZE); + while (!quit_signal) { + + /* deque the mbufs from workers_to_tx ring */ + nb_dq_mbufs = rte_ring_dequeue_burst(ring_in, + (void *)mbufs, MAX_PKTS_BURST); + + if (unlikely(nb_dq_mbufs == 0)) + continue; + + app_stats.tx.dequeue_pkts += nb_dq_mbufs; + + for (i = 0; i < nb_dq_mbufs; i++) { + /* send dequeued mbufs for reordering */ + ret = rte_reorder_insert(buffer, mbufs[i]); + + if (ret == -1 && rte_errno == ERANGE) { + /* Too early pkts should be transmitted out directly */ + LOG_DEBUG(REORDERAPP, "%s():Cannot reorder early packet" + "direct enqueuing to TX\n", __func__); + outp = mbufs[i]->port; + if ((portmask & (1 << outp)) == 0) { + rte_pktmbuf_free(mbufs[i]); + continue; + } + if (rte_eth_tx_burst(outp, 0, (void *)mbufs[i], 1) != 1) { + rte_pktmbuf_free(mbufs[i]); + app_stats.tx.early_pkts_tx_failed_woro++; + } else + app_stats.tx.early_pkts_txtd_woro++; + } else if (ret == -1 && rte_errno == ENOSPC) { + /** + * Early pkts just outside of window should be dropped + */ + rte_pktmbuf_free(mbufs[i]); + } + } + + /* + * drain MAX_PKTS_BURST of reordered + * mbufs for transmit + */ + dret = rte_reorder_drain(buffer, rombufs, MAX_PKTS_BURST); + for (i = 0; i < dret; i++) { + + struct output_buffer *outbuf; + uint8_t outp1; + + outp1 = rombufs[i]->port; + /* skip ports that are not enabled */ + if ((portmask & (1 << outp1)) == 0) { + rte_pktmbuf_free(rombufs[i]); + continue; + } + + outbuf = &tx_buffers[outp1]; + outbuf->mbufs[outbuf->count++] = rombufs[i]; + if (outbuf->count == MAX_PKTS_BURST) + flush_one_port(outbuf, outp1); + } + } + return 0; +} + +int +main(int argc, char **argv) +{ + int ret; + unsigned nb_ports; + unsigned int lcore_id, last_lcore_id, master_lcore_id; + uint8_t port_id; + uint8_t nb_ports_available; + struct worker_thread_args worker_args = {NULL, NULL}; + struct rte_ring *rx_to_workers; + struct rte_ring *workers_to_tx; + + /* catch ctrl-c so we can print on exit */ + signal(SIGINT, int_handler); + + /* Initialize EAL */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + return -1; + + argc -= ret; + argv += ret; + + /* Parse the application specific arguments */ + ret = parse_args(argc, argv); + if (ret < 0) + return -1; + + /* Check if we have enought cores */ + if (rte_lcore_count() < 3) + rte_exit(EXIT_FAILURE, "Error, This application needs at " + "least 3 logical cores to run:\n" + "1 lcore for packet RX\n" + "1 lcore for packet TX\n" + "and at least 1 lcore for worker threads\n"); + + nb_ports = rte_eth_dev_count(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "Error: no ethernet ports detected\n"); + if (nb_ports != 1 && (nb_ports & 1)) + rte_exit(EXIT_FAILURE, "Error: number of ports must be even, except " + "when using a single port\n"); + + mbuf_pool = rte_mempool_create("mbuf_pool", MBUF_PER_POOL, MBUF_SIZE, + MBUF_POOL_CACHE_SIZE, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (mbuf_pool == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + nb_ports_available = nb_ports; + + /* initialize all ports */ + for (port_id = 0; port_id < nb_ports; port_id++) { + /* skip ports that are not enabled */ + if ((portmask & (1 << port_id)) == 0) { + printf("\nSkipping disabled port %d\n", port_id); + nb_ports_available--; + continue; + } + /* init port */ + printf("Initializing port %u... done\n", (unsigned) port_id); + + if (configure_eth_port(port_id) != 0) + rte_exit(EXIT_FAILURE, "Cannot initialize port %"PRIu8"\n", + port_id); + } + + if (!nb_ports_available) { + rte_exit(EXIT_FAILURE, + "All available ports are disabled. Please set portmask.\n"); + } + + /* Create rings for inter core communication */ + rx_to_workers = rte_ring_create("rx_to_workers", RING_SIZE, rte_socket_id(), + RING_F_SP_ENQ); + if (rx_to_workers == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + workers_to_tx = rte_ring_create("workers_to_tx", RING_SIZE, rte_socket_id(), + RING_F_SC_DEQ); + if (workers_to_tx == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + last_lcore_id = get_last_lcore_id(); + master_lcore_id = rte_get_master_lcore(); + + worker_args.ring_in = rx_to_workers; + worker_args.ring_out = workers_to_tx; + + /* Start worker_thread() on all the available slave cores but the last 1 */ + for (lcore_id = 0; lcore_id <= get_previous_lcore_id(last_lcore_id); lcore_id++) + if (rte_lcore_is_enabled(lcore_id) && lcore_id != master_lcore_id) + rte_eal_remote_launch(worker_thread, (void *)&worker_args, + lcore_id); + + /* Start send_thread() on the last slave core */ + rte_eal_remote_launch((lcore_function_t *)send_thread, workers_to_tx, + last_lcore_id); + + /* Start rx_thread() on the master core */ + rx_thread(rx_to_workers); + + RTE_LCORE_FOREACH_SLAVE(lcore_id) { + if (rte_eal_wait_lcore(lcore_id) < 0) + return -1; + } + + print_stats(); + return 0; +} -- 1.8.3.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library 2015-01-07 16:39 [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library Reshma Pattan 2015-01-07 16:39 ` [dpdk-dev] [PATCH 2/3] librte_reorder: New unit test cases added Reshma Pattan 2015-01-07 16:39 ` [dpdk-dev] [PATCH 3/3] librte_reorder: New sample app for reorder library Reshma Pattan @ 2015-01-07 17:45 ` Neil Horman 2015-01-08 14:41 ` Pattan, Reshma 2015-01-07 21:09 ` Richard Sanger ` (2 subsequent siblings) 5 siblings, 1 reply; 44+ messages in thread From: Neil Horman @ 2015-01-07 17:45 UTC (permalink / raw) To: Reshma Pattan; +Cc: dev On Wed, Jan 07, 2015 at 04:39:11PM +0000, Reshma Pattan wrote: > From: Reshma Pattan <reshma.pattan@intel.com> > > 1)New library to provide reordering of out of ordered > mbufs based on sequence number of mbuf. Library uses reorder buffer structure > which in tern uses two circular buffers called ready and order buffers. > *rte_reorder_create API creates instance of reorder buffer. > *rte_reorder_init API initializes given reorder buffer instance. > *rte_reorder_reset API resets given reorder buffer instance. > *rte_reorder_insert API inserts the mbuf into order circular buffer. > *rte_reorder_fill_overflow moves mbufs from order buffer to ready buffer > to accomodate early packets in order buffer. > *rte_reorder_drain API provides draining facility to fetch out > reordered mbufs from order and ready buffers. > > Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> > Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> > --- > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > lib/Makefile | 1 + > lib/librte_eal/common/include/rte_tailq_elem.h | 2 + > lib/librte_mbuf/rte_mbuf.h | 3 + > lib/librte_reorder/Makefile | 50 +++ > lib/librte_reorder/rte_reorder.c | 464 +++++++++++++++++++++++++ > lib/librte_reorder/rte_reorder.h | 184 ++++++++++ > 8 files changed, 714 insertions(+) > create mode 100644 lib/librte_reorder/Makefile > create mode 100644 lib/librte_reorder/rte_reorder.c > create mode 100644 lib/librte_reorder/rte_reorder.h > + > +int > +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf) > +{ > + uint32_t offset, position; > + struct cir_buffer *order_buf = &b->order_buf; > + > + /* > + * calculate the offset from the head pointer we need to go. > + * The subtraction takes care of the sequence number wrapping. > + * For example (using 16-bit for brevity): > + * min_seqn = 0xFFFD > + * mbuf_seqn = 0x0010 > + * offset = 0x0010 - 0xFFFD = 0x13 > + */ > + offset = mbuf->seqn - b->min_seqn; > + > + /* > + * action to take depends on offset. > + * offset < buffer->size: the mbuf fits within the current window of > + * sequence numbers we can reorder. EXPECTED CASE. > + * offset > buffer->size: the mbuf is outside the current window. There > + * are a number of cases to consider: > + * 1. The packet sequence is just outside the window, then we need > + * to see about shifting the head pointer and taking any ready > + * to return packets out of the ring. If there was a delayed > + * or dropped packet preventing drains from shifting the window > + * this case will skip over the dropped packet instead, and any > + * packets dequeued here will be returned on the next drain call. > + * 2. The packet sequence number is vastly outside our window, taken > + * here as having offset greater than twice the buffer size. In > + * this case, the packet is probably an old or late packet that > + * was previously skipped, so just enqueue the packet for > + * immediate return on the next drain call, or else return error. > + */ > + if (offset < b->order_buf.size) { > + position = (order_buf->head + offset) & order_buf->mask; > + order_buf->entries[position] = mbuf; > + } else if (offset < 2 * b->order_buf.size) { > + if (rte_reorder_fill_overflow(b, offset - order_buf->size) < > + offset - order_buf->size) { > + /* Put in handling for enqueue straight to output */ > + rte_errno = ENOSPC; > + return -1; > + } > + offset = mbuf->seqn - b->min_seqn; > + position = (order_buf->head + offset) & order_buf->mask; > + order_buf->entries[position] = mbuf; > + } else { > + /* Put in handling for enqueue straight to output */ > + rte_errno = ERANGE; > + return -1; > + } How does this work if you get two packets with the same sequence number? That situation seems like it would happen frequently with your example app, and from my read of the above, you just wind up overwriting the same pointer in ther entries array here, which leads to silent packet loss. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library 2015-01-07 17:45 ` [dpdk-dev] [PATCH 1/3] librte_reorder: New " Neil Horman @ 2015-01-08 14:41 ` Pattan, Reshma 0 siblings, 0 replies; 44+ messages in thread From: Pattan, Reshma @ 2015-01-08 14:41 UTC (permalink / raw) To: Neil Horman; +Cc: dev > -----Original Message----- > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Wednesday, January 7, 2015 5:45 PM > To: Pattan, Reshma > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library > > On Wed, Jan 07, 2015 at 04:39:11PM +0000, Reshma Pattan wrote: > > From: Reshma Pattan <reshma.pattan@intel.com> > > > > 1)New library to provide reordering of out of ordered > > mbufs based on sequence number of mbuf. Library uses reorder buffer > structure > > which in tern uses two circular buffers called ready and order buffers. > > *rte_reorder_create API creates instance of reorder buffer. > > *rte_reorder_init API initializes given reorder buffer instance. > > *rte_reorder_reset API resets given reorder buffer instance. > > *rte_reorder_insert API inserts the mbuf into order circular buffer. > > *rte_reorder_fill_overflow moves mbufs from order buffer to ready > buffer > > to accomodate early packets in order buffer. > > *rte_reorder_drain API provides draining facility to fetch out > > reordered mbufs from order and ready buffers. > > > > Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> > > Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> > > --- > > config/common_bsdapp | 5 + > > config/common_linuxapp | 5 + > > lib/Makefile | 1 + > > lib/librte_eal/common/include/rte_tailq_elem.h | 2 + > > lib/librte_mbuf/rte_mbuf.h | 3 + > > lib/librte_reorder/Makefile | 50 +++ > > lib/librte_reorder/rte_reorder.c | 464 +++++++++++++++++++++++++ > > lib/librte_reorder/rte_reorder.h | 184 ++++++++++ > > 8 files changed, 714 insertions(+) > > create mode 100644 lib/librte_reorder/Makefile create mode 100644 > > lib/librte_reorder/rte_reorder.c create mode 100644 > > lib/librte_reorder/rte_reorder.h > > + > > +int > > +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf > > +*mbuf) { > > + uint32_t offset, position; > > + struct cir_buffer *order_buf = &b->order_buf; > > + > > + /* > > + * calculate the offset from the head pointer we need to go. > > + * The subtraction takes care of the sequence number wrapping. > > + * For example (using 16-bit for brevity): > > + * min_seqn = 0xFFFD > > + * mbuf_seqn = 0x0010 > > + * offset = 0x0010 - 0xFFFD = 0x13 > > + */ > > + offset = mbuf->seqn - b->min_seqn; > > + > > + /* > > + * action to take depends on offset. > > + * offset < buffer->size: the mbuf fits within the current window of > > + * sequence numbers we can reorder. EXPECTED CASE. > > + * offset > buffer->size: the mbuf is outside the current window. There > > + * are a number of cases to consider: > > + * 1. The packet sequence is just outside the window, then we need > > + * to see about shifting the head pointer and taking any ready > > + * to return packets out of the ring. If there was a delayed > > + * or dropped packet preventing drains from shifting the window > > + * this case will skip over the dropped packet instead, and any > > + * packets dequeued here will be returned on the next drain call. > > + * 2. The packet sequence number is vastly outside our window, taken > > + * here as having offset greater than twice the buffer size. In > > + * this case, the packet is probably an old or late packet that > > + * was previously skipped, so just enqueue the packet for > > + * immediate return on the next drain call, or else return error. > > + */ > > + if (offset < b->order_buf.size) { > > + position = (order_buf->head + offset) & order_buf->mask; > > + order_buf->entries[position] = mbuf; > > + } else if (offset < 2 * b->order_buf.size) { > > + if (rte_reorder_fill_overflow(b, offset - order_buf->size) < > > + offset - order_buf->size) { > > + /* Put in handling for enqueue straight to output */ > > + rte_errno = ENOSPC; > > + return -1; > > + } > > + offset = mbuf->seqn - b->min_seqn; > > + position = (order_buf->head + offset) & order_buf->mask; > > + order_buf->entries[position] = mbuf; > > + } else { > > + /* Put in handling for enqueue straight to output */ > > + rte_errno = ERANGE; > > + return -1; > > + } > How does this work if you get two packets with the same sequence number? > That situation seems like it would happen frequently with your example app, and > from my read of the above, you just wind up overwriting the same pointer in > ther entries array here, which leads to silent packet loss. Hi Neil, Sequence numbers are assigned globally by a single core , and not per port. So it is impossible to get the packets with same sequence number. Getting packets with same sequence number should happen only when sequence number wraps around in same window of earlier same sequence number packet and which is not drained, but this is unlikely as there will be sufficient wrap around times . Thanks, Reshma ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library 2015-01-07 16:39 [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library Reshma Pattan ` (2 preceding siblings ...) 2015-01-07 17:45 ` [dpdk-dev] [PATCH 1/3] librte_reorder: New " Neil Horman @ 2015-01-07 21:09 ` Richard Sanger 2015-01-08 16:28 ` Pattan, Reshma 2015-01-20 8:00 ` Thomas Monjalon 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 0/4] New Reorder Library Sergio Gonzalez Monroy 5 siblings, 1 reply; 44+ messages in thread From: Richard Sanger @ 2015-01-07 21:09 UTC (permalink / raw) To: Reshma Pattan; +Cc: dev On 8 January 2015 at 05:39, Reshma Pattan <reshma.pattan@intel.com> wrote: > From: Reshma Pattan <reshma.pattan@intel.com> > > 1)New library to provide reordering of out of ordered > mbufs based on sequence number of mbuf. Library uses reorder buffer structure > which in tern uses two circular buffers called ready and order buffers. > *rte_reorder_create API creates instance of reorder buffer. > *rte_reorder_init API initializes given reorder buffer instance. > *rte_reorder_reset API resets given reorder buffer instance. > *rte_reorder_insert API inserts the mbuf into order circular buffer. > *rte_reorder_fill_overflow moves mbufs from order buffer to ready buffer > to accomodate early packets in order buffer. > *rte_reorder_drain API provides draining facility to fetch out > reordered mbufs from order and ready buffers. > > Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> > Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> > --- > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > lib/Makefile | 1 + > lib/librte_eal/common/include/rte_tailq_elem.h | 2 + > lib/librte_mbuf/rte_mbuf.h | 3 + > lib/librte_reorder/Makefile | 50 +++ > lib/librte_reorder/rte_reorder.c | 464 +++++++++++++++++++++++++ > lib/librte_reorder/rte_reorder.h | 184 ++++++++++ > 8 files changed, 714 insertions(+) > create mode 100644 lib/librte_reorder/Makefile > create mode 100644 lib/librte_reorder/rte_reorder.c > create mode 100644 lib/librte_reorder/rte_reorder.h > > diff --git a/config/common_bsdapp b/config/common_bsdapp > index 9177db1..e3e0e94 100644 > --- a/config/common_bsdapp > +++ b/config/common_bsdapp > @@ -334,6 +334,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 > CONFIG_RTE_LIBRTE_DISTRIBUTOR=y > > # > +# Compile the reorder library > +# > +CONFIG_RTE_LIBRTE_REORDER=y > + > +# > # Compile librte_port > # > CONFIG_RTE_LIBRTE_PORT=y > diff --git a/config/common_linuxapp b/config/common_linuxapp > index 2f9643b..b5ec730 100644 > --- a/config/common_linuxapp > +++ b/config/common_linuxapp > @@ -342,6 +342,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 > CONFIG_RTE_LIBRTE_DISTRIBUTOR=y > > # > +# Compile the reorder library > +# > +CONFIG_RTE_LIBRTE_REORDER=y > + > +# > # Compile librte_port > # > CONFIG_RTE_LIBRTE_PORT=y > diff --git a/lib/Makefile b/lib/Makefile > index 0ffc982..5919d32 100644 > --- a/lib/Makefile > +++ b/lib/Makefile > @@ -65,6 +65,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += librte_distributor > DIRS-$(CONFIG_RTE_LIBRTE_PORT) += librte_port > DIRS-$(CONFIG_RTE_LIBRTE_TABLE) += librte_table > DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += librte_pipeline > +DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += librte_reorder > > ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) > DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni > diff --git a/lib/librte_eal/common/include/rte_tailq_elem.h b/lib/librte_eal/common/include/rte_tailq_elem.h > index f74fc7c..3013869 100644 > --- a/lib/librte_eal/common/include/rte_tailq_elem.h > +++ b/lib/librte_eal/common/include/rte_tailq_elem.h > @@ -84,6 +84,8 @@ rte_tailq_elem(RTE_TAILQ_ACL, "RTE_ACL") > > rte_tailq_elem(RTE_TAILQ_DISTRIBUTOR, "RTE_DISTRIBUTOR") > > +rte_tailq_elem(RTE_TAILQ_REORDER, "RTE_REORDER") > + > rte_tailq_end(RTE_TAILQ_NUM) > > #undef rte_tailq_elem > diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h > index 16059c6..ed27eb8 100644 > --- a/lib/librte_mbuf/rte_mbuf.h > +++ b/lib/librte_mbuf/rte_mbuf.h > @@ -262,6 +262,9 @@ struct rte_mbuf { > uint32_t usr; /**< User defined tags. See @rte_distributor_process */ > } hash; /**< hash information */ > > + /* sequence number - field used in distributor and reorder library */ > + uint32_t seqn; > + > /* second cache line - fields only used in slow path or on TX */ > MARKER cacheline1 __rte_cache_aligned; > > diff --git a/lib/librte_reorder/Makefile b/lib/librte_reorder/Makefile > new file mode 100644 > index 0000000..12b916f > --- /dev/null > +++ b/lib/librte_reorder/Makefile > @@ -0,0 +1,50 @@ > +# BSD LICENSE > +# > +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. > +# All rights reserved. > +# > +# Redistribution and use in source and binary forms, with or without > +# modification, are permitted provided that the following conditions > +# are met: > +# > +# * Redistributions of source code must retain the above copyright > +# notice, this list of conditions and the following disclaimer. > +# * Redistributions in binary form must reproduce the above copyright > +# notice, this list of conditions and the following disclaimer in > +# the documentation and/or other materials provided with the > +# distribution. > +# * Neither the name of Intel Corporation nor the names of its > +# contributors may be used to endorse or promote products derived > +# from this software without specific prior written permission. > +# > +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + > +include $(RTE_SDK)/mk/rte.vars.mk > + > +# library name > +LIB = librte_reorder.a > + > +CFLAGS += -O3 > +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) > + > +# all source are stored in SRCS-y > +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) := rte_reorder.c > + > +# install this header file > +SYMLINK-$(CONFIG_RTE_LIBRTE_REORDER)-include := rte_reorder.h > + > +# this lib depends upon: > +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_mbuf > +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_eal > + > +include $(RTE_SDK)/mk/rte.lib.mk > diff --git a/lib/librte_reorder/rte_reorder.c b/lib/librte_reorder/rte_reorder.c > new file mode 100644 > index 0000000..fb3e986 > --- /dev/null > +++ b/lib/librte_reorder/rte_reorder.c > @@ -0,0 +1,464 @@ > +/*- > + * BSD LICENSE > + * > + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * * Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in > + * the documentation and/or other materials provided with the > + * distribution. > + * * Neither the name of Intel Corporation nor the names of its > + * contributors may be used to endorse or promote products derived > + * from this software without specific prior written permission. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#include <inttypes.h> > +#include <string.h> > + > +#include <rte_log.h> > +#include <rte_mbuf.h> > +#include <rte_memzone.h> > +#include <rte_eal_memconfig.h> > +#include <rte_errno.h> > +#include <rte_tailq.h> > +#include <rte_malloc.h> > + > +#include "rte_reorder.h" > + > +TAILQ_HEAD(rte_reorder_list, rte_tailq_entry); > + > +#define NO_FLAGS 0 > +#define RTE_REORDER_PREFIX "RO_" > +#define RTE_REORDER_NAMESIZE 32 > + > +/* Macros for printing using RTE_LOG */ > +#define RTE_LOGTYPE_REORDER RTE_LOGTYPE_USER1 > + > +/* A generic circular buffer */ > +struct cir_buffer { > + unsigned int size; /**< Number of entries that can be stored */ > + unsigned int mask; /**< [buffer_size - 1]: used for wrap-around */ > + unsigned int head; /**< insertion point in buffer */ > + unsigned int tail; /**< extraction point in buffer */ > + struct rte_mbuf **entries; > +} __rte_cache_aligned; > + > +/* The reorder buffer data structure itself */ > +struct rte_reorder_buffer { > + char name[RTE_REORDER_NAMESIZE]; > + uint32_t min_seqn; /**< Lowest seq. number that can be in the buffer */ > + unsigned int memsize; /**< memory area size of reorder buffer */ > + struct cir_buffer ready_buf; /**< temp buffer for dequeued entries */ > + struct cir_buffer order_buf; /**< buffer used to reorder entries */ > +} __rte_cache_aligned; > + > +struct rte_reorder_buffer * > +rte_reorder_init(void *buf, unsigned int bufsize, > + const char *name, unsigned int size) > +{ > + struct rte_reorder_buffer *b = (struct rte_reorder_buffer *)buf; > + const unsigned int min_bufsize = sizeof(*b) + > + (2 * size * sizeof(struct rte_mbuf *)); > + > + struct rte_reorder_buffer *be; > + struct rte_tailq_entry *te; > + struct rte_reorder_list *reorder_list; > + > + /* check that we have an initialised tail queue */ > + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); > + if (!reorder_list) { > + rte_errno = E_RTE_NO_TAILQ; > + return NULL; > + } > + > + if (!rte_is_power_of_2(size)) { > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" > + " - Not a power of 2\n"); > + rte_errno = EINVAL; > + return NULL; > + } > + if (b == NULL) { > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer parameter:" > + " NULL\n"); > + rte_errno = EINVAL; > + return NULL; > + } > + if (name == NULL) { > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" > + " NULL\n"); > + rte_errno = EINVAL; > + return NULL; > + } > + if (bufsize < min_bufsize) { > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size:%u, " > + "should be minimum:%u\n", bufsize, min_bufsize); > + rte_errno = ENOMEM; > + return NULL; > + } > + > + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); > + > + /* guarantee there's no existing */ > + TAILQ_FOREACH(te, reorder_list, next) { > + be = (struct rte_reorder_buffer *) te->data; > + if (strncmp(name, be->name, RTE_REORDER_NAMESIZE) == 0) > + break; > + } > + if (te != NULL) { > + b = be; > + memset(b, 0, bufsize); > + snprintf(b->name, sizeof(b->name), "%s", name); > + b->memsize = bufsize; > + b->order_buf.size = b->ready_buf.size = size; > + b->order_buf.mask = b->ready_buf.mask = size - 1; > + b->ready_buf.entries = (void *)&b[1]; > + b->order_buf.entries = RTE_PTR_ADD(&b[1], > + size * sizeof(b->ready_buf.entries[0])); > + goto exit; > + } > + > + /* allocate tailq entry */ > + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); > + if (te == NULL) { > + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); > + goto exit; > + } > + > + memset(b, 0, bufsize); > + snprintf(b->name, sizeof(b->name), "%s", name); > + b->memsize = bufsize; > + b->order_buf.size = b->ready_buf.size = size; > + b->order_buf.mask = b->ready_buf.mask = size - 1; > + b->ready_buf.entries = (void *)&b[1]; > + b->order_buf.entries = RTE_PTR_ADD(&b[1], > + size * sizeof(b->ready_buf.entries[0])); > + > + te->data = (void *) b; > + > + TAILQ_INSERT_TAIL(reorder_list, te, next); > + > +exit: > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > + return b; > +} > + > +void rte_reorder_reset(struct rte_reorder_buffer *b) > +{ > + unsigned int i = 0; > + char name[RTE_REORDER_NAMESIZE]; > + /* Free up the mbufs of order buffer & ready buffer */ > + for (i = 0; i < b->order_buf.size; i++) { > + if (b->order_buf.entries[i]) > + rte_pktmbuf_free(b->order_buf.entries[i]); > + if (b->ready_buf.entries[i]) > + rte_pktmbuf_free(b->ready_buf.entries[i]); > + } > + snprintf(name, sizeof(name), "%s", b->name); > + rte_reorder_init(b, b->memsize, name, b->order_buf.size); > +} > + > +struct rte_reorder_buffer* > +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size) > +{ > + const struct rte_memzone *mz; > + struct rte_reorder_buffer *b = NULL; > + struct rte_tailq_entry *te; > + struct rte_reorder_list *reorder_list; > + char mz_name[RTE_MEMZONE_NAMESIZE]; > + > + /* check that we have an initialised tail queue */ > + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); > + if (!reorder_list) { > + rte_errno = E_RTE_NO_TAILQ; > + return NULL; > + } > + > + /* Check user arguments. */ > + if (!rte_is_power_of_2(size)) { > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" > + " - Not a power of 2\n"); > + rte_errno = EINVAL; > + return NULL; > + } > + if (name == NULL) { > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" > + " NULL\n"); > + rte_errno = EINVAL; > + return NULL; > + } > + > + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); > + > + /* guarantee there's no existing */ > + TAILQ_FOREACH(te, reorder_list, next) { > + b = (struct rte_reorder_buffer *) te->data; > + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) > + break; > + } > + if (te != NULL) > + goto exit; > + > + /* allocate tailq entry */ > + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); > + if (te == NULL) { > + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); > + goto exit; > + } > + > + /* Allocate memory to store the reorder buffer structure. */ > + const unsigned int bufsize = sizeof(struct rte_reorder_buffer) + > + (2 * size * sizeof(struct rte_mbuf *)); > + snprintf(mz_name, sizeof(mz_name), RTE_REORDER_PREFIX"%s", name); > + mz = rte_memzone_reserve(mz_name, bufsize, > + socket_id, NO_FLAGS); > + if (mz == NULL) { > + RTE_LOG(ERR, REORDER, "Memzone allocation failed\n"); > + rte_errno = ENOMEM; > + return NULL; > + } > + b = mz->addr; > + memset(b, 0, bufsize); > + snprintf(b->name, sizeof(b->name), "%s", name); > + b->memsize = bufsize; > + b->order_buf.size = b->ready_buf.size = size; > + b->order_buf.mask = b->ready_buf.mask = size - 1; > + b->ready_buf.entries = (void *)&b[1]; > + b->order_buf.entries = RTE_PTR_ADD(&b[1], > + size * sizeof(b->ready_buf.entries[0])); > + > + te->data = (void *) b; > + > + TAILQ_INSERT_TAIL(reorder_list, te, next); > + > +exit: > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > + return b; > +} > + > +void > +rte_reorder_free(struct rte_reorder_buffer *b) > +{ > + struct rte_reorder_list *reorder_list; > + struct rte_tailq_entry *te; > + > + /* Check user arguments. */ > + if (b == NULL) > + return; > + > + /* check that we have an initialised tail queue */ > + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); > + if (!reorder_list) { > + rte_errno = E_RTE_NO_TAILQ; > + return; > + } > + > + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); > + > + /* find our tailq entry */ > + TAILQ_FOREACH(te, reorder_list, next) { > + if (te->data == (void *) b) > + break; > + } > + if (te == NULL) { > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > + return; > + } > + > + TAILQ_REMOVE(reorder_list, te, next); > + > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > + > + rte_free(b); > + rte_free(te); > +} > + > +struct rte_reorder_buffer * > +rte_reorder_find_existing(const char *name) > +{ > + struct rte_reorder_buffer *b = NULL; > + struct rte_tailq_entry *te; > + struct rte_reorder_list *reorder_list; > + > + /* check that we have an initialised tail queue */ > + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); > + if (!reorder_list) { > + rte_errno = E_RTE_NO_TAILQ; > + return NULL; > + } > + > + rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); > + TAILQ_FOREACH(te, reorder_list, next) { > + b = (struct rte_reorder_buffer *) te->data; > + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) > + break; > + } > + rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); > + > + if (te == NULL) { > + rte_errno = ENOENT; > + return NULL; > + } > + > + return b; > +} > + > +static unsigned > +rte_reorder_fill_overflow(struct rte_reorder_buffer *b, unsigned n) > +{ > + /* > + * 1. Move all ready entries that fit to the ready_buf > + * 2. check if we meet the minimum needed (n). > + * 3. If not, then skip any gaps and keep moving. > + * 4. If at any point the ready buffer is full, stop > + * 5. Return the number of positions the order_buf head has moved > + */ > + > + struct cir_buffer *order_buf = &b->order_buf, > + *ready_buf = &b->ready_buf; > + > + unsigned int order_head_adv = 0; > + > + /* > + * move at least n packets to ready buffer, assuming ready buffer > + * has room for those packets. > + */ > + while (order_head_adv < n && > + ((ready_buf->head + 1) & ready_buf->mask) != ready_buf->tail) { > + > + /* if we are blocked waiting on a packet, skip it */ > + if (order_buf->entries[order_buf->head] == NULL) { > + order_buf->head++, order_head_adv++; > + > + if (order_buf->head == order_buf->size) > + order_buf->head = 0; > + } > + > + /* Move all ready entries that fit to the ready_buf */ > + while (order_buf->entries[order_buf->head] != NULL) { > + ready_buf->entries[ready_buf->head++] = > + order_buf->entries[order_buf->head]; > + > + order_buf->entries[order_buf->head++] = NULL; > + order_head_adv++; > + > + if (ready_buf->head == ready_buf->size) > + ready_buf->head = 0; > + if (order_buf->head == order_buf->size) > + order_buf->head = 0; > + > + if (((ready_buf->head+1) & ready_buf->mask) == ready_buf->tail) > + break; > + } > + } > + > + b->min_seqn += order_head_adv; > + /* Return the number of positions the order_buf head has moved */ > + return order_head_adv; > +} > + > +int > +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf) > +{ > + uint32_t offset, position; > + struct cir_buffer *order_buf = &b->order_buf; > + > + /* > + * calculate the offset from the head pointer we need to go. > + * The subtraction takes care of the sequence number wrapping. > + * For example (using 16-bit for brevity): > + * min_seqn = 0xFFFD > + * mbuf_seqn = 0x0010 > + * offset = 0x0010 - 0xFFFD = 0x13 > + */ > + offset = mbuf->seqn - b->min_seqn; > + > + /* > + * action to take depends on offset. > + * offset < buffer->size: the mbuf fits within the current window of > + * sequence numbers we can reorder. EXPECTED CASE. > + * offset > buffer->size: the mbuf is outside the current window. There > + * are a number of cases to consider: > + * 1. The packet sequence is just outside the window, then we need > + * to see about shifting the head pointer and taking any ready > + * to return packets out of the ring. If there was a delayed > + * or dropped packet preventing drains from shifting the window > + * this case will skip over the dropped packet instead, and any > + * packets dequeued here will be returned on the next drain call. > + * 2. The packet sequence number is vastly outside our window, taken > + * here as having offset greater than twice the buffer size. In > + * this case, the packet is probably an old or late packet that > + * was previously skipped, so just enqueue the packet for > + * immediate return on the next drain call, or else return error. > + */ > + if (offset < b->order_buf.size) { > + position = (order_buf->head + offset) & order_buf->mask; > + order_buf->entries[position] = mbuf; > + } else if (offset < 2 * b->order_buf.size) { > + if (rte_reorder_fill_overflow(b, offset - order_buf->size) < > + offset - order_buf->size) { > + /* Put in handling for enqueue straight to output */ > + rte_errno = ENOSPC; > + return -1; > + } > + offset = mbuf->seqn - b->min_seqn; > + position = (order_buf->head + offset) & order_buf->mask; > + order_buf->entries[position] = mbuf; > + } else { > + /* Put in handling for enqueue straight to output */ > + rte_errno = ERANGE; > + return -1; > + } > + return 0; > +} > + > +unsigned int > +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, > + unsigned max_mbufs) > +{ > + unsigned int drain_cnt = 0; > + > + struct cir_buffer *order_buf = &b->order_buf, > + *ready_buf = &b->ready_buf; > + > + /* Try to fetch requested number of mbufs from ready buffer */ > + while ((drain_cnt < max_mbufs) && (ready_buf->tail != ready_buf->head)) { > + mbufs[drain_cnt++] = ready_buf->entries[ready_buf->tail++]; > + if (ready_buf->tail == ready_buf->size) > + ready_buf->tail = 0; > + } > + > + /* > + * If requested number of buffers not fetched from ready buffer, fetch > + * remaining buffers from order buffer > + */ > + while ((drain_cnt < max_mbufs) && > + (order_buf->entries[order_buf->head] != NULL)) { > + mbufs[drain_cnt++] = order_buf->entries[order_buf->head]; > + order_buf->entries[order_buf->head] = NULL; > + b->min_seqn++; > + order_buf->head++; > + if (order_buf->head == order_buf->size) > + order_buf->head = 0; > + } > + > + return drain_cnt; > +} > diff --git a/lib/librte_reorder/rte_reorder.h b/lib/librte_reorder/rte_reorder.h > new file mode 100644 > index 0000000..3ec7011 > --- /dev/null > +++ b/lib/librte_reorder/rte_reorder.h > @@ -0,0 +1,184 @@ > +/*- > + * BSD LICENSE > + * > + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * * Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in > + * the documentation and/or other materials provided with the > + * distribution. > + * * Neither the name of Intel Corporation nor the names of its > + * contributors may be used to endorse or promote products derived > + * from this software without specific prior written permission. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#ifndef _RTE_REORDER_H_ > +#define _RTE_REORDER_H_ > + > +/** > + * @file > + * RTE reorder > + * > + * Reorder library is a component which is designed to > + * provide ordering of out of ordered packets based on > + * sequence number present in mbuf. > + * > + */ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +struct rte_reorder_buffer; > + > +/** > + * Create a new reorder buffer instance > + * > + * Allocate memory and initialize a new reorder buffer in that > + * memory, returning the reorder buffer pointer to the user > + * > + * @param name > + * The name to be given to the reorder buffer instance. > + * @param socket_id > + * The NUMA node on which the memory for the reorder buffer > + * instance is to be reserved. > + * @param size > + * Max number of elements that can be stored in the reorder buffer > + * @return > + * The initialized reorder buffer instance, or NULL on error > + * On error case, rte_errno will be set appropriately: > + * - ENOMEM - no appropriate memory area found in which to create memzone > + * - EINVAL - invalid parameters > + */ > +struct rte_reorder_buffer * > +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size); > + > +/** > + * Initializes given reorder buffer instance > + * > + * @param buf > + * Pointer to memory area where reorder buffer instance > + * should be initialized > + * @param bufsize > + * Size of the memory area to be used for reorder buffer instance > + * initialization > + * @param name > + * The name to be given to the reorder buffer instance > + * @param size > + * Number of elements that can be stored in reorder buffer > + * @return > + * The initialized reorder buffer instance, or NULL on error > + * On error case, rte_errno will be set appropriately: > + * - EINVAL - invalid parameters > + * - ENOMEM - not enough memory for reorder buffer instance > + * initialization > + */ > +struct rte_reorder_buffer * > +rte_reorder_init(void *buf, unsigned int bufsize, > + const char *name, unsigned int size); > + > +/** > + * Reset the given reorder buffer instance with initial values. > + * > + * @param b > + * Reorder buffer instance which has to be reset > + */ > +void rte_reorder_reset(struct rte_reorder_buffer *b); > + > +/** > + * Find an existing reorder buffer instance > + * and return a pointer to it. > + * > + * @param name > + * Name of the reorder buffer instacne as passed to rte_reorder_create() > + * @return > + * Pointer to reorder buffer instance or NULL if object not found with rte_errno > + * set appropriately. Possible rte_errno values include: > + * - ENOENT - required entry not available to return. > + * - E_RTE_NO_TAILQ - no tailq list could be got for the > + * reorder instance list > + */ > +struct rte_reorder_buffer * > +rte_reorder_find_existing(const char *name); > + > +/** > + * Free reorder buffer instance. > + * > + * @param b > + * reorder buffer instance > + * @return > + * None > + */ > +void > +rte_reorder_free(struct rte_reorder_buffer *b); > + > +/** > + * Insert given mbuf in reorder buffer in its correct position > + * > + * The given mbuf is to be reordered relative to other mbufs in the system. > + * The mbuf must contain a sequence number which is then used to place > + * the buffer in the correct position in the reorder buffer. Reordered > + * packets can later be taken from the buffer using the rte_reorder_drain() > + * API. > + * > + * @param b > + * Reorder buffer where the mbuf has to be inserted. > + * @param mbuf > + * mbuf of packet that needs to be inserted in reorder buffer. > + * @return > + * 0 on success > + * -1 on error > + * On error case, rte_errno will be set appropriately: > + * - ENOSPC - Cannot move existing mbufs from reorder buffer to accomodate ealry mbuf. > + * But mbuf can be accomodated by performing drain and then insert. > + * - ERANGE - Too early or late mbuf which is vastly out of > + * range of expected window should be ingnored without any handling. > + */ > +int > +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf); > + > +/** > + * Fetch reordered buffers > + * > + * Returns a set of in-order buffers from the reorder buffer structure. Gaps > + * may be present in the sequence numbers of the mbuf if packets have been > + * delayed too long before reaching the reorder window, or have been previously > + * dropped by the system. > + * > + * @param b > + * Reorder buffer instance from which packets are to be drained > + * @param mbufs > + * array of mbufs where reordered packets will be inserted from reorder buffer > + * @param max_mbufs > + * the number of elements in the mbufs array. > + * @return > + * number of mbuf pointers written to mbufs. 0 <= N < max_mbufs. > + */ > +unsigned int > +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, > + unsigned max_mbufs); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif /* _RTE_REORDER_H_ */ > -- > 1.8.3.1 > Using uint32_t's for sequence numbers is susceptible to overflow, they should probably be uint64_t's unless there is a good reason not to. Cheers, ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library 2015-01-07 21:09 ` Richard Sanger @ 2015-01-08 16:28 ` Pattan, Reshma 0 siblings, 0 replies; 44+ messages in thread From: Pattan, Reshma @ 2015-01-08 16:28 UTC (permalink / raw) To: Richard Sanger; +Cc: dev > -----Original Message----- > From: Richard Sanger [mailto:rsangerarj@gmail.com] > Sent: Wednesday, January 7, 2015 9:10 PM > To: Pattan, Reshma > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library > > On 8 January 2015 at 05:39, Reshma Pattan <reshma.pattan@intel.com> wrote: > > From: Reshma Pattan <reshma.pattan@intel.com> > > > > 1)New library to provide reordering of out of ordered > > mbufs based on sequence number of mbuf. Library uses reorder buffer > structure > > which in tern uses two circular buffers called ready and order buffers. > > *rte_reorder_create API creates instance of reorder buffer. > > *rte_reorder_init API initializes given reorder buffer instance. > > *rte_reorder_reset API resets given reorder buffer instance. > > *rte_reorder_insert API inserts the mbuf into order circular buffer. > > *rte_reorder_fill_overflow moves mbufs from order buffer to ready > buffer > > to accomodate early packets in order buffer. > > *rte_reorder_drain API provides draining facility to fetch out > > reordered mbufs from order and ready buffers. > > > > Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> > > Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> > > --- > > config/common_bsdapp | 5 + > > config/common_linuxapp | 5 + > > lib/Makefile | 1 + > > lib/librte_eal/common/include/rte_tailq_elem.h | 2 + > > lib/librte_mbuf/rte_mbuf.h | 3 + > > lib/librte_reorder/Makefile | 50 +++ > > lib/librte_reorder/rte_reorder.c | 464 +++++++++++++++++++++++++ > > lib/librte_reorder/rte_reorder.h | 184 ++++++++++ > > 8 files changed, 714 insertions(+) > > create mode 100644 lib/librte_reorder/Makefile create mode 100644 > > lib/librte_reorder/rte_reorder.c create mode 100644 > > lib/librte_reorder/rte_reorder.h > > > > diff --git a/config/common_bsdapp b/config/common_bsdapp index > > 9177db1..e3e0e94 100644 > > --- a/config/common_bsdapp > > +++ b/config/common_bsdapp > > @@ -334,6 +334,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 > > CONFIG_RTE_LIBRTE_DISTRIBUTOR=y > > > > # > > +# Compile the reorder library > > +# > > +CONFIG_RTE_LIBRTE_REORDER=y > > + > > +# > > # Compile librte_port > > # > > CONFIG_RTE_LIBRTE_PORT=y > > diff --git a/config/common_linuxapp b/config/common_linuxapp index > > 2f9643b..b5ec730 100644 > > --- a/config/common_linuxapp > > +++ b/config/common_linuxapp > > @@ -342,6 +342,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 > > CONFIG_RTE_LIBRTE_DISTRIBUTOR=y > > > > # > > +# Compile the reorder library > > +# > > +CONFIG_RTE_LIBRTE_REORDER=y > > + > > +# > > # Compile librte_port > > # > > CONFIG_RTE_LIBRTE_PORT=y > > diff --git a/lib/Makefile b/lib/Makefile index 0ffc982..5919d32 100644 > > --- a/lib/Makefile > > +++ b/lib/Makefile > > @@ -65,6 +65,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += > > librte_distributor > > DIRS-$(CONFIG_RTE_LIBRTE_PORT) += librte_port > > DIRS-$(CONFIG_RTE_LIBRTE_TABLE) += librte_table > > DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += librte_pipeline > > +DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += librte_reorder > > > > ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) > > DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni diff --git > > a/lib/librte_eal/common/include/rte_tailq_elem.h > > b/lib/librte_eal/common/include/rte_tailq_elem.h > > index f74fc7c..3013869 100644 > > --- a/lib/librte_eal/common/include/rte_tailq_elem.h > > +++ b/lib/librte_eal/common/include/rte_tailq_elem.h > > @@ -84,6 +84,8 @@ rte_tailq_elem(RTE_TAILQ_ACL, "RTE_ACL") > > > > rte_tailq_elem(RTE_TAILQ_DISTRIBUTOR, "RTE_DISTRIBUTOR") > > > > +rte_tailq_elem(RTE_TAILQ_REORDER, "RTE_REORDER") > > + > > rte_tailq_end(RTE_TAILQ_NUM) > > > > #undef rte_tailq_elem > > diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h > > index 16059c6..ed27eb8 100644 > > --- a/lib/librte_mbuf/rte_mbuf.h > > +++ b/lib/librte_mbuf/rte_mbuf.h > > @@ -262,6 +262,9 @@ struct rte_mbuf { > > uint32_t usr; /**< User defined tags. See @rte_distributor_process > */ > > } hash; /**< hash information */ > > > > + /* sequence number - field used in distributor and reorder library */ > > + uint32_t seqn; > > + > > /* second cache line - fields only used in slow path or on TX */ > > MARKER cacheline1 __rte_cache_aligned; > > > > diff --git a/lib/librte_reorder/Makefile b/lib/librte_reorder/Makefile > > new file mode 100644 index 0000000..12b916f > > --- /dev/null > > +++ b/lib/librte_reorder/Makefile > > @@ -0,0 +1,50 @@ > > +# BSD LICENSE > > +# > > +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. > > +# All rights reserved. > > +# > > +# Redistribution and use in source and binary forms, with or without > > +# modification, are permitted provided that the following conditions > > +# are met: > > +# > > +# * Redistributions of source code must retain the above copyright > > +# notice, this list of conditions and the following disclaimer. > > +# * Redistributions in binary form must reproduce the above copyright > > +# notice, this list of conditions and the following disclaimer in > > +# the documentation and/or other materials provided with the > > +# distribution. > > +# * Neither the name of Intel Corporation nor the names of its > > +# contributors may be used to endorse or promote products derived > > +# from this software without specific prior written permission. > > +# > > +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND > CONTRIBUTORS > > +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT > NOT > > +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND > FITNESS FOR > > +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > COPYRIGHT > > +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, > INCIDENTAL, > > +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT > NOT > > +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS > OF USE, > > +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED > AND ON ANY > > +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR > TORT > > +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > THE USE > > +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > DAMAGE. > > + > > +include $(RTE_SDK)/mk/rte.vars.mk > > + > > +# library name > > +LIB = librte_reorder.a > > + > > +CFLAGS += -O3 > > +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) > > + > > +# all source are stored in SRCS-y > > +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) := rte_reorder.c > > + > > +# install this header file > > +SYMLINK-$(CONFIG_RTE_LIBRTE_REORDER)-include := rte_reorder.h > > + > > +# this lib depends upon: > > +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_mbuf > > +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_eal > > + > > +include $(RTE_SDK)/mk/rte.lib.mk > > diff --git a/lib/librte_reorder/rte_reorder.c > > b/lib/librte_reorder/rte_reorder.c > > new file mode 100644 > > index 0000000..fb3e986 > > --- /dev/null > > +++ b/lib/librte_reorder/rte_reorder.c > > @@ -0,0 +1,464 @@ > > +/*- > > + * BSD LICENSE > > + * > > + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. > > + * All rights reserved. > > + * > > + * Redistribution and use in source and binary forms, with or without > > + * modification, are permitted provided that the following conditions > > + * are met: > > + * > > + * * Redistributions of source code must retain the above copyright > > + * notice, this list of conditions and the following disclaimer. > > + * * Redistributions in binary form must reproduce the above copyright > > + * notice, this list of conditions and the following disclaimer in > > + * the documentation and/or other materials provided with the > > + * distribution. > > + * * Neither the name of Intel Corporation nor the names of its > > + * contributors may be used to endorse or promote products derived > > + * from this software without specific prior written permission. > > + * > > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND > CONTRIBUTORS > > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT > NOT > > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND > FITNESS FOR > > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > COPYRIGHT > > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, > INCIDENTAL, > > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT > NOT > > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS > OF USE, > > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED > AND ON ANY > > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR > TORT > > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > THE USE > > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > DAMAGE. > > + */ > > + > > +#include <inttypes.h> > > +#include <string.h> > > + > > +#include <rte_log.h> > > +#include <rte_mbuf.h> > > +#include <rte_memzone.h> > > +#include <rte_eal_memconfig.h> > > +#include <rte_errno.h> > > +#include <rte_tailq.h> > > +#include <rte_malloc.h> > > + > > +#include "rte_reorder.h" > > + > > +TAILQ_HEAD(rte_reorder_list, rte_tailq_entry); > > + > > +#define NO_FLAGS 0 > > +#define RTE_REORDER_PREFIX "RO_" > > +#define RTE_REORDER_NAMESIZE 32 > > + > > +/* Macros for printing using RTE_LOG */ > > +#define RTE_LOGTYPE_REORDER RTE_LOGTYPE_USER1 > > + > > +/* A generic circular buffer */ > > +struct cir_buffer { > > + unsigned int size; /**< Number of entries that can be stored */ > > + unsigned int mask; /**< [buffer_size - 1]: used for wrap-around */ > > + unsigned int head; /**< insertion point in buffer */ > > + unsigned int tail; /**< extraction point in buffer */ > > + struct rte_mbuf **entries; > > +} __rte_cache_aligned; > > + > > +/* The reorder buffer data structure itself */ struct > > +rte_reorder_buffer { > > + char name[RTE_REORDER_NAMESIZE]; > > + uint32_t min_seqn; /**< Lowest seq. number that can be in the buffer */ > > + unsigned int memsize; /**< memory area size of reorder buffer */ > > + struct cir_buffer ready_buf; /**< temp buffer for dequeued entries */ > > + struct cir_buffer order_buf; /**< buffer used to reorder > > +entries */ } __rte_cache_aligned; > > + > > +struct rte_reorder_buffer * > > +rte_reorder_init(void *buf, unsigned int bufsize, > > + const char *name, unsigned int size) { > > + struct rte_reorder_buffer *b = (struct rte_reorder_buffer *)buf; > > + const unsigned int min_bufsize = sizeof(*b) + > > + (2 * size * sizeof(struct > > +rte_mbuf *)); > > + > > + struct rte_reorder_buffer *be; > > + struct rte_tailq_entry *te; > > + struct rte_reorder_list *reorder_list; > > + > > + /* check that we have an initialised tail queue */ > > + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, > rte_reorder_list); > > + if (!reorder_list) { > > + rte_errno = E_RTE_NO_TAILQ; > > + return NULL; > > + } > > + > > + if (!rte_is_power_of_2(size)) { > > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" > > + " - Not a power of 2\n"); > > + rte_errno = EINVAL; > > + return NULL; > > + } > > + if (b == NULL) { > > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer parameter:" > > + " NULL\n"); > > + rte_errno = EINVAL; > > + return NULL; > > + } > > + if (name == NULL) { > > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" > > + " NULL\n"); > > + rte_errno = EINVAL; > > + return NULL; > > + } > > + if (bufsize < min_bufsize) { > > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size:%u, " > > + "should be minimum:%u\n", bufsize, min_bufsize); > > + rte_errno = ENOMEM; > > + return NULL; > > + } > > + > > + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); > > + > > + /* guarantee there's no existing */ > > + TAILQ_FOREACH(te, reorder_list, next) { > > + be = (struct rte_reorder_buffer *) te->data; > > + if (strncmp(name, be->name, RTE_REORDER_NAMESIZE) == 0) > > + break; > > + } > > + if (te != NULL) { > > + b = be; > > + memset(b, 0, bufsize); > > + snprintf(b->name, sizeof(b->name), "%s", name); > > + b->memsize = bufsize; > > + b->order_buf.size = b->ready_buf.size = size; > > + b->order_buf.mask = b->ready_buf.mask = size - 1; > > + b->ready_buf.entries = (void *)&b[1]; > > + b->order_buf.entries = RTE_PTR_ADD(&b[1], > > + size * sizeof(b->ready_buf.entries[0])); > > + goto exit; > > + } > > + > > + /* allocate tailq entry */ > > + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); > > + if (te == NULL) { > > + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); > > + goto exit; > > + } > > + > > + memset(b, 0, bufsize); > > + snprintf(b->name, sizeof(b->name), "%s", name); > > + b->memsize = bufsize; > > + b->order_buf.size = b->ready_buf.size = size; > > + b->order_buf.mask = b->ready_buf.mask = size - 1; > > + b->ready_buf.entries = (void *)&b[1]; > > + b->order_buf.entries = RTE_PTR_ADD(&b[1], > > + size * sizeof(b->ready_buf.entries[0])); > > + > > + te->data = (void *) b; > > + > > + TAILQ_INSERT_TAIL(reorder_list, te, next); > > + > > +exit: > > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > > + return b; > > +} > > + > > +void rte_reorder_reset(struct rte_reorder_buffer *b) { > > + unsigned int i = 0; > > + char name[RTE_REORDER_NAMESIZE]; > > + /* Free up the mbufs of order buffer & ready buffer */ > > + for (i = 0; i < b->order_buf.size; i++) { > > + if (b->order_buf.entries[i]) > > + rte_pktmbuf_free(b->order_buf.entries[i]); > > + if (b->ready_buf.entries[i]) > > + rte_pktmbuf_free(b->ready_buf.entries[i]); > > + } > > + snprintf(name, sizeof(name), "%s", b->name); > > + rte_reorder_init(b, b->memsize, name, b->order_buf.size); } > > + > > +struct rte_reorder_buffer* > > +rte_reorder_create(const char *name, unsigned socket_id, unsigned int > > +size) { > > + const struct rte_memzone *mz; > > + struct rte_reorder_buffer *b = NULL; > > + struct rte_tailq_entry *te; > > + struct rte_reorder_list *reorder_list; > > + char mz_name[RTE_MEMZONE_NAMESIZE]; > > + > > + /* check that we have an initialised tail queue */ > > + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, > rte_reorder_list); > > + if (!reorder_list) { > > + rte_errno = E_RTE_NO_TAILQ; > > + return NULL; > > + } > > + > > + /* Check user arguments. */ > > + if (!rte_is_power_of_2(size)) { > > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" > > + " - Not a power of 2\n"); > > + rte_errno = EINVAL; > > + return NULL; > > + } > > + if (name == NULL) { > > + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" > > + " NULL\n"); > > + rte_errno = EINVAL; > > + return NULL; > > + } > > + > > + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); > > + > > + /* guarantee there's no existing */ > > + TAILQ_FOREACH(te, reorder_list, next) { > > + b = (struct rte_reorder_buffer *) te->data; > > + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) > > + break; > > + } > > + if (te != NULL) > > + goto exit; > > + > > + /* allocate tailq entry */ > > + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); > > + if (te == NULL) { > > + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); > > + goto exit; > > + } > > + > > + /* Allocate memory to store the reorder buffer structure. */ > > + const unsigned int bufsize = sizeof(struct rte_reorder_buffer) + > > + (2 * size * sizeof(struct rte_mbuf *)); > > + snprintf(mz_name, sizeof(mz_name), RTE_REORDER_PREFIX"%s", name); > > + mz = rte_memzone_reserve(mz_name, bufsize, > > + socket_id, NO_FLAGS); > > + if (mz == NULL) { > > + RTE_LOG(ERR, REORDER, "Memzone allocation failed\n"); > > + rte_errno = ENOMEM; > > + return NULL; > > + } > > + b = mz->addr; > > + memset(b, 0, bufsize); > > + snprintf(b->name, sizeof(b->name), "%s", name); > > + b->memsize = bufsize; > > + b->order_buf.size = b->ready_buf.size = size; > > + b->order_buf.mask = b->ready_buf.mask = size - 1; > > + b->ready_buf.entries = (void *)&b[1]; > > + b->order_buf.entries = RTE_PTR_ADD(&b[1], > > + size * sizeof(b->ready_buf.entries[0])); > > + > > + te->data = (void *) b; > > + > > + TAILQ_INSERT_TAIL(reorder_list, te, next); > > + > > +exit: > > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > > + return b; > > +} > > + > > +void > > +rte_reorder_free(struct rte_reorder_buffer *b) { > > + struct rte_reorder_list *reorder_list; > > + struct rte_tailq_entry *te; > > + > > + /* Check user arguments. */ > > + if (b == NULL) > > + return; > > + > > + /* check that we have an initialised tail queue */ > > + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, > rte_reorder_list); > > + if (!reorder_list) { > > + rte_errno = E_RTE_NO_TAILQ; > > + return; > > + } > > + > > + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); > > + > > + /* find our tailq entry */ > > + TAILQ_FOREACH(te, reorder_list, next) { > > + if (te->data == (void *) b) > > + break; > > + } > > + if (te == NULL) { > > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > > + return; > > + } > > + > > + TAILQ_REMOVE(reorder_list, te, next); > > + > > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > > + > > + rte_free(b); > > + rte_free(te); > > +} > > + > > +struct rte_reorder_buffer * > > +rte_reorder_find_existing(const char *name) { > > + struct rte_reorder_buffer *b = NULL; > > + struct rte_tailq_entry *te; > > + struct rte_reorder_list *reorder_list; > > + > > + /* check that we have an initialised tail queue */ > > + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, > rte_reorder_list); > > + if (!reorder_list) { > > + rte_errno = E_RTE_NO_TAILQ; > > + return NULL; > > + } > > + > > + rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); > > + TAILQ_FOREACH(te, reorder_list, next) { > > + b = (struct rte_reorder_buffer *) te->data; > > + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) > > + break; > > + } > > + rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); > > + > > + if (te == NULL) { > > + rte_errno = ENOENT; > > + return NULL; > > + } > > + > > + return b; > > +} > > + > > +static unsigned > > +rte_reorder_fill_overflow(struct rte_reorder_buffer *b, unsigned n) { > > + /* > > + * 1. Move all ready entries that fit to the ready_buf > > + * 2. check if we meet the minimum needed (n). > > + * 3. If not, then skip any gaps and keep moving. > > + * 4. If at any point the ready buffer is full, stop > > + * 5. Return the number of positions the order_buf head has moved > > + */ > > + > > + struct cir_buffer *order_buf = &b->order_buf, > > + *ready_buf = &b->ready_buf; > > + > > + unsigned int order_head_adv = 0; > > + > > + /* > > + * move at least n packets to ready buffer, assuming ready buffer > > + * has room for those packets. > > + */ > > + while (order_head_adv < n && > > + ((ready_buf->head + 1) & ready_buf->mask) != > > + ready_buf->tail) { > > + > > + /* if we are blocked waiting on a packet, skip it */ > > + if (order_buf->entries[order_buf->head] == NULL) { > > + order_buf->head++, order_head_adv++; > > + > > + if (order_buf->head == order_buf->size) > > + order_buf->head = 0; > > + } > > + > > + /* Move all ready entries that fit to the ready_buf */ > > + while (order_buf->entries[order_buf->head] != NULL) { > > + ready_buf->entries[ready_buf->head++] = > > + > > + order_buf->entries[order_buf->head]; > > + > > + order_buf->entries[order_buf->head++] = NULL; > > + order_head_adv++; > > + > > + if (ready_buf->head == ready_buf->size) > > + ready_buf->head = 0; > > + if (order_buf->head == order_buf->size) > > + order_buf->head = 0; > > + > > + if (((ready_buf->head+1) & ready_buf->mask) == ready_buf->tail) > > + break; > > + } > > + } > > + > > + b->min_seqn += order_head_adv; > > + /* Return the number of positions the order_buf head has moved */ > > + return order_head_adv; > > +} > > + > > +int > > +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf > > +*mbuf) { > > + uint32_t offset, position; > > + struct cir_buffer *order_buf = &b->order_buf; > > + > > + /* > > + * calculate the offset from the head pointer we need to go. > > + * The subtraction takes care of the sequence number wrapping. > > + * For example (using 16-bit for brevity): > > + * min_seqn = 0xFFFD > > + * mbuf_seqn = 0x0010 > > + * offset = 0x0010 - 0xFFFD = 0x13 > > + */ > > + offset = mbuf->seqn - b->min_seqn; > > + > > + /* > > + * action to take depends on offset. > > + * offset < buffer->size: the mbuf fits within the current window of > > + * sequence numbers we can reorder. EXPECTED CASE. > > + * offset > buffer->size: the mbuf is outside the current window. There > > + * are a number of cases to consider: > > + * 1. The packet sequence is just outside the window, then we need > > + * to see about shifting the head pointer and taking any ready > > + * to return packets out of the ring. If there was a delayed > > + * or dropped packet preventing drains from shifting the window > > + * this case will skip over the dropped packet instead, and any > > + * packets dequeued here will be returned on the next drain call. > > + * 2. The packet sequence number is vastly outside our window, taken > > + * here as having offset greater than twice the buffer size. In > > + * this case, the packet is probably an old or late packet that > > + * was previously skipped, so just enqueue the packet for > > + * immediate return on the next drain call, or else return error. > > + */ > > + if (offset < b->order_buf.size) { > > + position = (order_buf->head + offset) & order_buf->mask; > > + order_buf->entries[position] = mbuf; > > + } else if (offset < 2 * b->order_buf.size) { > > + if (rte_reorder_fill_overflow(b, offset - order_buf->size) < > > + offset - order_buf->size) { > > + /* Put in handling for enqueue straight to output */ > > + rte_errno = ENOSPC; > > + return -1; > > + } > > + offset = mbuf->seqn - b->min_seqn; > > + position = (order_buf->head + offset) & order_buf->mask; > > + order_buf->entries[position] = mbuf; > > + } else { > > + /* Put in handling for enqueue straight to output */ > > + rte_errno = ERANGE; > > + return -1; > > + } > > + return 0; > > +} > > + > > +unsigned int > > +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, > > + unsigned max_mbufs) > > +{ > > + unsigned int drain_cnt = 0; > > + > > + struct cir_buffer *order_buf = &b->order_buf, > > + *ready_buf = &b->ready_buf; > > + > > + /* Try to fetch requested number of mbufs from ready buffer */ > > + while ((drain_cnt < max_mbufs) && (ready_buf->tail != ready_buf->head)) > { > > + mbufs[drain_cnt++] = ready_buf->entries[ready_buf->tail++]; > > + if (ready_buf->tail == ready_buf->size) > > + ready_buf->tail = 0; > > + } > > + > > + /* > > + * If requested number of buffers not fetched from ready buffer, fetch > > + * remaining buffers from order buffer > > + */ > > + while ((drain_cnt < max_mbufs) && > > + (order_buf->entries[order_buf->head] != NULL)) { > > + mbufs[drain_cnt++] = order_buf->entries[order_buf->head]; > > + order_buf->entries[order_buf->head] = NULL; > > + b->min_seqn++; > > + order_buf->head++; > > + if (order_buf->head == order_buf->size) > > + order_buf->head = 0; > > + } > > + > > + return drain_cnt; > > +} > > diff --git a/lib/librte_reorder/rte_reorder.h > > b/lib/librte_reorder/rte_reorder.h > > new file mode 100644 > > index 0000000..3ec7011 > > --- /dev/null > > +++ b/lib/librte_reorder/rte_reorder.h > > @@ -0,0 +1,184 @@ > > +/*- > > + * BSD LICENSE > > + * > > + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. > > + * All rights reserved. > > + * > > + * Redistribution and use in source and binary forms, with or without > > + * modification, are permitted provided that the following conditions > > + * are met: > > + * > > + * * Redistributions of source code must retain the above copyright > > + * notice, this list of conditions and the following disclaimer. > > + * * Redistributions in binary form must reproduce the above copyright > > + * notice, this list of conditions and the following disclaimer in > > + * the documentation and/or other materials provided with the > > + * distribution. > > + * * Neither the name of Intel Corporation nor the names of its > > + * contributors may be used to endorse or promote products derived > > + * from this software without specific prior written permission. > > + * > > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND > CONTRIBUTORS > > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT > NOT > > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND > FITNESS FOR > > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > COPYRIGHT > > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, > INCIDENTAL, > > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT > NOT > > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS > OF USE, > > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED > AND ON ANY > > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR > TORT > > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > THE USE > > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > DAMAGE. > > + */ > > + > > +#ifndef _RTE_REORDER_H_ > > +#define _RTE_REORDER_H_ > > + > > +/** > > + * @file > > + * RTE reorder > > + * > > + * Reorder library is a component which is designed to > > + * provide ordering of out of ordered packets based on > > + * sequence number present in mbuf. > > + * > > + */ > > + > > +#ifdef __cplusplus > > +extern "C" { > > +#endif > > + > > +struct rte_reorder_buffer; > > + > > +/** > > + * Create a new reorder buffer instance > > + * > > + * Allocate memory and initialize a new reorder buffer in that > > + * memory, returning the reorder buffer pointer to the user > > + * > > + * @param name > > + * The name to be given to the reorder buffer instance. > > + * @param socket_id > > + * The NUMA node on which the memory for the reorder buffer > > + * instance is to be reserved. > > + * @param size > > + * Max number of elements that can be stored in the reorder buffer > > + * @return > > + * The initialized reorder buffer instance, or NULL on error > > + * On error case, rte_errno will be set appropriately: > > + * - ENOMEM - no appropriate memory area found in which to create > memzone > > + * - EINVAL - invalid parameters > > + */ > > +struct rte_reorder_buffer * > > +rte_reorder_create(const char *name, unsigned socket_id, unsigned int > > +size); > > + > > +/** > > + * Initializes given reorder buffer instance > > + * > > + * @param buf > > + * Pointer to memory area where reorder buffer instance > > + * should be initialized > > + * @param bufsize > > + * Size of the memory area to be used for reorder buffer instance > > + * initialization > > + * @param name > > + * The name to be given to the reorder buffer instance > > + * @param size > > + * Number of elements that can be stored in reorder buffer > > + * @return > > + * The initialized reorder buffer instance, or NULL on error > > + * On error case, rte_errno will be set appropriately: > > + * - EINVAL - invalid parameters > > + * - ENOMEM - not enough memory for reorder buffer instance > > + * initialization > > + */ > > +struct rte_reorder_buffer * > > +rte_reorder_init(void *buf, unsigned int bufsize, > > + const char *name, unsigned int size); > > + > > +/** > > + * Reset the given reorder buffer instance with initial values. > > + * > > + * @param b > > + * Reorder buffer instance which has to be reset > > + */ > > +void rte_reorder_reset(struct rte_reorder_buffer *b); > > + > > +/** > > + * Find an existing reorder buffer instance > > + * and return a pointer to it. > > + * > > + * @param name > > + * Name of the reorder buffer instacne as passed to rte_reorder_create() > > + * @return > > + * Pointer to reorder buffer instance or NULL if object not found with > rte_errno > > + * set appropriately. Possible rte_errno values include: > > + * - ENOENT - required entry not available to return. > > + * - E_RTE_NO_TAILQ - no tailq list could be got for the > > + * reorder instance list > > + */ > > +struct rte_reorder_buffer * > > +rte_reorder_find_existing(const char *name); > > + > > +/** > > + * Free reorder buffer instance. > > + * > > + * @param b > > + * reorder buffer instance > > + * @return > > + * None > > + */ > > +void > > +rte_reorder_free(struct rte_reorder_buffer *b); > > + > > +/** > > + * Insert given mbuf in reorder buffer in its correct position > > + * > > + * The given mbuf is to be reordered relative to other mbufs in the system. > > + * The mbuf must contain a sequence number which is then used to > > +place > > + * the buffer in the correct position in the reorder buffer. > > +Reordered > > + * packets can later be taken from the buffer using the > > +rte_reorder_drain() > > + * API. > > + * > > + * @param b > > + * Reorder buffer where the mbuf has to be inserted. > > + * @param mbuf > > + * mbuf of packet that needs to be inserted in reorder buffer. > > + * @return > > + * 0 on success > > + * -1 on error > > + * On error case, rte_errno will be set appropriately: > > + * - ENOSPC - Cannot move existing mbufs from reorder buffer to > accomodate ealry mbuf. > > + * But mbuf can be accomodated by performing drain and then insert. > > + * - ERANGE - Too early or late mbuf which is vastly out of > > + * range of expected window should be ingnored without any handling. > > + */ > > +int > > +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf > > +*mbuf); > > + > > +/** > > + * Fetch reordered buffers > > + * > > + * Returns a set of in-order buffers from the reorder buffer > > +structure. Gaps > > + * may be present in the sequence numbers of the mbuf if packets have > > +been > > + * delayed too long before reaching the reorder window, or have been > > +previously > > + * dropped by the system. > > + * > > + * @param b > > + * Reorder buffer instance from which packets are to be drained > > + * @param mbufs > > + * array of mbufs where reordered packets will be inserted from reorder > buffer > > + * @param max_mbufs > > + * the number of elements in the mbufs array. > > + * @return > > + * number of mbuf pointers written to mbufs. 0 <= N < max_mbufs. > > + */ > > +unsigned int > > +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, > > + unsigned max_mbufs); > > + > > +#ifdef __cplusplus > > +} > > +#endif > > + > > +#endif /* _RTE_REORDER_H_ */ > > -- > > 1.8.3.1 > > > > Using uint32_t's for sequence numbers is susceptible to overflow, they should > probably be uint64_t's unless there is a good reason not to. > > Cheers, Hi, Using Uint64_t for seqn consumes another 4 extra bytes in mbuf on cache line0. To avoid the same it is chosen uint32_t. Thanks, Reshma ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library 2015-01-07 16:39 [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library Reshma Pattan ` (3 preceding siblings ...) 2015-01-07 21:09 ` Richard Sanger @ 2015-01-20 8:00 ` Thomas Monjalon 2015-01-29 17:35 ` Gonzalez Monroy, Sergio 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 0/4] New Reorder Library Sergio Gonzalez Monroy 5 siblings, 1 reply; 44+ messages in thread From: Thomas Monjalon @ 2015-01-20 8:00 UTC (permalink / raw) To: Reshma Pattan; +Cc: dev Hi, 2015-01-07 16:39, Reshma Pattan: > 1)New library to provide reordering of out of ordered > mbufs based on sequence number of mbuf. Library uses reorder buffer structure > which in tern uses two circular buffers called ready and order buffers. > *rte_reorder_create API creates instance of reorder buffer. > *rte_reorder_init API initializes given reorder buffer instance. > *rte_reorder_reset API resets given reorder buffer instance. > *rte_reorder_insert API inserts the mbuf into order circular buffer. > *rte_reorder_fill_overflow moves mbufs from order buffer to ready buffer > to accomodate early packets in order buffer. > *rte_reorder_drain API provides draining facility to fetch out > reordered mbufs from order and ready buffers. > > Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> > Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> I think 2 things are missing in this patchset: 1) Could you show some performance numbers to compare a simple forwarding with and without this library, in the commit log? 2) Could you add some documentation in doc/ directory for programmer's guide? Thank you -- Thomas ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library 2015-01-20 8:00 ` Thomas Monjalon @ 2015-01-29 17:35 ` Gonzalez Monroy, Sergio 2015-01-29 20:39 ` Neil Horman 0 siblings, 1 reply; 44+ messages in thread From: Gonzalez Monroy, Sergio @ 2015-01-29 17:35 UTC (permalink / raw) To: Thomas Monjalon, Pattan, Reshma; +Cc: dev Hi Thomas, > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Thomas Monjalon > Sent: Tuesday, January 20, 2015 8:01 AM > To: Pattan, Reshma > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library > > Hi, > > 2015-01-07 16:39, Reshma Pattan: > > 1)New library to provide reordering of out of ordered > > mbufs based on sequence number of mbuf. Library uses reorder > buffer structure > > which in tern uses two circular buffers called ready and order buffers. > > *rte_reorder_create API creates instance of reorder buffer. > > *rte_reorder_init API initializes given reorder buffer instance. > > *rte_reorder_reset API resets given reorder buffer instance. > > *rte_reorder_insert API inserts the mbuf into order circular buffer. > > *rte_reorder_fill_overflow moves mbufs from order buffer to ready > buffer > > to accomodate early packets in order buffer. > > *rte_reorder_drain API provides draining facility to fetch out > > reordered mbufs from order and ready buffers. > > > > Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> > > Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> > > I think 2 things are missing in this patchset: > > 1) Could you show some performance numbers to compare a simple > forwarding with and without this library, in the commit log? > I'm not allowed to provide specific performance numbers. I could try to give some kind of relative performance estimate (%), but anyone else is free to provide those numbers. > 2) Could you add some documentation in doc/ directory for programmer's > guide? > I'll be sending a v2 shortly with a new entry for the programmers guide. Thank you, Sergio > Thank you > -- > Thomas ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library 2015-01-29 17:35 ` Gonzalez Monroy, Sergio @ 2015-01-29 20:39 ` Neil Horman 2015-01-30 9:35 ` Gonzalez Monroy, Sergio 0 siblings, 1 reply; 44+ messages in thread From: Neil Horman @ 2015-01-29 20:39 UTC (permalink / raw) To: Gonzalez Monroy, Sergio; +Cc: dev On Thu, Jan 29, 2015 at 05:35:09PM +0000, Gonzalez Monroy, Sergio wrote: > Hi Thomas, > > > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Thomas Monjalon > > Sent: Tuesday, January 20, 2015 8:01 AM > > To: Pattan, Reshma > > Cc: dev@dpdk.org > > Subject: Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library > > > > Hi, > > > > 2015-01-07 16:39, Reshma Pattan: > > > 1)New library to provide reordering of out of ordered > > > mbufs based on sequence number of mbuf. Library uses reorder > > buffer structure > > > which in tern uses two circular buffers called ready and order buffers. > > > *rte_reorder_create API creates instance of reorder buffer. > > > *rte_reorder_init API initializes given reorder buffer instance. > > > *rte_reorder_reset API resets given reorder buffer instance. > > > *rte_reorder_insert API inserts the mbuf into order circular buffer. > > > *rte_reorder_fill_overflow moves mbufs from order buffer to ready > > buffer > > > to accomodate early packets in order buffer. > > > *rte_reorder_drain API provides draining facility to fetch out > > > reordered mbufs from order and ready buffers. > > > > > > Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> > > > Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> > > > > I think 2 things are missing in this patchset: > > > > 1) Could you show some performance numbers to compare a simple > > forwarding with and without this library, in the commit log? > > > I'm not allowed to provide specific performance numbers. Can you elaborate on this? Why can you not provide specific performance numbers from your testing? Is there some concern over the validity of the measurements? ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library 2015-01-29 20:39 ` Neil Horman @ 2015-01-30 9:35 ` Gonzalez Monroy, Sergio 0 siblings, 0 replies; 44+ messages in thread From: Gonzalez Monroy, Sergio @ 2015-01-30 9:35 UTC (permalink / raw) To: Neil Horman; +Cc: dev > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Thursday, January 29, 2015 8:40 PM > To: Gonzalez Monroy, Sergio > Cc: Thomas Monjalon; Pattan, Reshma; dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library > > On Thu, Jan 29, 2015 at 05:35:09PM +0000, Gonzalez Monroy, Sergio wrote: > > Hi Thomas, > > > > > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Thomas > Monjalon > > > Sent: Tuesday, January 20, 2015 8:01 AM > > > To: Pattan, Reshma > > > Cc: dev@dpdk.org > > > Subject: Re: [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder > > > library > > > > > > Hi, > > > > > > 2015-01-07 16:39, Reshma Pattan: > > > > 1)New library to provide reordering of out of ordered > > > > mbufs based on sequence number of mbuf. Library uses > > > > reorder > > > buffer structure > > > > which in tern uses two circular buffers called ready and order > buffers. > > > > *rte_reorder_create API creates instance of reorder buffer. > > > > *rte_reorder_init API initializes given reorder buffer instance. > > > > *rte_reorder_reset API resets given reorder buffer instance. > > > > *rte_reorder_insert API inserts the mbuf into order circular > buffer. > > > > *rte_reorder_fill_overflow moves mbufs from order > > > > buffer to ready > > > buffer > > > > to accomodate early packets in order buffer. > > > > *rte_reorder_drain API provides draining facility to fetch out > > > > reordered mbufs from order and ready buffers. > > > > > > > > Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> > > > > Signed-off-by: Richardson Bruce > > > > <bruce.richardson@intel.com> > > > > > > I think 2 things are missing in this patchset: > > > > > > 1) Could you show some performance numbers to compare a simple > > > forwarding with and without this library, in the commit log? > > > > > I'm not allowed to provide specific performance numbers. > Can you elaborate on this? Why can you not provide specific performance > numbers from your testing? Is there some concern over the validity of the > measurements? Hi Neil, As far as I know, that is exactly the reason. Any Intel specific performance data goes through a performance validation team before being released. Thanks, Sergio ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v2 0/4] New Reorder Library 2015-01-07 16:39 [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library Reshma Pattan ` (4 preceding siblings ...) 2015-01-20 8:00 ` Thomas Monjalon @ 2015-01-30 13:14 ` Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 1/4] reorder: new reorder library Sergio Gonzalez Monroy ` (4 more replies) 5 siblings, 5 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-01-30 13:14 UTC (permalink / raw) To: dev This series introduces the new reorder library along with unit tests, sample app and a new entry in the programmers guide describing the library. The library provides reordering of mbufs based on their sequence number. As mention in the patch describing the library, one use case is the packet distributor. The distributor receives packets, assigns them a sequence number and sends them to the workers. The workers process those packets and return them to the distributor. The distributor collects out-of-order packets from the workers and uses this library to reorder the packets based on the sequence number they were assigned. v2: - add programmers guide entry describing the library - use malloc instead of memzone to allocate memory - modify create and init implementation, init takes a reorder buffer as input and create reserves memory and call init. - update unit tests Sergio Gonzalez Monroy (4): reorder: new reorder library app: New reorder unit test examples: new sample app packet_ordering doc: new reorder library description app/test/Makefile | 2 + app/test/test_reorder.c | 393 +++++++++++++++ config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/reorder_lib.rst | 115 +++++ examples/packet_ordering/Makefile | 50 ++ examples/packet_ordering/main.c | 637 +++++++++++++++++++++++++ lib/Makefile | 1 + lib/librte_eal/common/include/rte_tailq_elem.h | 2 + lib/librte_mbuf/rte_mbuf.h | 3 + lib/librte_reorder/Makefile | 50 ++ lib/librte_reorder/rte_reorder.c | 416 ++++++++++++++++ lib/librte_reorder/rte_reorder.h | 181 +++++++ mk/rte.app.mk | 4 + 15 files changed, 1865 insertions(+) create mode 100644 app/test/test_reorder.c create mode 100644 doc/guides/prog_guide/reorder_lib.rst create mode 100644 examples/packet_ordering/Makefile create mode 100644 examples/packet_ordering/main.c create mode 100644 lib/librte_reorder/Makefile create mode 100644 lib/librte_reorder/rte_reorder.c create mode 100644 lib/librte_reorder/rte_reorder.h -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v2 1/4] reorder: new reorder library 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 0/4] New Reorder Library Sergio Gonzalez Monroy @ 2015-01-30 13:14 ` Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 2/4] app: New reorder unit test Sergio Gonzalez Monroy ` (3 subsequent siblings) 4 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-01-30 13:14 UTC (permalink / raw) To: dev This library provides reordering capability for out of order mbufs based on a sequence number in the mbuf structure. Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_eal/common/include/rte_tailq_elem.h | 2 + lib/librte_mbuf/rte_mbuf.h | 3 + lib/librte_reorder/Makefile | 50 +++ lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++++++++++++ lib/librte_reorder/rte_reorder.h | 181 +++++++++++ 8 files changed, 663 insertions(+) create mode 100644 lib/librte_reorder/Makefile create mode 100644 lib/librte_reorder/rte_reorder.c create mode 100644 lib/librte_reorder/rte_reorder.h diff --git a/config/common_bsdapp b/config/common_bsdapp index 9177db1..e3e0e94 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -334,6 +334,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 2f9643b..b5ec730 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -342,6 +342,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/lib/Makefile b/lib/Makefile index 0ffc982..5919d32 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -65,6 +65,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += librte_distributor DIRS-$(CONFIG_RTE_LIBRTE_PORT) += librte_port DIRS-$(CONFIG_RTE_LIBRTE_TABLE) += librte_table DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += librte_pipeline +DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += librte_reorder ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni diff --git a/lib/librte_eal/common/include/rte_tailq_elem.h b/lib/librte_eal/common/include/rte_tailq_elem.h index f74fc7c..3013869 100644 --- a/lib/librte_eal/common/include/rte_tailq_elem.h +++ b/lib/librte_eal/common/include/rte_tailq_elem.h @@ -84,6 +84,8 @@ rte_tailq_elem(RTE_TAILQ_ACL, "RTE_ACL") rte_tailq_elem(RTE_TAILQ_DISTRIBUTOR, "RTE_DISTRIBUTOR") +rte_tailq_elem(RTE_TAILQ_REORDER, "RTE_REORDER") + rte_tailq_end(RTE_TAILQ_NUM) #undef rte_tailq_elem diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h index 16059c6..ed27eb8 100644 --- a/lib/librte_mbuf/rte_mbuf.h +++ b/lib/librte_mbuf/rte_mbuf.h @@ -262,6 +262,9 @@ struct rte_mbuf { uint32_t usr; /**< User defined tags. See @rte_distributor_process */ } hash; /**< hash information */ + /* sequence number - field used in distributor and reorder library */ + uint32_t seqn; + /* second cache line - fields only used in slow path or on TX */ MARKER cacheline1 __rte_cache_aligned; diff --git a/lib/librte_reorder/Makefile b/lib/librte_reorder/Makefile new file mode 100644 index 0000000..12b916f --- /dev/null +++ b/lib/librte_reorder/Makefile @@ -0,0 +1,50 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(RTE_SDK)/mk/rte.vars.mk + +# library name +LIB = librte_reorder.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) + +# all source are stored in SRCS-y +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) := rte_reorder.c + +# install this header file +SYMLINK-$(CONFIG_RTE_LIBRTE_REORDER)-include := rte_reorder.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_eal + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_reorder/rte_reorder.c b/lib/librte_reorder/rte_reorder.c new file mode 100644 index 0000000..42d2a47 --- /dev/null +++ b/lib/librte_reorder/rte_reorder.c @@ -0,0 +1,416 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <inttypes.h> +#include <string.h> + +#include <rte_log.h> +#include <rte_mbuf.h> +#include <rte_memzone.h> +#include <rte_eal_memconfig.h> +#include <rte_errno.h> +#include <rte_tailq.h> +#include <rte_malloc.h> + +#include "rte_reorder.h" + +TAILQ_HEAD(rte_reorder_list, rte_tailq_entry); + +#define NO_FLAGS 0 +#define RTE_REORDER_PREFIX "RO_" +#define RTE_REORDER_NAMESIZE 32 + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDER RTE_LOGTYPE_USER1 + +/* A generic circular buffer */ +struct cir_buffer { + unsigned int size; /**< Number of entries that can be stored */ + unsigned int mask; /**< [buffer_size - 1]: used for wrap-around */ + unsigned int head; /**< insertion point in buffer */ + unsigned int tail; /**< extraction point in buffer */ + struct rte_mbuf **entries; +} __rte_cache_aligned; + +/* The reorder buffer data structure itself */ +struct rte_reorder_buffer { + char name[RTE_REORDER_NAMESIZE]; + uint32_t min_seqn; /**< Lowest seq. number that can be in the buffer */ + unsigned int memsize; /**< memory area size of reorder buffer */ + struct cir_buffer ready_buf; /**< temp buffer for dequeued entries */ + struct cir_buffer order_buf; /**< buffer used to reorder entries */ +} __rte_cache_aligned; + +static void +rte_reorder_free_mbufs(struct rte_reorder_buffer *b); + +struct rte_reorder_buffer * +rte_reorder_init(struct rte_reorder_buffer *b, unsigned int bufsize, + const char *name, unsigned int size) +{ + const unsigned int min_bufsize = sizeof(*b) + + (2 * size * sizeof(struct rte_mbuf *)); + + if (b == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer parameter:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (bufsize < min_bufsize) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer memory size: %u, " + "minimum required: %u\n", bufsize, min_bufsize); + rte_errno = EINVAL; + return NULL; + } + + memset(b, 0, bufsize); + snprintf(b->name, sizeof(b->name), "%s", name); + b->memsize = bufsize; + b->order_buf.size = b->ready_buf.size = size; + b->order_buf.mask = b->ready_buf.mask = size - 1; + b->ready_buf.entries = (void *)&b[1]; + b->order_buf.entries = RTE_PTR_ADD(&b[1], + size * sizeof(b->ready_buf.entries[0])); + + return b; +} + +struct rte_reorder_buffer* +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + const unsigned int bufsize = sizeof(struct rte_reorder_buffer) + + (2 * size * sizeof(struct rte_mbuf *)); + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + /* Check user arguments. */ + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* guarantee there's no existing */ + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + if (te != NULL) + goto exit; + + /* allocate tailq entry */ + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); + if (te == NULL) { + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); + rte_errno = ENOMEM; + b = NULL; + goto exit; + } + + /* Allocate memory to store the reorder buffer structure. */ + b = rte_zmalloc_socket("REORDER_BUFFER", bufsize, 0, socket_id); + if (b == NULL) { + RTE_LOG(ERR, REORDER, "Memzone allocation failed\n"); + rte_errno = ENOMEM; + rte_free(te); + } else { + rte_reorder_init(b, bufsize, name, size); + te->data = (void *)b; + TAILQ_INSERT_TAIL(reorder_list, te, next); + } + +exit: + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return b; +} + +void +rte_reorder_reset(struct rte_reorder_buffer *b) +{ + char name[RTE_REORDER_NAMESIZE]; + + rte_reorder_free_mbufs(b); + snprintf(name, sizeof(name), "%s", b->name); + /* No error checking as current values should be valid */ + rte_reorder_init(b, b->memsize, name, b->order_buf.size); +} + +static void +rte_reorder_free_mbufs(struct rte_reorder_buffer *b) +{ + unsigned i; + + /* Free up the mbufs of order buffer & ready buffer */ + for (i = 0; i < b->order_buf.size; i++) { + if (b->order_buf.entries[i]) + rte_pktmbuf_free(b->order_buf.entries[i]); + if (b->ready_buf.entries[i]) + rte_pktmbuf_free(b->ready_buf.entries[i]); + } +} + +void +rte_reorder_free(struct rte_reorder_buffer *b) +{ + struct rte_reorder_list *reorder_list; + struct rte_tailq_entry *te; + + /* Check user arguments. */ + if (b == NULL) + return; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* find our tailq entry */ + TAILQ_FOREACH(te, reorder_list, next) { + if (te->data == (void *) b) + break; + } + if (te == NULL) { + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return; + } + + TAILQ_REMOVE(reorder_list, te, next); + + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + + rte_reorder_free_mbufs(b); + + rte_free(b); + rte_free(te); +} + +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); + + if (te == NULL) { + rte_errno = ENOENT; + return NULL; + } + + return b; +} + +static unsigned +rte_reorder_fill_overflow(struct rte_reorder_buffer *b, unsigned n) +{ + /* + * 1. Move all ready entries that fit to the ready_buf + * 2. check if we meet the minimum needed (n). + * 3. If not, then skip any gaps and keep moving. + * 4. If at any point the ready buffer is full, stop + * 5. Return the number of positions the order_buf head has moved + */ + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + unsigned int order_head_adv = 0; + + /* + * move at least n packets to ready buffer, assuming ready buffer + * has room for those packets. + */ + while (order_head_adv < n && + ((ready_buf->head + 1) & ready_buf->mask) != ready_buf->tail) { + + /* if we are blocked waiting on a packet, skip it */ + if (order_buf->entries[order_buf->head] == NULL) { + order_buf->head = (order_buf->head + 1) & order_buf->mask; + order_head_adv++; + } + + /* Move all ready entries that fit to the ready_buf */ + while (order_buf->entries[order_buf->head] != NULL) { + ready_buf->entries[ready_buf->head] = + order_buf->entries[order_buf->head]; + + order_buf->entries[order_buf->head] = NULL; + order_head_adv++; + + order_buf->head = (order_buf->head + 1) & order_buf->mask; + + if (((ready_buf->head + 1) & ready_buf->mask) == ready_buf->tail) + break; + + ready_buf->head = (ready_buf->head + 1) & ready_buf->mask; + } + } + + b->min_seqn += order_head_adv; + /* Return the number of positions the order_buf head has moved */ + return order_head_adv; +} + +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf) +{ + uint32_t offset, position; + struct cir_buffer *order_buf = &b->order_buf; + + /* + * calculate the offset from the head pointer we need to go. + * The subtraction takes care of the sequence number wrapping. + * For example (using 16-bit for brevity): + * min_seqn = 0xFFFD + * mbuf_seqn = 0x0010 + * offset = 0x0010 - 0xFFFD = 0x13 + */ + offset = mbuf->seqn - b->min_seqn; + + /* + * action to take depends on offset. + * offset < buffer->size: the mbuf fits within the current window of + * sequence numbers we can reorder. EXPECTED CASE. + * offset > buffer->size: the mbuf is outside the current window. There + * are a number of cases to consider: + * 1. The packet sequence is just outside the window, then we need + * to see about shifting the head pointer and taking any ready + * to return packets out of the ring. If there was a delayed + * or dropped packet preventing drains from shifting the window + * this case will skip over the dropped packet instead, and any + * packets dequeued here will be returned on the next drain call. + * 2. The packet sequence number is vastly outside our window, taken + * here as having offset greater than twice the buffer size. In + * this case, the packet is probably an old or late packet that + * was previously skipped, so just enqueue the packet for + * immediate return on the next drain call, or else return error. + */ + if (offset < b->order_buf.size) { + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else if (offset < 2 * b->order_buf.size) { + if (rte_reorder_fill_overflow(b, offset + 1 - order_buf->size) + < (offset + 1 - order_buf->size)) { + /* Put in handling for enqueue straight to output */ + rte_errno = ENOSPC; + return -1; + } + offset = mbuf->seqn - b->min_seqn; + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else { + /* Put in handling for enqueue straight to output */ + rte_errno = ERANGE; + return -1; + } + return 0; +} + +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs) +{ + unsigned int drain_cnt = 0; + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + /* Try to fetch requested number of mbufs from ready buffer */ + while ((drain_cnt < max_mbufs) && (ready_buf->tail != ready_buf->head)) { + mbufs[drain_cnt++] = ready_buf->entries[ready_buf->tail]; + ready_buf->tail = (ready_buf->tail + 1) & ready_buf->mask; + } + + /* + * If requested number of buffers not fetched from ready buffer, fetch + * remaining buffers from order buffer + */ + while ((drain_cnt < max_mbufs) && + (order_buf->entries[order_buf->head] != NULL)) { + mbufs[drain_cnt++] = order_buf->entries[order_buf->head]; + order_buf->entries[order_buf->head] = NULL; + b->min_seqn++; + order_buf->head = (order_buf->head + 1) & order_buf->mask; + } + + return drain_cnt; +} diff --git a/lib/librte_reorder/rte_reorder.h b/lib/librte_reorder/rte_reorder.h new file mode 100644 index 0000000..eda23ba --- /dev/null +++ b/lib/librte_reorder/rte_reorder.h @@ -0,0 +1,181 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_REORDER_H_ +#define _RTE_REORDER_H_ + +/** + * @file + * RTE reorder + * + * Reorder library is a component which is designed to + * provide ordering of out of ordered packets based on + * sequence number present in mbuf. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rte_reorder_buffer; + +/** + * Create a new reorder buffer instance + * + * Allocate memory and initialize a new reorder buffer in that + * memory, returning the reorder buffer pointer to the user + * + * @param name + * The name to be given to the reorder buffer instance. + * @param socket_id + * The NUMA node on which the memory for the reorder buffer + * instance is to be reserved. + * @param size + * Max number of elements that can be stored in the reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - ENOMEM - no appropriate memory area found in which to create memzone + * - EINVAL - invalid parameters + */ +struct rte_reorder_buffer * +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size); + +/** + * Initializes given reorder buffer instance + * + * @param b + * Reorder buffer instance to initialize + * @param bufsize + * Size of the reorder buffer + * @param name + * The name to be given to the reorder buffer + * @param size + * Number of elements that can be stored in reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - EINVAL - invalid parameters + */ +struct rte_reorder_buffer * +rte_reorder_init(struct rte_reorder_buffer *b, unsigned int bufsize, + const char *name, unsigned int size); + +/** + * Find an existing reorder buffer instance + * and return a pointer to it. + * + * @param name + * Name of the reorder buffer instacne as passed to rte_reorder_create() + * @return + * Pointer to reorder buffer instance or NULL if object not found with rte_errno + * set appropriately. Possible rte_errno values include: + * - ENOENT - required entry not available to return. + * - E_RTE_NO_TAILQ - no tailq list could be got for the + * reorder instance list + */ +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name); + +/** + * Reset the given reorder buffer instance with initial values. + * + * @param b + * Reorder buffer instance which has to be reset + */ +void +rte_reorder_reset(struct rte_reorder_buffer *b); + +/** + * Free reorder buffer instance. + * + * @param b + * reorder buffer instance + * @return + * None + */ +void +rte_reorder_free(struct rte_reorder_buffer *b); + +/** + * Insert given mbuf in reorder buffer in its correct position + * + * The given mbuf is to be reordered relative to other mbufs in the system. + * The mbuf must contain a sequence number which is then used to place + * the buffer in the correct position in the reorder buffer. Reordered + * packets can later be taken from the buffer using the rte_reorder_drain() + * API. + * + * @param b + * Reorder buffer where the mbuf has to be inserted. + * @param mbuf + * mbuf of packet that needs to be inserted in reorder buffer. + * @return + * 0 on success + * -1 on error + * On error case, rte_errno will be set appropriately: + * - ENOSPC - Cannot move existing mbufs from reorder buffer to accommodate + * ealry mbuf, but it can be accomodated by performing drain and then insert. + * - ERANGE - Too early or late mbuf which is vastly out of range of expected + * window should be ingnored without any handling. + */ +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf); + +/** + * Fetch reordered buffers + * + * Returns a set of in-order buffers from the reorder buffer structure. Gaps + * may be present in the sequence numbers of the mbuf if packets have been + * delayed too long before reaching the reorder window, or have been previously + * dropped by the system. + * + * @param b + * Reorder buffer instance from which packets are to be drained + * @param mbufs + * array of mbufs where reordered packets will be inserted from reorder buffer + * @param max_mbufs + * the number of elements in the mbufs array. + * @return + * number of mbuf pointers written to mbufs. 0 <= N < max_mbufs. + */ +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs); + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_REORDER_H_ */ -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v2 2/4] app: New reorder unit test 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 0/4] New Reorder Library Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 1/4] reorder: new reorder library Sergio Gonzalez Monroy @ 2015-01-30 13:14 ` Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 3/4] examples: new sample app packet_ordering Sergio Gonzalez Monroy ` (2 subsequent siblings) 4 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-01-30 13:14 UTC (permalink / raw) To: dev Adding new reorder unit test for the test app. The command to run the unit test from the test shell is: reorder_autotest Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- app/test/Makefile | 2 + app/test/test_reorder.c | 393 ++++++++++++++++++++++++++++++++++++++++++++++++ mk/rte.app.mk | 4 + 3 files changed, 399 insertions(+) create mode 100644 app/test/test_reorder.c diff --git a/app/test/Makefile b/app/test/Makefile index 4311f96..24b27d7 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -124,6 +124,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += test_ivshmem.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor_perf.c +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) += test_reorder.c + SRCS-y += test_devargs.c SRCS-y += virtual_pmd.c SRCS-y += packet_burst_generator.c diff --git a/app/test/test_reorder.c b/app/test/test_reorder.c new file mode 100644 index 0000000..3217206 --- /dev/null +++ b/app/test/test_reorder.c @@ -0,0 +1,393 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test.h" +#include "stdio.h" + +#include <unistd.h> +#include <string.h> + +#include <rte_cycles.h> +#include <rte_errno.h> +#include <rte_mbuf.h> +#include <rte_reorder.h> +#include <rte_lcore.h> +#include <rte_malloc.h> + +#include "test.h" + +#define BURST 32 +#define REORDER_BUFFER_SIZE 16384 +#define NUM_MBUFS (2*REORDER_BUFFER_SIZE) +#define REORDER_BUFFER_SIZE_INVALID 2049 +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) + +struct reorder_unittest_params { + struct rte_mempool *p; + struct rte_reorder_buffer *b; +}; + +static struct reorder_unittest_params default_params = { + .p = NULL, + .b = NULL +}; + +static struct reorder_unittest_params *test_params = &default_params; + +static int +test_reorder_create(void) +{ + struct rte_reorder_buffer *b = NULL; + + b = rte_reorder_create(NULL, rte_socket_id(), REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on create() with NULL name"); + + b = rte_reorder_create("PKT", rte_socket_id(), REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on create() with invalid buffer size param."); + + b = rte_reorder_create("PKT_RO1", rte_socket_id(), REORDER_BUFFER_SIZE); + printf("DEBUG: b= %p, orig_b= %p\n", b, test_params->b); + TEST_ASSERT_EQUAL(b, test_params->b, + "New reorder instance created with already existing name"); + + return 0; +} + +static int +test_reorder_init(void) +{ + struct rte_reorder_buffer *b = NULL; + unsigned int size; + /* + * The minimum memory area size that should be passed to library is, + * sizeof(struct rte_reorder_buffer) + (2 * size * sizeof(struct rte_mbuf *)); + * Otherwise error will be thrown + */ + + size = 100; + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with NULL buffer."); + + b = rte_malloc(NULL, size, 0); + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid mem zone size."); + rte_free(b); + + size = 262336; + b = rte_malloc(NULL, size, 0); + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid buffer size param."); + + b = rte_reorder_init(b, size, NULL, REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid name."); + rte_free(b); + + return 0; +} + +static int +test_reorder_find_existing(void) +{ + struct rte_reorder_buffer *b = NULL; + + /* Try to find existing reorder buffer instance */ + b = rte_reorder_find_existing("PKT_RO1"); + TEST_ASSERT_EQUAL(b, test_params->b, + "existing reorder buffer instance not found"); + + /* Try to find non existing reorder buffer instance */ + b = rte_reorder_find_existing("ro_find_non_existing"); + TEST_ASSERT((b == NULL) && (rte_errno == ENOENT), + "non existing reorder buffer instance found"); + + return 0; +} + +static int +test_reorder_free(void) +{ + struct rte_reorder_buffer *b1 = NULL, *b2 = NULL; + const char *name = "test_free"; + + b1 = rte_reorder_create(name, rte_socket_id(), 8); + TEST_ASSERT_NOT_NULL(b1, "Failed to create reorder buffer."); + + b2 = rte_reorder_find_existing(name); + TEST_ASSERT_EQUAL(b1, b2, "Failed to find existing reorder buffer"); + + rte_reorder_free(b1); + + b2 = rte_reorder_find_existing(name); + TEST_ASSERT((b2 == NULL) && (rte_errno == ENOENT), + "Found previously freed reorder buffer"); + + return 0; +} + +static int +test_reorder_insert(void) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_mempool *p = test_params->p; + const unsigned int size = 4; + const unsigned int num_bufs = 6; + struct rte_mbuf *bufs[num_bufs]; + int ret = 0; + unsigned i; + + /* This would create a reorder buffer instance consisting of: + * reorder_seq = 0 + * ready_buf: RB[size] = {NULL, NULL, NULL, NULL} + * order_buf: OB[size] = {NULL, NULL, NULL, NULL} + */ + b = rte_reorder_create("test_insert", rte_socket_id(), size); + TEST_ASSERT_NOT_NULL(b, "Failed to create reorder buffer"); + + ret = rte_mempool_get_bulk(p, (void *)bufs, num_bufs); + TEST_ASSERT_SUCCESS(ret, "Error getting mbuf from pool"); + + /* late packet */ + bufs[0]->seqn = 3 * size; + ret = rte_reorder_insert(b, bufs[0]); + if (!((ret == -1) && (rte_errno == ERANGE))) { + printf("%s:%d: No error inserting late packet with seqn:" + " 3 * size\n", __func__, __LINE__); + ret = -1; + goto exit; + } + + for (i = 0; i < num_bufs; i++) + bufs[i]->seqn = i; + + /* This should fill up order buffer: + * reorder_seq = 0 + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {0, 1, 2, 3} + */ + for (i = 0; i < size; i++) { + ret = rte_reorder_insert(b, bufs[i]); + if (ret != 0) { + printf("%s:%d: Error inserting packet with seqn less than size\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + } + + /* early packet - should move mbufs to ready buf and move sequence window + * reorder_seq = 4 + * RB[] = {0, 1, 2, 3} + * OB[] = {4, NULL, NULL, NULL} + */ + ret = rte_reorder_insert(b, bufs[4]); + if (ret != 0) { + printf("%s:%d: Error inserting early packet with seqn: size\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + /* early packet from current sequence window - full ready buffer */ + bufs[5]->seqn = 2 * size; + ret = rte_reorder_insert(b, bufs[5]); + if (!((ret == -1) && (rte_errno == ENOSPC))) { + printf("%s:%d: No error inserting early packet with full ready buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + ret = 0; +exit: + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + rte_reorder_free(b); + return ret; +} + +static int +test_reorder_drain(void) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_mempool *p = test_params->p; + const unsigned int size = 4; + const unsigned int num_bufs = 10; + struct rte_mbuf *bufs[num_bufs]; + int ret = 0; + unsigned i, cnt; + + /* This would create a reorder buffer instance consisting of: + * reorder_seq = 0 + * ready_buf: RB[size] = {NULL, NULL, NULL, NULL} + * order_buf: OB[size] = {NULL, NULL, NULL, NULL} + */ + b = rte_reorder_create("test_insert", rte_socket_id(), size); + TEST_ASSERT_NOT_NULL(b, "Failed to create reorder buffer"); + + ret = rte_mempool_get_bulk(p, (void *)bufs, num_bufs); + TEST_ASSERT_SUCCESS(ret, "Error getting mbuf from pool"); + + /* Check no drained packets if reorder is empty */ + cnt = rte_reorder_drain(b, bufs, 1); + if (cnt != 0) { + printf("%s:%d: drained packets from empty reorder buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + for (i = 0; i < num_bufs; i++) + bufs[i]->seqn = i; + + /* Insert packet with seqn 1: + * reorder_seq = 0 + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {NULL, 1, NULL, NULL} + */ + rte_reorder_insert(b, bufs[1]); + + /* Check no drained packets if no ready/order packets */ + cnt = rte_reorder_drain(b, bufs, 1); + if (cnt != 0) { + printf("%s:%d: drained packets from empty reorder buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + /* Insert more packets + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {0, 1, NULL, 3} + */ + rte_reorder_insert(b, bufs[0]); + rte_reorder_insert(b, bufs[3]); + + /* drained expected packets */ + cnt = rte_reorder_drain(b, bufs, 4); + if (cnt != 2) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + ret = -1; + goto exit; + } + + /* + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {NULL, 3, NULL, NULL} + */ + + rte_reorder_insert(b, bufs[4]); + rte_reorder_insert(b, bufs[7]); + + /* + * RB[] = {3, 4, NULL, NULL} + * OB[] = {NULL, NULL, 7, NULL} + */ + + cnt = rte_reorder_drain(b, bufs, 4); + if (cnt != 2) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + ret = -1; + goto exit; + } + + ret = 0; +exit: + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + rte_reorder_free(b); + return ret; +} + +static int +test_setup(void) +{ + /* reorder buffer instance creation */ + if (test_params->b == NULL) { + test_params->b = rte_reorder_create("PKT_RO1", rte_socket_id(), + REORDER_BUFFER_SIZE); + if (test_params->b == NULL) { + printf("%s: Error creating reorder buffer instance b\n", + __func__); + return -1; + } + } else + rte_reorder_reset(test_params->b); + + /* mempool creation */ + if (test_params->p == NULL) { + test_params->p = rte_mempool_create("RO_MBUF_POOL", NUM_MBUFS, + MBUF_SIZE, BURST, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->p == NULL) { + printf("%s: Error creating mempool\n", __func__); + return -1; + } + } + return 0; +} + +static struct unit_test_suite reorder_test_suite = { + + .setup = test_setup, + .suite_name = "Reorder Unit Test Suite", + .unit_test_cases = { + TEST_CASE(test_reorder_create), + TEST_CASE(test_reorder_init), + TEST_CASE(test_reorder_find_existing), + TEST_CASE(test_reorder_free), + TEST_CASE(test_reorder_insert), + TEST_CASE(test_reorder_drain), + TEST_CASES_END() + } +}; + +static int +test_reorder(void) +{ + return unit_test_suite_runner(&reorder_test_suite); +} + +static struct test_command reorder_cmd = { + .command = "reorder_autotest", + .callback = test_reorder, +}; +REGISTER_TEST_COMMAND(reorder_cmd); diff --git a/mk/rte.app.mk b/mk/rte.app.mk index e1a0dbf..2a08acb 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -67,6 +67,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_DISTRIBUTOR),y) LDLIBS += -lrte_distributor endif +ifeq ($(CONFIG_RTE_LIBRTE_REORDER),y) +LDLIBS += -lrte_reorder +endif + ifeq ($(CONFIG_RTE_LIBRTE_KNI),y) ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) LDLIBS += -lrte_kni -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v2 3/4] examples: new sample app packet_ordering 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 0/4] New Reorder Library Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 1/4] reorder: new reorder library Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 2/4] app: New reorder unit test Sergio Gonzalez Monroy @ 2015-01-30 13:14 ` Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 4/4] doc: new reorder library description Sergio Gonzalez Monroy 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy 4 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-01-30 13:14 UTC (permalink / raw) To: dev This new app makes use of the librte_reorder library. It requires at least 3 lcores for RX, Workers (1 or more) and TX threads. Communication between RX-Workers and Workers-TX is done by using rings. The flow of mbufs is the following: * RX thread gets mbufs from driver, set sequence number and enqueue them in ring. * Workers dequeue mbufs from ring, do some 'work' and enqueue mbufs in ring. * TX dequeue mbufs from ring, inserts them in reorder buffer, drains mbufs from reorder and sends them to the driver. Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- examples/packet_ordering/Makefile | 50 +++ examples/packet_ordering/main.c | 637 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 687 insertions(+) create mode 100644 examples/packet_ordering/Makefile create mode 100644 examples/packet_ordering/main.c diff --git a/examples/packet_ordering/Makefile b/examples/packet_ordering/Makefile new file mode 100644 index 0000000..44bd2e1 --- /dev/null +++ b/examples/packet_ordering/Makefile @@ -0,0 +1,50 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overridden by command line or environment +RTE_TARGET ?= x86_64-ivshmem-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +# binary name +APP = packet_ordering + +# all source are stored in SRCS-y +SRCS-y := main.c + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/packet_ordering/main.c b/examples/packet_ordering/main.c new file mode 100644 index 0000000..8b65275 --- /dev/null +++ b/examples/packet_ordering/main.c @@ -0,0 +1,637 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <signal.h> +#include <getopt.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_errno.h> +#include <rte_ethdev.h> +#include <rte_lcore.h> +#include <rte_mbuf.h> +#include <rte_mempool.h> +#include <rte_ring.h> +#include <rte_reorder.h> + +#define RX_DESC_PER_QUEUE 128 +#define TX_DESC_PER_QUEUE 512 + +#define MAX_PKTS_BURST 32 +#define REORDER_BUFFER_SIZE 8192 +#define MBUF_PER_POOL 65535 +#define MBUF_SIZE (1600 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_POOL_CACHE_SIZE 250 + +#define RING_SIZE 16384 + +/* uncommnet below line to enable debug logs */ +/* #define DEBUG */ + +#ifdef DEBUG +#define LOG_LEVEL RTE_LOG_DEBUG +#define LOG_DEBUG(log_type, fmt, args...) RTE_LOG(DEBUG, log_type, fmt, ##args) +#else +#define LOG_LEVEL RTE_LOG_INFO +#define LOG_DEBUG(log_type, fmt, args...) do {} while (0) +#endif + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDERAPP RTE_LOGTYPE_USER1 + +unsigned int portmask; +volatile uint8_t quit_signal; + +static struct rte_mempool *mbuf_pool; + +static struct rte_eth_conf port_conf_default; + +struct worker_thread_args { + struct rte_ring *ring_in; + struct rte_ring *ring_out; +}; + +struct output_buffer { + unsigned count; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; +}; + +volatile struct app_stats { + struct { + uint64_t rx_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } rx __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } wkr __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + /* Too early pkts transmitted directly w/o reordering */ + uint64_t early_pkts_txtd_woro; + /* Too early pkts failed from direct transmit */ + uint64_t early_pkts_tx_failed_woro; + uint64_t ro_tx_pkts; + uint64_t ro_tx_failed_pkts; + } tx __rte_cache_aligned; +} app_stats; + +/** + * Get the last enabled lcore ID + * + * @return + * The last enabled lcore ID. + */ +static unsigned int +get_last_lcore_id(void) +{ + int i; + + for (i = RTE_MAX_LCORE - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return 0; +} + +/** + * Get the previous enabled lcore ID + * @param id + * The current lcore ID + * @return + * The previous enabled lcore ID or the current lcore + * ID if it is the first available core. + */ +static unsigned int +get_previous_lcore_id(unsigned int id) +{ + int i; + + for (i = id - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return id; +} + +static inline void +pktmbuf_free_bulk(struct rte_mbuf *mbuf_table[], unsigned n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + rte_pktmbuf_free(mbuf_table[i]); +} + +/* display usage */ +static void +print_usage(const char *prgname) +{ + printf("%s [EAL options] -- -p PORTMASK\n" + " -p PORTMASK: hexadecimal bitmask of ports to configure\n", + prgname); +} + +static int +parse_portmask(const char *portmask) +{ + unsigned long pm; + char *end = NULL; + + /* parse hexadecimal string */ + pm = strtoul(portmask, &end, 16); + if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) + return -1; + + if (pm == 0) + return -1; + + return pm; +} + +/* Parse the argument given in the command line of the application */ +static int +parse_args(int argc, char **argv) +{ + int opt; + int option_index; + char **argvopt; + char *prgname = argv[0]; + static struct option lgopts[] = { + {NULL, 0, 0, 0} + }; + + argvopt = argv; + + while ((opt = getopt_long(argc, argvopt, "p:", + lgopts, &option_index)) != EOF) { + switch (opt) { + /* portmask */ + case 'p': + portmask = parse_portmask(optarg); + if (portmask == 0) { + printf("invalid portmask\n"); + print_usage(prgname); + return -1; + } + break; + default: + print_usage(prgname); + return -1; + } + } + if (optind <= 1) { + print_usage(prgname); + return -1; + } + + argv[optind-1] = prgname; + optind = 0; /* reset getopt lib */ + return 0; +} + +static inline int +configure_eth_port(uint8_t port_id) +{ + struct ether_addr addr; + const uint16_t rxRings = 1, txRings = 1; + const uint8_t nb_ports = rte_eth_dev_count(); + int ret; + uint16_t q; + + if (port_id > nb_ports) + return -1; + + ret = rte_eth_dev_configure(port_id, rxRings, txRings, &port_conf_default); + if (ret != 0) + return ret; + + for (q = 0; q < rxRings; q++) { + ret = rte_eth_rx_queue_setup(port_id, q, RX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL, + mbuf_pool); + if (ret < 0) + return ret; + } + + for (q = 0; q < txRings; q++) { + ret = rte_eth_tx_queue_setup(port_id, q, TX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL); + if (ret < 0) + return ret; + } + + ret = rte_eth_dev_start(port_id); + if (ret < 0) + return ret; + + rte_eth_macaddr_get(port_id, &addr); + printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 + " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", + (unsigned)port_id, + addr.addr_bytes[0], addr.addr_bytes[1], + addr.addr_bytes[2], addr.addr_bytes[3], + addr.addr_bytes[4], addr.addr_bytes[5]); + + rte_eth_promiscuous_enable(port_id); + + return 0; +} + +static void +print_stats(void) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + unsigned i; + struct rte_eth_stats eth_stats; + + printf("\nRX thread stats:\n"); + printf(" - Pkts rxd: %"PRIu64"\n", + app_stats.rx.rx_pkts); + printf(" - Pkts enqd to workers ring: %"PRIu64"\n", + app_stats.rx.enqueue_pkts); + + printf("\nWorker thread stats:\n"); + printf(" - Pkts deqd from workers ring: %"PRIu64"\n", + app_stats.wkr.dequeue_pkts); + printf(" - Pkts enqd to tx ring: %"PRIu64"\n", + app_stats.wkr.enqueue_pkts); + printf(" - Pkts enq to tx failed: %"PRIu64"\n", + app_stats.wkr.enqueue_failed_pkts); + + printf("\nTX stats:\n"); + printf(" - Pkts deqd from tx ring: %"PRIu64"\n", + app_stats.tx.dequeue_pkts); + printf(" - Ro Pkts transmitted: %"PRIu64"\n", + app_stats.tx.ro_tx_pkts); + printf(" - Ro Pkts tx failed: %"PRIu64"\n", + app_stats.tx.ro_tx_failed_pkts); + printf(" - Pkts transmitted w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_txtd_woro); + printf(" - Pkts tx failed w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_tx_failed_woro); + + for (i = 0; i < nb_ports; i++) { + rte_eth_stats_get(i, ð_stats); + printf("\nPort %u stats:\n", i); + printf(" - Pkts in: %"PRIu64"\n", eth_stats.ipackets); + printf(" - Pkts out: %"PRIu64"\n", eth_stats.opackets); + printf(" - In Errs: %"PRIu64"\n", eth_stats.ierrors); + printf(" - Out Errs: %"PRIu64"\n", eth_stats.oerrors); + printf(" - Mbuf Errs: %"PRIu64"\n", eth_stats.rx_nombuf); + } +} + +static void +int_handler(int sig_num) +{ + printf("Exiting on signal %d\n", sig_num); + quit_signal = 1; +} + +/** + * This thread receives mbufs from the port and affects them an internal + * sequence number to keep track of their order of arrival through an + * mbuf structure. + * The mbufs are then passed to the worker threads via the rx_to_workers + * ring. + */ +static int +rx_thread(struct rte_ring *ring_out) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint32_t seqn = 0; + uint16_t i, ret = 0; + uint16_t nb_rx_pkts; + uint8_t port_id; + struct rte_mbuf *pkts[MAX_PKTS_BURST]; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + + while (!quit_signal) { + + for (port_id = 0; port_id < nb_ports; port_id++) { + if ((portmask & (1 << port_id)) != 0) { + + /* receive packets */ + nb_rx_pkts = rte_eth_rx_burst(port_id, 0, + pkts, MAX_PKTS_BURST); + if (nb_rx_pkts == 0) { + LOG_DEBUG(REORDERAPP, + "%s():Received zero packets\n", __func__); + continue; + } + app_stats.rx.rx_pkts += nb_rx_pkts; + + /* mark sequence number */ + for (i = 0; i < nb_rx_pkts; ) + pkts[i++]->seqn = seqn++; + + /* enqueue to rx_to_workers ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *) pkts, + nb_rx_pkts); + app_stats.rx.enqueue_pkts += ret; + if (unlikely(ret < nb_rx_pkts)) { + app_stats.rx.enqueue_failed_pkts += + (nb_rx_pkts-ret); + pktmbuf_free_bulk(&pkts[ret], nb_rx_pkts - ret); + } + } + } + } + return 0; +} + +/** + * This thread takes bursts of packets from the rx_to_workers ring and + * Changes the input port value to output port value. And feds it to + * workers_to_tx + */ +static int +worker_thread(void *args_ptr) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint16_t i, ret = 0; + uint16_t burst_size = 0; + struct worker_thread_args *args; + struct rte_mbuf *burst_buffer[MAX_PKTS_BURST] = { NULL }; + struct rte_ring *ring_in, *ring_out; + const unsigned xor_val = (nb_ports > 1); + + args = (struct worker_thread_args *) args_ptr; + ring_in = args->ring_in; + ring_out = args->ring_out; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + + while (!quit_signal) { + + /* dequeue the mbufs from rx_to_workers ring */ + burst_size = rte_ring_dequeue_burst(ring_in, + (void *)burst_buffer, MAX_PKTS_BURST); + if (unlikely(burst_size == 0)) + continue; + + __sync_fetch_and_add(&app_stats.wkr.dequeue_pkts, burst_size); + + /* just do some operation on mbuf */ + for (i = 0; i < burst_size;) + burst_buffer[i++]->port ^= xor_val; + + /* enqueue the modified mbufs to workers_to_tx ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *)burst_buffer, burst_size); + __sync_fetch_and_add(&app_stats.wkr.enqueue_pkts, ret); + if (unlikely(ret < burst_size)) { + /* Return the mbufs to their respective pool, dropping packets */ + __sync_fetch_and_add(&app_stats.wkr.enqueue_failed_pkts, + (int)burst_size - ret); + pktmbuf_free_bulk(&burst_buffer[ret], burst_size - ret); + } + } + return 0; +} + +static inline void +flush_one_port(struct output_buffer *outbuf, uint8_t outp) +{ + unsigned nb_tx = rte_eth_tx_burst(outp, 0, outbuf->mbufs, + outbuf->count); + app_stats.tx.ro_tx_pkts += nb_tx; + + if (unlikely(nb_tx < outbuf->count)) { + /* free the mbufs which failed from transmit */ + app_stats.tx.ro_tx_failed_pkts += (outbuf->count - nb_tx); + LOG_DEBUG(REORDERAPP, "%s:Packet loss with tx_burst\n", __func__); + pktmbuf_free_bulk(&outbuf->mbufs[nb_tx], outbuf->count - nb_tx); + } + outbuf->count = 0; +} + +/** + * Dequeue mbufs from the workers_to_tx ring and reorder them before + * transmitting. + */ +static int +send_thread(struct rte_ring *ring_in) +{ + int ret; + unsigned int i, dret; + uint16_t nb_dq_mbufs; + uint8_t outp; + static struct output_buffer tx_buffers[RTE_MAX_ETHPORTS]; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; + struct rte_mbuf *rombufs[MAX_PKTS_BURST] = {NULL}; + struct rte_reorder_buffer *buffer; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + buffer = rte_reorder_create("PKT_RO", rte_socket_id(), REORDER_BUFFER_SIZE); + while (!quit_signal) { + + /* deque the mbufs from workers_to_tx ring */ + nb_dq_mbufs = rte_ring_dequeue_burst(ring_in, + (void *)mbufs, MAX_PKTS_BURST); + + if (unlikely(nb_dq_mbufs == 0)) + continue; + + app_stats.tx.dequeue_pkts += nb_dq_mbufs; + + for (i = 0; i < nb_dq_mbufs; i++) { + /* send dequeued mbufs for reordering */ + ret = rte_reorder_insert(buffer, mbufs[i]); + + if (ret == -1 && rte_errno == ERANGE) { + /* Too early pkts should be transmitted out directly */ + LOG_DEBUG(REORDERAPP, "%s():Cannot reorder early packet " + "direct enqueuing to TX\n", __func__); + outp = mbufs[i]->port; + if ((portmask & (1 << outp)) == 0) { + rte_pktmbuf_free(mbufs[i]); + continue; + } + if (rte_eth_tx_burst(outp, 0, (void *)mbufs[i], 1) != 1) { + rte_pktmbuf_free(mbufs[i]); + app_stats.tx.early_pkts_tx_failed_woro++; + } else + app_stats.tx.early_pkts_txtd_woro++; + } else if (ret == -1 && rte_errno == ENOSPC) { + /** + * Early pkts just outside of window should be dropped + */ + rte_pktmbuf_free(mbufs[i]); + } + } + + /* + * drain MAX_PKTS_BURST of reordered + * mbufs for transmit + */ + dret = rte_reorder_drain(buffer, rombufs, MAX_PKTS_BURST); + for (i = 0; i < dret; i++) { + + struct output_buffer *outbuf; + uint8_t outp1; + + outp1 = rombufs[i]->port; + /* skip ports that are not enabled */ + if ((portmask & (1 << outp1)) == 0) { + rte_pktmbuf_free(rombufs[i]); + continue; + } + + outbuf = &tx_buffers[outp1]; + outbuf->mbufs[outbuf->count++] = rombufs[i]; + if (outbuf->count == MAX_PKTS_BURST) + flush_one_port(outbuf, outp1); + } + } + return 0; +} + +int +main(int argc, char **argv) +{ + int ret; + unsigned nb_ports; + unsigned int lcore_id, last_lcore_id, master_lcore_id; + uint8_t port_id; + uint8_t nb_ports_available; + struct worker_thread_args worker_args = {NULL, NULL}; + struct rte_ring *rx_to_workers; + struct rte_ring *workers_to_tx; + + /* catch ctrl-c so we can print on exit */ + signal(SIGINT, int_handler); + + /* Initialize EAL */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + return -1; + + argc -= ret; + argv += ret; + + /* Parse the application specific arguments */ + ret = parse_args(argc, argv); + if (ret < 0) + return -1; + + /* Check if we have enought cores */ + if (rte_lcore_count() < 3) + rte_exit(EXIT_FAILURE, "Error, This application needs at " + "least 3 logical cores to run:\n" + "1 lcore for packet RX\n" + "1 lcore for packet TX\n" + "and at least 1 lcore for worker threads\n"); + + nb_ports = rte_eth_dev_count(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "Error: no ethernet ports detected\n"); + if (nb_ports != 1 && (nb_ports & 1)) + rte_exit(EXIT_FAILURE, "Error: number of ports must be even, except " + "when using a single port\n"); + + mbuf_pool = rte_mempool_create("mbuf_pool", MBUF_PER_POOL, MBUF_SIZE, + MBUF_POOL_CACHE_SIZE, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (mbuf_pool == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + nb_ports_available = nb_ports; + + /* initialize all ports */ + for (port_id = 0; port_id < nb_ports; port_id++) { + /* skip ports that are not enabled */ + if ((portmask & (1 << port_id)) == 0) { + printf("\nSkipping disabled port %d\n", port_id); + nb_ports_available--; + continue; + } + /* init port */ + printf("Initializing port %u... done\n", (unsigned) port_id); + + if (configure_eth_port(port_id) != 0) + rte_exit(EXIT_FAILURE, "Cannot initialize port %"PRIu8"\n", + port_id); + } + + if (!nb_ports_available) { + rte_exit(EXIT_FAILURE, + "All available ports are disabled. Please set portmask.\n"); + } + + /* Create rings for inter core communication */ + rx_to_workers = rte_ring_create("rx_to_workers", RING_SIZE, rte_socket_id(), + RING_F_SP_ENQ); + if (rx_to_workers == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + workers_to_tx = rte_ring_create("workers_to_tx", RING_SIZE, rte_socket_id(), + RING_F_SC_DEQ); + if (workers_to_tx == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + last_lcore_id = get_last_lcore_id(); + master_lcore_id = rte_get_master_lcore(); + + worker_args.ring_in = rx_to_workers; + worker_args.ring_out = workers_to_tx; + + /* Start worker_thread() on all the available slave cores but the last 1 */ + for (lcore_id = 0; lcore_id <= get_previous_lcore_id(last_lcore_id); lcore_id++) + if (rte_lcore_is_enabled(lcore_id) && lcore_id != master_lcore_id) + rte_eal_remote_launch(worker_thread, (void *)&worker_args, + lcore_id); + + /* Start send_thread() on the last slave core */ + rte_eal_remote_launch((lcore_function_t *)send_thread, workers_to_tx, + last_lcore_id); + + /* Start rx_thread() on the master core */ + rx_thread(rx_to_workers); + + RTE_LCORE_FOREACH_SLAVE(lcore_id) { + if (rte_eal_wait_lcore(lcore_id) < 0) + return -1; + } + + print_stats(); + return 0; +} -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v2 4/4] doc: new reorder library description 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 0/4] New Reorder Library Sergio Gonzalez Monroy ` (2 preceding siblings ...) 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 3/4] examples: new sample app packet_ordering Sergio Gonzalez Monroy @ 2015-01-30 13:14 ` Sergio Gonzalez Monroy 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy 4 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-01-30 13:14 UTC (permalink / raw) To: dev This patch introduces a new section in the programmers guide describing the reorder library. Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/reorder_lib.rst | 110 ++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 doc/guides/prog_guide/reorder_lib.rst diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 8d86dd4..de69682 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -61,6 +61,7 @@ Programmer's Guide lpm_lib lpm6_lib packet_distrib_lib + reorder_lib ip_fragment_reassembly_lib multi_proc_support kernel_nic_interface diff --git a/doc/guides/prog_guide/reorder_lib.rst b/doc/guides/prog_guide/reorder_lib.rst new file mode 100644 index 0000000..558804d --- /dev/null +++ b/doc/guides/prog_guide/reorder_lib.rst @@ -0,0 +1,110 @@ +.. BSD LICENSE + Copyright(c) 2010-2015 Intel Corporation. All rights reserved. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.. _Reorder_Library: + +Reorder Library +================= + +The Reorder Library provides a mechanism for reordering mbufs based on their +sequence number. + +Operation +---------- + +The reorder library is essentially a buffer that reorders mbufs. +The user inserts out of order mbufs into the reorder buffer and pulls ordered +mbufs from it. + +At a given time, the reorder buffer contains mbufs whose sequence number are +inside the sequence window. The sequence window is determined by the minimum +sequence number and the number of entries that the buffer was configured with. +As an example, given a reorder buffer with 200 entries and a minimum sequence +number of 350, the sequence window has high and low limits of +550 and 350 respectively. + +When inserting mbufs, the reorder library differentiates between valid, early +and late mbufs depending on the sequence number of the inserted mbuf: + +* valid: the sequence number is inside the window. +* late: the sequence number is outside the window and less than the low limit. +* early: the sequence number is outside the window and greater than the high + limit. + +The reorder buffer directly returns late mbufs and tries to allocate early +mbufs. + + +Implementation Details +------------------------- + +The reorder library is implemented as a double buffer, which we will refer to as +the *Order* buffer and the *Ready* buffer. + +Valid mbufs are inserted directly into the Order buffer and late mbufs are +returned to the user. + +In the case of early mbufs, the reorder buffer will try to move the window +(incrementing the minimum sequence number) so that the mbuf becomes a valid one. +To that end, mbufs in the Order buffer are moved into the Ready buffer. +Any mbufs that have not arrived yet are ignored and therefore will become +late mbufs. +This means that as long as there is room in the Ready buffer, the window will +be moved to accommodate early mbufs. + +For example, assuming that we have a buffer of 200 entries with a 350 minimum +sequence number, and we need to insert an early mbuf with 565 sequence number. +That means that we would need to move the windows at least 15 positions to +accommodate the mbuf. +The reorder buffer would try to move at least 15 mbufs from the Order to the +Ready buffer, as long as there is room in the Ready buffer. + +When draining mbufs, the reorder buffer would return mbufs in the Ready +buffer first and then from the Order buffer until a gap is found (mbufs that +have not arrived yet). + +Use Case: Packet Distributor +------------------------------- + +An application using the DPDK packet distributor could make use of the reorder +library to transmit packets in the same order they were received. + +A basic packet distributor use case would consist of a distributor core with +multiple workers. +The processing of packets by the workers is not guaranteed to be in order, +hence a reorder buffer can be used to order as many packets as possible. + +In such a scenario, the distributor assigns a sequence number to mbufs before +delivering them to the workers. +As the workers finish processing the packets, the distributor inserts those +mbufs into the reorder buffer and finally transmit drained mbufs. + +Currently the reorder buffer is not thread safe so the same thread is +responsible for inserting and draining mbufs. -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v3 0/5] New Reorder Library 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 0/4] New Reorder Library Sergio Gonzalez Monroy ` (3 preceding siblings ...) 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 4/4] doc: new reorder library description Sergio Gonzalez Monroy @ 2015-02-06 15:05 ` Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 1/5] reorder: new reorder library Sergio Gonzalez Monroy ` (6 more replies) 4 siblings, 7 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-06 15:05 UTC (permalink / raw) To: dev This series introduces the new reorder library along with unit tests, sample app and a new entry in the programmers guide describing the library. The library provides reordering of mbufs based on their sequence number. As mention in the patch describing the library, one use case is the packet distributor. The distributor receives packets, assigns them a sequence number and sends them to the workers. The workers process those packets and return them to the distributor. The distributor collects out-of-order packets from the workers and uses this library to reorder the packets based on the sequence number they were assigned. v3: - fix copyright date - add option to sample app to disable reordering - add packet ordering sample guide entry v2: - add programmers guide entry describing the library - use malloc instead of memzone to allocate memory - modify create and init implementation, init takes a reorder buffer as input and create reserves memory and call init. - update unit tests Sergio Gonzalez Monroy (5): reorder: new reorder library app: New reorder unit test examples: new sample app packet_ordering doc: new reorder library description doc: new packet ordering app description app/test/Makefile | 2 + app/test/test_reorder.c | 393 ++++++++++++++ config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/reorder_lib.rst | 115 ++++ doc/guides/sample_app_ug/index.rst | 1 + doc/guides/sample_app_ug/packet_ordering.rst | 102 ++++ examples/packet_ordering/Makefile | 50 ++ examples/packet_ordering/main.c | 695 +++++++++++++++++++++++++ lib/Makefile | 1 + lib/librte_eal/common/include/rte_tailq_elem.h | 2 + lib/librte_mbuf/rte_mbuf.h | 3 + lib/librte_reorder/Makefile | 50 ++ lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++ lib/librte_reorder/rte_reorder.h | 181 +++++++ mk/rte.app.mk | 4 + 17 files changed, 2026 insertions(+) create mode 100644 app/test/test_reorder.c create mode 100644 doc/guides/prog_guide/reorder_lib.rst create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst create mode 100644 examples/packet_ordering/Makefile create mode 100644 examples/packet_ordering/main.c create mode 100644 lib/librte_reorder/Makefile create mode 100644 lib/librte_reorder/rte_reorder.c create mode 100644 lib/librte_reorder/rte_reorder.h -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v3 1/5] reorder: new reorder library 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy @ 2015-02-06 15:06 ` Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 2/5] app: New reorder unit test Sergio Gonzalez Monroy ` (5 subsequent siblings) 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-06 15:06 UTC (permalink / raw) To: dev This library provides reordering capability for out of order mbufs based on a sequence number in the mbuf structure. Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_eal/common/include/rte_tailq_elem.h | 2 + lib/librte_mbuf/rte_mbuf.h | 3 + lib/librte_reorder/Makefile | 50 +++ lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++++++++++++ lib/librte_reorder/rte_reorder.h | 181 +++++++++++ 8 files changed, 663 insertions(+) create mode 100644 lib/librte_reorder/Makefile create mode 100644 lib/librte_reorder/rte_reorder.c create mode 100644 lib/librte_reorder/rte_reorder.h diff --git a/config/common_bsdapp b/config/common_bsdapp index 9177db1..e3e0e94 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -334,6 +334,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 2f9643b..b5ec730 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -342,6 +342,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/lib/Makefile b/lib/Makefile index 0ffc982..5919d32 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -65,6 +65,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += librte_distributor DIRS-$(CONFIG_RTE_LIBRTE_PORT) += librte_port DIRS-$(CONFIG_RTE_LIBRTE_TABLE) += librte_table DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += librte_pipeline +DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += librte_reorder ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni diff --git a/lib/librte_eal/common/include/rte_tailq_elem.h b/lib/librte_eal/common/include/rte_tailq_elem.h index f74fc7c..3013869 100644 --- a/lib/librte_eal/common/include/rte_tailq_elem.h +++ b/lib/librte_eal/common/include/rte_tailq_elem.h @@ -84,6 +84,8 @@ rte_tailq_elem(RTE_TAILQ_ACL, "RTE_ACL") rte_tailq_elem(RTE_TAILQ_DISTRIBUTOR, "RTE_DISTRIBUTOR") +rte_tailq_elem(RTE_TAILQ_REORDER, "RTE_REORDER") + rte_tailq_end(RTE_TAILQ_NUM) #undef rte_tailq_elem diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h index 16059c6..ed27eb8 100644 --- a/lib/librte_mbuf/rte_mbuf.h +++ b/lib/librte_mbuf/rte_mbuf.h @@ -262,6 +262,9 @@ struct rte_mbuf { uint32_t usr; /**< User defined tags. See @rte_distributor_process */ } hash; /**< hash information */ + /* sequence number - field used in distributor and reorder library */ + uint32_t seqn; + /* second cache line - fields only used in slow path or on TX */ MARKER cacheline1 __rte_cache_aligned; diff --git a/lib/librte_reorder/Makefile b/lib/librte_reorder/Makefile new file mode 100644 index 0000000..12b916f --- /dev/null +++ b/lib/librte_reorder/Makefile @@ -0,0 +1,50 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(RTE_SDK)/mk/rte.vars.mk + +# library name +LIB = librte_reorder.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) + +# all source are stored in SRCS-y +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) := rte_reorder.c + +# install this header file +SYMLINK-$(CONFIG_RTE_LIBRTE_REORDER)-include := rte_reorder.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_eal + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_reorder/rte_reorder.c b/lib/librte_reorder/rte_reorder.c new file mode 100644 index 0000000..42d2a47 --- /dev/null +++ b/lib/librte_reorder/rte_reorder.c @@ -0,0 +1,416 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <inttypes.h> +#include <string.h> + +#include <rte_log.h> +#include <rte_mbuf.h> +#include <rte_memzone.h> +#include <rte_eal_memconfig.h> +#include <rte_errno.h> +#include <rte_tailq.h> +#include <rte_malloc.h> + +#include "rte_reorder.h" + +TAILQ_HEAD(rte_reorder_list, rte_tailq_entry); + +#define NO_FLAGS 0 +#define RTE_REORDER_PREFIX "RO_" +#define RTE_REORDER_NAMESIZE 32 + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDER RTE_LOGTYPE_USER1 + +/* A generic circular buffer */ +struct cir_buffer { + unsigned int size; /**< Number of entries that can be stored */ + unsigned int mask; /**< [buffer_size - 1]: used for wrap-around */ + unsigned int head; /**< insertion point in buffer */ + unsigned int tail; /**< extraction point in buffer */ + struct rte_mbuf **entries; +} __rte_cache_aligned; + +/* The reorder buffer data structure itself */ +struct rte_reorder_buffer { + char name[RTE_REORDER_NAMESIZE]; + uint32_t min_seqn; /**< Lowest seq. number that can be in the buffer */ + unsigned int memsize; /**< memory area size of reorder buffer */ + struct cir_buffer ready_buf; /**< temp buffer for dequeued entries */ + struct cir_buffer order_buf; /**< buffer used to reorder entries */ +} __rte_cache_aligned; + +static void +rte_reorder_free_mbufs(struct rte_reorder_buffer *b); + +struct rte_reorder_buffer * +rte_reorder_init(struct rte_reorder_buffer *b, unsigned int bufsize, + const char *name, unsigned int size) +{ + const unsigned int min_bufsize = sizeof(*b) + + (2 * size * sizeof(struct rte_mbuf *)); + + if (b == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer parameter:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (bufsize < min_bufsize) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer memory size: %u, " + "minimum required: %u\n", bufsize, min_bufsize); + rte_errno = EINVAL; + return NULL; + } + + memset(b, 0, bufsize); + snprintf(b->name, sizeof(b->name), "%s", name); + b->memsize = bufsize; + b->order_buf.size = b->ready_buf.size = size; + b->order_buf.mask = b->ready_buf.mask = size - 1; + b->ready_buf.entries = (void *)&b[1]; + b->order_buf.entries = RTE_PTR_ADD(&b[1], + size * sizeof(b->ready_buf.entries[0])); + + return b; +} + +struct rte_reorder_buffer* +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + const unsigned int bufsize = sizeof(struct rte_reorder_buffer) + + (2 * size * sizeof(struct rte_mbuf *)); + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + /* Check user arguments. */ + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* guarantee there's no existing */ + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + if (te != NULL) + goto exit; + + /* allocate tailq entry */ + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); + if (te == NULL) { + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); + rte_errno = ENOMEM; + b = NULL; + goto exit; + } + + /* Allocate memory to store the reorder buffer structure. */ + b = rte_zmalloc_socket("REORDER_BUFFER", bufsize, 0, socket_id); + if (b == NULL) { + RTE_LOG(ERR, REORDER, "Memzone allocation failed\n"); + rte_errno = ENOMEM; + rte_free(te); + } else { + rte_reorder_init(b, bufsize, name, size); + te->data = (void *)b; + TAILQ_INSERT_TAIL(reorder_list, te, next); + } + +exit: + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return b; +} + +void +rte_reorder_reset(struct rte_reorder_buffer *b) +{ + char name[RTE_REORDER_NAMESIZE]; + + rte_reorder_free_mbufs(b); + snprintf(name, sizeof(name), "%s", b->name); + /* No error checking as current values should be valid */ + rte_reorder_init(b, b->memsize, name, b->order_buf.size); +} + +static void +rte_reorder_free_mbufs(struct rte_reorder_buffer *b) +{ + unsigned i; + + /* Free up the mbufs of order buffer & ready buffer */ + for (i = 0; i < b->order_buf.size; i++) { + if (b->order_buf.entries[i]) + rte_pktmbuf_free(b->order_buf.entries[i]); + if (b->ready_buf.entries[i]) + rte_pktmbuf_free(b->ready_buf.entries[i]); + } +} + +void +rte_reorder_free(struct rte_reorder_buffer *b) +{ + struct rte_reorder_list *reorder_list; + struct rte_tailq_entry *te; + + /* Check user arguments. */ + if (b == NULL) + return; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* find our tailq entry */ + TAILQ_FOREACH(te, reorder_list, next) { + if (te->data == (void *) b) + break; + } + if (te == NULL) { + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return; + } + + TAILQ_REMOVE(reorder_list, te, next); + + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + + rte_reorder_free_mbufs(b); + + rte_free(b); + rte_free(te); +} + +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); + + if (te == NULL) { + rte_errno = ENOENT; + return NULL; + } + + return b; +} + +static unsigned +rte_reorder_fill_overflow(struct rte_reorder_buffer *b, unsigned n) +{ + /* + * 1. Move all ready entries that fit to the ready_buf + * 2. check if we meet the minimum needed (n). + * 3. If not, then skip any gaps and keep moving. + * 4. If at any point the ready buffer is full, stop + * 5. Return the number of positions the order_buf head has moved + */ + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + unsigned int order_head_adv = 0; + + /* + * move at least n packets to ready buffer, assuming ready buffer + * has room for those packets. + */ + while (order_head_adv < n && + ((ready_buf->head + 1) & ready_buf->mask) != ready_buf->tail) { + + /* if we are blocked waiting on a packet, skip it */ + if (order_buf->entries[order_buf->head] == NULL) { + order_buf->head = (order_buf->head + 1) & order_buf->mask; + order_head_adv++; + } + + /* Move all ready entries that fit to the ready_buf */ + while (order_buf->entries[order_buf->head] != NULL) { + ready_buf->entries[ready_buf->head] = + order_buf->entries[order_buf->head]; + + order_buf->entries[order_buf->head] = NULL; + order_head_adv++; + + order_buf->head = (order_buf->head + 1) & order_buf->mask; + + if (((ready_buf->head + 1) & ready_buf->mask) == ready_buf->tail) + break; + + ready_buf->head = (ready_buf->head + 1) & ready_buf->mask; + } + } + + b->min_seqn += order_head_adv; + /* Return the number of positions the order_buf head has moved */ + return order_head_adv; +} + +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf) +{ + uint32_t offset, position; + struct cir_buffer *order_buf = &b->order_buf; + + /* + * calculate the offset from the head pointer we need to go. + * The subtraction takes care of the sequence number wrapping. + * For example (using 16-bit for brevity): + * min_seqn = 0xFFFD + * mbuf_seqn = 0x0010 + * offset = 0x0010 - 0xFFFD = 0x13 + */ + offset = mbuf->seqn - b->min_seqn; + + /* + * action to take depends on offset. + * offset < buffer->size: the mbuf fits within the current window of + * sequence numbers we can reorder. EXPECTED CASE. + * offset > buffer->size: the mbuf is outside the current window. There + * are a number of cases to consider: + * 1. The packet sequence is just outside the window, then we need + * to see about shifting the head pointer and taking any ready + * to return packets out of the ring. If there was a delayed + * or dropped packet preventing drains from shifting the window + * this case will skip over the dropped packet instead, and any + * packets dequeued here will be returned on the next drain call. + * 2. The packet sequence number is vastly outside our window, taken + * here as having offset greater than twice the buffer size. In + * this case, the packet is probably an old or late packet that + * was previously skipped, so just enqueue the packet for + * immediate return on the next drain call, or else return error. + */ + if (offset < b->order_buf.size) { + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else if (offset < 2 * b->order_buf.size) { + if (rte_reorder_fill_overflow(b, offset + 1 - order_buf->size) + < (offset + 1 - order_buf->size)) { + /* Put in handling for enqueue straight to output */ + rte_errno = ENOSPC; + return -1; + } + offset = mbuf->seqn - b->min_seqn; + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else { + /* Put in handling for enqueue straight to output */ + rte_errno = ERANGE; + return -1; + } + return 0; +} + +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs) +{ + unsigned int drain_cnt = 0; + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + /* Try to fetch requested number of mbufs from ready buffer */ + while ((drain_cnt < max_mbufs) && (ready_buf->tail != ready_buf->head)) { + mbufs[drain_cnt++] = ready_buf->entries[ready_buf->tail]; + ready_buf->tail = (ready_buf->tail + 1) & ready_buf->mask; + } + + /* + * If requested number of buffers not fetched from ready buffer, fetch + * remaining buffers from order buffer + */ + while ((drain_cnt < max_mbufs) && + (order_buf->entries[order_buf->head] != NULL)) { + mbufs[drain_cnt++] = order_buf->entries[order_buf->head]; + order_buf->entries[order_buf->head] = NULL; + b->min_seqn++; + order_buf->head = (order_buf->head + 1) & order_buf->mask; + } + + return drain_cnt; +} diff --git a/lib/librte_reorder/rte_reorder.h b/lib/librte_reorder/rte_reorder.h new file mode 100644 index 0000000..8300bf0 --- /dev/null +++ b/lib/librte_reorder/rte_reorder.h @@ -0,0 +1,181 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_REORDER_H_ +#define _RTE_REORDER_H_ + +/** + * @file + * RTE reorder + * + * Reorder library is a component which is designed to + * provide ordering of out of ordered packets based on + * sequence number present in mbuf. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rte_reorder_buffer; + +/** + * Create a new reorder buffer instance + * + * Allocate memory and initialize a new reorder buffer in that + * memory, returning the reorder buffer pointer to the user + * + * @param name + * The name to be given to the reorder buffer instance. + * @param socket_id + * The NUMA node on which the memory for the reorder buffer + * instance is to be reserved. + * @param size + * Max number of elements that can be stored in the reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - ENOMEM - no appropriate memory area found in which to create memzone + * - EINVAL - invalid parameters + */ +struct rte_reorder_buffer * +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size); + +/** + * Initializes given reorder buffer instance + * + * @param b + * Reorder buffer instance to initialize + * @param bufsize + * Size of the reorder buffer + * @param name + * The name to be given to the reorder buffer + * @param size + * Number of elements that can be stored in reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - EINVAL - invalid parameters + */ +struct rte_reorder_buffer * +rte_reorder_init(struct rte_reorder_buffer *b, unsigned int bufsize, + const char *name, unsigned int size); + +/** + * Find an existing reorder buffer instance + * and return a pointer to it. + * + * @param name + * Name of the reorder buffer instacne as passed to rte_reorder_create() + * @return + * Pointer to reorder buffer instance or NULL if object not found with rte_errno + * set appropriately. Possible rte_errno values include: + * - ENOENT - required entry not available to return. + * - E_RTE_NO_TAILQ - no tailq list could be got for the + * reorder instance list + */ +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name); + +/** + * Reset the given reorder buffer instance with initial values. + * + * @param b + * Reorder buffer instance which has to be reset + */ +void +rte_reorder_reset(struct rte_reorder_buffer *b); + +/** + * Free reorder buffer instance. + * + * @param b + * reorder buffer instance + * @return + * None + */ +void +rte_reorder_free(struct rte_reorder_buffer *b); + +/** + * Insert given mbuf in reorder buffer in its correct position + * + * The given mbuf is to be reordered relative to other mbufs in the system. + * The mbuf must contain a sequence number which is then used to place + * the buffer in the correct position in the reorder buffer. Reordered + * packets can later be taken from the buffer using the rte_reorder_drain() + * API. + * + * @param b + * Reorder buffer where the mbuf has to be inserted. + * @param mbuf + * mbuf of packet that needs to be inserted in reorder buffer. + * @return + * 0 on success + * -1 on error + * On error case, rte_errno will be set appropriately: + * - ENOSPC - Cannot move existing mbufs from reorder buffer to accommodate + * ealry mbuf, but it can be accomodated by performing drain and then insert. + * - ERANGE - Too early or late mbuf which is vastly out of range of expected + * window should be ingnored without any handling. + */ +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf); + +/** + * Fetch reordered buffers + * + * Returns a set of in-order buffers from the reorder buffer structure. Gaps + * may be present in the sequence numbers of the mbuf if packets have been + * delayed too long before reaching the reorder window, or have been previously + * dropped by the system. + * + * @param b + * Reorder buffer instance from which packets are to be drained + * @param mbufs + * array of mbufs where reordered packets will be inserted from reorder buffer + * @param max_mbufs + * the number of elements in the mbufs array. + * @return + * number of mbuf pointers written to mbufs. 0 <= N < max_mbufs. + */ +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs); + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_REORDER_H_ */ -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v3 2/5] app: New reorder unit test 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 1/5] reorder: new reorder library Sergio Gonzalez Monroy @ 2015-02-06 15:06 ` Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 3/5] examples: new sample app packet_ordering Sergio Gonzalez Monroy ` (4 subsequent siblings) 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-06 15:06 UTC (permalink / raw) To: dev Adding new reorder unit test for the test app. The command to run the unit test from the test shell is: reorder_autotest Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- app/test/Makefile | 2 + app/test/test_reorder.c | 393 ++++++++++++++++++++++++++++++++++++++++++++++++ mk/rte.app.mk | 4 + 3 files changed, 399 insertions(+) create mode 100644 app/test/test_reorder.c diff --git a/app/test/Makefile b/app/test/Makefile index 4311f96..24b27d7 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -124,6 +124,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += test_ivshmem.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor_perf.c +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) += test_reorder.c + SRCS-y += test_devargs.c SRCS-y += virtual_pmd.c SRCS-y += packet_burst_generator.c diff --git a/app/test/test_reorder.c b/app/test/test_reorder.c new file mode 100644 index 0000000..61cf8d3 --- /dev/null +++ b/app/test/test_reorder.c @@ -0,0 +1,393 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test.h" +#include "stdio.h" + +#include <unistd.h> +#include <string.h> + +#include <rte_cycles.h> +#include <rte_errno.h> +#include <rte_mbuf.h> +#include <rte_reorder.h> +#include <rte_lcore.h> +#include <rte_malloc.h> + +#include "test.h" + +#define BURST 32 +#define REORDER_BUFFER_SIZE 16384 +#define NUM_MBUFS (2*REORDER_BUFFER_SIZE) +#define REORDER_BUFFER_SIZE_INVALID 2049 +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) + +struct reorder_unittest_params { + struct rte_mempool *p; + struct rte_reorder_buffer *b; +}; + +static struct reorder_unittest_params default_params = { + .p = NULL, + .b = NULL +}; + +static struct reorder_unittest_params *test_params = &default_params; + +static int +test_reorder_create(void) +{ + struct rte_reorder_buffer *b = NULL; + + b = rte_reorder_create(NULL, rte_socket_id(), REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on create() with NULL name"); + + b = rte_reorder_create("PKT", rte_socket_id(), REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on create() with invalid buffer size param."); + + b = rte_reorder_create("PKT_RO1", rte_socket_id(), REORDER_BUFFER_SIZE); + printf("DEBUG: b= %p, orig_b= %p\n", b, test_params->b); + TEST_ASSERT_EQUAL(b, test_params->b, + "New reorder instance created with already existing name"); + + return 0; +} + +static int +test_reorder_init(void) +{ + struct rte_reorder_buffer *b = NULL; + unsigned int size; + /* + * The minimum memory area size that should be passed to library is, + * sizeof(struct rte_reorder_buffer) + (2 * size * sizeof(struct rte_mbuf *)); + * Otherwise error will be thrown + */ + + size = 100; + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with NULL buffer."); + + b = rte_malloc(NULL, size, 0); + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid mem zone size."); + rte_free(b); + + size = 262336; + b = rte_malloc(NULL, size, 0); + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid buffer size param."); + + b = rte_reorder_init(b, size, NULL, REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid name."); + rte_free(b); + + return 0; +} + +static int +test_reorder_find_existing(void) +{ + struct rte_reorder_buffer *b = NULL; + + /* Try to find existing reorder buffer instance */ + b = rte_reorder_find_existing("PKT_RO1"); + TEST_ASSERT_EQUAL(b, test_params->b, + "existing reorder buffer instance not found"); + + /* Try to find non existing reorder buffer instance */ + b = rte_reorder_find_existing("ro_find_non_existing"); + TEST_ASSERT((b == NULL) && (rte_errno == ENOENT), + "non existing reorder buffer instance found"); + + return 0; +} + +static int +test_reorder_free(void) +{ + struct rte_reorder_buffer *b1 = NULL, *b2 = NULL; + const char *name = "test_free"; + + b1 = rte_reorder_create(name, rte_socket_id(), 8); + TEST_ASSERT_NOT_NULL(b1, "Failed to create reorder buffer."); + + b2 = rte_reorder_find_existing(name); + TEST_ASSERT_EQUAL(b1, b2, "Failed to find existing reorder buffer"); + + rte_reorder_free(b1); + + b2 = rte_reorder_find_existing(name); + TEST_ASSERT((b2 == NULL) && (rte_errno == ENOENT), + "Found previously freed reorder buffer"); + + return 0; +} + +static int +test_reorder_insert(void) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_mempool *p = test_params->p; + const unsigned int size = 4; + const unsigned int num_bufs = 6; + struct rte_mbuf *bufs[num_bufs]; + int ret = 0; + unsigned i; + + /* This would create a reorder buffer instance consisting of: + * reorder_seq = 0 + * ready_buf: RB[size] = {NULL, NULL, NULL, NULL} + * order_buf: OB[size] = {NULL, NULL, NULL, NULL} + */ + b = rte_reorder_create("test_insert", rte_socket_id(), size); + TEST_ASSERT_NOT_NULL(b, "Failed to create reorder buffer"); + + ret = rte_mempool_get_bulk(p, (void *)bufs, num_bufs); + TEST_ASSERT_SUCCESS(ret, "Error getting mbuf from pool"); + + /* late packet */ + bufs[0]->seqn = 3 * size; + ret = rte_reorder_insert(b, bufs[0]); + if (!((ret == -1) && (rte_errno == ERANGE))) { + printf("%s:%d: No error inserting late packet with seqn:" + " 3 * size\n", __func__, __LINE__); + ret = -1; + goto exit; + } + + for (i = 0; i < num_bufs; i++) + bufs[i]->seqn = i; + + /* This should fill up order buffer: + * reorder_seq = 0 + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {0, 1, 2, 3} + */ + for (i = 0; i < size; i++) { + ret = rte_reorder_insert(b, bufs[i]); + if (ret != 0) { + printf("%s:%d: Error inserting packet with seqn less than size\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + } + + /* early packet - should move mbufs to ready buf and move sequence window + * reorder_seq = 4 + * RB[] = {0, 1, 2, 3} + * OB[] = {4, NULL, NULL, NULL} + */ + ret = rte_reorder_insert(b, bufs[4]); + if (ret != 0) { + printf("%s:%d: Error inserting early packet with seqn: size\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + /* early packet from current sequence window - full ready buffer */ + bufs[5]->seqn = 2 * size; + ret = rte_reorder_insert(b, bufs[5]); + if (!((ret == -1) && (rte_errno == ENOSPC))) { + printf("%s:%d: No error inserting early packet with full ready buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + ret = 0; +exit: + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + rte_reorder_free(b); + return ret; +} + +static int +test_reorder_drain(void) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_mempool *p = test_params->p; + const unsigned int size = 4; + const unsigned int num_bufs = 10; + struct rte_mbuf *bufs[num_bufs]; + int ret = 0; + unsigned i, cnt; + + /* This would create a reorder buffer instance consisting of: + * reorder_seq = 0 + * ready_buf: RB[size] = {NULL, NULL, NULL, NULL} + * order_buf: OB[size] = {NULL, NULL, NULL, NULL} + */ + b = rte_reorder_create("test_insert", rte_socket_id(), size); + TEST_ASSERT_NOT_NULL(b, "Failed to create reorder buffer"); + + ret = rte_mempool_get_bulk(p, (void *)bufs, num_bufs); + TEST_ASSERT_SUCCESS(ret, "Error getting mbuf from pool"); + + /* Check no drained packets if reorder is empty */ + cnt = rte_reorder_drain(b, bufs, 1); + if (cnt != 0) { + printf("%s:%d: drained packets from empty reorder buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + for (i = 0; i < num_bufs; i++) + bufs[i]->seqn = i; + + /* Insert packet with seqn 1: + * reorder_seq = 0 + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {NULL, 1, NULL, NULL} + */ + rte_reorder_insert(b, bufs[1]); + + /* Check no drained packets if no ready/order packets */ + cnt = rte_reorder_drain(b, bufs, 1); + if (cnt != 0) { + printf("%s:%d: drained packets from empty reorder buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + /* Insert more packets + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {0, 1, NULL, 3} + */ + rte_reorder_insert(b, bufs[0]); + rte_reorder_insert(b, bufs[3]); + + /* drained expected packets */ + cnt = rte_reorder_drain(b, bufs, 4); + if (cnt != 2) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + ret = -1; + goto exit; + } + + /* + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {NULL, 3, NULL, NULL} + */ + + rte_reorder_insert(b, bufs[4]); + rte_reorder_insert(b, bufs[7]); + + /* + * RB[] = {3, 4, NULL, NULL} + * OB[] = {NULL, NULL, 7, NULL} + */ + + cnt = rte_reorder_drain(b, bufs, 4); + if (cnt != 2) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + ret = -1; + goto exit; + } + + ret = 0; +exit: + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + rte_reorder_free(b); + return ret; +} + +static int +test_setup(void) +{ + /* reorder buffer instance creation */ + if (test_params->b == NULL) { + test_params->b = rte_reorder_create("PKT_RO1", rte_socket_id(), + REORDER_BUFFER_SIZE); + if (test_params->b == NULL) { + printf("%s: Error creating reorder buffer instance b\n", + __func__); + return -1; + } + } else + rte_reorder_reset(test_params->b); + + /* mempool creation */ + if (test_params->p == NULL) { + test_params->p = rte_mempool_create("RO_MBUF_POOL", NUM_MBUFS, + MBUF_SIZE, BURST, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->p == NULL) { + printf("%s: Error creating mempool\n", __func__); + return -1; + } + } + return 0; +} + +static struct unit_test_suite reorder_test_suite = { + + .setup = test_setup, + .suite_name = "Reorder Unit Test Suite", + .unit_test_cases = { + TEST_CASE(test_reorder_create), + TEST_CASE(test_reorder_init), + TEST_CASE(test_reorder_find_existing), + TEST_CASE(test_reorder_free), + TEST_CASE(test_reorder_insert), + TEST_CASE(test_reorder_drain), + TEST_CASES_END() + } +}; + +static int +test_reorder(void) +{ + return unit_test_suite_runner(&reorder_test_suite); +} + +static struct test_command reorder_cmd = { + .command = "reorder_autotest", + .callback = test_reorder, +}; +REGISTER_TEST_COMMAND(reorder_cmd); diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 4294d9a..74910a6 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -71,6 +71,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_DISTRIBUTOR),y) LDLIBS += -lrte_distributor endif +ifeq ($(CONFIG_RTE_LIBRTE_REORDER),y) +LDLIBS += -lrte_reorder +endif + ifeq ($(CONFIG_RTE_LIBRTE_KNI),y) ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) LDLIBS += -lrte_kni -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v3 3/5] examples: new sample app packet_ordering 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 1/5] reorder: new reorder library Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 2/5] app: New reorder unit test Sergio Gonzalez Monroy @ 2015-02-06 15:06 ` Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 4/5] doc: new reorder library description Sergio Gonzalez Monroy ` (3 subsequent siblings) 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-06 15:06 UTC (permalink / raw) To: dev This new app makes use of the librte_reorder library. It requires at least 3 lcores for RX, Workers (1 or more) and TX threads. Communication between RX-Workers and Workers-TX is done by using rings. The flow of mbufs is the following: * RX thread gets mbufs from driver, set sequence number and enqueue them in ring. * Workers dequeue mbufs from ring, do some 'work' and enqueue mbufs in ring. * TX dequeue mbufs from ring, inserts them in reorder buffer, drains mbufs from reorder and sends them to the driver. Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- examples/packet_ordering/Makefile | 50 +++ examples/packet_ordering/main.c | 695 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 745 insertions(+) create mode 100644 examples/packet_ordering/Makefile create mode 100644 examples/packet_ordering/main.c diff --git a/examples/packet_ordering/Makefile b/examples/packet_ordering/Makefile new file mode 100644 index 0000000..9e080a3 --- /dev/null +++ b/examples/packet_ordering/Makefile @@ -0,0 +1,50 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overridden by command line or environment +RTE_TARGET ?= x86_64-ivshmem-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +# binary name +APP = packet_ordering + +# all source are stored in SRCS-y +SRCS-y := main.c + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/packet_ordering/main.c b/examples/packet_ordering/main.c new file mode 100644 index 0000000..75e2f46 --- /dev/null +++ b/examples/packet_ordering/main.c @@ -0,0 +1,695 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <signal.h> +#include <getopt.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_errno.h> +#include <rte_ethdev.h> +#include <rte_lcore.h> +#include <rte_mbuf.h> +#include <rte_mempool.h> +#include <rte_ring.h> +#include <rte_reorder.h> + +#define RX_DESC_PER_QUEUE 128 +#define TX_DESC_PER_QUEUE 512 + +#define MAX_PKTS_BURST 32 +#define REORDER_BUFFER_SIZE 8192 +#define MBUF_PER_POOL 65535 +#define MBUF_SIZE (1600 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_POOL_CACHE_SIZE 250 + +#define RING_SIZE 16384 + +/* uncommnet below line to enable debug logs */ +/* #define DEBUG */ + +#ifdef DEBUG +#define LOG_LEVEL RTE_LOG_DEBUG +#define LOG_DEBUG(log_type, fmt, args...) RTE_LOG(DEBUG, log_type, fmt, ##args) +#else +#define LOG_LEVEL RTE_LOG_INFO +#define LOG_DEBUG(log_type, fmt, args...) do {} while (0) +#endif + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDERAPP RTE_LOGTYPE_USER1 + +unsigned int portmask; +unsigned int disable_reorder; +volatile uint8_t quit_signal; + +static struct rte_mempool *mbuf_pool; + +static struct rte_eth_conf port_conf_default; + +struct worker_thread_args { + struct rte_ring *ring_in; + struct rte_ring *ring_out; +}; + +struct output_buffer { + unsigned count; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; +}; + +volatile struct app_stats { + struct { + uint64_t rx_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } rx __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } wkr __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + /* Too early pkts transmitted directly w/o reordering */ + uint64_t early_pkts_txtd_woro; + /* Too early pkts failed from direct transmit */ + uint64_t early_pkts_tx_failed_woro; + uint64_t ro_tx_pkts; + uint64_t ro_tx_failed_pkts; + } tx __rte_cache_aligned; +} app_stats; + +/** + * Get the last enabled lcore ID + * + * @return + * The last enabled lcore ID. + */ +static unsigned int +get_last_lcore_id(void) +{ + int i; + + for (i = RTE_MAX_LCORE - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return 0; +} + +/** + * Get the previous enabled lcore ID + * @param id + * The current lcore ID + * @return + * The previous enabled lcore ID or the current lcore + * ID if it is the first available core. + */ +static unsigned int +get_previous_lcore_id(unsigned int id) +{ + int i; + + for (i = id - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return id; +} + +static inline void +pktmbuf_free_bulk(struct rte_mbuf *mbuf_table[], unsigned n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + rte_pktmbuf_free(mbuf_table[i]); +} + +/* display usage */ +static void +print_usage(const char *prgname) +{ + printf("%s [EAL options] -- -p PORTMASK\n" + " -p PORTMASK: hexadecimal bitmask of ports to configure\n", + prgname); +} + +static int +parse_portmask(const char *portmask) +{ + unsigned long pm; + char *end = NULL; + + /* parse hexadecimal string */ + pm = strtoul(portmask, &end, 16); + if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) + return -1; + + if (pm == 0) + return -1; + + return pm; +} + +/* Parse the argument given in the command line of the application */ +static int +parse_args(int argc, char **argv) +{ + int opt; + int option_index; + char **argvopt; + char *prgname = argv[0]; + static struct option lgopts[] = { + {"disable-reorder", 0, 0, 0}, + {NULL, 0, 0, 0} + }; + + argvopt = argv; + + while ((opt = getopt_long(argc, argvopt, "p:", + lgopts, &option_index)) != EOF) { + switch (opt) { + /* portmask */ + case 'p': + portmask = parse_portmask(optarg); + if (portmask == 0) { + printf("invalid portmask\n"); + print_usage(prgname); + return -1; + } + break; + /* long options */ + case 0: + if (!strcmp(lgopts[option_index].name, "disable-reorder")) { + printf("reorder disabled\n"); + disable_reorder = 1; + } + break; + default: + print_usage(prgname); + return -1; + } + } + if (optind <= 1) { + print_usage(prgname); + return -1; + } + + argv[optind-1] = prgname; + optind = 0; /* reset getopt lib */ + return 0; +} + +static inline int +configure_eth_port(uint8_t port_id) +{ + struct ether_addr addr; + const uint16_t rxRings = 1, txRings = 1; + const uint8_t nb_ports = rte_eth_dev_count(); + int ret; + uint16_t q; + + if (port_id > nb_ports) + return -1; + + ret = rte_eth_dev_configure(port_id, rxRings, txRings, &port_conf_default); + if (ret != 0) + return ret; + + for (q = 0; q < rxRings; q++) { + ret = rte_eth_rx_queue_setup(port_id, q, RX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL, + mbuf_pool); + if (ret < 0) + return ret; + } + + for (q = 0; q < txRings; q++) { + ret = rte_eth_tx_queue_setup(port_id, q, TX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL); + if (ret < 0) + return ret; + } + + ret = rte_eth_dev_start(port_id); + if (ret < 0) + return ret; + + rte_eth_macaddr_get(port_id, &addr); + printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 + " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", + (unsigned)port_id, + addr.addr_bytes[0], addr.addr_bytes[1], + addr.addr_bytes[2], addr.addr_bytes[3], + addr.addr_bytes[4], addr.addr_bytes[5]); + + rte_eth_promiscuous_enable(port_id); + + return 0; +} + +static void +print_stats(void) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + unsigned i; + struct rte_eth_stats eth_stats; + + printf("\nRX thread stats:\n"); + printf(" - Pkts rxd: %"PRIu64"\n", + app_stats.rx.rx_pkts); + printf(" - Pkts enqd to workers ring: %"PRIu64"\n", + app_stats.rx.enqueue_pkts); + + printf("\nWorker thread stats:\n"); + printf(" - Pkts deqd from workers ring: %"PRIu64"\n", + app_stats.wkr.dequeue_pkts); + printf(" - Pkts enqd to tx ring: %"PRIu64"\n", + app_stats.wkr.enqueue_pkts); + printf(" - Pkts enq to tx failed: %"PRIu64"\n", + app_stats.wkr.enqueue_failed_pkts); + + printf("\nTX stats:\n"); + printf(" - Pkts deqd from tx ring: %"PRIu64"\n", + app_stats.tx.dequeue_pkts); + printf(" - Ro Pkts transmitted: %"PRIu64"\n", + app_stats.tx.ro_tx_pkts); + printf(" - Ro Pkts tx failed: %"PRIu64"\n", + app_stats.tx.ro_tx_failed_pkts); + printf(" - Pkts transmitted w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_txtd_woro); + printf(" - Pkts tx failed w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_tx_failed_woro); + + for (i = 0; i < nb_ports; i++) { + rte_eth_stats_get(i, ð_stats); + printf("\nPort %u stats:\n", i); + printf(" - Pkts in: %"PRIu64"\n", eth_stats.ipackets); + printf(" - Pkts out: %"PRIu64"\n", eth_stats.opackets); + printf(" - In Errs: %"PRIu64"\n", eth_stats.ierrors); + printf(" - Out Errs: %"PRIu64"\n", eth_stats.oerrors); + printf(" - Mbuf Errs: %"PRIu64"\n", eth_stats.rx_nombuf); + } +} + +static void +int_handler(int sig_num) +{ + printf("Exiting on signal %d\n", sig_num); + quit_signal = 1; +} + +/** + * This thread receives mbufs from the port and affects them an internal + * sequence number to keep track of their order of arrival through an + * mbuf structure. + * The mbufs are then passed to the worker threads via the rx_to_workers + * ring. + */ +static int +rx_thread(struct rte_ring *ring_out) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint32_t seqn = 0; + uint16_t i, ret = 0; + uint16_t nb_rx_pkts; + uint8_t port_id; + struct rte_mbuf *pkts[MAX_PKTS_BURST]; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + + while (!quit_signal) { + + for (port_id = 0; port_id < nb_ports; port_id++) { + if ((portmask & (1 << port_id)) != 0) { + + /* receive packets */ + nb_rx_pkts = rte_eth_rx_burst(port_id, 0, + pkts, MAX_PKTS_BURST); + if (nb_rx_pkts == 0) { + LOG_DEBUG(REORDERAPP, + "%s():Received zero packets\n", __func__); + continue; + } + app_stats.rx.rx_pkts += nb_rx_pkts; + + /* mark sequence number */ + for (i = 0; i < nb_rx_pkts; ) + pkts[i++]->seqn = seqn++; + + /* enqueue to rx_to_workers ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *) pkts, + nb_rx_pkts); + app_stats.rx.enqueue_pkts += ret; + if (unlikely(ret < nb_rx_pkts)) { + app_stats.rx.enqueue_failed_pkts += + (nb_rx_pkts-ret); + pktmbuf_free_bulk(&pkts[ret], nb_rx_pkts - ret); + } + } + } + } + return 0; +} + +/** + * This thread takes bursts of packets from the rx_to_workers ring and + * Changes the input port value to output port value. And feds it to + * workers_to_tx + */ +static int +worker_thread(void *args_ptr) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint16_t i, ret = 0; + uint16_t burst_size = 0; + struct worker_thread_args *args; + struct rte_mbuf *burst_buffer[MAX_PKTS_BURST] = { NULL }; + struct rte_ring *ring_in, *ring_out; + const unsigned xor_val = (nb_ports > 1); + + args = (struct worker_thread_args *) args_ptr; + ring_in = args->ring_in; + ring_out = args->ring_out; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + + while (!quit_signal) { + + /* dequeue the mbufs from rx_to_workers ring */ + burst_size = rte_ring_dequeue_burst(ring_in, + (void *)burst_buffer, MAX_PKTS_BURST); + if (unlikely(burst_size == 0)) + continue; + + __sync_fetch_and_add(&app_stats.wkr.dequeue_pkts, burst_size); + + /* just do some operation on mbuf */ + for (i = 0; i < burst_size;) + burst_buffer[i++]->port ^= xor_val; + + /* enqueue the modified mbufs to workers_to_tx ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *)burst_buffer, burst_size); + __sync_fetch_and_add(&app_stats.wkr.enqueue_pkts, ret); + if (unlikely(ret < burst_size)) { + /* Return the mbufs to their respective pool, dropping packets */ + __sync_fetch_and_add(&app_stats.wkr.enqueue_failed_pkts, + (int)burst_size - ret); + pktmbuf_free_bulk(&burst_buffer[ret], burst_size - ret); + } + } + return 0; +} + +static inline void +flush_one_port(struct output_buffer *outbuf, uint8_t outp) +{ + unsigned nb_tx = rte_eth_tx_burst(outp, 0, outbuf->mbufs, + outbuf->count); + app_stats.tx.ro_tx_pkts += nb_tx; + + if (unlikely(nb_tx < outbuf->count)) { + /* free the mbufs which failed from transmit */ + app_stats.tx.ro_tx_failed_pkts += (outbuf->count - nb_tx); + LOG_DEBUG(REORDERAPP, "%s:Packet loss with tx_burst\n", __func__); + pktmbuf_free_bulk(&outbuf->mbufs[nb_tx], outbuf->count - nb_tx); + } + outbuf->count = 0; +} + +/** + * Dequeue mbufs from the workers_to_tx ring and reorder them before + * transmitting. + */ +static int +send_thread(struct rte_ring *ring_in) +{ + int ret; + unsigned int i, dret; + uint16_t nb_dq_mbufs; + uint8_t outp; + static struct output_buffer tx_buffers[RTE_MAX_ETHPORTS]; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; + struct rte_mbuf *rombufs[MAX_PKTS_BURST] = {NULL}; + struct rte_reorder_buffer *buffer; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + buffer = rte_reorder_create("PKT_RO", rte_socket_id(), REORDER_BUFFER_SIZE); + while (!quit_signal) { + + /* deque the mbufs from workers_to_tx ring */ + nb_dq_mbufs = rte_ring_dequeue_burst(ring_in, + (void *)mbufs, MAX_PKTS_BURST); + + if (unlikely(nb_dq_mbufs == 0)) + continue; + + app_stats.tx.dequeue_pkts += nb_dq_mbufs; + + for (i = 0; i < nb_dq_mbufs; i++) { + /* send dequeued mbufs for reordering */ + ret = rte_reorder_insert(buffer, mbufs[i]); + + if (ret == -1 && rte_errno == ERANGE) { + /* Too early pkts should be transmitted out directly */ + LOG_DEBUG(REORDERAPP, "%s():Cannot reorder early packet " + "direct enqueuing to TX\n", __func__); + outp = mbufs[i]->port; + if ((portmask & (1 << outp)) == 0) { + rte_pktmbuf_free(mbufs[i]); + continue; + } + if (rte_eth_tx_burst(outp, 0, (void *)mbufs[i], 1) != 1) { + rte_pktmbuf_free(mbufs[i]); + app_stats.tx.early_pkts_tx_failed_woro++; + } else + app_stats.tx.early_pkts_txtd_woro++; + } else if (ret == -1 && rte_errno == ENOSPC) { + /** + * Early pkts just outside of window should be dropped + */ + rte_pktmbuf_free(mbufs[i]); + } + } + + /* + * drain MAX_PKTS_BURST of reordered + * mbufs for transmit + */ + dret = rte_reorder_drain(buffer, rombufs, MAX_PKTS_BURST); + for (i = 0; i < dret; i++) { + + struct output_buffer *outbuf; + uint8_t outp1; + + outp1 = rombufs[i]->port; + /* skip ports that are not enabled */ + if ((portmask & (1 << outp1)) == 0) { + rte_pktmbuf_free(rombufs[i]); + continue; + } + + outbuf = &tx_buffers[outp1]; + outbuf->mbufs[outbuf->count++] = rombufs[i]; + if (outbuf->count == MAX_PKTS_BURST) + flush_one_port(outbuf, outp1); + } + } + return 0; +} + +/** + * Dequeue mbufs from the workers_to_tx ring and transmit them + */ +static int +tx_thread(struct rte_ring *ring_in) +{ + uint32_t i, dqnum; + uint8_t outp; + static struct output_buffer tx_buffers[RTE_MAX_ETHPORTS]; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; + struct output_buffer *outbuf; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + while (!quit_signal) { + + /* deque the mbufs from workers_to_tx ring */ + dqnum = rte_ring_dequeue_burst(ring_in, + (void *)mbufs, MAX_PKTS_BURST); + + if (unlikely(dqnum == 0)) + continue; + + app_stats.tx.dequeue_pkts += dqnum; + + for (i = 0; i < dqnum; i++) { + outp = mbufs[i]->port; + /* skip ports that are not enabled */ + if ((portmask & (1 << outp)) == 0) { + rte_pktmbuf_free(mbufs[i]); + continue; + } + + outbuf = &tx_buffers[outp]; + outbuf->mbufs[outbuf->count++] = mbufs[i]; + if (outbuf->count == MAX_PKTS_BURST) + flush_one_port(outbuf, outp); + } + } + + return 0; +} + +int +main(int argc, char **argv) +{ + int ret; + unsigned nb_ports; + unsigned int lcore_id, last_lcore_id, master_lcore_id; + uint8_t port_id; + uint8_t nb_ports_available; + struct worker_thread_args worker_args = {NULL, NULL}; + struct rte_ring *rx_to_workers; + struct rte_ring *workers_to_tx; + + /* catch ctrl-c so we can print on exit */ + signal(SIGINT, int_handler); + + /* Initialize EAL */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + return -1; + + argc -= ret; + argv += ret; + + /* Parse the application specific arguments */ + ret = parse_args(argc, argv); + if (ret < 0) + return -1; + + /* Check if we have enought cores */ + if (rte_lcore_count() < 3) + rte_exit(EXIT_FAILURE, "Error, This application needs at " + "least 3 logical cores to run:\n" + "1 lcore for packet RX\n" + "1 lcore for packet TX\n" + "and at least 1 lcore for worker threads\n"); + + nb_ports = rte_eth_dev_count(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "Error: no ethernet ports detected\n"); + if (nb_ports != 1 && (nb_ports & 1)) + rte_exit(EXIT_FAILURE, "Error: number of ports must be even, except " + "when using a single port\n"); + + mbuf_pool = rte_mempool_create("mbuf_pool", MBUF_PER_POOL, MBUF_SIZE, + MBUF_POOL_CACHE_SIZE, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (mbuf_pool == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + nb_ports_available = nb_ports; + + /* initialize all ports */ + for (port_id = 0; port_id < nb_ports; port_id++) { + /* skip ports that are not enabled */ + if ((portmask & (1 << port_id)) == 0) { + printf("\nSkipping disabled port %d\n", port_id); + nb_ports_available--; + continue; + } + /* init port */ + printf("Initializing port %u... done\n", (unsigned) port_id); + + if (configure_eth_port(port_id) != 0) + rte_exit(EXIT_FAILURE, "Cannot initialize port %"PRIu8"\n", + port_id); + } + + if (!nb_ports_available) { + rte_exit(EXIT_FAILURE, + "All available ports are disabled. Please set portmask.\n"); + } + + /* Create rings for inter core communication */ + rx_to_workers = rte_ring_create("rx_to_workers", RING_SIZE, rte_socket_id(), + RING_F_SP_ENQ); + if (rx_to_workers == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + workers_to_tx = rte_ring_create("workers_to_tx", RING_SIZE, rte_socket_id(), + RING_F_SC_DEQ); + if (workers_to_tx == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + last_lcore_id = get_last_lcore_id(); + master_lcore_id = rte_get_master_lcore(); + + worker_args.ring_in = rx_to_workers; + worker_args.ring_out = workers_to_tx; + + /* Start worker_thread() on all the available slave cores but the last 1 */ + for (lcore_id = 0; lcore_id <= get_previous_lcore_id(last_lcore_id); lcore_id++) + if (rte_lcore_is_enabled(lcore_id) && lcore_id != master_lcore_id) + rte_eal_remote_launch(worker_thread, (void *)&worker_args, + lcore_id); + + if (disable_reorder) + /* Start tx_thread() on the last slave core */ + rte_eal_remote_launch((lcore_function_t *)tx_thread, workers_to_tx, + last_lcore_id); + else + /* Start send_thread() on the last slave core */ + rte_eal_remote_launch((lcore_function_t *)send_thread, workers_to_tx, + last_lcore_id); + + /* Start rx_thread() on the master core */ + rx_thread(rx_to_workers); + + RTE_LCORE_FOREACH_SLAVE(lcore_id) { + if (rte_eal_wait_lcore(lcore_id) < 0) + return -1; + } + + print_stats(); + return 0; +} -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v3 4/5] doc: new reorder library description 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy ` (2 preceding siblings ...) 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 3/5] examples: new sample app packet_ordering Sergio Gonzalez Monroy @ 2015-02-06 15:06 ` Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 5/5] doc: new packet ordering app description Sergio Gonzalez Monroy ` (2 subsequent siblings) 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-06 15:06 UTC (permalink / raw) To: dev This patch introduces a new section in the programmers guide describing the reorder library. Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/reorder_lib.rst | 115 ++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 doc/guides/prog_guide/reorder_lib.rst diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 8d86dd4..de69682 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -61,6 +61,7 @@ Programmer's Guide lpm_lib lpm6_lib packet_distrib_lib + reorder_lib ip_fragment_reassembly_lib multi_proc_support kernel_nic_interface diff --git a/doc/guides/prog_guide/reorder_lib.rst b/doc/guides/prog_guide/reorder_lib.rst new file mode 100644 index 0000000..bbd0521 --- /dev/null +++ b/doc/guides/prog_guide/reorder_lib.rst @@ -0,0 +1,115 @@ +.. BSD LICENSE + Copyright(c) 2015 Intel Corporation. All rights reserved. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.. _Reorder_Library: + +Reorder Library +================= + +The Reorder Library provides a mechanism for reordering mbufs based on their +sequence number. + +Operation +---------- + +The reorder library is essentially a buffer that reorders mbufs. +The user inserts out of order mbufs into the reorder buffer and pulls in-order +mbufs from it. + +At a given time, the reorder buffer contains mbufs whose sequence number are +inside the sequence window. The sequence window is determined by the minimum +sequence number and the number of entries that the buffer was configured to hold. +For example, given a reorder buffer with 200 entries and a minimum sequence +number of 350, the sequence window has low and high limits of 350 and 550 +respectively. + +When inserting mbufs, the reorder library differentiates between valid, early +and late mbufs depending on the sequence number of the inserted mbuf: + +* valid: the sequence number is inside the window. +* late: the sequence number is outside the window and less than the low limit. +* early: the sequence number is outside the window and greater than the high + limit. + +The reorder buffer directly returns late mbufs and tries to accommodate early +mbufs. + + +Implementation Details +------------------------- + +The reorder library is implemented as a pair of buffers, which referred to as +the *Order* buffer and the *Ready* buffer. + +On an insert call, valid mbufs are inserted directly into the Order buffer and +late mbufs are returned to the user with an error. + +In the case of early mbufs, the reorder buffer will try to move the window +(incrementing the minimum sequence number) so that the mbuf becomes a valid one. +To that end, mbufs in the Order buffer are moved into the Ready buffer. +Any mbufs that have not arrived yet are ignored and therefore will become +late mbufs. +This means that as long as there is room in the Ready buffer, the window will +be moved to accommodate early mbufs that would otherwise be outside the +reordering window. + +For example, assuming that we have a buffer of 200 entries with a 350 minimum +sequence number, and we need to insert an early mbuf with 565 sequence number. +That means that we would need to move the windows at least 15 positions to +accommodate the mbuf. +The reorder buffer would try to move mbufs from at least the next 15 slots in +the Order buffer to the Ready buffer, as long as there is room in the Ready buffer. +Any gaps in the Order buffer at that point are skipped, and those packet will +be reported as late packets when they arrive. The process of moving packets +to the Ready buffer continues beyond the minimum required until a gap, +i.e. missing mbuf, in the Order buffer is encountered. + +When draining mbufs, the reorder buffer would return mbufs in the Ready +buffer first and then from the Order buffer until a gap is found (mbufs that +have not arrived yet). + +Use Case: Packet Distributor +------------------------------- + +An application using the DPDK packet distributor could make use of the reorder +library to transmit packets in the same order they were received. + +A basic packet distributor use case would consist of a distributor with +multiple workers cores. +The processing of packets by the workers is not guaranteed to be in order, +hence a reorder buffer can be used to order as many packets as possible. + +In such a scenario, the distributor assigns a sequence number to mbufs before +delivering them to the workers. +As the workers finish processing the packets, the distributor inserts those +mbufs into the reorder buffer and finally transmit drained mbufs. + +NOTE: Currently the reorder buffer is not thread safe so the same thread is +responsible for inserting and draining mbufs. -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v3 5/5] doc: new packet ordering app description 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy ` (3 preceding siblings ...) 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 4/5] doc: new reorder library description Sergio Gonzalez Monroy @ 2015-02-06 15:06 ` Sergio Gonzalez Monroy 2015-02-08 13:58 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Neil Horman 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-06 15:06 UTC (permalink / raw) To: dev This patch describes how to build and run he new packet ordering sample application that exercises the reorder library. Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- doc/guides/sample_app_ug/index.rst | 1 + doc/guides/sample_app_ug/packet_ordering.rst | 102 +++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst index d07dec3..5720181 100644 --- a/doc/guides/sample_app_ug/index.rst +++ b/doc/guides/sample_app_ug/index.rst @@ -60,6 +60,7 @@ Sample Applications User Guide intel_quickassist quota_watermark timer + packet_ordering vmdq_dcb_forwarding vhost netmap_compatibility diff --git a/doc/guides/sample_app_ug/packet_ordering.rst b/doc/guides/sample_app_ug/packet_ordering.rst new file mode 100644 index 0000000..481f1b7 --- /dev/null +++ b/doc/guides/sample_app_ug/packet_ordering.rst @@ -0,0 +1,102 @@ +.. BSD LICENSE + Copyright(c) 2015 Intel Corporation. All rights reserved. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Packet Ordering Application +============================ + +The Packet Ordering sample app simply shows the impact of reordering a stream. +It's meant to stress the library with different configurations for performance. + +Overview +-------- + +The application uses at least three CPU cores: + +* RX core (maser core) receives traffic from the NIC ports and feeds Worker + cores with traffic through SW queues. + +* Worker core (slave core) basically do some light work on the packet. + Currently it modifies the output port of the packet for configurations with + more than one port enabled. + +* TX Core (slave core) receives traffic from Woker cores through software queues, + inserts out-of-order packets into reorder buffer, extracts ordered packets + from the reorder buffer and sends them to the NIC ports for transmission. + +Compiling the Application +-------------------------- + +#. Go to the example directory: + + .. code-block:: console + + export RTE_SDK=/path/to/rte_sdk + cd ${RTE_SDK}/examples/helloworld + +#. Set the target (a default target is used if not specified). For example: + + .. code-block:: console + + export RTE_TARGET=x86_64-native-linuxapp-gcc + + See the *DPDK Getting Started* Guide for possible RTE_TARGET values. + +#. Build the application: + + .. code-block:: console + + make + +Running the Application +----------------------- + +Refer to *DPDK Getting Started Guide* for general information on running applications +and the Environment Abstraction Layer (EAL) options. + +Application Command Line +~~~~~~~~~~~~~~~~~~~~~~~~ + +The application execution command line is: + +.. code-block:: console + + ./test-pipeline [EAL options] -- -p PORTMASK [--disable-reorder] + +The -c EAL CPU_COREMASK option has to contain at least 3 CPU cores. +The first CPU core in the core mask is the master core and would be assigned to +RX core, the last to TX core and the rest to Worker cores. + +The PORTMASK parameter must contain either 1 or even enabled port numbers. +When setting more than 1 port, traffic would be forwarderd in pairs. +For example, if we enable 4 ports, traffic from port 0 to 1 and from 1 to 0, +then the other pair from 2 to 3 and from 3 to 2, having [0,1] and [2,3] pairs. + +The disable-reorder long option does, as its name implies, disable the reordering +of traffic, which should help evaluate reordering performance impact. -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v3 0/5] New Reorder Library 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy ` (4 preceding siblings ...) 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 5/5] doc: new packet ordering app description Sergio Gonzalez Monroy @ 2015-02-08 13:58 ` Neil Horman 2015-02-11 11:17 ` Gonzalez Monroy, Sergio 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy 6 siblings, 1 reply; 44+ messages in thread From: Neil Horman @ 2015-02-08 13:58 UTC (permalink / raw) To: Sergio Gonzalez Monroy; +Cc: dev On Fri, Feb 06, 2015 at 03:05:59PM +0000, Sergio Gonzalez Monroy wrote: > This series introduces the new reorder library along with unit tests, > sample app and a new entry in the programmers guide describing the library. > > The library provides reordering of mbufs based on their sequence number. > > As mention in the patch describing the library, one use case is the > packet distributor. > The distributor receives packets, assigns them a sequence number and sends > them to the workers. > The workers process those packets and return them to the distributor. > The distributor collects out-of-order packets from the workers and uses > this library to reorder the packets based on the sequence number they > were assigned. > > v3: > - fix copyright date > - add option to sample app to disable reordering > - add packet ordering sample guide entry > > v2: > - add programmers guide entry describing the library > - use malloc instead of memzone to allocate memory > - modify create and init implementation, init takes a reorder buffer as input > and create reserves memory and call init. > - update unit tests > > Sergio Gonzalez Monroy (5): > reorder: new reorder library > app: New reorder unit test > examples: new sample app packet_ordering > doc: new reorder library description > doc: new packet ordering app description > > app/test/Makefile | 2 + > app/test/test_reorder.c | 393 ++++++++++++++ > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/guides/prog_guide/index.rst | 1 + > doc/guides/prog_guide/reorder_lib.rst | 115 ++++ > doc/guides/sample_app_ug/index.rst | 1 + > doc/guides/sample_app_ug/packet_ordering.rst | 102 ++++ > examples/packet_ordering/Makefile | 50 ++ > examples/packet_ordering/main.c | 695 +++++++++++++++++++++++++ > lib/Makefile | 1 + > lib/librte_eal/common/include/rte_tailq_elem.h | 2 + > lib/librte_mbuf/rte_mbuf.h | 3 + > lib/librte_reorder/Makefile | 50 ++ > lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++ > lib/librte_reorder/rte_reorder.h | 181 +++++++ > mk/rte.app.mk | 4 + > 17 files changed, 2026 insertions(+) > create mode 100644 app/test/test_reorder.c > create mode 100644 doc/guides/prog_guide/reorder_lib.rst > create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst > create mode 100644 examples/packet_ordering/Makefile > create mode 100644 examples/packet_ordering/main.c > create mode 100644 lib/librte_reorder/Makefile > create mode 100644 lib/librte_reorder/rte_reorder.c > create mode 100644 lib/librte_reorder/rte_reorder.h > > -- > 1.9.3 > > Functionally it looks pretty good. You need to define an export map and LIBABIVER macro in your makefile though. Neil ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v3 0/5] New Reorder Library 2015-02-08 13:58 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Neil Horman @ 2015-02-11 11:17 ` Gonzalez Monroy, Sergio 0 siblings, 0 replies; 44+ messages in thread From: Gonzalez Monroy, Sergio @ 2015-02-11 11:17 UTC (permalink / raw) To: Neil Horman; +Cc: dev > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Sunday, February 8, 2015 1:59 PM > To: Gonzalez Monroy, Sergio > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v3 0/5] New Reorder Library > > On Fri, Feb 06, 2015 at 03:05:59PM +0000, Sergio Gonzalez Monroy wrote: > > This series introduces the new reorder library along with unit tests, > > sample app and a new entry in the programmers guide describing the > library. > > > > The library provides reordering of mbufs based on their sequence number. > > > > As mention in the patch describing the library, one use case is the > > packet distributor. > > The distributor receives packets, assigns them a sequence number and > > sends them to the workers. > > The workers process those packets and return them to the distributor. > > The distributor collects out-of-order packets from the workers and > > uses this library to reorder the packets based on the sequence number > > they were assigned. > > > > v3: > > - fix copyright date > > - add option to sample app to disable reordering > > - add packet ordering sample guide entry > > > > v2: > > - add programmers guide entry describing the library > > - use malloc instead of memzone to allocate memory > > - modify create and init implementation, init takes a reorder buffer as > input > > and create reserves memory and call init. > > - update unit tests > > > > Sergio Gonzalez Monroy (5): > > reorder: new reorder library > > app: New reorder unit test > > examples: new sample app packet_ordering > > doc: new reorder library description > > doc: new packet ordering app description > > > > app/test/Makefile | 2 + > > app/test/test_reorder.c | 393 ++++++++++++++ > > config/common_bsdapp | 5 + > > config/common_linuxapp | 5 + > > doc/guides/prog_guide/index.rst | 1 + > > doc/guides/prog_guide/reorder_lib.rst | 115 ++++ > > doc/guides/sample_app_ug/index.rst | 1 + > > doc/guides/sample_app_ug/packet_ordering.rst | 102 ++++ > > examples/packet_ordering/Makefile | 50 ++ > > examples/packet_ordering/main.c | 695 > +++++++++++++++++++++++++ > > lib/Makefile | 1 + > > lib/librte_eal/common/include/rte_tailq_elem.h | 2 + > > lib/librte_mbuf/rte_mbuf.h | 3 + > > lib/librte_reorder/Makefile | 50 ++ > > lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++ > > lib/librte_reorder/rte_reorder.h | 181 +++++++ > > mk/rte.app.mk | 4 + > > 17 files changed, 2026 insertions(+) > > create mode 100644 app/test/test_reorder.c create mode 100644 > > doc/guides/prog_guide/reorder_lib.rst > > create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst > > create mode 100644 examples/packet_ordering/Makefile create mode > > 100644 examples/packet_ordering/main.c create mode 100644 > > lib/librte_reorder/Makefile create mode 100644 > > lib/librte_reorder/rte_reorder.c create mode 100644 > > lib/librte_reorder/rte_reorder.h > > > > -- > > 1.9.3 > > > > > Functionally it looks pretty good. You need to define an export map and > LIBABIVER macro in your makefile though. > Neil Oh yes! I did forget about the versioning. I'll send v4 shortly. Regards, Sergio ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v4 0/5] New Reorder Library 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy ` (5 preceding siblings ...) 2015-02-08 13:58 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Neil Horman @ 2015-02-11 13:07 ` Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 1/5] reorder: new reorder library Sergio Gonzalez Monroy ` (8 more replies) 6 siblings, 9 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-11 13:07 UTC (permalink / raw) To: dev This series introduces the new reorder library along with unit tests, sample app and a new entry in the programmers guide describing the library. The library provides reordering of mbufs based on their sequence number. As mention in the patch describing the library, one use case is the packet distributor. The distributor receives packets, assigns them a sequence number and sends them to the workers. The workers process those packets and return them to the distributor. The distributor collects out-of-order packets from the workers and uses this library to reorder the packets based on the sequence number they were assigned. v4: - add missing version.map and related versioning macros v3: - fix copyright date - add option to sample app to disable reordering - add packet ordering sample guide entry v2: - add programmers guide entry describing the library - use malloc instead of memzone to allocate memory - modify create and init implementation, init takes a reorder buffer as input and create reserves memory and call init. - update unit tests Sergio Gonzalez Monroy (5): reorder: new reorder library app: New reorder unit test examples: new sample app packet_ordering doc: new reorder library description doc: new packet ordering app description app/test/Makefile | 2 + app/test/test_reorder.c | 393 ++++++++++++++ config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/reorder_lib.rst | 115 ++++ doc/guides/sample_app_ug/index.rst | 1 + doc/guides/sample_app_ug/packet_ordering.rst | 102 ++++ examples/packet_ordering/Makefile | 50 ++ examples/packet_ordering/main.c | 695 +++++++++++++++++++++++++ lib/Makefile | 1 + lib/librte_eal/common/include/rte_tailq_elem.h | 2 + lib/librte_mbuf/rte_mbuf.h | 3 + lib/librte_reorder/Makefile | 54 ++ lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++ lib/librte_reorder/rte_reorder.h | 181 +++++++ lib/librte_reorder/rte_reorder_version.map | 13 + mk/rte.app.mk | 4 + 18 files changed, 2043 insertions(+) create mode 100644 app/test/test_reorder.c create mode 100644 doc/guides/prog_guide/reorder_lib.rst create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst create mode 100644 examples/packet_ordering/Makefile create mode 100644 examples/packet_ordering/main.c create mode 100644 lib/librte_reorder/Makefile create mode 100644 lib/librte_reorder/rte_reorder.c create mode 100644 lib/librte_reorder/rte_reorder.h create mode 100644 lib/librte_reorder/rte_reorder_version.map -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v4 1/5] reorder: new reorder library 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy @ 2015-02-11 13:07 ` Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 2/5] app: New reorder unit test Sergio Gonzalez Monroy ` (7 subsequent siblings) 8 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-11 13:07 UTC (permalink / raw) To: dev This library provides reordering capability for out of order mbufs based on a sequence number in the mbuf structure. Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_eal/common/include/rte_tailq_elem.h | 2 + lib/librte_mbuf/rte_mbuf.h | 3 + lib/librte_reorder/Makefile | 54 ++++ lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++++++++++++ lib/librte_reorder/rte_reorder.h | 181 +++++++++++ lib/librte_reorder/rte_reorder_version.map | 13 + 9 files changed, 680 insertions(+) create mode 100644 lib/librte_reorder/Makefile create mode 100644 lib/librte_reorder/rte_reorder.c create mode 100644 lib/librte_reorder/rte_reorder.h create mode 100644 lib/librte_reorder/rte_reorder_version.map diff --git a/config/common_bsdapp b/config/common_bsdapp index 9177db1..e3e0e94 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -334,6 +334,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 2f9643b..b5ec730 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -342,6 +342,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/lib/Makefile b/lib/Makefile index 0ffc982..5919d32 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -65,6 +65,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += librte_distributor DIRS-$(CONFIG_RTE_LIBRTE_PORT) += librte_port DIRS-$(CONFIG_RTE_LIBRTE_TABLE) += librte_table DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += librte_pipeline +DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += librte_reorder ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni diff --git a/lib/librte_eal/common/include/rte_tailq_elem.h b/lib/librte_eal/common/include/rte_tailq_elem.h index f74fc7c..3013869 100644 --- a/lib/librte_eal/common/include/rte_tailq_elem.h +++ b/lib/librte_eal/common/include/rte_tailq_elem.h @@ -84,6 +84,8 @@ rte_tailq_elem(RTE_TAILQ_ACL, "RTE_ACL") rte_tailq_elem(RTE_TAILQ_DISTRIBUTOR, "RTE_DISTRIBUTOR") +rte_tailq_elem(RTE_TAILQ_REORDER, "RTE_REORDER") + rte_tailq_end(RTE_TAILQ_NUM) #undef rte_tailq_elem diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h index 16059c6..ed27eb8 100644 --- a/lib/librte_mbuf/rte_mbuf.h +++ b/lib/librte_mbuf/rte_mbuf.h @@ -262,6 +262,9 @@ struct rte_mbuf { uint32_t usr; /**< User defined tags. See @rte_distributor_process */ } hash; /**< hash information */ + /* sequence number - field used in distributor and reorder library */ + uint32_t seqn; + /* second cache line - fields only used in slow path or on TX */ MARKER cacheline1 __rte_cache_aligned; diff --git a/lib/librte_reorder/Makefile b/lib/librte_reorder/Makefile new file mode 100644 index 0000000..0c01de1 --- /dev/null +++ b/lib/librte_reorder/Makefile @@ -0,0 +1,54 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(RTE_SDK)/mk/rte.vars.mk + +# library name +LIB = librte_reorder.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) + +EXPORT_MAP := rte_reorder_version.map + +LIBABIVER := 1 + +# all source are stored in SRCS-y +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) := rte_reorder.c + +# install this header file +SYMLINK-$(CONFIG_RTE_LIBRTE_REORDER)-include := rte_reorder.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_eal + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_reorder/rte_reorder.c b/lib/librte_reorder/rte_reorder.c new file mode 100644 index 0000000..42d2a47 --- /dev/null +++ b/lib/librte_reorder/rte_reorder.c @@ -0,0 +1,416 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <inttypes.h> +#include <string.h> + +#include <rte_log.h> +#include <rte_mbuf.h> +#include <rte_memzone.h> +#include <rte_eal_memconfig.h> +#include <rte_errno.h> +#include <rte_tailq.h> +#include <rte_malloc.h> + +#include "rte_reorder.h" + +TAILQ_HEAD(rte_reorder_list, rte_tailq_entry); + +#define NO_FLAGS 0 +#define RTE_REORDER_PREFIX "RO_" +#define RTE_REORDER_NAMESIZE 32 + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDER RTE_LOGTYPE_USER1 + +/* A generic circular buffer */ +struct cir_buffer { + unsigned int size; /**< Number of entries that can be stored */ + unsigned int mask; /**< [buffer_size - 1]: used for wrap-around */ + unsigned int head; /**< insertion point in buffer */ + unsigned int tail; /**< extraction point in buffer */ + struct rte_mbuf **entries; +} __rte_cache_aligned; + +/* The reorder buffer data structure itself */ +struct rte_reorder_buffer { + char name[RTE_REORDER_NAMESIZE]; + uint32_t min_seqn; /**< Lowest seq. number that can be in the buffer */ + unsigned int memsize; /**< memory area size of reorder buffer */ + struct cir_buffer ready_buf; /**< temp buffer for dequeued entries */ + struct cir_buffer order_buf; /**< buffer used to reorder entries */ +} __rte_cache_aligned; + +static void +rte_reorder_free_mbufs(struct rte_reorder_buffer *b); + +struct rte_reorder_buffer * +rte_reorder_init(struct rte_reorder_buffer *b, unsigned int bufsize, + const char *name, unsigned int size) +{ + const unsigned int min_bufsize = sizeof(*b) + + (2 * size * sizeof(struct rte_mbuf *)); + + if (b == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer parameter:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (bufsize < min_bufsize) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer memory size: %u, " + "minimum required: %u\n", bufsize, min_bufsize); + rte_errno = EINVAL; + return NULL; + } + + memset(b, 0, bufsize); + snprintf(b->name, sizeof(b->name), "%s", name); + b->memsize = bufsize; + b->order_buf.size = b->ready_buf.size = size; + b->order_buf.mask = b->ready_buf.mask = size - 1; + b->ready_buf.entries = (void *)&b[1]; + b->order_buf.entries = RTE_PTR_ADD(&b[1], + size * sizeof(b->ready_buf.entries[0])); + + return b; +} + +struct rte_reorder_buffer* +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + const unsigned int bufsize = sizeof(struct rte_reorder_buffer) + + (2 * size * sizeof(struct rte_mbuf *)); + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + /* Check user arguments. */ + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* guarantee there's no existing */ + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + if (te != NULL) + goto exit; + + /* allocate tailq entry */ + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); + if (te == NULL) { + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); + rte_errno = ENOMEM; + b = NULL; + goto exit; + } + + /* Allocate memory to store the reorder buffer structure. */ + b = rte_zmalloc_socket("REORDER_BUFFER", bufsize, 0, socket_id); + if (b == NULL) { + RTE_LOG(ERR, REORDER, "Memzone allocation failed\n"); + rte_errno = ENOMEM; + rte_free(te); + } else { + rte_reorder_init(b, bufsize, name, size); + te->data = (void *)b; + TAILQ_INSERT_TAIL(reorder_list, te, next); + } + +exit: + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return b; +} + +void +rte_reorder_reset(struct rte_reorder_buffer *b) +{ + char name[RTE_REORDER_NAMESIZE]; + + rte_reorder_free_mbufs(b); + snprintf(name, sizeof(name), "%s", b->name); + /* No error checking as current values should be valid */ + rte_reorder_init(b, b->memsize, name, b->order_buf.size); +} + +static void +rte_reorder_free_mbufs(struct rte_reorder_buffer *b) +{ + unsigned i; + + /* Free up the mbufs of order buffer & ready buffer */ + for (i = 0; i < b->order_buf.size; i++) { + if (b->order_buf.entries[i]) + rte_pktmbuf_free(b->order_buf.entries[i]); + if (b->ready_buf.entries[i]) + rte_pktmbuf_free(b->ready_buf.entries[i]); + } +} + +void +rte_reorder_free(struct rte_reorder_buffer *b) +{ + struct rte_reorder_list *reorder_list; + struct rte_tailq_entry *te; + + /* Check user arguments. */ + if (b == NULL) + return; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* find our tailq entry */ + TAILQ_FOREACH(te, reorder_list, next) { + if (te->data == (void *) b) + break; + } + if (te == NULL) { + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return; + } + + TAILQ_REMOVE(reorder_list, te, next); + + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + + rte_reorder_free_mbufs(b); + + rte_free(b); + rte_free(te); +} + +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); + + if (te == NULL) { + rte_errno = ENOENT; + return NULL; + } + + return b; +} + +static unsigned +rte_reorder_fill_overflow(struct rte_reorder_buffer *b, unsigned n) +{ + /* + * 1. Move all ready entries that fit to the ready_buf + * 2. check if we meet the minimum needed (n). + * 3. If not, then skip any gaps and keep moving. + * 4. If at any point the ready buffer is full, stop + * 5. Return the number of positions the order_buf head has moved + */ + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + unsigned int order_head_adv = 0; + + /* + * move at least n packets to ready buffer, assuming ready buffer + * has room for those packets. + */ + while (order_head_adv < n && + ((ready_buf->head + 1) & ready_buf->mask) != ready_buf->tail) { + + /* if we are blocked waiting on a packet, skip it */ + if (order_buf->entries[order_buf->head] == NULL) { + order_buf->head = (order_buf->head + 1) & order_buf->mask; + order_head_adv++; + } + + /* Move all ready entries that fit to the ready_buf */ + while (order_buf->entries[order_buf->head] != NULL) { + ready_buf->entries[ready_buf->head] = + order_buf->entries[order_buf->head]; + + order_buf->entries[order_buf->head] = NULL; + order_head_adv++; + + order_buf->head = (order_buf->head + 1) & order_buf->mask; + + if (((ready_buf->head + 1) & ready_buf->mask) == ready_buf->tail) + break; + + ready_buf->head = (ready_buf->head + 1) & ready_buf->mask; + } + } + + b->min_seqn += order_head_adv; + /* Return the number of positions the order_buf head has moved */ + return order_head_adv; +} + +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf) +{ + uint32_t offset, position; + struct cir_buffer *order_buf = &b->order_buf; + + /* + * calculate the offset from the head pointer we need to go. + * The subtraction takes care of the sequence number wrapping. + * For example (using 16-bit for brevity): + * min_seqn = 0xFFFD + * mbuf_seqn = 0x0010 + * offset = 0x0010 - 0xFFFD = 0x13 + */ + offset = mbuf->seqn - b->min_seqn; + + /* + * action to take depends on offset. + * offset < buffer->size: the mbuf fits within the current window of + * sequence numbers we can reorder. EXPECTED CASE. + * offset > buffer->size: the mbuf is outside the current window. There + * are a number of cases to consider: + * 1. The packet sequence is just outside the window, then we need + * to see about shifting the head pointer and taking any ready + * to return packets out of the ring. If there was a delayed + * or dropped packet preventing drains from shifting the window + * this case will skip over the dropped packet instead, and any + * packets dequeued here will be returned on the next drain call. + * 2. The packet sequence number is vastly outside our window, taken + * here as having offset greater than twice the buffer size. In + * this case, the packet is probably an old or late packet that + * was previously skipped, so just enqueue the packet for + * immediate return on the next drain call, or else return error. + */ + if (offset < b->order_buf.size) { + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else if (offset < 2 * b->order_buf.size) { + if (rte_reorder_fill_overflow(b, offset + 1 - order_buf->size) + < (offset + 1 - order_buf->size)) { + /* Put in handling for enqueue straight to output */ + rte_errno = ENOSPC; + return -1; + } + offset = mbuf->seqn - b->min_seqn; + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else { + /* Put in handling for enqueue straight to output */ + rte_errno = ERANGE; + return -1; + } + return 0; +} + +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs) +{ + unsigned int drain_cnt = 0; + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + /* Try to fetch requested number of mbufs from ready buffer */ + while ((drain_cnt < max_mbufs) && (ready_buf->tail != ready_buf->head)) { + mbufs[drain_cnt++] = ready_buf->entries[ready_buf->tail]; + ready_buf->tail = (ready_buf->tail + 1) & ready_buf->mask; + } + + /* + * If requested number of buffers not fetched from ready buffer, fetch + * remaining buffers from order buffer + */ + while ((drain_cnt < max_mbufs) && + (order_buf->entries[order_buf->head] != NULL)) { + mbufs[drain_cnt++] = order_buf->entries[order_buf->head]; + order_buf->entries[order_buf->head] = NULL; + b->min_seqn++; + order_buf->head = (order_buf->head + 1) & order_buf->mask; + } + + return drain_cnt; +} diff --git a/lib/librte_reorder/rte_reorder.h b/lib/librte_reorder/rte_reorder.h new file mode 100644 index 0000000..8300bf0 --- /dev/null +++ b/lib/librte_reorder/rte_reorder.h @@ -0,0 +1,181 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_REORDER_H_ +#define _RTE_REORDER_H_ + +/** + * @file + * RTE reorder + * + * Reorder library is a component which is designed to + * provide ordering of out of ordered packets based on + * sequence number present in mbuf. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rte_reorder_buffer; + +/** + * Create a new reorder buffer instance + * + * Allocate memory and initialize a new reorder buffer in that + * memory, returning the reorder buffer pointer to the user + * + * @param name + * The name to be given to the reorder buffer instance. + * @param socket_id + * The NUMA node on which the memory for the reorder buffer + * instance is to be reserved. + * @param size + * Max number of elements that can be stored in the reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - ENOMEM - no appropriate memory area found in which to create memzone + * - EINVAL - invalid parameters + */ +struct rte_reorder_buffer * +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size); + +/** + * Initializes given reorder buffer instance + * + * @param b + * Reorder buffer instance to initialize + * @param bufsize + * Size of the reorder buffer + * @param name + * The name to be given to the reorder buffer + * @param size + * Number of elements that can be stored in reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - EINVAL - invalid parameters + */ +struct rte_reorder_buffer * +rte_reorder_init(struct rte_reorder_buffer *b, unsigned int bufsize, + const char *name, unsigned int size); + +/** + * Find an existing reorder buffer instance + * and return a pointer to it. + * + * @param name + * Name of the reorder buffer instacne as passed to rte_reorder_create() + * @return + * Pointer to reorder buffer instance or NULL if object not found with rte_errno + * set appropriately. Possible rte_errno values include: + * - ENOENT - required entry not available to return. + * - E_RTE_NO_TAILQ - no tailq list could be got for the + * reorder instance list + */ +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name); + +/** + * Reset the given reorder buffer instance with initial values. + * + * @param b + * Reorder buffer instance which has to be reset + */ +void +rte_reorder_reset(struct rte_reorder_buffer *b); + +/** + * Free reorder buffer instance. + * + * @param b + * reorder buffer instance + * @return + * None + */ +void +rte_reorder_free(struct rte_reorder_buffer *b); + +/** + * Insert given mbuf in reorder buffer in its correct position + * + * The given mbuf is to be reordered relative to other mbufs in the system. + * The mbuf must contain a sequence number which is then used to place + * the buffer in the correct position in the reorder buffer. Reordered + * packets can later be taken from the buffer using the rte_reorder_drain() + * API. + * + * @param b + * Reorder buffer where the mbuf has to be inserted. + * @param mbuf + * mbuf of packet that needs to be inserted in reorder buffer. + * @return + * 0 on success + * -1 on error + * On error case, rte_errno will be set appropriately: + * - ENOSPC - Cannot move existing mbufs from reorder buffer to accommodate + * ealry mbuf, but it can be accomodated by performing drain and then insert. + * - ERANGE - Too early or late mbuf which is vastly out of range of expected + * window should be ingnored without any handling. + */ +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf); + +/** + * Fetch reordered buffers + * + * Returns a set of in-order buffers from the reorder buffer structure. Gaps + * may be present in the sequence numbers of the mbuf if packets have been + * delayed too long before reaching the reorder window, or have been previously + * dropped by the system. + * + * @param b + * Reorder buffer instance from which packets are to be drained + * @param mbufs + * array of mbufs where reordered packets will be inserted from reorder buffer + * @param max_mbufs + * the number of elements in the mbufs array. + * @return + * number of mbuf pointers written to mbufs. 0 <= N < max_mbufs. + */ +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs); + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_REORDER_H_ */ diff --git a/lib/librte_reorder/rte_reorder_version.map b/lib/librte_reorder/rte_reorder_version.map new file mode 100644 index 0000000..0a8a54d --- /dev/null +++ b/lib/librte_reorder/rte_reorder_version.map @@ -0,0 +1,13 @@ +DPDK_2.0 { + global: + + rte_reorder_create; + rte_reorder_init; + rte_reorder_find_existing; + rte_reorder_reset; + rte_reorder_free; + rte_reorder_insert; + rte_reorder_drain; + + local: *; +}; -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v4 2/5] app: New reorder unit test 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 1/5] reorder: new reorder library Sergio Gonzalez Monroy @ 2015-02-11 13:07 ` Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 3/5] examples: new sample app packet_ordering Sergio Gonzalez Monroy ` (6 subsequent siblings) 8 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-11 13:07 UTC (permalink / raw) To: dev Adding new reorder unit test for the test app. The command to run the unit test from the test shell is: reorder_autotest Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- app/test/Makefile | 2 + app/test/test_reorder.c | 393 ++++++++++++++++++++++++++++++++++++++++++++++++ mk/rte.app.mk | 4 + 3 files changed, 399 insertions(+) create mode 100644 app/test/test_reorder.c diff --git a/app/test/Makefile b/app/test/Makefile index 4311f96..24b27d7 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -124,6 +124,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += test_ivshmem.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor_perf.c +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) += test_reorder.c + SRCS-y += test_devargs.c SRCS-y += virtual_pmd.c SRCS-y += packet_burst_generator.c diff --git a/app/test/test_reorder.c b/app/test/test_reorder.c new file mode 100644 index 0000000..61cf8d3 --- /dev/null +++ b/app/test/test_reorder.c @@ -0,0 +1,393 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test.h" +#include "stdio.h" + +#include <unistd.h> +#include <string.h> + +#include <rte_cycles.h> +#include <rte_errno.h> +#include <rte_mbuf.h> +#include <rte_reorder.h> +#include <rte_lcore.h> +#include <rte_malloc.h> + +#include "test.h" + +#define BURST 32 +#define REORDER_BUFFER_SIZE 16384 +#define NUM_MBUFS (2*REORDER_BUFFER_SIZE) +#define REORDER_BUFFER_SIZE_INVALID 2049 +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) + +struct reorder_unittest_params { + struct rte_mempool *p; + struct rte_reorder_buffer *b; +}; + +static struct reorder_unittest_params default_params = { + .p = NULL, + .b = NULL +}; + +static struct reorder_unittest_params *test_params = &default_params; + +static int +test_reorder_create(void) +{ + struct rte_reorder_buffer *b = NULL; + + b = rte_reorder_create(NULL, rte_socket_id(), REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on create() with NULL name"); + + b = rte_reorder_create("PKT", rte_socket_id(), REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on create() with invalid buffer size param."); + + b = rte_reorder_create("PKT_RO1", rte_socket_id(), REORDER_BUFFER_SIZE); + printf("DEBUG: b= %p, orig_b= %p\n", b, test_params->b); + TEST_ASSERT_EQUAL(b, test_params->b, + "New reorder instance created with already existing name"); + + return 0; +} + +static int +test_reorder_init(void) +{ + struct rte_reorder_buffer *b = NULL; + unsigned int size; + /* + * The minimum memory area size that should be passed to library is, + * sizeof(struct rte_reorder_buffer) + (2 * size * sizeof(struct rte_mbuf *)); + * Otherwise error will be thrown + */ + + size = 100; + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with NULL buffer."); + + b = rte_malloc(NULL, size, 0); + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid mem zone size."); + rte_free(b); + + size = 262336; + b = rte_malloc(NULL, size, 0); + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid buffer size param."); + + b = rte_reorder_init(b, size, NULL, REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid name."); + rte_free(b); + + return 0; +} + +static int +test_reorder_find_existing(void) +{ + struct rte_reorder_buffer *b = NULL; + + /* Try to find existing reorder buffer instance */ + b = rte_reorder_find_existing("PKT_RO1"); + TEST_ASSERT_EQUAL(b, test_params->b, + "existing reorder buffer instance not found"); + + /* Try to find non existing reorder buffer instance */ + b = rte_reorder_find_existing("ro_find_non_existing"); + TEST_ASSERT((b == NULL) && (rte_errno == ENOENT), + "non existing reorder buffer instance found"); + + return 0; +} + +static int +test_reorder_free(void) +{ + struct rte_reorder_buffer *b1 = NULL, *b2 = NULL; + const char *name = "test_free"; + + b1 = rte_reorder_create(name, rte_socket_id(), 8); + TEST_ASSERT_NOT_NULL(b1, "Failed to create reorder buffer."); + + b2 = rte_reorder_find_existing(name); + TEST_ASSERT_EQUAL(b1, b2, "Failed to find existing reorder buffer"); + + rte_reorder_free(b1); + + b2 = rte_reorder_find_existing(name); + TEST_ASSERT((b2 == NULL) && (rte_errno == ENOENT), + "Found previously freed reorder buffer"); + + return 0; +} + +static int +test_reorder_insert(void) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_mempool *p = test_params->p; + const unsigned int size = 4; + const unsigned int num_bufs = 6; + struct rte_mbuf *bufs[num_bufs]; + int ret = 0; + unsigned i; + + /* This would create a reorder buffer instance consisting of: + * reorder_seq = 0 + * ready_buf: RB[size] = {NULL, NULL, NULL, NULL} + * order_buf: OB[size] = {NULL, NULL, NULL, NULL} + */ + b = rte_reorder_create("test_insert", rte_socket_id(), size); + TEST_ASSERT_NOT_NULL(b, "Failed to create reorder buffer"); + + ret = rte_mempool_get_bulk(p, (void *)bufs, num_bufs); + TEST_ASSERT_SUCCESS(ret, "Error getting mbuf from pool"); + + /* late packet */ + bufs[0]->seqn = 3 * size; + ret = rte_reorder_insert(b, bufs[0]); + if (!((ret == -1) && (rte_errno == ERANGE))) { + printf("%s:%d: No error inserting late packet with seqn:" + " 3 * size\n", __func__, __LINE__); + ret = -1; + goto exit; + } + + for (i = 0; i < num_bufs; i++) + bufs[i]->seqn = i; + + /* This should fill up order buffer: + * reorder_seq = 0 + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {0, 1, 2, 3} + */ + for (i = 0; i < size; i++) { + ret = rte_reorder_insert(b, bufs[i]); + if (ret != 0) { + printf("%s:%d: Error inserting packet with seqn less than size\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + } + + /* early packet - should move mbufs to ready buf and move sequence window + * reorder_seq = 4 + * RB[] = {0, 1, 2, 3} + * OB[] = {4, NULL, NULL, NULL} + */ + ret = rte_reorder_insert(b, bufs[4]); + if (ret != 0) { + printf("%s:%d: Error inserting early packet with seqn: size\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + /* early packet from current sequence window - full ready buffer */ + bufs[5]->seqn = 2 * size; + ret = rte_reorder_insert(b, bufs[5]); + if (!((ret == -1) && (rte_errno == ENOSPC))) { + printf("%s:%d: No error inserting early packet with full ready buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + ret = 0; +exit: + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + rte_reorder_free(b); + return ret; +} + +static int +test_reorder_drain(void) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_mempool *p = test_params->p; + const unsigned int size = 4; + const unsigned int num_bufs = 10; + struct rte_mbuf *bufs[num_bufs]; + int ret = 0; + unsigned i, cnt; + + /* This would create a reorder buffer instance consisting of: + * reorder_seq = 0 + * ready_buf: RB[size] = {NULL, NULL, NULL, NULL} + * order_buf: OB[size] = {NULL, NULL, NULL, NULL} + */ + b = rte_reorder_create("test_insert", rte_socket_id(), size); + TEST_ASSERT_NOT_NULL(b, "Failed to create reorder buffer"); + + ret = rte_mempool_get_bulk(p, (void *)bufs, num_bufs); + TEST_ASSERT_SUCCESS(ret, "Error getting mbuf from pool"); + + /* Check no drained packets if reorder is empty */ + cnt = rte_reorder_drain(b, bufs, 1); + if (cnt != 0) { + printf("%s:%d: drained packets from empty reorder buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + for (i = 0; i < num_bufs; i++) + bufs[i]->seqn = i; + + /* Insert packet with seqn 1: + * reorder_seq = 0 + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {NULL, 1, NULL, NULL} + */ + rte_reorder_insert(b, bufs[1]); + + /* Check no drained packets if no ready/order packets */ + cnt = rte_reorder_drain(b, bufs, 1); + if (cnt != 0) { + printf("%s:%d: drained packets from empty reorder buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + /* Insert more packets + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {0, 1, NULL, 3} + */ + rte_reorder_insert(b, bufs[0]); + rte_reorder_insert(b, bufs[3]); + + /* drained expected packets */ + cnt = rte_reorder_drain(b, bufs, 4); + if (cnt != 2) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + ret = -1; + goto exit; + } + + /* + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {NULL, 3, NULL, NULL} + */ + + rte_reorder_insert(b, bufs[4]); + rte_reorder_insert(b, bufs[7]); + + /* + * RB[] = {3, 4, NULL, NULL} + * OB[] = {NULL, NULL, 7, NULL} + */ + + cnt = rte_reorder_drain(b, bufs, 4); + if (cnt != 2) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + ret = -1; + goto exit; + } + + ret = 0; +exit: + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + rte_reorder_free(b); + return ret; +} + +static int +test_setup(void) +{ + /* reorder buffer instance creation */ + if (test_params->b == NULL) { + test_params->b = rte_reorder_create("PKT_RO1", rte_socket_id(), + REORDER_BUFFER_SIZE); + if (test_params->b == NULL) { + printf("%s: Error creating reorder buffer instance b\n", + __func__); + return -1; + } + } else + rte_reorder_reset(test_params->b); + + /* mempool creation */ + if (test_params->p == NULL) { + test_params->p = rte_mempool_create("RO_MBUF_POOL", NUM_MBUFS, + MBUF_SIZE, BURST, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->p == NULL) { + printf("%s: Error creating mempool\n", __func__); + return -1; + } + } + return 0; +} + +static struct unit_test_suite reorder_test_suite = { + + .setup = test_setup, + .suite_name = "Reorder Unit Test Suite", + .unit_test_cases = { + TEST_CASE(test_reorder_create), + TEST_CASE(test_reorder_init), + TEST_CASE(test_reorder_find_existing), + TEST_CASE(test_reorder_free), + TEST_CASE(test_reorder_insert), + TEST_CASE(test_reorder_drain), + TEST_CASES_END() + } +}; + +static int +test_reorder(void) +{ + return unit_test_suite_runner(&reorder_test_suite); +} + +static struct test_command reorder_cmd = { + .command = "reorder_autotest", + .callback = test_reorder, +}; +REGISTER_TEST_COMMAND(reorder_cmd); diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 4294d9a..74910a6 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -71,6 +71,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_DISTRIBUTOR),y) LDLIBS += -lrte_distributor endif +ifeq ($(CONFIG_RTE_LIBRTE_REORDER),y) +LDLIBS += -lrte_reorder +endif + ifeq ($(CONFIG_RTE_LIBRTE_KNI),y) ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) LDLIBS += -lrte_kni -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v4 3/5] examples: new sample app packet_ordering 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 1/5] reorder: new reorder library Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 2/5] app: New reorder unit test Sergio Gonzalez Monroy @ 2015-02-11 13:07 ` Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 4/5] doc: new reorder library description Sergio Gonzalez Monroy ` (5 subsequent siblings) 8 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-11 13:07 UTC (permalink / raw) To: dev This new app makes use of the librte_reorder library. It requires at least 3 lcores for RX, Workers (1 or more) and TX threads. Communication between RX-Workers and Workers-TX is done by using rings. The flow of mbufs is the following: * RX thread gets mbufs from driver, set sequence number and enqueue them in ring. * Workers dequeue mbufs from ring, do some 'work' and enqueue mbufs in ring. * TX dequeue mbufs from ring, inserts them in reorder buffer, drains mbufs from reorder and sends them to the driver. Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- examples/packet_ordering/Makefile | 50 +++ examples/packet_ordering/main.c | 695 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 745 insertions(+) create mode 100644 examples/packet_ordering/Makefile create mode 100644 examples/packet_ordering/main.c diff --git a/examples/packet_ordering/Makefile b/examples/packet_ordering/Makefile new file mode 100644 index 0000000..9e080a3 --- /dev/null +++ b/examples/packet_ordering/Makefile @@ -0,0 +1,50 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overridden by command line or environment +RTE_TARGET ?= x86_64-ivshmem-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +# binary name +APP = packet_ordering + +# all source are stored in SRCS-y +SRCS-y := main.c + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/packet_ordering/main.c b/examples/packet_ordering/main.c new file mode 100644 index 0000000..75e2f46 --- /dev/null +++ b/examples/packet_ordering/main.c @@ -0,0 +1,695 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <signal.h> +#include <getopt.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_errno.h> +#include <rte_ethdev.h> +#include <rte_lcore.h> +#include <rte_mbuf.h> +#include <rte_mempool.h> +#include <rte_ring.h> +#include <rte_reorder.h> + +#define RX_DESC_PER_QUEUE 128 +#define TX_DESC_PER_QUEUE 512 + +#define MAX_PKTS_BURST 32 +#define REORDER_BUFFER_SIZE 8192 +#define MBUF_PER_POOL 65535 +#define MBUF_SIZE (1600 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_POOL_CACHE_SIZE 250 + +#define RING_SIZE 16384 + +/* uncommnet below line to enable debug logs */ +/* #define DEBUG */ + +#ifdef DEBUG +#define LOG_LEVEL RTE_LOG_DEBUG +#define LOG_DEBUG(log_type, fmt, args...) RTE_LOG(DEBUG, log_type, fmt, ##args) +#else +#define LOG_LEVEL RTE_LOG_INFO +#define LOG_DEBUG(log_type, fmt, args...) do {} while (0) +#endif + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDERAPP RTE_LOGTYPE_USER1 + +unsigned int portmask; +unsigned int disable_reorder; +volatile uint8_t quit_signal; + +static struct rte_mempool *mbuf_pool; + +static struct rte_eth_conf port_conf_default; + +struct worker_thread_args { + struct rte_ring *ring_in; + struct rte_ring *ring_out; +}; + +struct output_buffer { + unsigned count; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; +}; + +volatile struct app_stats { + struct { + uint64_t rx_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } rx __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } wkr __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + /* Too early pkts transmitted directly w/o reordering */ + uint64_t early_pkts_txtd_woro; + /* Too early pkts failed from direct transmit */ + uint64_t early_pkts_tx_failed_woro; + uint64_t ro_tx_pkts; + uint64_t ro_tx_failed_pkts; + } tx __rte_cache_aligned; +} app_stats; + +/** + * Get the last enabled lcore ID + * + * @return + * The last enabled lcore ID. + */ +static unsigned int +get_last_lcore_id(void) +{ + int i; + + for (i = RTE_MAX_LCORE - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return 0; +} + +/** + * Get the previous enabled lcore ID + * @param id + * The current lcore ID + * @return + * The previous enabled lcore ID or the current lcore + * ID if it is the first available core. + */ +static unsigned int +get_previous_lcore_id(unsigned int id) +{ + int i; + + for (i = id - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return id; +} + +static inline void +pktmbuf_free_bulk(struct rte_mbuf *mbuf_table[], unsigned n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + rte_pktmbuf_free(mbuf_table[i]); +} + +/* display usage */ +static void +print_usage(const char *prgname) +{ + printf("%s [EAL options] -- -p PORTMASK\n" + " -p PORTMASK: hexadecimal bitmask of ports to configure\n", + prgname); +} + +static int +parse_portmask(const char *portmask) +{ + unsigned long pm; + char *end = NULL; + + /* parse hexadecimal string */ + pm = strtoul(portmask, &end, 16); + if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) + return -1; + + if (pm == 0) + return -1; + + return pm; +} + +/* Parse the argument given in the command line of the application */ +static int +parse_args(int argc, char **argv) +{ + int opt; + int option_index; + char **argvopt; + char *prgname = argv[0]; + static struct option lgopts[] = { + {"disable-reorder", 0, 0, 0}, + {NULL, 0, 0, 0} + }; + + argvopt = argv; + + while ((opt = getopt_long(argc, argvopt, "p:", + lgopts, &option_index)) != EOF) { + switch (opt) { + /* portmask */ + case 'p': + portmask = parse_portmask(optarg); + if (portmask == 0) { + printf("invalid portmask\n"); + print_usage(prgname); + return -1; + } + break; + /* long options */ + case 0: + if (!strcmp(lgopts[option_index].name, "disable-reorder")) { + printf("reorder disabled\n"); + disable_reorder = 1; + } + break; + default: + print_usage(prgname); + return -1; + } + } + if (optind <= 1) { + print_usage(prgname); + return -1; + } + + argv[optind-1] = prgname; + optind = 0; /* reset getopt lib */ + return 0; +} + +static inline int +configure_eth_port(uint8_t port_id) +{ + struct ether_addr addr; + const uint16_t rxRings = 1, txRings = 1; + const uint8_t nb_ports = rte_eth_dev_count(); + int ret; + uint16_t q; + + if (port_id > nb_ports) + return -1; + + ret = rte_eth_dev_configure(port_id, rxRings, txRings, &port_conf_default); + if (ret != 0) + return ret; + + for (q = 0; q < rxRings; q++) { + ret = rte_eth_rx_queue_setup(port_id, q, RX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL, + mbuf_pool); + if (ret < 0) + return ret; + } + + for (q = 0; q < txRings; q++) { + ret = rte_eth_tx_queue_setup(port_id, q, TX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL); + if (ret < 0) + return ret; + } + + ret = rte_eth_dev_start(port_id); + if (ret < 0) + return ret; + + rte_eth_macaddr_get(port_id, &addr); + printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 + " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", + (unsigned)port_id, + addr.addr_bytes[0], addr.addr_bytes[1], + addr.addr_bytes[2], addr.addr_bytes[3], + addr.addr_bytes[4], addr.addr_bytes[5]); + + rte_eth_promiscuous_enable(port_id); + + return 0; +} + +static void +print_stats(void) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + unsigned i; + struct rte_eth_stats eth_stats; + + printf("\nRX thread stats:\n"); + printf(" - Pkts rxd: %"PRIu64"\n", + app_stats.rx.rx_pkts); + printf(" - Pkts enqd to workers ring: %"PRIu64"\n", + app_stats.rx.enqueue_pkts); + + printf("\nWorker thread stats:\n"); + printf(" - Pkts deqd from workers ring: %"PRIu64"\n", + app_stats.wkr.dequeue_pkts); + printf(" - Pkts enqd to tx ring: %"PRIu64"\n", + app_stats.wkr.enqueue_pkts); + printf(" - Pkts enq to tx failed: %"PRIu64"\n", + app_stats.wkr.enqueue_failed_pkts); + + printf("\nTX stats:\n"); + printf(" - Pkts deqd from tx ring: %"PRIu64"\n", + app_stats.tx.dequeue_pkts); + printf(" - Ro Pkts transmitted: %"PRIu64"\n", + app_stats.tx.ro_tx_pkts); + printf(" - Ro Pkts tx failed: %"PRIu64"\n", + app_stats.tx.ro_tx_failed_pkts); + printf(" - Pkts transmitted w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_txtd_woro); + printf(" - Pkts tx failed w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_tx_failed_woro); + + for (i = 0; i < nb_ports; i++) { + rte_eth_stats_get(i, ð_stats); + printf("\nPort %u stats:\n", i); + printf(" - Pkts in: %"PRIu64"\n", eth_stats.ipackets); + printf(" - Pkts out: %"PRIu64"\n", eth_stats.opackets); + printf(" - In Errs: %"PRIu64"\n", eth_stats.ierrors); + printf(" - Out Errs: %"PRIu64"\n", eth_stats.oerrors); + printf(" - Mbuf Errs: %"PRIu64"\n", eth_stats.rx_nombuf); + } +} + +static void +int_handler(int sig_num) +{ + printf("Exiting on signal %d\n", sig_num); + quit_signal = 1; +} + +/** + * This thread receives mbufs from the port and affects them an internal + * sequence number to keep track of their order of arrival through an + * mbuf structure. + * The mbufs are then passed to the worker threads via the rx_to_workers + * ring. + */ +static int +rx_thread(struct rte_ring *ring_out) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint32_t seqn = 0; + uint16_t i, ret = 0; + uint16_t nb_rx_pkts; + uint8_t port_id; + struct rte_mbuf *pkts[MAX_PKTS_BURST]; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + + while (!quit_signal) { + + for (port_id = 0; port_id < nb_ports; port_id++) { + if ((portmask & (1 << port_id)) != 0) { + + /* receive packets */ + nb_rx_pkts = rte_eth_rx_burst(port_id, 0, + pkts, MAX_PKTS_BURST); + if (nb_rx_pkts == 0) { + LOG_DEBUG(REORDERAPP, + "%s():Received zero packets\n", __func__); + continue; + } + app_stats.rx.rx_pkts += nb_rx_pkts; + + /* mark sequence number */ + for (i = 0; i < nb_rx_pkts; ) + pkts[i++]->seqn = seqn++; + + /* enqueue to rx_to_workers ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *) pkts, + nb_rx_pkts); + app_stats.rx.enqueue_pkts += ret; + if (unlikely(ret < nb_rx_pkts)) { + app_stats.rx.enqueue_failed_pkts += + (nb_rx_pkts-ret); + pktmbuf_free_bulk(&pkts[ret], nb_rx_pkts - ret); + } + } + } + } + return 0; +} + +/** + * This thread takes bursts of packets from the rx_to_workers ring and + * Changes the input port value to output port value. And feds it to + * workers_to_tx + */ +static int +worker_thread(void *args_ptr) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint16_t i, ret = 0; + uint16_t burst_size = 0; + struct worker_thread_args *args; + struct rte_mbuf *burst_buffer[MAX_PKTS_BURST] = { NULL }; + struct rte_ring *ring_in, *ring_out; + const unsigned xor_val = (nb_ports > 1); + + args = (struct worker_thread_args *) args_ptr; + ring_in = args->ring_in; + ring_out = args->ring_out; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + + while (!quit_signal) { + + /* dequeue the mbufs from rx_to_workers ring */ + burst_size = rte_ring_dequeue_burst(ring_in, + (void *)burst_buffer, MAX_PKTS_BURST); + if (unlikely(burst_size == 0)) + continue; + + __sync_fetch_and_add(&app_stats.wkr.dequeue_pkts, burst_size); + + /* just do some operation on mbuf */ + for (i = 0; i < burst_size;) + burst_buffer[i++]->port ^= xor_val; + + /* enqueue the modified mbufs to workers_to_tx ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *)burst_buffer, burst_size); + __sync_fetch_and_add(&app_stats.wkr.enqueue_pkts, ret); + if (unlikely(ret < burst_size)) { + /* Return the mbufs to their respective pool, dropping packets */ + __sync_fetch_and_add(&app_stats.wkr.enqueue_failed_pkts, + (int)burst_size - ret); + pktmbuf_free_bulk(&burst_buffer[ret], burst_size - ret); + } + } + return 0; +} + +static inline void +flush_one_port(struct output_buffer *outbuf, uint8_t outp) +{ + unsigned nb_tx = rte_eth_tx_burst(outp, 0, outbuf->mbufs, + outbuf->count); + app_stats.tx.ro_tx_pkts += nb_tx; + + if (unlikely(nb_tx < outbuf->count)) { + /* free the mbufs which failed from transmit */ + app_stats.tx.ro_tx_failed_pkts += (outbuf->count - nb_tx); + LOG_DEBUG(REORDERAPP, "%s:Packet loss with tx_burst\n", __func__); + pktmbuf_free_bulk(&outbuf->mbufs[nb_tx], outbuf->count - nb_tx); + } + outbuf->count = 0; +} + +/** + * Dequeue mbufs from the workers_to_tx ring and reorder them before + * transmitting. + */ +static int +send_thread(struct rte_ring *ring_in) +{ + int ret; + unsigned int i, dret; + uint16_t nb_dq_mbufs; + uint8_t outp; + static struct output_buffer tx_buffers[RTE_MAX_ETHPORTS]; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; + struct rte_mbuf *rombufs[MAX_PKTS_BURST] = {NULL}; + struct rte_reorder_buffer *buffer; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + buffer = rte_reorder_create("PKT_RO", rte_socket_id(), REORDER_BUFFER_SIZE); + while (!quit_signal) { + + /* deque the mbufs from workers_to_tx ring */ + nb_dq_mbufs = rte_ring_dequeue_burst(ring_in, + (void *)mbufs, MAX_PKTS_BURST); + + if (unlikely(nb_dq_mbufs == 0)) + continue; + + app_stats.tx.dequeue_pkts += nb_dq_mbufs; + + for (i = 0; i < nb_dq_mbufs; i++) { + /* send dequeued mbufs for reordering */ + ret = rte_reorder_insert(buffer, mbufs[i]); + + if (ret == -1 && rte_errno == ERANGE) { + /* Too early pkts should be transmitted out directly */ + LOG_DEBUG(REORDERAPP, "%s():Cannot reorder early packet " + "direct enqueuing to TX\n", __func__); + outp = mbufs[i]->port; + if ((portmask & (1 << outp)) == 0) { + rte_pktmbuf_free(mbufs[i]); + continue; + } + if (rte_eth_tx_burst(outp, 0, (void *)mbufs[i], 1) != 1) { + rte_pktmbuf_free(mbufs[i]); + app_stats.tx.early_pkts_tx_failed_woro++; + } else + app_stats.tx.early_pkts_txtd_woro++; + } else if (ret == -1 && rte_errno == ENOSPC) { + /** + * Early pkts just outside of window should be dropped + */ + rte_pktmbuf_free(mbufs[i]); + } + } + + /* + * drain MAX_PKTS_BURST of reordered + * mbufs for transmit + */ + dret = rte_reorder_drain(buffer, rombufs, MAX_PKTS_BURST); + for (i = 0; i < dret; i++) { + + struct output_buffer *outbuf; + uint8_t outp1; + + outp1 = rombufs[i]->port; + /* skip ports that are not enabled */ + if ((portmask & (1 << outp1)) == 0) { + rte_pktmbuf_free(rombufs[i]); + continue; + } + + outbuf = &tx_buffers[outp1]; + outbuf->mbufs[outbuf->count++] = rombufs[i]; + if (outbuf->count == MAX_PKTS_BURST) + flush_one_port(outbuf, outp1); + } + } + return 0; +} + +/** + * Dequeue mbufs from the workers_to_tx ring and transmit them + */ +static int +tx_thread(struct rte_ring *ring_in) +{ + uint32_t i, dqnum; + uint8_t outp; + static struct output_buffer tx_buffers[RTE_MAX_ETHPORTS]; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; + struct output_buffer *outbuf; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + while (!quit_signal) { + + /* deque the mbufs from workers_to_tx ring */ + dqnum = rte_ring_dequeue_burst(ring_in, + (void *)mbufs, MAX_PKTS_BURST); + + if (unlikely(dqnum == 0)) + continue; + + app_stats.tx.dequeue_pkts += dqnum; + + for (i = 0; i < dqnum; i++) { + outp = mbufs[i]->port; + /* skip ports that are not enabled */ + if ((portmask & (1 << outp)) == 0) { + rte_pktmbuf_free(mbufs[i]); + continue; + } + + outbuf = &tx_buffers[outp]; + outbuf->mbufs[outbuf->count++] = mbufs[i]; + if (outbuf->count == MAX_PKTS_BURST) + flush_one_port(outbuf, outp); + } + } + + return 0; +} + +int +main(int argc, char **argv) +{ + int ret; + unsigned nb_ports; + unsigned int lcore_id, last_lcore_id, master_lcore_id; + uint8_t port_id; + uint8_t nb_ports_available; + struct worker_thread_args worker_args = {NULL, NULL}; + struct rte_ring *rx_to_workers; + struct rte_ring *workers_to_tx; + + /* catch ctrl-c so we can print on exit */ + signal(SIGINT, int_handler); + + /* Initialize EAL */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + return -1; + + argc -= ret; + argv += ret; + + /* Parse the application specific arguments */ + ret = parse_args(argc, argv); + if (ret < 0) + return -1; + + /* Check if we have enought cores */ + if (rte_lcore_count() < 3) + rte_exit(EXIT_FAILURE, "Error, This application needs at " + "least 3 logical cores to run:\n" + "1 lcore for packet RX\n" + "1 lcore for packet TX\n" + "and at least 1 lcore for worker threads\n"); + + nb_ports = rte_eth_dev_count(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "Error: no ethernet ports detected\n"); + if (nb_ports != 1 && (nb_ports & 1)) + rte_exit(EXIT_FAILURE, "Error: number of ports must be even, except " + "when using a single port\n"); + + mbuf_pool = rte_mempool_create("mbuf_pool", MBUF_PER_POOL, MBUF_SIZE, + MBUF_POOL_CACHE_SIZE, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (mbuf_pool == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + nb_ports_available = nb_ports; + + /* initialize all ports */ + for (port_id = 0; port_id < nb_ports; port_id++) { + /* skip ports that are not enabled */ + if ((portmask & (1 << port_id)) == 0) { + printf("\nSkipping disabled port %d\n", port_id); + nb_ports_available--; + continue; + } + /* init port */ + printf("Initializing port %u... done\n", (unsigned) port_id); + + if (configure_eth_port(port_id) != 0) + rte_exit(EXIT_FAILURE, "Cannot initialize port %"PRIu8"\n", + port_id); + } + + if (!nb_ports_available) { + rte_exit(EXIT_FAILURE, + "All available ports are disabled. Please set portmask.\n"); + } + + /* Create rings for inter core communication */ + rx_to_workers = rte_ring_create("rx_to_workers", RING_SIZE, rte_socket_id(), + RING_F_SP_ENQ); + if (rx_to_workers == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + workers_to_tx = rte_ring_create("workers_to_tx", RING_SIZE, rte_socket_id(), + RING_F_SC_DEQ); + if (workers_to_tx == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + last_lcore_id = get_last_lcore_id(); + master_lcore_id = rte_get_master_lcore(); + + worker_args.ring_in = rx_to_workers; + worker_args.ring_out = workers_to_tx; + + /* Start worker_thread() on all the available slave cores but the last 1 */ + for (lcore_id = 0; lcore_id <= get_previous_lcore_id(last_lcore_id); lcore_id++) + if (rte_lcore_is_enabled(lcore_id) && lcore_id != master_lcore_id) + rte_eal_remote_launch(worker_thread, (void *)&worker_args, + lcore_id); + + if (disable_reorder) + /* Start tx_thread() on the last slave core */ + rte_eal_remote_launch((lcore_function_t *)tx_thread, workers_to_tx, + last_lcore_id); + else + /* Start send_thread() on the last slave core */ + rte_eal_remote_launch((lcore_function_t *)send_thread, workers_to_tx, + last_lcore_id); + + /* Start rx_thread() on the master core */ + rx_thread(rx_to_workers); + + RTE_LCORE_FOREACH_SLAVE(lcore_id) { + if (rte_eal_wait_lcore(lcore_id) < 0) + return -1; + } + + print_stats(); + return 0; +} -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v4 4/5] doc: new reorder library description 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy ` (2 preceding siblings ...) 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 3/5] examples: new sample app packet_ordering Sergio Gonzalez Monroy @ 2015-02-11 13:07 ` Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 5/5] doc: new packet ordering app description Sergio Gonzalez Monroy ` (4 subsequent siblings) 8 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-11 13:07 UTC (permalink / raw) To: dev This patch introduces a new section in the programmers guide describing the reorder library. Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/reorder_lib.rst | 115 ++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 doc/guides/prog_guide/reorder_lib.rst diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 8d86dd4..de69682 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -61,6 +61,7 @@ Programmer's Guide lpm_lib lpm6_lib packet_distrib_lib + reorder_lib ip_fragment_reassembly_lib multi_proc_support kernel_nic_interface diff --git a/doc/guides/prog_guide/reorder_lib.rst b/doc/guides/prog_guide/reorder_lib.rst new file mode 100644 index 0000000..bbd0521 --- /dev/null +++ b/doc/guides/prog_guide/reorder_lib.rst @@ -0,0 +1,115 @@ +.. BSD LICENSE + Copyright(c) 2015 Intel Corporation. All rights reserved. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.. _Reorder_Library: + +Reorder Library +================= + +The Reorder Library provides a mechanism for reordering mbufs based on their +sequence number. + +Operation +---------- + +The reorder library is essentially a buffer that reorders mbufs. +The user inserts out of order mbufs into the reorder buffer and pulls in-order +mbufs from it. + +At a given time, the reorder buffer contains mbufs whose sequence number are +inside the sequence window. The sequence window is determined by the minimum +sequence number and the number of entries that the buffer was configured to hold. +For example, given a reorder buffer with 200 entries and a minimum sequence +number of 350, the sequence window has low and high limits of 350 and 550 +respectively. + +When inserting mbufs, the reorder library differentiates between valid, early +and late mbufs depending on the sequence number of the inserted mbuf: + +* valid: the sequence number is inside the window. +* late: the sequence number is outside the window and less than the low limit. +* early: the sequence number is outside the window and greater than the high + limit. + +The reorder buffer directly returns late mbufs and tries to accommodate early +mbufs. + + +Implementation Details +------------------------- + +The reorder library is implemented as a pair of buffers, which referred to as +the *Order* buffer and the *Ready* buffer. + +On an insert call, valid mbufs are inserted directly into the Order buffer and +late mbufs are returned to the user with an error. + +In the case of early mbufs, the reorder buffer will try to move the window +(incrementing the minimum sequence number) so that the mbuf becomes a valid one. +To that end, mbufs in the Order buffer are moved into the Ready buffer. +Any mbufs that have not arrived yet are ignored and therefore will become +late mbufs. +This means that as long as there is room in the Ready buffer, the window will +be moved to accommodate early mbufs that would otherwise be outside the +reordering window. + +For example, assuming that we have a buffer of 200 entries with a 350 minimum +sequence number, and we need to insert an early mbuf with 565 sequence number. +That means that we would need to move the windows at least 15 positions to +accommodate the mbuf. +The reorder buffer would try to move mbufs from at least the next 15 slots in +the Order buffer to the Ready buffer, as long as there is room in the Ready buffer. +Any gaps in the Order buffer at that point are skipped, and those packet will +be reported as late packets when they arrive. The process of moving packets +to the Ready buffer continues beyond the minimum required until a gap, +i.e. missing mbuf, in the Order buffer is encountered. + +When draining mbufs, the reorder buffer would return mbufs in the Ready +buffer first and then from the Order buffer until a gap is found (mbufs that +have not arrived yet). + +Use Case: Packet Distributor +------------------------------- + +An application using the DPDK packet distributor could make use of the reorder +library to transmit packets in the same order they were received. + +A basic packet distributor use case would consist of a distributor with +multiple workers cores. +The processing of packets by the workers is not guaranteed to be in order, +hence a reorder buffer can be used to order as many packets as possible. + +In such a scenario, the distributor assigns a sequence number to mbufs before +delivering them to the workers. +As the workers finish processing the packets, the distributor inserts those +mbufs into the reorder buffer and finally transmit drained mbufs. + +NOTE: Currently the reorder buffer is not thread safe so the same thread is +responsible for inserting and draining mbufs. -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v4 5/5] doc: new packet ordering app description 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy ` (3 preceding siblings ...) 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 4/5] doc: new reorder library description Sergio Gonzalez Monroy @ 2015-02-11 13:07 ` Sergio Gonzalez Monroy 2015-02-12 5:33 ` [dpdk-dev] [PATCH v4 0/5] New Reorder Library Neil Horman ` (3 subsequent siblings) 8 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-11 13:07 UTC (permalink / raw) To: dev This patch describes how to build and run he new packet ordering sample application that exercises the reorder library. Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- doc/guides/sample_app_ug/index.rst | 1 + doc/guides/sample_app_ug/packet_ordering.rst | 102 +++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst index d07dec3..5720181 100644 --- a/doc/guides/sample_app_ug/index.rst +++ b/doc/guides/sample_app_ug/index.rst @@ -60,6 +60,7 @@ Sample Applications User Guide intel_quickassist quota_watermark timer + packet_ordering vmdq_dcb_forwarding vhost netmap_compatibility diff --git a/doc/guides/sample_app_ug/packet_ordering.rst b/doc/guides/sample_app_ug/packet_ordering.rst new file mode 100644 index 0000000..481f1b7 --- /dev/null +++ b/doc/guides/sample_app_ug/packet_ordering.rst @@ -0,0 +1,102 @@ +.. BSD LICENSE + Copyright(c) 2015 Intel Corporation. All rights reserved. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Packet Ordering Application +============================ + +The Packet Ordering sample app simply shows the impact of reordering a stream. +It's meant to stress the library with different configurations for performance. + +Overview +-------- + +The application uses at least three CPU cores: + +* RX core (maser core) receives traffic from the NIC ports and feeds Worker + cores with traffic through SW queues. + +* Worker core (slave core) basically do some light work on the packet. + Currently it modifies the output port of the packet for configurations with + more than one port enabled. + +* TX Core (slave core) receives traffic from Woker cores through software queues, + inserts out-of-order packets into reorder buffer, extracts ordered packets + from the reorder buffer and sends them to the NIC ports for transmission. + +Compiling the Application +-------------------------- + +#. Go to the example directory: + + .. code-block:: console + + export RTE_SDK=/path/to/rte_sdk + cd ${RTE_SDK}/examples/helloworld + +#. Set the target (a default target is used if not specified). For example: + + .. code-block:: console + + export RTE_TARGET=x86_64-native-linuxapp-gcc + + See the *DPDK Getting Started* Guide for possible RTE_TARGET values. + +#. Build the application: + + .. code-block:: console + + make + +Running the Application +----------------------- + +Refer to *DPDK Getting Started Guide* for general information on running applications +and the Environment Abstraction Layer (EAL) options. + +Application Command Line +~~~~~~~~~~~~~~~~~~~~~~~~ + +The application execution command line is: + +.. code-block:: console + + ./test-pipeline [EAL options] -- -p PORTMASK [--disable-reorder] + +The -c EAL CPU_COREMASK option has to contain at least 3 CPU cores. +The first CPU core in the core mask is the master core and would be assigned to +RX core, the last to TX core and the rest to Worker cores. + +The PORTMASK parameter must contain either 1 or even enabled port numbers. +When setting more than 1 port, traffic would be forwarderd in pairs. +For example, if we enable 4 ports, traffic from port 0 to 1 and from 1 to 0, +then the other pair from 2 to 3 and from 3 to 2, having [0,1] and [2,3] pairs. + +The disable-reorder long option does, as its name implies, disable the reordering +of traffic, which should help evaluate reordering performance impact. -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v4 0/5] New Reorder Library 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy ` (4 preceding siblings ...) 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 5/5] doc: new packet ordering app description Sergio Gonzalez Monroy @ 2015-02-12 5:33 ` Neil Horman 2015-02-12 12:00 ` Declan Doherty ` (2 subsequent siblings) 8 siblings, 0 replies; 44+ messages in thread From: Neil Horman @ 2015-02-12 5:33 UTC (permalink / raw) To: Sergio Gonzalez Monroy; +Cc: dev On Wed, Feb 11, 2015 at 01:07:30PM +0000, Sergio Gonzalez Monroy wrote: > This series introduces the new reorder library along with unit tests, > sample app and a new entry in the programmers guide describing the library. > > The library provides reordering of mbufs based on their sequence number. > > As mention in the patch describing the library, one use case is the > packet distributor. > The distributor receives packets, assigns them a sequence number and sends > them to the workers. > The workers process those packets and return them to the distributor. > The distributor collects out-of-order packets from the workers and uses > this library to reorder the packets based on the sequence number they > were assigned. > > v4: > - add missing version.map and related versioning macros > > v3: > - fix copyright date > - add option to sample app to disable reordering > - add packet ordering sample guide entry > > v2: > - add programmers guide entry describing the library > - use malloc instead of memzone to allocate memory > - modify create and init implementation, init takes a reorder buffer as input > and create reserves memory and call init. > - update unit tests > > Sergio Gonzalez Monroy (5): > reorder: new reorder library > app: New reorder unit test > examples: new sample app packet_ordering > doc: new reorder library description > doc: new packet ordering app description > > app/test/Makefile | 2 + > app/test/test_reorder.c | 393 ++++++++++++++ > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/guides/prog_guide/index.rst | 1 + > doc/guides/prog_guide/reorder_lib.rst | 115 ++++ > doc/guides/sample_app_ug/index.rst | 1 + > doc/guides/sample_app_ug/packet_ordering.rst | 102 ++++ > examples/packet_ordering/Makefile | 50 ++ > examples/packet_ordering/main.c | 695 +++++++++++++++++++++++++ > lib/Makefile | 1 + > lib/librte_eal/common/include/rte_tailq_elem.h | 2 + > lib/librte_mbuf/rte_mbuf.h | 3 + > lib/librte_reorder/Makefile | 54 ++ > lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++ > lib/librte_reorder/rte_reorder.h | 181 +++++++ > lib/librte_reorder/rte_reorder_version.map | 13 + > mk/rte.app.mk | 4 + > 18 files changed, 2043 insertions(+) > create mode 100644 app/test/test_reorder.c > create mode 100644 doc/guides/prog_guide/reorder_lib.rst > create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst > create mode 100644 examples/packet_ordering/Makefile > create mode 100644 examples/packet_ordering/main.c > create mode 100644 lib/librte_reorder/Makefile > create mode 100644 lib/librte_reorder/rte_reorder.c > create mode 100644 lib/librte_reorder/rte_reorder.h > create mode 100644 lib/librte_reorder/rte_reorder_version.map > > -- > 1.9.3 > > Series Acked-by: Neil Horman <nhorman@tuxdriver.com> ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v4 0/5] New Reorder Library 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy ` (5 preceding siblings ...) 2015-02-12 5:33 ` [dpdk-dev] [PATCH v4 0/5] New Reorder Library Neil Horman @ 2015-02-12 12:00 ` Declan Doherty 2015-02-18 14:22 ` Thomas Monjalon 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy 8 siblings, 0 replies; 44+ messages in thread From: Declan Doherty @ 2015-02-12 12:00 UTC (permalink / raw) To: Sergio Gonzalez Monroy, dev On 11/02/15 13:07, Sergio Gonzalez Monroy wrote: > This series introduces the new reorder library along with unit tests, > sample app and a new entry in the programmers guide describing the library. > > The library provides reordering of mbufs based on their sequence number. > > As mention in the patch describing the library, one use case is the > packet distributor. > The distributor receives packets, assigns them a sequence number and sends > them to the workers. > The workers process those packets and return them to the distributor. > The distributor collects out-of-order packets from the workers and uses > this library to reorder the packets based on the sequence number they > were assigned. > > v4: > - add missing version.map and related versioning macros > > v3: > - fix copyright date > - add option to sample app to disable reordering > - add packet ordering sample guide entry > > v2: > - add programmers guide entry describing the library > - use malloc instead of memzone to allocate memory > - modify create and init implementation, init takes a reorder buffer as input > and create reserves memory and call init. > - update unit tests > > Sergio Gonzalez Monroy (5): > reorder: new reorder library > app: New reorder unit test > examples: new sample app packet_ordering > doc: new reorder library description > doc: new packet ordering app description > > app/test/Makefile | 2 + > app/test/test_reorder.c | 393 ++++++++++++++ > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/guides/prog_guide/index.rst | 1 + > doc/guides/prog_guide/reorder_lib.rst | 115 ++++ > doc/guides/sample_app_ug/index.rst | 1 + > doc/guides/sample_app_ug/packet_ordering.rst | 102 ++++ > examples/packet_ordering/Makefile | 50 ++ > examples/packet_ordering/main.c | 695 +++++++++++++++++++++++++ > lib/Makefile | 1 + > lib/librte_eal/common/include/rte_tailq_elem.h | 2 + > lib/librte_mbuf/rte_mbuf.h | 3 + > lib/librte_reorder/Makefile | 54 ++ > lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++ > lib/librte_reorder/rte_reorder.h | 181 +++++++ > lib/librte_reorder/rte_reorder_version.map | 13 + > mk/rte.app.mk | 4 + > 18 files changed, 2043 insertions(+) > create mode 100644 app/test/test_reorder.c > create mode 100644 doc/guides/prog_guide/reorder_lib.rst > create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst > create mode 100644 examples/packet_ordering/Makefile > create mode 100644 examples/packet_ordering/main.c > create mode 100644 lib/librte_reorder/Makefile > create mode 100644 lib/librte_reorder/rte_reorder.c > create mode 100644 lib/librte_reorder/rte_reorder.h > create mode 100644 lib/librte_reorder/rte_reorder_version.map > Series Acked-by: Declan Doherty <declan.doherty@intel.com> ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v4 0/5] New Reorder Library 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy ` (6 preceding siblings ...) 2015-02-12 12:00 ` Declan Doherty @ 2015-02-18 14:22 ` Thomas Monjalon 2015-02-18 14:36 ` Gonzalez Monroy, Sergio 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy 8 siblings, 1 reply; 44+ messages in thread From: Thomas Monjalon @ 2015-02-18 14:22 UTC (permalink / raw) To: Sergio Gonzalez Monroy; +Cc: dev Hi Sergio, 2015-02-11 13:07, Sergio Gonzalez Monroy: > This series introduces the new reorder library along with unit tests, > sample app and a new entry in the programmers guide describing the library. > > The library provides reordering of mbufs based on their sequence number. > > As mention in the patch describing the library, one use case is the > packet distributor. > The distributor receives packets, assigns them a sequence number and sends > them to the workers. > The workers process those packets and return them to the distributor. > The distributor collects out-of-order packets from the workers and uses > this library to reorder the packets based on the sequence number they > were assigned. > > v4: > - add missing version.map and related versioning macros You forgot to update the MAINTAINERS file. > v3: > - fix copyright date > - add option to sample app to disable reordering > - add packet ordering sample guide entry > > v2: > - add programmers guide entry describing the library > - use malloc instead of memzone to allocate memory > - modify create and init implementation, init takes a reorder buffer as input > and create reserves memory and call init. > - update unit tests > > Sergio Gonzalez Monroy (5): > reorder: new reorder library > app: New reorder unit test > examples: new sample app packet_ordering > doc: new reorder library description > doc: new packet ordering app description ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v4 0/5] New Reorder Library 2015-02-18 14:22 ` Thomas Monjalon @ 2015-02-18 14:36 ` Gonzalez Monroy, Sergio 0 siblings, 0 replies; 44+ messages in thread From: Gonzalez Monroy, Sergio @ 2015-02-18 14:36 UTC (permalink / raw) To: Thomas Monjalon; +Cc: dev On 18/02/2015 14:22, Thomas Monjalon wrote: > Hi Sergio, > > 2015-02-11 13:07, Sergio Gonzalez Monroy: >> This series introduces the new reorder library along with unit tests, >> sample app and a new entry in the programmers guide describing the library. >> >> The library provides reordering of mbufs based on their sequence number. >> >> As mention in the patch describing the library, one use case is the >> packet distributor. >> The distributor receives packets, assigns them a sequence number and sends >> them to the workers. >> The workers process those packets and return them to the distributor. >> The distributor collects out-of-order packets from the workers and uses >> this library to reorder the packets based on the sequence number they >> were assigned. >> >> v4: >> - add missing version.map and related versioning macros > You forgot to update the MAINTAINERS file. > >> v3: >> - fix copyright date >> - add option to sample app to disable reordering >> - add packet ordering sample guide entry >> >> v2: >> - add programmers guide entry describing the library >> - use malloc instead of memzone to allocate memory >> - modify create and init implementation, init takes a reorder buffer as input >> and create reserves memory and call init. >> - update unit tests >> >> Sergio Gonzalez Monroy (5): >> reorder: new reorder library >> app: New reorder unit test >> examples: new sample app packet_ordering >> doc: new reorder library description >> doc: new packet ordering app description True! I'll do v5. Sergio ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 0/6] New Reorder Library 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy ` (7 preceding siblings ...) 2015-02-18 14:22 ` Thomas Monjalon @ 2015-02-18 14:58 ` Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 1/6] reorder: new reorder library Sergio Gonzalez Monroy ` (6 more replies) 8 siblings, 7 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-18 14:58 UTC (permalink / raw) To: dev This series introduces the new reorder library along with unit tests, sample app and a new entry in the programmers guide describing the library. The library provides reordering of mbufs based on their sequence number. As mention in the patch describing the library, one use case is the packet distributor. The distributor receives packets, assigns them a sequence number and sends them to the workers. The workers process those packets and return them to the distributor. The distributor collects out-of-order packets from the workers and uses this library to reorder the packets based on the sequence number they were assigned. v5: - update MAINTAINERS v4: - add missing version.map and related versioning macros v3: - fix copyright date - add option to sample app to disable reordering - add packet ordering sample guide entry v2: - add programmers guide entry describing the library - use malloc instead of memzone to allocate memory - modify create and init implementation, init takes a reorder buffer as input and create reserves memory and call init. - update unit tests Sergio Gonzalez Monroy (6): reorder: new reorder library app: New reorder unit test examples: new sample app packet_ordering doc: new reorder library description doc: new packet ordering app description MAINTAINERS: add and claim reorder MAINTAINERS | 8 + app/test/Makefile | 2 + app/test/test_reorder.c | 393 ++++++++++++++ config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/reorder_lib.rst | 115 ++++ doc/guides/sample_app_ug/index.rst | 1 + doc/guides/sample_app_ug/packet_ordering.rst | 102 ++++ examples/packet_ordering/Makefile | 50 ++ examples/packet_ordering/main.c | 695 +++++++++++++++++++++++++ lib/Makefile | 1 + lib/librte_eal/common/include/rte_tailq_elem.h | 2 + lib/librte_mbuf/rte_mbuf.h | 3 + lib/librte_reorder/Makefile | 54 ++ lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++ lib/librte_reorder/rte_reorder.h | 181 +++++++ lib/librte_reorder/rte_reorder_version.map | 13 + mk/rte.app.mk | 4 + 19 files changed, 2051 insertions(+) create mode 100644 app/test/test_reorder.c create mode 100644 doc/guides/prog_guide/reorder_lib.rst create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst create mode 100644 examples/packet_ordering/Makefile create mode 100644 examples/packet_ordering/main.c create mode 100644 lib/librte_reorder/Makefile create mode 100644 lib/librte_reorder/rte_reorder.c create mode 100644 lib/librte_reorder/rte_reorder.h create mode 100644 lib/librte_reorder/rte_reorder_version.map -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 1/6] reorder: new reorder library 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy @ 2015-02-18 14:58 ` Sergio Gonzalez Monroy 2015-02-19 9:20 ` Olivier MATZ 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 2/6] app: New reorder unit test Sergio Gonzalez Monroy ` (5 subsequent siblings) 6 siblings, 1 reply; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-18 14:58 UTC (permalink / raw) To: dev This library provides reordering capability for out of order mbufs based on a sequence number in the mbuf structure. Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_eal/common/include/rte_tailq_elem.h | 2 + lib/librte_mbuf/rte_mbuf.h | 3 + lib/librte_reorder/Makefile | 54 ++++ lib/librte_reorder/rte_reorder.c | 416 +++++++++++++++++++++++++ lib/librte_reorder/rte_reorder.h | 181 +++++++++++ lib/librte_reorder/rte_reorder_version.map | 13 + 9 files changed, 680 insertions(+) create mode 100644 lib/librte_reorder/Makefile create mode 100644 lib/librte_reorder/rte_reorder.c create mode 100644 lib/librte_reorder/rte_reorder.h create mode 100644 lib/librte_reorder/rte_reorder_version.map diff --git a/config/common_bsdapp b/config/common_bsdapp index 8cfa4e6..f11ff39 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -344,6 +344,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/config/common_linuxapp b/config/common_linuxapp index db8332d..f921d8c 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -352,6 +352,11 @@ CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 CONFIG_RTE_LIBRTE_DISTRIBUTOR=y # +# Compile the reorder library +# +CONFIG_RTE_LIBRTE_REORDER=y + +# # Compile librte_port # CONFIG_RTE_LIBRTE_PORT=y diff --git a/lib/Makefile b/lib/Makefile index 561a696..6575a4e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,6 +67,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += librte_distributor DIRS-$(CONFIG_RTE_LIBRTE_PORT) += librte_port DIRS-$(CONFIG_RTE_LIBRTE_TABLE) += librte_table DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += librte_pipeline +DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += librte_reorder ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni diff --git a/lib/librte_eal/common/include/rte_tailq_elem.h b/lib/librte_eal/common/include/rte_tailq_elem.h index f74fc7c..3013869 100644 --- a/lib/librte_eal/common/include/rte_tailq_elem.h +++ b/lib/librte_eal/common/include/rte_tailq_elem.h @@ -84,6 +84,8 @@ rte_tailq_elem(RTE_TAILQ_ACL, "RTE_ACL") rte_tailq_elem(RTE_TAILQ_DISTRIBUTOR, "RTE_DISTRIBUTOR") +rte_tailq_elem(RTE_TAILQ_REORDER, "RTE_REORDER") + rte_tailq_end(RTE_TAILQ_NUM) #undef rte_tailq_elem diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h index e3008c6..ace6736 100644 --- a/lib/librte_mbuf/rte_mbuf.h +++ b/lib/librte_mbuf/rte_mbuf.h @@ -289,6 +289,9 @@ struct rte_mbuf { uint32_t usr; /**< User defined tags. See @rte_distributor_process */ } hash; /**< hash information */ + /* sequence number - field used in distributor and reorder library */ + uint32_t seqn; + /* second cache line - fields only used in slow path or on TX */ MARKER cacheline1 __rte_cache_aligned; diff --git a/lib/librte_reorder/Makefile b/lib/librte_reorder/Makefile new file mode 100644 index 0000000..0c01de1 --- /dev/null +++ b/lib/librte_reorder/Makefile @@ -0,0 +1,54 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(RTE_SDK)/mk/rte.vars.mk + +# library name +LIB = librte_reorder.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) + +EXPORT_MAP := rte_reorder_version.map + +LIBABIVER := 1 + +# all source are stored in SRCS-y +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) := rte_reorder.c + +# install this header file +SYMLINK-$(CONFIG_RTE_LIBRTE_REORDER)-include := rte_reorder.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_REORDER) += lib/librte_eal + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_reorder/rte_reorder.c b/lib/librte_reorder/rte_reorder.c new file mode 100644 index 0000000..42d2a47 --- /dev/null +++ b/lib/librte_reorder/rte_reorder.c @@ -0,0 +1,416 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <inttypes.h> +#include <string.h> + +#include <rte_log.h> +#include <rte_mbuf.h> +#include <rte_memzone.h> +#include <rte_eal_memconfig.h> +#include <rte_errno.h> +#include <rte_tailq.h> +#include <rte_malloc.h> + +#include "rte_reorder.h" + +TAILQ_HEAD(rte_reorder_list, rte_tailq_entry); + +#define NO_FLAGS 0 +#define RTE_REORDER_PREFIX "RO_" +#define RTE_REORDER_NAMESIZE 32 + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDER RTE_LOGTYPE_USER1 + +/* A generic circular buffer */ +struct cir_buffer { + unsigned int size; /**< Number of entries that can be stored */ + unsigned int mask; /**< [buffer_size - 1]: used for wrap-around */ + unsigned int head; /**< insertion point in buffer */ + unsigned int tail; /**< extraction point in buffer */ + struct rte_mbuf **entries; +} __rte_cache_aligned; + +/* The reorder buffer data structure itself */ +struct rte_reorder_buffer { + char name[RTE_REORDER_NAMESIZE]; + uint32_t min_seqn; /**< Lowest seq. number that can be in the buffer */ + unsigned int memsize; /**< memory area size of reorder buffer */ + struct cir_buffer ready_buf; /**< temp buffer for dequeued entries */ + struct cir_buffer order_buf; /**< buffer used to reorder entries */ +} __rte_cache_aligned; + +static void +rte_reorder_free_mbufs(struct rte_reorder_buffer *b); + +struct rte_reorder_buffer * +rte_reorder_init(struct rte_reorder_buffer *b, unsigned int bufsize, + const char *name, unsigned int size) +{ + const unsigned int min_bufsize = sizeof(*b) + + (2 * size * sizeof(struct rte_mbuf *)); + + if (b == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer parameter:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + if (bufsize < min_bufsize) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer memory size: %u, " + "minimum required: %u\n", bufsize, min_bufsize); + rte_errno = EINVAL; + return NULL; + } + + memset(b, 0, bufsize); + snprintf(b->name, sizeof(b->name), "%s", name); + b->memsize = bufsize; + b->order_buf.size = b->ready_buf.size = size; + b->order_buf.mask = b->ready_buf.mask = size - 1; + b->ready_buf.entries = (void *)&b[1]; + b->order_buf.entries = RTE_PTR_ADD(&b[1], + size * sizeof(b->ready_buf.entries[0])); + + return b; +} + +struct rte_reorder_buffer* +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + const unsigned int bufsize = sizeof(struct rte_reorder_buffer) + + (2 * size * sizeof(struct rte_mbuf *)); + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + /* Check user arguments. */ + if (!rte_is_power_of_2(size)) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer size" + " - Not a power of 2\n"); + rte_errno = EINVAL; + return NULL; + } + if (name == NULL) { + RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:" + " NULL\n"); + rte_errno = EINVAL; + return NULL; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* guarantee there's no existing */ + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + if (te != NULL) + goto exit; + + /* allocate tailq entry */ + te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0); + if (te == NULL) { + RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n"); + rte_errno = ENOMEM; + b = NULL; + goto exit; + } + + /* Allocate memory to store the reorder buffer structure. */ + b = rte_zmalloc_socket("REORDER_BUFFER", bufsize, 0, socket_id); + if (b == NULL) { + RTE_LOG(ERR, REORDER, "Memzone allocation failed\n"); + rte_errno = ENOMEM; + rte_free(te); + } else { + rte_reorder_init(b, bufsize, name, size); + te->data = (void *)b; + TAILQ_INSERT_TAIL(reorder_list, te, next); + } + +exit: + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return b; +} + +void +rte_reorder_reset(struct rte_reorder_buffer *b) +{ + char name[RTE_REORDER_NAMESIZE]; + + rte_reorder_free_mbufs(b); + snprintf(name, sizeof(name), "%s", b->name); + /* No error checking as current values should be valid */ + rte_reorder_init(b, b->memsize, name, b->order_buf.size); +} + +static void +rte_reorder_free_mbufs(struct rte_reorder_buffer *b) +{ + unsigned i; + + /* Free up the mbufs of order buffer & ready buffer */ + for (i = 0; i < b->order_buf.size; i++) { + if (b->order_buf.entries[i]) + rte_pktmbuf_free(b->order_buf.entries[i]); + if (b->ready_buf.entries[i]) + rte_pktmbuf_free(b->ready_buf.entries[i]); + } +} + +void +rte_reorder_free(struct rte_reorder_buffer *b) +{ + struct rte_reorder_list *reorder_list; + struct rte_tailq_entry *te; + + /* Check user arguments. */ + if (b == NULL) + return; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return; + } + + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); + + /* find our tailq entry */ + TAILQ_FOREACH(te, reorder_list, next) { + if (te->data == (void *) b) + break; + } + if (te == NULL) { + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + return; + } + + TAILQ_REMOVE(reorder_list, te, next); + + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); + + rte_reorder_free_mbufs(b); + + rte_free(b); + rte_free(te); +} + +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_tailq_entry *te; + struct rte_reorder_list *reorder_list; + + /* check that we have an initialised tail queue */ + reorder_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_REORDER, rte_reorder_list); + if (!reorder_list) { + rte_errno = E_RTE_NO_TAILQ; + return NULL; + } + + rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); + TAILQ_FOREACH(te, reorder_list, next) { + b = (struct rte_reorder_buffer *) te->data; + if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0) + break; + } + rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); + + if (te == NULL) { + rte_errno = ENOENT; + return NULL; + } + + return b; +} + +static unsigned +rte_reorder_fill_overflow(struct rte_reorder_buffer *b, unsigned n) +{ + /* + * 1. Move all ready entries that fit to the ready_buf + * 2. check if we meet the minimum needed (n). + * 3. If not, then skip any gaps and keep moving. + * 4. If at any point the ready buffer is full, stop + * 5. Return the number of positions the order_buf head has moved + */ + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + unsigned int order_head_adv = 0; + + /* + * move at least n packets to ready buffer, assuming ready buffer + * has room for those packets. + */ + while (order_head_adv < n && + ((ready_buf->head + 1) & ready_buf->mask) != ready_buf->tail) { + + /* if we are blocked waiting on a packet, skip it */ + if (order_buf->entries[order_buf->head] == NULL) { + order_buf->head = (order_buf->head + 1) & order_buf->mask; + order_head_adv++; + } + + /* Move all ready entries that fit to the ready_buf */ + while (order_buf->entries[order_buf->head] != NULL) { + ready_buf->entries[ready_buf->head] = + order_buf->entries[order_buf->head]; + + order_buf->entries[order_buf->head] = NULL; + order_head_adv++; + + order_buf->head = (order_buf->head + 1) & order_buf->mask; + + if (((ready_buf->head + 1) & ready_buf->mask) == ready_buf->tail) + break; + + ready_buf->head = (ready_buf->head + 1) & ready_buf->mask; + } + } + + b->min_seqn += order_head_adv; + /* Return the number of positions the order_buf head has moved */ + return order_head_adv; +} + +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf) +{ + uint32_t offset, position; + struct cir_buffer *order_buf = &b->order_buf; + + /* + * calculate the offset from the head pointer we need to go. + * The subtraction takes care of the sequence number wrapping. + * For example (using 16-bit for brevity): + * min_seqn = 0xFFFD + * mbuf_seqn = 0x0010 + * offset = 0x0010 - 0xFFFD = 0x13 + */ + offset = mbuf->seqn - b->min_seqn; + + /* + * action to take depends on offset. + * offset < buffer->size: the mbuf fits within the current window of + * sequence numbers we can reorder. EXPECTED CASE. + * offset > buffer->size: the mbuf is outside the current window. There + * are a number of cases to consider: + * 1. The packet sequence is just outside the window, then we need + * to see about shifting the head pointer and taking any ready + * to return packets out of the ring. If there was a delayed + * or dropped packet preventing drains from shifting the window + * this case will skip over the dropped packet instead, and any + * packets dequeued here will be returned on the next drain call. + * 2. The packet sequence number is vastly outside our window, taken + * here as having offset greater than twice the buffer size. In + * this case, the packet is probably an old or late packet that + * was previously skipped, so just enqueue the packet for + * immediate return on the next drain call, or else return error. + */ + if (offset < b->order_buf.size) { + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else if (offset < 2 * b->order_buf.size) { + if (rte_reorder_fill_overflow(b, offset + 1 - order_buf->size) + < (offset + 1 - order_buf->size)) { + /* Put in handling for enqueue straight to output */ + rte_errno = ENOSPC; + return -1; + } + offset = mbuf->seqn - b->min_seqn; + position = (order_buf->head + offset) & order_buf->mask; + order_buf->entries[position] = mbuf; + } else { + /* Put in handling for enqueue straight to output */ + rte_errno = ERANGE; + return -1; + } + return 0; +} + +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs) +{ + unsigned int drain_cnt = 0; + + struct cir_buffer *order_buf = &b->order_buf, + *ready_buf = &b->ready_buf; + + /* Try to fetch requested number of mbufs from ready buffer */ + while ((drain_cnt < max_mbufs) && (ready_buf->tail != ready_buf->head)) { + mbufs[drain_cnt++] = ready_buf->entries[ready_buf->tail]; + ready_buf->tail = (ready_buf->tail + 1) & ready_buf->mask; + } + + /* + * If requested number of buffers not fetched from ready buffer, fetch + * remaining buffers from order buffer + */ + while ((drain_cnt < max_mbufs) && + (order_buf->entries[order_buf->head] != NULL)) { + mbufs[drain_cnt++] = order_buf->entries[order_buf->head]; + order_buf->entries[order_buf->head] = NULL; + b->min_seqn++; + order_buf->head = (order_buf->head + 1) & order_buf->mask; + } + + return drain_cnt; +} diff --git a/lib/librte_reorder/rte_reorder.h b/lib/librte_reorder/rte_reorder.h new file mode 100644 index 0000000..8300bf0 --- /dev/null +++ b/lib/librte_reorder/rte_reorder.h @@ -0,0 +1,181 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_REORDER_H_ +#define _RTE_REORDER_H_ + +/** + * @file + * RTE reorder + * + * Reorder library is a component which is designed to + * provide ordering of out of ordered packets based on + * sequence number present in mbuf. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rte_reorder_buffer; + +/** + * Create a new reorder buffer instance + * + * Allocate memory and initialize a new reorder buffer in that + * memory, returning the reorder buffer pointer to the user + * + * @param name + * The name to be given to the reorder buffer instance. + * @param socket_id + * The NUMA node on which the memory for the reorder buffer + * instance is to be reserved. + * @param size + * Max number of elements that can be stored in the reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - ENOMEM - no appropriate memory area found in which to create memzone + * - EINVAL - invalid parameters + */ +struct rte_reorder_buffer * +rte_reorder_create(const char *name, unsigned socket_id, unsigned int size); + +/** + * Initializes given reorder buffer instance + * + * @param b + * Reorder buffer instance to initialize + * @param bufsize + * Size of the reorder buffer + * @param name + * The name to be given to the reorder buffer + * @param size + * Number of elements that can be stored in reorder buffer + * @return + * The initialized reorder buffer instance, or NULL on error + * On error case, rte_errno will be set appropriately: + * - EINVAL - invalid parameters + */ +struct rte_reorder_buffer * +rte_reorder_init(struct rte_reorder_buffer *b, unsigned int bufsize, + const char *name, unsigned int size); + +/** + * Find an existing reorder buffer instance + * and return a pointer to it. + * + * @param name + * Name of the reorder buffer instacne as passed to rte_reorder_create() + * @return + * Pointer to reorder buffer instance or NULL if object not found with rte_errno + * set appropriately. Possible rte_errno values include: + * - ENOENT - required entry not available to return. + * - E_RTE_NO_TAILQ - no tailq list could be got for the + * reorder instance list + */ +struct rte_reorder_buffer * +rte_reorder_find_existing(const char *name); + +/** + * Reset the given reorder buffer instance with initial values. + * + * @param b + * Reorder buffer instance which has to be reset + */ +void +rte_reorder_reset(struct rte_reorder_buffer *b); + +/** + * Free reorder buffer instance. + * + * @param b + * reorder buffer instance + * @return + * None + */ +void +rte_reorder_free(struct rte_reorder_buffer *b); + +/** + * Insert given mbuf in reorder buffer in its correct position + * + * The given mbuf is to be reordered relative to other mbufs in the system. + * The mbuf must contain a sequence number which is then used to place + * the buffer in the correct position in the reorder buffer. Reordered + * packets can later be taken from the buffer using the rte_reorder_drain() + * API. + * + * @param b + * Reorder buffer where the mbuf has to be inserted. + * @param mbuf + * mbuf of packet that needs to be inserted in reorder buffer. + * @return + * 0 on success + * -1 on error + * On error case, rte_errno will be set appropriately: + * - ENOSPC - Cannot move existing mbufs from reorder buffer to accommodate + * ealry mbuf, but it can be accomodated by performing drain and then insert. + * - ERANGE - Too early or late mbuf which is vastly out of range of expected + * window should be ingnored without any handling. + */ +int +rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf); + +/** + * Fetch reordered buffers + * + * Returns a set of in-order buffers from the reorder buffer structure. Gaps + * may be present in the sequence numbers of the mbuf if packets have been + * delayed too long before reaching the reorder window, or have been previously + * dropped by the system. + * + * @param b + * Reorder buffer instance from which packets are to be drained + * @param mbufs + * array of mbufs where reordered packets will be inserted from reorder buffer + * @param max_mbufs + * the number of elements in the mbufs array. + * @return + * number of mbuf pointers written to mbufs. 0 <= N < max_mbufs. + */ +unsigned int +rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs, + unsigned max_mbufs); + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_REORDER_H_ */ diff --git a/lib/librte_reorder/rte_reorder_version.map b/lib/librte_reorder/rte_reorder_version.map new file mode 100644 index 0000000..0a8a54d --- /dev/null +++ b/lib/librte_reorder/rte_reorder_version.map @@ -0,0 +1,13 @@ +DPDK_2.0 { + global: + + rte_reorder_create; + rte_reorder_init; + rte_reorder_find_existing; + rte_reorder_reset; + rte_reorder_free; + rte_reorder_insert; + rte_reorder_drain; + + local: *; +}; -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v5 1/6] reorder: new reorder library 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 1/6] reorder: new reorder library Sergio Gonzalez Monroy @ 2015-02-19 9:20 ` Olivier MATZ 2015-02-19 9:50 ` Olivier MATZ 0 siblings, 1 reply; 44+ messages in thread From: Olivier MATZ @ 2015-02-19 9:20 UTC (permalink / raw) To: Sergio Gonzalez Monroy, dev Hi Sergio, On 02/18/2015 03:58 PM, Sergio Gonzalez Monroy wrote: > This library provides reordering capability for out of order mbufs based > on a sequence number in the mbuf structure. > > Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> > Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> > Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> > > [...] > > --- a/lib/librte_mbuf/rte_mbuf.h > +++ b/lib/librte_mbuf/rte_mbuf.h > @@ -289,6 +289,9 @@ struct rte_mbuf { > uint32_t usr; /**< User defined tags. See @rte_distributor_process */ > } hash; /**< hash information */ > > + /* sequence number - field used in distributor and reorder library */ > + uint32_t seqn; > + > /* second cache line - fields only used in slow path or on TX */ > MARKER cacheline1 __rte_cache_aligned; > Just one small comment about rte_mbuf: the comment should be in doxygen style. Regards, Olivier ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v5 1/6] reorder: new reorder library 2015-02-19 9:20 ` Olivier MATZ @ 2015-02-19 9:50 ` Olivier MATZ 0 siblings, 0 replies; 44+ messages in thread From: Olivier MATZ @ 2015-02-19 9:50 UTC (permalink / raw) To: Sergio Gonzalez Monroy, dev Hi, On 02/19/2015 10:20 AM, Olivier MATZ wrote: > Hi Sergio, > > On 02/18/2015 03:58 PM, Sergio Gonzalez Monroy wrote: >> This library provides reordering capability for out of order mbufs based >> on a sequence number in the mbuf structure. >> >> Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> >> Signed-off-by: Richardson Bruce <bruce.richardson@intel.com> >> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> >> >> [...] >> >> --- a/lib/librte_mbuf/rte_mbuf.h >> +++ b/lib/librte_mbuf/rte_mbuf.h >> @@ -289,6 +289,9 @@ struct rte_mbuf { >> uint32_t usr; /**< User defined tags. See >> @rte_distributor_process */ >> } hash; /**< hash information */ >> >> + /* sequence number - field used in distributor and reorder >> library */ >> + uint32_t seqn; >> + >> /* second cache line - fields only used in slow path or on TX */ >> MARKER cacheline1 __rte_cache_aligned; >> > > Just one small comment about rte_mbuf: the comment should be in doxygen > style. I've just realized it's already applied. I'll submit a patch for this. Olivier ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 2/6] app: New reorder unit test 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 1/6] reorder: new reorder library Sergio Gonzalez Monroy @ 2015-02-18 14:58 ` Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 3/6] examples: new sample app packet_ordering Sergio Gonzalez Monroy ` (4 subsequent siblings) 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-18 14:58 UTC (permalink / raw) To: dev Adding new reorder unit test for the test app. The command to run the unit test from the test shell is: reorder_autotest Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- app/test/Makefile | 2 + app/test/test_reorder.c | 393 ++++++++++++++++++++++++++++++++++++++++++++++++ mk/rte.app.mk | 4 + 3 files changed, 399 insertions(+) create mode 100644 app/test/test_reorder.c diff --git a/app/test/Makefile b/app/test/Makefile index 4311f96..24b27d7 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -124,6 +124,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += test_ivshmem.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor.c SRCS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += test_distributor_perf.c +SRCS-$(CONFIG_RTE_LIBRTE_REORDER) += test_reorder.c + SRCS-y += test_devargs.c SRCS-y += virtual_pmd.c SRCS-y += packet_burst_generator.c diff --git a/app/test/test_reorder.c b/app/test/test_reorder.c new file mode 100644 index 0000000..61cf8d3 --- /dev/null +++ b/app/test/test_reorder.c @@ -0,0 +1,393 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test.h" +#include "stdio.h" + +#include <unistd.h> +#include <string.h> + +#include <rte_cycles.h> +#include <rte_errno.h> +#include <rte_mbuf.h> +#include <rte_reorder.h> +#include <rte_lcore.h> +#include <rte_malloc.h> + +#include "test.h" + +#define BURST 32 +#define REORDER_BUFFER_SIZE 16384 +#define NUM_MBUFS (2*REORDER_BUFFER_SIZE) +#define REORDER_BUFFER_SIZE_INVALID 2049 +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) + +struct reorder_unittest_params { + struct rte_mempool *p; + struct rte_reorder_buffer *b; +}; + +static struct reorder_unittest_params default_params = { + .p = NULL, + .b = NULL +}; + +static struct reorder_unittest_params *test_params = &default_params; + +static int +test_reorder_create(void) +{ + struct rte_reorder_buffer *b = NULL; + + b = rte_reorder_create(NULL, rte_socket_id(), REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on create() with NULL name"); + + b = rte_reorder_create("PKT", rte_socket_id(), REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on create() with invalid buffer size param."); + + b = rte_reorder_create("PKT_RO1", rte_socket_id(), REORDER_BUFFER_SIZE); + printf("DEBUG: b= %p, orig_b= %p\n", b, test_params->b); + TEST_ASSERT_EQUAL(b, test_params->b, + "New reorder instance created with already existing name"); + + return 0; +} + +static int +test_reorder_init(void) +{ + struct rte_reorder_buffer *b = NULL; + unsigned int size; + /* + * The minimum memory area size that should be passed to library is, + * sizeof(struct rte_reorder_buffer) + (2 * size * sizeof(struct rte_mbuf *)); + * Otherwise error will be thrown + */ + + size = 100; + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with NULL buffer."); + + b = rte_malloc(NULL, size, 0); + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid mem zone size."); + rte_free(b); + + size = 262336; + b = rte_malloc(NULL, size, 0); + b = rte_reorder_init(b, size, "PKT1", REORDER_BUFFER_SIZE_INVALID); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid buffer size param."); + + b = rte_reorder_init(b, size, NULL, REORDER_BUFFER_SIZE); + TEST_ASSERT((b == NULL) && (rte_errno == EINVAL), + "No error on init with invalid name."); + rte_free(b); + + return 0; +} + +static int +test_reorder_find_existing(void) +{ + struct rte_reorder_buffer *b = NULL; + + /* Try to find existing reorder buffer instance */ + b = rte_reorder_find_existing("PKT_RO1"); + TEST_ASSERT_EQUAL(b, test_params->b, + "existing reorder buffer instance not found"); + + /* Try to find non existing reorder buffer instance */ + b = rte_reorder_find_existing("ro_find_non_existing"); + TEST_ASSERT((b == NULL) && (rte_errno == ENOENT), + "non existing reorder buffer instance found"); + + return 0; +} + +static int +test_reorder_free(void) +{ + struct rte_reorder_buffer *b1 = NULL, *b2 = NULL; + const char *name = "test_free"; + + b1 = rte_reorder_create(name, rte_socket_id(), 8); + TEST_ASSERT_NOT_NULL(b1, "Failed to create reorder buffer."); + + b2 = rte_reorder_find_existing(name); + TEST_ASSERT_EQUAL(b1, b2, "Failed to find existing reorder buffer"); + + rte_reorder_free(b1); + + b2 = rte_reorder_find_existing(name); + TEST_ASSERT((b2 == NULL) && (rte_errno == ENOENT), + "Found previously freed reorder buffer"); + + return 0; +} + +static int +test_reorder_insert(void) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_mempool *p = test_params->p; + const unsigned int size = 4; + const unsigned int num_bufs = 6; + struct rte_mbuf *bufs[num_bufs]; + int ret = 0; + unsigned i; + + /* This would create a reorder buffer instance consisting of: + * reorder_seq = 0 + * ready_buf: RB[size] = {NULL, NULL, NULL, NULL} + * order_buf: OB[size] = {NULL, NULL, NULL, NULL} + */ + b = rte_reorder_create("test_insert", rte_socket_id(), size); + TEST_ASSERT_NOT_NULL(b, "Failed to create reorder buffer"); + + ret = rte_mempool_get_bulk(p, (void *)bufs, num_bufs); + TEST_ASSERT_SUCCESS(ret, "Error getting mbuf from pool"); + + /* late packet */ + bufs[0]->seqn = 3 * size; + ret = rte_reorder_insert(b, bufs[0]); + if (!((ret == -1) && (rte_errno == ERANGE))) { + printf("%s:%d: No error inserting late packet with seqn:" + " 3 * size\n", __func__, __LINE__); + ret = -1; + goto exit; + } + + for (i = 0; i < num_bufs; i++) + bufs[i]->seqn = i; + + /* This should fill up order buffer: + * reorder_seq = 0 + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {0, 1, 2, 3} + */ + for (i = 0; i < size; i++) { + ret = rte_reorder_insert(b, bufs[i]); + if (ret != 0) { + printf("%s:%d: Error inserting packet with seqn less than size\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + } + + /* early packet - should move mbufs to ready buf and move sequence window + * reorder_seq = 4 + * RB[] = {0, 1, 2, 3} + * OB[] = {4, NULL, NULL, NULL} + */ + ret = rte_reorder_insert(b, bufs[4]); + if (ret != 0) { + printf("%s:%d: Error inserting early packet with seqn: size\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + /* early packet from current sequence window - full ready buffer */ + bufs[5]->seqn = 2 * size; + ret = rte_reorder_insert(b, bufs[5]); + if (!((ret == -1) && (rte_errno == ENOSPC))) { + printf("%s:%d: No error inserting early packet with full ready buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + ret = 0; +exit: + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + rte_reorder_free(b); + return ret; +} + +static int +test_reorder_drain(void) +{ + struct rte_reorder_buffer *b = NULL; + struct rte_mempool *p = test_params->p; + const unsigned int size = 4; + const unsigned int num_bufs = 10; + struct rte_mbuf *bufs[num_bufs]; + int ret = 0; + unsigned i, cnt; + + /* This would create a reorder buffer instance consisting of: + * reorder_seq = 0 + * ready_buf: RB[size] = {NULL, NULL, NULL, NULL} + * order_buf: OB[size] = {NULL, NULL, NULL, NULL} + */ + b = rte_reorder_create("test_insert", rte_socket_id(), size); + TEST_ASSERT_NOT_NULL(b, "Failed to create reorder buffer"); + + ret = rte_mempool_get_bulk(p, (void *)bufs, num_bufs); + TEST_ASSERT_SUCCESS(ret, "Error getting mbuf from pool"); + + /* Check no drained packets if reorder is empty */ + cnt = rte_reorder_drain(b, bufs, 1); + if (cnt != 0) { + printf("%s:%d: drained packets from empty reorder buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + for (i = 0; i < num_bufs; i++) + bufs[i]->seqn = i; + + /* Insert packet with seqn 1: + * reorder_seq = 0 + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {NULL, 1, NULL, NULL} + */ + rte_reorder_insert(b, bufs[1]); + + /* Check no drained packets if no ready/order packets */ + cnt = rte_reorder_drain(b, bufs, 1); + if (cnt != 0) { + printf("%s:%d: drained packets from empty reorder buffer\n", + __func__, __LINE__); + ret = -1; + goto exit; + } + + /* Insert more packets + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {0, 1, NULL, 3} + */ + rte_reorder_insert(b, bufs[0]); + rte_reorder_insert(b, bufs[3]); + + /* drained expected packets */ + cnt = rte_reorder_drain(b, bufs, 4); + if (cnt != 2) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + ret = -1; + goto exit; + } + + /* + * RB[] = {NULL, NULL, NULL, NULL} + * OB[] = {NULL, 3, NULL, NULL} + */ + + rte_reorder_insert(b, bufs[4]); + rte_reorder_insert(b, bufs[7]); + + /* + * RB[] = {3, 4, NULL, NULL} + * OB[] = {NULL, NULL, 7, NULL} + */ + + cnt = rte_reorder_drain(b, bufs, 4); + if (cnt != 2) { + printf("%s:%d:%d: number of expected packets not drained\n", + __func__, __LINE__, cnt); + ret = -1; + goto exit; + } + + ret = 0; +exit: + rte_mempool_put_bulk(p, (void *)bufs, num_bufs); + rte_reorder_free(b); + return ret; +} + +static int +test_setup(void) +{ + /* reorder buffer instance creation */ + if (test_params->b == NULL) { + test_params->b = rte_reorder_create("PKT_RO1", rte_socket_id(), + REORDER_BUFFER_SIZE); + if (test_params->b == NULL) { + printf("%s: Error creating reorder buffer instance b\n", + __func__); + return -1; + } + } else + rte_reorder_reset(test_params->b); + + /* mempool creation */ + if (test_params->p == NULL) { + test_params->p = rte_mempool_create("RO_MBUF_POOL", NUM_MBUFS, + MBUF_SIZE, BURST, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->p == NULL) { + printf("%s: Error creating mempool\n", __func__); + return -1; + } + } + return 0; +} + +static struct unit_test_suite reorder_test_suite = { + + .setup = test_setup, + .suite_name = "Reorder Unit Test Suite", + .unit_test_cases = { + TEST_CASE(test_reorder_create), + TEST_CASE(test_reorder_init), + TEST_CASE(test_reorder_find_existing), + TEST_CASE(test_reorder_free), + TEST_CASE(test_reorder_insert), + TEST_CASE(test_reorder_drain), + TEST_CASES_END() + } +}; + +static int +test_reorder(void) +{ + return unit_test_suite_runner(&reorder_test_suite); +} + +static struct test_command reorder_cmd = { + .command = "reorder_autotest", + .callback = test_reorder, +}; +REGISTER_TEST_COMMAND(reorder_cmd); diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 4a6236a..5a93b46 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -71,6 +71,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_DISTRIBUTOR),y) LDLIBS += -lrte_distributor endif +ifeq ($(CONFIG_RTE_LIBRTE_REORDER),y) +LDLIBS += -lrte_reorder +endif + ifeq ($(CONFIG_RTE_LIBRTE_KNI),y) ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) LDLIBS += -lrte_kni -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 3/6] examples: new sample app packet_ordering 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 1/6] reorder: new reorder library Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 2/6] app: New reorder unit test Sergio Gonzalez Monroy @ 2015-02-18 14:58 ` Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 4/6] doc: new reorder library description Sergio Gonzalez Monroy ` (3 subsequent siblings) 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-18 14:58 UTC (permalink / raw) To: dev This new app makes use of the librte_reorder library. It requires at least 3 lcores for RX, Workers (1 or more) and TX threads. Communication between RX-Workers and Workers-TX is done by using rings. The flow of mbufs is the following: * RX thread gets mbufs from driver, set sequence number and enqueue them in ring. * Workers dequeue mbufs from ring, do some 'work' and enqueue mbufs in ring. * TX dequeue mbufs from ring, inserts them in reorder buffer, drains mbufs from reorder and sends them to the driver. Signed-off-by: Reshma Pattan <reshma.pattan@intel.com> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- examples/packet_ordering/Makefile | 50 +++ examples/packet_ordering/main.c | 695 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 745 insertions(+) create mode 100644 examples/packet_ordering/Makefile create mode 100644 examples/packet_ordering/main.c diff --git a/examples/packet_ordering/Makefile b/examples/packet_ordering/Makefile new file mode 100644 index 0000000..9e080a3 --- /dev/null +++ b/examples/packet_ordering/Makefile @@ -0,0 +1,50 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overridden by command line or environment +RTE_TARGET ?= x86_64-ivshmem-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +# binary name +APP = packet_ordering + +# all source are stored in SRCS-y +SRCS-y := main.c + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/packet_ordering/main.c b/examples/packet_ordering/main.c new file mode 100644 index 0000000..75e2f46 --- /dev/null +++ b/examples/packet_ordering/main.c @@ -0,0 +1,695 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <signal.h> +#include <getopt.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_errno.h> +#include <rte_ethdev.h> +#include <rte_lcore.h> +#include <rte_mbuf.h> +#include <rte_mempool.h> +#include <rte_ring.h> +#include <rte_reorder.h> + +#define RX_DESC_PER_QUEUE 128 +#define TX_DESC_PER_QUEUE 512 + +#define MAX_PKTS_BURST 32 +#define REORDER_BUFFER_SIZE 8192 +#define MBUF_PER_POOL 65535 +#define MBUF_SIZE (1600 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_POOL_CACHE_SIZE 250 + +#define RING_SIZE 16384 + +/* uncommnet below line to enable debug logs */ +/* #define DEBUG */ + +#ifdef DEBUG +#define LOG_LEVEL RTE_LOG_DEBUG +#define LOG_DEBUG(log_type, fmt, args...) RTE_LOG(DEBUG, log_type, fmt, ##args) +#else +#define LOG_LEVEL RTE_LOG_INFO +#define LOG_DEBUG(log_type, fmt, args...) do {} while (0) +#endif + +/* Macros for printing using RTE_LOG */ +#define RTE_LOGTYPE_REORDERAPP RTE_LOGTYPE_USER1 + +unsigned int portmask; +unsigned int disable_reorder; +volatile uint8_t quit_signal; + +static struct rte_mempool *mbuf_pool; + +static struct rte_eth_conf port_conf_default; + +struct worker_thread_args { + struct rte_ring *ring_in; + struct rte_ring *ring_out; +}; + +struct output_buffer { + unsigned count; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; +}; + +volatile struct app_stats { + struct { + uint64_t rx_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } rx __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + uint64_t enqueue_pkts; + uint64_t enqueue_failed_pkts; + } wkr __rte_cache_aligned; + + struct { + uint64_t dequeue_pkts; + /* Too early pkts transmitted directly w/o reordering */ + uint64_t early_pkts_txtd_woro; + /* Too early pkts failed from direct transmit */ + uint64_t early_pkts_tx_failed_woro; + uint64_t ro_tx_pkts; + uint64_t ro_tx_failed_pkts; + } tx __rte_cache_aligned; +} app_stats; + +/** + * Get the last enabled lcore ID + * + * @return + * The last enabled lcore ID. + */ +static unsigned int +get_last_lcore_id(void) +{ + int i; + + for (i = RTE_MAX_LCORE - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return 0; +} + +/** + * Get the previous enabled lcore ID + * @param id + * The current lcore ID + * @return + * The previous enabled lcore ID or the current lcore + * ID if it is the first available core. + */ +static unsigned int +get_previous_lcore_id(unsigned int id) +{ + int i; + + for (i = id - 1; i >= 0; i--) + if (rte_lcore_is_enabled(i)) + return i; + return id; +} + +static inline void +pktmbuf_free_bulk(struct rte_mbuf *mbuf_table[], unsigned n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + rte_pktmbuf_free(mbuf_table[i]); +} + +/* display usage */ +static void +print_usage(const char *prgname) +{ + printf("%s [EAL options] -- -p PORTMASK\n" + " -p PORTMASK: hexadecimal bitmask of ports to configure\n", + prgname); +} + +static int +parse_portmask(const char *portmask) +{ + unsigned long pm; + char *end = NULL; + + /* parse hexadecimal string */ + pm = strtoul(portmask, &end, 16); + if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) + return -1; + + if (pm == 0) + return -1; + + return pm; +} + +/* Parse the argument given in the command line of the application */ +static int +parse_args(int argc, char **argv) +{ + int opt; + int option_index; + char **argvopt; + char *prgname = argv[0]; + static struct option lgopts[] = { + {"disable-reorder", 0, 0, 0}, + {NULL, 0, 0, 0} + }; + + argvopt = argv; + + while ((opt = getopt_long(argc, argvopt, "p:", + lgopts, &option_index)) != EOF) { + switch (opt) { + /* portmask */ + case 'p': + portmask = parse_portmask(optarg); + if (portmask == 0) { + printf("invalid portmask\n"); + print_usage(prgname); + return -1; + } + break; + /* long options */ + case 0: + if (!strcmp(lgopts[option_index].name, "disable-reorder")) { + printf("reorder disabled\n"); + disable_reorder = 1; + } + break; + default: + print_usage(prgname); + return -1; + } + } + if (optind <= 1) { + print_usage(prgname); + return -1; + } + + argv[optind-1] = prgname; + optind = 0; /* reset getopt lib */ + return 0; +} + +static inline int +configure_eth_port(uint8_t port_id) +{ + struct ether_addr addr; + const uint16_t rxRings = 1, txRings = 1; + const uint8_t nb_ports = rte_eth_dev_count(); + int ret; + uint16_t q; + + if (port_id > nb_ports) + return -1; + + ret = rte_eth_dev_configure(port_id, rxRings, txRings, &port_conf_default); + if (ret != 0) + return ret; + + for (q = 0; q < rxRings; q++) { + ret = rte_eth_rx_queue_setup(port_id, q, RX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL, + mbuf_pool); + if (ret < 0) + return ret; + } + + for (q = 0; q < txRings; q++) { + ret = rte_eth_tx_queue_setup(port_id, q, TX_DESC_PER_QUEUE, + rte_eth_dev_socket_id(port_id), NULL); + if (ret < 0) + return ret; + } + + ret = rte_eth_dev_start(port_id); + if (ret < 0) + return ret; + + rte_eth_macaddr_get(port_id, &addr); + printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 + " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", + (unsigned)port_id, + addr.addr_bytes[0], addr.addr_bytes[1], + addr.addr_bytes[2], addr.addr_bytes[3], + addr.addr_bytes[4], addr.addr_bytes[5]); + + rte_eth_promiscuous_enable(port_id); + + return 0; +} + +static void +print_stats(void) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + unsigned i; + struct rte_eth_stats eth_stats; + + printf("\nRX thread stats:\n"); + printf(" - Pkts rxd: %"PRIu64"\n", + app_stats.rx.rx_pkts); + printf(" - Pkts enqd to workers ring: %"PRIu64"\n", + app_stats.rx.enqueue_pkts); + + printf("\nWorker thread stats:\n"); + printf(" - Pkts deqd from workers ring: %"PRIu64"\n", + app_stats.wkr.dequeue_pkts); + printf(" - Pkts enqd to tx ring: %"PRIu64"\n", + app_stats.wkr.enqueue_pkts); + printf(" - Pkts enq to tx failed: %"PRIu64"\n", + app_stats.wkr.enqueue_failed_pkts); + + printf("\nTX stats:\n"); + printf(" - Pkts deqd from tx ring: %"PRIu64"\n", + app_stats.tx.dequeue_pkts); + printf(" - Ro Pkts transmitted: %"PRIu64"\n", + app_stats.tx.ro_tx_pkts); + printf(" - Ro Pkts tx failed: %"PRIu64"\n", + app_stats.tx.ro_tx_failed_pkts); + printf(" - Pkts transmitted w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_txtd_woro); + printf(" - Pkts tx failed w/o reorder: %"PRIu64"\n", + app_stats.tx.early_pkts_tx_failed_woro); + + for (i = 0; i < nb_ports; i++) { + rte_eth_stats_get(i, ð_stats); + printf("\nPort %u stats:\n", i); + printf(" - Pkts in: %"PRIu64"\n", eth_stats.ipackets); + printf(" - Pkts out: %"PRIu64"\n", eth_stats.opackets); + printf(" - In Errs: %"PRIu64"\n", eth_stats.ierrors); + printf(" - Out Errs: %"PRIu64"\n", eth_stats.oerrors); + printf(" - Mbuf Errs: %"PRIu64"\n", eth_stats.rx_nombuf); + } +} + +static void +int_handler(int sig_num) +{ + printf("Exiting on signal %d\n", sig_num); + quit_signal = 1; +} + +/** + * This thread receives mbufs from the port and affects them an internal + * sequence number to keep track of their order of arrival through an + * mbuf structure. + * The mbufs are then passed to the worker threads via the rx_to_workers + * ring. + */ +static int +rx_thread(struct rte_ring *ring_out) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint32_t seqn = 0; + uint16_t i, ret = 0; + uint16_t nb_rx_pkts; + uint8_t port_id; + struct rte_mbuf *pkts[MAX_PKTS_BURST]; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + + while (!quit_signal) { + + for (port_id = 0; port_id < nb_ports; port_id++) { + if ((portmask & (1 << port_id)) != 0) { + + /* receive packets */ + nb_rx_pkts = rte_eth_rx_burst(port_id, 0, + pkts, MAX_PKTS_BURST); + if (nb_rx_pkts == 0) { + LOG_DEBUG(REORDERAPP, + "%s():Received zero packets\n", __func__); + continue; + } + app_stats.rx.rx_pkts += nb_rx_pkts; + + /* mark sequence number */ + for (i = 0; i < nb_rx_pkts; ) + pkts[i++]->seqn = seqn++; + + /* enqueue to rx_to_workers ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *) pkts, + nb_rx_pkts); + app_stats.rx.enqueue_pkts += ret; + if (unlikely(ret < nb_rx_pkts)) { + app_stats.rx.enqueue_failed_pkts += + (nb_rx_pkts-ret); + pktmbuf_free_bulk(&pkts[ret], nb_rx_pkts - ret); + } + } + } + } + return 0; +} + +/** + * This thread takes bursts of packets from the rx_to_workers ring and + * Changes the input port value to output port value. And feds it to + * workers_to_tx + */ +static int +worker_thread(void *args_ptr) +{ + const uint8_t nb_ports = rte_eth_dev_count(); + uint16_t i, ret = 0; + uint16_t burst_size = 0; + struct worker_thread_args *args; + struct rte_mbuf *burst_buffer[MAX_PKTS_BURST] = { NULL }; + struct rte_ring *ring_in, *ring_out; + const unsigned xor_val = (nb_ports > 1); + + args = (struct worker_thread_args *) args_ptr; + ring_in = args->ring_in; + ring_out = args->ring_out; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + + while (!quit_signal) { + + /* dequeue the mbufs from rx_to_workers ring */ + burst_size = rte_ring_dequeue_burst(ring_in, + (void *)burst_buffer, MAX_PKTS_BURST); + if (unlikely(burst_size == 0)) + continue; + + __sync_fetch_and_add(&app_stats.wkr.dequeue_pkts, burst_size); + + /* just do some operation on mbuf */ + for (i = 0; i < burst_size;) + burst_buffer[i++]->port ^= xor_val; + + /* enqueue the modified mbufs to workers_to_tx ring */ + ret = rte_ring_enqueue_burst(ring_out, (void *)burst_buffer, burst_size); + __sync_fetch_and_add(&app_stats.wkr.enqueue_pkts, ret); + if (unlikely(ret < burst_size)) { + /* Return the mbufs to their respective pool, dropping packets */ + __sync_fetch_and_add(&app_stats.wkr.enqueue_failed_pkts, + (int)burst_size - ret); + pktmbuf_free_bulk(&burst_buffer[ret], burst_size - ret); + } + } + return 0; +} + +static inline void +flush_one_port(struct output_buffer *outbuf, uint8_t outp) +{ + unsigned nb_tx = rte_eth_tx_burst(outp, 0, outbuf->mbufs, + outbuf->count); + app_stats.tx.ro_tx_pkts += nb_tx; + + if (unlikely(nb_tx < outbuf->count)) { + /* free the mbufs which failed from transmit */ + app_stats.tx.ro_tx_failed_pkts += (outbuf->count - nb_tx); + LOG_DEBUG(REORDERAPP, "%s:Packet loss with tx_burst\n", __func__); + pktmbuf_free_bulk(&outbuf->mbufs[nb_tx], outbuf->count - nb_tx); + } + outbuf->count = 0; +} + +/** + * Dequeue mbufs from the workers_to_tx ring and reorder them before + * transmitting. + */ +static int +send_thread(struct rte_ring *ring_in) +{ + int ret; + unsigned int i, dret; + uint16_t nb_dq_mbufs; + uint8_t outp; + static struct output_buffer tx_buffers[RTE_MAX_ETHPORTS]; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; + struct rte_mbuf *rombufs[MAX_PKTS_BURST] = {NULL}; + struct rte_reorder_buffer *buffer; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + buffer = rte_reorder_create("PKT_RO", rte_socket_id(), REORDER_BUFFER_SIZE); + while (!quit_signal) { + + /* deque the mbufs from workers_to_tx ring */ + nb_dq_mbufs = rte_ring_dequeue_burst(ring_in, + (void *)mbufs, MAX_PKTS_BURST); + + if (unlikely(nb_dq_mbufs == 0)) + continue; + + app_stats.tx.dequeue_pkts += nb_dq_mbufs; + + for (i = 0; i < nb_dq_mbufs; i++) { + /* send dequeued mbufs for reordering */ + ret = rte_reorder_insert(buffer, mbufs[i]); + + if (ret == -1 && rte_errno == ERANGE) { + /* Too early pkts should be transmitted out directly */ + LOG_DEBUG(REORDERAPP, "%s():Cannot reorder early packet " + "direct enqueuing to TX\n", __func__); + outp = mbufs[i]->port; + if ((portmask & (1 << outp)) == 0) { + rte_pktmbuf_free(mbufs[i]); + continue; + } + if (rte_eth_tx_burst(outp, 0, (void *)mbufs[i], 1) != 1) { + rte_pktmbuf_free(mbufs[i]); + app_stats.tx.early_pkts_tx_failed_woro++; + } else + app_stats.tx.early_pkts_txtd_woro++; + } else if (ret == -1 && rte_errno == ENOSPC) { + /** + * Early pkts just outside of window should be dropped + */ + rte_pktmbuf_free(mbufs[i]); + } + } + + /* + * drain MAX_PKTS_BURST of reordered + * mbufs for transmit + */ + dret = rte_reorder_drain(buffer, rombufs, MAX_PKTS_BURST); + for (i = 0; i < dret; i++) { + + struct output_buffer *outbuf; + uint8_t outp1; + + outp1 = rombufs[i]->port; + /* skip ports that are not enabled */ + if ((portmask & (1 << outp1)) == 0) { + rte_pktmbuf_free(rombufs[i]); + continue; + } + + outbuf = &tx_buffers[outp1]; + outbuf->mbufs[outbuf->count++] = rombufs[i]; + if (outbuf->count == MAX_PKTS_BURST) + flush_one_port(outbuf, outp1); + } + } + return 0; +} + +/** + * Dequeue mbufs from the workers_to_tx ring and transmit them + */ +static int +tx_thread(struct rte_ring *ring_in) +{ + uint32_t i, dqnum; + uint8_t outp; + static struct output_buffer tx_buffers[RTE_MAX_ETHPORTS]; + struct rte_mbuf *mbufs[MAX_PKTS_BURST]; + struct output_buffer *outbuf; + + RTE_LOG(INFO, REORDERAPP, "%s() started on lcore %u\n", __func__, + rte_lcore_id()); + while (!quit_signal) { + + /* deque the mbufs from workers_to_tx ring */ + dqnum = rte_ring_dequeue_burst(ring_in, + (void *)mbufs, MAX_PKTS_BURST); + + if (unlikely(dqnum == 0)) + continue; + + app_stats.tx.dequeue_pkts += dqnum; + + for (i = 0; i < dqnum; i++) { + outp = mbufs[i]->port; + /* skip ports that are not enabled */ + if ((portmask & (1 << outp)) == 0) { + rte_pktmbuf_free(mbufs[i]); + continue; + } + + outbuf = &tx_buffers[outp]; + outbuf->mbufs[outbuf->count++] = mbufs[i]; + if (outbuf->count == MAX_PKTS_BURST) + flush_one_port(outbuf, outp); + } + } + + return 0; +} + +int +main(int argc, char **argv) +{ + int ret; + unsigned nb_ports; + unsigned int lcore_id, last_lcore_id, master_lcore_id; + uint8_t port_id; + uint8_t nb_ports_available; + struct worker_thread_args worker_args = {NULL, NULL}; + struct rte_ring *rx_to_workers; + struct rte_ring *workers_to_tx; + + /* catch ctrl-c so we can print on exit */ + signal(SIGINT, int_handler); + + /* Initialize EAL */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + return -1; + + argc -= ret; + argv += ret; + + /* Parse the application specific arguments */ + ret = parse_args(argc, argv); + if (ret < 0) + return -1; + + /* Check if we have enought cores */ + if (rte_lcore_count() < 3) + rte_exit(EXIT_FAILURE, "Error, This application needs at " + "least 3 logical cores to run:\n" + "1 lcore for packet RX\n" + "1 lcore for packet TX\n" + "and at least 1 lcore for worker threads\n"); + + nb_ports = rte_eth_dev_count(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "Error: no ethernet ports detected\n"); + if (nb_ports != 1 && (nb_ports & 1)) + rte_exit(EXIT_FAILURE, "Error: number of ports must be even, except " + "when using a single port\n"); + + mbuf_pool = rte_mempool_create("mbuf_pool", MBUF_PER_POOL, MBUF_SIZE, + MBUF_POOL_CACHE_SIZE, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (mbuf_pool == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + nb_ports_available = nb_ports; + + /* initialize all ports */ + for (port_id = 0; port_id < nb_ports; port_id++) { + /* skip ports that are not enabled */ + if ((portmask & (1 << port_id)) == 0) { + printf("\nSkipping disabled port %d\n", port_id); + nb_ports_available--; + continue; + } + /* init port */ + printf("Initializing port %u... done\n", (unsigned) port_id); + + if (configure_eth_port(port_id) != 0) + rte_exit(EXIT_FAILURE, "Cannot initialize port %"PRIu8"\n", + port_id); + } + + if (!nb_ports_available) { + rte_exit(EXIT_FAILURE, + "All available ports are disabled. Please set portmask.\n"); + } + + /* Create rings for inter core communication */ + rx_to_workers = rte_ring_create("rx_to_workers", RING_SIZE, rte_socket_id(), + RING_F_SP_ENQ); + if (rx_to_workers == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + workers_to_tx = rte_ring_create("workers_to_tx", RING_SIZE, rte_socket_id(), + RING_F_SC_DEQ); + if (workers_to_tx == NULL) + rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); + + last_lcore_id = get_last_lcore_id(); + master_lcore_id = rte_get_master_lcore(); + + worker_args.ring_in = rx_to_workers; + worker_args.ring_out = workers_to_tx; + + /* Start worker_thread() on all the available slave cores but the last 1 */ + for (lcore_id = 0; lcore_id <= get_previous_lcore_id(last_lcore_id); lcore_id++) + if (rte_lcore_is_enabled(lcore_id) && lcore_id != master_lcore_id) + rte_eal_remote_launch(worker_thread, (void *)&worker_args, + lcore_id); + + if (disable_reorder) + /* Start tx_thread() on the last slave core */ + rte_eal_remote_launch((lcore_function_t *)tx_thread, workers_to_tx, + last_lcore_id); + else + /* Start send_thread() on the last slave core */ + rte_eal_remote_launch((lcore_function_t *)send_thread, workers_to_tx, + last_lcore_id); + + /* Start rx_thread() on the master core */ + rx_thread(rx_to_workers); + + RTE_LCORE_FOREACH_SLAVE(lcore_id) { + if (rte_eal_wait_lcore(lcore_id) < 0) + return -1; + } + + print_stats(); + return 0; +} -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 4/6] doc: new reorder library description 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy ` (2 preceding siblings ...) 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 3/6] examples: new sample app packet_ordering Sergio Gonzalez Monroy @ 2015-02-18 14:58 ` Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 5/6] doc: new packet ordering app description Sergio Gonzalez Monroy ` (2 subsequent siblings) 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-18 14:58 UTC (permalink / raw) To: dev This patch introduces a new section in the programmers guide describing the reorder library. Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/reorder_lib.rst | 115 ++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 doc/guides/prog_guide/reorder_lib.rst diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 8d86dd4..de69682 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -61,6 +61,7 @@ Programmer's Guide lpm_lib lpm6_lib packet_distrib_lib + reorder_lib ip_fragment_reassembly_lib multi_proc_support kernel_nic_interface diff --git a/doc/guides/prog_guide/reorder_lib.rst b/doc/guides/prog_guide/reorder_lib.rst new file mode 100644 index 0000000..bbd0521 --- /dev/null +++ b/doc/guides/prog_guide/reorder_lib.rst @@ -0,0 +1,115 @@ +.. BSD LICENSE + Copyright(c) 2015 Intel Corporation. All rights reserved. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.. _Reorder_Library: + +Reorder Library +================= + +The Reorder Library provides a mechanism for reordering mbufs based on their +sequence number. + +Operation +---------- + +The reorder library is essentially a buffer that reorders mbufs. +The user inserts out of order mbufs into the reorder buffer and pulls in-order +mbufs from it. + +At a given time, the reorder buffer contains mbufs whose sequence number are +inside the sequence window. The sequence window is determined by the minimum +sequence number and the number of entries that the buffer was configured to hold. +For example, given a reorder buffer with 200 entries and a minimum sequence +number of 350, the sequence window has low and high limits of 350 and 550 +respectively. + +When inserting mbufs, the reorder library differentiates between valid, early +and late mbufs depending on the sequence number of the inserted mbuf: + +* valid: the sequence number is inside the window. +* late: the sequence number is outside the window and less than the low limit. +* early: the sequence number is outside the window and greater than the high + limit. + +The reorder buffer directly returns late mbufs and tries to accommodate early +mbufs. + + +Implementation Details +------------------------- + +The reorder library is implemented as a pair of buffers, which referred to as +the *Order* buffer and the *Ready* buffer. + +On an insert call, valid mbufs are inserted directly into the Order buffer and +late mbufs are returned to the user with an error. + +In the case of early mbufs, the reorder buffer will try to move the window +(incrementing the minimum sequence number) so that the mbuf becomes a valid one. +To that end, mbufs in the Order buffer are moved into the Ready buffer. +Any mbufs that have not arrived yet are ignored and therefore will become +late mbufs. +This means that as long as there is room in the Ready buffer, the window will +be moved to accommodate early mbufs that would otherwise be outside the +reordering window. + +For example, assuming that we have a buffer of 200 entries with a 350 minimum +sequence number, and we need to insert an early mbuf with 565 sequence number. +That means that we would need to move the windows at least 15 positions to +accommodate the mbuf. +The reorder buffer would try to move mbufs from at least the next 15 slots in +the Order buffer to the Ready buffer, as long as there is room in the Ready buffer. +Any gaps in the Order buffer at that point are skipped, and those packet will +be reported as late packets when they arrive. The process of moving packets +to the Ready buffer continues beyond the minimum required until a gap, +i.e. missing mbuf, in the Order buffer is encountered. + +When draining mbufs, the reorder buffer would return mbufs in the Ready +buffer first and then from the Order buffer until a gap is found (mbufs that +have not arrived yet). + +Use Case: Packet Distributor +------------------------------- + +An application using the DPDK packet distributor could make use of the reorder +library to transmit packets in the same order they were received. + +A basic packet distributor use case would consist of a distributor with +multiple workers cores. +The processing of packets by the workers is not guaranteed to be in order, +hence a reorder buffer can be used to order as many packets as possible. + +In such a scenario, the distributor assigns a sequence number to mbufs before +delivering them to the workers. +As the workers finish processing the packets, the distributor inserts those +mbufs into the reorder buffer and finally transmit drained mbufs. + +NOTE: Currently the reorder buffer is not thread safe so the same thread is +responsible for inserting and draining mbufs. -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 5/6] doc: new packet ordering app description 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy ` (3 preceding siblings ...) 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 4/6] doc: new reorder library description Sergio Gonzalez Monroy @ 2015-02-18 14:58 ` Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 6/6] MAINTAINERS: add and claim reorder Sergio Gonzalez Monroy 2015-02-18 15:52 ` [dpdk-dev] [PATCH v5 0/6] New Reorder Library Thomas Monjalon 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-18 14:58 UTC (permalink / raw) To: dev This patch describes how to build and run he new packet ordering sample application that exercises the reorder library. Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- doc/guides/sample_app_ug/index.rst | 1 + doc/guides/sample_app_ug/packet_ordering.rst | 102 +++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 doc/guides/sample_app_ug/packet_ordering.rst diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst index d07dec3..5720181 100644 --- a/doc/guides/sample_app_ug/index.rst +++ b/doc/guides/sample_app_ug/index.rst @@ -60,6 +60,7 @@ Sample Applications User Guide intel_quickassist quota_watermark timer + packet_ordering vmdq_dcb_forwarding vhost netmap_compatibility diff --git a/doc/guides/sample_app_ug/packet_ordering.rst b/doc/guides/sample_app_ug/packet_ordering.rst new file mode 100644 index 0000000..481f1b7 --- /dev/null +++ b/doc/guides/sample_app_ug/packet_ordering.rst @@ -0,0 +1,102 @@ +.. BSD LICENSE + Copyright(c) 2015 Intel Corporation. All rights reserved. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Packet Ordering Application +============================ + +The Packet Ordering sample app simply shows the impact of reordering a stream. +It's meant to stress the library with different configurations for performance. + +Overview +-------- + +The application uses at least three CPU cores: + +* RX core (maser core) receives traffic from the NIC ports and feeds Worker + cores with traffic through SW queues. + +* Worker core (slave core) basically do some light work on the packet. + Currently it modifies the output port of the packet for configurations with + more than one port enabled. + +* TX Core (slave core) receives traffic from Woker cores through software queues, + inserts out-of-order packets into reorder buffer, extracts ordered packets + from the reorder buffer and sends them to the NIC ports for transmission. + +Compiling the Application +-------------------------- + +#. Go to the example directory: + + .. code-block:: console + + export RTE_SDK=/path/to/rte_sdk + cd ${RTE_SDK}/examples/helloworld + +#. Set the target (a default target is used if not specified). For example: + + .. code-block:: console + + export RTE_TARGET=x86_64-native-linuxapp-gcc + + See the *DPDK Getting Started* Guide for possible RTE_TARGET values. + +#. Build the application: + + .. code-block:: console + + make + +Running the Application +----------------------- + +Refer to *DPDK Getting Started Guide* for general information on running applications +and the Environment Abstraction Layer (EAL) options. + +Application Command Line +~~~~~~~~~~~~~~~~~~~~~~~~ + +The application execution command line is: + +.. code-block:: console + + ./test-pipeline [EAL options] -- -p PORTMASK [--disable-reorder] + +The -c EAL CPU_COREMASK option has to contain at least 3 CPU cores. +The first CPU core in the core mask is the master core and would be assigned to +RX core, the last to TX core and the rest to Worker cores. + +The PORTMASK parameter must contain either 1 or even enabled port numbers. +When setting more than 1 port, traffic would be forwarderd in pairs. +For example, if we enable 4 ports, traffic from port 0 to 1 and from 1 to 0, +then the other pair from 2 to 3 and from 3 to 2, having [0,1] and [2,3] pairs. + +The disable-reorder long option does, as its name implies, disable the reordering +of traffic, which should help evaluate reordering performance impact. -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 6/6] MAINTAINERS: add and claim reorder 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy ` (4 preceding siblings ...) 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 5/6] doc: new packet ordering app description Sergio Gonzalez Monroy @ 2015-02-18 14:58 ` Sergio Gonzalez Monroy 2015-02-18 15:52 ` [dpdk-dev] [PATCH v5 0/6] New Reorder Library Thomas Monjalon 6 siblings, 0 replies; 44+ messages in thread From: Sergio Gonzalez Monroy @ 2015-02-18 14:58 UTC (permalink / raw) To: dev Add files related to reorder library and claim it. Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e7a425b..d7d672c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -264,6 +264,14 @@ F: app/test/test_distributor* F: examples/distributor/ F: doc/guides/sample_app_ug/dist_app.rst +Reorder +M: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> +F: lib/librte_reorder/ +F: doc/guides/prog_guide/reorder_lib.rst +F: app/test/test_reorder* +F: examples/packet_ordering/ +F: doc/guides/sample_app_ug/packet_ordering.rst + Hierarchical scheduler M: Cristian Dumitrescu <cristian.dumitrescu@intel.com> F: lib/librte_sched/ -- 1.9.3 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v5 0/6] New Reorder Library 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy ` (5 preceding siblings ...) 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 6/6] MAINTAINERS: add and claim reorder Sergio Gonzalez Monroy @ 2015-02-18 15:52 ` Thomas Monjalon 6 siblings, 0 replies; 44+ messages in thread From: Thomas Monjalon @ 2015-02-18 15:52 UTC (permalink / raw) To: Sergio Gonzalez Monroy; +Cc: dev > This series introduces the new reorder library along with unit tests, > sample app and a new entry in the programmers guide describing the library. > > The library provides reordering of mbufs based on their sequence number. > > As mention in the patch describing the library, one use case is the > packet distributor. > The distributor receives packets, assigns them a sequence number and sends > them to the workers. > The workers process those packets and return them to the distributor. > The distributor collects out-of-order packets from the workers and uses > this library to reorder the packets based on the sequence number they > were assigned. > > v5: > - update MAINTAINERS > > v4: > - add missing version.map and related versioning macros > > v3: > - fix copyright date > - add option to sample app to disable reordering > - add packet ordering sample guide entry > > v2: > - add programmers guide entry describing the library > - use malloc instead of memzone to allocate memory > - modify create and init implementation, init takes a reorder buffer as input > and create reserves memory and call init. > - update unit tests > > Sergio Gonzalez Monroy (6): > reorder: new reorder library > app: New reorder unit test > examples: new sample app packet_ordering > doc: new reorder library description > doc: new packet ordering app description > MAINTAINERS: add and claim reorder Applied, thanks ^ permalink raw reply [flat|nested] 44+ messages in thread
end of thread, other threads:[~2015-02-19 9:50 UTC | newest] Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-01-07 16:39 [dpdk-dev] [PATCH 1/3] librte_reorder: New reorder library Reshma Pattan 2015-01-07 16:39 ` [dpdk-dev] [PATCH 2/3] librte_reorder: New unit test cases added Reshma Pattan 2015-01-07 16:39 ` [dpdk-dev] [PATCH 3/3] librte_reorder: New sample app for reorder library Reshma Pattan 2015-01-07 17:45 ` [dpdk-dev] [PATCH 1/3] librte_reorder: New " Neil Horman 2015-01-08 14:41 ` Pattan, Reshma 2015-01-07 21:09 ` Richard Sanger 2015-01-08 16:28 ` Pattan, Reshma 2015-01-20 8:00 ` Thomas Monjalon 2015-01-29 17:35 ` Gonzalez Monroy, Sergio 2015-01-29 20:39 ` Neil Horman 2015-01-30 9:35 ` Gonzalez Monroy, Sergio 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 0/4] New Reorder Library Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 1/4] reorder: new reorder library Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 2/4] app: New reorder unit test Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 3/4] examples: new sample app packet_ordering Sergio Gonzalez Monroy 2015-01-30 13:14 ` [dpdk-dev] [PATCH v2 4/4] doc: new reorder library description Sergio Gonzalez Monroy 2015-02-06 15:05 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 1/5] reorder: new reorder library Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 2/5] app: New reorder unit test Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 3/5] examples: new sample app packet_ordering Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 4/5] doc: new reorder library description Sergio Gonzalez Monroy 2015-02-06 15:06 ` [dpdk-dev] [PATCH v3 5/5] doc: new packet ordering app description Sergio Gonzalez Monroy 2015-02-08 13:58 ` [dpdk-dev] [PATCH v3 0/5] New Reorder Library Neil Horman 2015-02-11 11:17 ` Gonzalez Monroy, Sergio 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 " Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 1/5] reorder: new reorder library Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 2/5] app: New reorder unit test Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 3/5] examples: new sample app packet_ordering Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 4/5] doc: new reorder library description Sergio Gonzalez Monroy 2015-02-11 13:07 ` [dpdk-dev] [PATCH v4 5/5] doc: new packet ordering app description Sergio Gonzalez Monroy 2015-02-12 5:33 ` [dpdk-dev] [PATCH v4 0/5] New Reorder Library Neil Horman 2015-02-12 12:00 ` Declan Doherty 2015-02-18 14:22 ` Thomas Monjalon 2015-02-18 14:36 ` Gonzalez Monroy, Sergio 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 0/6] " Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 1/6] reorder: new reorder library Sergio Gonzalez Monroy 2015-02-19 9:20 ` Olivier MATZ 2015-02-19 9:50 ` Olivier MATZ 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 2/6] app: New reorder unit test Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 3/6] examples: new sample app packet_ordering Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 4/6] doc: new reorder library description Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 5/6] doc: new packet ordering app description Sergio Gonzalez Monroy 2015-02-18 14:58 ` [dpdk-dev] [PATCH v5 6/6] MAINTAINERS: add and claim reorder Sergio Gonzalez Monroy 2015-02-18 15:52 ` [dpdk-dev] [PATCH v5 0/6] New Reorder Library Thomas Monjalon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).