Coverage Report

Created: 2026-08-14 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/io/message.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/**
18
 * $Id: 942c5dd3333163dfce530ae8c179cb7dae61d1a6 $
19
 *
20
 * @brief Messages for inter-thread communication
21
 * @file io/message.c
22
 *
23
 * @copyright 2016 Alan DeKok (aland@freeradius.org)
24
 */
25
RCSID("$Id: 942c5dd3333163dfce530ae8c179cb7dae61d1a6 $")
26
27
#include <freeradius-devel/io/message.h>
28
#include <freeradius-devel/util/strerror.h>
29
30
31
/*
32
 *  Debugging, mainly for message_set_test
33
 */
34
#if 0
35
#define MPRINT(...) fprintf(stderr, __VA_ARGS__)
36
#else
37
#define MPRINT(...)
38
#endif
39
40
0
#define MSG_ARRAY_SIZE (16)
41
42
0
#define CACHE_ALIGN(_x) do { _x += 63; _x &= ~(size_t) 63; } while (0)
43
44
/** A Message set, composed of message headers and ring buffer data.
45
 *
46
 *  A message set is intended to send short-lived messages.  The
47
 *  message headers are fixed in size, and allocated from an array
48
 *  which is treated like a circular buffer.  Message bodies (i.e. raw
49
 *  packets) are variable in size, and live in a separate ring buffer.
50
 *
51
 *  The message set starts off with a small array of message headers,
52
 *  and a small ring buffer.  If an array/buffer fills up, a new one
53
 *  is allocated at double the size of the previous one.
54
 *
55
 * The array / buffers are themselves kept in fixed-size arrays, of
56
 *  MSG_ARRAY_SIZE.  The reason is that the memory for fr_message_set_t
57
 *  should be contiguous, and not in a linked list scattered in
58
 *  memory.
59
 *
60
 *  The originator allocates a message, and sends it to a recipient.
61
 *  The recipient (usually in another thread) uses the message, and
62
 *  marks it as FR_MESSAGE_DONE.  The originator then asynchronously
63
 *  cleans up the message.
64
 *
65
 *  This asynchronous cleanup is done via self-clocking.  If there is
66
 *  no need to clean up the messages, it isn't done.  Only when we run
67
 *  out of space to store messages (or packets) is the cleanup done.
68
 *
69
 *  This cleanup latency ensures that we don't have cache line
70
 *  bouncing, where the originator sends the message, and then while
71
 *  the recipieent is reading it... thrashes the cache line with
72
 *  checks for "are you done?  Are you done?"
73
 *
74
 *  If there are more than one used entry in either array, we then try
75
 *  to coalesce the buffers on cleanup.  If we discover that one array
76
 *  is empty, we discard it, and move the used array entries into it's
77
 *  place.
78
 *
79
 *  This process ensures that we don't have too many buffers in
80
 *  progress.  It is better to have a few large buffers than many
81
 *  small ones.
82
 *
83
 *  MSG_ARRAY_SIZE is defined to be large (16 doublings) to allow for
84
 *  the edge case where messages are stuck for long periods of time.
85
 *
86
 *  With an initial message array size of 64, this gives us room for
87
 *  2M packets, if *all* of the mr_array entries have packets stuck in
88
 *  them that aren't cleaned up for extended periods of time.
89
 *
90
 *  @todo Add a flag for UDP-style protocols, where we can put the
91
 *  message into the ring buffer.  This helps with locality of
92
 *  reference, and removes the need to track two separate things.
93
 */
94
struct fr_message_set_s {
95
  int     mr_current; //!< current used message ring entry
96
  int     mr_max;   //!< max used message ring entry
97
98
  size_t      message_size; //!< size of the callers message, including fr_message_t
99
100
  int     mr_cleaned; //!< where we last cleaned
101
102
  int     rb_current; //!< current used ring buffer entry
103
  int     rb_max;   //!< max used ring buffer entry
104
105
  size_t      max_allocation; //!< maximum allocation size
106
107
  int     allocated;
108
  int     freed;
109
110
  fr_ring_buffer_t  *mr_array[MSG_ARRAY_SIZE]; //!< array of message arrays
111
112
  fr_ring_buffer_t  *rb_array[MSG_ARRAY_SIZE]; //!< array of ring buffers
113
};
114
115
116
/** Create a message set
117
 *
118
 * @param[in] ctx the context for talloc
119
 * @param[in] num_messages size of the initial message array.  MUST be a power of 2.
120
 * @param[in] message_size the size of each message, INCLUDING fr_message_t, which MUST be at the start of the struct
121
 * @param[in] ring_buffer_size of the ring buffer.  MUST be a power of 2.
122
 * @param[in] unlimited_size allow any message size to be allocated.  If false it is limited to ring_buffer_size / 2.
123
 * @return
124
 *  - NULL on error
125
 *  - newly allocated fr_message_set_t on success
126
 */
127
fr_message_set_t *fr_message_set_create(TALLOC_CTX *ctx, int num_messages, size_t message_size, size_t ring_buffer_size,
128
          bool unlimited_size)
129
0
{
130
0
  fr_message_set_t *ms;
131
132
  /*
133
   *  Too small, or not a power of 2.
134
   */
135
0
  if (num_messages < 8) num_messages = 8;
136
137
0
  if ((num_messages & (num_messages - 1)) != 0) {
138
0
    fr_strerror_const("Number of messages must be a power of 2");
139
0
    return NULL;
140
0
  }
141
142
0
  if (message_size < sizeof(fr_message_t)) {
143
0
    fr_strerror_printf("Message size must be at least %zd", sizeof(fr_message_t));
144
0
    return NULL;
145
0
  }
146
147
0
  if (message_size > 1024) {
148
0
    fr_strerror_const("Message size must be no larger than 1024");
149
0
    return NULL;
150
0
  }
151
152
0
  ms = talloc_zero(ctx, fr_message_set_t);
153
0
  if (!ms) {
154
0
    fr_strerror_const("Failed allocating memory");
155
0
    return NULL;
156
0
  }
157
158
0
  CACHE_ALIGN(message_size);
159
0
  ms->message_size = message_size;
160
161
0
  ms->rb_array[0] = fr_ring_buffer_create(ms, ring_buffer_size);
162
0
  if (!ms->rb_array[0]) {
163
0
    talloc_free(ms);
164
0
    return NULL;
165
0
  }
166
0
  ms->rb_max = 0;
167
168
0
  ms->mr_array[0] = fr_ring_buffer_create(ms, num_messages * message_size);
169
0
  if (!ms->mr_array[0]) {
170
0
    talloc_free(ms);
171
0
    return NULL;
172
0
  }
173
174
  /*
175
   * If unlimited size is allowed, set the max allocation to 1 << 29
176
   * which is based on the maximum ring buffer reservation of 1 << 30
177
   */
178
0
  ms->max_allocation = unlimited_size ? 1 << 29 : ring_buffer_size / 2;
179
180
0
  return ms;
181
0
}
182
183
184
/** Mark a message as done
185
 *
186
 *  Note that this call is usually done from a thread OTHER than the
187
 *  originator of the message.  As such, the message is NOT actually
188
 *  freed.  Instead, it is just marked as freed.
189
 *
190
 * @param[in] m the message to make as done.
191
 * @return
192
 *  - <0 on error
193
 *  - 0 on success
194
 */
