libfabric基本功能介绍
1. 整体功能
Libfabric,又称为“Open Fabrics Interfaces”,应用程序接口。向中间件和应用程序提供通信服务。特点:对外提供统一API接口,高性能和高扩展性,向下支持多种传输协议和网络设备(NIC、RNIC、GPU、共享内存等)。
2. 软件架构
libfabric中有两个核心组件:libfabric和OFI provider。 libfabric 库定义了应用程序使用的接口,并提供了一些通用服务;OFI provider一般有底层传输协议提供商来提供,用于接入到 libfabric 并提供对特定传输服务和硬件的访问,这部分通常是与特定的硬件设备或者NIC相关联。
2.1. libfabric
Libfabric提供了以下4种服务:
1)控制服务:发现系统中可用的通信服务类型的信息,给用户提供了资源查询接口,实现对当前系统中可用通信服务资源、能力和属性的探测。(类似于getaddrinfo、ibv_get_device_list)
2)通信服务:用于建立节点之间的通信,给用户提供了连接建立接口(类似于connect、ibv_modify_qp)
3)完成服务:向应用程序报告先前启动的异步操作的结果,给用户了提供了数据异步发送和接收完成情况的报告接口(类似于ibv_poll_cq)
4)数据传输服务:提供了四种数据传输接口,包括消息、标签消息、内存、原子操作(类似于send、ibv_post_send/recv)
2.2. OFI provider动态选择
支持provider的动态选择,有两种方式:
1)编译阶段,在build阶段,会检查当前环境,如果缺乏必要的条件或者依赖,就会disable对应的provider;用户也可以使用configure选项,选择enable或者disable 对应的provider;
configure –enable-udp =yes OR configure –enable-udp =no
2)运行阶段,使用环境变量FI_PROVIDER开启或者关闭特定的provider;
FI_PROVIDER=”udp,tcp”,表示只使能tcp和udp
provider可以直接编译到libfabric中,也可以编译成独立的so,libfabric以dlopen的方式进行调用。
2.3. OFI provider功能
Libfabric目前支持的provider 包括:bgq、efa、gni、hook、mrail、netdir、opx、psm2、psm3、rxd、rxm、shm、sockets、tcp、ucx、udp、usnic、verbs。在上述的provider类型中,比如sockets、tcp、ucx、udp、usnic、verbs等这些provider是可以独立工作的,是功能性provider,提供了某一种特定的传输协议。有些provider 则必须和其他provider配合工作,完成附加功能实现。
1) hook provider
Hook provider并不独立工作,而且配合其他provider,如efa provider、verbs provder,提供DFX功能,包括性能监控、路径采集、调用次数监控等功能。
fi_fabric 打开指定provider的fabric时,调用ofi_hook_install安装hook provider。不管在是在控制路径上,还是数据路径下,每个API调用的时候就是层层的调用关系,从最外层的实例依次执行到最内层的provider实例
下图以perf hook provider和trace hook provider的工作原理进行举例说明。当业务调用fi_fabric 打开verbs的fabric时,同时安装了perf 和trace provider。创建了perf_fabric和trace_fabric两个实例,perf_fabric、trace_fabric和vrb_fabric形成了一种链表关系,最外层是perf fabric,该实例直接返回给了业务。当业务利用该实例发送消息fi_send的时候,就会形成层层的调用关系,先执行perf fabric的send ops:perf_msg_send,然后调用到trace fabric的send ops:trace send,最后才会执行到vrb fabric的send ops:vrb_msg_ep_send。
2) rxm provider
Rxm provider是一个辅助性provider,不能独立使用,需要配合可以提供FI_EP_MSG类型的provider一起使用,向业务提供FI_EP_RDM(可靠数据报)类型的服务。可以和verbs rc、tcp provider配合使用,提供rdm类型的服务。
支持fi_rma接口,利用rendezvous协议实现大消息传输
Ø 0-16k 使用eager
Ø 16k-128k 使用SAR(Segmentation And Reassembly)
Ø >128k 使用rndv
按需创建连接,对于无连接ep来说,业务创建ep之后就会开始发送数据,但是rxm内部有创建连接的过程,因为刚开始发送的数据会出现发包失败,需要业务进行重试。
由于rxm采用了rndv,所以需要业务主动监听CQ事件。
3. 对象模型
libfabric 架构基于面向对象的设计理念。 Libfabric提供的服务都与一组接口相关联。比如说libfabric提供的连接管理服务,就有一组定义明确的函数可以访问连接管理服务(fi_connect、fi_accept、fi_listen)。 接口集与 libfabric 公开的对象相关联。 对象和接口集之间的关系大致类似于面向对象的类及其成员函数之间的关系。
4. 使用部署
4.1. 控制面接口
控制面接口 |
功能描述 |
fi_getinfo |
Fabric Information,检查当前环境下支持的网络服务 |
fi_fabric |
Fabric Domain,代表一个physical 或者virtual network的硬件和软件资源的一个集合 |
fi_domain |
Access Domains,代表接入到一个fabric domain上的一组方法 |
fi_endpoint |
Fabric Endpoint,代表一个连接 |
fi_eq |
Event Queue,用于收集和上报异步操作和事件的完成结果 |
fi_cq |
Completion Queue,上报数据传输的完成事件 |
fi_cntr |
Event Counters,用于统计异步操作的完成数量 |
fi_mr |
Memory Region,描述应用的本地内存buffer,通过内存注册,允许provider访问应用内存 |
fi_av |
Address Vector,地址向量 |
1) fi_getinfo
入口参数:node可选项,通常指定一个IP地址或者是hostname;service 通常是一个传输层地址,比如tcp port,provider可以将node和service映射成一个fabric address。
返回值:fi_info 链表,每个fi_info代表一个provider提供的通信资源。包含caps(业务要求provider可以提供的能力)、mode(业务本身支持的特性)、addr_fomat、src_addrlen、dest_addrlen、src_addr、dest_addr、handle、tx_attr、rx_attr、ep_attr、domain_attr、fabric_attr和nic等多个维度的描述;
2) fi_fabric
在fi_getinfo之后调用,指定fi_fabric_attr,获取fid_fabric。
Fabric表示访问单个物理或虚拟网络的硬件和软件资源的集合。 系统上所有可以通过Fabric相互通信的网络端口都属于同一个Fabric域。 Fabric不仅包括本地和远程 NIC,还包括相应的软件、交换机、路由器以及任何必要的结构或子网管理组件。
3) fi_domain
在fi_fabric之后调用,指定fid_fabric和fi_info,获取fid_domain。domain代表与fabric的逻辑连接。 例如,域可以映射到物理或虚拟NIC。 每个domain都属于一个fabric。 domain属性包括有关应用程序的线程模型以及如何在线程之间分配结构资源的信息。 它还定义了ep、cq和counter以及av之间发生的交互。
4) fi_mr
注册单个内存到硬件
注册mr成功后,获取mr key等信息,当数据面使用该buf收发包时,将desc信息传递给libfabric
5) fi_eq
基于指定的fabric,创建eq。eq用于上报控制面操作的结果,比如内存注册、连接管理等。如果domain绑定eq,设置了FI_REG_MR标志,表示基于domain创建的MR需要异步执行,并且provider上报异步执行的结果;对于连接管理中,接收到新连接请求,也是通过eq上报。
6) fi_passive_ep
创建passive ep, 启动一个监听,该监听是基于fid_fabric创建的,创建后,该ep必须绑定一个事件队列eq,并调用fi_listen(pep)使能监听。
7) fi_endpoint
类似于socket接口,创建一个active ep的实例,连接具体的创建方法由fid_domain提供。Active ep可用于数据传输。不同于sf-stack,OpenConnection中直接指定了向对端的某一个地址建立连接,fid_endpoint只是创建了本地的连接实例,但是ep还处于不可用状态,需要调用fi_connect()/fi_accept()或者是fi_enable()让ep进入可用状态
8) fi_cm
Libfabric的连接管理模块,只是提供了一个框架,支持业务调用cm相关的API实现节点间建立连接,但是具体连接创建、协商和建立的过程,都是由底层的provider来提供的,也就是provider的ep需要定义好cm相关的ops。
9) fi_cq
cq用于上报数据传输的结果。
Libfabric定义了几种完成“级别”,每一种级别代表了tx事件当前的完成情况,provider在fi_info的fi_tx_attr中向业务呈现完成能力。
Ø FI_INJECT_COMPLETE:代表tx buffer可以复用,当数据从tx buffer 复制到网络缓冲区或者NIC上后,就可以上送该级别的完成事件
Ø FI_TRANSMIT_COMPLETE:代表本节点上的provider已经完成了发送,对于可靠的连接,当数据已经送到对端节点,就可以上送该级别的完成事件;对于不可靠的连接,数据已经送到了网络中,就可以上送该级别的完成事件;
Ø FI_DELIVERY_COMPLETE:代表数据已经达到了对端业务的内存上。
10) fi_cntr
counter记录了请求操作的完成个数,是一种轻量级的完成机制,完成之后,无需上送一个entry表示具体的完成情况,而是counter+1表示操作已完成。该计数可以为流控或者是跟踪业务情况提供依据。
11) fi_av
AV通常用于无连接的ep,为业务提供了将fabric address映射为统一地址格式(fi_addr_t)的机制,方便业务使用。 比如tcp provider中使用IP地址和端口号标识一个地址,可以将xxxx:xx的格式转换成统一的fi_addr_t(8B)的格式进行管理,不仅可以减少内存的使用量,也方便管理。具体映射方法由底层provider提供。
12) fi_poll
poll set 是一组cq和counter的集合,有点类似于epoll 的使用方法,将cq放入cq组中进行监听。
4.2. 数据面接口
控制面接口 |
功能描述 |
fi_msg |
Message Queue,FIFO队列,应用程序使用该队列进行消息的收发 |
fi_tagged |
Tagged Message Queue,消息携带64bit标签,接收数据的时候,会根据标签匹配接收队列 |
fi_rma |
Remote Memory Access,单边操作,直接读写远端内存, |
fi_atomic |
Atomic,在远端内存上进行原子操作,比如atomic-add/compare-and-swap等,不同于数据传输接口,可以感知到远端内存的数据格式 |
fi_collective |
集合通信接口 |
1) fi_msg
Ø desc::mr的描述信息,
Ø src_addr:只用于无连接ep,用于接收指定源地址的报文
Ø dest_addr:只用于无连接ep,指定数据包的目的地址
Ø flags:设置属性要求,比如要求完成类型inject、transmit、delivery等
fi_inject接口返回后,tx buff即可复用,传输完成也不再产生CQ entry,verbs provider实现这个接口,采用的是IBV_SEND_INLINE的方式,将数据复制到了WQE中。
2) fi_tagged
数据传输时携带一个key或者是tag,这个tag用来在接收侧匹配对应的buffer。匹配规则一般是send_tag & ~ignore == recv & ~ignore 或者是send_tag | ignore == recv | ignore,ignore中全1代表所有bit都无需匹配。
3) fi_rma
fi_rma接口是内存语义接口,入口参数传入了本端内存地址和远端内存地址
4) fi_atomic
5) fi_collective
5. 进程和线程模型
Libfabric提供了两种进程模型:自动和手动,在fi_domain_attr中体现。
1) 自动模型:业务初始化某个操作后,即使业务不会再去进一步调用libfabric接口,libfabric也会把剩余的工作执行完毕,可能硬件或者操作系统完成的,也可能是在libfabric其他的线程中完成的。这个模型可能会增加系统开销;
2) 手动模型:对于未卸载到硬件中的操作, 需要业务去主动调用libfabric提供的接口进行完成,类似于sf-stack设计。这样会对业务调用API的频率有严格的要求。
Libfabric 中最常用的线程模型是FI_THREAD_SAFE,允许多个线程同时访问同一个domain资源。要求provider能够提供多线程保护。每一个provider中都定义了一个 xxx_progress的结构体,主要是提供锁保护,在同一个domain内进行控制面和数据面的操作都需要去lock这个锁。需要注意的是,并不是所有的provider都可以提供FI_THREAD_SAFE类型的线程模型,比如说ucx provider提供的线程模式是FI_THREAD_DOMAIN,domain内的资源必须单线程访问和使用。
6. 特点总结
1) API接口的设计思想、调用流程和socket是比较类似的
2) 数据结构的设计比较巧妙,sf-stack提供的接口都是对称的,在libfabric的设计思想上,所有实例的销毁通过fi_close就可以搞定。包括hook_provider的实现,都是依赖于巧妙的数据结构的设计。
3) 提供了很丰富和细化的功能,方便扩展不同特点的provider。提供了非常多的flags选项,通过flags选项可以将provider的功能细致给呈现给业务。
4) 支持各类操作转为异步处理,比如说mr注册可以是一个异步的动作,交给provider进行处理,当provider处理完毕后,通过eq通知业务结果,这样就不会阻塞业务线程。