The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
slab_tests.c
Go to the documentation of this file.
1 /** Tests for slab allocator
2  *
3  * @file src/lib/util/slab_tests.c
4  *
5  * @copyright 2023 Network RADIUS SAS <legal@networkradius.com>
6  */
7 #include <freeradius-devel/util/acutest.h>
8 #include <freeradius-devel/util/acutest_helpers.h>
9 
10 #include "slab.h"
11 
12 typedef struct {
13  int num;
14  char *name;
16 
17 typedef struct {
18  int count;
19 } test_uctx_t;
20 
21 typedef struct {
22  int initial;
23 } test_conf_t;
24 
26  .elements_per_slab = 2,
27  .min_elements = 1,
28  .max_elements = 4,
29  .at_max_fail = false,
30  .num_children = 0,
31  .child_pool_size = 0,
32  .interval.value = 1 * NSEC
33 };
34 
35 static int test_element_free(test_element_t *elem, void *uctx)
36 {
37  test_uctx_t *test_uctx = uctx;
38  test_uctx->count = strlen(elem->name);
39  return 0;
40 }
41 
43 static fr_time_t test_time(void)
44 {
45  return test_time_base;
46 }
47 
49 
51 
52 /** Test basic allocation and reservation of elements
53  *
54  */
55 static void test_alloc(void)
56 {
57  test_slab_list_t *test_slab_list;
58  test_element_t *test_elements[5];
59  test_uctx_t test_uctx, test_uctx2;
60 
61  /*
62  * Each slab will contain 2 elements, maximum of 4 elements allocated from slabs.
63  */
64  test_slab_list = test_slab_list_alloc(NULL, NULL, &def_slab_config, NULL, NULL, NULL, true, false);
65  TEST_CHECK(test_slab_list != NULL);
66  if (!test_slab_list) return;
67 
68  test_elements[0] = test_slab_reserve(test_slab_list);
69  TEST_CHECK(test_elements[0] != NULL);
70  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 1);
71  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 1);
72 
73  test_uctx.count = 0;
74  test_uctx2.count = 0;
75 
76  /* "if" to keep clang scan happy */
77  if (test_elements[0]) test_elements[0]->name = talloc_strdup(test_elements[0], "Hello there");
78  if (test_elements[0]) test_slab_element_set_destructor(test_elements[0], test_element_free, &test_uctx);
79 
80  test_elements[1] = test_slab_reserve(test_slab_list);
81  TEST_CHECK(test_elements[1] != NULL);
82  TEST_CHECK(test_elements[1] != test_elements[0]);
83  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 1);
84  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 2);
85 
86  /* This will cause a second slab to be allocated */
87  test_elements[2] = test_slab_reserve(test_slab_list);
88  TEST_CHECK(test_elements[2] != NULL);
89  if (test_elements[2]) test_elements[2]->name = talloc_strdup(test_elements[2], "Hello there testing");
90  if (test_elements[2]) test_slab_element_set_destructor(test_elements[2], test_element_free, &test_uctx2);
91  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
92  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 3);
93 
94  test_elements[3] = test_slab_reserve(test_slab_list);
95  TEST_CHECK(test_elements[3] != NULL);
96  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
97  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 4);
98 
99  /* This is more elements than max_elements */
100  test_elements[4] = test_slab_reserve(test_slab_list);
101  TEST_CHECK(test_elements[4] != NULL);
102  /* Allocations beyond the maximum do not amend the slab stats */
103  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
104  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 4);
105 
106  if (test_elements[0]) test_slab_release(test_elements[0]);
107  TEST_CHECK(test_uctx.count == 11);
108  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
109  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 3);
110 
111  if (test_elements[1]) test_slab_release(test_elements[1]);
112  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
113  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 2);
114 
115  if (test_elements[2]) test_slab_release(test_elements[2]);
116  TEST_CHECK(test_uctx2.count == 19);
117  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
118  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 1);
119 
120  talloc_free(test_slab_list);
121 }
122 
123 /** Test allocation beyond max fails correctly
124  *
125  */
126 static void test_alloc_fail(void)
127 {
128  test_slab_list_t *test_slab_list;
129  test_element_t *test_elements[5];
130  fr_slab_config_t slab_config = def_slab_config;
131 
132  /*
133  * Each slab will contain 2 elements, maximum of 4 elements allocated from slabs.
134  */
135  slab_config.at_max_fail = true;
136  test_slab_list = test_slab_list_alloc(NULL, NULL, &slab_config, NULL, NULL, NULL, true, false);
137  TEST_CHECK(test_slab_list != NULL);
138  if (!test_slab_list) return;
139 
140  test_elements[0] = test_slab_reserve(test_slab_list);
141  TEST_CHECK(test_elements[0] != NULL);
142  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 1);
143  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 1);
144 
145  test_elements[1] = test_slab_reserve(test_slab_list);
146  test_elements[2] = test_slab_reserve(test_slab_list);
147  test_elements[3] = test_slab_reserve(test_slab_list);
148  TEST_CHECK(test_elements[3] != NULL);
149  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
150  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 4);
151 
152  /* This is more elements than max_elements */
153  test_elements[4] = test_slab_reserve(test_slab_list);
154  TEST_CHECK(test_elements[4] == NULL);
155  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
156  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 4);
157 
158  talloc_free(test_slab_list);
159 }
160 
161 /** Test that freeing an element makes it available for reuse with the element reset between uses
162  *
163  */
164 static void test_reuse_reset(void)
165 {
166  test_slab_list_t *test_slab_list;
167  test_element_t *test_elements[5];
168  test_uctx_t test_uctx;
169 
170  test_slab_list = test_slab_list_alloc(NULL, NULL, &def_slab_config, NULL, NULL, NULL, true, false);
171  TEST_CHECK(test_slab_list != NULL);
172  if (!test_slab_list) return;
173 
174  test_elements[0] = test_slab_reserve(test_slab_list);
175  TEST_CHECK(test_elements[0] != NULL);
176  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 1);
177  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 1);
178 
179  test_uctx.count = 0;
180 
181  if (test_elements[0]) test_elements[0]->name = talloc_strdup(test_elements[0], "Hello there");
182  if (test_elements[0]) test_slab_element_set_destructor(test_elements[0], test_element_free, &test_uctx);
183 
184  test_elements[1] = test_slab_reserve(test_slab_list);
185  TEST_CHECK(test_elements[1] != NULL);
186  TEST_CHECK(test_elements[1] != test_elements[0]);
187 
188  test_elements[2] = test_slab_reserve(test_slab_list);
189  TEST_CHECK(test_elements[2] != NULL);
190 
191  test_elements[3] = test_slab_reserve(test_slab_list);
192  TEST_CHECK(test_elements[3] != NULL);
193 
194  if (test_elements[0]) test_slab_release(test_elements[0]);
195  TEST_CHECK(test_uctx.count == 11);
196 
197  /*
198  * Having released the first element allocated from a slab
199  * reserving another should grab that first one again, but
200  * with the entry memset to zero.
201  */
202  test_elements[4] = test_slab_reserve(test_slab_list);
203  TEST_CHECK(test_elements[4] != NULL);
204  TEST_CHECK(test_elements[4] == test_elements[0]);
205  if (test_elements[4]) TEST_CHECK(test_elements[4]->name == NULL);
206  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
207  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 4);
208 
209  /*
210  * Releasing the first element should reset the destructor
211  * so releasing this reuse of it will not update the result
212  * of the initial release.
213  */
214  if (test_elements[4]) test_elements[4]->name = talloc_strdup(test_elements[4], "Different length string");
215  if (test_elements[4]) test_slab_release(test_elements[4]);
216  TEST_CHECK(test_uctx.count == 11);
217 
218  talloc_free(test_slab_list);
219 }
220 
221 /** Test that freeing an element makes it available for reuse with the element not reset between uses
222  *
223  */
224 static void test_reuse_noreset(void)
225 {
226  test_slab_list_t *test_slab_list;
227  test_element_t *test_elements[3];
228  test_uctx_t test_uctx;
229 
230  test_slab_list = test_slab_list_alloc(NULL, NULL, &def_slab_config, NULL, NULL, NULL, false, false);
231  TEST_CHECK(test_slab_list != NULL);
232  if (!test_slab_list) return;
233 
234  test_elements[0] = test_slab_reserve(test_slab_list);
235  TEST_CHECK(test_elements[0] != NULL);
236  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 1);
237  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 1);
238 
239  test_uctx.count = 0;
240 
241  if (test_elements[0]) test_elements[0]->name = talloc_strdup(test_elements[0], "Hello there");
242  if (test_elements[0]) test_slab_element_set_destructor(test_elements[0], test_element_free, &test_uctx);
243 
244  test_elements[1] = test_slab_reserve(test_slab_list);
245  TEST_CHECK(test_elements[1] != NULL);
246  TEST_CHECK(test_elements[1] != test_elements[0]);
247 
248  if (test_elements[0]) test_slab_release(test_elements[0]);
249  TEST_CHECK(test_uctx.count == 11);
250 
251  /*
252  * Having released the first element allocated from a slab
253  * reserving another should grab that first one again.
254  * Since no reset was done, the element should be as it was before.
255  */
256  test_elements[2] = test_slab_reserve(test_slab_list);
257  TEST_CHECK(test_elements[2] != NULL);
258  TEST_CHECK(test_elements[2] == test_elements[0]);
259  if (test_elements[0] && test_elements[2]) TEST_CHECK(test_elements[2]->name == test_elements[0]->name);
260 
261  /*
262  * Replace the element's string so that the callback on release has
263  * a different string to work on.
264  */
265  if (test_elements[2]) talloc_free(test_elements[2]->name);
266  if (test_elements[2]) test_elements[2]->name = talloc_strdup(test_elements[2], "Different length string");
267  if (test_elements[2]) test_slab_release(test_elements[2]);
268  TEST_CHECK(test_uctx.count == 23);
269 
270  talloc_free(test_slab_list);
271 }
272 
273 /** Test that setting reserve_mru to true works
274  *
275  * After releasing an element, a subsequent reserve should return the same element
276  */
277 static void test_reserve_mru(void)
278 {
279  test_slab_list_t *test_slab_list;
280  test_element_t *test_elements[2];
281 
282  /*
283  * First use a slab list with reserve_mru = false to verify that the two reservations
284  * result in different elements being returned
285  */
286  test_slab_list = test_slab_list_alloc(NULL, NULL, &def_slab_config, NULL, NULL, NULL, true, false);
287  TEST_CHECK(test_slab_list != NULL);
288  if (!test_slab_list) return;
289 
290  test_elements[0] = test_slab_reserve(test_slab_list);
291  TEST_CHECK(test_elements[0] != NULL);
292 
293  if (test_elements[0]) test_slab_release(test_elements[0]);
294 
295  test_elements[1] = test_slab_reserve(test_slab_list);
296  TEST_CHECK(test_elements[1] != NULL);
297  TEST_CHECK(test_elements[0] != test_elements[1]);
298 
299  talloc_free(test_slab_list);
300 
301  /*
302  * Now use a slab list with reserve_mru = true
303  */
304  test_slab_list = test_slab_list_alloc(NULL, NULL, &def_slab_config, NULL, NULL, NULL, true, true);
305  TEST_CHECK(test_slab_list != NULL);
306  if (!test_slab_list) return;
307 
308  test_elements[0] = test_slab_reserve(test_slab_list);
309  TEST_CHECK(test_elements[0] != NULL);
310 
311  if (test_elements[0]) test_slab_release(test_elements[0]);
312 
313  test_elements[1] = test_slab_reserve(test_slab_list);
314  TEST_CHECK(test_elements[1] != NULL);
315  TEST_CHECK(test_elements[0] == test_elements[1]);
316 
317  talloc_free(test_slab_list);
318 }
319 
320 /** Test that talloc freeing an element results in destructor being called
321  *
322  */
323 static void test_free(void)
324 {
325  test_slab_list_t *test_slab_list;
326  test_element_t *test_element;
327  test_uctx_t test_uctx;
328 
329  test_slab_list = test_slab_list_alloc(NULL, NULL, &def_slab_config, NULL, NULL, NULL, true, false);
330  TEST_CHECK(test_slab_list != NULL);
331  if (!test_slab_list) return;
332 
333  test_element = test_slab_reserve(test_slab_list);
334  TEST_CHECK(test_element != NULL);
335 
336  test_uctx.count = 0;
337 
338  if (test_element) test_element->name = talloc_strdup(test_element, "Hello there");
339  if (test_element) test_slab_element_set_destructor(test_element, test_element_free, &test_uctx);
340 
341  if (test_element) talloc_free(test_element);
342  TEST_CHECK(test_uctx.count == 11);
343 
344  talloc_free(test_slab_list);
345 }
346 
347 static int test_element_alloc(test_element_t *elem, void *uctx)
348 {
349  test_conf_t *test_conf = uctx;
350  elem->num = test_conf->initial;
351  return 0;
352 }
353 
354 /** Test that a callback correctly initialises slab elements on first use
355  *
356  */
357 static void test_init(void)
358 {
359  test_slab_list_t *test_slab_list;
360  test_element_t *test_elements[2];
361  test_conf_t test_conf = { .initial = 10 };
362  fr_slab_config_t slab_config = def_slab_config;
363 
364  slab_config.elements_per_slab = 1;
365  test_slab_list = test_slab_list_alloc(NULL, NULL, &slab_config, test_element_alloc, NULL, &test_conf, false, false);
366  TEST_CHECK(test_slab_list != NULL);
367  if (!test_slab_list) return;
368 
369  test_elements[0] = test_slab_reserve(test_slab_list);
370  TEST_CHECK(test_elements[0] != NULL);
371  TEST_CHECK(test_elements[0] && (test_elements[0]->num == 10));
372 
373  /*
374  * Change element data and release
375  */
376  if (test_elements[0]) {
377  test_elements[0]->num = 5;
378  test_slab_release(test_elements[0]);
379  }
380 
381  /*
382  * Re-reserve and check element is unchanged.
383  */
384  test_elements[1] = test_slab_reserve(test_slab_list);
385  TEST_CHECK(test_elements[1] != NULL);
386  TEST_CHECK(test_elements[1] == test_elements[0]);
387  if (test_elements[1]) TEST_CHECK(test_elements[1]->num == 5);
388 
389  talloc_free(test_slab_list);
390 }
391 
392 /** Test that a reserve callback correctly initialises slab elements
393  *
394  */
395 static void test_reserve(void)
396 {
397  test_slab_list_t *test_slab_list;
398  test_element_t *test_elements[2];
399  test_conf_t test_conf = { .initial = 10 };
400  fr_slab_config_t slab_config = def_slab_config;
401 
402  slab_config.elements_per_slab = 1;
403  test_slab_list = test_slab_list_alloc(NULL, NULL, &slab_config, NULL, test_element_alloc, &test_conf, false, false);
404  TEST_CHECK(test_slab_list != NULL);
405  if (!test_slab_list) return;
406 
407  test_elements[0] = test_slab_reserve(test_slab_list);
408  TEST_CHECK(test_elements[0] != NULL);
409  TEST_CHECK(test_elements[0] && (test_elements[0]->num == 10));
410 
411  /*
412  * Change element data and release
413  */
414  if (test_elements[0]) {
415  test_elements[0]->num = 5;
416  test_slab_release(test_elements[0]);
417  }
418 
419  /*
420  * Re-reserve and check element is re-initialised.
421  */
422  test_elements[1] = test_slab_reserve(test_slab_list);
423  TEST_CHECK(test_elements[1] != NULL);
424  TEST_CHECK(test_elements[1] == test_elements[0]);
425  if (test_elements[1]) TEST_CHECK(test_elements[1]->num == 10);
426 
427  talloc_free(test_slab_list);
428 }
429 
430 static int test_element_reserve(test_element_t *elem, void *uctx)
431 {
432  test_conf_t *test_conf = uctx;
433  elem->num = test_conf->initial * 2;
434  return 0;
435 }
436 
437 /** Test that reserve callback runs after init callback
438  *
439  */
440 static void test_init_reserve(void)
441 {
442  test_slab_list_t *test_slab_list;
443  test_element_t *test_elements[2];
444  test_conf_t test_conf = { .initial = 10 };
445  fr_slab_config_t slab_config = def_slab_config;
446 
447  slab_config.elements_per_slab = 1;
448  test_slab_list = test_slab_list_alloc(NULL, NULL, &slab_config, test_element_alloc, test_element_reserve, &test_conf, false, false);
449  TEST_CHECK(test_slab_list != NULL);
450  if (!test_slab_list) return;
451 
452  test_elements[0] = test_slab_reserve(test_slab_list);
453  TEST_CHECK(test_elements[0] != NULL);
454  TEST_CHECK(test_elements[0] && (test_elements[0]->num == 20));
455 
456  /*
457  * Change element data and release
458  */
459  if (test_elements[0]) {
460  test_elements[0]->num = 5;
461  test_slab_release(test_elements[0]);
462  }
463 
464  /*
465  * Re-reserve and check reset callback is run.
466  */
467  test_elements[1] = test_slab_reserve(test_slab_list);
468  TEST_CHECK(test_elements[1] != NULL);
469  TEST_CHECK(test_elements[1] == test_elements[0]);
470  if (test_elements[1]) TEST_CHECK(test_elements[1]->num == 20);
471 
472  talloc_free(test_slab_list);
473 }
474 
475 /** Test of clearing unused slabs
476  *
477  */
478 static void test_clearup_1(void)
479 {
480  TALLOC_CTX *ctx = talloc_init_const("test");
482  test_slab_list_t *test_slab_list;
483  test_element_t *test_elements[6];
484  int i, events;
485  fr_slab_config_t slab_config = def_slab_config;
486 
487  el = fr_event_list_alloc(ctx, NULL, NULL);
489 
490  slab_config.max_elements = 6;
491  test_slab_list = test_slab_list_alloc(NULL, el, &slab_config, NULL, NULL, NULL, true, false);
492  TEST_CHECK(test_slab_list != NULL);
493  if (!test_slab_list) return;
494 
495  /*
496  * Allocate all the slab elements
497  */
498  for (i = 0; i < 6; i++) {
499  test_elements[i] = test_slab_reserve(test_slab_list);
500  TEST_CHECK(test_elements[i] != NULL);
501  }
502  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 3);
503  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 6);
504 
505  /*
506  * Release four of the six elements
507  */
508  for (i = 0; i < 4; i++) {
509  test_slab_release(test_elements[i]);
510  }
511  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 3);
512  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 2);
513 
514  /*
515  * Running clearup should free one slab - half of the
516  * difference between the high water mark and the in use count.
517  */
520  TEST_CHECK(events == 1);
522  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
523  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 2);
524 
525  talloc_free(test_slab_list);
526  talloc_free(ctx);
527 }
528 
529 /** Test that slab clearing does not go beyond the minimum
530  *
531  */
532 static void test_clearup_2(void)
533 {
534  TALLOC_CTX *ctx = talloc_init_const("test");
536  test_slab_list_t *test_slab_list;
537  test_element_t *test_elements[20];
538  int i, events;
539  fr_slab_config_t slab_config = def_slab_config;
540 
541  el = fr_event_list_alloc(ctx, NULL, NULL);
543 
544  slab_config.min_elements = 16;
545  slab_config.max_elements = 20;
546  test_slab_list = test_slab_list_alloc(NULL, el, &slab_config, NULL, NULL, NULL, true, false);
547  TEST_CHECK(test_slab_list != NULL);
548  if (!test_slab_list) return;
549 
550  /*
551  * Allocate all the slab elements
552  */
553  for (i = 0; i < 20; i++) {
554  test_elements[i] = test_slab_reserve(test_slab_list);
555  TEST_CHECK(test_elements[i] != NULL);
556  }
557  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 10);
558  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 20);
559 
560  /*
561  * Release all of the elements
562  */
563  for (i = 0; i < 20; i++) {
564  test_slab_release(test_elements[i]);
565  }
566  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 10);
567  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
568 
569  /*
570  * Running clearup should free two slabs - the minimum element
571  * count will keep the remainder allocated
572  */
575  TEST_CHECK(events == 1);
577  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 8);
578  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
579 
580  /*
581  * Re-run the event - no more slabs should be cleared
582  */
585  TEST_CHECK(events == 1);
587  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 8);
588  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
589 
590  talloc_free(test_slab_list);
591  talloc_free(ctx);
592 }
593 
594 /** Test that repeated clearing frees more slabs
595  *
596  */
597 static void test_clearup_3(void)
598 {
599  TALLOC_CTX *ctx = talloc_init_const("test");
601  test_slab_list_t *test_slab_list;
602  test_element_t *test_elements[20];
603  int i, events;
604  fr_slab_config_t slab_config = def_slab_config;
605 
606  el = fr_event_list_alloc(ctx, NULL, NULL);
608 
609  slab_config.min_elements = 0;
610  slab_config.max_elements = 20;
611  test_slab_list = test_slab_list_alloc(NULL, el, &slab_config, NULL, NULL, NULL, true, false);
612  TEST_CHECK(test_slab_list != NULL);
613  if (!test_slab_list) return;
614 
615  /*
616  * Allocate all the slab elements
617  */
618  for (i = 0; i < 20; i++) {
619  test_elements[i] = test_slab_reserve(test_slab_list);
620  TEST_CHECK(test_elements[i] != NULL);
621  }
622  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 10);
623  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 20);
624 
625  /*
626  * Release all of the elements
627  */
628  for (i = 0; i < 20; i++) {
629  test_slab_release(test_elements[i]);
630  }
631  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 10);
632  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
633 
634  /*
635  * Running clearup should free five slabs (20 - 0) / 2 / 2 = 5
636  */
639  TEST_CHECK(events == 1);
641  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 5);
642  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
643 
644  /*
645  * Re-run the event - two more slabs should be freed (10 - 0) / 2 / 2 = 2.5
646  */
649  TEST_CHECK(events == 1);
651  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 3);
652  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
653 
654  /*
655  * Re-run the event - one more slab should be freed (6 - 0) / 2 / 2 = 1.5
656  */
659  TEST_CHECK(events == 1);
661  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 2);
662  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
663 
664  /*
665  * Re-run the event - one more slab should be freed (4 - 0) / 2 / 2 = 1
666  */
669  TEST_CHECK(events == 1);
671  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 1);
672  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
673 
674  /*
675  * Re-run the event - no more will be freed as (2 - 0) / 2 / 2 = 0.5
676  */
679  TEST_CHECK(events == 1);
681  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 1);
682  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
683 
684  talloc_free(test_slab_list);
685  talloc_free(ctx);
686 }
687 
688 /** Test that reserving after clearup results in new slab allocation
689  *
690  */
691 static void test_realloc(void)
692 {
693  TALLOC_CTX *ctx = talloc_init_const("test");
695  test_slab_list_t *test_slab_list;
696  test_element_t *test_elements[20];
697  int i, events;
698  fr_slab_config_t slab_config = def_slab_config;
699 
700  el = fr_event_list_alloc(ctx, NULL, NULL);
702 
703  slab_config.min_elements = 0;
704  slab_config.max_elements = 20;
705  test_slab_list = test_slab_list_alloc(NULL, el, &slab_config, NULL, NULL, NULL, true, false);
706  TEST_CHECK(test_slab_list != NULL);
707  if (!test_slab_list) return;
708 
709  /*
710  * Allocate all the slab elements
711  */
712  for (i = 0; i < 20; i++) {
713  test_elements[i] = test_slab_reserve(test_slab_list);
714  TEST_CHECK(test_elements[i] != NULL);
715  }
716  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 10);
717  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 20);
718 
719  /*
720  * Release all of the elements
721  */
722  for (i = 0; i < 20; i++) {
723  test_slab_release(test_elements[i]);
724  }
725  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 10);
726  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
727 
728  /*
729  * Running clearup should free five slabs
730  */
733  TEST_CHECK(events == 1);
735  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 5);
736  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 0);
737 
738  /*
739  * Allocate all the slab elements
740  * With new slabs allocated, the slab stats will change
741  */
742  for (i = 0; i < 20; i++) {
743  test_elements[i] = test_slab_reserve(test_slab_list);
744  TEST_CHECK(test_elements[i] != NULL);
745  }
746  TEST_CHECK_RET(test_slab_num_allocated(test_slab_list), 10);
747  TEST_CHECK_RET(test_slab_num_elements_used(test_slab_list), 20);
748 
749  talloc_free(test_slab_list);
750  talloc_free(ctx);
751 }
752 
753 static void test_child_alloc(void)
754 {
755  test_slab_list_t *test_slab_list;
756  test_element_t *test_elements[2];
757  fr_slab_config_t slab_config = def_slab_config;
758 
759  slab_config.max_elements = 2;
760  slab_config.num_children = 1;
761  slab_config.child_pool_size = 128;
762  test_slab_list = test_slab_list_alloc(NULL, NULL, &slab_config, NULL, NULL, NULL, false, false);
763  TEST_CHECK(test_slab_list != NULL);
764  if (!test_slab_list) return;
765 
766  test_elements[0] = test_slab_reserve(test_slab_list);
767  TEST_CHECK(test_elements[0] != NULL);
768  test_elements[1] = test_slab_reserve(test_slab_list);
769  TEST_CHECK(test_elements[1] != NULL);
770 
771  /*
772  * Allocate a child of the first element. If this has used the pool memory
773  * allocated to the element, then it's location should be after the element
774  * but before the next element.
775  * This is a rough test, which can be improved if additional talloc functions
776  * become available to check pool allocation status.
777  */
778  if (test_elements[0]) {
779  test_elements[0]->name = talloc_strdup(test_elements[0], "Hello there");
780  TEST_CHECK((void *)test_elements[0]->name > (void *)test_elements[0]);
781  if (test_elements[1] && (test_elements[1] > test_elements[0])) {
782  TEST_CHECK((void *)test_elements[0]->name < (void *)test_elements[1]);
783  TEST_MSG("element 0: %p, name %p, element 1: %p",
784  test_elements[0], test_elements[0]->name, test_elements[1]);
785  }
786  }
787 
788  talloc_free(test_slab_list);
789 }
790 
792  { "test_alloc", test_alloc },
793  { "test_alloc_fail", test_alloc_fail },
794  { "test_reuse_reset", test_reuse_reset },
795  { "test_reuse_noreset", test_reuse_noreset },
796  { "test_reserve_mru", test_reserve_mru },
797  { "test_free", test_free },
798  { "test_init", test_init },
799  { "test_reserve", test_reserve },
800  { "test_init_reserve", test_init_reserve },
801  { "test_clearup_1", test_clearup_1 },
802  { "test_clearup_2", test_clearup_2 },
803  { "test_clearup_3", test_clearup_3 },
804  { "test_realloc", test_realloc },
805  { "test_child_alloc", test_child_alloc },
806 
807  { NULL }
808 };
#define TEST_CHECK(cond)
Definition: acutest.h:85
#define TEST_MSG(...)
Definition: acutest.h:215
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
TEST_CHECK_RET((int) count,(int) 3)
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
Definition: event.c:2549
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Definition: event.c:2899
void fr_event_list_set_time_func(fr_event_list_t *el, fr_event_time_source_t func)
Override event list time source.
Definition: event.c:2971
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
Definition: event.c:2414
talloc_free(reap)
Stores all information relating to an event list.
Definition: event.c:411
static fr_event_list_t * events
Definition: radsniff.c:59
static char const * name
Resource pool management using slabs of memory.
#define FR_SLAB_FUNCS(_name, _type)
Define type specific wrapper functions for slabs and slab elements.
Definition: slab.h:120
#define FR_SLAB_TYPES(_name, _type)
Define type specific wrapper structs for slabs and slab elements.
Definition: slab.h:72
unsigned int min_elements
Minimum number of elements to keep allocated.
Definition: slab.h:44
unsigned int max_elements
Maximum number of elements to allocate using slabs.
Definition: slab.h:45
bool at_max_fail
Should requests for additional elements fail when the number in use has reached max_elements.
Definition: slab.h:46
unsigned int elements_per_slab
Number of elements to allocate per slab.
Definition: slab.h:43
unsigned int num_children
How many child allocations are expected off each element.
Definition: slab.h:48
size_t child_pool_size
Size of pool space to be allocated to each element.
Definition: slab.h:49
Tuneable parameters for slabs.
Definition: slab.h:42
static fr_time_t test_time(void)
Definition: slab_tests.c:43
static void test_clearup_3(void)
Test that repeated clearing frees more slabs.
Definition: slab_tests.c:597
static void test_reserve(void)
Test that a reserve callback correctly initialises slab elements.
Definition: slab_tests.c:395
TEST_LIST
Definition: slab_tests.c:791
static fr_time_t test_time_base
Definition: slab_tests.c:42
static void test_init_reserve(void)
Test that reserve callback runs after init callback.
Definition: slab_tests.c:440
static fr_slab_config_t def_slab_config
Definition: slab_tests.c:25
static void test_reserve_mru(void)
Test that setting reserve_mru to true works.
Definition: slab_tests.c:277
static void test_clearup_2(void)
Test that slab clearing does not go beyond the minimum.
Definition: slab_tests.c:532
static void test_reuse_noreset(void)
Test that freeing an element makes it available for reuse with the element not reset between uses.
Definition: slab_tests.c:224
static void test_child_alloc(void)
Definition: slab_tests.c:753
static int test_element_reserve(test_element_t *elem, void *uctx)
Definition: slab_tests.c:430
static void test_clearup_1(void)
Test of clearing unused slabs.
Definition: slab_tests.c:478
static void test_free(void)
Test that talloc freeing an element results in destructor being called.
Definition: slab_tests.c:323
static void test_alloc(void)
Test basic allocation and reservation of elements.
Definition: slab_tests.c:55
static void test_reuse_reset(void)
Test that freeing an element makes it available for reuse with the element reset between uses.
Definition: slab_tests.c:164
static int test_element_free(test_element_t *elem, void *uctx)
Definition: slab_tests.c:35
static void test_init(void)
Test that a callback correctly initialises slab elements on first use.
Definition: slab_tests.c:357
static void test_alloc_fail(void)
Test allocation beyond max fails correctly.
Definition: slab_tests.c:126
static int test_element_alloc(test_element_t *elem, void *uctx)
Definition: slab_tests.c:347
static void test_realloc(void)
Test that reserving after clearup results in new slab allocation.
Definition: slab_tests.c:691
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition: talloc.h:112
static fr_time_t fr_time_add_time_delta(fr_time_t a, fr_time_delta_t b)
Definition: time.h:173
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition: time.h:590
#define fr_time_wrap(_time)
Definition: time.h:145
#define NSEC
Definition: time.h:379
"server local" time.
Definition: time.h:69
static fr_event_list_t * el