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