08.DPU网络开发SDK—DPDK(七)

接上次内容继续对rte_eal_init()所做的工作进行分析。
 
24. 内存初始化
24.2. 内存分配
调用eal_memalloc_init()来处理内存分配,前半部分根据进程是primary还是secondary走不同的流程,后半部分两者相同。

前后两个初始化过程中,用到了一个特殊的func,rte_memseg_list_walk(),该func的传入参数是一个rte_memseg_list_walk_t类型的函数指针及void *通用指针。在list_walk()中,会对rte_config.mem_config.memsegs列表中每一个memseg list依次调用函数指针指向的函数,且保证了整个过程中是在mem_config.memory_hotplug_lock锁的保护下进行的。

  • 以secondary方式
通过list_walk()来调用secondary_msl_create_walk()来为每个memseg list分配内存,该func中,根据primary的memseg list的大小,初始化一个local_memsegs结构,初始化还是调用rte_fbarray_init()来实现。primary的memseg list的大小从mem_config.memsegs获取到。

  • 以primary方式
该过程中,通过test_memfd_create执行memfd_create系统调用,测试当前系统是否支持创建匿名的内存共享,检查结果根据当前系统的一些配置情况决定是否以错误退出。

分primary和secondary之后,不管哪种方式初始化,都会执行fd_list_create_walk()。create_walk()对每个memseg list调用alloc_list(),alloc_list()会初始化文件描述符列表数组fd_list,每个memseg list对应一个数组项,数组项中会指向一块内存,用于存储文件描述符,这里的文件描述符即为匿名内存共享文件的描述符。

24.3. 初始化大页
初始化大页同样分primary和secondary

  • 以primary方式
调用rte_eal_hugepage_init(),如果internal_config设置了legacy_mem,那么调用eal_legacy_hugepage_init(),否则调用eal_dynmem_hugepage_init()。

 A. eal_dynmem_hugepage_init()
对于每一种大小类型的大页,确定好每个socket上页面的数量;统计出每个numa node上的内存大小;根据这些信息调用eal_dymem_calc_num_pages_per_socket()最终确定每种大小类型的页面的数量。

确定好这些信息之后,多次调用eal_memalloc_alloc_seg_bulk()来映射页面,该func通过list_walk()调用alloc_seg_walk()来完成这些工作。

B. eal_legacy_hugepage_init()
分为两种情况执行,一种禁用hugetlbfs情况下,另一种是在启用情况下。
 
a. 禁用hugetlbfs
此时允许的内存大小为64GB,页面大小为4KB,以此得出所需要的一个页面数量之后,调用eal_memseg_list_init_named()来重新初始化mem_config.memsegs[0]这个memseg list。然后通过eal_memseg_list_alloc()间接调用eal_get_virtual_area()为memseg分配虚拟地址,下一步通过mmap()系统调用创建匿名映射并获得匿名映射地址addr,接下来通过eal_memseg_list_populate()填充mem_config.memsegs[0]这个memseg list,以addr为基准计算出每个memseg的地址,填到相应的结构体中。
 
b. 启用hugetlbfs
首先统计出用了哪几种大小类型的大页内存,并且计算出总的大页内存页面数量,分别存放在used_hp数组和nr_hugepages当中。接下来开辟一块内存tmp_hp,用于存放nr_hugepages个struct hugepage_file结构,hugepage_file结构用于存放一些信息,比如该大页被映射到进程地址空间的哪个虚拟地址,大页物理地址是多少,所属socket_id是多少。

接下来,对于每一种页面大小类型的大页,做如下操作:

1.调用map_all_hugepages()映射该大小类型的所有大页,具体做法是调用open()打开大页在/sys文件系统中的文件,然后mmap()之后获取一个虚拟地址,并记录在结构体当中。

2.如果启用了物理地址,且IOVA的模式不是VA,那么调用find_physaddrs()获取到每个大页的物理地址并保存下来,否则调用set_physaddrs()设定一个伪物理地址。

