You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
7.8 KiB
243 lines
7.8 KiB
/* |
|
* 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); |
|
}
|
|
|