首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >vpp plugins插件相关介绍

vpp plugins插件相关介绍

作者头像
dpdk-vpp源码解读
发布2023-03-07 17:17:19
发布2023-03-07 17:17:19
2.3K0
举报
文章被收录于专栏:DPDK VPP源码分析DPDK VPP源码分析

前面文章有介绍过通过make-plugin.sh命令行自动生成plugins的架构,vpp 软件架构介绍;本文就来讲解一下plugins插件的加载和使用流程。

plugins加载流程

plugins插件加载是比较靠前的,应为plugins里面会有node节点的注册,需要在生成node节点注册及node图初始化完成之前。下面是阅读源码后总结的流程:

代码语言:javascript
复制
int main (int argc, char *argv[]) /*main函数位置src\vpp\vnet\main.c*/
|   |--解析配置文件是否配置plugin_path路径,如果配置存储到全局变量vlib_plugin_path 
|   |   (!strncmp (argv[i], "plugin_path", 11)) vlib_plugin_path = argv[++i];
|   |--vpe_main_init (vm)/*如果没由配置plugin_path路径,如果动态查询*/
|      |--vpp_find_plugin_path()/*查询plugin目录,配置到全局变量vlib_plugin_path */
|   |--vlib_unix_main() 
|      |--vlib_plugin_config() /*解析配置文件中plugins配置信息*/
|      |--vlib_plugin_early_init()加载插件
|      |--vlib_main()
|          |-- vlib_register_all_static_nodes (vm)/*node节点注册*/
|          |--vlib_node_main_init (vm) /*node 图初始化*/

插件必须在vlib_main前执行的原因: plugin库中有很多attribute__((__constructor))类型的构造函数是在dlopen()装载时被调用。 比如下面的宏定义完成注册,都是串联到全局的链表上, VLIB_CONFIG_FUNCTION (acl_plugin_config, "acl-plugin"); VLIB_REGISTER_NODE (acl_in_l2_ip6_node)

通过命令行show plugins可以查询当前环境vpp加载插件信息及插件描述信息(版本号,名称,描述)。

代码语言:javascript
复制
DBGvpp# show plugins
 Plugin path is: /usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins

     Plugin                                   Version                          Description
  1. ioam_plugin.so                           20.09-rc0~472-g4ee78fbcc         Inbound Operations, Administration, and Maintenance (OAM)
  2. perfmon_plugin.so                        20.09-rc0~472-g4ee78fbcc         Performance Monitor
  3. tracedump_plugin.so                      20.09-rc0~472-g4ee78fbcc         Streaming packet trace dump plugin
  4. urpf_plugin.so                           20.09-rc0~472-g4ee78fbcc         Unicast Reverse Path Forwarding (uRPF)
  5. tlspicotls_plugin.so                     20.09-rc0~472-g4ee78fbcc         Transport Layer Security (TLS) Engine, Picotls Based
  6. l3xc_plugin.so                           20.09-rc0~472-g4ee78fbcc         L3 Cross-Connect (L3XC)
  7. mdata_plugin.so                          20.09-rc0~472-g4ee78fbcc         Buffer metadata change tracker.

plugins路径查询

上面讲到如果配置文件中没有设置plugin_path插件路径时,会动态查询,下面时函数vpp_find_plugin_path 查询插件路径的部分代码如下:

代码语言:javascript
复制
static void vpp_find_plugin_path ()
{
  extern char *vat_plugin_path;
  char *p, path[PATH_MAX];
  int rv;
  u8 *s;
  /* find executable path */
  if ((rv = readlink ("/proc/self/exe", path, PATH_MAX - 1)) == -1)
    return;
  /* readlink doesn't provide null termination */
  path[rv] = 0;
  /* strip filename strip bin 跳过执行文件名*/
  ...
  s = format (0, "%s/lib/" CLIB_TARGET_TRIPLET "/vpp_plugins:"
          "%s/lib/vpp_plugins", path, path);
  vec_add1 (s, 0);
 /*保存插件路径*/
  vlib_plugin_path = (char *) s;
....
}

