searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享

BlueField3 DOCA Core

2024-09-04 09:41:55
49
0

DOCA Core

介绍

DOCA Core对象为应用程序开发人员提供了与各种DOCA库交互的统一的接口。DOCA Core API和对象为应用程序提供了一个标准化的流程和构建,同时隐藏了软硬件内部实现细节。DOCA Core旨在保持性能的同时提供适当的抽象。

DOCA Core对于DPUCPU安装具有相同的API(头文件),但是如果该CPU没有实现该API,则该API调用可能会返回DOCA_ERROR_NOT_SUPPORTED。但,WindowsLinux的除外,因为DOCA CoreWindowsLinux安装之间确实存在API差异。

DOCA Core向应用程序编写者公开了C语言API,用户必须根据其应用程序所需的DOCA Core组件包含要使用的正确头文件。

DOCA Core可分为以下几个软件模块:

DOCA Core模块

说明

General

l  DOCA Core枚举和基本的数据结构

l  头文件:DOCA_error.hDOCA_types.h

Device操作

l  查询设备信息(主机端和DPU)Device capability(例如,设备的PCIe BDF地址)

n  DPU

u  Get local DPU devices

u  Get representors list (表示host local devices)

n  host

u  Get local devices

u  Query device capabilities and library capabilities

u  Open and use the selected device representor

u  Relevant entities DOCA_devinfo, DOCA_devinfo_rep, DOCA_dev, DOCA_dev_rep

n  头文件:DOCA_dev.h

内存管理

l  处理应用程序使用的优化过的内存池,并支持在DOCA库之间共享资源(同时隐藏与硬件相关的技术)

l  Data buffer服务(例如,支持SGL)

l  将主机内存映射到DPU以便直接访问

l  相关实体:DOCA_buff, doca_mmap, DOCA_buf_inventory, DOCA_buf_array, DOCA_bufpool

l  头文件:doc_buf_h, doc_buf_inventory .h, doc_mmap .h, doc_buf_array .h, doc_bufpool .h

处理引擎与任务执行

l  支持向DOCA库提交任务并跟踪任务进度(支持轮询模式和事件驱动模式)

l  相关实体:doc_ctx, doc_task, doc_event, doc_event_handle_t, doc_pe

l  头文件:doc_ctx .h

Sync event

l  用于同步不同的处理器(例如:DPU和主机)

l  头文件: DOCA_dpa_sync_event.h, DOCA_sync_event.h

前提条件

DPU target和主机侧支持DOCA Core对象,两者必须满足以下前提条件:

1.      DOCA version 2.0.2或更高

2.      NVIDIA BlueField软件版本4.0.2或更高

3.      NVIDIA BlueField-3固件版本32.37.1000或更高

4.      NVIDIA BlueField-2固件版本24.37.1000或更高

架构

以下章节将介绍DOCA Core各个软件模块的架构。

General

所有的Core对象都遵循相同的流程,在之后的快路径中不用再分配,流程如下:

1.      创建对象实例(如:doca_mmap_create

2.      配置实例(如:doca_mmap_set_memory_range

3.      启动实例(如:doca_mmap_start

在实例启动后,可以在数据路径中安全的使用,不用再分配,当实例完成后,必须停止和destroy掉(doca_mmap_stop, doca_mmap_destroy

Core对象可以被重新配置和重新启动(如:create->configure->start->stop->configure->start),对象是否支持该功能请阅读该对象相关的头文件。

DOCA_error_t

所有DOCA API都会返回类型为DOCA_error_t的状态码。

更多详细信息请参考DOCA_error.h

通用数据结构和枚举

以下数据结构对于所有的DOCA API都适用。

更多信息请参考DOCA_types.h

DOCA Device
Local Device and Representor

先决条件

对于representors模型,BlueField必须工作在DPU模式下。

拓扑结构

DOCA device代表一个由软件或硬件实现的处理单元。DOCA device公开了其属性以便于应用程序选择正确的设备。DOCA Core支持两种类型的设备:

Local device:在本地系统(DPUhost)中公开的真实的设备,可以执行DOCA库的处理任务。

Representor device local device的代表,local device通常在host(SF除外),而representor device总是在DPU(主机端设备在DPU上的代理)

上图表示一个DPU(图右侧)连接到一个host(图左侧)。Host有两个PFPF0PF1PF0有两个VFVF0VF1PF1只有一个VFVF1。通过DOCA SDK API,用户可以获取到这五个设备,这五个设备是host上的local device

DPU端,对于每一个host functionPFVF)都有一个一对一的representor device(例如:hpf0host device pf0representor device)。每个SF也有一个一对一的representor deviceSF及其representor device都位于DPU中。

如果用户在DPU端查询local device,会获取到两个设备,即:pf0pf1,它们是以下设备的父设备:

l  7representor device

n  5个前缀为hpfrepresentor devicehostDPU5个箭头指向的设备,即:hpf0hpf0vf0hpf0vf1hpf1hpf1vf0

n  2SF devicerepresentor device,即:pf0sf0pf1sf0

l  2local SF device,即:p0s0p1s0

也就是说,pf0hpf0hpf0vf0hpf0vf1p0s0pf0sf0的父设备,pf1hpf1hpf1vf0p1s0pf1sf0的父设备。

如上图所示,虚线风格成了两部分,上半部分表示DPU设备p0,下半部分表示DPU设备p1p0p1负责创建所有其它local devicehost PFpf0pf1),host VFpf0vf0pf0vf1pf1vf0),DPU SFp0s0p1s0)。所以,p0p1是所有设备的父设备,也可以访问每个functionrepresentor(通过DOCA_devinfo_rep_list_create)。

Local devicerepresentor device的映射关系

host侧的local device

DPU侧的representor device

DPU侧的device

pf0

hpf0

p0

pf0vf0

hpf0vf0

pf0vf1

hpf0vf1

pf1

hpf1

p1

pf1vf0

hpf1vf0

使用doca_mmap_export_dpu()选择host侧的local device,使用doca_mmap_create_from_export()选择DPU侧的device

SFVF的区别

VFSR-IOV标准中定义的,VF共享PF的资源,由PF创建和分配。SFscalable functionsub-function)是Mellanox DPU系列产品提供的一种轻量级的function,具有独立的function和资源。VFSF都有一个parent function,即PFSF可以单个进行部署,而VF需要整体一起使能。SF因为没有全部实现PCI配置空间、寄存器和复位,所以比较轻量级。多个SF可以共享MSI-X向量,减少了硬件和中断控制器的中断数量。SF主要用于容器,当创建容器的时候,就可以SF对应的netdevice分配给容器使用。SF可以从DPU以云原生方式部署,这避免了在representor设置中的跨系统同步。换句话说,在将SF热插到host之前,管理员可以完全设置SF、它的representorQoSSF所需的参数。

device discovery

为了使用DOCA库和DOCA Core对象,应用程序必须打开DPUhost上的device。如上图所示,通常有多个设备可用。应用程序可以根据device capabilitiesDOCA Core API来选择打开哪个设备。流程如下:

1.      获取Device列表;

2.      根据某些capability和属性来选择一个DOCA_devinfo,上图根据PCI地址来选择;

3.      一旦找到符合要求的DOCA_devinfo,就打开DOCA_dev

4.      在打开了DOCA_dev后,就可以关闭DOCA_devinfoDOCA_dev可以继续使用;

5.      最后关闭DOCA_dev

representor device discovery

为了和DOCA 库和DOCA Core对象一起工作,一些应用程序必须打开并使用DPU上的representor device。在打开并使用representor device之前,需要工具来选择representor deviceDOCA Core API提供了丰富的Device capability以便应用程序选择正确的Device对(Device及其representor device)。

1.      DPU端,应用程序获取某个local Devicerepresentor Device列表;

2.      根据某些属性来选择一个DOCA_devinfo_rep,上图根据PCI地址来选择;

3.      一旦找到符合要求的DOCA_devinfo_rep,就打开DOCA_dev_rep

4.      在打开了DOCA_dev_rep后,就可以关闭DOCA_devinfo_repDOCA_dev_rep可以继续使用;

5.      最后关闭DOCA_dev_rep

需要注意的是,representor device的属性存在缓存,因此当representor device的任何属性动态改变后,需要重新获取representor device列表才能感知新的改动。

DOCA Memory Subsystem

