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