上一节通过show plugins命令行查询到当前环境plugins目录:

代码语言:javascript
复制
/usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins

这里由2点疑问: 1、readlink函数作用? readlink()会将参数path的符号链接内容存储到参数buf所指的内存空间,返回的内容不是以‘\0’作字符串结尾,但会将字符串的字符数返回,这使得添加‘\0’变得简单。若参数bufsiz小于符号连接的内容长度,过长的内容会被截断;如果 readlink 第一个参数指向一个文件而不是符号链接时,readlink 设 置errno 为 EINVAL 并返回 -1。readlink()函数组合了open()、read()和close()的所有操作。 path是一个存在的软连接。 path="/proc/self/exe"标识获取当前执行程序的绝对路径。

在github上写了readlink测测试程序,你可以在你环境上去执行看一下:https://github.com/jin13417/dpdk-vpp-learning/test/readlink.c

2、CLIB_TARGET_TRIPLET 这个宏应该就是x86_64-linux-gnu,怎么来的。 这里是使用了cmake的configure_file来设置的CLIB_TARGET_TRIPLET。你可以通过下面链接来学习cmake的configure_file使用。

https://www.cnblogs.com/gaox97329498/p/10952732.html

1、在源码路径上通过grep -rn CLIB_TARGET_TRIPLET 查询宏定义的位置

代码语言:javascript
复制
/mnt/f/workspce/vpp/src$ grep -rn CLIB_TARGET_TRIPLET
vppinfra/config.h.in:23:#define CLIB_TARGET_TRIPLET "@CMAKE_C_COMPILER_TARGET@"

2、再次查询CMAKE_C_COMPILER_TARGET,如下

代码语言:javascript
复制
:/mnt/f/workspce/vpp/src$ grep -rn CMAKE_C_COMPILER_TARGET
CMakeLists.txt:46:set(CMAKE_C_COMPILER_TARGET ${CMAKE_SYSTEM_PROCESSOR}-linux-gnu)

CMAKE_SYSTEM_PROCESSOR是cmake环境变量,我们可以通过下面命令来查询设置。

代码语言:javascript
复制
$ cmake --system-information | grep CMAKE_SYSTEM_PROCESSOR
CMAKE_SYSTEM_PROCESSOR "x86_64"

3、configure_file设置时在vppinfra/CMakeLists.txt文件中。

代码语言:javascript
复制
###vppinfra/CMakeLists.txt cmake编译文件生成config.h 定义了CLIB_TARGET_TRIPLET 内容
configure_file(
  ${CMAKE_SOURCE_DIR}/vppinfra/config.h.in
  ${CMAKE_BINARY_DIR}/vppinfra/config.h
)

4、下面时我编译完成后再vppinfra目录下config.h的内容

代码语言:javascript
复制
 cat build-root/install-vpp-native/vpp/include/vppinfra/config.h
...
#ifndef included_clib_config_h
#define included_clib_config_h
#ifndef CLIB_LOG2_CACHE_LINE_BYTES
#define CLIB_LOG2_CACHE_LINE_BYTES 6
#endif
#define CLIB_TARGET_TRIPLET "x86_64-linux-gnu"
#define CLIB_VECTOR_GROW_BY_ONE 0
#endif

plugins配置解析

vlib_plugin_config函数完成plugins相关配置信息的解析,代码比较简单这里就不说了。下面是vpp默认statup.conf文件中plugins字段的信息内容

代码语言:javascript
复制
plugins {
   ## Adjusting the plugin path depending on where the VPP plugins are
   #path /ws/vpp/build-root/install-vpp-native/vpp/lib/vpp_plugins
   ## Disable all plugins by default and then selectively enable specific plugins 
   #plugin default { disable } 
   plugin dpdk_plugin.so { enable }
   plugin acl_plugin.so { enable }
   ## Enable all plugins by default and then selectively disable specific plugins
   # plugin dpdk_plugin.so { disable }
   # plugin acl_plugin.so { disable }
}