DOCA内存子系统旨在优化性能,同时保持最小的内存占用作为主要设计目标。DOCA内存有如下两个主要组件:

l  DOCA_bufdata buffer的描述符,不是实际的data buffer,只是拥有data buffermetadata

l  doca_mmapDOCA_buf指向的data buffer pool,应用程序将内存作为单个内存区域提供,并为某些设备提供访问它的权限。

doca_mmap作为data bufferpool,也有个实体叫DOCA_buf_inventory,作为DOCA_bufpool。与所有DOCA实体一样,DOCA内存子系统对象是不透明的,只能由DOCA SDK实例化。

下图所示,显示了DOCA内存子系统的各个模块:

在图中,有两个DOCA_buf_inventory。每个DOCA_buf都指向一个memory buffer,而memory buffer又是doca_mmap的一部分,DOCAo_mmap是一段连续的内存,并且被映射给两个dev

要求和注意事项

l  DOCA内存子系统要求使用池,而不是动态分配:

n  DOCA_buf_inventoryDOCA_bufpool

n  doca_mmapdata memorypool

l  doca_mmap中的内存可以映射给一个或多个dev

l  dev访问内存有权限限制;

l  DOCA_buf不是实际的data buffer,包含的是描述的data buffer相关信息,即:metadata

l  内存映射和dev的使用(如:mr)对应用程序是隐藏了实现细节的。

doca_mmap

doca_mmap不仅仅是一个数据缓冲区,因为它向应用程序开发人员隐藏了许多细节(例如,RDMA技术,设备处理等),为软件提供了适当的抽象。doca_mmap是在主机和DPU之间共享内存的最佳方式,这样DPU就可以直接访问主机内存,主机也可以直接访问DPU内存。后续,doca_mmap映射的内存简称mmap

local mmap

这是doca_mmap的基本类型,它将本地内存映射到本地设备。

1.      应用程序创建doca_mmap

2.      应用程序使用doc_mmap_set_memrange设置mmap的内存范围;

3.      应用程序添加dev,授予dev访问内存区域的权限;

4.      应用程序可以使用doc_mmap_set_permissions指定设备对该内存的访问权限;

5.      如果mmap仅在本地使用,则必须指定DOCA_ACCESS_LOCAL_*

6.      如果mmap是在主机上创建,但是要与DPU共享,那么必须指定DOCA_ACCESS_PCI_*

7.      如果mmap是在DPU上创建,但是要与主机共享,那么必须指定DOCA_ACCESS_PCI_*

8.      如果mmap与远端RDMA target共享,则必须指定DOCA_ACCESS_RDMA_*

9.      应用程序启动mmap,此后不能再对mmap做修改;

10.    调用doca_mmap_export_pci和主机或DPU共享mmap,调用doca_mmap_export_rdmaRDMA target共享mmap,成功返回一个blob,如果权限不对,export会失败;

11.如果与远端RDMA target共享mmap,可以使用套接字在带外交换上一步生成的blob;如与DPU共享,建议使用DOCA Comm Channel

mmap from export

这用于访问主机内存(DPU)或远端RDMA target的内存。

1.      应用程序接收来自对端的blob

2.      应用程序调用doca_mmap_create_from_export导入对端定义的mmap

现在应用程序可以创建DOCA_buf指向这个导入的mmap来直接访问对端的内存。

注意:DPU可以访问同一台机器上的主机导出的内存;如果内存是RDMA导出的,不管内存位于同一台主机还是远程主机还是远程DPU上,DPU都可以访问。但主机只能访问通过RDMA导出的内存,不管这些内存位于远程主机上还是远程DPU上还是同一机器的DPU上。

doca_buf

doca_buf是可由DPU硬件访问的内存,且可以跨不同的DPU加速器使用。doca_buf可以CPU内存、GPU内存、主机内存甚至RDMA内存,创建后的使用方式都是类似的。doca_buf有一个地址和长度来描述一段内存区域。每个buffer还可以指向区域内的某段内存。于是可以将doca_buf分为三个部分:headroomdataroomtailroom。如下图所示:

doca_buf的注意事项

l  doca_buf有多种创建方式,一旦创建,表现出的行为都一致;

l  doca_buf可以是CPU不访问的内存,例如:RDMA内存;

l  doca_buf是线程非安全的;

l  doca_buf可以是非连续的内存区域;

l  doca_buf不拥有也不管理它所引用的数据,释放buffer不会影响它引用的内存。

Headroom

用户空间,用户可以用来保存一些私有信息,所有DOCAAPI都会忽略这部分空间。

Dataroom

保存数据或者结果。

Tailroom

可以自由写入的空间。

Buffer as Source

doca_buf作为源时,源数据就是dataroom

Buffer as Destination

doca_buf作为目的时,数据以追加的方式被写到tailroom,数据长度相应的进行增加。

Scatter/Gather List

要在非连续内存区域上执行操作,可以创建一个缓冲区链表,用单个doc_buf指向链表的头部。要创建一个缓冲区链表,用户必须首先单独分配每个缓冲区,然后将它们链接起来。

l  doca_buf_chain_list将第二个链表附加到第一个链表尾部形成一个链表;

l  doca_buf_unchain_list将指定元素从链表中摘除;

l  一旦创建了列表,就可以使用doc_buf_get_next_in_list()遍历它。一旦到达最后一个元素就返回NULL

传递一个链表和传递单个buffer是一样的。支持这个特性的DOCA库可以将一个链表组成的内存区域看做一个连续的内存区域。

当使用一个buffer链表作为源时,收集每个buffer中的数据(在dataroom中)聚合成一段连续的数据再做处理。

当使用一个buffer链表作为目的时,数据会被分散写到buffertailroom中直到全部写入为止。

Buffer Use Cases

除非另有说明,否则对缓冲区数据进行的操作不是原子性的。一旦缓冲区作为任务的一部分被传递给库,缓冲区的所有权就会转移到库中,直到任务完成。当使用doca_buf作为某个库(例如,doca_dma)的输入时,在处理完成之前,doca_buf必须保持有效且不可修改。写入正在in-flight缓冲区可能会导致异常行为。同样,从in-flight缓冲区读取数据时也不能保证数据的有效性。

Inventory

inventory负责分配doca_bufinventory有多种类型。最基本的inventory分配doca_buf时无需申请任何系统内存。其它类型的inventory要求buffer地址不能重叠。

l  所有类型的inventory启动后都零分配;

l  申请一个doca buffer需要一个data source和一个inventory

n  data source定义了数据存放的位置,谁可以访问以及访问的权限;

n  data source必须由应用程序创建;

l  inventory描述了buffer的申请模式,例如:随机访问或pool、可变大小或固定大小、连续或非连续内存;

l  有些inventory需要在分配缓冲区时提供data sourcedoca_mmap,有些则需要在创建库存时提供;

l  所有类型的inventory都是线程不安全的。

Inventory type

Inventory type

特点

使用场景

doca_buf_inventory

多个mmap,灵活的地址,灵活的buffer大小

多个大小或mmap

doca_buf_array

单个mmap,固定buffer大小;用户接收一个指向DOCA buf的数组;在DPA场景下,mmapbuffer size不能被用户配置,只能稍后从DPA设置

用于在GPUDPA上创建DOCA buffer

doca_bufpool

单个mmap,固定buffer大小,地址不受用户控制

buffer地址不重要时用作具有相同特征的buffer pool

举例

以下步骤展示了如何将主机mmap导出到DPU以供DOCA用于直接访问主机内存(例如,用于DMA)

1.在主机上创建mmap;添加单个doca_devmmap并导出,以便DPURDMA端可以访问;

2.导出到DPURDMA端(使用mmap描述符输出参数作为doc_mmap_create_from_export的输入);

DOCA Execution Model

执行模型是基于数据和应用线程的硬件处理的。DOCA不创建内部线程(操作系统的线程)。工作负载由taskevent组成。有些任务用于数据传输。最基本的数据传输就是DMA操作,把数据从一个内存位置移动到另外一个内存位置。其它操作允许接收来自网络的数据包,或者源数据的SHA值并将其写到目的地。

举例来说,一份负责数据传输的工作负载分以下三部:

1.      读源数据;

2.      对被读的数据启动操作(由专用的硬件加速器完成);

3.      写操作的结果到目的地。

