fix vector index range checks
[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   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   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   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   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   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   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 * s,
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 = s->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           memset (f, 0, sizeof (*f));
391           f->nitems = data_size_in_bytes;
392           f->ooos_list_head = OOO_SEGMENT_INVALID_INDEX;
393           f->refcnt = 1;
394           f->freelist_index = freelist_index;
395           goto found;
396         }
397       break;
398     case FIFO_SEGMENT_FREELIST_NONE:
399       break;
400
401     default:
402       clib_warning ("ignore bogus freelist %d", list_index);
403       break;
404     }
405
406   /* Catch all that allocates just one fifo. Note: this can fail,
407    * in which case: create another segment */
408   oldheap = ssvm_push_heap (sh);
409   f = svm_fifo_create (data_size_in_bytes);
410   ssvm_pop_heap (oldheap);
411   if (PREDICT_FALSE (f == 0))
412     goto done;
413   f->freelist_index = freelist_index;
414
415 found:
416   /* If rx_freelist add to active fifos list. When cleaning up segment,
417    * we need a list of active sessions that should be disconnected. Since
418    * both rx and tx fifos keep pointers to the session, it's enough to track
419    * only one. */
420   if (list_index == FIFO_SEGMENT_RX_FREELIST)
421     {
422       if (fsh->fifos)
423         {
424           fsh->fifos->prev = f;
425           f->next = fsh->fifos;
426         }
427       fsh->fifos = f;
428     }
429   fsh->n_active_fifos++;
430
431 done:
432   ssvm_unlock_non_recursive (sh);
433   return (f);
434 }
435
436 void
437 svm_fifo_segment_free_fifo (svm_fifo_segment_private_t * s, svm_fifo_t * f,
438                             svm_fifo_segment_freelist_t list_index)
439 {
440   ssvm_shared_header_t *sh;
441   svm_fifo_segment_header_t *fsh;
442   void *oldheap;
443   int freelist_index;
444
445   ASSERT (f->refcnt > 0);
446
447   if (--f->refcnt > 0)
448     return;
449
450   sh = s->ssvm.sh;
451   fsh = (svm_fifo_segment_header_t *) sh->opaque[0];
452
453   freelist_index = f->freelist_index;
454
455   ASSERT (freelist_index < vec_len (fsh->free_fifos));
456
457   ssvm_lock_non_recursive (sh, 2);
458   oldheap = ssvm_push_heap (sh);
459
460   switch (list_index)
461     {
462     case FIFO_SEGMENT_RX_FREELIST:
463       /* Remove from active list */
464       if (f->prev)
465         f->prev->next = f->next;
466       else
467         fsh->fifos = f->next;
468       if (f->next)
469         f->next->prev = f->prev;
470       /* Fall through: we add only rx fifos to active pool */
471     case FIFO_SEGMENT_TX_FREELIST:
472       /* Add to free list */
473       f->next = fsh->free_fifos[freelist_index];
474       f->prev = 0;
475       fsh->free_fifos[freelist_index] = f;
476       break;
477     case FIFO_SEGMENT_FREELIST_NONE:
478       break;
479
480     default:
481       clib_warning ("ignore bogus freelist %d", list_index);
482       break;
483     }
484
485   if (CLIB_DEBUG)
486     {
487       f->master_session_index = ~0;
488       f->master_thread_index = ~0;
489     }
490
491   fsh->n_active_fifos--;
492   ssvm_pop_heap (oldheap);
493   ssvm_unlock_non_recursive (sh);
494 }
495
496 void
497 svm_fifo_segment_main_init (u64 baseva, u32 timeout_in_seconds)
498 {
499   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
500
501   sm->next_baseva = baseva;
502   sm->timeout_in_seconds = timeout_in_seconds;
503 }
504
505 u32
506 svm_fifo_segment_index (svm_fifo_segment_private_t * s)
507 {
508   return s - svm_fifo_segment_main.segments;
509 }
510
511 /**
512  * Retrieve svm segments pool. Used only for debug purposes.
513  */
514 svm_fifo_segment_private_t *
515 svm_fifo_segment_segments_pool (void)
516 {
517   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
518   return sm->segments;
519 }
520
521 /**
522  * Get number of active fifos
523  */
524 u32
525 svm_fifo_segment_num_fifos (svm_fifo_segment_private_t * fifo_segment)
526 {
527   return fifo_segment->h->n_active_fifos;
528 }
529
530 u32
531 svm_fifo_segment_num_free_fifos (svm_fifo_segment_private_t * fifo_segment,
532                                  u32 fifo_size_in_bytes)
533 {
534   ssvm_shared_header_t *sh;
535   svm_fifo_segment_header_t *fsh;
536   svm_fifo_t *f;
537   int i;
538   u32 count = 0, rounded_data_size, freelist_index;
539
540   sh = fifo_segment->ssvm.sh;
541   fsh = (svm_fifo_segment_header_t *) sh->opaque[0];
542
543   /* Count all free fifos? */
544   if (fifo_size_in_bytes == ~0)
545     {
546       for (i = 0; i < vec_len (fsh->free_fifos); i++)
547         {
548           f = fsh->free_fifos[i];
549           if (f == 0)
550             continue;
551
552           while (f)
553             {
554               f = f->next;
555               count++;
556             }
557         }
558       return count;
559     }
560
561   rounded_data_size = (1 << (max_log2 (fifo_size_in_bytes)));
562   freelist_index = max_log2 (rounded_data_size)
563     - max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE);
564
565   if (freelist_index >= vec_len (fsh->free_fifos))
566     return 0;
567
568   f = fsh->free_fifos[freelist_index];
569   if (f == 0)
570     return 0;
571
572   while (f)
573     {
574       f = f->next;
575       count++;
576     }
577   return count;
578 }
579
580 void
581 svm_fifo_segment_info (svm_fifo_segment_private_t * seg, uword * address,
582                        u64 * size)
583 {
584   if (ssvm_type (&seg->ssvm) == SSVM_SEGMENT_PRIVATE)
585     {
586 #if USE_DLMALLOC == 0
587       mheap_t *heap_header;
588
589       *address = pointer_to_uword (seg->ssvm.sh->heap);
590       heap_header = mheap_header (seg->ssvm.sh->heap);
591       *size = heap_header->max_size;
592 #else
593       mspace_get_address_and_size (seg->ssvm.sh->heap,
594                                    (unsigned long long *) address,
595                                    (unsigned long long *) size);
596 #endif
597     }
598   else
599     {
600       *address = seg->ssvm.sh->ssvm_va;
601       *size = seg->ssvm.ssvm_size;
602     }
603 }
604
605 void *
606 svm_fifo_segment_heap (svm_fifo_segment_private_t * seg)
607 {
608   return seg->ssvm.sh->heap;
609 }
610
611 u8 *
612 format_svm_fifo_segment_type (u8 * s, va_list * args)
613 {
614   svm_fifo_segment_private_t *sp;
615   sp = va_arg (*args, svm_fifo_segment_private_t *);
616   ssvm_segment_type_t st = ssvm_type (&sp->ssvm);
617
618   if (st == SSVM_SEGMENT_PRIVATE)
619     s = format (s, "%s", "private-heap");
620   else if (st == SSVM_SEGMENT_MEMFD)
621     s = format (s, "%s", "memfd");
622   else if (st == SSVM_SEGMENT_SHM)
623     s = format (s, "%s", "shm");
624   else
625     s = format (s, "%s", "unknown");
626   return s;
627 }
628
629 /**
630  * Segment format function
631  */
632 u8 *
633 format_svm_fifo_segment (u8 * s, va_list * args)
634 {
635   svm_fifo_segment_private_t *sp
636     = va_arg (*args, svm_fifo_segment_private_t *);
637   int verbose __attribute__ ((unused)) = va_arg (*args, int);
638   svm_fifo_segment_header_t *fsh = sp->h;
639   u32 count, indent;
640   svm_fifo_t *f;
641   int i;
642
643   indent = format_get_indent (s) + 2;
644 #if USE_DLMALLOC == 0
645   s = format (s, "%U segment heap: %U\n", format_white_space, indent,
646               format_mheap, svm_fifo_segment_heap (sp), verbose);
647   s = format (s, "%U segment has %u active fifos\n",
648               format_white_space, indent, svm_fifo_segment_num_fifos (sp));
649 #endif
650
651   for (i = 0; i < vec_len (fsh->free_fifos); i++)
652     {
653       f = fsh->free_fifos[i];
654       if (f == 0)
655         continue;
656       count = 0;
657       while (f)
658         {
659           f = f->next;
660           count++;
661         }
662
663       s = format (s, "%U%-5u Kb: %u free",
664                   format_white_space, indent + 2,
665                   1 << (i + max_log2 (FIFO_SEGMENT_MIN_FIFO_SIZE) - 10),
666                   count);
667     }
668   return s;
669 }
670
671 /*
672  * fd.io coding-style-patch-verification: ON
673  *
674  * Local Variables:
675  * eval: (c-set-style "gnu")
676  * End:
677  */