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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <vnet/session/segment_manager.h>
17 #include <vnet/session/session.h>
18 #include <vnet/session/application.h>
21 * Counter used to build segment names
23 u32 segment_name_counter = 0;
26 * Pool of segment managers
28 segment_manager_t *segment_managers = 0;
31 * Process private segment index
33 u32 *private_segment_indices;
36 * Default fifo and segment size. TODO config.
38 u32 default_fifo_size = 1 << 16;
39 u32 default_segment_size = 1 << 20;
42 segment_manager_get_segment_info (u32 index, u8 ** name, u32 * size)
44 svm_fifo_segment_private_t *s;
45 s = svm_fifo_segment_get_segment (index);
46 *name = s->h->segment_name;
47 *size = s->ssvm.ssvm_size;
51 session_manager_add_segment_i (segment_manager_t * sm, u32 segment_size,
54 svm_fifo_segment_create_args_t _ca, *ca = &_ca;
57 memset (ca, 0, sizeof (*ca));
59 if (!sm->properties->use_private_segment)
61 ca->segment_name = (char *) segment_name;
62 ca->segment_size = segment_size;
63 ca->rx_fifo_size = sm->properties->rx_fifo_size;
64 ca->tx_fifo_size = sm->properties->tx_fifo_size;
65 ca->preallocated_fifo_pairs = sm->properties->preallocated_fifo_pairs;
67 rv = svm_fifo_segment_create (ca);
70 clib_warning ("svm_fifo_segment_create ('%s', %d) failed",
71 ca->segment_name, ca->segment_size);
72 return VNET_API_ERROR_SVM_SEGMENT_CREATE_FAIL;
77 ca->segment_name = "process-private-segment";
78 ca->segment_size = ~0;
79 ca->rx_fifo_size = sm->properties->rx_fifo_size;
80 ca->tx_fifo_size = sm->properties->tx_fifo_size;
81 ca->preallocated_fifo_pairs = sm->properties->preallocated_fifo_pairs;
82 ca->private_segment_count = sm->properties->private_segment_count;
83 ca->private_segment_size = sm->properties->private_segment_size;
85 if (svm_fifo_segment_create_process_private (ca))
86 clib_warning ("Failed to create process private segment");
88 ASSERT (vec_len (ca->new_segment_indices));
90 vec_append (sm->segment_indices, ca->new_segment_indices);
91 vec_free (ca->new_segment_indices);
96 session_manager_add_segment (segment_manager_t * sm)
99 svm_fifo_segment_create_args_t _ca, *ca = &_ca;
100 u32 add_segment_size;
103 memset (ca, 0, sizeof (*ca));
104 segment_name = format (0, "%d-%d%c", getpid (), segment_name_counter++, 0);
105 add_segment_size = sm->properties->add_segment_size ?
106 sm->properties->add_segment_size : default_segment_size;
108 rv = session_manager_add_segment_i (sm, add_segment_size, segment_name);
109 vec_free (segment_name);
114 session_manager_add_first_segment (segment_manager_t * sm, u32 segment_size)
119 segment_name = format (0, "%d-%d%c", getpid (), segment_name_counter++, 0);
120 rv = session_manager_add_segment_i (sm, segment_size, segment_name);
121 vec_free (segment_name);
126 segment_manager_new ()
128 segment_manager_t *sm;
129 pool_get (segment_managers, sm);
130 memset (sm, 0, sizeof (*sm));
135 * Initializes segment manager based on options provided.
136 * Returns error if svm segment allocation fails.
139 segment_manager_init (segment_manager_t * sm,
140 segment_manager_properties_t * properties,
145 /* app allocates these */
146 sm->properties = properties;
148 first_seg_size = first_seg_size > 0 ? first_seg_size : default_segment_size;
150 rv = session_manager_add_first_segment (sm, first_seg_size);
153 clib_warning ("Failed to allocate segment");
157 clib_spinlock_init (&sm->lockp);
162 segment_manager_has_fifos (segment_manager_t * sm)
164 svm_fifo_segment_private_t *segment;
165 /* Weird, but handle it */
166 if (vec_len (sm->segment_indices) == 0)
168 if (vec_len (sm->segment_indices) == 1)
170 segment = svm_fifo_segment_get_segment (sm->segment_indices[0]);
171 if (svm_fifo_segment_num_fifos (segment) == 0)
176 svm_fifo_segment_private_t *segment;
178 for (i = 1; i < vec_len (sm->segment_indices); i++)
180 segment = svm_fifo_segment_get_segment (sm->segment_indices[i]);
181 if (!svm_fifo_segment_has_fifos (segment))
182 clib_warning ("segment has no fifos!");
189 segment_manager_del_segment (segment_manager_t * sm, u32 segment_index)
191 svm_fifo_segment_private_t *fifo_segment;
192 u32 svm_segment_index;
193 clib_spinlock_lock (&sm->lockp);
194 svm_segment_index = sm->segment_indices[segment_index];
195 fifo_segment = svm_fifo_segment_get_segment (svm_segment_index);
196 svm_fifo_segment_delete (fifo_segment);
197 vec_del1 (sm->segment_indices, segment_index);
198 clib_spinlock_unlock (&sm->lockp);
202 * Initiate disconnects for all sessions 'owned' by a segment manager
205 segment_manager_del_sessions (segment_manager_t * sm)
208 svm_fifo_segment_private_t *fifo_segment;
211 ASSERT (vec_len (sm->segment_indices));
213 /* Across all fifo segments used by the server */
214 for (j = 0; j < vec_len (sm->segment_indices); j++)
216 fifo_segment = svm_fifo_segment_get_segment (sm->segment_indices[j]);
217 fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
220 * Remove any residual sessions from the session lookup table
221 * Don't bother deleting the individual fifos, we're going to
222 * throw away the fifo segment in a minute.
226 u32 session_index, thread_index;
227 stream_session_t *session;
229 session_index = fifo->master_session_index;
230 thread_index = fifo->master_thread_index;
231 session = stream_session_get (session_index, thread_index);
233 /* Instead of directly removing the session call disconnect */
234 if (session->session_state != SESSION_STATE_CLOSED)
236 session->session_state = SESSION_STATE_CLOSED;
237 session_send_session_evt_to_thread (stream_session_handle
239 FIFO_EVENT_DISCONNECT,
245 /* Instead of removing the segment, test when cleaning up disconnected
246 * sessions if the segment can be removed.
252 * Removes segment manager.
254 * Since the fifos allocated in the segment keep backpointers to the sessions
255 * prior to removing the segment, we call session disconnect. This
256 * subsequently propages into transport.
259 segment_manager_del (segment_manager_t * sm)
262 ASSERT (vec_len (sm->segment_indices) <= 1);
263 if (vec_len (sm->segment_indices))
265 /* The first segment in the first segment manager is not removed when
266 * all fifos are removed. It can only be removed when the manager is
267 * explicitly deleted/detached by the app. */
270 svm_fifo_segment_private_t *fifo_segment;
272 svm_fifo_segment_get_segment (sm->segment_indices[0]);
273 ASSERT (!svm_fifo_segment_has_fifos (fifo_segment));
275 segment_manager_del_segment (sm, 0);
277 clib_spinlock_free (&sm->lockp);
279 memset (sm, 0xfe, sizeof (*sm));
280 pool_put (segment_managers, sm);
284 segment_manager_init_del (segment_manager_t * sm)
286 if (segment_manager_has_fifos (sm))
287 segment_manager_del_sessions (sm);
290 ASSERT (!sm->first_is_protected
291 || sm->app_index == SEGMENT_MANAGER_INVALID_APP_INDEX);
292 segment_manager_del (sm);
297 segment_manager_alloc_session_fifos (segment_manager_t * sm,
298 svm_fifo_t ** server_rx_fifo,
299 svm_fifo_t ** server_tx_fifo,
300 u32 * fifo_segment_index)
302 svm_fifo_segment_private_t *fifo_segment;
303 u32 fifo_size, sm_index;
304 u8 added_a_segment = 0;
307 ASSERT (vec_len (sm->segment_indices));
309 /* Make sure we don't have multiple threads trying to allocate segments
310 * at the same time. */
311 clib_spinlock_lock (&sm->lockp);
313 /* Allocate svm fifos */
315 for (i = 0; i < vec_len (sm->segment_indices); i++)
317 *fifo_segment_index = sm->segment_indices[i];
318 fifo_segment = svm_fifo_segment_get_segment (*fifo_segment_index);
320 fifo_size = sm->properties->rx_fifo_size;
321 fifo_size = (fifo_size == 0) ? default_fifo_size : fifo_size;
323 svm_fifo_segment_alloc_fifo (fifo_segment, fifo_size,
324 FIFO_SEGMENT_RX_FREELIST);
326 fifo_size = sm->properties->tx_fifo_size;
327 fifo_size = (fifo_size == 0) ? default_fifo_size : fifo_size;
329 svm_fifo_segment_alloc_fifo (fifo_segment, fifo_size,
330 FIFO_SEGMENT_TX_FREELIST);
332 if (*server_rx_fifo == 0)
334 /* This would be very odd, but handle it... */
335 if (*server_tx_fifo != 0)
337 svm_fifo_segment_free_fifo (fifo_segment, *server_tx_fifo,
338 FIFO_SEGMENT_TX_FREELIST);
343 if (*server_tx_fifo == 0)
345 if (*server_rx_fifo != 0)
347 svm_fifo_segment_free_fifo (fifo_segment, *server_rx_fifo,
348 FIFO_SEGMENT_RX_FREELIST);
356 /* See if we're supposed to create another segment */
357 if (*server_rx_fifo == 0)
359 if (sm->properties->add_segment && !sm->properties->use_private_segment)
363 clib_warning ("added a segment, still can't allocate a fifo");
364 clib_spinlock_unlock (&sm->lockp);
365 return SESSION_ERROR_NEW_SEG_NO_SPACE;
368 if (session_manager_add_segment (sm))
370 clib_spinlock_unlock (&sm->lockp);
371 return VNET_API_ERROR_URI_FIFO_CREATE_FAILED;
379 clib_warning ("No space to allocate fifos!");
380 clib_spinlock_unlock (&sm->lockp);
381 return SESSION_ERROR_NO_SPACE;
385 /* Backpointers to segment manager */
386 sm_index = segment_manager_index (sm);
387 (*server_tx_fifo)->segment_manager = sm_index;
388 (*server_rx_fifo)->segment_manager = sm_index;
390 clib_spinlock_unlock (&sm->lockp);
393 return application_add_segment_notify (sm->app_index,
394 *fifo_segment_index);
400 segment_manager_dealloc_fifos (u32 svm_segment_index, svm_fifo_t * rx_fifo,
401 svm_fifo_t * tx_fifo)
403 segment_manager_t *sm;
404 svm_fifo_segment_private_t *fifo_segment;
405 u32 i, segment_index = ~0;
408 sm = segment_manager_get_if_valid (rx_fifo->segment_manager);
410 /* It's possible to have no segment manager if the session was removed
411 * as result of a detach. */
415 fifo_segment = svm_fifo_segment_get_segment (svm_segment_index);
416 svm_fifo_segment_free_fifo (fifo_segment, rx_fifo,
417 FIFO_SEGMENT_RX_FREELIST);
418 svm_fifo_segment_free_fifo (fifo_segment, tx_fifo,
419 FIFO_SEGMENT_TX_FREELIST);
422 * Try to remove svm segment if it has no fifos. This can be done only if
423 * the segment is not the first in the segment manager or if it is first
424 * and it is not protected. Moreover, if the segment is first and the app
425 * has detached from the segment manager, remove the segment manager.
427 if (!svm_fifo_segment_has_fifos (fifo_segment))
429 is_first = sm->segment_indices[0] == svm_segment_index;
431 /* Remove segment if it holds no fifos or first but not protected */
432 if (!is_first || !sm->first_is_protected)
434 /* Find the segment manager segment index */
435 for (i = 0; i < vec_len (sm->segment_indices); i++)
436 if (sm->segment_indices[i] == svm_segment_index)
441 ASSERT (segment_index != (u32) ~ 0);
442 segment_manager_del_segment (sm, segment_index);
445 /* Remove segment manager if no sessions and detached from app */
446 if (sm->app_index == SEGMENT_MANAGER_INVALID_APP_INDEX && is_first)
447 segment_manager_del (sm);
452 * Allocates shm queue in the first segment
454 unix_shared_memory_queue_t *
455 segment_manager_alloc_queue (segment_manager_t * sm, u32 queue_size)
457 ssvm_shared_header_t *sh;
458 svm_fifo_segment_private_t *segment;
459 unix_shared_memory_queue_t *q;
462 ASSERT (sm->segment_indices != 0);
464 segment = svm_fifo_segment_get_segment (sm->segment_indices[0]);
465 sh = segment->ssvm.sh;
467 oldheap = ssvm_push_heap (sh);
468 q = unix_shared_memory_queue_init (queue_size,
469 sizeof (session_fifo_event_t),
470 0 /* consumer pid */ ,
471 0 /* signal when queue non-empty */ );
472 ssvm_pop_heap (oldheap);
477 * Frees shm queue allocated in the first segment
480 segment_manager_dealloc_queue (segment_manager_t * sm,
481 unix_shared_memory_queue_t * q)
483 ssvm_shared_header_t *sh;
484 svm_fifo_segment_private_t *segment;
487 ASSERT (sm->segment_indices != 0);
489 segment = svm_fifo_segment_get_segment (sm->segment_indices[0]);
490 sh = segment->ssvm.sh;
492 oldheap = ssvm_push_heap (sh);
493 unix_shared_memory_queue_free (q);
494 ssvm_pop_heap (oldheap);
497 static clib_error_t *
498 segment_manager_show_fn (vlib_main_t * vm, unformat_input_t * input,
499 vlib_cli_command_t * cmd)
501 svm_fifo_segment_private_t *segments, *seg;
502 segment_manager_t *sm;
503 u8 show_segments = 0, verbose = 0, *name;
507 mheap_t *heap_header;
509 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
511 if (unformat (input, "segments"))
513 else if (unformat (input, "verbose"))
516 return clib_error_return (0, "unknown input `%U'",
517 format_unformat_error, input);
519 vlib_cli_output (vm, "%d segment managers allocated",
520 pool_elts (segment_managers));
521 if (verbose && pool_elts (segment_managers))
523 vlib_cli_output (vm, "%-10s%=15s%=12s", "Index", "App Index",
527 pool_foreach (sm, segment_managers, ({
528 vlib_cli_output (vm, "%-10d%=15d%=12d", segment_manager_index(sm),
529 sm->app_index, vec_len (sm->segment_indices));
536 segments = svm_fifo_segment_segments_pool ();
537 vlib_cli_output (vm, "%d svm fifo segments allocated",
538 pool_elts (segments));
539 vlib_cli_output (vm, "%-20s%=12s%=12s%=15s", "Name", "Size (M)",
543 pool_foreach (seg, segments, ({
544 if (seg->h->flags & FIFO_SEGMENT_F_IS_PRIVATE)
546 address = pointer_to_uword (seg->ssvm.sh->heap);
547 if (seg->h->flags & FIFO_SEGMENT_F_IS_MAIN_HEAP)
548 name = format (0, "main heap");
550 name = format (0, "private heap");
551 heap_header = mheap_header (seg->ssvm.sh->heap);
552 size = heap_header->max_size;
556 address = seg->ssvm.sh->ssvm_va;
557 size = seg->ssvm.ssvm_size;
558 name = seg->ssvm.sh->name;
560 fifos = svm_fifo_segment_num_fifos (seg);
561 vlib_cli_output (vm, "%-20s%=12u%=12u%=15x", name, size << 20, fifos,
563 if (seg->h->flags & FIFO_SEGMENT_F_IS_PRIVATE)
573 VLIB_CLI_COMMAND (segment_manager_show_command, static) =
575 .path = "show segment-manager",
576 .short_help = "show segment-manager [segments]",
577 .function = segment_manager_show_fn,
582 * fd.io coding-style-patch-verification: ON
585 * eval: (c-set-style "gnu")