diff options
Diffstat (limited to 'src/plugins/tracedump/graph_api.c')
-rw-r--r-- | src/plugins/tracedump/graph_api.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/src/plugins/tracedump/graph_api.c b/src/plugins/tracedump/graph_api.c new file mode 100644 index 00000000000..19b336ccb04 --- /dev/null +++ b/src/plugins/tracedump/graph_api.c @@ -0,0 +1,270 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * Copyright 2020 Rubicon Communications, LLC. + * + * 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 <vnet/vnet.h> +#include <vlibmemory/api.h> + +#include <tracedump/graph.api_enum.h> +#include <tracedump/graph.api_types.h> + +#define REPLY_MSG_ID_BASE gmp->msg_id_base +#include <vlibapi/api_helper_macros.h> + +#include <tracedump/graph.h> + + +graph_main_t graph_main; + + +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) + + +/* + * If ever the graph or set of nodes changes, this cache of + * nodes in sorted order should be invalidated. + */ +void +graph_node_invalid_cache (void) +{ + graph_main_t *gmp = &graph_main; + + vec_free (gmp->sorted_node_vec); +} + + +static clib_error_t * +graph_node_cache_reaper (u32 client_index) +{ + graph_node_invalid_cache (); + return 0; +} + +VL_MSG_API_REAPER_FUNCTION (graph_node_cache_reaper); + + +static void +send_graph_node_reply (vl_api_registration_t * rp, + u32 context, u32 retval, u32 cursor) +{ + graph_main_t *gmp = &graph_main; + vl_api_graph_node_get_reply_t *rmp; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + rmp->_vl_msg_id = htons (VL_API_GRAPH_NODE_GET_REPLY + gmp->msg_id_base); + rmp->context = context; + rmp->retval = clib_host_to_net_u32 (retval); + rmp->cursor = htonl (cursor); + + vl_api_send_msg (rp, (u8 *) rmp); +} + + +static void +send_graph_node_details (vlib_node_main_t * nm, + vl_api_registration_t * reg, + u32 context, vlib_node_t * n, bool want_arcs) +{ + graph_main_t *gmp = &graph_main; + vl_api_graph_node_details_t *mp; + u32 msg_size; + + msg_size = sizeof (*mp); + if (want_arcs) + msg_size += vec_len (n->next_nodes) * sizeof (*n->next_nodes); + + mp = vl_msg_api_alloc (msg_size); + if (!mp) + return; + + clib_memset (mp, 0, msg_size); + + mp->_vl_msg_id = htons (VL_API_GRAPH_NODE_DETAILS + gmp->msg_id_base); + mp->context = context; + mp->index = htonl (n->index); + mp->flags = htonl (n->flags); + + clib_strncpy ((char *) mp->name, (char *) n->name, + MIN (sizeof (mp->name) - 1, vec_len (n->name))); + + if (want_arcs) + { + int i; + + mp->n_arcs = htonl (vec_len (n->next_nodes)); + for (i = 0; i < vec_len (n->next_nodes); ++i) + { + mp->arcs_out[i] = htonl (n->next_nodes[i]); + } + } + + vl_api_send_msg (reg, (u8 *) mp); +} + + +static int +node_cmp (void *a1, void *a2) +{ + vlib_node_t **n1 = a1; + vlib_node_t **n2 = a2; + + return vec_cmp (n1[0]->name, n2[0]->name); +} + + +/* + * When cursor == ~0, it begins a request: + * if index != ~0, dump node with given index + * if index == ~0 and name[0] != 0, dump node with given name + * if index == ~0 and name[0] == 0, and flag != 0, dump flagged nodes + * else + * index == ~0 and name[0] == 0 and flag == 0, so dump all nodes. + * + * When cursor != ~0, it is the middle of a request: + * The same (index, name, and flag) parameters are assumed, + * The next results resume from cursor. + */ +static void +vl_api_graph_node_get_t_handler (vl_api_graph_node_get_t * mp) +{ + vl_api_registration_t *rp; + + rp = vl_api_client_index_to_registration (mp->client_index); + if (!rp) + return; + + vlib_main_t *vm = vlib_get_main (); + vlib_node_main_t *nm = &vm->node_main; + graph_main_t *gmp = &graph_main; + vlib_node_t *n; + u32 cursor; + u32 node_index; + bool want_arcs; + + want_arcs = ! !mp->want_arcs; + cursor = ntohl (mp->cursor); + n = 0; + + /* + * Return details on a specific node by index? + */ + node_index = ntohl (mp->index); + if (cursor == ~0 && node_index != ~0) + { + if (node_index < vec_len (nm->nodes)) + n = vlib_get_node (vm, node_index); + if (!n) + { + send_graph_node_reply (rp, mp->context, + VNET_API_ERROR_NO_SUCH_ENTRY, ~0); + return; + } + send_graph_node_details (nm, rp, mp->context, n, want_arcs); + send_graph_node_reply (rp, mp->context, 0, ~0); + return; + } + + /* + * Return details on a specific node by name? + */ + if (cursor == ~0 && mp->name[0] != 0) + { + n = vlib_get_node_by_name (vm, (u8 *) mp->name); + if (!n) + { + send_graph_node_reply (rp, mp->context, + VNET_API_ERROR_NO_SUCH_ENTRY, ~0); + return; + } + + send_graph_node_details (nm, rp, mp->context, n, want_arcs); + send_graph_node_reply (rp, mp->context, 0, ~0); + return; + } + + /* + * Inspect all nodes, but potentially limit them by flag selection. + * As iteration my need to occur over multiple streaming API calls, + * determine the API client index and cache a sorted list of nodes. + * + * First time through, make a sorted node list and cache it. + */ + vlib_node_t **nodes = gmp->sorted_node_vec; + if (!nodes) + { + nodes = vec_dup (nm->nodes); + vec_sort_with_function (nodes, node_cmp); + gmp->sorted_node_vec = nodes; + } + + u32 flags = ntohl (mp->flags); + u32 first_index = (cursor == ~0) ? 0 : cursor; + + /* Don't overflow the existing queue space. */ + svm_queue_t *q = rp->vl_input_queue; + u32 queue_slots_available = q->maxsize - q->cursize; + int chunk = (queue_slots_available > 0) ? queue_slots_available - 1 : 0; + u32 i; + + for (i = first_index; i < vec_len (nodes); ++i) + { + if (chunk-- == 0) + { + /* + * Pick up again at cursor = i. + */ + send_graph_node_reply (rp, mp->context, VNET_API_ERROR_EAGAIN, i); + return; + } + + n = nodes[i]; + if (flags == 0 || (n->flags & flags)) + { + send_graph_node_details (nm, rp, mp->context, n, want_arcs); + } + } + + send_graph_node_reply (rp, mp->context, 0, ~0); +} + + +#include <vnet/format_fns.h> +#include <tracedump/graph.api.c> + +static clib_error_t * +graph_api_hookup (vlib_main_t * vm) +{ + api_main_t *am = vlibapi_get_main (); + graph_main_t *gmp = &graph_main; + + gmp->msg_id_base = setup_message_id_table (); + + am->is_mp_safe[gmp->msg_id_base + VL_API_GRAPH_NODE_GET] = 1; + + am->is_autoendian[gmp->msg_id_base + VL_API_GRAPH_NODE_DETAILS] = 1; + + return 0; +} + +VLIB_INIT_FUNCTION (graph_api_hookup); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |