fdbc8102587b0e5c7dab02c05425a33681572699
[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   *address = (char *) seg->ssvm.sh->ssvm_va;
194   *size = seg->ssvm.ssvm_size;
195 }
196
197 void
198 fifo_segment_main_init (fifo_segment_main_t * sm, u64 baseva,
199                         u32 timeout_in_seconds)
200 {
201   sm->next_baseva = baseva;
202   sm->timeout_in_seconds = timeout_in_seconds;
203 }
204
205 static void
206 fifo_init_for_segment (svm_fifo_t * f, svm_fifo_chunk_t * c)
207 {
208   f->start_chunk = f->end_chunk = c->next = c;
209   f->head_chunk = f->tail_chunk = f->ooo_enq = f->ooo_deq = f->start_chunk;
210 }
211
212 static void
213 fifo_init_chunk_for_segment (svm_fifo_chunk_t * c, u32 size)
214 {
215   c->start_byte = 0;
216   c->length = size;
217   c->next = 0;
218 }
219
220 static inline int
221 fs_free_list_for_size (u32 size)
222 {
223   return max_log2 (size) - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
224 }
225
226 static inline int
227 fs_chunk_size_is_valid (u32 size)
228 {
229   /*
230    * 4K minimum. It's not likely that anything good will happen
231    * with a smaller FIFO.
232    */
233   return size >= FIFO_SEGMENT_MIN_FIFO_SIZE
234     && size <= FIFO_SEGMENT_MAX_FIFO_SIZE;
235 }
236
237 static svm_fifo_t *
238 fs_try_alloc_fifo_from_freelist (fifo_segment_header_t * fsh, u32 data_bytes)
239 {
240   svm_fifo_chunk_t *c;
241   svm_fifo_t *f;
242   u32 fl_index;
243
244   f = fsh->free_fifos;
245   fl_index = fs_free_list_for_size (data_bytes);
246   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
247   c = fsh->free_chunks[fl_index];
248
249   if (!f || !c)
250     return 0;
251
252   fsh->free_fifos = f->next;
253   fsh->free_chunks[fl_index] = c->next;
254   c->next = c;
255   c->start_byte = 0;
256   c->length = data_bytes;
257   memset (f, 0, sizeof (*f));
258   f->start_chunk = c;
259   f->end_chunk = c;
260   return f;
261 }
262
263 static svm_fifo_t *
264 fs_try_allocate_fifo_batch (ssvm_shared_header_t * sh,
265                             fifo_segment_header_t * fsh, u32 data_bytes)
266 {
267   u32 size, rounded_data_size;
268   svm_fifo_chunk_t *c;
269   u32 fl_index, hdrs;
270   svm_fifo_t *f;
271   void *oldheap;
272   u8 *fmem;
273   int i;
274
275   rounded_data_size = (1 << (max_log2 (data_bytes)));
276   fl_index = fs_free_list_for_size (data_bytes);
277   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
278
279   oldheap = ssvm_push_heap (sh);
280   hdrs = sizeof (*f) + sizeof (*c);
281   size = (hdrs + rounded_data_size) * FIFO_SEGMENT_ALLOC_BATCH_SIZE;
282   fmem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
283                                            0 /* align_offset */ ,
284                                            0 /* os_out_of_memory */ );
285   ssvm_pop_heap (oldheap);
286
287   /* Out of space.. */
288   if (fmem == 0)
289     return 0;
290
291   /* Carve fifo + chunk space */
292   for (i = 0; i < FIFO_SEGMENT_ALLOC_BATCH_SIZE; i++)
293     {
294       f = (svm_fifo_t *) fmem;
295       memset (f, 0, sizeof (*f));
296       f->next = fsh->free_fifos;
297       fsh->free_fifos = f;
298       c = (svm_fifo_chunk_t *) (fmem + sizeof (*f));
299       c->start_byte = 0;
300       c->length = rounded_data_size;
301       c->next = fsh->free_chunks[fl_index];
302       fsh->free_chunks[fl_index] = c;
303       fmem += hdrs + rounded_data_size;
304     }
305
306   return fs_try_alloc_fifo_from_freelist (fsh, data_bytes);
307 }
308
309 /**
310  * Allocate fifo in fifo segment
311  */
312 svm_fifo_t *
313 fifo_segment_alloc_fifo (fifo_segment_t * fs, u32 data_bytes,
314                          fifo_segment_ftype_t ftype)
315 {
316   fifo_segment_header_t *fsh;
317   ssvm_shared_header_t *sh;
318   svm_fifo_t *f = 0;
319
320   if (!fs_chunk_size_is_valid (data_bytes))
321     {
322       clib_warning ("fifo size out of range %d", data_bytes);
323       return 0;
324     }
325
326   sh = fs->ssvm.sh;
327   ssvm_lock_non_recursive (sh, 1);
328   fsh = fs->h;
329
330   /* Try the following steps in order:
331    * - grab fifo and chunk from freelists
332    * - batch fifo and chunk allocation
333    * - single fifo allocation
334    */
335   f = fs_try_alloc_fifo_from_freelist (fsh, data_bytes);
336   if (!f)
337     f = fs_try_allocate_fifo_batch (sh, fsh, data_bytes);
338   if (!f)
339     {
340       void *oldheap = ssvm_push_heap (sh);
341       f = svm_fifo_create (data_bytes);
342       ssvm_pop_heap (oldheap);
343       if (!f)
344         goto done;
345     }
346
347   /* (re)initialize the fifo, as in svm_fifo_create */
348   svm_fifo_init (f, data_bytes);
349
350   /* If rx fifo type add to active fifos list. When cleaning up segment,
351    * we need a list of active sessions that should be disconnected. Since
352    * both rx and tx fifos keep pointers to the session, it's enough to track
353    * only one. */
354   if (ftype == FIFO_SEGMENT_RX_FIFO)
355     {
356       if (fsh->fifos)
357         {
358           fsh->fifos->prev = f;
359           f->next = fsh->fifos;
360         }
361       fsh->fifos = f;
362       f->flags |= SVM_FIFO_F_LL_TRACKED;
363     }
364   fsh->n_active_fifos++;
365
366 done:
367   ssvm_unlock_non_recursive (sh);
368   return (f);
369 }
370
371 /**
372  * Free fifo allocated in fifo segment
373  */
374 void
375 fifo_segment_free_fifo (fifo_segment_t * fs, svm_fifo_t * f)
376 {
377   svm_fifo_chunk_t *cur, *next;
378   fifo_segment_header_t *fsh;
379   ssvm_shared_header_t *sh;
380   void *oldheap;
381   int fl_index;
382
383   ASSERT (f->refcnt > 0);
384
385   if (--f->refcnt > 0)
386     return;
387
388   sh = fs->ssvm.sh;
389   fsh = fs->h;
390
391   ssvm_lock_non_recursive (sh, 2);
392
393   /* Remove from active list. Only rx fifos are tracked */
394   if (f->flags & SVM_FIFO_F_LL_TRACKED)
395     {
396       if (f->prev)
397         f->prev->next = f->next;
398       else
399         fsh->fifos = f->next;
400       if (f->next)
401         f->next->prev = f->prev;
402       f->flags &= ~SVM_FIFO_F_LL_TRACKED;
403     }
404
405   /* Add to free list */
406   f->next = fsh->free_fifos;
407   f->prev = 0;
408   fsh->free_fifos = f;
409
410   /* Free fifo chunks */
411   cur = f->start_chunk;
412   do
413     {
414       next = cur->next;
415       fl_index = fs_free_list_for_size (cur->length);
416       ASSERT (fl_index < vec_len (fsh->free_chunks));
417       cur->next = fsh->free_chunks[fl_index];
418       fsh->free_chunks[fl_index] = cur;
419       cur = next;
420     }
421   while (cur != f->start_chunk);
422
423   oldheap = ssvm_push_heap (sh);
424   svm_fifo_free_chunk_lookup (f);
425   ssvm_pop_heap (oldheap);
426
427   /* not allocated on segment heap */
428   svm_fifo_free_ooo_data (f);
429
430   if (CLIB_DEBUG)
431     {
432       f->master_session_index = ~0;
433       f->master_thread_index = ~0;
434     }
435
436   fsh->n_active_fifos--;
437   ssvm_unlock_non_recursive (sh);
438 }
439
440 /**
441  * Pre-allocates fifo pairs in fifo segment
442  */
443 void
444 fifo_segment_preallocate_fifo_pairs (fifo_segment_t * fs,
445                                      u32 rx_fifo_size, u32 tx_fifo_size,
446                                      u32 * n_fifo_pairs)
447 {
448   u32 rx_rounded_data_size, tx_rounded_data_size, pair_size;
449   u32 rx_fifos_size, tx_fifos_size, pairs_to_allocate;
450   ssvm_shared_header_t *sh = fs->ssvm.sh;
451   fifo_segment_header_t *fsh = fs->h;
452   int i, rx_fl_index, tx_fl_index;
453   u8 *rx_fifo_mem, *tx_fifo_mem;
454   uword space_available;
455   svm_fifo_chunk_t *c;
456   void *oldheap;
457   svm_fifo_t *f;
458   u32 hdrs;
459
460   /* Parameter check */
461   if (rx_fifo_size == 0 || tx_fifo_size == 0 || *n_fifo_pairs == 0)
462     return;
463
464   if (!fs_chunk_size_is_valid (rx_fifo_size))
465     {
466       clib_warning ("rx fifo_size out of range %d", rx_fifo_size);
467       return;
468     }
469
470   if (!fs_chunk_size_is_valid (tx_fifo_size))
471     {
472       clib_warning ("tx fifo_size out of range %d", tx_fifo_size);
473       return;
474     }
475
476   rx_rounded_data_size = (1 << (max_log2 (rx_fifo_size)));
477   rx_fl_index = fs_free_list_for_size (rx_fifo_size);
478   tx_rounded_data_size = (1 << (max_log2 (tx_fifo_size)));
479   tx_fl_index = fs_free_list_for_size (tx_fifo_size);
480
481   hdrs = sizeof (*f) + sizeof (*c);
482
483   /* Calculate space requirements */
484   pair_size = 2 * hdrs + rx_rounded_data_size + tx_rounded_data_size;
485 #if USE_DLMALLOC == 0
486   space_available = fs->ssvm.ssvm_size - mheap_bytes (sh->heap);
487 #else
488   space_available = fs->ssvm.ssvm_size - mspace_usable_size (sh->heap);
489 #endif
490
491   pairs_to_allocate = clib_min (space_available / pair_size, *n_fifo_pairs);
492   rx_fifos_size = (hdrs + rx_rounded_data_size) * pairs_to_allocate;
493   tx_fifos_size = (hdrs + tx_rounded_data_size) * pairs_to_allocate;
494
495   vec_validate_init_empty (fsh->free_chunks,
496                            clib_max (rx_fl_index, tx_fl_index), 0);
497
498   oldheap = ssvm_push_heap (sh);
499
500   /* Allocate rx and tx fifo memory. May fail. */
501   rx_fifo_mem = clib_mem_alloc_aligned_at_offset (rx_fifos_size,
502                                                   CLIB_CACHE_LINE_BYTES,
503                                                   0 /* align_offset */ ,
504                                                   0 /* os_out_of_memory */ );
505   tx_fifo_mem = clib_mem_alloc_aligned_at_offset (tx_fifos_size,
506                                                   CLIB_CACHE_LINE_BYTES,
507                                                   0 /* align_offset */ ,
508                                                   0 /* os_out_of_memory */ );
509
510   /* Make sure it worked. Clean up if it didn't... */
511   if (rx_fifo_mem == 0 || tx_fifo_mem == 0)
512     {
513       rx_fifo_mem ? clib_mem_free (rx_fifo_mem) : clib_mem_free (tx_fifo_mem);
514       clib_warning ("fifo preallocation failure: rx size %d tx size %u "
515                     "npairs %d", rx_fifo_size, tx_fifo_size, *n_fifo_pairs);
516       ssvm_pop_heap (oldheap);
517       return;
518     }
519
520   /* Carve rx and tx fifo memory */
521   for (i = 0; i < pairs_to_allocate; i++)
522     {
523       f = (svm_fifo_t *) rx_fifo_mem;
524       c = (svm_fifo_chunk_t *) (rx_fifo_mem + sizeof (*f));
525       fifo_init_chunk_for_segment (c, rx_rounded_data_size);
526       fifo_init_for_segment (f, c);
527       rx_fifo_mem += hdrs + rx_rounded_data_size;
528
529       f = (svm_fifo_t *) tx_fifo_mem;
530       c = (svm_fifo_chunk_t *) (tx_fifo_mem + sizeof (*f));
531       fifo_init_chunk_for_segment (c, tx_rounded_data_size);
532       fifo_init_for_segment (f, c);
533       tx_fifo_mem += hdrs + tx_rounded_data_size;
534     }
535
536   /* Account for the pairs allocated */
537   *n_fifo_pairs -= pairs_to_allocate;
538   ssvm_pop_heap (oldheap);
539 }
540
541 int
542 fifo_segment_grow_fifo (fifo_segment_t * fs, svm_fifo_t * f, u32 chunk_size)
543 {
544   ssvm_shared_header_t *sh;
545   svm_fifo_chunk_t *c;
546   void *oldheap;
547   int fl_index;
548
549   if (!fs_chunk_size_is_valid (chunk_size))
550     {
551       clib_warning ("chunk size out of range %d", chunk_size);
552       return 0;
553     }
554
555   fl_index = fs_free_list_for_size (chunk_size);
556
557   sh = fs->ssvm.sh;
558   ssvm_lock_non_recursive (sh, 1);
559
560   vec_validate_init_empty (fs->h->free_chunks, fl_index, 0);
561   c = fs->h->free_chunks[fl_index];
562
563   oldheap = ssvm_push_heap (sh);
564
565   if (!c)
566     {
567       c = svm_fifo_chunk_alloc (chunk_size);
568       if (!c)
569         {
570           ssvm_pop_heap (oldheap);
571           return -1;
572         }
573     }
574   else
575     {
576       fs->h->free_chunks[fl_index] = c->next;
577       c->next = 0;
578     }
579
580   svm_fifo_add_chunk (f, c);
581
582   ssvm_pop_heap (oldheap);
583   ssvm_unlock_non_recursive (sh);
584   return 0;
585 }
586
587 int
588 fifo_segment_collect_fifo_chunks (fifo_segment_t * fs, svm_fifo_t * f)
589 {
590   svm_fifo_chunk_t *cur, *next;
591   ssvm_shared_header_t *sh;
592   void *oldheap;
593   int fl_index;
594
595   sh = fs->ssvm.sh;
596   ssvm_lock_non_recursive (sh, 1);
597
598   oldheap = ssvm_push_heap (sh);
599   cur = svm_fifo_collect_chunks (f);
600
601   while (cur)
602     {
603       next = cur->next;
604       fl_index = fs_free_list_for_size (cur->length);
605       cur->next = fs->h->free_chunks[fl_index];
606       fs->h->free_chunks[fl_index] = cur;
607       cur = next;
608     }
609
610   ssvm_pop_heap (oldheap);
611   ssvm_unlock_non_recursive (sh);
612
613   return 0;
614 }
615
616 /**
617  * Get number of active fifos
618  */
619 u32
620 fifo_segment_num_fifos (fifo_segment_t * fs)
621 {
622   return fs->h->n_active_fifos;
623 }
624
625 u32
626 fifo_segment_num_free_fifos (fifo_segment_t * fs, u32 fifo_size_in_bytes)
627 {
628   fifo_segment_header_t *fsh;
629   ssvm_shared_header_t *sh;
630   svm_fifo_t *f;
631   u32 count = 0;
632
633   sh = fs->ssvm.sh;
634   fsh = (fifo_segment_header_t *) sh->opaque[0];
635
636   f = fsh->free_fifos;
637   if (f == 0)
638     return 0;
639
640   while (f)
641     {
642       f = f->next;
643       count++;
644     }
645   return count;
646 }
647
648 u32
649 fifo_segment_num_free_chunks (fifo_segment_t * fs, u32 size)
650 {
651   u32 count = 0, rounded_size, fl_index;
652   fifo_segment_header_t *fsh;
653   svm_fifo_chunk_t *c;
654   int i;
655
656   fsh = fs->h;
657
658   /* Count all free chunks? */
659   if (size == ~0)
660     {
661       for (i = 0; i < vec_len (fsh->free_chunks); i++)
662         {
663           c = fsh->free_chunks[i];
664           if (c == 0)
665             continue;
666
667           while (c)
668             {
669               c = c->next;
670               count++;
671             }
672         }
673       return count;
674     }
675
676   rounded_size = (1 << (max_log2 (size)));
677   fl_index = fs_free_list_for_size (rounded_size);
678
679   if (fl_index >= vec_len (fsh->free_chunks))
680     return 0;
681
682   c = fsh->free_chunks[fl_index];
683   if (c == 0)
684     return 0;
685
686   while (c)
687     {
688       c = c->next;
689       count++;
690     }
691   return count;
692 }
693
694 u8
695 fifo_segment_has_fifos (fifo_segment_t * fs)
696 {
697   return fs->h->fifos != 0;
698 }
699
700 svm_fifo_t *
701 fifo_segment_get_fifo_list (fifo_segment_t * fs)
702 {
703   return fs->h->fifos;
704 }
705
706 u8 *
707 format_fifo_segment_type (u8 * s, va_list * args)
708 {
709   fifo_segment_t *sp;
710   sp = va_arg (*args, fifo_segment_t *);
711   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
712
713   if (st == SSVM_SEGMENT_PRIVATE)
714     s = format (s, "%s", "private-heap");
715   else if (st == SSVM_SEGMENT_MEMFD)
716     s = format (s, "%s", "memfd");
717   else if (st == SSVM_SEGMENT_SHM)
718     s = format (s, "%s", "shm");
719   else
720     s = format (s, "%s", "unknown");
721   return s;
722 }
723
724 /**
725  * Segment format function
726  */
727 u8 *
728 format_fifo_segment (u8 * s, va_list * args)
729 {
730   fifo_segment_t *sp = va_arg (*args, fifo_segment_t *);
731   int verbose __attribute__ ((unused)) = va_arg (*args, int);
732   fifo_segment_header_t *fsh = sp->h;
733   u32 count, indent;
734   svm_fifo_chunk_t *c;
735   int i;
736
737   indent = format_get_indent (s) + 2;
738 #if USE_DLMALLOC == 0
739   s = format (s, "%U segment heap: %U\n", format_white_space, indent,
740               format_mheap, svm_fifo_segment_heap (sp), verbose);
741   s = format (s, "%U segment has %u active fifos\n",
742               format_white_space, indent, fifo_segment_num_fifos (sp));
743 #endif
744
745   for (i = 0; i < vec_len (fsh->free_chunks); i++)
746     {
747       c = fsh->free_chunks[i];
748       if (c == 0)
749         continue;
750       count = 0;
751       while (c)
752         {
753           c = c->next;
754           count++;
755         }
756
757       s = format (s, "%U%-5u Kb: %u free",
758                   format_white_space, indent + 2,
759                   1 << (i + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE) - 10),
760                   count);
761     }
762   return s;
763 }
764
765 /*
766  * fd.io coding-style-patch-verification: ON
767  *
768  * Local Variables:
769  * eval: (c-set-style "gnu")
770  * End:
771  */