session: segment handle in accept/connect notifications
[vpp.git] / src / svm / svm_fifo_segment.c
1 /*
2  * Copyright (c) 2016 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/svm_fifo_segment.h>
17
18 svm_fifo_segment_main_t svm_fifo_segment_main;
19
20 static void
21 allocate_new_fifo_chunk (svm_fifo_segment_header_t * fsh,
22                          u32 data_size_in_bytes, int chunk_size)
23 {
24   int freelist_index;
25   u32 size;
26   u8 *fifo_space;
27   u32 rounded_data_size;
28   svm_fifo_t *f;
29   int i;
30
31   rounded_data_size = (1 << (max_log2 (data_size_in_bytes)));
32   freelist_index = max_log2 (rounded_data_size)
33     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
34
35   /* Calculate space requirement $$$ round-up data_size_in_bytes */
36   size = (sizeof (*f) + rounded_data_size) * chunk_size;
37
38   /* Allocate fifo space. May fail. */
39   fifo_space = clib_mem_alloc_aligned_at_offset
40     (size, CLIB_CACHE_LINE_BYTES, 0 /* align_offset */ ,
41      0 /* os_out_of_memory */ );
42
43   /* Out of space.. */
44   if (fifo_space == 0)
45     return;
46
47   /* Carve fifo space */
48   f = (svm_fifo_t *) fifo_space;
49   for (i = 0; i < chunk_size; i++)
50     {
51       f->freelist_index = freelist_index;
52       f->next = fsh->free_fifos[freelist_index];
53       fsh->free_fifos[freelist_index] = f;
54       fifo_space += sizeof (*f) + rounded_data_size;
55       f = (svm_fifo_t *) fifo_space;
56     }
57 }
58
59 /**
60  * Pre-allocates fifo pairs in fifo segment
61  *
62  * The number of fifos pre-allocated is the minimum of the requested number
63  * of pairs and the maximum number that fit within the segment. If the maximum
64  * is hit, the number of fifo pairs requested is updated by subtracting the
65  * number of fifos that have been successfully allocated.
66  */
67 void
68 svm_fifo_segment_preallocate_fifo_pairs (svm_fifo_segment_private_t * s,
69                                          u32 rx_fifo_size, u32 tx_fifo_size,
70                                          u32 * n_fifo_pairs)
71 {
72   u32 rx_rounded_data_size, tx_rounded_data_size, pair_size;
73   u32 rx_fifos_size, tx_fifos_size, pairs_to_allocate;
74   int rx_freelist_index, tx_freelist_index;
75   ssvm_shared_header_t *sh = s->ssvm.sh;
76   svm_fifo_segment_header_t *fsh = s->h;
77   u8 *rx_fifo_space, *tx_fifo_space;
78   uword space_available;
79   void *oldheap;
80   svm_fifo_t *f;
81   int i;
82
83   /* Parameter check */
84   if (rx_fifo_size == 0 || tx_fifo_size == 0 || *n_fifo_pairs == 0)
85     return;
86
87   if (rx_fifo_size < FIFO_SEGMENT_MIN_FIFO_SIZE ||
88       rx_fifo_size > FIFO_SEGMENT_MAX_FIFO_SIZE)
89     {
90       clib_warning ("rx fifo_size out of range %d", rx_fifo_size);
91       return;
92     }
93
94   if (tx_fifo_size < FIFO_SEGMENT_MIN_FIFO_SIZE ||
95       tx_fifo_size > FIFO_SEGMENT_MAX_FIFO_SIZE)
96     {
97       clib_warning ("tx fifo_size out of range %d", rx_fifo_size);
98       return;
99     }
100
101   rx_rounded_data_size = (1 << (max_log2 (rx_fifo_size)));
102   rx_freelist_index = max_log2 (rx_fifo_size)
103     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
104   tx_rounded_data_size = (1 << (max_log2 (tx_fifo_size)));
105   tx_freelist_index = max_log2 (tx_fifo_size)
106     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
107
108   /* Calculate space requirements */
109   pair_size = 2 * sizeof (*f) + rx_rounded_data_size + tx_rounded_data_size;
110 #if USE_DLMALLOC == 0
111   space_available = s->ssvm.ssvm_size - mheap_bytes (sh->heap);
112 #else
113   space_available = s->ssvm.ssvm_size - mspace_usable_size (sh->heap);
114 #endif
115
116   pairs_to_allocate = clib_min (space_available / pair_size, *n_fifo_pairs);
117   rx_fifos_size = (sizeof (*f) + rx_rounded_data_size) * pairs_to_allocate;
118   tx_fifos_size = (sizeof (*f) + tx_rounded_data_size) * pairs_to_allocate;
119
120   vec_validate_init_empty (fsh->free_fifos,
121                            clib_max (rx_freelist_index, tx_freelist_index),
122                            0);
123
124   oldheap = ssvm_push_heap (sh);
125   /* Allocate rx fifo space. May fail. */
126   rx_fifo_space = clib_mem_alloc_aligned_at_offset
127     (rx_fifos_size, CLIB_CACHE_LINE_BYTES, 0 /* align_offset */ ,
128      0 /* os_out_of_memory */ );
129
130   /* Same for TX */
131   tx_fifo_space = clib_mem_alloc_aligned_at_offset
132     (tx_fifos_size, CLIB_CACHE_LINE_BYTES, 0 /* align_offset */ ,
133      0 /* os_out_of_memory */ );
134
135   /* Make sure it worked. Clean up if it didn't... */
136   if (rx_fifo_space == 0 || tx_fifo_space == 0)
137     {
138       if (rx_fifo_space)
139         clib_mem_free (rx_fifo_space);
140       else
141         clib_warning ("rx fifo preallocation failure: size %d npairs %d",
142                       rx_fifo_size, *n_fifo_pairs);
143
144       if (tx_fifo_space)
145         clib_mem_free (tx_fifo_space);
146       else
147         clib_warning ("tx fifo preallocation failure: size %d nfifos %d",
148                       tx_fifo_size, *n_fifo_pairs);
149       ssvm_pop_heap (oldheap);
150       return;
151     }
152
153   /* Carve rx fifo space */
154   f = (svm_fifo_t *) rx_fifo_space;
155   for (i = 0; i < pairs_to_allocate; i++)
156     {
157       f->freelist_index = rx_freelist_index;
158       f->next = fsh->free_fifos[rx_freelist_index];
159       fsh->free_fifos[rx_freelist_index] = f;
160       rx_fifo_space += sizeof (*f) + rx_rounded_data_size;
161       f = (svm_fifo_t *) rx_fifo_space;
162     }
163   /* Carve tx fifo space */
164   f = (svm_fifo_t *) tx_fifo_space;
165   for (i = 0; i < pairs_to_allocate; i++)
166     {
167       f->freelist_index = tx_freelist_index;
168       f->next = fsh->free_fifos[tx_freelist_index];
169       fsh->free_fifos[tx_freelist_index] = f;
170       tx_fifo_space += sizeof (*f) + tx_rounded_data_size;
171       f = (svm_fifo_t *) tx_fifo_space;
172     }
173
174   /* Account for the pairs allocated */
175   *n_fifo_pairs -= pairs_to_allocate;
176   ssvm_pop_heap (oldheap);
177 }
178
179 /**
180  * Initialize svm fifo segment shared header
181  */
182 int
183 svm_fifo_segment_init (svm_fifo_segment_private_t * s)
184 {
185   svm_fifo_segment_header_t *fsh;
186   ssvm_shared_header_t *sh;
187   void *oldheap;
188
189   sh = s->ssvm.sh;
190   oldheap = ssvm_push_heap (sh);
191
192   fsh = clib_mem_alloc (sizeof (*fsh));
193   clib_memset (fsh, 0, sizeof (*fsh));
194   s->h = sh->opaque[0] = fsh;
195
196   ssvm_pop_heap (oldheap);
197
198   sh->ready = 1;
199   return (0);
200 }
201
202 /**
203  * Create an svm fifo segment and initialize as master
204  */
205 int
206 svm_fifo_segment_create (svm_fifo_segment_create_args_t * a)
207 {
208   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
209   svm_fifo_segment_private_t *s;
210   int rv;
211
212   /* Allocate a fresh segment */
213   pool_get (sm->segments, s);
214   clib_memset (s, 0, sizeof (*s));
215
216   s->ssvm.ssvm_size = a->segment_size;
217   s->ssvm.i_am_master = 1;
218   s->ssvm.my_pid = getpid ();
219   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
220   s->ssvm.requested_va = sm->next_baseva;
221
222   if ((rv = ssvm_master_init (&s->ssvm, a->segment_type)))
223     {
224       pool_put (sm->segments, s);
225       return (rv);
226     }
227
228   /* Note: requested_va updated due to seg base addr randomization */
229   sm->next_baseva = s->ssvm.sh->ssvm_va + a->segment_size;
230
231   svm_fifo_segment_init (s);
232   vec_add1 (a->new_segment_indices, s - sm->segments);
233   return (0);
234 }
235
236 /**
237  * Create an svm fifo segment in process-private memory
238  */
239 int
240 svm_fifo_segment_create_process_private (svm_fifo_segment_create_args_t * a)
241 {
242   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
243   svm_fifo_segment_private_t *s;
244   ssvm_shared_header_t *sh;
245   u32 rnd_size = 0;
246   u8 *heap;
247   u32 pagesize = clib_mem_get_page_size ();
248
249   pool_get (sm->segments, s);
250   clib_memset (s, 0, sizeof (*s));
251
252   rnd_size = (a->segment_size + (pagesize - 1)) & ~pagesize;
253
254 #if USE_DLMALLOC == 0
255   heap = mheap_alloc (0, rnd_size);
256   if (heap == 0)
257     {
258       clib_unix_warning ("mheap alloc");
259       pool_put (sm->segments, s);
260       return -1;
261     }
262   {
263     mheap_t *heap_header;
264     heap_header = mheap_header (heap);
265     heap_header->flags |= MHEAP_FLAG_THREAD_SAFE;
266   }
267 #else
268   heap = create_mspace (rnd_size, 1 /* locked */ );
269 #endif
270
271   s->ssvm.ssvm_size = rnd_size;
272   s->ssvm.i_am_master = 1;
273   s->ssvm.my_pid = getpid ();
274   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
275   s->ssvm.requested_va = ~0;
276
277   /* Allocate a [sic] shared memory header, in process memory... */
278   sh = clib_mem_alloc_aligned (sizeof (*sh), CLIB_CACHE_LINE_BYTES);
279   s->ssvm.sh = sh;
280
281   clib_memset (sh, 0, sizeof (*sh));
282   sh->heap = heap;
283
284   svm_fifo_segment_init (s);
285   vec_add1 (a->new_segment_indices, s - sm->segments);
286
287   return (0);
288 }
289
290 /**
291  * Attach as slave to an svm fifo segment
292  */
293 int
294 svm_fifo_segment_attach (svm_fifo_segment_create_args_t * a)
295 {
296   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
297   svm_fifo_segment_private_t *s;
298   int rv;
299
300   /* Allocate a fresh segment */
301   pool_get (sm->segments, s);
302   clib_memset (s, 0, sizeof (*s));
303
304   s->ssvm.ssvm_size = a->segment_size;
305   s->ssvm.my_pid = getpid ();
306   s->ssvm.name = format (0, "%s%c", a->segment_name, 0);
307   s->ssvm.requested_va = sm->next_baseva;
308   if (a->segment_type == SSVM_SEGMENT_MEMFD)
309     s->ssvm.fd = a->memfd_fd;
310   else
311     s->ssvm.attach_timeout = sm->timeout_in_seconds;
312
313   if ((rv = ssvm_slave_init (&s->ssvm, a->segment_type)))
314     {
315       _vec_len (s) = vec_len (s) - 1;
316       return (rv);
317     }
318
319   /* Fish the segment header */
320   s->h = s->ssvm.sh->opaque[0];
321
322   vec_add1 (a->new_segment_indices, s - sm->segments);
323   return (0);
324 }
325
326 void
327 svm_fifo_segment_delete (svm_fifo_segment_private_t * s)
328 {
329   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
330
331   ssvm_delete (&s->ssvm);
332   clib_memset (s, 0xfe, sizeof (*s));
333   pool_put (sm->segments, s);
334 }
335
336 /**
337  * Allocate fifo in svm segment
338  */
339 svm_fifo_t *
340 svm_fifo_segment_alloc_fifo (svm_fifo_segment_private_t * fs,
341                              u32 data_size_in_bytes,
342                              svm_fifo_segment_freelist_t list_index)
343 {
344   ssvm_shared_header_t *sh;
345   svm_fifo_segment_header_t *fsh;
346   svm_fifo_t *f = 0;
347   void *oldheap;
348   int freelist_index;
349
350   /*
351    * 4K minimum. It's not likely that anything good will happen
352    * with a smaller FIFO.
353    */
354   if (data_size_in_bytes < FIFO_SEGMENT_MIN_FIFO_SIZE ||
355       data_size_in_bytes > FIFO_SEGMENT_MAX_FIFO_SIZE)
356     {
357       clib_warning ("fifo size out of range %d", data_size_in_bytes);
358       return 0;
359     }
360
361   freelist_index = max_log2 (data_size_in_bytes)
362     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
363
364   sh = fs->ssvm.sh;
365   ssvm_lock_non_recursive (sh, 1);
366   fsh = (svm_fifo_segment_header_t *) sh->opaque[0];
367
368   switch (list_index)
369     {
370     case FIFO_SEGMENT_RX_FREELIST:
371     case FIFO_SEGMENT_TX_FREELIST:
372       vec_validate_init_empty (fsh->free_fifos, freelist_index, 0);
373       f = fsh->free_fifos[freelist_index];
374       if (PREDICT_FALSE (!f))
375         {
376           /* Preallocated and no fifo left. Don't even try */
377           if (fsh->flags & FIFO_SEGMENT_F_IS_PREALLOCATED)
378             goto done;
379
380           oldheap = ssvm_push_heap (sh);
381           allocate_new_fifo_chunk (fsh, data_size_in_bytes,
382                                    FIFO_SEGMENT_ALLOC_CHUNK_SIZE);
383           ssvm_pop_heap (oldheap);
384           f = fsh->free_fifos[freelist_index];
385         }
386       if (PREDICT_TRUE (f != 0))
387         {
388           fsh->free_fifos[freelist_index] = f->next;
389           /* (re)initialize the fifo, as in svm_fifo_create */
390           clib_memset (f, 0, sizeof (*f));
391           f->nitems = data_size_in_bytes;
392           f->ooos_list_head = OOO_SEGMENT_INVALID_INDEX;
393           f->ct_session_index = SVM_FIFO_INVALID_SESSION_INDEX;
394           f->refcnt = 1;
395           f->freelist_index = freelist_index;
396           goto found;
397         }
398       break;
399     case FIFO_SEGMENT_FREELIST_NONE:
400       break;
401
402     default:
403       clib_warning ("ignore bogus freelist %d", list_index);
404       break;
405     }
406
407   /* Catch all that allocates just one fifo. Note: this can fail,
408    * in which case: create another segment */
409   oldheap = ssvm_push_heap (sh);
410   f = svm_fifo_create (data_size_in_bytes);
411   ssvm_pop_heap (oldheap);
412   if (PREDICT_FALSE (f == 0))
413     goto done;
414   f->freelist_index = freelist_index;
415
416 found:
417   /* If rx_freelist add to active fifos list. When cleaning up segment,
418    * we need a list of active sessions that should be disconnected. Since
419    * both rx and tx fifos keep pointers to the session, it's enough to track
420    * only one. */
421   if (list_index == FIFO_SEGMENT_RX_FREELIST)
422     {
423       if (fsh->fifos)
424         {
425           fsh->fifos->prev = f;
426           f->next = fsh->fifos;
427         }
428       fsh->fifos = f;
429     }
430   fsh->n_active_fifos++;
431
432 done:
433   ssvm_unlock_non_recursive (sh);
434   return (f);
435 }
436
437 void
438 svm_fifo_segment_free_fifo (svm_fifo_segment_private_t * s, svm_fifo_t * f,
439                             svm_fifo_segment_freelist_t list_index)
440 {
441   ssvm_shared_header_t *sh;
442   svm_fifo_segment_header_t *fsh;
443   void *oldheap;
444   int freelist_index;
445
446   ASSERT (f->refcnt > 0);
447
448   if (--f->refcnt > 0)
449     return;
450
451   sh = s->ssvm.sh;
452   fsh = (svm_fifo_segment_header_t *) sh->opaque[0];
453
454   freelist_index = f->freelist_index;
455
456   ASSERT (freelist_index < vec_len (fsh->free_fifos));
457
458   ssvm_lock_non_recursive (sh, 2);
459   oldheap = ssvm_push_heap (sh);
460
461   switch (list_index)
462     {
463     case FIFO_SEGMENT_RX_FREELIST:
464       /* Remove from active list */
465       if (f->prev)
466         f->prev->next = f->next;
467       else
468         fsh->fifos = f->next;
469       if (f->next)
470         f->next->prev = f->prev;
471       /* Fall through: we add only rx fifos to active pool */
472     case FIFO_SEGMENT_TX_FREELIST:
473       /* Add to free list */
474       f->next = fsh->free_fifos[freelist_index];
475       f->prev = 0;
476       fsh->free_fifos[freelist_index] = f;
477       break;
478     case FIFO_SEGMENT_FREELIST_NONE:
479       break;
480
481     default:
482       clib_warning ("ignore bogus freelist %d", list_index);
483       break;
484     }
485
486   if (CLIB_DEBUG)
487     {
488       f->master_session_index = ~0;
489       f->master_thread_index = ~0;
490     }
491
492   fsh->n_active_fifos--;
493   ssvm_pop_heap (oldheap);
494   ssvm_unlock_non_recursive (sh);
495 }
496
497 void
498 svm_fifo_segment_main_init (u64 baseva, u32 timeout_in_seconds)
499 {
500   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
501
502   sm->next_baseva = baseva;
503   sm->timeout_in_seconds = timeout_in_seconds;
504 }
505
506 u32
507 svm_fifo_segment_index (svm_fifo_segment_private_t * s)
508 {
509   return s - svm_fifo_segment_main.segments;
510 }
511
512 /**
513  * Retrieve svm segments pool. Used only for debug purposes.
514  */
515 svm_fifo_segment_private_t *
516 svm_fifo_segment_segments_pool (void)
517 {
518   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
519   return sm->segments;
520 }
521
522 /**
523  * Get number of active fifos
524  */
525 u32
526 svm_fifo_segment_num_fifos (svm_fifo_segment_private_t * fifo_segment)
527 {
528   return fifo_segment->h->n_active_fifos;
529 }
530
531 u32
532 svm_fifo_segment_num_free_fifos (svm_fifo_segment_private_t * fifo_segment,
533                                  u32 fifo_size_in_bytes)
534 {
535   ssvm_shared_header_t *sh;
536   svm_fifo_segment_header_t *fsh;
537   svm_fifo_t *f;
538   int i;
539   u32 count = 0, rounded_data_size, freelist_index;
540
541   sh = fifo_segment->ssvm.sh;
542   fsh = (svm_fifo_segment_header_t *) sh->opaque[0];
543
544   /* Count all free fifos? */
545   if (fifo_size_in_bytes == ~0)
546     {
547       for (i = 0; i < vec_len (fsh->free_fifos); i++)
548         {
549           f = fsh->free_fifos[i];
550           if (f == 0)
551             continue;
552
553           while (f)
554             {
555               f = f->next;
556               count++;
557             }
558         }
559       return count;
560     }
561
562   rounded_data_size = (1 << (max_log2 (fifo_size_in_bytes)));
563   freelist_index = max_log2 (rounded_data_size)
564     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
565
566   if (freelist_index >= vec_len (fsh->free_fifos))
567     return 0;
568
569   f = fsh->free_fifos[freelist_index];
570   if (f == 0)
571     return 0;
572
573   while (f)
574     {
575       f = f->next;
576       count++;
577     }
578   return count;
579 }
580
581 void
582 svm_fifo_segment_info (svm_fifo_segment_private_t * seg, uword * address,
583                        u64 * size)
584 {
585   if (ssvm_type (&seg->ssvm) == SSVM_SEGMENT_PRIVATE)
586     {
587 #if USE_DLMALLOC == 0
588       mheap_t *heap_header;
589
590       *address = pointer_to_uword (seg->ssvm.sh->heap);
591       heap_header = mheap_header (seg->ssvm.sh->heap);
592       *size = heap_header->max_size;
593 #else
594       mspace_get_address_and_size (seg->ssvm.sh->heap,
595                                    (unsigned long long *) address,
596                                    (unsigned long long *) size);
597 #endif
598     }
599   else
600     {
601       *address = seg->ssvm.sh->ssvm_va;
602       *size = seg->ssvm.ssvm_size;
603     }
604 }
605
606 void *
607 svm_fifo_segment_heap (svm_fifo_segment_private_t * seg)
608 {
609   return seg->ssvm.sh->heap;
610 }
611
612 u8 *
613 format_svm_fifo_segment_type (u8 * s, va_list * args)
614 {
615   svm_fifo_segment_private_t *sp;
616   sp = va_arg (*args, svm_fifo_segment_private_t *);
617   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
618
619   if (st == SSVM_SEGMENT_PRIVATE)
620     s = format (s, "%s", "private-heap");
621   else if (st == SSVM_SEGMENT_MEMFD)
622     s = format (s, "%s", "memfd");
623   else if (st == SSVM_SEGMENT_SHM)
624     s = format (s, "%s", "shm");
625   else
626     s = format (s, "%s", "unknown");
627   return s;
628 }
629
630 /**
631  * Segment format function
632  */
633 u8 *
634 format_svm_fifo_segment (u8 * s, va_list * args)
635 {
636   svm_fifo_segment_private_t *sp
637     = va_arg (*args, svm_fifo_segment_private_t *);
638   int verbose __attribute__ ((unused)) = va_arg (*args, int);
639   svm_fifo_segment_header_t *fsh = sp->h;
640   u32 count, indent;
641   svm_fifo_t *f;
642   int i;
643
644   indent = format_get_indent (s) + 2;
645 #if USE_DLMALLOC == 0
646   s = format (s, "%U segment heap: %U\n", format_white_space, indent,
647               format_mheap, svm_fifo_segment_heap (sp), verbose);
648   s = format (s, "%U segment has %u active fifos\n",
649               format_white_space, indent, svm_fifo_segment_num_fifos (sp));
650 #endif
651
652   for (i = 0; i < vec_len (fsh->free_fifos); i++)
653     {
654       f = fsh->free_fifos[i];
655       if (f == 0)
656         continue;
657       count = 0;
658       while (f)
659         {
660           f = f->next;
661           count++;
662         }
663
664       s = format (s, "%U%-5u Kb: %u free",
665                   format_white_space, indent + 2,
666                   1 << (i + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE) - 10),
667                   count);
668     }
669   return s;
670 }
671
672 /*
673  * fd.io coding-style-patch-verification: ON
674  *
675  * Local Variables:
676  * eval: (c-set-style "gnu")
677  * End:
678  */