195
int fr_message_done(fr_message_t *m)
196
0
{
197
0
  fr_assert(m->status != FR_MESSAGE_FREE);
198
0
  fr_assert(m->status != FR_MESSAGE_DONE);
199
200
  /*
201
   *  Mark a message as freed.  The originator will take
202
   *  care of cleaning it up.
203
   */
204
0
  if (m->status == FR_MESSAGE_USED) {
205
0
    m->status = FR_MESSAGE_DONE;
206
0
    return 0;
207
0
  }
208
209
  /*
210
   *  This message was localized, so we can free it via
211
   *  talloc.
212
   */
213
0
  if (m->status == FR_MESSAGE_LOCALIZED) {
214
0
    talloc_free(m);
215
0
    return 0;
216
0
  }
217
218
  /*
219
   *  A catastrophic error.
220
   */
221
0
  fr_assert(0 == 1);
222
223
0
  fr_strerror_const("Failed marking message as done");
224
0
  return -1;
225
0
}
226
227
228
/** Localize a message by copying it to local storage
229
 *
230
 *  This function "localizes" a message by copying it to local
231
 *  storage.  In the case where the recipient of a message has to sit
232
 *  on it for a while, that blocks the originator from cleaning up the
233
 *  message.  The recipient can then copy the message to local
234
 *  storage, so that the originator can clean it up.
235
 *
236
 *  The localized message is marked as FR_MESSAGE_LOCALIZED, so that
237
 *  the recipient can call the normal fr_message_done() function to
238
 *  free it.
239
 *
240
 * @param[in] ctx the talloc context to use for localization
241
 * @param[in] m the message to be localized
242
 * @param[in] message_size the size of the message, including the fr_message_t
243
 * @return
244
 *  - NULL on allocation error
245
 *  - a newly localized message
246
 */
247
fr_message_t *fr_message_localize(TALLOC_CTX *ctx, fr_message_t *m, size_t message_size)
248
0
{
249
0
  fr_message_t *l;
250
251
0
  if (m->status != FR_MESSAGE_USED) {
252
0
    fr_strerror_const("Cannot localize message unless it is in use");
253
0
    return NULL;
254
0
  }
255
256
0
  if (message_size < sizeof(fr_message_t)) {
257
0
    fr_strerror_const("Message size is too small");
258
0
    return NULL;
259
0
  }
260
261
0
  l = talloc_memdup(ctx, m, message_size);
262
0
  if (!l) {
263
0
  nomem:
264
0
    fr_strerror_const("Failed allocating memory");
265
0
    return NULL;
266
0
  }
267
268
0
  l->data = NULL;
269
270
0
  if (l->data_size) {
271
0
    l->data = talloc_memdup(l, m->data, l->data_size);
272
0
    if (!l->data) {
273
0
      talloc_free(l);
274
0
      goto nomem;
275
0
    }
276
0
  }
277
278
0
  l->status = FR_MESSAGE_LOCALIZED;
279
280
  /*
281
   *  After this change, "m" should not be used for
282
   *  anything.
283
   */
284
0
  m->status = FR_MESSAGE_DONE;
285
286
  /*
287
   *  Now clean up the other fields of the newly localized
288
   *  message.
289
   */
290
0
  l->rb = NULL;
291
0
  l->rb_size = 0;
292
293
0
  return l;
294
0
}
295
296
297
/** Clean up messages in a message ring.
298
 *
299
 *  Find the oldest messages which are marked FR_MESSAGE_DONE,
300
 *  and mark them FR_MESSAGE_FREE.
301
 *
302
 *  FIXME: If we care, track which ring buffer is in use, and how
303
 *  many contiguous chunks we can free.  Then, free the chunks at
304
 *  once, instead of piecemeal.  Realistically tho... this will
305
 *  probably make little difference.
306
 *
307
 * @param[in] ms the message set
308
 * @param[in] mr the message ring
309
 * @param[in] max_to_clean maximum number of messages to clean at a time.
310
 */
311
static int message_ring_gc(fr_message_set_t *ms, fr_ring_buffer_t *mr, int max_to_clean)
312
0
{
313
0
  int messages_cleaned = 0;
314
0
  size_t size;
315
0
  fr_message_t *m;
316
317
0
  while (true) {
318
0
    (void) fr_ring_buffer_start(mr, (uint8_t **) &m, &size);
319
0
    if (size == 0) break;
320
321
0
    fr_assert(m != NULL);
322
0
    fr_assert(size >= ms->message_size);
323
324
0
    fr_assert(m->status != FR_MESSAGE_FREE);
325
0
    if (m->status != FR_MESSAGE_DONE) break;
326
327
0
    messages_cleaned++;
328
0
    m->status = FR_MESSAGE_FREE;
329
0
    ms->freed++;
330
331
0
    if (m->rb) {
332
0
      (void) fr_ring_buffer_free(m->rb, m->rb_size);
333
0
#ifndef NDEBUG
334
0
      memset(m, 0, ms->message_size);
335
0
#endif
336
0
    }
337
338
0
    fr_ring_buffer_free(mr, ms->message_size);
339
340
0
    if (messages_cleaned >= max_to_clean) break;
341
0
  }
342
343
0
  MPRINT("CLEANED %d (%p) left\n", messages_cleaned, mr);
344
0
  return messages_cleaned;
345
0
}
346
347
348
/** Garbage collect "done" messages.
349
 *
350
 *  Called only from the originating thread.  We also clean a limited
351
 *  number of messages at a time, so that we don't have sudden latency
352
 *  spikes when cleaning 1M messages.
353
 *
354
 * @param[in] ms the message set
355
 * @param[in] max_to_clean the maximum number of messages to clean
356
 */
