diff options
Diffstat (limited to 'src/vlibapi')
-rw-r--r-- | src/vlibapi/api.h | 131 | ||||
-rw-r--r-- | src/vlibapi/api_common.h | 330 | ||||
-rw-r--r-- | src/vlibapi/api_doc.md | 352 | ||||
-rw-r--r-- | src/vlibapi/api_helper_macros.h | 261 | ||||
-rw-r--r-- | src/vlibapi/api_shared.c | 925 | ||||
-rw-r--r-- | src/vlibapi/node_serialize.c | 394 | ||||
-rw-r--r-- | src/vlibapi/node_unserialize.c | 227 | ||||
-rw-r--r-- | src/vlibapi/vat_helper_macros.h | 76 |
8 files changed, 2696 insertions, 0 deletions
diff --git a/src/vlibapi/api.h b/src/vlibapi/api.h new file mode 100644 index 00000000..49f5d5fb --- /dev/null +++ b/src/vlibapi/api.h @@ -0,0 +1,131 @@ +/* + *------------------------------------------------------------------ + * api.h + * + * Copyright (c) 2009-2015 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 included_api_h +#define included_api_h + +#include <stddef.h> +#include <vppinfra/error.h> +#include <svm/svm.h> +#include <vlib/vlib.h> +#include <vlibmemory/unix_shared_memory_queue.h> +#include <vlib/unix/unix.h> +#include <vlibapi/api_common.h> + +/* *INDENT-OFF* */ +typedef CLIB_PACKED +(struct + { + u8 endian; u8 wrapped; + u32 nitems; +}) 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); + +#define VLIB_API_INIT_FUNCTION(x) VLIB_DECLARE_INIT_FUNCTION(x,api_init) + +/* Call given init function: used for init function dependencies. */ +#define vlib_call_api_init_function(vm, x) \ + ({ \ + extern vlib_init_function_t * _VLIB_INIT_FUNCTION_SYMBOL (x,api_init); \ + vlib_init_function_t * _f = _VLIB_INIT_FUNCTION_SYMBOL (x,api_init); \ + clib_error_t * _error = 0; \ + if (! hash_get (vm->init_functions_called, _f)) \ + { \ + hash_set1 (vm->init_functions_called, _f); \ + _error = _f (vm); \ + } \ + _error; \ + }) + + +#define _VL_MSG_API_FUNCTION_SYMBOL(x, type) \ + _vl_msg_api_##type##_function_##x + +#define VL_MSG_API_FUNCTION_SYMBOL(x) \ + _VL_MSG_API_FUNCTION_SYMBOL(x, reaper) + +#define VLIB_DECLARE_REAPER_FUNCTION(x, tag) \ +vl_msg_api_init_function_t * _VL_MSG_API_FUNCTION_SYMBOL (x, tag) = x; \ +static void __vl_msg_api_add_##tag##_function_##x (void) \ + __attribute__((__constructor__)) ; \ + \ +static void __vl_msg_api_add_##tag##_function_##x (void) \ +{ \ + api_main_t * am = &api_main; \ + static _vl_msg_api_function_list_elt_t _vl_msg_api_function; \ + _vl_msg_api_function.next_init_function \ + = am->tag##_function_registrations; \ + am->tag##_function_registrations = &_vl_msg_api_function; \ + _vl_msg_api_function.f = &x; \ +} + +#define VL_MSG_API_REAPER_FUNCTION(x) VLIB_DECLARE_REAPER_FUNCTION(x,reaper) + +/* Call reaper function with client index */ +#define vl_msg_api_call_reaper_function(ci) \ + ({ \ + extern vlib_init_function_t * VLIB_INIT_FUNCTION_SYMBOL (reaper); \ + vlib_init_function_t * _f = VLIB_INIT_FUNCTION_SYMBOL (reaper); \ + clib_error_t * _error = 0; \ + _error = _f (ci); \ + }) + +static inline u32 +vl_msg_api_get_msg_length_inline (void *msg_arg) +{ + u8 *msg = (u8 *) msg_arg; + + msgbuf_t *header = (msgbuf_t *) (msg - offsetof (msgbuf_t, data)); + + return clib_net_to_host_u32 (header->data_len); +} + +int vl_msg_api_rx_trace_enabled (api_main_t * am); +int vl_msg_api_tx_trace_enabled (api_main_t * am); +void vl_msg_api_trace (api_main_t * am, vl_api_trace_t * tp, void *msg); +int vl_msg_api_trace_onoff (api_main_t * am, vl_api_trace_which_t which, + int onoff); +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, + void *the_msg, vlib_main_t * vm, + vlib_node_runtime_t * node); +vl_api_trace_t *vl_msg_api_trace_get (api_main_t * am, + vl_api_trace_which_t which); +void vl_msg_api_add_msg_name_crc (api_main_t * am, const char *string, + u32 id); +/* node_serialize.c prototypes */ +u8 *vlib_node_serialize (vlib_node_main_t * nm, u8 * vector, + u32 max_threads, int include_nexts, + int include_stats); +vlib_node_t **vlib_node_unserialize (u8 * vector); +u32 vl_msg_api_get_msg_length (void *msg_arg); + +#endif /* included_api_h */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vlibapi/api_common.h b/src/vlibapi/api_common.h new file mode 100644 index 00000000..8e6ab0ff --- /dev/null +++ b/src/vlibapi/api_common.h @@ -0,0 +1,330 @@ +/* + *------------------------------------------------------------------ + * api_common.h + * + * Copyright (c) 2009-2015 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 included_api_common_h +#define included_api_common_h + +/** \file API common definitions + * See api_doc.md for more info + */ + +#include <vppinfra/clib_error.h> +#include <svm/svm_common.h> +#include <vlibmemory/unix_shared_memory_queue.h> + +/** API registration types + */ +typedef enum +{ + REGISTRATION_TYPE_FREE = 0, + REGISTRATION_TYPE_SHMEM, /**< Shared memory connection */ + REGISTRATION_TYPE_SOCKET_LISTEN, /**< Socket listener */ + REGISTRATION_TYPE_SOCKET_SERVER, /**< Socket server */ + REGISTRATION_TYPE_SOCKET_CLIENT, /**< Socket client */ +} vl_registration_type_t; + +/** An API client registration, only in vpp/vlib */ + +typedef struct vl_api_registration_ +{ + vl_registration_type_t registration_type; /**< type */ + + /** Index in VLIB's brain (not shared memory). */ + u32 vl_api_registration_pool_index; + + u8 *name; /**< Client name */ + + /** shared memory only: pointer to client input queue */ + unix_shared_memory_queue_t *vl_input_queue; + + /* socket server and client */ + u32 clib_file_index; /**< Socket only: file index */ + i8 *unprocessed_input; /**< Socket only: pending input */ + u32 unprocessed_msg_length; /**< Socket only: unprocssed length */ + u8 *output_vector; /**< Socket only: output vecto */ + + /* socket client only */ + u32 server_handle; /**< Socket client only: server handle */ + u32 server_index; /**< Socket client only: server index */ +} vl_api_registration_t; + + +/** 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 + */ +typedef struct +{ + u8 endian; /**< trace endianness */ + u8 enabled; /**< trace is enabled */ + u8 wrapped; /**< trace has wrapped */ + u8 pad; + u32 nitems; /**< Number of trace records */ + u32 curindex; /**< Current index in circular buffer */ + u8 **traces; /**< Trace ring */ +} vl_api_trace_t; + +/** Trace RX / TX enum */ +typedef enum +{ + VL_API_TRACE_TX, + VL_API_TRACE_RX, +} vl_api_trace_which_t; + +#define VL_API_LITTLE_ENDIAN 0x00 +#define VL_API_BIG_ENDIAN 0x01 + +/** Message range (belonging to a plugin) */ +typedef struct +{ + u8 *name; /**< name of the plugin */ + u16 first_msg_id; /**< first assigned message ID */ + u16 last_msg_id; /**< last assigned message ID */ +} vl_api_msg_range_t; + +/** Message configuration definition */ +typedef struct +{ + int id; /**< the message ID */ + char *name; /**< the message name */ + u32 crc; /**< message definition CRC */ + void *handler; /**< the message handler */ + void *cleanup; /**< non-default message cleanup handler */ + void *endian; /**< message endian function */ + void *print; /**< message print function */ + 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? */ +} vl_msg_api_msg_config_t; + +/** Message header structure */ +typedef struct msgbuf_ +{ + unix_shared_memory_queue_t *q; /**< message allocated in this shmem ring */ + u32 data_len; /**< message length not including header */ + u32 gc_mark_timestamp; /**< message garbage collector mark TS */ + u8 data[0]; /**< actual message begins here */ +} msgbuf_t; + +/* 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_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 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); +void vl_msg_api_queue_handler (unix_shared_memory_queue_t * q); + +void vl_msg_api_barrier_sync (void) __attribute__ ((weak)); +void vl_msg_api_barrier_release (void) __attribute__ ((weak)); +#ifdef BARRIER_TRACING +void vl_msg_api_barrier_trace_context (const char *context) + __attribute__ ((weak)); +#else +#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); +u32 vl_api_get_msg_index (u8 * name_and_crc); + +typedef clib_error_t *(vl_msg_api_init_function_t) (u32 client_index); + +typedef struct _vl_msg_api_init_function_list_elt +{ + struct _vl_msg_api_init_function_list_elt *next_init_function; + vl_msg_api_init_function_t *f; +} _vl_msg_api_function_list_elt_t; + +/** API main structure, used by both vpp and binary API clients */ +typedef struct +{ + /** Message handler vector */ + void (**msg_handlers) (void *); + /** Plaform-dependent (aka hardware) message handler vector */ + int (**pd_msg_handlers) (void *, int); + + /** non-default message cleanup handler vector */ + void (**msg_cleanup_handlers) (void *); + + /** Message endian handler vector */ + void (**msg_endian_handlers) (void *); + + /** Message print function vector */ + void (**msg_print_handlers) (void *, void *); + + /** Message name vector */ + const char **msg_names; + + /** Don't automatically free message buffer vetor */ + u8 *message_bounce; + + /** Message is mp safe vector */ + u8 *is_mp_safe; + + /** Allocator ring vectors (in shared memory) */ + struct ring_alloc_ *arings; + + /** Number of times that the ring allocator failed */ + u32 ring_misses; + + /** Number of garbage-collected message buffers */ + u32 garbage_collects; + + /** Number of missing clients / failed message sends */ + u32 missing_clients; + + /** Received message trace configuration */ + vl_api_trace_t *rx_trace; + + /** Sent message trace configuration */ + vl_api_trace_t *tx_trace; + + /** Print every received message */ + int msg_print_flag; + + /** Current trace configuration */ + trace_cfg_t *api_trace_cfg; + + /** Current process PID */ + int our_pid; + + /** Binary api segment descriptor */ + svm_region_t *vlib_rp; + + /** Vector of all mapped shared-VM segments */ + svm_region_t **mapped_shmem_regions; + + /** Binary API shared-memory segment header pointer */ + struct vl_shmem_hdr_ *shmem_hdr; + + /** vlib/vpp only: vector of client registrations */ + vl_api_registration_t **vl_clients; + + /** vlib/vpp only: serialized (message, name, crc) table */ + u8 *serialized_message_table_in_shmem; + + /** First available message ID, for theplugin msg allocator */ + u16 first_available_msg_id; + + /** Message range by name hash */ + uword *msg_range_by_name; + + /** vector of message ranges */ + vl_api_msg_range_t *msg_ranges; + + /** uid for the api shared memory region */ + int api_uid; + + /** gid for the api shared memory region */ + int api_gid; + + /** base virtual address for global VM region */ + u64 global_baseva; + + /** size of the global VM region */ + u64 global_size; + + /** size of the API region */ + u64 api_size; + + /** size of the global VM private mheap */ + u64 global_pvt_heap_size; + + /** size of the api private mheap */ + u64 api_pvt_heap_size; + + /** Peer input queue pointer */ + unix_shared_memory_queue_t *vl_input_queue; + + /** + * All VLIB-side message handlers use my_client_index to identify + * the queue / client. This works in sim replay. + */ + int my_client_index; + /** + * This is the (shared VM) address of the registration, + * don't use it to id the connection since it can't possibly + * work in simulator replay. + */ + vl_api_registration_t *my_registration; + + /** (Historical) signal-based queue non-empty signal, to be removed */ + i32 vlib_signal; + + /** vpp/vlib input queue length */ + u32 vlib_input_queue_length; + + /** client message index hash table */ + uword *msg_index_by_name_and_crc; + + /** Shared VM binary API region name */ + const char *region_name; + + /** Chroot path to the shared memory API files */ + const char *root_path; + + /** Replay in progress? */ + int replay_in_progress; + + /** Dump (msg-name, crc) snapshot here at startup */ + u8 *save_msg_table_filename; + + /** List of API client reaper functions */ + _vl_msg_api_function_list_elt_t *reaper_function_registrations; + +} api_main_t; + +extern api_main_t api_main; + +#endif /* included_api_common_h */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vlibapi/api_doc.md b/src/vlibapi/api_doc.md new file mode 100644 index 00000000..2e7ae09a --- /dev/null +++ b/src/vlibapi/api_doc.md @@ -0,0 +1,352 @@ +# Binary API support {#api_doc} + +VPP provides a binary API scheme to allow a wide variety of client codes to +program data-plane tables. As of this writing, there are hundreds of binary +APIs. + +Messages are defined in `*.api` files. Today, there are about 50 api files, +with more arriving as folks add programmable features. The API file compiler +sources reside in @ref src/tools/vppapigen. + +From @ref src/vnet/interface.api, here's a typical request/response message +definition: + +```{.c} + autoreply define sw_interface_set_flags + { + u32 client_index; + u32 context; + u32 sw_if_index; + /* 1 = up, 0 = down */ + u8 admin_up_down; + }; +``` + +To a first approximation, the API compiler renders this definition into +`build-root/.../vpp/include/vnet/interface.api.h` as follows: + +```{.c} + /****** Message ID / handler enum ******/ + #ifdef vl_msg_id + vl_msg_id(VL_API_SW_INTERFACE_SET_FLAGS, vl_api_sw_interface_set_flags_t_handler) + vl_msg_id(VL_API_SW_INTERFACE_SET_FLAGS_REPLY, vl_api_sw_interface_set_flags_reply_t_handler) + #endif + + /****** Message names ******/ + #ifdef vl_msg_name + vl_msg_name(vl_api_sw_interface_set_flags_t, 1) + vl_msg_name(vl_api_sw_interface_set_flags_reply_t, 1) + #endif + + /****** Message name, crc list ******/ + #ifdef vl_msg_name_crc_list + #define foreach_vl_msg_name_crc_interface \ + _(VL_API_SW_INTERFACE_SET_FLAGS, sw_interface_set_flags, f890584a) \ + _(VL_API_SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply, dfbf3afa) \ + #endif + + /****** Typedefs *****/ + #ifdef vl_typedefs + typedef VL_API_PACKED(struct _vl_api_sw_interface_set_flags { + u16 _vl_msg_id; + u32 client_index; + u32 context; + u32 sw_if_index; + u8 admin_up_down; + }) vl_api_sw_interface_set_flags_t; + + typedef VL_API_PACKED(struct _vl_api_sw_interface_set_flags_reply { + u16 _vl_msg_id; + u32 context; + i32 retval; + }) vl_api_sw_interface_set_flags_reply_t; + + ... + #endif /* vl_typedefs */ +``` + +To change the admin state of an interface, a binary api client sends a +@ref vl_api_sw_interface_set_flags_t to VPP, which will respond with a +@ref vl_api_sw_interface_set_flags_reply_t message. + +Multiple layers of software, transport types, and shared libraries +implement a variety of features: + +* API message allocation, tracing, pretty-printing, and replay. +* Message transport via global shared memory, pairwise/private shared + memory, and sockets. +* Barrier synchronization of worker threads across thread-unsafe + message handlers. + +Correctly-coded message handlers know nothing about the transport used to +deliver messages to/from VPP. It's reasonably straighforward to use multiple +API message transport types simultaneously. + +For historical reasons, binary api messages are (putatively) sent in network +byte order. As of this writing, we're seriously considering whether that +choice makes sense. + + +## Message Allocation + +Since binary API messages are always processed in order, we allocate messages +using a ring allocator whenever possible. This scheme is extremely fast when +compared with a traditional memory allocator, and doesn't cause heap +fragmentation. See +@ref src/vlibmemory/memory_shared.c @ref vl_msg_api_alloc_internal(). + +Regardless of transport, binary api messages always follow a @ref msgbuf_t +header: + +```{.c} + typedef struct msgbuf_ + { + unix_shared_memory_queue_t *q; + u32 data_len; + u32 gc_mark_timestamp; + u8 data[0]; + } msgbuf_t; +``` + +This structure makes it easy to trace messages without having to +decode them - simply save data_len bytes - and allows +@ref vl_msg_api_free() to rapidly dispose of message buffers: + +```{.c} + void + vl_msg_api_free (void *a) + { + msgbuf_t *rv; + api_main_t *am = &api_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; + rv->gc_mark_timestamp = 0; + return; + } + <snip> + } +``` + +## Message Tracing and Replay + +It's extremely important that VPP can capture and replay sizeable binary API +traces. System-level issues involving hundreds of thousands of API +transactions can be re-run in a second or less. Partial replay allows one to +binary-search for the point where the wheels fall off. One can add scaffolding +to the data plane, to trigger when complex conditions obtain. + +With binary API trace, print, and replay, system-level bug reports of the form +"after 300,000 API transactions, the VPP data-plane stopped forwarding +traffic, FIX IT!" can be solved offline. + +More often than not, one discovers that a control-plane client +misprograms the data plane after a long time or under complex +circumstances. Without direct evidence, "it's a data-plane problem!" + +See @ref src/vlibmemory/memory_vlib.c @ref vl_msg_api_process_file(), +and @ref src/vlibapi/api_shared.c. See also the debug CLI command "api trace" + +## Client connection details + +Establishing a binary API connection to VPP from a C-language client +is easy: + +```{.c} + int + connect_to_vpe (char *client_name, int client_message_queue_length) + { + vat_main_t *vam = &vat_main; + api_main_t *am = &api_main; + + if (vl_client_connect_to_vlib ("/vpe-api", client_name, + client_message_queue_length) < 0) + return -1; + + /* Memorize vpp's binary API message input queue address */ + vam->vl_input_queue = am->shmem_hdr->vl_input_queue; + /* And our client index */ + vam->my_client_index = am->my_client_index; + return 0; + } +``` + +32 is a typical value for client_message_queue_length. VPP cannot +block when it needs to send an API message to a binary API client, and +the VPP-side binary API message handlers are very fast. When sending +asynchronous messages, make sure to scrape the binary API rx ring with +some enthusiasm. + +### binary API message RX pthread + +Calling @ref vl_client_connect_to_vlib spins up a binary API message RX +pthread: + +```{.c} + static void * + rx_thread_fn (void *arg) + { + unix_shared_memory_queue_t *q; + memory_client_main_t *mm = &memory_client_main; + api_main_t *am = &api_main; + + q = am->vl_input_queue; + + /* So we can make the rx thread terminate cleanly */ + if (setjmp (mm->rx_thread_jmpbuf) == 0) + { + mm->rx_thread_jmpbuf_valid = 1; + while (1) + { + vl_msg_api_queue_handler (q); + } + } + pthread_exit (0); + } +``` + +To handle the binary API message queue yourself, use +@ref vl_client_connect_to_vlib_no_rx_pthread. + +In turn, vl_msg_api_queue_handler(...) uses mutex/condvar signalling +to wake up, process VPP -> client traffic, then sleep. VPP supplies a +condvar broadcast when the VPP -> client API message queue transitions +from empty to nonempty. + +VPP checks its own binary API input queue at a very high rate. VPP +invokes message handlers in "process" context [aka cooperative +multitasking thread context] at a variable rate, depending on +data-plane packet processing requirements. + +## Client disconnection details + +To disconnect from VPP, call @ref vl_client_disconnect_from_vlib. +Please arrange to call this function if the client application +terminates abnormally. VPP makes every effort to hold a decent funeral +for dead clients, but VPP can't guarantee to free leaked memory in the +shared binary API segment. + +## Sending binary API messages to VPP + +The point of the exercise is to send binary API messages to VPP, and +to receive replies from VPP. Many VPP binary APIs comprise a client +request message, and a simple status reply. For example, to +set the admin status of an interface, one codes: + +```{.c} + vl_api_sw_interface_set_flags_t *mp; + + mp = vl_msg_api_alloc (sizeof (*mp)); + memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_SW_INTERFACE_SET_FLAGS); + mp->client_index = api_main.my_client_index; + mp->sw_if_index = clib_host_to_net_u32 (<interface-sw-if-index>); + vl_msg_api_send (api_main.shmem_hdr->vl_input_queue, (u8 *)mp); +``` + +Key points: + +* Use @ref vl_msg_api_alloc to allocate message buffers + +* Allocated message buffers are not initialized, and must be presumed + to contain trash. + +* Don't forget to set the _vl_msg_id field! + +* As of this writing, binary API message IDs and data are sent in + network byte order + +* The client-library global data structure @ref api_main keeps track + of sufficient pointers and handles used to communicate with VPP + +## Receiving binary API messages from VPP + +Unless you've made other arrangements (see @ref +vl_client_connect_to_vlib_no_rx_pthread), *messages are received on a +separate rx pthread*. Synchronization with the client application main +thread is the responsibility of the application! + +Set up message handlers about as follows: + +```{.c} + #define vl_typedefs /* define message structures */ + #include <vpp/api/vpe_all_api_h.h> + #undef vl_typedefs + + /* declare message handlers for each api */ + + #define vl_endianfun /* define message structures */ + #include <vpp/api/vpe_all_api_h.h> + #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 + + /* Define a list of all message that the client handles */ + #define foreach_vpe_api_reply_msg \ + _(SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply) + + static clib_error_t * + my_api_hookup (vlib_main_t * vm) + { + api_main_t *am = &api_main; + + #define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N, #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_vpe_api_msg; + #undef _ + + return 0; + } +``` + +The key API used to establish message handlers is @ref +vl_msg_api_set_handlers , which sets values in multiple parallel +vectors in the @ref api_main_t structure. As of this writing: not all +vector element values can be set through the API. You'll see sporadic +API message registrations followed by minor adjustments of this form: + +```{.c} + /* + * Thread-safe API messages + */ + am->is_mp_safe[VL_API_IP_ADD_DEL_ROUTE] = 1; + am->is_mp_safe[VL_API_GET_NODE_GRAPH] = 1; +``` + + + + + + + + + + + + + + + + + + + + + diff --git a/src/vlibapi/api_helper_macros.h b/src/vlibapi/api_helper_macros.h new file mode 100644 index 00000000..052cc6e7 --- /dev/null +++ b/src/vlibapi/api_helper_macros.h @@ -0,0 +1,261 @@ +/* + *------------------------------------------------------------------ + * api_helper_macros.h - message handler helper macros + * + * Copyright (c) 2016 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 __api_helper_macros_h__ +#define __api_helper_macros_h__ + +#define f64_endian(a) +#define f64_print(a,b) + +#ifndef REPLY_MSG_ID_BASE +#define REPLY_MSG_ID_BASE 0 +#endif + +#define REPLY_MACRO(t) \ +do { \ + unix_shared_memory_queue_t * q; \ + rv = vl_msg_api_pd_handler (mp, rv); \ + q = vl_api_client_index_to_input_queue (mp->client_index); \ + if (!q) \ + 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_msg_api_send_shmem (q, (u8 *)&rmp); \ +} while(0); + +#define REPLY_MACRO2(t, body) \ +do { \ + unix_shared_memory_queue_t * q; \ + rv = vl_msg_api_pd_handler (mp, rv); \ + q = vl_api_client_index_to_input_queue (mp->client_index); \ + if (!q) \ + 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_msg_api_send_shmem (q, (u8 *)&rmp); \ +} while(0); + +#define REPLY_MACRO3(t, n, body) \ +do { \ + unix_shared_memory_queue_t * q; \ + rv = vl_msg_api_pd_handler (mp, rv); \ + q = vl_api_client_index_to_input_queue (mp->client_index); \ + if (!q) \ + 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_msg_api_send_shmem (q, (u8 *)&rmp); \ +} while(0); + +#define REPLY_MACRO4(t, n, body) \ +do { \ + unix_shared_memory_queue_t * q; \ + u8 is_error = 0; \ + rv = vl_msg_api_pd_handler (mp, rv); \ + q = vl_api_client_index_to_input_queue (mp->client_index); \ + if (!q) \ + 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; \ + \ + memset (rmp, 0, sizeof (*rmp)); \ + rv = VNET_API_ERROR_TABLE_TOO_BIG; \ + is_error = 1; \ + } \ + rmp->_vl_msg_id = htons((t)+(REPLY_MSG_ID_BASE)); \ + rmp->context = mp->context; \ + rmp->retval = ntohl(rv); \ + if (!is_error) \ + do {body;} while (0); \ + vl_msg_api_send_shmem (q, (u8 *)&rmp); \ +} while(0); + +/* "trust, but verify" */ + +static inline uword +vnet_sw_if_index_is_api_valid (u32 sw_if_index) +{ + return vnet_sw_interface_is_api_valid (vnet_get_main (), sw_if_index); +} + +#define VALIDATE_SW_IF_INDEX(mp) \ + do { u32 __sw_if_index = ntohl(mp->sw_if_index); \ + if (!vnet_sw_if_index_is_api_valid(__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: \ + ; \ +} while (0); + +#define VALIDATE_RX_SW_IF_INDEX(mp) \ + do { u32 __rx_sw_if_index = ntohl(mp->rx_sw_if_index); \ + if (!vnet_sw_if_index_is_api_valid(__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: \ + ; \ +} while (0); + +#define VALIDATE_TX_SW_IF_INDEX(mp) \ + do { u32 __tx_sw_if_index = ntohl(mp->tx_sw_if_index); \ + if (!vnet_sw_if_index_is_api_valid(__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: \ + ; \ +} while (0); + +#define VALIDATE_BD_ID(mp) \ + do { u32 __rx_bd_id = ntohl(mp->bd_id); \ + if (__rx_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...", 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); \ +} + +#define foreach_registration_hash \ +_(interface_events) \ +_(to_netconf_server) \ +_(from_netconf_server) \ +_(to_netconf_client) \ +_(from_netconf_client) \ +_(oam_events) \ +_(bfd_events) \ +_(wc_ip6_nd_events) \ +_(wc_ip4_arp_events) + +typedef struct +{ + u32 client_index; /* in memclnt registration pool */ + u32 client_pid; +} vpe_client_registration_t; + +struct _vl_api_ip4_arp_event; +struct _vl_api_ip6_nd_event; + +typedef struct +{ +#define _(a) uword *a##_registration_hash; \ + vpe_client_registration_t * a##_registrations; + foreach_registration_hash +#undef _ + /* notifications happen really early in the game */ + u8 link_state_process_up; + + /* ip4 arp event registration pool */ + struct _vl_api_ip4_arp_event *arp_events; + + /* ip6 nd event registration pool */ + struct _vl_api_ip6_nd_event *nd_events; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; +} vpe_api_main_t; + +extern vpe_api_main_t vpe_api_main; + +#endif /* __api_helper_macros_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vlibapi/api_shared.c b/src/vlibapi/api_shared.c new file mode 100644 index 00000000..27898c3b --- /dev/null +++ b/src/vlibapi/api_shared.c @@ -0,0 +1,925 @@ +/* + *------------------------------------------------------------------ + * api_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 <vppinfra/format.h> +#include <vppinfra/byte_order.h> +#include <vppinfra/error.h> +#include <vlib/vlib.h> +#include <vlib/unix/unix.h> +#include <vlibapi/api.h> +#include <vppinfra/elog.h> + +/* *INDENT-OFF* */ +api_main_t api_main = + { + .region_name = "/unset", + .api_uid = -1, + .api_gid = -1, + }; +/* *INDENT-ON* */ + +void +vl_msg_api_increment_missing_client_counter (void) +{ + api_main_t *am = &api_main; + am->missing_clients++; +} + +int +vl_msg_api_rx_trace_enabled (api_main_t * am) +{ + return (am->rx_trace && am->rx_trace->enabled); +} + +int +vl_msg_api_tx_trace_enabled (api_main_t * am) +{ + return (am->tx_trace && am->tx_trace->enabled); +} + +/* + * vl_msg_api_trace + */ +void +vl_msg_api_trace (api_main_t * am, vl_api_trace_t * tp, void *msg) +{ + u8 **this_trace; + u8 **old_trace; + u8 *msg_copy; + u32 length; + trace_cfg_t *cfgp; + u16 msg_id = ntohs (*((u16 *) msg)); + msgbuf_t *header = (msgbuf_t *) (((u8 *) msg) - offsetof (msgbuf_t, data)); + + cfgp = am->api_trace_cfg + msg_id; + + if (!cfgp || !cfgp->trace_enable) + return; + + msg_copy = 0; + + if (tp->nitems == 0) + { + clib_warning ("tp->nitems is 0"); + return; + } + + if (vec_len (tp->traces) < tp->nitems) + { + vec_add1 (tp->traces, 0); + this_trace = tp->traces + vec_len (tp->traces) - 1; + } + else + { + tp->wrapped = 1; + old_trace = tp->traces + tp->curindex++; + if (tp->curindex == tp->nitems) + tp->curindex = 0; + vec_free (*old_trace); + this_trace = old_trace; + } + + length = clib_net_to_host_u32 (header->data_len); + + vec_validate (msg_copy, length - 1); + clib_memcpy (msg_copy, msg, length); + *this_trace = msg_copy; +} + +int +vl_msg_api_trace_onoff (api_main_t * am, vl_api_trace_which_t which, + int onoff) +{ + vl_api_trace_t *tp; + int rv; + + switch (which) + { + case VL_API_TRACE_TX: + tp = am->tx_trace; + if (tp == 0) + { + vl_msg_api_trace_configure (am, which, 1024); + tp = am->tx_trace; + } + break; + + case VL_API_TRACE_RX: + tp = am->rx_trace; + if (tp == 0) + { + vl_msg_api_trace_configure (am, which, 1024); + tp = am->rx_trace; + } + break; + + default: + /* duh? */ + return -1; + } + + /* Configured? */ + if (tp == 0 || tp->nitems == 0) + return -1; + + rv = tp->enabled; + tp->enabled = onoff; + + return rv; +} + +int +vl_msg_api_trace_free (api_main_t * am, vl_api_trace_which_t which) +{ + vl_api_trace_t *tp; + int i; + + switch (which) + { + case VL_API_TRACE_TX: + tp = am->tx_trace; + break; + + case VL_API_TRACE_RX: + tp = am->rx_trace; + break; + + default: + /* duh? */ + return -1; + } + + /* Configured? */ + if (!tp || tp->nitems == 0) + return -1; + + tp->curindex = 0; + tp->wrapped = 0; + + for (i = 0; i < vec_len (tp->traces); i++) + { + vec_free (tp->traces[i]); + } + vec_free (tp->traces); + + return 0; +} + +int +vl_msg_api_trace_save (api_main_t * am, vl_api_trace_which_t which, FILE * fp) +{ + vl_api_trace_t *tp; + vl_api_trace_file_header_t fh; + int i; + u8 *msg; + + switch (which) + { + case VL_API_TRACE_TX: + tp = am->tx_trace; + break; + + case VL_API_TRACE_RX: + tp = am->rx_trace; + break; + + default: + /* duh? */ + return -1; + } + + /* Configured, data present? */ + if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0) + return -1; + + /* "Dare to be stupid" check */ + if (fp == 0) + { + return -2; + } + + /* Write the file header */ + fh.nitems = vec_len (tp->traces); + fh.endian = tp->endian; + fh.wrapped = tp->wrapped; + + if (fwrite (&fh, sizeof (fh), 1, fp) != 1) + { + return (-10); + } + + /* No-wrap case */ + if (tp->wrapped == 0) + { + /* + * Note: vec_len return 0 when fed a NULL pointer. + * Unfortunately, the static analysis tool doesn't + * figure it out, hence the suppressed warnings. + * What a great use of my time. + */ + for (i = 0; i < vec_len (tp->traces); i++) + { + u32 msg_length; + /*sa_ignore NO_NULL_CHK */ + msg = tp->traces[i]; + /* + * This retarded check required to pass + * [sic] SA-checking. + */ + if (!msg) + continue; + + msg_length = clib_host_to_net_u32 (vec_len (msg)); + if (fwrite (&msg_length, 1, sizeof (msg_length), fp) + != sizeof (msg_length)) + { + return (-14); + } + if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg)) + { + return (-11); + } + } + } + else + { + /* Wrap case: write oldest -> end of buffer */ + for (i = tp->curindex; i < vec_len (tp->traces); i++) + { + u32 msg_length; + msg = tp->traces[i]; + /* + * This retarded check required to pass + * [sic] SA-checking + */ + if (!msg) + continue; + + msg_length = clib_host_to_net_u32 (vec_len (msg)); + if (fwrite (&msg_length, 1, sizeof (msg_length), fp) + != sizeof (msg_length)) + { + return (-14); + } + + if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg)) + { + return (-12); + } + } + /* write beginning of buffer -> oldest-1 */ + for (i = 0; i < tp->curindex; i++) + { + u32 msg_length; + /*sa_ignore NO_NULL_CHK */ + msg = tp->traces[i]; + /* + * This retarded check required to pass + * [sic] SA-checking + */ + if (!msg) + continue; + + msg_length = clib_host_to_net_u32 (vec_len (msg)); + if (fwrite (&msg_length, 1, sizeof (msg_length), fp) + != sizeof (msg_length)) + { + return (-14); + } + + if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg)) + { + return (-13); + } + } + } + return 0; +} + +int +vl_msg_api_trace_configure (api_main_t * am, vl_api_trace_which_t which, + u32 nitems) +{ + vl_api_trace_t *tp; + int was_on = 0; + + switch (which) + { + case VL_API_TRACE_TX: + tp = am->tx_trace; + if (tp == 0) + { + vec_validate (am->tx_trace, 0); + tp = am->tx_trace; + } + break; + + case VL_API_TRACE_RX: + tp = am->rx_trace; + if (tp == 0) + { + vec_validate (am->rx_trace, 0); + tp = am->rx_trace; + } + + break; + + default: + return -1; + + } + + if (tp->enabled) + { + was_on = vl_msg_api_trace_onoff (am, which, 0); + } + if (tp->traces) + { + vl_msg_api_trace_free (am, which); + } + + memset (tp, 0, sizeof (*tp)); + + if (clib_arch_is_big_endian) + { + tp->endian = VL_API_BIG_ENDIAN; + } + else + { + tp->endian = VL_API_LITTLE_ENDIAN; + } + + tp->nitems = nitems; + if (was_on) + { + (void) vl_msg_api_trace_onoff (am, which, was_on); + } + return 0; +} + +void +vl_msg_api_barrier_sync (void) +{ +} + +void +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) +{ + u16 id = ntohs (*((u16 *) the_msg)); + u8 *(*print_fp) (void *, void *); + + if (id < vec_len (am->msg_handlers) && am->msg_handlers[id]) + { + 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 + { + (*print_fp) (the_msg, stdout); + } + } + + if (do_it) + { + if (!am->is_mp_safe[id]) + { + vl_msg_api_barrier_trace_context (am->msg_names[id]); + vl_msg_api_barrier_sync (); + } + (*am->msg_handlers[id]) (the_msg); + if (!am->is_mp_safe[id]) + vl_msg_api_barrier_release (); + } + } + else + { + clib_warning ("no handler for msg id %d", id); + } + + if (free_it) + vl_msg_api_free (the_msg); +} + +/* set to 1 if you want before/after message handler event logging */ +#define ELOG_API_MESSAGE_HANDLERS 0 + +#if ELOG_API_MESSAGE_HANDLERS > 0 +static u32 +elog_id_for_msg_name (vlib_main_t * vm, char *msg_name) +{ + uword *p, r; + static uword *h; + u8 *name_copy; + + if (!h) + h = hash_create_string (0, sizeof (uword)); + + p = hash_get_mem (h, msg_name); + if (p) + return p[0]; + r = elog_string (&vm->elog_main, "%s", msg_name); + + name_copy = format (0, "%s%c", msg_name, 0); + + hash_set_mem (h, name_copy, r); + + return r; +} +#endif + +/* This is only to be called from a vlib/vnet app */ +void +vl_msg_api_handler_with_vm_node (api_main_t * am, + void *the_msg, vlib_main_t * vm, + vlib_node_runtime_t * node) +{ + u16 id = ntohs (*((u16 *) the_msg)); + u8 *(*handler) (void *, void *, void *); + +#if ELOG_API_MESSAGE_HANDLERS > 0 + { + /* *INDENT-OFF* */ + ELOG_TYPE_DECLARE (e) = + { + .format = "api-msg: %s", + .format_args = "T4", + }; + /* *INDENT-ON* */ + struct + { + u32 c; + } *ed; + ed = ELOG_DATA (&vm->elog_main, e); + if (id < vec_len (am->msg_names)) + ed->c = elog_id_for_msg_name (vm, am->msg_names[id]); + else + ed->c = elog_id_for_msg_name (vm, "BOGUS"); + } +#endif + + if (id < vec_len (am->msg_handlers) && am->msg_handlers[id]) + { + handler = (void *) am->msg_handlers[id]; + + if (am->rx_trace && am->rx_trace->enabled) + vl_msg_api_trace (am, am->rx_trace, the_msg); + + if (!am->is_mp_safe[id]) + { + vl_msg_api_barrier_trace_context (am->msg_names[id]); + vl_msg_api_barrier_sync (); + } + (*handler) (the_msg, vm, node); + if (!am->is_mp_safe[id]) + 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 (!(am->message_bounce[id])) + vl_msg_api_free (the_msg); + +#if ELOG_API_MESSAGE_HANDLERS > 0 + { + /* *INDENT-OFF* */ + ELOG_TYPE_DECLARE (e) = { + .format = "api-msg-done: %s", + .format_args = "T4", + }; + /* *INDENT-ON* */ + + struct + { + u32 c; + } *ed; + ed = ELOG_DATA (&vm->elog_main, e); + if (id < vec_len (am->msg_names)) + ed->c = elog_id_for_msg_name (vm, am->msg_names[id]); + else + ed->c = elog_id_for_msg_name (vm, "BOGUS"); + } +#endif +} + +void +vl_msg_api_handler (void *the_msg) +{ + api_main_t *am = &api_main; + + msg_handler_internal (am, the_msg, + (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) +{ + api_main_t *am = &api_main; + msg_handler_internal (am, the_msg, + (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) +{ + api_main_t *am = &api_main; + msg_handler_internal (am, the_msg, 0 /* trace_it */ , 1 /* do_it */ , + 0 /* free_it */ ); +} + +/* + * Add a trace record to the API message trace buffer, if + * API message tracing is enabled. Handy for adding sufficient + * data to the trace to reproduce autonomous state, as opposed to + * state downloaded via control-plane API messages. Example: the NAT + * application creates database entries based on packet traffic, not + * control-plane messages. + * + */ +void +vl_msg_api_trace_only (void *the_msg) +{ + api_main_t *am = &api_main; + + msg_handler_internal (am, the_msg, + (am->rx_trace + && am->rx_trace->enabled) /* trace_it */ , + 0 /* do_it */ , 0 /* free_it */ ); +} + +void +vl_msg_api_cleanup_handler (void *the_msg) +{ + api_main_t *am = &api_main; + u16 id = ntohs (*((u16 *) the_msg)); + + if (PREDICT_FALSE (id >= vec_len (am->msg_cleanup_handlers))) + { + clib_warning ("_vl_msg_id too large: %d\n", id); + return; + } + if (am->msg_cleanup_handlers[id]) + (*am->msg_cleanup_handlers[id]) (the_msg); + + vl_msg_api_free (the_msg); +} + +/* + * vl_msg_api_replay_handler + */ +void +vl_msg_api_replay_handler (void *the_msg) +{ + api_main_t *am = &api_main; + + u16 id = ntohs (*((u16 *) the_msg)); + + if (PREDICT_FALSE (id >= vec_len (am->msg_handlers))) + { + 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); + /* do NOT free the message buffer... */ +} + +u32 +vl_msg_api_get_msg_length (void *msg_arg) +{ + return vl_msg_api_get_msg_length_inline (msg_arg); +} + +/* + * vl_msg_api_socket_handler + */ +void +vl_msg_api_socket_handler (void *the_msg) +{ + api_main_t *am = &api_main; + + msg_handler_internal (am, the_msg, + (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) \ +_(api_trace_cfg) \ +_(message_bounce) \ +_(is_mp_safe) + +void +vl_msg_api_config (vl_msg_api_msg_config_t * c) +{ + api_main_t *am = &api_main; + + ASSERT (c->id > 0); + +#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->message_bounce[c->id] = c->message_bounce; + am->is_mp_safe[c->id] = c->is_mp_safe; + + 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; +} + +/* + * 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) +{ + vl_msg_api_msg_config_t cfg; + vl_msg_api_msg_config_t *c = &cfg; + + 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; + vl_msg_api_config (c); +} + +void +vl_msg_api_clean_handlers (int msg_id) +{ + vl_msg_api_msg_config_t cfg; + vl_msg_api_msg_config_t *c = &cfg; + + memset (c, 0, sizeof (*c)); + + c->id = msg_id; + vl_msg_api_config (c); +} + +void +vl_msg_api_set_cleanup_handler (int msg_id, void *fp) +{ + api_main_t *am = &api_main; + ASSERT (msg_id > 0); + + vec_validate (am->msg_cleanup_handlers, msg_id); + am->msg_cleanup_handlers[msg_id] = fp; +} + +void +vl_msg_api_queue_handler (unix_shared_memory_queue_t * q) +{ + uword msg; + + while (!unix_shared_memory_queue_sub (q, (u8 *) & msg, 0)) + vl_msg_api_handler ((void *) msg); +} + +vl_api_trace_t * +vl_msg_api_trace_get (api_main_t * am, vl_api_trace_which_t which) +{ + switch (which) + { + case VL_API_TRACE_RX: + return am->rx_trace; + case VL_API_TRACE_TX: + return am->tx_trace; + default: + return 0; + } +} + +void +vl_noop_handler (void *mp) +{ +} + + +static u8 post_mortem_dump_enabled; + +void +vl_msg_api_post_mortem_dump_enable_disable (int enable) +{ + post_mortem_dump_enabled = enable; +} + +void +vl_msg_api_post_mortem_dump (void) +{ + api_main_t *am = &api_main; + FILE *fp; + char filename[64]; + int rv; + + if (post_mortem_dump_enabled == 0) + return; + + snprintf (filename, sizeof (filename), "/tmp/api_post_mortem.%d", + getpid ()); + + fp = fopen (filename, "w"); + if (fp == NULL) + { + rv = write (2, "Couldn't create ", 16); + rv = write (2, filename, strlen (filename)); + rv = write (2, "\n", 1); + return; + } + rv = vl_msg_api_trace_save (am, VL_API_TRACE_RX, fp); + fclose (fp); + if (rv < 0) + { + rv = write (2, "Failed to save post-mortem API trace to ", 40); + rv = write (2, filename, strlen (filename)); + rv = write (2, "\n", 1); + } + +} + +/* Layered message handling support */ + +void +vl_msg_api_register_pd_handler (void *fp, u16 msg_id_host_byte_order) +{ + api_main_t *am = &api_main; + + /* Mild idiot proofing */ + if (msg_id_host_byte_order > 10000) + clib_warning ("msg_id_host_byte_order endian issue? %d arg vs %d", + msg_id_host_byte_order, + clib_net_to_host_u16 (msg_id_host_byte_order)); + vec_validate (am->pd_msg_handlers, msg_id_host_byte_order); + am->pd_msg_handlers[msg_id_host_byte_order] = fp; +} + +int +vl_msg_api_pd_handler (void *mp, int rv) +{ + api_main_t *am = &api_main; + int (*fp) (void *, int); + u16 msg_id; + + if (clib_arch_is_little_endian) + msg_id = clib_net_to_host_u16 (*((u16 *) mp)); + else + msg_id = *((u16 *) mp); + + if (msg_id >= vec_len (am->pd_msg_handlers) + || am->pd_msg_handlers[msg_id] == 0) + return rv; + + fp = am->pd_msg_handlers[msg_id]; + rv = (*fp) (mp, rv); + return rv; +} + +void +vl_msg_api_set_first_available_msg_id (u16 first_avail) +{ + api_main_t *am = &api_main; + + am->first_available_msg_id = first_avail; +} + +u16 +vl_msg_api_get_msg_ids (const char *name, int n) +{ + api_main_t *am = &api_main; + u8 *name_copy; + vl_api_msg_range_t *rp; + uword *p; + u16 rv; + + if (am->msg_range_by_name == 0) + am->msg_range_by_name = hash_create_string (0, sizeof (uword)); + + name_copy = format (0, "%s%c", name, 0); + + p = hash_get_mem (am->msg_range_by_name, name_copy); + if (p) + { + clib_warning ("WARNING: duplicate message range registration for '%s'", + name_copy); + vec_free (name_copy); + return ((u16) ~ 0); + } + + if (n < 0 || n > 1024) + { + clib_warning + ("WARNING: bad number of message-IDs (%d) requested by '%s'", + n, name_copy); + vec_free (name_copy); + return ((u16) ~ 0); + } + + vec_add2 (am->msg_ranges, rp, 1); + + rv = rp->first_msg_id = am->first_available_msg_id; + am->first_available_msg_id += n; + rp->last_msg_id = am->first_available_msg_id - 1; + rp->name = name_copy; + + hash_set_mem (am->msg_range_by_name, name_copy, rp - am->msg_ranges); + + return rv; +} + +void +vl_msg_api_add_msg_name_crc (api_main_t * am, const char *string, u32 id) +{ + uword *p; + + if (am->msg_index_by_name_and_crc == 0) + am->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword)); + + p = hash_get_mem (am->msg_index_by_name_and_crc, string); + if (p) + { + clib_warning ("attempt to redefine '%s' ignored...", string); + return; + } + + hash_set_mem (am->msg_index_by_name_and_crc, string, id); +} + + +/* + * 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 new file mode 100644 index 00000000..50e5c41c --- /dev/null +++ b/src/vlibapi/node_serialize.c @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2016 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 <vlib/vlib.h> + +#include <vppinfra/serialize.h> + +extern void vl_msg_api_barrier_sync (void); +extern void vl_msg_api_barrier_release (void); + +/* serialized representation of state strings */ + +#define foreach_state_string_code \ +_(STATE_DONE, "done") \ +_(STATE_DISABLED, "disabled") \ +_(STATE_TIME_WAIT, "time wait") \ +_(STATE_EVENT_WAIT, "event wait") \ +_(STATE_ANY_WAIT, "any wait") \ +_(STATE_POLLING, "polling") \ +_(STATE_INTERRUPT_WAIT, "interrupt wait") \ +_(STATE_INTERNAL, "internal") + +typedef enum +{ +#define _(a,b) a, + foreach_state_string_code +#undef _ +} state_string_enum_t; + +static char *state_strings[] = { +#define _(a,b) b, + foreach_state_string_code +#undef _ +}; + +/* + * Serialize a vlib_node_main_t. Appends the result to vector. + * Pass 0 to create a new vector, use vec_reset_length(vector) + * to recycle a vector / avoid memory allocation, etc. + * Switch heaps before/after to serialize into API client shared memory. + */ + +u8 * +vlib_node_serialize (vlib_node_main_t * nm, u8 * vector, + u32 max_threads, int include_nexts, int include_stats) +{ + serialize_main_t _sm, *sm = &_sm; + vlib_main_t *vm = vlib_get_main (); + vlib_node_t *n; + static vlib_node_t ***node_dups; + vlib_node_t **nodes; + static vlib_main_t **stat_vms; + vlib_main_t *stat_vm; + u8 *namep; + u32 name_bytes; + uword i, j, k; + u64 l, v, c, d; + state_string_enum_t state_code; + u32 threads_to_serialize; + + vec_reset_length (node_dups); + + if (vec_len (stat_vms) == 0) + { + for (i = 0; i < vec_len (vlib_mains); i++) + { + stat_vm = vlib_mains[i]; + if (stat_vm) + vec_add1 (stat_vms, stat_vm); + } + } + + threads_to_serialize = clib_min (max_threads, vec_len (stat_vms)); + + /* + * Barrier sync across stats scraping. + * Otherwise, the counts will be grossly inaccurate. + */ + vl_msg_api_barrier_sync (); + + for (j = 0; j < threads_to_serialize; j++) + { + stat_vm = stat_vms[j]; + nm = &stat_vm->node_main; + + if (include_stats) + { + for (i = 0; i < vec_len (nm->nodes); i++) + { + n = nm->nodes[i]; + vlib_node_sync_stats (stat_vm, n); + } + } + + nodes = vec_dup (nm->nodes); + + vec_add1 (node_dups, nodes); + } + vl_msg_api_barrier_release (); + + serialize_open_vector (sm, vector); + + serialize_likely_small_unsigned_integer (sm, vec_len (stat_vms)); + + for (j = 0; j < vec_len (stat_vms); j++) + { + stat_vm = stat_vms[j]; + nodes = node_dups[j]; + + serialize_likely_small_unsigned_integer (sm, vec_len (nodes)); + + for (i = 0; i < vec_len (nodes); i++) + { + n = nodes[i]; + + l = n->stats_total.clocks - n->stats_last_clear.clocks; + v = n->stats_total.vectors - n->stats_last_clear.vectors; + c = n->stats_total.calls - n->stats_last_clear.calls; + d = n->stats_total.suspends - n->stats_last_clear.suspends; + + state_code = STATE_INTERNAL; + + if (n->type == VLIB_NODE_TYPE_PROCESS) + { + vlib_process_t *p = vlib_get_process_from_node (vm, n); + + switch (p->flags + & (VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK + | VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT)) + { + default: + if (!(p->flags & VLIB_PROCESS_IS_RUNNING)) + state_code = STATE_DONE; + break; + + case VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK: + state_code = STATE_TIME_WAIT; + break; + + case VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT: + state_code = STATE_EVENT_WAIT; + break; + + case (VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT | VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK): + state_code = + STATE_ANY_WAIT; + break; + } + } + else if (n->type != VLIB_NODE_TYPE_INTERNAL) + { + state_code = STATE_POLLING; + if (n->state == VLIB_NODE_STATE_DISABLED) + state_code = STATE_DISABLED; + else if (n->state == VLIB_NODE_STATE_INTERRUPT) + state_code = STATE_INTERRUPT_WAIT; + } + + /* See unserialize_cstring */ + name_bytes = vec_len (n->name); + serialize_likely_small_unsigned_integer (sm, name_bytes); + namep = serialize_get (sm, name_bytes); + memcpy (namep, n->name, name_bytes); + + serialize_likely_small_unsigned_integer (sm, (u64) state_code); + serialize_likely_small_unsigned_integer (sm, n->type); + + if (include_nexts) + { + serialize_likely_small_unsigned_integer + (sm, vec_len (n->next_nodes)); + for (k = 0; k < vec_len (n->next_nodes); k++) + serialize_likely_small_unsigned_integer (sm, + n->next_nodes[k]); + } + else + serialize_likely_small_unsigned_integer (sm, 0); + + if (include_stats) + { + /* stats present */ + serialize_likely_small_unsigned_integer (sm, 1); + /* total clocks */ + serialize_integer (sm, l, 8); + /* Total calls */ + serialize_integer (sm, c, 8); + /* Total vectors */ + serialize_integer (sm, v, 8); + /* Total suspends */ + serialize_integer (sm, d, 8); + } + else /* no stats */ + serialize_likely_small_unsigned_integer (sm, 0); + } + vec_free (nodes); + } + return (serialize_close_vector (sm)); +} + +vlib_node_t *** +vlib_node_unserialize (u8 * vector) +{ + serialize_main_t _sm, *sm = &_sm; + u32 nnodes, nnexts; + u32 nstat_vms; + vlib_node_t *node; + vlib_node_t **nodes; + vlib_node_t ***nodes_by_thread = 0; + int i, j, k; + u64 l, v, c, d; + state_string_enum_t state_code; + int stats_present; + + serialize_open_vector (sm, vector); + + nstat_vms = unserialize_likely_small_unsigned_integer (sm); + + vec_validate (nodes_by_thread, nstat_vms - 1); + _vec_len (nodes_by_thread) = 0; + + for (i = 0; i < nstat_vms; i++) + { + nnodes = unserialize_likely_small_unsigned_integer (sm); + + nodes = 0; + vec_validate (nodes, nnodes - 1); + vec_add1 (nodes_by_thread, nodes); + + for (j = 0; j < nnodes; j++) + { + node = 0; + vec_validate (node, 0); + nodes[j] = node; + + unserialize_cstring (sm, (char **) &(node->name)); + state_code = unserialize_likely_small_unsigned_integer (sm); + node->state_string = (u8 *) state_strings[state_code]; + + node->type = unserialize_likely_small_unsigned_integer (sm); + nnexts = unserialize_likely_small_unsigned_integer (sm); + if (nnexts > 0) + vec_validate (node->next_nodes, nnexts - 1); + for (k = 0; k < nnexts; k++) + node->next_nodes[k] = + unserialize_likely_small_unsigned_integer (sm); + + stats_present = unserialize_likely_small_unsigned_integer (sm); + + if (stats_present) + { + /* total clocks */ + unserialize_integer (sm, &l, 8); + node->stats_total.clocks = l; + node->stats_last_clear.clocks = 0; + + /* Total calls */ + unserialize_integer (sm, &c, 8); + node->stats_total.calls = c; + + /* Total vectors */ + unserialize_integer (sm, &v, 8); + node->stats_total.vectors = v; + + /* Total suspends */ + unserialize_integer (sm, &d, 8); + node->stats_total.suspends = d; + } + } + } + return nodes_by_thread; +} + +#if TEST_CODE + +static clib_error_t * +test_node_serialize_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + vlib_node_main_t *nm = &vm->node_main; + u8 *vector = 0; + vlib_node_t ***nodes_by_thread; + vlib_node_t **nodes; + vlib_node_t *node; + vlib_node_t *next_node; + int i, j, k; + u32 max_threads = (u32) ~ 0; + int include_nexts = 0; + int include_stats = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "max-threads %d", &max_threads)) + ; + else if (unformat (input, "stats")) + include_stats = 1; + else if (unformat (input, "nexts")) + include_nexts = 1; + else + break; + } + + /* + * Keep the number of memcpy ops to a minimum (e.g. 1). + * The current size of the serialized vector is + * slightly under 4K. + */ + vec_validate (vector, 16383); + vec_reset_length (vector); + + vector = vlib_node_serialize (nm, vector, max_threads, + include_nexts, include_stats); + + vlib_cli_output (vm, "result vector %d bytes", vec_len (vector)); + + nodes_by_thread = vlib_node_unserialize (vector); + + vec_free (vector); + + for (i = 0; i < vec_len (nodes_by_thread); i++) + { + nodes = nodes_by_thread[i]; + + vlib_cli_output (vm, "thread %d", i); + + for (j = 0; j < vec_len (nodes); j++) + { + node = nodes[j]; + + vlib_cli_output (vm, "[%d] %s state %s", j, node->name, + node->state_string); + + vlib_cli_output + (vm, " clocks %lld calls %lld suspends" + " %lld vectors %lld", + node->stats_total.clocks, + node->stats_total.calls, + node->stats_total.suspends, node->stats_total.vectors); + + for (k = 0; k < vec_len (node->next_nodes); k++) + { + if (node->next_nodes[k] != ~0) + { + next_node = nodes[node->next_nodes[k]]; + vlib_cli_output (vm, " [%d] %s", k, next_node->name); + } + } + } + } + + for (j = 0; j < vec_len (nodes_by_thread); j++) + { + nodes = nodes_by_thread[j]; + + for (i = 0; i < vec_len (nodes); i++) + { + vec_free (nodes[i]->name); + vec_free (nodes[i]->next_nodes); + vec_free (nodes[i]); + } + vec_free (nodes); + } + vec_free (nodes_by_thread); + + 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 + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vlibapi/node_unserialize.c b/src/vlibapi/node_unserialize.c new file mode 100644 index 00000000..bb0edfed --- /dev/null +++ b/src/vlibapi/node_unserialize.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2016 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 <vlib/vlib.h> + +#include <vppinfra/serialize.h> + +/* serialized representation of state strings */ + +#define foreach_state_string_code \ +_(STATE_DONE, "done") \ +_(STATE_DISABLED, "disabled") \ +_(STATE_TIME_WAIT, "time wait") \ +_(STATE_EVENT_WAIT, "event wait") \ +_(STATE_ANY_WAIT, "any wait") \ +_(STATE_POLLING, "polling") \ +_(STATE_INTERRUPT_WAIT, "interrupt wait") \ +_(STATE_INTERNAL, "internal") + +typedef enum +{ +#define _(a,b) a, + foreach_state_string_code +#undef _ +} state_string_enum_t; + +static char *state_strings[] = { +#define _(a,b) b, + foreach_state_string_code +#undef _ +}; + +vlib_node_t *** +vlib_node_unserialize (u8 * vector) +{ + serialize_main_t _sm, *sm = &_sm; + u32 nnodes, nnexts; + u32 nstat_vms; + vlib_node_t *node; + vlib_node_t **nodes; + vlib_node_t ***nodes_by_thread = 0; + int i, j, k; + u64 l, v, c, d; + state_string_enum_t state_code; + int stats_present; + + serialize_open_vector (sm, vector); + + nstat_vms = unserialize_likely_small_unsigned_integer (sm); + + vec_validate (nodes_by_thread, nstat_vms - 1); + _vec_len (nodes_by_thread) = 0; + + for (i = 0; i < nstat_vms; i++) + { + nnodes = unserialize_likely_small_unsigned_integer (sm); + + nodes = 0; + vec_validate (nodes, nnodes - 1); + vec_add1 (nodes_by_thread, nodes); + + for (j = 0; j < nnodes; j++) + { + node = 0; + vec_validate (node, 0); + nodes[j] = node; + + unserialize_cstring (sm, (char **) &(node->name)); + state_code = unserialize_likely_small_unsigned_integer (sm); + node->state_string = (u8 *) state_strings[state_code]; + + node->type = unserialize_likely_small_unsigned_integer (sm); + nnexts = unserialize_likely_small_unsigned_integer (sm); + if (nnexts > 0) + vec_validate (node->next_nodes, nnexts - 1); + for (k = 0; k < nnexts; k++) + node->next_nodes[k] = + unserialize_likely_small_unsigned_integer (sm); + + stats_present = unserialize_likely_small_unsigned_integer (sm); + + if (stats_present) + { + /* total clocks */ + unserialize_integer (sm, &l, 8); + node->stats_total.clocks = l; + node->stats_last_clear.clocks = 0; + + /* Total calls */ + unserialize_integer (sm, &c, 8); + node->stats_total.calls = c; + + /* Total vectors */ + unserialize_integer (sm, &v, 8); + node->stats_total.vectors = v; + + /* Total suspends */ + unserialize_integer (sm, &d, 8); + node->stats_total.suspends = d; + } + } + } + return nodes_by_thread; +} + +#if TEST_CODE + +static clib_error_t * +test_node_serialize_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + vlib_node_main_t *nm = &vm->node_main; + u8 *vector = 0; + vlib_node_t ***nodes_by_thread; + vlib_node_t **nodes; + vlib_node_t *node; + vlib_node_t *next_node; + int i, j, k; + u32 max_threads = (u32) ~ 0; + int include_nexts = 0; + int include_stats = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "max-threads %d", &max_threads)) + ; + else if (unformat (input, "stats")) + include_stats = 1; + else if (unformat (input, "nexts")) + include_nexts = 1; + else + break; + } + + /* + * Keep the number of memcpy ops to a minimum (e.g. 1). + * The current size of the serialized vector is + * slightly under 4K. + */ + vec_validate (vector, 16383); + vec_reset_length (vector); + + vector = vlib_node_serialize (nm, vector, max_threads, + include_nexts, include_stats); + + vlib_cli_output (vm, "result vector %d bytes", vec_len (vector)); + + nodes_by_thread = vlib_node_unserialize (vector); + + vec_free (vector); + + for (i = 0; i < vec_len (nodes_by_thread); i++) + { + nodes = nodes_by_thread[i]; + + vlib_cli_output (vm, "thread %d", i); + + for (j = 0; j < vec_len (nodes); j++) + { + node = nodes[j]; + + vlib_cli_output (vm, "[%d] %s state %s", j, node->name, + node->state_string); + + vlib_cli_output + (vm, " clocks %lld calls %lld suspends" + " %lld vectors %lld", + node->stats_total.clocks, + node->stats_total.calls, + node->stats_total.suspends, node->stats_total.vectors); + + for (k = 0; k < vec_len (node->next_nodes); k++) + { + if (node->next_nodes[k] != ~0) + { + next_node = nodes[node->next_nodes[k]]; + vlib_cli_output (vm, " [%d] %s", k, next_node->name); + } + } + } + } + + for (j = 0; j < vec_len (nodes_by_thread); j++) + { + nodes = nodes_by_thread[j]; + + for (i = 0; i < vec_len (nodes); i++) + { + vec_free (nodes[i]->name); + vec_free (nodes[i]->next_nodes); + vec_free (nodes[i]); + } + vec_free (nodes); + } + vec_free (nodes_by_thread); + + 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 + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vlibapi/vat_helper_macros.h b/src/vlibapi/vat_helper_macros.h new file mode 100644 index 00000000..57ad520b --- /dev/null +++ b/src/vlibapi/vat_helper_macros.h @@ -0,0 +1,76 @@ +/* + *------------------------------------------------------------------ + * vat_helper_macros.h - collect api client helper macros in one place + * + * Copyright (c) 2016 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 __vat_helper_macros_h__ +#define __vat_helper_macros_h__ + +/* M: construct, but don't yet send a message */ +#define M(T, mp) \ +do { \ + vam->result_ready = 0; \ + mp = vl_msg_api_alloc_as_if_client(sizeof(*mp)); \ + memset (mp, 0, sizeof (*mp)); \ + mp->_vl_msg_id = ntohs (VL_API_##T+__plugin_msg_base); \ + mp->client_index = vam->my_client_index; \ +} while(0); + +#define M2(T, mp, n) \ +do { \ + vam->result_ready = 0; \ + mp = vl_msg_api_alloc_as_if_client(sizeof(*mp)+(n)); \ + memset (mp, 0, sizeof (*mp)); \ + mp->_vl_msg_id = ntohs (VL_API_##T+__plugin_msg_base); \ + mp->client_index = vam->my_client_index; \ +} while(0); + +/* S: send a message */ +#define S(mp) (vl_msg_api_send_shmem (vam->vl_input_queue, (u8 *)&mp)) + +/* W: wait for results, with timeout */ +#define W(ret) \ +do { \ + f64 timeout = vat_time_now (vam) + 1.0; \ + ret = -99; \ + \ + while (vat_time_now (vam) < timeout) { \ + if (vam->result_ready == 1) { \ + ret = vam->retval; \ + break; \ + } \ + vat_suspend (vam->vlib_main, 1e-5); \ + } \ +} while(0); + +/* W2: wait for results, with timeout */ +#define W2(ret, body) \ +do { \ + f64 timeout = vat_time_now (vam) + 1.0; \ + ret = -99; \ + \ + while (vat_time_now (vam) < timeout) { \ + if (vam->result_ready == 1) { \ + (body); \ + ret = vam->retval; \ + break; \ + } \ + vat_suspend (vam->vlib_main, 1e-5); \ + } \ +} while(0); + + +#endif /* __vat_helper_macros_h__ */ |