cbf6207060c42320603cf27ba39e1c2edb373b21
[vpp.git] / src / vnet / session / segment_manager.c
1 /*
2  * Copyright (c) 2017-2019 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 typedef struct segment_manager_main_
21 {
22   segment_manager_t *segment_managers;  /**< Pool of segment managers */
23   u32 seg_name_counter;                 /**< Counter for segment names */
24
25   /*
26    * Configuration
27    */
28   u32 default_fifo_size;        /**< default rx/tx fifo size */
29   u32 default_segment_size;     /**< default fifo segment size */
30   u32 default_app_mq_size;      /**< default app msg q size */
31   u32 default_max_fifo_size;    /**< default max fifo size */
32   u8 default_high_watermark;    /**< default high watermark % */
33   u8 default_low_watermark;     /**< default low watermark % */
34 } segment_manager_main_t;
35
36 static segment_manager_main_t sm_main;
37
38 #define segment_manager_foreach_segment_w_lock(VAR, SM, BODY)           \
39 do {                                                                    \
40     clib_rwlock_reader_lock (&(SM)->segments_rwlock);                   \
41     pool_foreach((VAR), ((SM)->segments)) (BODY);                       \
42     clib_rwlock_reader_unlock (&(SM)->segments_rwlock);                 \
43 } while (0)
44
45 static segment_manager_props_t *
46 segment_manager_properties_get (segment_manager_t * sm)
47 {
48   app_worker_t *app_wrk = app_worker_get (sm->app_wrk_index);
49   return application_get_segment_manager_properties (app_wrk->app_index);
50 }
51
52 segment_manager_props_t *
53 segment_manager_props_init (segment_manager_props_t * props)
54 {
55   props->add_segment_size = sm_main.default_segment_size;
56   props->rx_fifo_size = sm_main.default_fifo_size;
57   props->tx_fifo_size = sm_main.default_fifo_size;
58   props->evt_q_size = sm_main.default_app_mq_size;
59   props->max_fifo_size = sm_main.default_max_fifo_size;
60   props->high_watermark = sm_main.default_high_watermark;
61   props->low_watermark = sm_main.default_low_watermark;
62   props->n_slices = vlib_num_workers () + 1;
63   return props;
64 }
65
66 u8
67 segment_manager_app_detached (segment_manager_t * sm)
68 {
69   return (sm->flags & SEG_MANAGER_F_DETACHED);
70 }
71
72 void
73 segment_manager_app_detach (segment_manager_t * sm)
74 {
75   sm->flags |= SEG_MANAGER_F_DETACHED;
76 }
77
78 always_inline u32
79 segment_manager_segment_index (segment_manager_t * sm, fifo_segment_t * seg)
80 {
81   return (seg - sm->segments);
82 }
83
84 /**
85  * Adds segment to segment manager's pool
86  *
87  * If needed a writer's lock is acquired before allocating a new segment
88  * to avoid affecting any of the segments pool readers.
89  */
90 static inline int
91 segment_manager_add_segment_inline (segment_manager_t *sm, uword segment_size,
92                                     u8 notify_app, u8 flags, u8 need_lock)
93 {
94   segment_manager_main_t *smm = &sm_main;
95   segment_manager_props_t *props;
96   app_worker_t *app_wrk;
97   fifo_segment_t *fs;
98   u32 fs_index = ~0;
99   u8 *seg_name;
100   int rv;
101
102   props = segment_manager_properties_get (sm);
103   app_wrk = app_worker_get (sm->app_wrk_index);
104
105   /* Not configured for addition of new segments and not first */
106   if (!props->add_segment && !segment_size)
107     {
108       clib_warning ("cannot allocate new segment");
109       return VNET_API_ERROR_INVALID_VALUE;
110     }
111
112   /*
113    * Allocate fifo segment and grab lock if needed
114    */
115   if (need_lock)
116     clib_rwlock_writer_lock (&sm->segments_rwlock);
117
118   pool_get_zero (sm->segments, fs);
119
120   /*
121    * Allocate ssvm segment
122    */
123   segment_size = segment_size ? segment_size : props->add_segment_size;
124   /* add overhead to ensure the result segment size is at least
125    * of that requested */
126   segment_size +=
127     sizeof (fifo_segment_header_t) +
128     vlib_thread_main.n_vlib_mains * sizeof (fifo_segment_slice_t) +
129     FIFO_SEGMENT_ALLOC_OVERHEAD;
130   segment_size = round_pow2 (segment_size, clib_mem_get_page_size ());
131
132   seg_name = format (0, "seg-%u-%u-%u%c", app_wrk->app_index,
133                      app_wrk->wrk_index, smm->seg_name_counter++, 0);
134
135   fs->ssvm.ssvm_size = segment_size;
136   fs->ssvm.name = seg_name;
137   fs->ssvm.requested_va = 0;
138
139   if ((rv = ssvm_server_init (&fs->ssvm, props->segment_type)))
140     {
141       clib_warning ("svm_master_init ('%v', %u) failed", seg_name,
142                     segment_size);
143       pool_put (sm->segments, fs);
144       goto done;
145     }
146
147   /*
148    * Initialize fifo segment
149    */
150   fs->n_slices = props->n_slices;
151   fifo_segment_init (fs);
152
153   /*
154    * Save segment index before dropping lock, if any held
155    */
156   fs_index = fs - sm->segments;
157
158   /*
159    * Set watermarks in segment
160    */
161   fs->high_watermark = sm->high_watermark;
162   fs->low_watermark = sm->low_watermark;
163   fs->flags = flags;
164   fs->flags &= ~FIFO_SEGMENT_F_MEM_LIMIT;
165   fs->h->pct_first_alloc = props->pct_first_alloc;
166
167   if (notify_app)
168     {
169       app_worker_t *app_wrk;
170       u64 fs_handle;
171       fs_handle = segment_manager_segment_handle (sm, fs);
172       app_wrk = app_worker_get (sm->app_wrk_index);
173       rv = app_worker_add_segment_notify (app_wrk, fs_handle);
174       if (rv)
175         {
176           fs_index = rv;
177           goto done;
178         }
179     }
180 done:
181
182   if (need_lock)
183     clib_rwlock_writer_unlock (&sm->segments_rwlock);
184
185   return fs_index;
186 }
187
188 int
189 segment_manager_add_segment (segment_manager_t *sm, uword segment_size,
190                              u8 notify_app)
191 {
192   return segment_manager_add_segment_inline (sm, segment_size, notify_app,
193                                              0 /* flags */, 0 /* need_lock */);
194 }
195
196 int
197 segment_manager_add_segment2 (segment_manager_t *sm, uword segment_size,
198                               u8 flags)
199 {
200   return segment_manager_add_segment_inline (sm, segment_size, 0, flags,
201                                              vlib_num_workers ());
202 }
203
204 /**
205  * Remove segment without lock
206  */
207 void
208 segment_manager_del_segment (segment_manager_t * sm, fifo_segment_t * fs)
209 {
210   if (ssvm_type (&fs->ssvm) != SSVM_SEGMENT_PRIVATE)
211     {
212       if (!segment_manager_app_detached (sm))
213         {
214           app_worker_t *app_wrk;
215           u64 segment_handle;
216           app_wrk = app_worker_get (sm->app_wrk_index);
217           segment_handle = segment_manager_segment_handle (sm, fs);
218           app_worker_del_segment_notify (app_wrk, segment_handle);
219         }
220     }
221
222   fifo_segment_cleanup (fs);
223   ssvm_delete (&fs->ssvm);
224
225   if (CLIB_DEBUG)
226     clib_memset (fs, 0xfb, sizeof (*fs));
227   pool_put (sm->segments, fs);
228 }
229
230 static fifo_segment_t *
231 segment_manager_get_segment_if_valid (segment_manager_t * sm,
232                                       u32 segment_index)
233 {
234   if (pool_is_free_index (sm->segments, segment_index))
235     return 0;
236   return pool_elt_at_index (sm->segments, segment_index);
237 }
238
239 /**
240  * Removes segment after acquiring writer lock
241  */
242 static inline void
243 sm_lock_and_del_segment_inline (segment_manager_t *sm, u32 fs_index,
244                                 u8 check_if_empty)
245 {
246   fifo_segment_t *fs;
247   u8 is_prealloc;
248
249   clib_rwlock_writer_lock (&sm->segments_rwlock);
250
251   fs = segment_manager_get_segment_if_valid (sm, fs_index);
252   if (!fs)
253     goto done;
254
255   if (check_if_empty && fifo_segment_has_fifos (fs))
256     goto done;
257
258   is_prealloc = fifo_segment_flags (fs) & FIFO_SEGMENT_F_IS_PREALLOCATED;
259   if (is_prealloc && !segment_manager_app_detached (sm))
260     goto done;
261
262   segment_manager_del_segment (sm, fs);
263
264 done:
265   clib_rwlock_writer_unlock (&sm->segments_rwlock);
266 }
267
268 void
269 segment_manager_lock_and_del_segment (segment_manager_t * sm, u32 fs_index)
270 {
271   sm_lock_and_del_segment_inline (sm, fs_index, 0 /* check_if_empty */);
272 }
273
274 /**
275  * Reads a segment from the segment manager's pool without lock
276  */
277 fifo_segment_t *
278 segment_manager_get_segment (segment_manager_t * sm, u32 segment_index)
279 {
280   return pool_elt_at_index (sm->segments, segment_index);
281 }
282
283 u64
284 segment_manager_segment_handle (segment_manager_t * sm,
285                                 fifo_segment_t * segment)
286 {
287   u32 segment_index = segment_manager_segment_index (sm, segment);
288   return (((u64) segment_manager_index (sm) << 32) | segment_index);
289 }
290
291 u64
292 segment_manager_make_segment_handle (u32 segment_manager_index,
293                                      u32 segment_index)
294 {
295   return (((u64) segment_manager_index << 32) | segment_index);
296 }
297
298 fifo_segment_t *
299 segment_manager_get_segment_w_handle (u64 segment_handle)
300 {
301   u32 sm_index, segment_index;
302   segment_manager_t *sm;
303
304   segment_manager_parse_segment_handle (segment_handle, &sm_index,
305                                         &segment_index);
306   sm = segment_manager_get (sm_index);
307   if (!sm || pool_is_free_index (sm->segments, segment_index))
308     return 0;
309   return pool_elt_at_index (sm->segments, segment_index);
310 }
311
312 /**
313  * Reads a segment from the segment manager's pool and acquires reader lock
314  *
315  * Caller must drop the reader's lock by calling
316  * @ref segment_manager_segment_reader_unlock once it finishes working with
317  * the segment.
318  */
319 fifo_segment_t *
320 segment_manager_get_segment_w_lock (segment_manager_t * sm, u32 segment_index)
321 {
322   clib_rwlock_reader_lock (&sm->segments_rwlock);
323   return pool_elt_at_index (sm->segments, segment_index);
324 }
325
326 void
327 segment_manager_segment_reader_lock (segment_manager_t * sm)
328 {
329   clib_rwlock_reader_lock (&sm->segments_rwlock);
330 }
331
332 void
333 segment_manager_segment_reader_unlock (segment_manager_t * sm)
334 {
335   clib_rwlock_reader_unlock (&sm->segments_rwlock);
336 }
337
338 segment_manager_t *
339 segment_manager_alloc (void)
340 {
341   segment_manager_main_t *smm = &sm_main;
342   segment_manager_t *sm;
343
344   pool_get_zero (smm->segment_managers, sm);
345   clib_rwlock_init (&sm->segments_rwlock);
346   return sm;
347 }
348
349 int
350 segment_manager_init (segment_manager_t * sm)
351 {
352   segment_manager_props_t *props;
353
354   props = segment_manager_properties_get (sm);
355
356   sm->max_fifo_size = props->max_fifo_size ?
357     props->max_fifo_size : sm_main.default_max_fifo_size;
358   sm->max_fifo_size = clib_max (sm->max_fifo_size, 4096);
359
360   segment_manager_set_watermarks (sm,
361                                   props->high_watermark,
362                                   props->low_watermark);
363   return 0;
364 }
365
366 /**
367  * Initializes segment manager based on options provided.
368  * Returns error if ssvm segment(s) allocation fails.
369  */
370 int
371 segment_manager_init_first (segment_manager_t * sm)
372 {
373   segment_manager_props_t *props;
374   uword first_seg_size;
375   fifo_segment_t *fs;
376   int fs_index, i;
377
378   segment_manager_init (sm);
379   props = segment_manager_properties_get (sm);
380   first_seg_size = clib_max (props->segment_size,
381                              sm_main.default_segment_size);
382
383   if (props->prealloc_fifos)
384     {
385       u64 approx_total_size, max_seg_size = ((u64) 1 << 32) - (128 << 10);
386       u32 rx_rounded_data_size, tx_rounded_data_size;
387       u32 prealloc_fifo_pairs = props->prealloc_fifos;
388       u32 rx_fifo_size, tx_fifo_size, pair_size;
389       u32 approx_segment_count;
390
391       /* Figure out how many segments should be preallocated */
392       rx_rounded_data_size = (1 << (max_log2 (props->rx_fifo_size)));
393       tx_rounded_data_size = (1 << (max_log2 (props->tx_fifo_size)));
394
395       rx_fifo_size = sizeof (svm_fifo_t) + rx_rounded_data_size;
396       tx_fifo_size = sizeof (svm_fifo_t) + tx_rounded_data_size;
397       pair_size = rx_fifo_size + tx_fifo_size;
398
399       approx_total_size = (u64) prealloc_fifo_pairs *pair_size;
400       if (first_seg_size > approx_total_size)
401         max_seg_size = first_seg_size;
402       approx_segment_count = (approx_total_size + (max_seg_size - 1))
403         / max_seg_size;
404
405       /* Allocate the segments */
406       for (i = 0; i < approx_segment_count + 1; i++)
407         {
408           fs_index = segment_manager_add_segment (sm, max_seg_size, 0);
409           if (fs_index < 0)
410             {
411               clib_warning ("Failed to preallocate segment %d", i);
412               return fs_index;
413             }
414
415           fs = segment_manager_get_segment (sm, fs_index);
416           if (i == 0)
417             sm->event_queue = segment_manager_alloc_queue (fs, props);
418
419           fifo_segment_preallocate_fifo_pairs (fs,
420                                                props->rx_fifo_size,
421                                                props->tx_fifo_size,
422                                                &prealloc_fifo_pairs);
423           fifo_segment_flags (fs) = FIFO_SEGMENT_F_IS_PREALLOCATED;
424           if (prealloc_fifo_pairs == 0)
425             break;
426         }
427       return 0;
428     }
429
430   fs_index = segment_manager_add_segment (sm, first_seg_size, 0);
431   if (fs_index < 0)
432     {
433       clib_warning ("Failed to allocate segment");
434       return fs_index;
435     }
436
437   fs = segment_manager_get_segment (sm, fs_index);
438   sm->event_queue = segment_manager_alloc_queue (fs, props);
439
440   if (props->prealloc_fifo_hdrs)
441     {
442       u32 hdrs_per_slice;
443
444       /* Do not preallocate on slice associated to main thread */
445       i = (vlib_num_workers ()? 1 : 0);
446       hdrs_per_slice = props->prealloc_fifo_hdrs / (fs->n_slices - i);
447
448       for (; i < fs->n_slices; i++)
449         {
450           if (fifo_segment_prealloc_fifo_hdrs (fs, i, hdrs_per_slice))
451             return VNET_API_ERROR_SVM_SEGMENT_CREATE_FAIL;
452         }
453     }
454
455   return 0;
456 }
457
458 void
459 segment_manager_cleanup_detached_listener (segment_manager_t * sm)
460 {
461   app_worker_t *app_wrk;
462
463   app_wrk = app_worker_get_if_valid (sm->app_wrk_index);
464   if (!app_wrk)
465     return;
466
467   app_worker_del_detached_sm (app_wrk, segment_manager_index (sm));
468 }
469
470 /**
471  * Cleanup segment manager.
472  */
473 void
474 segment_manager_free (segment_manager_t * sm)
475 {
476   segment_manager_main_t *smm = &sm_main;
477   fifo_segment_t *fifo_segment;
478
479   ASSERT (vlib_get_thread_index () == 0
480           && !segment_manager_has_fifos (sm)
481           && segment_manager_app_detached (sm));
482
483   if (sm->flags & SEG_MANAGER_F_DETACHED_LISTENER)
484     segment_manager_cleanup_detached_listener (sm);
485
486   /* If we have empty preallocated segments that haven't been removed, remove
487    * them now. Apart from that, the first segment in the first segment manager
488    * is not removed when all fifos are removed. It can only be removed when
489    * the manager is explicitly deleted/detached by the app. */
490   clib_rwlock_writer_lock (&sm->segments_rwlock);
491
492   /* *INDENT-OFF* */
493   pool_foreach (fifo_segment, sm->segments)  {
494     segment_manager_del_segment (sm, fifo_segment);
495   }
496   /* *INDENT-ON* */
497
498   pool_free (sm->segments);
499   clib_rwlock_writer_unlock (&sm->segments_rwlock);
500
501   clib_rwlock_free (&sm->segments_rwlock);
502   if (CLIB_DEBUG)
503     clib_memset (sm, 0xfe, sizeof (*sm));
504   pool_put (smm->segment_managers, sm);
505 }
506
507 static void
508 sm_free_w_index_helper (void *arg)
509 {
510   u32 sm_index = *(u32 *) arg;
511   segment_manager_t *sm;
512
513   ASSERT (vlib_get_thread_index () == 0);
514
515   if ((sm = segment_manager_get_if_valid (sm_index)))
516     segment_manager_free (sm);
517 }
518
519 void
520 segment_manager_free_safe (segment_manager_t *sm)
521 {
522   if (!vlib_thread_is_main_w_barrier ())
523     {
524       u32 sm_index = segment_manager_index (sm);
525       vlib_rpc_call_main_thread (sm_free_w_index_helper, (u8 *) & sm_index,
526                                  sizeof (sm_index));
527     }
528   else
529     {
530       segment_manager_free (sm);
531     }
532 }
533
534 void
535 segment_manager_init_free (segment_manager_t * sm)
536 {
537   ASSERT (vlib_get_thread_index () == 0);
538
539   segment_manager_app_detach (sm);
540   if (segment_manager_has_fifos (sm))
541     segment_manager_del_sessions (sm);
542   else
543     {
544       ASSERT (!sm->first_is_protected || segment_manager_app_detached (sm));
545       segment_manager_free (sm);
546     }
547 }
548
549 segment_manager_t *
550 segment_manager_get (u32 index)
551 {
552   return pool_elt_at_index (sm_main.segment_managers, index);
553 }
554
555 segment_manager_t *
556 segment_manager_get_if_valid (u32 index)
557 {
558   if (pool_is_free_index (sm_main.segment_managers, index))
559     return 0;
560   return pool_elt_at_index (sm_main.segment_managers, index);
561 }
562
563 u32
564 segment_manager_index (segment_manager_t * sm)
565 {
566   return sm - sm_main.segment_managers;
567 }
568
569 u8
570 segment_manager_has_fifos (segment_manager_t * sm)
571 {
572   fifo_segment_t *seg;
573   u8 first = 1;
574
575   /* *INDENT-OFF* */
576   segment_manager_foreach_segment_w_lock (seg, sm, ({
577     if (CLIB_DEBUG && !first && !fifo_segment_has_fifos (seg)
578         && !(fifo_segment_flags (seg) & FIFO_SEGMENT_F_IS_PREALLOCATED))
579       {
580         clib_warning ("segment %d has no fifos!",
581                       segment_manager_segment_index (sm, seg));
582         first = 0;
583       }
584     if (fifo_segment_has_fifos (seg))
585       {
586         segment_manager_segment_reader_unlock (sm);
587         return 1;
588       }
589   }));
590   /* *INDENT-ON* */
591
592   return 0;
593 }
594
595 /**
596  * Initiate disconnects for all sessions 'owned' by a segment manager
597  */
598 void
599 segment_manager_del_sessions (segment_manager_t * sm)
600 {
601   session_handle_t *handles = 0, *handle;
602   fifo_segment_t *fs;
603   session_t *session;
604   int slice_index;
605   svm_fifo_t *f;
606
607   ASSERT (pool_elts (sm->segments) != 0);
608
609   /* Across all fifo segments used by the server */
610   /* *INDENT-OFF* */
611   segment_manager_foreach_segment_w_lock (fs, sm, ({
612     for (slice_index = 0; slice_index < fs->n_slices; slice_index++)
613       {
614         f = fifo_segment_get_slice_fifo_list (fs, slice_index);
615
616         /*
617          * Remove any residual sessions from the session lookup table
618          * Don't bother deleting the individual fifos, we're going to
619          * throw away the fifo segment in a minute.
620          */
621         while (f)
622           {
623             session = session_get_if_valid (f->shr->master_session_index,
624                                             f->master_thread_index);
625             if (session)
626               vec_add1 (handles, session_handle (session));
627             f = f->next;
628           }
629       }
630
631     /* Instead of removing the segment, test when cleaning up disconnected
632      * sessions if the segment can be removed.
633      */
634   }));
635   /* *INDENT-ON* */
636
637   vec_foreach (handle, handles)
638   {
639     session = session_get_from_handle (*handle);
640     session_close (session);
641     /* Avoid propagating notifications back to the app */
642     session->app_wrk_index = APP_INVALID_INDEX;
643   }
644   vec_free (handles);
645 }
646
647 /**
648  * Initiate disconnects for sessions in specified state 'owned' by a segment
649  * manager
650  */
651 void
652 segment_manager_del_sessions_filter (segment_manager_t *sm,
653                                      session_state_t *states)
654 {
655   session_handle_t *handles = 0, *handle;
656   fifo_segment_t *fs;
657   session_t *session;
658   int slice_index;
659   svm_fifo_t *f;
660
661   ASSERT (pool_elts (sm->segments) != 0);
662
663   /* Across all fifo segments used by the server */
664   segment_manager_foreach_segment_w_lock (
665     fs, sm, ({
666       for (slice_index = 0; slice_index < fs->n_slices; slice_index++)
667         {
668           f = fifo_segment_get_slice_fifo_list (fs, slice_index);
669           while (f)
670             {
671               session = session_get_if_valid (f->shr->master_session_index,
672                                               f->master_thread_index);
673               if (session)
674                 {
675                   session_state_t *state;
676                   vec_foreach (state, states)
677                     {
678                       if (session->session_state == *state)
679                         {
680                           vec_add1 (handles, session_handle (session));
681                           break;
682                         }
683                     }
684                 }
685               f = f->next;
686             }
687         }
688     }));
689
690   vec_foreach (handle, handles)
691     {
692       session = session_get_from_handle (*handle);
693       session_close (session);
694       /* Avoid propagating notifications back to the app */
695       session->app_wrk_index = APP_INVALID_INDEX;
696     }
697   vec_free (handles);
698 }
699
700 int
701 segment_manager_try_alloc_fifos (fifo_segment_t * fifo_segment,
702                                  u32 thread_index,
703                                  u32 rx_fifo_size, u32 tx_fifo_size,
704                                  svm_fifo_t ** rx_fifo, svm_fifo_t ** tx_fifo)
705 {
706   rx_fifo_size = clib_max (rx_fifo_size, sm_main.default_fifo_size);
707   *rx_fifo = fifo_segment_alloc_fifo_w_slice (fifo_segment, thread_index,
708                                               rx_fifo_size,
709                                               FIFO_SEGMENT_RX_FIFO);
710
711   tx_fifo_size = clib_max (tx_fifo_size, sm_main.default_fifo_size);
712   *tx_fifo = fifo_segment_alloc_fifo_w_slice (fifo_segment, thread_index,
713                                               tx_fifo_size,
714                                               FIFO_SEGMENT_TX_FIFO);
715
716   if (*rx_fifo == 0)
717     {
718       /* This would be very odd, but handle it... */
719       if (*tx_fifo != 0)
720         {
721           fifo_segment_free_fifo (fifo_segment, *tx_fifo);
722           *tx_fifo = 0;
723         }
724       return -1;
725     }
726   if (*tx_fifo == 0)
727     {
728       if (*rx_fifo != 0)
729         {
730           fifo_segment_free_fifo (fifo_segment, *rx_fifo);
731           *rx_fifo = 0;
732         }
733       return -1;
734     }
735
736   return 0;
737 }
738
739 static inline int
740 sm_lookup_segment_and_alloc_fifos (segment_manager_t *sm,
741                                    segment_manager_props_t *props,
742                                    u32 thread_index, svm_fifo_t **rx_fifo,
743                                    svm_fifo_t **tx_fifo)
744 {
745   uword free_bytes, max_free_bytes;
746   fifo_segment_t *cur, *fs = 0;
747   u32 fs_index;
748   int rv;
749
750   max_free_bytes = props->rx_fifo_size + props->tx_fifo_size - 1;
751
752   pool_foreach (cur, sm->segments)
753     {
754       if (fifo_segment_flags (cur) & FIFO_SEGMENT_F_CUSTOM_USE)
755         continue;
756       free_bytes = fifo_segment_available_bytes (cur);
757       if (free_bytes > max_free_bytes)
758         {
759           max_free_bytes = free_bytes;
760           fs = cur;
761         }
762     }
763
764   if (PREDICT_FALSE (!fs))
765     return SESSION_E_SEG_NO_SPACE;
766
767   fs_index = segment_manager_segment_index (sm, fs);
768   rv = segment_manager_try_alloc_fifos (fs, thread_index, props->rx_fifo_size,
769                                         props->tx_fifo_size, rx_fifo, tx_fifo);
770
771   return rv ? SESSION_E_SEG_NO_SPACE : fs_index;
772 }
773
774 static int
775 sm_lock_and_alloc_segment_and_fifos (segment_manager_t *sm,
776                                      segment_manager_props_t *props,
777                                      u32 thread_index, svm_fifo_t **rx_fifo,
778                                      svm_fifo_t **tx_fifo)
779 {
780   int new_fs_index, rv;
781   fifo_segment_t *fs;
782
783   if (!props->add_segment)
784     return SESSION_E_SEG_NO_SPACE;
785
786   clib_rwlock_writer_lock (&sm->segments_rwlock);
787
788   /* Make sure there really is no free space. Another worker might've freed
789    * some fifos or allocated a segment */
790   rv = sm_lookup_segment_and_alloc_fifos (sm, props, thread_index, rx_fifo,
791                                           tx_fifo);
792   if (rv > 0)
793     goto done;
794
795   new_fs_index =
796     segment_manager_add_segment (sm, 0 /* segment_size*/, 1 /* notify_app */);
797   if (new_fs_index < 0)
798     {
799       rv = SESSION_E_SEG_CREATE;
800       goto done;
801     }
802   fs = segment_manager_get_segment (sm, new_fs_index);
803   rv = segment_manager_try_alloc_fifos (fs, thread_index, props->rx_fifo_size,
804                                         props->tx_fifo_size, rx_fifo, tx_fifo);
805   if (rv)
806     {
807       clib_warning ("Added a segment, still can't allocate a fifo");
808       rv = SESSION_E_SEG_NO_SPACE2;
809       goto done;
810     }
811
812   rv = new_fs_index;
813
814 done:
815
816   clib_rwlock_writer_unlock (&sm->segments_rwlock);
817
818   return rv;
819 }
820
821 int
822 segment_manager_alloc_session_fifos (segment_manager_t * sm,
823                                      u32 thread_index,
824                                      svm_fifo_t ** rx_fifo,
825                                      svm_fifo_t ** tx_fifo)
826 {
827   segment_manager_props_t *props;
828   u32 sm_index;
829   int fs_index;
830
831   props = segment_manager_properties_get (sm);
832   sm_index = segment_manager_index (sm);
833
834   /*
835    * Fast path: find the first segment with enough free space and
836    * try to allocate the fifos. Done with reader lock
837    */
838
839   segment_manager_segment_reader_lock (sm);
840
841   fs_index = sm_lookup_segment_and_alloc_fifos (sm, props, thread_index,
842                                                 rx_fifo, tx_fifo);
843
844   segment_manager_segment_reader_unlock (sm);
845
846   /*
847    * Slow path: if no fifo segment or alloc fail grab writer lock and try
848    * to allocate new segment
849    */
850   if (PREDICT_FALSE (fs_index < 0))
851     {
852       fs_index = sm_lock_and_alloc_segment_and_fifos (sm, props, thread_index,
853                                                       rx_fifo, tx_fifo);
854       if (fs_index < 0)
855         return fs_index;
856     }
857
858   (*tx_fifo)->segment_manager = sm_index;
859   (*rx_fifo)->segment_manager = sm_index;
860   (*tx_fifo)->segment_index = fs_index;
861   (*rx_fifo)->segment_index = fs_index;
862
863   return 0;
864 }
865
866 void
867 segment_manager_dealloc_fifos (svm_fifo_t * rx_fifo, svm_fifo_t * tx_fifo)
868 {
869   segment_manager_t *sm;
870   fifo_segment_t *fs;
871   u32 segment_index;
872   u8 try_delete = 0;
873
874   if (!rx_fifo || !tx_fifo)
875     return;
876
877   /* It's possible to have no segment manager if the session was removed
878    * as result of a detach. */
879   if (!(sm = segment_manager_get_if_valid (rx_fifo->segment_manager)))
880     return;
881
882   segment_index = rx_fifo->segment_index;
883   fs = segment_manager_get_segment_w_lock (sm, segment_index);
884   fifo_segment_free_fifo (fs, rx_fifo);
885   fifo_segment_free_fifo (fs, tx_fifo);
886
887   /*
888    * Try to remove fifo segment if it has no fifos. This can be done only if
889    * the segment is not the first in the segment manager or if it is first
890    * and it is not protected. Moreover, if the segment is first and the app
891    * has detached from the segment manager, remove the segment manager.
892    */
893   if (!fifo_segment_has_fifos (fs))
894     {
895       /* If first, remove only if not protected */
896       try_delete = segment_index != 0 || !sm->first_is_protected;
897     }
898
899   segment_manager_segment_reader_unlock (sm);
900
901   if (PREDICT_FALSE (try_delete))
902     {
903       /* Only remove if empty after writer lock acquired */
904       sm_lock_and_del_segment_inline (sm, segment_index,
905                                       1 /* check_if_empty */);
906
907       /* Remove segment manager if no sessions and detached from app */
908       if (segment_manager_app_detached (sm)
909           && !segment_manager_has_fifos (sm))
910         segment_manager_free_safe (sm);
911     }
912 }
913
914 void
915 segment_manager_detach_fifo (segment_manager_t *sm, svm_fifo_t **f)
916 {
917   fifo_segment_t *fs;
918
919   fs = segment_manager_get_segment_w_lock (sm, (*f)->segment_index);
920   fifo_segment_detach_fifo (fs, f);
921   segment_manager_segment_reader_unlock (sm);
922 }
923
924 void
925 segment_manager_attach_fifo (segment_manager_t *sm, svm_fifo_t **f,
926                              session_t *s)
927 {
928   fifo_segment_t *fs;
929
930   fs = segment_manager_get_segment_w_lock (sm, (*f)->segment_index);
931   fifo_segment_attach_fifo (fs, f, s->thread_index);
932   segment_manager_segment_reader_unlock (sm);
933
934   (*f)->shr->master_session_index = s->session_index;
935   (*f)->master_thread_index = s->thread_index;
936 }
937
938 u32
939 segment_manager_evt_q_expected_size (u32 q_len)
940 {
941   u32 fifo_evt_size, notif_q_size, q_hdrs;
942   u32 msg_q_sz, fifo_evt_ring_sz, session_ntf_ring_sz;
943
944   fifo_evt_size = 1 << max_log2 (sizeof (session_event_t));
945   notif_q_size = clib_max (16, q_len >> 4);
946
947   msg_q_sz = q_len * sizeof (svm_msg_q_msg_t);
948   fifo_evt_ring_sz = q_len * fifo_evt_size;
949   session_ntf_ring_sz = notif_q_size * 256;
950   q_hdrs = sizeof (svm_queue_t) + sizeof (svm_msg_q_t);
951
952   return (msg_q_sz + fifo_evt_ring_sz + session_ntf_ring_sz + q_hdrs);
953 }
954
955 /**
956  * Allocates shm queue in the first segment
957  *
958  * Must be called with lock held
959  */
960 svm_msg_q_t *
961 segment_manager_alloc_queue (fifo_segment_t * segment,
962                              segment_manager_props_t * props)
963 {
964   u32 fifo_evt_size, session_evt_size = 256, notif_q_size;
965   svm_msg_q_cfg_t _cfg, *cfg = &_cfg;
966   svm_msg_q_t *q;
967
968   fifo_evt_size = sizeof (session_event_t);
969   notif_q_size = clib_max (16, props->evt_q_size >> 4);
970   /* *INDENT-OFF* */
971   svm_msg_q_ring_cfg_t rc[SESSION_MQ_N_RINGS] = {
972     {props->evt_q_size, fifo_evt_size, 0},
973     {notif_q_size, session_evt_size, 0}
974   };
975   /* *INDENT-ON* */
976   cfg->consumer_pid = 0;
977   cfg->n_rings = 2;
978   cfg->q_nitems = props->evt_q_size;
979   cfg->ring_cfgs = rc;
980
981   q = fifo_segment_msg_q_alloc (segment, 0, cfg);
982
983   if (props->use_mq_eventfd)
984     {
985       if (svm_msg_q_alloc_eventfd (q))
986         clib_warning ("failed to alloc eventfd");
987     }
988   return q;
989 }
990
991 svm_msg_q_t *
992 segment_manager_event_queue (segment_manager_t * sm)
993 {
994   return sm->event_queue;
995 }
996
997 /**
998  * Frees shm queue allocated in the first segment
999  */
1000 void
1001 segment_manager_dealloc_queue (segment_manager_t * sm, svm_queue_t * q)
1002 {
1003   fifo_segment_t *segment;
1004   ssvm_shared_header_t *sh;
1005   void *oldheap;
1006
1007   ASSERT (!pool_is_free_index (sm->segments, 0));
1008
1009   segment = segment_manager_get_segment_w_lock (sm, 0);
1010   sh = segment->ssvm.sh;
1011
1012   oldheap = ssvm_push_heap (sh);
1013   svm_queue_free (q);
1014   ssvm_pop_heap (oldheap);
1015   segment_manager_segment_reader_unlock (sm);
1016 }
1017
1018 /*
1019  * Init segment vm address allocator
1020  */
1021 void
1022 segment_manager_main_init (void)
1023 {
1024   segment_manager_main_t *sm = &sm_main;
1025
1026   sm->default_fifo_size = 1 << 12;
1027   sm->default_segment_size = 1 << 20;
1028   sm->default_app_mq_size = 128;
1029   sm->default_max_fifo_size = 4 << 20;
1030   sm->default_high_watermark = 80;
1031   sm->default_low_watermark = 50;
1032 }
1033
1034 static u8 *
1035 format_segment_manager (u8 *s, va_list *args)
1036 {
1037   segment_manager_t *sm = va_arg (*args, segment_manager_t *);
1038   int verbose = va_arg (*args, int);
1039   app_worker_t *app_wrk;
1040   uword max_fifo_size;
1041   fifo_segment_t *seg;
1042   application_t *app;
1043   u8 custom_logic;
1044
1045   app_wrk = app_worker_get_if_valid (sm->app_wrk_index);
1046   app = app_wrk ? application_get (app_wrk->app_index) : 0;
1047   custom_logic = (app && (app->cb_fns.fifo_tuning_callback)) ? 1 : 0;
1048   max_fifo_size = sm->max_fifo_size;
1049
1050   s = format (s,
1051               "[%u] %v app-wrk: %u segs: %u max-fifo-sz: %U "
1052               "wmarks: %u %u %s flags: 0x%x",
1053               segment_manager_index (sm), app->name, sm->app_wrk_index,
1054               pool_elts (sm->segments), format_memory_size, max_fifo_size,
1055               sm->high_watermark, sm->low_watermark,
1056               custom_logic ? "custom-tuning" : "no-tuning", sm->flags);
1057
1058   if (!verbose || !pool_elts (sm->segments))
1059     return s;
1060
1061   s = format (s, "\n\n");
1062
1063   segment_manager_foreach_segment_w_lock (
1064     seg, sm, ({ s = format (s, " *%U", format_fifo_segment, seg, verbose); }));
1065
1066   return s;
1067 }
1068
1069 static clib_error_t *
1070 segment_manager_show_fn (vlib_main_t * vm, unformat_input_t * input,
1071                          vlib_cli_command_t * cmd)
1072 {
1073   unformat_input_t _line_input, *line_input = &_line_input;
1074   segment_manager_main_t *smm = &sm_main;
1075   u8 show_segments = 0, verbose = 0;
1076   segment_manager_t *sm;
1077   u32 sm_index = ~0;
1078
1079   if (!unformat_user (input, unformat_line_input, line_input))
1080     {
1081       vlib_cli_output (vm, "%d segment managers allocated",
1082                        pool_elts (smm->segment_managers));
1083       return 0;
1084     }
1085
1086   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1087     {
1088       if (unformat (line_input, "segments"))
1089         show_segments = 1;
1090       else if (unformat (line_input, "verbose"))
1091         verbose = 1;
1092       else if (unformat (line_input, "index %u", &sm_index))
1093         ;
1094       else
1095         {
1096           vlib_cli_output (vm, "unknown input [%U]", format_unformat_error,
1097                            line_input);
1098           goto done;
1099         }
1100     }
1101
1102   if (!pool_elts (smm->segment_managers))
1103     goto done;
1104
1105   if (sm_index != ~0)
1106     {
1107       sm = segment_manager_get_if_valid (sm_index);
1108       if (!sm)
1109         {
1110           vlib_cli_output (vm, "segment manager %u not allocated", sm_index);
1111           goto done;
1112         }
1113       vlib_cli_output (vm, "%U", format_segment_manager, sm, 1 /* verbose */);
1114       goto done;
1115     }
1116
1117   if (verbose || show_segments)
1118     {
1119       pool_foreach (sm, smm->segment_managers)  {
1120           vlib_cli_output (vm, "%U", format_segment_manager, sm,
1121                            show_segments);
1122       }
1123
1124       vlib_cli_output (vm, "\n");
1125     }
1126
1127 done:
1128
1129   unformat_free (line_input);
1130
1131   return 0;
1132 }
1133
1134 /* *INDENT-OFF* */
1135 VLIB_CLI_COMMAND (segment_manager_show_command, static) = {
1136   .path = "show segment-manager",
1137   .short_help = "show segment-manager [segments][verbose][index <nn>]",
1138   .function = segment_manager_show_fn,
1139 };
1140 /* *INDENT-ON* */
1141
1142 void
1143 segment_manager_format_sessions (segment_manager_t * sm, int verbose)
1144 {
1145   vlib_main_t *vm = vlib_get_main ();
1146   app_worker_t *app_wrk;
1147   fifo_segment_t *fs;
1148   const u8 *app_name;
1149   int slice_index;
1150   u8 *s = 0, *str;
1151   svm_fifo_t *f;
1152
1153   if (!sm)
1154     {
1155       if (verbose)
1156         vlib_cli_output (vm, "%-" SESSION_CLI_ID_LEN "s%-20s%-15s%-10s",
1157                          "Connection", "App", "API Client", "SegManager");
1158       else
1159         vlib_cli_output (vm, "%-" SESSION_CLI_ID_LEN "s%-20s", "Connection",
1160                          "App");
1161       return;
1162     }
1163
1164   app_wrk = app_worker_get (sm->app_wrk_index);
1165   app_name = application_name_from_index (app_wrk->app_index);
1166
1167   clib_rwlock_reader_lock (&sm->segments_rwlock);
1168
1169   /* *INDENT-OFF* */
1170   pool_foreach (fs, sm->segments)  {
1171     for (slice_index = 0; slice_index < fs->n_slices; slice_index++)
1172       {
1173         f = fifo_segment_get_slice_fifo_list (fs, slice_index);
1174         while (f)
1175           {
1176             u32 session_index, thread_index;
1177             session_t *session;
1178
1179             session_index = f->shr->master_session_index;
1180             thread_index = f->master_thread_index;
1181
1182             session = session_get (session_index, thread_index);
1183             str = format (0, "%U", format_session, session, verbose);
1184
1185             if (verbose)
1186               s = format (s, "%-" SESSION_CLI_ID_LEN "v%-20v%-15u%-10u", str,
1187                           app_name, app_wrk->api_client_index,
1188                           app_wrk->connects_seg_manager);
1189             else
1190               s = format (s, "%-" SESSION_CLI_ID_LEN "v%-20v", str, app_name);
1191
1192             vlib_cli_output (vm, "%v", s);
1193             vec_reset_length (s);
1194             vec_free (str);
1195
1196             f = f->next;
1197           }
1198         vec_free (s);
1199       }
1200   }
1201   /* *INDENT-ON* */
1202
1203   clib_rwlock_reader_unlock (&sm->segments_rwlock);
1204 }
1205
1206 void
1207 segment_manager_set_watermarks (segment_manager_t * sm,
1208                                 u8 high_watermark, u8 low_watermark)
1209 {
1210   ASSERT (high_watermark <= 100 && low_watermark <= 100 &&
1211           low_watermark <= high_watermark);
1212
1213   sm->high_watermark = high_watermark;
1214   sm->low_watermark = low_watermark;
1215 }
1216
1217 /*
1218  * fd.io coding-style-patch-verification: ON
1219  *
1220  * Local Variables:
1221  * eval: (c-set-style "gnu")
1222  * End:
1223  */