35. 在用户空间应用程序中使用 XSTATE 特性¶
x86 架构支持通过 CPUID 枚举的浮点扩展。 应用程序查询 CPUID 并使用 XGETBV 来评估内核 XCR0 启用了哪些特性。
对于 AVX-512 和 PKRU 状态,如果可用,内核会自动启用这些特性。 像 AMX TILE_DATA(XSTATE 组件 18)这样的特性也由 XCR0 启用,但第一次使用相关指令会被内核捕获,因为默认情况下不会自动分配所需的大型 XSTATE 缓冲区。
35.1. 动态特性的目的¶
传统的用户空间库通常对备用信号堆栈具有硬编码的静态大小,通常使用 MINSIGSTKSZ,它通常是 2KB。 该堆栈必须能够存储至少内核在跳转到信号处理程序之前设置的信号帧。 该信号帧必须包含由 CPU 定义的 XSAVE 缓冲区。
然而,这意味着信号堆栈的大小是动态的,而不是静态的,因为不同的 CPU 具有不同大小的 XSAVE 缓冲区。 对于像 AMX 这样的新 CPU 特性,现有应用程序的 2KB 编译大小太小了。 内核不需要普遍要求更大的堆栈,而是通过动态启用,内核可以强制用户空间应用程序拥有适当大小的备用堆栈。
35.2. 在用户空间应用程序中使用动态启用的 XSTATE 特性¶
内核提供了一个基于 arch_prctl(2) 的机制,供应用程序请求使用此类特性。 与此相关的 arch_prctl(2) 选项是
- -ARCH_GET_XCOMP_SUPP
arch_prctl(ARCH_GET_XCOMP_SUPP, &features);
ARCH_GET_XCOMP_SUPP 将支持的特性存储在类型为 uint64_t 的用户空间存储中。 第二个参数是指向该存储的指针。
- -ARCH_GET_XCOMP_PERM
arch_prctl(ARCH_GET_XCOMP_PERM, &features);
ARCH_GET_XCOMP_PERM 将用户空间进程具有权限的特性存储在类型为 uint64_t 的用户空间存储中。 第二个参数是指向该存储的指针。
- -ARCH_REQ_XCOMP_PERM
arch_prctl(ARCH_REQ_XCOMP_PERM, feature_nr);
ARCH_REQ_XCOMP_PERM 允许请求动态启用的特性或特性集的权限。 特性集可以映射到一个设施,例如 AMX,并且可能需要启用一个或多个 XSTATE 组件。
特性参数是设施工作所需的最高 XSTATE 组件的编号。
当请求一个特性的权限时,内核会检查可用性。 内核确保进程任务中的 sigaltstacks 足够大,以容纳生成的大的信号帧。 它在 ARCH_REQ_XCOMP_SUPP 和任何后续 sigaltstack(2) 调用期间强制执行这一点。 如果已安装的 sigaltstack 小于生成的信号帧大小,则 ARCH_REQ_XCOMP_SUPP 会导致 -ENOSUPP。 此外,如果请求的 altstack 对于允许的特性来说太小,sigaltstack(2) 会导致 -ENOMEM。
授予的权限对每个进程都有效。 权限在 fork(2) 上继承,并在 exec(3) 上清除。
第一次使用与动态启用特性相关的指令会被内核捕获。 陷阱处理程序检查该进程是否具有使用该特性的权限。 如果进程没有权限,则内核会将 SIGILL 发送到应用程序。 如果进程有权限,则处理程序会为任务分配更大的 xstate 缓冲区,以便可以上下文切换大状态。 在不太可能发生的分配失败的情况下,内核会发送 SIGSEGV。
35.2.1. AMX TILE_DATA 启用示例¶
以下是用户空间应用程序如何动态启用 TILE_DATA 的示例
应用程序首先需要向内核查询 AMX 支持
#include <asm/prctl.h> #include <sys/syscall.h> #include <stdio.h> #include <unistd.h> #ifndef ARCH_GET_XCOMP_SUPP #define ARCH_GET_XCOMP_SUPP 0x1021 #endif #ifndef ARCH_XCOMP_TILECFG #define ARCH_XCOMP_TILECFG 17 #endif #ifndef ARCH_XCOMP_TILEDATA #define ARCH_XCOMP_TILEDATA 18 #endif #define MASK_XCOMP_TILE ((1 << ARCH_XCOMP_TILECFG) | \ (1 << ARCH_XCOMP_TILEDATA)) unsigned long features; long rc; ... rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features); if (!rc && (features & MASK_XCOMP_TILE) == MASK_XCOMP_TILE) printf("AMX is available.\n");之后,在确定对 AMX 的支持后,应用程序必须明确请求使用它的权限
#ifndef ARCH_REQ_XCOMP_PERM #define ARCH_REQ_XCOMP_PERM 0x1023 #endif ... rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, ARCH_XCOMP_TILEDATA); if (!rc) printf("AMX is ready for use.\n");
请注意,此示例不包括 sigaltstack 准备。
35.3. 信号帧中的动态特性¶
如果动态启用的特性处于其初始配置中,则在信号进入时不会将其写入信号帧。 这与非动态特性不同,无论其配置如何,它们总是被写入。 信号处理程序可以检查 XSAVE 缓冲区的 XSTATE_BV 字段以确定是否写入了特性。
35.4. 虚拟机上的动态特性¶
访客状态组件的权限需要与主机分开管理,因为它们彼此排斥。 扩展了一对选项来控制访客权限
- -ARCH_GET_XCOMP_GUEST_PERM
arch_prctl(ARCH_GET_XCOMP_GUEST_PERM, &features);
ARCH_GET_XCOMP_GUEST_PERM 是 ARCH_GET_XCOMP_PERM 的变体。 因此,它为访客组件提供相同的语义和功能。
- -ARCH_REQ_XCOMP_GUEST_PERM
arch_prctl(ARCH_REQ_XCOMP_GUEST_PERM, feature_nr);
ARCH_REQ_XCOMP_GUEST_PERM 是 ARCH_REQ_XCOMP_PERM 的变体。 它对访客权限具有相同的语义。 虽然提供类似的功能,但这带有一个约束。 当创建第一个 VCPU 时,权限会被冻结。 在那之后,任何更改权限的尝试都将被拒绝。 因此,必须在创建第一个 VCPU 之前请求权限。
请注意,一些 VMM 可能已经建立了一组受支持的状态组件。 这些选项不假定支持任何特定的 VMM。