虚拟加速器交换板 (VAS) 用户空间 API

简介

Power9 处理器引入了虚拟加速器交换板 (VAS),它允许用户空间和内核与被称为嵌套加速器 (NX) 的协处理器(硬件加速器)进行通信。NX 单元包含一个或多个硬件引擎或协处理器类型,例如 842 压缩、GZIP 压缩和加密。在 power9 上,用户空间应用程序将只能访问硬件支持 ZLIB 和 GZIP 压缩算法的 GZIP 压缩引擎。

为了与 NX 通信,内核必须建立一个通道或窗口,然后可以直接提交请求,无需内核参与。对 GZIP 引擎的请求必须格式化为协处理器请求块 (CRB),并且这些 CRB 必须使用 COPY/PASTE 指令提交给 NX,以便将 CRB 粘贴到与引擎请求队列关联的硬件地址。

GZIP 引擎提供两个优先级的请求:普通(Normal)和高(High)。目前用户空间仅支持普通请求。

本文档解释了用于与内核交互以设置通道/窗口的用户空间 API,该通道/窗口可用于直接向 NX 加速器发送压缩请求。

概述

应用程序对 GZIP 引擎的访问是通过由 VAS/NX 设备驱动程序实现的 /dev/crypto/nx-gzip 设备节点提供的。应用程序必须打开 /dev/crypto/nx-gzip 设备以获取文件描述符 (fd)。然后应使用此 fd 发出 VAS_TX_WIN_OPEN ioctl 以建立与引擎的连接。这意味着为此进程在 GZIP 引擎上打开了发送窗口。一旦建立连接,应用程序就应该使用 mmap() 系统调用将引擎请求队列的硬件地址映射到应用程序的虚拟地址空间中。

然后,应用程序可以通过使用 copy/paste 指令并将 CRB 粘贴到 mmap() 返回的虚拟地址(又称 paste_address)来向引擎提交一个或多个请求。用户空间可以通过关闭文件描述符 (close(fd)) 或在进程退出时关闭已建立的连接或发送窗口。

请注意,应用程序可以使用同一个窗口发送多个请求,也可以建立多个窗口,但每个文件描述符对应一个窗口。

以下各节提供有关各个步骤的更多详细信息和参考。

NX-GZIP 设备节点

系统中有一个 /dev/crypto/nx-gzip 节点,它提供对系统中所有 GZIP 引擎的访问。对 /dev/crypto/nx-gzip 仅有的有效操作是:

  • 以读写方式 open() 设备。

  • 发出 VAS_TX_WIN_OPEN ioctl

  • 将引擎的请求队列 mmap() 到应用程序的虚拟地址空间中(即为协处理器引擎获取 paste_address)。

  • close 设备节点。

对该设备节点的其他文件操作未定义。

请注意,复制和粘贴操作直接发送到硬件,不通过此设备。有关更多详细信息,请参考 COPY/PASTE 文档。

尽管系统可能具有 NX 协处理器引擎的多个实例(通常每个 P9 芯片一个),但系统中只有一个 /dev/crypto/nx-gzip 设备节点。当打开 nx-gzip 设备节点时,内核会在 NX 加速器的合适实例上打开发送窗口。它会查找用户进程正在其上执行的 CPU,并确定该 CPU 所属的对应芯片的 NX 实例。

应用程序可以使用 VAS_TX_WIN_OPEN ioctl 中的 vas_id 字段选择 NX 协处理器的特定实例,如下所述。

用户空间库 libnxz 可在此处获取,但仍在开发中

使用 inflate / deflate 调用的应用程序可以链接到 libnxz 而不是 libz,并且无需任何修改即可使用 NX GZIP 压缩。

打开 /dev/crypto/nx-gzip

应以读写方式打开 nx-gzip 设备。打开设备不需要特殊权限。每个窗口对应一个文件描述符。因此,如果用户空间进程需要多个窗口,则必须发出多次 open 调用。

有关返回值、错误代码和限制等其他详细信息,请参阅 open(2) 系统调用手册页。

VAS_TX_WIN_OPEN ioctl

应用程序应按如下方式使用 VAS_TX_WIN_OPEN ioctl 与 NX 协处理器引擎建立连接

struct vas_tx_win_open_attr {
        __u32   version;
        __s16   vas_id; /* specific instance of vas or -1
                                for default */
        __u16   reserved1;
        __u64   flags;  /* For future use */
        __u64   reserved2[6];
};
version

当前 version 字段必须设置为 1。

vas_id

如果传入 ‘-1’,内核将尽最大努力为该进程分配最佳的 NX 实例。要选择特定的 VAS 实例,请参阅下文的“发现可用的 VAS 引擎”一节。

flags、reserved1 和 reserved2[6] 字段用于将来扩展,必须设置为 0。

VAS_TX_WIN_OPEN ioctl 的属性 attr 定义如下

#define VAS_MAGIC 'v'
#define VAS_TX_WIN_OPEN _IOW(VAS_MAGIC, 1,
                                struct vas_tx_win_open_attr)

struct vas_tx_win_open_attr attr;
rc = ioctl(fd, VAS_TX_WIN_OPEN, &attr);

VAS_TX_WIN_OPEN ioctl 成功时返回 0。如果出错,它返回 -1 并设置 errno 变量以指示错误。

错误条件

EINVAL

fd 不指向有效的 VAS 设备。

EINVAL

无效的 vas ID

