svm: fix fifo segment free chunk bytes accounting
[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  * Fifo segment free space
20  *
21  * Queries the underlying memory manager, dlmalloc, for free space. Since this
22  * ends up walking the internal data structures, it should not be called
23  * indiscriminately.
24  *
25  * @param fs            fifo segment
26  * @return              number of free bytes
27  */
28 static u32
29 fs_free_space (fifo_segment_t * fs)
30 {
31   struct dlmallinfo dlminfo;
32
33   dlminfo = mspace_mallinfo (fs->ssvm.sh->heap);
34   return dlminfo.fordblks;
35 }
36
37 /**
38  * Initialize fifo segment shared header
39  */
40 int
41 fifo_segment_init (fifo_segment_t * fs)
42 {
43   fifo_segment_header_t *fsh;
44   ssvm_shared_header_t *sh;
45   void *oldheap;
46
47   sh = fs->ssvm.sh;
48   oldheap = ssvm_push_heap (sh);
49
50   fsh = clib_mem_alloc (sizeof (*fsh));
51   clib_memset (fsh, 0, sizeof (*fsh));
52   fs->h = sh->opaque[0] = fsh;
53
54   ssvm_pop_heap (oldheap);
55
56   fsh->n_free_bytes = fs_free_space (fs);
57   sh->ready = 1;
58   return (0);
59 }
60
61 /**
62  * Create a fifo segment and initialize as master
63  */
64 int
65 fifo_segment_create (fifo_segment_main_t * sm, fifo_segment_create_args_t * a)
66 {
67   fifo_segment_t *fs;
68   uword baseva;
69   int rv;
70
71   /* Allocate a fresh segment */
72   pool_get_zero (sm->segments, fs);
73
74   baseva = a->segment_type == SSVM_SEGMENT_PRIVATE ? ~0ULL : sm->next_baseva;
75   fs->ssvm.ssvm_size = a->segment_size;
76   fs->ssvm.i_am_master = 1;
77   fs->ssvm.my_pid = getpid ();
78   fs->ssvm.name = format (0, "%s%c", a->segment_name, 0);
79   fs->ssvm.requested_va = baseva;
80
81   if ((rv = ssvm_master_init (&fs->ssvm, a->segment_type)))
82     {
83       pool_put (sm->segments, fs);
84       return (rv);
85     }
86
87   /* Note: requested_va updated due to seg base addr randomization */
88   sm->next_baseva = fs->ssvm.sh->ssvm_va + fs->ssvm.ssvm_size;
89
90   fifo_segment_init (fs);
91   vec_add1 (a->new_segment_indices, fs - sm->segments);
92   return (0);
93 }
94
95 /**
96  * Attach as slave to a fifo segment
97  */
98 int
99 fifo_segment_attach (fifo_segment_main_t * sm, fifo_segment_create_args_t * a)
100 {
101   fifo_segment_t *s;
102   int rv;
103
104   pool_get_zero (sm->segments, s);
105
106   s->ssvm.ssvm_size = a->segment_size;
107   s->ssvm.my_pid = getpid ();
108   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
109   s->ssvm.requested_va = sm->next_baseva;
110   if (a->segment_type == SSVM_SEGMENT_MEMFD)
111     s->ssvm.fd = a->memfd_fd;
112   else
113     s->ssvm.attach_timeout = sm->timeout_in_seconds;
114
115   if ((rv = ssvm_slave_init (&s->ssvm, a->segment_type)))
116     {
117       _vec_len (s) = vec_len (s) - 1;
118       return (rv);
119     }
120
121   /* Fish the segment header */
122   s->h = s->ssvm.sh->opaque[0];
123
124   vec_add1 (a->new_segment_indices, s - sm->segments);
125   return (0);
126 }
127
128 void
129 fifo_segment_delete (fifo_segment_main_t * sm, fifo_segment_t * s)
130 {
131   ssvm_delete (&s->ssvm);
132   clib_memset (s, 0xfe, sizeof (*s));
133   pool_put (sm->segments, s);
134 }
135
136 u32
137 fifo_segment_index (fifo_segment_main_t * sm, fifo_segment_t * s)
138 {
139   return s - sm->segments;
140 }
141
142 void *
143 svm_fifo_segment_heap (fifo_segment_t * seg)
144 {
145   return seg->ssvm.sh->heap;
146 }
147
148 fifo_segment_t *
149 fifo_segment_get_segment (fifo_segment_main_t * sm, u32 segment_index)
150 {
151   return pool_elt_at_index (sm->segments, segment_index);
152 }
153
154 void
155 fifo_segment_info (fifo_segment_t * seg, char **address, size_t * size)
156 {
157   *address = (char *) seg->ssvm.sh->ssvm_va;
158   *size = seg->ssvm.ssvm_size;
159 }
160
161 void
162 fifo_segment_main_init (fifo_segment_main_t * sm, u64 baseva,
163                         u32 timeout_in_seconds)
164 {
165   sm->next_baseva = baseva;
166   sm->timeout_in_seconds = timeout_in_seconds;
167 }
168
169 static inline u32
170 fs_freelist_for_size (u32 size)
171 {
172   return max_log2 (size) - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
173 }
174
175 static inline u32
176 fs_freelist_index_to_size (u32 fl_index)
177 {
178   return 1 << (fl_index + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE));
179 }
180
181 static inline int
182 fs_chunk_size_is_valid (u32 size)
183 {
184   /*
185    * 4K minimum. It's not likely that anything good will happen
186    * with a smaller FIFO.
187    */
188   return size >= FIFO_SEGMENT_MIN_FIFO_SIZE
189     && size <= FIFO_SEGMENT_MAX_FIFO_SIZE;
190 }
191
192 static svm_fifo_t *
193 fs_try_alloc_fifo_freelist (fifo_segment_t * fs, u32 fl_index, u32 data_bytes)
194 {
195   fifo_segment_header_t *fsh = fs->h;
196   svm_fifo_chunk_t *c;
197   svm_fifo_t *f;
198
199   f = fsh->free_fifos;
200   c = fsh->free_chunks[fl_index];
201
202   if (!f || !c)
203     return 0;
204
205   fsh->free_fifos = f->next;
206   fsh->free_chunks[fl_index] = c->next;
207   c->next = c;
208   c->start_byte = 0;
209   c->length = data_bytes;
210   memset (f, 0, sizeof (*f));
211   f->start_chunk = c;
212   f->end_chunk = c;
213
214   fsh->n_fl_chunk_bytes -= fs_freelist_index_to_size (fl_index);
215   return f;
216 }
217
218 static svm_fifo_t *
219 fs_try_alloc_fifo_freelist_multi_chunk (fifo_segment_t * fs, u32 data_bytes)
220 {
221   svm_fifo_chunk_t *c, *first = 0, *last = 0;
222   fifo_segment_header_t *fsh = fs->h;
223   u32 fl_index, fl_size, n_alloc = 0;
224   svm_fifo_t *f;
225
226   f = fsh->free_fifos;
227   if (!f)
228     {
229       void *oldheap = ssvm_push_heap (fs->ssvm.sh);
230       f = clib_mem_alloc_aligned (sizeof (*f), CLIB_CACHE_LINE_BYTES);
231       ssvm_pop_heap (oldheap);
232       if (!f)
233         return 0;
234       memset (f, 0, sizeof (*f));
235     }
236
237   fl_index = fs_freelist_for_size (data_bytes) - 1;
238   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
239   fl_size = fs_freelist_index_to_size (fl_index);
240
241   while (data_bytes)
242     {
243       c = fsh->free_chunks[fl_index];
244       if (c)
245         {
246           fsh->free_chunks[fl_index] = c->next;
247           if (!last)
248             last = c;
249           c->next = first;
250           first = c;
251           n_alloc += fl_size;
252           c->length = clib_min (fl_size, data_bytes);
253           data_bytes -= c->length;
254         }
255       else
256         {
257           ASSERT (fl_index > 0);
258           fl_index -= 1;
259           fl_size = fl_size >> 1;
260         }
261     }
262   f->start_chunk = first;
263   f->end_chunk = last;
264   last->next = first;
265   fsh->n_fl_chunk_bytes -= n_alloc;
266   return f;
267 }
268
269 static int
270 fs_try_alloc_fifo_batch (fifo_segment_t * fs, u32 fl_index, u32 batch_size)
271 {
272   fifo_segment_header_t *fsh = fs->h;
273   u32 size, hdrs, rounded_data_size;
274   svm_fifo_chunk_t *c;
275   svm_fifo_t *f;
276   void *oldheap;
277   u8 *fmem;
278   int i;
279
280   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
281   rounded_data_size = fs_freelist_index_to_size (fl_index);
282   hdrs = sizeof (*f) + sizeof (*c);
283   size = (hdrs + rounded_data_size) * batch_size;
284
285   oldheap = ssvm_push_heap (fs->ssvm.sh);
286   fmem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
287                                            0 /* align_offset */ ,
288                                            0 /* os_out_of_memory */ );
289   ssvm_pop_heap (oldheap);
290
291   /* Out of space.. */
292   if (fmem == 0)
293     return -1;
294
295   /* Carve fifo + chunk space */
296   for (i = 0; i < batch_size; i++)
297     {
298       f = (svm_fifo_t *) fmem;
299       memset (f, 0, sizeof (*f));
300       f->next = fsh->free_fifos;
301       fsh->free_fifos = f;
302       c = (svm_fifo_chunk_t *) (fmem + sizeof (*f));
303       c->start_byte = 0;
304       c->length = rounded_data_size;
305       c->next = fsh->free_chunks[fl_index];
306       fsh->free_chunks[fl_index] = c;
307       fmem += hdrs + rounded_data_size;
308     }
309
310   fsh->n_fl_chunk_bytes += batch_size * rounded_data_size;
311   fsh->n_free_bytes -= size;
312
313   return 0;
314 }
315
316 /**
317  * Try to allocate new fifo
318  *
319  * Tries the following steps in order:
320  * - grab fifo and chunk from freelists
321  * - batch fifo and chunk allocation
322  * - single fifo allocation
323  * - grab multiple fifo chunks from freelists
324  */
325 static svm_fifo_t *
326 fs_try_alloc_fifo (fifo_segment_t * fs, u32 data_bytes)
327 {
328   fifo_segment_header_t *fsh = fs->h;
329   u32 fifo_sz, fl_index;
330   svm_fifo_t *f = 0;
331
332   fl_index = fs_freelist_for_size (data_bytes);
333   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
334   fifo_sz = sizeof (svm_fifo_t) + sizeof (svm_fifo_chunk_t);
335   fifo_sz += 1 << max_log2 (data_bytes);
336
337   if (fsh->free_fifos && fsh->free_chunks[fl_index])
338     {
339       f = fs_try_alloc_fifo_freelist (fs, fl_index, data_bytes);
340       if (f)
341         goto done;
342     }
343   if (fifo_sz * FIFO_SEGMENT_ALLOC_BATCH_SIZE < fsh->n_free_bytes)
344     {
345       if (fs_try_alloc_fifo_batch (fs, fl_index,
346                                    FIFO_SEGMENT_ALLOC_BATCH_SIZE))
347         goto done;
348
349       f = fs_try_alloc_fifo_freelist (fs, fl_index, data_bytes);
350       goto done;
351     }
352   if (fifo_sz <= fsh->n_free_bytes)
353     {
354       void *oldheap = ssvm_push_heap (fs->ssvm.sh);
355       f = svm_fifo_create (data_bytes);
356       ssvm_pop_heap (oldheap);
357       if (f)
358         {
359           fsh->n_free_bytes -= fifo_sz;
360           goto done;
361         }
362     }
363   if (data_bytes <= fsh->n_fl_chunk_bytes)
364     f = fs_try_alloc_fifo_freelist_multi_chunk (fs, data_bytes);
365
366 done:
367
368   return f;
369 }
370
371 /**
372  * Allocate fifo in fifo segment
373  */
374 svm_fifo_t *
375 fifo_segment_alloc_fifo (fifo_segment_t * fs, u32 data_bytes,
376                          fifo_segment_ftype_t ftype)
377 {
378   fifo_segment_header_t *fsh;
379   svm_fifo_t *f = 0;
380
381   if (!fs_chunk_size_is_valid (data_bytes))
382     {
383       clib_warning ("fifo size out of range %d", data_bytes);
384       return 0;
385     }
386
387   fsh = fs->h;
388   ssvm_lock_non_recursive (fs->ssvm.sh, 1);
389
390   f = fs_try_alloc_fifo (fs, data_bytes);
391   if (!f)
392     goto done;
393
394   /* (re)initialize the fifo, as in svm_fifo_create */
395   svm_fifo_init (f, data_bytes);
396
397   /* If rx fifo type add to active fifos list. When cleaning up segment,
398    * we need a list of active sessions that should be disconnected. Since
399    * both rx and tx fifos keep pointers to the session, it's enough to track
400    * only one. */
401   if (ftype == FIFO_SEGMENT_RX_FIFO)
402     {
403       if (fsh->fifos)
404         {
405           fsh->fifos->prev = f;
406           f->next = fsh->fifos;
407         }
408       fsh->fifos = f;
409       f->flags |= SVM_FIFO_F_LL_TRACKED;
410     }
411   fsh->n_active_fifos++;
412
413 done:
414   ssvm_unlock_non_recursive (fs->ssvm.sh);
415   return (f);
416 }
417
418 /**
419  * Free fifo allocated in fifo segment
420  */
421 void
422 fifo_segment_free_fifo (fifo_segment_t * fs, svm_fifo_t * f)
423 {
424   svm_fifo_chunk_t *cur, *next;
425   fifo_segment_header_t *fsh;
426   ssvm_shared_header_t *sh;
427   void *oldheap;
428   int fl_index;
429
430   ASSERT (f->refcnt > 0);
431
432   if (--f->refcnt > 0)
433     return;
434
435   sh = fs->ssvm.sh;
436   fsh = fs->h;
437
438   ssvm_lock_non_recursive (sh, 2);
439
440   /* Remove from active list. Only rx fifos are tracked */
441   if (f->flags & SVM_FIFO_F_LL_TRACKED)
442     {
443       if (f->prev)
444         f->prev->next = f->next;
445       else
446         fsh->fifos = f->next;
447       if (f->next)
448         f->next->prev = f->prev;
449       f->flags &= ~SVM_FIFO_F_LL_TRACKED;
450     }
451
452   /* Add to free list */
453   f->next = fsh->free_fifos;
454   f->prev = 0;
455   fsh->free_fifos = f;
456
457   /* Free fifo chunks */
458   cur = f->start_chunk;
459   do
460     {
461       next = cur->next;
462       fl_index = fs_freelist_for_size (cur->length);
463       ASSERT (fl_index < vec_len (fsh->free_chunks));
464       cur->next = fsh->free_chunks[fl_index];
465       fsh->free_chunks[fl_index] = cur;
466       fsh->n_fl_chunk_bytes += fs_freelist_index_to_size (fl_index);
467       cur = next;
468     }
469   while (cur != f->start_chunk);
470
471   f->start_chunk = f->end_chunk = f->new_chunks = 0;
472   f->head_chunk = f->tail_chunk = f->ooo_enq = f->ooo_deq = 0;
473
474   oldheap = ssvm_push_heap (sh);
475   svm_fifo_free_chunk_lookup (f);
476   ssvm_pop_heap (oldheap);
477
478   /* not allocated on segment heap */
479   svm_fifo_free_ooo_data (f);
480
481   if (CLIB_DEBUG)
482     {
483       f->master_session_index = ~0;
484       f->master_thread_index = ~0;
485     }
486
487   fsh->n_active_fifos--;
488   ssvm_unlock_non_recursive (sh);
489 }
490
491 int
492 fifo_segment_prealloc_fifo_hdrs (fifo_segment_t * fs, u32 batch_size)
493 {
494   fifo_segment_header_t *fsh = fs->h;
495   svm_fifo_t *f;
496   void *oldheap;
497   u32 size;
498   u8 *fmem;
499   int i;
500
501   size = (sizeof (*f)) * batch_size;
502
503   oldheap = ssvm_push_heap (fs->ssvm.sh);
504   fmem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
505                                            0 /* align_offset */ ,
506                                            0 /* os_out_of_memory */ );
507   ssvm_pop_heap (oldheap);
508
509   /* Out of space.. */
510   if (fmem == 0)
511     return -1;
512
513   /* Carve fifo + chunk space */
514   for (i = 0; i < batch_size; i++)
515     {
516       f = (svm_fifo_t *) fmem;
517       memset (f, 0, sizeof (*f));
518       f->next = fsh->free_fifos;
519       fsh->free_fifos = f;
520       fmem += sizeof (*f);
521     }
522
523   fsh->n_free_bytes -= size;
524
525   return 0;
526 }
527
528 int
529 fifo_segment_prealloc_fifo_chunks (fifo_segment_t * fs, u32 chunk_size,
530                                    u32 batch_size)
531 {
532   fifo_segment_header_t *fsh = fs->h;
533   u32 size, rounded_data_size, fl_index;
534   svm_fifo_chunk_t *c;
535   void *oldheap;
536   u8 *cmem;
537   int i;
538
539   if (!fs_chunk_size_is_valid (chunk_size))
540     {
541       clib_warning ("chunk size out of range %d", chunk_size);
542       return -1;
543     }
544
545   fl_index = fs_freelist_for_size (chunk_size);
546   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
547   rounded_data_size = fs_freelist_index_to_size (fl_index);
548   size = (sizeof (*c) + rounded_data_size) * batch_size;
549
550   oldheap = ssvm_push_heap (fs->ssvm.sh);
551   cmem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
552                                            0 /* align_offset */ ,
553                                            0 /* os_out_of_memory */ );
554   ssvm_pop_heap (oldheap);
555
556   /* Out of space.. */
557   if (cmem == 0)
558     return -1;
559
560   /* Carve fifo + chunk space */
561   for (i = 0; i < batch_size; i++)
562     {
563       c = (svm_fifo_chunk_t *) cmem;
564       c->start_byte = 0;
565       c->length = rounded_data_size;
566       c->next = fsh->free_chunks[fl_index];
567       fsh->free_chunks[fl_index] = c;
568       cmem += sizeof (*c) + rounded_data_size;
569     }
570
571   fsh->n_fl_chunk_bytes += batch_size * rounded_data_size;
572   fsh->n_free_bytes -= size;
573
574   return 0;
575 }
576
577 /**
578  * Pre-allocates fifo pairs in fifo segment
579  */
580 void
581 fifo_segment_preallocate_fifo_pairs (fifo_segment_t * fs,
582                                      u32 rx_fifo_size, u32 tx_fifo_size,
583                                      u32 * n_fifo_pairs)
584 {
585   u32 rx_rounded_data_size, tx_rounded_data_size, pair_size, pairs_to_alloc;
586   int rx_fl_index, tx_fl_index;
587   uword space_available;
588   u32 hdrs;
589
590   /* Parameter check */
591   if (rx_fifo_size == 0 || tx_fifo_size == 0 || *n_fifo_pairs == 0)
592     return;
593
594   if (!fs_chunk_size_is_valid (rx_fifo_size))
595     {
596       clib_warning ("rx fifo_size out of range %d", rx_fifo_size);
597       return;
598     }
599
600   if (!fs_chunk_size_is_valid (tx_fifo_size))
601     {
602       clib_warning ("tx fifo_size out of range %d", tx_fifo_size);
603       return;
604     }
605
606   rx_rounded_data_size = (1 << (max_log2 (rx_fifo_size)));
607   rx_fl_index = fs_freelist_for_size (rx_fifo_size);
608   tx_rounded_data_size = (1 << (max_log2 (tx_fifo_size)));
609   tx_fl_index = fs_freelist_for_size (tx_fifo_size);
610
611   hdrs = sizeof (svm_fifo_t) + sizeof (svm_fifo_chunk_t);
612
613   /* Calculate space requirements */
614   pair_size = 2 * hdrs + rx_rounded_data_size + tx_rounded_data_size;
615   space_available = fs_free_space (fs);
616   pairs_to_alloc = clib_min (space_available / pair_size, *n_fifo_pairs);
617
618   if (fs_try_alloc_fifo_batch (fs, rx_fl_index, pairs_to_alloc))
619     clib_warning ("rx prealloc failed");
620   if (fs_try_alloc_fifo_batch (fs, tx_fl_index, pairs_to_alloc))
621     clib_warning ("tx prealloc failed");
622
623   /* Account for the pairs allocated */
624   *n_fifo_pairs -= pairs_to_alloc;
625 }
626
627 int
628 fifo_segment_grow_fifo (fifo_segment_t * fs, svm_fifo_t * f, u32 chunk_size)
629 {
630   ssvm_shared_header_t *sh;
631   svm_fifo_chunk_t *c;
632   void *oldheap;
633   int fl_index;
634
635   if (!fs_chunk_size_is_valid (chunk_size))
636     {
637       clib_warning ("chunk size out of range %d", chunk_size);
638       return -1;
639     }
640
641   fl_index = fs_freelist_for_size (chunk_size);
642
643   sh = fs->ssvm.sh;
644   ssvm_lock_non_recursive (sh, 1);
645
646   vec_validate_init_empty (fs->h->free_chunks, fl_index, 0);
647   c = fs->h->free_chunks[fl_index];
648
649   oldheap = ssvm_push_heap (sh);
650
651   if (!c)
652     {
653       c = svm_fifo_chunk_alloc (chunk_size);
654       if (!c)
655         {
656           ssvm_pop_heap (oldheap);
657           ssvm_unlock_non_recursive (sh);
658           return -1;
659         }
660     }
661   else
662     {
663       fs->h->free_chunks[fl_index] = c->next;
664       c->next = 0;
665       fs->h->n_fl_chunk_bytes -= fs_freelist_index_to_size (fl_index);
666     }
667
668   svm_fifo_add_chunk (f, c);
669
670   ssvm_pop_heap (oldheap);
671   ssvm_unlock_non_recursive (sh);
672   return 0;
673 }
674
675 int
676 fifo_segment_collect_fifo_chunks (fifo_segment_t * fs, svm_fifo_t * f)
677 {
678   svm_fifo_chunk_t *cur, *next;
679   ssvm_shared_header_t *sh;
680   void *oldheap;
681   int fl_index;
682
683   sh = fs->ssvm.sh;
684   ssvm_lock_non_recursive (sh, 1);
685
686   oldheap = ssvm_push_heap (sh);
687   cur = svm_fifo_collect_chunks (f);
688
689   while (cur)
690     {
691       next = cur->next;
692       fl_index = fs_freelist_for_size (cur->length);
693       cur->next = fs->h->free_chunks[fl_index];
694       fs->h->free_chunks[fl_index] = cur;
695       cur = next;
696     }
697
698   ssvm_pop_heap (oldheap);
699   ssvm_unlock_non_recursive (sh);
700
701   return 0;
702 }
703
704 /**
705  * Get number of active fifos
706  */
707 u32
708 fifo_segment_num_fifos (fifo_segment_t * fs)
709 {
710   return fs->h->n_active_fifos;
711 }
712
713 u32
714 fifo_segment_num_free_fifos (fifo_segment_t * fs)
715 {
716   fifo_segment_header_t *fsh = fs->h;
717   svm_fifo_t *f;
718   u32 count = 0;
719
720   f = fsh->free_fifos;
721   if (f == 0)
722     return 0;
723
724   while (f)
725     {
726       f = f->next;
727       count++;
728     }
729   return count;
730 }
731
732 u32
733 fifo_segment_num_free_chunks (fifo_segment_t * fs, u32 size)
734 {
735   u32 count = 0, rounded_size, fl_index;
736   fifo_segment_header_t *fsh;
737   svm_fifo_chunk_t *c;
738   int i;
739
740   fsh = fs->h;
741
742   /* Count all free chunks? */
743   if (size == ~0)
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
751           while (c)
752             {
753               c = c->next;
754               count++;
755             }
756         }
757       return count;
758     }
759
760   rounded_size = (1 << (max_log2 (size)));
761   fl_index = fs_freelist_for_size (rounded_size);
762
763   if (fl_index >= vec_len (fsh->free_chunks))
764     return 0;
765
766   c = fsh->free_chunks[fl_index];
767   if (c == 0)
768     return 0;
769
770   while (c)
771     {
772       c = c->next;
773       count++;
774     }
775   return count;
776 }
777
778 void
779 fifo_segment_update_free_bytes (fifo_segment_t * fs)
780 {
781   fs->h->n_free_bytes = fs_free_space (fs);
782 }
783
784 u32
785 fifo_segment_free_bytes (fifo_segment_t * fs)
786 {
787   return fs->h->n_free_bytes;
788 }
789
790 u32
791 fifo_segment_fl_chunk_bytes (fifo_segment_t * fs)
792 {
793   return fs->h->n_fl_chunk_bytes;
794 }
795
796 u8
797 fifo_segment_has_fifos (fifo_segment_t * fs)
798 {
799   return fs->h->fifos != 0;
800 }
801
802 svm_fifo_t *
803 fifo_segment_get_fifo_list (fifo_segment_t * fs)
804 {
805   return fs->h->fifos;
806 }
807
808 u8 *
809 format_fifo_segment_type (u8 * s, va_list * args)
810 {
811   fifo_segment_t *sp;
812   sp = va_arg (*args, fifo_segment_t *);
813   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
814
815   if (st == SSVM_SEGMENT_PRIVATE)
816     s = format (s, "%s", "private-heap");
817   else if (st == SSVM_SEGMENT_MEMFD)
818     s = format (s, "%s", "memfd");
819   else if (st == SSVM_SEGMENT_SHM)
820     s = format (s, "%s", "shm");
821   else
822     s = format (s, "%s", "unknown");
823   return s;
824 }
825
826 /**
827  * Segment format function
828  */
829 u8 *
830 format_fifo_segment (u8 * s, va_list * args)
831 {
832   fifo_segment_t *fs = va_arg (*args, fifo_segment_t *);
833   int verbose __attribute__ ((unused)) = va_arg (*args, int);
834   fifo_segment_header_t *fsh;
835   svm_fifo_chunk_t *c;
836   u32 count, indent;
837   u32 active_fifos;
838   u32 free_fifos;
839   char *address;
840   size_t size;
841   int i;
842
843   indent = format_get_indent (s) + 2;
844 #if USE_DLMALLOC == 0
845   s = format (s, "%U segment heap: %U\n", format_white_space, indent,
846               format_mheap, svm_fifo_segment_heap (fs), verbose);
847   s = format (s, "%U segment has %u active fifos\n",
848               format_white_space, indent, fifo_segment_num_fifos (fs));
849 #endif
850
851   if (fs == 0)
852     {
853       s = format (s, "%-15s%15s%15s%15s%15s%15s", "Name", "Type",
854                   "HeapSize (M)", "ActiveFifos", "FreeFifos", "Address");
855       return s;
856     }
857
858   fsh = fs->h;
859   fifo_segment_info (fs, &address, &size);
860   active_fifos = fifo_segment_num_fifos (fs);
861   free_fifos = fifo_segment_num_free_fifos (fs);
862
863   s = format (s, "%-15v%15U%15llu%15u%15u%15llx", ssvm_name (&fs->ssvm),
864               format_fifo_segment_type, fs, size >> 20ULL, active_fifos,
865               free_fifos, address);
866
867   if (!verbose)
868     return s;
869
870   s = format (s, "\n");
871   for (i = 0; i < vec_len (fsh->free_chunks); i++)
872     {
873       c = fsh->free_chunks[i];
874       if (c == 0)
875         continue;
876       count = 0;
877       while (c)
878         {
879           c = c->next;
880           count++;
881         }
882
883       s = format (s, "%U%-5u Kb: %u free", format_white_space, indent + 2,
884                   1 << (i + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE) - 10),
885                   count);
886     }
887   s = format (s, "%Ufree bytes %U", format_white_space, indent + 2,
888               format_memory_size, fsh->n_free_bytes);
889
890   return s;
891 }
892
893 /*
894  * fd.io coding-style-patch-verification: ON
895  *
896  * Local Variables:
897  * eval: (c-set-style "gnu")
898  * End:
899  */