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