非对称 32 位 SoC

作者:Will Deacon <will@kernel.org>

本文档描述了非对称 32 位 SoC 对执行 32 位 (AArch32) 应用程序的影响。

日期:2021-05-17

简介

一些 Armv9 SoC 存在一个 big.LITTLE 的缺陷,即只有一部分 CPU 能够执行 32 位用户应用程序。 在这样的系统上,Linux 默认将这种不对称视为“不匹配”,并禁用对 PER_LINUX32 personality 和 32 位 ELF 二进制文件的 execve(2) 的支持,后者返回 -ENOEXEC。 如果在 64 位专用 CPU 的后期联机期间检测到不匹配,则联机操作将失败,并且新的 CPU 将无法用于调度。

令人惊讶的是,生产这些 SoC 的目的是为了运行旧的 32 位二进制文件。 毫不奇怪,这与 Linux 的默认行为并不太兼容。

未来的 SoC 似乎不可避免地会放弃 32 位支持,因此,如果您不幸需要在此类过渡平台上运行 32 位代码,那么明智的做法是考虑重新编译、模拟或淘汰等替代方案。 如果这些选项都不实用,请继续阅读。

启用内核支持

由于内核支持对用户空间来说并非完全透明,因此,允许 32 位任务在非对称 32 位系统上运行需要显式的“选择加入”,并且可以通过在内核命令行上传递 allow_mismatched_32bit_el0 参数来启用。

在本文档的其余部分,我们将使用非对称系统来表示运行 Linux 且启用了此内核命令行选项的非对称 32 位 SoC。

用户空间影响

在非对称系统上运行的 32 位任务的行为与在同构系统上的行为基本相同,只有少数与 CPU 亲和性相关的关键差异。

sysfs

能够在 /sys/devices/system/cpu/aarch32_el0 中描述运行 32 位任务的 CPU 子集,并在 ABI 文件测试/sysfs-devices-system-cpu 中进一步记录。

注意: CPU 由此文件进行通告,因为它们被检测到,因此稍后联机支持 32 位的 CPU 可能导致内核在运行时修改文件内容。 一旦通告,CPU 将永远不会从此文件中删除。

execve(2)

在同构系统上,任务的 CPU 亲和性会在 execve(2) 过程中保留。 这在非对称系统上并非总是可能的,尤其是在要执行的新程序是 32 位,但亲和性掩码包含仅限 64 位的 CPU 时。 在这种情况下,内核将按如下方式确定新的亲和性掩码

  1. 如果亲和性掩码中支持 32 位的子集不为空,则亲和性将限制为该子集,并保存旧的亲和性掩码。 此保存的掩码会在 fork(2) 中继承,并在 32 位程序的 execve(2) 中保留。

    注意: 此步骤不适用于 SCHED_DEADLINE 任务。 请参见 SCHED_DEADLINE

  2. 否则,将遍历任务的 cpuset 层级结构,直到找到包含至少一个支持 32 位的 CPU 的祖先。 然后,任务的亲和性将更改为与遍历确定的 cpuset 中支持 32 位的子集匹配。

  3. 如果失败(即内存不足),则亲和性将更改为内核知道的所有支持 32 位的 CPU 的集合。

32 位任务随后对 64 位程序执行的 execve(2) 将使在 (1) 中保存的亲和性掩码无效,并尝试使用保存的掩码恢复任务的 CPU 亲和性(如果先前有效)。 由于截止时间策略或 cpuset 层级结构的中间更改,此恢复可能会失败,在这种情况下,execve(2) 将继续执行,且亲和性不变。

对 32 位任务调用 sched_setaffinity(2) 将仅考虑请求的亲和性掩码中支持 32 位的 CPU。 成功后,将更新任务的亲和性,并且先前 execve(2) 中保存的任何掩码都将无效。

SCHED_DEADLINE

在非对称 32 位系统上,拒绝显式允许 32 位截止时间任务进入默认根域(例如,通过调用 sched_setattr(2)),除非通过将 -1 写入 /proc/sys/kernel/sched_rt_runtime_us 来禁用准入控制。

如果任务的根域包含任何仅限 64 位的 CPU 且启用了准入控制,则从 64 位截止时间任务执行 execve(2) 的 32 位程序将返回 -ENOEXEC。 并发离线支持 32 位的 CPU 仍然可能需要 execve(2) 中描述的过程,在这种情况下,将跳过步骤 (1),并在控制台上发出警告。

注意: 如果要在非对称系统上将 SCHED_DEADLINE 与 32 位任务一起使用,建议将一组支持 32 位的 CPU 放入单独的根域中。 否则,很可能会错过截止时间。

Cpusets

非对称系统上 32 位任务的亲和性可能包括 cpuset 未明确允许的 CPU。

  • 连接到仅允许 64 位 CPU 的 cpuset 的 64 位任务执行 32 位程序。

  • cpuset 允许的所有支持 32 位的 CPU(包含 32 位任务)均已离线。

在这两种情况下,都会根据 execve(2) 中描述的过程的步骤 (2) 来计算新的亲和性,并且无论 cgroup 版本如何,cpuset 层级结构都不会更改。

CPU 热插拔

在非对称系统上,阻止用户空间离线检测到的第一个支持 32 位的 CPU,并且任何此类尝试都将返回 -EPERM。 请注意,即使主 CPU(即 CPU 0)是仅限 64 位的 CPU,也仍然允许挂起。

KVM

虽然 KVM 不会在非对称系统上向任何 vCPU 通告 32 位 EL0 支持,但 EL1 上已损坏的客户机仍可能尝试在 EL0 上执行 32 位代码。 在这种情况下,从 32 位模式的 vCPU 线程退出将返回到主用户空间,exit_reasonKVM_EXIT_FAIL_ENTRY,并且在后续 KVM_ARM_VCPU_INIT 操作成功重新初始化之前,将保持不可运行状态。

NOHZ FULL

为了避免在强制迁移 32 位任务时扰乱自适应滴答 CPU(使用 nohz_full= 指定),当启用对非对称 32 位系统的支持时,会将这些 CPU 视为仅限 64 位的 CPU。