每个这样的操作就被叫做任务(doca_task)。任务描述了应用程序想要提交给DOCA(硬件或DPU)的操作。为此,应用程序需要一种与硬件或DPU通信的方法。这就是doca_pe发挥作用的地方。Process engine(PE)是一个单线程对象,用于将任务排队以卸载到DOCA,并最终接收它们的compeletion状态。

Doca_pe引入了三个主要操作:

1.      提交任务;

2.      检查提交任务的进度或状态;

3.      接收任务完成的通知(以回调的形式)

一个工作负载可以被分成许多不同的任务,这些任务可以在不同的线程上执行;每个线程由不同的PE表示。每个任务必须与某个context相关联,其中context定义要执行的任务类型。

context可以从DOCA SDK中的一些库中获得。例如,要提交DMA任务,可以从doca_dma.h获取DMA context,而可以使用doca_sha.h获取SHA context。每个这样的context允许提交多种任务类型。

任务被认为是异步的,因为一旦应用程序提交任务,DOCA执行引擎(硬件或DPU)将开始处理它,应用程序可以继续执行其他处理,直到硬件完成。为了跟踪哪个任务已经完成,有两种操作模式:轮询模式和事件驱动模式。

要求和注意事项

l  任务提交/执行/API针对性能(延迟)进行了优化;

l  DOCA不管理内部线程(操作系统的线程),进度是通过轮询或者事件驱动的方式来获取;

l  执行任务基本的对象是doc_task,每个任务都是从特定的DOCA context分配的;

l  doca_pe表示一个逻辑上的线程,执行提交给PE的应用程序和任务;

l  PE不是线程安全的,每个PE由单个应用程序线程管理;

l  与执行相关的元素(例如,doca_pe, doca_ctx, doca_task)是不透明的,应用程序在使用这些元素之前执行最小的初始化或配置;

l  提交给PE的任务可能会执行失败(即使在提交成功之后);在某些情况下,可以从错误中恢复;某些情况下,只能重新初始化相关对象;

l  PE不保证顺序(例如,按一定顺序提交的任务可能会乱序完成);保序由应用程序自己负责(如:一个任务完成后再提交另外一个任务);

l  PE可以在轮询模式或事件驱动模式下工作,但不能同时在这两种模式下工作;

l  所有DOCA context都支持轮询模式。

DOCA context

DOCA Context (struct doc_ctx)定义并实现了task/event的处理。Context有多种类型。

l  Context至少利用了一种DOCA Device功能或硬件加速能力;

l  对于每种task类型能支持它的有且仅有一种context

l  一个context包含每种task类型支持的inventory

l  Context包含了每个任务类型执行的所有参数(例如:库存大小、加速处理设备);

l  每个context都需要一个PE的实例来运行其任务,也就是说context必须和PE关联才能执行任务。

下图显示了DOCA Core实体之间的关系:

1.      Doca_task与执行任务的相关doc_ctx相关联(在相关doc_dev的帮助下)

2.      Doca_task初始化后,提交给doca_pe执行;

3.      Doca_ctxs关联到doca_pe,一旦doc_task排队到doc_pe,它就由与相关联的doc_ctx执行。

下图描述了context的初始化顺序:

在启动context之后,就可以根据其支持的任务类型向PE提交任务。Context是一个线程不安全的对象,它只能关联到单个PE

配置阶段

在尝试使用doc_ctx_start()启动DOCAcontext之前,必须先配置它。有些配置是强制性的(例如,提供doc_dev)

l  配置用于启用某些任务或事件、启用默认禁用的feature以及优化性能;

l  配置使用setter函数提供,有关强制性和可选配置及其相应api的列表,请参阅context相关文档;

l  配置在创建context之后和启动之前完成,context一旦启动,就不能再配置它,除非再次停止它。

执行阶段

一旦context配置完成,就可以使用context执行任务。context通过将工作负载卸载给硬件来执行任务,而任务是异步的,因此软件需要轮询任务,直到它们完成。

应用程序采用以下模式中的一种来轮询任务:

l  Poll mode

l  Notification-driven mode

在此阶段,context和所有DOCA Core对象通过内存池执行零分配。建议应用程序也使用相同的方法。

State Machine

State

说明

dile

没有任务执行;

初始化阶段(就在doca_<T>_create(ctx)之后):所有配置api都有效;

重新配置(stop状态转换过来):部分配置api有效;

starting

context是强制性的;

Running

允许申请和提交任务,且只有此状态允许;

所有配置api被禁止;

Stopping

任务停止前的准备;

清楚所有未完成的任务;

下图描述了DOCA context状态的转换:

Internal error

DOCA Context在任何时候都可能遇到一些内部错误。如果状态正处于startingrunning状态,则可能导致状态转换到stopping

停止后,状态可能变为idle状态。但是,如果存在配置问题或错误事件阻止正确转换到startingrunningdoc_ctx_start()可能会失败。

DOCA task

任务是可卸载到硬件的(功能或处理)工作负载单元。大多数任务使用NVIDIA BlueFieldNVIDIA ConnectX硬件来加速处理任务定义的工作负载。任务是异步的(例如,通过非阻塞doca_task_submit() API提交处理的任务)

任务完成后,在context中执行预设的completion callbackcompletion callback是任务的基本属性,类似于用户数据。大多数任务都是由NVIDIA设备硬件执行或加速的IO操作。

Task Properties

任务熟悉分通用属性和特定属性。由于任务的结构是不透明的(即,它的内容不向用户公开),所以对任务属性的访问由set/get api提供。

以下是通用任务属性:

l  Setting completion callback-成功和失败是两个单独的callback

l  Getting/setting user data-用于completion callback

l  Getting task status-在失败完成时获取错误代码。

对于每个任务,只有一个所有者,就是context对象,可以通过doc_task_get_ctx () API来获取通用context对象。

以下是通用的任务api:

l  CTX(内部/虚拟)inventory中分配和释放;

l  通过setter(init API)进行配置;

l  提交任务(即,doca_task_submit(task))

l  完成后,有一组getter来访问任务执行的结果。

Task Lifecycle

每个DOCA任务对象的生命周期:

l  DOCA context进入running状态即开始,一旦进入运行状态,应用程序就可以通过调用doc_ <CTX名称>_task_<任务名称>_alloc_init(CTX…)CTX获得任务;

l  DOCA context进入stopping状态时结束,一旦相关的DOCA context离开running状态,应用程序就不能再分配任务;

从应用程序的角度来看,DOCA context提供了一个虚拟的任务清单。

DOCA Progress Engine

Progress engine(PE)支持在单线程执行环境中异步处理不同类型的多个任务和事件。它是所有基于contextDOCA库的事件循环,其中I/O completion是最常见的事件类型。

PE被设计为线程不安全的(即,它一次只能在一个线程中使用),但单个操作系统线程可以使用多个PE。用户可以通过将不同的context添加到不同的PE中,并相应地调整每个PE的轮询频率,从而为不同的context分配不同的优先级。另外一种角度看,PE是一个计划执行的工作负载队列。

没有专门的api来添加或在PE上调度工作负载,但可以通过以下方式添加工作负载:

l  PE添加DOCA context

l  注册一个DOCA eventprobe,在主动probe时执行相关的处理程序。

PE负责调度工作负载(即选择下一个要执行的工作负载)。工作负载执行的顺序与任务提交顺序、事件注册顺序或关联context的顺序无关。多个任务的completion callback可能以不同于任务提交的顺序执行。

PE的初始化流程如下图所示:

PE被创建并关联到context之后,它可以开始处理提交给context的任务。请参阅context相关文档以查找详细信息,例如可以使用context提交哪些任务。

注意:PE可以关联到多个context。这些context可以是相同类型的,也可以是不同类型的。这允许将不同的任务类型提交到相同的PE,并等待它们从相同的位置/线程完成。

初始化PE后,应用程序可以使用以下模式之一定义事件循环:

l  Poll mode

l  Notification-driven mode

PE as Event Loop Mode of Operation

任务和事件的所有completion handler都在doca_pe_progress()context中执行。doca_pe_progress()会循环执行的每个工作负载:

运行选定的工作负载单元。以下情况:

l  任务完成后,执行相关处理程序并中断循环并返回状态;

l  主动探测事件,执行相关处理程序,打破循环并返回状态;

l  后续任务处理完成,打破循环并返回状态。否则,循环结束并返回状态no progress

Polling mode

在这种模式下,应用程序提交一个任务,然后执行忙等待,以确定任务何时完成。执行顺序如下图所示:

