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