357
static void message_gc(fr_message_set_t *ms, int max_to_clean)
358
0
{
359
0
  int i;
360
0
  int arrays_freed, arrays_used, empty_slot;
361
0
  int largest_free_slot;
362
0
  int total_cleaned;
363
0
  size_t largest_free_size;
364
365
  /*
366
   *  Clean up "done" messages.
367
   */
368
0
  total_cleaned = 0;
369
370
  /*
371
   *  Garbage collect the smaller buffers first.
372
   */
373
0
  for (i = 0; i <= ms->mr_max; i++) {
374
0
    int cleaned;
375
376
0
    cleaned = message_ring_gc(ms, ms->mr_array[i], max_to_clean - total_cleaned);
377
0
    total_cleaned += cleaned;
378
0
    fr_assert(total_cleaned <= max_to_clean);
379
380
    /*
381
     *  Stop when we've reached our GC limit.
382
     */
383
0
    if (total_cleaned == max_to_clean) break;
384
0
  }
385
386
  /*
387
   *  Couldn't GC anything.  Don't do more work.
388
   */
389
0
  if (total_cleaned == 0) return;
390
391
0
  arrays_freed = 0;
392
0
  arrays_used = 0;
393
394
  /*
395
   *  Keep the two largest message buffers (used or not),
396
   *  and free all smaller ones which are empty.
397
   */
398
0
  for (i = ms->mr_max; i >= 0; i--) {
399
0
    fr_assert(ms->mr_array[i] != NULL);
400
401
0
    if (arrays_used < 2) {
402
0
      MPRINT("\tleaving entry %d alone\n", i);
403
0
      arrays_used++;
404
0
      continue;
405
0
    }
406
407
    /*
408
     *  If the message ring buffer is empty, check if
409
     *  we should perhaps delete it.
410
     */
411
0
    if (fr_ring_buffer_used(ms->mr_array[i]) == 0) {
412
0
      MPRINT("\tfreeing entry %d\n", i);
413
0
      TALLOC_FREE(ms->mr_array[i]);
414
0
      arrays_freed++;
415
0
      continue;
416
0
    }
417
418
0
    MPRINT("\tstill in use entry %d\n", i);
419
0
  }
420
421
  /*
422
   *  Some entries have been freed.  We need to coalesce the
423
   *  remaining entries.
424
   */
425
0
  if (arrays_freed) {
426
0
    MPRINT("TRYING TO PACK from %d free arrays out of %d\n", arrays_freed, ms->rb_max + 1);
427
428
0
    empty_slot = -1;
429
430
    /*
431
     *  Pack the rb array by moving used entries to
432
     *  the bottom of the array.
433
     */
434
0
    for (i = 0; i <= ms->mr_max; i++) {
435
0
      int j;
436
437
      /*
438
       *  Skip over empty entries, but set
439
       *  "empty_slot" to the first empty on we
440
       *  found.
441
       */
442
0
      if (!ms->mr_array[i]) {
443
0
        if (empty_slot < 0) empty_slot = i;
444
445
0
        continue;
446
0
      }
447
448
      /*
449
       *  This array entry is used, but there is
450
       *  no empty slot to put it into.  Ignore
451
       *  it, and continue
452
       */
453
0
      if (empty_slot < 0) continue;
454
455
0
      fr_assert(ms->mr_array[empty_slot] == NULL);
456
457
0
      ms->mr_array[empty_slot] = ms->mr_array[i];
458
0
      ms->mr_array[i] = NULL;
459
460
      /*
461
       *  Find the next empty slot which is
462
       *  greater than the one we just used.
463
       */
464
0
      for (j = empty_slot + 1; j <= i; j++) {
465
0
        if (!ms->mr_array[j]) {
466
0
          empty_slot = j;
467
0
          break;
468
0
        }
469
0
      }
470
0
    }
471
472
    /*
473
     *  Lower max, and set current to the largest
474
     *  array, whether or not it's used.
475
     */
476
0
    ms->mr_max -= arrays_freed;
477
0
    ms->mr_current = ms->mr_max;
478
479
0
#ifndef NDEBUG
480
0
    MPRINT("NUM RB ARRAYS NOW %d\n", ms->mr_max + 1);
481
0
    for (i = 0; i <= ms->mr_max; i++) {
482
0
      MPRINT("\t%d %p\n", i, ms->mr_array[i]);
483
0
      fr_assert(ms->mr_array[i] != NULL);
484
0
    }
485
0
#endif
486
0
  }
487
488
  /*
489
   *  And now we do the same thing for the ring buffers.
490
   *  Except that freeing the messages above also cleaned up
491
   *  the contents of each ring buffer, so all we need to do
492
   *  is find the largest empty ring buffer.
493
   *
494
   *  We do this by keeping the two largest ring buffers
495
   *  (used or not), and then freeing all smaller ones which
496
   *  are empty.
497
   */
498
0
  arrays_used = 0;
499
0
  arrays_freed = 0;
500
0
  MPRINT("TRYING TO FREE ARRAYS %d\n", ms->rb_max);
501
0
  for (i = ms->rb_max; i >= 0; i--) {
502
0
    fr_assert(ms->rb_array[i] != NULL);
503
504
0
    if (arrays_used < 2) {
505
0
      MPRINT("\tleaving entry %d alone\n", i);
506
0
      arrays_used++;
507
0
      continue;
508
0
    }
509
510
0
    if (fr_ring_buffer_used(ms->rb_array[i]) == 0) {
511
0
      MPRINT("\tfreeing entry %d\n", i);
512
0
      TALLOC_FREE(ms->rb_array[i]);
513
0
      arrays_freed++;
514
0
      continue;
515
0
    }
516
517
0
    MPRINT("\tstill in use entry %d\n", i);
518
0
  }
519
520
  /*
521
   *  Pack the array entries back down.
522
   */
523
0
  if (arrays_freed > 0) {
524
0
    MPRINT("TRYING TO PACK from %d free arrays out of %d\n", arrays_freed, ms->rb_max + 1);
525
526
0
    empty_slot = -1;
527
528
    /*
529
     *  Pack the rb array by moving used entries to
530
     *  the bottom of the array.
531
     */
532
0
    for (i = 0; i <= ms->rb_max; i++) {
533
0
      int j;
534
535
      /*
536
       *  Skip over empty entries, but set
537
       *  "empty_slot" to the first empty on we
538
       *  found.
539
       */
540
0
      if (!ms->rb_array[i]) {
541
0
        if (empty_slot < 0) empty_slot = i;
542
543
0
        continue;
544
0
      }
545
546
      /*
547
       *  This array entry is used, but there is
548
       *  no empty slot to put it into.  Ignore
549
       *  it, and continue
550
       */
551
0
      if (empty_slot < 0) continue;
552
553
0
      fr_assert(ms->rb_array[empty_slot] == NULL);
554
555
0
      ms->rb_array[empty_slot] = ms->rb_array[i];
556
0
      ms->rb_array[i] = NULL;
557
558
      /*
559
       *  Find the next empty slot which is
560
       *  greater than the one we just used.
561
       */
562
0
      for (j = empty_slot + 1; j <= i; j++) {
563
0
        if (!ms->rb_array[j]) {
564
0
          empty_slot = j;
565
0
          break;
566
0
        }
567
0
      }
568
0
    }
569
570
    /*
571
     *  Lower max, and set current to the largest
572
     *  array, whether or not it's used.
573
     */
574
0
    ms->rb_max -= arrays_freed;
575
0
    ms->rb_current = ms->rb_max;
576
577
0
#ifndef NDEBUG
578
0
    MPRINT("NUM RB ARRAYS NOW %d\n", ms->rb_max + 1);
579
0
    for (i = 0; i <= ms->rb_max; i++) {
580
0
      MPRINT("\t%d %p\n", i, ms->rb_array[i]);
581
0
      fr_assert(ms->rb_array[i] != NULL);
582
0
    }
583
0
#endif
584
0
  }
585
586
  /*
587
   *  Set the current ring buffer to the one with the
588
   *  largest free space in it.
589
   *
590
   *  This is different from the allocation strategy for
591
   *  messages.
592
   */
593
0
  if (!fr_cond_assert(ms->rb_array[ms->rb_max] != NULL)) return;
594
595
0
  largest_free_slot = ms->rb_max;
596
0
  largest_free_size = (fr_ring_buffer_size(ms->rb_array[ms->rb_max]) -
597
0
           fr_ring_buffer_used(ms->rb_array[ms->rb_max]));
598
599
0
  for (i = 0; i < ms->rb_max; i++) {
600
0
    size_t free_size;
601
602
0
    fr_assert(ms->rb_array[i] != NULL);
603
604
0
    free_size = (fr_ring_buffer_size(ms->rb_array[i]) -
605
0
           fr_ring_buffer_used(ms->rb_array[i]));
606
0
    if (largest_free_size < free_size) {
607
0
      largest_free_slot = i;
608
0
      largest_free_size = free_size;
609
0
    }
610
0
  }
611
612
0
  ms->rb_current = largest_free_slot;
613
0
  fr_assert(ms->rb_current >= 0);
614
0
  fr_assert(ms->rb_current <= ms->rb_max);
615
0
}
616
617
typedef uint8_t *(*fr_ring_buffer_op_t)(fr_ring_buffer_t *rb, size_t size);
618
619
/** Find a message ring with space, performing GC and expansion as needed.
620
 *
621
 *  The caller passes in the operation to perform on the ring buffer
622
 *  (fr_ring_buffer_alloc or fr_ring_buffer_reserve).  This function
623
 *  handles GC, searching across all message rings, and doubling the
624
 *  ring size when all are full.
625
 *
626
 * @param[out] p_cleaned a flag to indicate if we cleaned the message array
627
 * @param[in] ms the message set
628
 * @param[in] op the ring buffer operation (alloc or reserve)
629
 * @return
630
 *  - NULL on error
631
 *      - pointer to message-sized region on success
632
 */
