一致性加速器接口 (CXL)

简介

一致性加速器接口旨在允许将加速器(FPGA 和其他设备)一致地连接到 POWER 系统。 这些设备需要遵守一致性加速器接口架构 (CAIA)。

IBM 将其称为一致性加速器处理器接口或 CAPI。 在内核中,它被称为 CXL,以避免与 ISDN CAPI 子系统混淆。

在此上下文中,一致性意味着加速器和 CPU 都可以直接且使用相同的有效地址访问系统内存。

硬件概述

  POWER8/9             FPGA
+----------+        +---------+
|          |        |         |
|   CPU    |        |   AFU   |
|          |        |         |
|          |        |         |
|          |        |         |
+----------+        +---------+
|   PHB    |        |         |
|   +------+        |   PSL   |
|   | CAPP |<------>|         |
+---+------+  PCIE  +---------+

POWER8/9 芯片具有一个一致连接的处理器代理 (CAPP) 单元,它是 PCIe 主机桥 (PHB) 的一部分。 它由 Linux 通过调用 OPAL 进行管理。 Linux 不会直接对 CAPP 进行编程。

FPGA(或一致连接的设备)由两部分组成。 POWER 服务层 (PSL) 和加速器功能单元 (AFU)。 AFU 用于在 PSL 后面实现特定功能。 PSL 除其他外,还提供内存地址转换服务,以允许每个 AFU 直接访问用户空间内存。

AFU 是加速器的核心部分(例如压缩、加密等功能)。 内核不知道 AFU 的功能。 只有用户空间直接与 AFU 交互。

PSL 提供 AFU 所需的转换和中断服务。 这是内核与之交互的内容。 例如,如果 AFU 需要读取特定的有效地址,它会将该地址发送到 PSL,然后 PSL 对其进行转换,从内存中提取数据并将其返回给 AFU。 如果 PSL 出现转换错误,它会中断内核,并且内核会处理该错误。 处理此错误的上下文基于谁拥有该加速功能。

  • POWER8 和 PSL 版本 8 符合 CAIA 版本 1.0。

  • POWER9 和 PSL 版本 9 符合 CAIA 版本 2.0。

此 PSL 版本 9 提供了新功能,例如

  • 与 P9 芯片上的嵌套 MMU 交互。

  • 原生 DMA 支持。

  • 支持发送 ASB_Notify 消息以唤醒主机线程。

  • 支持原子操作。

  • 等等。

具有 PSL9 的卡无法在 POWER8 系统上工作,而具有 PSL8 的卡无法在 POWER9 系统上工作。

AFU 模式

AFU 支持两种编程模式。 专用模式和 AFU 指导模式。 AFU 可以支持一种或两种模式。

当使用专用模式时,仅支持一个 MMU 上下文。 在此模式下,一次只有一个用户空间进程可以使用加速器。

当使用 AFU 指导模式时,最多可以支持 16K 个并发上下文。 这意味着最多可以有 16K 个并发用户空间应用程序可以使用加速器(尽管特定的 AFU 可能支持更少)。 在此模式下,AFU 会在其每个请求中发送一个 16 位上下文 ID。 这会告诉 PSL 每个操作与哪个上下文关联。 如果 PSL 无法转换操作,则内核也可以访问该 ID,以便它可以确定与操作关联的用户空间上下文。

MMIO 空间

加速器 MMIO 空间的一部分可以直接从 AFU 映射到用户空间。 可以映射整个空间,也可以仅映射每个上下文部分。 硬件是自我描述的,因此内核可以确定每个上下文部分的偏移量和大小。

中断

AFU 可能会生成目标为用户空间的中断。 这些中断由内核作为硬件中断接收,并通过下面记录的读取系统调用传递到用户空间。

数据存储故障和错误中断由内核驱动程序处理。

工作元素描述符 (WED)

WED 是在上下文启动时传递给 AFU 的 64 位参数。 它的格式取决于 AFU,因此内核不知道它代表什么。 通常,它将是工作队列或状态块的有效地址,AFU 和用户空间可以在其中共享控制和状态信息。

用户 API

1. AFU 字符设备

对于在 AFU 指导模式下运行的 AFU,将创建两个字符设备文件。 /dev/cxl/afu0.0m 将对应于主上下文,而 /dev/cxl/afu0.0s 将对应于从属上下文。 主上下文可以访问 AFU 提供的完整 MMIO 空间。 从属上下文只能访问 AFU 提供的每个进程的 MMIO 空间。