3.调用find_numasocket()确定每个大页所属的socket_id。

4.根据物理地址将tmp_hp进行排序。

接下来根据tmp_hp中的信息,统计出每个socket每种大小类型的大页的数量,更新这些信息更新到internal_config.hugepage_info结构体当中,并最终确定每个socket下大页的数量。

接下来调用create_shared_memory()创建一个共享文件,大小为nr_hugepages个struct hugepage_file结构体,路径为/var/run/dpdk/rte/hugepage_data;接下来调用copy_hugepages_to_shared_mem()将该进程中分配到的大页信息写入到该文件当中。需要指出的是,到此为止所需要分配的内存就分配完了,后续不会再根据需要再额外分配大页内存,即使在DPDK进程运行过程中遇到内存耗尽的情况,所以在最后的收尾工作中,会将一些不需要的memseg list做释放处理。

  • 以secondary方式
调用rte_eal_hugepage_attach()来实现,同样分为是否设置了lagacy_mem,分别调用eal_legacy_hugepage_attach()和eal_hugepage_attach()

A. eal_legacy_hugepage_attach()
打开primary进程创建的文件/var/run/dpdk/rte/hugepage_data,并读取相应的信息,该文件的是多个hugepage_file结构体。对于每一个结构体中包含的信息,通过mmap()将大页内存还原到当前secondary进程当中,并以此初始化memseg list。

B. eal_hugepage_attach()
调用eal_memalloc_sync_with_primary(),该func会调用sync_walk()。sync_walk()会根据primary进程中的memseg list信息初始化自己的memseg list。sync_walk()会调用sync_existing()去打开/sys文件系统中的大页文件去确认已经被primary分配的内存和未被primary分配的内存跟memseg list中的信息是一致的,在确保一致的情况下才可以进行后续工作。
未完待续… 
往期回顾:
07.DPU网络开发SDK—DPDK(六)
06.DPU网络开发SDK—DPDK(五)
05.DPU网络开发SDK—DPDK(四)

07.DPU网络开发SDK—DPDK(六)

上次内容继续对rte_eal_init()所做的工作进行分析。
 
  1. 大页内存配置
internal_conf中的no_hugetlbfs指明是否禁用大页内存,通过命令行参数”–no-huge”设置禁用,默认情况下大页内存是开启的。DPDK根据进程是primary还是secondary的调用不同的初始化过程来初始化大页内存。
  • 按照primary方式初始化
调用eal_hugepage_info_init()来初始化primary进程。
该func首先调用hugepage_info_init()来初始化大页信息,首先遍历/sys/kernel/mm/hugepages中每个名称以”hugepages-“开头的目录,这种目录每个都代表了一种大小的页面,从目录名称中即可获得大页的大小,如hugepages-1048576kB代表1GB大小的页面。遍历过程中,根据目录名称获取到大页内存页面大小之后,接下来检查大页是否被正确挂载。打开/proc/mounts文件,查找到fstype为hugetlbfs类型的挂载项,获取到挂载点;如果没有挂载点,那么会寻找下一个大小类型的大页。找到挂载点之后,会调用calc_num_pages()计算页面数量。
需要额外注意的是,对于arm架构,可以支持4种页面大小类型,其他架构则只支持3种页面类型,多出的页面大小类型不做处理。多种符合要求的大页类型经过分析之后,相关信息会存放在internal_config的hugepage_info数组中。
hugepage_info_init()之后,如果是不共享文件模式(internal_config中的no_shconf置位),那么初始化过程就到此为止了,如果是共享模式,需要额外完成如下工作:通过调用create_shared_memory()在/var/run/dpdk/rte目录下创建hugepage_info文件,并通过mmap()将文件映射到DPDK进程的虚拟地址空间,之后将internal_config的hugepage_info数组拷贝到该文件中。
  • 按照secondary方式初始化
调用eal_hugepage_info_read()来初始化secondary进程,该func打开primary进程创建的/var/run/dpdk/rte目录下的hugepage_info文件,将文件中的信息拷贝到internal_config结构中的hugepage_info数组当中。
  1. 日志初始化
