session: cleanup segment manager and fifo segment
[vpp.git] / src / svm / fifo_segment.c
1 /*
2  * Copyright (c) 2016-2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <svm/fifo_segment.h>
17
18 /**
19  * Initialize fifo segment shared header
20  */
21 int
22 fifo_segment_init (fifo_segment_t * fs)
23 {
24   fifo_segment_header_t *fsh;
25   ssvm_shared_header_t *sh;
26   void *oldheap;
27
28   sh = fs->ssvm.sh;
29   oldheap = ssvm_push_heap (sh);
30
31   fsh = clib_mem_alloc (sizeof (*fsh));
32   clib_memset (fsh, 0, sizeof (*fsh));
33   fs->h = sh->opaque[0] = fsh;
34
35   ssvm_pop_heap (oldheap);
36
37   sh->ready = 1;
38   return (0);
39 }
40
41 /**
42  * Create a fifo segment in process-private memory
43  */
44 static int
45 fifo_segment_create_process_private (fifo_segment_main_t * sm,
46                                      fifo_segment_create_args_t * a)
47 {
48   u32 pagesize = clib_mem_get_page_size ();
49   ssvm_shared_header_t *sh;
50   fifo_segment_t *s;
51   u32 rnd_size = 0;
52   u8 *heap;
53
54   pool_get (sm->segments, s);
55   clib_memset (s, 0, sizeof (*s));
56
57   rnd_size = (a->segment_size + (pagesize - 1)) & ~pagesize;
58
59 #if USE_DLMALLOC == 0
60   heap = mheap_alloc (0, rnd_size);
61   if (heap == 0)
62     {
63       clib_unix_warning ("mheap alloc");
64       pool_put (sm->segments, s);
65       return -1;
66     }
67   {
68     mheap_t *heap_header;
69     heap_header = mheap_header (heap);
70     heap_header->flags |= MHEAP_FLAG_THREAD_SAFE;
71   }
72 #else
73   heap = create_mspace (rnd_size, 1 /* locked */ );
74 #endif
75
76   s->ssvm.ssvm_size = rnd_size;
77   s->ssvm.i_am_master = 1;
78   s->ssvm.my_pid = getpid ();
79   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
80   s->ssvm.requested_va = ~0;
81
82   /* Allocate a [sic] shared memory header, in process memory... */
83   sh = clib_mem_alloc_aligned (sizeof (*sh), CLIB_CACHE_LINE_BYTES);
84   s->ssvm.sh = sh;
85
86   clib_memset (sh, 0, sizeof (*sh));
87   sh->heap = heap;
88
89   fifo_segment_init (s);
90   vec_add1 (a->new_segment_indices, s - sm->segments);
91
92   return (0);
93 }
94
95 /**
96  * Create a fifo segment and initialize as master
97  */
98 int
99 fifo_segment_create (fifo_segment_main_t * sm, fifo_segment_create_args_t * a)
100 {
101   fifo_segment_t *s;
102   int rv;
103
104   if (a->segment_type == SSVM_SEGMENT_PRIVATE)
105     return fifo_segment_create_process_private (sm, a);
106
107   /* Allocate a fresh segment */
108   pool_get (sm->segments, s);
109   clib_memset (s, 0, sizeof (*s));
110
111   s->ssvm.ssvm_size = a->segment_size;
112   s->ssvm.i_am_master = 1;
113   s->ssvm.my_pid = getpid ();
114   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
115   s->ssvm.requested_va = sm->next_baseva;
116
117   if ((rv = ssvm_master_init (&s->ssvm, a->segment_type)))
118     {
119       pool_put (sm->segments, s);
120       return (rv);
121     }
122
123   /* Note: requested_va updated due to seg base addr randomization */
124   sm->next_baseva = s->ssvm.sh->ssvm_va + a->segment_size;
125
126   fifo_segment_init (s);
127   vec_add1 (a->new_segment_indices, s - sm->segments);
128   return (0);
129 }
130
131 /**
132  * Attach as slave to a fifo segment
133  */
134 int
135 fifo_segment_attach (fifo_segment_main_t * sm, fifo_segment_create_args_t * a)
136 {
137   fifo_segment_t *s;
138   int rv;
139
140   pool_get_zero (sm->segments, s);
141
142   s->ssvm.ssvm_size = a->segment_size;
143   s->ssvm.my_pid = getpid ();
144   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
145   s->ssvm.requested_va = sm->next_baseva;
146   if (a->segment_type == SSVM_SEGMENT_MEMFD)
147     s->ssvm.fd = a->memfd_fd;
148   else
149     s->ssvm.attach_timeout = sm->timeout_in_seconds;
150
151   if ((rv = ssvm_slave_init (&s->ssvm, a->segment_type)))
152     {
153       _vec_len (s) = vec_len (s) - 1;
154       return (rv);
155     }
156
157   /* Fish the segment header */
158   s->h = s->ssvm.sh->opaque[0];
159
160   vec_add1 (a->new_segment_indices, s - sm->segments);
161   return (0);
162 }
163
164 void
165 fifo_segment_delete (fifo_segment_main_t * sm, fifo_segment_t * s)
166 {
167   ssvm_delete (&s->ssvm);
168   clib_memset (s, 0xfe, sizeof (*s));
169   pool_put (sm->segments, s);
170 }
171
172 u32
173 fifo_segment_index (fifo_segment_main_t * sm, fifo_segment_t * s)
174 {
175   return s - sm->segments;
176 }
177
178 void *
179 svm_fifo_segment_heap (fifo_segment_t * seg)
180 {
181   return seg->ssvm.sh->heap;
182 }
183
184 fifo_segment_t *
185 fifo_segment_get_segment (fifo_segment_main_t * sm, u32 segment_index)
186 {
187   return pool_elt_at_index (sm->segments, segment_index);
188 }
189
190 void
191 fifo_segment_info (fifo_segment_t * seg, char **address, size_t * size)
192 {
193   if (ssvm_type (&seg->ssvm) == SSVM_SEGMENT_PRIVATE)
194     {
195 #if USE_DLMALLOC == 0
196       mheap_t *heap_header;
197
198       *address = pointer_to_uword (seg->ssvm.sh->heap);
199       heap_header = mheap_header (seg->ssvm.sh->heap);
200       *size = heap_header->max_size;
201 #else
202       mspace_get_address_and_size (seg->ssvm.sh->heap, address, size);
203 #endif
204     }
205   else
206     {
207       *address = (char *) seg->ssvm.sh->ssvm_va;
208       *size = seg->ssvm.ssvm_size;
209     }
210 }
211
212 void
213 fifo_segment_main_init (fifo_segment_main_t * sm, u64 baseva,
214                         u32 timeout_in_seconds)
215 {
216   sm->next_baseva = baseva;
217   sm->timeout_in_seconds = timeout_in_seconds;
218 }
219
220 static void
221 fifo_init_for_segment (fifo_segment_header_t * fsh, svm_fifo_t * f,
222                        u32 size, u32 fl_index)
223 {
224   f->freelist_index = fl_index;
225   f->default_chunk.start_byte = 0;
226   f->default_chunk.length = size;
227   f->default_chunk.next = f->start_chunk = f->end_chunk = &f->default_chunk;
228   f->head_chunk = f->tail_chunk = f->ooo_enq = f->ooo_deq = f->start_chunk;
229   f->next = fsh->free_fifos[fl_index];
230   fsh->free_fifos[fl_index] = f;
231 }
232
233 static inline int
234 fs_free_list_for_size (u32 size)
235 {
236   return max_log2 (size) - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
237 }
238
239 static inline int
240 fs_fifo_size_is_valid (u32 size)
241 {
242   /*
243    * 4K minimum. It's not likely that anything good will happen
244    * with a smaller FIFO.
245    */
246   return size >= FIFO_SEGMENT_MIN_FIFO_SIZE
247     && size <= FIFO_SEGMENT_MAX_FIFO_SIZE;
248 }
249
250 static void
251 allocate_new_fifo_batch (fifo_segment_header_t * fsh,
252                          u32 data_size_in_bytes, int chunk_size)
253 {
254   u32 size, rounded_data_size;
255   int i, fl_index;
256   u8 *fifos_mem;
257   svm_fifo_t *f;
258
259   rounded_data_size = (1 << (max_log2 (data_size_in_bytes)));
260   fl_index = fs_free_list_for_size (data_size_in_bytes);
261
262   /* Calculate space requirement $$$ round-up data_size_in_bytes */
263   size = (sizeof (*f) + rounded_data_size) * chunk_size;
264
265   /* Allocate fifo space. May fail. */
266   fifos_mem = clib_mem_alloc_aligned_at_offset (size, CLIB_CACHE_LINE_BYTES,
267                                                 0 /* align_offset */ ,
268                                                 0 /* os_out_of_memory */ );
269
270   /* Out of space.. */
271   if (fifos_mem == 0)
272     return;
273
274   /* Carve fifo space */
275   for (i = 0; i < chunk_size; i++)
276     {
277       f = (svm_fifo_t *) fifos_mem;
278       fifo_init_for_segment (fsh, f, rounded_data_size, fl_index);
279       fifos_mem += sizeof (*f) + rounded_data_size;
280     }
281 }
282
283 /**
284  * Allocate fifo in fifo segment
285  */
286 svm_fifo_t *
287 fifo_segment_alloc_fifo (fifo_segment_t * fs, u32 data_bytes,
288                          fifo_segment_ftype_t ftype)
289 {
290   fifo_segment_header_t *fsh;
291   ssvm_shared_header_t *sh;
292   svm_fifo_t *f = 0;
293   void *oldheap;
294   int fl_index;
295
296   if (!fs_fifo_size_is_valid (data_bytes))
297     {
298       clib_warning ("fifo size out of range %d", data_bytes);
299       return 0;
300     }
301
302   fl_index = fs_free_list_for_size (data_bytes);
303
304   sh = fs->ssvm.sh;
305   ssvm_lock_non_recursive (sh, 1);
306
307   fsh = (fifo_segment_header_t *) sh->opaque[0];
308   vec_validate_init_empty (fsh->free_fifos, fl_index, 0);
309   f = fsh->free_fifos[fl_index];
310
311   /* Try to allocate batch of fifos */
312   if (PREDICT_FALSE (!f))
313     {
314       oldheap = ssvm_push_heap (sh);
315       allocate_new_fifo_batch (fsh, data_bytes,
316                                FIFO_SEGMENT_ALLOC_BATCH_SIZE);
317       ssvm_pop_heap (oldheap);
318       f = fsh->free_fifos[fl_index];
319     }
320   if (PREDICT_TRUE (f != 0))
321     {
322       fsh->free_fifos[fl_index] = f->next;
323       /* (re)initialize the fifo, as in svm_fifo_create */
324       memset (f, 0, sizeof (*f));
325       svm_fifo_init (f, data_bytes);
326       goto found;
327     }
328
329   /* Failed to allocate batch, try just one fifo. This can also fail,
330    * in which case, create another segment */
331   oldheap = ssvm_push_heap (sh);
332   f = svm_fifo_create (data_bytes);
333   ssvm_pop_heap (oldheap);
334
335   if (PREDICT_FALSE (f == 0))
336     goto done;
337
338   f->freelist_index = fl_index;
339
340 found:
341   /* If rx fifo type add to active fifos list. When cleaning up segment,
342    * we need a list of active sessions that should be disconnected. Since
343    * both rx and tx fifos keep pointers to the session, it's enough to track
344    * only one. */
345   if (ftype == FIFO_SEGMENT_RX_FIFO)
346     {
347       if (fsh->fifos)
348         {
349           fsh->fifos->prev = f;
350           f->next = fsh->fifos;
351         }
352       fsh->fifos = f;
353       f->flags |= SVM_FIFO_F_LL_TRACKED;
354     }
355   fsh->n_active_fifos++;
356
357 done:
358   ssvm_unlock_non_recursive (sh);
359   return (f);
360 }
361
362 /**
363  * Free fifo allocated in fifo segment
364  */
365 void
366 fifo_segment_free_fifo (fifo_segment_t * s, svm_fifo_t * f)
367 {
368   fifo_segment_header_t *fsh;
369   ssvm_shared_header_t *sh;
370   int fl_index;
371
372   ASSERT (f->refcnt > 0);
373
374   if (--f->refcnt > 0)
375     return;
376
377   sh = s->ssvm.sh;
378   fsh = (fifo_segment_header_t *) sh->opaque[0];
379   fl_index = f->freelist_index;
380
381   ASSERT (fl_index < vec_len (fsh->free_fifos));
382
383   ssvm_lock_non_recursive (sh, 2);
384
385   /* Remove from active list. Only rx fifos are tracked */
386   if (f->flags & SVM_FIFO_F_LL_TRACKED)
387     {
388       if (f->prev)
389         f->prev->next = f->next;
390       else
391         fsh->fifos = f->next;
392       if (f->next)
393         f->next->prev = f->prev;
394       f->flags &= ~SVM_FIFO_F_LL_TRACKED;
395     }
396
397   /* Add to free list */
398   f->next = fsh->free_fifos[fl_index];
399   f->prev = 0;
400   fsh->free_fifos[fl_index] = f;
401
402   if (CLIB_DEBUG)
403     {
404       f->master_session_index = ~0;
405       f->master_thread_index = ~0;
406     }
407
408   fsh->n_active_fifos--;
409   ssvm_unlock_non_recursive (sh);
410 }
411
412 /**
413  * Pre-allocates fifo pairs in fifo segment
414  */
415 void
416 fifo_segment_preallocate_fifo_pairs (fifo_segment_t * fs,
417                                      u32 rx_fifo_size, u32 tx_fifo_size,
418                                      u32 * n_fifo_pairs)
419 {
420   u32 rx_rounded_data_size, tx_rounded_data_size, pair_size;
421   u32 rx_fifos_size, tx_fifos_size, pairs_to_allocate;
422   ssvm_shared_header_t *sh = fs->ssvm.sh;
423   fifo_segment_header_t *fsh = fs->h;
424   int i, rx_fl_index, tx_fl_index;
425   u8 *rx_fifo_mem, *tx_fifo_mem;
426   uword space_available;
427   void *oldheap;
428   svm_fifo_t *f;
429
430   /* Parameter check */
431   if (rx_fifo_size == 0 || tx_fifo_size == 0 || *n_fifo_pairs == 0)
432     return;
433
434   if (!fs_fifo_size_is_valid (rx_fifo_size))
435     {
436       clib_warning ("rx fifo_size out of range %d", rx_fifo_size);
437       return;
438     }
439
440   if (!fs_fifo_size_is_valid (tx_fifo_size))
441     {
442       clib_warning ("tx fifo_size out of range %d", tx_fifo_size);
443       return;
444     }
445
446   rx_rounded_data_size = (1 << (max_log2 (rx_fifo_size)));
447   rx_fl_index = fs_free_list_for_size (rx_fifo_size);
448   tx_rounded_data_size = (1 << (max_log2 (tx_fifo_size)));
449   tx_fl_index = fs_free_list_for_size (tx_fifo_size);
450
451   /* Calculate space requirements */
452   pair_size = 2 * sizeof (*f) + rx_rounded_data_size + tx_rounded_data_size;
453 #if USE_DLMALLOC == 0
454   space_available = fs->ssvm.ssvm_size - mheap_bytes (sh->heap);
455 #else
456   space_available = fs->ssvm.ssvm_size - mspace_usable_size (sh->heap);
457 #endif
458
459   pairs_to_allocate = clib_min (space_available / pair_size, *n_fifo_pairs);
460   rx_fifos_size = (sizeof (*f) + rx_rounded_data_size) * pairs_to_allocate;
461   tx_fifos_size = (sizeof (*f) + tx_rounded_data_size) * pairs_to_allocate;
462
463   vec_validate_init_empty (fsh->free_fifos,
464                            clib_max (rx_fl_index, tx_fl_index), 0);
465
466   oldheap = ssvm_push_heap (sh);
467
468   /* Allocate rx and tx fifo memory. May fail. */
469   rx_fifo_mem = clib_mem_alloc_aligned_at_offset (rx_fifos_size,
470                                                   CLIB_CACHE_LINE_BYTES,
471                                                   0 /* align_offset */ ,
472                                                   0 /* os_out_of_memory */ );
473   tx_fifo_mem = clib_mem_alloc_aligned_at_offset (tx_fifos_size,
474                                                   CLIB_CACHE_LINE_BYTES,
475                                                   0 /* align_offset */ ,
476                                                   0 /* os_out_of_memory */ );
477
478   /* Make sure it worked. Clean up if it didn't... */
479   if (rx_fifo_mem == 0 || tx_fifo_mem == 0)
480     {
481       rx_fifo_mem ? clib_mem_free (rx_fifo_mem) : clib_mem_free (tx_fifo_mem);
482       clib_warning ("fifo preallocation failure: rx size %d tx size %u "
483                     "npairs %d", rx_fifo_size, tx_fifo_size, *n_fifo_pairs);
484       ssvm_pop_heap (oldheap);
485       return;
486     }
487
488   /* Carve rx and tx fifo memory */
489   for (i = 0; i < pairs_to_allocate; i++)
490     {
491       f = (svm_fifo_t *) rx_fifo_mem;
492       fifo_init_for_segment (fsh, f, rx_rounded_data_size, rx_fl_index);
493       rx_fifo_mem += sizeof (*f) + rx_rounded_data_size;
494
495       f = (svm_fifo_t *) tx_fifo_mem;
496       fifo_init_for_segment (fsh, f, tx_rounded_data_size, tx_fl_index);
497       tx_fifo_mem += sizeof (*f) + tx_rounded_data_size;
498     }
499
500   /* Account for the pairs allocated */
501   *n_fifo_pairs -= pairs_to_allocate;
502   ssvm_pop_heap (oldheap);
503 }
504
505 /**
506  * Get number of active fifos
507  */
508 u32
509 fifo_segment_num_fifos (fifo_segment_t * fs)
510 {
511   return fs->h->n_active_fifos;
512 }
513
514 u32
515 fifo_segment_num_free_fifos (fifo_segment_t * fs, u32 fifo_size_in_bytes)
516 {
517   u32 count = 0, rounded_data_size, fl_index;
518   fifo_segment_header_t *fsh;
519   ssvm_shared_header_t *sh;
520   svm_fifo_t *f;
521   int i;
522
523   sh = fs->ssvm.sh;
524   fsh = (fifo_segment_header_t *) sh->opaque[0];
525
526   /* Count all free fifos? */
527   if (fifo_size_in_bytes == ~0)
528     {
529       for (i = 0; i < vec_len (fsh->free_fifos); i++)
530         {
531           f = fsh->free_fifos[i];
532           if (f == 0)
533             continue;
534
535           while (f)
536             {
537               f = f->next;
538               count++;
539             }
540         }
541       return count;
542     }
543
544   rounded_data_size = (1 << (max_log2 (fifo_size_in_bytes)));
545   fl_index = fs_free_list_for_size (rounded_data_size);
546
547   if (fl_index >= vec_len (fsh->free_fifos))
548     return 0;
549
550   f = fsh->free_fifos[fl_index];
551   if (f == 0)
552     return 0;
553
554   while (f)
555     {
556       f = f->next;
557       count++;
558     }
559   return count;
560 }
561
562 u8
563 fifo_segment_has_fifos (fifo_segment_t * fs)
564 {
565   return fs->h->fifos != 0;
566 }
567
568 svm_fifo_t *
569 fifo_segment_get_fifo_list (fifo_segment_t * fs)
570 {
571   return fs->h->fifos;
572 }
573
574 u8 *
575 format_fifo_segment_type (u8 * s, va_list * args)
576 {
577   fifo_segment_t *sp;
578   sp = va_arg (*args, fifo_segment_t *);
579   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
580
581   if (st == SSVM_SEGMENT_PRIVATE)
582     s = format (s, "%s", "private-heap");
583   else if (st == SSVM_SEGMENT_MEMFD)
584     s = format (s, "%s", "memfd");
585   else if (st == SSVM_SEGMENT_SHM)
586     s = format (s, "%s", "shm");
587   else
588     s = format (s, "%s", "unknown");
589   return s;
590 }
591
592 /**
593  * Segment format function
594  */
595 u8 *
596 format_fifo_segment (u8 * s, va_list * args)
597 {
598   fifo_segment_t *sp = va_arg (*args, fifo_segment_t *);
599   int verbose __attribute__ ((unused)) = va_arg (*args, int);
600   fifo_segment_header_t *fsh = sp->h;
601   u32 count, indent;
602   svm_fifo_t *f;
603   int i;
604
605   indent = format_get_indent (s) + 2;
606 #if USE_DLMALLOC == 0
607   s = format (s, "%U segment heap: %U\n", format_white_space, indent,
608               format_mheap, svm_fifo_segment_heap (sp), verbose);
609   s = format (s, "%U segment has %u active fifos\n",
610               format_white_space, indent, fifo_segment_num_fifos (sp));
611 #endif
612
613   for (i = 0; i < vec_len (fsh->free_fifos); i++)
614     {
615       f = fsh->free_fifos[i];
616       if (f == 0)
617         continue;
618       count = 0;
619       while (f)
620         {
621           f = f->next;
622           count++;
623         }
624
625       s = format (s, "%U%-5u Kb: %u free",
626                   format_white_space, indent + 2,
627                   1 << (i + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE) - 10),
628                   count);
629     }
630   return s;
631 }
632
633 /*
634  * fd.io coding-style-patch-verification: ON
635  *
636  * Local Variables:
637  * eval: (c-set-style "gnu")
638  * End:
639  */