From: Olivier Matz Date: Tue, 9 Jul 2019 15:31:29 +0000 (+0200) Subject: mbuf: support dynamic fields and flags X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=133fe48c925edaf67b838b2ba7ac283befb396b7;p=dpdk.git mbuf: support dynamic fields and flags Signed-off-by: Olivier Matz --- diff --git a/app/test/test_mbuf.c b/app/test/test_mbuf.c index 2a97afe204..2ad2372f89 100644 --- a/app/test/test_mbuf.c +++ b/app/test/test_mbuf.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "test.h" @@ -502,7 +503,6 @@ fail: rte_pktmbuf_free(clone2); return -1; } -#undef GOTO_FAIL /* * test allocation and free of mbufs @@ -1121,6 +1121,81 @@ test_tx_offload(void) return (v1 == v2) ? 0 : -EINVAL; } +static int +test_mbuf_dyn(struct rte_mempool *pktmbuf_pool) +{ + struct rte_mbuf *m = NULL; + int offset, offset2; + int flag, flag2; + + offset = rte_mbuf_dynfield_register("test-dynfield", sizeof(uint8_t), + __alignof__(uint8_t), 0); + if (offset == -1) + GOTO_FAIL("failed to register dynamic field, offset=%d: %s", + offset, strerror(errno)); + + offset2 = rte_mbuf_dynfield_register("test-dynfield", sizeof(uint8_t), + __alignof__(uint8_t), 0); + if (offset2 != offset) + GOTO_FAIL("failed to lookup dynamic field, offset=%d, offset2=%d: %s", + offset, offset2, strerror(errno)); + + offset2 = rte_mbuf_dynfield_register("test-dynfield2", sizeof(uint16_t), + __alignof__(uint16_t), 0); + if (offset2 == -1 || offset2 == offset || (offset & 1)) + GOTO_FAIL("failed to register dynfield field 2, offset=%d, offset2=%d: %s", + offset, offset2, strerror(errno)); + + printf("offset = %d, offset2 = %d\n", offset, offset2); + + offset = rte_mbuf_dynfield_register("test-dynfield-fail", 256, 1, 0); + if (offset != -1) + GOTO_FAIL("dynamic field creation should fail (too big)"); + + offset = rte_mbuf_dynfield_register("test-dynfield-fail", 1, 3, 0); + if (offset != -1) + GOTO_FAIL("dynamic field creation should fail (bad alignment)"); + + flag = rte_mbuf_dynflag_register("test-dynflag"); + if (flag == -1) + GOTO_FAIL("failed to register dynamic field, flag=%d: %s", + flag, strerror(errno)); + + flag2 = rte_mbuf_dynflag_register("test-dynflag"); + if (flag2 != flag) + GOTO_FAIL("failed to lookup dynamic field, flag=%d, flag2=%d: %s", + flag, flag2, strerror(errno)); + + flag2 = rte_mbuf_dynflag_register("test-dynflag2"); + if (flag2 == -1 || flag2 == flag) + GOTO_FAIL("failed to register dynflag field 2, flag=%d, flag2=%d: %s", + flag, flag2, strerror(errno)); + + printf("flag = %d, flag2 = %d\n", flag, flag2); + + /* set, get dynamic field */ + m = rte_pktmbuf_alloc(pktmbuf_pool); + if (m == NULL) + GOTO_FAIL("Cannot allocate mbuf"); + + *RTE_MBUF_DYNFIELD(m, offset, uint8_t *) = 1; + if (*RTE_MBUF_DYNFIELD(m, offset, uint8_t *) != 1) + GOTO_FAIL("failed to read dynamic field"); + *RTE_MBUF_DYNFIELD(m, offset2, uint16_t *) = 1000; + if (*RTE_MBUF_DYNFIELD(m, offset2, uint16_t *) != 1000) + GOTO_FAIL("failed to read dynamic field"); + + /* set a dynamic flag */ + m->ol_flags |= (1ULL << flag); + + rte_pktmbuf_free(m); + return 0; +fail: + rte_pktmbuf_free(m); + return -1; +} +#undef GOTO_FAIL + static int test_mbuf(void) { @@ -1140,6 +1215,12 @@ test_mbuf(void) goto err; } + if (test_mbuf_dyn(pktmbuf_pool) < 0) { + printf("mbuf dynflag test failed\n"); + goto err; + } + return 0; + /* create a specific pktmbuf pool with a priv_size != 0 and no data * room size */ pktmbuf_pool2 = rte_pktmbuf_pool_create("test_pktmbuf_pool2", diff --git a/lib/librte_mbuf/Makefile b/lib/librte_mbuf/Makefile index c8f6d26895..adb3411f13 100644 --- a/lib/librte_mbuf/Makefile +++ b/lib/librte_mbuf/Makefile @@ -17,8 +17,10 @@ LIBABIVER := 5 # all source are stored in SRCS-y SRCS-$(CONFIG_RTE_LIBRTE_MBUF) := rte_mbuf.c rte_mbuf_ptype.c rte_mbuf_pool_ops.c +SRCS-$(CONFIG_RTE_LIBRTE_MBUF) += rte_mbuf_dynfield.c # install includes SYMLINK-$(CONFIG_RTE_LIBRTE_MBUF)-include := rte_mbuf.h rte_mbuf_ptype.h rte_mbuf_pool_ops.h +SYMLINK-$(CONFIG_RTE_LIBRTE_MBUF)-include += rte_mbuf_dynfield.h include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_mbuf/meson.build b/lib/librte_mbuf/meson.build index 6cc11ebb40..dcb5b2d465 100644 --- a/lib/librte_mbuf/meson.build +++ b/lib/librte_mbuf/meson.build @@ -2,8 +2,10 @@ # Copyright(c) 2017 Intel Corporation version = 5 -sources = files('rte_mbuf.c', 'rte_mbuf_ptype.c', 'rte_mbuf_pool_ops.c') -headers = files('rte_mbuf.h', 'rte_mbuf_ptype.h', 'rte_mbuf_pool_ops.h') +sources = files('rte_mbuf.c', 'rte_mbuf_ptype.c', 'rte_mbuf_pool_ops.c', + 'rte_mbuf_dynfield.c') +headers = files('rte_mbuf.h', 'rte_mbuf_ptype.h', 'rte_mbuf_pool_ops.h', + 'rte_mbuf_dynfield.h') deps += ['mempool'] allow_experimental_apis = true diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h index 98225ec80b..e7e8cd796e 100644 --- a/lib/librte_mbuf/rte_mbuf.h +++ b/lib/librte_mbuf/rte_mbuf.h @@ -198,9 +198,12 @@ extern "C" { #define PKT_RX_OUTER_L4_CKSUM_GOOD (1ULL << 22) #define PKT_RX_OUTER_L4_CKSUM_INVALID ((1ULL << 21) | (1ULL << 22)) -/* add new RX flags here */ +/* add new RX flags here, don't forget to update PKT_FIRST_FREE */ -/* add new TX flags here */ +#define PKT_FIRST_FREE (1ULL << 23) +#define PKT_LAST_FREE (1ULL << 39) + +/* add new TX flags here, don't forget to update PKT_LAST_FREE */ /** * Indicate that the metadata field in the mbuf is in use. @@ -672,6 +675,7 @@ struct rte_mbuf { * current device timestamp. */ uint64_t timestamp; + MARKER cacheline0_end; /* second cache line - fields only used in slow path or on TX */ MARKER cacheline1 __rte_cache_min_aligned; @@ -738,6 +742,7 @@ struct rte_mbuf { */ struct rte_mbuf_ext_shared_info *shinfo; + MARKER cacheline1_end; } __rte_cache_aligned; /** diff --git a/lib/librte_mbuf/rte_mbuf_dynfield.c b/lib/librte_mbuf/rte_mbuf_dynfield.c new file mode 100644 index 0000000000..52898536e6 --- /dev/null +++ b/lib/librte_mbuf/rte_mbuf_dynfield.c @@ -0,0 +1,326 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2019 6WIND S.A. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTE_MBUF_DYNFIELD_MZNAME "rte_mbuf_dynfield" + +struct mbuf_dynfield { + TAILQ_ENTRY(mbuf_dynfield) next; + char name[RTE_MBUF_DYNFIELD_NAMESIZE]; + size_t size; + size_t align; + unsigned int flags; + int offset; +}; +TAILQ_HEAD(mbuf_dynfield_list, rte_tailq_entry); + +static struct rte_tailq_elem mbuf_dynfield_tailq = { + .name = "RTE_MBUF_DYNFIELD", +}; +EAL_REGISTER_TAILQ(mbuf_dynfield_tailq); + +struct mbuf_dynflag { + TAILQ_ENTRY(mbuf_dynflag) next; + char name[RTE_MBUF_DYNFLAG_NAMESIZE]; + int bitnum; +}; +TAILQ_HEAD(mbuf_dynflag_list, rte_tailq_entry); + +static struct rte_tailq_elem mbuf_dynflag_tailq = { + .name = "RTE_MBUF_DYNFLAG", +}; +EAL_REGISTER_TAILQ(mbuf_dynflag_tailq); + +struct mbuf_dyn_shm { + /** For each mbuf byte, free_space[i] == 1 if space is free. */ + uint8_t free_space[sizeof(struct rte_mbuf)]; + /** Bitfield of available flags. */ + uint64_t free_flags; +}; +static struct mbuf_dyn_shm *shm; + +/* allocate and initialize the shared memory */ +static int +init_shared_mem(void) +{ + const struct rte_memzone *mz; + uint64_t mask; + size_t i; + + if (rte_eal_process_type() == RTE_PROC_PRIMARY) { + mz = rte_memzone_reserve_aligned(RTE_MBUF_DYNFIELD_MZNAME, + sizeof(struct mbuf_dyn_shm), + SOCKET_ID_ANY, 0, + RTE_CACHE_LINE_SIZE); + } else { + mz = rte_memzone_lookup(RTE_MBUF_DYNFIELD_MZNAME); + } + if (mz == NULL) + return -1; + + shm = mz->addr; + + if (rte_eal_process_type() == RTE_PROC_PRIMARY) { + memset(shm, 0, sizeof(*shm)); + + /* init free_space */ + for (i = 0; i < sizeof(struct rte_mbuf); i++) { + if (i < offsetof(struct rte_mbuf, cacheline0_end)) + continue; + if (i >= offsetof(struct rte_mbuf, cacheline1) && + i < offsetof(struct rte_mbuf, + cacheline1_end)) + continue; + shm->free_space[i] = 1; + } + + /* init free_flags */ + for (mask = PKT_FIRST_FREE; mask <= PKT_LAST_FREE; mask <<= 1) + shm->free_flags |= mask; + } + + return 0; +} + +/* check if this offset can be used */ +static int +check_offset(size_t offset, size_t size, size_t align, unsigned int flags) +{ + size_t i; + + (void)flags; + + if ((offset & (align - 1)) != 0) + return -1; + if (offset + size > sizeof(struct rte_mbuf)) + return -1; + + for (i = 0; i < size; i++) { + if (!shm->free_space[i + offset]) + return -1; + } + + return 0; +} + +/* assume tailq is locked */ +static struct mbuf_dynfield * +mbuf_dynfield_lookup(const char *name) +{ + struct mbuf_dynfield_list *mbuf_dynfield_list; + struct mbuf_dynfield *mbuf_dynfield; + struct rte_tailq_entry *te; + + mbuf_dynfield_list = RTE_TAILQ_CAST( + mbuf_dynfield_tailq.head, mbuf_dynfield_list); + + TAILQ_FOREACH(te, mbuf_dynfield_list, next) { + mbuf_dynfield = (struct mbuf_dynfield *)te->data; + if (strncmp(name, mbuf_dynfield->name, + RTE_MBUF_DYNFIELD_NAMESIZE) == 0) + break; + } + + if (te == NULL) { + rte_errno = ENOENT; + return NULL; + } + + return mbuf_dynfield; +} + +__rte_experimental +int rte_mbuf_dynfield_register(const char *name, size_t size, size_t align, + unsigned int flags) +{ + struct mbuf_dynfield_list *mbuf_dynfield_list; + struct mbuf_dynfield *mbuf_dynfield = NULL; + struct rte_tailq_entry *te = NULL; + int offset, ret; + size_t i; + + if (shm == NULL && init_shared_mem() < 0) + goto fail; + if (size >= sizeof(struct rte_mbuf)) { + rte_errno = EINVAL; + goto fail; + } + if (!rte_is_power_of_2(align)) { + rte_errno = EINVAL; + goto fail; + } + + rte_mcfg_tailq_write_lock(); + + mbuf_dynfield = mbuf_dynfield_lookup(name); + if (mbuf_dynfield != NULL) { + if (mbuf_dynfield->size != size || + mbuf_dynfield->align != align || + mbuf_dynfield->flags != flags) { + rte_errno = EEXIST; + goto fail_unlock; + } + offset = mbuf_dynfield->offset; + goto out_unlock; + } + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) { + rte_errno = EPERM; + goto fail_unlock; + } + + for (offset = offsetof(struct rte_mbuf, cacheline0_end); + offset < (int)sizeof(struct rte_mbuf); + offset++) { + if (check_offset(offset, size, align, flags) == 0) + break; + } + + if (offset == sizeof(struct rte_mbuf)) { + rte_errno = ENOENT; + goto fail_unlock; + } + + mbuf_dynfield_list = RTE_TAILQ_CAST( + mbuf_dynfield_tailq.head, mbuf_dynfield_list); + + te = rte_zmalloc("MBUF_DYNFIELD_TAILQ_ENTRY", sizeof(*te), 0); + if (te == NULL) + goto fail_unlock; + + mbuf_dynfield = rte_zmalloc("mbuf_dynfield", sizeof(*mbuf_dynfield), 0); + if (mbuf_dynfield == NULL) + goto fail_unlock; + + ret = strlcpy(mbuf_dynfield->name, name, sizeof(mbuf_dynfield->name)); + if (ret < 0 || ret >= (int)sizeof(mbuf_dynfield->name)) { + rte_errno = ENAMETOOLONG; + goto fail_unlock; + } + mbuf_dynfield->size = size; + mbuf_dynfield->align = align; + mbuf_dynfield->flags = flags; + mbuf_dynfield->offset = offset; + te->data = mbuf_dynfield; + + TAILQ_INSERT_TAIL(mbuf_dynfield_list, te, next); + + for (i = offset; i < offset + size; i++) + shm->free_space[i] = 0; + +out_unlock: + rte_mcfg_tailq_write_unlock(); + + return offset; + +fail_unlock: + rte_mcfg_tailq_write_unlock(); +fail: + rte_free(mbuf_dynfield); + rte_free(te); + return -1; +} + +/* assume tailq is locked */ +static struct mbuf_dynflag * +mbuf_dynflag_lookup(const char *name) +{ + struct mbuf_dynflag_list *mbuf_dynflag_list; + struct mbuf_dynflag *mbuf_dynflag; + struct rte_tailq_entry *te; + + mbuf_dynflag_list = RTE_TAILQ_CAST( + mbuf_dynflag_tailq.head, mbuf_dynflag_list); + + TAILQ_FOREACH(te, mbuf_dynflag_list, next) { + mbuf_dynflag = (struct mbuf_dynflag *)te->data; + if (strncmp(name, mbuf_dynflag->name, + RTE_MBUF_DYNFLAG_NAMESIZE) == 0) + break; + } + + if (te == NULL) { + rte_errno = ENOENT; + return NULL; + } + + return mbuf_dynflag; +} + +__rte_experimental +int rte_mbuf_dynflag_register(const char *name) +{ + struct mbuf_dynflag_list *mbuf_dynflag_list; + struct mbuf_dynflag *mbuf_dynflag = NULL; + struct rte_tailq_entry *te = NULL; + int bitnum, ret; + + if (shm == NULL && init_shared_mem() < 0) + goto fail; + + rte_mcfg_tailq_write_lock(); + + mbuf_dynflag = mbuf_dynflag_lookup(name); + if (mbuf_dynflag != NULL) { + bitnum = mbuf_dynflag->bitnum; + goto out_unlock; + } + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) { + rte_errno = EPERM; + goto fail_unlock; + } + + if (shm->free_flags == 0) { + rte_errno = ENOENT; + goto fail_unlock; + } + bitnum = rte_bsf64(shm->free_flags); + + mbuf_dynflag_list = RTE_TAILQ_CAST( + mbuf_dynflag_tailq.head, mbuf_dynflag_list); + + te = rte_zmalloc("MBUF_DYNFLAG_TAILQ_ENTRY", sizeof(*te), 0); + if (te == NULL) + goto fail_unlock; + + mbuf_dynflag = rte_zmalloc("mbuf_dynflag", sizeof(*mbuf_dynflag), 0); + if (mbuf_dynflag == NULL) + goto fail_unlock; + + ret = strlcpy(mbuf_dynflag->name, name, sizeof(mbuf_dynflag->name)); + if (ret < 0 || ret >= (int)sizeof(mbuf_dynflag->name)) { + rte_errno = ENAMETOOLONG; + goto fail_unlock; + } + mbuf_dynflag->bitnum = bitnum; + te->data = mbuf_dynflag; + + TAILQ_INSERT_TAIL(mbuf_dynflag_list, te, next); + + shm->free_flags &= ~(1ULL << bitnum); + +out_unlock: + rte_mcfg_tailq_write_unlock(); + + return bitnum; + +fail_unlock: + rte_mcfg_tailq_write_unlock(); +fail: + rte_free(mbuf_dynflag); + rte_free(te); + return -1; +} diff --git a/lib/librte_mbuf/rte_mbuf_dynfield.h b/lib/librte_mbuf/rte_mbuf_dynfield.h new file mode 100644 index 0000000000..05a6fd4e06 --- /dev/null +++ b/lib/librte_mbuf/rte_mbuf_dynfield.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2019 6WIND S.A. + */ + +#ifndef _RTE_MBUF_DYNFIELD_H_ +#define _RTE_MBUF_DYNFIELD_H_ + +/** + * Register space for a dynamic field in the mbuf structure. + * + * For specific features, PMDs, libraries or applications can reserve an + * area in the mbuf, which can be used to store data related to this + * feature. + * + * The typical use case is a PMD that registers space for an offload + * feature, when the application requests to enable this feature. As + * the space in mbuf is limited, the space should only be reserved if it + * is going to be used. It can be done at any moment. + * + * @param name + * A string identifying the dynamic field. + * @param size + * The number of bytes to reserve. + * @param align + * The alignment constraint, which must be a power of 2. + * @param flags + * Reserved for future use. + * @return + * The offset in the mbuf structure, or -1 on error (rte_errno is set). + */ +__rte_experimental +int rte_mbuf_dynfield_register(const char *name, size_t size, size_t align, + unsigned int flags); + +/** + * Register a dynamic flag in the mbuf structure. + * + * For specific features, PMDs, libraries or applications can reserve a + * dynamic flag in the mbuf, which can be used to store data related to + * this feature. + * + * @param name + * A string identifying the dynamic flag. + * @return + * The number of the reserved bit, or -1 on error (rte_errno is set). + */ +__rte_experimental +int rte_mbuf_dynflag_register(const char *name); + +/** + * Helper macro to access to a dynamic field. + */ +#define RTE_MBUF_DYNFIELD(m, offset, type) ((type)((char *)(m) + (offset))) + +/** + * Maximum length of the dynamic field string. + */ +#define RTE_MBUF_DYNFIELD_NAMESIZE 32 + +/** + * Maximum length of the dynamic flag string. + */ +#define RTE_MBUF_DYNFLAG_NAMESIZE 32 + +#endif diff --git a/lib/librte_mbuf/rte_mbuf_version.map b/lib/librte_mbuf/rte_mbuf_version.map index 2662a37bf6..339881bb5e 100644 --- a/lib/librte_mbuf/rte_mbuf_version.map +++ b/lib/librte_mbuf/rte_mbuf_version.map @@ -50,4 +50,6 @@ EXPERIMENTAL { global: rte_mbuf_check; + rte_mbuf_dynfield_register; + rte_mbuf_dynflag_register; } DPDK_18.08;