9e6a1b55cbbd36f80ed8e86ef253dd236f98781b
[vpp.git] / src / vnet / session / segment_manager.c
1 /*
2  * Copyright (c) 2017 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 <vnet/session/segment_manager.h>
17 #include <vnet/session/session.h>
18 #include <vnet/session/application.h>
19
20 /**
21  * Counter used to build segment names
22  */
23 u32 segment_name_counter = 0;
24
25 /**
26  * Pool of segment managers
27  */
28 segment_manager_t *segment_managers = 0;
29
30 /*
31  * Pool of segment manager properties
32  */
33 static segment_manager_properties_t *segment_manager_properties_pool;
34
35 /**
36  * Process private segment index
37  */
38 u32 *private_segment_indices;
39
40 /**
41  * Default fifo and segment size. TODO config.
42  */
43 u32 default_fifo_size = 1 << 16;
44 u32 default_segment_size = 1 << 20;
45
46 segment_manager_properties_t *
47 segment_manager_properties_alloc (void)
48 {
49   segment_manager_properties_t *props;
50   pool_get (segment_manager_properties_pool, props);
51   memset (props, 0, sizeof (*props));
52   return props;
53 }
54
55 void
56 segment_manager_properties_free (segment_manager_properties_t * props)
57 {
58   pool_put (segment_manager_properties_pool, props);
59   memset (props, 0xFB, sizeof (*props));
60 }
61
62 segment_manager_properties_t *
63 segment_manager_properties_get (u32 smp_index)
64 {
65   if (pool_is_free_index (segment_manager_properties_pool, smp_index))
66     return 0;
67   return pool_elt_at_index (segment_manager_properties_pool, smp_index);
68 }
69
70 u32
71 segment_manager_properties_index (segment_manager_properties_t * p)
72 {
73   return p - segment_manager_properties_pool;
74 }
75
76 void
77 segment_manager_get_segment_info (u32 index, u8 ** name, u32 * size)
78 {
79   svm_fifo_segment_private_t *s;
80   s = svm_fifo_segment_get_segment (index);
81   *name = s->h->segment_name;
82   *size = s->ssvm.ssvm_size;
83 }
84
85 always_inline int
86 session_manager_add_segment_i (segment_manager_t * sm, u32 segment_size,
87                                u8 * segment_name)
88 {
89   svm_fifo_segment_create_args_t _ca, *ca = &_ca;
90   segment_manager_properties_t *props;
91   int rv;
92
93   memset (ca, 0, sizeof (*ca));
94   props = segment_manager_properties_get (sm->properties_index);
95   if (!props->use_private_segment)
96     {
97       ca->segment_name = (char *) segment_name;
98       ca->segment_size = segment_size;
99       ca->rx_fifo_size = props->rx_fifo_size;
100       ca->tx_fifo_size = props->tx_fifo_size;
101       ca->preallocated_fifo_pairs = props->preallocated_fifo_pairs;
102
103       rv = svm_fifo_segment_create (ca);
104       if (rv)
105         {
106           clib_warning ("svm_fifo_segment_create ('%s', %d) failed",
107                         ca->segment_name, ca->segment_size);
108           return VNET_API_ERROR_SVM_SEGMENT_CREATE_FAIL;
109         }
110     }
111   else
112     {
113       u32 rx_fifo_size, tx_fifo_size, rx_rounded_data_size,
114         tx_rounded_data_size;
115       u32 approx_segment_count;
116       u64 approx_total_size;
117
118       ca->segment_name = "process-private-segment";
119       ca->segment_size = segment_size;
120       ca->rx_fifo_size = props->rx_fifo_size;
121       ca->tx_fifo_size = props->tx_fifo_size;
122       ca->preallocated_fifo_pairs = props->preallocated_fifo_pairs;
123       ca->private_segment_count = props->private_segment_count;
124
125       /* Calculate space requirements */
126       rx_rounded_data_size = (1 << (max_log2 (ca->rx_fifo_size)));
127       tx_rounded_data_size = (1 << (max_log2 (ca->tx_fifo_size)));
128
129       rx_fifo_size = sizeof (svm_fifo_t) + rx_rounded_data_size;
130       tx_fifo_size = sizeof (svm_fifo_t) + tx_rounded_data_size;
131
132       approx_total_size = (u64) ca->preallocated_fifo_pairs
133         * (rx_fifo_size + tx_fifo_size);
134       approx_segment_count = (approx_total_size + (ca->segment_size - 1))
135         / (u64) ca->segment_size;
136
137       /* The user asked us to figure it out... */
138       if (ca->private_segment_count == 0)
139         {
140           ca->private_segment_count = approx_segment_count;
141         }
142       /* Follow directions, but issue a warning */
143       else if (approx_segment_count != ca->private_segment_count)
144         {
145           clib_warning ("Honoring segment count %u, calculated count was %u",
146                         ca->private_segment_count, approx_segment_count);
147         }
148
149       if (svm_fifo_segment_create_process_private (ca))
150         clib_warning ("Failed to create process private segment");
151
152       ASSERT (vec_len (ca->new_segment_indices));
153     }
154   vec_append (sm->segment_indices, ca->new_segment_indices);
155   vec_free (ca->new_segment_indices);
156   return 0;
157 }
158
159 int
160 session_manager_add_segment (segment_manager_t * sm)
161 {
162   svm_fifo_segment_create_args_t _ca, *ca = &_ca;
163   segment_manager_properties_t *props;
164   u32 add_segment_size;
165   u8 *segment_name;
166   int rv;
167
168   memset (ca, 0, sizeof (*ca));
169   props = segment_manager_properties_get (sm->properties_index);
170   segment_name = format (0, "%d-%d%c", getpid (), segment_name_counter++, 0);
171   add_segment_size = props->add_segment_size ?
172     props->add_segment_size : default_segment_size;
173
174   rv = session_manager_add_segment_i (sm, add_segment_size, segment_name);
175   vec_free (segment_name);
176   return rv;
177 }
178
179 int
180 session_manager_add_first_segment (segment_manager_t * sm, u32 segment_size)
181 {
182   u8 *segment_name;
183   int rv;
184
185   segment_name = format (0, "%d-%d%c", getpid (), segment_name_counter++, 0);
186   rv = session_manager_add_segment_i (sm, segment_size, segment_name);
187   vec_free (segment_name);
188   return rv;
189 }
190
191 segment_manager_t *
192 segment_manager_new ()
193 {
194   segment_manager_t *sm;
195   pool_get (segment_managers, sm);
196   memset (sm, 0, sizeof (*sm));
197   return sm;
198 }
199
200 /**
201  * Initializes segment manager based on options provided.
202  * Returns error if svm segment allocation fails.
203  */
204 int
205 segment_manager_init (segment_manager_t * sm, u32 props_index,
206                       u32 first_seg_size)
207 {
208   int rv;
209
210   /* app allocates these */
211   sm->properties_index = props_index;
212   first_seg_size = first_seg_size > 0 ? first_seg_size : default_segment_size;
213   rv = session_manager_add_first_segment (sm, first_seg_size);
214   if (rv)
215     {
216       clib_warning ("Failed to allocate segment");
217       return rv;
218     }
219
220   clib_spinlock_init (&sm->lockp);
221   return 0;
222 }
223
224 u8
225 segment_manager_has_fifos (segment_manager_t * sm)
226 {
227   svm_fifo_segment_private_t *segment;
228   int i;
229
230   for (i = 0; i < vec_len (sm->segment_indices); i++)
231     {
232       segment = svm_fifo_segment_get_segment (sm->segment_indices[i]);
233       if (CLIB_DEBUG && i && !svm_fifo_segment_has_fifos (segment)
234           && !(segment->h->flags & FIFO_SEGMENT_F_IS_PREALLOCATED))
235         clib_warning ("segment %d has no fifos!", sm->segment_indices[i]);
236       if (svm_fifo_segment_has_fifos (segment))
237         return 1;
238     }
239   return 0;
240 }
241
242 static u8
243 segment_manager_app_detached (segment_manager_t * sm)
244 {
245   return (sm->app_index == SEGMENT_MANAGER_INVALID_APP_INDEX);
246 }
247
248 void
249 segment_manager_app_detach (segment_manager_t * sm)
250 {
251   sm->app_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
252 }
253
254 static void
255 segment_manager_del_segment (segment_manager_t * sm, u32 segment_index)
256 {
257   svm_fifo_segment_private_t *fifo_segment;
258   u32 svm_segment_index;
259   clib_spinlock_lock (&sm->lockp);
260   svm_segment_index = sm->segment_indices[segment_index];
261   fifo_segment = svm_fifo_segment_get_segment (svm_segment_index);
262   if (!fifo_segment
263       || ((fifo_segment->h->flags & FIFO_SEGMENT_F_IS_PREALLOCATED)
264           && !segment_manager_app_detached (sm)))
265     {
266       clib_spinlock_unlock (&sm->lockp);
267       return;
268     }
269   svm_fifo_segment_delete (fifo_segment);
270   vec_del1 (sm->segment_indices, segment_index);
271   clib_spinlock_unlock (&sm->lockp);
272 }
273
274 /**
275  * Initiate disconnects for all sessions 'owned' by a segment manager
276  */
277 void
278 segment_manager_del_sessions (segment_manager_t * sm)
279 {
280   int j;
281   svm_fifo_segment_private_t *fifo_segment;
282   svm_fifo_t *fifo;
283
284   ASSERT (vec_len (sm->segment_indices));
285
286   /* Across all fifo segments used by the server */
287   for (j = 0; j < vec_len (sm->segment_indices); j++)
288     {
289       fifo_segment = svm_fifo_segment_get_segment (sm->segment_indices[j]);
290       fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
291
292       /*
293        * Remove any residual sessions from the session lookup table
294        * Don't bother deleting the individual fifos, we're going to
295        * throw away the fifo segment in a minute.
296        */
297       while (fifo)
298         {
299           u32 session_index, thread_index;
300           stream_session_t *session;
301
302           session_index = fifo->master_session_index;
303           thread_index = fifo->master_thread_index;
304           session = session_get (session_index, thread_index);
305
306           /* Instead of directly removing the session call disconnect */
307           if (session->session_state != SESSION_STATE_CLOSED)
308             {
309               session->session_state = SESSION_STATE_CLOSED;
310               session_send_session_evt_to_thread (session_handle
311                                                   (session),
312                                                   FIFO_EVENT_DISCONNECT,
313                                                   thread_index);
314             }
315           fifo = fifo->next;
316         }
317
318       /* Instead of removing the segment, test when cleaning up disconnected
319        * sessions if the segment can be removed.
320        */
321     }
322 }
323
324 /**
325  * Removes segment manager.
326  *
327  * Since the fifos allocated in the segment keep backpointers to the sessions
328  * prior to removing the segment, we call session disconnect. This
329  * subsequently propagates into transport.
330  */
331 void
332 segment_manager_del (segment_manager_t * sm)
333 {
334   int i;
335
336   ASSERT (!segment_manager_has_fifos (sm)
337           && segment_manager_app_detached (sm));
338
339   /* If we have empty preallocated segments that haven't been removed, remove
340    * them now. Apart from that, the first segment in the first segment manager
341    * is not removed when all fifos are removed. It can only be removed when
342    * the manager is explicitly deleted/detached by the app. */
343   for (i = vec_len (sm->segment_indices) - 1; i >= 0; i--)
344     {
345       if (CLIB_DEBUG)
346         {
347           svm_fifo_segment_private_t *segment;
348           segment = svm_fifo_segment_get_segment (sm->segment_indices[i]);
349           ASSERT (!svm_fifo_segment_has_fifos (segment));
350         }
351       segment_manager_del_segment (sm, i);
352     }
353   clib_spinlock_free (&sm->lockp);
354   if (CLIB_DEBUG)
355     memset (sm, 0xfe, sizeof (*sm));
356   pool_put (segment_managers, sm);
357 }
358
359 void
360 segment_manager_init_del (segment_manager_t * sm)
361 {
362   segment_manager_app_detach (sm);
363   if (segment_manager_has_fifos (sm))
364     segment_manager_del_sessions (sm);
365   else
366     {
367       ASSERT (!sm->first_is_protected || segment_manager_app_detached (sm));
368       segment_manager_del (sm);
369     }
370 }
371
372 int
373 segment_manager_alloc_session_fifos (segment_manager_t * sm,
374                                      svm_fifo_t ** server_rx_fifo,
375                                      svm_fifo_t ** server_tx_fifo,
376                                      u32 * fifo_segment_index)
377 {
378   svm_fifo_segment_private_t *fifo_segment;
379   segment_manager_properties_t *props;
380   u32 fifo_size, sm_index;
381   u8 added_a_segment = 0;
382   int i;
383
384   ASSERT (vec_len (sm->segment_indices));
385
386   /* Make sure we don't have multiple threads trying to allocate segments
387    * at the same time. */
388   clib_spinlock_lock (&sm->lockp);
389
390   /* Allocate svm fifos */
391   props = segment_manager_properties_get (sm->properties_index);
392 again:
393   for (i = 0; i < vec_len (sm->segment_indices); i++)
394     {
395       *fifo_segment_index = sm->segment_indices[i];
396       fifo_segment = svm_fifo_segment_get_segment (*fifo_segment_index);
397
398       fifo_size = props->rx_fifo_size;
399       fifo_size = (fifo_size == 0) ? default_fifo_size : fifo_size;
400       *server_rx_fifo =
401         svm_fifo_segment_alloc_fifo (fifo_segment, fifo_size,
402                                      FIFO_SEGMENT_RX_FREELIST);
403
404       fifo_size = props->tx_fifo_size;
405       fifo_size = (fifo_size == 0) ? default_fifo_size : fifo_size;
406       *server_tx_fifo =
407         svm_fifo_segment_alloc_fifo (fifo_segment, fifo_size,
408                                      FIFO_SEGMENT_TX_FREELIST);
409
410       if (*server_rx_fifo == 0)
411         {
412           /* This would be very odd, but handle it... */
413           if (*server_tx_fifo != 0)
414             {
415               svm_fifo_segment_free_fifo (fifo_segment, *server_tx_fifo,
416                                           FIFO_SEGMENT_TX_FREELIST);
417               *server_tx_fifo = 0;
418             }
419           continue;
420         }
421       if (*server_tx_fifo == 0)
422         {
423           if (*server_rx_fifo != 0)
424             {
425               svm_fifo_segment_free_fifo (fifo_segment, *server_rx_fifo,
426                                           FIFO_SEGMENT_RX_FREELIST);
427               *server_rx_fifo = 0;
428             }
429           continue;
430         }
431       break;
432     }
433
434   /* See if we're supposed to create another segment */
435   if (*server_rx_fifo == 0)
436     {
437       if (props->add_segment && !props->use_private_segment)
438         {
439           if (added_a_segment)
440             {
441               clib_warning ("added a segment, still can't allocate a fifo");
442               clib_spinlock_unlock (&sm->lockp);
443               return SESSION_ERROR_NEW_SEG_NO_SPACE;
444             }
445
446           if (session_manager_add_segment (sm))
447             {
448               clib_spinlock_unlock (&sm->lockp);
449               return VNET_API_ERROR_URI_FIFO_CREATE_FAILED;
450             }
451
452           added_a_segment = 1;
453           goto again;
454         }
455       else
456         {
457           clib_warning ("No space to allocate fifos!");
458           clib_spinlock_unlock (&sm->lockp);
459           return SESSION_ERROR_NO_SPACE;
460         }
461     }
462
463   /* Backpointers to segment manager */
464   sm_index = segment_manager_index (sm);
465   (*server_tx_fifo)->segment_manager = sm_index;
466   (*server_rx_fifo)->segment_manager = sm_index;
467
468   clib_spinlock_unlock (&sm->lockp);
469
470   if (added_a_segment)
471     return application_add_segment_notify (sm->app_index,
472                                            *fifo_segment_index);
473
474   return 0;
475 }
476
477 void
478 segment_manager_dealloc_fifos (u32 svm_segment_index, svm_fifo_t * rx_fifo,
479                                svm_fifo_t * tx_fifo)
480 {
481   segment_manager_t *sm;
482   svm_fifo_segment_private_t *fifo_segment;
483   u32 i, segment_index = ~0;
484   u8 is_first;
485
486   sm = segment_manager_get_if_valid (rx_fifo->segment_manager);
487
488   /* It's possible to have no segment manager if the session was removed
489    * as result of a detach. */
490   if (!sm)
491     return;
492
493   fifo_segment = svm_fifo_segment_get_segment (svm_segment_index);
494   svm_fifo_segment_free_fifo (fifo_segment, rx_fifo,
495                               FIFO_SEGMENT_RX_FREELIST);
496   svm_fifo_segment_free_fifo (fifo_segment, tx_fifo,
497                               FIFO_SEGMENT_TX_FREELIST);
498
499   /*
500    * Try to remove svm segment if it has no fifos. This can be done only if
501    * the segment is not the first in the segment manager or if it is first
502    * and it is not protected. Moreover, if the segment is first and the app
503    * has detached from the segment manager, remove the segment manager.
504    */
505   if (!svm_fifo_segment_has_fifos (fifo_segment))
506     {
507       is_first = sm->segment_indices[0] == svm_segment_index;
508
509       /* Remove segment if it holds no fifos or first but not protected */
510       if (!is_first || !sm->first_is_protected)
511         {
512           /* Find the segment manager segment index */
513           for (i = 0; i < vec_len (sm->segment_indices); i++)
514             if (sm->segment_indices[i] == svm_segment_index)
515               {
516                 segment_index = i;
517                 break;
518               }
519           ASSERT (segment_index != (u32) ~ 0);
520           segment_manager_del_segment (sm, segment_index);
521         }
522
523       /* Remove segment manager if no sessions and detached from app */
524       if (segment_manager_app_detached (sm)
525           && !segment_manager_has_fifos (sm))
526         segment_manager_del (sm);
527     }
528 }
529
530 /**
531  * Allocates shm queue in the first segment
532  */
533 unix_shared_memory_queue_t *
534 segment_manager_alloc_queue (segment_manager_t * sm, u32 queue_size)
535 {
536   ssvm_shared_header_t *sh;
537   svm_fifo_segment_private_t *segment;
538   unix_shared_memory_queue_t *q;
539   void *oldheap;
540
541   ASSERT (sm->segment_indices != 0);
542
543   segment = svm_fifo_segment_get_segment (sm->segment_indices[0]);
544   sh = segment->ssvm.sh;
545
546   oldheap = ssvm_push_heap (sh);
547   q = unix_shared_memory_queue_init (queue_size,
548                                      sizeof (session_fifo_event_t),
549                                      0 /* consumer pid */ ,
550                                      0 /* signal when queue non-empty */ );
551   ssvm_pop_heap (oldheap);
552   return q;
553 }
554
555 /**
556  * Frees shm queue allocated in the first segment
557  */
558 void
559 segment_manager_dealloc_queue (segment_manager_t * sm,
560                                unix_shared_memory_queue_t * q)
561 {
562   ssvm_shared_header_t *sh;
563   svm_fifo_segment_private_t *segment;
564   void *oldheap;
565
566   ASSERT (sm->segment_indices != 0);
567
568   segment = svm_fifo_segment_get_segment (sm->segment_indices[0]);
569   sh = segment->ssvm.sh;
570
571   oldheap = ssvm_push_heap (sh);
572   unix_shared_memory_queue_free (q);
573   ssvm_pop_heap (oldheap);
574 }
575
576 static clib_error_t *
577 segment_manager_show_fn (vlib_main_t * vm, unformat_input_t * input,
578                          vlib_cli_command_t * cmd)
579 {
580   svm_fifo_segment_private_t *segments, *seg;
581   segment_manager_t *sm;
582   u8 show_segments = 0, verbose = 0, *name;
583   uword address;
584   u64 size;
585   u32 active_fifos;
586   u32 free_fifos;
587
588   mheap_t *heap_header;
589
590   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
591     {
592       if (unformat (input, "segments"))
593         show_segments = 1;
594       else if (unformat (input, "verbose"))
595         verbose = 1;
596       else
597         return clib_error_return (0, "unknown input `%U'",
598                                   format_unformat_error, input);
599     }
600   vlib_cli_output (vm, "%d segment managers allocated",
601                    pool_elts (segment_managers));
602   if (verbose && pool_elts (segment_managers))
603     {
604       vlib_cli_output (vm, "%-10s%=15s%=12s", "Index", "App Index",
605                        "Segments");
606
607       /* *INDENT-OFF* */
608       pool_foreach (sm, segment_managers, ({
609         vlib_cli_output (vm, "%-10d%=15d%=12d", segment_manager_index(sm),
610                            sm->app_index, vec_len (sm->segment_indices));
611       }));
612       /* *INDENT-ON* */
613
614     }
615   if (show_segments)
616     {
617       segments = svm_fifo_segment_segments_pool ();
618       vlib_cli_output (vm, "%d svm fifo segments allocated",
619                        pool_elts (segments));
620       vlib_cli_output (vm, "%-25s%15s%16s%16s%16s", "Name",
621                        "HeapSize (M)", "ActiveFifos", "FreeFifos", "Address");
622
623       /* *INDENT-OFF* */
624       pool_foreach (seg, segments, ({
625         if (seg->h->flags & FIFO_SEGMENT_F_IS_PRIVATE)
626           {
627             address = pointer_to_uword (seg->ssvm.sh->heap);
628             if (seg->h->flags & FIFO_SEGMENT_F_IS_MAIN_HEAP)
629               name = format (0, "main heap");
630             else
631               name = format (0, "private heap");
632             heap_header = mheap_header (seg->ssvm.sh->heap);
633             size = heap_header->max_size;
634           }
635         else
636           {
637             address =  seg->ssvm.sh->ssvm_va;
638             size = seg->ssvm.ssvm_size;
639             name = seg->ssvm.sh->name;
640           }
641         active_fifos = svm_fifo_segment_num_fifos (seg);
642         free_fifos = svm_fifo_segment_num_free_fifos (seg, ~0 /* size */);
643         vlib_cli_output (vm, "%-25v%15llu%16u%16u%16llx",
644                          name, size >> 20ULL, active_fifos, free_fifos,
645                          address);
646         if (verbose)
647           vlib_cli_output (vm, "%U",
648                            format_svm_fifo_segment, seg, verbose);
649         if (seg->h->flags & FIFO_SEGMENT_F_IS_PRIVATE)
650           vec_free (name);
651       }));
652       /* *INDENT-ON* */
653
654     }
655   return 0;
656 }
657
658 /* *INDENT-OFF* */
659 VLIB_CLI_COMMAND (segment_manager_show_command, static) =
660 {
661   .path = "show segment-manager",
662   .short_help = "show segment-manager [segments][verbose]",
663   .function = segment_manager_show_fn,
664 };
665 /* *INDENT-ON* */
666
667 /*
668  * fd.io coding-style-patch-verification: ON
669  *
670  * Local Variables:
671  * eval: (c-set-style "gnu")
672  * End:
673  */