svm: fix multi-chunk fifo alloc and add more tests
[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   /* Initialize chunks and rbtree for multi-chunk fifos */
398   if (f->start_chunk->next != f->start_chunk)
399     {
400       void *oldheap = ssvm_push_heap (fs->ssvm.sh);
401       svm_fifo_init_chunks (f);
402       ssvm_pop_heap (oldheap);
403     }
404
405   /* If rx fifo type add to active fifos list. When cleaning up segment,
406    * we need a list of active sessions that should be disconnected. Since
407    * both rx and tx fifos keep pointers to the session, it's enough to track
408    * only one. */
409   if (ftype == FIFO_SEGMENT_RX_FIFO)
410     {
411       if (fsh->fifos)
412         {
413           fsh->fifos->prev = f;
414           f->next = fsh->fifos;
415         }
416       fsh->fifos = f;
417       f->flags |= SVM_FIFO_F_LL_TRACKED;
418     }
419   fsh->n_active_fifos++;
420
421 done:
422   ssvm_unlock_non_recursive (fs->ssvm.sh);
423   return (f);
424 }
425
426 /**
427  * Free fifo allocated in fifo segment
428  */
429 void
430 fifo_segment_free_fifo (fifo_segment_t * fs, svm_fifo_t * f)
431 {
432   svm_fifo_chunk_t *cur, *next;
433   fifo_segment_header_t *fsh;
434   ssvm_shared_header_t *sh;
435   void *oldheap;
436   int fl_index;
437
438   ASSERT (f->refcnt > 0);
439
440   if (--f->refcnt > 0)
441     return;
442
443   sh = fs->ssvm.sh;
444   fsh = fs->h;
445
446   ssvm_lock_non_recursive (sh, 2);
447
448   /* Remove from active list. Only rx fifos are tracked */
449   if (f->flags & SVM_FIFO_F_LL_TRACKED)
450     {
451       if (f->prev)
452         f->prev->next = f->next;
453       else
454         fsh->fifos = f->next;
455       if (f->next)
456         f->next->prev = f->prev;
457       f->flags &= ~SVM_FIFO_F_LL_TRACKED;
458     }
459
460   /* Add to free list */
461   f->next = fsh->free_fifos;
462   f->prev = 0;
463   fsh->free_fifos = f;
464
465   /* Free fifo chunks */
466   cur = f->start_chunk;
467   do
468     {
469       next = cur->next;
470       fl_index = fs_freelist_for_size (cur->length);
471       ASSERT (fl_index < vec_len (fsh->free_chunks));
472       cur->next = fsh->free_chunks[fl_index];
473       fsh->free_chunks[fl_index] = cur;
474       fsh->n_fl_chunk_bytes += fs_freelist_index_to_size (fl_index);
475       cur = next;
476     }
477   while (cur != f->start_chunk);
478
479   f->start_chunk = f->end_chunk = f->new_chunks = 0;
480   f->head_chunk = f->tail_chunk = f->ooo_enq = f->ooo_deq = 0;
481
482   oldheap = ssvm_push_heap (sh);
483   svm_fifo_free_chunk_lookup (f);
484   ssvm_pop_heap (oldheap);
485
486   /* not allocated on segment heap */
487   svm_fifo_free_ooo_data (f);
488
489   if (CLIB_DEBUG)
490     {
491       f->master_session_index = ~0;
492       f->master_thread_index = ~0;
493     }
494
495   fsh->n_active_fifos--;
496   ssvm_unlock_non_recursive (sh);
497 }
498
499 int
500 fifo_segment_prealloc_fifo_hdrs (fifo_segment_t * fs, u32 batch_size)
501 {
502   fifo_segment_header_t *fsh = fs->h;
503   svm_fifo_t *f;
504   void *oldheap;
505   u32 size;
506   u8 *fmem;
507   int i;
508
509   size = (sizeof (*f)) * batch_size;
510
511   oldheap = ssvm_push_heap (fs->ssvm.sh);
512   fmem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
513                                            0 /* align_offset */ ,
514                                            0 /* os_out_of_memory */ );
515   ssvm_pop_heap (oldheap);
516
517   /* Out of space.. */
518   if (fmem == 0)
519     return -1;
520
521   /* Carve fifo + chunk space */
522   for (i = 0; i < batch_size; i++)
523     {
524       f = (svm_fifo_t *) fmem;
525       memset (f, 0, sizeof (*f));
526       f->next = fsh->free_fifos;
527       fsh->free_fifos = f;
528       fmem += sizeof (*f);
529     }
530
531   fsh->n_free_bytes -= size;
532
533   return 0;
534 }
535
536 int
537 fifo_segment_prealloc_fifo_chunks (fifo_segment_t * fs, u32 chunk_size,
538                                    u32 batch_size)
539 {
540   fifo_segment_header_t *fsh = fs->h;
541   u32 size, rounded_data_size, fl_index;
542   svm_fifo_chunk_t *c;
543   void *oldheap;
544   u8 *cmem;
545   int i;
546
547   if (!fs_chunk_size_is_valid (chunk_size))
548     {
549       clib_warning ("chunk size out of range %d", chunk_size);
550       return -1;
551     }
552
553   fl_index = fs_freelist_for_size (chunk_size);
554   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
555   rounded_data_size = fs_freelist_index_to_size (fl_index);
556   size = (sizeof (*c) + rounded_data_size) * batch_size;
557
558   oldheap = ssvm_push_heap (fs->ssvm.sh);
559   cmem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
560                                            0 /* align_offset */ ,
561                                            0 /* os_out_of_memory */ );
562   ssvm_pop_heap (oldheap);
563
564   /* Out of space.. */
565   if (cmem == 0)
566     return -1;
567
568   /* Carve fifo + chunk space */
569   for (i = 0; i < batch_size; i++)
570     {
571       c = (svm_fifo_chunk_t *) cmem;
572       c->start_byte = 0;
573       c->length = rounded_data_size;
574       c->next = fsh->free_chunks[fl_index];
575       fsh->free_chunks[fl_index] = c;
576       cmem += sizeof (*c) + rounded_data_size;
577     }
578
579   fsh->n_fl_chunk_bytes += batch_size * rounded_data_size;
580   fsh->n_free_bytes -= size;
581
582   return 0;
583 }
584
585 /**
586  * Pre-allocates fifo pairs in fifo segment
587  */
588 void
589 fifo_segment_preallocate_fifo_pairs (fifo_segment_t * fs,
590                                      u32 rx_fifo_size, u32 tx_fifo_size,
591                                      u32 * n_fifo_pairs)
592 {
593   u32 rx_rounded_data_size, tx_rounded_data_size, pair_size, pairs_to_alloc;
594   int rx_fl_index, tx_fl_index;
595   uword space_available;
596   u32 hdrs;
597
598   /* Parameter check */
599   if (rx_fifo_size == 0 || tx_fifo_size == 0 || *n_fifo_pairs == 0)
600     return;
601
602   if (!fs_chunk_size_is_valid (rx_fifo_size))
603     {
604       clib_warning ("rx fifo_size out of range %d", rx_fifo_size);
605       return;
606     }
607
608   if (!fs_chunk_size_is_valid (tx_fifo_size))
609     {
610       clib_warning ("tx fifo_size out of range %d", tx_fifo_size);
611       return;
612     }
613
614   rx_rounded_data_size = (1 << (max_log2 (rx_fifo_size)));
615   rx_fl_index = fs_freelist_for_size (rx_fifo_size);
616   tx_rounded_data_size = (1 << (max_log2 (tx_fifo_size)));
617   tx_fl_index = fs_freelist_for_size (tx_fifo_size);
618
619   hdrs = sizeof (svm_fifo_t) + sizeof (svm_fifo_chunk_t);
620
621   /* Calculate space requirements */
622   pair_size = 2 * hdrs + rx_rounded_data_size + tx_rounded_data_size;
623   space_available = fs_free_space (fs);
624   pairs_to_alloc = space_available / pair_size;
625   pairs_to_alloc = clib_min (pairs_to_alloc, *n_fifo_pairs);
626
627   if (!pairs_to_alloc)
628     return;
629
630   if (fs_try_alloc_fifo_batch (fs, rx_fl_index, pairs_to_alloc))
631     clib_warning ("rx prealloc failed: pairs %u", pairs_to_alloc);
632   if (fs_try_alloc_fifo_batch (fs, tx_fl_index, pairs_to_alloc))
633     clib_warning ("tx prealloc failed: pairs %u", pairs_to_alloc);
634
635   /* Account for the pairs allocated */
636   *n_fifo_pairs -= pairs_to_alloc;
637 }
638
639 int
640 fifo_segment_grow_fifo (fifo_segment_t * fs, svm_fifo_t * f, u32 chunk_size)
641 {
642   ssvm_shared_header_t *sh;
643   svm_fifo_chunk_t *c;
644   void *oldheap;
645   int fl_index;
646
647   if (!fs_chunk_size_is_valid (chunk_size))
648     {
649       clib_warning ("chunk size out of range %d", chunk_size);
650       return -1;
651     }
652
653   fl_index = fs_freelist_for_size (chunk_size);
654
655   sh = fs->ssvm.sh;
656   ssvm_lock_non_recursive (sh, 1);
657
658   vec_validate_init_empty (fs->h->free_chunks, fl_index, 0);
659   c = fs->h->free_chunks[fl_index];
660
661   oldheap = ssvm_push_heap (sh);
662
663   if (!c)
664     {
665       c = svm_fifo_chunk_alloc (chunk_size);
666       if (!c)
667         {
668           ssvm_pop_heap (oldheap);
669           ssvm_unlock_non_recursive (sh);
670           return -1;
671         }
672     }
673   else
674     {
675       fs->h->free_chunks[fl_index] = c->next;
676       c->next = 0;
677       fs->h->n_fl_chunk_bytes -= fs_freelist_index_to_size (fl_index);
678     }
679
680   svm_fifo_add_chunk (f, c);
681
682   ssvm_pop_heap (oldheap);
683   ssvm_unlock_non_recursive (sh);
684   return 0;
685 }
686
687 int
688 fifo_segment_collect_fifo_chunks (fifo_segment_t * fs, svm_fifo_t * f)
689 {
690   svm_fifo_chunk_t *cur, *next;
691   ssvm_shared_header_t *sh;
692   void *oldheap;
693   int fl_index;
694
695   sh = fs->ssvm.sh;
696   ssvm_lock_non_recursive (sh, 1);
697
698   oldheap = ssvm_push_heap (sh);
699   cur = svm_fifo_collect_chunks (f);
700
701   while (cur)
702     {
703       next = cur->next;
704       fl_index = fs_freelist_for_size (cur->length);
705       cur->next = fs->h->free_chunks[fl_index];
706       fs->h->free_chunks[fl_index] = cur;
707       cur = next;
708     }
709
710   ssvm_pop_heap (oldheap);
711   ssvm_unlock_non_recursive (sh);
712
713   return 0;
714 }
715
716 /**
717  * Get number of active fifos
718  */
719 u32
720 fifo_segment_num_fifos (fifo_segment_t * fs)
721 {
722   return fs->h->n_active_fifos;
723 }
724
725 u32
726 fifo_segment_num_free_fifos (fifo_segment_t * fs)
727 {
728   fifo_segment_header_t *fsh = fs->h;
729   svm_fifo_t *f;
730   u32 count = 0;
731
732   f = fsh->free_fifos;
733   if (f == 0)
734     return 0;
735
736   while (f)
737     {
738       f = f->next;
739       count++;
740     }
741   return count;
742 }
743
744 u32
745 fifo_segment_num_free_chunks (fifo_segment_t * fs, u32 size)
746 {
747   u32 count = 0, rounded_size, fl_index;
748   fifo_segment_header_t *fsh;
749   svm_fifo_chunk_t *c;
750   int i;
751
752   fsh = fs->h;
753
754   /* Count all free chunks? */
755   if (size == ~0)
756     {
757       for (i = 0; i < vec_len (fsh->free_chunks); i++)
758         {
759           c = fsh->free_chunks[i];
760           if (c == 0)
761             continue;
762
763           while (c)
764             {
765               c = c->next;
766               count++;
767             }
768         }
769       return count;
770     }
771
772   rounded_size = (1 << (max_log2 (size)));
773   fl_index = fs_freelist_for_size (rounded_size);
774
775   if (fl_index >= vec_len (fsh->free_chunks))
776     return 0;
777
778   c = fsh->free_chunks[fl_index];
779   if (c == 0)
780     return 0;
781
782   while (c)
783     {
784       c = c->next;
785       count++;
786     }
787   return count;
788 }
789
790 void
791 fifo_segment_update_free_bytes (fifo_segment_t * fs)
792 {
793   fs->h->n_free_bytes = fs_free_space (fs);
794 }
795
796 u32
797 fifo_segment_free_bytes (fifo_segment_t * fs)
798 {
799   return fs->h->n_free_bytes;
800 }
801
802 u32
803 fifo_segment_fl_chunk_bytes (fifo_segment_t * fs)
804 {
805   return fs->h->n_fl_chunk_bytes;
806 }
807
808 u8
809 fifo_segment_has_fifos (fifo_segment_t * fs)
810 {
811   return fs->h->fifos != 0;
812 }
813
814 svm_fifo_t *
815 fifo_segment_get_fifo_list (fifo_segment_t * fs)
816 {
817   return fs->h->fifos;
818 }
819
820 u8 *
821 format_fifo_segment_type (u8 * s, va_list * args)
822 {
823   fifo_segment_t *sp;
824   sp = va_arg (*args, fifo_segment_t *);
825   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
826
827   if (st == SSVM_SEGMENT_PRIVATE)
828     s = format (s, "%s", "private-heap");
829   else if (st == SSVM_SEGMENT_MEMFD)
830     s = format (s, "%s", "memfd");
831   else if (st == SSVM_SEGMENT_SHM)
832     s = format (s, "%s", "shm");
833   else
834     s = format (s, "%s", "unknown");
835   return s;
836 }
837
838 /**
839  * Segment format function
840  */
841 u8 *
842 format_fifo_segment (u8 * s, va_list * args)
843 {
844   fifo_segment_t *fs = va_arg (*args, fifo_segment_t *);
845   int verbose __attribute__ ((unused)) = va_arg (*args, int);
846   fifo_segment_header_t *fsh;
847   svm_fifo_chunk_t *c;
848   u32 count, indent;
849   u32 active_fifos;
850   u32 free_fifos;
851   char *address;
852   size_t size;
853   int i;
854
855   indent = format_get_indent (s) + 2;
856 #if USE_DLMALLOC == 0
857   s = format (s, "%U segment heap: %U\n", format_white_space, indent,
858               format_mheap, svm_fifo_segment_heap (fs), verbose);
859   s = format (s, "%U segment has %u active fifos\n",
860               format_white_space, indent, fifo_segment_num_fifos (fs));
861 #endif
862
863   if (fs == 0)
864     {
865       s = format (s, "%-15s%15s%15s%15s%15s%15s", "Name", "Type",
866                   "HeapSize (M)", "ActiveFifos", "FreeFifos", "Address");
867       return s;
868     }
869
870   fsh = fs->h;
871   fifo_segment_info (fs, &address, &size);
872   active_fifos = fifo_segment_num_fifos (fs);
873   free_fifos = fifo_segment_num_free_fifos (fs);
874
875   s = format (s, "%-15v%15U%15llu%15u%15u%15llx", ssvm_name (&fs->ssvm),
876               format_fifo_segment_type, fs, size >> 20ULL, active_fifos,
877               free_fifos, address);
878
879   if (!verbose)
880     return s;
881
882   s = format (s, "\n");
883   for (i = 0; i < vec_len (fsh->free_chunks); i++)
884     {
885       c = fsh->free_chunks[i];
886       if (c == 0)
887         continue;
888       count = 0;
889       while (c)
890         {
891           c = c->next;
892           count++;
893         }
894
895       s = format (s, "%U%-5u Kb: %u free", format_white_space, indent + 2,
896                   1 << (i + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE) - 10),
897                   count);
898     }
899   s = format (s, "%Ufree bytes %U", format_white_space, indent + 2,
900               format_memory_size, fsh->n_free_bytes);
901
902   return s;
903 }
904
905 /*
906  * fd.io coding-style-patch-verification: ON
907  *
908  * Local Variables:
909  * eval: (c-set-style "gnu")
910  * End:
911  */