除了默认配置文件中写的,还又下面不经常使用的参数: name-filter :设置过滤的插件名称 vat-path :vat插件路径 vat-name-filter:设置vat过滤插件名称 plugin acl_plugin.so skip-version-check :跳过版本的检查。在注册插件的时候执行依赖vpp的版本时,才会检查。

1、上述配置中,设置了加载dpdk、acl查询但是命令行查询的时候,还是全部加载上了,这个因为插件默认是全部加载的,只能先disable所有的插件,再设置需要加载的插件才能生效。 2、path、plugin_path设置有什么区别?两个都设置后,那个生效? 这个需要阅读代码的实现了,默认是path的优先级更高。

plugins加载

注册插件

下面是单元测试插件的注册函数,默认指定是不需要加载的。

代码语言:javascript
复制
VLIB_PLUGIN_REGISTER () =
{
  .version = VPP_BUILD_VER,
  .description = "C unit tests",
  .default_disabled = 1,
};

对应相关结构体字段的描述信息。

代码语言:javascript
复制
/* *INDENT-OFF 注册插件结构体字段描述* */
typedef CLIB_PACKED(struct {
  u8 default_disabled; /*是否需要加载*/
  const char version[32];  /*插件的版本信息*/
  const char version_required[32];/*设置查询依赖的vpp版本信息*/
  const char overrides[256];/*不是太理解*/
  const char *early_init; /*指定early_init函数名称,在加载so时进行初始化*/
  const char *description;/*插件描述信息*/
}) vlib_plugin_registration_t;
/* *INDENT-ON 插件信息结构体* */
typedef struct
{
  u8 *name; /*plugin插件名称libcpudef_cplist_plugin.so*/
  u8 *filename;/*plugin so文件名称libcpudef_cplist_plugin.so.0.0.0*/
  struct stat file_info;/*文件详细信息*/
  void *handle;/*dlopen返回的so句柄*/
  /* plugin registration 插件注册信息*/
  vlib_plugin_registration_t *reg;
  char *version;/*版本信息*/
} plugin_info_t;

平时不太关注插件注册的一些信息,但是可能在某些场景中会使用到,不如下面提供了2个比较好的参数 1、version_required:设置当前so依赖的vpp版本信息,如果和vpp版本不一会会报错。 但是也可以在配置文件中设置跳过检查, 2、early_init:指定early_init函数名称,在加载so时执行初始化 使用dlsym函数得到函数的地址,执行初始化动作。

插件加载

插件加载的流程大概意义就是读取插件目录下的文件,判断是否是.so格式后,逐个调用load_one_plugin函数来加载动态so。代码也比较简单,自己看下吧。

代码语言:javascript
复制
vlib_plugin_early_init()
   |---vlib_load_new_plugins()
   |---load_one_plugin()

plugins之间互相访问

vpp本身并不支持插件之间互相访问,但是我们可以通过一些方法来解决。我工作中用的方法就行是src目录下添加一个test目录,参考src/vet/CMakeLists.txt的方式来定义自己的so库。

代码语言:javascript
复制
/*CMakeLists.txt如下*/
##############################################################################
# test Library
##############################################################################
add_vpp_library(test
  SOURCES ${TEST_SOURCES}
  MULTIARCH_SOURCES ${TEST_MULTIARCH_SOURCES}
  INSTALL_HEADERS ${TEST_HEADERS}
  API_FILES ${TEST_API_FILES}
  LINK_LIBRARIES vppinfra svm vlib ${OPENSSL_LIBRARIES}
  DEPENDS vpp_version_h api_headers
)

把2个插件都需要调用函数,或者全局变量放在这里面来声明和定义(当然你也可以直接放在vnet下)。

总结

本文简单描述了vpp插件的加载流程及代码逻辑的一些解读,另外前段时间在vpp群里面有同学问到两个插件之间怎么互相访问。上面也说了大概实现思路。你们有更好的方法,欢迎留言讨论。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DPDK VPP源码分析 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • plugins加载流程
  • plugins路径查询
  • plugins配置解析
  • plugins加载
  • 注册插件
  • 插件加载
    • plugins之间互相访问
    • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档