The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
ring_buffer.c
Go to the documentation of this file.
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: 3589c946da23712a0b0d8a251f91b9b6ba610e80 $
19  *
20  * @brief Simple ring buffers for packet contents
21  * @file io/ring_buffer.c
22  *
23  * @copyright 2016 Alan DeKok (aland@freeradius.org)
24  */
25 RCSID("$Id: 3589c946da23712a0b0d8a251f91b9b6ba610e80 $")
26 
27 #include <freeradius-devel/io/ring_buffer.h>
28 #include <freeradius-devel/util/strerror.h>
29 #include <freeradius-devel/util/debug.h>
30 #include <string.h>
31 
32 /*
33  * Ring buffers are allocated in a block.
34  */
36  uint8_t *buffer; //!< actual start of the ring buffer
37  size_t size; //!< Size of this ring buffer
38 
39  size_t data_start; //!< start of used portion of the buffer
40  size_t data_end; //!< end of used portion of the buffer
41 
42  size_t write_offset; //!< where writes are done
43  size_t reserved; //!< amount of reserved data at write_offset
44 
45  bool closed; //!< whether allocations are closed
46 };
47 
48 /** Create a ring buffer.
49  *
50  * The size provided will be rounded up to the next highest power of
51  * 2, if it's not already a power of 2.
52  *
53  * The ring buffer manages how much room is reserved (i.e. available
54  * to write to), and used. The application is responsible for
55  * tracking the start of the reservation, *and* it's write offset
56  * within that reservation.
57  *
58  * @param[in] ctx a talloc context
59  * @param[in] size of the raw ring buffer array to allocate.
60  * @return
61  * - A new ring buffer on success.
62  * - NULL on failure.
63  */
64 fr_ring_buffer_t *fr_ring_buffer_create(TALLOC_CTX *ctx, size_t size)
65 {
67 
68  rb = talloc_zero(ctx, fr_ring_buffer_t);
69  if (!rb) {
70  fail:
71  fr_strerror_const("Failed allocating memory.");
72  return NULL;
73  }
74 
75  if (size < 1024) size = 1024;
76 
77  if (size > (1 << 30)) {
78  fr_strerror_const("Ring buffer size must be no more than (1 << 30)");
79  talloc_free(rb);
80  return NULL;
81  }
82 
83  /*
84  * Round up to the nearest power of 2.
85  */
86  size--;
87  size |= size >> 1;
88  size |= size >> 2;
89  size |= size >> 4;
90  size |= size >> 8;
91  size |= size >> 16;
92  size++;
93 
94  rb->buffer = talloc_array(rb, uint8_t, size);
95  if (!rb->buffer) {
96  talloc_free(rb);
97  goto fail;
98  }
99  rb->size = size;
100 
101  return rb;
102 }
103 
104 
105 /** Reserve room in the ring buffer.
106  *
107  * The size does not need to be a power of two. The application is
108  * responsible for doing cache alignment, if required.
109  *
110  * If the reservation fails, the application should create a new ring
111  * buffer, and start reserving data there.
112  *
113  * @param[in] rb a ring buffer
114  * @param[in] size to see if we can reserve
115  * @return
116  * - NULL on error. Which can only be "ring buffer is full".
117  * - pointer to data on success
118  */
120 {
121  (void) talloc_get_type_abort(rb, fr_ring_buffer_t);
122 
123  if (rb->closed) {
124  fr_strerror_const("Allocation request after ring buffer is closed");
125  return NULL;
126  }
127 
128  /*
129  * We're writing to the start of the buffer, and there is
130  * already data in it. See if the data fits.
131  *
132  * |***W....S****E....|
133  */
134  if (rb->write_offset < rb->data_start) {
135  if ((rb->write_offset + size) < rb->data_start) {
136  rb->reserved = size;
137  return rb->buffer + rb->write_offset;
138  }
139 
140  fr_strerror_const("No memory available in ring buffer");
141  return NULL;
142  }
143 
145 
146  /*
147  * Data fits at the end of the ring buffer.
148  *
149  * |....S****WE....|
150  */
151  if ((rb->write_offset + size) <= rb->size) {
152  rb->reserved = size;
153  return rb->buffer + rb->write_offset;
154  }
155 
156  /*
157  * Data fits at the start of the ring buffer, ensure that
158  * we write it there. This also catches the case where
159  * data_start==0.
160  *
161  * |W....S****E....|
162  */
163  if (size < rb->data_start) {
164  rb->write_offset = 0;
165  rb->reserved = size;
166  return rb->buffer;
167  }
168 
169  /*
170  * Not enough room for the new data, fail the allocation.
171  *
172  * |....S****WE....|
173  */
174  fr_strerror_const("No memory available in ring buffer");
175  return NULL;
176 }
177 
178 
179 /** Mark data as allocated.
180  *
181  * The size does not need to be a power of two. The application is
182  * responsible for doing cache-line alignment, if required.
183  *
184  * The application does NOT need to call fr_ring_buffer_reserve() before
185  * calling this function.
186  *
187  * If the allocation fails, the application should create a new ring
188  * buffer, and start allocating data there.
189  *
190  * @param[in] rb a ring buffer
191  * @param[in] size to mark as "used" at the tail end of the buffer.
192  * @return
193  * - NULL on error. Which can only be "ring buffer is full".
194  * - pointer to data on success
195  */
197 {
198  uint8_t *p;
199 
200  (void) talloc_get_type_abort(rb, fr_ring_buffer_t);
201 
202  if (rb->closed) {
203 #ifndef NDEBUG
204  fr_strerror_const("Allocation request after ring buffer is closed");
205 #endif
206  return NULL;
207  }
208 
209  /*
210  * Shrink the "reserved" portion of the buffer by the
211  * allocated size.
212  */
213  if (rb->reserved >= size) {
214  rb->reserved -= size;
215  } else {
216  rb->reserved = 0;
217  }
218 
219  /*
220  * We're writing to the start of the buffer, and there is
221  * already data in it. See if the data fits.
222  *
223  * |***W....S****E....|
224  */
225  if (rb->write_offset < rb->data_start) {
226  if ((rb->write_offset + size) < rb->data_start) {
227  p = rb->buffer + rb->write_offset;
228  rb->write_offset += size;
229  return p;
230  }
231 
232 #ifndef NDEBUG
233  fr_strerror_const("No memory available in ring buffer");
234 #endif
235  return NULL;
236  }
237 
239 
240  /*
241  * Data fits at the end of the ring buffer.
242  *
243  * |....S****WE....|
244  */
245  if ((rb->write_offset + size) <= rb->size) {
246  p = rb->buffer + rb->write_offset;
247 
248  rb->write_offset += size;
250 
251  /*
252  * Don't update write_offset if it's fallen off
253  * of the end of the buffer. The data_start may
254  * be zero, and we don't want to over-write
255  * that.
256  */
257  return p;
258  }
259 
260  /*
261  * Data fits at the start of the ring buffer, ensure that
262  * we write it there. This also catches the case where
263  * data_start==0.
264  *
265  * |W....S****E....|
266  */
267  if (size < rb->data_start) {
268  rb->write_offset = size;
269 
270  /*
271  * Don't update data_end. It points to the tail
272  * end of the ring buffer.
273  */
274  return rb->buffer;
275  }
276 
277  /*
278  * Not enough room for the new data, fail the allocation.
279  *
280  * |....S****WE....|
281  */
282 #ifndef NDEBUG
283  fr_strerror_const("No memory available in ring buffer");
284 #endif
285  return NULL;
286 }
287 
288 /** Mark data as free,
289  *
290  * The size does not need to be a power of two. The application is
291  * responsible for doing cache alignment, if required. The
292  * application is responsible for tracking sizes of packets in the
293  * ring buffer.
294  *
295  * If "unused" bytes are more than what's in the buffer, the used
296  * bytes are reset to zero.
297  *
298  * @param[in] rb a ring buffer
299  * @param[in] size_to_free bytes to mark as "unused" in the buffer.
300  * @return
301  * - <0 on error. Which can only be "ring buffer is full".
302  * - 0 on success
303  */
304 int fr_ring_buffer_free(fr_ring_buffer_t *rb, size_t size_to_free)
305 {
306  size_t block_size;
307 
308  (void) talloc_get_type_abort(rb, fr_ring_buffer_t);
309 
310  /*
311  * Nothing to free, do nothing.
312  */
313  if (!size_to_free) return 0;
314 
315  /*
316  * Freeing data from the middle of the buffer.
317  *
318  * |***W....S****E....|
319  */
320  if (rb->write_offset < rb->data_start) {
321  block_size = rb->data_end - rb->data_start;
322 
323  /*
324  * |***W....S****E....|, free 3
325  *
326  * |***W.......S*E....|
327  */
328  if (size_to_free < block_size) {
329  rb->data_start += size_to_free;
330  return 0;
331  }
332 
333  /*
334  * Free all (or more than) the block.
335  */
336  rb->data_start = 0;
338  size_to_free -= block_size;
339 
340  /*
341  * Free everything left: empty the buffer
342  * entirely. This also handles the case of
343  * size_to_free==0 and write_offset==0.
344  */
345  if (size_to_free == rb->write_offset) {
346  goto empty_buffer;
347  }
348 
349  /*
350  * The buffer has data but we're not freeing
351  * any more of it, return.
352  */
353  if (!size_to_free) return 0;
354  }
355 
357 
358  block_size = rb->data_end - rb->data_start;
359 
360  /*
361  * Freeing too much, return an error.
362  */
363  if (size_to_free > block_size) {
364  fr_strerror_const("Cannot free more memory than exists.");
365  return -1;
366  }
367 
368  /*
369  * Free some data from the start.
370  */
371  if (size_to_free < block_size) {
372  rb->data_start += size_to_free;
373  return 0;
374  }
375 
376  /*
377  * Free all data in the buffer.
378  */
379 empty_buffer:
380  rb->data_start = 0;
381  rb->data_end = 0;
382  rb->write_offset = 0;
383 
384  /*
385  * If the ring buffer is closed to all allocations, and
386  * it's now empty, we automatically free it.
387  */
388  if (rb->closed) talloc_free(rb);
389 
390  return 0;
391 }
392 
393 /** Close a ring buffer so that no further allocations can take place.
394  *
395  * Once the ring buffer is empty, it will be automatically freed.
396  * It's called "close" and not "delete", because the ring buffer will
397  * still be active until all data has been removed.
398  *
399  * If you don't care about the data in the ring buffer, you can just
400  * call talloc_free() on it.
401  *
402  * @param[in] rb a ring buffer
403  * @return
404  * - <0 on error.
405  * - 0 on success
406  */
408 {
409  (void) talloc_get_type_abort(rb, fr_ring_buffer_t);
410 
411  rb->closed = true;
412  return 0;
413 }
414 
415 
416 /** Get the size of the ring buffer
417  *
418  * @param[in] rb a ring buffer
419  * @return size of the ring buffer.
420  * - <0 on error.
421  * - 0 on success
422  */
424 {
425  (void) talloc_get_type_abort(rb, fr_ring_buffer_t);
426 
427  return rb->size;
428 }
429 
430 /** Get the amount of data used in a ring buffer.
431  *
432  * @param[in] rb a ring buffer
433  * @return size of the used data in the ring buffer.
434  * - <0 on error.
435  * - 0 on success
436  */
438 {
439  size_t size;
440 
441  (void) talloc_get_type_abort(rb, fr_ring_buffer_t);
442 
443  if (rb->write_offset < rb->data_start) {
444  size = rb->write_offset;
445  } else {
447  size = 0;
448  }
449 
450  size += (rb->data_end - rb->data_start);
451 
452  return size;
453 }
454 
455 /** Get a pointer to the data at the start of the ring buffer.
456  *
457  * @param[in] rb a ring buffer
458  * @param[out] p_start pointer to data at the start of the ring buffer
459  * @param[in] p_size size of the allocated block at the start of the ring buffer.
460  * @return size of the used data in the ring buffer.
461  * - <0 on error.
462  * - 0 on success
463  */
464 int fr_ring_buffer_start(fr_ring_buffer_t *rb, uint8_t **p_start, size_t *p_size)
465 {
466  (void) talloc_get_type_abort(rb, fr_ring_buffer_t);
467 
468  *p_start = rb->buffer + rb->data_start;
469 
470  if (rb->write_offset < rb->data_start) {
471  *p_size = rb->write_offset;
472  return 0;
473  }
474 
475  *p_size = (rb->data_end - rb->data_start);
476 
477  return 0;
478 }
479 
480 /** Print debug information about the ring buffer
481  *
482  * @param[in] rb the ring buffer
483  * @param[in] fp the FILE where the messages are printed.
484  */
486 {
487  fprintf(fp, "Buffer %p, write_offset %zu, data_start %zu, data_end %zu\n",
489 }
#define RCSID(id)
Definition: build.h:444
static fr_ring_buffer_t * rb
Definition: control_test.c:51
talloc_free(reap)
unsigned char uint8_t
Definition: merged_model.c:30
size_t data_start
start of used portion of the buffer
Definition: ring_buffer.c:39
size_t reserved
amount of reserved data at write_offset
Definition: ring_buffer.c:43
fr_ring_buffer_t * fr_ring_buffer_create(TALLOC_CTX *ctx, size_t size)
Create a ring buffer.
Definition: ring_buffer.c:64
size_t size
Size of this ring buffer.
Definition: ring_buffer.c:37
uint8_t * fr_ring_buffer_reserve(fr_ring_buffer_t *rb, size_t size)
Reserve room in the ring buffer.
Definition: ring_buffer.c:119
int fr_ring_buffer_free(fr_ring_buffer_t *rb, size_t size_to_free)
Mark data as free,.
Definition: ring_buffer.c:304
size_t write_offset
where writes are done
Definition: ring_buffer.c:42
bool closed
whether allocations are closed
Definition: ring_buffer.c:45
void fr_ring_buffer_debug(fr_ring_buffer_t *rb, FILE *fp)
Print debug information about the ring buffer.
Definition: ring_buffer.c:485
size_t data_end
end of used portion of the buffer
Definition: ring_buffer.c:40
int fr_ring_buffer_close(fr_ring_buffer_t *rb)
Close a ring buffer so that no further allocations can take place.
Definition: ring_buffer.c:407
size_t fr_ring_buffer_used(fr_ring_buffer_t *rb)
Get the amount of data used in a ring buffer.
Definition: ring_buffer.c:437
size_t fr_ring_buffer_size(fr_ring_buffer_t *rb)
Get the size of the ring buffer.
Definition: ring_buffer.c:423
int fr_ring_buffer_start(fr_ring_buffer_t *rb, uint8_t **p_start, size_t *p_size)
Get a pointer to the data at the start of the ring buffer.
Definition: ring_buffer.c:464
uint8_t * buffer
actual start of the ring buffer
Definition: ring_buffer.c:36
uint8_t * fr_ring_buffer_alloc(fr_ring_buffer_t *rb, size_t size)
Mark data as allocated.
Definition: ring_buffer.c:196
fr_assert(0)
#define fr_strerror_const(_msg)
Definition: strerror.h:223