调用rte_eal_log_init()来初始化文件,该func中调用系统调用fopencookie和openlog来初始化日志文件。
  1. VFIO初始化(可选)
VFIO是否进行初始化由DPDK编译时是否开启了VFIO_PRESENT编译选项决定,开启了该编译选项时,调用rte_eal_vfio_setup()来初始化VFIO。初始化过程主要是判断当前系统中是否存在vifo模块。
  1. 内存域初始化
rte_eal_memzone_init()初始化的是rte_config中的mem_config下的memzone成员。同样根据DPDK进程是primary还是secondary分为不同的初始化过程。
  • 按照primary方式初始化
调用rte_fbarray_init()来初始化primary进程的memzone。DPDK中rte_memzone结构体是表示memzone的结构,需要为其分配内存空间;分配内存空间之前需要确定空间大小;DPDK允许RTE_MAX_MEMZONE(默认值2560)个内存域,那么就需要sizeof(rte_memzone) * RTE_MAX_MEMZONE大小的内存空间;同时还需要一个位图结构来表示这些内存域的使用情况,cal_data_size()完成了上述的计算工作。
确定完需要的内存大小之后,通过eal_get_virtual_area()确定一个虚拟地址data作为rte_memzone结构体的存储地址。确定好地址之后,接下来会根据是否设置了no_shconf对data进行不同的处理:如果是非共享模式,那么需要在虚拟地址data上创建一个匿名的内存映射;如果是共享的,会在/var/run/dpdk/rte目录下创建一个名称为fbarray_memzone的文件,并将文件通过mmap映射到虚拟地址data上。上面提到的一些信息,会存放在一个mem_area类型的结构体当中,并插入到模块全局列表mem_area_tailq中;另外还会存放在mem_config下的memzone成员当中。
  • 按照secondary方式初始化
调用rte_fbarray_attach()来初始化secondary进程的memzone,部分过程和rte_fbarry_init()相同,但是会直接打开fbarray_memzone的文件将该文件mmap()到虚拟地址空间data上。
  1. 内存初始化
调用rte_eal_memory_init()来初始化内存配置,分为多个步骤进行。
24.1. 内存段初始化
首先调用rte_eal_memseg_init()来初始化memseg。初始化memseg之前,需要将当前进程的资源限制进行下修改,更改可打开文件的最大数量。初始化memseg同样按照进程是primary还是secondary分别执行不同的初始化过程。
  • 按照primary方式初始化
编译DPDK时,会通过宏定义指定编译的系统架构为32位还是64位,这也决定了memseg的初始化方式的不同。
以64位系统初始化memseg_primary_init()为例,首先会初始化一个本地的结构体memtype的数组,结构体内包含hugepagesize和socket_id两个值,也就是说数组大小n_memtypes为前面统计出的大页内存种类数量*主机socket数量。memtype数组信息填充之后,计算出一些内存大小限制条件:通过宏定义RTE_MAX_MEM_MB(值为512GB)、RTE_MAX_MEM_MB_PER_TYPE(值为64GB)和n_memtypes确定单种类内存的大小限制max_mem_per_type和总的内存大小限制max_mem;通过RTE_MAX_MEMSEG_LISTS和n_memtypes确定每种memtype的segment数量。
接下来需要为每种memtype创建segment list,针对每种类型,需要将下列内容列入考虑范围后,得出一个精确的segment大小。
  • 该memtype可用内存总量
  • 每个segment允许的内存总量
  • 适配内存总量需要的segment数量
  • memtype允许的segment数量
  • 每个segment list允许的segment数量
  • 允许创建的segment list数量
确定好这些限制之后,调用eal_memseg_list_init()初始化每一个memseg list,该func最终是调用rte_fbarray_init()来实现初始化;然后是调用eal_memseg_list_alloc()来分配空间,该func最终是调用eal_get_virtual_area()来实现虚拟地址的分配。
  • 按照secondary方式初始化