对于在专用进程模式下运行的 AFU,驱动程序将仅为每个 AFU 创建一个名为 /dev/cxl/afu0.0d 的字符设备。 这将可以访问 AFU 提供的整个 MMIO 空间(类似于 AFU 指导中的主上下文)。

下面描述的类型在 include/uapi/misc/cxl.h 中定义

以下文件操作在从属和主设备上均受支持。

此处提供用户空间库 libcxl

这为该内核 API 提供了 C 接口。

打开

打开设备并分配一个文件描述符,用于其余的 API。

专用模式 AFU 只有一个上下文,并且只允许打开设备一次。

AFU 指导模式 AFU 可以有多个上下文,每个可用上下文可以打开设备一次。

当所有可用上下文都被分配时,打开调用将失败并返回 -ENOSPC。

注意

需要为每个上下文分配 IRQ,这可能会限制可以创建的上下文数量,因此会限制设备可以打开的次数。 POWER8 CAPP 支持 2040 个 IRQ,内核使用 3 个 IRQ,因此剩余 2037 个。 如果每个上下文需要 1 个 IRQ,则只能分配 2037 个上下文。 如果每个上下文需要 4 个 IRQ,则只能分配 2037/4 = 509 个上下文。

ioctl

CXL_IOCTL_START_WORK

启动 AFU 上下文并将其与当前进程关联。 成功执行此 ioctl 后,映射到此进程的所有内存都可以使用相同的有效地址访问此 AFU 上下文。 不需要额外的调用来映射/取消映射内存。 当用户空间分配和释放内存时,AFU 内存上下文将会更新。 此 ioctl 在 AFU 上下文启动后返回。

获取指向结构 cxl_ioctl_start_work 的指针

struct cxl_ioctl_start_work {
        __u64 flags;
        __u64 work_element_descriptor;
        __u64 amr;
        __s16 num_interrupts;
        __s16 reserved1;
        __s32 reserved2;
        __u64 reserved3;
        __u64 reserved4;
        __u64 reserved5;
        __u64 reserved6;
};
标志

指示结构中的哪些可选字段有效。

work_element_descriptor

工作元素描述符 (WED) 是由 AFU 定义的 64 位参数。 通常,这是一个指向 AFU 特定结构的有效地址,该结构描述要执行的工作。

amr

授权掩码寄存器 (AMR),与 powerpc AMR 相同。 仅当在标志中指定相应的 CXL_START_WORK_AMR 值时,内核才会使用此字段。 如果未指定,则内核将使用默认值 0。

num_interrupts

要请求的用户空间中断数。 仅当在标志中指定相应的 CXL_START_WORK_NUM_IRQS 值时,内核才会使用此字段。 如果未指定,则将分配 AFU 所需的最小数量。 可以从 sysfs 获取最小和最大数量。

保留字段

用于 ABI 填充和未来扩展

CXL_IOCTL_GET_PROCESS_ELEMENT

获取当前上下文 ID,也称为进程元素。 该值作为 __u32 从内核返回。

mmap

AFU 可能具有一个 MMIO 空间,以便于与 AFU 进行通信。 如果是,则可以通过 mmap 访问 MMIO 空间。 此区域的大小和内容特定于特定的 AFU。 可以通过 sysfs 发现大小。

在 AFU 指导模式下,允许主上下文映射所有 MMIO 空间,而从属上下文仅允许映射与上下文关联的每个进程的 MMIO 空间。 在专用进程模式下,始终可以映射整个 MMIO 空间。

必须在 START_WORK ioctl 之后完成此 mmap 调用。

访问 MMIO 空间时应格外小心。 POWER8 仅支持 32 位和 64 位访问。 此外,AFU 将使用特定的字节序进行设计,因此所有 MMIO 访问都应考虑字节序(建议使用 endian(3) 变体,例如:le64toh()、be64toh() 等)。 这些字节序问题同样适用于 WED 可能描述的共享内存队列。

读取

从 AFU 读取事件。如果没有任何挂起的事件,则会阻塞(除非提供了 O_NONBLOCK)。如果出现不可恢复的错误或卡被移除,则返回 -EIO。

read() 将始终返回整数个事件。

传递给 read() 的缓冲区必须至少为 4K 字节。

读取的结果将是一个或多个事件的缓冲区,每个事件的类型为 struct cxl_event,大小可变。

