| /* 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 (); |
| |
| vl_api_set_msg_thread_safe (am, gmp->msg_id_base + VL_API_GRAPH_NODE_GET, 1); |
| |
| return 0; |
| } |
| |
| VLIB_INIT_FUNCTION (graph_api_hookup); |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "gnu") |
| * End: |
| */ |