调用memseg_secondary_init()来初始化memseg,该func中首先调用rte_fbarray_attach()来实现从文件获取primary进程memseg分配情况,然后通过调用eal_memseg_list_alloc()并最终调用eal_get_virtual_area()来分配虚拟内存空间。出现的几个func在前面memzone的初始化过程中已经出现过,这里不再具体说明。
 
内存初始化过程很长,未完待续……
 
往期回顾:
06.DPU网络开发SDK—DPDK(五)
05.DPU网络开发SDK—DPDK(四)
04.DPU网络开发SDK—DPDK(三)

05.DPU网络开发SDK—DPDK(四)

接上次内容继续对rte_eal_init()所做的工作进行分析。
12.初始化配置
rte_config_init()中,会根据process_type进行不同的初始化任务。process_type是由eal的启动参数指定的,目前支持两种模式:primary和secondary。process_type在解析参数时存放在internal_config中,然后此处赋值到rte_config中。
 
  • 按照primary方式启动
DPDK在解析参数的时候,会初始化一个runtime_dir全局变量。如果没有特殊配置,这个目录为/var/run/dpdk/rte,DPDK会基于此目录中的文件实现一些进程间通信。
 
primary方式启动时,会调用rte_eal_config_create(),该过程在runtime_dir中创建一个名称为config的文件,将文件大小截为rte_mem_config的大小,并添加一个文件锁。文件锁类型为struct flock,这种锁可以指定文件的哪些数据片段需要被保护。在此处保护的是整个文件,也就是整个rte_mem_config。
 
接下来需要将该文件映射到DPDK进程的虚拟空间当中,调用mmap()进行此操作,映射之后可读可写,且其他进程同样可以映射该文件到其虚拟空间当中。在映射之前,会确定映射到进程的具体哪个虚拟地址上,该过程由eal_get_virtual_area()实现。
 
eal_get_virtual_area()会基于internal_config中的base_virtaddr确定目标映射地址(该值默认为0,可通过启动参数修改之)。在base_virtaddr的基础上,以系统的page_size为递增单位,依次检查某个映射地址是否满足映射要求,直到找第一个满足要求的映射地址为止。
 
完成对config文件的映射之后,将rte_config中的rte_mem_config拷贝到映射好的虚拟地址空间,并将rte_config中rte_mem_config的指针指向此空间,后续对rte_mem_config的修改就可以直接反馈到config文件当中了。
 
  • 按照secondary方式启动
如果是secondary启动DPDK进程,则直接打开primary进程创建的config文件并mmap()到secondary进程中,后将rte_config中rte_mem_config的指针指向此空间。如此secondary进程看到的rte_mem_config看到的内容和primary进程看到的是一样的,两者在mem配置方面实现了进程间的通信,且primary进程在创建config文件的过程中为其设置了锁,使得对其的写操作是互斥的,避免了两个进程之间的竞争问题。
13.初始化信号处理
此处启动一个线程eal_intr_thread_main(),该线程中无限循环执行如下的过程:
  • 创建一个epoll,并将中断信号传递管道中读信号的一侧的文件描述符intr_pipe.readfp添加到epoll中进行监听。
  • 将intr模块的全局变量intr_sources列表中的所有文件描述符添加到epoll中进行监听。
  • 无限循环等待epoll中的文件描述符的事件的发生,并依次处理发生的事件。
  • 关闭epoll。
14.创建一个定时器
此处调用Linux的系统函数timerfd_create()创建一个定时器的文件描述符,并将其存在alarm模块的全局变量intr_handle中。在后续的操作中,intr_handle会注册到前面提到的intr_sources列表中。
15.初始化进程间通信文件
rte_mp_channel_init()在/var/run/dpdk/rte目录下生成一个文件,名称为mp_socket_*。调用open_socket_id创建UDP类型的socket,保存于全局变量mp_fd中,并调用bind将mp_fd与通信文件进行绑定。
如果进程是primary,那么通信文件为/var/run/dpdk/rte/mp_socket,如果是secondary,通信文件为/var/run/dpdk/rte/mp_socket_${pid}_${timestamp}。然后重启一个线程,线程处理函数为mp_handle(),该处理函数无限循环接收mp_fd代表的socket发来的信息并处理之。该进程间通信文件的是primary和secondary进程之间的通信,secondary进程之间是没有通信需求的。
 
