ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Linux 内核 PowerPC VAS 用户态 API 详解:NX-GZIP 硬件压缩引擎的直通使用

Linux 内核 PowerPC VAS 用户态 API 详解:NX-GZIP 硬件压缩引擎的直通使用 Linux 内核 PowerPC VAS 用户态 API 详解NX-GZIP 硬件压缩引擎的直通使用【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux导读本文基于 Linux 内核文档 Documentation/arch/powerpc/vas-api.rst完整讲解 Power9 处理器引入的 Virtual Accelerator SwitchboardVAS虚拟加速器交换板用户态编程接口。VAS 允许用户态程序绕过内核通过 COPY/PASTE 指令直接向 NXNest Accelerator硬件压缩引擎提交请求。读完本文你将掌握/dev/crypto/nx-gzip设备节点的完整使用流程open →VAS_TX_WIN_OPENioctl → mmap → COPY/PASTE理解窗口window、CRBco-processor Request Block、CSBco-processor Status Block等核心概念以及 NX 页错误page fault的处理机制并能够编写可运行的 NX-GZIP 压缩调用程序。背景VAS 与 NX 加速器Power9 处理器引入了 Virtual Accelerator SwitchboardVAS它使得用户态和内核态都可以与协处理器硬件加速器通信这里的协处理器被称为 Nest AcceleratorNX。NX 单元由一个或多个硬件引擎协处理器类型组成例如 842 压缩、GZIP 压缩以及加密引擎。在 Power9 上用户态应用程序只能访问 GZIP 压缩引擎该引擎在硬件中支持 ZLIB 和 GZIP 两种压缩算法。从内核源码arch/powerpc/include/asm/vas.h中的enum vas_cop_type可以看到内核登记的全部协处理器类型其中 GZIP 分为普通优先级VAS_COP_TYPE_GZIP和高优先级VAS_COP_TYPE_GZIP_HIPRI两类enum vas_cop_type { VAS_COP_TYPE_FAULT, VAS_COP_TYPE_842, VAS_COP_TYPE_842_HIPRI, VAS_COP_TYPE_GZIP, VAS_COP_TYPE_GZIP_HIPRI, VAS_COP_TYPE_FTW, VAS_COP_TYPE_MAX, };GZIP 引擎提供两种请求优先级Normal 和 High。目前从用户态只支持 Normal 优先级的请求。当前内核实现vas-api.c也只注册了 NX-GZIP 这一种协处理器类型但 API 框架本身预留了扩展到其他协处理器类型的空间。与 NX 通信的关键在于内核必须先建立一个通道channel或窗口window此后请求就可以在不经内核参与的情况下直接提交。发往 GZIP 引擎的请求必须格式化为协处理器请求块CRB并使用 COPY/PASTE 指令将 CRB 粘贴到与该引擎请求队列关联的硬件地址上。COPY 指令把 CRB 拷入 L2 缓存PASTE 指令再把 CRB 从 L2 缓存提交给硬件——这两条指令的编码定义在 arch/powerpc/include/asm/ppc-opcode.h 中PPC_INST_COPY与PPC_INST_PASTE。整体工作流程概览应用程序通过 VAS/NX 设备驱动实现的/dev/crypto/nx-gzip设备节点获得对 GZIP 引擎的访问权限。典型流程如下open(/dev/crypto/nx-gzip, O_RDWR)获取文件描述符fd对该 fd 发起VAS_TX_WIN_OPENioctl在 GZIP 引擎上为当前进程打开一个发送窗口send window建立与引擎的连接连接建立后使用mmap()将引擎请求队列的硬件地址映射到应用程序的虚拟地址空间得到 paste 地址通过 COPY/PASTE 指令把 CRB 粘贴到 mmap() 返回的虚拟地址即 paste_address上向引擎提交一个或多个请求通过close(fd)关闭连接即关闭发送窗口或在进程退出时由内核自动清理。需要注意应用程序可以用同一个窗口发送多个请求也可以建立多个窗口但每个文件描述符只能对应一个窗口详见下文 ioctl 实现中-EEXIST的检查。NX-GZIP 设备节点系统中只有一个/dev/crypto/nx-gzip节点它提供对所有 GZIP 引擎的访问。该节点唯一合法的文件操作是open()以读写方式打开设备发起VAS_TX_WIN_OPENioctlmmap()将引擎的请求队列映射到应用程序虚拟地址空间即获取协处理器引擎的 paste 地址close()关闭设备节点。该设备节点上的其他文件操作都是未定义的。COPY/PASTE 操作是直接发给硬件的不经过这个设备因此不受上述限制约束。虽然系统中可能有多个 NX 协处理器实例通常每个 P9 芯片一个但系统里只有一个/dev/crypto/nx-gzip设备节点。当该设备节点被打开时内核会在一个合适的 NX 加速器实例上打开发送窗口它找到用户进程正在执行的 CPU然后确定该 CPU 所属芯片对应的 NX 实例。从源码上看POWER9 上芯片与 VAS 实例是 1:1 映射见 asm/vas.h 中chip_to_vas_id()的注释未来若变为 1:N 映射则需要更新该辅助函数。应用程序也可以通过VAS_TX_WIN_OPENioctl 的vas_id字段选择特定的 NX 协处理器实例具体方法见下文发现可用的 VAS 引擎一节。关于用户态库文档提到一个仍在开发中的用户态库libnxz它位于外部仓库power-gzip 项目。使用 inflate/deflate 调用的应用程序可以链接 libnxz 替代 libz无需修改即可使用 NX GZIP 压缩。仓库内的 NX-GZIP 用户手册与 libnxz 测试代码均托管在外部项目中本文不展开外部链接仅提示读者在集成时以官方发布版本为准。第一步打开 /dev/crypto/nx-gzipnx-gzip 设备应以读写方式打开O_RDWR打开该设备不需要特殊权限。每个窗口对应一个文件描述符因此如果用户态进程需要多个窗口就必须发起多次 open 调用。其他细节返回值、错误码、限制参见 open(2) 手册页。从驱动实现看vas-api.c 中的coproc_open()每次 open 会分配一个struct coproc_instance并把它挂到文件私有数据fp-private_data上该实例持有指向全局coproc_device唯一的 cdev 设备的指针以及将来存放发送窗口指针的txwin字段。设备节点的命名由coproc_devnode()实现它在crypto/子目录下创建名为设备名的节点这正是/dev/crypto/nx-gzip路径的由来。VAS_TX_WIN_OPEN ioctl建立与引擎的连接属性结构体应用程序通过如下 ioctl 与 NX 协处理器引擎建立连接。文档给出的属性结构体定义如下struct vas_tx_win_open_attr { __u32 version; __s16 vas_id; /* 特定的 VAS 实例或 -1 使用默认 */ __u16 reserved1; __u64 flags; /* 保留给将来使用 */ __u64 reserved2[6]; };各字段含义version当前必须设置为 1。vas_id如果传入-1内核会尽力为进程分配一个最优的 NX 实例若要选择特定的 VAS 实例参见下文发现可用的 VAS 引擎。flags、reserved1、reserved2[6]为将来扩展保留必须置 0。该结构体与仓库中 arch/powerpc/include/uapi/asm/vas-api.h 的定义完全一致。值得注意的是实际 UAPI 头文件比文档多定义了一个 flags 位——VAS_TX_WIN_FLAG_QOS_CREDIT值为0x0000000000000001用于申请带 QoS credit 的窗口否则使用默认 credit。这说明文档中flags 仅用于未来的描述已经部分过时当前内核已经支持通过 flags 申请 QoS credit 窗口。ioctl 命令号文档给出的命令定义如下#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);不过从仓库实际 UAPI 头文件看内核实现中的命令号是_IOW(VAS_MAGIC, 0x20, struct vas_tx_win_open_attr)即序号 32 而非 1。这一点两者存在出入以内核头文件为准#define VAS_MAGIC v #define VAS_TX_WIN_OPEN _IOW(VAS_MAGIC, 0x20, struct vas_tx_win_open_attr)VAS_TX_WIN_OPEN成功时返回 0出错时返回 -1 并设置 errno。错误条件errno含义EINVALfd 不是有效的 VAS 设备EINVAL无效的 vas IDEINVALversion 未设置为正确值EEXIST该 fd 上已经打开了窗口ENOMEM没有足够内存分配窗口ENOSPC系统打开的窗口连接过多EINVALreserved 字段未置 0从源码coproc_ioc_tx_win_open()的实现可以逐一印证这些错误路径首先检查cp_inst-txwin是否已存在若已打开窗口则返回-EEXIST对应一个 fd 一个窗口的约束通过copy_from_user把用户态属性拷贝进来失败返回-EFAULT校验uattr.version ! 1则返回-EINVAL若 VAS API 尚未注册vops或open_win为空则返回-EACCES随后调用平台相关的vops-open_win(uattr.vas_id, uattr.flags, cop_type)真正打开窗口失败时返回PTR_ERR(txwin)。窗口打开成功后驱动还会为窗口初始化task_ref.mmap_mutex互斥锁用于保护后续 mmap 与 DLPAR 事件之间的竞态。mmap() NX-GZIP 设备获取 paste 地址对 NX-GZIP 设备 fd 调用mmap()会返回一个 paste 地址应用程序可以用 COPY/PASTE 指令把自己的 CRB 发送给硬件引擎paste_addr mmap(addr, size, prot, flags, fd, offset);对 NX-GZIP 设备 fd 的 mmap 只有两个限制size必须为 PAGE_SIZEoffset参数必须为 0ULL。除了 mmap(2) 手册页列出的错误条件外还可能返回以下错误errno含义EINVALfd 未关联任何打开的窗口即 mmap 之前没有成功调用 VAS_TX_WIN_OPEN ioctlEINVALoffset 字段不是 0ULL源码层面的 mmap 实现coproc_mmap()vas-api.c的实现细节如下校验映射区间不超过 PAGE_SIZE否则返回-EINVAL校验vma-vm_pgoff为 0即 offset 为 0否则返回-EINVAL校验实例已有打开的发送窗口txwin非空否则返回-EINVAL在mmap_mutex保护下检查窗口状态必须为VAS_WIN_ACTIVE否则返回-EACCES这对应于 pseries 上因核心移除DLPAR导致丢失 credit、窗口在 hypervisor 中被关闭的场景通过vops-paste_addr(txwin)取得硬件 paste 地址换算成 PFN 后调用remap_pfn_range()完成物理页帧映射并设置VM_IO | VM_PFNMAP标志与可缓存属性映射成功后把 VMA 保存在txwin-task_ref.vma并注册vas_vm_opsfault/close回调。这里的vas_vm_ops值得注意vas_mmap_fault()处理 paste 地址上的页错误——例如 LPAR 因核心移除或迁移而丢失 credit 时内核会先取消既有映射并把窗口置为非活动之后若窗口恢复活动credit 重新可用会在同一虚拟地址上重新映射新的 paste 地址若窗口始终未恢复则通过do_fail_paste()模拟执行 paste 指令失败清 CR0[EQ] 并让 NIP 前进 4 字节把失败结果返回给用户态用户态可据此决定重试、回退到软件压缩或改用其他窗口。发现可用的 VAS 引擎系统中每个可用的 VAS 实例都有一个设备树节点形如/proc/device-tree/vas*或/proc/device-tree/xscom*/vas*。确定芯片或 VAS 实例后使用该节点中的ibm,vas-id属性值即可在 ioctl 中选定特定的 VAS 实例将该值填入vas_id字段。从内核源码看chip_to_vas_id()在 POWER9 上是芯片 ID 到 VAS ID 的 1:1 映射这保证了找到进程所在 CPU 的芯片 → 得到 VAS 实例这一默认分配策略的正确性。COPY/PASTE 操作把请求交给硬件应用程序应使用 COPY 和 PASTE 指令把 CRB 发送给 NXCOPY 指令vas_copy(crb, offset, count)——把 CRB 拷入本地 L2 缓存。内核提供的封装是vas_copy_crb(void *crb, int offset)见 asm/vas.h对应指令编码PPC_INST_COPYPASTE 指令vas_paste(paste_addr, offset, count)——把之前拷入 L2 缓存的 CRB 粘贴到与窗口关联的硬件地址。内核封装为vas_paste_crb(struct vas_window *win, int offset, bool re)其中reretry 使能对 NX 窗口假定为真对应指令编码PPC_INST_PASTE。两条指令的详细语义在 PowerISA 3.0 规范的 4.4 节Copy/Paste instructions中有完整定义这里给出的是仓库源码层面的指令编码与封装入口方便读者在内核源码中继续追查。CRB 规范与使用 NX应用程序必须使用协处理器请求块CRB来格式化发往协处理器的请求。CRB 的具体格式源/目标 Data Descriptor EntryDDE、NX Stamp 等字段以及从用户态使用 NX 的方法发送请求、检查请求状态在 NX-GZIP 用户手册中有详细说明。内核调试辅助函数vas_dump_crb()vas-api.c打印的字段SrcDDE 的 addr/len/count/index/flags、TgtDDE 的同名字段、NX Stamp 的 PSWID/FSA/flags/fault_status可作为理解 CRB 结构的参考索引。NX 故障处理Page Fault 机制应用程序向 NX 发送请求后通过轮询协处理器状态块CSB的 flags 来等待状态。NX 每处理完一个请求都会更新 CSB 中的状态CSB 的格式与状态标志定义见 NX-GZIP 用户手册。当 NX 在 CSB 地址或任意请求缓冲区上遇到地址翻译错误称为NX page fault即 NX 页错误时会向 CPU 发起中断以处理该故障。页错误可能发生在应用程序传入了无效地址或者请求缓冲区不在内存中的情况。操作系统处理该故障的方式是更新 CSB 为以下数据csb.flags CSB_V; csb.cc CSB_CC_FAULT_ADDRESS; csb.ce CSB_CE_TERMINATION; csb.address fault_address;当应用程序收到翻译错误后可以 touch访问包含故障地址的页面使该页进入内存然后重新向 NX 发送这个请求。内核的 CSB 更新实现vas_update_csb()vas-api.c完整实现了上述流程从 CRB 中取出csb_addr大端格式用be64_to_cpu转换填充 CSBcc CSB_CC_FAULT_ADDRESS、ce CSB_CE_TERMINATION、cs 0、count 0、address crb-stamp.nx.fault_storage_addrNX 以大端格式写回用户态需自行转换借助kthread_use_mm(task_ref-mm)借用打开窗口进程的地址空间先copy_to_user写入除 flags 外的整个 CSB再通过smp_mb()屏障保证可见性后单独写入第一个字节csb.flags CSB_V——因为用户态正是轮询csb.flags第一个字节来判断完成状态的所以 flags 的更新必须最后、原子地可见。无效 CSB 地址时的信号上报如果由于 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。是否忽略或处理这些信号由应用程序自行决定。内核通过struct vas_user_win_ref见 asm/vas.h保存窗口打开者的pid、tgid和mm引用窗口打开时get_vas_user_win_ref()会对 pid 与 mm 各取一次引用防止进程退出后 pid 被复用同时保存 tgid 引用因为子线程可能退出但窗口未关闭。发送信号时ref_get_pid_and_task()会优先使用 pid 找到任务若该线程已不存在则回退到 tgid。窗口关闭时由put_vas_user_win_ref()统一释放 pid、tgid、mm 三个引用。简单示例程序文档给出的完整示例注意示例中使用struct vas_setup_attr实际内核 UAPI 头文件中的正确类型名为struct vas_tx_win_open_attr请以头文件为准int use_nx_gzip() { int rc, fd; void *addr; struct vas_tx_win_open_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 { // 用压缩或解压缩数据格式化 CRB 请求 // 参考测试中的 vas_copy / vas_paste 用法 vas_copy(crb, 0, 1); vas_paste(addr, 0, 1); // 带超时地轮询 csb.flags // csb 地址位于 CRB 中 } while (true); close(fd); // 或随进程退出关闭窗口 }对示例的要点解读open以O_RDWR打开设备无需特权ioctlversion 1、vas_id -1让内核自动选择最优 NX 实例其余字段 memset 清零后整体传给VAS_TX_WIN_OPENmmapsize 传 4096PAGE_SIZE、offset 传 0ULL返回的addr即 paste 地址提交请求vas_copy(crb, 0, 1)把 CRB 拷入 L2vas_paste(addr, 0, 1)把 CRB 提交给引擎随后轮询 CRB 中指定的 CSB 地址上的csb.flags直至完成清理close(fd)关闭窗口或依赖进程退出时内核自动回收。更多使用场景与测试用例可参考 libnxzpower-gzip项目仓库内的 vas-api.c 头部注释同样给出了这段最小调用序列open → ioctl → mmap → vas_copy/vas_paste → close可作为核对依据。总结VAS/NX 用户态 API 的精髓在于建立一次通道、之后完全直通内核只在打开窗口ioctl与映射 paste 地址mmap两个阶段参与此后每次压缩请求都通过 COPY/PASTE 指令直接送达 NX 硬件请求完成状态由用户态轮询 CSB 获得一旦发生地址翻译错误内核的vas_update_csb()会把故障信息写回 CSB或对无效 CSB 地址发送 SIGSEGV应用程序只需触达故障页后重发请求即可。这套机制让 Power9 上的用户态程序能够以极低的软件开销利用硬件 GZIP 压缩能力并且对多线程共享窗口、DLPAR 迁移/核心移除等复杂场景都提供了对应的内核侧保障。关键参考文件Documentation/arch/powerpc/vas-api.rst —— 本文主题文档arch/powerpc/include/uapi/asm/vas-api.h —— 用户态 API 头文件结构体、ioctl 命令、QoS credit 标志arch/powerpc/platforms/book3s/vas-api.c —— 设备驱动实现open/ioctl/mmap/fault 处理/CSB 更新arch/powerpc/include/asm/vas.h —— VAS 内核接口窗口属性、vas_copy_crb/vas_paste_crb、用户窗口引用管理arch/powerpc/include/asm/ppc-opcode.h —— COPY/PASTE 指令编码定义【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表