diff options
Diffstat (limited to 'src/vlibapi')
-rw-r--r-- | src/vlibapi/CMakeLists.txt | 21 | ||||
-rw-r--r-- | src/vlibapi/api.h | 33 | ||||
-rw-r--r-- | src/vlibapi/api_common.h | 119 | ||||
-rw-r--r-- | src/vlibapi/api_doc.rst | 1 | ||||
-rw-r--r-- | src/vlibapi/api_format.c | 39 | ||||
-rw-r--r-- | src/vlibapi/api_helper_macros.h | 589 | ||||
-rw-r--r-- | src/vlibapi/api_shared.c | 431 | ||||
-rw-r--r-- | src/vlibapi/memory_shared.c | 815 | ||||
-rw-r--r-- | src/vlibapi/memory_shared.h | 147 | ||||
-rw-r--r-- | src/vlibapi/node_serialize.c | 4 |
10 files changed, 1674 insertions, 525 deletions
diff --git a/src/vlibapi/CMakeLists.txt b/src/vlibapi/CMakeLists.txt index e6937a6db01..6476b5a2f33 100644 --- a/src/vlibapi/CMakeLists.txt +++ b/src/vlibapi/CMakeLists.txt @@ -11,16 +11,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -install( - FILES - api_helper_macros.h +add_vpp_library (vlibapi + SOURCES + api_shared.c + api_format.c + node_serialize.c + memory_shared.c + + INSTALL_HEADERS api.h - vat_helper_macros.h api_common.h + api_helper_macros.h api_types.h - - DESTINATION - ${CMAKE_INSTALL_INCLUDEDIR}/vlibapi - - COMPONENT vpp-dev + vat_helper_macros.h + memory_shared.h ) + diff --git a/src/vlibapi/api.h b/src/vlibapi/api.h index d05395a213c..74957a6f0f6 100644 --- a/src/vlibapi/api.h +++ b/src/vlibapi/api.h @@ -28,13 +28,11 @@ #include <vlib/unix/unix.h> #include <vlibapi/api_common.h> -/* *INDENT-OFF* */ typedef CLIB_PACKED ( struct { u32 nitems; u32 msgtbl_size; u8 wrapped; }) vl_api_trace_file_header_t; -/* *INDENT-ON* */ int vl_msg_api_trace_save (api_main_t *am, vl_api_trace_which_t which, FILE *fp, u8 is_json); @@ -105,10 +103,6 @@ int vl_msg_api_trace_onoff (api_main_t * am, vl_api_trace_which_t which, int vl_msg_api_trace_free (api_main_t * am, vl_api_trace_which_t which); int vl_msg_api_trace_configure (api_main_t * am, vl_api_trace_which_t which, u32 nitems); -void vl_msg_api_handler_with_vm_node (api_main_t * am, svm_region_t * vlib_rp, - void *the_msg, vlib_main_t * vm, - vlib_node_runtime_t * node, - u8 is_private); u32 vl_msg_api_max_length (void *mp); vl_api_trace_t *vl_msg_api_trace_get (api_main_t * am, vl_api_trace_which_t which); @@ -128,6 +122,33 @@ typedef int (*vl_msg_traverse_trace_fn) (u8 *, void *); int vl_msg_traverse_trace (vl_api_trace_t *tp, vl_msg_traverse_trace_fn fn, void *ctx); +always_inline void +vl_api_increase_msg_trace_size (api_main_t *am, u32 msg_id, u32 inc) +{ + am->msg_data[msg_id].trace_size += inc; +} + +always_inline void +vl_api_set_msg_thread_safe (api_main_t *am, u32 msg_id, int v) +{ + am->msg_data[msg_id].is_mp_safe = v != 0; +} + +always_inline void +vl_api_set_msg_autoendian (api_main_t *am, u32 msg_id, int v) +{ + am->msg_data[msg_id].is_autoendian = v != 0; +} + +always_inline void +vl_api_allow_msg_replay (api_main_t *am, u32 msg_id, int v) +{ + am->msg_data[msg_id].replay_allowed = v != 0; +} + +format_function_t format_vl_api_msg_text; +format_function_t format_vl_api_msg_json; + #endif /* included_api_h */ /* * fd.io coding-style-patch-verification: ON diff --git a/src/vlibapi/api_common.h b/src/vlibapi/api_common.h index 320e7c44e16..c09334136c0 100644 --- a/src/vlibapi/api_common.h +++ b/src/vlibapi/api_common.h @@ -75,18 +75,12 @@ typedef struct vl_api_registration_ /* socket client only */ u32 server_handle; /**< Socket client only: server handle */ u32 server_index; /**< Socket client only: server index */ + + bool keepalive; /**< Dead client scan */ } vl_api_registration_t; #define VL_API_INVALID_FI ((u32)~0) -/** Trace configuration for a single message */ -typedef struct -{ - int size; /**< for sanity checking */ - int trace_enable; /**< trace this message */ - int replay_enable; /**< This message can be replayed */ -} trace_cfg_t; - /** * API trace state */ @@ -128,16 +122,16 @@ typedef struct void *handler; /**< the message handler */ void *cleanup; /**< non-default message cleanup handler */ void *endian; /**< message endian function */ - void *print; /**< message print function */ - void *print_json; /**< message print function (JSON format) */ + void *format_fn; /**< message format function */ void *tojson; /**< binary to JSON convert function */ void *fromjson; /**< JSON to binary convert function */ + void *calc_size; /**< message size calculation */ int size; /**< message size */ - int traced; /**< is this message to be traced? */ - int replay; /**< is this message to be replayed? */ - int message_bounce; /**< do not free message after processing */ - int is_mp_safe; /**< worker thread barrier required? */ - int is_autoendian; /**< endian conversion required? */ + u32 traced : 1; /**< is this message to be traced? */ + u32 replay : 1; /**< is this message to be replayed? */ + u32 message_bounce : 1; /**< do not free message after processing */ + u32 is_mp_safe : 1; /**< worker thread barrier required? */ + u32 is_autoendian : 1; /**< endian conversion required? */ } vl_msg_api_msg_config_t; /** Message header structure */ @@ -149,38 +143,34 @@ typedef struct msgbuf_ u8 data[0]; /**< actual message begins here */ } msgbuf_t; -CLIB_NOSANITIZE_ADDR static inline void +__clib_nosanitize_addr static inline void VL_MSG_API_UNPOISON (const void *a) { const msgbuf_t *m = &((const msgbuf_t *) a)[-1]; - CLIB_MEM_UNPOISON (m, sizeof (*m) + ntohl (m->data_len)); + clib_mem_unpoison (m, sizeof (*m) + ntohl (m->data_len)); } -CLIB_NOSANITIZE_ADDR static inline void -VL_MSG_API_SVM_QUEUE_UNPOISON (const svm_queue_t * q) +__clib_nosanitize_addr static inline void +VL_MSG_API_SVM_QUEUE_UNPOISON (const svm_queue_t *q) { - CLIB_MEM_UNPOISON (q, sizeof (*q) + q->elsize * q->maxsize); + clib_mem_unpoison (q, sizeof (*q) + q->elsize * q->maxsize); } static inline void VL_MSG_API_POISON (const void *a) { const msgbuf_t *m = &((const msgbuf_t *) a)[-1]; - CLIB_MEM_POISON (m, sizeof (*m) + ntohl (m->data_len)); + clib_mem_poison (m, sizeof (*m) + ntohl (m->data_len)); } /* api_shared.c prototypes */ -void vl_msg_api_handler (void *the_msg); -void vl_msg_api_handler_no_free (void *the_msg); -void vl_msg_api_handler_no_trace_no_free (void *the_msg); -void vl_msg_api_trace_only (void *the_msg); +void vl_msg_api_handler (void *the_msg, uword msg_len); +void vl_msg_api_handler_no_free (void *the_msg, uword msg_len); +void vl_msg_api_handler_no_trace_no_free (void *the_msg, uword msg_len); +void vl_msg_api_trace_only (void *the_msg, uword msg_len); void vl_msg_api_cleanup_handler (void *the_msg); void vl_msg_api_replay_handler (void *the_msg); -void vl_msg_api_socket_handler (void *the_msg); -void vl_msg_api_set_handlers (int msg_id, char *msg_name, void *handler, - void *cleanup, void *endian, void *print, - int msg_size, int traced, void *print_json, - void *tojson, void *fromjson); +void vl_msg_api_socket_handler (void *the_msg, uword msg_len); void vl_msg_api_clean_handlers (int msg_id); void vl_msg_api_config (vl_msg_api_msg_config_t *); void vl_msg_api_set_cleanup_handler (int msg_id, void *fp); @@ -195,13 +185,11 @@ void vl_msg_api_barrier_trace_context (const char *context) #define vl_msg_api_barrier_trace_context(X) #endif void vl_msg_api_free (void *); -void vl_noop_handler (void *mp); void vl_msg_api_increment_missing_client_counter (void); void vl_msg_api_post_mortem_dump (void); void vl_msg_api_post_mortem_dump_enable_disable (int enable); void vl_msg_api_register_pd_handler (void *handler, u16 msg_id_host_byte_order); -int vl_msg_api_pd_handler (void *mp, int rv); void vl_msg_api_set_first_available_msg_id (u16 first_avail); u16 vl_msg_api_get_msg_ids (const char *name, int n); @@ -227,44 +215,51 @@ typedef struct char name[64]; } api_version_t; -/** API main structure, used by both vpp and binary API clients */ -typedef struct api_main_t +typedef struct { /** Message handler vector */ - void (**msg_handlers) (void *); + void (*handler) (void *); /** non-default message cleanup handler vector */ - void (**msg_cleanup_handlers) (void *); - - /** Message endian handler vector */ - void (**msg_endian_handlers) (void *); + void (*cleanup_handler) (void *); - /** Message print function vector */ - void (**msg_print_handlers) (void *, void *); + /** Message name vector */ + const char *name; - /** Message print function vector in JSON */ - void (**msg_print_json_handlers) (void *, void *); + /** Message format function */ + format_function_t *format_fn; /** Message convert function vector */ - cJSON *(**msg_tojson_handlers) (void *); + cJSON *(*tojson_handler) (void *); /** Message convert function vector */ - void *(**msg_fromjson_handlers) (cJSON *, int *); + void *(*fromjson_handler) (cJSON *, int *); - /** Message name vector */ - const char **msg_names; + /** Message endian handler vector. */ + void (*endian_handler) (void *, bool to_net); - /** API message ID by name hash table */ - uword *msg_id_by_name; + /** Message calc size function vector */ + uword (*calc_size_func) (void *); - /** Don't automatically free message buffer vetor */ - u8 *message_bounce; + /** trace size for sanity checking */ + int trace_size; - /** Message is mp safe vector */ - u8 *is_mp_safe; + /** Flags */ + u8 bounce : 1; /**> Don't automatically free message buffer vetor */ + u8 is_mp_safe : 1; /**< Message is mp safe vector */ + u8 is_autoendian : 1; /**< Message requires us to do endian conversion */ + u8 trace_enable : 1; /**< trace this message */ + u8 replay_allowed : 1; /**< This message can be replayed */ - /** Message requires us to do endian conversion */ - u8 *is_autoendian; +} vl_api_msg_data_t; + +/** API main structure, used by both vpp and binary API clients */ +typedef struct api_main_t +{ + vl_api_msg_data_t *msg_data; + + /** API message ID by name hash table */ + uword *msg_id_by_name; /** Allocator ring vectors (in shared memory) */ struct ring_alloc_ *arings; @@ -287,9 +282,6 @@ typedef struct api_main_t /** Print every received message */ int msg_print_flag; - /** Current trace configuration */ - trace_cfg_t *api_trace_cfg; - /** Current process PID */ int our_pid; @@ -362,6 +354,8 @@ typedef struct api_main_t /** client message index hash table */ uword *msg_index_by_name_and_crc; + /** plugin JSON representation vector table */ + u8 **json_api_repr; /** api version list */ api_version_t *api_version_list; @@ -397,7 +391,6 @@ typedef struct api_main_t } api_main_t; extern __thread api_main_t *my_api_main; -extern api_main_t api_global_main; always_inline api_main_t * vlibapi_get_main (void) @@ -405,6 +398,14 @@ vlibapi_get_main (void) return my_api_main; } +always_inline vl_api_msg_data_t * +vl_api_get_msg_data (api_main_t *am, u32 msg_id) +{ + if (msg_id >= vec_len (am->msg_data)) + return 0; + return am->msg_data + msg_id; +} + always_inline void vlibapi_set_main (api_main_t * am) { diff --git a/src/vlibapi/api_doc.rst b/src/vlibapi/api_doc.rst index 7d2b80a2e06..2131cc1919c 100644 --- a/src/vlibapi/api_doc.rst +++ b/src/vlibapi/api_doc.rst @@ -300,7 +300,6 @@ Set up message handlers about as follows: #undef vl_endianfun /* instantiate all the print functions we know about */ - #define vl_print(handle, ...) #define vl_printfun #include <vpp/api/vpe_all_api_h.h> #undef vl_printfun diff --git a/src/vlibapi/api_format.c b/src/vlibapi/api_format.c new file mode 100644 index 00000000000..f7bb9d3970f --- /dev/null +++ b/src/vlibapi/api_format.c @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#include <vppinfra/format.h> +#include <vlibapi/api.h> + +u8 * +format_vl_api_msg_text (u8 *s, va_list *args) +{ + api_main_t *am = va_arg (*args, api_main_t *); + u32 msg_id = va_arg (*args, u32); + void *msg = va_arg (*args, void *); + vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id); + + if (m->format_fn) + s = format (s, "%U", m->format_fn, msg); + else + s = format (s, "[format handler missing for `%s`]", m->name); + return s; +} + +u8 * +format_vl_api_msg_json (u8 *s, va_list *args) +{ + api_main_t *am = va_arg (*args, api_main_t *); + u32 msg_id = va_arg (*args, u32); + void *msg = va_arg (*args, void *); + vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id); + + cJSON *o = m->tojson_handler (msg); + char *out = cJSON_Print (o); + + s = format (s, "%s", out); + + cJSON_Delete (o); + cJSON_free (out); + return s; +} diff --git a/src/vlibapi/api_helper_macros.h b/src/vlibapi/api_helper_macros.h index 7a04bb056f2..0380692b80e 100644 --- a/src/vlibapi/api_helper_macros.h +++ b/src/vlibapi/api_helper_macros.h @@ -27,72 +27,96 @@ #define REPLY_MSG_ID_BASE 0 #endif -#define REPLY_MACRO(t) \ -do { \ - vl_api_registration_t *rp; \ - rp = vl_api_client_index_to_registration (mp->client_index); \ - if (rp == 0) \ - return; \ - \ - rmp = vl_msg_api_alloc (sizeof (*rmp)); \ - rmp->_vl_msg_id = htons((t)+(REPLY_MSG_ID_BASE)); \ - rmp->context = mp->context; \ - rmp->retval = ntohl(rv); \ - \ - vl_api_send_msg (rp, (u8 *)rmp); \ -} while(0); +#define _NATIVE_TO_NETWORK(t, rmp) \ + api_main_t *am = vlibapi_get_main (); \ + void (*endian_fp) (void *, bool); \ + endian_fp = am->msg_data[t + (REPLY_MSG_ID_BASE)].endian_handler; \ + (*endian_fp) (rmp, 1 /* to network */); -#define REPLY_MACRO_END(t) \ -do { \ - vl_api_registration_t *rp; \ - rp = vl_api_client_index_to_registration (mp->client_index); \ - if (rp == 0) \ - return; \ - \ - rmp = vl_msg_api_alloc (sizeof (*rmp)); \ - rmp->_vl_msg_id = t+(REPLY_MSG_ID_BASE); \ - rmp->context = mp->context; \ - rmp->retval = rv; \ - api_main_t *am = vlibapi_get_main (); \ - void (*endian_fp) (void *); \ - endian_fp = am->msg_endian_handlers[t+(REPLY_MSG_ID_BASE)]; \ - (*endian_fp) (rmp); \ - vl_api_send_msg (rp, (u8 *)rmp); \ -} while(0); +#define REPLY_MACRO(msg) \ + do \ + { \ + STATIC_ASSERT ( \ + msg##_IS_CONSTANT_SIZE, \ + "REPLY_MACRO can only be used with constant size messages, " \ + "use REPLY_MACRO[3|4]* instead"); \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + rmp->_vl_msg_id = htons (msg + (REPLY_MSG_ID_BASE)); \ + rmp->context = mp->context; \ + rmp->retval = ntohl (rv); \ + \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); -#define REPLY_MACRO2(t, body) \ -do { \ - vl_api_registration_t *rp; \ - rp = vl_api_client_index_to_registration (mp->client_index); \ - if (rp == 0) \ - return; \ - \ - rmp = vl_msg_api_alloc (sizeof (*rmp)); \ - rmp->_vl_msg_id = htons((t)+(REPLY_MSG_ID_BASE)); \ - rmp->context = mp->context; \ - rmp->retval = ntohl(rv); \ - do {body;} while (0); \ - vl_api_send_msg (rp, (u8 *)rmp); \ -} while(0); +#define REPLY_MACRO_END(t) \ + do \ + { \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + rmp->_vl_msg_id = t + (REPLY_MSG_ID_BASE); \ + rmp->context = mp->context; \ + rmp->retval = rv; \ + _NATIVE_TO_NETWORK (t, rmp); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); -#define REPLY_MACRO2_END(t, body) \ -do { \ - vl_api_registration_t *rp; \ - rp = vl_api_client_index_to_registration (mp->client_index); \ - if (rp == 0) \ - return; \ - \ - rmp = vl_msg_api_alloc (sizeof (*rmp)); \ - rmp->_vl_msg_id = t+(REPLY_MSG_ID_BASE); \ - rmp->context = mp->context; \ - rmp->retval = rv; \ - do {body;} while (0); \ - api_main_t *am = vlibapi_get_main (); \ - void (*endian_fp) (void *); \ - endian_fp = am->msg_endian_handlers[t+(REPLY_MSG_ID_BASE)]; \ - (*endian_fp) (rmp); \ - vl_api_send_msg (rp, (u8 *)rmp); \ -} while(0); +#define REPLY_MACRO2(t, body) \ + do \ + { \ + STATIC_ASSERT ( \ + t##_IS_CONSTANT_SIZE, \ + "REPLY_MACRO2 can only be used with constant size messages, " \ + "use REPLY_MACRO[3|4]* instead"); \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + rmp->_vl_msg_id = htons ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = mp->context; \ + rmp->retval = ntohl (rv); \ + do \ + { \ + body; \ + } \ + while (0); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); + +#define REPLY_MACRO2_END(t, body) \ + do \ + { \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + rmp->_vl_msg_id = t + (REPLY_MSG_ID_BASE); \ + rmp->context = mp->context; \ + rmp->retval = rv; \ + do \ + { \ + body; \ + } \ + while (0); \ + _NATIVE_TO_NETWORK (t, rmp); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); #define REPLY_MACRO2_ZERO(t, body) \ do { \ @@ -109,6 +133,28 @@ do { \ vl_api_send_msg (rp, (u8 *)rmp); \ } while(0); +#define REPLY_MACRO2_ZERO_END(t, body) \ + do \ + { \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc_zero (sizeof (*rmp)); \ + rmp->_vl_msg_id = ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = mp->context; \ + rmp->retval = rv; \ + do \ + { \ + body; \ + } \ + while (0); \ + _NATIVE_TO_NETWORK (t, rmp); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); + #define REPLY_MACRO_DETAILS2(t, body) \ do { \ vl_api_registration_t *rp; \ @@ -123,6 +169,27 @@ do { \ vl_api_send_msg (rp, (u8 *)rmp); \ } while(0); +#define REPLY_MACRO_DETAILS2_END(t, body) \ + do \ + { \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + rmp->_vl_msg_id = ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = mp->context; \ + do \ + { \ + body; \ + } \ + while (0); \ + _NATIVE_TO_NETWORK (t, rmp); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); + #define REPLY_MACRO_DETAILS4(t, rp, context, body) \ do { \ rmp = vl_msg_api_alloc (sizeof (*rmp)); \ @@ -132,20 +199,95 @@ do { \ vl_api_send_msg (rp, (u8 *)rmp); \ } while(0); -#define REPLY_MACRO3(t, n, body) \ -do { \ - vl_api_registration_t *rp; \ - rp = vl_api_client_index_to_registration (mp->client_index); \ - if (rp == 0) \ - return; \ - \ - rmp = vl_msg_api_alloc (sizeof (*rmp) + n); \ - rmp->_vl_msg_id = htons((t)+(REPLY_MSG_ID_BASE)); \ - rmp->context = mp->context; \ - rmp->retval = ntohl(rv); \ - do {body;} while (0); \ - vl_api_send_msg (rp, (u8 *)rmp); \ -} while(0); +#define REPLY_MACRO_DETAILS4_END(t, rp, context, body) \ + do \ + { \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + rmp->_vl_msg_id = ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = context; \ + do \ + { \ + body; \ + } \ + while (0); \ + _NATIVE_TO_NETWORK (t, rmp); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); + +#define REPLY_MACRO_DETAILS5(t, n, rp, context, body) \ + do \ + { \ + rmp = vl_msg_api_alloc (sizeof (*rmp) + n); \ + rmp->_vl_msg_id = htons ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = context; \ + do \ + { \ + body; \ + } \ + while (0); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); + +#define REPLY_MACRO_DETAILS5_END(t, n, rp, context, body) \ + do \ + { \ + rmp = vl_msg_api_alloc (sizeof (*rmp) + n); \ + rmp->_vl_msg_id = ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = context; \ + do \ + { \ + body; \ + } \ + while (0); \ + _NATIVE_TO_NETWORK (t, rmp); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); + +#define REPLY_MACRO3(t, n, body) \ + do \ + { \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp) + (n)); \ + rmp->_vl_msg_id = htons ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = mp->context; \ + rmp->retval = ntohl (rv); \ + do \ + { \ + body; \ + } \ + while (0); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); + +#define REPLY_MACRO3_END(t, n, body) \ + do \ + { \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp) + n); \ + rmp->_vl_msg_id = ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = mp->context; \ + rmp->retval = rv; \ + do \ + { \ + body; \ + } \ + while (0); \ + _NATIVE_TO_NETWORK (t, rmp); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); #define REPLY_MACRO3_ZERO(t, n, body) \ do { \ @@ -162,6 +304,28 @@ do { \ vl_api_send_msg (rp, (u8 *)rmp); \ } while(0); +#define REPLY_MACRO3_ZERO_END(t, n, body) \ + do \ + { \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc_zero (sizeof (*rmp) + n); \ + rmp->_vl_msg_id = ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = mp->context; \ + rmp->retval = rv; \ + do \ + { \ + body; \ + } \ + while (0); \ + _NATIVE_TO_NETWORK (t, rmp); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); + #define REPLY_MACRO4(t, n, body) \ do { \ vl_api_registration_t *rp; \ @@ -192,6 +356,43 @@ do { \ vl_api_send_msg (rp, (u8 *)rmp); \ } while(0); +#define REPLY_MACRO4_END(t, n, body) \ + do \ + { \ + vl_api_registration_t *rp; \ + u8 is_error = 0; \ + \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + \ + rmp = vl_msg_api_alloc_or_null (sizeof (*rmp) + n); \ + if (!rmp) \ + { \ + /* if there isn't enough memory, try to allocate */ \ + /* some at least for returning an error */ \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + if (!rmp) \ + return; \ + \ + clib_memset (rmp, 0, sizeof (*rmp)); \ + rv = VNET_API_ERROR_TABLE_TOO_BIG; \ + is_error = 1; \ + } \ + rmp->_vl_msg_id = ((t) + (REPLY_MSG_ID_BASE)); \ + rmp->context = mp->context; \ + rmp->retval = rv; \ + if (!is_error) \ + do \ + { \ + body; \ + } \ + while (0); \ + _NATIVE_TO_NETWORK (t, rmp); \ + vl_api_send_msg (rp, (u8 *) rmp); \ + } \ + while (0); + #define REPLY_AND_DETAILS_MACRO(t, p, body) \ do \ { \ @@ -232,6 +433,46 @@ do { \ } \ while (0); +#define REPLY_AND_DETAILS_MACRO_END(t, p, body) \ + do \ + { \ + if (pool_elts (p) == 0) \ + { \ + REPLY_MACRO_END (t); \ + break; \ + } \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + u32 cursor = mp->cursor; \ + vlib_main_t *vm = vlib_get_main (); \ + f64 start = vlib_time_now (vm); \ + if (pool_is_free_index (p, cursor)) \ + { \ + cursor = pool_next_index (p, cursor); \ + if (cursor == ~0) \ + rv = VNET_API_ERROR_INVALID_VALUE; \ + } \ + while (cursor != ~0) \ + { \ + do \ + { \ + body; \ + } \ + while (0); \ + cursor = pool_next_index (p, cursor); \ + if (vl_api_process_may_suspend (vm, rp, start)) \ + { \ + if (cursor != ~0) \ + rv = VNET_API_ERROR_EAGAIN; \ + break; \ + } \ + } \ + REPLY_MACRO2_END (t, ({ rmp->cursor = cursor; })); \ + } \ + while (0); + #define REPLY_AND_DETAILS_VEC_MACRO(t, v, mp, rmp, rv, body) \ do { \ vl_api_registration_t *rp; \ @@ -260,6 +501,41 @@ do { \ })); \ } while(0); +#define REPLY_AND_DETAILS_VEC_MACRO_END(t, v, mp, rmp, rv, body) \ + do \ + { \ + vl_api_registration_t *rp; \ + rp = vl_api_client_index_to_registration (mp->client_index); \ + if (rp == 0) \ + return; \ + u32 cursor = mp->cursor; \ + vlib_main_t *vm = vlib_get_main (); \ + f64 start = vlib_time_now (vm); \ + if (!v || vec_len (v) == 0) \ + { \ + cursor = ~0; \ + rv = VNET_API_ERROR_INVALID_VALUE; \ + } \ + else if (cursor == ~0) \ + cursor = 0; \ + while (cursor != ~0 && cursor < vec_len (v)) \ + { \ + do \ + { \ + body; \ + } \ + while (0); \ + ++cursor; \ + if (vl_api_process_may_suspend (vm, rp, start)) \ + { \ + if (cursor < vec_len (v)) \ + rv = VNET_API_ERROR_EAGAIN; \ + break; \ + } \ + } \ + REPLY_MACRO2_END (t, ({ rmp->cursor = cursor; })); \ + } \ + while (0); /* "trust, but verify" */ #define vnet_sw_if_index_is_api_valid(sw_if_index) \ @@ -273,6 +549,17 @@ do { \ } \ } while(0); +#define VALIDATE_SW_IF_INDEX_END(mp) \ + do \ + { \ + if (!vnet_sw_if_index_is_api_valid ((mp)->sw_if_index)) \ + { \ + rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \ + goto bad_sw_if_index; \ + } \ + } \ + while (0); + #define BAD_SW_IF_INDEX_LABEL \ do { \ bad_sw_if_index: \ @@ -287,6 +574,17 @@ bad_sw_if_index: \ } \ } while(0); +#define VALIDATE_RX_SW_IF_INDEX_END(mp) \ + do \ + { \ + if (!vnet_sw_if_index_is_api_valid ((mp)->rx_sw_if_index)) \ + { \ + rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \ + goto bad_rx_sw_if_index; \ + } \ + } \ + while (0); + #define BAD_RX_SW_IF_INDEX_LABEL \ do { \ bad_rx_sw_if_index: \ @@ -301,6 +599,17 @@ bad_rx_sw_if_index: \ } \ } while(0); +#define VALIDATE_TX_SW_IF_INDEX_END(mp) \ + do \ + { \ + if (!vnet_sw_if_index_is_api_valid (mp->tx_sw_if_index)) \ + { \ + rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \ + goto bad_tx_sw_if_index; \ + } \ + } \ + while (0); + #define BAD_TX_SW_IF_INDEX_LABEL \ do { \ bad_tx_sw_if_index: \ @@ -315,68 +624,82 @@ bad_tx_sw_if_index: \ } \ } while(0); +#define VALIDATE_BD_ID_END(mp) \ + do \ + { \ + if (mp->bd_id > L2_BD_ID_MAX) \ + { \ + rv = VNET_API_ERROR_BD_ID_EXCEED_MAX; \ + goto bad_bd_id; \ + } \ + } \ + while (0); + #define BAD_BD_ID_LABEL \ do { \ bad_bd_id: \ ; \ } while (0); -#define pub_sub_handler(lca,UCA) \ -static void vl_api_want_##lca##_t_handler ( \ - vl_api_want_##lca##_t *mp) \ -{ \ - vpe_api_main_t *vam = &vpe_api_main; \ - vpe_client_registration_t *rp; \ - vl_api_want_##lca##_reply_t *rmp; \ - uword *p; \ - i32 rv = 0; \ - \ - p = hash_get (vam->lca##_registration_hash, mp->client_index); \ - if (p) { \ - if (mp->enable_disable) { \ - clib_warning ("pid %d: already enabled...", ntohl(mp->pid)); \ - rv = VNET_API_ERROR_INVALID_REGISTRATION; \ - goto reply; \ - } else { \ - rp = pool_elt_at_index (vam->lca##_registrations, p[0]); \ - pool_put (vam->lca##_registrations, rp); \ - hash_unset (vam->lca##_registration_hash, \ - mp->client_index); \ - goto reply; \ - } \ - } \ - if (mp->enable_disable == 0) { \ - clib_warning ("pid %d: already disabled...", mp->pid); \ - rv = VNET_API_ERROR_INVALID_REGISTRATION; \ - goto reply; \ - } \ - pool_get (vam->lca##_registrations, rp); \ - rp->client_index = mp->client_index; \ - rp->client_pid = mp->pid; \ - hash_set (vam->lca##_registration_hash, rp->client_index, \ - rp - vam->lca##_registrations); \ - \ -reply: \ - REPLY_MACRO (VL_API_WANT_##UCA##_REPLY); \ -} \ - \ -static clib_error_t * vl_api_want_##lca##_t_reaper (u32 client_index) \ -{ \ - vpe_api_main_t *vam = &vpe_api_main; \ - vpe_client_registration_t *rp; \ - uword *p; \ - \ - p = hash_get (vam->lca##_registration_hash, client_index); \ - if (p) \ - { \ - rp = pool_elt_at_index (vam->lca##_registrations, p[0]); \ - pool_put (vam->lca##_registrations, rp); \ - hash_unset (vam->lca##_registration_hash, client_index); \ - } \ - return (NULL); \ -} \ - \ -VL_MSG_API_REAPER_FUNCTION (vl_api_want_##lca##_t_reaper); \ +#define pub_sub_handler(lca, UCA) \ + static void vl_api_want_##lca##_t_handler (vl_api_want_##lca##_t *mp) \ + { \ + vpe_api_main_t *vam = &vpe_api_main; \ + vpe_client_registration_t *rp; \ + vl_api_want_##lca##_reply_t *rmp; \ + uword *p; \ + i32 rv = 0; \ + \ + p = hash_get (vam->lca##_registration_hash, mp->client_index); \ + if (p) \ + { \ + if (mp->enable_disable) \ + { \ + clib_warning ("pid %d: already enabled...", ntohl (mp->pid)); \ + rv = VNET_API_ERROR_INVALID_REGISTRATION; \ + goto reply; \ + } \ + else \ + { \ + rp = pool_elt_at_index (vam->lca##_registrations, p[0]); \ + pool_put (vam->lca##_registrations, rp); \ + hash_unset (vam->lca##_registration_hash, mp->client_index); \ + goto reply; \ + } \ + } \ + if (mp->enable_disable == 0) \ + { \ + clib_warning ("pid %d: already disabled...", mp->pid); \ + rv = VNET_API_ERROR_INVALID_REGISTRATION; \ + goto reply; \ + } \ + pool_get (vam->lca##_registrations, rp); \ + rp->client_index = mp->client_index; \ + rp->client_pid = mp->pid; \ + hash_set (vam->lca##_registration_hash, rp->client_index, \ + rp - vam->lca##_registrations); \ + \ + reply: \ + REPLY_MACRO (VL_API_WANT_##UCA##_REPLY); \ + } \ + \ + static clib_error_t *vl_api_want_##lca##_t_reaper (u32 client_index) \ + { \ + vpe_api_main_t *vam = &vpe_api_main; \ + vpe_client_registration_t *rp; \ + uword *p; \ + \ + p = hash_get (vam->lca##_registration_hash, client_index); \ + if (p) \ + { \ + rp = pool_elt_at_index (vam->lca##_registrations, p[0]); \ + pool_put (vam->lca##_registrations, rp); \ + hash_unset (vam->lca##_registration_hash, client_index); \ + } \ + return (NULL); \ + } \ + \ + VL_MSG_API_REAPER_FUNCTION (vl_api_want_##lca##_t_reaper); #define foreach_registration_hash \ _(interface_events) \ diff --git a/src/vlibapi/api_shared.c b/src/vlibapi/api_shared.c index dd51ee5fae1..79064b292c9 100644 --- a/src/vlibapi/api_shared.c +++ b/src/vlibapi/api_shared.c @@ -32,14 +32,11 @@ #include <vppinfra/elog.h> #include <vppinfra/callback.h> -/* *INDENT-OFF* */ -api_main_t api_global_main = - { - .region_name = "/unset", - .api_uid = -1, - .api_gid = -1, - }; -/* *INDENT-ON* */ +static api_main_t api_global_main = { + .region_name = "/unset", + .api_uid = -1, + .api_gid = -1, +}; /* Please use vlibapi_get_main() to access my_api_main */ __thread api_main_t *my_api_main = &api_global_main; @@ -80,13 +77,11 @@ vl_msg_api_trace (api_main_t * am, vl_api_trace_t * tp, void *msg) u8 **old_trace; u8 *msg_copy; u32 length; - trace_cfg_t *cfgp; u16 msg_id = clib_net_to_host_u16 (*((u16 *) msg)); + vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id); msgbuf_t *header = (msgbuf_t *) (((u8 *) msg) - offsetof (msgbuf_t, data)); - cfgp = am->api_trace_cfg + msg_id; - - if (!cfgp || !cfgp->trace_enable) + if (!m || !m->trace_enable) return; msg_copy = 0; @@ -212,13 +207,11 @@ vl_api_serialize_message_table (api_main_t * am, u8 * vector) /* serialize the count */ serialize_integer (sm, nmsg, sizeof (u32)); - /* *INDENT-OFF* */ hash_foreach_pair (hp, am->msg_index_by_name_and_crc, ({ serialize_likely_small_unsigned_integer (sm, hp->value[0]); serialize_cstring (sm, (char *) hp->key); })); - /* *INDENT-ON* */ return serialize_close_vector (sm); } @@ -228,21 +221,21 @@ vl_msg_api_trace_write_one (api_main_t *am, u8 *msg, FILE *fp) { u8 *tmpmem = 0; int tlen, slen; - cJSON *(*tojson_fn) (void *); u32 msg_length = vec_len (msg); vec_validate (tmpmem, msg_length - 1); clib_memcpy_fast (tmpmem, msg, msg_length); u16 id = clib_net_to_host_u16 (*((u16 *) msg)); + vl_api_msg_data_t *m = vl_api_get_msg_data (am, id); - void (*endian_fp) (void *); - endian_fp = am->msg_endian_handlers[id]; - (*endian_fp) (tmpmem); + if (m && m->endian_handler) + { + m->endian_handler (tmpmem, 1); + } - if (id < vec_len (am->msg_tojson_handlers) && am->msg_tojson_handlers[id]) + if (m && m->tojson_handler) { - tojson_fn = am->msg_tojson_handlers[id]; - cJSON *o = tojson_fn (tmpmem); + cJSON *o = m->tojson_handler (tmpmem); char *s = cJSON_Print (o); slen = strlen (s); tlen = fwrite (s, 1, slen, fp); @@ -500,76 +493,87 @@ vl_msg_api_barrier_release (void) } always_inline void -msg_handler_internal (api_main_t * am, - void *the_msg, int trace_it, int do_it, int free_it) +msg_handler_internal (api_main_t *am, void *the_msg, uword msg_len, + int trace_it, int do_it, int free_it) { u16 id = clib_net_to_host_u16 (*((u16 *) the_msg)); - u8 *(*print_fp) (void *, void *); + vl_api_msg_data_t *m = vl_api_get_msg_data (am, id); if (PREDICT_FALSE (am->elog_trace_api_messages)) { - /* *INDENT-OFF* */ ELOG_TYPE_DECLARE (e) = { .format = "api-msg: %s", .format_args = "T4", }; - /* *INDENT-ON* */ struct { u32 c; } *ed; ed = ELOG_DATA (am->elog_main, e); - if (id < vec_len (am->msg_names) && am->msg_names[id]) - ed->c = elog_string (am->elog_main, (char *) am->msg_names[id]); + if (m && m->name) + ed->c = elog_string (am->elog_main, (char *) m->name); else ed->c = elog_string (am->elog_main, "BOGUS"); } - if (id < vec_len (am->msg_handlers) && am->msg_handlers[id]) + if (m && m->handler) { if (trace_it) vl_msg_api_trace (am, am->rx_trace, the_msg); if (am->msg_print_flag) { - fformat (stdout, "[%d]: %s\n", id, am->msg_names[id]); - print_fp = (void *) am->msg_print_handlers[id]; - if (print_fp == 0) - { - fformat (stdout, " [no registered print fn]\n"); - } - else + fformat (stdout, "[%d]: %s\n", id, m->name); + fformat (stdout, "%U", format_vl_api_msg_text, am, id, the_msg); + } + + uword calc_size = 0; + ASSERT (NULL != m->calc_size_func); + if (m->calc_size_func) + { + calc_size = m->calc_size_func (the_msg); + if (calc_size > msg_len) { - (*print_fp) (the_msg, stdout); + clib_warning ( + "Truncated message '%s' (id %u) received, calculated size " + "%lu is bigger than actual size %llu, message dropped.", + m->name, id, calc_size, msg_len); } } + else + { + clib_warning ("Message '%s' (id %u) has NULL calc_size_func, cannot " + "verify message size is correct", + m->name, id); + } - if (do_it) + /* don't process message if it's truncated, otherwise byte swaps + * and stuff could corrupt memory even beyond message if it's malicious + * e.g. VLA length field set to 1M elements, but VLA empty */ + if (do_it && calc_size <= msg_len) { - if (!am->is_mp_safe[id]) + + if (!m->is_mp_safe) { vl_msg_api_barrier_trace_context (am->msg_names[id]); vl_msg_api_barrier_sync (); } - if (am->is_autoendian[id]) - { - void (*endian_fp) (void *); - endian_fp = am->msg_endian_handlers[id]; - (*endian_fp) (the_msg); - } + if (m->is_autoendian) + m->endian_handler (the_msg, 0); if (PREDICT_FALSE (vec_len (am->perf_counter_cbs) != 0)) clib_call_callbacks (am->perf_counter_cbs, am, id, 0 /* before */ ); - (*am->msg_handlers[id]) (the_msg); + m->handler (the_msg); if (PREDICT_FALSE (vec_len (am->perf_counter_cbs) != 0)) clib_call_callbacks (am->perf_counter_cbs, am, id, 1 /* after */ ); - if (!am->is_mp_safe[id]) + + if (!m->is_mp_safe) vl_msg_api_barrier_release (); } } @@ -583,7 +587,6 @@ msg_handler_internal (api_main_t * am, if (PREDICT_FALSE (am->elog_trace_api_messages)) { - /* *INDENT-OFF* */ ELOG_TYPE_DECLARE (e) = { .format = "api-msg-done(%s): %s", @@ -595,7 +598,6 @@ msg_handler_internal (api_main_t * am, "mp-safe", } }; - /* *INDENT-ON* */ struct { @@ -603,10 +605,10 @@ msg_handler_internal (api_main_t * am, u32 c; } *ed; ed = ELOG_DATA (am->elog_main, e); - if (id < vec_len (am->msg_names) && am->msg_names[id]) + if (m && m->name) { - ed->c = elog_string (am->elog_main, (char *) am->msg_names[id]); - ed->barrier = !am->is_mp_safe[id]; + ed->c = elog_string (am->elog_main, (char *) m->name); + ed->barrier = !m->is_mp_safe; } else { @@ -616,183 +618,31 @@ msg_handler_internal (api_main_t * am, } } -void (*vl_msg_api_fuzz_hook) (u16, void *); - -/* This is only to be called from a vlib/vnet app */ void -vl_msg_api_handler_with_vm_node (api_main_t * am, svm_region_t * vlib_rp, - void *the_msg, vlib_main_t * vm, - vlib_node_runtime_t * node, u8 is_private) -{ - u16 id = clib_net_to_host_u16 (*((u16 *) the_msg)); - u8 *(*handler) (void *, void *, void *); - u8 *(*print_fp) (void *, void *); - svm_region_t *old_vlib_rp; - void *save_shmem_hdr; - int is_mp_safe = 1; - - if (PREDICT_FALSE (am->elog_trace_api_messages)) - { - /* *INDENT-OFF* */ - ELOG_TYPE_DECLARE (e) = - { - .format = "api-msg: %s", - .format_args = "T4", - }; - /* *INDENT-ON* */ - struct - { - u32 c; - } *ed; - ed = ELOG_DATA (am->elog_main, e); - if (id < vec_len (am->msg_names) && am->msg_names[id]) - ed->c = elog_string (am->elog_main, (char *) am->msg_names[id]); - else - ed->c = elog_string (am->elog_main, "BOGUS"); - } - - if (id < vec_len (am->msg_handlers) && am->msg_handlers[id]) - { - handler = (void *) am->msg_handlers[id]; - - if (PREDICT_FALSE (am->rx_trace && am->rx_trace->enabled)) - vl_msg_api_trace (am, am->rx_trace, the_msg); - - if (PREDICT_FALSE (am->msg_print_flag)) - { - fformat (stdout, "[%d]: %s\n", id, am->msg_names[id]); - print_fp = (void *) am->msg_print_handlers[id]; - if (print_fp == 0) - { - fformat (stdout, " [no registered print fn for msg %d]\n", id); - } - else - { - (*print_fp) (the_msg, vm); - } - } - is_mp_safe = am->is_mp_safe[id]; - - if (!is_mp_safe) - { - vl_msg_api_barrier_trace_context (am->msg_names[id]); - vl_msg_api_barrier_sync (); - } - if (is_private) - { - old_vlib_rp = am->vlib_rp; - save_shmem_hdr = am->shmem_hdr; - am->vlib_rp = vlib_rp; - am->shmem_hdr = (void *) vlib_rp->user_ctx; - } - - if (PREDICT_FALSE (vl_msg_api_fuzz_hook != 0)) - (*vl_msg_api_fuzz_hook) (id, the_msg); - - if (am->is_autoendian[id]) - { - void (*endian_fp) (void *); - endian_fp = am->msg_endian_handlers[id]; - (*endian_fp) (the_msg); - } - if (PREDICT_FALSE (vec_len (am->perf_counter_cbs) != 0)) - clib_call_callbacks (am->perf_counter_cbs, am, id, 0 /* before */ ); - - (*handler) (the_msg, vm, node); - - if (PREDICT_FALSE (vec_len (am->perf_counter_cbs) != 0)) - clib_call_callbacks (am->perf_counter_cbs, am, id, 1 /* after */ ); - if (is_private) - { - am->vlib_rp = old_vlib_rp; - am->shmem_hdr = save_shmem_hdr; - } - if (!is_mp_safe) - vl_msg_api_barrier_release (); - } - else - { - clib_warning ("no handler for msg id %d", id); - } - - /* - * Special-case, so we can e.g. bounce messages off the vnet - * main thread without copying them... - */ - if (id >= vec_len (am->message_bounce) || !(am->message_bounce[id])) - { - if (is_private) - { - old_vlib_rp = am->vlib_rp; - save_shmem_hdr = am->shmem_hdr; - am->vlib_rp = vlib_rp; - am->shmem_hdr = (void *) vlib_rp->user_ctx; - } - vl_msg_api_free (the_msg); - if (is_private) - { - am->vlib_rp = old_vlib_rp; - am->shmem_hdr = save_shmem_hdr; - } - } - - if (PREDICT_FALSE (am->elog_trace_api_messages)) - { - /* *INDENT-OFF* */ - ELOG_TYPE_DECLARE (e) = - { - .format = "api-msg-done(%s): %s", - .format_args = "t4T4", - .n_enum_strings = 2, - .enum_strings = - { - "barrier", - "mp-safe", - } - }; - /* *INDENT-ON* */ - - struct - { - u32 barrier; - u32 c; - } *ed; - ed = ELOG_DATA (am->elog_main, e); - if (id < vec_len (am->msg_names) && am->msg_names[id]) - ed->c = elog_string (am->elog_main, (char *) am->msg_names[id]); - else - ed->c = elog_string (am->elog_main, "BOGUS"); - ed->barrier = is_mp_safe; - } -} - -void -vl_msg_api_handler (void *the_msg) +vl_msg_api_handler (void *the_msg, uword msg_len) { api_main_t *am = vlibapi_get_main (); - msg_handler_internal (am, the_msg, - (am->rx_trace - && am->rx_trace->enabled) /* trace_it */ , - 1 /* do_it */ , 1 /* free_it */ ); + msg_handler_internal (am, the_msg, msg_len, + (am->rx_trace && am->rx_trace->enabled) /* trace_it */, + 1 /* do_it */, 1 /* free_it */); } void -vl_msg_api_handler_no_free (void *the_msg) +vl_msg_api_handler_no_free (void *the_msg, uword msg_len) { api_main_t *am = vlibapi_get_main (); - msg_handler_internal (am, the_msg, - (am->rx_trace - && am->rx_trace->enabled) /* trace_it */ , - 1 /* do_it */ , 0 /* free_it */ ); + msg_handler_internal (am, the_msg, msg_len, + (am->rx_trace && am->rx_trace->enabled) /* trace_it */, + 1 /* do_it */, 0 /* free_it */); } void -vl_msg_api_handler_no_trace_no_free (void *the_msg) +vl_msg_api_handler_no_trace_no_free (void *the_msg, uword msg_len) { api_main_t *am = vlibapi_get_main (); - msg_handler_internal (am, the_msg, 0 /* trace_it */ , 1 /* do_it */ , - 0 /* free_it */ ); + msg_handler_internal (am, the_msg, msg_len, 0 /* trace_it */, 1 /* do_it */, + 0 /* free_it */); } /* @@ -805,14 +655,13 @@ vl_msg_api_handler_no_trace_no_free (void *the_msg) * */ void -vl_msg_api_trace_only (void *the_msg) +vl_msg_api_trace_only (void *the_msg, uword msg_len) { api_main_t *am = vlibapi_get_main (); - msg_handler_internal (am, the_msg, - (am->rx_trace - && am->rx_trace->enabled) /* trace_it */ , - 0 /* do_it */ , 0 /* free_it */ ); + msg_handler_internal (am, the_msg, msg_len, + (am->rx_trace && am->rx_trace->enabled) /* trace_it */, + 0 /* do_it */, 0 /* free_it */); } void @@ -820,14 +669,16 @@ vl_msg_api_cleanup_handler (void *the_msg) { api_main_t *am = vlibapi_get_main (); u16 id = clib_net_to_host_u16 (*((u16 *) the_msg)); + vl_api_msg_data_t *m = vl_api_get_msg_data (am, id); - if (PREDICT_FALSE (id >= vec_len (am->msg_cleanup_handlers))) + if (PREDICT_FALSE (!m)) { clib_warning ("_vl_msg_id too large: %d\n", id); return; } - if (am->msg_cleanup_handlers[id]) - (*am->msg_cleanup_handlers[id]) (the_msg); + + if (m->cleanup_handler) + m->cleanup_handler (the_msg); vl_msg_api_free (the_msg); } @@ -839,17 +690,17 @@ void vl_msg_api_replay_handler (void *the_msg) { api_main_t *am = vlibapi_get_main (); - u16 id = clib_net_to_host_u16 (*((u16 *) the_msg)); + vl_api_msg_data_t *m = vl_api_get_msg_data (am, id); - if (PREDICT_FALSE (id >= vec_len (am->msg_handlers))) + if (PREDICT_FALSE (!m)) { clib_warning ("_vl_msg_id too large: %d\n", id); return; } /* do NOT trace the message... */ - if (am->msg_handlers[id]) - (*am->msg_handlers[id]) (the_msg); + if (m->handler) + m->handler (the_msg); /* do NOT free the message buffer... */ } @@ -863,34 +714,20 @@ vl_msg_api_get_msg_length (void *msg_arg) * vl_msg_api_socket_handler */ void -vl_msg_api_socket_handler (void *the_msg) +vl_msg_api_socket_handler (void *the_msg, uword msg_len) { api_main_t *am = vlibapi_get_main (); - msg_handler_internal (am, the_msg, - (am->rx_trace - && am->rx_trace->enabled) /* trace_it */ , - 1 /* do_it */ , 0 /* free_it */ ); + msg_handler_internal (am, the_msg, msg_len, + (am->rx_trace && am->rx_trace->enabled) /* trace_it */, + 1 /* do_it */, 0 /* free_it */); } -#define foreach_msg_api_vector \ - _ (msg_names) \ - _ (msg_handlers) \ - _ (msg_cleanup_handlers) \ - _ (msg_endian_handlers) \ - _ (msg_print_handlers) \ - _ (msg_print_json_handlers) \ - _ (msg_tojson_handlers) \ - _ (msg_fromjson_handlers) \ - _ (api_trace_cfg) \ - _ (message_bounce) \ - _ (is_mp_safe) \ - _ (is_autoendian) - void vl_msg_api_config (vl_msg_api_msg_config_t * c) { api_main_t *am = vlibapi_get_main (); + vl_api_msg_data_t *m; /* * This happens during the java core tests if the message @@ -909,31 +746,29 @@ vl_msg_api_config (vl_msg_api_msg_config_t * c) return; } -#define _(a) vec_validate (am->a, c->id); - foreach_msg_api_vector; -#undef _ - - if (am->msg_handlers[c->id] && am->msg_handlers[c->id] != c->handler) - clib_warning - ("BUG: re-registering 'vl_api_%s_t_handler'." - "Handler was %llx, replaced by %llx", - c->name, am->msg_handlers[c->id], c->handler); - - am->msg_names[c->id] = c->name; - am->msg_handlers[c->id] = c->handler; - am->msg_cleanup_handlers[c->id] = c->cleanup; - am->msg_endian_handlers[c->id] = c->endian; - am->msg_print_handlers[c->id] = c->print; - am->msg_print_json_handlers[c->id] = c->print_json; - am->msg_tojson_handlers[c->id] = c->tojson; - am->msg_fromjson_handlers[c->id] = c->fromjson; - am->message_bounce[c->id] = c->message_bounce; - am->is_mp_safe[c->id] = c->is_mp_safe; - am->is_autoendian[c->id] = c->is_autoendian; - - am->api_trace_cfg[c->id].size = c->size; - am->api_trace_cfg[c->id].trace_enable = c->traced; - am->api_trace_cfg[c->id].replay_enable = c->replay; + vec_validate (am->msg_data, c->id); + m = vl_api_get_msg_data (am, c->id); + + if (m->handler && m->handler != c->handler) + clib_warning ("BUG: re-registering 'vl_api_%s_t_handler'." + "Handler was %llx, replaced by %llx", + c->name, m->handler, c->handler); + + m->name = c->name; + m->handler = c->handler; + m->cleanup_handler = c->cleanup; + m->endian_handler = c->endian; + m->format_fn = c->format_fn; + m->tojson_handler = c->tojson; + m->fromjson_handler = c->fromjson; + m->calc_size_func = c->calc_size; + m->bounce = c->message_bounce; + m->is_mp_safe = c->is_mp_safe; + m->is_autoendian = c->is_autoendian; + + m->trace_size = c->size; + m->trace_enable = c->traced; + m->replay_allowed = c->replay; if (!am->msg_id_by_name) am->msg_id_by_name = hash_create_string (0, sizeof (uword)); @@ -941,37 +776,6 @@ vl_msg_api_config (vl_msg_api_msg_config_t * c) hash_set_mem (am->msg_id_by_name, c->name, c->id); } -/* - * vl_msg_api_set_handlers - * preserve the old API for a while - */ -void -vl_msg_api_set_handlers (int id, char *name, void *handler, void *cleanup, - void *endian, void *print, int size, int traced, - void *print_json, void *tojson, void *fromjson) -{ - vl_msg_api_msg_config_t cfg; - vl_msg_api_msg_config_t *c = &cfg; - - clib_memset (c, 0, sizeof (*c)); - - c->id = id; - c->name = name; - c->handler = handler; - c->cleanup = cleanup; - c->endian = endian; - c->print = print; - c->traced = traced; - c->replay = 1; - c->message_bounce = 0; - c->is_mp_safe = 0; - c->is_autoendian = 0; - c->tojson = tojson; - c->fromjson = fromjson; - c->print_json = print_json; - vl_msg_api_config (c); -} - void vl_msg_api_clean_handlers (int msg_id) { @@ -988,10 +792,10 @@ void vl_msg_api_set_cleanup_handler (int msg_id, void *fp) { api_main_t *am = vlibapi_get_main (); + vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id); ASSERT (msg_id > 0); - vec_validate (am->msg_cleanup_handlers, msg_id); - am->msg_cleanup_handlers[msg_id] = fp; + m->cleanup_handler = fp; } void @@ -999,8 +803,12 @@ vl_msg_api_queue_handler (svm_queue_t * q) { uword msg; - while (!svm_queue_sub (q, (u8 *) & msg, SVM_Q_WAIT, 0)) - vl_msg_api_handler ((void *) msg); + while (!svm_queue_sub (q, (u8 *) &msg, SVM_Q_WAIT, 0)) + { + VL_MSG_API_UNPOISON ((u8 *) msg); + msgbuf_t *msgbuf = (msgbuf_t *) ((u8 *) msg - offsetof (msgbuf_t, data)); + vl_msg_api_handler ((void *) msg, ntohl (msgbuf->data_len)); + } } u32 @@ -1032,12 +840,6 @@ vl_msg_api_trace_get (api_main_t * am, vl_api_trace_which_t which) } } -void -vl_noop_handler (void *mp) -{ -} - - static u8 post_mortem_dump_enabled; void @@ -1250,8 +1052,9 @@ vl_api_from_api_to_new_vec (void *mp, vl_api_string_t * astr) u8 *v = 0; if (vl_msg_api_max_length (mp) < clib_net_to_host_u32 (astr->length)) - return format (0, "insane astr->length %u%c", - clib_net_to_host_u32 (astr->length), 0); + return format (0, "Invalid astr->length %u > max (%u)%c", + clib_net_to_host_u32 (astr->length), + vl_msg_api_max_length (mp), 0); vec_add (v, astr->buf, clib_net_to_host_u32 (astr->length)); return v; } diff --git a/src/vlibapi/memory_shared.c b/src/vlibapi/memory_shared.c new file mode 100644 index 00000000000..efa45e1f5b5 --- /dev/null +++ b/src/vlibapi/memory_shared.c @@ -0,0 +1,815 @@ +/* + *------------------------------------------------------------------ + * memclnt_shared.c - API message handling, common code for both clients + * and the vlib process itself. + * + * + * Copyright (c) 2009 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> + +#include <vppinfra/format.h> +#include <vppinfra/byte_order.h> +#include <vppinfra/error.h> +#include <vppinfra/elog.h> +#include <svm/queue.h> +#include <vlib/vlib.h> +#include <vlib/unix/unix.h> +#include <vlibmemory/memory_api.h> +#include <vlibmemory/vl_memory_msg_enum.h> +#include <vlibapi/api_common.h> + +#define vl_typedefs +#include <vlibmemory/vl_memory_api_h.h> +#undef vl_typedefs + +#define DEBUG_MESSAGE_BUFFER_OVERRUN 0 + +__clib_nosanitize_addr static inline void * +vl_msg_api_alloc_internal (svm_region_t *vlib_rp, int nbytes, int pool, + int may_return_null) +{ + int i; + msgbuf_t *rv; + ring_alloc_t *ap; + svm_queue_t *q; + void *oldheap; + vl_shmem_hdr_t *shmem_hdr; + api_main_t *am = vlibapi_get_main (); + + shmem_hdr = (void *) vlib_rp->user_ctx; + +#if DEBUG_MESSAGE_BUFFER_OVERRUN > 0 + nbytes += 4; +#endif + + ASSERT (pool == 0 || vlib_get_thread_index () == 0); + + if (shmem_hdr == 0) + { + clib_warning ("shared memory header NULL"); + return 0; + } + + /* account for the msgbuf_t header */ + nbytes += sizeof (msgbuf_t); + + if (shmem_hdr->vl_rings == 0) + { + clib_warning ("vl_rings NULL"); + ASSERT (0); + abort (); + } + + if (shmem_hdr->client_rings == 0) + { + clib_warning ("client_rings NULL"); + ASSERT (0); + abort (); + } + + ap = pool ? shmem_hdr->vl_rings : shmem_hdr->client_rings; + for (i = 0; i < vec_len (ap); i++) + { + /* Too big? */ + if (nbytes > ap[i].size) + { + continue; + } + + q = ap[i].rp; + if (pool == 0) + { + pthread_mutex_lock (&q->mutex); + } + rv = (msgbuf_t *) (&q->data[0] + q->head * q->elsize); + /* + * Is this item still in use? + */ + if (rv->q) + { + u32 now = (u32) time (0); + + if (PREDICT_TRUE (rv->gc_mark_timestamp == 0)) + rv->gc_mark_timestamp = now; + else + { + if (now - rv->gc_mark_timestamp > 10) + { + if (CLIB_DEBUG > 0) + { + u16 *msg_idp, msg_id; + vl_api_msg_data_t *m; + clib_warning + ("garbage collect pool %d ring %d index %d", pool, i, + q->head); + msg_idp = (u16 *) (rv->data); + msg_id = clib_net_to_host_u16 (*msg_idp); + m = vl_api_get_msg_data (am, msg_id); + if (m) + clib_warning ("msg id %d name %s", (u32) msg_id, + m->name); + } + shmem_hdr->garbage_collects++; + goto collected; + } + } + + + /* yes, loser; try next larger pool */ + ap[i].misses++; + if (pool == 0) + pthread_mutex_unlock (&q->mutex); + continue; + } + collected: + + /* OK, we have a winner */ + ap[i].hits++; + /* + * Remember the source queue, although we + * don't need to know the queue to free the item. + */ + rv->q = q; + rv->gc_mark_timestamp = 0; + q->head++; + if (q->head == q->maxsize) + q->head = 0; + + if (pool == 0) + pthread_mutex_unlock (&q->mutex); + goto out; + } + + /* + * Request too big, or head element of all size-compatible rings + * still in use. Fall back to shared-memory malloc. + */ + am->ring_misses++; + + oldheap = vl_msg_push_heap_w_region (vlib_rp); + if (may_return_null) + { + rv = clib_mem_alloc_or_null (nbytes); + if (PREDICT_FALSE (rv == 0)) + { + vl_msg_pop_heap_w_region (vlib_rp, oldheap); + return 0; + } + } + else + rv = clib_mem_alloc (nbytes); + + rv->q = 0; + rv->gc_mark_timestamp = 0; + vl_msg_pop_heap_w_region (vlib_rp, oldheap); + +out: +#if DEBUG_MESSAGE_BUFFER_OVERRUN > 0 + { + nbytes -= 4; + u32 *overrun; + overrun = (u32 *) (rv->data + nbytes - sizeof (msgbuf_t)); + *overrun = 0x1badbabe; + } +#endif + rv->data_len = htonl (nbytes - sizeof (msgbuf_t)); + + VL_MSG_API_UNPOISON (rv->data); + return (rv->data); +} + +void * +vl_msg_api_alloc (int nbytes) +{ + int pool; + api_main_t *am = vlibapi_get_main (); + vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr; + + /* + * Clients use pool-0, vlib proc uses pool 1 + */ + pool = (am->our_pid == shmem_hdr->vl_pid); + return vl_msg_api_alloc_internal (am->vlib_rp, nbytes, pool, + 0 /* may_return_null */ ); +} + +void * +vl_msg_api_alloc_zero (int nbytes) +{ + void *ret; + + ret = vl_msg_api_alloc (nbytes); + clib_memset (ret, 0, nbytes); + return ret; +} + +void * +vl_msg_api_alloc_or_null (int nbytes) +{ + int pool; + api_main_t *am = vlibapi_get_main (); + vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr; + + pool = (am->our_pid == shmem_hdr->vl_pid); + return vl_msg_api_alloc_internal (am->vlib_rp, nbytes, pool, + 1 /* may_return_null */ ); +} + +void * +vl_msg_api_alloc_as_if_client (int nbytes) +{ + api_main_t *am = vlibapi_get_main (); + return vl_msg_api_alloc_internal (am->vlib_rp, nbytes, 0, + 0 /* may_return_null */ ); +} + +void * +vl_msg_api_alloc_zero_as_if_client (int nbytes) +{ + void *ret; + + ret = vl_msg_api_alloc_as_if_client (nbytes); + clib_memset (ret, 0, nbytes); + return ret; +} + +void * +vl_msg_api_alloc_as_if_client_or_null (int nbytes) +{ + api_main_t *am = vlibapi_get_main (); + return vl_msg_api_alloc_internal (am->vlib_rp, nbytes, 0, + 1 /* may_return_null */ ); +} + +void * +vl_mem_api_alloc_as_if_client_w_reg (vl_api_registration_t * reg, int nbytes) +{ + return vl_msg_api_alloc_internal (reg->vlib_rp, nbytes, 0, + 0 /* may_return_null */ ); +} + +void +vl_msg_api_free_w_region (svm_region_t * vlib_rp, void *a) +{ + msgbuf_t *rv; + void *oldheap; + + rv = (msgbuf_t *) (((u8 *) a) - offsetof (msgbuf_t, data)); + + /* + * Here's the beauty of the scheme. Only one proc/thread has + * control of a given message buffer. To free a buffer, we just clear the + * queue field, and leave. No locks, no hits, no errors... + */ + if (rv->q) + { + rv->q = 0; + rv->gc_mark_timestamp = 0; +#if DEBUG_MESSAGE_BUFFER_OVERRUN > 0 + { + u32 *overrun; + overrun = (u32 *) (rv->data + ntohl (rv->data_len)); + ASSERT (*overrun == 0x1badbabe); + } +#endif + VL_MSG_API_POISON (rv->data); + return; + } + + oldheap = vl_msg_push_heap_w_region (vlib_rp); + +#if DEBUG_MESSAGE_BUFFER_OVERRUN > 0 + { + u32 *overrun; + overrun = (u32 *) (rv->data + ntohl (rv->data_len)); + ASSERT (*overrun == 0x1badbabe); + } +#endif + + clib_mem_free (rv); + vl_msg_pop_heap_w_region (vlib_rp, oldheap); +} + +void +vl_msg_api_free (void *a) +{ + api_main_t *am = vlibapi_get_main (); + vl_msg_api_free_w_region (am->vlib_rp, a); +} + +static void +vl_msg_api_free_nolock (void *a) +{ + msgbuf_t *rv; + void *oldheap; + api_main_t *am = vlibapi_get_main (); + + rv = (msgbuf_t *) (((u8 *) a) - offsetof (msgbuf_t, data)); + /* + * Here's the beauty of the scheme. Only one proc/thread has + * control of a given message buffer. To free a buffer, we just clear the + * queue field, and leave. No locks, no hits, no errors... + */ + if (rv->q) + { + rv->q = 0; + VL_MSG_API_POISON (rv->data); + return; + } + + oldheap = svm_push_data_heap (am->vlib_rp); + clib_mem_free (rv); + svm_pop_heap (oldheap); +} + +void +vl_set_memory_root_path (const char *name) +{ + api_main_t *am = vlibapi_get_main (); + + am->root_path = name; +} + +void +vl_set_memory_uid (int uid) +{ + api_main_t *am = vlibapi_get_main (); + + am->api_uid = uid; +} + +void +vl_set_memory_gid (int gid) +{ + api_main_t *am = vlibapi_get_main (); + + am->api_gid = gid; +} + +void +vl_set_global_memory_baseva (u64 baseva) +{ + api_main_t *am = vlibapi_get_main (); + + am->global_baseva = baseva; +} + +void +vl_set_global_memory_size (u64 size) +{ + api_main_t *am = vlibapi_get_main (); + + am->global_size = size; +} + +void +vl_set_api_memory_size (u64 size) +{ + api_main_t *am = vlibapi_get_main (); + + am->api_size = size; +} + +void +vl_set_global_pvt_heap_size (u64 size) +{ + api_main_t *am = vlibapi_get_main (); + + am->global_pvt_heap_size = size; +} + +void +vl_set_api_pvt_heap_size (u64 size) +{ + api_main_t *am = vlibapi_get_main (); + + am->api_pvt_heap_size = size; +} + +static void +vl_api_default_mem_config (vl_shmem_hdr_t * shmem_hdr) +{ + api_main_t *am = vlibapi_get_main (); + u32 vlib_input_queue_length; + + /* vlib main input queue */ + vlib_input_queue_length = 1024; + if (am->vlib_input_queue_length) + vlib_input_queue_length = am->vlib_input_queue_length; + + shmem_hdr->vl_input_queue = + svm_queue_alloc_and_init (vlib_input_queue_length, sizeof (uword), + getpid ()); + +#define _(sz,n) \ + do { \ + ring_alloc_t _rp; \ + _rp.rp = svm_queue_alloc_and_init ((n), (sz), 0); \ + _rp.size = (sz); \ + _rp.nitems = n; \ + _rp.hits = 0; \ + _rp.misses = 0; \ + vec_add1(shmem_hdr->vl_rings, _rp); \ + } while (0); + + foreach_vl_aring_size; +#undef _ + +#define _(sz,n) \ + do { \ + ring_alloc_t _rp; \ + _rp.rp = svm_queue_alloc_and_init ((n), (sz), 0); \ + _rp.size = (sz); \ + _rp.nitems = n; \ + _rp.hits = 0; \ + _rp.misses = 0; \ + vec_add1(shmem_hdr->client_rings, _rp); \ + } while (0); + + foreach_clnt_aring_size; +#undef _ +} + +void +vl_api_mem_config (vl_shmem_hdr_t * hdr, vl_api_shm_elem_config_t * config) +{ + vl_api_shm_elem_config_t *c; + ring_alloc_t *rp; + u32 size; + + if (!config) + { + vl_api_default_mem_config (hdr); + return; + } + + vec_foreach (c, config) + { + switch (c->type) + { + case VL_API_QUEUE: + hdr->vl_input_queue = svm_queue_alloc_and_init (c->count, c->size, + getpid ()); + continue; + case VL_API_VLIB_RING: + vec_add2 (hdr->vl_rings, rp, 1); + break; + case VL_API_CLIENT_RING: + vec_add2 (hdr->client_rings, rp, 1); + break; + default: + clib_warning ("unknown config type: %d", c->type); + continue; + } + + size = sizeof (ring_alloc_t) + c->size; + rp->rp = svm_queue_alloc_and_init (c->count, size, 0); + rp->size = size; + rp->nitems = c->count; + rp->hits = 0; + rp->misses = 0; + } +} + +void +vl_init_shmem (svm_region_t * vlib_rp, vl_api_shm_elem_config_t * config, + int is_vlib, int is_private_region) +{ + api_main_t *am = vlibapi_get_main (); + vl_shmem_hdr_t *shmem_hdr = 0; + void *oldheap; + ASSERT (vlib_rp); + + /* $$$$ need private region config parameters */ + + oldheap = svm_push_data_heap (vlib_rp); + + vec_validate (shmem_hdr, 0); + shmem_hdr->version = VL_SHM_VERSION; + shmem_hdr->clib_file_index = VL_API_INVALID_FI; + + /* Set up the queue and msg ring allocator */ + vl_api_mem_config (shmem_hdr, config); + + if (is_private_region == 0) + { + am->shmem_hdr = shmem_hdr; + am->vlib_rp = vlib_rp; + am->our_pid = getpid (); + if (is_vlib) + am->shmem_hdr->vl_pid = am->our_pid; + } + else + shmem_hdr->vl_pid = am->our_pid; + + svm_pop_heap (oldheap); + + /* + * After absolutely everything that a client might see is set up, + * declare the shmem region valid + */ + vlib_rp->user_ctx = shmem_hdr; + + pthread_mutex_unlock (&vlib_rp->mutex); +} + +int +vl_map_shmem (const char *region_name, int is_vlib) +{ + svm_map_region_args_t _a, *a = &_a; + svm_region_t *vlib_rp, *root_rp; + api_main_t *am = vlibapi_get_main (); + int i; + struct timespec ts, tsrem; + char *vpe_api_region_suffix = "-vpe-api"; + + clib_memset (a, 0, sizeof (*a)); + + if (strstr (region_name, vpe_api_region_suffix)) + { + u8 *root_path = format (0, "%s", region_name); + vec_set_len (root_path, + vec_len (root_path) - strlen (vpe_api_region_suffix)); + vec_terminate_c_string (root_path); + a->root_path = (const char *) root_path; + am->root_path = (const char *) root_path; + } + + if (is_vlib == 0) + { + int tfd; + u8 *api_name; + /* + * Clients wait for vpp to set up the root / API regioins + */ + if (am->root_path) + api_name = format (0, "/dev/shm/%s-%s%c", am->root_path, + region_name + 1, 0); + else + api_name = format (0, "/dev/shm%s%c", region_name, 0); + + /* Wait up to 100 seconds... */ + for (i = 0; i < 10000; i++) + { + ts.tv_sec = 0; + ts.tv_nsec = 10000 * 1000; /* 10 ms */ + while (nanosleep (&ts, &tsrem) < 0) + ts = tsrem; + tfd = open ((char *) api_name, O_RDWR); + if (tfd >= 0) + break; + } + vec_free (api_name); + if (tfd < 0) + { + clib_warning ("region init fail"); + return -2; + } + close (tfd); + svm_region_init_chroot_uid_gid (am->root_path, getuid (), getgid ()); + } + + if (a->root_path != NULL) + { + a->name = "/vpe-api"; + } + else + a->name = region_name; + a->size = am->api_size ? am->api_size : (16 << 20); + a->flags = SVM_FLAGS_MHEAP; + a->uid = am->api_uid; + a->gid = am->api_gid; + a->pvt_heap_size = am->api_pvt_heap_size; + + vlib_rp = svm_region_find_or_create (a); + + if (vlib_rp == 0) + return (-2); + + pthread_mutex_lock (&vlib_rp->mutex); + /* Has someone else set up the shared-memory variable table? */ + if (vlib_rp->user_ctx) + { + am->shmem_hdr = (void *) vlib_rp->user_ctx; + am->our_pid = getpid (); + if (is_vlib) + { + svm_queue_t *q; + uword old_msg; + /* + * application restart. Reset cached pids, API message + * rings, list of clients; otherwise, various things + * fail. (e.g. queue non-empty notification) + */ + + /* ghosts keep the region from disappearing properly */ + svm_client_scan_this_region_nolock (vlib_rp); + am->shmem_hdr->application_restarts++; + q = am->shmem_hdr->vl_input_queue; + am->shmem_hdr->vl_pid = getpid (); + q->consumer_pid = am->shmem_hdr->vl_pid; + /* Drain the input queue, freeing msgs */ + for (i = 0; i < 10; i++) + { + if (pthread_mutex_trylock (&q->mutex) == 0) + { + pthread_mutex_unlock (&q->mutex); + goto mutex_ok; + } + ts.tv_sec = 0; + ts.tv_nsec = 10000 * 1000; /* 10 ms */ + while (nanosleep (&ts, &tsrem) < 0) + ts = tsrem; + } + /* Mutex buggered, "fix" it */ + clib_memset (&q->mutex, 0, sizeof (q->mutex)); + clib_warning ("forcibly release main input queue mutex"); + + mutex_ok: + am->vlib_rp = vlib_rp; + while (svm_queue_sub (q, (u8 *) & old_msg, SVM_Q_NOWAIT, 0) + != -2 /* queue underflow */ ) + { + vl_msg_api_free_nolock ((void *) old_msg); + am->shmem_hdr->restart_reclaims++; + } + pthread_mutex_unlock (&vlib_rp->mutex); + root_rp = svm_get_root_rp (); + ASSERT (root_rp); + /* Clean up the root region client list */ + pthread_mutex_lock (&root_rp->mutex); + svm_client_scan_this_region_nolock (root_rp); + pthread_mutex_unlock (&root_rp->mutex); + } + else + { + pthread_mutex_unlock (&vlib_rp->mutex); + } + am->vlib_rp = vlib_rp; + vec_add1 (am->mapped_shmem_regions, vlib_rp); + return 0; + } + /* Clients simply have to wait... */ + if (!is_vlib) + { + pthread_mutex_unlock (&vlib_rp->mutex); + + /* Wait up to 100 seconds... */ + for (i = 0; i < 10000; i++) + { + ts.tv_sec = 0; + ts.tv_nsec = 10000 * 1000; /* 10 ms */ + while (nanosleep (&ts, &tsrem) < 0) + ts = tsrem; + if (vlib_rp->user_ctx) + goto ready; + } + /* Clean up and leave... */ + svm_region_unmap (vlib_rp); + clib_warning ("region init fail"); + return (-2); + + ready: + am->shmem_hdr = (void *) vlib_rp->user_ctx; + am->our_pid = getpid (); + am->vlib_rp = vlib_rp; + vec_add1 (am->mapped_shmem_regions, vlib_rp); + return 0; + } + + /* Nope, it's our problem... */ + vl_init_shmem (vlib_rp, 0 /* default config */ , 1 /* is vlib */ , + 0 /* is_private_region */ ); + + vec_add1 (am->mapped_shmem_regions, vlib_rp); + return 0; +} + +void +vl_register_mapped_shmem_region (svm_region_t * rp) +{ + api_main_t *am = vlibapi_get_main (); + + vec_add1 (am->mapped_shmem_regions, rp); +} + +static void +vl_unmap_shmem_internal (u8 is_client) +{ + svm_region_t *rp; + int i; + api_main_t *am = vlibapi_get_main (); + + if (!svm_get_root_rp ()) + return; + + for (i = 0; i < vec_len (am->mapped_shmem_regions); i++) + { + rp = am->mapped_shmem_regions[i]; + is_client ? svm_region_unmap_client (rp) : svm_region_unmap (rp); + } + + vec_free (am->mapped_shmem_regions); + am->shmem_hdr = 0; + + is_client ? svm_region_exit_client () : svm_region_exit (); + + vec_free (am->msg_data); +} + +void +vl_unmap_shmem (void) +{ + vl_unmap_shmem_internal (0); +} + +void +vl_unmap_shmem_client (void) +{ + vl_unmap_shmem_internal (1); +} + +void +vl_msg_api_send_shmem (svm_queue_t * q, u8 * elem) +{ + api_main_t *am = vlibapi_get_main (); + void *msg = (void *) *(uword *) elem; + + if (am->tx_trace && am->tx_trace->enabled) + vl_msg_api_trace (am, am->tx_trace, msg); + + /* + * Announce a probable binary API client bug: + * some client's input queue is stuffed. + * The situation may be recoverable, or not. + */ + if (PREDICT_FALSE + (am->vl_clients /* vpp side */ && (q->cursize == q->maxsize))) + { + if (PREDICT_FALSE (am->elog_trace_api_messages)) + { + ELOG_TYPE_DECLARE (e) = + { + .format = "api-client-queue-stuffed: %x%x", + .format_args = "i4i4", + }; + struct + { + u32 hi, low; + } *ed; + ed = ELOG_DATA (am->elog_main, e); + ed->hi = (uword) q >> 32; + ed->low = (uword) q & 0xFFFFFFFF; + clib_warning ("WARNING: client input queue at %llx is stuffed...", + q); + } + } + VL_MSG_API_POISON (msg); + (void) svm_queue_add (q, elem, 0 /* nowait */ ); +} + +int +vl_mem_api_can_send (svm_queue_t * q) +{ + return (q->cursize < q->maxsize); +} + +void +vl_msg_api_send_shmem_nolock (svm_queue_t * q, u8 * elem) +{ + api_main_t *am = vlibapi_get_main (); + void *msg = (void *) *(uword *) elem; + + if (am->tx_trace && am->tx_trace->enabled) + vl_msg_api_trace (am, am->tx_trace, msg); + + (void) svm_queue_add_nolock (q, elem); + VL_MSG_API_POISON (msg); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vlibapi/memory_shared.h b/src/vlibapi/memory_shared.h new file mode 100644 index 00000000000..4c4773d060b --- /dev/null +++ b/src/vlibapi/memory_shared.h @@ -0,0 +1,147 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#ifndef SRC_VLIBMEMORY_MEMORY_SHARED_H_ +#define SRC_VLIBMEMORY_MEMORY_SHARED_H_ + +#include <vlibapi/api_common.h> +#include <vppinfra/error.h> + +/* Allocated in shared memory */ + +/* + * Ring-allocation scheme for client API messages + * + * Only one proc/thread has control of a given message buffer. + * To free a buffer allocated from one of these rings, we clear + * a field in the buffer (header), and leave. + * + * No locks, no hits, no errors... + */ +typedef struct ring_alloc_ +{ + svm_queue_t *rp; + u16 size; + u16 nitems; + u32 hits; + u32 misses; +} ring_alloc_t; + +typedef enum +{ + VL_API_VLIB_RING, + VL_API_CLIENT_RING, + VL_API_QUEUE +} vl_api_shm_config_type_t; + +typedef struct vl_api_shm_elem_config_ +{ + u8 type; + u8 _pad; + u16 count; + u32 size; +} vl_api_shm_elem_config_t; + +STATIC_ASSERT (sizeof (vl_api_shm_elem_config_t) == 8, + "Size must be exactly 8 bytes"); + +/* + * Initializers for the (shared-memory) rings + * _(size, n). Note: each msg has space for a header. + */ +#define foreach_vl_aring_size \ +_(64+sizeof(ring_alloc_t), 1024) \ +_(256+sizeof(ring_alloc_t), 128) \ +_(1024+sizeof(ring_alloc_t), 64) + +#define foreach_clnt_aring_size \ + _(1024+sizeof(ring_alloc_t), 1024) \ + _(2048+sizeof(ring_alloc_t), 128) \ + _(4096+sizeof(ring_alloc_t), 8) + +typedef struct vl_shmem_hdr_ +{ + int version; + + /* getpid () for the VLIB client process */ + volatile int vl_pid; + + /* Client sends VLIB msgs here. */ + svm_queue_t *vl_input_queue; + + /* Vector of rings; one for each size. */ + + /* VLIB allocates buffers to send msgs to clients here. */ + ring_alloc_t *vl_rings; + + /* Clients allocate buffer to send msgs to VLIB here. */ + ring_alloc_t *client_rings; + + /* Number of detected application restarts */ + u32 application_restarts; + + /* Number of messages reclaimed during application restart */ + u32 restart_reclaims; + + /* Number of garbage-collected messages */ + u32 garbage_collects; + + /* Socket file index used to bootstrap shmem region */ + u32 clib_file_index; +} vl_shmem_hdr_t; + +#define VL_SHM_VERSION 2 +#define VL_API_EPOCH_MASK 0xFF +#define VL_API_EPOCH_SHIFT 8 + +void *vl_msg_api_alloc (int nbytes); +void *vl_msg_api_alloc_zero (int nbytes); +void *vl_msg_api_alloc_or_null (int nbytes); +void *vl_msg_api_alloc_as_if_client (int nbytes); +void *vl_msg_api_alloc_zero_as_if_client (int nbytes); +void *vl_msg_api_alloc_as_if_client_or_null (int nbytes); +void *vl_mem_api_alloc_as_if_client_w_reg (vl_api_registration_t * reg, + int nbytes); +void vl_msg_api_free (void *a); +void vl_msg_api_free_w_region (svm_region_t * vlib_rp, void *a); +int vl_map_shmem (const char *region_name, int is_vlib); +void vl_unmap_shmem (void); +void vl_unmap_shmem_client (void); +void vl_register_mapped_shmem_region (svm_region_t * rp); +void vl_msg_api_send_shmem (svm_queue_t * q, u8 * elem); +int vl_mem_api_can_send (svm_queue_t * q); +void vl_set_memory_region_name (const char *name); +void vl_set_memory_root_path (const char *root_path); +void vl_set_memory_uid (int uid); +void vl_set_memory_gid (int gid); +void vl_set_global_memory_baseva (u64 baseva); +void vl_set_global_memory_size (u64 size); +void vl_set_api_memory_size (u64 size); +void vl_set_global_pvt_heap_size (u64 size); +void vl_set_api_pvt_heap_size (u64 size); +void vl_init_shmem (svm_region_t * vlib_rp, vl_api_shm_elem_config_t * config, + int is_vlib, int is_private_region); + +#endif /* SRC_VLIBMEMORY_MEMORY_SHARED_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vlibapi/node_serialize.c b/src/vlibapi/node_serialize.c index b50d79e2922..09b59247eab 100644 --- a/src/vlibapi/node_serialize.c +++ b/src/vlibapi/node_serialize.c @@ -176,7 +176,7 @@ vlib_node_unserialize (u8 * vector) nstat_vms = unserialize_likely_small_unsigned_integer (sm); vec_validate (nodes_by_thread, nstat_vms - 1); - _vec_len (nodes_by_thread) = 0; + vec_set_len (nodes_by_thread, 0); for (i = 0; i < nstat_vms; i++) { @@ -326,13 +326,11 @@ test_node_serialize_command_fn (vlib_main_t * vm, return 0; } -/* *INDENT-OFF* */ VLIB_CLI_COMMAND (test_node_serialize_node, static) = { .path = "test node serialize", .short_help = "test node serialize [max-threads NN] nexts stats", .function = test_node_serialize_command_fn, }; -/* *INDENT-ON* */ #endif /* |