1 /*
2  * Copyright (c) 2013-2021 Joris Vink <joris@coders.se>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include <sys/types.h>
18 #include <sys/mman.h>
19 #include <sys/queue.h>
20 
21 #include <stdint.h>
22 
23 #include "kore.h"
24 
25 #define POOL_MIN_ELEMENTS		16
26 
27 #define POOL_ELEMENT_BUSY		0
28 #define POOL_ELEMENT_FREE		1
29 
30 #if defined(KORE_USE_TASKS)
31 static void		pool_lock(struct kore_pool *);
32 static void		pool_unlock(struct kore_pool *);
33 #endif
34 
35 static void		pool_region_create(struct kore_pool *, size_t);
36 static void		pool_region_destroy(struct kore_pool *);
37 
38 void
kore_pool_init(struct kore_pool * pool,const char * name,size_t len,size_t elm)39 kore_pool_init(struct kore_pool *pool, const char *name,
40     size_t len, size_t elm)
41 {
42 	kore_debug("kore_pool_init(%p, %s, %zu, %zu)", pool, name, len, elm);
43 
44 	if (elm < POOL_MIN_ELEMENTS)
45 		elm = POOL_MIN_ELEMENTS;
46 
47 	if ((pool->name = strdup(name)) == NULL)
48 		fatal("kore_pool_init: strdup %s", errno_s);
49 
50 	pool->lock = 0;
51 	pool->elms = 0;
52 	pool->inuse = 0;
53 	pool->elen = len;
54 	pool->growth = elm * 0.25f;
55 	pool->slen = pool->elen + sizeof(struct kore_pool_entry);
56 
57 	LIST_INIT(&(pool->regions));
58 	LIST_INIT(&(pool->freelist));
59 
60 	pool_region_create(pool, elm);
61 }
62 
63 void
kore_pool_cleanup(struct kore_pool * pool)64 kore_pool_cleanup(struct kore_pool *pool)
65 {
66 	pool->lock = 0;
67 	pool->elms = 0;
68 	pool->inuse = 0;
69 	pool->elen = 0;
70 	pool->slen = 0;
71 
72 	free(pool->name);
73 	pool->name = NULL;
74 
75 	pool_region_destroy(pool);
76 }
77 
78 void *
kore_pool_get(struct kore_pool * pool)79 kore_pool_get(struct kore_pool *pool)
80 {
81 	u_int8_t			*ptr;
82 	struct kore_pool_entry		*entry;
83 
84 #if defined(KORE_USE_TASKS)
85 	pool_lock(pool);
86 #endif
87 
88 	if (LIST_EMPTY(&(pool->freelist)))
89 		pool_region_create(pool, pool->growth);
90 
91 	entry = LIST_FIRST(&(pool->freelist));
92 	if (entry->state != POOL_ELEMENT_FREE)
93 		fatal("%s: element %p was not free", pool->name, entry);
94 	LIST_REMOVE(entry, list);
95 
96 	entry->state = POOL_ELEMENT_BUSY;
97 	ptr = (u_int8_t *)entry + sizeof(struct kore_pool_entry);
98 
99 	pool->inuse++;
100 
101 #if defined(KORE_USE_TASKS)
102 	pool_unlock(pool);
103 #endif
104 
105 	return (ptr);
106 }
107 
108 void
kore_pool_put(struct kore_pool * pool,void * ptr)109 kore_pool_put(struct kore_pool *pool, void *ptr)
110 {
111 	struct kore_pool_entry		*entry;
112 
113 #if defined(KORE_USE_TASKS)
114 	pool_lock(pool);
115 #endif
116 
117 	entry = (struct kore_pool_entry *)
118 	    ((u_int8_t *)ptr - sizeof(struct kore_pool_entry));
119 
120 	if (entry->state != POOL_ELEMENT_BUSY)
121 		fatal("%s: element %p was not busy", pool->name, ptr);
122 
123 	entry->state = POOL_ELEMENT_FREE;
124 	LIST_INSERT_HEAD(&(pool->freelist), entry, list);
125 
126 	pool->inuse--;
127 
128 #if defined(KORE_USE_TASKS)
129 	pool_unlock(pool);
130 #endif
131 }
132 
133 static void
pool_region_create(struct kore_pool * pool,size_t elms)134 pool_region_create(struct kore_pool *pool, size_t elms)
135 {
136 	size_t				i;
137 	u_int8_t			*p;
138 	struct kore_pool_region		*reg;
139 	struct kore_pool_entry		*entry;
140 
141 	kore_debug("pool_region_create(%p, %zu)", pool, elms);
142 
143 	if ((reg = calloc(1, sizeof(struct kore_pool_region))) == NULL)
144 		fatal("pool_region_create: calloc: %s", errno_s);
145 
146 	LIST_INSERT_HEAD(&(pool->regions), reg, list);
147 
148 	if (SIZE_MAX / elms < pool->slen)
149 		fatal("pool_region_create: overflow");
150 
151 	reg->length = elms * pool->slen;
152 	reg->start = mmap(NULL, reg->length, PROT_READ | PROT_WRITE,
153 	    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
154 	if (reg->start == MAP_FAILED)
155 		fatal("mmap: %s", errno_s);
156 
157 	p = (u_int8_t *)reg->start;
158 
159 	for (i = 0; i < elms; i++) {
160 		entry = (struct kore_pool_entry *)p;
161 		entry->region = reg;
162 		entry->state = POOL_ELEMENT_FREE;
163 		LIST_INSERT_HEAD(&(pool->freelist), entry, list);
164 
165 		p = p + pool->slen;
166 	}
167 
168 	pool->elms += elms;
169 }
170 
171 static void
pool_region_destroy(struct kore_pool * pool)172 pool_region_destroy(struct kore_pool *pool)
173 {
174 	struct kore_pool_region		*reg;
175 
176 	kore_debug("pool_region_destroy(%p)", pool);
177 
178 	/* Take care iterating when modifying list contents */
179 	while (!LIST_EMPTY(&pool->regions)) {
180 		reg = LIST_FIRST(&pool->regions);
181 		LIST_REMOVE(reg, list);
182 		(void)munmap(reg->start, reg->length);
183 		free(reg);
184 	}
185 
186 	/* Freelist references into the regions memory allocations */
187 	LIST_INIT(&pool->freelist);
188 	pool->elms = 0;
189 }
190 
191 #if defined(KORE_USE_TASKS)
192 static void
pool_lock(struct kore_pool * pool)193 pool_lock(struct kore_pool *pool)
194 {
195 	for (;;) {
196 		if (__sync_bool_compare_and_swap(&pool->lock, 0, 1))
197 			break;
198 	}
199 }
200 
201 static void
pool_unlock(struct kore_pool * pool)202 pool_unlock(struct kore_pool *pool)
203 {
204 	if (!__sync_bool_compare_and_swap(&pool->lock, 1, 0))
205 		fatal("pool_unlock: failed to release %s", pool->name);
206 }
207 #endif
208