1.      应用程序提交所有任务(一个或多个)并跟踪任务完成的数量,以了解是否完成了所有任务;

2.      应用程序通过不断轮询doc_pe_progress()等待任务完成;

3.      如果doca_pe_progress()返回1,则表示某些任务或事件处理完成;

4.      每次任务或事件处理完成时,都会相应地执行其预设的回调函数;

5.      如果任务执行出错,则执行错误回调函数。

6.      应用程序可以向完成回调函数中添加代码,以跟踪已完成和待处理工作负载的数量。在这种模式下,应用程序总是占用CPU,即使它什么都不做(忙等)

阻塞模式- Notification Driven

在这种模式下,应用程序提交任务,然后等待接收通知,最后再查询状态。执行顺序如下:

1.      应用程序从doca_pe中获得一个通知句柄,该句柄是一个Linux文件描述符,用于通知应用程序某些工作已经完成;

2.      应用程序用doc_pe_request_notification()告知PE为事件通知模式,之后不允许调用doc_pe_progress()

3.      应用程序提交任务;

4.      应用程序等待在pe-fd上接收通知(例如,Linux epoll/select)

5.      应用程序清除接收到的通知,通知PE已接收到信号,并允许PE执行通知处理;

6.      应用程序尝试通过(多次)调用doca_pe_progress()来处理接收到的通知;

请注意:不能保证对doca_pe_progress()的调用将执行任何任务完成/事件处理程序,但是PE可以继续操作。

7.      应用程序处理任务完成或事件处理程序引起的内部状态更改;

8.      重复步骤2-7,直到所有任务完成,所有预期事件都得到处理。

Progress Engine vs Epoll

Linux中的epoll机制和DOCA PE处理在事件驱动架构中的高并发性。这两个系统在等待事件或任务完成时都能有效地管理资源。

DOCA Event

要注册一个事件,用户必须调用doca_<event_type>_reg(pe,…)函数,事件处理程序必须与PE关联。注册事件后,将由doc_pe_progress()函数定期检查该事件,该函数与PE在相同的执行context中运行。如果满足事件条件,则调用处理程序函数。事件不是线程安全的,只能由它们绑定的PE访问。

Error Handling

在一个任务被成功提交后,后续对doca_pe_progress()的调用可能会失败。一旦任务失败,上下文可能会转换到stopping状态,在这种状态下,应用程序必须在销毁或重新启动上下文之前执行完所有in-flight的任务。

下图显示了应用程序如何处理来自doca_pe_progress()的错误:

1.      应用程序轮询doca_pe_progress;

2.      可能发生下列情况:

l  任务失败,调用fail completion callback;

n  由错误的参数或其他致命错误引起;

n  处理程序释放任务和所有相关资源;

l  context转换到stopping状态,并调用context state changed handler;

n  由任务失败或其他致命错误引起;

n  在这种状态下,所有in-flight任务都肯定会失败;

n  释放非in-flight的任务;

l  context转换到idle状态,并调用context state changed handler;

n  遇到错误并且context没有任何必须由应用程序释放的资源;

n  应用程序可通过再次调用start来恢复context,或者销毁context并退出应用程序。

DOCA Graph Execution

DOCA Graph可以以特定的顺序和依赖关系运行一组操作(任务、用户回调)DOCA Graph运行在DOCA PE上。DOCA Graph创建的图形实例通过doc_graph_instance_submit提交给PE

Nodes

DOCA Graphcontextusersub-graph nodes组成,这些节点可以位于网络中的位置如下:

l  根节点:根节点没有父节点,图可以有一个或多个根节点。当提交图形实例时,所有根节点开始运行;

l  边缘节点:没有子节点的节点,叶子节点;当所有边缘节点完成时,图实例就执行完成;

l  中间节点:有父节点和子节点的节点。

Context Node

Context node运行特定的任务并使用特点的DOCA context(doca_ctx)context必须在图启动之前关联到PE。任务的生命周期必须大于或等于图实例的生命周期。

User Node

User node运行用户的回调函数,以便在图实例运行期间执行某些操作(例如,调整下一个节点任务数据,比较结果等)

Sub-graph Node

sub-graph node运行着一个graph的实例。

Using DOCA Graph

1.      使用doc_graph_create创建图;

2.      创建图形节点(例如,doca_graph_node_create_from_ctx)

3.      使用doc_graph_add_dependency定义依赖项;

l  注意:DOCA graph不支持循环依赖(例如,A => B => A)

4.      使用doca_graph_start启动图;

5.      使用doc_graph_instance_create创建图形实例;

6.      设置节点数据(例如,doca_graph_instance_set_ctx_node_data)

7.      使用doc_graph_instance_submit将图实例提交给PE

8.      调用doca_pe_progress,直到图形回调被调用;

 

l  进度引擎可以同时运行图形实例和独立的任务。

DOCA Graph Limitations

l  不支持循环依赖;

l  必须至少包含一个context node

Alternative Data Path

除了DOCA PE将数据路径从CPU卸载到硬件外,另外一些库还支持备选数据路径,可以卸载DPAGPU

注意事项:

l  并非所有上下文都支持备选数据路径;

l  配置阶段总是在CPU上完成;

l  数据路径操作总是卸载到硬件上,卸载的对象可以是CPU/DPA/GPU

l  默认数据路径模式是CPU

l  每种模式都会引入一组不同的API,以在执行路径中使用;所使用的API对于特定的context实例是互斥的。

DPA

用户必须首先检查DPA上的数据路径是否支持。

要将数据路径模式设置为DPA,需要获取一个DOCA DPA实例,然后使用doc_ctx_set_datapath_on_dpa ()

使用这种模式启动context之后,就可以使用相关的API获取DPA句柄(例如,doc_rdma_get_dpa_handle())。这个句柄可以用来访问DPA代码中的DPA数据路径API

GPU

用户必须首先检查是否支持GPU上的数据路径。

如果需要将数据路径模式设置为GPU,需要获取一个DOCA GPU实例,然后使用doc_ctx_set_datapath_on_gpu ()

context以这种模式启动之后,可以使用相关的API获得GPU句柄(例如,doca_eth_rxq_get_gpu_handle())。这个句柄可以用来访问GPU代码中的GPU数据路径API

Task and Event批处理

DOCA批处理可以将多个DOCA任务或相同类型的DOCA事件组成一组并将其作为单个单元来处理。批处理的completion需要等待所有项目的completion执行完成才会执行。

批处理的completion以批处理中所有项目的completion为基础,并作为单个completion进行处理。这允许在单个API调用中进行多个DOCA任务初始化/提交和多个DOCA任务/事件completion处理。

Object Life Cycle

大多数DOCA Core对象都有着相同的处理模型:

1.      该对象由DOCA分配,因此对应用程序来说是不透明的(例如,doca_buf_inventory_create, doca_mmap_create)

2.      应用程序初始化对象并设置所需的属性(例如,doca_mmap_set_memrange)

3.      对象已启动,不允许更改配置或属性(例如,doca_buf_inventory_start, doca_mmap_start)

4.      对象被使用;对象被停止和删除(例如,doca_buf_inventory_stop→doca_buf_inventory_destroy, doca_mmap_stop→doca_mmap_destroy)

下面的过程描述了两台机器(远程机器或主机-DPU)之间的mmap导出机制:

1.      Machine1分配内存;

2.      Machine1创建mmap

3.      mmap导出到Machine2ping住内存;

4.      Machine2创建导入的mmap,并holdMachine1内存的引用;

5.      Machine2可以使用导入的mmap来分配buffer

6.      Machine2销毁导入的mmap

7.      Machine1销毁导出的mmap

8.      销毁释放内存。

RDMA bridge

DOCA Core库提供了应用程序使用的模块,同时抽象了依赖于RDMA驱动程序的许多细节。这减少了复杂性,增加了灵活性,特别是对于已经基于rdma-core的应用程序。RDMA bridge允许DOCA SDKRDMA -core之间的互相转换,可以将基于DOCA的对象转换为基于RDMA -core的对象。

要求和注意事项

对于已经使用rdma-core的应用程序,DOCA Core库可以方便其移植或K对其进行扩展。Bridge允许将DOCA Core对象转换为等价的rdma-core对象。

DOCA Core对象到rdma-core对象的映射

0条评论
0 / 1000
c****6
13文章数
2粉丝数
c****6
13 文章 | 2 粉丝
c****6
13文章数
2粉丝数
c****6
13 文章 | 2 粉丝

