2018-08-10 11:05:52 -04:00
|
|
|
Multi-Architecture Graph Node Cookbook
|
|
|
|
======================================
|
|
|
|
|
|
|
|
In the context of graph node dispatch functions, it's easy enough to
|
|
|
|
use the vpp multi-architecture support setup. The point of the scheme
|
|
|
|
is simple: for performance-critical nodes, generate multiple CPU
|
|
|
|
hardware-dependent versions of the node dispatch functions, and pick
|
|
|
|
the best one at runtime.
|
|
|
|
|
|
|
|
The vpp scheme is simple enough to use, but details matter.
|
|
|
|
|
|
|
|
100,000 foot view
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
We compile entire graph node dispatch function implementation files
|
|
|
|
multiple times. These compilations give rise to multiple versions of
|
|
|
|
the graph node dispatch functions. Per-node constructor-functions
|
|
|
|
interrogate CPU hardware, select the node dispatch function variant to
|
|
|
|
use, and set the vlib_node_registration_t ".function" member to the
|
|
|
|
address of the selected variant.
|
|
|
|
|
|
|
|
Details
|
|
|
|
-------
|
|
|
|
|
|
|
|
Declare the node dispatch function as shown, using the VLIB\_NODE\_FN macro. The
|
2021-08-19 11:38:06 +02:00
|
|
|
name of the node function **MUST** match the name of the graph node.
|
2018-08-10 11:05:52 -04:00
|
|
|
|
2021-08-19 11:38:06 +02:00
|
|
|
::
|
2018-08-10 11:05:52 -04:00
|
|
|
|
|
|
|
VLIB_NODE_FN (ip4_sdp_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
|
|
|
|
vlib_frame_t * frame)
|
|
|
|
{
|
|
|
|
if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
|
|
|
|
return ip46_sdp_inline (vm, node, frame, 1 /* is_ip4 */ ,
|
|
|
|
1 /* is_trace */ );
|
|
|
|
else
|
|
|
|
return ip46_sdp_inline (vm, node, frame, 1 /* is_ip4 */ ,
|
|
|
|
0 /* is_trace */ );
|
2021-08-19 11:38:06 +02:00
|
|
|
}
|
2018-08-10 11:05:52 -04:00
|
|
|
|
|
|
|
We need to generate *precisely one copy* of the
|
|
|
|
vlib_node_registration_t, error strings, and packet trace decode function.
|
|
|
|
|
|
|
|
Simply bracket these items with "#ifndef CLIB_MARCH_VARIANT...#endif":
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
#ifndef CLIB_MARCH_VARIANT
|
|
|
|
static u8 *
|
|
|
|
format_sdp_trace (u8 * s, va_list * args)
|
|
|
|
{
|
|
|
|
<snip>
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
#ifndef CLIB_MARCH_VARIANT
|
|
|
|
static char *sdp_error_strings[] = {
|
|
|
|
#define _(sym,string) string,
|
|
|
|
foreach_sdp_error
|
|
|
|
#undef _
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
#ifndef CLIB_MARCH_VARIANT
|
|
|
|
VLIB_REGISTER_NODE (ip4_sdp_node) =
|
|
|
|
{
|
|
|
|
// DO NOT set the .function structure member.
|
|
|
|
// The multiarch selection __attribute__((constructor)) function
|
|
|
|
// takes care of it at runtime
|
|
|
|
.name = "ip4-sdp",
|
|
|
|
.vector_size = sizeof (u32),
|
|
|
|
.format_trace = format_sdp_trace,
|
|
|
|
.type = VLIB_NODE_TYPE_INTERNAL,
|
|
|
|
|
|
|
|
.n_errors = ARRAY_LEN(sdp_error_strings),
|
|
|
|
.error_strings = sdp_error_strings,
|
|
|
|
|
|
|
|
.n_next_nodes = SDP_N_NEXT,
|
|
|
|
|
|
|
|
/* edit / add dispositions here */
|
|
|
|
.next_nodes =
|
|
|
|
{
|
|
|
|
[SDP_NEXT_DROP] = "ip4-drop",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
To belabor the point: *do not* set the ".function" member! That's the job of the multi-arch
|
|
|
|
selection \_\_attribute\_\_((constructor)) function
|
|
|
|
|
|
|
|
Always inline node dispatch functions
|
|
|
|
-------------------------------------
|
|
|
|
|
|
|
|
It's typical for a graph dispatch function to contain one or more
|
|
|
|
calls to an inline function. See above. If your node dispatch function
|
|
|
|
is structured that way, make *ABSOLUTELY CERTAIN* to use the
|
|
|
|
"always_inline" macro:
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
always_inline uword
|
2021-08-19 11:38:06 +02:00
|
|
|
ip46_sdp_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
|
2018-08-10 11:05:52 -04:00
|
|
|
vlib_frame_t * frame,
|
|
|
|
int is_ip4, int is_trace)
|
|
|
|
{ ... }
|
|
|
|
|
|
|
|
Otherwise, the compiler is highly likely NOT to build multiple
|
2021-08-19 11:38:06 +02:00
|
|
|
versions of the guts of your dispatch function.
|
2018-08-10 11:05:52 -04:00
|
|
|
|
|
|
|
It's fairly easy to spot this mistake in "perf top." If you see, for
|
|
|
|
example, a bunch of functions with names of the form
|
|
|
|
"xxx_node_fn_avx2" in the profile, *BUT* your brand-new node function
|
|
|
|
shows up with a name of the form "xxx_inline.isra.1", it's quite likely
|
|
|
|
that the inline was declared "static inline" instead of "always_inline".
|
|
|
|
|
2018-10-24 09:23:23 -04:00
|
|
|
Modify CMakeLists.txt
|
|
|
|
---------------------
|
2018-08-10 11:05:52 -04:00
|
|
|
|
2018-10-24 09:23:23 -04:00
|
|
|
If the component in question already lists "MULTIARCH_SOURCES", simply
|
|
|
|
add the indicated .c file to the list. Otherwise, add as shown
|
|
|
|
below. Note that the added file "new_multiarch_node.c" should appear in
|
|
|
|
*both* SOURCES and MULTIARCH_SOURCES:
|
2018-08-10 11:05:52 -04:00
|
|
|
|
|
|
|
::
|
|
|
|
|
2018-10-24 09:23:23 -04:00
|
|
|
add_vpp_plugin(myplugin
|
|
|
|
SOURCES
|
|
|
|
new_multiarch_node.c
|
2021-08-19 11:38:06 +02:00
|
|
|
...
|
2018-10-24 09:23:23 -04:00
|
|
|
|
|
|
|
MULTIARCH_SOURCES
|
|
|
|
new_ multiarch_node.c
|
|
|
|
...
|
|
|
|
)
|