16.设备热插拔初始化
eal_mp_dev_hotplug_init()中,如果当前进程是primary,则会注册一个回调函数handle_secondary_request()用于处理处理来自secondary进程的设备热插拔请求。反之secondary进程中会注册handle_primary_request()函数。
 
17.各总线遍历设备
bus模块全局变量rte_bus_list存储了所有的总线的列表,此处依次遍历每个总线并调用总线的scan()方法。
 
在DPDK中存在一个描述总线的结构struct rte_bus,该结构中定义了一些总线的接口函数,scan()就是其中之一。对于总线来说,scan()方法的主要作用是用来遍历所有的设备,并将设备与注册在该总线上的驱动进行匹配,当匹配成功时,建立驱动和设备之间的对应关系。DPDK只提供了接口要求各总线实现该接口,具体如何实现则由各总线设备自行定义。
 
总线通过注册的方式注册到rte_bus_list当中,目前DPDK支持的总线有:
  • vmbus
  • vdev
  • pci
  • fpga
  • fslmc
  • dpaa
struct rte_bus的结构如下,除了scan()方法,还有probe(),find_device()等。

 

WechatIMG318

rte_bus的实现是仿照了Linux内核中总线的角色,主要是为了建立设备和驱动之间的联系,后期我们会以某个总线设备为例查看这些接口方法的具体实现。
未完待续… 
 
往期回顾:
04.DPU网络开发SDK—DPDK(三)
03.DPU网络开发SDK——DPDK(二)
02.DPU网络开发SDK——DPDK(一)
01.DPU简介

02.DPU网络开发SDK——DPDK(一)

作者:大空新一

随着软件定义网络SDN的不断发展,网络数据转发面的需求越来越多样化,这体现在更快的数据包处理速率,更高的网络吞吐带宽,更灵活的自定义网络协议。

传统的硬件设备无法满足网络协议的自定义,而基于Linux内核网络协议栈的网络数据转发则存在处理流程繁琐,效率低下等问题。在此背景下,Intel公司提供了一款高性能网络数据平面开发套件DPDK(data plane development kit),它提供了一个简单方便的,完整的,快速的数据包处理解决方案。

目前,DPDK被广泛应用在网络转发组件的开发当中,如负载均衡网关,NAT网关,虚拟机交换机OVS也同样包含了对DPDK的支持。由于DPDK能够向网络转发面组件提供快速高效的对原生数据包进行处理的能力,其在DPU的研发上也扮演着重要的角色。

DPDK主要包含以下几大特性,借助这些特性网络转发面组件得以实现快速高效的数据包处理:

 

  • 用户态数据包处理
传统情况下,当网卡收到数据包时,需要经过内核网络协议栈的处理,协议栈将头部一层层进行剥离之后,才能将最终数据交付给用户态进程;过程中会存在内核空间向用户空间进行内存拷贝的情况,这会消耗大量的计算资源。
Linux内核源码中提供了uio驱动模块,网卡硬件设备在绑定该模块之后,会将设备暴露在用户空间;DPDK通过读取该uio文件,能够直接收取原始的二层数据包并直接写入用户态进程的内存空间当中;用户态进程可直接对数据包的各层数据进行分析并根据需要进行修改、转发或选择丢弃;发送数据包时,则通过写入该uio文件进行发送。
在收取-处理-发送的过程中,数据包绕开了内核网络协议栈的处理逻辑,且处理过程中未发生过内存的拷贝,这就节省了大量的开销;且用户态进程拿到的数据包是原始的二层数据包,开发者可根据需要自主决定对该数据包的处理方式,更便于自定义网络协议的实现。
无DPDK与有DPDK的对比
DPDK工作原理
 
  • PMD轮询模式收包