BlueField3 DOCA Core

2024-09-04 09:41:55
49
0

DOCA Core

介绍

DOCA Core对象为应用程序开发人员提供了与各种DOCA库交互的统一的接口。DOCA Core API和对象为应用程序提供了一个标准化的流程和构建,同时隐藏了软硬件内部实现细节。DOCA Core旨在保持性能的同时提供适当的抽象。

DOCA Core对于DPUCPU安装具有相同的API(头文件),但是如果该CPU没有实现该API,则该API调用可能会返回DOCA_ERROR_NOT_SUPPORTED。但,WindowsLinux的除外,因为DOCA CoreWindowsLinux安装之间确实存在API差异。

DOCA Core向应用程序编写者公开了C语言API,用户必须根据其应用程序所需的DOCA Core组件包含要使用的正确头文件。

DOCA Core可分为以下几个软件模块:

DOCA Core模块

说明

General

l  DOCA Core枚举和基本的数据结构

l  头文件:DOCA_error.hDOCA_types.h

Device操作

l  查询设备信息(主机端和DPU)Device capability(例如,设备的PCIe BDF地址)

n  DPU

u  Get local DPU devices

u  Get representors list (表示host local devices)

n  host

u  Get local devices

u  Query device capabilities and library capabilities

u  Open and use the selected device representor

u  Relevant entities DOCA_devinfo, DOCA_devinfo_rep, DOCA_dev, DOCA_dev_rep

n  头文件:DOCA_dev.h

内存管理

l  处理应用程序使用的优化过的内存池,并支持在DOCA库之间共享资源(同时隐藏与硬件相关的技术)

l  Data buffer服务(例如,支持SGL)

l  将主机内存映射到DPU以便直接访问

l  相关实体:DOCA_buff, doca_mmap, DOCA_buf_inventory, DOCA_buf_array, DOCA_bufpool

l  头文件:doc_buf_h, doc_buf_inventory .h, doc_mmap .h, doc_buf_array .h, doc_bufpool .h

处理引擎与任务执行

l  支持向DOCA库提交任务并跟踪任务进度(支持轮询模式和事件驱动模式)

l  相关实体:doc_ctx, doc_task, doc_event, doc_event_handle_t, doc_pe

l  头文件:doc_ctx .h

Sync event

l  用于同步不同的处理器(例如:DPU和主机)

l  头文件: DOCA_dpa_sync_event.h, DOCA_sync_event.h

前提条件

DPU target和主机侧支持DOCA Core对象,两者必须满足以下前提条件:

1.      DOCA version 2.0.2或更高

2.      NVIDIA BlueField软件版本4.0.2或更高

3.      NVIDIA BlueField-3固件版本32.37.1000或更高

4.      NVIDIA BlueField-2固件版本24.37.1000或更高

架构

以下章节将介绍DOCA Core各个软件模块的架构。

General

所有的Core对象都遵循相同的流程,在之后的快路径中不用再分配,流程如下:

1.      创建对象实例(如:doca_mmap_create

2.      配置实例(如:doca_mmap_set_memory_range

3.      启动实例(如:doca_mmap_start

在实例启动后,可以在数据路径中安全的使用,不用再分配,当实例完成后,必须停止和destroy掉(doca_mmap_stop, doca_mmap_destroy

Core对象可以被重新配置和重新启动(如:create->configure->start->stop->configure->start),对象是否支持该功能请阅读该对象相关的头文件。

DOCA_error_t

所有DOCA API都会返回类型为DOCA_error_t的状态码。

更多详细信息请参考DOCA_error.h

通用数据结构和枚举

以下数据结构对于所有的DOCA API都适用。

更多信息请参考DOCA_types.h

DOCA Device
Local Device and Representor

先决条件

对于representors模型,BlueField必须工作在DPU模式下。

拓扑结构

DOCA device代表一个由软件或硬件实现的处理单元。DOCA device公开了其属性以便于应用程序选择正确的设备。DOCA Core支持两种类型的设备:

Local device:在本地系统(DPUhost)中公开的真实的设备,可以执行DOCA库的处理任务。

Representor device local device的代表,local device通常在host(SF除外),而representor device总是在DPU(主机端设备在DPU上的代理)

上图表示一个DPU(图右侧)连接到一个host(图左侧)。Host有两个PFPF0PF1PF0有两个VFVF0VF1PF1只有一个VFVF1。通过DOCA SDK API,用户可以获取到这五个设备,这五个设备是host上的local device

DPU端,对于每一个host functionPFVF)都有一个一对一的representor device(例如:hpf0host device pf0representor device)。每个SF也有一个一对一的representor deviceSF及其representor device都位于DPU中。

如果用户在DPU端查询local device,会获取到两个设备,即:pf0pf1,它们是以下设备的父设备:

l  7representor device

n  5个前缀为hpfrepresentor devicehostDPU5个箭头指向的设备,即:hpf0hpf0vf0hpf0vf1hpf1hpf1vf0

n  2SF devicerepresentor device,即:pf0sf0pf1sf0

l  2local SF device,即:p0s0p1s0

也就是说,pf0hpf0hpf0vf0hpf0vf1p0s0pf0sf0的父设备,pf1hpf1hpf1vf0p1s0pf1sf0的父设备。

如上图所示,虚线风格成了两部分,上半部分表示DPU设备p0,下半部分表示DPU设备p1p0p1负责创建所有其它local devicehost PFpf0pf1),host VFpf0vf0pf0vf1pf1vf0),DPU SFp0s0p1s0)。所以,p0p1是所有设备的父设备,也可以访问每个functionrepresentor(通过DOCA_devinfo_rep_list_create)。

Local devicerepresentor device的映射关系

host侧的local device

DPU侧的representor device

DPU侧的device

pf0

hpf0

p0

pf0vf0

hpf0vf0

pf0vf1

hpf0vf1

pf1

hpf1

p1

pf1vf0

hpf1vf0

使用doca_mmap_export_dpu()选择host侧的local device,使用doca_mmap_create_from_export()选择DPU侧的device

SFVF的区别

VFSR-IOV标准中定义的,VF共享PF的资源,由PF创建和分配。SFscalable functionsub-function)是Mellanox DPU系列产品提供的一种轻量级的function,具有独立的function和资源。VFSF都有一个parent function,即PFSF可以单个进行部署,而VF需要整体一起使能。SF因为没有全部实现PCI配置空间、寄存器和复位,所以比较轻量级。多个SF可以共享MSI-X向量,减少了硬件和中断控制器的中断数量。SF主要用于容器,当创建容器的时候,就可以SF对应的netdevice分配给容器使用。SF可以从DPU以云原生方式部署,这避免了在representor设置中的跨系统同步。换句话说,在将SF热插到host之前,管理员可以完全设置SF、它的representorQoSSF所需的参数。

device discovery

为了使用DOCA库和DOCA Core对象,应用程序必须打开DPUhost上的device。如上图所示,通常有多个设备可用。应用程序可以根据device capabilitiesDOCA Core API来选择打开哪个设备。流程如下:

1.      获取Device列表;

2.      根据某些capability和属性来选择一个DOCA_devinfo,上图根据PCI地址来选择;

3.      一旦找到符合要求的DOCA_devinfo,就打开DOCA_dev

4.      在打开了DOCA_dev后,就可以关闭DOCA_devinfoDOCA_dev可以继续使用;

5.      最后关闭DOCA_dev

representor device discovery

为了和DOCA 库和DOCA Core对象一起工作,一些应用程序必须打开并使用DPU上的representor device。在打开并使用representor device之前,需要工具来选择representor deviceDOCA Core API提供了丰富的Device capability以便应用程序选择正确的Device对(Device及其representor device)。

1.      DPU端,应用程序获取某个local Devicerepresentor Device列表;

2.      根据某些属性来选择一个DOCA_devinfo_rep,上图根据PCI地址来选择;

3.      一旦找到符合要求的DOCA_devinfo_rep,就打开DOCA_dev_rep

4.      在打开了DOCA_dev_rep后,就可以关闭DOCA_devinfo_repDOCA_dev_rep可以继续使用;

5.      最后关闭DOCA_dev_rep

需要注意的是,representor device的属性存在缓存,因此当representor device的任何属性动态改变后,需要重新获取representor device列表才能感知新的改动。

DOCA Memory Subsystem

