/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 | } |