dab97a55209af76b15355c7f417d767515bc0ea1
[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   else
237     {
238       fsh->free_fifos = f->next;
239     }
240
241   fl_index = fs_freelist_for_size (data_bytes) - 1;
242   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
243   fl_size = fs_freelist_index_to_size (fl_index);
244
245   while (data_bytes)
246     {
247       c = fsh->free_chunks[fl_index];
248       if (c)
249         {
250           fsh->free_chunks[fl_index] = c->next;
251           if (!last)
252             last = c;
253           c->next = first;
254           first = c;
255           n_alloc += fl_size;
256           c->length = clib_min (fl_size, data_bytes);
257           data_bytes -= c->length;
258         }
259       else
260         {
261           ASSERT (fl_index > 0);
262           fl_index -= 1;
263           fl_size = fl_size >> 1;
264         }
265     }
266   f->start_chunk = first;
267   f->end_chunk = last;
268   last->next = first;
269   fsh->n_fl_chunk_bytes -= n_alloc;
270   return f;
271 }
272
273 static int
274 fs_try_alloc_fifo_batch (fifo_segment_t * fs, u32 fl_index, u32 batch_size)
275 {
276   fifo_segment_header_t *fsh = fs->h;
277   u32 size, hdrs, rounded_data_size;
278   svm_fifo_chunk_t *c;
279   svm_fifo_t *f;
280   void *oldheap;
281   u8 *fmem;
282   int i;
283
284   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
285   rounded_data_size = fs_freelist_index_to_size (fl_index);
286   hdrs = sizeof (*f) + sizeof (*c);
287   size = (hdrs + rounded_data_size) * batch_size;
288
289   oldheap = ssvm_push_heap (fs->ssvm.sh);
290   fmem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
291                                            0 /* align_offset */ ,
292                                            0 /* os_out_of_memory */ );
293   ssvm_pop_heap (oldheap);
294
295   /* Out of space.. */
296   if (fmem == 0)
297     return -1;
298
299   /* Carve fifo + chunk space */
300   for (i = 0; i < batch_size; i++)
301     {
302       f = (svm_fifo_t *) fmem;
303       memset (f, 0, sizeof (*f));
304       f->next = fsh->free_fifos;
305       fsh->free_fifos = f;
306       c = (svm_fifo_chunk_t *) (fmem + sizeof (*f));
307       c->start_byte = 0;
308       c->length = rounded_data_size;
309       c->next = fsh->free_chunks[fl_index];
310       fsh->free_chunks[fl_index] = c;
311       fmem += hdrs + rounded_data_size;
312     }
313
314   fsh->n_fl_chunk_bytes += batch_size * rounded_data_size;
315   fsh->n_free_bytes -= size;
316
317   return 0;
318 }
319
320 /**
321  * Try to allocate new fifo
322  *
323  * Tries the following steps in order:
324  * - grab fifo and chunk from freelists
325  * - batch fifo and chunk allocation
326  * - single fifo allocation
327  * - grab multiple fifo chunks from freelists
328  */
329 static svm_fifo_t *
330 fs_try_alloc_fifo (fifo_segment_t * fs, u32 data_bytes)
331 {
332   fifo_segment_header_t *fsh = fs->h;
333   u32 fifo_sz, fl_index;
334   svm_fifo_t *f = 0;
335
336   fl_index = fs_freelist_for_size (data_bytes);
337   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
338   fifo_sz = sizeof (svm_fifo_t) + sizeof (svm_fifo_chunk_t);
339   fifo_sz += 1 << max_log2 (data_bytes);
340
341   if (fsh->free_fifos && fsh->free_chunks[fl_index])
342     {
343       f = fs_try_alloc_fifo_freelist (fs, fl_index, data_bytes);
344       if (f)
345         goto done;
346     }
347   if (fifo_sz * FIFO_SEGMENT_ALLOC_BATCH_SIZE < fsh->n_free_bytes)
348     {
349       if (fs_try_alloc_fifo_batch (fs, fl_index,
350                                    FIFO_SEGMENT_ALLOC_BATCH_SIZE))
351         goto done;
352
353       f = fs_try_alloc_fifo_freelist (fs, fl_index, data_bytes);
354       goto done;
355     }
356   if (fifo_sz <= fsh->n_free_bytes)
357     {
358       void *oldheap = ssvm_push_heap (fs->ssvm.sh);
359       f = svm_fifo_create (data_bytes);
360       ssvm_pop_heap (oldheap);
361       if (f)
362         {
363           fsh->n_free_bytes -= fifo_sz;
364           goto done;
365         }
366     }
367   if (data_bytes <= fsh->n_fl_chunk_bytes)
368     f = fs_try_alloc_fifo_freelist_multi_chunk (fs, data_bytes);
369
370 done:
371
372   return f;
373 }
374
375 /**
376  * Allocate fifo in fifo segment
377  */
378 svm_fifo_t *
379 fifo_segment_alloc_fifo (fifo_segment_t * fs, u32 data_bytes,
380                          fifo_segment_ftype_t ftype)
381 {
382   fifo_segment_header_t *fsh;
383   svm_fifo_t *f = 0;
384
385   if (!fs_chunk_size_is_valid (data_bytes))
386     {
387       clib_warning ("fifo size out of range %d", data_bytes);
388       return 0;
389     }
390
391   fsh = fs->h;
392   ssvm_lock_non_recursive (fs->ssvm.sh, 1);
393
394   f = fs_try_alloc_fifo (fs, data_bytes);
395   if (!f)
396     goto done;
397
398   /* (re)initialize the fifo, as in svm_fifo_create */
399   svm_fifo_init (f, data_bytes);
400
401   /* Initialize chunks and rbtree for multi-chunk fifos */
402   if (f->start_chunk->next != f->start_chunk)
403     {
404       void *oldheap = ssvm_push_heap (fs->ssvm.sh);
405       svm_fifo_init_chunks (f);
406       ssvm_pop_heap (oldheap);
407     }
408
409   /* If rx fifo type add to active fifos list. When cleaning up segment,
410    * we need a list of active sessions that should be disconnected. Since
411    * both rx and tx fifos keep pointers to the session, it's enough to track
412    * only one. */
413   if (ftype == FIFO_SEGMENT_RX_FIFO)
414     {
415       if (fsh->fifos)
416         {
417           fsh->fifos->prev = f;
418           f->next = fsh->fifos;
419         }
420       fsh->fifos = f;
421       f->flags |= SVM_FIFO_F_LL_TRACKED;
422     }
423   fsh->n_active_fifos++;
424
425 done:
426   ssvm_unlock_non_recursive (fs->ssvm.sh);
427   return (f);
428 }
429
430 /**
431  * Free fifo allocated in fifo segment
432  */
433 void
434 fifo_segment_free_fifo (fifo_segment_t * fs, svm_fifo_t * f)
435 {
436   svm_fifo_chunk_t *cur, *next;
437   fifo_segment_header_t *fsh;
438   ssvm_shared_header_t *sh;
439   void *oldheap;
440   int fl_index;
441
442   ASSERT (f->refcnt > 0);
443
444   if (--f->refcnt > 0)
445     return;
446
447   sh = fs->ssvm.sh;
448   fsh = fs->h;
449
450   ssvm_lock_non_recursive (sh, 2);
451
452   /* Remove from active list. Only rx fifos are tracked */
453   if (f->flags & SVM_FIFO_F_LL_TRACKED)
454     {
455       if (f->prev)
456         f->prev->next = f->next;
457       else
458         fsh->fifos = f->next;
459       if (f->next)
460         f->next->prev = f->prev;
461       f->flags &= ~SVM_FIFO_F_LL_TRACKED;
462     }
463
464   /* Add to free list */
465   f->next = fsh->free_fifos;
466   f->prev = 0;
467   fsh->free_fifos = f;
468
469   /* Free fifo chunks */
470   cur = f->start_chunk;
471   do
472     {
473       next = cur->next;
474       fl_index = fs_freelist_for_size (cur->length);
475       ASSERT (fl_index < vec_len (fsh->free_chunks));
476       cur->next = fsh->free_chunks[fl_index];
477       fsh->free_chunks[fl_index] = cur;
478       fsh->n_fl_chunk_bytes += fs_freelist_index_to_size (fl_index);
479       cur = next;
480     }
481   while (cur != f->start_chunk);
482
483   f->start_chunk = f->end_chunk = f->new_chunks = 0;
484   f->head_chunk = f->tail_chunk = f->ooo_enq = f->ooo_deq = 0;
485
486   oldheap = ssvm_push_heap (sh);
487   svm_fifo_free_chunk_lookup (f);
488   ssvm_pop_heap (oldheap);
489
490   /* not allocated on segment heap */
491   svm_fifo_free_ooo_data (f);
492
493   if (CLIB_DEBUG)
494     {
495       f->master_session_index = ~0;
496       f->master_thread_index = ~0;
497     }
498
499   fsh->n_active_fifos--;
500   ssvm_unlock_non_recursive (sh);
501 }
502
503 int
504 fifo_segment_prealloc_fifo_hdrs (fifo_segment_t * fs, u32 batch_size)
505 {
506   fifo_segment_header_t *fsh = fs->h;
507   svm_fifo_t *f;
508   void *oldheap;
509   u32 size;
510   u8 *fmem;
511   int i;
512
513   size = (sizeof (*f)) * batch_size;
514
515   oldheap = ssvm_push_heap (fs->ssvm.sh);
516   fmem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
517                                            0 /* align_offset */ ,
518                                            0 /* os_out_of_memory */ );
519   ssvm_pop_heap (oldheap);
520
521   /* Out of space.. */
522   if (fmem == 0)
523     return -1;
524
525   /* Carve fifo + chunk space */
526   for (i = 0; i < batch_size; i++)
527     {
528       f = (svm_fifo_t *) fmem;
529       memset (f, 0, sizeof (*f));
530       f->next = fsh->free_fifos;
531       fsh->free_fifos = f;
532       fmem += sizeof (*f);
533     }
534
535   fsh->n_free_bytes -= size;
536
537   return 0;
538 }
539
540 int
541 fifo_segment_prealloc_fifo_chunks (fifo_segment_t * fs, u32 chunk_size,
542                                    u32 batch_size)
543 {
544   fifo_segment_header_t *fsh = fs->h;
545   u32 size, rounded_data_size, fl_index;
546   svm_fifo_chunk_t *c;
547   void *oldheap;
548   u8 *cmem;
549   int i;
550
551   if (!fs_chunk_size_is_valid (chunk_size))
552     {
553       clib_warning ("chunk size out of range %d", chunk_size);
554       return -1;
555     }
556
557   fl_index = fs_freelist_for_size (chunk_size);
558   vec_validate_init_empty (fsh->free_chunks, fl_index, 0);
559   rounded_data_size = fs_freelist_index_to_size (fl_index);
560   size = (sizeof (*c) + rounded_data_size) * batch_size;
561
562   oldheap = ssvm_push_heap (fs->ssvm.sh);
563   cmem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
564                                            0 /* align_offset */ ,
565                                            0 /* os_out_of_memory */ );
566   ssvm_pop_heap (oldheap);
567
568   /* Out of space.. */
569   if (cmem == 0)
570     return -1;
571
572   /* Carve fifo + chunk space */
573   for (i = 0; i < batch_size; i++)
574     {
575       c = (svm_fifo_chunk_t *) cmem;
576       c->start_byte = 0;
577       c->length = rounded_data_size;
578       c->next = fsh->free_chunks[fl_index];
579       fsh->free_chunks[fl_index] = c;
580       cmem += sizeof (*c) + rounded_data_size;
581     }
582
583   fsh->n_fl_chunk_bytes += batch_size * rounded_data_size;
584   fsh->n_free_bytes -= size;
585
586   return 0;
587 }
588
589 /**
590  * Pre-allocates fifo pairs in fifo segment
591  */
592 void
593 fifo_segment_preallocate_fifo_pairs (fifo_segment_t * fs,
594                                      u32 rx_fifo_size, u32 tx_fifo_size,
595                                      u32 * n_fifo_pairs)
596 {
597   u32 rx_rounded_data_size, tx_rounded_data_size, pair_size, pairs_to_alloc;
598   int rx_fl_index, tx_fl_index;
599   uword space_available;
600   u32 hdrs;
601
602   /* Parameter check */
603   if (rx_fifo_size == 0 || tx_fifo_size == 0 || *n_fifo_pairs == 0)
604     return;
605
606   if (!fs_chunk_size_is_valid (rx_fifo_size))
607     {
608       clib_warning ("rx fifo_size out of range %d", rx_fifo_size);
609       return;
610     }
611
612   if (!fs_chunk_size_is_valid (tx_fifo_size))
613     {
614       clib_warning ("tx fifo_size out of range %d", tx_fifo_size);
615       return;
616     }
617
618   rx_rounded_data_size = (1 << (max_log2 (rx_fifo_size)));
619   rx_fl_index = fs_freelist_for_size (rx_fifo_size);
620   tx_rounded_data_size = (1 << (max_log2 (tx_fifo_size)));
621   tx_fl_index = fs_freelist_for_size (tx_fifo_size);
622
623   hdrs = sizeof (svm_fifo_t) + sizeof (svm_fifo_chunk_t);
624
625   /* Calculate space requirements */
626   pair_size = 2 * hdrs + rx_rounded_data_size + tx_rounded_data_size;
627   space_available = fs_free_space (fs);
628   pairs_to_alloc = space_available / pair_size;
629   pairs_to_alloc = clib_min (pairs_to_alloc, *n_fifo_pairs);
630
631   if (!pairs_to_alloc)
632     return;
633
634   if (fs_try_alloc_fifo_batch (fs, rx_fl_index, pairs_to_alloc))
635     clib_warning ("rx prealloc failed: pairs %u", pairs_to_alloc);
636   if (fs_try_alloc_fifo_batch (fs, tx_fl_index, pairs_to_alloc))
637     clib_warning ("tx prealloc failed: pairs %u", pairs_to_alloc);
638
639   /* Account for the pairs allocated */
640   *n_fifo_pairs -= pairs_to_alloc;
641 }
642
643 int
644 fifo_segment_grow_fifo (fifo_segment_t * fs, svm_fifo_t * f, u32 chunk_size)
645 {
646   ssvm_shared_header_t *sh;
647   svm_fifo_chunk_t *c;
648   void *oldheap;
649   int fl_index;
650
651   if (!fs_chunk_size_is_valid (chunk_size))
652     {
653       clib_warning ("chunk size out of range %d", chunk_size);
654       return -1;
655     }
656
657   fl_index = fs_freelist_for_size (chunk_size);
658
659   sh = fs->ssvm.sh;
660   ssvm_lock_non_recursive (sh, 1);
661
662   vec_validate_init_empty (fs->h->free_chunks, fl_index, 0);
663   c = fs->h->free_chunks[fl_index];
664
665   oldheap = ssvm_push_heap (sh);
666
667   if (!c)
668     {
669       c = svm_fifo_chunk_alloc (chunk_size);
670       if (!c)
671         {
672           ssvm_pop_heap (oldheap);
673           ssvm_unlock_non_recursive (sh);
674           return -1;
675         }
676     }
677   else
678     {
679       fs->h->free_chunks[fl_index] = c->next;
680       c->next = 0;
681       fs->h->n_fl_chunk_bytes -= fs_freelist_index_to_size (fl_index);
682     }
683
684   svm_fifo_add_chunk (f, c);
685
686   ssvm_pop_heap (oldheap);
687   ssvm_unlock_non_recursive (sh);
688   return 0;
689 }
690
691 int
692 fifo_segment_collect_fifo_chunks (fifo_segment_t * fs, svm_fifo_t * f)
693 {
694   svm_fifo_chunk_t *cur, *next;
695   ssvm_shared_header_t *sh;
696   void *oldheap;
697   int fl_index;
698
699   sh = fs->ssvm.sh;
700   ssvm_lock_non_recursive (sh, 1);
701
702   oldheap = ssvm_push_heap (sh);
703   cur = svm_fifo_collect_chunks (f);
704
705   while (cur)
706     {
707       next = cur->next;
708       fl_index = fs_freelist_for_size (cur->length);
709       cur->next = fs->h->free_chunks[fl_index];
710       fs->h->free_chunks[fl_index] = cur;
711       cur = next;
712     }
713
714   ssvm_pop_heap (oldheap);
715   ssvm_unlock_non_recursive (sh);
716
717   return 0;
718 }
719
720 /**
721  * Get number of active fifos
722  */
723 u32
724 fifo_segment_num_fifos (fifo_segment_t * fs)
725 {
726   return fs->h->n_active_fifos;
727 }
728
729 u32
730 fifo_segment_num_free_fifos (fifo_segment_t * fs)
731 {
732   fifo_segment_header_t *fsh = fs->h;
733   svm_fifo_t *f;
734   u32 count = 0;
735
736   f = fsh->free_fifos;
737   if (f == 0)
738     return 0;
739
740   while (f)
741     {
742       f = f->next;
743       count++;
744     }
745   return count;
746 }
747
748 u32
749 fifo_segment_num_free_chunks (fifo_segment_t * fs, u32 size)
750 {
751   u32 count = 0, rounded_size, fl_index;
752   fifo_segment_header_t *fsh;
753   svm_fifo_chunk_t *c;
754   int i;
755
756   fsh = fs->h;
757
758   /* Count all free chunks? */
759   if (size == ~0)
760     {
761       for (i = 0; i < vec_len (fsh->free_chunks); i++)
762         {
763           c = fsh->free_chunks[i];
764           if (c == 0)
765             continue;
766
767           while (c)
768             {
769               c = c->next;
770               count++;
771             }
772         }
773       return count;
774     }
775
776   rounded_size = (1 << (max_log2 (size)));
777   fl_index = fs_freelist_for_size (rounded_size);
778
779   if (fl_index >= vec_len (fsh->free_chunks))
780     return 0;
781
782   c = fsh->free_chunks[fl_index];
783   if (c == 0)
784     return 0;
785
786   while (c)
787     {
788       c = c->next;
789       count++;
790     }
791   return count;
792 }
793
794 void
795 fifo_segment_update_free_bytes (fifo_segment_t * fs)
796 {
797   fs->h->n_free_bytes = fs_free_space (fs);
798 }
799
800 u32
801 fifo_segment_free_bytes (fifo_segment_t * fs)
802 {
803   return fs->h->n_free_bytes;
804 }
805
806 u32
807 fifo_segment_fl_chunk_bytes (fifo_segment_t * fs)
808 {
809   return fs->h->n_fl_chunk_bytes;
810 }
811
812 u8
813 fifo_segment_has_fifos (fifo_segment_t * fs)
814 {
815   return fs->h->fifos != 0;
816 }
817
818 svm_fifo_t *
819 fifo_segment_get_fifo_list (fifo_segment_t * fs)
820 {
821   return fs->h->fifos;
822 }
823
824 u8 *
825 format_fifo_segment_type (u8 * s, va_list * args)
826 {
827   fifo_segment_t *sp;
828   sp = va_arg (*args, fifo_segment_t *);
829   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
830
831   if (st == SSVM_SEGMENT_PRIVATE)
832     s = format (s, "%s", "private-heap");
833   else if (st == SSVM_SEGMENT_MEMFD)
834     s = format (s, "%s", "memfd");
835   else if (st == SSVM_SEGMENT_SHM)
836     s = format (s, "%s", "shm");
837   else
838     s = format (s, "%s", "unknown");
839   return s;
840 }
841
842 /**
843  * Segment format function
844  */
845 u8 *
846 format_fifo_segment (u8 * s, va_list * args)
847 {
848   fifo_segment_t *fs = va_arg (*args, fifo_segment_t *);
849   int verbose __attribute__ ((unused)) = va_arg (*args, int);
850   fifo_segment_header_t *fsh;
851   svm_fifo_chunk_t *c;
852   u32 count, indent;
853   u32 active_fifos;
854   u32 free_fifos;
855   char *address;
856   size_t size;
857   int i;
858
859   indent = format_get_indent (s) + 2;
860 #if USE_DLMALLOC == 0
861   s = format (s, "%U segment heap: %U\n", format_white_space, indent,
862               format_mheap, svm_fifo_segment_heap (fs), verbose);
863   s = format (s, "%U segment has %u active fifos\n",
864               format_white_space, indent, fifo_segment_num_fifos (fs));
865 #endif
866
867   if (fs == 0)
868     {
869       s = format (s, "%-15s%15s%15s%15s%15s%15s", "Name", "Type",
870                   "HeapSize (M)", "ActiveFifos", "FreeFifos", "Address");
871       return s;
872     }
873
874   fsh = fs->h;
875   fifo_segment_info (fs, &address, &size);
876   active_fifos = fifo_segment_num_fifos (fs);
877   free_fifos = fifo_segment_num_free_fifos (fs);
878
879   s = format (s, "%-15v%15U%15llu%15u%15u%15llx", ssvm_name (&fs->ssvm),
880               format_fifo_segment_type, fs, size >> 20ULL, active_fifos,
881               free_fifos, address);
882
883   if (!verbose)
884     return s;
885
886   s = format (s, "\n");
887   for (i = 0; i < vec_len (fsh->free_chunks); i++)
888     {
889       c = fsh->free_chunks[i];
890       if (c == 0)
891         continue;
892       count = 0;
893       while (c)
894         {
895           c = c->next;
896           count++;
897         }
898
899       s = format (s, "%U%-5u Kb: %u free", format_white_space, indent + 2,
900                   1 << (i + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE) - 10),
901                   count);
902     }
903   s = format (s, "%Ufree bytes %U", format_white_space, indent + 2,
904               format_memory_size, fsh->n_free_bytes);
905
906   return s;
907 }
908
909 /*
910  * fd.io coding-style-patch-verification: ON
911  *
912  * Local Variables:
913  * eval: (c-set-style "gnu")
914  * End:
915  */