0263d1a57ccf04f4dcc9fdb7b2f9ed89d3c7bdca
[vpp.git] / src / svm / fifo_segment.c
1 /*
2  * Copyright (c) 2016-2019 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/fifo_segment.h>
17
18 /**
19  * Initialize fifo segment shared header
20  */
21 int
22 fifo_segment_init (fifo_segment_t * fs)
23 {
24   fifo_segment_header_t *fsh;
25   ssvm_shared_header_t *sh;
26   void *oldheap;
27
28   sh = fs->ssvm.sh;
29   oldheap = ssvm_push_heap (sh);
30
31   fsh = clib_mem_alloc (sizeof (*fsh));
32   clib_memset (fsh, 0, sizeof (*fsh));
33   fs->h = sh->opaque[0] = fsh;
34
35   ssvm_pop_heap (oldheap);
36
37   sh->ready = 1;
38   return (0);
39 }
40
41 /**
42  * Create a fifo segment in process-private memory
43  */
44 static int
45 fifo_segment_create_process_private (fifo_segment_main_t * sm,
46                                      fifo_segment_create_args_t * a)
47 {
48   u32 pagesize = clib_mem_get_page_size ();
49   ssvm_shared_header_t *sh;
50   fifo_segment_t *s;
51   u32 rnd_size = 0;
52   u8 *heap;
53
54   pool_get (sm->segments, s);
55   clib_memset (s, 0, sizeof (*s));
56
57   rnd_size = (a->segment_size + (pagesize - 1)) & ~pagesize;
58
59 #if USE_DLMALLOC == 0
60   heap = mheap_alloc (0, rnd_size);
61   if (heap == 0)
62     {
63       clib_unix_warning ("mheap alloc");
64       pool_put (sm->segments, s);
65       return -1;
66     }
67   {
68     mheap_t *heap_header;
69     heap_header = mheap_header (heap);
70     heap_header->flags |= MHEAP_FLAG_THREAD_SAFE;
71   }
72 #else
73   heap = create_mspace (rnd_size, 1 /* locked */ );
74 #endif
75
76   s->ssvm.ssvm_size = rnd_size;
77   s->ssvm.i_am_master = 1;
78   s->ssvm.my_pid = getpid ();
79   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
80   s->ssvm.requested_va = ~0;
81
82   /* Allocate a [sic] shared memory header, in process memory... */
83   sh = clib_mem_alloc_aligned (sizeof (*sh), CLIB_CACHE_LINE_BYTES);
84   s->ssvm.sh = sh;
85
86   clib_memset (sh, 0, sizeof (*sh));
87   sh->heap = heap;
88
89   fifo_segment_init (s);
90   vec_add1 (a->new_segment_indices, s - sm->segments);
91
92   return (0);
93 }
94
95 /**
96  * Create a fifo segment and initialize as master
97  */
98 int
99 fifo_segment_create (fifo_segment_main_t * sm, fifo_segment_create_args_t * a)
100 {
101   fifo_segment_t *s;
102   int rv;
103
104   if (a->segment_type == SSVM_SEGMENT_PRIVATE)
105     return fifo_segment_create_process_private (sm, a);
106
107   /* Allocate a fresh segment */
108   pool_get (sm->segments, s);
109   clib_memset (s, 0, sizeof (*s));
110
111   s->ssvm.ssvm_size = a->segment_size;
112   s->ssvm.i_am_master = 1;
113   s->ssvm.my_pid = getpid ();
114   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
115   s->ssvm.requested_va = sm->next_baseva;
116
117   if ((rv = ssvm_master_init (&s->ssvm, a->segment_type)))
118     {
119       pool_put (sm->segments, s);
120       return (rv);
121     }
122
123   /* Note: requested_va updated due to seg base addr randomization */
124   sm->next_baseva = s->ssvm.sh->ssvm_va + a->segment_size;
125
126   fifo_segment_init (s);
127   vec_add1 (a->new_segment_indices, s - sm->segments);
128   return (0);
129 }
130
131 /**
132  * Attach as slave to a fifo segment
133  */
134 int
135 fifo_segment_attach (fifo_segment_main_t * sm, fifo_segment_create_args_t * a)
136 {
137   fifo_segment_t *s;
138   int rv;
139
140   pool_get_zero (sm->segments, s);
141
142   s->ssvm.ssvm_size = a->segment_size;
143   s->ssvm.my_pid = getpid ();
144   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
145   s->ssvm.requested_va = sm->next_baseva;
146   if (a->segment_type == SSVM_SEGMENT_MEMFD)
147     s->ssvm.fd = a->memfd_fd;
148   else
149     s->ssvm.attach_timeout = sm->timeout_in_seconds;
150
151   if ((rv = ssvm_slave_init (&s->ssvm, a->segment_type)))
152     {
153       _vec_len (s) = vec_len (s) - 1;
154       return (rv);
155     }
156
157   /* Fish the segment header */
158   s->h = s->ssvm.sh->opaque[0];
159
160   vec_add1 (a->new_segment_indices, s - sm->segments);
161   return (0);
162 }
163
164 void
165 fifo_segment_delete (fifo_segment_main_t * sm, fifo_segment_t * s)
166 {
167   ssvm_delete (&s->ssvm);
168   clib_memset (s, 0xfe, sizeof (*s));
169   pool_put (sm->segments, s);
170 }
171
172 u32
173 fifo_segment_index (fifo_segment_main_t * sm, fifo_segment_t * s)
174 {
175   return s - sm->segments;
176 }
177
178 void *
179 svm_fifo_segment_heap (fifo_segment_t * seg)
180 {
181   return seg->ssvm.sh->heap;
182 }
183
184 fifo_segment_t *
185 fifo_segment_get_segment (fifo_segment_main_t * sm, u32 segment_index)
186 {
187   return pool_elt_at_index (sm->segments, segment_index);
188 }
189
190 void
191 fifo_segment_info (fifo_segment_t * seg, char **address, size_t * size)
192 {
193   if (ssvm_type (&seg->ssvm) == SSVM_SEGMENT_PRIVATE)
194     {
195 #if USE_DLMALLOC == 0
196       mheap_t *heap_header;
197
198       *address = pointer_to_uword (seg->ssvm.sh->heap);
199       heap_header = mheap_header (seg->ssvm.sh->heap);
200       *size = heap_header->max_size;
201 #else
202       mspace_get_address_and_size (seg->ssvm.sh->heap, address, size);
203 #endif
204     }
205   else
206     {
207       *address = (char *) seg->ssvm.sh->ssvm_va;
208       *size = seg->ssvm.ssvm_size;
209     }
210 }
211
212 void
213 fifo_segment_main_init (fifo_segment_main_t * sm, u64 baseva,
214                         u32 timeout_in_seconds)
215 {
216   sm->next_baseva = baseva;
217   sm->timeout_in_seconds = timeout_in_seconds;
218 }
219
220 static void
221 fifo_init_for_segment (fifo_segment_header_t * fsh, svm_fifo_t * f,
222                        u32 size, u32 fl_index)
223 {
224   f->freelist_index = fl_index;
225   f->default_chunk.start_byte = 0;
226   f->default_chunk.length = size;
227   f->default_chunk.next = f->start_chunk = f->end_chunk = &f->default_chunk;
228   f->head_chunk = f->tail_chunk = f->ooo_enq = f->ooo_deq = f->start_chunk;
229   f->next = fsh->free_fifos[fl_index];
230   fsh->free_fifos[fl_index] = f;
231 }
232
233 static inline int
234 fs_free_list_for_size (u32 size)
235 {
236   return max_log2 (size) - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
237 }
238
239 static inline int
240 fs_chunk_size_is_valid (u32 size)
241 {
242   /*
243    * 4K minimum. It's not likely that anything good will happen
244    * with a smaller FIFO.
245    */
246   return size >= FIFO_SEGMENT_MIN_FIFO_SIZE
247     && size <= FIFO_SEGMENT_MAX_FIFO_SIZE;
248 }
249
250 static void
251 allocate_new_fifo_batch (fifo_segment_header_t * fsh,
252                          u32 data_size_in_bytes, int chunk_size)
253 {
254   u32 size, rounded_data_size;
255   int i, fl_index;
256   u8 *fifos_mem;
257   svm_fifo_t *f;
258
259   rounded_data_size = (1 << (max_log2 (data_size_in_bytes)));
260   fl_index = fs_free_list_for_size (data_size_in_bytes);
261
262   /* Calculate space requirement $$$ round-up data_size_in_bytes */
263   size = (sizeof (*f) + rounded_data_size) * chunk_size;
264
265   /* Allocate fifo space. May fail. */
266   fifos_mem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
267                                                 0 /* align_offset */ ,
268                                                 0 /* os_out_of_memory */ );
269
270   /* Out of space.. */
271   if (fifos_mem == 0)
272     return;
273
274   /* Carve fifo space */
275   for (i = 0; i < chunk_size; i++)
276     {
277       f = (svm_fifo_t *) fifos_mem;
278       fifo_init_for_segment (fsh, f, rounded_data_size, fl_index);
279       fifos_mem += sizeof (*f) + rounded_data_size;
280     }
281 }
282
283 /**
284  * Allocate fifo in fifo segment
285  */
286 svm_fifo_t *
287 fifo_segment_alloc_fifo (fifo_segment_t * fs, u32 data_bytes,
288                          fifo_segment_ftype_t ftype)
289 {
290   fifo_segment_header_t *fsh;
291   ssvm_shared_header_t *sh;
292   svm_fifo_t *f = 0;
293   void *oldheap;
294   int fl_index;
295
296   if (!fs_chunk_size_is_valid (data_bytes))
297     {
298       clib_warning ("fifo size out of range %d", data_bytes);
299       return 0;
300     }
301
302   fl_index = fs_free_list_for_size (data_bytes);
303
304   sh = fs->ssvm.sh;
305   ssvm_lock_non_recursive (sh, 1);
306
307   fsh = fs->h;
308   vec_validate_init_empty (fsh->free_fifos, fl_index, 0);
309   f = fsh->free_fifos[fl_index];
310
311   /* Try to allocate batch of fifos */
312   if (PREDICT_FALSE (!f))
313     {
314       oldheap = ssvm_push_heap (sh);
315       allocate_new_fifo_batch (fsh, data_bytes,
316                                FIFO_SEGMENT_ALLOC_BATCH_SIZE);
317       ssvm_pop_heap (oldheap);
318       f = fsh->free_fifos[fl_index];
319     }
320   if (PREDICT_TRUE (f != 0))
321     {
322       fsh->free_fifos[fl_index] = f->next;
323       /* (re)initialize the fifo, as in svm_fifo_create */
324       memset (f, 0, sizeof (*f));
325       svm_fifo_init (f, data_bytes);
326       goto found;
327     }
328
329   /* Failed to allocate batch, try just one fifo. This can also fail,
330    * in which case, create another segment */
331   oldheap = ssvm_push_heap (sh);
332   f = svm_fifo_create (data_bytes);
333   ssvm_pop_heap (oldheap);
334
335   if (PREDICT_FALSE (f == 0))
336     goto done;
337
338   f->freelist_index = fl_index;
339
340 found:
341   /* If rx fifo type add to active fifos list. When cleaning up segment,
342    * we need a list of active sessions that should be disconnected. Since
343    * both rx and tx fifos keep pointers to the session, it's enough to track
344    * only one. */
345   if (ftype == FIFO_SEGMENT_RX_FIFO)
346     {
347       if (fsh->fifos)
348         {
349           fsh->fifos->prev = f;
350           f->next = fsh->fifos;
351         }
352       fsh->fifos = f;
353       f->flags |= SVM_FIFO_F_LL_TRACKED;
354     }
355   fsh->n_active_fifos++;
356
357 done:
358   ssvm_unlock_non_recursive (sh);
359   return (f);
360 }
361
362 /**
363  * Free fifo allocated in fifo segment
364  */
365 void
366 fifo_segment_free_fifo (fifo_segment_t * fs, svm_fifo_t * f)
367 {
368   fifo_segment_header_t *fsh;
369   ssvm_shared_header_t *sh;
370   int fl_index;
371
372   ASSERT (f->refcnt > 0);
373
374   if (--f->refcnt > 0)
375     return;
376
377   sh = fs->ssvm.sh;
378   fsh = fs->h;
379   fl_index = f->freelist_index;
380
381   ASSERT (fl_index < vec_len (fsh->free_fifos));
382
383   ssvm_lock_non_recursive (sh, 2);
384
385   /* Remove from active list. Only rx fifos are tracked */
386   if (f->flags & SVM_FIFO_F_LL_TRACKED)
387     {
388       if (f->prev)
389         f->prev->next = f->next;
390       else
391         fsh->fifos = f->next;
392       if (f->next)
393         f->next->prev = f->prev;
394       f->flags &= ~SVM_FIFO_F_LL_TRACKED;
395     }
396
397   /* Add to free list */
398   f->next = fsh->free_fifos[fl_index];
399   f->prev = 0;
400   fsh->free_fifos[fl_index] = f;
401
402   /* If fifo has more chunks, free them */
403   if (f->flags & SVM_FIFO_F_MULTI_CHUNK)
404     {
405       svm_fifo_chunk_t *cur, *next;
406       void *oldheap;
407
408       next = f->start_chunk->next;
409       while (next != f->start_chunk)
410         {
411           cur = next;
412           next = next->next;
413           fl_index = fs_free_list_for_size (cur->length);
414           cur->next = fsh->free_chunks[fl_index];
415           fsh->free_chunks[fl_index] = cur;
416         }
417       oldheap = ssvm_push_heap (sh);
418       svm_fifo_free_chunk_lookup (f);
419       ssvm_pop_heap (oldheap);
420     }
421
422   /* not allocated on segment heap */
423   svm_fifo_free_ooo_data (f);
424
425   if (CLIB_DEBUG)
426     {
427       f->master_session_index = ~0;
428       f->master_thread_index = ~0;
429     }
430
431   fsh->n_active_fifos--;
432   ssvm_unlock_non_recursive (sh);
433 }
434
435 /**
436  * Pre-allocates fifo pairs in fifo segment
437  */
438 void
439 fifo_segment_preallocate_fifo_pairs (fifo_segment_t * fs,
440                                      u32 rx_fifo_size, u32 tx_fifo_size,
441                                      u32 * n_fifo_pairs)
442 {
443   u32 rx_rounded_data_size, tx_rounded_data_size, pair_size;
444   u32 rx_fifos_size, tx_fifos_size, pairs_to_allocate;
445   ssvm_shared_header_t *sh = fs->ssvm.sh;
446   fifo_segment_header_t *fsh = fs->h;
447   int i, rx_fl_index, tx_fl_index;
448   u8 *rx_fifo_mem, *tx_fifo_mem;
449   uword space_available;
450   void *oldheap;
451   svm_fifo_t *f;
452
453   /* Parameter check */
454   if (rx_fifo_size == 0 || tx_fifo_size == 0 || *n_fifo_pairs == 0)
455     return;
456
457   if (!fs_chunk_size_is_valid (rx_fifo_size))
458     {
459       clib_warning ("rx fifo_size out of range %d", rx_fifo_size);
460       return;
461     }
462
463   if (!fs_chunk_size_is_valid (tx_fifo_size))
464     {
465       clib_warning ("tx fifo_size out of range %d", tx_fifo_size);
466       return;
467     }
468
469   rx_rounded_data_size = (1 << (max_log2 (rx_fifo_size)));
470   rx_fl_index = fs_free_list_for_size (rx_fifo_size);
471   tx_rounded_data_size = (1 << (max_log2 (tx_fifo_size)));
472   tx_fl_index = fs_free_list_for_size (tx_fifo_size);
473
474   /* Calculate space requirements */
475   pair_size = 2 * sizeof (*f) + rx_rounded_data_size + tx_rounded_data_size;
476 #if USE_DLMALLOC == 0
477   space_available = fs->ssvm.ssvm_size - mheap_bytes (sh->heap);
478 #else
479   space_available = fs->ssvm.ssvm_size - mspace_usable_size (sh->heap);
480 #endif
481
482   pairs_to_allocate = clib_min (space_available / pair_size, *n_fifo_pairs);
483   rx_fifos_size = (sizeof (*f) + rx_rounded_data_size) * pairs_to_allocate;
484   tx_fifos_size = (sizeof (*f) + tx_rounded_data_size) * pairs_to_allocate;
485
486   vec_validate_init_empty (fsh->free_fifos,
487                            clib_max (rx_fl_index, tx_fl_index), 0);
488
489   oldheap = ssvm_push_heap (sh);
490
491   /* Allocate rx and tx fifo memory. May fail. */
492   rx_fifo_mem = clib_mem_alloc_aligned_at_offset (rx_fifos_size,
493                                                   CLIB_CACHE_LINE_BYTES,
494                                                   0 /* align_offset */ ,
495                                                   0 /* os_out_of_memory */ );
496   tx_fifo_mem = clib_mem_alloc_aligned_at_offset (tx_fifos_size,
497                                                   CLIB_CACHE_LINE_BYTES,
498                                                   0 /* align_offset */ ,
499                                                   0 /* os_out_of_memory */ );
500
501   /* Make sure it worked. Clean up if it didn't... */
502   if (rx_fifo_mem == 0 || tx_fifo_mem == 0)
503     {
504       rx_fifo_mem ? clib_mem_free (rx_fifo_mem) : clib_mem_free (tx_fifo_mem);
505       clib_warning ("fifo preallocation failure: rx size %d tx size %u "
506                     "npairs %d", rx_fifo_size, tx_fifo_size, *n_fifo_pairs);
507       ssvm_pop_heap (oldheap);
508       return;
509     }
510
511   /* Carve rx and tx fifo memory */
512   for (i = 0; i < pairs_to_allocate; i++)
513     {
514       f = (svm_fifo_t *) rx_fifo_mem;
515       fifo_init_for_segment (fsh, f, rx_rounded_data_size, rx_fl_index);
516       rx_fifo_mem += sizeof (*f) + rx_rounded_data_size;
517
518       f = (svm_fifo_t *) tx_fifo_mem;
519       fifo_init_for_segment (fsh, f, tx_rounded_data_size, tx_fl_index);
520       tx_fifo_mem += sizeof (*f) + tx_rounded_data_size;
521     }
522
523   /* Account for the pairs allocated */
524   *n_fifo_pairs -= pairs_to_allocate;
525   ssvm_pop_heap (oldheap);
526 }
527
528 int
529 fifo_segment_grow_fifo (fifo_segment_t * fs, svm_fifo_t * f, u32 chunk_size)
530 {
531   ssvm_shared_header_t *sh;
532   svm_fifo_chunk_t *c;
533   void *oldheap;
534   int fl_index;
535
536   if (!fs_chunk_size_is_valid (chunk_size))
537     {
538       clib_warning ("chunk size out of range %d", chunk_size);
539       return 0;
540     }
541
542   fl_index = fs_free_list_for_size (chunk_size);
543
544   sh = fs->ssvm.sh;
545   ssvm_lock_non_recursive (sh, 1);
546
547   vec_validate_init_empty (fs->h->free_chunks, fl_index, 0);
548   c = fs->h->free_chunks[fl_index];
549
550   oldheap = ssvm_push_heap (sh);
551
552   if (!c)
553     {
554       c = svm_fifo_chunk_alloc (chunk_size);
555       if (!c)
556         {
557           ssvm_pop_heap (oldheap);
558           return -1;
559         }
560     }
561   else
562     {
563       fs->h->free_chunks[fl_index] = c->next;
564     }
565
566   svm_fifo_add_chunk (f, c);
567
568   ssvm_pop_heap (oldheap);
569   ssvm_unlock_non_recursive (sh);
570   return 0;
571 }
572
573 int
574 fifo_segment_collect_fifo_chunks (fifo_segment_t * fs, svm_fifo_t * f)
575 {
576   svm_fifo_chunk_t *cur, *next;
577   ssvm_shared_header_t *sh;
578   void *oldheap;
579   int fl_index;
580
581   sh = fs->ssvm.sh;
582   ssvm_lock_non_recursive (sh, 1);
583
584   oldheap = ssvm_push_heap (sh);
585   cur = svm_fifo_collect_chunks (f);
586
587   while (cur)
588     {
589       next = cur->next;
590       fl_index = fs_free_list_for_size (cur->length);
591       cur->next = fs->h->free_chunks[fl_index];
592       fs->h->free_chunks[fl_index] = cur;
593       cur = next;
594     }
595
596   ssvm_pop_heap (oldheap);
597   ssvm_unlock_non_recursive (sh);
598
599   return 0;
600 }
601
602 /**
603  * Get number of active fifos
604  */
605 u32
606 fifo_segment_num_fifos (fifo_segment_t * fs)
607 {
608   return fs->h->n_active_fifos;
609 }
610
611 u32
612 fifo_segment_num_free_fifos (fifo_segment_t * fs, u32 fifo_size_in_bytes)
613 {
614   u32 count = 0, rounded_data_size, fl_index;
615   fifo_segment_header_t *fsh;
616   ssvm_shared_header_t *sh;
617   svm_fifo_t *f;
618   int i;
619
620   sh = fs->ssvm.sh;
621   fsh = (fifo_segment_header_t *) sh->opaque[0];
622
623   /* Count all free fifos? */
624   if (fifo_size_in_bytes == ~0)
625     {
626       for (i = 0; i < vec_len (fsh->free_fifos); i++)
627         {
628           f = fsh->free_fifos[i];
629           if (f == 0)
630             continue;
631
632           while (f)
633             {
634               f = f->next;
635               count++;
636             }
637         }
638       return count;
639     }
640
641   rounded_data_size = (1 << (max_log2 (fifo_size_in_bytes)));
642   fl_index = fs_free_list_for_size (rounded_data_size);
643
644   if (fl_index >= vec_len (fsh->free_fifos))
645     return 0;
646
647   f = fsh->free_fifos[fl_index];
648   if (f == 0)
649     return 0;
650
651   while (f)
652     {
653       f = f->next;
654       count++;
655     }
656   return count;
657 }
658
659 u32
660 fifo_segment_num_free_chunks (fifo_segment_t * fs, u32 size)
661 {
662   u32 count = 0, rounded_size, fl_index;
663   fifo_segment_header_t *fsh;
664   svm_fifo_chunk_t *c;
665   int i;
666
667   fsh = fs->h;
668
669   /* Count all free chunks? */
670   if (size == ~0)
671     {
672       for (i = 0; i < vec_len (fsh->free_chunks); i++)
673         {
674           c = fsh->free_chunks[i];
675           if (c == 0)
676             continue;
677
678           while (c)
679             {
680               c = c->next;
681               count++;
682             }
683         }
684       return count;
685     }
686
687   rounded_size = (1 << (max_log2 (size)));
688   fl_index = fs_free_list_for_size (rounded_size);
689
690   if (fl_index >= vec_len (fsh->free_chunks))
691     return 0;
692
693   c = fsh->free_chunks[fl_index];
694   if (c == 0)
695     return 0;
696
697   while (c)
698     {
699       c = c->next;
700       count++;
701     }
702   return count;
703 }
704
705 u8
706 fifo_segment_has_fifos (fifo_segment_t * fs)
707 {
708   return fs->h->fifos != 0;
709 }
710
711 svm_fifo_t *
712 fifo_segment_get_fifo_list (fifo_segment_t * fs)
713 {
714   return fs->h->fifos;
715 }
716
717 u8 *
718 format_fifo_segment_type (u8 * s, va_list * args)
719 {
720   fifo_segment_t *sp;
721   sp = va_arg (*args, fifo_segment_t *);
722   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
723
724   if (st == SSVM_SEGMENT_PRIVATE)
725     s = format (s, "%s", "private-heap");
726   else if (st == SSVM_SEGMENT_MEMFD)
727     s = format (s, "%s", "memfd");
728   else if (st == SSVM_SEGMENT_SHM)
729     s = format (s, "%s", "shm");
730   else
731     s = format (s, "%s", "unknown");
732   return s;
733 }
734
735 /**
736  * Segment format function
737  */
738 u8 *
739 format_fifo_segment (u8 * s, va_list * args)
740 {
741   fifo_segment_t *sp = va_arg (*args, fifo_segment_t *);
742   int verbose __attribute__ ((unused)) = va_arg (*args, int);
743   fifo_segment_header_t *fsh = sp->h;
744   u32 count, indent;
745   svm_fifo_t *f;
746   int i;
747
748   indent = format_get_indent (s) + 2;
749 #if USE_DLMALLOC == 0
750   s = format (s, "%U segment heap: %U\n", format_white_space, indent,
751               format_mheap, svm_fifo_segment_heap (sp), verbose);
752   s = format (s, "%U segment has %u active fifos\n",
753               format_white_space, indent, fifo_segment_num_fifos (sp));
754 #endif
755
756   for (i = 0; i < vec_len (fsh->free_fifos); i++)
757     {
758       f = fsh->free_fifos[i];
759       if (f == 0)
760         continue;
761       count = 0;
762       while (f)
763         {
764           f = f->next;
765           count++;
766         }
767
768       s = format (s, "%U%-5u Kb: %u free",
769                   format_white_space, indent + 2,
770                   1 << (i + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE) - 10),
771                   count);
772     }
773   return s;
774 }
775
776 /*
777  * fd.io coding-style-patch-verification: ON
778  *
779  * Local Variables:
780  * eval: (c-set-style "gnu")
781  * End:
782  */