session: add support for memfd segments
[vpp.git] / src / svm / svm_fifo_segment.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <svm/svm_fifo_segment.h>
17
18 svm_fifo_segment_main_t svm_fifo_segment_main;
19
20 static void
21 allocate_new_fifo_chunk (svm_fifo_segment_header_t * fsh,
22                          u32 data_size_in_bytes, int chunk_size)
23 {
24   int freelist_index;
25   u32 size;
26   u8 *fifo_space;
27   u32 rounded_data_size;
28   svm_fifo_t *f;
29   int i;
30
31   rounded_data_size = (1 << (max_log2 (data_size_in_bytes)));
32   freelist_index = max_log2 (rounded_data_size)
33     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
34
35   /* Calculate space requirement $$$ round-up data_size_in_bytes */
36   size = (sizeof (*f) + rounded_data_size) * chunk_size;
37
38   /* Allocate fifo space. May fail. */
39   fifo_space = clib_mem_alloc_aligned_at_offset
40     (size, CLIB_CACHE_LINE_BYTES, 0 /* align_offset */ ,
41      0 /* os_out_of_memory */ );
42
43   /* Out of space.. */
44   if (fifo_space == 0)
45     return;
46
47   /* Carve fifo space */
48   f = (svm_fifo_t *) fifo_space;
49   for (i = 0; i < chunk_size; i++)
50     {
51       f->freelist_index = freelist_index;
52       f->next = fsh->free_fifos[freelist_index];
53       fsh->free_fifos[freelist_index] = f;
54       fifo_space += sizeof (*f) + rounded_data_size;
55       f = (svm_fifo_t *) fifo_space;
56     }
57 }
58
59 static void
60 preallocate_fifo_pairs (svm_fifo_segment_private_t * s,
61                         svm_fifo_segment_create_args_t * a, u32 protect_size)
62 {
63   svm_fifo_segment_header_t *fsh = s->h;
64   u32 rx_fifo_size, tx_fifo_size, pairs_to_allocate;
65   u32 rx_rounded_data_size, tx_rounded_data_size, pair_size;
66   svm_fifo_t *f;
67   u8 *rx_fifo_space, *tx_fifo_space;
68   int rx_freelist_index, tx_freelist_index;
69   int i;
70
71   /* Parameter check */
72   if (a->rx_fifo_size == 0 || a->tx_fifo_size == 0
73       || a->preallocated_fifo_pairs == 0)
74     return;
75
76   if (a->rx_fifo_size < FIFO_SEGMENT_MIN_FIFO_SIZE ||
77       a->rx_fifo_size > FIFO_SEGMENT_MAX_FIFO_SIZE)
78     {
79       clib_warning ("rx fifo_size out of range %d", a->rx_fifo_size);
80       return;
81     }
82
83   if (a->tx_fifo_size < FIFO_SEGMENT_MIN_FIFO_SIZE ||
84       a->tx_fifo_size > FIFO_SEGMENT_MAX_FIFO_SIZE)
85     {
86       clib_warning ("tx fifo_size out of range %d", a->rx_fifo_size);
87       return;
88     }
89
90   rx_rounded_data_size = (1 << (max_log2 (a->rx_fifo_size)));
91
92   rx_freelist_index = max_log2 (a->rx_fifo_size)
93     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
94
95   tx_rounded_data_size = (1 << (max_log2 (a->rx_fifo_size)));
96
97   tx_freelist_index = max_log2 (a->tx_fifo_size)
98     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
99
100   /* Calculate space requirements */
101   pair_size = 2 * sizeof (*f) + rx_rounded_data_size + tx_rounded_data_size;
102   if (protect_size)
103     protect_size += mheap_bytes (s->ssvm.sh->heap);
104   pairs_to_allocate =
105     clib_min ((s->ssvm.ssvm_size - protect_size) / pair_size,
106               a->preallocated_fifo_pairs);
107   rx_fifo_size = (sizeof (*f) + rx_rounded_data_size) * pairs_to_allocate;
108   tx_fifo_size = (sizeof (*f) + tx_rounded_data_size) * pairs_to_allocate;
109
110   vec_validate_init_empty (fsh->free_fifos,
111                            clib_max (rx_freelist_index, tx_freelist_index),
112                            0);
113
114   /* Allocate rx fifo space. May fail. */
115   rx_fifo_space = clib_mem_alloc_aligned_at_offset
116     (rx_fifo_size, CLIB_CACHE_LINE_BYTES, 0 /* align_offset */ ,
117      0 /* os_out_of_memory */ );
118
119   /* Same for TX */
120   tx_fifo_space = clib_mem_alloc_aligned_at_offset
121     (tx_fifo_size, CLIB_CACHE_LINE_BYTES, 0 /* align_offset */ ,
122      0 /* os_out_of_memory */ );
123
124   /* Make sure it worked. Clean up if it didn't... */
125   if (rx_fifo_space == 0 || tx_fifo_space == 0)
126     {
127       if (rx_fifo_space)
128         clib_mem_free (rx_fifo_space);
129       else
130         clib_warning ("rx fifo preallocation failure: size %d npairs %d",
131                       a->rx_fifo_size, a->preallocated_fifo_pairs);
132
133       if (tx_fifo_space)
134         clib_mem_free (tx_fifo_space);
135       else
136         clib_warning ("tx fifo preallocation failure: size %d nfifos %d",
137                       a->tx_fifo_size, a->preallocated_fifo_pairs);
138       return;
139     }
140
141   /* Carve rx fifo space */
142   f = (svm_fifo_t *) rx_fifo_space;
143   for (i = 0; i < pairs_to_allocate; i++)
144     {
145       f->freelist_index = rx_freelist_index;
146       f->next = fsh->free_fifos[rx_freelist_index];
147       fsh->free_fifos[rx_freelist_index] = f;
148       rx_fifo_space += sizeof (*f) + rx_rounded_data_size;
149       f = (svm_fifo_t *) rx_fifo_space;
150     }
151   /* Carve tx fifo space */
152   f = (svm_fifo_t *) tx_fifo_space;
153   for (i = 0; i < pairs_to_allocate; i++)
154     {
155       f->freelist_index = tx_freelist_index;
156       f->next = fsh->free_fifos[tx_freelist_index];
157       fsh->free_fifos[tx_freelist_index] = f;
158       tx_fifo_space += sizeof (*f) + tx_rounded_data_size;
159       f = (svm_fifo_t *) tx_fifo_space;
160     }
161
162   /* Account for the pairs allocated */
163   a->preallocated_fifo_pairs -= pairs_to_allocate;
164 }
165
166 /**
167  * Create an svm fifo segment and initialize as master
168  */
169 int
170 svm_fifo_segment_create (svm_fifo_segment_create_args_t * a)
171 {
172   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
173   svm_fifo_segment_private_t *s;
174   ssvm_shared_header_t *sh;
175   svm_fifo_segment_header_t *fsh;
176   void *oldheap;
177   int rv;
178
179   /* Allocate a fresh segment */
180   pool_get (sm->segments, s);
181   memset (s, 0, sizeof (*s));
182
183   s->ssvm.ssvm_size = a->segment_size;
184   s->ssvm.i_am_master = 1;
185   s->ssvm.my_pid = getpid ();
186   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
187   s->ssvm.requested_va = sm->next_baseva;
188
189   if ((rv = ssvm_master_init (&s->ssvm, a->segment_type)))
190     {
191       pool_put (sm->segments, s);
192       return (rv);
193     }
194
195   /* Note: requested_va updated due to seg base addr randomization */
196   sm->next_baseva = s->ssvm.sh->ssvm_va + a->segment_size;
197
198   sh = s->ssvm.sh;
199   oldheap = ssvm_push_heap (sh);
200
201   /* Set up svm_fifo_segment shared header */
202   fsh = clib_mem_alloc (sizeof (*fsh));
203   memset (fsh, 0, sizeof (*fsh));
204   s->h = sh->opaque[0] = fsh;
205   preallocate_fifo_pairs (s, a, a->seg_protected_space);
206
207   ssvm_pop_heap (oldheap);
208
209   sh->ready = 1;
210   vec_add1 (a->new_segment_indices, s - sm->segments);
211   return (0);
212 }
213
214 /**
215  * Create an svm fifo segment in process-private memory
216  */
217 int
218 svm_fifo_segment_create_process_private (svm_fifo_segment_create_args_t * a)
219 {
220   svm_fifo_segment_private_t *s;
221   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
222   ssvm_shared_header_t *sh;
223   svm_fifo_segment_header_t *fsh;
224   void *oldheap;
225   u8 **heaps = 0;
226   mheap_t *heap_header;
227   int segment_count = 1;
228   u32 rnd_size = 0;
229   int i;
230
231   if (a->private_segment_count)
232     {
233       u8 *heap;
234       u32 pagesize = clib_mem_get_page_size ();
235       rnd_size = (a->segment_size + (pagesize - 1)) & ~pagesize;
236
237       for (i = 0; i < a->private_segment_count; i++)
238         {
239           heap = mheap_alloc (0, rnd_size);
240           if (heap == 0)
241             {
242               clib_unix_warning ("mheap alloc");
243               return -1;
244             }
245           heap_header = mheap_header (heap);
246           heap_header->flags |= MHEAP_FLAG_THREAD_SAFE;
247           vec_add1 (heaps, heap);
248         }
249       segment_count = a->private_segment_count;
250     }
251
252   /* Allocate segments */
253   for (i = 0; i < segment_count; i++)
254     {
255       pool_get (sm->segments, s);
256       memset (s, 0, sizeof (*s));
257
258       s->ssvm.ssvm_size = rnd_size;
259       s->ssvm.i_am_master = 1;
260       s->ssvm.my_pid = getpid ();
261       s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
262       s->ssvm.requested_va = ~0;
263
264       /* Allocate a [sic] shared memory header, in process memory... */
265       sh = clib_mem_alloc_aligned (sizeof (*sh), CLIB_CACHE_LINE_BYTES);
266       s->ssvm.sh = sh;
267
268       memset (sh, 0, sizeof (*sh));
269       sh->heap = a->private_segment_count ? heaps[i] : clib_mem_get_heap ();
270
271       /* Set up svm_fifo_segment shared header */
272       fsh = clib_mem_alloc (sizeof (*fsh));
273       memset (fsh, 0, sizeof (*fsh));
274       fsh->flags = FIFO_SEGMENT_F_IS_PRIVATE;
275       s->h = sh->opaque[0] = fsh;
276       if (!a->private_segment_count)
277         fsh->flags |= FIFO_SEGMENT_F_IS_MAIN_HEAP;
278
279       if (a->private_segment_count)
280         {
281           if (i != 0)
282             fsh->flags |= FIFO_SEGMENT_F_IS_PREALLOCATED;
283           oldheap = clib_mem_get_heap ();
284           clib_mem_set_heap (sh->heap);
285           preallocate_fifo_pairs (s, a, i == 0 ? a->seg_protected_space : 0);
286           clib_mem_set_heap (oldheap);
287         }
288       sh->ready = 1;
289       vec_add1 (a->new_segment_indices, s - sm->segments);
290     }
291   vec_free (heaps);
292   return (0);
293 }
294
295 /**
296  * Attach as slave to an svm fifo segment
297  */
298 int
299 svm_fifo_segment_attach (svm_fifo_segment_create_args_t * a)
300 {
301   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
302   svm_fifo_segment_private_t *s;
303   int rv;
304
305   /* Allocate a fresh segment */
306   pool_get (sm->segments, s);
307   memset (s, 0, sizeof (*s));
308
309   s->ssvm.ssvm_size = a->segment_size;
310   s->ssvm.my_pid = getpid ();
311   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
312   s->ssvm.requested_va = sm->next_baseva;
313   if (a->segment_type == SSVM_SEGMENT_MEMFD)
314     s->ssvm.fd = a->memfd_fd;
315   else
316     s->ssvm.attach_timeout = sm->timeout_in_seconds;
317
318   if ((rv = ssvm_slave_init (&s->ssvm, a->segment_type)))
319     {
320       _vec_len (s) = vec_len (s) - 1;
321       return (rv);
322     }
323
324   /* Fish the segment header */
325   s->h = s->ssvm.sh->opaque[0];
326
327   vec_add1 (a->new_segment_indices, s - sm->segments);
328   return (0);
329 }
330
331 void
332 svm_fifo_segment_delete (svm_fifo_segment_private_t * s)
333 {
334   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
335
336   if (s->h->flags & FIFO_SEGMENT_F_IS_PRIVATE)
337     {
338       /* Don't try to free vpp's heap! */
339       if (!(s->h->flags & FIFO_SEGMENT_F_IS_MAIN_HEAP))
340         mheap_free (s->ssvm.sh->heap);
341       clib_mem_free (s->ssvm.sh);
342       clib_mem_free (s->h);
343     }
344   else
345     {
346       ssvm_delete (&s->ssvm);
347     }
348   memset (s, 0xfe, sizeof (*s));
349   pool_put (sm->segments, s);
350 }
351
352 /**
353  * Allocate fifo in svm segment
354  */
355 svm_fifo_t *
356 svm_fifo_segment_alloc_fifo (svm_fifo_segment_private_t * s,
357                              u32 data_size_in_bytes,
358                              svm_fifo_segment_freelist_t list_index)
359 {
360   ssvm_shared_header_t *sh;
361   svm_fifo_segment_header_t *fsh;
362   svm_fifo_t *f;
363   void *oldheap;
364   int freelist_index;
365
366   /*
367    * 4K minimum. It's not likely that anything good will happen
368    * with a smaller FIFO.
369    */
370   if (data_size_in_bytes < FIFO_SEGMENT_MIN_FIFO_SIZE ||
371       data_size_in_bytes > FIFO_SEGMENT_MAX_FIFO_SIZE)
372     {
373       clib_warning ("fifo size out of range %d", data_size_in_bytes);
374       return 0;
375     }
376
377   freelist_index = max_log2 (data_size_in_bytes)
378     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
379
380   sh = s->ssvm.sh;
381   fsh = (svm_fifo_segment_header_t *) sh->opaque[0];
382
383   ssvm_lock_non_recursive (sh, 1);
384   oldheap = ssvm_push_heap (sh);
385
386   switch (list_index)
387     {
388     case FIFO_SEGMENT_RX_FREELIST:
389     case FIFO_SEGMENT_TX_FREELIST:
390       vec_validate_init_empty (fsh->free_fifos, freelist_index, 0);
391
392       f = fsh->free_fifos[freelist_index];
393       if (PREDICT_FALSE (f == 0))
394         {
395           allocate_new_fifo_chunk (fsh, data_size_in_bytes,
396                                    FIFO_SEGMENT_ALLOC_CHUNK_SIZE);
397           f = fsh->free_fifos[freelist_index];
398         }
399       if (PREDICT_TRUE (f != 0))
400         {
401           fsh->free_fifos[freelist_index] = f->next;
402           /* (re)initialize the fifo, as in svm_fifo_create */
403           memset (f, 0, sizeof (*f));
404           f->nitems = data_size_in_bytes;
405           f->ooos_list_head = OOO_SEGMENT_INVALID_INDEX;
406           f->refcnt = 1;
407           f->freelist_index = freelist_index;
408           goto found;
409         }
410       /* FALLTHROUGH */
411     case FIFO_SEGMENT_FREELIST_NONE:
412       break;
413
414     default:
415       clib_warning ("ignore bogus freelist %d", list_index);
416       break;
417     }
418
419   /* Note: this can fail, in which case: create another segment */
420   f = svm_fifo_create (data_size_in_bytes);
421   if (PREDICT_FALSE (f == 0))
422     {
423       ssvm_pop_heap (oldheap);
424       ssvm_unlock_non_recursive (sh);
425       return (0);
426     }
427   f->freelist_index = freelist_index;
428
429 found:
430   /* If rx_freelist add to active fifos list. When cleaning up segment,
431    * we need a list of active sessions that should be disconnected. Since
432    * both rx and tx fifos keep pointers to the session, it's enough to track
433    * only one. */
434   if (list_index == FIFO_SEGMENT_RX_FREELIST)
435     {
436       if (fsh->fifos)
437         {
438           fsh->fifos->prev = f;
439           f->next = fsh->fifos;
440         }
441       fsh->fifos = f;
442     }
443   fsh->n_active_fifos++;
444
445   ssvm_pop_heap (oldheap);
446   ssvm_unlock_non_recursive (sh);
447   return (f);
448 }
449
450 void
451 svm_fifo_segment_free_fifo (svm_fifo_segment_private_t * s, svm_fifo_t * f,
452                             svm_fifo_segment_freelist_t list_index)
453 {
454   ssvm_shared_header_t *sh;
455   svm_fifo_segment_header_t *fsh;
456   void *oldheap;
457   int freelist_index;
458
459   ASSERT (f->refcnt > 0);
460
461   if (--f->refcnt > 0)
462     return;
463
464   sh = s->ssvm.sh;
465   fsh = (svm_fifo_segment_header_t *) sh->opaque[0];
466
467   freelist_index = f->freelist_index;
468
469   ASSERT (freelist_index < vec_len (fsh->free_fifos));
470
471   ssvm_lock_non_recursive (sh, 2);
472   oldheap = ssvm_push_heap (sh);
473
474   switch (list_index)
475     {
476     case FIFO_SEGMENT_RX_FREELIST:
477       /* Remove from active list */
478       if (f->prev)
479         f->prev->next = f->next;
480       else
481         fsh->fifos = f->next;
482       if (f->next)
483         f->next->prev = f->prev;
484       /* Fall through: we add only rx fifos to active pool */
485     case FIFO_SEGMENT_TX_FREELIST:
486       /* Add to free list */
487       f->next = fsh->free_fifos[freelist_index];
488       f->prev = 0;
489       fsh->free_fifos[freelist_index] = f;
490       break;
491     case FIFO_SEGMENT_FREELIST_NONE:
492       break;
493
494     default:
495       clib_warning ("ignore bogus freelist %d", list_index);
496       break;
497     }
498
499   if (CLIB_DEBUG)
500     {
501       f->master_session_index = ~0;
502       f->master_thread_index = ~0;
503     }
504
505   fsh->n_active_fifos--;
506   ssvm_pop_heap (oldheap);
507   ssvm_unlock_non_recursive (sh);
508 }
509
510 void
511 svm_fifo_segment_init (u64 baseva, u32 timeout_in_seconds)
512 {
513   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
514
515   sm->next_baseva = baseva;
516   sm->timeout_in_seconds = timeout_in_seconds;
517 }
518
519 u32
520 svm_fifo_segment_index (svm_fifo_segment_private_t * s)
521 {
522   return s - svm_fifo_segment_main.segments;
523 }
524
525 /**
526  * Retrieve svm segments pool. Used only for debug purposes.
527  */
528 svm_fifo_segment_private_t *
529 svm_fifo_segment_segments_pool (void)
530 {
531   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
532   return sm->segments;
533 }
534
535 /**
536  * Get number of active fifos
537  */
538 u32
539 svm_fifo_segment_num_fifos (svm_fifo_segment_private_t * fifo_segment)
540 {
541   return fifo_segment->h->n_active_fifos;
542 }
543
544 u32
545 svm_fifo_segment_num_free_fifos (svm_fifo_segment_private_t * fifo_segment,
546                                  u32 fifo_size_in_bytes)
547 {
548   ssvm_shared_header_t *sh;
549   svm_fifo_segment_header_t *fsh;
550   svm_fifo_t *f;
551   int i;
552   u32 count = 0, rounded_data_size, freelist_index;
553
554   sh = fifo_segment->ssvm.sh;
555   fsh = (svm_fifo_segment_header_t *) sh->opaque[0];
556
557   /* Count all free fifos? */
558   if (fifo_size_in_bytes == ~0)
559     {
560       for (i = 0; i < vec_len (fsh->free_fifos); i++)
561         {
562           f = fsh->free_fifos[i];
563           if (f == 0)
564             continue;
565
566           while (f)
567             {
568               f = f->next;
569               count++;
570             }
571         }
572       return count;
573     }
574
575   rounded_data_size = (1 << (max_log2 (fifo_size_in_bytes)));
576   freelist_index = max_log2 (rounded_data_size)
577     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
578
579   if (freelist_index > vec_len (fsh->free_fifos))
580     return 0;
581
582   f = fsh->free_fifos[freelist_index];
583   if (f == 0)
584     return 0;
585
586   while (f)
587     {
588       f = f->next;
589       count++;
590     }
591   return count;
592 }
593
594 void
595 svm_fifo_segment_info (svm_fifo_segment_private_t * seg, uword * address,
596                        u64 * size)
597 {
598   if (seg->h->flags & FIFO_SEGMENT_F_IS_PRIVATE)
599     {
600       mheap_t *heap_header;
601
602       *address = pointer_to_uword (seg->ssvm.sh->heap);
603       heap_header = mheap_header (seg->ssvm.sh->heap);
604       *size = heap_header->max_size;
605     }
606   else
607     {
608       *address = seg->ssvm.sh->ssvm_va;
609       *size = seg->ssvm.ssvm_size;
610     }
611 }
612
613 mheap_t *
614 svm_fifo_segment_heap (svm_fifo_segment_private_t * seg)
615 {
616   return seg->ssvm.sh->heap;
617 }
618
619 u8 *
620 format_svm_fifo_segment_type (u8 * s, va_list * args)
621 {
622   svm_fifo_segment_private_t *sp;
623   sp = va_arg (*args, svm_fifo_segment_private_t *);
624   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
625
626   if ((sp->h->flags & FIFO_SEGMENT_F_IS_PRIVATE)
627       && !(sp->h->flags & FIFO_SEGMENT_F_IS_MAIN_HEAP))
628     s = format (s, "%s", "private-heap");
629   else if ((sp->h->flags & FIFO_SEGMENT_F_IS_PRIVATE)
630            && (sp->h->flags & FIFO_SEGMENT_F_IS_MAIN_HEAP))
631     s = format (s, "%s", "main-heap");
632   else if (st == SSVM_SEGMENT_MEMFD)
633     s = format (s, "%s", "memfd");
634   else if (st == SSVM_SEGMENT_SHM)
635     s = format (s, "%s", "shm");
636   else
637     s = format (s, "%s", "unknown");
638   return s;
639 }
640
641 /**
642  * Segment format function
643  */
644 u8 *
645 format_svm_fifo_segment (u8 * s, va_list * args)
646 {
647   svm_fifo_segment_private_t *sp
648     = va_arg (*args, svm_fifo_segment_private_t *);
649   int verbose = va_arg (*args, int);
650   svm_fifo_segment_header_t *fsh = sp->h;
651   u32 count, indent;
652   svm_fifo_t *f;
653   int i;
654
655   indent = format_get_indent (s) + 2;
656   s = format (s, "%U segment heap: %U\n", format_white_space, indent,
657               format_mheap, svm_fifo_segment_heap (sp), verbose);
658   s = format (s, "%U segment has %u active fifos\n",
659               format_white_space, indent, svm_fifo_segment_num_fifos (sp));
660
661   for (i = 0; i < vec_len (fsh->free_fifos); i++)
662     {
663       f = fsh->free_fifos[i];
664       if (f == 0)
665         continue;
666       count = 0;
667       while (f)
668         {
669           f = f->next;
670           count++;
671         }
672
673       s = format (s, "%U%-5u Kb: %u free",
674                   format_white_space, indent + 2,
675                   1 << (i + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE) - 10),
676                   count);
677     }
678   return s;
679 }
680
681 /*
682  * fd.io coding-style-patch-verification: ON
683  *
684  * Local Variables:
685  * eval: (c-set-style "gnu")
686  * End:
687  */