传统Linux系统中,当网卡收到数据包时,会借助DMA技术将数据包发送到预先分配好的内存缓冲区里面,之后产生中断通知内核有数据包过来。Linux内核会对中断进行响应,将接收到的数据帧交给内核中的网络协议栈进行处理,最后数据被复制到了用户空间或者直接在内核协议栈中处理完毕。
通过中断通知内核有数据包到来的方法在低带宽低吞吐等环境下能够满足需求,但在云平台这样要求高带宽高吞吐的情况下,中断方式会引发中断风暴,内核需不间断的处理中断请求,不利于内核对数据包和其他内核运算的处理。
DPDK提供了PMD(Poll mode driving)轮询模式实现对数据包的收取,不需要网卡通过中断通知内核收取到数据包,而是由用户态进程不间断去查询网卡是否收取到数据包,有则处理之,无则进行下一轮查询。在高吞吐情况下,一次轮询可批量处理多个数据包,减少了中断引发的系统开销。
  • 大页内存
Linux内核中默认页面的大小是4KB,在64位系统中页表被划分为5级,用户态进程在寻址时需要经过4次页表查找才能将虚拟地址转换为物理地址。页表查找过程是高性能处理进程的一项瓶颈,而使用大页内存可有效减少页表查询的次数,减轻TLB的查询压力和查询miss的概率。在64位系统中支持2MB和1GB两种类型的大页内存,分别需要4级和3级页表的支持,DPDK一般使用1G大页内存,仅需2次页表查询即可实现虚拟地址到物理地址的转换。
  • CPU独占及亲和性
运行基于DPDK的网络转发面处理组件的主机会提前将部分CPU的Processor隔离出来专门提供给DPDK使用,DPDK进程在初始化阶段会根据配置初始化线程数量,并将线程和Processor进行绑定。绑定之后Processor不会再处理绑定线程以外的其他线程,被绑定线程也不会调度到其他Processor上进行处理,此举有效减少了因线程调度产生的上下文切换的开销,并减少了Cache Miss发生的概率。
  • 有助于转发处理逻辑的数据结构
DPDK提供了一些有助于编写网络转发平面处理逻辑的数据结构,如Timer,Mbuf,Ring等。Timer实现了计时器的功能;Mbuf提供了基本的内存缓存片段,用于缓存数据包,并支持片段向前后进行扩展;Ring实现了FIFO队列,且该队列是无锁的。
  • QoS和ACL
DPDK提供了相应的数据结构,基于此数据结构及关联的API,可以在用户态进程中实现限速和访问控制这两项网络转发平面必须的功能。
  • 适配多种类型的实体或虚拟网络设备
DPDK是由Intel开发并维护的,最初仅支持对Intel系列网卡的支持,后DPDK陆续支持了对Mellanox,Virtio等设备的支持,使得DPDK的使用范围进一步扩大。
 
以上是DPDK的在加速网络数据面处理上减少的开销和提供的便利,底层实现上仍然是基于Linux内核提供的功能,在接下来的几期文章中将对DPDK深入分析,以理清其内部实现机制。 
往期推荐
01.DPU简介

01.DPU简介

作者:大空新一

DPU是Data Processing Unit的简称,它是近几年发展起来的专用处理器,是CPU、GPU之后,数据中心场景中的第三颗重要的算力芯片,为诸如云平台等需要高带宽、低延迟、数据密集的计算场景提供计算能力。

 

为什么需要DPU

 

传统的云计算主机上,CPU除了负担客户购买的计算能力之外,还需要负担云平台中必要的支撑组件的运行,典型例子如云平台VPC网络数据转发平面的常见组件OVS。

 