633
static inline CC_HINT(always_inline)
634
uint8_t *_message_ring_find(bool *p_cleaned, fr_message_set_t *ms, fr_ring_buffer_op_t op)
635
0
{
636
0
  int i;
637
0
  uint8_t *p;
638
0
  fr_ring_buffer_t *mr;
639
640
0
  *p_cleaned = false;
641
642
  /*
643
   *  Grab the current message array.  In the general case,
644
   *  there's room, so we grab a message and go find a ring
645
   *  buffer.
646
   */
647
0
  mr = ms->mr_array[ms->mr_current];
648
0
  p = op(mr, ms->message_size);
649
0
  if (p) {
650
0
    MPRINT("RING FIND normal\n");
651
0
    return p;
652
0
  }
653
654
0
  MPRINT("CLEANING UP (%d - %d = %d)\n", ms->allocated, ms->freed,
655
0
    ms->allocated - ms->freed);
656
657
  /*
658
   *  Else the buffer is full.  Do a global cleanup.
659
   */
660
0
  message_gc(ms, 128);
661
0
  *p_cleaned = true;
662
663
  /*
664
   *  If we're lucky, the cleanup has given us a new
665
   *  "current" buffer, which is empty.  If so, use it.
666
   *
667
   *  Do a small per-ring GC first to free up the oldest
668
   *  entries.  This results in a small amount of cache line
669
   *  thrashing, but if the buffer is full, it's likely
670
   *  that the oldest entry can be freed.
671
   */
672
0
  mr = ms->mr_array[ms->mr_current];
673
0
  (void) message_ring_gc(ms, mr, 4);
674
0
  p = op(mr, ms->message_size);
675
0
  if (p) {
676
0
    MPRINT("RING FIND after cleanup\n");
677
0
    return p;
678
0
  }
679
680
  /*
681
   *  We've tried two allocations, and both failed.  Brute
682
   *  force over all arrays, trying to allocate one
683
   *  somewhere... anywhere.  We start from the largest
684
   *  array, because that is the one we want to use the
685
   *  most.
686
   *
687
   *  We want to avoid allocations in the smallest array,
688
   *  because that array will quickly wrap, and will cause
689
   *  us to do cleanups more often.  That also lets old
690
   *  entries in the smallest array age out, so that we can
691
   *  free the smallest arrays.
692
   */
693
0
  for (i = ms->mr_max; i >= 0; i--) {
694
0
    mr = ms->mr_array[i];
695
696
0
    (void) message_ring_gc(ms, mr, 4);
697
0
    p = op(mr, ms->message_size);
698
0
    if (p) {
699
0
      ms->mr_current = i;
700
0
      MPRINT("RING FIND from changed ring buffer\n");
701
0
      MPRINT("SET MR to changed %d\n", ms->mr_current);
702
0
      return p;
703
0
    }
704
0
  }
705
706
  /*
707
   *  All of the arrays are full.  If we don't have
708
   *  room to allocate another array, we're dead.
709
   */
710
0
  if ((ms->mr_max + 1) >= MSG_ARRAY_SIZE) {
711
0
    fr_strerror_const("All message arrays are full");
712
0
    return NULL;
713
0
  }
714
715
  /*
716
   *  Allocate another message ring, double the size
717
   *  of the previous maximum.
718
   */
719
0
  mr = fr_ring_buffer_create(ms, fr_ring_buffer_size(ms->mr_array[ms->mr_max]) * 2);
720
0
  if (!mr) {
721
0
    fr_strerror_const_push("Failed allocating ring buffer");
722
0
    return NULL;
723
0
  }
724
725
  /*
726
   *  Set the new one as current for all new
727
   *  allocations, and perform the operation on the
728
   *  entirely empty message ring.
729
   */
730
0
  ms->mr_max++;
731
0
  ms->mr_current = ms->mr_max;
732
0
  ms->mr_array[ms->mr_max] = mr;
733
734
0
  MPRINT("SET MR to doubled %d\n", ms->mr_current);
735
736
0
  return op(mr, ms->message_size);
737
0
}
738
739
/** Allocate a fr_message_t, WITHOUT a ring buffer
740
 *
741
 * @param[in] ms the message set
742
 * @param[out] p_cleaned a flag to indicate if we cleaned the message array
743
 * @return
744
 *      - NULL on error
745
 *  - fr_message_t* on success
746
 */
