session: segment manager improvements
[vpp.git] / src / vnet / session / application.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/application.h>
17 #include <vnet/session/application_interface.h>
18 #include <vnet/session/session.h>
19
20 /**
21  * Pool from which we allocate all applications
22  */
23 static application_t *app_pool;
24
25 /**
26  * Hash table of apps by api client index
27  */
28 static uword *app_by_api_client_index;
29
30 /**
31  * Default application event queue size
32  */
33 static u32 default_app_evt_queue_size = 128;
34
35 int
36 application_api_queue_is_full (application_t * app)
37 {
38   unix_shared_memory_queue_t *q;
39
40   /* builtin servers are always OK */
41   if (app->api_client_index == ~0)
42     return 0;
43
44   q = vl_api_client_index_to_input_queue (app->api_client_index);
45   if (!q)
46     return 1;
47
48   if (q->cursize == q->maxsize)
49     return 1;
50   return 0;
51 }
52
53 static void
54 application_table_add (application_t * app)
55 {
56   hash_set (app_by_api_client_index, app->api_client_index, app->index);
57 }
58
59 static void
60 application_table_del (application_t * app)
61 {
62   hash_unset (app_by_api_client_index, app->api_client_index);
63 }
64
65 application_t *
66 application_lookup (u32 api_client_index)
67 {
68   uword *p;
69   p = hash_get (app_by_api_client_index, api_client_index);
70   if (p)
71     return application_get (p[0]);
72
73   return 0;
74 }
75
76 application_t *
77 application_new ()
78 {
79   application_t *app;
80   pool_get (app_pool, app);
81   memset (app, 0, sizeof (*app));
82   app->index = application_get_index (app);
83   app->connects_seg_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
84   app->first_segment_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
85   if (CLIB_DEBUG > 1)
86     clib_warning ("[%d] New app (%d)", getpid (), app->index);
87   return app;
88 }
89
90 void
91 application_del (application_t * app)
92 {
93   segment_manager_t *sm;
94   u64 handle;
95   u32 index, *handles = 0;
96   int i;
97   vnet_unbind_args_t _a, *a = &_a;
98
99   /*
100    * The app event queue allocated in first segment is cleared with
101    * the segment manager. No need to explicitly free it.
102    */
103   if (CLIB_DEBUG > 1)
104     clib_warning ("[%d] Delete app (%d)", getpid (), app->index);
105
106   /*
107    *  Listener cleanup
108    */
109
110   /* *INDENT-OFF* */
111   hash_foreach (handle, index, app->listeners_table,
112   ({
113     vec_add1 (handles, handle);
114   }));
115   /* *INDENT-ON* */
116
117   for (i = 0; i < vec_len (handles); i++)
118     {
119       a->app_index = app->index;
120       a->handle = handles[i];
121       /* seg manager is removed when unbind completes */
122       vnet_unbind (a);
123     }
124
125   /*
126    * Connects segment manager cleanup
127    */
128
129   if (app->connects_seg_manager != APP_INVALID_SEGMENT_MANAGER_INDEX)
130     {
131       sm = segment_manager_get (app->connects_seg_manager);
132       sm->app_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
133       segment_manager_init_del (sm);
134     }
135
136
137   /* If first segment manager is used by a listener */
138   if (app->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX
139       && app->first_segment_manager != app->connects_seg_manager)
140     {
141       sm = segment_manager_get (app->first_segment_manager);
142       /* .. and has no fifos, e.g. it might be used for redirected sessions,
143        * remove it */
144       if (!segment_manager_has_fifos (sm))
145         {
146           sm->app_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
147           segment_manager_del (sm);
148         }
149     }
150
151   application_table_del (app);
152   pool_put (app_pool, app);
153 }
154
155 static void
156 application_verify_cb_fns (session_cb_vft_t * cb_fns)
157 {
158   if (cb_fns->session_accept_callback == 0)
159     clib_warning ("No accept callback function provided");
160   if (cb_fns->session_connected_callback == 0)
161     clib_warning ("No session connected callback function provided");
162   if (cb_fns->session_disconnect_callback == 0)
163     clib_warning ("No session disconnect callback function provided");
164   if (cb_fns->session_reset_callback == 0)
165     clib_warning ("No session reset callback function provided");
166 }
167
168 int
169 application_init (application_t * app, u32 api_client_index, u64 * options,
170                   session_cb_vft_t * cb_fns)
171 {
172   segment_manager_t *sm;
173   segment_manager_properties_t *props;
174   u32 app_evt_queue_size, first_seg_size;
175   u32 default_rx_fifo_size = 16 << 10, default_tx_fifo_size = 16 << 10;
176   int rv;
177
178   app_evt_queue_size = options[APP_EVT_QUEUE_SIZE] > 0 ?
179     options[APP_EVT_QUEUE_SIZE] : default_app_evt_queue_size;
180
181   /* Setup segment manager */
182   sm = segment_manager_new ();
183   sm->app_index = app->index;
184   props = &app->sm_properties;
185   props->add_segment_size = options[SESSION_OPTIONS_ADD_SEGMENT_SIZE];
186   props->rx_fifo_size = options[SESSION_OPTIONS_RX_FIFO_SIZE];
187   props->rx_fifo_size =
188     props->rx_fifo_size ? props->rx_fifo_size : default_rx_fifo_size;
189   props->tx_fifo_size = options[SESSION_OPTIONS_TX_FIFO_SIZE];
190   props->tx_fifo_size =
191     props->tx_fifo_size ? props->tx_fifo_size : default_tx_fifo_size;
192   props->add_segment = props->add_segment_size != 0;
193   props->preallocated_fifo_pairs = options[APP_OPTIONS_PREALLOC_FIFO_PAIRS];
194   props->use_private_segment = options[APP_OPTIONS_FLAGS]
195     & APP_OPTIONS_FLAGS_BUILTIN_APP;
196   props->private_segment_count = options[APP_OPTIONS_PRIVATE_SEGMENT_COUNT];
197   props->private_segment_size = options[APP_OPTIONS_PRIVATE_SEGMENT_SIZE];
198
199   first_seg_size = options[SESSION_OPTIONS_SEGMENT_SIZE];
200   if ((rv = segment_manager_init (sm, props, first_seg_size)))
201     return rv;
202   sm->first_is_protected = 1;
203
204   app->first_segment_manager = segment_manager_index (sm);
205   app->api_client_index = api_client_index;
206   app->flags = options[APP_OPTIONS_FLAGS];
207   app->cb_fns = *cb_fns;
208
209   /* Allocate app event queue in the first shared-memory segment */
210   app->event_queue = segment_manager_alloc_queue (sm, app_evt_queue_size);
211
212   /* Check that the obvious things are properly set up */
213   application_verify_cb_fns (cb_fns);
214
215   /* Add app to lookup by api_client_index table */
216   application_table_add (app);
217
218   return 0;
219 }
220
221 application_t *
222 application_get (u32 index)
223 {
224   return pool_elt_at_index (app_pool, index);
225 }
226
227 application_t *
228 application_get_if_valid (u32 index)
229 {
230   if (pool_is_free_index (app_pool, index))
231     return 0;
232
233   return pool_elt_at_index (app_pool, index);
234 }
235
236 u32
237 application_get_index (application_t * app)
238 {
239   return app - app_pool;
240 }
241
242 static segment_manager_t *
243 application_alloc_segment_manager (application_t * app)
244 {
245   segment_manager_t *sm = 0;
246
247   /* If the first segment manager is not in use, don't allocate a new one */
248   if (app->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX
249       && app->first_segment_manager_in_use == 0)
250     {
251       sm = segment_manager_get (app->first_segment_manager);
252       app->first_segment_manager_in_use = 1;
253       return sm;
254     }
255
256   sm = segment_manager_new ();
257   sm->properties = &app->sm_properties;
258
259   return sm;
260 }
261
262 /**
263  * Start listening local transport endpoint for requested transport.
264  *
265  * Creates a 'dummy' stream session with state LISTENING to be used in session
266  * lookups, prior to establishing connection. Requests transport to build
267  * it's own specific listening connection.
268  */
269 int
270 application_start_listen (application_t * srv, session_type_t session_type,
271                           transport_endpoint_t * tep, u64 * res)
272 {
273   segment_manager_t *sm;
274   stream_session_t *s;
275   u64 handle;
276
277   s = listen_session_new (session_type);
278   s->app_index = srv->index;
279
280   if (stream_session_listen (s, tep))
281     goto err;
282
283   /* Allocate segment manager. All sessions derived out of a listen session
284    * have fifos allocated by the same segment manager. */
285   sm = application_alloc_segment_manager (srv);
286   if (sm == 0)
287     goto err;
288
289   /* Add to app's listener table. Useful to find all child listeners
290    * when app goes down, although, just for unbinding this is not needed */
291   handle = listen_session_get_handle (s);
292   hash_set (srv->listeners_table, handle, segment_manager_index (sm));
293
294   *res = handle;
295   return 0;
296
297 err:
298   listen_session_del (s);
299   return -1;
300 }
301
302 /**
303  * Stop listening on session associated to handle
304  */
305 int
306 application_stop_listen (application_t * srv, u64 handle)
307 {
308   stream_session_t *listener;
309   uword *indexp;
310   segment_manager_t *sm;
311
312   if (srv && hash_get (srv->listeners_table, handle) == 0)
313     {
314       clib_warning ("app doesn't own handle %llu!", handle);
315       return -1;
316     }
317
318   listener = listen_session_get_from_handle (handle);
319   stream_session_stop_listen (listener);
320
321   indexp = hash_get (srv->listeners_table, handle);
322   ASSERT (indexp);
323
324   sm = segment_manager_get (*indexp);
325   if (srv->first_segment_manager == *indexp)
326     {
327       /* Delete sessions but don't remove segment manager */
328       srv->first_segment_manager_in_use = 0;
329       segment_manager_del_sessions (sm);
330     }
331   else
332     {
333       segment_manager_init_del (sm);
334     }
335   hash_unset (srv->listeners_table, handle);
336   listen_session_del (listener);
337
338   return 0;
339 }
340
341 int
342 application_open_session (application_t * app, session_type_t sst,
343                           transport_endpoint_t * tep, u32 api_context)
344 {
345   segment_manager_t *sm;
346   transport_connection_t *tc = 0;
347   int rv;
348
349   /* Make sure we have a segment manager for connects */
350   if (app->connects_seg_manager == (u32) ~ 0)
351     {
352       sm = application_alloc_segment_manager (app);
353       if (sm == 0)
354         return -1;
355       app->connects_seg_manager = segment_manager_index (sm);
356     }
357
358   if ((rv = stream_session_open (app->index, sst, tep, &tc)))
359     return rv;
360
361   /* Store api_context for when the reply comes. Not the nicest thing
362    * but better than allocating a separate half-open pool. */
363   tc->s_index = api_context;
364
365   return 0;
366 }
367
368 segment_manager_t *
369 application_get_connect_segment_manager (application_t * app)
370 {
371   ASSERT (app->connects_seg_manager != (u32) ~ 0);
372   return segment_manager_get (app->connects_seg_manager);
373 }
374
375 segment_manager_t *
376 application_get_listen_segment_manager (application_t * app,
377                                         stream_session_t * s)
378 {
379   uword *smp;
380   smp = hash_get (app->listeners_table, listen_session_get_handle (s));
381   ASSERT (smp != 0);
382   return segment_manager_get (*smp);
383 }
384
385 static u8 *
386 app_get_name_from_reg_index (application_t * app)
387 {
388   u8 *app_name;
389
390   vl_api_registration_t *regp;
391   regp = vl_api_client_index_to_registration (app->api_client_index);
392   if (!regp)
393     app_name = format (0, "builtin-%d%c", app->index, 0);
394   else
395     app_name = format (0, "%s%c", regp->name, 0);
396
397   return app_name;
398 }
399
400 int
401 application_is_proxy (application_t * app)
402 {
403   return !(app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
404 }
405
406 int
407 application_add_segment_notify (u32 app_index, u32 fifo_segment_index)
408 {
409   application_t *app = application_get (app_index);
410   u32 seg_size = 0;
411   u8 *seg_name;
412
413   /* Send an API message to the external app, to map new segment */
414   ASSERT (app->cb_fns.add_segment_callback);
415
416   segment_manager_get_segment_info (fifo_segment_index, &seg_name, &seg_size);
417   return app->cb_fns.add_segment_callback (app->api_client_index, seg_name,
418                                            seg_size);
419 }
420
421 u8 *
422 format_application_listener (u8 * s, va_list * args)
423 {
424   application_t *app = va_arg (*args, application_t *);
425   u64 handle = va_arg (*args, u64);
426   u32 index = va_arg (*args, u32);
427   int verbose = va_arg (*args, int);
428   stream_session_t *listener;
429   u8 *app_name, *str;
430
431   if (app == 0)
432     {
433       if (verbose)
434         s = format (s, "%-40s%-20s%-15s%-15s%-10s", "Connection", "App",
435                     "API Client", "ListenerID", "SegManager");
436       else
437         s = format (s, "%-40s%-20s", "Connection", "App");
438
439       return s;
440     }
441
442   app_name = app_get_name_from_reg_index (app);
443   listener = listen_session_get_from_handle (handle);
444   str = format (0, "%U", format_stream_session, listener, verbose);
445
446   if (verbose)
447     {
448       s = format (s, "%-40s%-20s%-15u%-15u%-10u", str, app_name,
449                   app->api_client_index, handle, index);
450     }
451   else
452     s = format (s, "%-40s%-20s", str, app_name);
453
454   vec_free (app_name);
455   return s;
456 }
457
458 void
459 application_format_connects (application_t * app, int verbose)
460 {
461   vlib_main_t *vm = vlib_get_main ();
462   segment_manager_t *sm;
463   u8 *app_name, *s = 0;
464   int j;
465
466   /* Header */
467   if (app == 0)
468     {
469       if (verbose)
470         vlib_cli_output (vm, "%-40s%-20s%-15s%-10s", "Connection", "App",
471                          "API Client", "SegManager");
472       else
473         vlib_cli_output (vm, "%-40s%-20s", "Connection", "App");
474       return;
475     }
476
477   /* make sure */
478   if (app->connects_seg_manager == (u32) ~ 0)
479     return;
480
481   app_name = app_get_name_from_reg_index (app);
482
483   /* Across all fifo segments */
484   sm = segment_manager_get (app->connects_seg_manager);
485   for (j = 0; j < vec_len (sm->segment_indices); j++)
486     {
487       svm_fifo_segment_private_t *fifo_segment;
488       svm_fifo_t *fifo;
489       u8 *str;
490
491       fifo_segment = svm_fifo_segment_get_segment (sm->segment_indices[j]);
492       fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
493       while (fifo)
494         {
495           u32 session_index, thread_index;
496           stream_session_t *session;
497
498           session_index = fifo->master_session_index;
499           thread_index = fifo->master_thread_index;
500
501           session = stream_session_get (session_index, thread_index);
502           str = format (0, "%U", format_stream_session, session, verbose);
503
504           if (verbose)
505             s = format (s, "%-40s%-20s%-15u%-10u", str, app_name,
506                         app->api_client_index, app->connects_seg_manager);
507           else
508             s = format (s, "%-40s%-20s", str, app_name);
509
510           vlib_cli_output (vm, "%v", s);
511           vec_reset_length (s);
512           vec_free (str);
513
514           fifo = fifo->next;
515         }
516       vec_free (s);
517     }
518
519   vec_free (app_name);
520 }
521
522 u8 *
523 format_application (u8 * s, va_list * args)
524 {
525   application_t *app = va_arg (*args, application_t *);
526   CLIB_UNUSED (int verbose) = va_arg (*args, int);
527   u8 *app_name;
528
529   if (app == 0)
530     {
531       if (verbose)
532         s = format (s, "%-10s%-20s%-15s%-15s%-15s%-15s", "Index", "Name",
533                     "API Client", "Add seg size", "Rx fifo size",
534                     "Tx fifo size");
535       else
536         s = format (s, "%-10s%-20s%-20s", "Index", "Name", "API Client");
537       return s;
538     }
539
540   app_name = app_get_name_from_reg_index (app);
541   if (verbose)
542     s = format (s, "%-10d%-20s%-15d%-15d%-15d%-15d", app->index, app_name,
543                 app->api_client_index, app->sm_properties.add_segment_size,
544                 app->sm_properties.rx_fifo_size,
545                 app->sm_properties.tx_fifo_size);
546   else
547     s = format (s, "%-10d%-20s%-20d", app->index, app_name,
548                 app->api_client_index);
549   return s;
550 }
551
552 static clib_error_t *
553 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
554                      vlib_cli_command_t * cmd)
555 {
556   application_t *app;
557   int do_server = 0;
558   int do_client = 0;
559   int verbose = 0;
560
561   if (!session_manager_is_enabled ())
562     {
563       clib_error_return (0, "session layer is not enabled");
564     }
565
566   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
567     {
568       if (unformat (input, "server"))
569         do_server = 1;
570       else if (unformat (input, "client"))
571         do_client = 1;
572       else if (unformat (input, "verbose"))
573         verbose = 1;
574       else
575         break;
576     }
577
578   if (do_server)
579     {
580       u64 handle;
581       u32 index;
582       if (pool_elts (app_pool))
583         {
584           vlib_cli_output (vm, "%U", format_application_listener,
585                            0 /* header */ , 0, 0,
586                            verbose);
587           /* *INDENT-OFF* */
588           pool_foreach (app, app_pool,
589           ({
590             /* App's listener sessions */
591             if (hash_elts (app->listeners_table) == 0)
592               continue;
593             hash_foreach (handle, index, app->listeners_table,
594             ({
595               vlib_cli_output (vm, "%U", format_application_listener, app,
596                                        handle, index, verbose);
597             }));
598           }));
599           /* *INDENT-ON* */
600         }
601       else
602         vlib_cli_output (vm, "No active server bindings");
603     }
604
605   if (do_client)
606     {
607       if (pool_elts (app_pool))
608         {
609           application_format_connects (0, verbose);
610
611           /* *INDENT-OFF* */
612           pool_foreach (app, app_pool,
613           ({
614             if (app->connects_seg_manager == (u32)~0)
615               continue;
616             application_format_connects (app, verbose);
617           }));
618           /* *INDENT-ON* */
619         }
620       else
621         vlib_cli_output (vm, "No active client bindings");
622     }
623
624   /* Print app related info */
625   if (!do_server && !do_client)
626     {
627       vlib_cli_output (vm, "%U", format_application, 0, verbose);
628       pool_foreach (app, app_pool, (
629                                      {
630                                      vlib_cli_output (vm, "%U",
631                                                       format_application, app,
632                                                       verbose);
633                                      }
634                     ));
635     }
636
637   return 0;
638 }
639
640 /* *INDENT-OFF* */
641 VLIB_CLI_COMMAND (show_app_command, static) =
642 {
643   .path = "show app",
644   .short_help = "show app [server|client] [verbose]",
645   .function = show_app_command_fn,
646 };
647 /* *INDENT-ON* */
648
649 /*
650  * fd.io coding-style-patch-verification: ON
651  *
652  * Local Variables:
653  * eval: (c-set-style "gnu")
654  * End:
655  */