一般场景下,OVS运行在云计算主机上,基于VXLAN技术实现云平台VPC网络。在转发网络数据包的过程中,需要处理大量的计算工作,如流表匹配,数据包的封装与解封,checksum计算等,这些工作必然要消耗云主机大量的CPU资源。那么是否可以有一个额外的处理单元,替CPU分担这些支撑组件的运行工作呢?

 

DPU就是这个额外的处理单元。DPU在物理上体现为一个插在主机上的PCIe设备,通过PCI总线与主机进行交互。

 

DPU存在一个ARM架构的片上系统(SoC),SoC上可运行基础的Linux,如此类似OVS这样的云平台中的支撑组件则不必运行在主机中,而是运行在DPU的SoC上。

这样主机上的CPU则可专注于处理客户的计算任务,使得CPU能够发挥其最大的计算能力为客户提供服务。

 

 

DPU的发展历程

 

Fungible最先于2016年提出DPU的概念,也是第一家专注于设计DPU的创业公司。

 

Mellanox在2019年率先推出了基于BlueField的DPU,后Nvidia于2020年收购Mellanox,同年提出BlueField 2.0 DPU,自此,DPU开始活跃起来,并吸引了大量国内外知名创业公司的加入。

 

国外公司的典型代表有Marvell、Pensando、Broadcom、Intel,国内的公司集中在创业团队,他们凭借自身的技术积累入局DPU,如中科驭数、星云智联等。

 

2021年Nvidia推出了基于BlueField 3.0的DPU。

 

 

DPU的工作内容

 

DPU的角色类似于CPU的“小秘书”,主机将一些需要重复计算或者计算量大的“杂活”“累活”卸载给DPU进行处理。

 

DPU卸载的内容常见的有:数据中心网络服务,比如前文提到的OVS虚拟交换、虚拟路由;数据中心存储服务,比如RDMA、NVMe(一种远程存储技术);数据中心的安全服务,比如防火墙、加解密等等。

 

DPU有自己的处理单元和硬件设备,前面提到的加密和虚拟交换等通常使用软件实现,并在CPU里运行。而DPU可以使用硬件实现并运行这些支撑组件,这样比在CPU运行要快好几个数量级,这也就是我们常常会听到的“硬件加速”。

 

此外,由于将支撑组件运行在了DPU中,而客户的应用则在CPU里运行,这样就把二者隔离开了,会带来很多安全和性能上的好处。

 

 

DPU在网络上的应用

 

DPU的前身是Smart NIC,Smart NIC是没有SoC的,运行不能脱离主机CPU,且其对网络的卸载主要是在数据面上,控制面仍然是主机CPU上的工作。

 

Smart NIC在网络方面的可卸载的工作内容是由硬件设定好的,内容不可变,而DPU中引入了可编程硬件,使用方可根据自身需要自行决定硬件行为,而无需再受限于固定化程式化的硬件处理流程。

 

在诸如云计算等数据中心不断提高对带宽要求的背景下,DPU相比于Smart NIC的灵活性使得其在网络上的应用更加的宽泛。

 

典型的应用场景包括:

 

  • 像OVS一样对数据包进行解析、匹配和处理
  • 基于RoCE的RDMA数据传输加速
     
  • 通过GPU-Direct加速器绕过CPU,将来自存储和其他GPU的数据通过网络直接传给GPU
  • TCP通信加速,包括RSS、LRO、checksum等操作
     
  • 网络虚拟化的VXLAN和Geneve Overlay卸载和VTEP卸载
     
  • 面向多媒体流、CDN和新的4K/8K IP视频的“Packet Pacing” 流量整型加速
     
  • 电信Cloud RAN的精准时钟加速器,例如5T for 5G (精准时钟调度5G无线报文传输技术)功能
     
  • 在线IPSEC和TLS加密加速,但不影响其它正在运行的加速操作
     
  • 支持SR-IOV、VirtIO 和PV(Para-Virtualization)等虚拟化
     
  • 安全隔离:如信任根、安全启动、安全固件升级以及基于身份验证的容器和应用的生命周期管理等