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