747
static inline CC_HINT(always_inline) fr_message_t *message_alloc(fr_message_set_t *ms, bool *p_cleaned)
748
0
{
749
0
  fr_message_t *m;
750
0
751
0
  m = (fr_message_t *)_message_ring_find(p_cleaned, ms, fr_ring_buffer_alloc);
752
0
  if (!m) return NULL;
753
0
754
0
  memset(m, 0, ms->message_size);
755
0
  m->status = FR_MESSAGE_USED;
756
0
  ms->allocated++;
757
0
758
0
  return m;
759
0
}
760
761
/** Reserve a fr_message_t, WITHOUT a ring buffer
762
 *
763
 * @note This only _reserves_ the message, i.e. checks there's space, and initialises the message
764
 *   memory, it does not perform the actual allocation.  This is useful if there are error
765
 *   paths which could result in the message needing to be returned to the message set without
766
 *   being used.  Multiple pending reservations are NOT permitted.
767
 *
768
 * @param[in] ms the message set to reserve the message in.
769
 * @param[out] p_cleaned a flag to indicate if we cleaned the message array
770
 * @return
771
 *      - NULL on error
772
 *  - fr_message_t* on success
773
 */
774
static inline CC_HINT(always_inline) fr_message_t *message_reserve(fr_message_set_t *ms, bool *p_cleaned)
775
0
{
776
0
  fr_message_t *m;
777
778
0
  m = (fr_message_t *)_message_ring_find(p_cleaned, ms, fr_ring_buffer_reserve);
779
0
  if (!m) return NULL;
780
781
0
  memset(m, 0, ms->message_size);
782
0
  m->status = FR_MESSAGE_USED;
783
784
0
  return m;
785
0
}
786
787
/** Finalise a reserved message allocation
788
 *
789
 * @note This finalises the allocation of a previous message.  No additional reserve calls can be made
790
 *   between the one that initialised memory for m, and this finalise call.
791
 *
792
 * @param[in] ms the message set to finalise the allocation in.
793
 * @param[in] m the message to perform the allocation for.
794
 * @return
795
 *      - <0 on error
796
 *  - 0 on success.
797
 */
798
static inline CC_HINT(always_inline) int message_finalise(fr_message_set_t *ms, fr_message_t *m)
799
{
800
  bool cleaned = false;
801
  fr_message_t *nm;
802
803
  /*
804
   *  This _should_ use the same ring as before, unless someone has
805
   *  interleaved _another_ reserve call between the initial reserve
806
   *  call and this finalisation.
807
   */
808
  nm = (fr_message_t *)_message_ring_find(&cleaned, ms, fr_ring_buffer_alloc);
809
  if (!fr_cond_assert_msg(nm == m, "Alloc interleaved between reserving and finalising message allocation %p", m)) {
810
    return -1;
811
  }
812
813
  ms->allocated++;
814
815
  return 0;
816
}
817
818
/** Reserve data in a ring buffer for this message
819
 *
820
 * @param[in] ms the message set
821
 * @param[in] m the message
822
 * @param[in] cleaned_up whether the message set was partially garbage collected
823
 * @return
824
 *  - NULL on error, and m is deallocated
825
 *  - m on success
826
 */