struct cxl_event {
        struct cxl_event_header header;
        union {
                struct cxl_event_afu_interrupt irq;
                struct cxl_event_data_storage fault;
                struct cxl_event_afu_error afu_error;
        };
};

struct cxl_event_header 定义为:

struct cxl_event_header {
        __u16 type;
        __u16 size;
        __u16 process_element;
        __u16 reserved1;
};
type

这定义了事件的类型。类型决定了事件其余部分的结构。这些类型在下面描述,并由 enum cxl_event_type 定义。

size

这是事件的大小,以字节为单位,包括 struct cxl_event_header。下一个事件的起始位置可以从当前事件的起始位置偏移这个大小来找到。

process_element

事件的上下文 ID。

保留字段

用于未来扩展和填充。

如果事件类型为 CXL_EVENT_AFU_INTERRUPT,则事件结构定义为:

struct cxl_event_afu_interrupt {
        __u16 flags;
        __u16 irq; /* Raised AFU interrupt number */
        __u32 reserved1;
};
标志

这些标志指示此结构中存在哪些可选字段。目前所有字段都是强制性的。

irq

AFU 发送的 IRQ 号。

保留字段

用于未来扩展和填充。

如果事件类型为 CXL_EVENT_DATA_STORAGE,则事件结构定义为:

struct cxl_event_data_storage {
        __u16 flags;
        __u16 reserved1;
        __u32 reserved2;
        __u64 addr;
        __u64 dsisr;
        __u64 reserved3;
};
标志

这些标志指示此结构中存在哪些可选字段。目前所有字段都是强制性的。

address

AFU 未成功尝试访问的地址。有效的访问将由内核透明地处理,但无效的访问将生成此事件。

dsisr

此字段提供有关故障类型的信息。它是发生地址故障时来自 PSL 硬件的 DSISR 的副本。DSISR 的形式在 CAIA 中定义。

保留字段

用于未来扩展

如果事件类型为 CXL_EVENT_AFU_ERROR,则事件结构定义为:

struct cxl_event_afu_error {
        __u16 flags;
        __u16 reserved1;
        __u32 reserved2;
        __u64 error;
};
标志

这些标志指示此结构中存在哪些可选字段。目前所有字段都是强制性的。

error

来自 AFU 的错误状态。由 AFU 定义。

保留字段

用于未来扩展和填充

2. 卡字符设备 (仅限 powerVM 虚拟机)

在 powerVM 虚拟机中,会为卡创建一个额外的字符设备。该设备仅用于在 FPGA 加速器上写入(刷写)新映像。一旦映像写入并验证后,设备树将更新,并且卡将重置以重新加载更新的映像。

open

打开设备并分配一个文件描述符,以便与 API 的其余部分一起使用。设备只能打开一次。

ioctl

CXL_IOCTL_DOWNLOAD_IMAGE / CXL_IOCTL_VALIDATE_IMAGE

启动并控制刷写新的 FPGA 映像。不支持(尚未)部分重新配置,因此映像必须包含 PSL 和 AFU(s) 的副本。由于映像可能非常大,因此调用者可能需要迭代,将映像拆分为较小的块。

接受指向 struct cxl_adapter_image 的指针。

struct cxl_adapter_image {
    __u64 flags;
    __u64 data;
    __u64 len_data;
    __u64 len_image;
    __u64 reserved1;
    __u64 reserved2;
    __u64 reserved3;
    __u64 reserved4;
};
标志

这些标志指示此结构中存在哪些可选字段。目前所有字段都是强制性的。

data

指向包含要写入卡的映像部分的缓冲区的指针。

len_data

由数据指向的缓冲区的大小。

len_image

映像的完整大小。

Sysfs 类

在 /sys/class/cxl 下添加了一个 cxl sysfs 类,以方便加速器的枚举和调整。其布局在 Documentation/ABI/testing/sysfs-class-cxl 中描述。

Udev 规则

可以使用以下 udev 规则为在任何编程模式(专用模式为 afuX.Yd,afu 指向模式为 afuX.Ys)下使用的最符合逻辑的字符设备创建一个符号链接,因为每个模式的 API 实际上是相同的。

SUBSYSTEM=="cxl", ATTRS{mode}=="dedicated_process", SYMLINK="cxl/%b"
SUBSYSTEM=="cxl", ATTRS{mode}=="afu_directed", \
                  KERNEL=="afu[0-9]*.[0-9]*s", SYMLINK="cxl/%b"