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
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
49
51
52/** Test basic allocation and reservation of elements
53 *
54 */
55static 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 */
126static 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 */
164static 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 */
224static 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 */
277static 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 */
323static 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
347static 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 */
357static 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 */
395static 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
430static 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 */
440static 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 */
478static 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 */
532static 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 */
597static 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 */
691static 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
753static 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
#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:2549
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)
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
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