Subject: [PATCH v4 5/8] mem-pool: add lifecycle management functions

From: Jameson Miller <[email-masked]>
Date: Wed, 20 Jun 2018 20:17:30 +0000

Add initialization and discard functions to mem_pool type. As the
memory allocated by mem_pool can now be freed, we also track the large
memory alllocations so they can be freed as well.

If the there are existing mp_blocks in the mem_pool's linked list of
mp_blocks, then the mp_block for a large allocation is inserted
after the head block. This is because only the head mp_block is considered
when searching for availble space. This results in the following
desirable properties:

1) The mp_block allocated for the large request will not be included
in the search for available space in future requests, as the large
mp_block is sized for the specific request and does not contain any
spare space.

2) The head mp_block will not bumped from considation for future
memory requests just because a request for a large chunk of memory
came in.

These changes are in preparation for a future commit that will utilize
creating and discarding memory pool.

Signed-off-by: Jameson Miller <[email-masked]>
---
 mem-pool.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++------------
 mem-pool.h | 10 ++++++++++
 2 files changed, 61 insertions(+), 12 deletions(-)

diff --git a/mem-pool.c b/mem-pool.c
index a5d5eed923..4e544459e9 100644
--- a/mem-pool.c
+++ b/mem-pool.c
@@ -5,7 +5,14 @@
 #include "cache.h"
 #include "mem-pool.h"
 
-static struct mp_block *mem_pool_alloc_block(struct mem_pool *mem_pool, size_t block_alloc)
+#define BLOCK_GROWTH_SIZE 1024*1024 - sizeof(struct mp_block);
+
+/*
+ * Allocate a new mp_block and insert it after the block specified in
+ * `insert_after`. If `insert_after` is NULL, then insert block at the
+ * head of the linked list.
+ */
+static struct mp_block *mem_pool_alloc_block(struct mem_pool *mem_pool, size_t block_alloc, struct mp_block *insert_after)
 {
 	struct mp_block *p;
 	size_t total_alloc = st_add(offsetof(struct mp_block, space), block_alloc);
@@ -13,14 +20,51 @@ static struct mp_block *mem_pool_alloc_block(struct mem_pool *mem_pool, size_t b
 	mem_pool->pool_alloc = st_add(mem_pool->pool_alloc, total_alloc);
 	p = xmalloc(total_alloc);
 
-	p->next_block = mem_pool->mp_block;
 	p->next_free = (char *)p->space;
 	p->end = p->next_free + block_alloc;
-	mem_pool->mp_block = p;
+
+	if (insert_after) {
+		p->next_block = insert_after->next_block;
+		insert_after->next_block = p;
+	} else {
+		p->next_block = mem_pool->mp_block;
+		mem_pool->mp_block = p;
+	}
 
 	return p;
 }
 
+void mem_pool_init(struct mem_pool **mem_pool, size_t initial_size)
+{
+	struct mem_pool *pool;
+
+	if (*mem_pool)
+		return;
+
+	pool = xcalloc(1, sizeof(*pool));
+
+	pool->block_alloc = BLOCK_GROWTH_SIZE;
+
+	if (initial_size > 0)
+		mem_pool_alloc_block(pool, initial_size, NULL);
+
+	*mem_pool = pool;
+}
+
+void mem_pool_discard(struct mem_pool *mem_pool)
+{
+	struct mp_block *block, *block_to_free;
+
+	while ((block = mem_pool->mp_block))
+	{
+		block_to_free = block;
+		block = block->next_block;
+		free(block_to_free);
+	}
+
+	free(mem_pool);
+}
+
 void *mem_pool_alloc(struct mem_pool *mem_pool, size_t len)
 {
 	struct mp_block *p = NULL;
@@ -33,15 +77,10 @@ void *mem_pool_alloc(struct mem_pool *mem_pool, size_t len)
 	if (mem_pool->mp_block &&
 	    mem_pool->mp_block->end - mem_pool->mp_block->next_free >= len)
 		p = mem_pool->mp_block;
-
-	if (!p) {
-		if (len >= (mem_pool->block_alloc / 2)) {
-			mem_pool->pool_alloc += len;
-			return xmalloc(len);
-		}
-
-		p = mem_pool_alloc_block(mem_pool, mem_pool->block_alloc);
-	}
+	else if (len >= (mem_pool->block_alloc / 2))
+		p = mem_pool_alloc_block(mem_pool, len, mem_pool->mp_block);
+	else
+		p = mem_pool_alloc_block(mem_pool, mem_pool->block_alloc, NULL);
 
 	r = p->next_free;
 	p->next_free += len;
diff --git a/mem-pool.h b/mem-pool.h
index 829ad58ecf..f75b3365d5 100644
--- a/mem-pool.h
+++ b/mem-pool.h
@@ -21,6 +21,16 @@ struct mem_pool {
 	size_t pool_alloc;
 };
 
+/*
+ * Initialize mem_pool with specified initial size.
+ */
+void mem_pool_init(struct mem_pool **mem_pool, size_t initial_size);
+
+/*
+ * Discard a memory pool and free all the memory it is responsible for.
+ */
+void mem_pool_discard(struct mem_pool *mem_pool);
+
 /*
  * Alloc memory from the mem_pool.
  */
-- 
2.14.3