DOCA内存子系统旨在优化性能,同时保持最小的内存占用作为主要设计目标。DOCA内存有如下两个主要组件:

l  DOCA_bufdata buffer的描述符,不是实际的data buffer,只是拥有data buffermetadata

l  doca_mmapDOCA_buf指向的data buffer pool,应用程序将内存作为单个内存区域提供,并为某些设备提供访问它的权限。

doca_mmap作为data bufferpool,也有个实体叫DOCA_buf_inventory,作为DOCA_bufpool。与所有DOCA实体一样,DOCA内存子系统对象是不透明的,只能由DOCA SDK实例化。

下图所示,显示了DOCA内存子系统的各个模块:

在图中,有两个DOCA_buf_inventory。每个DOCA_buf都指向一个memory buffer,而memory buffer又是doca_mmap的一部分,DOCAo_mmap是一段连续的内存,并且被映射给两个dev

要求和注意事项

l  DOCA内存子系统要求使用池,而不是动态分配:

n  DOCA_buf_inventoryDOCA_bufpool

n  doca_mmapdata memorypool

l  doca_mmap中的内存可以映射给一个或多个dev

l  dev访问内存有权限限制;

l  DOCA_buf不是实际的data buffer,包含的是描述的data buffer相关信息,即:metadata

l  内存映射和dev的使用(如:mr)对应用程序是隐藏了实现细节的。

doca_mmap

doca_mmap不仅仅是一个数据缓冲区,因为它向应用程序开发人员隐藏了许多细节(例如,RDMA技术,设备处理等),为软件提供了适当的抽象。doca_mmap是在主机和DPU之间共享内存的最佳方式,这样DPU就可以直接访问主机内存,主机也可以直接访问DPU内存。后续,doca_mmap映射的内存简称mmap

local mmap

这是doca_mmap的基本类型,它将本地内存映射到本地设备。

1.      应用程序创建doca_mmap

2.      应用程序使用doc_mmap_set_memrange设置mmap的内存范围;

3.      应用程序添加dev,授予dev访问内存区域的权限;

4.      应用程序可以使用doc_mmap_set_permissions指定设备对该内存的访问权限;

5.      如果mmap仅在本地使用,则必须指定DOCA_ACCESS_LOCAL_*

6.      如果mmap是在主机上创建,但是要与DPU共享,那么必须指定DOCA_ACCESS_PCI_*

7.      如果mmap是在DPU上创建,但是要与主机共享,那么必须指定DOCA_ACCESS_PCI_*

8.      如果mmap与远端RDMA target共享,则必须指定DOCA_ACCESS_RDMA_*

9.      应用程序启动mmap,此后不能再对mmap做修改;

10.    调用doca_mmap_export_pci和主机或DPU共享mmap,调用doca_mmap_export_rdmaRDMA target共享mmap,成功返回一个blob,如果权限不对,export会失败;

11.如果与远端RDMA target共享mmap,可以使用套接字在带外交换上一步生成的blob;如与DPU共享,建议使用DOCA Comm Channel

mmap from export

这用于访问主机内存(DPU)或远端RDMA target的内存。

1.      应用程序接收来自对端的blob

2.      应用程序调用doca_mmap_create_from_export导入对端定义的mmap

现在应用程序可以创建DOCA_buf指向这个导入的mmap来直接访问对端的内存。

注意:DPU可以访问同一台机器上的主机导出的内存;如果内存是RDMA导出的,不管内存位于同一台主机还是远程主机还是远程DPU上,DPU都可以访问。但主机只能访问通过RDMA导出的内存,不管这些内存位于远程主机上还是远程DPU上还是同一机器的DPU上。

doca_buf

doca_buf是可由DPU硬件访问的内存,且可以跨不同的DPU加速器使用。doca_buf可以CPU内存、GPU内存、主机内存甚至RDMA内存,创建后的使用方式都是类似的。doca_buf有一个地址和长度来描述一段内存区域。每个buffer还可以指向区域内的某段内存。于是可以将doca_buf分为三个部分:headroomdataroomtailroom。如下图所示:

doca_buf的注意事项

l  doca_buf有多种创建方式,一旦创建,表现出的行为都一致;

l  doca_buf可以是CPU不访问的内存,例如:RDMA内存;

l  doca_buf是线程非安全的;

l  doca_buf可以是非连续的内存区域;

l  doca_buf不拥有也不管理它所引用的数据,释放buffer不会影响它引用的内存。

Headroom

用户空间,用户可以用来保存一些私有信息,所有DOCAAPI都会忽略这部分空间。

Dataroom

保存数据或者结果。

Tailroom

可以自由写入的空间。

Buffer as Source

doca_buf作为源时,源数据就是dataroom

Buffer as Destination

doca_buf作为目的时,数据以追加的方式被写到tailroom,数据长度相应的进行增加。

Scatter/Gather List

要在非连续内存区域上执行操作,可以创建一个缓冲区链表,用单个doc_buf指向链表的头部。要创建一个缓冲区链表,用户必须首先单独分配每个缓冲区,然后将它们链接起来。

l  doca_buf_chain_list将第二个链表附加到第一个链表尾部形成一个链表;

l  doca_buf_unchain_list将指定元素从链表中摘除;

l  一旦创建了列表,就可以使用doc_buf_get_next_in_list()遍历它。一旦到达最后一个元素就返回NULL

传递一个链表和传递单个buffer是一样的。支持这个特性的DOCA库可以将一个链表组成的内存区域看做一个连续的内存区域。

当使用一个buffer链表作为源时,收集每个buffer中的数据(在dataroom中)聚合成一段连续的数据再做处理。

当使用一个buffer链表作为目的时,数据会被分散写到buffertailroom中直到全部写入为止。

Buffer Use Cases

除非另有说明,否则对缓冲区数据进行的操作不是原子性的。一旦缓冲区作为任务的一部分被传递给库,缓冲区的所有权就会转移到库中,直到任务完成。当使用doca_buf作为某个库(例如,doca_dma)的输入时,在处理完成之前,doca_buf必须保持有效且不可修改。写入正在in-flight缓冲区可能会导致异常行为。同样,从in-flight缓冲区读取数据时也不能保证数据的有效性。

Inventory

inventory负责分配doca_bufinventory有多种类型。最基本的inventory分配doca_buf时无需申请任何系统内存。其它类型的inventory要求buffer地址不能重叠。

l  所有类型的inventory启动后都零分配;

l  申请一个doca buffer需要一个data source和一个inventory

n  data source定义了数据存放的位置,谁可以访问以及访问的权限;

n  data source必须由应用程序创建;

l  inventory描述了buffer的申请模式,例如:随机访问或pool、可变大小或固定大小、连续或非连续内存;

l  有些inventory需要在分配缓冲区时提供data sourcedoca_mmap,有些则需要在创建库存时提供;

l  所有类型的inventory都是线程不安全的。

Inventory type

Inventory type

特点

使用场景

doca_buf_inventory

多个mmap,灵活的地址,灵活的buffer大小

多个大小或mmap

doca_buf_array

单个mmap,固定buffer大小;用户接收一个指向DOCA buf的数组;在DPA场景下,mmapbuffer size不能被用户配置,只能稍后从DPA设置

用于在GPUDPA上创建DOCA buffer

doca_bufpool

单个mmap,固定buffer大小,地址不受用户控制

buffer地址不重要时用作具有相同特征的buffer pool

举例

以下步骤展示了如何将主机mmap导出到DPU以供DOCA用于直接访问主机内存(例如,用于DMA)

1.在主机上创建mmap;添加单个doca_devmmap并导出,以便DPURDMA端可以访问;

2.导出到DPURDMA端(使用mmap描述符输出参数作为doc_mmap_create_from_export的输入);

DOCA Execution Model

执行模型是基于数据和应用线程的硬件处理的。DOCA不创建内部线程(操作系统的线程)。工作负载由taskevent组成。有些任务用于数据传输。最基本的数据传输就是DMA操作,把数据从一个内存位置移动到另外一个内存位置。其它操作允许接收来自网络的数据包,或者源数据的SHA值并将其写到目的地。

举例来说,一份负责数据传输的工作负载分以下三部:

1.      读源数据;

2.      对被读的数据启动操作(由专用的硬件加速器完成);

3.      写操作的结果到目的地。

