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