01=上期内容回顾
在往期的内容中,我们介绍了思科VPP软件核心函数vlib_main()函数的业务逻辑介绍,
深入浅出思科VPP24.02系列:vlib_unix_main初始化介绍
深入浅出思科VPP24.02系列:thread0函数业务逻辑介绍
深入浅出思科VPP24.02系列:vlib_main函数业务逻辑介绍
本期我们将继续深入浅出思科vpp24.02系列专题,介绍VPP初始化phymems初始化的函数的业务逻辑介绍。
02=vlib_physmem_init函数介绍
在往期的内容中,我们介绍了思科VPP软件对内存空间初始化的函数vlib_physmem_init()的业务逻辑介绍,其在vlib_main()中备调用的。
其逻辑如下所示:
clib_error_t *
vlib_physmem_init (vlib_main_t * vm)
{
vlib_physmem_main_t *vpm = &vm->physmem_main;
clib_error_t *error = 0;
u64 *pt = 0;
void *p;
/* check if pagemap is accessible */
pt = clib_mem_vm_get_paddr (&pt, min_log2 (sysconf (_SC_PAGESIZE)), 1);
if (pt && pt[0])
vpm->flags |= VLIB_PHYSMEM_MAIN_F_HAVE_PAGEMAP;
vec_free (pt);
#ifdef __linux__
if ((error = linux_vfio_init (vm)))
return error;
#endif /* __linux__ */
p = clib_mem_alloc_aligned (sizeof (clib_pmalloc_main_t),
CLIB_CACHE_LINE_BYTES);
memset (p, 0, sizeof (clib_pmalloc_main_t));
vpm->pmalloc_main = (clib_pmalloc_main_t *) p;
if (vpm->base_addr == 0)
vpm->base_addr = VLIB_PHYSMEM_DEFAULT_BASE_ADDDR;
clib_pmalloc_init (vpm->pmalloc_main, vpm->base_addr, vpm->max_size);
/* update base_addr and max_size per actual allocation */
vpm->base_addr = (uword) vpm->pmalloc_main->base;
vpm->max_size = (uword) vpm->pmalloc_main->max_pages <<
vpm->pmalloc_main->def_log2_page_sz;
return error;
}
函数声明:clib_error_t *vlib_physmem_init (vlib_main_t * vm)
返回值:函数返回一个指向 clib_error_t 类型的指针,如果初始化成功,返回NULL(或0),表示没有错误;如果失败,返回一个错误信息的指针。
03=函数工程意义分析
1、变量声明:指向物理内存管理主结构体的指针。
vlib_physmem_main_t *vpm = &vm->physmem_main;
typedef struct
{
u32 flags;
uword base_addr;
uword max_size;
#define VLIB_PHYSMEM_MAIN_F_HAVE_PAGEMAP (1 << 0)
#define VLIB_PHYSMEM_MAIN_F_HAVE_IOMMU (1 << 1)
vlib_physmem_map_t *maps;
clib_pmalloc_main_t *pmalloc_main;
} vlib_physmem_main_t;
2、检测页表可访问性
使用 clib_mem_vm_get_paddr
函数尝试获取页表地址,通过open函数打开虚拟文件系统proc下的路径(/proc/self/pagemap)获取pagemap的关系,如果成功且页表非空,则设置 vpm
的标志位 VLIB_PHYSMEM_MAIN_F_HAVE_PAGEMAP
,表示有页表可用。
/* check if pagemap is accessible */
pt = clib_mem_vm_get_paddr (&pt, min_log2 (sysconf (_SC_PAGESIZE)), 1);
if (pt && pt[0])
vpm->flags |= VLIB_PHYSMEM_MAIN_F_HAVE_PAGEMAP;
vec_free (pt);
那么DPDK/VPP中区别传统linux,为什么需要大页呢?
答:在DPDK中,页表大页(Huge Pages for Mbuf Pools)是为了提高内存的效率和性能,通过将内存区域映射到大型页面上减少内存碎片,从而使得DPDK的内存池能够更高效地管理内存。
3、Linux VFIO 初始化:
如果代码在Linux系统上编译和运行,尝试初始化VFIO(Virtual Function I/O),这是Linux内核的一个框架,允许用户空间程序直接管理硬件设备。如果初始化失败,则返回错误。
#ifdef __linux__
if ((error = linux_vfio_init (vm)))
return error;
#endif /* __linux__ */
4、分配并初始化物理内存分配器:
使用 clib_mem_alloc_aligned
函数分配一块对齐到缓存行大小的内存,用于物理内存分配器 clib_pmalloc_main_t
。
p = clib_mem_alloc_aligned (sizeof (clib_pmalloc_main_t),
CLIB_CACHE_LINE_BYTES);
memset (p, 0, sizeof (clib_pmalloc_main_t));
将分配的内存清零,并设置为 vpm->pmalloc_main
。
vpm->pmalloc_main = (clib_pmalloc_main_t *) p;
5、设置物理内存基地址和最大max_size数值大小:
如果 vpm->base_addr
为0,则将其设置为默认值 VLIB_PHYSMEM_DEFAULT_BASE_ADDDR。
调用 clib_pmalloc_init
函数初始化物理内存分配器,传入基地址和最大大小。
更新 vpm->base_addr
和 vpm->max_size
,根据实际的物理内存分配器初始化结果。
if (vpm->base_addr == 0)
vpm->base_addr = VLIB_PHYSMEM_DEFAULT_BASE_ADDDR;
clib_pmalloc_init (vpm->pmalloc_main, vpm->base_addr, vpm->max_size);
/* update base_addr and max_size per actual allocation */
vpm->base_addr = (uword) vpm->pmalloc_main->base;
vpm->max_size = (uword) vpm->pmalloc_main->max_pages <<
vpm->pmalloc_main->def_log2_page_sz;
6、向上一级调用函数 vlib_main()返回类型为clib_error_t的返回值。
04=读取phymems参数的方法
1、该部分通过命令行show physmem [verbose | detail | map]读取vpm->pmalloc_main显示上述初始化配置的参数。
static clib_error_t *
show_physmem (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
vlib_physmem_main_t *vpm = &vm->physmem_main;
unformat_input_t _line_input, *line_input = &_line_input;
u32 verbose = 0, map = 0;
if (unformat_user (input, unformat_line_input, line_input))
{
while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (line_input, "verbose"))
verbose = 1;
else if (unformat (line_input, "v"))
verbose = 1;
else if (unformat (line_input, "detail"))
verbose = 2;
else if (unformat (line_input, "d"))
verbose = 2;
else if (unformat (line_input, "map"))
map = 1;
else
break;
}
unformat_free (line_input);
}
if (map)
vlib_cli_output (vm, " %U", format_pmalloc_map, vpm->pmalloc_main);
else
vlib_cli_output (vm, " %U", format_pmalloc, vpm->pmalloc_main, verbose);
return 0;
}
VLIB_CLI_COMMAND (show_physmem_command, static) = {
.path = "show physmem",
.short_help = "show physmem [verbose | detail | map]",
.function = show_physmem,
};
05=本次内容总结
本次内容主要是介绍了VPP中对于底层硬件大页相关的初始化操作的介绍,其思维导图可以总结为:
总的来说,本次介绍的vlib_physmem_init函数的目的是初始化VPP库中的物理内存管理模块,包括检查页表可访问性、在Linux上初始化VFIO(如果适用)、分配并初始化物理内存分配器,并设置物理内存的基地址和最大大小。函数show_physmem是对配置进行读取显示的功能。本次介绍就到此为止。小伙伴们,你们学会了吗?
作者简介
作者:通信行业搬砖工
前深圳某大型通信设备厂商从业人员
前H3Com骨干网核心交换路由器人员
前某全球500强通信公司通信砖家