每个这样的操作就被叫做任务(doca_task)。任务描述了应用程序想要提交给DOCA(硬件或DPU)的操作。为此,应用程序需要一种与硬件或DPU通信的方法。这就是doca_pe发挥作用的地方。Process engine(PE)是一个单线程对象,用于将任务排队以卸载到DOCA,并最终接收它们的compeletion状态。

Doca_pe引入了三个主要操作:

1.      提交任务;

2.      检查提交任务的进度或状态;

3.      接收任务完成的通知(以回调的形式)

一个工作负载可以被分成许多不同的任务,这些任务可以在不同的线程上执行;每个线程由不同的PE表示。每个任务必须与某个context相关联,其中context定义要执行的任务类型。

context可以从DOCA SDK中的一些库中获得。例如,要提交DMA任务,可以从doca_dma.h获取DMA context,而可以使用doca_sha.h获取SHA context。每个这样的context允许提交多种任务类型。

任务被认为是异步的,因为一旦应用程序提交任务,DOCA执行引擎(硬件或DPU)将开始处理它,应用程序可以继续执行其他处理,直到硬件完成。为了跟踪哪个任务已经完成,有两种操作模式:轮询模式和事件驱动模式。

要求和注意事项

l  任务提交/执行/API针对性能(延迟)进行了优化;

l  DOCA不管理内部线程(操作系统的线程),进度是通过轮询或者事件驱动的方式来获取;

l  执行任务基本的对象是doc_task,每个任务都是从特定的DOCA context分配的;

l  doca_pe表示一个逻辑上的线程,执行提交给PE的应用程序和任务;

l  PE不是线程安全的,每个PE由单个应用程序线程管理;

l  与执行相关的元素(例如,doca_pe, doca_ctx, doca_task)是不透明的,应用程序在使用这些元素之前执行最小的初始化或配置;

l  提交给PE的任务可能会执行失败(即使在提交成功之后);在某些情况下,可以从错误中恢复;某些情况下,只能重新初始化相关对象;

l  PE不保证顺序(例如,按一定顺序提交的任务可能会乱序完成);保序由应用程序自己负责(如:一个任务完成后再提交另外一个任务);

l  PE可以在轮询模式或事件驱动模式下工作,但不能同时在这两种模式下工作;

l  所有DOCA context都支持轮询模式。

DOCA context

DOCA Context (struct doc_ctx)定义并实现了task/event的处理。Context有多种类型。

l  Context至少利用了一种DOCA Device功能或硬件加速能力;

l  对于每种task类型能支持它的有且仅有一种context

l  一个context包含每种task类型支持的inventory

l  Context包含了每个任务类型执行的所有参数(例如:库存大小、加速处理设备);

l  每个context都需要一个PE的实例来运行其任务,也就是说context必须和PE关联才能执行任务。

下图显示了DOCA Core实体之间的关系:

1.      Doca_task与执行任务的相关doc_ctx相关联(在相关doc_dev的帮助下)

2.      Doca_task初始化后,提交给doca_pe执行;

3.      Doca_ctxs关联到doca_pe,一旦doc_task排队到doc_pe,它就由与相关联的doc_ctx执行。

下图描述了context的初始化顺序:

在启动context之后,就可以根据其支持的任务类型向PE提交任务。Context是一个线程不安全的对象,它只能关联到单个PE

配置阶段

在尝试使用doc_ctx_start()启动DOCAcontext之前,必须先配置它。有些配置是强制性的(例如,提供doc_dev)

l  配置用于启用某些任务或事件、启用默认禁用的feature以及优化性能;

l  配置使用setter函数提供,有关强制性和可选配置及其相应api的列表,请参阅context相关文档;

l  配置在创建context之后和启动之前完成,context一旦启动,就不能再配置它,除非再次停止它。

执行阶段

一旦context配置完成,就可以使用context执行任务。context通过将工作负载卸载给硬件来执行任务,而任务是异步的,因此软件需要轮询任务,直到它们完成。

应用程序采用以下模式中的一种来轮询任务:

l  Poll mode

l  Notification-driven mode

在此阶段,context和所有DOCA Core对象通过内存池执行零分配。建议应用程序也使用相同的方法。

State Machine

State

说明

dile

没有任务执行;

初始化阶段(就在doca_<T>_create(ctx)之后):所有配置api都有效;

重新配置(stop状态转换过来):部分配置api有效;

starting

context是强制性的;

Running

允许申请和提交任务,且只有此状态允许;

所有配置api被禁止;

Stopping

任务停止前的准备;

清楚所有未完成的任务;

下图描述了DOCA context状态的转换:

Internal error

DOCA Context在任何时候都可能遇到一些内部错误。如果状态正处于startingrunning状态,则可能导致状态转换到stopping

停止后,状态可能变为idle状态。但是,如果存在配置问题或错误事件阻止正确转换到startingrunningdoc_ctx_start()可能会失败。

DOCA task

任务是可卸载到硬件的(功能或处理)工作负载单元。大多数任务使用NVIDIA BlueFieldNVIDIA ConnectX硬件来加速处理任务定义的工作负载。任务是异步的(例如,通过非阻塞doca_task_submit() API提交处理的任务)

任务完成后,在context中执行预设的completion callbackcompletion callback是任务的基本属性,类似于用户数据。大多数任务都是由NVIDIA设备硬件执行或加速的IO操作。

Task Properties

任务熟悉分通用属性和特定属性。由于任务的结构是不透明的(即,它的内容不向用户公开),所以对任务属性的访问由set/get api提供。

以下是通用任务属性:

l  Setting completion callback-成功和失败是两个单独的callback

l  Getting/setting user data-用于completion callback

l  Getting task status-在失败完成时获取错误代码。

对于每个任务,只有一个所有者,就是context对象,可以通过doc_task_get_ctx () API来获取通用context对象。

以下是通用的任务api:

l  CTX(内部/虚拟)inventory中分配和释放;

l  通过setter(init API)进行配置;

l  提交任务(即,doca_task_submit(task))

l  完成后,有一组getter来访问任务执行的结果。

Task Lifecycle

每个DOCA任务对象的生命周期:

l  DOCA context进入running状态即开始,一旦进入运行状态,应用程序就可以通过调用doc_ <CTX名称>_task_<任务名称>_alloc_init(CTX…)CTX获得任务;

l  DOCA context进入stopping状态时结束,一旦相关的DOCA context离开running状态,应用程序就不能再分配任务;

从应用程序的角度来看,DOCA context提供了一个虚拟的任务清单。

DOCA Progress Engine

Progress engine(PE)支持在单线程执行环境中异步处理不同类型的多个任务和事件。它是所有基于contextDOCA库的事件循环,其中I/O completion是最常见的事件类型。

PE被设计为线程不安全的(即,它一次只能在一个线程中使用),但单个操作系统线程可以使用多个PE。用户可以通过将不同的context添加到不同的PE中,并相应地调整每个PE的轮询频率,从而为不同的context分配不同的优先级。另外一种角度看,PE是一个计划执行的工作负载队列。

没有专门的api来添加或在PE上调度工作负载,但可以通过以下方式添加工作负载:

l  PE添加DOCA context

l  注册一个DOCA eventprobe,在主动probe时执行相关的处理程序。

PE负责调度工作负载(即选择下一个要执行的工作负载)。工作负载执行的顺序与任务提交顺序、事件注册顺序或关联context的顺序无关。多个任务的completion callback可能以不同于任务提交的顺序执行。

PE的初始化流程如下图所示:

PE被创建并关联到context之后,它可以开始处理提交给context的任务。请参阅context相关文档以查找详细信息,例如可以使用context提交哪些任务。

注意:PE可以关联到多个context。这些context可以是相同类型的,也可以是不同类型的。这允许将不同的任务类型提交到相同的PE,并等待它们从相同的位置/线程完成。

初始化PE后,应用程序可以使用以下模式之一定义事件循环:

l  Poll mode

l  Notification-driven mode

PE as Event Loop Mode of Operation

任务和事件的所有completion handler都在doca_pe_progress()context中执行。doca_pe_progress()会循环执行的每个工作负载:

运行选定的工作负载单元。以下情况:

l  任务完成后,执行相关处理程序并中断循环并返回状态;

l  主动探测事件,执行相关处理程序,打破循环并返回状态;

l  后续任务处理完成,打破循环并返回状态。否则,循环结束并返回状态no progress

Polling mode

