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