From 0d2a5bbe17628695fd941c589599cca5365529df Mon Sep 17 00:00:00 2001 From: Jim Harris Date: Tue, 25 Aug 2020 12:20:41 -0700 Subject: [PATCH] util: add spdk_bit_pool spdk_bit_pool is a wrapper around spdk_bit_array with the intentions of providing much better performance for allocating from a fragmented bit array. The cost of searching a large bit array for a cleared bit can become expensive so the spdk_bit_pool will provide an ability to track extents of recently cleared bits. This initial commit does not adding the tracking yet - it is strictly a wrapper around spdk_bit_array with enough functionality to replace the use of spdk_bit_pool in SPDK blobstore with equivalent performance. This will allow us to switch blobstore to use this minimal wrapper first, and then iteratively improve spdk_bit_pool to provide the better performance. Signed-off-by: Jim Harris Change-Id: I95d0d12db47eac73e0641eb7f94fa5df43d42e45 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/3974 Reviewed-by: Ben Walker Reviewed-by: Paul Luse Reviewed-by: Changpeng Liu Reviewed-by: Tomasz Zawadzki Tested-by: SPDK CI Jenkins Community-CI: Broadcom CI --- include/spdk/bit_pool.h | 191 ++++++++++++++++++++++++++++++++++++++++ lib/util/Makefile | 2 +- lib/util/bit_array.c | 158 +++++++++++++++++++++++++++++++++ lib/util/spdk_util.map | 15 ++++ 4 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 include/spdk/bit_pool.h diff --git a/include/spdk/bit_pool.h b/include/spdk/bit_pool.h new file mode 100644 index 000000000..29357959b --- /dev/null +++ b/include/spdk/bit_pool.h @@ -0,0 +1,191 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel 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: + * + * * 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. + */ + +/** \file + * Bit pool data structure + */ + +#ifndef SPDK_BIT_POOL_H +#define SPDK_BIT_POOL_H + +#include "spdk/stdinc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct spdk_bit_pool; +struct spdk_bit_array; + +/** + * Return the number of bits that a bit pool is currently sized to hold. + * + * \param pool Bit pool to query. + * + * \return the number of bits. + */ +uint32_t spdk_bit_pool_capacity(const struct spdk_bit_pool *pool); + +/** + * Create a bit pool. + * + * All bits in the pool will be available for allocation. + * + * \param num_bits Number of bits that the bit pool is sized to hold. + * + * \return a pointer to the new bit pool. + */ +struct spdk_bit_pool *spdk_bit_pool_create(uint32_t num_bits); + +/** + * Create a bit pool from an existing spdk_bit_array. + * + * The starting state of the bit pool will be specified by the state + * of the specified spdk_bit_array. + * + * The new spdk_bit_pool will consume the spdk_bit_array and assumes + * responsibility for freeing it. The caller should not use the + * spdk_bit_array after this function returns. + * + * \param array spdk_bit_array representing the starting state of the new bit pool. + * + * \return a pointer to the new bit pool, NULL if one could not be created (in which + * case the caller maintains responsibility for the spdk_bit_array) + */ +struct spdk_bit_pool *spdk_bit_pool_create_from_array(struct spdk_bit_array *array); + +/** + * Free a bit pool and set the pointer to NULL. + * + * \param pool Bit pool to free. + */ +void spdk_bit_pool_free(struct spdk_bit_pool **pool); + +/** + * Create or resize a bit pool. + * + * To create a new bit pool, pass a pointer to a spdk_bit_pool pointer that is + * NULL. + * + * The bit pool will be sized to hold at least num_bits. + * + * If num_bits is larger than the previous size of the bit pool, + * the new bits will all be available for future allocations. + * + * \param pool Bit pool to create/resize. + * \param num_bits Number of bits that the bit pool is sized to hold. + * + * \return 0 on success, negative errno on failure. + */ +int spdk_bit_pool_resize(struct spdk_bit_pool **pool, uint32_t num_bits); + +/** + * Return whether the specified bit has been allocated from the bit pool. + * + * If bit_index is beyond the end of the current size of the bit pool, this + * function will return false (i.e. bits beyond the end of the pool cannot be allocated). + * + * \param pool Bit pool to query. + * \param bit_index The index of a bit to query. + * + * \return true if the bit has been allocated, false otherwise + */ +bool spdk_bit_pool_is_allocated(const struct spdk_bit_pool *pool, uint32_t bit_index); + +/** + * Allocate a bit from the bit pool. + * + * \param pool Bit pool to allocate a bit from + * + * \return index of the allocated bit, UINT32_MAX if no free bits exist + */ +uint32_t spdk_bit_pool_allocate_bit(struct spdk_bit_pool *pool); + +/** + * Free a bit back to the bit pool. + * + * Callers must not try to free a bit that has not been allocated, otherwise the + * pool may become corrupted without notification. Freeing a bit that has not + * been allocated will result in an assert in debug builds. + * + * \param pool Bit pool to place the freed bit + * \param bit_index The index of a bit to free. + */ +void spdk_bit_pool_free_bit(struct spdk_bit_pool *pool, uint32_t bit_index); + +/** + * Count the number of bits allocated from the pool. + * + * \param pool The bit pool to count. + * + * \return the number of bits allocated from the pool. + */ +uint32_t spdk_bit_pool_count_allocated(const struct spdk_bit_pool *pool); + +/** + * Count the number of free bits in the pool. + * + * \param pool The bit pool to count. + * + * \return the number of free bits in the pool. + */ +uint32_t spdk_bit_pool_count_free(const struct spdk_bit_pool *pool); + +/** + * Store bitmask from bit pool. + * + * \param pool Bit pool. + * \param mask Destination mask. Mask and bit array pool must be equal. + */ +void spdk_bit_pool_store_mask(const struct spdk_bit_pool *pool, void *mask); + +/** + * Load bitmask to bit pool. + * + * \param pool Bit pool. + * \param mask Source mask. Mask and bit array pool must be equal. + */ +void spdk_bit_pool_load_mask(struct spdk_bit_pool *pool, const void *mask); + +/** + * Free all bits back into the bit pool. + * + * \param pool Bit pool. + */ +void spdk_bit_pool_free_all_bits(struct spdk_bit_pool *pool); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/util/Makefile b/lib/util/Makefile index 23f8db6d0..cfacef242 100644 --- a/lib/util/Makefile +++ b/lib/util/Makefile @@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk SO_VER := 2 -SO_MINOR := 0 +SO_MINOR := 1 C_SRCS = base64.c bit_array.c cpuset.c crc16.c crc32.c crc32c.c crc32_ieee.c \ dif.c fd.c file.c iov.c math.c pipe.c strerror_tls.c string.c uuid.c diff --git a/lib/util/bit_array.c b/lib/util/bit_array.c index 43c1a4d9b..c85be9d74 100644 --- a/lib/util/bit_array.c +++ b/lib/util/bit_array.c @@ -34,6 +34,7 @@ #include "spdk/stdinc.h" #include "spdk/bit_array.h" +#include "spdk/bit_pool.h" #include "spdk/env.h" #include "spdk/likely.h" @@ -361,3 +362,160 @@ spdk_bit_array_clear_mask(struct spdk_bit_array *ba) spdk_bit_array_clear(ba, i + size * CHAR_BIT); } } + +struct spdk_bit_pool { + struct spdk_bit_array *array; + uint32_t lowest_free_bit; + uint32_t free_count; +}; + +struct spdk_bit_pool * +spdk_bit_pool_create(uint32_t num_bits) +{ + struct spdk_bit_pool *pool = NULL; + struct spdk_bit_array *array; + + array = spdk_bit_array_create(num_bits); + if (array == NULL) { + return NULL; + } + + pool = calloc(1, sizeof(*pool)); + if (pool == NULL) { + spdk_bit_array_free(&array); + return NULL; + } + + pool->array = array; + pool->lowest_free_bit = 0; + pool->free_count = num_bits; + + return pool; +} + +struct spdk_bit_pool * +spdk_bit_pool_create_from_array(struct spdk_bit_array *array) +{ + struct spdk_bit_pool *pool = NULL; + + pool = calloc(1, sizeof(*pool)); + if (pool == NULL) { + return NULL; + } + + pool->array = array; + pool->lowest_free_bit = spdk_bit_array_find_first_clear(array, 0); + pool->free_count = spdk_bit_array_count_clear(array); + + return pool; +} + +void +spdk_bit_pool_free(struct spdk_bit_pool **ppool) +{ + struct spdk_bit_pool *pool; + + if (!ppool) { + return; + } + + pool = *ppool; + *ppool = NULL; + if (pool != NULL) { + spdk_bit_array_free(&pool->array); + free(pool); + } +} + +int +spdk_bit_pool_resize(struct spdk_bit_pool **ppool, uint32_t num_bits) +{ + struct spdk_bit_pool *pool; + int rc; + + assert(ppool != NULL); + + pool = *ppool; + rc = spdk_bit_array_resize(&pool->array, num_bits); + if (rc) { + return rc; + } + + pool->lowest_free_bit = spdk_bit_array_find_first_clear(pool->array, 0); + pool->free_count = spdk_bit_array_count_clear(pool->array); + + return 0; +} + +uint32_t +spdk_bit_pool_capacity(const struct spdk_bit_pool *pool) +{ + return spdk_bit_array_capacity(pool->array); +} + +bool +spdk_bit_pool_is_allocated(const struct spdk_bit_pool *pool, uint32_t bit_index) +{ + return spdk_bit_array_get(pool->array, bit_index); +} + +uint32_t +spdk_bit_pool_allocate_bit(struct spdk_bit_pool *pool) +{ + uint32_t bit_index = pool->lowest_free_bit; + + if (bit_index == UINT32_MAX) { + return UINT32_MAX; + } + + spdk_bit_array_set(pool->array, bit_index); + pool->lowest_free_bit = spdk_bit_array_find_first_clear(pool->array, bit_index); + pool->free_count--; + return bit_index; +} + +void +spdk_bit_pool_free_bit(struct spdk_bit_pool *pool, uint32_t bit_index) +{ + assert(spdk_bit_array_get(pool->array, bit_index) == true); + + spdk_bit_array_clear(pool->array, bit_index); + if (pool->lowest_free_bit > bit_index) { + pool->lowest_free_bit = bit_index; + } + pool->free_count++; +} + +uint32_t +spdk_bit_pool_count_allocated(const struct spdk_bit_pool *pool) +{ + return spdk_bit_array_capacity(pool->array) - pool->free_count; +} + +uint32_t +spdk_bit_pool_count_free(const struct spdk_bit_pool *pool) +{ + return pool->free_count; +} + +void +spdk_bit_pool_store_mask(const struct spdk_bit_pool *pool, void *mask) +{ + spdk_bit_array_store_mask(pool->array, mask); +} + +void +spdk_bit_pool_load_mask(struct spdk_bit_pool *pool, const void *mask) +{ + spdk_bit_array_load_mask(pool->array, mask); + pool->lowest_free_bit = spdk_bit_array_find_first_clear(pool->array, 0); + pool->free_count = spdk_bit_array_count_clear(pool->array); +} + +void +spdk_bit_pool_free_all_bits(struct spdk_bit_pool *pool) +{ + spdk_bit_array_clear_mask(pool->array); + pool->lowest_free_bit = 0; + pool->free_count = spdk_bit_array_capacity(pool->array); +} diff --git a/lib/util/spdk_util.map b/lib/util/spdk_util.map index 07e067faa..118f7511d 100644 --- a/lib/util/spdk_util.map +++ b/lib/util/spdk_util.map @@ -23,6 +23,21 @@ spdk_bit_array_load_mask; spdk_bit_array_clear_mask; + # public functions in bit_pool.h + spdk_bit_pool_capacity; + spdk_bit_pool_create; + spdk_bit_pool_create_from_array; + spdk_bit_pool_free; + spdk_bit_pool_resize; + spdk_bit_pool_is_allocated; + spdk_bit_pool_allocate_bit; + spdk_bit_pool_free_bit; + spdk_bit_pool_count_allocated; + spdk_bit_pool_count_free; + spdk_bit_pool_store_mask; + spdk_bit_pool_load_mask; + spdk_bit_pool_free_all_bits; + # public functions in cpuset.h spdk_cpuset_alloc; spdk_cpuset_free;