827
static fr_message_t *message_data_reserve(fr_message_set_t *ms, fr_message_t *m, bool cleaned_up)
828
0
{
829
0
  int i;
830
0
  fr_ring_buffer_t *rb;
831
0
  size_t alloc_size;
832
833
  /*
834
   *  And... we go through a bunch of hoops, all over again.
835
   */
836
0
  m->rb = ms->rb_array[ms->rb_current];
837
0
  fr_assert(m->rb != NULL);
838
0
  m->data = fr_ring_buffer_reserve(m->rb, m->rb_size);
839
0
  if (m->data) return m;
840
841
  /*
842
   *  When the simple allocation fails, ensure we don't do
843
   *  the cleanup twice in one allocation.
844
   */
845
0
  if (!cleaned_up) {
846
    /*
847
     *  If we run out of room in the current ring
848
     *  buffer, AND it's our only one, then just
849
     *  double it in size.
850
     */
851
0
    if (ms->rb_max == 0) goto alloc_rb;
852
853
    /*
854
     *  We're using multiple ring buffers, and we
855
     *  haven't already done a cleanup.  Force a
856
     *  cleanup.
857
     */
858
0
    MPRINT("CLEANED UP BECAUSE OF RING BUFFER (%d - %d = %d)\n", ms->allocated, ms->freed,
859
0
      ms->allocated - ms->freed);
860
861
0
    message_gc(ms, 128);
862
863
    /*
864
     *  Try to allocate the packet from the newly current ring buffer.
865
     */
866
0
    m->rb = ms->rb_array[ms->rb_current];
867
0
    fr_assert(m->rb != NULL);
868
0
    m->data = fr_ring_buffer_reserve(m->rb, m->rb_size);
869
0
    if (m->data) return m;
870
871
0
    MPRINT("CLEANUP RING BUFFER FAILED\n");
872
0
  }
873
874
  /*
875
   *  We've tried two allocations, and both failed.  Brute
876
   *  force over all arrays, trying to allocate one
877
   *  somewhere... anywhere.  We start from the largest
878
   *  array, because that is the one we want to use the
879
   *  most.
880
   *
881
   *  We want to avoid allocations in the smallest array,
882
   *  because that array will quickly wrap, and will cause
883
   *  us to do cleanups more often.  That also lets old
884
   *  entries in the smallest array age out, so that we can
885
   *  free the smallest arrays.
886
   */
887
0
  for (i = ms->rb_max; i >= 0; i--) {
888
0
    m->rb = ms->rb_array[i];
889
0
    fr_assert(m->rb != NULL);
890
0
    m->data = fr_ring_buffer_reserve(m->rb, m->rb_size);
891
0
    if (m->data) {
892
0
      MPRINT("MOVED TO RING BUFFER %d\n", i);
893
0
      ms->rb_current = i;
894
0
      return m;
895
0
    }
896
0
  }
897
898
  /*
899
   *  All of the arrays are full.  If we don't have
900
   *  room to allocate another array, we're dead.
901
   */
902
0
  if ((ms->rb_max + 1) >= MSG_ARRAY_SIZE) {
903
0
    fr_strerror_const("Message arrays are full");
904
0
    goto cleanup;
905
0
  }
906
907
0
alloc_rb:
908
  /*
909
   *  Allocate another message ring, double the size
910
   *  of the previous maximum, or large enough to hold
911
   *  twice the requested size if that is larger.
912
   *  fr_ring_buffer_create will round to the next power of 2.
913
   */
914
0
  alloc_size = fr_ring_buffer_size(ms->rb_array[ms->rb_max]) * 2;
915
0
  if (alloc_size < m->rb_size) {
916
0
    alloc_size = m->rb_size * 2;
917
0
  }
918
0
  rb = fr_ring_buffer_create(ms, alloc_size);
919
0
  if (!rb) {
920
0
    fr_strerror_const_push("Failed allocating ring buffer");
921
0
    goto cleanup;
922
0
  }
923
924
0
  MPRINT("RING BUFFER DOUBLES\n");
925
926
  /*
927
   *  Set the new one as current for all new
928
   *  allocations, allocate a message, and go try to
929
   *  reserve room for the raw packet data.
930
   */
931
0
  ms->rb_max++;
932
0
  ms->rb_current = ms->rb_max;
933
0
  ms->rb_array[ms->rb_current] = rb;
934
935
  /*
936
   *  And we should now have an entirely empty message ring.
937
   */
938
0
  m->rb = rb;
939
0
  m->data = fr_ring_buffer_reserve(m->rb, m->rb_size);
940
0
  if (m->data) return m;
941
942
0
cleanup:
943
0
  MPRINT("OUT OF MEMORY\n");
944
945
0
  m->rb = NULL;
946
0
  m->status = FR_MESSAGE_DONE;
947
0
  return NULL;
948
0
}
949
950
951
/** Reserve a message
952
 *
953
 *  A later call to fr_message_and_data_commit() will commit the reservation.
954
 *  This call just reserves a message header and space for the packet data.
955
 *
956
 *  If the caller later decides that the message is not needed, he
957
 *  should call fr_message_free() to free the message.
958
 *
959
 *  We assume that the caller will call fr_message_and_data_reserve(), and then
960
 *  almost immediately fr_message_and_data_commit().  Multiple calls in series
961
 *  to fr_message_and_data_reserve() MUST NOT be done.  The caller could also
962
 *  just call fr_ring_buffer_alloc(m->rb, size) if they wanted, and
963
 *  then update m->data_size by hand...
964
 *
965
 *  The message is returned
966
 *
967
 * @param[in] ms the message set
968
 * @param[in] reserve_size to reserve
969
 * @return
970
 *      - NULL on error
971
 *  - fr_message_t* on success
972
 */
973
fr_message_t *fr_message_and_data_reserve(fr_message_set_t *ms, size_t reserve_size)
974
0
{
975
0
  bool cleaned_up;
976
0
  fr_message_t *m;
977
978
0
  (void) talloc_get_type_abort(ms, fr_message_set_t);
979
980
0
  if (reserve_size > ms->max_allocation) {
981
0
    fr_strerror_printf("Cannot reserve %zd > max allocation %zd\n", reserve_size, ms->max_allocation);
982
0
    return NULL;
983
0
  }
984
985
  /*
986
   *  Reserve a bare message.
987
   */
988
0
  m = message_reserve(ms, &cleaned_up);
989
0
  if (!m) {
990
0
    MPRINT("Failed to reserve message\n");
991
0
    return NULL;
992
0
  }
993
994
  /*
995
   *  If the caller is not allocating any packet data, just
996
   *  return the empty message.
997
   */
998
0
  if (!reserve_size) return m;
999
1000
  /*
1001
   *  We leave m->data_size as zero, and m->rb_size as the
1002
   *  reserved size.  This indicates that the message has
1003
   *  reserved room for the packet data, but nothing has
1004
   *  been allocated.
1005
   */
1006
0
  CACHE_ALIGN(reserve_size);
1007
0
  m->rb_size = reserve_size;
1008
1009
0
  return message_data_reserve(ms, m, cleaned_up);
1010
0
}
1011
1012
/** Cancel a reservation made by fr_message_and_data_reserve(), returning the slot to the set.
1013
 *
1014
 *  Both rings are uncommitted at this point (write_offset was not advanced).
1015
 *  Clearing the message fields is sufficient: the next reserve overwrites the
1016
 *  slot, and the data ring's reserved field is replaced by the new reservation size.
1017
 *
1018
 *  Callers MUST verify m->data_size == 0 before calling this.  If a concurrent
1019
 *  fr_message_and_data_alloc() aliased this reservation (same ring slot, same
1020
 *  data address), m->data_size will be non-zero and the slot is already committed;
1021
 *  calling this function in that case corrupts the committed message.
1022
 *
1023
 * @param[in] ms  the message set the reservation was made against.
1024
 * @param[in] m   the previously reserved message to cancel.
1025
 */
1026
void fr_message_and_data_reset(fr_message_set_t *ms, fr_message_t *m)
1027
0
{
1028
0
  (void) talloc_get_type_abort(ms, fr_message_set_t);
1029
0
  fr_assert(m->status == FR_MESSAGE_USED);
1030
0
  m->status = FR_MESSAGE_FREE;
1031
0
  m->rb = NULL;
1032
0
  m->data = NULL;
1033
0
  m->data_size = 0;
1034
0
  m->rb_size = 0;
1035
0
}
1036
1037
/** Commit a previously reserved message, allocating exactly total_size bytes of packet data.
1038
 *
1039
 *  Commits both the message ring slot and the data ring buffer.  total_size is the
1040
 *  final packet size; it may be less than the original reservation.  For streaming
1041
 *  reads where m->data_size is already non-zero (leftover bytes from a prior read),
1042
 *  total_size must include those bytes.
1043
 *
1044
 * @param[in] ms the message set
1045
 * @param[in] m the previously reserved message (m is NOT talloc'd)
1046
 * @param[in] total_size final packet size in bytes
1047
 * @return
1048
 *      - NULL on error, input message m is left alone
1049
 *  - m on success
1050
 */
