/* * Copyright (C) 2017 The Android Open Source Project * * 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 "ufdt_node_pool.h" #include "libufdt_sysdeps.h" #include "ufdt_types.h" /* Define DEBUG_DISABLE_POOL to use dto_malloc and dto_free directly */ /* #define DEBUG_DISABLE_POOL */ #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define UFDT_NODE_POOL_ENTRIES_PER_BLOCK 1024 /* UFDT_NODE_POOL_ENTRY_SIZE must be equal to or larger than sizeof(struct ufdt_node_fdt_node) and sizeof(struct ufdt_node_fdt_prop) */ #define UFDT_NODE_POOL_ENTRY_SIZE \ MAX(sizeof(struct ufdt_node_fdt_node), sizeof(struct ufdt_node_fdt_prop)) /* A block is a header appended UFDT_NODE_POOL_ENTRIES_PER_BLOCK entries */ #define UFDT_NODE_POOL_BLOCK_SIZE \ (sizeof(struct ufdt_node_pool_block_header) + \ UFDT_NODE_POOL_ENTRY_SIZE * UFDT_NODE_POOL_ENTRIES_PER_BLOCK) /* * The data structure: * * pool block block * +--------------+ +--------------------+ +-----------------+ * | *first_block |---->| block_header: | | ... | ... * | ... | | *next_block |---->| |----> * +--------------+ | *first_free_entry |--\ | ... | * | ... | | * +--------------------+ | * | entry_header: |<-/ * | *next |--\ * +--------------------+ | * | ... |<-/ * | |--\ * +--------------------+ | * | ... | v */ struct ufdt_node_pool_entry_header { struct ufdt_node_pool_entry_header *next; }; struct ufdt_node_pool_block_header { struct ufdt_node_pool_block_header *next_block; struct ufdt_node_pool_entry_header *first_free_entry; int alloc_entry_cnt; }; void ufdt_node_pool_construct(struct ufdt_node_pool *pool) { pool->first_block = NULL; pool->last_block_ptr = &pool->first_block; } void ufdt_node_pool_destruct(struct ufdt_node_pool *pool) { int is_leak = 0; struct ufdt_node_pool_block_header *block = pool->first_block; while (block != NULL) { if (block->alloc_entry_cnt != 0) is_leak = 1; struct ufdt_node_pool_block_header *next_block = block->next_block; dto_free(block); block = next_block; } if (is_leak) { dto_error("Some nodes aren't freed before ufdt_node_pool_destruct().\n"); } pool->first_block = NULL; pool->last_block_ptr = NULL; } static struct ufdt_node_pool_block_header *_ufdt_node_pool_create_block() { char *block_buf = (char *)dto_malloc(UFDT_NODE_POOL_BLOCK_SIZE); char *block_buf_start = block_buf + sizeof(struct ufdt_node_pool_block_header); char *block_buf_end = block_buf + UFDT_NODE_POOL_BLOCK_SIZE; struct ufdt_node_pool_block_header *block = (struct ufdt_node_pool_block_header *)block_buf; // Setup all entries to be a one way link list struct ufdt_node_pool_entry_header **next_ptr = &block->first_free_entry; for (char *entry_buf = block_buf_start; entry_buf < block_buf_end; entry_buf += UFDT_NODE_POOL_ENTRY_SIZE) { struct ufdt_node_pool_entry_header *entry = (struct ufdt_node_pool_entry_header *)entry_buf; *next_ptr = entry; next_ptr = &entry->next; } *next_ptr = NULL; block->next_block = NULL; block->alloc_entry_cnt = 0; return block; } static void _ufdt_node_pool_destory_block( struct ufdt_node_pool_block_header *block) { dto_free(block); } static void *_ufdt_node_pool_block_alloc( struct ufdt_node_pool_block_header *block) { // take the first free enrtry struct ufdt_node_pool_entry_header *entry = block->first_free_entry; block->first_free_entry = entry->next; block->alloc_entry_cnt++; return entry; } static void _ufdt_node_pool_block_free(struct ufdt_node_pool_block_header *block, void *node) { // put the node to the first free enrtry struct ufdt_node_pool_entry_header *entry = node; entry->next = block->first_free_entry; block->first_free_entry = entry; block->alloc_entry_cnt--; } static void _ufdt_node_pool_preppend_block( struct ufdt_node_pool *pool, struct ufdt_node_pool_block_header *block) { struct ufdt_node_pool_block_header *origin_first_block = pool->first_block; block->next_block = origin_first_block; pool->first_block = block; if (origin_first_block == NULL) { pool->last_block_ptr = &block->next_block; } } static void _ufdt_node_pool_append_block( struct ufdt_node_pool *pool, struct ufdt_node_pool_block_header *block) { block->next_block = NULL; *pool->last_block_ptr = block; pool->last_block_ptr = &block->next_block; } static void _ufdt_node_pool_remove_block( struct ufdt_node_pool *pool, struct ufdt_node_pool_block_header **block_ptr) { struct ufdt_node_pool_block_header *block = *block_ptr; struct ufdt_node_pool_block_header *next_block = block->next_block; *block_ptr = next_block; if (next_block == NULL) { pool->last_block_ptr = block_ptr; } block->next_block = NULL; } void *ufdt_node_pool_alloc(struct ufdt_node_pool *pool) { #ifdef DEBUG_DISABLE_POOL return dto_malloc(UFDT_NODE_POOL_ENTRY_SIZE); #endif // return dto_malloc(UFDT_NODE_POOL_ENTRY_SIZE); // If there is no free block, create a new one struct ufdt_node_pool_block_header *block = pool->first_block; if (block == NULL || block->first_free_entry == NULL) { block = _ufdt_node_pool_create_block(); _ufdt_node_pool_preppend_block(pool, block); } void *node = _ufdt_node_pool_block_alloc(block); // Move the block to the last if there is no free entry if (block->first_free_entry == NULL && *pool->last_block_ptr != block) { _ufdt_node_pool_remove_block(pool, &pool->first_block); _ufdt_node_pool_append_block(pool, block); } return node; } static struct ufdt_node_pool_block_header **_ufdt_node_pool_search_block( struct ufdt_node_pool *pool, void *node) { struct ufdt_node_pool_block_header **block_ptr = &pool->first_block; while (*block_ptr != NULL) { struct ufdt_node_pool_block_header *block = *block_ptr; const char *block_buf_start = (char *)block + sizeof(struct ufdt_node_pool_block_header); const char *block_buf_end = (char *)block + UFDT_NODE_POOL_BLOCK_SIZE; if ((char *)node >= block_buf_start && (char *)node < block_buf_end) { break; } block_ptr = &block->next_block; } return block_ptr; } void ufdt_node_pool_free(struct ufdt_node_pool *pool, void *node) { #ifdef DEBUG_DISABLE_POOL return dto_free(node); #endif struct ufdt_node_pool_block_header **block_ptr = _ufdt_node_pool_search_block(pool, node); if (*block_ptr == NULL) { dto_error("Given node is not in the pool.\n"); return; } struct ufdt_node_pool_block_header *block = *block_ptr; _ufdt_node_pool_block_free(block, node); _ufdt_node_pool_remove_block(pool, block_ptr); /* Delay free block: free the block only if the block is all freed and there has at least one another free block */ if (block->alloc_entry_cnt == 0 && pool->first_block != NULL && pool->first_block->first_free_entry != NULL) { _ufdt_node_pool_destory_block(block); return; } _ufdt_node_pool_preppend_block(pool, block); }