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-unittest.h"
45 #include "util-debug.h"
46 #include "util-validate.h"
47 
48 /**
49  * \brief Check if data is preallocated
50  * \note make sure to call with nonnull pointers
51  * \retval false if not inside the prealloc'd block, true if inside */
52 static bool PoolDataPreAllocated(Pool *p, void *data)
53 {
54  ptrdiff_t delta = data - p->data_buffer;
55  return delta >= 0 && delta <= p->data_buffer_size;
56 }
57 
58 static bool PoolInitData(const Pool *p, void *data)
59 {
60  if (p->Init != NULL) {
61  return p->Init(data) == 1;
62  }
63  memset(data, 0, p->elt_size);
64  return true;
65 }
66 
67 /** \brief Init a Pool
68  *
69  * PoolInit() creates a ::Pool. The Alloc function must only do
70  * allocation stuff. The Cleanup function must not try to free
71  * the PoolBucket::data. This is done by the ::Pool management
72  * system.
73  *
74  * \param size
75  * \param prealloc_size
76  * \param elt_size Memory size of an element
77  * \param Alloc An allocation function or NULL to use a standard SCMalloc
78  * \param Init An init function or NULL to use a standard memset to 0
79  * \param Cleanup a free function or NULL if no special treatment is needed
80  * \retval the allocated Pool
81  */
82 Pool *PoolInit(const uint32_t size, const uint32_t prealloc_size, const uint32_t elt_size,
83  void *(*Alloc)(void), int (*Init)(void *), void (*Cleanup)(void *))
84 {
85  sc_errno = SC_OK;
86 
87  Pool *p = NULL;
88 
89  if (size != 0 && prealloc_size > size) {
91  goto error;
92  }
93  if (size != 0 && elt_size == 0) {
95  goto error;
96  }
97  if (elt_size == 0 && Alloc == NULL) {
99  goto error;
100  }
101 
102  /* setup the filter */
103  p = SCCalloc(1, sizeof(Pool));
104  if (unlikely(p == NULL)) {
106  goto error;
107  }
108 
109  p->max_buckets = size;
110  p->preallocated = prealloc_size;
111  p->elt_size = elt_size;
112  p->data_buffer_size = prealloc_size * elt_size;
113  p->Alloc = Alloc;
114  p->Init = Init;
115  p->Cleanup = Cleanup;
116 
117  /* alloc the buckets and place them in the empty list */
118  if (size > 0) {
119  p->data_buffer = SCCalloc(prealloc_size, elt_size);
120  if (p->data_buffer == NULL) {
122  goto error;
123  }
124 
125  PoolBucket *pb = SCCalloc(size, sizeof(PoolBucket));
126  if (unlikely(pb == NULL)) {
128  goto error;
129  }
130  p->pb_buffer = pb;
131  PoolBucket *b = pb;
132 
133  for (uint32_t i = 0; i < size; i++) {
134  /* populate pool */
135  b[i].next = p->empty_stack;
137  p->empty_stack = &b[i];
138  p->empty_stack_size++;
139  }
140  }
141  /* prealloc the buckets and requeue them to the alloc list */
142  for (uint32_t i = 0; i < prealloc_size; i++) {
143  if (size == 0) { /* unlimited */
144  PoolBucket *pb = SCCalloc(1, sizeof(PoolBucket));
145  if (unlikely(pb == NULL)) {
147  goto error;
148  }
149 
150  if (p->Alloc) {
151  pb->data = p->Alloc();
152  } else {
153  pb->data = SCMalloc(p->elt_size);
154  }
155  if (pb->data == NULL) {
156  SCFree(pb);
158  goto error;
159  }
160  if (PoolInitData(p, pb->data) == false) {
161  SCFree(pb->data);
162  SCFree(pb);
164  goto error;
165  }
166  p->allocated++;
167 
168  pb->next = p->alloc_stack;
169  p->alloc_stack = pb;
170  p->alloc_stack_size++;
171  } else {
172  PoolBucket *pb = p->empty_stack;
173  if (pb == NULL) {
175  goto error;
176  }
177 
178  pb->data = (char *)p->data_buffer + i * elt_size;
179  if (PoolInitData(p, pb->data) == false) {
180  pb->data = NULL;
182  goto error;
183  }
184 
185  p->empty_stack = pb->next;
186  p->empty_stack_size--;
187 
188  p->allocated++;
189 
190  pb->next = p->alloc_stack;
191  p->alloc_stack = pb;
192  p->alloc_stack_size++;
193  }
194  }
195 
196  return p;
197 
198 error:
199  if (p != NULL) {
200  PoolFree(p);
201  }
202  return NULL;
203 }
204 
206 {
207  if (p == NULL)
208  return;
209 
210  while (p->alloc_stack != NULL) {
211  PoolBucket *pb = p->alloc_stack;
212  p->alloc_stack = pb->next;
213  if (pb->data != NULL) {
214  if (p->Cleanup)
215  p->Cleanup(pb->data);
216  if (p->data_buffer == NULL || !PoolDataPreAllocated(p, pb->data)) {
217  DEBUG_VALIDATE_BUG_ON(p->data_buffer == pb->data);
218  SCFree(pb->data);
219  }
220  pb->data = NULL;
221  }
222  if (!(pb->flags & POOL_BUCKET_PREALLOCATED)) {
223  SCFree(pb);
224  }
225  }
226 
227  while (p->empty_stack != NULL) {
228  PoolBucket *pb = p->empty_stack;
229  p->empty_stack = pb->next;
230  if (pb->data != NULL) {
231  if (p->Cleanup)
232  p->Cleanup(pb->data);
233  if (p->data_buffer == NULL || !PoolDataPreAllocated(p, pb->data)) {
234  DEBUG_VALIDATE_BUG_ON(p->data_buffer == pb->data);
235  SCFree(pb->data);
236  }
237  pb->data = NULL;
238  }
239  if (!(pb->flags & POOL_BUCKET_PREALLOCATED)) {
240  SCFree(pb);
241  }
242  }
243 
244  if (p->pb_buffer)
245  SCFree(p->pb_buffer);
246  if (p->data_buffer)
247  SCFree(p->data_buffer);
248  SCFree(p);
249 }
250 
251 void *PoolGet(Pool *p)
252 {
253  SCEnter();
254 
255  PoolBucket *pb = p->alloc_stack;
256  if (pb != NULL) {
257  /* pull from the alloc list */
258  p->alloc_stack = pb->next;
259  p->alloc_stack_size--;
260 
261  /* put in the empty list */
262  pb->next = p->empty_stack;
263  p->empty_stack = pb;
264  p->empty_stack_size++;
265  } else {
266  if (p->max_buckets == 0 || p->allocated < p->max_buckets) {
267  void *pitem;
268  SCLogDebug("max_buckets %"PRIu32"", p->max_buckets);
269 
270  if (p->Alloc != NULL) {
271  pitem = p->Alloc();
272  } else {
273  pitem = SCMalloc(p->elt_size);
274  }
275 
276  if (pitem != NULL) {
277  if (PoolInitData(p, pitem) == false) {
278  SCFree(pitem);
279  SCReturnPtr(NULL, "void");
280  }
281 
282  p->allocated++;
283  p->outstanding++;
284 #ifdef DEBUG
285  if (p->outstanding > p->max_outstanding)
286  p->max_outstanding = p->outstanding;
287 #endif
288  }
289 
290  SCReturnPtr(pitem, "void");
291  } else {
292  SCReturnPtr(NULL, "void");
293  }
294  }
295 
296  void *ptr = pb->data;
297  pb->data = NULL;
298  p->outstanding++;
299 #ifdef DEBUG
300  if (p->outstanding > p->max_outstanding)
301  p->max_outstanding = p->outstanding;
302 #endif
303  SCReturnPtr(ptr,"void");
304 }
305 
306 void PoolReturn(Pool *p, void *data)
307 {
308  SCEnter();
309 
310  PoolBucket *pb = p->empty_stack;
311 
312  SCLogDebug("pb %p", pb);
313 
314  if (pb == NULL) {
315  p->allocated--;
316  p->outstanding--;
317  if (data) {
318  if (p->Cleanup != NULL) {
319  p->Cleanup(data);
320  }
321  if (p->data_buffer == NULL || !PoolDataPreAllocated(p, data)) {
322  DEBUG_VALIDATE_BUG_ON(p->data_buffer == data);
323  SCFree(data);
324  }
325  }
326 
327  SCLogDebug("tried to return data %p to the pool %p, but no more "
328  "buckets available. Just freeing the data.", data, p);
329  SCReturn;
330  }
331 
332  /* pull from the alloc list */
333  p->empty_stack = pb->next;
334  p->empty_stack_size--;
335 
336  /* put in the alloc list */
337  pb->next = p->alloc_stack;
338  p->alloc_stack = pb;
339  p->alloc_stack_size++;
340 
341  pb->data = data;
342  p->outstanding--;
343  SCReturn;
344 }
345 
346 /*
347  * ONLY TESTS BELOW THIS COMMENT
348  */
349 
350 #ifdef UNITTESTS
351 static void *PoolTestAlloc(void)
352 {
353  void *ptr = SCMalloc(10);
354  if (unlikely(ptr == NULL))
355  return NULL;
356  return ptr;
357 }
358 
359 static void PoolTestFree(void *ptr)
360 {
361 }
362 
363 static int PoolTestInit01 (void)
364 {
365  Pool *p = PoolInit(10, 5, 10, PoolTestAlloc, NULL, PoolTestFree);
366  FAIL_IF_NOT(p != NULL);
367 
368  PoolFree(p);
369  PASS;
370 }
371 
372 static int PoolTestInit02 (void)
373 {
374  Pool *p = PoolInit(10, 5, 10, PoolTestAlloc, NULL, PoolTestFree);
375  FAIL_IF_NOT(p != NULL);
376 
377  FAIL_IF_NOT(p->alloc_stack != NULL);
378 
379  FAIL_IF_NOT(p->empty_stack != NULL);
380 
381  FAIL_IF_NOT(p->Alloc == PoolTestAlloc);
382 
383  FAIL_IF_NOT(p->Cleanup == PoolTestFree);
384 
385  PoolFree(p);
386  PASS;
387 }
388 
389 static int PoolTestInit03 (void)
390 {
391  Pool *p = PoolInit(10, 5, 10, PoolTestAlloc, NULL, PoolTestFree);
392  FAIL_IF_NOT(p != NULL);
393 
394  void *data = PoolGet(p);
395  FAIL_IF_NOT(data != NULL);
396 
397  FAIL_IF_NOT(p->alloc_stack_size == 4);
398 
399  FAIL_IF_NOT(p->empty_stack_size == 6);
400 
401  PoolFree(p);
402  PASS;
403 }
404 
405 static int PoolTestInit04 (void)
406 {
407  Pool *p = PoolInit(10, 5, strlen("test") + 1, NULL, NULL, PoolTestFree);
408  FAIL_IF_NOT(p != NULL);
409 
410  char *str = PoolGet(p);
411  FAIL_IF_NOT(str != NULL);
412 
413  FAIL_IF_NOT(p->alloc_stack_size == 4);
414 
415  FAIL_IF_NOT(p->empty_stack_size == 6);
416 
417  PoolFree(p);
418  PASS;
419 }
420 
421 static int PoolTestInit05 (void)
422 {
423  Pool *p = PoolInit(10, 5, 10, PoolTestAlloc, NULL, PoolTestFree);
424  FAIL_IF_NOT(p != NULL);
425 
426  void *data = PoolGet(p);
427  FAIL_IF_NOT(data != NULL);
428 
429  FAIL_IF_NOT(p->alloc_stack_size == 4);
430 
431  FAIL_IF_NOT(p->empty_stack_size == 6);
432 
433  PoolReturn(p, data);
434  data = NULL;
435 
436  FAIL_IF_NOT(p->alloc_stack_size == 5);
437 
438  FAIL_IF_NOT(p->empty_stack_size == 5);
439 
440  PoolFree(p);
441  PASS;
442 }
443 
444 static int PoolTestInit06 (void)
445 {
446  Pool *p = PoolInit(1, 0, 10, PoolTestAlloc, NULL, PoolTestFree);
447  FAIL_IF_NOT(p != NULL);
448 
449  FAIL_IF_NOT(p->allocated == 0);
450 
451  void *data = PoolGet(p);
452  FAIL_IF_NOT(data != NULL);
453 
454  FAIL_IF_NOT(p->allocated == 1);
455 
456  void *data2 = PoolGet(p);
457  FAIL_IF_NOT(data2 == NULL);
458 
459  PoolReturn(p,data);
460  data = NULL;
461 
462  FAIL_IF_NOT(p->allocated == 1);
463 
464  FAIL_IF_NOT(p->alloc_stack_size == 1);
465 
466  PoolFree(p);
467  PASS;
468 }
469 
470 /** \test pool with unlimited size */
471 static int PoolTestInit07 (void)
472 {
473  Pool *p = PoolInit(0, 1, 10, PoolTestAlloc, NULL, PoolTestFree);
474  FAIL_IF_NOT(p != NULL);
475 
476  FAIL_IF_NOT(p->max_buckets == 0);
477 
478  FAIL_IF_NOT(p->allocated == 1);
479 
480  void *data = PoolGet(p);
481  FAIL_IF_NOT(data != NULL);
482 
483  FAIL_IF_NOT(p->allocated == 1);
484 
485  void *data2 = PoolGet(p);
486  FAIL_IF_NOT(data2 != NULL);
487 
488  FAIL_IF_NOT(p->allocated == 2);
489 
490  PoolReturn(p,data);
491  data = NULL;
492 
493  FAIL_IF_NOT(p->allocated == 2);
494 
495  FAIL_IF_NOT(p->alloc_stack_size == 1);
496 
497  PoolReturn(p,data2);
498  data2 = NULL;
499 
500  FAIL_IF_NOT(p->allocated == 1);
501 
502  PoolFree(p);
503  PASS;
504 }
505 #endif /* UNITTESTS */
506 
508 {
509 #ifdef UNITTESTS
510  UtRegisterTest("PoolTestInit01", PoolTestInit01);
511  UtRegisterTest("PoolTestInit02", PoolTestInit02);
512  UtRegisterTest("PoolTestInit03", PoolTestInit03);
513  UtRegisterTest("PoolTestInit04", PoolTestInit04);
514  UtRegisterTest("PoolTestInit05", PoolTestInit05);
515  UtRegisterTest("PoolTestInit06", PoolTestInit06);
516  UtRegisterTest("PoolTestInit07", PoolTestInit07);
517 
519 #endif /* UNITTESTS */
520 }
521 
522 
523 /**
524  * @}
525  */
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:103
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:507
SC_EINVAL
@ SC_EINVAL
Definition: util-error.h:30
Pool_
Definition: util-pool.h:43
p
Packet * p
Definition: fuzz_iprep.c:21
util-unittest.h
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:205
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:306
PoolThreadRegisterTests
void PoolThreadRegisterTests(void)
Definition: util-pool-thread.c:361
suricata-common.h
util-validate.h
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
str
#define str(s)
Definition: suricata-common.h:316
PoolGet
void * PoolGet(Pool *p)
Definition: util-pool.c:251
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:82
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