1051
fr_message_t *fr_message_and_data_commit(fr_message_set_t *ms, fr_message_t *m, size_t total_size)
1052
0
{
1053
0
  uint8_t *p;
1054
0
  size_t align_size;
1055
1056
0
  (void) talloc_get_type_abort(ms, fr_message_set_t);
1057
1058
  /* m is NOT talloc'd */
1059
0
  if (!m) {
1060
0
    m = fr_message_and_data_reserve(ms, total_size);
1061
0
    if (!m) return NULL;
1062
0
  }
1063
1064
0
  fr_assert(m->status == FR_MESSAGE_USED);
1065
0
  fr_assert(m->rb_size >= total_size);
1066
1067
0
  if (total_size == 0) {
1068
0
    m->data = NULL;
1069
0
    m->rb = NULL;
1070
0
    m->data_size = m->rb_size = 0;
1071
0
    message_finalise(ms, m);
1072
0
    return m;
1073
0
  }
1074
1075
0
  fr_assert(m->rb != NULL);
1076
0
  fr_assert(m->data != NULL);
1077
1078
0
  align_size = total_size;
1079
0
  CACHE_ALIGN(align_size);
1080
1081
0
  p = fr_ring_buffer_alloc(m->rb, align_size);
1082
0
  fr_assert(p != NULL);
1083
0
  if (!p) {
1084
0
    fr_strerror_const_push("Failed allocating from ring buffer");
1085
0
    return NULL;
1086
0
  }
1087
1088
0
  fr_assert(p == m->data);
1089
1090
0
  m->data_size = total_size;
1091
0
  m->rb_size = align_size;
1092
0
  message_finalise(ms, m);
1093
1094
0
  return m;
1095
0
}
1096
1097
/** Reserve and commit a message atomically.
1098
 *
1099
 *  Combines fr_message_and_data_reserve() and fr_message_and_data_commit() into a
1100
 *  single call for callers that know the exact packet size upfront.
1101
 *
1102
 * @param[in] ms the message set
1103
 * @param[in] size packet size in bytes
1104
 * @return
1105
 *      - NULL on error
1106
 *  - fr_message_t* on success
1107
 */
1108
fr_message_t *fr_message_and_data_alloc(fr_message_set_t *ms, size_t size)
1109
0
{
1110
0
  fr_message_t *m;
1111
1112
0
  m = fr_message_and_data_reserve(ms, size);
1113
0
  if (!m) return NULL;
1114
1115
0
  return fr_message_and_data_commit(ms, m, size);
1116
0
}
1117
1118
/** Allocate packet data for a message, and reserve a new message
1119
 *
1120
 *  This function allocates a previously reserved message, and then
1121
 *  reserves a new message.
1122
 *
1123
 *  The application should call fr_message_and_data_reserve() with a large
1124
 *  buffer, and then read data into the buffer.  If the buffer
1125
 *  contains multiple packets, the application should call
1126
 *  fr_message_and_data_commit_with_leftover() repeatedly to allocate the full
1127
 *  packets, while reserving room for the partial packet.
1128
 *
1129
 *  When the application is determines that there is only one full
1130
 *  packet, and one partial packet in the buffer, it should call this
1131
 *  function with actual_packet_size, and a large reserve_size.  The
1132
 *  partial packet will be reserved.  If the ring buffer is full, the
1133
 *  partial packet will be copied to a new ring buffer.
1134
 *
1135
 *  When the application determines that there are multiple full
1136
 *  packets in the buffer, it should call this function with
1137
 *  actual_packet_size for each buffer, and reserve_size which
1138
 *  reserves all of the data in the buffer.  i.e. the full packets +
1139
 *  partial packets, which should start off as the original
1140
 *  reserve_size.
1141
 *
1142
 *  The application should call this function to allocate each packet,
1143
 *  while decreasing reserve_size by each actual_packet_size that was
1144
 *  allocated.  Once there is only one full and a partial packet in
1145
 *  the buffer, it should use a large reserve_size, as above.
1146
 *
1147
 *  The application could just always ecall this function with a large
1148
 *  reserve_size, at the cost of substantially more memcpy()s.
1149
 *
1150
 * @param[in] ms the message set
1151
 * @param[in] m the message message to allocate packet data for
1152
 * @param[in] actual_packet_size to use
1153
 * @param[in] leftover "dirty" bytes in the buffer
1154
 * @param[in] reserve_size to reserve for new message
1155
 * @return
1156
 *      - NULL on error, and input message m is left alone
1157
 *  - fr_message_t* on success.  Will always be a new message.
1158
 */
1159
fr_message_t *fr_message_and_data_commit_with_leftover(fr_message_set_t *ms, fr_message_t *m,
1160
               size_t actual_packet_size,
1161
               size_t leftover, size_t reserve_size)
