非对称 32 位 SoC¶
作者:Will Deacon <will@kernel.org>
本文档描述了非对称 32 位 SoC 对执行 32 位(AArch32)应用程序的影响。
日期:2021-05-17
简介¶
一些 Armv9 SoC 存在一种 big.LITTLE 的缺陷(misfeature),即只有一部分 CPU 能够执行 32 位用户应用程序。在这样的系统上,Linux 默认将这种非对称性视为“不匹配(mismatch)”,并禁用对 PER_LINUX32 进程属性以及 32 位 ELF 二进制文件 execve(2) 的支持,后者会返回 -ENOEXEC。如果在仅支持 64 位的 CPU 的后期上线(late onlining)过程中检测到这种不匹配,那么上线操作将失败,并且新 CPU 将无法用于调度。
令人惊讶的是,这些 SoC 的生产初衷却是为了运行传统的 32 位二进制文件。毫不意外的是,这与 Linux 的默认行为配合得并不好。
未来的 SoC 彻底放弃 32 位支持似乎是不可避免的,因此,如果你陷入了必须在这些过渡平台上运行 32 位代码的尴尬境地,明智的做法是考虑诸如重新编译、仿真或退役等替代方案。如果这些选项都不切实际,请继续阅读下文。
启用内核支持¶
由于内核支持对用户空间并非完全透明,因此允许 32 位任务在非对称 32 位系统上运行需要显式的“选择加入(opt-in)”,可以通过在内核命令行中传递 allow_mismatched_32bit_el0 参数来启用。
在本文档的剩余部分中,我们将 非对称系统 定义为启用了此内核命令行选项的、运行 Linux 的非对称 32 位 SoC。
对用户空间的影响¶
在非对称系统上运行的 32 位任务其表现与在同构系统上大多相同,但在 CPU 亲和力(affinity)方面有一些关键区别。
sysfs¶
能够运行 32 位任务的 CPU 子集在 /sys/devices/system/cpu/aarch32_el0 中进行了描述,并在 ABI 文件 testing/sysfs-devices-system-cpu 中进行了进一步文档说明。
注意: CPU 在被检测到时会通过此文件通告,因此对支持 32 位的 CPU 进行后期上线(late-onlining)可能会导致内核在运行时修改该文件的内容。一旦通告,CPU 绝不会从该文件中移除。
execve(2)¶
在同构系统上,任务的 CPU 亲和力在 execve(2) 期间得以保留。在非对称系统上,这并不总是可行的,特别是当正在执行的新程序是 32 位但亲和力掩码包含仅支持 64 位的 CPU 时。在这种情况下,内核按如下方式确定新的亲和力掩码
如果亲和力掩码中支持 32 位的子集不为空,则亲和力将限制在该子集,并且旧的亲和力掩码会被保存。这个保存的掩码在
fork(2)时会被继承,并在执行 32 位程序的execve(2)期间得以保留。注意: 此步骤不适用于
SCHED_DEADLINE任务。请参阅 SCHED_DEADLINE。否则,将遍历该任务的 cpuset 层级结构,直到找到包含至少一个支持 32 位 CPU 的祖先。然后,任务的亲和力会更改为与遍历确定的 cpuset 的支持 32 位子集相匹配。
如果失败(即内存不足),亲和力将更改为内核已知的、所有支持 32 位的 CPU 集合。
随后该 32 位任务对 64 位程序执行 execve(2) 将使 (1) 中保存的亲和力掩码失效,并尝试使用之前有效的保存掩码来恢复任务的 CPU 亲和力。由于截止时间策略或 cpuset 层级结构的中间更改,此恢复可能会失败,在这种情况下,execve(2) 继续执行且亲和力保持不变。
对 32 位任务调用 sched_setaffinity(2) 时,将仅考虑请求的亲和力掩码中支持 32 位的 CPU。成功后,任务的亲和力将被更新,并且先前 execve(2) 保存的任何掩码都将失效。
SCHED_DEADLINE¶
除非通过向 /proc/sys/kernel/sched_rt_runtime_us 写入 -1 来禁用准入控制,否则在非对称 32 位系统上,将 32 位 deadline 任务显式接入默认根域(例如通过调用 sched_setattr(2))将被拒绝。
如果任务的根域包含任何仅 64 位 CPU 且准入控制已启用,则 64 位 deadline 任务对 32 位程序执行 execve(2) 将返回 -ENOEXEC。并发下线支持 32 位的 CPU 可能仍需要执行 execve(2) 中描述的流程,在这种情况下,将跳过步骤 (1) 并在控制台上发出警告。
注意: 如果要在非对称系统上将 SCHED_DEADLINE 用于 32 位任务,建议将一组支持 32 位的 CPU 放入单独的根域中。不这样做很可能会导致错过 deadline(任务超时)。
Cpusets¶
非对称系统上 32 位任务的亲和力可能包含其所附加的 cpuset 未显式允许的 CPU。这可能是由以下两种情况导致的
附加到仅允许 64 位 CPU 的 cpuset 上的 64 位任务执行了 32 位程序。
包含 32 位任务的 cpuset 所允许的所有支持 32 位的 CPU 都已下线。
在这两种情况下,新亲和力都是根据 execve(2) 中描述的过程的步骤 (2) 计算的,并且无论 cgroup 版本如何,cpuset 层级结构都保持不变。
CPU 热插拔¶
在非对称系统上,用户空间无法将第一个检测到的支持 32 位的 CPU 下线,任何此类尝试都将返回 -EPERM。请注意,即使主 CPU(即 CPU 0)仅支持 64 位,仍然允许系统挂起。
KVM¶
尽管 KVM 不会向非对称系统上的任何 vCPU 通告 32 位 EL0 支持,但 EL1 处损坏的客户机仍可能尝试在 EL0 处执行 32 位代码。在这种情况下,32 位模式下的 vCPU 线程退出将返回到宿主机用户空间,其 exit_reason 为 KVM_EXIT_FAIL_ENTRY,并且在通过随后的 KVM_ARM_VCPU_INIT 操作成功重新初始化之前,它将保持不可运行状态。
调度域隔离¶
为了避免在强制迁移 32 位任务时干扰引导定义的域隔离 CPU(使用 isolcpus=[domain] 指定),当启用对非对称 32 位系统的支持时,这些 CPU 将被视为仅 64 位。
然而,与引导定义的域隔离相反,在非对称 32 位系统上不建议使用 cpuset 隔离分区进行运行时定义的域隔离,这会导致未定义行为。