From 6ee6b65ef03f8a479cccb2ae04e6b156f309f045 Mon Sep 17 00:00:00 2001 From: "Enrico Loparco (eloparco)" Date: Thu, 8 Sep 2022 12:18:09 +0000 Subject: feat(slab): add slab allocator to store hashtables' keys Ref: HICN-777 Signed-off-by: Enrico Loparco (eloparco) Change-Id: Ibbd5c5e73cfd2f6adf757f7248dff8a933515d21 --- lib/src/util/slab.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 lib/src/util/slab.c (limited to 'lib/src/util') diff --git a/lib/src/util/slab.c b/lib/src/util/slab.c new file mode 100644 index 000000000..763dcb444 --- /dev/null +++ b/lib/src/util/slab.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +/* BLOCK LINKED LISTS */ + +static void +block_add_to_head (block_t **head_ptr, block_t *block) +{ + if (head_ptr == NULL || block == NULL) + return; + + block->prev = NULL; + if (*head_ptr != NULL) + { + block->next = *head_ptr; + block->next->prev = block; + } + + *head_ptr = block; +} + +static block_t * +block_remove_from_head (block_t **head_ptr) +{ + if (head_ptr == NULL || *head_ptr == NULL) + return NULL; + + block_t *old_head = *head_ptr; + *head_ptr = old_head->next; + + if (*head_ptr != NULL) + (*head_ptr)->prev = NULL; + return old_head; +} + +static void +block_remove (block_t **head_ptr, block_t *block) +{ + if (*head_ptr == NULL || block == NULL) + return; + + if (block == *head_ptr) + *head_ptr = block->next; + if (block->next != NULL) + block->next->prev = block->prev; + if (block->prev != NULL) + block->prev->next = block->next; +} + +static bool +is_block_list_empty (block_t *block) +{ + return block == NULL; +} + +static void +block_list_free (block_t **head) +{ + if (head == NULL || *head == NULL) + return; + + block_t *curr_block = *head; + while (curr_block != NULL) + { + block_t *next = curr_block->next; + pool_free (curr_block->pool); + free (curr_block); + + curr_block = next; + } + + *head = NULL; +} + +/* BLOCK */ + +static bool +is_block_full (block_t *block) +{ + return pool_get_free_indices_size (block->pool) == 0; +} + +static void +block_set_ptr_in_chunks (block_t *block, size_t chunk_size) +{ + void *chunk = block->pool; + + // Cannot use `pool_foreach()` since it requires to know the type + // while here we use a generic (void *) + for (int i = 0; i < pool_get_alloc_size (block->pool); i++) + { + chunk_hdr_t *hdr = (chunk_hdr_t *) chunk; + hdr->block = block; + + chunk = (uint8_t *) (chunk) + chunk_size; // Move to next chunk + } +} + +static block_t * +block_create (size_t chunk_size, size_t num_chunks) +{ + block_t *block = malloc (sizeof (block_t)); + if (block == NULL) + return NULL; + + block->prev = block->next = NULL; + _pool_init (&block->pool, chunk_size, num_chunks, 0); + block_set_ptr_in_chunks (block, chunk_size); + + return block; +} + +/* SLAB */ + +slab_t * +_slab_create (size_t elt_size, size_t num_elts) +{ + // Initialize slab + slab_t *slab = malloc (sizeof (slab_t)); + if (slab == NULL) + return NULL; + + *slab = (slab_t){ .num_chunks = next_pow2 (num_elts), + .chunk_size = CHUNK_HDRLEN + elt_size, + .full = NULL, + .partial_or_empty = NULL }; + + // Add initial empty block to partial or empty list + block_t *block = block_create (slab->chunk_size, slab->num_chunks); + block_add_to_head (&slab->partial_or_empty, block); + + return slab; +} + +void +slab_free (slab_t *slab) +{ + block_list_free (&slab->full); + block_list_free (&slab->partial_or_empty); + free (slab); +} + +void * +_slab_get (slab_t *slab) +{ + // Create new empty block if none with available chunks + if (is_block_list_empty (slab->partial_or_empty)) + { + block_t *block = block_create (slab->chunk_size, slab->num_chunks); + block_add_to_head (&slab->partial_or_empty, block); + + slab->num_chunks *= 2; // Grow size exponentially + } + + // Get chunck from first block in 'partial_or_empty' list + void *chunk; + _pool_get (&slab->partial_or_empty->pool, &chunk, slab->chunk_size); + + // If the current block (i.e. head of 'partial_or_empty' list) if full, + // move it to the 'full' list + if (is_block_full (slab->partial_or_empty)) + { + block_t *block = block_remove_from_head (&slab->partial_or_empty); + block_add_to_head (&slab->full, block); + } + + return (uint8_t *) chunk + CHUNK_HDRLEN; +} + +void +_slab_put (slab_t *slab, void *chunk) +{ + // Get which block the chunk (that we want to release) belong to + chunk_hdr_t *hdr = chunk_hdr (chunk); + block_t *block = hdr->block; + + // Put chunk back into block + bool is_full = is_block_full (block); + _pool_put (&block->pool, (void *) &hdr, slab->chunk_size); + + // If the block was previously full, move it to 'partial_or_empty' list + if (is_full) + { + block_remove (&slab->full, block); + block_add_to_head (&slab->partial_or_empty, block); + } +} \ No newline at end of file -- cgit 1.2.3-korg