什么是DSA
英特尔® DSA(Data Stream Accelerator 数据流加速器)是一款高性能的数据复制和转换加速器,自第四代英特尔® 至强® 处理器开始集成在英特尔® 处理器中。专为优化流数据传输和转换而设计。
如何配置
IOMMU 驱动
必须在内核配置中启用支持可扩展模式的 Intel® IOMMU 驱动程序,相关内核配置:1
2
3
4CONFIG_INTEL_IOMMU=y
CONFIG_INTEL_IOMMU_SVM=y
CONFIG_INTEL_IOMMU_DEFAULT_ON=y
CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON=y
如果使能CONFIG_INTEL_IOMMU_DEFAULT_ON
或者CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON
选项,则必须在内核启动参数中添加intel_iommu=on,sm_on
。
DSA 驱动
CONFIG_INTEL_IDXD=m
CONFIG_INTEL_IDXD_SVM=y
CONFIG_INTEL_IDXD_PERFMON=y
工作队列(WQs)是设备上的存储,用于存放提交给设备的描述符,可以配置为两种模式:专用模式(DWQ)或共享模式(SWQ)。SWQ 允许多个客户端同时提交描述符,而无需进行跟踪工作队列占用情况的同步操作,因此没有软件开销。SWQ 是首选的工作队列模式,因为它相比于 DWQ 的硬分区提供了更好的设备利用率,而 DWQ 可能会导致资源未充分利用。支持 DWQ 的 Intel® DSA 驱动程序(IDXD)在内核版本 5.6 中引入,而支持 SWQ 的 IDXD 驱动程序在 Linux 主线内核 5.18 及更高版本中提供。
device ID
DSA的PCI设备ID,在GNR之前(SPR,EMR,GNR)是0x0b25,但是这个只在下一代的CPU上,会发生变化。(可以专门一讲,一块学习下DSA的Device ID为什么会发生变化:p)
1 | # lspci | grep 0b25 |
完整的PCI信息列一下,可以看到更多DSA工作所需要的服务:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
706a:01.0 System peripheral: Intel Corporation Device 0b25
Subsystem: Intel Corporation Device 0000
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping- SERR+ FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
NUMA node: 0
IOMMU group: 12
Region 0: Memory at 206ffff40000 (64-bit, prefetchable) [size=64K]
Region 2: Memory at 206ffff00000 (64-bit, prefetchable) [size=128K]
Capabilities: [40] Express (v2) Root Complex Integrated Endpoint, MSI 00
DevCap: MaxPayload 128 bytes, PhantFunc 0
ExtTag+ RBE+ FLReset+
DevCtl: CorrErr+ NonFatalErr+ FatalErr+ UnsupReq-
RlxdOrd+ ExtTag+ PhantFunc- AuxPwr- NoSnoop+ FLReset-
MaxPayload 128 bytes, MaxReadReq 4096 bytes
DevSta: CorrErr- NonFatalErr- FatalErr- UnsupReq- AuxPwr- TransPend-
DevCap2: Completion Timeout: Not Supported, TimeoutDis+ NROPrPrP- LTR+
10BitTagComp+ 10BitTagReq+ OBFF Not Supported, ExtFmt+ EETLPPrefix+, MaxEETLPPrefixes 1
EmergencyPowerReduction Not Supported, EmergencyPowerReductionInit-
FRS-
AtomicOpsCap: 32bit- 64bit- 128bitCAS-
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis- LTR- OBFF Disabled,
AtomicOpsCtl: ReqEn-
Capabilities: [80] MSI-X: Enable+ Count=9 Masked-
Vector table: BAR=0 offset=00002000
PBA: BAR=0 offset=00003000
Capabilities: [90] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [100 v2] Advanced Error Reporting
UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq+ ACSViol-
UESvrt: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP+ ECRC- UnsupReq- ACSViol-
CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- AdvNonFatalErr-
CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- AdvNonFatalErr-
AERCap: First Error Pointer: 00, ECRCGenCap- ECRCGenEn- ECRCChkCap- ECRCChkEn-
MultHdrRecCap- MultHdrRecEn- TLPPfxPres- HdrLogCap-
HeaderLog: 00000000 00000000 00000000 00000000
Capabilities: [150 v1] Latency Tolerance Reporting
Max snoop latency: 0ns
Max no snoop latency: 0ns
Capabilities: [160 v1] Transaction Processing Hints
Device specific mode supported
Steering table in TPH capability structure
Capabilities: [170 v1] Virtual Channel
Caps: LPEVC=1 RefClk=100ns PATEntryBits=1
Arb: Fixed+ WRR32- WRR64- WRR128-
Ctrl: ArbSelect=Fixed
Status: InProgress-
VC0: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
Arb: Fixed- WRR32- WRR64- WRR128- TWRR128- WRR256-
Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VC=fd
Status: NegoPending- InProgress-
VC1: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
Arb: Fixed- WRR32- WRR64- WRR128- TWRR128- WRR256-
Ctrl: Enable+ ID=1 ArbSelect=Fixed TC/VC=02
Status: NegoPending- InProgress-
Capabilities: [200 v1] Designated Vendor-Specific: Vendor=8086 ID=0005 Rev=0 Len=24 <?>
Capabilities: [220 v1] Address Translation Service (ATS)
ATSCap: Invalidate Queue Depth: 00
ATSCtl: Enable+, Smallest Translation Unit: 00
Capabilities: [230 v1] Process Address Space ID (PASID)
PASIDCap: Exec- Priv+, Max PASID Width: 14
PASIDCtl: Enable+ Exec- Priv+
Capabilities: [240 v1] Page Request Interface (PRI)
PRICtl: Enable+ Reset-
PRISta: RF- UPRGI- Stopped+
Page Request Capacity: 00000200, Page Request Allocation: 00000200
Kernel driver in use: idxd
Kernel modules: idxd
SVM
DSA(数据流加速器)设备支持SVM技术,在Linux共享虚拟地址(SVA)框架下实现。 Intel的SVM(共享虚拟内存)技术,是将设备运行时的地址放在访问该设备的应用程序的 CPU 虚拟地址空间中。设备可以访问虚拟地址空间,而且设备无需具备复杂的MMU功能,使用 PASID 来区分不同应用程序的虚拟地址空间。通过PCI的PASID(进程地址空间标识符)将特定的CPU进程进行绑定,同时IOMMU能够访问CPU的MMU页表。当设备访问的地址不在设备的地址转换缓存(ATC)中时,它会通过地址转换服务(ATS)向IOMMU请求相应的页表转换,无需将设备访问的页面固定在内存中。
上面lspci的输出中可以看到最后三个Capabilities
: ATSCtrl, PASIDCtl和PRICtl 三项已经启用。
sysfs
Linux的sysfs文件系统是一种伪文件系统,为内核数据结构提供了一个接口。sysfs 下的文件提供了关于设备、内核模块、文件系统以及其他内核组件的信息。
Linux 驱动程序会为每个处理器生成如下所示的4 个sysfs目录。Intel DSA 和 Intel® IAA 设备都由 IDXD 设备驱动程序进行管理。
可以看到驱动程序创建了4个dsa 设备文件dsa{0,2,4,6},4个iax设备文件iax{1,3,5,7,9}。同时还有每个设备上的8个workqueue,4个engine和4个group。
1 | # ll /sys/bus/dsa/devices/ |
设备配置
DSA WQs
软件通过在内存中构建描述符并将其提交到工作队列(SWQ)来指定设备的工作。共享工作队列(WQ)允许多个客户端同时提交描述符,因此推荐用于应用程序场景。专用工作队列(DWQ)要求软件通过跟踪已提交和已完成的描述符来管理流控制,以确保不会超出工作队列的容量。因此,当单个操作系统级进程使用工作队列时,专用 WQ 非常有用。
DSA Engines
引擎是 Intel DSA 设备中的一个操作单元。
DSA Group
组(Group)是逻辑上对一组工作队列和引擎的组织。多个组可以在共享设备的应用程序之间提供性能隔离。
accel-config
accel-config是一个Linux应用程序,提供了配置DSA设备的命令行工具。
所有DSA以及workqueue engine group等的相关的配置要求root用户。下图可以一个图来说明配置的步骤:
便于copy-paste1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22方式1:
echo 0 > /sys/bus/dsa/devices/dsa0/wq0.0/group_id
echo dedicated > /sys/bus/dsa/devices/dsa0/wq0.0/mode
echo 10 > /sys/bus/dsa/devices/dsa0/wq0.0/priority
echo 16 > /sys/bus/dsa/devices/dsa0/wq0.0/size
echo "kernel" > /sys/bus/dsa/devices/dsa0/wq0.0/type
echo "dma2chan0" > /sys/bus/dsa/devices/dsa0/wq0.0/name
echo "dmaengine" > /sys/bus/dsa/devices/dsa0/wq0.0/driver_name
echo 0 > /sys/bus/dsa/devices/dsa0/engine0.0/group_id
echo dsa0 > /sys/bus/dsa/drivers/idxd/bind
echo wq0.0 > /sys/bus/dsa/drivers/dmaengine/bind
方式2:
accel-config config-engine dsa0/engine0.2 --group-id=0.0
accel-config config-wq dsa0/wq0.0 --group-id=0 --wq-size=32 --priority=1 --block-on-fault=0 --threshold=4 --type=user --name=swq --mode=shared
accel-config enable-device dsa0
accel-config enable-wq dsa0/wq0.0
accel-config save-config -s save_config.conf
方式3:
accel-config load-config -c contrib/configs/app_profile.conf -e
使用DSA
在配置好设备之后,使用DSA需要下面的几步:
- 配置描述符(descriptor)
- 打开/映射 /dev/dsa/wq0.0 文件
- 提交描述符(descriptor)
- 确认完成
描述符descriptor
描述符是一个64字节并且对齐的结构体,用的时候,需要查阅开发者手册,填写对应字节/位上的值,然后把地址通过指定提交给DSA设备。下面是手册中一个基本的描述符的样子。但是对数据的操作不同,描述符的内容不尽相同,具体operation code不同,还需要具体查阅手册。
/dev/dsa/wq0.0
在向设备提交描述符之前,应用程序必须打开一个之前配置好的工作队列(WQ)设备文件(例如 /dev/dsa/wq0.0),并将该工作队列的任务提交端口映射到其地址空间(或者使用系统调用write),将描述符提交到设备。
共享工作队列(WQ)设备文件可以被多个进程同时打开,而专用工作队列设备文件在任何时刻只能由单个进程打开。
提交描述符
根据工作队列(WQ)的类型,软件可能会使用 ENQCMD 或 MOVDIR64B 指令来提交描述符。Intel® DSA 架构规范中的“共享工作队列”部分描述了当描述符未被设备接受时,ENQCMD 会返回一个非零值。gcc10通过 -menqcmd 和 -mmovdir64b 选项支持 ENQCMD 和 MOVDIR64B 的内置函数 _enqcmd() 和 _movdir64b()。对于较旧的编译器版本,可以直接使用机器码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17static inline void
movdir64b(void *dst, const void *src)
{
asm volatile(".byte 0x66, 0x0f, 0x38, 0xf8, 0x02\t\n"
: : "a" (dst), "d" (src));
}
static inline unsigned int
enqcmd(void *dst, const void *src)
{
uint8_t retry;
asm volatile(".byte 0xf2, 0x0f, 0x38, 0xf8, 0x02\t\n"
"setz %0\t\n"
: "=r"(retry) : "a" (dst), "d" (src));
return (unsigned int)retry;
}
所以,一个典型DSA应用程序最终这样提交:1
2
3
4
5
6if (dedicated)
_movdir64b(wq_portal, &desc);
else {
retry = 0;
while (_enqcmd(wq_portal, &desc) && retry++ < ENQ_RETRY_MAX);
}
检查完成
当 Intel DSA 硬件完成描述符的处理后,会更新完成记录的状态字段。下面的代码片段展示了完成检查的过程。
1 | retry = 0; |