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