An alternative series for asymmetric AArch32 systems
Subsystems (7)
Will Deacon
maintainerv5 cover An alternative series for asymmetric AArch32 systems
Hi all,
Christmas has come early: it's time for version five of these patches which have previously appeared here:
v1: https://lore.kernel.org/r/[email protected]
v2: https://lore.kernel.org/r/[email protected]
v3: https://lore.kernel.org/r/[email protected]
v4: https://lore.kernel.org/r/[email protected]
and which started life as a reimplementation of some patches from Qais:
https://lore.kernel.org/r/[email protected]
There's also now a nice writeup on LWN:
https://lwn.net/Articles/838339/
and rumours of a feature film are doing the rounds.
[subscriber-only, but if you're reading this then you should really
subscribe.]
The aim of this series is to allow 32-bit ARM applications to run on arm64 SoCs where not all of the CPUs support the 32-bit instruction set. Unfortunately, such SoCs are real and will continue to be productised over the next few years at least. I can assure you that I'm not just doing this for fun.
Changes in v5 include:
* Teach cpuset_cpus_allowed() about task_cpu_possible_mask() so that we can avoid returning incompatible CPUs for a given task. This means that sched_setaffinity() can be used with larger masks (like the online mask) from userspace and also allows us to take into account the cpuset hierarchy when forcefully overriding the affinity for a task on execve().
* Honour task_cpu_possible_mask() when attaching a task to a cpuset, so that the resulting affinity mask does not contain any incompatible CPUs (since it would be rejected by set_cpus_allowed_ptr() otherwise).
* Moved overriding of the affinity mask into the scheduler core rather than munge affinity masks directly in the architecture backend.
* Extended comments and documentation.
* Some renaming and cosmetic changes.
I'm pretty happy with this now, although it still needs review and will require rebasing to play nicely with the SCA changes in -next.
Cheers,
Will
--->8
Will Deacon (15):
arm64: cpuinfo: Split AArch32 registers out into a separate struct
arm64: Allow mismatched 32-bit EL0 support
KVM: arm64: Kill 32-bit vCPUs on systems with mismatched EL0 support
arm64: Kill 32-bit applications scheduled on 64-bit-only CPUs
arm64: Advertise CPUs capable of running 32-bit applications in sysfs
sched: Introduce task_cpu_possible_mask() to limit fallback rq
selection
cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
cpuset: Honour task_cpu_possible_mask() in guarantee_online_cpus()
sched: Reject CPU affinity changes based on task_cpu_possible_mask()
sched: Introduce force_compatible_cpus_allowed_ptr() to limit CPU
affinity
arm64: Implement task_cpu_possible_mask()
arm64: exec: Adjust affinity for compat tasks with mismatched 32-bit
EL0
arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched
system
arm64: Hook up cmdline parameter to allow mismatched 32-bit EL0
arm64: Remove logic to kill 32-bit tasks on 64-bit-only cores
.../ABI/testing/sysfs-devices-system-cpu | 9 + .../admin-guide/kernel-parameters.txt | 8 + arch/arm64/include/asm/cpu.h | 44 ++-- arch/arm64/include/asm/cpucaps.h | 3 +- arch/arm64/include/asm/cpufeature.h | 8 +- arch/arm64/include/asm/mmu_context.h | 13 ++ arch/arm64/kernel/cpufeature.c | 219 ++++++++++++++---- arch/arm64/kernel/cpuinfo.c | 53 +++-- arch/arm64/kernel/process.c | 19 +- arch/arm64/kvm/arm.c | 11 +- include/linux/cpuset.h | 3 +- include/linux/mmu_context.h | 8 + include/linux/sched.h | 1 + kernel/cgroup/cpuset.c | 39 ++-- kernel/sched/core.c | 112 +++++++-- 15 files changed, 426 insertions(+), 124 deletions(-)
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 1/15 arm64: cpuinfo: Split AArch32 registers out into a separate struct
In preparation for late initialisation of the "sanitised" AArch32 register state, move the AArch32 registers out of 'struct cpuinfo' and into their own struct definition.
--- arch/arm64/include/asm/cpu.h | 44 +++++++++++---------- arch/arm64/kernel/cpufeature.c | 71 ++++++++++++++++++---------------- arch/arm64/kernel/cpuinfo.c | 53 +++++++++++++------------ 3 files changed, 89 insertions(+), 79 deletions(-)
arch/arm64/include/asm/cpu.h +24 -20
@@ -12,26 +12,7 @@/* * Records attributes of an individual CPU. */ -struct cpuinfo_arm64 { -struct cpu cpu; -struct kobject kobj; -u32 reg_ctr; -u32 reg_cntfrq; -u32 reg_dczid; -u32 reg_midr; -u32 reg_revidr; - -u64 reg_id_aa64dfr0; -u64 reg_id_aa64dfr1; -u64 reg_id_aa64isar0; -u64 reg_id_aa64isar1; -u64 reg_id_aa64mmfr0; -u64 reg_id_aa64mmfr1; -u64 reg_id_aa64mmfr2; -u64 reg_id_aa64pfr0; -u64 reg_id_aa64pfr1; -u64 reg_id_aa64zfr0; - +struct cpuinfo_32bit {u32 reg_id_dfr0;u32 reg_id_dfr1;u32 reg_id_isar0;
@@ -54,6 +35,29 @@ struct cpuinfo_arm64 {u32 reg_mvfr0;u32 reg_mvfr1;u32 reg_mvfr2; +}; + +struct cpuinfo_arm64 { +struct cpu cpu; +struct kobject kobj; +u32 reg_ctr; +u32 reg_cntfrq; +u32 reg_dczid; +u32 reg_midr; +u32 reg_revidr; + +u64 reg_id_aa64dfr0; +u64 reg_id_aa64dfr1; +u64 reg_id_aa64isar0; +u64 reg_id_aa64isar1; +u64 reg_id_aa64mmfr0; +u64 reg_id_aa64mmfr1; +u64 reg_id_aa64mmfr2; +u64 reg_id_aa64pfr0; +u64 reg_id_aa64pfr1; +u64 reg_id_aa64zfr0; + +struct cpuinfo_32bit aarch32;/* pseudo-ZCR for recording maximum ZCR_EL1 LEN value: */ u64 reg_zcr;
arch/arm64/kernel/cpufeature.c +37 -34
102 lines not on this page · raw message
arch/arm64/kernel/cpuinfo.c +28 -25
65 lines not on this page · raw message
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 2/15 arm64: Allow mismatched 32-bit EL0 support
In reply to Will DeaconWhen confronted with a mixture of CPUs, some of which support 32-bit applications and others which don't, we quite sensibly treat the system as 64-bit only for userspace and prevent execve() of 32-bit binaries.
Unfortunately, some crazy folks have decided to build systems like this with the intention of running 32-bit applications, so relax our sanitisation logic to continue to advertise 32-bit support to userspace on these systems and track the real 32-bit capable cores in a cpumask instead. For now, the default behaviour remains but will be tied to a command-line option in a later patch.
--- arch/arm64/include/asm/cpucaps.h | 3 +- arch/arm64/include/asm/cpufeature.h | 8 ++- arch/arm64/kernel/cpufeature.c | 106 ++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 9 deletions(-)
arch/arm64/include/asm/cpucaps.h +2 -1
@@ -20,7 +20,8 @@#define ARM64_ALT_PAN_NOT_UAO 10#define ARM64_HAS_VIRT_HOST_EXTN 11#define ARM64_WORKAROUND_CAVIUM_27456 12 -#define ARM64_HAS_32BIT_EL0 13 +/* Unreliable: use system_supports_32bit_el0() instead. */ +#define ARM64_HAS_32BIT_EL0_DO_NOT_USE 13#define ARM64_HARDEN_EL2_VECTORS 14#define ARM64_HAS_CNP 15#define ARM64_HAS_NO_FPSIMD 16
arch/arm64/include/asm/cpufeature.h +7 -1
@@ -606,9 +606,15 @@ static inline bool cpu_supports_mixed_endian_el0(void)return id_aa64mmfr0_mixed_endian_el0 (read_cpuid (ID_AA64MMFR0_EL1 )); } +const struct cpumask *system_32bit_el0_cpumask (void ); +DECLARE_STATIC_KEY_FALSE (arm64_mismatched_32bit_el0); +static inline bool system_supports_32bit_el0 (void ) { -return cpus_have_const_cap (ARM64_HAS_32BIT_EL0 ); +u64 pfr0 =read_sanitised_ftr_reg (SYS_ID_AA64PFR0_EL1 ); + +return static_branch_unlikely (&arm64_mismatched_32bit_el0) || +id_aa64pfr0_32bit_el0 (pfr0); }static inline bool system_supports_4kb_granule (void )
arch/arm64/kernel/cpufeature.c +99 -7
167 lines not on this page · raw message
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 3/15 KVM: arm64: Kill 32-bit vCPUs on systems with mismatched EL0 support
In reply to Will DeaconIf a vCPU is caught running 32-bit code on a system with mismatched support at EL0, then we should kill it.
--- arch/arm64/kvm/arm.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
arch/arm64/kvm/arm.c +10 -1
@@ -649,6 +649,15 @@ static void check_vcpu_requests(struct kvm_vcpu *vcpu) } } +static bool vcpu_mode_is_bad_32bit (struct kvm_vcpu *vcpu) +{ +if (likely (!vcpu_mode_is_32bit (vcpu))) +return false; + +return !system_supports_32bit_el0 () || +static_branch_unlikely (&arm64_mismatched_32bit_el0); +} +/** * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code * @vcpu: The VCPU pointer
@@ -832,7 +841,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)* with the asymmetric AArch32 case), return to userspace with * a fatal error. */ -if (!system_supports_32bit_el0 () &&vcpu_mode_is_32bit (vcpu)) { +if (vcpu_mode_is_bad_32bit (vcpu)) {/* * As we have caught the guest red-handed, decide that * it isn't fit for purpose anymore by making the vcpu
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 4/15 arm64: Kill 32-bit applications scheduled on 64-bit-only CPUs
In reply to Will DeaconScheduling a 32-bit application on a 64-bit-only CPU is a bad idea.
Ensure that 32-bit applications always take the slow-path when returning to userspace on a system with mismatched support at EL0, so that we can avoid trying to run on a 64-bit-only CPU and force a SIGKILL instead.
--- arch/arm64/kernel/process.c | 19 ++++++++++++++++++- arch/arm64/kernel/signal.c | 26 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-)
arch/arm64/kernel/process.c +18 -1
@@ -541,6 +541,15 @@ static void erratum_1418040_thread_switch(struct task_struct *prev,write_sysreg (val, cntkctl_el1); } +static void compat_thread_switch (struct task_struct *next) +{ +if (!is_compat_thread (task_thread_info (next))) +return ; + +if (static_branch_unlikely (&arm64_mismatched_32bit_el0)) +set_tsk_thread_flag (next,TIF_NOTIFY_RESUME ); +} +/* * Thread switching. */
@@ -557,6 +566,7 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,uao_thread_switch (next);ssbs_thread_switch (next);erratum_1418040_thread_switch (prev, next); +compat_thread_switch (next);/* * Complete any pending TLB or cache maintenance on this CPU in case
@@ -619,8 +629,15 @@ unsigned long arch_align_stack(unsigned long sp)*/ void arch_setup_new_exec (void ) { - current->mm->context.flags =is_compat_task () ?MMCF_AARCH32 :0 ; +unsigned long mmflags =0 ; + +if (is_compat_task ()) { + mmflags =MMCF_AARCH32 ; +if (static_branch_unlikely (&arm64_mismatched_32bit_el0)) +set_tsk_thread_flag (current,TIF_NOTIFY_RESUME ); + } + current->mm->context.flags = mmflags;ptrauth_thread_init_user (current);if (task_spec_ssb_noexec (current)) {
arch/arm64/kernel/signal.c +26 -0
@@ -911,6 +911,19 @@ static void do_signal(struct pt_regs *regs)restore_saved_sigmask (); } +static bool cpu_affinity_invalid (struct pt_regs *regs) +{ +if (!compat_user_mode (regs)) +return false; + +/* +* We're preemptible, but a reschedule will cause us to check the +* affinity again. +*/ +return !cpumask_test_cpu (raw_smp_processor_id (), +system_32bit_el0_cpumask ()); +} + asmlinkage void do_notify_resume(struct pt_regs *regs,unsigned long thread_flags) {
@@ -948,6 +961,19 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,if (thread_flags & _TIF_NOTIFY_RESUME) {tracehook_notify_resume (regs);rseq_handle_notify_resume (NULL , regs); + +/* +* If we reschedule after checking the affinity +* then we must ensure that TIF_NOTIFY_RESUME +* is set so that we check the affinity again. +* Since tracehook_notify_resume() clears the +* flag, ensure that the compiler doesn't move +* it after the affinity check. +*/ +barrier (); + +if (cpu_affinity_invalid (regs)) +force_sig (SIGKILL ); }if (thread_flags & _TIF_FOREIGN_FPSTATE)
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 5/15 arm64: Advertise CPUs capable of running 32-bit applications in sysfs
In reply to Will DeaconSince 32-bit applications will be killed if they are caught trying to execute on a 64-bit-only CPU in a mismatched system, advertise the set of 32-bit capable CPUs to userspace in sysfs.
--- .../ABI/testing/sysfs-devices-system-cpu | 9 +++++++++ arch/arm64/kernel/cpufeature.c | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +9 -0
@@ -493,6 +493,15 @@ Description: AArch64 CPU registers 'identification' directory exposes the CPU ID registers for identifying model and revision of the CPU. +What: /sys/devices/system/cpu/aarch32_el0 +Date: November 2020 +Contact: Linux ARM Kernel Mailing list <[email protected]> +Description: Identifies the subset of CPUs in the system that can execute + AArch32 (32-bit ARM) applications. If present, the same format as + /sys/devices/system/cpu/{offline,online,possible,present} is used. + If absent, then all or none of the CPUs can execute AArch32 + applications and execve() will behave accordingly. + What: /sys/devices/system/cpu/cpu#/cpu_capacity Date: December 2016 Contact: Linux kernel mailing list <[email protected]>
arch/arm64/kernel/cpufeature.c +19 -0
@@ -67,6 +67,7 @@#include <linux/crash_dump.h> #include <linux/sort.h> #include <linux/stop_machine.h> +#include <linux/sysfs.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/cpu.h>
@@ -1272,6 +1273,24 @@ const struct cpumask *system_32bit_el0_cpumask(void)return cpu_possible_mask; } +static ssize_t aarch32_el0_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +const struct cpumask *mask =system_32bit_el0_cpumask (); + +return sysfs_emit (buf,"%*pbl\n" ,cpumask_pr_args (mask)); +} +static const DEVICE_ATTR_RO (aarch32_el0); + +static int __init aarch32_el0_sysfs_init (void ) +{ +if (!allow_mismatched_32bit_el0) +return 0 ; + +return device_create_file (cpu_subsys.dev_root, &dev_attr_aarch32_el0); +} +device_initcall (aarch32_el0_sysfs_init); +static bool has_32bit_el0 (const struct arm64_cpu_capabilities *entry,int scope) {if (!has_cpuid_feature (entry, scope))
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 6/15 sched: Introduce task_cpu_possible_mask() to limit fallback rq selection
In reply to Will DeaconAsymmetric systems may not offer the same level of userspace ISA support across all CPUs, meaning that some applications cannot be executed by some CPUs. As a concrete example, upcoming arm64 big.LITTLE designs do not feature support for 32-bit applications on both clusters.
On such a system, we must take care not to migrate a task to an unsupported CPU when forcefully moving tasks in select_fallback_rq() in response to a CPU hot-unplug operation.
Introduce a task_cpu_possible_mask() hook which, given a task argument, allows an architecture to return a cpumask of CPUs that are capable of executing that task. The default implementation returns the cpu_possible_mask, since sane machines do not suffer from per-cpu ISA limitations that affect scheduling. The new mask is used when selecting the fallback runqueue as a last resort before forcing a migration to the first active CPU.
--- include/linux/mmu_context.h | 8 ++++++++ kernel/sched/core.c | 8 +++++--- 2 files changed, 13 insertions(+), 3 deletions(-)
include/linux/mmu_context.h +8 -0
@@ -14,4 +14,12 @@static inline void leave_mm (int cpu) { }#endif +/* +* CPUs that are capable of running task @p. By default, we assume a sane, +* homogeneous system. Must contain at least one active CPU. +*/ +#ifndef task_cpu_possible_mask +# define task_cpu_possible_mask (p) cpu_possible_mask +#endif +#endif
kernel/sched/core.c +5 -3
@@ -1708,7 +1708,10 @@ static inline bool is_cpu_allowed(struct task_struct *p, int cpu)if (is_per_cpu_kthread (p))return cpu_online (cpu); -return cpu_active (cpu); +if (!cpu_active (cpu)) +return false; + +return cpumask_test_cpu (cpu,task_cpu_possible_mask (p)); }/*
@@ -2318,10 +2321,9 @@ static int select_fallback_rq(int cpu, struct task_struct *p) } fallthrough;case possible: -do_set_cpus_allowed (p, cpu_possible_mask); +do_set_cpus_allowed (p,task_cpu_possible_mask (p)); state = fail;break ; -case fail:BUG ();break ;
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 7/15 cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
In reply to Will DeaconIf the scheduler cannot find an allowed CPU for a task, cpuset_cpus_allowed_fallback() will widen the affinity to cpu_possible_mask if cgroup v1 is in use.
In preparation for allowing architectures to provide their own fallback mask, just return early if we're not using cgroup v2 and allow select_fallback_rq() to figure out the mask by itself.
--- kernel/cgroup/cpuset.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
kernel/cgroup/cpuset.c +4 -2
@@ -3299,9 +3299,11 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)void cpuset_cpus_allowed_fallback (struct task_struct *tsk) { +if (!is_in_v2_mode ()) +return ;/* select_fallback_rq will try harder */ +rcu_read_lock (); -do_set_cpus_allowed (tsk,is_in_v2_mode () ? -task_cs (tsk)->cpus_allowed : cpu_possible_mask); +do_set_cpus_allowed (tsk,task_cs (tsk)->cpus_allowed);rcu_read_unlock ();/*
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Re: [PATCH v5 07/15] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
Quoted 22 lines · Will Deacon
If the scheduler cannot find an allowed CPU for a task,
cpuset_cpus_allowed_fallback()will widen the affinity tocpu_possible_maskif cgroup v1 is in use.In preparation for allowing architectures to provide their own fallback mask, just return early if we're not using cgroup v2 and allow
select_fallback_rq()to figure out the mask by itself.Cc Li Zefan <[email protected]>Cc Tejun Heo <[email protected]>Cc Johannes Weiner <[email protected]>Reviewed-by Quentin Perret <[email protected]>Signed-off-by Will Deacon <[email protected]>--- kernel/cgroup/cpuset.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)kernel/cgroup/cpuset.c +0
-0
kernel/cgroup/cpuset.c void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask) · line 3306
Quoted 10 lines
kernel/cgroup/cpuset.c +4
-2@@ -3299,9 +3299,11 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)void cpuset_cpus_allowed_fallback (struct task_struct *tsk) { +if (!is_in_v2_mode ()) +return ;/* select_fallback_rq will try harder */ +rcu_read_lock (); -do_set_cpus_allowed (tsk,is_in_v2_mode () ? -task_cs (tsk)->cpus_allowed : cpu_possible_mask); +do_set_cpus_allowed (tsk,task_cs (tsk)->cpus_allowed);
Why is it safe to return that for cpuset v2? task_cs(tsk)->cpus_allowed is the original user configured settings of the cpuset.cpus; which could have empty intersection with task_cpu_possible_mask(), no?
do_set_cpus_allowed() will call set_cpus_allowed_common() which will end up copying the mask as-is.
So unless I missed something there's a risk a 32bit task ends up having a 64bit only cpu_mask when using cpuset v2.
Thanks
--
Qais Yousef
Quoted 6 lines
kernel/cgroup/cpuset.c +0
-0rcu_read_unlock ();/* Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Peter Zijlstra
maintainerRe: [PATCH v5 07/15] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
Quoted 24 lines · Qais Yousef
Quoted 23 lines · Will Deacon
If the scheduler cannot find an allowed CPU for a task,
cpuset_cpus_allowed_fallback()will widen the affinity tocpu_possible_maskif cgroup v1 is in use.In preparation for allowing architectures to provide their own fallback mask, just return early if we're not using cgroup v2 and allow
select_fallback_rq()to figure out the mask by itself.Cc Li Zefan <[email protected]>Cc Tejun Heo <[email protected]>Cc Johannes Weiner <[email protected]>Reviewed-by Quentin Perret <[email protected]>Signed-off-by Will Deacon <[email protected]>--- kernel/cgroup/cpuset.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)kernel/cgroup/cpuset.c +0
-0@@ -3299,9 +3299,11 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
Quoted 11 lines
kernel/cgroup/cpuset.c void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask) · line 3306
Quoted 9 lines
kernel/cgroup/cpuset.c +4
-2void cpuset_cpus_allowed_fallback (struct task_struct *tsk) { +if (!is_in_v2_mode ()) +return ;/* select_fallback_rq will try harder */ +rcu_read_lock (); -do_set_cpus_allowed (tsk,is_in_v2_mode () ? -task_cs (tsk)->cpus_allowed : cpu_possible_mask); +do_set_cpus_allowed (tsk,task_cs (tsk)->cpus_allowed);Why is it safe to return that for cpuset v2?
v1
Because in that case it does cpu_possible_mask, which, if you look at select_fallback_rq(), is exactly what happens when cpuset 'fails' to find a candidate.
Or at least, that's how I read the patch.
Will Deacon
maintainerRe: [PATCH v5 07/15] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
Quoted 37 lines · Peter Zijlstra
Quoted 24 lines · Qais Yousef
Quoted 23 lines · Will Deacon
If the scheduler cannot find an allowed CPU for a task,
cpuset_cpus_allowed_fallback()will widen the affinity tocpu_possible_maskif cgroup v1 is in use.In preparation for allowing architectures to provide their own fallback mask, just return early if we're not using cgroup v2 and allow
select_fallback_rq()to figure out the mask by itself.Cc Li Zefan <[email protected]>Cc Tejun Heo <[email protected]>Cc Johannes Weiner <[email protected]>Reviewed-by Quentin Perret <[email protected]>Signed-off-by Will Deacon <[email protected]>--- kernel/cgroup/cpuset.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)kernel/cgroup/cpuset.c +0
-0@@ -3299,9 +3299,11 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)Quoted 11 lines
kernel/cgroup/cpuset.c void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask) · line 3306
Quoted 9 lines
kernel/cgroup/cpuset.c +4
-2void cpuset_cpus_allowed_fallback (struct task_struct *tsk) { +if (!is_in_v2_mode ()) +return ;/* select_fallback_rq will try harder */ +rcu_read_lock (); -do_set_cpus_allowed (tsk,is_in_v2_mode () ? -task_cs (tsk)->cpus_allowed : cpu_possible_mask); +do_set_cpus_allowed (tsk,task_cs (tsk)->cpus_allowed);Why is it safe to return that for cpuset v2?
Quoted 7 lines
v1
Because in that case it does
cpu_possible_mask, which, if you look atselect_fallback_rq(), is exactly what happens when cpuset 'fails' to find a candidate.Or at least, that's how I read the patch.
I think Qais a point with v2 though: if task_cs(tsk)->cpus_allowed contains 64-bit-only CPUs, then we're in trouble here. I should be taking the intersection with the task_cpu_possible_mask() for the task.
Will
Re: [PATCH v5 07/15] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
In reply to Peter ZijlstraQuoted 37 lines · Peter Zijlstra
Quoted 24 lines · Qais Yousef
Quoted 23 lines · Will Deacon
If the scheduler cannot find an allowed CPU for a task,
cpuset_cpus_allowed_fallback()will widen the affinity tocpu_possible_maskif cgroup v1 is in use.In preparation for allowing architectures to provide their own fallback mask, just return early if we're not using cgroup v2 and allow
select_fallback_rq()to figure out the mask by itself.Cc Li Zefan <[email protected]>Cc Tejun Heo <[email protected]>Cc Johannes Weiner <[email protected]>Reviewed-by Quentin Perret <[email protected]>Signed-off-by Will Deacon <[email protected]>--- kernel/cgroup/cpuset.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)kernel/cgroup/cpuset.c +0
-0@@ -3299,9 +3299,11 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)Quoted 11 lines
kernel/cgroup/cpuset.c void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask) · line 3306
Quoted 9 lines
kernel/cgroup/cpuset.c +4
-2void cpuset_cpus_allowed_fallback (struct task_struct *tsk) { +if (!is_in_v2_mode ()) +return ;/* select_fallback_rq will try harder */ +rcu_read_lock (); -do_set_cpus_allowed (tsk,is_in_v2_mode () ? -task_cs (tsk)->cpus_allowed : cpu_possible_mask); +do_set_cpus_allowed (tsk,task_cs (tsk)->cpus_allowed);Why is it safe to return that for cpuset v2?
Quoted 7 lines
v1
Because in that case it does
cpu_possible_mask, which, if you look atselect_fallback_rq(), is exactly what happens when cpuset 'fails' to find a candidate.Or at least, that's how I read the patch.
Okay I can see that if v2 has effectively empty mask for the 32bit tasks, then we'll fallback to the 'possible' switch case where we set task_cpu_possible_mask().
But how about when task_cs(tsk)->cpus_allowed contains partially invalid cpus?
The search for a candidate cpu will return a correct dest_cpu, but the actual cpu_mask of the task will contain invalid cpus that could be picked up later, no? Shouldn't we
cpumask_and(mask, task_cs(tsk)->cpus_allowed, task_cpu_possible_mask())
to remove those invalid cpus?
Thanks
--
Qais Yousef
Will Deacon
maintainerPATCH v5 8/15 cpuset: Honour task_cpu_possible_mask() in guarantee_online_cpus()
In reply to Will DeaconAsymmetric systems may not offer the same level of userspace ISA support across all CPUs, meaning that some applications cannot be executed by some CPUs. As a concrete example, upcoming arm64 big.LITTLE designs do not feature support for 32-bit applications on both clusters.
Modify guarantee_online_cpus() to take task_cpu_possible_mask() into account when trying to find a suitable set of online CPUs for a given task. This will avoid passing an invalid mask to set_cpus_allowed_ptr() during ->attach() and will subsequently allow the cpuset hierarchy to be taken into account when forcefully overriding the affinity mask for a task which requires migration to a compatible CPU.
--- include/linux/cpuset.h | 3 ++- kernel/cgroup/cpuset.c | 33 +++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-)
include/linux/cpuset.h +2 -1
@@ -15,6 +15,7 @@#include <linux/cpumask.h> #include <linux/nodemask.h> #include <linux/mm.h> +#include <linux/mmu_context.h> #include <linux/jump_label.h> #ifdef CONFIG_CPUSETS
@@ -184,7 +185,7 @@ static inline void cpuset_read_unlock(void) { }static inline void cpuset_cpus_allowed (struct task_struct *p,struct cpumask *mask) { -cpumask_copy (mask, cpu_possible_mask); +cpumask_copy (mask,task_cpu_possible_mask (p)); }static inline void cpuset_cpus_allowed_fallback (struct task_struct *p)
kernel/cgroup/cpuset.c +19 -14
@@ -372,18 +372,26 @@ static inline bool is_in_v2_mode(void) }/* -* Return in pmask the portion of a cpusets's cpus_allowed that -* are online. If none are online, walk up the cpuset hierarchy -* until we find one that does have some online cpus. +* Return in pmask the portion of a task's cpusets's cpus_allowed that +* are online and are capable of running the task. If none are found, +* walk up the cpuset hierarchy until we find one that does have some +* appropriate cpus. * * One way or another, we guarantee to return some non-empty subset * of cpu_online_mask. * * Call with callback_lock or cpuset_mutex held. */ -static void guarantee_online_cpus (struct cpuset *cs,struct cpumask *pmask) +static void guarantee_online_cpus (struct task_struct *tsk, +struct cpumask *pmask) { -while (!cpumask_intersects (cs->effective_cpus, cpu_online_mask)) { +struct cpuset *cs =task_cs (tsk); +const struct cpumask *possible_mask =task_cpu_possible_mask (tsk); + +if (WARN_ON (!cpumask_and (pmask, possible_mask, cpu_online_mask))) +cpumask_copy (pmask, cpu_online_mask); + +while (!cpumask_intersects (cs->effective_cpus, pmask)) { cs =parent_cs (cs);if (unlikely (!cs)) {/*
@@ -393,11 +401,10 @@ static void guarantee_online_cpus(struct cpuset *cs, struct cpumask *pmask)* cpuset's effective_cpus is on its way to be * identical to cpu_online_mask. */ -cpumask_copy (pmask, cpu_online_mask);return ; } } -cpumask_and (pmask, cs->effective_cpus, cpu_online_mask); +cpumask_and (pmask, pmask, cs->effective_cpus); }/*
@@ -2176,15 +2183,13 @@ static void cpuset_attach(struct cgroup_taskset *tset)percpu_down_write (&cpuset_rwsem); -/* prepare for attach */ -if (cs == &top_cpuset) -cpumask_copy (cpus_attach, cpu_possible_mask); -else -guarantee_online_cpus (cs, cpus_attach); -guarantee_online_mems (cs, &cpuset_attach_nodemask_to);cgroup_taskset_for_each (task, css, tset) { +if (cs != &top_cpuset) +guarantee_online_cpus (task, cpus_attach); +else +cpumask_copy (cpus_attach,task_cpu_possible_mask (task));/* * can_attach beforehand should guarantee that this doesn't * fail. TODO: have a better way to handle failure here
@@ -3280,7 +3285,7 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)spin_lock_irqsave (&callback_lock, flags);rcu_read_lock (); -guarantee_online_cpus (task_cs (tsk), pmask); +guarantee_online_cpus (tsk, pmask);rcu_read_unlock ();spin_unlock_irqrestore (&callback_lock, flags); }
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Re: [PATCH v5 08/15] cpuset: Honour task_cpu_possible_mask() in guarantee_online_cpus()
Quoted 63 lines · Will Deacon
Asymmetric systems may not offer the same level of userspace ISA support across all CPUs, meaning that some applications cannot be executed by some CPUs. As a concrete example, upcoming arm64 big.LITTLE designs do not feature support for 32-bit applications on both clusters.
Modify
guarantee_online_cpus()to taketask_cpu_possible_mask()into account when trying to find a suitable set of online CPUs for a given task. This will avoid passing an invalid mask toset_cpus_allowed_ptr()during ->attach() and will subsequently allow the cpuset hierarchy to be taken into account when forcefully overriding the affinity mask for a task which requires migration to a compatible CPU.Cc Li Zefan <[email protected]>Cc Tejun Heo <[email protected]>Cc Johannes Weiner <[email protected]>Signed-off-by Will Deacon <[email protected]>--- include/linux/cpuset.h | 3 ++- kernel/cgroup/cpuset.c | 33 +++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-)include/linux/cpuset.h +2
-1@@ -15,6 +15,7 @@#include <linux/cpumask.h> #include <linux/nodemask.h> #include <linux/mm.h> +#include <linux/mmu_context.h> #include <linux/jump_label.h> #ifdef CONFIG_CPUSETS @@ -184,7 +185,7 @@ static inline void cpuset_read_unlock(void) { }static inline void cpuset_cpus_allowed (struct task_struct *p,struct cpumask *mask) { -cpumask_copy (mask, cpu_possible_mask); +cpumask_copy (mask,task_cpu_possible_mask (p)); }static inline void cpuset_cpus_allowed_fallback (struct task_struct *p)kernel/cgroup/cpuset.c +4
-3@@ -372,18 +372,26 @@ static inline bool is_in_v2_mode(void) }/* -* Return in pmask the portion of a cpusets's cpus_allowed that -* are online. If none are online, walk up the cpuset hierarchy -* until we find one that does have some online cpus. +* Return in pmask the portion of a task's cpusets's cpus_allowed that +* are online and are capable of running the task. If none are found, +* walk up the cpuset hierarchy until we find one that does have some +* appropriate cpus. * * One way or another, we guarantee to return some non-empty subset * of cpu_online_mask. * * Call with callback_lock or cpuset_mutex held.
kernel/cgroup/cpuset.c static inline bool is_in_v2_mode(void) · line 391
Quoted 10 lines
kernel/cgroup/cpuset.c +6
-2*/ -static void guarantee_online_cpus (struct cpuset *cs,struct cpumask *pmask) +static void guarantee_online_cpus (struct task_struct *tsk, +struct cpumask *pmask) { -while (!cpumask_intersects (cs->effective_cpus, cpu_online_mask)) { +struct cpuset *cs =task_cs (tsk); +const struct cpumask *possible_mask =task_cpu_possible_mask (tsk); + +if (WARN_ON (!cpumask_and (pmask, possible_mask, cpu_online_mask)))
IIUC, this represents the case when there is no online CPU that can run this task. In this situation guarantee_online_cpus() will return an online CPU which can't run the task (because we ignore possible_mask). I don't think this can be considered a valid fallback path. However I think patch [13/15] ensures that we never end up in this situation by disallowing to offline the last 32-bit capable CPU. If that's true then maybe the patches can be reordered so that [13/15] comes before this one and this condition can be treated as a bug here?
Quoted 51 lines
kernel/cgroup/cpuset.c +9
-10+cpumask_copy (pmask, cpu_online_mask); + +while (!cpumask_intersects (cs->effective_cpus, pmask)) { cs =parent_cs (cs);if (unlikely (!cs)) {/* @@ -393,11 +401,10 @@ static void guarantee_online_cpus(struct cpuset *cs, struct cpumask *pmask)* cpuset's effective_cpus is on its way to be * identical to cpu_online_mask. */ -cpumask_copy (pmask, cpu_online_mask);return ; } } -cpumask_and (pmask, cs->effective_cpus, cpu_online_mask); +cpumask_and (pmask, pmask, cs->effective_cpus); }/* @@ -2176,15 +2183,13 @@ static void cpuset_attach(struct cgroup_taskset *tset)percpu_down_write (&cpuset_rwsem); -/* prepare for attach */ -if (cs == &top_cpuset) -cpumask_copy (cpus_attach, cpu_possible_mask); -else -guarantee_online_cpus (cs, cpus_attach); -guarantee_online_mems (cs, &cpuset_attach_nodemask_to);cgroup_taskset_for_each (task, css, tset) { +if (cs != &top_cpuset) +guarantee_online_cpus (task, cpus_attach); +else +cpumask_copy (cpus_attach,task_cpu_possible_mask (task));/* * can_attach beforehand should guarantee that this doesn't * fail. TODO: have a better way to handle failure here @@ -3280,7 +3285,7 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)spin_lock_irqsave (&callback_lock, flags);rcu_read_lock (); -guarantee_online_cpus (task_cs (tsk), pmask); +guarantee_online_cpus (tsk, pmask);rcu_read_unlock ();spin_unlock_irqrestore (&callback_lock, flags); } --2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 9/15 sched: Reject CPU affinity changes based on task_cpu_possible_mask()
In reply to Will DeaconReject explicit requests to change the affinity mask of a task via set_cpus_allowed_ptr() if the requested mask is not a subset of the mask returned by task_cpu_possible_mask(). This ensures that the 'cpus_mask' for a given task cannot contain CPUs which are incapable of executing it, except in cases where the affinity is forced.
--- kernel/sched/core.c | 4 ++++ 1 file changed, 4 insertions(+)
kernel/sched/core.c +4 -0
@@ -1875,6 +1875,7 @@ static int __set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask, bool check) {const struct cpumask *cpu_valid_mask = cpu_active_mask; +const struct cpumask *cpu_allowed_mask =task_cpu_possible_mask (p);unsigned int dest_cpu;struct rq_flags rf;struct rq *rq;
@@ -1888,6 +1889,9 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,* Kernel threads are allowed on online && !active CPUs */ cpu_valid_mask = cpu_online_mask; + }else if (!cpumask_subset (new_mask, cpu_allowed_mask)) { + ret = -EINVAL ; + goto out; }/*
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 10/15 sched: Introduce force_compatible_cpus_allowed_ptr() to limit CPU affinity
In reply to Will DeaconAsymmetric systems may not offer the same level of userspace ISA support across all CPUs, meaning that some applications cannot be executed by some CPUs. As a concrete example, upcoming arm64 big.LITTLE designs do not feature support for 32-bit applications on both clusters.
Although userspace can carefully manage the affinity masks for such tasks, one place where it is particularly problematic is execve() because the CPU on which the execve() is occurring may be incompatible with the new application image. In such a situation, it is desirable to restrict the affinity mask of the task and ensure that the new image is entered on a compatible CPU. From userspace's point of view, this looks the same as if the incompatible CPUs have been hotplugged off in the task's affinity mask.
In preparation for restricting the affinity mask for compat tasks on arm64 systems without uniform support for 32-bit applications, introduce force_compatible_cpus_allowed_ptr(), which restricts the affinity mask for a task to contain only compatible CPUs.
--- include/linux/sched.h | 1 + kernel/sched/core.c | 100 +++++++++++++++++++++++++++++++++++------- 2 files changed, 86 insertions(+), 15 deletions(-)
include/linux/sched.h +1 -0
@@ -1653,6 +1653,7 @@ extern int task_can_attach(struct task_struct *p, const struct cpumask *cs_cpus_#ifdef CONFIG_SMP extern void do_set_cpus_allowed (struct task_struct *p,const struct cpumask *new_mask);extern int set_cpus_allowed_ptr (struct task_struct *p,const struct cpumask *new_mask); +extern void force_compatible_cpus_allowed_ptr (struct task_struct *p);#else static inline void do_set_cpus_allowed (struct task_struct *p,const struct cpumask *new_mask) {
kernel/sched/core.c +85 -15
137 lines not on this page · raw message
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Re: [PATCH v5 10/15] sched: Introduce force_compatible_cpus_allowed_ptr() to limit CPU affinity
Just a couple minor nits.
Quoted 45 lines · Will Deacon
Asymmetric systems may not offer the same level of userspace ISA support across all CPUs, meaning that some applications cannot be executed by some CPUs. As a concrete example, upcoming arm64 big.LITTLE designs do not feature support for 32-bit applications on both clusters.
Although userspace can carefully manage the affinity masks for such tasks, one place where it is particularly problematic is
execve()because the CPU on which theexecve()is occurring may be incompatible with the new application image. In such a situation, it is desirable to restrict the affinity mask of the task and ensure that the new image is entered on a compatible CPU. From userspace's point of view, this looks the same as if the incompatible CPUs have been hotplugged off in the task's affinity mask.In preparation for restricting the affinity mask for compat tasks on arm64 systems without uniform support for 32-bit applications, introduce
force_compatible_cpus_allowed_ptr(), which restricts the affinity mask for a task to contain only compatible CPUs.Reviewed-by Quentin Perret <[email protected]>Signed-off-by Will Deacon <[email protected]>--- include/linux/sched.h | 1 + kernel/sched/core.c | 100 +++++++++++++++++++++++++++++++++++------- 2 files changed, 86 insertions(+), 15 deletions(-)include/linux/sched.h +1
-0@@ -1653,6 +1653,7 @@ extern int task_can_attach(struct task_struct *p, const struct cpumask *cs_cpus_#ifdef CONFIG_SMP extern void do_set_cpus_allowed (struct task_struct *p,const struct cpumask *new_mask);extern int set_cpus_allowed_ptr (struct task_struct *p,const struct cpumask *new_mask); +extern void force_compatible_cpus_allowed_ptr (struct task_struct *p);#else static inline void do_set_cpus_allowed (struct task_struct *p,const struct cpumask *new_mask) {kernel/sched/core.c +0
-0@@ -1863,25 +1863,19 @@ void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask) }
kernel/sched/core.c void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask) · line 1866
Quoted 10 lines
kernel/sched/core.c +1
-7/* -* Change a given task's CPU affinity. Migrate the thread to a -* proper CPU and schedule it away if the CPU it's executing on -* is removed from the allowed bitmask. -* -* NOTE: the caller must have a valid reference to the task, the -* task must not exit() & deallocate itself prematurely. The -* call is not atomic; no spinlocks may be held. +* Called with both p->pi_lock and rq->lock held; drops both before returning.
Maybe annotate with __releases()?
Quoted 84 lines
kernel/sched/core.c +43
-8*/ -static int __set_cpus_allowed_ptr (struct task_struct *p, -const struct cpumask *new_mask,bool check) +static int __set_cpus_allowed_ptr_locked (struct task_struct *p, +const struct cpumask *new_mask, +bool check, +struct rq *rq, +struct rq_flags *rf) {const struct cpumask *cpu_valid_mask = cpu_active_mask;const struct cpumask *cpu_allowed_mask =task_cpu_possible_mask (p);unsigned int dest_cpu; -struct rq_flags rf; -struct rq *rq;int ret =0 ; - rq =task_rq_lock (p, &rf);update_rq_clock (rq);if (p->flags &PF_KTHREAD ) {@@ -1936,7 +1930,7 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,if (task_running (rq, p) || p->state ==TASK_WAKING ) {struct migration_arg arg = { p, dest_cpu };/* Need help from migration thread: drop lock and wait. */ -task_rq_unlock (rq, p, &rf); +task_rq_unlock (rq, p, rf);stop_one_cpu (cpu_of (rq), migration_cpu_stop, &arg);return 0 ; }else if (task_on_rq_queued (p)) {@@ -1944,20 +1938,96 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,* OK, since we're going to drop the lock immediately * afterwards anyway. */ - rq =move_queued_task (rq, &rf, p, dest_cpu); + rq =move_queued_task (rq, rf, p, dest_cpu); } out: -task_rq_unlock (rq, p, &rf); +task_rq_unlock (rq, p, rf);return ret; } +/* +* Change a given task's CPU affinity. Migrate the thread to a +* proper CPU and schedule it away if the CPU it's executing on +* is removed from the allowed bitmask. +* +* NOTE: the caller must have a valid reference to the task, the +* task must not exit() & deallocate itself prematurely. The +* call is not atomic; no spinlocks may be held. +*/ +static int __set_cpus_allowed_ptr (struct task_struct *p, +const struct cpumask *new_mask,bool check) +{ +struct rq_flags rf; +struct rq *rq; + + rq =task_rq_lock (p, &rf); +return __set_cpus_allowed_ptr_locked (p, new_mask, check, rq, &rf); +} +int set_cpus_allowed_ptr (struct task_struct *p,const struct cpumask *new_mask) {return __set_cpus_allowed_ptr (p, new_mask, false); }EXPORT_SYMBOL_GPL (set_cpus_allowed_ptr); +/* +* Change a given task's CPU affinity to the intersection of its current +* affinity mask and @subset_mask, writing the resulting mask to @new_mask. +* If the resulting mask is empty, leave the affinity unchanged and return +* -EINVAL. +*/ +static int restrict_cpus_allowed_ptr (struct task_struct *p, +struct cpumask *new_mask, +const struct cpumask *subset_mask) +{ +struct rq_flags rf; +struct rq *rq; + + rq =task_rq_lock (p, &rf); +if (!cpumask_and (new_mask, &p->cpus_mask, subset_mask)) { +task_rq_unlock (rq, p, &rf);
kernel/sched/core.c static int __set_cpus_allowed_ptr(struct task_struct *p, · line 1999
Quoted 10 lines
kernel/sched/core.c +10
-0+return -EINVAL ; + } + +return __set_cpus_allowed_ptr_locked (p, new_mask, false, rq, &rf); +} + +/* +* Restrict a given task's CPU affinity so that it is a subset of +* task_cpu_possible_mask(). If the resulting mask is empty, we warn and +* walk up the cpuset hierarchy until we find a suitable mask.
Technically you also have a case when new_mask allocation fails, in which case task_cpu_possible_mask() is forced instead. Maybe add a clarifying comment at "goto out_set_mask" ?
Quoted 37 lines
kernel/sched/core.c +31
-1+*/ +void force_compatible_cpus_allowed_ptr (struct task_struct *p) +{ +cpumask_var_t new_mask; +const struct cpumask *override_mask =task_cpu_possible_mask (p); + +if (!alloc_cpumask_var (&new_mask,GFP_KERNEL )) + goto out_set_mask; + +if (!restrict_cpus_allowed_ptr (p, new_mask, override_mask)) + goto out_free_mask; + +/* +* We failed to find a valid subset of the affinity mask for the +* task, so override it based on its cpuset hierarchy. +*/ +cpuset_cpus_allowed (p, new_mask); + override_mask = new_mask; + +out_set_mask: +if (printk_ratelimit ()) { +printk_deferred ("Overriding affinity for process %d (%s) to CPUs %*pbl\n" , +task_pid_nr (p), p->comm, +cpumask_pr_args (override_mask)); + } + +set_cpus_allowed_ptr (p, override_mask); +out_free_mask: +free_cpumask_var (new_mask); +} +void set_task_cpu (struct task_struct *p,unsigned int new_cpu) {#ifdef CONFIG_SCHED_DEBUG --2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 11/15 arm64: Implement task_cpu_possible_mask()
In reply to Will DeaconProvide an implementation of task_cpu_possible_mask() so that we can prevent 64-bit-only cores being added to the 'cpus_mask' for compat tasks on systems with mismatched 32-bit support at EL0,
--- arch/arm64/include/asm/mmu_context.h | 13 +++++++++++++ 1 file changed, 13 insertions(+)
arch/arm64/include/asm/mmu_context.h +13 -0
@@ -251,6 +251,19 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,#define deactivate_mm (tsk,mm) do { } while (0)#define activate_mm (prev,next) switch_mm(prev, next, current) +static inline const struct cpumask * +task_cpu_possible_mask (struct task_struct *p) +{ +if (!static_branch_unlikely (&arm64_mismatched_32bit_el0)) +return cpu_possible_mask; + +if (!is_compat_thread (task_thread_info (p))) +return cpu_possible_mask; + +return system_32bit_el0_cpumask (); +} +#define task_cpu_possible_mask task_cpu_possible_mask +void verify_cpu_asid_bits (void );void post_ttbr_update_workaround (void );
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 12/15 arm64: exec: Adjust affinity for compat tasks with mismatched 32-bit EL0
In reply to Will DeaconWhen exec'ing a 32-bit task on a system with mismatched support for 32-bit EL0, try to ensure that it starts life on a CPU that can actually run it.
--- arch/arm64/kernel/process.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
arch/arm64/kernel/process.c +13 -1
@@ -633,8 +633,20 @@ void arch_setup_new_exec(void)if (is_compat_task ()) { mmflags =MMCF_AARCH32 ; -if (static_branch_unlikely (&arm64_mismatched_32bit_el0)) + +/* +* Restrict the CPU affinity mask for a 32-bit task so that +* it contains only 32-bit-capable CPUs. +* +* From the perspective of the task, this looks similar to +* what would happen if the 64-bit-only CPUs were hot-unplugged +* at the point of execve(), although we try a bit harder to +* honour the cpuset hierarchy. +*/ +if (static_branch_unlikely (&arm64_mismatched_32bit_el0)) { +force_compatible_cpus_allowed_ptr (current);set_tsk_thread_flag (current,TIF_NOTIFY_RESUME ); + } } current->mm->context.flags = mmflags;
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 13/15 arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system
In reply to Will DeaconIf we want to support 32-bit applications, then when we identify a CPU with mismatched 32-bit EL0 support we must ensure that we will always have an active 32-bit CPU available to us from then on. This is important for the scheduler, because is_cpu_allowed() will be constrained to 32-bit CPUs for compat tasks and forced migration due to a hotplug event will hang if no 32-bit CPUs are available.
On detecting a mismatch, prevent offlining of either the mismatching CPU if it is 32-bit capable, or find the first active 32-bit capable CPU otherwise.
--- arch/arm64/kernel/cpufeature.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
arch/arm64/kernel/cpufeature.c +18 -0
@@ -1237,6 +1237,8 @@ has_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope)static int enable_mismatched_32bit_el0 (unsigned int cpu) { +static int lucky_winner =-1 ; +struct cpuinfo_arm64 *info = &per_cpu (cpu_data, cpu);bool cpu_32bit =id_aa64pfr0_32bit_el0 (info->reg_id_aa64pfr0);
@@ -1245,6 +1247,22 @@ static int enable_mismatched_32bit_el0(unsigned int cpu)static_branch_enable_cpuslocked (&arm64_mismatched_32bit_el0); } +if (cpumask_test_cpu (0 , cpu_32bit_el0_mask) == cpu_32bit) +return 0 ; + +if (lucky_winner >=0 ) +return 0 ; + +/* +* We've detected a mismatch. We need to keep one of our CPUs with +* 32-bit EL0 online so that is_cpu_allowed() doesn't end up rejecting +* every CPU in the system for a 32-bit task. +*/ + lucky_winner = cpu_32bit ? cpu :cpumask_any_and (cpu_32bit_el0_mask, + cpu_active_mask); +get_cpu_device (lucky_winner)->offline_disabled = true; +pr_info ("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n" , + cpu, lucky_winner);return 0 ; }
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 14/15 arm64: Hook up cmdline parameter to allow mismatched 32-bit EL0
In reply to Will DeaconAllow systems with mismatched 32-bit support at EL0 to run 32-bit applications based on a new kernel parameter.
--- Documentation/admin-guide/kernel-parameters.txt | 8 ++++++++ arch/arm64/kernel/cpufeature.c | 7 +++++++ 2 files changed, 15 insertions(+)
Documentation/admin-guide/kernel-parameters.txt +8 -0
@@ -289,6 +289,14 @@ do not want to use tracing_snapshot_alloc() as it needs to be done where GFP_KERNEL allocations are allowed. + allow_mismatched_32bit_el0 [ARM64] + Allow execve() of 32-bit applications and setting of the + PER_LINUX32 personality on systems where only a strict + subset of the CPUs support 32-bit EL0. When this + parameter is present, the set of CPUs supporting 32-bit + EL0 is indicated by /sys/devices/system/cpu/aarch32_el0 + and hot-unplug operations may be restricted. + amd_iommu= [HW,X86-64] Pass parameters to the AMD IOMMU driver in the system. Possible values are:
arch/arm64/kernel/cpufeature.c +7 -0
@@ -1291,6 +1291,13 @@ const struct cpumask *system_32bit_el0_cpumask(void)return cpu_possible_mask; } +static int __init parse_32bit_el0_param (char *str) +{ + allow_mismatched_32bit_el0 = true; +return 0 ; +} +early_param ("allow_mismatched_32bit_el0" , parse_32bit_el0_param); +static ssize_t aarch32_el0_show (struct device *dev,struct device_attribute *attr,char *buf) {
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Will Deacon
maintainerPATCH v5 15/15 arm64: Remove logic to kill 32-bit tasks on 64-bit-only cores
In reply to Will DeaconThe scheduler now knows enough about these braindead systems to place 32-bit tasks accordingly, so throw out the safety checks and allow the ret-to-user path to avoid do_notify_resume() if there is nothing to do.
--- arch/arm64/kernel/process.c | 14 +------------- arch/arm64/kernel/signal.c | 26 -------------------------- 2 files changed, 1 insertion(+), 39 deletions(-)
arch/arm64/kernel/process.c +1 -13
@@ -541,15 +541,6 @@ static void erratum_1418040_thread_switch(struct task_struct *prev,write_sysreg (val, cntkctl_el1); } -static void compat_thread_switch (struct task_struct *next) -{ -if (!is_compat_thread (task_thread_info (next))) -return ; - -if (static_branch_unlikely (&arm64_mismatched_32bit_el0)) -set_tsk_thread_flag (next,TIF_NOTIFY_RESUME ); -} -/* * Thread switching. */
@@ -566,7 +557,6 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,uao_thread_switch (next);ssbs_thread_switch (next);erratum_1418040_thread_switch (prev, next); -compat_thread_switch (next);/* * Complete any pending TLB or cache maintenance on this CPU in case
@@ -643,10 +633,8 @@ void arch_setup_new_exec(void)* at the point of execve(), although we try a bit harder to * honour the cpuset hierarchy. */ -if (static_branch_unlikely (&arm64_mismatched_32bit_el0)) { +if (static_branch_unlikely (&arm64_mismatched_32bit_el0))force_compatible_cpus_allowed_ptr (current); -set_tsk_thread_flag (current,TIF_NOTIFY_RESUME ); - } } current->mm->context.flags = mmflags;
arch/arm64/kernel/signal.c +0 -26
@@ -911,19 +911,6 @@ static void do_signal(struct pt_regs *regs)restore_saved_sigmask (); } -static bool cpu_affinity_invalid (struct pt_regs *regs) -{ -if (!compat_user_mode (regs)) -return false; - -/* -* We're preemptible, but a reschedule will cause us to check the -* affinity again. -*/ -return !cpumask_test_cpu (raw_smp_processor_id (), -system_32bit_el0_cpumask ()); -} - asmlinkage void do_notify_resume(struct pt_regs *regs,unsigned long thread_flags) {
@@ -961,19 +948,6 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,if (thread_flags & _TIF_NOTIFY_RESUME) {tracehook_notify_resume (regs);rseq_handle_notify_resume (NULL , regs); - -/* -* If we reschedule after checking the affinity -* then we must ensure that TIF_NOTIFY_RESUME -* is set so that we check the affinity again. -* Since tracehook_notify_resume() clears the -* flag, ensure that the compiler doesn't move -* it after the affinity check. -*/ -barrier (); - -if (cpu_affinity_invalid (regs)) -force_sig (SIGKILL ); }if (thread_flags & _TIF_FOREIGN_FPSTATE)
Signature · 1 line
2.29.2.576.ga3fc446d84-goog
Peter Zijlstra
maintainerQuoted 15 lines · Will Deacon
The aim of this series is to allow 32-bit ARM applications to run on arm64 SoCs where not all of the CPUs support the 32-bit instruction set. Unfortunately, such SoCs are real and will continue to be productised over the next few years at least. I can assure you that I'm not just doing this for fun.
Changes in v5 include:
* Teach
cpuset_cpus_allowed()abouttask_cpu_possible_mask()so that we can avoid returning incompatible CPUs for a given task. This means thatsched_setaffinity()can be used with larger masks (like the online mask) from userspace and also allows us to take into account the cpuset hierarchy when forcefully overriding the affinity for a task onexecve().
Quoted 6 lines
* Honour
task_cpu_possible_mask()when attaching a task to a cpuset, so that the resulting affinity mask does not contain any incompatible CPUs (since it would be rejected byset_cpus_allowed_ptr()otherwise).* Moved overriding of the affinity mask into the scheduler core rather than munge affinity masks directly in the architecture backend.
Hurmph... so if I can still read, this thing will auto truncate the affinity mask to something that only contains compatible CPUs, right?
Assuming our system has 8 CPUs (0xFF), half of which are 32bit capable (0x0F), then, when our native task (with affinity 0x3c) does a fork()+execve() of a 32bit thingy the resulting task has 0x0c.
If that in turn does fork()+execve() of a native task, it will retain the trucated affinity mask (0x0c), instead of returning to the wider mask (0x3c).
IOW, any (accidental or otherwise) trip through a 32bit helper, will destroy user state (the affinity mask: 0x3c).
Should we perhaps split task_struct::cpus_mask, one to keep an original copy of the user state, and one to be an effective cpumask for the task? That way, the moment a task constricts or widens it's task_cpu_possible_mask() we can re-compute the effective mask without loss of information.
Will Deacon
maintainerHi Peter,
Cheers for taking a look.
Quoted 30 lines · Peter Zijlstra
Quoted 15 lines · Will Deacon
The aim of this series is to allow 32-bit ARM applications to run on arm64 SoCs where not all of the CPUs support the 32-bit instruction set. Unfortunately, such SoCs are real and will continue to be productised over the next few years at least. I can assure you that I'm not just doing this for fun.
Changes in v5 include:
* Teach
cpuset_cpus_allowed()abouttask_cpu_possible_mask()so that we can avoid returning incompatible CPUs for a given task. This means thatsched_setaffinity()can be used with larger masks (like the online mask) from userspace and also allows us to take into account the cpuset hierarchy when forcefully overriding the affinity for a task onexecve().Quoted 6 lines
* Honour
task_cpu_possible_mask()when attaching a task to a cpuset, so that the resulting affinity mask does not contain any incompatible CPUs (since it would be rejected byset_cpus_allowed_ptr()otherwise).* Moved overriding of the affinity mask into the scheduler core rather than munge affinity masks directly in the architecture backend.
Hurmph... so if I can still read, this thing will auto truncate the affinity mask to something that only contains compatible CPUs, right?
Assuming our system has 8 CPUs (
0xFF), half of which are 32bit capable (0x0F), then, when our native task (with affinity0x3c) does a fork()+execve() of a 32bit thingy the resulting task has0x0c.
Quoted 6 lines
If that in turn does fork()+execve() of a native task, it will retain the trucated affinity mask (
0x0c), instead of returning to the wider mask (0x3c).IOW, any (accidental or otherwise) trip through a 32bit helper, will destroy user state (the affinity mask:
0x3c).
Yes, that's correct, and I agree that it's a rough edge. If you're happy with the idea of adding an extra mask to make this work, then I can start hacking that up (although I doubt I'll get something out before the new year at this point).
Quoted 5 lines
Should we perhaps split
task_struct::cpus_mask, one to keep an original copy of the user state, and one to be an effective cpumask for the task? That way, the moment a task constricts or widens it'stask_cpu_possible_mask()we can re-compute the effective mask without loss of information.
Hmm, we might already have most of the pieces in place for this (modulo the extra field), since cpuset_cpus_allowed() provides the limiting mask now so this might be relatively straightforward.
Famous last words...
Will
Peter Zijlstra
maintainerQuoted 8 lines · Will Deacon
Quoted 2 lines · Peter Zijlstra
IOW, any (accidental or otherwise) trip through a 32bit helper, will destroy user state (the affinity mask:
0x3c).Yes, that's correct, and I agree that it's a rough edge. If you're happy with the idea of adding an extra mask to make this work, then I can start hacking that up
Yeah, I'm afraid we'll have to, this asymmetric muck is only going to get worse from here on.
Anyway, I think we can avoid adding another cpumask_t to task_struct and do with a cpumask_t * insteads. After all, for 'normal' tasks, the task_cpu_possible_mask() will be cpu_possible_mask and we don't need to carry anything extra.
Only once we hit one of these assymetric ISA things, can the task allocate the additional cpumask and retain the full mask.
Quoted 2 lines
(although I doubt I'll get something out before the new year at this point).
Yeah, we're all about to shut down for a bit, I'll not be looking at email for 2 weeks either, so even if you send it, I might not see it until the next year.
Hi Will
Quoted 45 lines · Will Deacon
Hi all,
Christmas has come early: it's time for version five of these patches which have previously appeared here:
v1: https://lore.kernel.org/r/[email protected]
v2: https://lore.kernel.org/r/[email protected]
v3: https://lore.kernel.org/r/[email protected]
v4: https://lore.kernel.org/r/[email protected]and which started life as a reimplementation of some patches from Qais:
https://lore.kernel.org/r/[email protected]
There's also now a nice writeup on LWN:
https://lwn.net/Articles/838339/
and rumours of a feature film are doing the rounds.
[subscriber-only, but if you're reading this then you should really
subscribe.]The aim of this series is to allow 32-bit ARM applications to run on arm64 SoCs where not all of the CPUs support the 32-bit instruction set. Unfortunately, such SoCs are real and will continue to be productised over the next few years at least. I can assure you that I'm not just doing this for fun.
Changes in v5 include:
* Teach
cpuset_cpus_allowed()abouttask_cpu_possible_mask()so that we can avoid returning incompatible CPUs for a given task. This means thatsched_setaffinity()can be used with larger masks (like the online mask) from userspace and also allows us to take into account the cpuset hierarchy when forcefully overriding the affinity for a task onexecve().* Honour
task_cpu_possible_mask()when attaching a task to a cpuset, so that the resulting affinity mask does not contain any incompatible CPUs (since it would be rejected byset_cpus_allowed_ptr()otherwise).* Moved overriding of the affinity mask into the scheduler core rather than munge affinity masks directly in the architecture backend.
Quoted 6 lines
* Extended comments and documentation.
* Some renaming and cosmetic changes.
I'm pretty happy with this now, although it still needs review and will require rebasing to play nicely with the SCA changes in -next.
I still have concerns about the cpuset v1 handling. Specifically:
1. Attaching a 32bit task to 64bit only cpuset is allowed.
I think the right behavior here is to prevent that as the intersection will appear as offline cpus for the 32bit tasks. So it shouldn't be allowed to move there.
2. Modifying cpuset.cpus could result with empty set for 32bit tasks.
It is a variation of the above, it's just the cpuset transforms into 64bit only after we attach.
I think the right behavior here is to move the 32bit tasks to the nearest ancestor like we do when all cpuset.cpus are hotplugged out.
We could too return an error if the new set will result an empty set for the 32bit tasks. In a similar manner to how it fails if you write a cpu that is offline.
3. If a 64bit task belongs to 64bit-only-cpuset execs a 32bit binary, the 32 tasks will inherit the cgroup setting.
Like above, we should move this to the nearest ancestor.
I was worried if in a hierarchy the parent cpuset.cpus is modified such that the childs no longer have a valid cpu for 32bit tasks. But I checked for v1 and this isn't a problem. You'll get an error if you try to change it in a way that ends up with an empty cpuset.
I played with v2, and the model allows tasks to remain attached even if cpus are hotplugged, or cpusets.cpus is modified in such a way we end up with an empty cpuset. So I think breaking the affinity of the cpuset for v2 is okay.
To simplify the problem for v1, we could say that asym ISA tasks can only live in the root cpuset for v1. This will simplify the solution too since we will only need to ensure that these tasks are moved to the root group on exec and block any future move to anything else. Of course this dictates that such systems must use cpuset v2 if they care. Not a terrible restriction IMO.
I hacked a patch to fix the exec scenario and it was easy to do. I just need to block clone3 (cgroup_post_fork()) and task_can_attach() from allowing these tasks from moving anywhere else.
Thanks
--
Qais Yousef
Will Deacon
maintainerHi Qais,
Quoted 24 lines · Qais Yousef
Quoted 16 lines · Will Deacon
Changes in v5 include:
* Teach
cpuset_cpus_allowed()abouttask_cpu_possible_mask()so that we can avoid returning incompatible CPUs for a given task. This means thatsched_setaffinity()can be used with larger masks (like the online mask) from userspace and also allows us to take into account the cpuset hierarchy when forcefully overriding the affinity for a task onexecve().* Honour
task_cpu_possible_mask()when attaching a task to a cpuset, so that the resulting affinity mask does not contain any incompatible CPUs (since it would be rejected byset_cpus_allowed_ptr()otherwise).* Moved overriding of the affinity mask into the scheduler core rather than munge affinity masks directly in the architecture backend.
Quoted 6 lines
* Extended comments and documentation.
* Some renaming and cosmetic changes.
I'm pretty happy with this now, although it still needs review and will require rebasing to play nicely with the SCA changes in -next.
Quoted 7 lines
I still have concerns about the cpuset v1 handling. Specifically:
1. Attaching a 32bit task to 64bit only cpuset is allowed.
I think the right behavior here is to prevent that as the intersection will appear as offline cpus for the 32bit tasks. So it shouldn't be allowed to move there.
Suren or Quantin can correct me if I'm wrong I'm here, but I think Android relies on this working so it's not an option for us to prevent the attach. I also don't think it really achieves much, since as you point out, the same problem exists in other cases such as execve() of a 32-bit binary, or hotplugging off all 32-bit CPUs within a mixed cpuset. Allowing the attach and immediately reparenting would probably be better, but see below.
Quoted 8 lines
2. Modifying cpuset.cpus could result with empty set for 32bit tasks.
It is a variation of the above, it's just the cpuset transforms into 64bit only after we attach.
I think the right behavior here is to move the 32bit tasks to the nearest ancestor like we do when all cpuset.cpus are hotplugged out.
Quoted 8 lines
We could too return an error if the new set will result an empty set for the 32bit tasks. In a similar manner to how it fails if you write a cpu that is offline.
3. If a 64bit task belongs to 64bit-only-cpuset execs a 32bit binary, the 32 tasks will inherit the cgroup setting.
Like above, we should move this to the nearest ancestor.
I considered this when I was writing the patches, but the reality is that by allowing 32-bit tasks to attach to a 64-bit only cpuset (which is required by Android), we have no choice but to expose a new ABI to userspace. This is all gated behind a command-line option, so I think that's fine, but then why not just have the same behaviour as cgroup v2? I don't see the point in creating two new ABIs (for cgroup v1 and v2 respectively) if we don't need to. If it was _identical_ to the hotplug case, then we would surely just follow the existing behaviour, but it's really quite different in this situation because the cpuset is not empty.
One thing we should definitely do though is add this to the documentation for the command-line option.
Quoted 5 lines
To simplify the problem for v1, we could say that asym ISA tasks can only live in the root cpuset for v1. This will simplify the solution too since we will only need to ensure that these tasks are moved to the root group on exec and block any future move to anything else. Of course this dictates that such systems must use cpuset v2 if they care. Not a terrible restriction IMO.
Sadly, I think Android is still on cgroup v1 for cpuset, but Suren will know better the status of cgroup v2 for cpusets. If it's just around the corner, then maybe we could simplify things here. Suren?
Will
Quoted 29 lines · Will Deacon
Hi Qais,
Quoted 26 lines · Qais Yousef
Quoted 16 lines · Will Deacon
Changes in v5 include:
* Teach
cpuset_cpus_allowed()abouttask_cpu_possible_mask()so that we can avoid returning incompatible CPUs for a given task. This means thatsched_setaffinity()can be used with larger masks (like the online mask) from userspace and also allows us to take into account the cpuset hierarchy when forcefully overriding the affinity for a task onexecve().* Honour
task_cpu_possible_mask()when attaching a task to a cpuset, so that the resulting affinity mask does not contain any incompatible CPUs (since it would be rejected byset_cpus_allowed_ptr()otherwise).* Moved overriding of the affinity mask into the scheduler core rather than munge affinity masks directly in the architecture backend.
Quoted 6 lines
* Extended comments and documentation.
* Some renaming and cosmetic changes.
I'm pretty happy with this now, although it still needs review and will require rebasing to play nicely with the SCA changes in -next.
I still have concerns about the cpuset v1 handling. Specifically:
Quoted 8 lines
Quoted 5 lines
1. Attaching a 32bit task to 64bit only cpuset is allowed.
I think the right behavior here is to prevent that as the intersection will appear as offline cpus for the 32bit tasks. So it shouldn't be allowed to move there.
Suren or Quantin can correct me if I'm wrong I'm here, but I think Android relies on this working so it's not an option for us to prevent the attach.
I don't think so. It's just a matter who handles the error. ie: kernel fix it up silently and effectively make the cpuset a NOP since we don't respect the affinity of the cpuset, or user space pick the next best thing. Since this could return an error anyway, likely user space already handles this.
Quoted 4 lines
I also don't think it really achieves much, since as you point out, the same problem exists in other cases such as
execve()of a 32-bit binary, or hotplugging off all 32-bit CPUs within a mixed cpuset. Allowing the attach and immediately reparenting would probably be better, but see below.
I am just wary that we're introducing a generic asymmetric ISA support, so my concerns have been related to making sure the behavior is sane generally. When this gets merged, I can bet more 'fun' hardware will appear all over the place. We're opening the flood gates I'm afraid :p
Quoted 15 lines
Quoted 15 lines
2. Modifying cpuset.cpus could result with empty set for 32bit tasks.
It is a variation of the above, it's just the cpuset transforms into 64bit only after we attach.
I think the right behavior here is to move the 32bit tasks to the nearest ancestor like we do when all cpuset.cpus are hotplugged out.
We could too return an error if the new set will result an empty set for the 32bit tasks. In a similar manner to how it fails if you write a cpu that is offline.
3. If a 64bit task belongs to 64bit-only-cpuset execs a 32bit binary, the 32 tasks will inherit the cgroup setting.
Quoted 8 lines
Quoted 1 line
Like above, we should move this to the nearest ancestor.
I considered this when I was writing the patches, but the reality is that by allowing 32-bit tasks to attach to a 64-bit only cpuset (which is required by Android), we have no choice but to expose a new ABI to userspace. This is all gated behind a command-line option, so I think that's fine, but then why not just have the same behaviour as cgroup v2? I don't see the point in creating two new ABIs (for cgroup v1 and v2 respectively) if we don't need
Ultimately it's up to Tejun and Peter I guess. I thought we need to preserve the v1 behavior for the new class of tasks. I won't object to the new ABI myself. Maybe we just need to make the commit messages and cgroup-v1 documentation reflect that explicitly.
Quoted 3 lines
to. If it was _identical_ to the hotplug case, then we would surely just follow the existing behaviour, but it's really quite different in this situation because the cpuset is not empty.
It is actually effectively empty for those tasks. But I see that one could look at it from two different angles.
Quoted 2 lines
One thing we should definitely do though is add this to the documentation for the command-line option.
+1
By the way, should the command-line option be renamed to something more generic? This has already grown beyond just enabling the support for one isolated case. No strong opinion, just a suggestion.
Thanks
--
Qais Yousef
Quoted 37 lines · Qais Yousef
Quoted 35 lines · Will Deacon
Hi Qais,
Quoted 31 lines · Qais Yousef
Quoted 16 lines · Will Deacon
Changes in v5 include:
* Teach
cpuset_cpus_allowed()abouttask_cpu_possible_mask()so that we can avoid returning incompatible CPUs for a given task. This means thatsched_setaffinity()can be used with larger masks (like the online mask) from userspace and also allows us to take into account the cpuset hierarchy when forcefully overriding the affinity for a task onexecve().* Honour
task_cpu_possible_mask()when attaching a task to a cpuset, so that the resulting affinity mask does not contain any incompatible CPUs (since it would be rejected byset_cpus_allowed_ptr()otherwise).* Moved overriding of the affinity mask into the scheduler core rather than munge affinity masks directly in the architecture backend.
Quoted 6 lines
* Extended comments and documentation.
* Some renaming and cosmetic changes.
I'm pretty happy with this now, although it still needs review and will require rebasing to play nicely with the SCA changes in -next.
I still have concerns about the cpuset v1 handling. Specifically:
1. Attaching a 32bit task to 64bit only cpuset is allowed.
I think the right behavior here is to prevent that as the intersection will appear as offline cpus for the 32bit tasks. So it shouldn't be allowed to move there.
Quoted 7 lines
Quoted 2 lines
Suren or Quantin can correct me if I'm wrong I'm here, but I think Android relies on this working so it's not an option for us to prevent the attach.
I don't think so. It's just a matter who handles the error. ie: kernel fix it up silently and effectively make the cpuset a NOP since we don't respect the affinity of the cpuset, or user space pick the next best thing. Since this could return an error anyway, likely user space already handles this.
Moving a 32bit task around the hierarchy when it lost the last 32bit capable CPU in its affinity mask would not work for Android. We move the tasks in the hierarchy only when they change their role (background/foreground/etc) and does not expect the tasks to migrate by themselves. I think the current approach of adjusting affinity without migration while not ideal is much better. Consistency with cgroup v2 is a big plus as well.
We do plan on moving cpuset controller to cgroup v2 but the transition is slow, so my guess is that we will stick to it for another Android release.
Quoted 59 lines
Quoted 4 lines
I also don't think it really achieves much, since as you point out, the same problem exists in other cases such as
execve()of a 32-bit binary, or hotplugging off all 32-bit CPUs within a mixed cpuset. Allowing the attach and immediately reparenting would probably be better, but see below.I am just wary that we're introducing a generic asymmetric ISA support, so my concerns have been related to making sure the behavior is sane generally. When this gets merged, I can bet more 'fun' hardware will appear all over the place. We're opening the flood gates I'm afraid :p
Quoted 15 lines
Quoted 15 lines
2. Modifying cpuset.cpus could result with empty set for 32bit tasks.
It is a variation of the above, it's just the cpuset transforms into 64bit only after we attach.
I think the right behavior here is to move the 32bit tasks to the nearest ancestor like we do when all cpuset.cpus are hotplugged out.
We could too return an error if the new set will result an empty set for the 32bit tasks. In a similar manner to how it fails if you write a cpu that is offline.
3. If a 64bit task belongs to 64bit-only-cpuset execs a 32bit binary, the 32 tasks will inherit the cgroup setting.
Quoted 8 lines
Quoted 1 line
Like above, we should move this to the nearest ancestor.
I considered this when I was writing the patches, but the reality is that by allowing 32-bit tasks to attach to a 64-bit only cpuset (which is required by Android), we have no choice but to expose a new ABI to userspace. This is all gated behind a command-line option, so I think that's fine, but then why not just have the same behaviour as cgroup v2? I don't see the point in creating two new ABIs (for cgroup v1 and v2 respectively) if we don't need
Ultimately it's up to Tejun and Peter I guess. I thought we need to preserve the v1 behavior for the new class of tasks. I won't object to the new ABI myself. Maybe we just need to make the commit messages and cgroup-v1 documentation reflect that explicitly.
Quoted 3 lines
to. If it was _identical_ to the hotplug case, then we would surely just follow the existing behaviour, but it's really quite different in this situation because the cpuset is not empty.
It is actually effectively empty for those tasks. But I see that one could look at it from two different angles.
Quoted 2 lines
One thing we should definitely do though is add this to the documentation for the command-line option.
+1
By the way, should the command-line option be renamed to something more generic? This has already grown beyond just enabling the support for one isolated case. No strong opinion, just a suggestion.
Thanks
--
Qais Yousef