Replaced the fixed size object array in the per-lcore local cache with a variable size array, allocated at mempool creation. For faster indexing into the per-lcore array of caches, pre-calculate the size (in bytes) of the per-lcore local cache.
Using a variable size array makes the RTE_MEMPOOL_CACHE_MAX_SIZE build time configuration parameter superfluous, but it was kept for compatibility purposes, as it is often used for the cache_size parameter when creating mempools. Signed-off-by: Morten Brørup <[email protected]> --- Supersedes: patch-169235 ("[v2] mempool: no cache size limit") --- app/test/test_mempool.c | 2 +- doc/guides/rel_notes/release_26_11.rst | 12 ++- lib/mempool/rte_mempool.c | 118 ++++++++++++++----------- lib/mempool/rte_mempool.h | 17 ++-- 4 files changed, 86 insertions(+), 63 deletions(-) diff --git a/app/test/test_mempool.c b/app/test/test_mempool.c index 6ff8746474..8a21b385d9 100644 --- a/app/test/test_mempool.c +++ b/app/test/test_mempool.c @@ -193,7 +193,7 @@ static int test_mempool_creation_with_exceeded_cache_size(void) mp_cov = rte_mempool_create("test_cache_too_big", MEMPOOL_SIZE, MEMPOOL_ELT_SIZE, - RTE_MEMPOOL_CACHE_MAX_SIZE + 32, 0, + MEMPOOL_SIZE + 32, 0, NULL, NULL, my_obj_init, NULL, SOCKET_ID_ANY, 0); diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst index 59b8514360..64f6802e58 100644 --- a/doc/guides/rel_notes/release_26_11.rst +++ b/doc/guides/rel_notes/release_26_11.rst @@ -97,10 +97,20 @@ API Changes Also, make sure to start the actual text at the margin. ======================================================= +* mempool: When creating a mempool, the cache size can be freely specified + (although still not exceed the number of elements), + and is no longer limited by the ``RTE_MEMPOOL_CACHE_MAX_SIZE`` build time configuration parameter. + Although ``RTE_MEMPOOL_CACHE_MAX_SIZE`` has lost its original meaning, + it was kept for compatibility purposes, + as it is often used for the ``cache_size`` parameter when creating mempools. + +* mempool: Updated the ``rte_mempool`` structure as follows: + - Added the ``sizeof_cache_per_lcore`` field, for indexing into the per-lcore local cache. + * mempool: Updated the ``rte_mempool_cache`` structure as follows: - Removed the deprecated and obsolete ``flushthresh`` field. - Removed the ``unused`` field. - - Reduced the size of the ``objs`` array from ``RTE_MEMPOOL_CACHE_MAX_SIZE`` * 2 to ``RTE_MEMPOOL_CACHE_MAX_SIZE``. + - Changed the ``objs`` array from fixed size to variable size. ABI Changes ----------- diff --git a/lib/mempool/rte_mempool.c b/lib/mempool/rte_mempool.c index 211763aced..069aabaa70 100644 --- a/lib/mempool/rte_mempool.c +++ b/lib/mempool/rte_mempool.c @@ -759,20 +759,21 @@ mempool_cache_init(struct rte_mempool_cache *cache, uint32_t size) /* * Create and initialize a cache for objects that are retrieved from and * returned to an underlying mempool. This structure is identical to the - * local_cache[lcore_id] pointed to by the mempool structure. + * local_cache entry pointed to by the mempool structure. */ RTE_EXPORT_SYMBOL(rte_mempool_cache_create) struct rte_mempool_cache * rte_mempool_cache_create(uint32_t size, int socket_id) { struct rte_mempool_cache *cache; + size_t sizeof_cache = sizeof(struct rte_mempool_cache) + size * sizeof(void *); - if (size == 0 || size > RTE_MEMPOOL_CACHE_MAX_SIZE) { + if (size == 0 || sizeof_cache > UINT32_MAX) { rte_errno = EINVAL; return NULL; } - cache = rte_zmalloc_socket("MEMPOOL_CACHE", sizeof(*cache), + cache = rte_zmalloc_socket("MEMPOOL_CACHE", sizeof_cache, RTE_CACHE_LINE_SIZE, socket_id); if (cache == NULL) { RTE_MEMPOOL_LOG(ERR, "Cannot allocate mempool cache."); @@ -811,10 +812,9 @@ rte_mempool_create_empty(const char *name, unsigned n, unsigned elt_size, struct rte_mempool *mp = NULL; struct rte_tailq_entry *te = NULL; const struct rte_memzone *mz = NULL; - size_t mempool_size; + size_t mempool_size, sizeof_cache_per_lcore; unsigned int mz_flags = RTE_MEMZONE_1GB|RTE_MEMZONE_SIZE_HINT_ONLY; struct rte_mempool_objsz objsz; - unsigned lcore_id; int ret; /* compilation-time checks */ @@ -822,6 +822,8 @@ rte_mempool_create_empty(const char *name, unsigned n, unsigned elt_size, RTE_CACHE_LINE_MASK) != 0); RTE_BUILD_BUG_ON((sizeof(struct rte_mempool_cache) & RTE_CACHE_LINE_MASK) != 0); + RTE_BUILD_BUG_ON(offsetof(struct rte_mempool_cache, objs) != + sizeof(struct rte_mempool_cache)); #ifdef RTE_LIBRTE_MEMPOOL_STATS RTE_BUILD_BUG_ON((sizeof(struct rte_mempool_debug_stats) & RTE_CACHE_LINE_MASK) != 0); @@ -838,7 +840,18 @@ rte_mempool_create_empty(const char *name, unsigned n, unsigned elt_size, } /* asked cache too big */ - if (cache_size > RTE_MEMPOOL_CACHE_MAX_SIZE || + sizeof_cache_per_lcore = 0; + if (cache_size != 0) { + sizeof_cache_per_lcore = sizeof(struct rte_mempool_cache); + sizeof_cache_per_lcore += RTE_CACHE_LINE_ROUNDUP(cache_size * sizeof(void *)); + /* + * Add padding, to guard against false sharing-like effects + * on systems with a next-N-lines hardware prefetcher, when + * accessing objects at the end of the cache. + */ + sizeof_cache_per_lcore += RTE_CACHE_GUARD_LINES * RTE_CACHE_LINE_SIZE; + } + if (sizeof_cache_per_lcore > UINT32_MAX || cache_size > n) { rte_errno = EINVAL; return NULL; @@ -890,8 +903,7 @@ rte_mempool_create_empty(const char *name, unsigned n, unsigned elt_size, mempool_size = sizeof(struct rte_mempool); mempool_size += private_data_size; - if (cache_size != 0) - mempool_size += RTE_MAX_LCORE * sizeof(struct rte_mempool_cache); + mempool_size += RTE_MAX_LCORE * sizeof_cache_per_lcore; ret = snprintf(mz_name, sizeof(mz_name), RTE_MEMPOOL_MZ_FORMAT, name); if (ret < 0 || ret >= (int)sizeof(mz_name)) { @@ -918,7 +930,6 @@ rte_mempool_create_empty(const char *name, unsigned n, unsigned elt_size, mp->elt_size = objsz.elt_size; mp->header_size = objsz.header_size; mp->trailer_size = objsz.trailer_size; - /* Size of default caches, zero means disabled. */ mp->cache_size = cache_size; mp->private_data_size = private_data_size; STAILQ_INIT(&mp->elt_list); @@ -942,18 +953,17 @@ rte_mempool_create_empty(const char *name, unsigned n, unsigned elt_size, goto exit_unlock; } - /* - * local_cache pointer is set even if cache_size is zero. - * The local_cache points to just past the private data. - */ - mp->local_cache = (struct rte_mempool_cache *) - RTE_PTR_ADD(mp, sizeof(struct rte_mempool) + private_data_size); - - /* Init all default caches. */ + /* local_cache pointer is only set if per-lcore local cache is present */ if (cache_size != 0) { - for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) - mempool_cache_init(&mp->local_cache[lcore_id], - cache_size); + mp->local_cache = (struct rte_mempool_cache *) + RTE_PTR_ADD(mp, sizeof(struct rte_mempool) + private_data_size); + mp->sizeof_cache_per_lcore = sizeof_cache_per_lcore; + + /* Init all default caches. */ + struct rte_mempool_cache *cache = mp->local_cache; + for (unsigned int lcore_id = 0; lcore_id < RTE_MAX_LCORE; + lcore_id++, cache = RTE_PTR_ADD(cache, sizeof_cache_per_lcore)) + mempool_cache_init(cache, cache_size); } te->data = mp; @@ -1016,16 +1026,17 @@ RTE_EXPORT_SYMBOL(rte_mempool_avail_count) unsigned int rte_mempool_avail_count(const struct rte_mempool *mp) { - unsigned count; - unsigned lcore_id; + unsigned int count; count = rte_mempool_ops_get_count(mp); - if (mp->cache_size == 0) + if (mp->local_cache == NULL) return count; - for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) - count += mp->local_cache[lcore_id].len; + const struct rte_mempool_cache *cache = mp->local_cache; + for (unsigned int lcore_id = 0; lcore_id < RTE_MAX_LCORE; + lcore_id++, cache = RTE_PTR_ADD(cache, mp->sizeof_cache_per_lcore)) + count += cache->len; /* * due to race condition (access to len is not locked), the @@ -1053,11 +1064,11 @@ rte_mempool_stats_reset(struct rte_mempool *mp) #ifdef RTE_LIBRTE_MEMPOOL_STATS memset(&mp->stats, 0, sizeof(mp->stats)); - if (mp->cache_size != 0) { - for (unsigned int lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { - memset(&mp->local_cache[lcore_id].stats, 0, - sizeof(mp->local_cache[lcore_id].stats)); - } + if (mp->local_cache != NULL) { + struct rte_mempool_cache *cache = mp->local_cache; + for (unsigned int lcore_id = 0; lcore_id < RTE_MAX_LCORE; + lcore_id++, cache = RTE_PTR_ADD(cache, mp->sizeof_cache_per_lcore)) + memset(&cache->stats, 0, sizeof(cache->stats)); } RTE_MEMPOOL_LOG(DEBUG, "<%s>@%p: statistics reset", mp->name, mp); @@ -1071,18 +1082,18 @@ rte_mempool_stats_reset(struct rte_mempool *mp) static unsigned rte_mempool_dump_cache(FILE *f, const struct rte_mempool *mp) { - unsigned lcore_id; - unsigned count = 0; - unsigned cache_count; + unsigned int count = 0; fprintf(f, " internal cache infos (hide zero value items):\n"); fprintf(f, " cache_size=%"PRIu32"\n", mp->cache_size); - if (mp->cache_size == 0) + if (mp->local_cache == NULL) return count; - for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { - cache_count = mp->local_cache[lcore_id].len; + const struct rte_mempool_cache *cache = mp->local_cache; + for (unsigned int lcore_id = 0; lcore_id < RTE_MAX_LCORE; + lcore_id++, cache = RTE_PTR_ADD(cache, mp->sizeof_cache_per_lcore)) { + unsigned int cache_count = cache->len; if (cache_count == 0) continue; fprintf(f, " cache_count[%u]=%"PRIu32"\n", @@ -1223,15 +1234,13 @@ mempool_audit_cookies(struct rte_mempool *mp) static void mempool_audit_cache(const struct rte_mempool *mp) { - unsigned lcore_id; - - if (mp->cache_size == 0) + if (mp->local_cache == NULL) return; - for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { - const struct rte_mempool_cache *cache; - cache = &mp->local_cache[lcore_id]; - if (cache->size > RTE_DIM(cache->objs)) { + const struct rte_mempool_cache *cache = mp->local_cache; + for (unsigned int lcore_id = 0; lcore_id < RTE_MAX_LCORE; + lcore_id++, cache = RTE_PTR_ADD(cache, mp->sizeof_cache_per_lcore)) { + if (cache->size > mp->size) { RTE_MEMPOOL_LOG(CRIT, "badness on cache[%u] size", lcore_id); rte_panic("MEMPOOL: invalid cache[%u] size\n", lcore_id); } @@ -1324,13 +1333,15 @@ rte_mempool_dump(FILE *f, struct rte_mempool *mp) sum.get_success_blks += mp->stats[lcore_id].get_success_blks; sum.get_fail_blks += mp->stats[lcore_id].get_fail_blks; } - if (mp->cache_size != 0) { + if (mp->local_cache != NULL) { /* Add the statistics stored in the mempool caches. */ - for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { - sum.put_bulk += mp->local_cache[lcore_id].stats.put_bulk; - sum.put_objs += mp->local_cache[lcore_id].stats.put_objs; - sum.get_success_bulk += mp->local_cache[lcore_id].stats.get_success_bulk; - sum.get_success_objs += mp->local_cache[lcore_id].stats.get_success_objs; + const struct rte_mempool_cache *cache = mp->local_cache; + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; + lcore_id++, cache = RTE_PTR_ADD(cache, mp->sizeof_cache_per_lcore)) { + sum.put_bulk += cache->stats.put_bulk; + sum.put_objs += cache->stats.put_objs; + sum.get_success_bulk += cache->stats.get_success_bulk; + sum.get_success_objs += cache->stats.get_success_objs; } } fprintf(f, " stats:\n"); @@ -1627,10 +1638,11 @@ mempool_info_cb(struct rte_mempool *mp, void *arg) mp->populated_size); cache_count = 0; - if (mp->cache_size > 0) { - int lcore_id; - for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) - cache_count += mp->local_cache[lcore_id].len; + if (mp->local_cache != NULL) { + const struct rte_mempool_cache *cache = mp->local_cache; + for (unsigned int lcore_id = 0; lcore_id < RTE_MAX_LCORE; + lcore_id++, cache = RTE_PTR_ADD(cache, mp->sizeof_cache_per_lcore)) + cache_count += cache->len; } rte_tel_data_add_dict_uint(info->d, "total_cache_count", cache_count); common_count = rte_mempool_ops_get_count(mp); diff --git a/lib/mempool/rte_mempool.h b/lib/mempool/rte_mempool.h index 5a81e53a9f..5fc7cdb79c 100644 --- a/lib/mempool/rte_mempool.h +++ b/lib/mempool/rte_mempool.h @@ -105,8 +105,7 @@ struct __rte_cache_aligned rte_mempool_cache { } stats; /**< Statistics */ #endif /** Cache objects */ - alignas(RTE_CACHE_LINE_SIZE) void *objs[RTE_MEMPOOL_CACHE_MAX_SIZE]; - RTE_CACHE_GUARD; + alignas(RTE_CACHE_LINE_SIZE) void *objs[]; }; /** @@ -265,6 +264,7 @@ struct __rte_cache_aligned rte_mempool { int32_t ops_index; struct rte_mempool_cache *local_cache; /**< Per-lcore local cache */ + uint32_t sizeof_cache_per_lcore; /**< Multiplier for indexing into the local cache. */ uint32_t populated_size; /**< Number of populated objects. */ struct rte_mempool_objhdr_list elt_list; /**< List of objects in pool */ @@ -1050,8 +1050,7 @@ rte_mempool_free(struct rte_mempool *mp); * @param cache_size * If cache_size is non-zero, the rte_mempool library will try to * limit the accesses to the common lockless pool, by maintaining a - * per-lcore object cache. This argument must be lower or equal to - * RTE_MEMPOOL_CACHE_MAX_SIZE and n. + * per-lcore object cache. This argument must be lower or equal to n. * The access to the per-lcore table is of course * faster than the multi-producer/consumer pool. The cache can be * disabled if the cache_size argument is set to 0; it can be useful to @@ -1371,15 +1370,17 @@ rte_mempool_cache_create(uint32_t size, int socket_id) static __rte_always_inline struct rte_mempool_cache * rte_mempool_default_cache(struct rte_mempool *mp, unsigned lcore_id) { - if (unlikely(mp->cache_size == 0)) + if (unlikely(mp->local_cache == NULL)) return NULL; if (unlikely(lcore_id == LCORE_ID_ANY)) return NULL; - rte_mempool_trace_default_cache(mp, lcore_id, - &mp->local_cache[lcore_id]); - return &mp->local_cache[lcore_id]; + struct rte_mempool_cache *cache = (struct rte_mempool_cache *)RTE_PTR_ADD(mp->local_cache, + lcore_id * (size_t)mp->sizeof_cache_per_lcore); + rte_mempool_trace_default_cache(mp, lcore_id, cache); + __rte_assume(cache != NULL); + return cache; } /** -- 2.43.0

