[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCH v5 02/11] lib/uknetdev: Introduce Netbufs
Adds `struct uk_netbuf`, a structure for describing network packets. It is designed to support zero-copy operation and simple embedding to existing network stacks. Netbufs can be (1) initialized on user-supplied memory regions, intended for network stacks that come with a packet buffer allocator, or (2) uk_alloc-based allocation. This commit introduces the netbuf struct and intialization and allocation functions. Signed-off-by: Simon Kuenzer <simon.kuenzer@xxxxxxxxx> Reviewed-by: Sharan Santhanam <sharan.santhanam@xxxxxxxxx> --- lib/uknetdev/Config.uk | 1 + lib/uknetdev/Makefile.uk | 2 + lib/uknetdev/exportsyms.uk | 4 + lib/uknetdev/include/uk/netbuf.h | 289 +++++++++++++++++++++++++++++++++++++++ lib/uknetdev/netbuf.c | 190 +++++++++++++++++++++++++ 5 files changed, 486 insertions(+) create mode 100644 lib/uknetdev/exportsyms.uk create mode 100644 lib/uknetdev/include/uk/netbuf.h create mode 100644 lib/uknetdev/netbuf.c diff --git a/lib/uknetdev/Config.uk b/lib/uknetdev/Config.uk index b5e11a9..878b822 100644 --- a/lib/uknetdev/Config.uk +++ b/lib/uknetdev/Config.uk @@ -3,3 +3,4 @@ config LIBUKNETDEV default n select LIBNOLIBC if !HAVE_LIBC select LIBUKDEBUG + select LIBUKALLOC diff --git a/lib/uknetdev/Makefile.uk b/lib/uknetdev/Makefile.uk index 34f30be..81afae4 100644 --- a/lib/uknetdev/Makefile.uk +++ b/lib/uknetdev/Makefile.uk @@ -2,3 +2,5 @@ $(eval $(call addlib_s,libuknetdev,$(CONFIG_LIBUKNETDEV))) CINCLUDES-$(CONFIG_LIBUKNETDEV) += -I$(LIBUKNETDEV_BASE)/include CXXINCLUDES-$(CONFIG_LIBUKNETDEV) += -I$(LIBUKNETDEV_BASE)/include + +LIBUKNETDEV_SRCS-y += $(LIBUKNETDEV_BASE)/netbuf.c diff --git a/lib/uknetdev/exportsyms.uk b/lib/uknetdev/exportsyms.uk new file mode 100644 index 0000000..5473bba --- /dev/null +++ b/lib/uknetdev/exportsyms.uk @@ -0,0 +1,4 @@ +uk_netbuf_init_indir +uk_netbuf_alloc_indir +uk_netbuf_alloc_buf +uk_netbuf_prepare_buf diff --git a/lib/uknetdev/include/uk/netbuf.h b/lib/uknetdev/include/uk/netbuf.h new file mode 100644 index 0000000..551793a --- /dev/null +++ b/lib/uknetdev/include/uk/netbuf.h @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +#ifndef __UK_NETBUF__ +#define __UK_NETBUF__ + +#include <sys/types.h> +#include <inttypes.h> +#include <stddef.h> +#include <limits.h> +#include <errno.h> +#include <uk/assert.h> +#include <uk/refcount.h> +#include <uk/alloc.h> +#include <uk/essentials.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct uk_netbuf; + +typedef void (*uk_netbuf_dtor_t)(struct uk_netbuf *); + +/** + * The netbuf structure is used to describe a single contiguous packet buffer. + * The structure can be chained to describe a packet with multiple scattered + * buffers. + * + * NETBUF + * +----------------------+ + * | struct uk_netbuf | + * | | + * +----------------------+ + * + * PRIVATE META DATA + * *priv -> +----------------------+ + * | private meta | + * | data area | + * +----------------------+ + * + * PACKET BUFFER + * *buf -> +----------------------+ \ + * | HEAD ROOM | | + * | ^ ^ | | + * *data -> +---|--------------|---+ | contiguous + * | v v | | buffer + * | PACKET DATA | | area + * | ^ ^ | | + * *data + len -> +---|--------------|---+ | + * | v v | | + * | TAIL ROOM | | + * *buf + buflen -> +----------------------+ / + * + * + * The private data area is intended for glue code that wants to embed stack- + * specific data to the netbuf (e.g., `struct pbuf` for lwIP). This avoids + * separate memory management for stack-specific packet meta-data. libuknetdev + * or device drivers are not inspecting or modifying these data. + * + * The buffer region contains the packet data. `struct uk_netbuf` is pointing + * with `*data` to the first byte of the packet data within the buffer. + * `len` describes the number of bytes used. All packet data has to fit into the + * given buffer. The buffer is described with the `buf` and `buflen` field. + * These two fields should not change during the netbuf life time. + * The available headroom bytes are calculated by subtracting the `*data` + * pointer from `*buf` pointer. The available tailroom bytes are calculated + * with: (*buf + buflen) - (*data + len). + * When more packet data space is required or whenever packet data is scattered + * in memory, netbufs can be chained. + * + * The netbuf structure, private meta data area, and buffer area can be backed + * by independent memory allocations. uk_netbuf_alloc_buf() and + * uk_netbuf_prepare_buf() are placing all these three regions into a single + * allocation. + */ +struct uk_netbuf { + struct uk_netbuf *next; + struct uk_netbuf *prev; + + void *data; /**< Payload start, is part of buf. */ + uint16_t len; /**< Payload length (should be <= buflen). */ + __atomic refcount; /**< Reference counter */ + + void *priv; /**< Reference to user-provided private data */ + + void *buf; /**< Start address of contiguous buffer. */ + size_t buflen; /**< Length of buffer. */ + + uk_netbuf_dtor_t dtor; /**< Destructor callback */ + struct uk_alloc *_a; /**< @internal Allocator for free'ing */ +}; + +/** + * Initializes an external allocated netbuf. + * This netbuf has no data area assigned. It is intended + * that m->buf, m->buflen, and m->data is initialized by the caller. + * m->len is initialized with 0. + * Note: On the last free, only the destructor is called, + * no memory is released by the API. + * @param m + * reference to uk_netbuf that shall be initialized + * @param priv + * Reference to external (meta) data that corresponds to this netbuf. + * @param dtor + * Destructor that is called when netbuf's refcount reaches zero (recommended) + */ +#define uk_netbuf_init(m, priv, dtor) \ + uk_netbuf_init_indir((m), NULL, 0, 0, (priv), (dtor)) + +/** + * Initializes an external allocated netbuf. + * This netbuf will point to an user-provided contiguous buffer area. + * m->len is initialized with 0. + * Note: On the last free, only the destructor is called, + * no memory is released by the API. + * @param m + * reference to uk_netbuf that shall be initialized + * @param buf + * Reference to buffer area that is belonging to this netbuf + * @param buflen + * Size of the buffer area + * @param headroom + * Number of bytes reserved as headroom from buf, `m->data` will point to + * to the first byte in `buf` after the headroom. `headroom` has to be smaller + * or equal to `buflen`. + * Note: Some drivers may require extra headroom space in the first netbuf of + * a chain in order to do a packet transmission. + * @param priv + * Reference to external (meta) data that corresponds to this netbuf. + * @param dtor + * Destructor that is called when netbuf's refcount reaches zero (recommended) + */ +void uk_netbuf_init_indir(struct uk_netbuf *m, + void *buf, size_t buflen, uint16_t headroom, + void *priv, uk_netbuf_dtor_t dtor); + +/** + * Allocates and initializes a netbuf. + * This netbuf has no buffer area assigned. It is intended + * that m->buf, m->buflen, and m->data is initialized by the caller. + * m->len is initialized with 0. + * Note: On the last free, the buffer area is not free'd. It is intended + * to provide a destructor for this operation. + * @param a + * Allocator to use for allocating `struct uk_netbuf` and the + * corresponding private data area. + * @param dtor + * Destructor that is called when netbuf is free'd (recommended) + * @param privlen + * Length for reserved memory to store private data. This memory is free'd + * together with this netbuf. If privlen is 0, either no private data is + * required or external meta data corresponds to this netbuf. m->priv can be + * modified after the allocation. + * @returns + * - (NULL): Allocation failed + * - initialized uk_netbuf + */ +#define uk_netbuf_alloc(a, privlen, dtor) \ + uk_netbuf_alloc_indir((a), NULL, 0, 0, (privlen), (dtor)) + +/** + * Allocate and initialize netbuf to reference to an existing + * contiguous data area + * m->len is initialized with 0. + * It will not be free'd together with this netbuf on the last free call. + * @param a + * Allocator to be used for allocating `struct uk_netbuf` + * On uk_netdev_free() and refcount == 0 the allocation is free'd + * to this allocator. + * @param buf + * Reference to the buffer area that is belonging to this netbuf + * @param buflen + * Size of the buffer area + * @param headroom + * Number of bytes reserved as headroom from buf. + * headroom has to be smaller or equal to buflen. + * Please note that m->data is aligned when reserved headroom is 0. + * In order to keep this property align up the headroom value. + * @param privlen + * Length for reserved memory to store private data. This memory is free'd + * together with this netbuf. If privlen is 0, either no private data is + * required or external meta data corresponds to this netbuf. m->priv can be + * modified after the allocation. + * @param dtor + * Destructor that is called when netbuf is free'd (recommended) + * @returns + * - (NULL): Allocation failed + * - initialized uk_netbuf + */ +struct uk_netbuf *uk_netbuf_alloc_indir(struct uk_alloc *a, + void *buf, size_t buflen, + uint16_t headroom, + size_t privlen, uk_netbuf_dtor_t dtor); + +/** + * Allocate and initialize netbuf with data buffer area. + * m->len is initialized with 0. + * @param a + * Allocator to be used for allocating `struct uk_netbuf` and the + * corresponding buffer area (single allocation). + * On uk_netbuf_free() and refcount == 0 the allocation is free'd + * to this allocator. + * @param buflen + * Size of the buffer area + * @param headroom + * Number of bytes reserved as headroom from the buffer area. + * `headroom` has to be smaller or equal to `buflen`. + * Please note that `m->data` is aligned when `headroom` is 0. + * In order to keep this property when a headroom is used, + * it is recommended to align up the required headroom. + * @param privlen + * Length for reserved memory to store private data. This memory is free'd + * together with this netbuf. If privlen is 0, either no private data is + * required or external meta data corresponds to this netbuf. m->priv can be + * modified after the allocation. + * @param dtor + * Destructor that is called when netbuf is free'd (optional) + * @returns + * - (NULL): Allocation failed + * - initialized uk_netbuf + */ +struct uk_netbuf *uk_netbuf_alloc_buf(struct uk_alloc *a, size_t buflen, + uint16_t headroom, + size_t privlen, uk_netbuf_dtor_t dtor); + +/** + * Initialize netbuf with data buffer on a user given allocated memory area + * m->len is initialized with 0. + * @param mem + * Reference to user provided memory region + * @param buflen + * Size of the data that shall be allocated together with this netbuf + * @param headroom + * Number of bytes reserved as headroom from the buffer area. + * `headroom` has to be smaller or equal to `buflen`. + * Please note that `m->data` is aligned when `headroom` is 0. + * In order to keep this property when a headroom is used, + * it is recommended to align up the required headroom. + * @param privlen + * Length for reserved memory to store private data. This memory will be + * embedded together with this netbuf. If privlen is 0, either no private data + * is required or external meta data corresponds to this netbuf. m->priv can + * be modified after the preparation. + * @param dtor + * Destructor that is called when netbuf's refcount reaches zero (recommended) + * @returns + * - (NULL): given failed + * - initialized uk_netbuf + */ +struct uk_netbuf *uk_netbuf_prepare_buf(void *mem, size_t size, + uint16_t headroom, + size_t privlen, uk_netbuf_dtor_t dtor); + +#ifdef __cplusplus +} +#endif + +#endif /* __UK_NETBUF__ */ diff --git a/lib/uknetdev/netbuf.c b/lib/uknetdev/netbuf.c new file mode 100644 index 0000000..72c349b --- /dev/null +++ b/lib/uknetdev/netbuf.c @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +#include <uk/netbuf.h> +#include <uk/essentials.h> + +/* Used to align netbuf's priv and data areas to `long long` data type */ +#define NETBUF_ADDR_ALIGNMENT (sizeof(long long)) +#define NETBUF_ADDR_ALIGN_UP(x) ALIGN_UP((x), NETBUF_ADDR_ALIGNMENT) + +void uk_netbuf_init_indir(struct uk_netbuf *m, + void *buf, size_t buflen, uint16_t headroom, + void *priv, uk_netbuf_dtor_t dtor) +{ + UK_ASSERT(m); + UK_ASSERT(buf || (buf == NULL && buflen == 0)); + UK_ASSERT(headroom <= buflen); + + m->buf = buf; + m->buflen = buflen; + m->data = (void *) ((uintptr_t) buf + headroom); + m->len = 0; + m->prev = NULL; + m->next = NULL; + + uk_refcount_init(&m->refcount, 1); + + m->priv = priv; + m->dtor = dtor; + m->_a = NULL; +} + +struct uk_netbuf *uk_netbuf_alloc_indir(struct uk_alloc *a, + void *buf, size_t buflen, + uint16_t headroom, + size_t privlen, uk_netbuf_dtor_t dtor) +{ + struct uk_netbuf *m; + + if (privlen) + m = uk_malloc(a, NETBUF_ADDR_ALIGN_UP(sizeof(*m)) + privlen); + else + m = uk_malloc(a, sizeof(*m)); + if (!m) + return NULL; + + uk_netbuf_init_indir(m, + buf, + buflen, + headroom, + privlen > 0 + ? (void *)((uintptr_t) m + + NETBUF_ADDR_ALIGN_UP(sizeof(*m))) + : NULL, + dtor); + + /* Save reference to allocator that is used + * for free'ing this uk_netbuf. + */ + m->_a = a; + + return m; +} + +struct uk_netbuf *uk_netbuf_alloc_buf(struct uk_alloc *a, size_t buflen, + uint16_t headroom, + size_t privlen, uk_netbuf_dtor_t dtor) +{ + struct uk_netbuf *m; + size_t buf_offset = 0; + size_t priv_offset = 0; + size_t headroom_extra = 0; + + UK_ASSERT(buflen > 0); + UK_ASSERT(headroom <= buflen); + + m = uk_malloc(a, NETBUF_ADDR_ALIGN_UP(sizeof(*m)) + + NETBUF_ADDR_ALIGN_UP(privlen) + + buflen); + if (!m) + return NULL; + + /* Place buf right behind `m` or `m->priv` region if privlen > 0. + * In order to keep `m->data - headroom` aligned the padding bytes + * are added to the headroom. + * We can only do this if the given headroom stays within + * uint16_t bounds after the operation. + */ + if (likely(UINT16_MAX - headroom > NETBUF_ADDR_ALIGNMENT)) { + if (privlen == 0) { + priv_offset = 0; + buf_offset = sizeof(*m); + headroom_extra = NETBUF_ADDR_ALIGN_UP(sizeof(*m)) + - sizeof(*m); + } else { + priv_offset = NETBUF_ADDR_ALIGN_UP(sizeof(*m)); + buf_offset = priv_offset + privlen; + headroom_extra = NETBUF_ADDR_ALIGN_UP(privlen) + - privlen; + } + } + + uk_netbuf_init_indir(m, + (void *) m + buf_offset, + buflen + headroom_extra, + headroom + headroom_extra, + privlen > 0 ? ((void *) m + priv_offset) : NULL, + dtor); + + /* Save reference to allocator that is used + * for free'ing this uk_netbuf. + */ + m->_a = a; + + return m; +} + +struct uk_netbuf *uk_netbuf_prepare_buf(void *mem, size_t size, + uint16_t headroom, + size_t privlen, uk_netbuf_dtor_t dtor) +{ + struct uk_netbuf *m; + size_t buf_offset = 0; + size_t priv_offset = 0; + + UK_ASSERT(mem); + if ((NETBUF_ADDR_ALIGN_UP(sizeof(*m)) + + NETBUF_ADDR_ALIGN_UP(privlen) + + headroom) > size) + return NULL; + + /* Place buf right behind `m` or `m->priv` region if privlen > 0. + * In order to keep `m->data - headroom` aligned the padding bytes + * are added to the headroom. + * We can only do this if the given headroom stays within + * uint16_t bounds after the operation. + */ + if (likely(UINT16_MAX - headroom > NETBUF_ADDR_ALIGNMENT)) { + if (privlen == 0) { + priv_offset = 0; + buf_offset = sizeof(*m); + headroom += NETBUF_ADDR_ALIGN_UP(sizeof(*m)) + - sizeof(*m); + } else { + priv_offset = NETBUF_ADDR_ALIGN_UP(sizeof(*m)); + buf_offset = priv_offset + privlen; + headroom += NETBUF_ADDR_ALIGN_UP(privlen) + - privlen; + } + } + + m = (struct uk_netbuf *) mem; + uk_netbuf_init_indir(m, + mem + buf_offset, + size - buf_offset, + headroom, + privlen > 0 ? (mem + priv_offset) : NULL, + dtor); + return m; +} -- 2.7.4 _______________________________________________ Minios-devel mailing list Minios-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/minios-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |