前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入浅出思科VPP24.02系列:内存初始化vlib_physmem_init逻辑介绍

深入浅出思科VPP24.02系列:内存初始化vlib_physmem_init逻辑介绍

作者头像
通信行业搬砖工
发布2024-10-14 15:35:23
1030
发布2024-10-14 15:35:23
举报
文章被收录于专栏:网络虚拟化

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()中备调用的。

其逻辑如下所示:

代码语言:javascript
复制
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、变量声明:指向物理内存管理主结构体的指针。

代码语言:javascript
复制
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,表示有页表可用。

代码语言:javascript
复制
  /* 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内核的一个框架,允许用户空间程序直接管理硬件设备。如果初始化失败,则返回错误。

代码语言:javascript
复制
#ifdef __linux__
  if ((error = linux_vfio_init (vm)))
    return error;
#endif /* __linux__ */

4、分配并初始化物理内存分配器:

使用 clib_mem_alloc_aligned 函数分配一块对齐到缓存行大小的内存,用于物理内存分配器 clib_pmalloc_main_t

代码语言:javascript
复制
  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

代码语言:javascript
复制
 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_addrvpm->max_size,根据实际的物理内存分配器初始化结果。

代码语言:javascript
复制
  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显示上述初始化配置的参数。

代码语言:javascript
复制
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强通信公司通信砖家

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

本文分享自 通信行业搬砖工 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档