一、源码目录(Directory layout)
二、源码分类(Implemetation taxonomy)
vpp数据平面分为四个不同的层:
- 基础架构层:包括vppinfra,vlib,svm和二进制api库。源码:/src/{vppinfra, vlib, svm, vlibapi, vlibmemory}
- 通用网络协议栈层:vnet。源码:/src/vnet
- 应用程序shell:vpp。源码:/src/vpp
- 日益丰富的数据平面插件。源码:/src/plugins
Vppinfra:
Vppinfra是一个基本的c-library服务集合,可以构建直接在裸机上运行的独立程序。它提供高性能动态数组,hash表,位图,高精度实时时钟支持,精细的日志记录和数据结构序列化。
Vectors
Vppinfra向量是无处不在的动态调整大小的数组,具有用户定义的“头”。许多vpppinfra数据结构(例如hash,堆,池)是具有各种不同头的向量。
内存布局如下所示:
User header (optional, uword aligned)
Alignment padding (if needed)
Vector length in elements
User's pointer -> Vector element 0
Vector element 1
...
Vector element N-1
如上所示,向量API处理指向向量第0个元素的指针。 如果指为空针则说明向量的有效长度为0。
为了避免内存分配器的分配失败,通常在保留内存分配的同时将向量的长度重置为零。通过vec_reset_length(v)宏将向量长度字段设置为零。
通常情况下不存在用户标头。用户头允许在vppinfra向量上构建其他数据结构。用户可以通过vec * _aligned宏指定数据元素的对齐方式。
向量元素可以是任何C语言数据结构类型,例如 (int,double,struct )。许多宏都用a类型的变体支持向量数据的对齐,而h类型的变体支持非零长度向量头。_ha类型的变体支持以上两者。
头或对齐相关宏类型的变体使用不一致将导致延迟以及各种失败问题。
Bitmaps
Vppinfra位图是动态的,使用vppinfra向量API来构建。
Pools
Vppinfra池结合了Vectors和Bitmaps,可以快速分配和释放具有独立生命周期的固定大小的数据结构。池非常适合给每个会话分配结构。
Hashes
Vppinfra提供了几种不通类型的哈希。
在涉及数据包分类/会话查找相关的数据平面问题是通常使用/src/vppinfra/bihash_templat.[ch] 有界索引可扩展哈希值。Bihashes是线程安全的。,不需要读取锁。只需要一个简单的自旋锁确保在同一时刻一次只有一个线程写入一个entry。
在涉及需要字符串精确匹配的控制平面代码中通常使用/src/vppinfra/hash.[ch]中的原始vppinfra哈希。
Format
Vppinfra中的Format大致相当于c语言中的printf。
Format的第一个参数是(u8 *)向量,它附加当前格式化操作的结果。
调用示例:
u8 * result;
result = format (0, "junk = %d, ", junk);
result = format (result, "more junk = %d\n", more_junk);
NULL指针是完全正确的0长度向量。格式返回(u8 *)向量,而不是C字符串。如果要打印(u8 *)向量,需要使用“%v”格式字符串。
如果想要一个(u8 *)向量同时也是一个正确的C字符串,可以使用以下任一方案:
vec_add1 (result, 0)
or
result = format (result, "<whatever>%c", 0);
Format通过“%U”格式实现了一种特别方便的用户格式方案。 例如:
u8 * format_junk (u8 * s, va_list *va)
{
junk = va_arg (va, u32);
s = format (s, "%s", junk);
return s;
}
result = format (0, "junk = %U, format_junk, "This is some junk");
Unformat
Vppinfra的unformat与scanf有点类似,但是更加的通用。
从C-string或(u8 *)向量初始化unformat_input_t,然后通过unformat()进行解析,如下所示:
unformat_input_t input;
unformat_init_string (&input, "<some-C-string>");
/* or */
unformat_init_vector (&input, <u8-vector>);
然后循环解析各个元素:
while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (&input, "value1 %d", &value1))
;/* unformat sets value1 */
else if (unformat (&input, "value2 %d", &value2)
;/* unformat sets value2 */
else
return clib_error_return (0, "unknown input '%U'", format_unformat_error,
input);
}
Vppinfra errors and warnings
vpp数据平面中的许多函数都具有clib_error_t *类型的返回值。如果返回NULL则clib_error_t *表示“正确,没有错误”。
Clib_warning(<format-args>)是一种添加调试的便捷方式;clib警告预置的功能:自动添加行信息以明确定位消息源。
Serialization
Vppinfra序列化功能支持允许程序员轻松地序列化和反序列化复杂的数据结构。底层的原始序列化/反序列化函数使用网络字节序,因此在小端主机上序列化并在大端主机上反序列化不会存在结构问题。
Event-logger, graphical event log viewer
vppinfra事件记录器提供轻量级的精确时间戳(低于100ns)事件记录服务。代码:/src/vppinfra/{elog.c, elog.h}
序列化支持可以轻松保存并合并一组事件日志。
典型的事件定义和日志记录调用如下所示:
ELOG_TYPE_DECLARE (e) =
{
.format = "tx-msg: stream %d local seq %d attempt %d",
.format_args = "i4i4i4",
};
struct { u32 stream_id, local_sequence, retry_count; } * ed;
ed = ELOG_DATA (m->elog_main, e);
ed->stream_id = stream_id;
ed->local_sequence = local_sequence;
ed->retry_count = retry_count;
ELOG_DATA宏返回一个指向20字节任意事件数据的指针,并按照format_args所述进行格式化。除了整数格式之外,CLIB事件记录器还提供了一些有趣的补充。“t4”格式可以很好的打印枚举值:
ELOG_TYPE_DECLARE (e) =
{
.format = "get_or_create: %s",
.format_args = "t4",
.n_enum_strings = 2,
.enum_strings = { "old", "new", },
};
如下所示:“.format”字段可能包含以下一个或多个实例:
i1 - 8-bit unsigned integer i2 - 16-bit unsigned integer i4 - 32-bit unsigned integer i8 - 64-bit unsigned integer f4 - float f8 - double s - 以NULL结尾的字符串 sN - N字节字符数组 t1,2,4 - 每一个事件枚举ID T4 - 事件日志字符串表的偏移量
vpp引擎有几个事件日志的调试CLI:
vpp# event-logger clear
vpp# event-logger save <filename> # for security, writes into /tmp/<filename>.
# <filename> must not contain '.' or '/' characters
vpp# show event-logger [all] [<nnn>] # display the event log
# by default, the last 250 entries
事件日志默认为128K条entry。 命令行参数“vlib {elog-events <nnn>}”配置事件日志的entry大小。
VLIB:
Vlib提供向量处理支持,包括图形节点调度,可靠多播支持,超轻量级协作多任务线程,CLI,.DLL插件支持,物理内存和Linux epoll支持。
Node Graph Initialization
vlib数据包处理应用程序总是定义一组图节点来处理数据包。通过VLIB_REGISTER_NODE宏来构造vlib_node_registration_t数据结构。在运行时,框架将这样的注册集处理成有向图。在运行时向图添加节点很容易,该框架不支持删除节点。
vlib_node_registration_t的类型成员函数如下:
VLIB_NODE_TYPE_PRE_INPUT:在所有其他节点类型之前运行
VLIB_NODE_TYPE_INPUT:在pre_input节点之后尽可能的经常运行
VLIB_NODE_TYPE_INTERNAL:仅当通过添加待处理帧进行处理而明确地使其可运行时
VLIB_NODE_TYPE_PROCESS:只有在明确地设置为runnable时。“进程”节点实际上是协作的多任务线程。他们必须在相当短的时间后明确暂停。
要准确理解图节点调度程序,请阅读:/src/vlib/main.c:vlib_main_loop
Graph node dispatcher
Vlib_main_loop()调度图节点。