1162
0
{
1163
0
  bool cleaned_up;
1164
0
  uint8_t *p;
1165
0
  fr_message_t *m2;
1166
0
  size_t m_rb_size, align_size;
1167
1168
0
  (void) talloc_get_type_abort(ms, fr_message_set_t);
1169
1170
0
  align_size = actual_packet_size;
1171
0
  CACHE_ALIGN(align_size);
1172
1173
  /* m is NOT talloc'd */
1174
1175
0
  fr_assert(m->status == FR_MESSAGE_USED);
1176
0
  fr_assert(m->rb != NULL);
1177
0
  fr_assert(m->data != NULL);
1178
0
  fr_assert(m->rb_size >= actual_packet_size);
1179
1180
0
  p = fr_ring_buffer_alloc(m->rb, align_size);
1181
0
  fr_assert(p != NULL);
1182
0
  if (!p) {
1183
0
    fr_strerror_const_push("Failed allocating from ring buffer");
1184
0
    return NULL;
1185
0
  }
1186
1187
0
  fr_assert(p == m->data);
1188
1189
0
  m_rb_size = m->rb_size; /* for ring buffer cleanups */
1190
0
  m->data_size = actual_packet_size;
1191
0
  m->rb_size = align_size;
1192
0
  message_finalise(ms, m);
1193
1194
  /*
1195
   *  If we've allocated all of the reserved ring buffer
1196
   *  data, then just reserve a brand new reservation.
1197
   *
1198
   *  This will be automatically cache aligned.
1199
   */
1200
0
  if (!leftover) return fr_message_and_data_reserve(ms, reserve_size);
1201
1202
  /*
1203
   *  Reserve a new message.
1204
   */
1205
0
  m2 = message_reserve(ms, &cleaned_up);
1206
0
  if (!m2) return NULL;
1207
1208
  /*
1209
   *  Ensure that there's enough room to shift the next
1210
   *  packet, so that it's cache aligned.  Moving small
1211
   *  amounts of memory is likely faster than having two
1212
   *  CPUs fight over the same cache lines.
1213
   */
1214
0
  reserve_size += (align_size - actual_packet_size);
1215
0
  CACHE_ALIGN(reserve_size);
1216
1217
  /*
1218
   *  Track how much data there is in the packet.
1219
   */
1220
0
  m2->rb = m->rb;
1221
0
  m2->data_size = leftover;
1222
0
  m2->rb_size = reserve_size;
1223
1224
  /*
1225
   *  Try to extend the reservation.  If we can do it,
1226
   *  return.
1227
   */
1228
0
  m2->data = fr_ring_buffer_reserve(m2->rb, reserve_size);
1229
0
  if (m2->data) {
1230
    /*
1231
     *  The next packet pointer doesn't point to the
1232
     *  actual data after the current packet.  Move
1233
     *  the next packet to match up with the ring
1234
     *  buffer allocation.
1235
     */
1236
0
    if (m2->data != (m->data + actual_packet_size)) {
1237
0
      memmove(m2->data, m->data + actual_packet_size, leftover);
1238
0
    }
1239
0
    return m2;
1240
0
  }
1241
1242
  /*
1243
   *  We failed reserving more memory at the end of the
1244
   *  current ring buffer.
1245
   *
1246
   *  Reserve data from a new ring buffer.  If it doesn't
1247
   *  succeed, ensure that the old message will properly
1248
   *  clean up the old ring buffer.
1249
   */
1250
0
  if (!message_data_reserve(ms, m2, false)) {
1251
0
    m->rb_size = m_rb_size;
1252
0
    return NULL;
1253
0
  }
1254
1255
  /*
1256
   *  If necessary, copy the remaining data from the old
1257
   *  buffer to the new one.
1258
   */
1259
0
  if (m2->data != (m->data + actual_packet_size)) {
1260
0
    memmove(m2->data, m->data + actual_packet_size, leftover);
1261
0
  }
1262
1263
  /*
1264
   *  The messages are in different ring buffers.  We've
1265
   *  aligned m->rb_size above for the current packet, but
1266
   *  there's no subsequent message to clean up this
1267
   *  reservation.  Re-extend the current message to it's
1268
   *  original size, so that cleaning it up will clean up the ring buffer.
1269
   */
1270
0
  if (m2->rb != m->rb) {
1271
0
    m->rb_size = m_rb_size;
1272
0
    return m2;
1273
0
  }
1274
1275
  /*
1276
   *  If we've managed to allocate the next message in the
1277
   *  current ring buffer, then it really should have
1278
   *  wrapped around.  In which case, re-extend the current
1279
   *  message as above.
1280
   */
1281
0
  if (m2->data < m->data) {
1282
0
    m->rb_size = m_rb_size;
1283
0
    return m2;
1284
0
  }
1285
1286
0
  return m2;
1287
0
}
1288
1289
/** Count the number of used messages
1290
 *
1291
 * @param[in] ms the message set
1292
 * @return
1293
 *      - number of used messages
1294
 */
1295
int fr_message_set_messages_used(fr_message_set_t *ms)
1296
0
{
1297
0
  int i, used;
1298
1299
0
  (void) talloc_get_type_abort(ms, fr_message_set_t);
1300
1301
0
  used = 0;
1302
0
  for (i = 0; i <= ms->mr_max; i++) {
1303
0
    fr_ring_buffer_t *mr;
1304
1305
0
    mr = ms->mr_array[i];
1306
1307
0
    used += fr_ring_buffer_used(mr) / ms->message_size;
1308
0
  }
1309
1310
0
  return used;
1311
0
}
1312
1313
/** Garbage collect the message set.
1314
 *
1315
 *  This function should ONLY be called just before freeing the
1316
 *  message set.  It is intended only for debugging, and will cause
1317
 *  huge latency spikes if used at run time.
1318
 *
1319
 * @param[in] ms the message set
1320
 */
1321
void fr_message_set_gc(fr_message_set_t *ms)
1322
0
{
1323
0
  int i;
1324
1325
0
  (void) talloc_get_type_abort(ms, fr_message_set_t);
1326
1327
  /*
1328
   *  Manually clean up each message ring.
1329
   */
1330
0
  for (i = 0; i <= ms->mr_max; i++) {
1331
0
    (void) message_ring_gc(ms, ms->mr_array[i], INT_MAX);
1332
0
  }
1333
1334
  /*
1335
   *  And then do one last pass to clean up the arrays.
1336
   */
1337
0
  message_gc(ms, INT_MAX);
1338
0
}
1339
1340
/** Print debug information about the message set.
1341
 *
1342
 * @param[in] ms the message set
1343
 * @param[in] fp the FILE where the messages are printed.
1344
 */
1345
void fr_message_set_debug(FILE *fp, fr_message_set_t *ms)
1346
0
{
1347
0
  int i;
1348
1349
0
  (void) talloc_get_type_abort(ms, fr_message_set_t);
1350
1351
0
  fprintf(fp, "message arrays = %d\t(current %d)\n", ms->mr_max + 1, ms->mr_current);
1352
0
  fprintf(fp, "ring buffers   = %d\t(current %d)\n", ms->rb_max + 1, ms->rb_current);
1353
1354
0
  for (i = 0; i <= ms->mr_max; i++) {
1355
0
    fr_ring_buffer_t *mr = ms->mr_array[i];
1356
1357
0
    fprintf(fp, "messages[%d] =\tsize %zu, used %zu\n",
1358
0
      i, fr_ring_buffer_size(mr), fr_ring_buffer_used(mr));
1359
0
  }
1360
1361
0
  for (i = 0; i <= ms->rb_max; i++) {
1362
0
    fprintf(fp, "ring buffer[%d] =\tsize %zu, used %zu\n",
1363
0
      i, fr_ring_buffer_size(ms->rb_array[i]), fr_ring_buffer_used(ms->rb_array[i]));
1364
0
  }
1365
0
}