EINVAL

version 未设置为正确的值

EEXIST

给定的 fd 已经打开了窗口

ENOMEM

没有可用内存来分配窗口

ENOSPC

系统打开了太多活动窗口(连接)

EINVAL

保留字段未设置为 0。

有关更多详细信息、错误代码和限制,请参阅 ioctl(2) 手册页。

mmap() NX-GZIP 设备

针对 NX-GZIP 设备 fd 的 mmap() 系统调用返回一个 paste_address,应用程序可以使用它将 CRB 复制/粘贴到硬件引擎。

paste_addr = mmap(addr, size, prot, flags, fd, offset);

对 NX-GZIP 设备 fd 进行 mmap 的唯一限制是

  • size 应该是 PAGE_SIZE

  • offset 参数应该是 0ULL

有关其他详细信息/限制,请参阅 mmap(2) 手册页。除了 mmap(2) 手册页上列出的错误条件外,还可能出现以下错误代码之一导致失败

EINVAL

fd 未与打开的窗口关联(即 mmap() 没有跟在对 VAS_TX_WIN_OPEN ioctl 的成功调用之后)。

EINVAL

offset 字段不为 0ULL。

发现可用的 VAS 引擎

系统中每个可用的 VAS 实例都将有一个设备树节点,例如 /proc/device-tree/vas@* 或 /proc/device-tree/xscom@*/vas@*。确定芯片或 VAS 实例,并使用此节点中相应的 ibm,vas-id 属性值来选择特定的 VAS 实例。

复制/粘贴操作

应用程序应使用 copy 和 paste 指令将 CRB 发送到 NX。有关 Copy/Paste 指令,请参考 PowerISA 中的第 4.4 节:https://openpowerfoundation.org/?resource_lib=power-isa-version-3-0

CRB 规范和使用 NX

应用程序应使用协处理器请求块 (CRB) 将对协处理器的请求格式化。有关 CRB 的格式以及如何从用户空间使用 NX(例如发送请求和检查请求状态),请参考 NX-GZIP 用户手册。

NX 故障处理

应用程序向 NX 发送请求,并通过轮询协处理器状态块 (CSB) 标志来等待状态。NX 在处理完每个请求后会更新 CSB 中的状态。有关 CSB 和状态标志的格式,请参考 NX-GZIP 用户手册。

如果 NX 在 CSB 地址或任何请求缓冲区上遇到转换错误(称为 NX 页错误),它会在 CPU 上引发中断以处理该故障。如果应用程序传递了无效地址或请求缓冲区不在内存中,则可能会发生页错误。操作系统通过使用以下数据更新 CSB 来处理该故障

csb.flags = CSB_V;
csb.cc = CSB_CC_FAULT_ADDRESS;
csb.ce = CSB_CE_TERMINATION;
csb.address = fault_address;

当应用程序收到转换错误时,它可以触碰或访问具有故障地址的页面,以便将该页面调入内存。然后应用程序可以重新将此请求发送给 NX。

如果操作系统由于 CSB 地址无效而无法更新 CSB,则会向打开了发出原始请求的发送窗口的进程发送 SEGV 信号。该信号返回时带有以下 siginfo 结构体

siginfo.si_signo = SIGSEGV;
siginfo.si_errno = EFAULT;
siginfo.si_code = SEGV_MAPERR;
siginfo.si_addr = CSB address;

对于多线程应用程序,NX 发送窗口可以在所有线程之间共享。例如,子线程可以打开发送窗口,但其他线程可以使用此窗口向 NX 发送请求。只要 CSB 地址有效,即使在操作系统处理故障的情况下,这些请求也会成功。如果 NX 请求包含无效的 CSB 地址,则该信号将发送到打开窗口的子线程。但是,如果线程在未关闭窗口的情况下退出,并且使用此窗口发出了请求,则该信号将发送给线程组领头线程 (tgid)。应用程序可以自行决定是忽略还是处理这些信号。

NX-GZIP 用户手册:https://github.com/libnxz/power-gzip/blob/master/doc/power_nx_gzip_um.pdf

简单示例

int use_nx_gzip()
{
        int rc, fd;
        void *addr;
        struct vas_setup_attr txattr;

        fd = open("/dev/crypto/nx-gzip", O_RDWR);
        if (fd < 0) {
                fprintf(stderr, "open nx-gzip failed\n");
                return -1;
        }
        memset(&txattr, 0, sizeof(txattr));
        txattr.version = 1;
        txattr.vas_id = -1
        rc = ioctl(fd, VAS_TX_WIN_OPEN,
                        (unsigned long)&txattr);
        if (rc < 0) {
                fprintf(stderr, "ioctl() n %d, error %d\n",
                                rc, errno);
                return rc;
        }
        addr = mmap(NULL, 4096, PROT_READ|PROT_WRITE,
                        MAP_SHARED, fd, 0ULL);
        if (addr == MAP_FAILED) {
                fprintf(stderr, "mmap() failed, errno %d\n",
                                errno);
                return -errno;
        }
        do {
                //Format CRB request with compression or
                //uncompression
                // Refer tests for vas_copy/vas_paste
                vas_copy(&crb, 0, 1);
                vas_paste(addr, 0, 1);
                // Poll on csb.flags with timeout
                // csb address is listed in CRB
        } while (true)
        close(fd) or window can be closed upon process exit
}

有关测试或更多用例,请参考 https://github.com/libnxz/power-gzip