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 * Pool of segment manager properties
33 static segment_manager_properties_t *segment_manager_properties_pool;
36 * Process private segment index
38 u32 *private_segment_indices;
41 * Default fifo and segment size. TODO config.
43 u32 default_fifo_size = 1 << 16;
44 u32 default_segment_size = 1 << 20;
46 segment_manager_properties_t *
47 segment_manager_properties_alloc (void)
49 segment_manager_properties_t *props;
50 pool_get (segment_manager_properties_pool, props);
51 memset (props, 0, sizeof (*props));
52 props->add_segment_size = default_segment_size;
53 props->rx_fifo_size = default_fifo_size;
54 props->tx_fifo_size = default_fifo_size;
59 segment_manager_properties_free (segment_manager_properties_t * props)
61 pool_put (segment_manager_properties_pool, props);
62 memset (props, 0xFB, sizeof (*props));
65 segment_manager_properties_t *
66 segment_manager_properties_get (u32 smp_index)
68 if (pool_is_free_index (segment_manager_properties_pool, smp_index))
70 return pool_elt_at_index (segment_manager_properties_pool, smp_index);
74 segment_manager_properties_index (segment_manager_properties_t * p)
76 return p - segment_manager_properties_pool;
79 svm_fifo_segment_private_t *
80 segment_manager_get_segment (u32 segment_index)
82 return svm_fifo_segment_get_segment (segment_index);
86 segment_manager_get_segment_info (u32 index, u8 ** name, u32 * size)
88 svm_fifo_segment_private_t *s;
89 s = svm_fifo_segment_get_segment (index);
91 *size = s->ssvm.ssvm_size;
95 segment_manager_add_segment_i (segment_manager_t * sm, u32 segment_size,
98 svm_fifo_segment_create_args_t _ca = { 0 }, *ca = &_ca;
99 segment_manager_properties_t *props;
101 props = segment_manager_properties_get (sm->properties_index);
103 /* Not configured for addition of new segments and not first */
104 if (!props->add_segment && !segment_size)
106 clib_warning ("cannot allocate new segment");
107 return VNET_API_ERROR_INVALID_VALUE;
110 ca->segment_size = segment_size ? segment_size : props->add_segment_size;
111 ca->rx_fifo_size = props->rx_fifo_size;
112 ca->tx_fifo_size = props->tx_fifo_size;
113 ca->preallocated_fifo_pairs = props->preallocated_fifo_pairs;
114 ca->seg_protected_space = protected_space ? protected_space : 0;
116 if (props->segment_type != SSVM_N_SEGMENT_TYPES)
118 ca->segment_name = (char *) format (0, "%d-%d%c", getpid (),
119 segment_name_counter++, 0);
120 ca->segment_type = props->segment_type;
121 if (svm_fifo_segment_create (ca))
123 clib_warning ("svm_fifo_segment_create ('%s', %d) failed",
124 ca->segment_name, ca->segment_size);
125 return VNET_API_ERROR_SVM_SEGMENT_CREATE_FAIL;
127 vec_free (ca->segment_name);
131 u32 rx_fifo_size, tx_fifo_size;
132 u32 rx_rounded_data_size, tx_rounded_data_size;
133 u32 approx_segment_count;
134 u64 approx_total_size;
136 ca->segment_name = "process-private-segment";
137 ca->private_segment_count = props->private_segment_count;
139 /* Calculate space requirements */
140 rx_rounded_data_size = (1 << (max_log2 (ca->rx_fifo_size)));
141 tx_rounded_data_size = (1 << (max_log2 (ca->tx_fifo_size)));
143 rx_fifo_size = sizeof (svm_fifo_t) + rx_rounded_data_size;
144 tx_fifo_size = sizeof (svm_fifo_t) + tx_rounded_data_size;
146 approx_total_size = (u64) ca->preallocated_fifo_pairs
147 * (rx_fifo_size + tx_fifo_size);
148 approx_segment_count = (approx_total_size + protected_space
149 + (ca->segment_size -
150 1)) / (u64) ca->segment_size;
152 /* The user asked us to figure it out... */
153 if (ca->private_segment_count == 0
154 || approx_segment_count < ca->private_segment_count)
156 ca->private_segment_count = approx_segment_count;
158 /* Follow directions, but issue a warning */
159 else if (approx_segment_count < ca->private_segment_count)
161 clib_warning ("Honoring segment count %u, calculated count was %u",
162 ca->private_segment_count, approx_segment_count);
164 else if (approx_segment_count > ca->private_segment_count)
166 clib_warning ("Segment count too low %u, calculated %u.",
167 ca->private_segment_count, approx_segment_count);
168 return VNET_API_ERROR_INVALID_VALUE;
171 if (svm_fifo_segment_create_process_private (ca))
172 clib_warning ("Failed to create process private segment");
174 ASSERT (vec_len (ca->new_segment_indices));
177 vec_append (sm->segment_indices, ca->new_segment_indices);
178 vec_free (ca->new_segment_indices);
183 segment_manager_add_segment (segment_manager_t * sm)
185 return segment_manager_add_segment_i (sm, 0, 0);
189 segment_manager_new ()
191 segment_manager_t *sm;
192 pool_get (segment_managers, sm);
193 memset (sm, 0, sizeof (*sm));
198 * Initializes segment manager based on options provided.
199 * Returns error if svm segment allocation fails.
202 segment_manager_init (segment_manager_t * sm, u32 props_index,
203 u32 first_seg_size, u32 evt_q_size)
208 sm->properties_index = props_index;
210 protected_space = max_pow2 (sizeof (svm_queue_t)
211 + evt_q_size * sizeof (session_fifo_event_t));
212 protected_space = round_pow2_u64 (protected_space, CLIB_CACHE_LINE_BYTES);
213 first_seg_size = first_seg_size > 0 ? first_seg_size : default_segment_size;
214 rv = segment_manager_add_segment_i (sm, first_seg_size, protected_space);
217 clib_warning ("Failed to allocate segment");
221 clib_spinlock_init (&sm->lockp);
226 segment_manager_has_fifos (segment_manager_t * sm)
228 svm_fifo_segment_private_t *segment;
231 for (i = 0; i < vec_len (sm->segment_indices); i++)
233 segment = svm_fifo_segment_get_segment (sm->segment_indices[i]);
234 if (CLIB_DEBUG && i && !svm_fifo_segment_has_fifos (segment)
235 && !(segment->h->flags & FIFO_SEGMENT_F_IS_PREALLOCATED))
236 clib_warning ("segment %d has no fifos!", sm->segment_indices[i]);
237 if (svm_fifo_segment_has_fifos (segment))
244 segment_manager_app_detached (segment_manager_t * sm)
246 return (sm->app_index == SEGMENT_MANAGER_INVALID_APP_INDEX);
250 segment_manager_app_detach (segment_manager_t * sm)
252 sm->app_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
256 segment_manager_del_segment (segment_manager_t * sm, u32 segment_index)
258 svm_fifo_segment_private_t *fifo_segment;
259 u32 svm_segment_index;
260 clib_spinlock_lock (&sm->lockp);
261 svm_segment_index = vec_elt (sm->segment_indices, segment_index);
262 fifo_segment = svm_fifo_segment_get_segment (svm_segment_index);
264 || ((fifo_segment->h->flags & FIFO_SEGMENT_F_IS_PREALLOCATED)
265 && !segment_manager_app_detached (sm)))
267 clib_spinlock_unlock (&sm->lockp);
270 svm_fifo_segment_delete (fifo_segment);
271 vec_del1 (sm->segment_indices, segment_index);
272 clib_spinlock_unlock (&sm->lockp);
276 * Initiate disconnects for all sessions 'owned' by a segment manager
279 segment_manager_del_sessions (segment_manager_t * sm)
282 svm_fifo_segment_private_t *fifo_segment;
285 ASSERT (vec_len (sm->segment_indices));
287 /* Across all fifo segments used by the server */
288 for (j = 0; j < vec_len (sm->segment_indices); j++)
290 fifo_segment = svm_fifo_segment_get_segment (sm->segment_indices[j]);
291 fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
294 * Remove any residual sessions from the session lookup table
295 * Don't bother deleting the individual fifos, we're going to
296 * throw away the fifo segment in a minute.
300 u32 session_index, thread_index;
301 stream_session_t *session;
303 session_index = fifo->master_session_index;
304 thread_index = fifo->master_thread_index;
305 session = session_get (session_index, thread_index);
307 /* Instead of directly removing the session call disconnect */
308 if (session->session_state != SESSION_STATE_CLOSED)
309 stream_session_disconnect (session);
313 /* Instead of removing the segment, test when cleaning up disconnected
314 * sessions if the segment can be removed.
320 * Removes segment manager.
322 * Since the fifos allocated in the segment keep backpointers to the sessions
323 * prior to removing the segment, we call session disconnect. This
324 * subsequently propagates into transport.
327 segment_manager_del (segment_manager_t * sm)
331 ASSERT (!segment_manager_has_fifos (sm)
332 && segment_manager_app_detached (sm));
334 /* If we have empty preallocated segments that haven't been removed, remove
335 * them now. Apart from that, the first segment in the first segment manager
336 * is not removed when all fifos are removed. It can only be removed when
337 * the manager is explicitly deleted/detached by the app. */
338 for (i = vec_len (sm->segment_indices) - 1; i >= 0; i--)
342 svm_fifo_segment_private_t *segment;
343 segment = svm_fifo_segment_get_segment (sm->segment_indices[i]);
344 ASSERT (!svm_fifo_segment_has_fifos (segment));
346 segment_manager_del_segment (sm, i);
348 clib_spinlock_free (&sm->lockp);
350 memset (sm, 0xfe, sizeof (*sm));
351 pool_put (segment_managers, sm);
355 segment_manager_init_del (segment_manager_t * sm)
357 segment_manager_app_detach (sm);
358 if (segment_manager_has_fifos (sm))
359 segment_manager_del_sessions (sm);
362 ASSERT (!sm->first_is_protected || segment_manager_app_detached (sm));
363 segment_manager_del (sm);
368 segment_manager_alloc_session_fifos (segment_manager_t * sm,
369 svm_fifo_t ** rx_fifo,
370 svm_fifo_t ** tx_fifo,
371 u32 * fifo_segment_index)
373 svm_fifo_segment_private_t *fifo_segment;
374 segment_manager_properties_t *props;
375 u32 fifo_size, sm_index;
376 u8 added_a_segment = 0;
379 ASSERT (vec_len (sm->segment_indices));
381 /* Make sure we don't have multiple threads trying to allocate segments
382 * at the same time. */
383 clib_spinlock_lock (&sm->lockp);
385 /* Allocate svm fifos */
386 props = segment_manager_properties_get (sm->properties_index);
388 for (i = 0; i < vec_len (sm->segment_indices); i++)
390 *fifo_segment_index = vec_elt (sm->segment_indices, i);
391 fifo_segment = svm_fifo_segment_get_segment (*fifo_segment_index);
393 fifo_size = props->rx_fifo_size;
394 fifo_size = (fifo_size == 0) ? default_fifo_size : fifo_size;
395 *rx_fifo = svm_fifo_segment_alloc_fifo (fifo_segment, fifo_size,
396 FIFO_SEGMENT_RX_FREELIST);
398 fifo_size = props->tx_fifo_size;
399 fifo_size = (fifo_size == 0) ? default_fifo_size : fifo_size;
400 *tx_fifo = svm_fifo_segment_alloc_fifo (fifo_segment, fifo_size,
401 FIFO_SEGMENT_TX_FREELIST);
405 /* This would be very odd, but handle it... */
408 svm_fifo_segment_free_fifo (fifo_segment, *tx_fifo,
409 FIFO_SEGMENT_TX_FREELIST);
418 svm_fifo_segment_free_fifo (fifo_segment, *rx_fifo,
419 FIFO_SEGMENT_RX_FREELIST);
427 /* See if we're supposed to create another segment */
430 if (props->add_segment && !props->segment_type)
434 clib_warning ("added a segment, still can't allocate a fifo");
435 clib_spinlock_unlock (&sm->lockp);
436 return SESSION_ERROR_NEW_SEG_NO_SPACE;
439 if (segment_manager_add_segment (sm))
441 clib_spinlock_unlock (&sm->lockp);
442 return VNET_API_ERROR_URI_FIFO_CREATE_FAILED;
450 clib_warning ("No space to allocate fifos!");
451 clib_spinlock_unlock (&sm->lockp);
452 return SESSION_ERROR_NO_SPACE;
456 /* Backpointers to segment manager */
457 sm_index = segment_manager_index (sm);
458 (*tx_fifo)->segment_manager = sm_index;
459 (*rx_fifo)->segment_manager = sm_index;
461 clib_spinlock_unlock (&sm->lockp);
464 return application_add_segment_notify (sm->app_index,
465 *fifo_segment_index);
471 segment_manager_dealloc_fifos (u32 svm_segment_index, svm_fifo_t * rx_fifo,
472 svm_fifo_t * tx_fifo)
474 segment_manager_t *sm;
475 svm_fifo_segment_private_t *fifo_segment;
476 u32 i, segment_index = ~0;
479 sm = segment_manager_get_if_valid (rx_fifo->segment_manager);
481 /* It's possible to have no segment manager if the session was removed
482 * as result of a detach. */
486 fifo_segment = svm_fifo_segment_get_segment (svm_segment_index);
487 svm_fifo_segment_free_fifo (fifo_segment, rx_fifo,
488 FIFO_SEGMENT_RX_FREELIST);
489 svm_fifo_segment_free_fifo (fifo_segment, tx_fifo,
490 FIFO_SEGMENT_TX_FREELIST);
493 * Try to remove svm segment if it has no fifos. This can be done only if
494 * the segment is not the first in the segment manager or if it is first
495 * and it is not protected. Moreover, if the segment is first and the app
496 * has detached from the segment manager, remove the segment manager.
498 if (!svm_fifo_segment_has_fifos (fifo_segment))
500 is_first = sm->segment_indices[0] == svm_segment_index;
502 /* Remove segment if it holds no fifos or first but not protected */
503 if (!is_first || !sm->first_is_protected)
505 /* Find the segment manager segment index */
506 for (i = 0; i < vec_len (sm->segment_indices); i++)
507 if (sm->segment_indices[i] == svm_segment_index)
512 ASSERT (segment_index != (u32) ~ 0);
513 segment_manager_del_segment (sm, segment_index);
516 /* Remove segment manager if no sessions and detached from app */
517 if (segment_manager_app_detached (sm)
518 && !segment_manager_has_fifos (sm))
519 segment_manager_del (sm);
524 * Allocates shm queue in the first segment
527 segment_manager_alloc_queue (segment_manager_t * sm, u32 queue_size)
529 ssvm_shared_header_t *sh;
530 svm_fifo_segment_private_t *segment;
534 ASSERT (sm->segment_indices != 0);
536 segment = svm_fifo_segment_get_segment (sm->segment_indices[0]);
537 sh = segment->ssvm.sh;
539 oldheap = ssvm_push_heap (sh);
540 q = svm_queue_init (queue_size,
541 sizeof (session_fifo_event_t), 0 /* consumer pid */ ,
542 0 /* signal when queue non-empty */ );
543 ssvm_pop_heap (oldheap);
548 * Frees shm queue allocated in the first segment
551 segment_manager_dealloc_queue (segment_manager_t * sm, svm_queue_t * q)
553 ssvm_shared_header_t *sh;
554 svm_fifo_segment_private_t *segment;
557 ASSERT (sm->segment_indices != 0);
559 segment = svm_fifo_segment_get_segment (sm->segment_indices[0]);
560 sh = segment->ssvm.sh;
562 oldheap = ssvm_push_heap (sh);
564 ssvm_pop_heap (oldheap);
567 static clib_error_t *
568 segment_manager_show_fn (vlib_main_t * vm, unformat_input_t * input,
569 vlib_cli_command_t * cmd)
571 svm_fifo_segment_private_t *segments, *seg;
572 segment_manager_t *sm;
573 u8 show_segments = 0, verbose = 0;
579 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
581 if (unformat (input, "segments"))
583 else if (unformat (input, "verbose"))
586 return clib_error_return (0, "unknown input `%U'",
587 format_unformat_error, input);
589 vlib_cli_output (vm, "%d segment managers allocated",
590 pool_elts (segment_managers));
591 if (verbose && pool_elts (segment_managers))
593 vlib_cli_output (vm, "%-10s%=15s%=12s", "Index", "App Index",
597 pool_foreach (sm, segment_managers, ({
598 vlib_cli_output (vm, "%-10d%=15d%=12d", segment_manager_index(sm),
599 sm->app_index, vec_len (sm->segment_indices));
606 segments = svm_fifo_segment_segments_pool ();
607 vlib_cli_output (vm, "%d svm fifo segments allocated",
608 pool_elts (segments));
609 vlib_cli_output (vm, "%-15s%15s%15s%15s%15s%15s", "Name", "Type",
610 "HeapSize (M)", "ActiveFifos", "FreeFifos", "Address");
613 pool_foreach (seg, segments, ({
614 svm_fifo_segment_info (seg, &address, &size);
615 active_fifos = svm_fifo_segment_num_fifos (seg);
616 free_fifos = svm_fifo_segment_num_free_fifos (seg, ~0 /* size */);
617 vlib_cli_output (vm, "%-15v%15U%15llu%15u%15u%15llx",
618 ssvm_name (&seg->ssvm), format_svm_fifo_segment_type,
619 seg, size >> 20ULL, active_fifos, free_fifos,
622 vlib_cli_output (vm, "%U", format_svm_fifo_segment, seg, verbose);
631 VLIB_CLI_COMMAND (segment_manager_show_command, static) =
633 .path = "show segment-manager",
634 .short_help = "show segment-manager [segments][verbose]",
635 .function = segment_manager_show_fn,
640 * fd.io coding-style-patch-verification: ON
643 * eval: (c-set-style "gnu")