suricata
util-pool.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2010 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \defgroup utilpool Pool
20  *
21  * ::Pool are an effective way to maintain a set of ready to use
22  * structures.
23  *
24  * To create a ::Pool, you need to use PoolInit(). You can
25  * get an item from the ::Pool by using PoolGet(). When you're
26  * done with it call PoolReturn().
27  * To destroy the ::Pool, call PoolFree(), it will free all used
28  * memory.
29  *
30  * @{
31  */
32 
33 /**
34  * \file
35  *
36  * \author Victor Julien <victor@inliniac.net>
37  *
38  * Pool utility functions
39  */
40 
41 #include "suricata-common.h"
42 #include "util-pool.h"
43 #include "util-pool-thread.h"
44 #include "util-debug.h"
45 #include "util-validate.h"
46 
47 /**
48  * \brief Check if data is preallocated
49  * \note make sure to call with nonnull pointers
50  * \retval false if not inside the prealloc'd block, true if inside */
51 static bool PoolDataPreAllocated(Pool *p, void *data)
52 {
53  ptrdiff_t delta = data - p->data_buffer;
54  return delta >= 0 && delta <= p->data_buffer_size;
55 }
56 
57 static bool PoolInitData(const Pool *p, void *data)
58 {
59  if (p->Init != NULL) {
60  return p->Init(data) == 1;
61  }
62  memset(data, 0, p->elt_size);
63  return true;
64 }
65 
66 /** \brief Init a Pool
67  *
68  * PoolInit() creates a ::Pool. The Alloc function must only do
69  * allocation stuff. The Cleanup function must not try to free
70  * the PoolBucket::data. This is done by the ::Pool management
71  * system.
72  *
73  * \param size
74  * \param prealloc_size
75  * \param elt_size Memory size of an element
76  * \param Alloc An allocation function or NULL to use a standard SCMalloc
77  * \param Init An init function or NULL to use a standard memset to 0
78  * \param Cleanup a free function or NULL if no special treatment is needed
79  * \retval the allocated Pool
80  */
81 Pool *PoolInit(const uint32_t size, const uint32_t prealloc_size, const uint32_t elt_size,
82  void *(*Alloc)(void), int (*Init)(void *), void (*Cleanup)(void *))
83 {
84  sc_errno = SC_OK;
85 
86  Pool *p = NULL;
87 
88  if (size != 0 && prealloc_size > size) {
90  goto error;
91  }
92  if (size != 0 && elt_size == 0) {
94  goto error;
95  }
96  if (elt_size == 0 && Alloc == NULL) {
98  goto error;
99  }
100 
101  /* setup the filter */
102  p = SCCalloc(1, sizeof(Pool));
103  if (unlikely(p == NULL)) {
105  goto error;
106  }
107 
108  p->max_buckets = size;
109  p->preallocated = prealloc_size;
110  p->elt_size = elt_size;
111  p->data_buffer_size = prealloc_size * elt_size;
112  p->Alloc = Alloc;
113  p->Init = Init;
114  p->Cleanup = Cleanup;
115 
116  /* alloc the buckets and place them in the empty list */
117  if (size > 0) {
118  p->data_buffer = SCCalloc(prealloc_size, elt_size);
119  if (p->data_buffer == NULL) {
121  goto error;
122  }
123 
124  PoolBucket *pb = SCCalloc(size, sizeof(PoolBucket));
125  if (unlikely(pb == NULL)) {
127  goto error;
128  }
129  p->pb_buffer = pb;
130  PoolBucket *b = pb;
131 
132  for (uint32_t i = 0; i < size; i++) {
133  /* populate pool */
134  b[i].next = p->empty_stack;
136  p->empty_stack = &b[i];
137  p->empty_stack_size++;
138  }
139  }
140  /* prealloc the buckets and requeue them to the alloc list */
141  for (uint32_t i = 0; i < prealloc_size; i++) {
142  if (size == 0) { /* unlimited */
143  PoolBucket *pb = SCCalloc(1, sizeof(PoolBucket));
144  if (unlikely(pb == NULL)) {
146  goto error;
147  }
148 
149  if (p->Alloc) {
150  pb->data = p->Alloc();
151  } else {
152  pb->data = SCMalloc(p->elt_size);
153  }
154  if (pb->data == NULL) {
155  SCFree(pb);
157  goto error;
158  }
159  if (PoolInitData(p, pb->data) == false) {
160  SCFree(pb->data);
161  SCFree(pb);
163  goto error;
164  }
165  p->allocated++;
166 
167  pb->next = p->alloc_stack;
168  p->alloc_stack = pb;
169  p->alloc_stack_size++;
170  } else {
171  PoolBucket *pb = p->empty_stack;
172  if (pb == NULL) {
174  goto error;
175  }
176 
177  pb->data = (char *)p->data_buffer + i * elt_size;
178  if (PoolInitData(p, pb->data) == false) {
179  pb->data = NULL;
181  goto error;
182  }
183 
184  p->empty_stack = pb->next;
185  p->empty_stack_size--;
186 
187  p->allocated++;
188 
189  pb->next = p->alloc_stack;
190  p->alloc_stack = pb;
191  p->alloc_stack_size++;
192  }
193  }
194 
195  return p;
196 
197 error:
198  if (p != NULL) {
199  PoolFree(p);
200  }
201  return NULL;
202 }
203 
205 {
206  if (p == NULL)
207  return;
208 
209  while (p->alloc_stack != NULL) {
210  PoolBucket *pb = p->alloc_stack;
211  p->alloc_stack = pb->next;
212  if (pb->data != NULL) {
213  if (p->Cleanup)
214  p->Cleanup(pb->data);
215  if (p->data_buffer == NULL || !PoolDataPreAllocated(p, pb->data)) {
216  DEBUG_VALIDATE_BUG_ON(p->data_buffer == pb->data);
217  SCFree(pb->data);
218  }
219  pb->data = NULL;
220  }
221  if (!(pb->flags & POOL_BUCKET_PREALLOCATED)) {
222  SCFree(pb);
223  }
224  }
225 
226  while (p->empty_stack != NULL) {
227  PoolBucket *pb = p->empty_stack;
228  p->empty_stack = pb->next;
229  if (pb->data != NULL) {
230  if (p->Cleanup)
231  p->Cleanup(pb->data);
232  if (p->data_buffer == NULL || !PoolDataPreAllocated(p, pb->data)) {
233  DEBUG_VALIDATE_BUG_ON(p->data_buffer == pb->data);
234  SCFree(pb->data);
235  }
236  pb->data = NULL;
237  }
238  if (!(pb->flags & POOL_BUCKET_PREALLOCATED)) {
239  SCFree(pb);
240  }
241  }
242 
243  if (p->pb_buffer)
244  SCFree(p->pb_buffer);
245  if (p->data_buffer)
246  SCFree(p->data_buffer);
247  SCFree(p);
248 }
249 
250 void *PoolGet(Pool *p)
251 {
252  SCEnter();
253 
254  PoolBucket *pb = p->alloc_stack;
255  if (pb != NULL) {
256  /* pull from the alloc list */
257  p->alloc_stack = pb->next;
258  p->alloc_stack_size--;
259 
260  /* put in the empty list */
261  pb->next = p->empty_stack;
262  p->empty_stack = pb;
263  p->empty_stack_size++;
264  } else {
265  if (p->max_buckets == 0 || p->allocated < p->max_buckets) {
266  void *pitem;
267  SCLogDebug("max_buckets %"PRIu32"", p->max_buckets);
268 
269  if (p->Alloc != NULL) {
270  pitem = p->Alloc();
271  } else {
272  pitem = SCMalloc(p->elt_size);
273  }
274 
275  if (pitem != NULL) {
276  if (PoolInitData(p, pitem) == false) {
277  SCFree(pitem);
278  SCReturnPtr(NULL, "void");
279  }
280 
281  p->allocated++;
282  p->outstanding++;
283 #ifdef DEBUG
284  if (p->outstanding > p->max_outstanding)
285  p->max_outstanding = p->outstanding;
286 #endif
287  }
288 
289  SCReturnPtr(pitem, "void");
290  } else {
291  SCReturnPtr(NULL, "void");
292  }
293  }
294 
295  void *ptr = pb->data;
296  pb->data = NULL;
297  p->outstanding++;
298 #ifdef DEBUG
299  if (p->outstanding > p->max_outstanding)
300  p->max_outstanding = p->outstanding;
301 #endif
302  SCReturnPtr(ptr,"void");
303 }
304 
305 void PoolReturn(Pool *p, void *data)
306 {
307  SCEnter();
308 
309  PoolBucket *pb = p->empty_stack;
310 
311  SCLogDebug("pb %p", pb);
312 
313  if (pb == NULL) {
314  p->allocated--;
315  p->outstanding--;
316  if (data) {
317  if (p->Cleanup != NULL) {
318  p->Cleanup(data);
319  }
320  if (p->data_buffer == NULL || !PoolDataPreAllocated(p, data)) {
321  DEBUG_VALIDATE_BUG_ON(p->data_buffer == data);
322  SCFree(data);
323  }
324  }
325 
326  SCLogDebug("tried to return data %p to the pool %p, but no more "
327  "buckets available. Just freeing the data.", data, p);
328  SCReturn;
329  }
330 
331  /* pull from the alloc list */
332  p->empty_stack = pb->next;
333  p->empty_stack_size--;
334 
335  /* put in the alloc list */
336  pb->next = p->alloc_stack;
337  p->alloc_stack = pb;
338  p->alloc_stack_size++;
339 
340  pb->data = data;
341  p->outstanding--;
342  SCReturn;
343 }
344 
345 /*
346  * ONLY TESTS BELOW THIS COMMENT
347  */
348 
349 #ifdef UNITTESTS
350 static void *PoolTestAlloc(void)
351 {
352  void *ptr = SCMalloc(10);
353  if (unlikely(ptr == NULL))
354  return NULL;
355  return ptr;
356 }
357 
358 static void PoolTestFree(void *ptr)
359 {
360 }
361 
362 static int PoolTestInit01 (void)
363 {
364  Pool *p = PoolInit(10, 5, 10, PoolTestAlloc, NULL, PoolTestFree);
365  FAIL_IF_NOT(p != NULL);
366 
367  PoolFree(p);
368  PASS;
369 }
370 
371 static int PoolTestInit02 (void)
372 {
373  Pool *p = PoolInit(10, 5, 10, PoolTestAlloc, NULL, PoolTestFree);
374  FAIL_IF_NOT(p != NULL);
375 
376  FAIL_IF_NOT(p->alloc_stack != NULL);
377 
378  FAIL_IF_NOT(p->empty_stack != NULL);
379 
380  FAIL_IF_NOT(p->Alloc == PoolTestAlloc);
381 
382  FAIL_IF_NOT(p->Cleanup == PoolTestFree);
383 
384  PoolFree(p);
385  PASS;
386 }
387 
388 static int PoolTestInit03 (void)
389 {
390  Pool *p = PoolInit(10, 5, 10, PoolTestAlloc, NULL, PoolTestFree);
391  FAIL_IF_NOT(p != NULL);
392 
393  void *data = PoolGet(p);
394  FAIL_IF_NOT(data != NULL);
395 
396  FAIL_IF_NOT(p->alloc_stack_size == 4);
397 
398  FAIL_IF_NOT(p->empty_stack_size == 6);
399 
400  PoolFree(p);
401  PASS;
402 }
403 
404 static int PoolTestInit04 (void)
405 {
406  Pool *p = PoolInit(10, 5, strlen("test") + 1, NULL, NULL, PoolTestFree);
407  FAIL_IF_NOT(p != NULL);
408 
409  char *str = PoolGet(p);
410  FAIL_IF_NOT(str != NULL);
411 
412  FAIL_IF_NOT(p->alloc_stack_size == 4);
413 
414  FAIL_IF_NOT(p->empty_stack_size == 6);
415 
416  PoolFree(p);
417  PASS;
418 }
419 
420 static int PoolTestInit05 (void)
421 {
422  Pool *p = PoolInit(10, 5, 10, PoolTestAlloc, NULL, PoolTestFree);
423  FAIL_IF_NOT(p != NULL);
424 
425  void *data = PoolGet(p);
426  FAIL_IF_NOT(data != NULL);
427 
428  FAIL_IF_NOT(p->alloc_stack_size == 4);
429 
430  FAIL_IF_NOT(p->empty_stack_size == 6);
431 
432  PoolReturn(p, data);
433  data = NULL;
434 
435  FAIL_IF_NOT(p->alloc_stack_size == 5);
436 
437  FAIL_IF_NOT(p->empty_stack_size == 5);
438 
439  PoolFree(p);
440  PASS;
441 }
442 
443 static int PoolTestInit06 (void)
444 {
445  Pool *p = PoolInit(1, 0, 10, PoolTestAlloc, NULL, PoolTestFree);
446  FAIL_IF_NOT(p != NULL);
447 
448  FAIL_IF_NOT(p->allocated == 0);
449 
450  void *data = PoolGet(p);
451  FAIL_IF_NOT(data != NULL);
452 
453  FAIL_IF_NOT(p->allocated == 1);
454 
455  void *data2 = PoolGet(p);
456  FAIL_IF_NOT(data2 == NULL);
457 
458  PoolReturn(p,data);
459  data = NULL;
460 
461  FAIL_IF_NOT(p->allocated == 1);
462 
463  FAIL_IF_NOT(p->alloc_stack_size == 1);
464 
465  PoolFree(p);
466  PASS;
467 }
468 
469 /** \test pool with unlimited size */
470 static int PoolTestInit07 (void)
471 {
472  Pool *p = PoolInit(0, 1, 10, PoolTestAlloc, NULL, PoolTestFree);
473  FAIL_IF_NOT(p != NULL);
474 
475  FAIL_IF_NOT(p->max_buckets == 0);
476 
477  FAIL_IF_NOT(p->allocated == 1);
478 
479  void *data = PoolGet(p);
480  FAIL_IF_NOT(data != NULL);
481 
482  FAIL_IF_NOT(p->allocated == 1);
483 
484  void *data2 = PoolGet(p);
485  FAIL_IF_NOT(data2 != NULL);
486 
487  FAIL_IF_NOT(p->allocated == 2);
488 
489  PoolReturn(p,data);
490  data = NULL;
491 
492  FAIL_IF_NOT(p->allocated == 2);
493 
494  FAIL_IF_NOT(p->alloc_stack_size == 1);
495 
496  PoolReturn(p,data2);
497  data2 = NULL;
498 
499  FAIL_IF_NOT(p->allocated == 1);
500 
501  PoolFree(p);
502  PASS;
503 }
504 #endif /* UNITTESTS */
505 
507 {
508 #ifdef UNITTESTS
509  UtRegisterTest("PoolTestInit01", PoolTestInit01);
510  UtRegisterTest("PoolTestInit02", PoolTestInit02);
511  UtRegisterTest("PoolTestInit03", PoolTestInit03);
512  UtRegisterTest("PoolTestInit04", PoolTestInit04);
513  UtRegisterTest("PoolTestInit05", PoolTestInit05);
514  UtRegisterTest("PoolTestInit06", PoolTestInit06);
515  UtRegisterTest("PoolTestInit07", PoolTestInit07);
516 
518 #endif /* UNITTESTS */
519 }
520 
521 
522 /**
523  * @}
524  */
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:101
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
POOL_BUCKET_PREALLOCATED
#define POOL_BUCKET_PREALLOCATED
Definition: util-pool.h:33
util-pool-thread.h
PoolRegisterTests
void PoolRegisterTests(void)
Definition: util-pool.c:506
SC_EINVAL
@ SC_EINVAL
Definition: util-error.h:30
Pool_
Definition: util-pool.h:43
p
Packet * p
Definition: fuzz_dataset.c:30
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
SC_ENOMEM
@ SC_ENOMEM
Definition: util-error.h:29
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
PoolBucket_::next
struct PoolBucket_ * next
Definition: util-pool.h:39
SCEnter
#define SCEnter(...)
Definition: util-debug.h:284
PoolBucket_::data
void * data
Definition: util-pool.h:37
SCReturn
#define SCReturn
Definition: util-debug.h:286
PoolFree
void PoolFree(Pool *p)
Definition: util-pool.c:204
SC_OK
@ SC_OK
Definition: util-error.h:27
SCReturnPtr
#define SCReturnPtr(x, type)
Definition: util-debug.h:300
PoolBucket_
Definition: util-pool.h:36
PoolReturn
void PoolReturn(Pool *p, void *data)
Definition: util-pool.c:305
PoolThreadRegisterTests
void PoolThreadRegisterTests(void)
Definition: util-pool-thread.c:360
suricata-common.h
util-validate.h
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
str
#define str(s)
Definition: suricata-common.h:313
PoolGet
void * PoolGet(Pool *p)
Definition: util-pool.c:250
SCFree
#define SCFree(p)
Definition: util-mem.h:61
sc_errno
thread_local SCError sc_errno
Definition: util-error.c:31
PoolInit
Pool * PoolInit(const uint32_t size, const uint32_t prealloc_size, const uint32_t elt_size, void *(*Alloc)(void), int(*Init)(void *), void(*Cleanup)(void *))
Init a Pool.
Definition: util-pool.c:81
PoolBucket_::flags
uint8_t flags
Definition: util-pool.h:38
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
util-pool.h
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109