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