FIB Memory Usage Diagnostics
[vpp.git] / vnet / vnet / dpo / dpo.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /**
16  * @brief
17  * A Data-Path Object is an object that represents actions that are
18  * applied to packets are they are switched through VPP.
19  * 
20  * The DPO is a base class that is specialised by other objects to provide
21  * concreate actions
22  *
23  * The VLIB graph nodes are graph of types, the DPO graph is a graph of instances.
24  */
25
26 #include <vnet/dpo/dpo.h>
27 #include <vnet/ip/lookup.h>
28 #include <vnet/ip/format.h>
29 #include <vnet/adj/adj.h>
30
31 #include <vnet/dpo/load_balance.h>
32 #include <vnet/dpo/mpls_label_dpo.h>
33 #include <vnet/dpo/lookup_dpo.h>
34 #include <vnet/dpo/drop_dpo.h>
35 #include <vnet/dpo/receive_dpo.h>
36 #include <vnet/dpo/punt_dpo.h>
37 #include <vnet/dpo/classify_dpo.h>
38
39 /**
40  * Array of char* names for the DPO types and protos
41  */
42 static const char* dpo_type_names[] = DPO_TYPES;
43 static const char* dpo_proto_names[] = DPO_PROTOS;
44
45 /**
46  * @brief Vector of virtual function tables for the DPO types
47  *
48  * This is a vector so we can dynamically register new DPO types in plugins.
49  */
50 static dpo_vft_t *dpo_vfts;
51
52 /**
53  * @brief vector of graph node names associated with each DPO type and protocol.
54  *
55  *   dpo_nodes[child_type][child_proto][node_X] = node_name;
56  * i.e.
57  *   dpo_node[DPO_LOAD_BALANCE][DPO_PROTO_IP4][0] = "ip4-lookup"
58  *   dpo_node[DPO_LOAD_BALANCE][DPO_PROTO_IP4][1] = "ip4-load-balance"
59  *
60  * This is a vector so we can dynamically register new DPO types in plugins.
61  */
62 static const char* const * const ** dpo_nodes;
63
64 /**
65  * @brief Vector of edge indicies from parent DPO nodes to child
66  *
67  * dpo_edges[child_type][child_proto][parent_type] = edge_index
68  *
69  * This array is derived at init time from the dpo_nodes above. Note that
70  * the third dimension in dpo_nodes is lost, hence, the edge index from each
71  * node MUST be the same.
72  *
73  * Note that this array is child type specific, not child instance specific.
74  */
75 static u32 ***dpo_edges;
76
77 /**
78  * @brief The DPO type value that can be assigend to the next dynamic
79  *        type registration.
80  */
81 static dpo_type_t dpo_dynamic = DPO_LAST;
82
83 u8 *
84 format_dpo_type (u8 * s, va_list * args)
85 {
86     dpo_type_t type = va_arg (*args, int);
87
88     s = format(s, "%s", dpo_type_names[type]);
89
90     return (s);
91 }
92
93 u8 *
94 format_dpo_id (u8 * s, va_list * args)
95 {
96     dpo_id_t *dpo = va_arg (*args, dpo_id_t*);
97     u32 indent = va_arg (*args, u32);
98
99     s = format(s, "[@%d]: ", dpo->dpoi_next_node);
100
101     if (NULL != dpo_vfts[dpo->dpoi_type].dv_format)
102     {
103         return (format(s, "%U",
104                        dpo_vfts[dpo->dpoi_type].dv_format,
105                        dpo->dpoi_index,
106                        indent));
107     }
108
109     switch (dpo->dpoi_type)
110     {
111     case DPO_FIRST:
112         s = format(s, "unset");
113         break;
114     default:
115         s = format(s, "unknown");
116         break;
117     }
118     return (s);
119 }
120
121 u8 *
122 format_dpo_proto (u8 * s, va_list * args)
123 {
124     dpo_proto_t proto = va_arg (*args, int);
125
126     return (format(s, "%s", dpo_proto_names[proto]));
127 }
128
129 void
130 dpo_set (dpo_id_t *dpo,
131          dpo_type_t type,
132          dpo_proto_t proto,
133          index_t index)
134 {
135     dpo_id_t tmp = *dpo;
136
137     dpo->dpoi_type = type;
138     dpo->dpoi_proto = proto,
139     dpo->dpoi_index = index;
140
141     if (DPO_ADJACENCY == type)
142     {
143         /*
144          * set the adj subtype
145          */
146         ip_adjacency_t *adj;
147
148         adj = adj_get(index);
149
150         switch (adj->lookup_next_index)
151         {
152         case IP_LOOKUP_NEXT_ARP:
153             dpo->dpoi_type = DPO_ADJACENCY_INCOMPLETE;
154             break;
155         case IP_LOOKUP_NEXT_MIDCHAIN:
156             dpo->dpoi_type = DPO_ADJACENCY_MIDCHAIN;
157             break;
158         default:
159             break;
160         }
161     }
162     dpo_lock(dpo);
163     dpo_unlock(&tmp);
164 }
165
166 void
167 dpo_reset (dpo_id_t *dpo)
168 {
169     dpo_set(dpo, DPO_FIRST, DPO_PROTO_NONE, INDEX_INVALID);
170 }
171
172 /**
173  * \brief
174  * Compare two Data-path objects
175  *
176  * like memcmp, return 0 is matching, !0 otherwise.
177  */
178 int
179 dpo_cmp (const dpo_id_t *dpo1,
180          const dpo_id_t *dpo2)
181 {
182     int res;
183
184     res = dpo1->dpoi_type - dpo2->dpoi_type;
185
186     if (0 != res) return (res);
187
188     return (dpo1->dpoi_index - dpo2->dpoi_index);
189 }
190
191 void
192 dpo_copy (dpo_id_t *dst,
193           const dpo_id_t *src)
194 {
195     dpo_id_t tmp = *dst;
196
197     /*
198      * the destination is written in a single u64 write - hence atomically w.r.t
199      * any packets inflight.
200      */
201     *((u64*)dst) = *(u64*)src; 
202
203     dpo_lock(dst);
204     dpo_unlock(&tmp);    
205 }
206
207 int
208 dpo_is_adj (const dpo_id_t *dpo)
209 {
210     return ((dpo->dpoi_type == DPO_ADJACENCY) ||
211             (dpo->dpoi_type == DPO_ADJACENCY_INCOMPLETE) ||
212             (dpo->dpoi_type == DPO_ADJACENCY_MIDCHAIN) ||
213             (dpo->dpoi_type == DPO_ADJACENCY_GLEAN));
214 }
215
216 void
217 dpo_register (dpo_type_t type,
218               const dpo_vft_t *vft,
219               const char * const * const * nodes)
220 {
221     vec_validate(dpo_vfts, type);
222     dpo_vfts[type] = *vft;
223
224     vec_validate(dpo_nodes, type);
225     dpo_nodes[type] = nodes;
226 }
227
228 dpo_type_t
229 dpo_register_new_type (const dpo_vft_t *vft,
230                        const char * const * const * nodes)
231 {
232     dpo_type_t type = dpo_dynamic++;
233
234     dpo_register(type, vft, nodes);
235
236     return (type);
237 }
238
239 void
240 dpo_lock (dpo_id_t *dpo)
241 {
242     if (!dpo_id_is_valid(dpo))
243         return;
244
245     dpo_vfts[dpo->dpoi_type].dv_lock(dpo);
246 }
247
248 void
249 dpo_unlock (dpo_id_t *dpo)
250 {
251     if (!dpo_id_is_valid(dpo))
252         return;
253
254     dpo_vfts[dpo->dpoi_type].dv_unlock(dpo);
255 }
256
257
258 static u32
259 dpo_get_next_node (dpo_type_t child_type,
260                    dpo_proto_t child_proto,
261                    const dpo_id_t *parent_dpo)
262 {
263     dpo_proto_t parent_proto;
264     dpo_type_t parent_type;
265
266     parent_type = parent_dpo->dpoi_type;
267     parent_proto = parent_dpo->dpoi_proto;
268
269     vec_validate(dpo_edges, child_type);
270     vec_validate(dpo_edges[child_type], child_proto);
271     vec_validate_init_empty(dpo_edges[child_type][child_proto],
272                             parent_dpo->dpoi_type, ~0);
273
274     /*
275      * if the edge index has not yet been created for this node to node transistion
276      */
277     if (~0 == dpo_edges[child_type][child_proto][parent_type])
278     {
279         vlib_node_t *parent_node, *child_node;
280         vlib_main_t *vm;
281         u32 edge ,pp, cc;
282
283         vm = vlib_get_main();
284
285         ASSERT(NULL != dpo_nodes[child_type]);
286         ASSERT(NULL != dpo_nodes[child_type][child_proto]);
287         ASSERT(NULL != dpo_nodes[parent_type]);
288         ASSERT(NULL != dpo_nodes[parent_type][parent_proto]);
289
290         pp = 0;
291
292         /*
293          * create a graph arc from each of the parent's registered node types,
294          * to each of the childs.
295          */
296         while (NULL != dpo_nodes[child_type][child_proto][pp])
297         {
298             parent_node =
299                 vlib_get_node_by_name(vm,
300                                       (u8*) dpo_nodes[child_type][child_proto][pp]);
301
302             cc = 0;
303
304             while (NULL != dpo_nodes[parent_type][child_proto][cc])
305             {
306                 child_node =
307                     vlib_get_node_by_name(vm,
308                                           (u8*) dpo_nodes[parent_type][parent_proto][cc]);
309
310                 edge = vlib_node_add_next(vm,
311                                           parent_node->index,
312                                           child_node->index);
313
314                 if (~0 == dpo_edges[child_type][child_proto][parent_type])
315                 {
316                     dpo_edges[child_type][child_proto][parent_type] = edge;
317                 }
318                 else
319                 {
320                     ASSERT(dpo_edges[child_type][child_proto][parent_type] == edge);
321                 }
322                 cc++;
323             }
324             pp++;
325         }
326     }
327
328     return (dpo_edges[child_type][child_proto][parent_type]);
329 }
330
331 /**
332  * @brief Stack one DPO object on another, and thus establish a child parent
333  * relationship. The VLIB graph arc used is taken from the parent and child types
334  * passed.
335  */
336 static void
337 dpo_stack_i (u32 edge,
338              dpo_id_t *dpo,
339              const dpo_id_t *parent)
340 {
341     /*
342      * in order to get an atomic update of the parent we create a temporary,
343      * from a copy of the child, and add the next_node. then we copy to the parent
344      */
345     dpo_id_t tmp = DPO_NULL;
346     dpo_copy(&tmp, parent);
347
348     /*
349      * get the edge index for the parent to child VLIB graph transisition
350      */
351     tmp.dpoi_next_node = edge;
352
353     /*
354      * this update is atomic.
355      */
356     dpo_copy(dpo, &tmp);
357
358     dpo_reset(&tmp);
359 }
360
361 /**
362  * @brief Stack one DPO object on another, and thus establish a child-parent
363  * relationship. The VLIB graph arc used is taken from the parent and child types
364  * passed.
365  */
366 void
367 dpo_stack (dpo_type_t child_type,
368            dpo_proto_t child_proto,
369            dpo_id_t *dpo,
370            const dpo_id_t *parent)
371 {
372     dpo_stack_i(dpo_get_next_node(child_type, child_proto, parent), dpo, parent);
373 }
374
375 /**
376  * @brief Stack one DPO object on another, and thus establish a child parent
377  * relationship. A new VLIB graph arc is created from the child node passed
378  * to the nodes registered by the parent. The VLIB infra will ensure this arc
379  * is added only once.
380  */
381 void
382 dpo_stack_from_node (u32 child_node_index,
383                      dpo_id_t *dpo,
384                      const dpo_id_t *parent)
385 {
386     dpo_proto_t parent_proto;
387     vlib_node_t *parent_node;
388     dpo_type_t parent_type;
389     vlib_main_t *vm;
390     u32 edge;
391
392     parent_type = parent->dpoi_type;
393     parent_proto = parent->dpoi_proto;
394
395     vm = vlib_get_main();
396
397     ASSERT(NULL != dpo_nodes[parent_type]);
398     ASSERT(NULL != dpo_nodes[parent_type][parent_proto]);
399
400     parent_node =
401         vlib_get_node_by_name(vm, (u8*) dpo_nodes[parent_type][parent_proto][0]);
402
403     edge = vlib_node_add_next(vm,
404                               child_node_index,
405                               parent_node->index);
406
407     dpo_stack_i(edge, dpo, parent);
408 }
409
410 static clib_error_t *
411 dpo_module_init (vlib_main_t * vm)
412 {
413     drop_dpo_module_init();
414     punt_dpo_module_init();
415     receive_dpo_module_init();
416     load_balance_module_init();
417     mpls_label_dpo_module_init();
418     classify_dpo_module_init();
419     lookup_dpo_module_init();
420
421     return (NULL);
422 }
423
424 VLIB_INIT_FUNCTION(dpo_module_init);
425
426 static clib_error_t *
427 dpo_memory_show (vlib_main_t * vm,
428                  unformat_input_t * input,
429                  vlib_cli_command_t * cmd)
430 {
431     dpo_vft_t *vft;
432
433     vlib_cli_output (vm, "DPO memory");
434     vlib_cli_output (vm, "%=30s %=5s %=8s/%=9s   totals",
435                      "Name","Size", "in-use", "allocated");
436
437     vec_foreach(vft, dpo_vfts)
438     {
439         if (NULL != vft->dv_mem_show)
440             vft->dv_mem_show();
441     }
442
443     return (NULL);
444 }
445
446 /* *INDENT-OFF* */
447 /*?
448  * The '<em>sh dpo memory </em>' command displays the memory usage for each
449  * data-plane object type.
450  *
451  * @cliexpar
452  * @cliexstart{show dpo memory}
453  * DPO memory
454  *             Name               Size  in-use /allocated   totals
455  *         load-balance            64     12   /    12      768/768
456  *           Adjacency            256      1   /    1       256/256
457  *            Receive              24      5   /    5       120/120
458  *            Lookup               12      0   /    0       0/0
459  *           Classify              12      0   /    0       0/0
460  *          MPLS label             24      0   /    0       0/0
461  * @cliexend
462 ?*/
463 VLIB_CLI_COMMAND (show_fib_memory, static) = {
464     .path = "show dpo memory",
465     .function = dpo_memory_show,
466     .short_help = "show dpo memory",
467 };
468 /* *INDENT-ON* */