在这种模式下,应用程序提交一个任务,然后执行忙等待,以确定任务何时完成。执行顺序如下图所示:

1.      应用程序提交所有任务(一个或多个)并跟踪任务完成的数量,以了解是否完成了所有任务;

2.      应用程序通过不断轮询doc_pe_progress()等待任务完成;

3.      如果doca_pe_progress()返回1,则表示某些任务或事件处理完成;

4.      每次任务或事件处理完成时,都会相应地执行其预设的回调函数;

5.      如果任务执行出错,则执行错误回调函数。

6.      应用程序可以向完成回调函数中添加代码,以跟踪已完成和待处理工作负载的数量。在这种模式下,应用程序总是占用CPU,即使它什么都不做(忙等)

阻塞模式- Notification Driven

在这种模式下,应用程序提交任务,然后等待接收通知,最后再查询状态。执行顺序如下:

1.      应用程序从doca_pe中获得一个通知句柄,该句柄是一个Linux文件描述符,用于通知应用程序某些工作已经完成;

2.      应用程序用doc_pe_request_notification()告知PE为事件通知模式,之后不允许调用doc_pe_progress()

3.      应用程序提交任务;

4.      应用程序等待在pe-fd上接收通知(例如,Linux epoll/select)

5.      应用程序清除接收到的通知,通知PE已接收到信号,并允许PE执行通知处理;

6.      应用程序尝试通过(多次)调用doca_pe_progress()来处理接收到的通知;

请注意:不能保证对doca_pe_progress()的调用将执行任何任务完成/事件处理程序,但是PE可以继续操作。

7.      应用程序处理任务完成或事件处理程序引起的内部状态更改;

8.      重复步骤2-7,直到所有任务完成,所有预期事件都得到处理。

Progress Engine vs Epoll

Linux中的epoll机制和DOCA PE处理在事件驱动架构中的高并发性。这两个系统在等待事件或任务完成时都能有效地管理资源。

DOCA Event

要注册一个事件,用户必须调用doca_<event_type>_reg(pe,…)函数,事件处理程序必须与PE关联。注册事件后,将由doc_pe_progress()函数定期检查该事件,该函数与PE在相同的执行context中运行。如果满足事件条件,则调用处理程序函数。事件不是线程安全的,只能由它们绑定的PE访问。

Error Handling

在一个任务被成功提交后,后续对doca_pe_progress()的调用可能会失败。一旦任务失败,上下文可能会转换到stopping状态,在这种状态下,应用程序必须在销毁或重新启动上下文之前执行完所有in-flight的任务。

下图显示了应用程序如何处理来自doca_pe_progress()的错误:

1.      应用程序轮询doca_pe_progress;

2.      可能发生下列情况:

l  任务失败,调用fail completion callback;

n  由错误的参数或其他致命错误引起;

n  处理程序释放任务和所有相关资源;

l  context转换到stopping状态,并调用context state changed handler;

n  由任务失败或其他致命错误引起;

n  在这种状态下,所有in-flight任务都肯定会失败;

n  释放非in-flight的任务;

l  context转换到idle状态,并调用context state changed handler;

n  遇到错误并且context没有任何必须由应用程序释放的资源;

n  应用程序可通过再次调用start来恢复context,或者销毁context并退出应用程序。

DOCA Graph Execution

DOCA Graph可以以特定的顺序和依赖关系运行一组操作(任务、用户回调)DOCA Graph运行在DOCA PE上。DOCA Graph创建的图形实例通过doc_graph_instance_submit提交给PE

Nodes

DOCA Graphcontextusersub-graph nodes组成,这些节点可以位于网络中的位置如下:

l  根节点:根节点没有父节点,图可以有一个或多个根节点。当提交图形实例时,所有根节点开始运行;

l  边缘节点:没有子节点的节点,叶子节点;当所有边缘节点完成时,图实例就执行完成;

l  中间节点:有父节点和子节点的节点。

Context Node

Context node运行特定的任务并使用特点的DOCA context(doca_ctx)context必须在图启动之前关联到PE。任务的生命周期必须大于或等于图实例的生命周期。

User Node

User node运行用户的回调函数,以便在图实例运行期间执行某些操作(例如,调整下一个节点任务数据,比较结果等)

Sub-graph Node

sub-graph node运行着一个graph的实例。

Using DOCA Graph

1.      使用doc_graph_create创建图;

2.      创建图形节点(例如,doca_graph_node_create_from_ctx)

3.      使用doc_graph_add_dependency定义依赖项;

l  注意:DOCA graph不支持循环依赖(例如,A => B => A)

4.      使用doca_graph_start启动图;

5.      使用doc_graph_instance_create创建图形实例;

6.      设置节点数据(例如,doca_graph_instance_set_ctx_node_data)

7.      使用doc_graph_instance_submit将图实例提交给PE

8.      调用doca_pe_progress,直到图形回调被调用;

 

l  进度引擎可以同时运行图形实例和独立的任务。

DOCA Graph Limitations

l  不支持循环依赖;

l  必须至少包含一个context node

Alternative Data Path

除了DOCA PE将数据路径从CPU卸载到硬件外,另外一些库还支持备选数据路径,可以卸载DPAGPU

注意事项:

l  并非所有上下文都支持备选数据路径;

l  配置阶段总是在CPU上完成;

l  数据路径操作总是卸载到硬件上,卸载的对象可以是CPU/DPA/GPU

l  默认数据路径模式是CPU

l  每种模式都会引入一组不同的API,以在执行路径中使用;所使用的API对于特定的context实例是互斥的。

DPA

用户必须首先检查DPA上的数据路径是否支持。

要将数据路径模式设置为DPA,需要获取一个DOCA DPA实例,然后使用doc_ctx_set_datapath_on_dpa ()

使用这种模式启动context之后,就可以使用相关的API获取DPA句柄(例如,doc_rdma_get_dpa_handle())。这个句柄可以用来访问DPA代码中的DPA数据路径API

GPU

用户必须首先检查是否支持GPU上的数据路径。

如果需要将数据路径模式设置为GPU,需要获取一个DOCA GPU实例,然后使用doc_ctx_set_datapath_on_gpu ()

context以这种模式启动之后,可以使用相关的API获得GPU句柄(例如,doca_eth_rxq_get_gpu_handle())。这个句柄可以用来访问GPU代码中的GPU数据路径API

Task and Event批处理

DOCA批处理可以将多个DOCA任务或相同类型的DOCA事件组成一组并将其作为单个单元来处理。批处理的completion需要等待所有项目的completion执行完成才会执行。

批处理的completion以批处理中所有项目的completion为基础,并作为单个completion进行处理。这允许在单个API调用中进行多个DOCA任务初始化/提交和多个DOCA任务/事件completion处理。

Object Life Cycle

大多数DOCA Core对象都有着相同的处理模型:

1.      该对象由DOCA分配,因此对应用程序来说是不透明的(例如,doca_buf_inventory_create, doca_mmap_create)

2.      应用程序初始化对象并设置所需的属性(例如,doca_mmap_set_memrange)

3.      对象已启动,不允许更改配置或属性(例如,doca_buf_inventory_start, doca_mmap_start)

4.      对象被使用;对象被停止和删除(例如,doca_buf_inventory_stop→doca_buf_inventory_destroy, doca_mmap_stop→doca_mmap_destroy)

下面的过程描述了两台机器(远程机器或主机-DPU)之间的mmap导出机制:

1.      Machine1分配内存;

2.      Machine1创建mmap

3.      mmap导出到Machine2ping住内存;

4.      Machine2创建导入的mmap,并holdMachine1内存的引用;

5.      Machine2可以使用导入的mmap来分配buffer

6.      Machine2销毁导入的mmap

7.      Machine1销毁导出的mmap

8.      销毁释放内存。

RDMA bridge

DOCA Core库提供了应用程序使用的模块,同时抽象了依赖于RDMA驱动程序的许多细节。这减少了复杂性,增加了灵活性,特别是对于已经基于rdma-core的应用程序。RDMA bridge允许DOCA SDKRDMA -core之间的互相转换,可以将基于DOCA的对象转换为基于RDMA -core的对象。

要求和注意事项

对于已经使用rdma-core的应用程序,DOCA Core库可以方便其移植或K对其进行扩展。Bridge允许将DOCA Core对象转换为等价的rdma-core对象。

DOCA Core对象到rdma-core对象的映射

文章来自个人专栏
技术讨论
13 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0