d474b85f03bc080e6cf06e1123b72d2d899aecfa
[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 static inline void *
19 fsh_alloc_aligned (fifo_segment_header_t *fsh, uword size, uword align)
20 {
21   uword cur_pos, cur_pos_align, new_pos;
22
23   cur_pos = clib_atomic_load_relax_n (&fsh->byte_index);
24   cur_pos_align = round_pow2_u64 (cur_pos, align);
25   size = round_pow2_u64 (size, align);
26   new_pos = cur_pos_align + size;
27
28   if (new_pos >= fsh->max_byte_index)
29     return 0;
30
31   while (!clib_atomic_cmp_and_swap_acq_relax (&fsh->byte_index, &cur_pos,
32                                               &new_pos, 1 /* weak */))
33     {
34       cur_pos_align = round_pow2_u64 (cur_pos, align);
35       new_pos = cur_pos_align + size;
36       if (new_pos >= fsh->max_byte_index)
37         return 0;
38     }
39   return uword_to_pointer ((u8 *) fsh + cur_pos_align, void *);
40 }
41
42 static inline void *
43 fsh_alloc (fifo_segment_header_t *fsh, uword size)
44 {
45   return fsh_alloc_aligned (fsh, size, 8);
46 }
47
48 static inline fifo_segment_slice_t *
49 fsh_slice_get (fifo_segment_header_t * fsh, u32 slice_index)
50 {
51   return &fsh->slices[slice_index];
52 }
53
54 static char *fifo_segment_mem_status_strings[] = {
55 #define _(sym,str) str,
56   foreach_segment_mem_status
57 #undef _
58 };
59
60 static inline uword
61 fsh_n_free_bytes (fifo_segment_header_t * fsh)
62 {
63   uword cur_pos = clib_atomic_load_relax_n (&fsh->byte_index);
64   ASSERT (fsh->max_byte_index > cur_pos);
65   return fsh->max_byte_index - cur_pos;
66 }
67
68 static inline void
69 fsh_cached_bytes_add (fifo_segment_header_t * fsh, uword size)
70 {
71   clib_atomic_fetch_add_rel (&fsh->n_cached_bytes, size);
72 }
73
74 static inline void
75 fsh_cached_bytes_sub (fifo_segment_header_t * fsh, uword size)
76 {
77   clib_atomic_fetch_sub_rel (&fsh->n_cached_bytes, size);
78 }
79
80 static inline uword
81 fsh_n_cached_bytes (fifo_segment_header_t * fsh)
82 {
83   uword n_cached = clib_atomic_load_relax_n (&fsh->n_cached_bytes);
84   return n_cached;
85 }
86
87 static inline void
88 fsh_active_fifos_update (fifo_segment_header_t * fsh, int inc)
89 {
90   clib_atomic_fetch_add_rel (&fsh->n_active_fifos, inc);
91 }
92
93 static inline u32
94 fsh_n_active_fifos (fifo_segment_header_t * fsh)
95 {
96   return clib_atomic_load_relax_n (&fsh->n_active_fifos);
97 }
98
99 static inline uword
100 fsh_virtual_mem (fifo_segment_header_t * fsh)
101 {
102   fifo_segment_slice_t *fss;
103   uword total_vm = 0;
104   int i;
105
106   for (i = 0; i < fsh->n_slices; i++)
107     {
108       fss = fsh_slice_get (fsh, i);
109       total_vm += clib_atomic_load_relax_n (&fss->virtual_mem);
110     }
111   return total_vm;
112 }
113
114 void
115 fsh_virtual_mem_update (fifo_segment_header_t * fsh, u32 slice_index,
116                         int n_bytes)
117 {
118   fifo_segment_slice_t *fss = fsh_slice_get (fsh, slice_index);
119   fss->virtual_mem += n_bytes;
120 }
121
122 static inline void
123 fss_chunk_freelist_lock (fifo_segment_slice_t *fss)
124 {
125   u32 free = 0;
126   while (!clib_atomic_cmp_and_swap_acq_relax_n (&fss->chunk_lock, &free, 1, 0))
127     {
128       /* atomic load limits number of compare_exchange executions */
129       while (clib_atomic_load_relax_n (&fss->chunk_lock))
130         CLIB_PAUSE ();
131       /* on failure, compare_exchange writes (*p)->lock into free */
132       free = 0;
133     }
134 }
135
136 static inline void
137 fss_chunk_freelist_unlock (fifo_segment_slice_t *fss)
138 {
139   /* Make sure all reads/writes are complete before releasing the lock */
140   clib_atomic_release (&fss->chunk_lock);
141 }
142
143 static inline int
144 fss_chunk_fl_index_is_valid (fifo_segment_slice_t * fss, u32 fl_index)
145 {
146   return (fl_index < fss->n_chunk_lens);
147 }
148
149 static void
150 fss_chunk_free_list_push (fifo_segment_slice_t * fss, u32 fl_index,
151                           svm_fifo_chunk_t * c)
152 {
153   fss_chunk_freelist_lock (fss);
154   c->next = fss->free_chunks[fl_index];
155   fss->free_chunks[fl_index] = c;
156   fss_chunk_freelist_unlock (fss);
157 }
158
159 static void
160 fss_chunk_free_list_push_list (fifo_segment_slice_t * fss, u32 fl_index,
161                                svm_fifo_chunk_t * head,
162                                svm_fifo_chunk_t * tail)
163 {
164   fss_chunk_freelist_lock (fss);
165   tail->next = fss->free_chunks[fl_index];
166   fss->free_chunks[fl_index] = head;
167   fss_chunk_freelist_unlock (fss);
168 }
169
170 static svm_fifo_chunk_t *
171 fss_chunk_free_list_pop (fifo_segment_slice_t * fss, u32 fl_index)
172 {
173   svm_fifo_chunk_t *c;
174
175   ASSERT (fss_chunk_fl_index_is_valid (fss, fl_index));
176
177   fss_chunk_freelist_lock (fss);
178
179   if (!fss->free_chunks[fl_index])
180     {
181       fss_chunk_freelist_unlock (fss);
182       return 0;
183     }
184
185   c = fss->free_chunks[fl_index];
186   fss->free_chunks[fl_index] = c->next;
187
188   fss_chunk_freelist_unlock (fss);
189
190   return c;
191 }
192
193 static inline void
194 fss_fifo_add_active_list (fifo_segment_slice_t * fss, svm_fifo_t * f)
195 {
196   if (fss->fifos)
197     {
198       fss->fifos->prev = f;
199       f->next = fss->fifos;
200     }
201   fss->fifos = f;
202 }
203
204 static inline void
205 fss_fifo_del_active_list (fifo_segment_slice_t * fss, svm_fifo_t * f)
206 {
207   if (f->flags & SVM_FIFO_F_LL_TRACKED)
208     {
209       if (f->prev)
210         f->prev->next = f->next;
211       else
212         fss->fifos = f->next;
213       if (f->next)
214         f->next->prev = f->prev;
215     }
216 }
217
218 static inline uword
219 fss_fl_chunk_bytes (fifo_segment_slice_t * fss)
220 {
221   return clib_atomic_load_relax_n (&fss->n_fl_chunk_bytes);
222 }
223
224 static inline void
225 fss_fl_chunk_bytes_add (fifo_segment_slice_t * fss, uword size)
226 {
227   clib_atomic_fetch_add_relax (&fss->n_fl_chunk_bytes, size);
228 }
229
230 static inline void
231 fss_fl_chunk_bytes_sub (fifo_segment_slice_t * fss, uword size)
232 {
233   clib_atomic_fetch_sub_relax (&fss->n_fl_chunk_bytes, size);
234 }
235
236 /**
237  * Initialize fifo segment shared header
238  */
239 int
240 fifo_segment_init (fifo_segment_t * fs)
241 {
242   u32 max_chunk_sz, align = 8, offset = 2 * 4096, vec_sz;
243   uword max_fifo, seg_start, seg_size;
244   fifo_segment_header_t *fsh;
245   fifo_segment_slice_t *fss;
246   ssvm_shared_header_t *sh;
247   void *seg_data;
248   int i;
249
250   /* TODO remove ssvm heap entirely */
251   sh = fs->ssvm.sh;
252
253   seg_data = (u8 *) sh + offset;
254   seg_size = sh->ssvm_size - offset;
255
256   seg_start = round_pow2_u64 (pointer_to_uword (seg_data), align);
257   fsh = uword_to_pointer (seg_start, void *);
258   memset (fsh, 0, sizeof (*fsh));
259   fsh->byte_index = sizeof (*fsh);
260   fsh->max_byte_index = seg_size;
261   fs->max_byte_index = fsh->max_byte_index;
262   ASSERT (fsh->max_byte_index <= sh->ssvm_va + sh->ssvm_size);
263
264   fs->h = sh->opaque[0] = fsh;
265   fs->n_slices = clib_max (fs->n_slices, 1);
266
267   fsh->n_slices = fs->n_slices;
268   max_fifo =
269     clib_min ((fsh_n_free_bytes (fsh) - 4096) / 2, FIFO_SEGMENT_MAX_FIFO_SIZE);
270   fsh->max_log2_chunk_size = max_log2 (max_fifo);
271
272   fsh->slices = fsh_alloc (fsh, sizeof (*fss) * fs->n_slices);
273   memset (fsh->slices, 0, sizeof (*fss) * fs->n_slices);
274   max_chunk_sz = fsh->max_log2_chunk_size - FIFO_SEGMENT_MIN_LOG2_FIFO_SIZE;
275   max_chunk_sz += 1;
276
277   for (i = 0; i < fs->n_slices; i++)
278     {
279       fss = fsh_slice_get (fsh, i);
280       vec_sz = max_chunk_sz * sizeof (*fss->free_chunks);
281       fss->free_chunks = fsh_alloc (fsh, vec_sz);
282       memset (fss->free_chunks, 0, vec_sz);
283       vec_sz = max_chunk_sz * sizeof (*fss->num_chunks);
284       fss->num_chunks = fsh_alloc (fsh, vec_sz);
285       memset (fss->num_chunks, 0, vec_sz);
286
287       fss->chunk_lock = 0;
288       fss->n_chunk_lens = max_chunk_sz;
289     }
290
291   fsh->n_cached_bytes = 0;
292   fsh->n_reserved_bytes = fsh->byte_index;
293   sh->ready = 1;
294   return (0);
295 }
296
297 /**
298  * Create a fifo segment and initialize as master
299  */
300 int
301 fifo_segment_create (fifo_segment_main_t * sm, fifo_segment_create_args_t * a)
302 {
303   fifo_segment_t *fs;
304   uword baseva;
305   int rv;
306
307   /* Allocate a fresh segment */
308   pool_get_zero (sm->segments, fs);
309
310   baseva = a->segment_type == SSVM_SEGMENT_PRIVATE ? ~0ULL : sm->next_baseva;
311   fs->ssvm.ssvm_size = a->segment_size;
312   fs->ssvm.is_server = 1;
313   fs->ssvm.my_pid = getpid ();
314   fs->ssvm.name = format (0, "%s%c", a->segment_name, 0);
315   fs->ssvm.requested_va = baseva;
316
317   if ((rv = ssvm_server_init (&fs->ssvm, a->segment_type)))
318     {
319       pool_put (sm->segments, fs);
320       return (rv);
321     }
322
323   /* Note: requested_va updated due to seg base addr randomization */
324   sm->next_baseva = fs->ssvm.sh->ssvm_va + fs->ssvm.ssvm_size;
325
326   fifo_segment_init (fs);
327   vec_add1 (a->new_segment_indices, fs - sm->segments);
328   return (0);
329 }
330
331 /**
332  * Attach as slave to a fifo segment
333  */
334 int
335 fifo_segment_attach (fifo_segment_main_t * sm, fifo_segment_create_args_t * a)
336 {
337   fifo_segment_t *fs;
338   int rv;
339
340   pool_get_zero (sm->segments, fs);
341
342   fs->ssvm.ssvm_size = a->segment_size;
343   fs->ssvm.my_pid = getpid ();
344   fs->ssvm.name = format (0, "%s%c", a->segment_name, 0);
345   fs->ssvm.requested_va = sm->next_baseva;
346   if (a->segment_type == SSVM_SEGMENT_MEMFD)
347     fs->ssvm.fd = a->memfd_fd;
348   else
349     fs->ssvm.attach_timeout = sm->timeout_in_seconds;
350
351   if ((rv = ssvm_client_init (&fs->ssvm, a->segment_type)))
352     {
353       _vec_len (fs) = vec_len (fs) - 1;
354       return (rv);
355     }
356
357   /* Fish the segment header */
358   fs->h = fs->ssvm.sh->opaque[0];
359
360   vec_add1 (a->new_segment_indices, fs - sm->segments);
361   return (0);
362 }
363
364 void
365 fifo_segment_delete (fifo_segment_main_t * sm, fifo_segment_t * s)
366 {
367   ssvm_delete (&s->ssvm);
368   clib_memset (s, 0xfe, sizeof (*s));
369   pool_put (sm->segments, s);
370 }
371
372 u32
373 fifo_segment_index (fifo_segment_main_t * sm, fifo_segment_t * s)
374 {
375   return s - sm->segments;
376 }
377
378 fifo_segment_t *
379 fifo_segment_get_segment (fifo_segment_main_t * sm, u32 segment_index)
380 {
381   return pool_elt_at_index (sm->segments, segment_index);
382 }
383
384 void
385 fifo_segment_info (fifo_segment_t * seg, char **address, size_t * size)
386 {
387   *address = (char *) seg->ssvm.sh->ssvm_va;
388   *size = seg->ssvm.ssvm_size;
389 }
390
391 void
392 fifo_segment_main_init (fifo_segment_main_t * sm, u64 baseva,
393                         u32 timeout_in_seconds)
394 {
395   sm->next_baseva = baseva;
396   sm->timeout_in_seconds = timeout_in_seconds;
397 }
398
399 static inline u32
400 fs_freelist_for_size (u32 size)
401 {
402   if (PREDICT_FALSE (size < FIFO_SEGMENT_MIN_FIFO_SIZE))
403     return 0;
404   return max_log2 (size) - FIFO_SEGMENT_MIN_LOG2_FIFO_SIZE;
405 }
406
407 static inline u32
408 fs_freelist_index_to_size (u32 fl_index)
409 {
410   return 1 << (fl_index + FIFO_SEGMENT_MIN_LOG2_FIFO_SIZE);
411 }
412
413 static inline int
414 fs_chunk_size_is_valid (fifo_segment_header_t * fsh, u32 size)
415 {
416   /*
417    * 4K minimum. It's not likely that anything good will happen
418    * with a smaller FIFO.
419    */
420   return size >= FIFO_SEGMENT_MIN_FIFO_SIZE
421     && size <= (1ULL << fsh->max_log2_chunk_size);
422 }
423
424 svm_fifo_chunk_t *
425 fs_try_alloc_multi_chunk (fifo_segment_header_t * fsh,
426                           fifo_segment_slice_t * fss, u32 data_bytes)
427 {
428   u32 fl_index, fl_size, n_alloc = 0, req_bytes = data_bytes;
429   svm_fifo_chunk_t *c, *first = 0, *next;
430
431   fl_index = fs_freelist_for_size (req_bytes);
432   if (fl_index > 0)
433     fl_index -= 1;
434
435   fl_size = fs_freelist_index_to_size (fl_index);
436
437   while (req_bytes)
438     {
439       c = fss_chunk_free_list_pop (fss, fl_index);
440       if (c)
441         {
442           c->next = first;
443           first = c;
444           n_alloc += fl_size;
445           req_bytes -= clib_min (fl_size, req_bytes);
446         }
447       else
448         {
449           /* Failed to allocate with smaller chunks */
450           if (fl_index == 0)
451             {
452               /* Free all chunks if any allocated */
453               c = first;
454               while (c)
455                 {
456                   fl_index = fs_freelist_for_size (c->length);
457                   next = c->next;
458                   fss_chunk_free_list_push (fss, fl_index, c);
459                   c = next;
460                 }
461               n_alloc = 0;
462               first = 0;
463               /* As last attempt, try allocating a chunk larger than
464                * the requested size, if possible */
465               fl_index = fs_freelist_for_size (data_bytes) + 1;
466               if (!fss_chunk_fl_index_is_valid (fss, fl_index))
467                 return 0;
468               first = fss_chunk_free_list_pop (fss, fl_index);
469               if (first)
470                 {
471                   first->next = 0;
472                   n_alloc = fs_freelist_index_to_size (fl_index);
473                   goto done;
474                 }
475               return 0;
476             }
477           fl_index -= 1;
478           fl_size = fl_size >> 1;
479         }
480     }
481
482 done:
483   fss_fl_chunk_bytes_sub (fss, n_alloc);
484   fsh_cached_bytes_sub (fsh, n_alloc);
485   return first;
486 }
487
488 static int
489 fsh_try_alloc_fifo_hdr_batch (fifo_segment_header_t * fsh,
490                               fifo_segment_slice_t * fss, u32 batch_size)
491 {
492   svm_fifo_t *f;
493   uword size;
494   u8 *fmem;
495   int i;
496
497   ASSERT (batch_size != 0);
498
499   size = (uword) sizeof (*f) * batch_size;
500
501   fmem = fsh_alloc_aligned (fsh, size, CLIB_CACHE_LINE_BYTES);
502   if (fmem == 0)
503     return -1;
504
505   /* Carve fifo hdr space */
506   for (i = 0; i < batch_size; i++)
507     {
508       f = (svm_fifo_t *) fmem;
509       memset (f, 0, sizeof (*f));
510       f->next = fss->free_fifos;
511       fss->free_fifos = f;
512       fmem += sizeof (*f);
513     }
514
515   return 0;
516 }
517
518 static int
519 fsh_try_alloc_chunk_batch (fifo_segment_header_t * fsh,
520                            fifo_segment_slice_t * fss,
521                            u32 fl_index, u32 batch_size)
522 {
523   svm_fifo_chunk_t *c, *head = 0, *tail;
524   uword size, total_chunk_bytes;
525   u32 rounded_data_size;
526   u8 *cmem;
527   int i;
528
529   ASSERT (batch_size != 0);
530
531   rounded_data_size = fs_freelist_index_to_size (fl_index);
532   total_chunk_bytes = (uword) batch_size *rounded_data_size;
533   size = (uword) (sizeof (*c) + rounded_data_size) * batch_size;
534
535   cmem = fsh_alloc_aligned (fsh, size, 8 /* chunk hdr is 24B */);
536   if (cmem == 0)
537     return -1;
538
539   /* Carve fifo + chunk space */
540   tail = c = (svm_fifo_chunk_t *) cmem;
541   for (i = 0; i < batch_size; i++)
542     {
543       c->start_byte = 0;
544       c->length = rounded_data_size;
545       c->next = head;
546       head = c;
547       cmem += sizeof (*c) + rounded_data_size;
548       c = (svm_fifo_chunk_t *) cmem;
549     }
550
551   fss_chunk_free_list_push_list (fss, fl_index, head, tail);
552   fss->num_chunks[fl_index] += batch_size;
553   fss_fl_chunk_bytes_add (fss, total_chunk_bytes);
554   fsh_cached_bytes_add (fsh, total_chunk_bytes);
555
556   return 0;
557 }
558
559 static int
560 fs_try_alloc_fifo_batch (fifo_segment_header_t * fsh,
561                          fifo_segment_slice_t * fss,
562                          u32 fl_index, u32 batch_size)
563 {
564   if (fsh_try_alloc_fifo_hdr_batch (fsh, fss, batch_size))
565     return 0;
566   return fsh_try_alloc_chunk_batch (fsh, fss, fl_index, batch_size);
567 }
568
569 static svm_fifo_t *
570 fsh_try_alloc_fifo_hdr (fifo_segment_header_t * fsh,
571                         fifo_segment_slice_t * fss)
572 {
573   svm_fifo_t *f;
574
575   if (!fss->free_fifos)
576     {
577       if (fsh_try_alloc_fifo_hdr_batch (fsh, fss,
578                                         FIFO_SEGMENT_ALLOC_BATCH_SIZE))
579         return 0;
580     }
581
582   f = fss->free_fifos;
583   fss->free_fifos = f->next;
584   memset (f, 0, sizeof (*f));
585   return f;
586 }
587
588 static svm_fifo_chunk_t *
589 fsh_try_alloc_chunk (fifo_segment_header_t * fsh,
590                      fifo_segment_slice_t * fss, u32 data_bytes)
591 {
592   svm_fifo_chunk_t *c;
593   u32 fl_index;
594
595   fl_index = fs_freelist_for_size (data_bytes);
596
597 free_list:
598   c = fss_chunk_free_list_pop (fss, fl_index);
599   if (c)
600     {
601       c->next = 0;
602       fss_fl_chunk_bytes_sub (fss, fs_freelist_index_to_size (fl_index));
603       fsh_cached_bytes_sub (fsh, fs_freelist_index_to_size (fl_index));
604     }
605   else
606     {
607       u32 chunk_size, batch = FIFO_SEGMENT_ALLOC_BATCH_SIZE;
608       uword n_free;
609
610       chunk_size = fs_freelist_index_to_size (fl_index);
611       n_free = fsh_n_free_bytes (fsh);
612
613       if (chunk_size <= n_free)
614         {
615           batch = chunk_size * batch <= n_free ? batch : 1;
616           if (!fsh_try_alloc_chunk_batch (fsh, fss, fl_index, batch))
617             goto free_list;
618         }
619       /* Failed to allocate larger chunk, try to allocate multi-chunk
620        * that is close to what was actually requested */
621       if (data_bytes <= fss_fl_chunk_bytes (fss))
622         {
623           c = fs_try_alloc_multi_chunk (fsh, fss, data_bytes);
624           if (c)
625             goto done;
626           batch = n_free / FIFO_SEGMENT_MIN_FIFO_SIZE;
627           if (!batch || fsh_try_alloc_chunk_batch (fsh, fss, 0, batch))
628             goto done;
629         }
630       if (data_bytes <= fss_fl_chunk_bytes (fss) + n_free)
631         {
632           u32 min_size = FIFO_SEGMENT_MIN_FIFO_SIZE;
633
634           batch = (data_bytes - fss_fl_chunk_bytes (fss)) / min_size;
635           batch = clib_min (batch + 1, n_free / min_size);
636           if (fsh_try_alloc_chunk_batch (fsh, fss, 0, batch))
637             goto done;
638           c = fs_try_alloc_multi_chunk (fsh, fss, data_bytes);
639         }
640     }
641
642 done:
643
644   return c;
645 }
646
647 /**
648  * Try to allocate new fifo
649  *
650  * Tries the following steps in order:
651  * - grab fifo and chunk from freelists
652  * - batch fifo and chunk allocation
653  * - single fifo allocation
654  * - grab multiple fifo chunks from freelists
655  */
656 static svm_fifo_t *
657 fs_try_alloc_fifo (fifo_segment_header_t * fsh, fifo_segment_slice_t * fss,
658                    u32 data_bytes)
659 {
660   u32 fl_index, min_size;
661   svm_fifo_chunk_t *c;
662   svm_fifo_t *f = 0;
663
664   min_size = clib_max ((fsh->pct_first_alloc * data_bytes) / 100, 4096);
665   fl_index = fs_freelist_for_size (min_size);
666
667   if (!fss_chunk_fl_index_is_valid (fss, fl_index))
668     return 0;
669
670   f = fsh_try_alloc_fifo_hdr (fsh, fss);
671   if (!f)
672     return 0;
673
674   c = fsh_try_alloc_chunk (fsh, fss, min_size);
675   if (!c)
676     {
677       f->next = fss->free_fifos;
678       fss->free_fifos = f;
679       return 0;
680     }
681
682   f->start_chunk = c;
683   while (c->next)
684     c = c->next;
685   f->end_chunk = c;
686   f->size = data_bytes;
687   f->fs_hdr = fsh;
688
689   return f;
690 }
691
692 svm_fifo_chunk_t *
693 fsh_alloc_chunk (fifo_segment_header_t * fsh, u32 slice_index, u32 chunk_size)
694 {
695   fifo_segment_slice_t *fss;
696   svm_fifo_chunk_t *c;
697
698   fss = fsh_slice_get (fsh, slice_index);
699   c = fsh_try_alloc_chunk (fsh, fss, chunk_size);
700
701   return c;
702 }
703
704 static void
705 fsh_slice_collect_chunks (fifo_segment_header_t * fsh,
706                           fifo_segment_slice_t * fss, svm_fifo_chunk_t * c)
707 {
708   u32 n_collect = 0, fl_index;
709   svm_fifo_chunk_t *next;
710
711   while (c)
712     {
713       CLIB_MEM_UNPOISON (c, sizeof (*c));
714       next = c->next;
715       fl_index = fs_freelist_for_size (c->length);
716       fss_chunk_free_list_push (fss, fl_index, c);
717       n_collect += fs_freelist_index_to_size (fl_index);
718       c = next;
719     }
720
721   fss_fl_chunk_bytes_add (fss, n_collect);
722   fsh_cached_bytes_add (fsh, n_collect);
723 }
724
725 void
726 fsh_collect_chunks (fifo_segment_header_t * fsh, u32 slice_index,
727                     svm_fifo_chunk_t * c)
728 {
729   fifo_segment_slice_t *fss;
730   fss = fsh_slice_get (fsh, slice_index);
731   fsh_slice_collect_chunks (fsh, fss, c);
732 }
733
734 /**
735  * Allocate fifo in fifo segment
736  */
737 svm_fifo_t *
738 fifo_segment_alloc_fifo_w_slice (fifo_segment_t * fs, u32 slice_index,
739                                  u32 data_bytes, fifo_segment_ftype_t ftype)
740 {
741   fifo_segment_header_t *fsh = fs->h;
742   fifo_segment_slice_t *fss;
743   svm_fifo_t *f = 0;
744
745   ASSERT (slice_index < fs->n_slices);
746
747   if (PREDICT_FALSE (data_bytes > 1 << fsh->max_log2_chunk_size))
748     return 0;
749
750   fss = fsh_slice_get (fsh, slice_index);
751   f = fs_try_alloc_fifo (fsh, fss, data_bytes);
752   if (!f)
753     goto done;
754
755   f->slice_index = slice_index;
756
757   svm_fifo_init (f, data_bytes);
758
759   /* If rx fifo type add to active fifos list. When cleaning up segment,
760    * we need a list of active sessions that should be disconnected. Since
761    * both rx and tx fifos keep pointers to the session, it's enough to track
762    * only one. */
763   if (ftype == FIFO_SEGMENT_RX_FIFO)
764     {
765       fss_fifo_add_active_list (fss, f);
766       f->flags |= SVM_FIFO_F_LL_TRACKED;
767     }
768
769   fsh_active_fifos_update (fsh, 1);
770   fss->virtual_mem += svm_fifo_size (f);
771
772 done:
773   return (f);
774 }
775
776 /**
777  * Free fifo allocated in fifo segment
778  */
779 void
780 fifo_segment_free_fifo (fifo_segment_t * fs, svm_fifo_t * f)
781 {
782   fifo_segment_header_t *fsh = fs->h;
783   fifo_segment_slice_t *fss;
784
785   ASSERT (f->refcnt > 0);
786
787   if (--f->refcnt > 0)
788     return;
789
790   fss = fsh_slice_get (fsh, f->slice_index);
791
792   /* Remove from active list. Only rx fifos are tracked */
793   if (f->flags & SVM_FIFO_F_LL_TRACKED)
794     {
795       fss_fifo_del_active_list (fss, f);
796       f->flags &= ~SVM_FIFO_F_LL_TRACKED;
797     }
798
799   /* Free fifo chunks */
800   fsh_slice_collect_chunks (fsh, fss, f->start_chunk);
801
802   f->start_chunk = f->end_chunk = 0;
803   f->head_chunk = f->tail_chunk = f->ooo_enq = f->ooo_deq = 0;
804
805   /* not allocated on segment heap */
806   svm_fifo_free_chunk_lookup (f);
807   svm_fifo_free_ooo_data (f);
808
809   if (CLIB_DEBUG)
810     {
811       f->master_session_index = ~0;
812       f->master_thread_index = ~0;
813     }
814
815   fss->virtual_mem -= svm_fifo_size (f);
816
817   /* Add to free list */
818   f->next = fss->free_fifos;
819   f->prev = 0;
820   fss->free_fifos = f;
821
822   fsh_active_fifos_update (fsh, -1);
823 }
824
825 void
826 fifo_segment_detach_fifo (fifo_segment_t * fs, svm_fifo_t * f)
827 {
828   fifo_segment_slice_t *fss;
829   svm_fifo_chunk_t *c;
830   u32 fl_index;
831
832   ASSERT (f->refcnt == 1);
833
834   fss = fsh_slice_get (fs->h, f->slice_index);
835   fss->virtual_mem -= svm_fifo_size (f);
836   if (f->flags & SVM_FIFO_F_LL_TRACKED)
837     fss_fifo_del_active_list (fss, f);
838
839   c = f->start_chunk;
840   while (c)
841     {
842       fl_index = fs_freelist_for_size (c->length);
843       clib_atomic_fetch_sub_rel (&fss->num_chunks[fl_index], 1);
844       c = c->next;
845     }
846 }
847
848 void
849 fifo_segment_attach_fifo (fifo_segment_t * fs, svm_fifo_t * f,
850                           u32 slice_index)
851 {
852   fifo_segment_slice_t *fss;
853   svm_fifo_chunk_t *c;
854   u32 fl_index;
855
856   f->slice_index = slice_index;
857   fss = fsh_slice_get (fs->h, f->slice_index);
858   fss->virtual_mem += svm_fifo_size (f);
859   if (f->flags & SVM_FIFO_F_LL_TRACKED)
860     fss_fifo_add_active_list (fss, f);
861
862   c = f->start_chunk;
863   while (c)
864     {
865       fl_index = fs_freelist_for_size (c->length);
866       clib_atomic_fetch_add_rel (&fss->num_chunks[fl_index], 1);
867       c = c->next;
868     }
869 }
870
871 int
872 fifo_segment_prealloc_fifo_hdrs (fifo_segment_t * fs, u32 slice_index,
873                                  u32 batch_size)
874 {
875   fifo_segment_header_t *fsh = fs->h;
876   fifo_segment_slice_t *fss;
877
878   fss = fsh_slice_get (fsh, slice_index);
879   return fsh_try_alloc_fifo_hdr_batch (fsh, fss, batch_size);
880 }
881
882 int
883 fifo_segment_prealloc_fifo_chunks (fifo_segment_t * fs, u32 slice_index,
884                                    u32 chunk_size, u32 batch_size)
885 {
886   fifo_segment_header_t *fsh = fs->h;
887   fifo_segment_slice_t *fss;
888   u32 fl_index;
889
890   if (!fs_chunk_size_is_valid (fsh, chunk_size))
891     {
892       clib_warning ("chunk size out of range %d", chunk_size);
893       return -1;
894     }
895
896   fl_index = fs_freelist_for_size (chunk_size);
897   fss = fsh_slice_get (fsh, slice_index);
898
899   return fsh_try_alloc_chunk_batch (fsh, fss, fl_index, batch_size);
900 }
901
902 /**
903  * Pre-allocates fifo pairs in fifo segment
904  */
905 void
906 fifo_segment_preallocate_fifo_pairs (fifo_segment_t * fs,
907                                      u32 rx_fifo_size, u32 tx_fifo_size,
908                                      u32 * n_fifo_pairs)
909 {
910   u32 rx_rounded_data_size, tx_rounded_data_size, pair_size, pairs_to_alloc;
911   u32 hdrs, pairs_per_slice, alloc_now;
912   fifo_segment_header_t *fsh = fs->h;
913   int rx_fl_index, tx_fl_index, i;
914   fifo_segment_slice_t *fss;
915   uword space_available;
916
917   /* Parameter check */
918   if (rx_fifo_size == 0 || tx_fifo_size == 0 || *n_fifo_pairs == 0)
919     return;
920
921   if (!fs_chunk_size_is_valid (fsh, rx_fifo_size))
922     {
923       clib_warning ("rx fifo_size out of range %d", rx_fifo_size);
924       return;
925     }
926
927   if (!fs_chunk_size_is_valid (fsh, tx_fifo_size))
928     {
929       clib_warning ("tx fifo_size out of range %d", tx_fifo_size);
930       return;
931     }
932
933   rx_rounded_data_size = (1 << (max_log2 (rx_fifo_size)));
934   rx_fl_index = fs_freelist_for_size (rx_fifo_size);
935   tx_rounded_data_size = (1 << (max_log2 (tx_fifo_size)));
936   tx_fl_index = fs_freelist_for_size (tx_fifo_size);
937
938   hdrs = sizeof (svm_fifo_t) + sizeof (svm_fifo_chunk_t);
939
940   /* Calculate space requirements */
941   pair_size = 2 * hdrs + rx_rounded_data_size + tx_rounded_data_size;
942   space_available = fsh_n_free_bytes (fsh);
943   pairs_to_alloc = space_available / pair_size;
944   pairs_to_alloc = clib_min (pairs_to_alloc, *n_fifo_pairs);
945   pairs_per_slice = pairs_to_alloc / fs->n_slices;
946   pairs_per_slice += pairs_to_alloc % fs->n_slices ? 1 : 0;
947
948   if (!pairs_per_slice)
949     return;
950
951   for (i = 0; i < fs->n_slices; i++)
952     {
953       alloc_now = clib_min (pairs_per_slice, *n_fifo_pairs);
954       if (0 == alloc_now)
955         break;
956
957       fss = fsh_slice_get (fsh, i);
958       if (fs_try_alloc_fifo_batch (fsh, fss, rx_fl_index, alloc_now))
959         clib_warning ("rx prealloc failed: pairs %u", alloc_now);
960       if (fs_try_alloc_fifo_batch (fsh, fss, tx_fl_index, alloc_now))
961         clib_warning ("tx prealloc failed: pairs %u", alloc_now);
962
963       /* Account for the pairs allocated */
964       *n_fifo_pairs -= alloc_now;
965     }
966 }
967
968 /**
969  * Get number of active fifos
970  */
971 u32
972 fifo_segment_num_fifos (fifo_segment_t * fs)
973 {
974   return fsh_n_active_fifos (fs->h);
975 }
976
977 static u32
978 fs_slice_num_free_fifos (fifo_segment_slice_t * fss)
979 {
980   svm_fifo_t *f;
981   u32 count = 0;
982
983   f = fss->free_fifos;
984   if (f == 0)
985     return 0;
986
987   while (f)
988     {
989       f = f->next;
990       count++;
991     }
992   return count;
993 }
994
995 u32
996 fifo_segment_num_free_fifos (fifo_segment_t * fs)
997 {
998   fifo_segment_header_t *fsh = fs->h;
999   fifo_segment_slice_t *fss;
1000   int slice_index;
1001   u32 count = 0;
1002
1003   for (slice_index = 0; slice_index < fs->n_slices; slice_index++)
1004     {
1005       fss = fsh_slice_get (fsh, slice_index);
1006       count += fs_slice_num_free_fifos (fss);
1007     }
1008   return count;
1009 }
1010
1011 static u32
1012 fs_slice_num_free_chunks (fifo_segment_slice_t * fss, u32 size)
1013 {
1014   u32 count = 0, rounded_size, fl_index;
1015   svm_fifo_chunk_t *c;
1016   int i;
1017
1018   /* Count all free chunks? */
1019   if (size == ~0)
1020     {
1021       for (i = 0; i < fss->n_chunk_lens; i++)
1022         {
1023           c = fss->free_chunks[i];
1024           if (c == 0)
1025             continue;
1026
1027           while (c)
1028             {
1029               c = c->next;
1030               count++;
1031             }
1032         }
1033       return count;
1034     }
1035
1036   rounded_size = (1 << (max_log2 (size)));
1037   fl_index = fs_freelist_for_size (rounded_size);
1038
1039   if (fl_index >= fss->n_chunk_lens)
1040     return 0;
1041
1042   c = fss->free_chunks[fl_index];
1043   if (c == 0)
1044     return 0;
1045
1046   while (c)
1047     {
1048       c = c->next;
1049       count++;
1050     }
1051   return count;
1052 }
1053
1054 u32
1055 fifo_segment_num_free_chunks (fifo_segment_t * fs, u32 size)
1056 {
1057   fifo_segment_header_t *fsh = fs->h;
1058   fifo_segment_slice_t *fss;
1059   int slice_index;
1060   u32 count = 0;
1061
1062   for (slice_index = 0; slice_index < fs->n_slices; slice_index++)
1063     {
1064       fss = fsh_slice_get (fsh, slice_index);
1065       count += fs_slice_num_free_chunks (fss, size);
1066     }
1067   return count;
1068 }
1069
1070 uword
1071 fifo_segment_size (fifo_segment_t * fs)
1072 {
1073   return fs->h->max_byte_index - fs->h->n_reserved_bytes;
1074 }
1075
1076 u8
1077 fsh_has_reached_mem_limit (fifo_segment_header_t * fsh)
1078 {
1079   return (fsh->flags & FIFO_SEGMENT_F_MEM_LIMIT) ? 1 : 0;
1080 }
1081
1082 void
1083 fsh_reset_mem_limit (fifo_segment_header_t * fsh)
1084 {
1085   fsh->flags &= ~FIFO_SEGMENT_F_MEM_LIMIT;
1086 }
1087
1088 void *
1089 fifo_segment_alloc (fifo_segment_t *fs, uword size)
1090 {
1091   void *rv = fsh_alloc (fs->h, size);
1092   /* Mark externally allocated bytes as reserved. This helps
1093    * @ref fifo_segment_size report bytes used only for fifos */
1094   fs->h->n_reserved_bytes += size;
1095   return rv;
1096 }
1097
1098 uword
1099 fifo_segment_free_bytes (fifo_segment_t * fs)
1100 {
1101   return fsh_n_free_bytes (fs->h);
1102 }
1103
1104 uword
1105 fifo_segment_cached_bytes (fifo_segment_t * fs)
1106 {
1107   return fsh_n_cached_bytes (fs->h);
1108 }
1109
1110 uword
1111 fifo_segment_available_bytes (fifo_segment_t * fs)
1112 {
1113   return fsh_n_free_bytes (fs->h) + fsh_n_cached_bytes (fs->h);
1114 }
1115
1116 uword
1117 fifo_segment_fl_chunk_bytes (fifo_segment_t * fs)
1118 {
1119   fifo_segment_header_t *fsh = fs->h;
1120   fifo_segment_slice_t *fss;
1121   uword n_bytes = 0;
1122   int slice_index;
1123
1124   for (slice_index = 0; slice_index < fs->n_slices; slice_index++)
1125     {
1126       fss = fsh_slice_get (fsh, slice_index);
1127       n_bytes += fss_fl_chunk_bytes (fss);
1128     }
1129
1130   return n_bytes;
1131 }
1132
1133 u8
1134 fifo_segment_has_fifos (fifo_segment_t * fs)
1135 {
1136   return (fsh_n_active_fifos (fs->h) != 0);
1137 }
1138
1139 svm_fifo_t *
1140 fifo_segment_get_slice_fifo_list (fifo_segment_t * fs, u32 slice_index)
1141 {
1142   fifo_segment_header_t *fsh = fs->h;
1143   fifo_segment_slice_t *fss;
1144
1145   fss = fsh_slice_get (fsh, slice_index);
1146   return fss->fifos;
1147 }
1148
1149 u8
1150 fifo_segment_get_mem_usage (fifo_segment_t * fs)
1151 {
1152   uword size, in_use;
1153
1154   size = fifo_segment_size (fs);
1155   in_use =
1156     size - fifo_segment_free_bytes (fs) - fifo_segment_cached_bytes (fs);
1157   return (in_use * 100) / size;
1158 }
1159
1160 fifo_segment_mem_status_t
1161 fifo_segment_determine_status (fifo_segment_header_t * fsh, u8 usage)
1162 {
1163   if (!fsh->high_watermark || !fsh->low_watermark)
1164     return MEMORY_PRESSURE_NO_PRESSURE;
1165
1166   /* once the no-memory is detected, the status continues
1167    * until memory usage gets below the high watermark
1168    */
1169   if (fsh_has_reached_mem_limit (fsh))
1170     {
1171       if (usage >= fsh->high_watermark)
1172         return MEMORY_PRESSURE_NO_MEMORY;
1173       else
1174         fsh_reset_mem_limit (fsh);
1175     }
1176
1177   if (usage >= fsh->high_watermark)
1178     return MEMORY_PRESSURE_HIGH_PRESSURE;
1179
1180   else if (usage >= fsh->low_watermark)
1181     return MEMORY_PRESSURE_LOW_PRESSURE;
1182
1183   return MEMORY_PRESSURE_NO_PRESSURE;
1184 }
1185
1186 fifo_segment_mem_status_t
1187 fifo_segment_get_mem_status (fifo_segment_t * fs)
1188 {
1189   fifo_segment_header_t *fsh = fs->h;
1190   u8 usage = fifo_segment_get_mem_usage (fs);
1191
1192   return fifo_segment_determine_status (fsh, usage);
1193 }
1194
1195 u8 *
1196 format_fifo_segment_type (u8 * s, va_list * args)
1197 {
1198   fifo_segment_t *sp;
1199   sp = va_arg (*args, fifo_segment_t *);
1200   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
1201
1202   if (st == SSVM_SEGMENT_PRIVATE)
1203     s = format (s, "%s", "private");
1204   else if (st == SSVM_SEGMENT_MEMFD)
1205     s = format (s, "%s", "memfd");
1206   else if (st == SSVM_SEGMENT_SHM)
1207     s = format (s, "%s", "shm");
1208   else
1209     s = format (s, "%s", "unknown");
1210   return s;
1211 }
1212
1213 /**
1214  * Segment format function
1215  */
1216 u8 *
1217 format_fifo_segment (u8 * s, va_list * args)
1218 {
1219   u32 count, indent, active_fifos, free_fifos;
1220   fifo_segment_t *fs = va_arg (*args, fifo_segment_t *);
1221   int verbose __attribute__ ((unused)) = va_arg (*args, int);
1222   uword est_chunk_bytes, est_free_seg_bytes, free_chunks;
1223   uword chunk_bytes = 0, free_seg_bytes, chunk_size;
1224   uword tracked_cached_bytes;
1225   uword fifo_hdr = 0, reserved;
1226   fifo_segment_header_t *fsh;
1227   fifo_segment_slice_t *fss;
1228   svm_fifo_chunk_t *c;
1229   u32 slice_index;
1230   char *address;
1231   size_t size;
1232   int i;
1233   uword allocated, in_use, virt;
1234   f64 usage;
1235   fifo_segment_mem_status_t mem_st;
1236
1237   indent = format_get_indent (s) + 2;
1238
1239   if (fs == 0)
1240     {
1241       s = format (s, "%-20s%10s%15s%15s%15s%15s", "Name", "Type",
1242                   "HeapSize (M)", "ActiveFifos", "FreeFifos", "Address");
1243       return s;
1244     }
1245
1246   fifo_segment_info (fs, &address, &size);
1247   active_fifos = fifo_segment_num_fifos (fs);
1248   free_fifos = fifo_segment_num_free_fifos (fs);
1249
1250   s = format (s, "%-20v%10U%15llu%15u%15u%15llx", ssvm_name (&fs->ssvm),
1251               format_fifo_segment_type, fs, size >> 20ULL, active_fifos,
1252               free_fifos, address);
1253
1254   if (!verbose)
1255     return s;
1256
1257   fsh = fs->h;
1258
1259   free_chunks = fifo_segment_num_free_chunks (fs, ~0);
1260   if (free_chunks)
1261     s =
1262       format (s, "\n\n%UFree/Allocated chunks by size:\n", format_white_space,
1263               indent + 2);
1264   else
1265     s = format (s, "\n");
1266
1267   for (slice_index = 0; slice_index < fs->n_slices; slice_index++)
1268     {
1269       fss = fsh_slice_get (fsh, slice_index);
1270       for (i = 0; i < fss->n_chunk_lens; i++)
1271         {
1272           c = fss->free_chunks[i];
1273           if (c == 0 && fss->num_chunks[i] == 0)
1274             continue;
1275           count = 0;
1276           while (c)
1277             {
1278               c = c->next;
1279               count++;
1280             }
1281
1282           chunk_size = fs_freelist_index_to_size (i);
1283           s = format (s, "%U%-5u kB: %u/%u\n", format_white_space, indent + 2,
1284                       chunk_size >> 10, count, fss->num_chunks[i]);
1285
1286           chunk_bytes += count * chunk_size;
1287         }
1288     }
1289
1290   fifo_hdr = free_fifos * sizeof (svm_fifo_t);
1291   est_chunk_bytes = fifo_segment_fl_chunk_bytes (fs);
1292   est_free_seg_bytes = fifo_segment_free_bytes (fs);
1293   free_seg_bytes = fifo_segment_free_bytes (fs);
1294   tracked_cached_bytes = fifo_segment_cached_bytes (fs);
1295   allocated = fifo_segment_size (fs);
1296   in_use = fifo_segment_size (fs) - est_free_seg_bytes - tracked_cached_bytes;
1297   usage = (100.0 * in_use) / allocated;
1298   mem_st = fifo_segment_get_mem_status (fs);
1299   virt = fsh_virtual_mem (fsh);
1300   reserved = fsh->n_reserved_bytes;
1301
1302   s = format (s, "\n%Useg free bytes: %U (%lu) estimated: %U (%lu) reserved:"
1303               " %U (%lu)\n", format_white_space, indent + 2,
1304               format_memory_size, free_seg_bytes, free_seg_bytes,
1305               format_memory_size, est_free_seg_bytes, est_free_seg_bytes,
1306               format_memory_size, reserved, reserved);
1307   s = format (s, "%Uchunk free bytes: %U (%lu) estimated: %U (%lu) tracked:"
1308               " %U (%lu)\n", format_white_space, indent + 2,
1309               format_memory_size, chunk_bytes, chunk_bytes,
1310               format_memory_size, est_chunk_bytes, est_chunk_bytes,
1311               format_memory_size, tracked_cached_bytes, tracked_cached_bytes);
1312   s = format (s, "%Ufifo active: %u hdr free bytes: %U (%u) \n",
1313               format_white_space, indent + 2, fsh->n_active_fifos,
1314               format_memory_size, fifo_hdr, fifo_hdr);
1315   s = format (s, "%Usegment usage: %.2f%% (%U / %U) virt: %U status: %s\n",
1316               format_white_space, indent + 2, usage, format_memory_size,
1317               in_use, format_memory_size, allocated, format_memory_size, virt,
1318               fifo_segment_mem_status_strings[mem_st]);
1319   s = format (s, "\n");
1320
1321   return s;
1322 }
1323
1324 /*
1325  * fd.io coding-style-patch-verification: ON
1326  *
1327  * Local Variables:
1328  * eval: (c-set-style "gnu")
1329  * End:
1330  */