linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems
@ 2021-06-02 16:47 Will Deacon
  2021-06-02 16:47 ` [PATCH v8 01/19] arm64: cpuinfo: Split AArch32 registers out into a separate struct Will Deacon
                   ` (18 more replies)
  0 siblings, 19 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

Hi again folks,

Here is v8 of the asymmetric 32-bit support patches that I previously
posted here:

  v1: https://lore.kernel.org/r/20201027215118.27003-1-will@kernel.org
  v2: https://lore.kernel.org/r/20201109213023.15092-1-will@kernel.org
  v3: https://lore.kernel.org/r/20201113093720.21106-1-will@kernel.org
  v4: https://lore.kernel.org/r/20201124155039.13804-1-will@kernel.org
  v5: https://lore.kernel.org/r/20201208132835.6151-1-will@kernel.org
  v6: https://lore.kernel.org/r/20210518094725.7701-1-will@kernel.org
  v7: https://lore.kernel.org/r/20210525151432.16875-1-will@kernel.org

There was also a nice LWN writeup in case you've forgotten what this is
about:

	https://lwn.net/Articles/838339/

Changes since v7 include:

  * Dropped the scheduler migration fix, as Valentin has kindly fixed
    this separately in -tip:

    https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/commit/?h=sched/core&id=475ea6c60279e9f2ddf7e4cf2648cd8ae0608361

  * Dropped the freezer/ttwu changes, as Peter is rewriting that:

    https://lore.kernel.org/r/YLYZv4v68OnAlx+3@hirez.programming.kicks-ass.net

  * Reworded documentation wrt KVM behaviour [maz]

  * Tidied up control flow in cpuset_cpus_allowed_fallback() [peterz]

  * Fixed interaction with migrate_disable() [peterz]

  * Don't ignore allocation failure in restrict_cpus_allowed_ptr() [peterz]

  * Reordered the patches to put arm64 prep work first

  * Added some more acks/reviewed-by tags

Cheers,

Will

Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Morten Rasmussen <morten.rasmussen@arm.com>
Cc: Qais Yousef <qais.yousef@arm.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Quentin Perret <qperret@google.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Juri Lelli <juri.lelli@redhat.com>
Cc: Vincent Guittot <vincent.guittot@linaro.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Dietmar Eggemann <dietmar.eggemann@arm.com>
Cc: Daniel Bristot de Oliveira <bristot@redhat.com>
Cc: Valentin Schneider <valentin.schneider@arm.com>
Cc: kernel-team@android.com

--->8

Will Deacon (19):
  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
  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 task_struct::user_cpus_ptr to track requested
    affinity
  sched: Split the guts of sched_setaffinity() into a helper function
  sched: Allow task CPU affinity to be restricted on asymmetric systems
  sched: Introduce task_cpus_dl_admissible() to check proposed 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: Advertise CPUs capable of running 32-bit applications in sysfs
  arm64: Hook up cmdline parameter to allow mismatched 32-bit EL0
  arm64: Remove logic to kill 32-bit tasks on 64-bit-only cores
  Documentation: arm64: describe asymmetric 32-bit support

 .../ABI/testing/sysfs-devices-system-cpu      |   9 +
 .../admin-guide/kernel-parameters.txt         |  11 +
 Documentation/arm64/asymmetric-32bit.rst      | 155 ++++++++
 Documentation/arm64/index.rst                 |   1 +
 arch/arm64/include/asm/cpu.h                  |  44 +--
 arch/arm64/include/asm/cpufeature.h           |   8 +-
 arch/arm64/include/asm/elf.h                  |   6 +-
 arch/arm64/include/asm/mmu_context.h          |  13 +
 arch/arm64/kernel/cpufeature.c                | 227 +++++++++---
 arch/arm64/kernel/cpuinfo.c                   |  53 +--
 arch/arm64/kernel/process.c                   |  44 ++-
 arch/arm64/kvm/arm.c                          |  11 +-
 arch/arm64/tools/cpucaps                      |   3 +-
 include/linux/cpuset.h                        |   3 +-
 include/linux/mmu_context.h                   |  11 +
 include/linux/sched.h                         |  21 ++
 init/init_task.c                              |   1 +
 kernel/cgroup/cpuset.c                        |  49 ++-
 kernel/fork.c                                 |   2 +
 kernel/sched/core.c                           | 334 ++++++++++++++----
 kernel/sched/sched.h                          |   1 +
 21 files changed, 819 insertions(+), 188 deletions(-)
 create mode 100644 Documentation/arm64/asymmetric-32bit.rst

-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v8 01/19] arm64: cpuinfo: Split AArch32 registers out into a separate struct
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-03 12:38   ` Mark Rutland
  2021-06-02 16:47 ` [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support Will Deacon
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

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.

Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 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(-)

diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h
index 7faae6ff3ab4..f4e01aa0f442 100644
--- a/arch/arm64/include/asm/cpu.h
+++ b/arch/arm64/include/asm/cpu.h
@@ -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;
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index efed2830d141..a4db25cd7122 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -863,6 +863,31 @@ static void __init init_cpu_hwcaps_indirect_list(void)
 
 static void __init setup_boot_cpu_capabilities(void);
 
+static void __init init_32bit_cpu_features(struct cpuinfo_32bit *info)
+{
+	init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
+	init_cpu_ftr_reg(SYS_ID_DFR1_EL1, info->reg_id_dfr1);
+	init_cpu_ftr_reg(SYS_ID_ISAR0_EL1, info->reg_id_isar0);
+	init_cpu_ftr_reg(SYS_ID_ISAR1_EL1, info->reg_id_isar1);
+	init_cpu_ftr_reg(SYS_ID_ISAR2_EL1, info->reg_id_isar2);
+	init_cpu_ftr_reg(SYS_ID_ISAR3_EL1, info->reg_id_isar3);
+	init_cpu_ftr_reg(SYS_ID_ISAR4_EL1, info->reg_id_isar4);
+	init_cpu_ftr_reg(SYS_ID_ISAR5_EL1, info->reg_id_isar5);
+	init_cpu_ftr_reg(SYS_ID_ISAR6_EL1, info->reg_id_isar6);
+	init_cpu_ftr_reg(SYS_ID_MMFR0_EL1, info->reg_id_mmfr0);
+	init_cpu_ftr_reg(SYS_ID_MMFR1_EL1, info->reg_id_mmfr1);
+	init_cpu_ftr_reg(SYS_ID_MMFR2_EL1, info->reg_id_mmfr2);
+	init_cpu_ftr_reg(SYS_ID_MMFR3_EL1, info->reg_id_mmfr3);
+	init_cpu_ftr_reg(SYS_ID_MMFR4_EL1, info->reg_id_mmfr4);
+	init_cpu_ftr_reg(SYS_ID_MMFR5_EL1, info->reg_id_mmfr5);
+	init_cpu_ftr_reg(SYS_ID_PFR0_EL1, info->reg_id_pfr0);
+	init_cpu_ftr_reg(SYS_ID_PFR1_EL1, info->reg_id_pfr1);
+	init_cpu_ftr_reg(SYS_ID_PFR2_EL1, info->reg_id_pfr2);
+	init_cpu_ftr_reg(SYS_MVFR0_EL1, info->reg_mvfr0);
+	init_cpu_ftr_reg(SYS_MVFR1_EL1, info->reg_mvfr1);
+	init_cpu_ftr_reg(SYS_MVFR2_EL1, info->reg_mvfr2);
+}
+
 void __init init_cpu_features(struct cpuinfo_arm64 *info)
 {
 	/* Before we start using the tables, make sure it is sorted */
@@ -882,29 +907,8 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)
 	init_cpu_ftr_reg(SYS_ID_AA64PFR1_EL1, info->reg_id_aa64pfr1);
 	init_cpu_ftr_reg(SYS_ID_AA64ZFR0_EL1, info->reg_id_aa64zfr0);
 
-	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
-		init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
-		init_cpu_ftr_reg(SYS_ID_DFR1_EL1, info->reg_id_dfr1);
-		init_cpu_ftr_reg(SYS_ID_ISAR0_EL1, info->reg_id_isar0);
-		init_cpu_ftr_reg(SYS_ID_ISAR1_EL1, info->reg_id_isar1);
-		init_cpu_ftr_reg(SYS_ID_ISAR2_EL1, info->reg_id_isar2);
-		init_cpu_ftr_reg(SYS_ID_ISAR3_EL1, info->reg_id_isar3);
-		init_cpu_ftr_reg(SYS_ID_ISAR4_EL1, info->reg_id_isar4);
-		init_cpu_ftr_reg(SYS_ID_ISAR5_EL1, info->reg_id_isar5);
-		init_cpu_ftr_reg(SYS_ID_ISAR6_EL1, info->reg_id_isar6);
-		init_cpu_ftr_reg(SYS_ID_MMFR0_EL1, info->reg_id_mmfr0);
-		init_cpu_ftr_reg(SYS_ID_MMFR1_EL1, info->reg_id_mmfr1);
-		init_cpu_ftr_reg(SYS_ID_MMFR2_EL1, info->reg_id_mmfr2);
-		init_cpu_ftr_reg(SYS_ID_MMFR3_EL1, info->reg_id_mmfr3);
-		init_cpu_ftr_reg(SYS_ID_MMFR4_EL1, info->reg_id_mmfr4);
-		init_cpu_ftr_reg(SYS_ID_MMFR5_EL1, info->reg_id_mmfr5);
-		init_cpu_ftr_reg(SYS_ID_PFR0_EL1, info->reg_id_pfr0);
-		init_cpu_ftr_reg(SYS_ID_PFR1_EL1, info->reg_id_pfr1);
-		init_cpu_ftr_reg(SYS_ID_PFR2_EL1, info->reg_id_pfr2);
-		init_cpu_ftr_reg(SYS_MVFR0_EL1, info->reg_mvfr0);
-		init_cpu_ftr_reg(SYS_MVFR1_EL1, info->reg_mvfr1);
-		init_cpu_ftr_reg(SYS_MVFR2_EL1, info->reg_mvfr2);
-	}
+	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
+		init_32bit_cpu_features(&info->aarch32);
 
 	if (id_aa64pfr0_sve(info->reg_id_aa64pfr0)) {
 		init_cpu_ftr_reg(SYS_ZCR_EL1, info->reg_zcr);
@@ -975,20 +979,12 @@ static void relax_cpu_ftr_reg(u32 sys_id, int field)
 	WARN_ON(!ftrp->width);
 }
 
-static int update_32bit_cpu_features(int cpu, struct cpuinfo_arm64 *info,
-				     struct cpuinfo_arm64 *boot)
+static int update_32bit_cpu_features(int cpu, struct cpuinfo_32bit *info,
+				     struct cpuinfo_32bit *boot)
 {
 	int taint = 0;
 	u64 pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
 
-	/*
-	 * If we don't have AArch32 at all then skip the checks entirely
-	 * as the register values may be UNKNOWN and we're not going to be
-	 * using them for anything.
-	 */
-	if (!id_aa64pfr0_32bit_el0(pfr0))
-		return taint;
-
 	/*
 	 * If we don't have AArch32 at EL1, then relax the strictness of
 	 * EL1-dependent register fields to avoid spurious sanity check fails.
@@ -1135,10 +1131,17 @@ void update_cpu_features(int cpu,
 	}
 
 	/*
+	 * If we don't have AArch32 at all then skip the checks entirely
+	 * as the register values may be UNKNOWN and we're not going to be
+	 * using them for anything.
+	 *
 	 * This relies on a sanitised view of the AArch64 ID registers
 	 * (e.g. SYS_ID_AA64PFR0_EL1), so we call it last.
 	 */
-	taint |= update_32bit_cpu_features(cpu, info, boot);
+	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
+		taint |= update_32bit_cpu_features(cpu, &info->aarch32,
+						   &boot->aarch32);
+	}
 
 	/*
 	 * Mismatched CPU features are a recipe for disaster. Don't even
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 51fcf99d5351..264c119a6cae 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -344,6 +344,32 @@ static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
 	pr_info("Detected %s I-cache on CPU%d\n", icache_policy_str[l1ip], cpu);
 }
 
+static void __cpuinfo_store_cpu_32bit(struct cpuinfo_32bit *info)
+{
+	info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
+	info->reg_id_dfr1 = read_cpuid(ID_DFR1_EL1);
+	info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
+	info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
+	info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
+	info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
+	info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
+	info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
+	info->reg_id_isar6 = read_cpuid(ID_ISAR6_EL1);
+	info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
+	info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
+	info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
+	info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
+	info->reg_id_mmfr4 = read_cpuid(ID_MMFR4_EL1);
+	info->reg_id_mmfr5 = read_cpuid(ID_MMFR5_EL1);
+	info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
+	info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
+	info->reg_id_pfr2 = read_cpuid(ID_PFR2_EL1);
+
+	info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
+	info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
+	info->reg_mvfr2 = read_cpuid(MVFR2_EL1);
+}
+
 static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
 {
 	info->reg_cntfrq = arch_timer_get_cntfrq();
@@ -371,31 +397,8 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
 	info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);
 	info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1);
 
-	/* Update the 32bit ID registers only if AArch32 is implemented */
-	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
-		info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
-		info->reg_id_dfr1 = read_cpuid(ID_DFR1_EL1);
-		info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
-		info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
-		info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
-		info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
-		info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
-		info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
-		info->reg_id_isar6 = read_cpuid(ID_ISAR6_EL1);
-		info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
-		info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
-		info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
-		info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
-		info->reg_id_mmfr4 = read_cpuid(ID_MMFR4_EL1);
-		info->reg_id_mmfr5 = read_cpuid(ID_MMFR5_EL1);
-		info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
-		info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
-		info->reg_id_pfr2 = read_cpuid(ID_PFR2_EL1);
-
-		info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
-		info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
-		info->reg_mvfr2 = read_cpuid(MVFR2_EL1);
-	}
+	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
+		__cpuinfo_store_cpu_32bit(&info->aarch32);
 
 	if (IS_ENABLED(CONFIG_ARM64_SVE) &&
 	    id_aa64pfr0_sve(info->reg_id_aa64pfr0))
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
  2021-06-02 16:47 ` [PATCH v8 01/19] arm64: cpuinfo: Split AArch32 registers out into a separate struct Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-03 12:37   ` Mark Rutland
  2021-06-02 16:47 ` [PATCH v8 03/19] KVM: arm64: Kill 32-bit vCPUs on systems with mismatched " Will Deacon
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

When 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.

Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 arch/arm64/include/asm/cpufeature.h |   8 +-
 arch/arm64/kernel/cpufeature.c      | 114 ++++++++++++++++++++++++----
 arch/arm64/tools/cpucaps            |   3 +-
 3 files changed, 110 insertions(+), 15 deletions(-)

diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 338840c00e8e..603bf4160cd6 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -630,9 +630,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)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index a4db25cd7122..4194a47de62d 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -107,6 +107,24 @@ DECLARE_BITMAP(boot_capabilities, ARM64_NPATCHABLE);
 bool arm64_use_ng_mappings = false;
 EXPORT_SYMBOL(arm64_use_ng_mappings);
 
+/*
+ * Permit PER_LINUX32 and execve() of 32-bit binaries even if not all CPUs
+ * support it?
+ */
+static bool __read_mostly allow_mismatched_32bit_el0;
+
+/*
+ * Static branch enabled only if allow_mismatched_32bit_el0 is set and we have
+ * seen at least one CPU capable of 32-bit EL0.
+ */
+DEFINE_STATIC_KEY_FALSE(arm64_mismatched_32bit_el0);
+
+/*
+ * Mask of CPUs supporting 32-bit EL0.
+ * Only valid if arm64_mismatched_32bit_el0 is enabled.
+ */
+static cpumask_var_t cpu_32bit_el0_mask __cpumask_var_read_mostly;
+
 /*
  * Flag to indicate if we have computed the system wide
  * capabilities based on the boot time active CPUs. This
@@ -767,7 +785,7 @@ static void __init sort_ftr_regs(void)
  * Any bits that are not covered by an arm64_ftr_bits entry are considered
  * RES0 for the system-wide value, and must strictly match.
  */
-static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
+static void init_cpu_ftr_reg(u32 sys_reg, u64 new)
 {
 	u64 val = 0;
 	u64 strict_mask = ~0x0ULL;
@@ -863,7 +881,7 @@ static void __init init_cpu_hwcaps_indirect_list(void)
 
 static void __init setup_boot_cpu_capabilities(void);
 
-static void __init init_32bit_cpu_features(struct cpuinfo_32bit *info)
+static void init_32bit_cpu_features(struct cpuinfo_32bit *info)
 {
 	init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
 	init_cpu_ftr_reg(SYS_ID_DFR1_EL1, info->reg_id_dfr1);
@@ -979,6 +997,22 @@ static void relax_cpu_ftr_reg(u32 sys_id, int field)
 	WARN_ON(!ftrp->width);
 }
 
+static void update_mismatched_32bit_el0_cpu_features(struct cpuinfo_arm64 *info,
+						     struct cpuinfo_arm64 *boot)
+{
+	static bool boot_cpu_32bit_regs_overridden = false;
+
+	if (!allow_mismatched_32bit_el0 || boot_cpu_32bit_regs_overridden)
+		return;
+
+	if (id_aa64pfr0_32bit_el0(boot->reg_id_aa64pfr0))
+		return;
+
+	boot->aarch32 = info->aarch32;
+	init_32bit_cpu_features(&boot->aarch32);
+	boot_cpu_32bit_regs_overridden = true;
+}
+
 static int update_32bit_cpu_features(int cpu, struct cpuinfo_32bit *info,
 				     struct cpuinfo_32bit *boot)
 {
@@ -1139,6 +1173,7 @@ void update_cpu_features(int cpu,
 	 * (e.g. SYS_ID_AA64PFR0_EL1), so we call it last.
 	 */
 	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
+		update_mismatched_32bit_el0_cpu_features(info, boot);
 		taint |= update_32bit_cpu_features(cpu, &info->aarch32,
 						   &boot->aarch32);
 	}
@@ -1251,6 +1286,28 @@ has_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope)
 	return feature_matches(val, entry);
 }
 
+const struct cpumask *system_32bit_el0_cpumask(void)
+{
+	if (!system_supports_32bit_el0())
+		return cpu_none_mask;
+
+	if (static_branch_unlikely(&arm64_mismatched_32bit_el0))
+		return cpu_32bit_el0_mask;
+
+	return cpu_possible_mask;
+}
+
+static bool has_32bit_el0(const struct arm64_cpu_capabilities *entry, int scope)
+{
+	if (!has_cpuid_feature(entry, scope))
+		return allow_mismatched_32bit_el0;
+
+	if (scope == SCOPE_SYSTEM)
+		pr_info("detected: 32-bit EL0 Support\n");
+
+	return true;
+}
+
 static bool has_useable_gicv3_cpuif(const struct arm64_cpu_capabilities *entry, int scope)
 {
 	bool has_sre;
@@ -1869,10 +1926,9 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
 		.cpu_enable = cpu_copy_el2regs,
 	},
 	{
-		.desc = "32-bit EL0 Support",
-		.capability = ARM64_HAS_32BIT_EL0,
+		.capability = ARM64_HAS_32BIT_EL0_DO_NOT_USE,
 		.type = ARM64_CPUCAP_SYSTEM_FEATURE,
-		.matches = has_cpuid_feature,
+		.matches = has_32bit_el0,
 		.sys_reg = SYS_ID_AA64PFR0_EL1,
 		.sign = FTR_UNSIGNED,
 		.field_pos = ID_AA64PFR0_EL0_SHIFT,
@@ -2381,7 +2437,7 @@ static const struct arm64_cpu_capabilities compat_elf_hwcaps[] = {
 	{},
 };
 
-static void __init cap_set_elf_hwcap(const struct arm64_cpu_capabilities *cap)
+static void cap_set_elf_hwcap(const struct arm64_cpu_capabilities *cap)
 {
 	switch (cap->hwcap_type) {
 	case CAP_HWCAP:
@@ -2426,7 +2482,7 @@ static bool cpus_have_elf_hwcap(const struct arm64_cpu_capabilities *cap)
 	return rc;
 }
 
-static void __init setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps)
+static void setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps)
 {
 	/* We support emulation of accesses to CPU ID feature registers */
 	cpu_set_named_feature(CPUID);
@@ -2601,7 +2657,7 @@ static void check_early_cpu_features(void)
 }
 
 static void
-verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps)
+__verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps)
 {
 
 	for (; caps->matches; caps++)
@@ -2612,6 +2668,14 @@ verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps)
 		}
 }
 
+static void verify_local_elf_hwcaps(void)
+{
+	__verify_local_elf_hwcaps(arm64_elf_hwcaps);
+
+	if (id_aa64pfr0_32bit_el0(read_cpuid(ID_AA64PFR0_EL1)))
+		__verify_local_elf_hwcaps(compat_elf_hwcaps);
+}
+
 static void verify_sve_features(void)
 {
 	u64 safe_zcr = read_sanitised_ftr_reg(SYS_ZCR_EL1);
@@ -2676,11 +2740,7 @@ static void verify_local_cpu_capabilities(void)
 	 * on all secondary CPUs.
 	 */
 	verify_local_cpu_caps(SCOPE_ALL & ~SCOPE_BOOT_CPU);
-
-	verify_local_elf_hwcaps(arm64_elf_hwcaps);
-
-	if (system_supports_32bit_el0())
-		verify_local_elf_hwcaps(compat_elf_hwcaps);
+	verify_local_elf_hwcaps();
 
 	if (system_supports_sve())
 		verify_sve_features();
@@ -2815,6 +2875,34 @@ void __init setup_cpu_features(void)
 			ARCH_DMA_MINALIGN);
 }
 
+static int enable_mismatched_32bit_el0(unsigned int cpu)
+{
+	struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
+	bool cpu_32bit = id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0);
+
+	if (cpu_32bit) {
+		cpumask_set_cpu(cpu, cpu_32bit_el0_mask);
+		static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0);
+		setup_elf_hwcaps(compat_elf_hwcaps);
+	}
+
+	return 0;
+}
+
+static int __init init_32bit_el0_mask(void)
+{
+	if (!allow_mismatched_32bit_el0)
+		return 0;
+
+	if (!zalloc_cpumask_var(&cpu_32bit_el0_mask, GFP_KERNEL))
+		return -ENOMEM;
+
+	return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+				 "arm64/mismatched_32bit_el0:online",
+				 enable_mismatched_32bit_el0, NULL);
+}
+subsys_initcall_sync(init_32bit_el0_mask);
+
 static void __maybe_unused cpu_enable_cnp(struct arm64_cpu_capabilities const *cap)
 {
 	cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 21fbdda7086e..49305c2e6dfd 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -3,7 +3,8 @@
 # Internal CPU capabilities constants, keep this list sorted
 
 BTI
-HAS_32BIT_EL0
+# Unreliable: use system_supports_32bit_el0() instead.
+HAS_32BIT_EL0_DO_NOT_USE
 HAS_32BIT_EL1
 HAS_ADDRESS_AUTH
 HAS_ADDRESS_AUTH_ARCH
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 03/19] KVM: arm64: Kill 32-bit vCPUs on systems with mismatched EL0 support
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
  2021-06-02 16:47 ` [PATCH v8 01/19] arm64: cpuinfo: Split AArch32 registers out into a separate struct Will Deacon
  2021-06-02 16:47 ` [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-02 16:47 ` [PATCH v8 04/19] arm64: Kill 32-bit applications scheduled on 64-bit-only CPUs Will Deacon
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

If a vCPU is caught running 32-bit code on a system with mismatched
support at EL0, then we should kill it.

Acked-by: Marc Zyngier <maz@kernel.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 arch/arm64/kvm/arm.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 1cb39c0803a4..5bdba97a7654 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -692,6 +692,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
@@ -875,7 +884,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
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 04/19] arm64: Kill 32-bit applications scheduled on 64-bit-only CPUs
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (2 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 03/19] KVM: arm64: Kill 32-bit vCPUs on systems with mismatched " Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-02 16:47 ` [PATCH v8 05/19] sched: Introduce task_cpu_possible_mask() to limit fallback rq selection Will Deacon
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

Scheduling 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.

Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 arch/arm64/kernel/process.c | 19 ++++++++++++++++++-
 arch/arm64/kernel/signal.c  | 26 ++++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index b4bb67f17a2c..f4a91bf1ce0c 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -527,6 +527,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);
+}
+
 static void update_sctlr_el1(u64 sctlr)
 {
 	/*
@@ -568,6 +577,7 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,
 	ssbs_thread_switch(next);
 	erratum_1418040_thread_switch(prev, next);
 	ptrauth_thread_switch_user(next);
+	compat_thread_switch(next);
 
 	/*
 	 * Complete any pending TLB or cache maintenance on this CPU in case
@@ -633,8 +643,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();
 	mte_thread_init_user();
 
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 6237486ff6bb..f8192f4ae0b8 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -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)
 {
@@ -938,6 +951,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)
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 05/19] sched: Introduce task_cpu_possible_mask() to limit fallback rq selection
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (3 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 04/19] arm64: Kill 32-bit applications scheduled on 64-bit-only CPUs Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-04 17:10   ` Valentin Schneider
  2021-06-02 16:47 ` [PATCH v8 06/19] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1 Will Deacon
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

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.

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.

Reviewed-by: Quentin Perret <qperret@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 include/linux/mmu_context.h | 11 +++++++++++
 kernel/sched/core.c         |  5 ++---
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/include/linux/mmu_context.h b/include/linux/mmu_context.h
index 03dee12d2b61..1a599ba3524f 100644
--- a/include/linux/mmu_context.h
+++ b/include/linux/mmu_context.h
@@ -14,4 +14,15 @@
 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
+# define task_cpu_possible(cpu, p)	true
+#else
+# define task_cpu_possible(cpu, p)	cpumask_test_cpu((cpu), task_cpu_possible_mask(p))
+#endif
+
 #endif
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 5226cc26a095..0c1b6f1a6c91 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1814,7 +1814,7 @@ static inline bool is_cpu_allowed(struct task_struct *p, int cpu)
 
 	/* Non kernel threads are not allowed during either online or offline. */
 	if (!(p->flags & PF_KTHREAD))
-		return cpu_active(cpu);
+		return cpu_active(cpu) && task_cpu_possible(cpu, p);
 
 	/* KTHREAD_IS_PER_CPU is always allowed. */
 	if (kthread_is_per_cpu(p))
@@ -2792,10 +2792,9 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
 			 *
 			 * More yuck to audit.
 			 */
-			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;
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 06/19] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (4 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 05/19] sched: Introduce task_cpu_possible_mask() to limit fallback rq selection Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-04 17:11   ` Valentin Schneider
  2021-06-02 16:47 ` [PATCH v8 07/19] cpuset: Honour task_cpu_possible_mask() in guarantee_online_cpus() Will Deacon
                   ` (12 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team,
	Li Zefan

If 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 either using cgroup v1 or we're using
cgroup v2 with a mask that contains invalid CPUs. This will allow
select_fallback_rq() to figure out the mask by itself.

Cc: Li Zefan <lizefan@huawei.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Reviewed-by: Quentin Perret <qperret@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 include/linux/cpuset.h | 1 +
 kernel/cgroup/cpuset.c | 8 ++++++--
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index 04c20de66afc..ed6ec677dd6b 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -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
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index a945504c0ae7..6ec7303d5b1f 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -3322,9 +3322,13 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
 
 void cpuset_cpus_allowed_fallback(struct task_struct *tsk)
 {
+	const struct cpumask *cs_mask;
+	const struct cpumask *possible_mask = task_cpu_possible_mask(tsk);
+
 	rcu_read_lock();
-	do_set_cpus_allowed(tsk, is_in_v2_mode() ?
-		task_cs(tsk)->cpus_allowed : cpu_possible_mask);
+	cs_mask = task_cs(tsk)->cpus_allowed;
+	if (is_in_v2_mode() && cpumask_subset(cs_mask, possible_mask))
+		do_set_cpus_allowed(tsk, cs_mask);
 	rcu_read_unlock();
 
 	/*
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 07/19] cpuset: Honour task_cpu_possible_mask() in guarantee_online_cpus()
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (5 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 06/19] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1 Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-04 17:11   ` Valentin Schneider
  2021-06-02 16:47 ` [PATCH v8 08/19] sched: Reject CPU affinity changes based on task_cpu_possible_mask() Will Deacon
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team,
	Li Zefan

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 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.

Cc: Li Zefan <lizefan@huawei.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Signed-off-by: Will Deacon <will@kernel.org>
---
 include/linux/cpuset.h |  2 +-
 kernel/cgroup/cpuset.c | 41 ++++++++++++++++++++++++++---------------
 2 files changed, 27 insertions(+), 16 deletions(-)

diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index ed6ec677dd6b..414a8e694413 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -185,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)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 6ec7303d5b1f..3b53da2f25e7 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -372,18 +372,29 @@ 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)) {
+	const struct cpumask *possible_mask = task_cpu_possible_mask(tsk);
+	struct cpuset *cs;
+
+	if (WARN_ON(!cpumask_and(pmask, possible_mask, cpu_online_mask)))
+		cpumask_copy(pmask, cpu_online_mask);
+
+	rcu_read_lock();
+	cs = task_cs(tsk);
+
+	while (!cpumask_intersects(cs->effective_cpus, pmask)) {
 		cs = parent_cs(cs);
 		if (unlikely(!cs)) {
 			/*
@@ -393,11 +404,13 @@ 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;
+			goto out_unlock;
 		}
 	}
-	cpumask_and(pmask, cs->effective_cpus, cpu_online_mask);
+	cpumask_and(pmask, pmask, cs->effective_cpus);
+
+out_unlock:
+	rcu_read_unlock();
 }
 
 /*
@@ -2199,15 +2212,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
@@ -3303,7 +3314,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.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 08/19] sched: Reject CPU affinity changes based on task_cpu_possible_mask()
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (6 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 07/19] cpuset: Honour task_cpu_possible_mask() in guarantee_online_cpus() Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-04 17:11   ` Valentin Schneider
  2021-06-02 16:47 ` [PATCH v8 09/19] sched: Introduce task_struct::user_cpus_ptr to track requested affinity Will Deacon
                   ` (10 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

Reject 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.

Reviewed-by: Quentin Perret <qperret@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 kernel/sched/core.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 0c1b6f1a6c91..b23c7f0ab31a 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2347,15 +2347,17 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
 				  u32 flags)
 {
 	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;
+	bool kthread = p->flags & PF_KTHREAD;
 
 	rq = task_rq_lock(p, &rf);
 	update_rq_clock(rq);
 
-	if (p->flags & PF_KTHREAD || is_migration_disabled(p)) {
+	if (kthread || is_migration_disabled(p)) {
 		/*
 		 * Kernel threads are allowed on online && !active CPUs,
 		 * however, during cpu-hot-unplug, even these might get pushed
@@ -2369,6 +2371,11 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
 		cpu_valid_mask = cpu_online_mask;
 	}
 
+	if (!kthread && !cpumask_subset(new_mask, cpu_allowed_mask)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
 	/*
 	 * Must re-check here, to close a race against __kthread_bind(),
 	 * sched_setaffinity() is not guaranteed to observe the flag.
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 09/19] sched: Introduce task_struct::user_cpus_ptr to track requested affinity
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (7 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 08/19] sched: Reject CPU affinity changes based on task_cpu_possible_mask() Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-04 17:12   ` Valentin Schneider
  2021-06-02 16:47 ` [PATCH v8 10/19] sched: Split the guts of sched_setaffinity() into a helper function Will Deacon
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

In preparation for saving and restoring the user-requested CPU affinity
mask of a task, add a new cpumask_t pointer to 'struct task_struct'.

If the pointer is non-NULL, then the mask is copied across fork() and
freed on task exit.

Signed-off-by: Will Deacon <will@kernel.org>
---
 include/linux/sched.h | 13 +++++++++++++
 init/init_task.c      |  1 +
 kernel/fork.c         |  2 ++
 kernel/sched/core.c   | 20 ++++++++++++++++++++
 4 files changed, 36 insertions(+)

diff --git a/include/linux/sched.h b/include/linux/sched.h
index d2c881384517..db32d4f7e5b3 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -730,6 +730,7 @@ struct task_struct {
 	unsigned int			policy;
 	int				nr_cpus_allowed;
 	const cpumask_t			*cpus_ptr;
+	cpumask_t			*user_cpus_ptr;
 	cpumask_t			cpus_mask;
 	void				*migration_pending;
 #ifdef CONFIG_SMP
@@ -1688,6 +1689,8 @@ 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 int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src, int node);
+extern void release_user_cpus_ptr(struct task_struct *p);
 #else
 static inline void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask)
 {
@@ -1698,6 +1701,16 @@ static inline int set_cpus_allowed_ptr(struct task_struct *p, const struct cpuma
 		return -EINVAL;
 	return 0;
 }
+static inline int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src, int node)
+{
+	if (src->user_cpus_ptr)
+		return -EINVAL;
+	return 0;
+}
+static inline void release_user_cpus_ptr(struct task_struct *p)
+{
+	WARN_ON(p->user_cpus_ptr);
+}
 #endif
 
 extern int yield_to(struct task_struct *p, bool preempt);
diff --git a/init/init_task.c b/init/init_task.c
index 8b08c2e19cbb..158c2b1689e1 100644
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -80,6 +80,7 @@ struct task_struct init_task
 	.normal_prio	= MAX_PRIO - 20,
 	.policy		= SCHED_NORMAL,
 	.cpus_ptr	= &init_task.cpus_mask,
+	.user_cpus_ptr	= NULL,
 	.cpus_mask	= CPU_MASK_ALL,
 	.nr_cpus_allowed= NR_CPUS,
 	.mm		= NULL,
diff --git a/kernel/fork.c b/kernel/fork.c
index dc06afd725cb..d3710e7f1686 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -446,6 +446,7 @@ void put_task_stack(struct task_struct *tsk)
 
 void free_task(struct task_struct *tsk)
 {
+	release_user_cpus_ptr(tsk);
 	scs_release(tsk);
 
 #ifndef CONFIG_THREAD_INFO_IN_TASK
@@ -918,6 +919,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
 #endif
 	if (orig->cpus_ptr == &orig->cpus_mask)
 		tsk->cpus_ptr = &tsk->cpus_mask;
+	dup_user_cpus_ptr(tsk, orig, node);
 
 	/*
 	 * One for the user space visible state that goes away when reaped.
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index b23c7f0ab31a..206e7bf95c79 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2126,6 +2126,26 @@ void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask)
 	__do_set_cpus_allowed(p, new_mask, 0);
 }
 
+int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src,
+		      int node)
+{
+	if (!src->user_cpus_ptr)
+		return 0;
+
+	dst->user_cpus_ptr = kmalloc_node(cpumask_size(), GFP_KERNEL, node);
+	if (!dst->user_cpus_ptr)
+		return -ENOMEM;
+
+	cpumask_copy(dst->user_cpus_ptr, src->user_cpus_ptr);
+	return 0;
+}
+
+void release_user_cpus_ptr(struct task_struct *p)
+{
+	kfree(p->user_cpus_ptr);
+	p->user_cpus_ptr = NULL;
+}
+
 /*
  * This function is wildly self concurrent; here be dragons.
  *
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 10/19] sched: Split the guts of sched_setaffinity() into a helper function
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (8 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 09/19] sched: Introduce task_struct::user_cpus_ptr to track requested affinity Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-04 17:12   ` Valentin Schneider
  2021-06-02 16:47 ` [PATCH v8 11/19] sched: Allow task CPU affinity to be restricted on asymmetric systems Will Deacon
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

In preparation for replaying user affinity requests using a saved mask,
split sched_setaffinity() up so that the initial task lookup and
security checks are only performed when the request is coming directly
from userspace.

Signed-off-by: Will Deacon <will@kernel.org>
---
 kernel/sched/core.c | 105 ++++++++++++++++++++++++--------------------
 1 file changed, 57 insertions(+), 48 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 206e7bf95c79..f210d0b06944 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -6788,53 +6788,22 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr,
 	return retval;
 }
 
-long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
+static int
+__sched_setaffinity(struct task_struct *p, const struct cpumask *mask)
 {
-	cpumask_var_t cpus_allowed, new_mask;
-	struct task_struct *p;
 	int retval;
+	cpumask_var_t cpus_allowed, new_mask;
 
-	rcu_read_lock();
-
-	p = find_process_by_pid(pid);
-	if (!p) {
-		rcu_read_unlock();
-		return -ESRCH;
-	}
-
-	/* Prevent p going away */
-	get_task_struct(p);
-	rcu_read_unlock();
+	if (!alloc_cpumask_var(&cpus_allowed, GFP_KERNEL))
+		return -ENOMEM;
 
-	if (p->flags & PF_NO_SETAFFINITY) {
-		retval = -EINVAL;
-		goto out_put_task;
-	}
-	if (!alloc_cpumask_var(&cpus_allowed, GFP_KERNEL)) {
-		retval = -ENOMEM;
-		goto out_put_task;
-	}
 	if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) {
 		retval = -ENOMEM;
 		goto out_free_cpus_allowed;
 	}
-	retval = -EPERM;
-	if (!check_same_owner(p)) {
-		rcu_read_lock();
-		if (!ns_capable(__task_cred(p)->user_ns, CAP_SYS_NICE)) {
-			rcu_read_unlock();
-			goto out_free_new_mask;
-		}
-		rcu_read_unlock();
-	}
-
-	retval = security_task_setscheduler(p);
-	if (retval)
-		goto out_free_new_mask;
-
 
 	cpuset_cpus_allowed(p, cpus_allowed);
-	cpumask_and(new_mask, in_mask, cpus_allowed);
+	cpumask_and(new_mask, mask, cpus_allowed);
 
 	/*
 	 * Since bandwidth control happens on root_domain basis,
@@ -6855,23 +6824,63 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
 #endif
 again:
 	retval = __set_cpus_allowed_ptr(p, new_mask, SCA_CHECK);
+	if (retval)
+		goto out_free_new_mask;
 
-	if (!retval) {
-		cpuset_cpus_allowed(p, cpus_allowed);
-		if (!cpumask_subset(new_mask, cpus_allowed)) {
-			/*
-			 * We must have raced with a concurrent cpuset
-			 * update. Just reset the cpus_allowed to the
-			 * cpuset's cpus_allowed
-			 */
-			cpumask_copy(new_mask, cpus_allowed);
-			goto again;
-		}
+	cpuset_cpus_allowed(p, cpus_allowed);
+	if (!cpumask_subset(new_mask, cpus_allowed)) {
+		/*
+		 * We must have raced with a concurrent cpuset update.
+		 * Just reset the cpumask to the cpuset's cpus_allowed.
+		 */
+		cpumask_copy(new_mask, cpus_allowed);
+		goto again;
 	}
+
 out_free_new_mask:
 	free_cpumask_var(new_mask);
 out_free_cpus_allowed:
 	free_cpumask_var(cpus_allowed);
+	return retval;
+}
+
+long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
+{
+	struct task_struct *p;
+	int retval;
+
+	rcu_read_lock();
+
+	p = find_process_by_pid(pid);
+	if (!p) {
+		rcu_read_unlock();
+		return -ESRCH;
+	}
+
+	/* Prevent p going away */
+	get_task_struct(p);
+	rcu_read_unlock();
+
+	if (p->flags & PF_NO_SETAFFINITY) {
+		retval = -EINVAL;
+		goto out_put_task;
+	}
+
+	if (!check_same_owner(p)) {
+		rcu_read_lock();
+		if (!ns_capable(__task_cred(p)->user_ns, CAP_SYS_NICE)) {
+			rcu_read_unlock();
+			retval = -EPERM;
+			goto out_put_task;
+		}
+		rcu_read_unlock();
+	}
+
+	retval = security_task_setscheduler(p);
+	if (retval)
+		goto out_put_task;
+
+	retval = __sched_setaffinity(p, in_mask);
 out_put_task:
 	put_task_struct(p);
 	return retval;
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 11/19] sched: Allow task CPU affinity to be restricted on asymmetric systems
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (9 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 10/19] sched: Split the guts of sched_setaffinity() into a helper function Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-04 17:12   ` Valentin Schneider
  2021-06-02 16:47 ` [PATCH v8 12/19] sched: Introduce task_cpus_dl_admissible() to check proposed affinity Will Deacon
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

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 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. Similarly, if a subsequent execve() reverts to
a compatible image, then the old affinity is restored if it is still
valid.

In preparation for restricting the affinity mask for compat tasks on
arm64 systems without uniform support for 32-bit applications, introduce
{force,relax}_compatible_cpus_allowed_ptr(), which respectively restrict
and restore the affinity mask for a task based on the compatible CPUs.

Reviewed-by: Quentin Perret <qperret@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 include/linux/sched.h |   2 +
 kernel/sched/core.c   | 177 ++++++++++++++++++++++++++++++++++++++----
 kernel/sched/sched.h  |   1 +
 3 files changed, 164 insertions(+), 16 deletions(-)

diff --git a/include/linux/sched.h b/include/linux/sched.h
index db32d4f7e5b3..91a6cfeae242 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1691,6 +1691,8 @@ extern void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new
 extern int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask);
 extern int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src, int node);
 extern void release_user_cpus_ptr(struct task_struct *p);
+extern void force_compatible_cpus_allowed_ptr(struct task_struct *p);
+extern void relax_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)
 {
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index f210d0b06944..58e2cf7520c0 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2354,27 +2354,22 @@ static int affine_move_task(struct rq *rq, struct task_struct *p, struct rq_flag
 }
 
 /*
- * 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.
  */
-static int __set_cpus_allowed_ptr(struct task_struct *p,
-				  const struct cpumask *new_mask,
-				  u32 flags)
+static int __set_cpus_allowed_ptr_locked(struct task_struct *p,
+					 const struct cpumask *new_mask,
+					 u32 flags,
+					 struct rq *rq,
+					 struct rq_flags *rf)
+	__releases(rq->lock)
+	__releases(p->pi_lock)
 {
 	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;
 	bool kthread = p->flags & PF_KTHREAD;
 
-	rq = task_rq_lock(p, &rf);
 	update_rq_clock(rq);
 
 	if (kthread || is_migration_disabled(p)) {
@@ -2430,20 +2425,170 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
 
 	__do_set_cpus_allowed(p, new_mask, flags);
 
-	return affine_move_task(rq, p, &rf, dest_cpu, flags);
+	if (flags & SCA_USER)
+		release_user_cpus_ptr(p);
+
+	return affine_move_task(rq, p, rf, dest_cpu, flags);
 
 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, u32 flags)
+{
+	struct rq_flags rf;
+	struct rq *rq;
+
+	rq = task_rq_lock(p, &rf);
+	return __set_cpus_allowed_ptr_locked(p, new_mask, flags, rq, &rf);
+}
+
 int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask)
 {
 	return __set_cpus_allowed_ptr(p, new_mask, 0);
 }
 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
+ * and pointing @p->user_cpus_ptr to a copy of the old 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;
+	int err;
+	struct cpumask *user_mask = NULL;
+
+	if (!p->user_cpus_ptr) {
+		user_mask = kmalloc(cpumask_size(), GFP_KERNEL);
+
+		if (!user_mask)
+			return -ENOMEM;
+	}
+
+	rq = task_rq_lock(p, &rf);
+
+	/*
+	 * Forcefully restricting the affinity of a deadline task is
+	 * likely to cause problems, so fail and noisily override the
+	 * mask entirely.
+	 */
+	if (task_has_dl_policy(p) && dl_bandwidth_enabled()) {
+		err = -EPERM;
+		goto err_unlock;
+	}
+
+	if (!cpumask_and(new_mask, &p->cpus_mask, subset_mask)) {
+		err = -EINVAL;
+		goto err_unlock;
+	}
+
+	/*
+	 * We're about to butcher the task affinity, so keep track of what
+	 * the user asked for in case we're able to restore it later on.
+	 */
+	if (user_mask) {
+		cpumask_copy(user_mask, p->cpus_ptr);
+		p->user_cpus_ptr = user_mask;
+	}
+
+	return __set_cpus_allowed_ptr_locked(p, new_mask, 0, rq, &rf);
+
+err_unlock:
+	task_rq_unlock(rq, p, &rf);
+	kfree(user_mask);
+	return err;
+}
+
+/*
+ * Restrict the CPU affinity of task @p so that it is a subset of
+ * task_cpu_possible_mask() and point @p->user_cpu_ptr to a copy of the
+ * old affinity mask. If the resulting mask is empty, we warn and walk
+ * up the cpuset hierarchy until we find a suitable mask.
+ */
+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);
+
+	alloc_cpumask_var(&new_mask, GFP_KERNEL);
+
+	/*
+	 * __migrate_task() can fail silently in the face of concurrent
+	 * offlining of the chosen destination CPU, so take the hotplug
+	 * lock to ensure that the migration succeeds.
+	 */
+	cpus_read_lock();
+	if (!cpumask_available(new_mask))
+		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));
+	}
+
+	WARN_ON(set_cpus_allowed_ptr(p, override_mask));
+out_free_mask:
+	cpus_read_unlock();
+	free_cpumask_var(new_mask);
+}
+
+static int
+__sched_setaffinity(struct task_struct *p, const struct cpumask *mask);
+
+/*
+ * Restore the affinity of a task @p which was previously restricted by a
+ * call to force_compatible_cpus_allowed_ptr(). This will clear (and free)
+ * @p->user_cpus_ptr.
+ */
+void relax_compatible_cpus_allowed_ptr(struct task_struct *p)
+{
+	unsigned long flags;
+	struct cpumask *mask = p->user_cpus_ptr;
+
+	/*
+	 * Try to restore the old affinity mask. If this fails, then
+	 * we free the mask explicitly to avoid it being inherited across
+	 * a subsequent fork().
+	 */
+	if (!mask || !__sched_setaffinity(p, mask))
+		return;
+
+	raw_spin_lock_irqsave(&p->pi_lock, flags);
+	release_user_cpus_ptr(p);
+	raw_spin_unlock_irqrestore(&p->pi_lock, flags);
+}
+
 void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
 {
 #ifdef CONFIG_SCHED_DEBUG
@@ -6823,7 +6968,7 @@ __sched_setaffinity(struct task_struct *p, const struct cpumask *mask)
 	}
 #endif
 again:
-	retval = __set_cpus_allowed_ptr(p, new_mask, SCA_CHECK);
+	retval = __set_cpus_allowed_ptr(p, new_mask, SCA_CHECK | SCA_USER);
 	if (retval)
 		goto out_free_new_mask;
 
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index a189bec13729..29c35b51411b 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1956,6 +1956,7 @@ extern struct task_struct *pick_next_task_idle(struct rq *rq);
 #define SCA_CHECK		0x01
 #define SCA_MIGRATE_DISABLE	0x02
 #define SCA_MIGRATE_ENABLE	0x04
+#define SCA_USER		0x08
 
 #ifdef CONFIG_SMP
 
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 12/19] sched: Introduce task_cpus_dl_admissible() to check proposed affinity
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (10 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 11/19] sched: Allow task CPU affinity to be restricted on asymmetric systems Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-03  9:43   ` Daniel Bristot de Oliveira
  2021-06-02 16:47 ` [PATCH v8 13/19] arm64: Implement task_cpu_possible_mask() Will Deacon
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

In preparation for restricting the affinity of a task during execve()
on arm64, introduce a new task_cpus_dl_admissible() helper function to
give an indication as to whether the restricted mask is admissible for
a deadline task.

Signed-off-by: Will Deacon <will@kernel.org>
---
 include/linux/sched.h |  6 ++++++
 kernel/sched/core.c   | 44 +++++++++++++++++++++++++++----------------
 2 files changed, 34 insertions(+), 16 deletions(-)

diff --git a/include/linux/sched.h b/include/linux/sched.h
index 91a6cfeae242..9b17d8cfa6ef 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1691,6 +1691,7 @@ extern void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new
 extern int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask);
 extern int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src, int node);
 extern void release_user_cpus_ptr(struct task_struct *p);
+extern bool task_cpus_dl_admissible(struct task_struct *p, const struct cpumask *mask);
 extern void force_compatible_cpus_allowed_ptr(struct task_struct *p);
 extern void relax_compatible_cpus_allowed_ptr(struct task_struct *p);
 #else
@@ -1713,6 +1714,11 @@ static inline void release_user_cpus_ptr(struct task_struct *p)
 {
 	WARN_ON(p->user_cpus_ptr);
 }
+
+static inline bool task_cpus_dl_admissible(struct task_struct *p, const struct cpumask *mask)
+{
+	return true;
+}
 #endif
 
 extern int yield_to(struct task_struct *p, bool preempt);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 58e2cf7520c0..b4f8dc18ae11 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -6933,6 +6933,31 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr,
 	return retval;
 }
 
+#ifdef CONFIG_SMP
+bool task_cpus_dl_admissible(struct task_struct *p, const struct cpumask *mask)
+{
+	bool ret;
+
+	/*
+	 * If the task isn't a deadline task or admission control is
+	 * disabled then we don't care about affinity changes.
+	 */
+	if (!task_has_dl_policy(p) || !dl_bandwidth_enabled())
+		return true;
+
+	/*
+	 * Since bandwidth control happens on root_domain basis,
+	 * if admission test is enabled, we only admit -deadline
+	 * tasks allowed to run on all the CPUs in the task's
+	 * root_domain.
+	 */
+	rcu_read_lock();
+	ret = cpumask_subset(task_rq(p)->rd->span, mask);
+	rcu_read_unlock();
+	return ret;
+}
+#endif
+
 static int
 __sched_setaffinity(struct task_struct *p, const struct cpumask *mask)
 {
@@ -6950,23 +6975,10 @@ __sched_setaffinity(struct task_struct *p, const struct cpumask *mask)
 	cpuset_cpus_allowed(p, cpus_allowed);
 	cpumask_and(new_mask, mask, cpus_allowed);
 
-	/*
-	 * Since bandwidth control happens on root_domain basis,
-	 * if admission test is enabled, we only admit -deadline
-	 * tasks allowed to run on all the CPUs in the task's
-	 * root_domain.
-	 */
-#ifdef CONFIG_SMP
-	if (task_has_dl_policy(p) && dl_bandwidth_enabled()) {
-		rcu_read_lock();
-		if (!cpumask_subset(task_rq(p)->rd->span, new_mask)) {
-			retval = -EBUSY;
-			rcu_read_unlock();
-			goto out_free_new_mask;
-		}
-		rcu_read_unlock();
+	if (!task_cpus_dl_admissible(p, new_mask)) {
+		retval = -EBUSY;
+		goto out_free_new_mask;
 	}
-#endif
 again:
 	retval = __set_cpus_allowed_ptr(p, new_mask, SCA_CHECK | SCA_USER);
 	if (retval)
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 13/19] arm64: Implement task_cpu_possible_mask()
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (11 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 12/19] sched: Introduce task_cpus_dl_admissible() to check proposed affinity Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-02 16:47 ` [PATCH v8 14/19] arm64: exec: Adjust affinity for compat tasks with mismatched 32-bit EL0 Will Deacon
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

Provide 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,

Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 arch/arm64/include/asm/mmu_context.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index d3cef9133539..bb9b7510f334 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -231,6 +231,19 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
 	update_saved_ttbr0(tsk, next);
 }
 
+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);
 
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 14/19] arm64: exec: Adjust affinity for compat tasks with mismatched 32-bit EL0
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (12 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 13/19] arm64: Implement task_cpu_possible_mask() Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-03  9:45   ` Daniel Bristot de Oliveira
  2021-06-02 16:47 ` [PATCH v8 15/19] arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system Will Deacon
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

When 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.

Similarly, when exec'ing a 64-bit task on such a system, try to restore
the old affinity mask if it was previously restricted.

Reviewed-by: Quentin Perret <qperret@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 arch/arm64/include/asm/elf.h |  6 ++----
 arch/arm64/kernel/process.c  | 39 +++++++++++++++++++++++++++++++++++-
 2 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index 8d1c8dcb87fd..97932fbf973d 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -213,10 +213,8 @@ typedef compat_elf_greg_t		compat_elf_gregset_t[COMPAT_ELF_NGREG];
 
 /* AArch32 EABI. */
 #define EF_ARM_EABI_MASK		0xff000000
-#define compat_elf_check_arch(x)	(system_supports_32bit_el0() && \
-					 ((x)->e_machine == EM_ARM) && \
-					 ((x)->e_flags & EF_ARM_EABI_MASK))
-
+int compat_elf_check_arch(const struct elf32_hdr *);
+#define compat_elf_check_arch		compat_elf_check_arch
 #define compat_start_thread		compat_start_thread
 /*
  * Unlike the native SET_PERSONALITY macro, the compat version maintains
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index f4a91bf1ce0c..3aea06fdd1f9 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -22,6 +22,7 @@
 #include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/nospec.h>
+#include <linux/sched.h>
 #include <linux/stddef.h>
 #include <linux/sysctl.h>
 #include <linux/unistd.h>
@@ -638,6 +639,28 @@ unsigned long arch_align_stack(unsigned long sp)
 	return sp & ~0xf;
 }
 
+#ifdef CONFIG_COMPAT
+int compat_elf_check_arch(const struct elf32_hdr *hdr)
+{
+	if (!system_supports_32bit_el0())
+		return false;
+
+	if ((hdr)->e_machine != EM_ARM)
+		return false;
+
+	if (!((hdr)->e_flags & EF_ARM_EABI_MASK))
+		return false;
+
+	/*
+	 * Prevent execve() of a 32-bit program from a deadline task
+	 * if the restricted affinity mask would be inadmissible on an
+	 * asymmetric system.
+	 */
+	return !static_branch_unlikely(&arm64_mismatched_32bit_el0) ||
+	       task_cpus_dl_admissible(current, system_32bit_el0_cpumask());
+}
+#endif
+
 /*
  * Called from setup_new_exec() after (COMPAT_)SET_PERSONALITY.
  */
@@ -647,8 +670,22 @@ 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);
+		}
+	} else if (static_branch_unlikely(&arm64_mismatched_32bit_el0)) {
+		relax_compatible_cpus_allowed_ptr(current);
 	}
 
 	current->mm->context.flags = mmflags;
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 15/19] arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (13 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 14/19] arm64: exec: Adjust affinity for compat tasks with mismatched 32-bit EL0 Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-03 12:58   ` Mark Rutland
  2021-06-02 16:47 ` [PATCH v8 16/19] arm64: Advertise CPUs capable of running 32-bit applications in sysfs Will Deacon
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

If 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.

Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 arch/arm64/kernel/cpufeature.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 4194a47de62d..b31d7a1eaed6 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -2877,15 +2877,33 @@ void __init setup_cpu_features(void)
 
 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);
 
 	if (cpu_32bit) {
 		cpumask_set_cpu(cpu, cpu_32bit_el0_mask);
 		static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0);
-		setup_elf_hwcaps(compat_elf_hwcaps);
 	}
 
+	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;
+	setup_elf_hwcaps(compat_elf_hwcaps);
+	pr_info("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n",
+		cpu, lucky_winner);
 	return 0;
 }
 
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 16/19] arm64: Advertise CPUs capable of running 32-bit applications in sysfs
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (14 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 15/19] arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-02 16:47 ` [PATCH v8 17/19] arm64: Hook up cmdline parameter to allow mismatched 32-bit EL0 Will Deacon
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

Since 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.

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 .../ABI/testing/sysfs-devices-system-cpu      |  9 +++++++++
 arch/arm64/kernel/cpufeature.c                | 19 +++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index fe13baa53c59..899377b2715a 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -494,6 +494,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:		May 2021
+Contact:	Linux ARM Kernel Mailing list <linux-arm-kernel@lists.infradead.org>
+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 <linux-kernel@vger.kernel.org>
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index b31d7a1eaed6..72efdc611b14 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -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/minmax.h>
 #include <linux/mm.h>
@@ -1297,6 +1298,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))
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 17/19] arm64: Hook up cmdline parameter to allow mismatched 32-bit EL0
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (15 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 16/19] arm64: Advertise CPUs capable of running 32-bit applications in sysfs Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-02 16:47 ` [PATCH v8 18/19] arm64: Remove logic to kill 32-bit tasks on 64-bit-only cores Will Deacon
  2021-06-02 16:47 ` [PATCH v8 19/19] Documentation: arm64: describe asymmetric 32-bit support Will Deacon
  18 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

Allow systems with mismatched 32-bit support at EL0 to run 32-bit
applications based on a new kernel parameter.

Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 Documentation/admin-guide/kernel-parameters.txt | 8 ++++++++
 arch/arm64/kernel/cpufeature.c                  | 7 +++++++
 2 files changed, 15 insertions(+)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index cb89dbdedc46..a2e453919bb6 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -287,6 +287,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:
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 72efdc611b14..f2c97baa050f 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -1298,6 +1298,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)
 {
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 18/19] arm64: Remove logic to kill 32-bit tasks on 64-bit-only cores
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (16 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 17/19] arm64: Hook up cmdline parameter to allow mismatched 32-bit EL0 Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  2021-06-02 16:47 ` [PATCH v8 19/19] Documentation: arm64: describe asymmetric 32-bit support Will Deacon
  18 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

The 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.

Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 arch/arm64/kernel/process.c | 14 +-------------
 arch/arm64/kernel/signal.c  | 26 --------------------------
 2 files changed, 1 insertion(+), 39 deletions(-)

diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 3aea06fdd1f9..5581c376644e 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -528,15 +528,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);
-}
-
 static void update_sctlr_el1(u64 sctlr)
 {
 	/*
@@ -578,7 +569,6 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,
 	ssbs_thread_switch(next);
 	erratum_1418040_thread_switch(prev, next);
 	ptrauth_thread_switch_user(next);
-	compat_thread_switch(next);
 
 	/*
 	 * Complete any pending TLB or cache maintenance on this CPU in case
@@ -680,10 +670,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);
-		}
 	} else if (static_branch_unlikely(&arm64_mismatched_32bit_el0)) {
 		relax_compatible_cpus_allowed_ptr(current);
 	}
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index f8192f4ae0b8..6237486ff6bb 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -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)
 {
@@ -951,19 +938,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)
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v8 19/19] Documentation: arm64: describe asymmetric 32-bit support
  2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
                   ` (17 preceding siblings ...)
  2021-06-02 16:47 ` [PATCH v8 18/19] arm64: Remove logic to kill 32-bit tasks on 64-bit-only cores Will Deacon
@ 2021-06-02 16:47 ` Will Deacon
  18 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-02 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

Document support for running 32-bit tasks on asymmetric 32-bit systems
and its impact on the user ABI when enabled.

Signed-off-by: Will Deacon <will@kernel.org>
---
 .../admin-guide/kernel-parameters.txt         |   3 +
 Documentation/arm64/asymmetric-32bit.rst      | 155 ++++++++++++++++++
 Documentation/arm64/index.rst                 |   1 +
 3 files changed, 159 insertions(+)
 create mode 100644 Documentation/arm64/asymmetric-32bit.rst

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index a2e453919bb6..5a1dc7e628a5 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -295,6 +295,9 @@
 			EL0 is indicated by /sys/devices/system/cpu/aarch32_el0
 			and hot-unplug operations may be restricted.
 
+			See Documentation/arm64/asymmetric-32bit.rst for more
+			information.
+
 	amd_iommu=	[HW,X86-64]
 			Pass parameters to the AMD IOMMU driver in the system.
 			Possible values are:
diff --git a/Documentation/arm64/asymmetric-32bit.rst b/Documentation/arm64/asymmetric-32bit.rst
new file mode 100644
index 000000000000..64a0b505da7d
--- /dev/null
+++ b/Documentation/arm64/asymmetric-32bit.rst
@@ -0,0 +1,155 @@
+======================
+Asymmetric 32-bit SoCs
+======================
+
+Author: Will Deacon <will@kernel.org>
+
+This document describes the impact of asymmetric 32-bit SoCs on the
+execution of 32-bit (``AArch32``) applications.
+
+Date: 2021-05-17
+
+Introduction
+============
+
+Some Armv9 SoCs suffer from a big.LITTLE misfeature where only a subset
+of the CPUs are capable of executing 32-bit user applications. On such
+a system, Linux by default treats the asymmetry as a "mismatch" and
+disables support for both the ``PER_LINUX32`` personality and
+``execve(2)`` of 32-bit ELF binaries, with the latter returning
+``-ENOEXEC``. If the mismatch is detected during late onlining of a
+64-bit-only CPU, then the onlining operation fails and the new CPU is
+unavailable for scheduling.
+
+Surprisingly, these SoCs have been produced with the intention of
+running legacy 32-bit binaries. Unsurprisingly, that doesn't work very
+well with the default behaviour of Linux.
+
+It seems inevitable that future SoCs will drop 32-bit support
+altogether, so if you're stuck in the unenviable position of needing to
+run 32-bit code on one of these transitionary platforms then you would
+be wise to consider alternatives such as recompilation, emulation or
+retirement. If neither of those options are practical, then read on.
+
+Enabling kernel support
+=======================
+
+Since the kernel support is not completely transparent to userspace,
+allowing 32-bit tasks to run on an asymmetric 32-bit system requires an
+explicit "opt-in" and can be enabled by passing the
+``allow_mismatched_32bit_el0`` parameter on the kernel command-line.
+
+For the remainder of this document we will refer to an *asymmetric
+system* to mean an asymmetric 32-bit SoC running Linux with this kernel
+command-line option enabled.
+
+Userspace impact
+================
+
+32-bit tasks running on an asymmetric system behave in mostly the same
+way as on a homogeneous system, with a few key differences relating to
+CPU affinity.
+
+sysfs
+-----
+
+The subset of CPUs capable of running 32-bit tasks is described in
+``/sys/devices/system/cpu/aarch32_el0`` and is documented further in
+``Documentation/ABI/testing/sysfs-devices-system-cpu``.
+
+**Note:** CPUs are advertised by this file as they are detected and so
+late-onlining of 32-bit-capable CPUs can result in the file contents
+being modified by the kernel at runtime. Once advertised, CPUs are never
+removed from the file.
+
+``execve(2)``
+-------------
+
+On a homogeneous system, the CPU affinity of a task is preserved across
+``execve(2)``. This is not always possible on an asymmetric system,
+specifically when the new program being executed is 32-bit yet the
+affinity mask contains 64-bit-only CPUs. In this situation, the kernel
+determines the new affinity mask as follows:
+
+  1. If the 32-bit-capable subset of the affinity mask is not empty,
+     then the affinity is restricted to that subset and the old affinity
+     mask is saved. This saved mask is inherited over ``fork(2)`` and
+     preserved across ``execve(2)`` of 32-bit programs.
+
+     **Note:** This step does not apply to ``SCHED_DEADLINE`` tasks.
+     See `SCHED_DEADLINE`_.
+
+  2. Otherwise, the cpuset hierarchy of the task is walked until an
+     ancestor is found containing at least one 32-bit-capable CPU. The
+     affinity of the task is then changed to match the 32-bit-capable
+     subset of the cpuset determined by the walk.
+
+  3. On failure (i.e. out of memory), the affinity is changed to the set
+     of all 32-bit-capable CPUs of which the kernel is aware.
+
+A subsequent ``execve(2)`` of a 64-bit program by the 32-bit task will
+invalidate the affinity mask saved in (1) and attempt to restore the CPU
+affinity of the task using the saved mask if it was previously valid.
+This restoration may fail due to intervening changes to the deadline
+policy or cpuset hierarchy, in which case the ``execve(2)`` continues
+with the affinity unchanged.
+
+Calls to ``sched_setaffinity(2)`` for a 32-bit task will consider only
+the 32-bit-capable CPUs of the requested affinity mask. On success, the
+affinity for the task is updated and any saved mask from a prior
+``execve(2)`` is invalidated.
+
+``SCHED_DEADLINE``
+------------------
+
+Explicit admission of a 32-bit deadline task to the default root domain
+(e.g. by calling ``sched_setattr(2)``) is rejected on an asymmetric
+32-bit system unless admission control is disabled by writing -1 to
+``/proc/sys/kernel/sched_rt_runtime_us``.
+
+``execve(2)`` of a 32-bit program from a 64-bit deadline task will
+return ``-ENOEXEC`` if the root domain for the task contains any
+64-bit-only CPUs and admission control is enabled. Concurrent offlining
+of 32-bit-capable CPUs may still necessitate the procedure described in
+`execve(2)`_, in which case step (1) is skipped and a warning is
+emitted on the console.
+
+**Note:** It is recommended that a set of 32-bit-capable CPUs are placed
+into a separate root domain if ``SCHED_DEADLINE`` is to be used with
+32-bit tasks on an asymmetric system. Failure to do so is likely to
+result in missed deadlines.
+
+Cpusets
+-------
+
+The affinity of a 32-bit task on an asymmetric system may include CPUs
+that are not explicitly allowed by the cpuset to which it is attached.
+This can occur as a result of the following two situations:
+
+  - A 64-bit task attached to a cpuset which allows only 64-bit CPUs
+    executes a 32-bit program.
+
+  - All of the 32-bit-capable CPUs allowed by a cpuset containing a
+    32-bit task are offlined.
+
+In both of these cases, the new affinity is calculated according to step
+(2) of the process described in `execve(2)`_ and the cpuset hierarchy is
+unchanged irrespective of the cgroup version.
+
+CPU hotplug
+-----------
+
+On an asymmetric system, the first detected 32-bit-capable CPU is
+prevented from being offlined by userspace and any such attempt will
+return ``-EPERM``. Note that suspend is still permitted even if the
+primary CPU (i.e. CPU 0) is 64-bit-only.
+
+KVM
+---
+
+Although KVM will not advertise 32-bit EL0 support to any vCPUs on an
+asymmetric system, a broken guest at EL1 could still attempt to execute
+32-bit code at EL0. In this case, an exit from a vCPU thread in 32-bit
+mode will return to host userspace with an ``exit_reason`` of
+``KVM_EXIT_FAIL_ENTRY`` and will remain non-runnable until successfully
+re-initialised by a subsequent ``KVM_ARM_VCPU_INIT`` operation.
diff --git a/Documentation/arm64/index.rst b/Documentation/arm64/index.rst
index 97d65ba12a35..4f840bac083e 100644
--- a/Documentation/arm64/index.rst
+++ b/Documentation/arm64/index.rst
@@ -10,6 +10,7 @@ ARM64 Architecture
     acpi_object_usage
     amu
     arm-acpi
+    asymmetric-32bit
     booting
     cpu-feature-registers
     elf_hwcaps
-- 
2.32.0.rc0.204.g9fa02ecfa5-goog


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 12/19] sched: Introduce task_cpus_dl_admissible() to check proposed affinity
  2021-06-02 16:47 ` [PATCH v8 12/19] sched: Introduce task_cpus_dl_admissible() to check proposed affinity Will Deacon
@ 2021-06-03  9:43   ` Daniel Bristot de Oliveira
  2021-06-03  9:52     ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Daniel Bristot de Oliveira @ 2021-06-03  9:43 UTC (permalink / raw)
  To: Will Deacon, linux-arm-kernel
  Cc: linux-arch, linux-kernel, Catalin Marinas, Marc Zyngier,
	Greg Kroah-Hartman, Peter Zijlstra, Morten Rasmussen,
	Qais Yousef, Suren Baghdasaryan, Quentin Perret, Tejun Heo,
	Johannes Weiner, Ingo Molnar, Juri Lelli, Vincent Guittot,
	Rafael J. Wysocki, Dietmar Eggemann, Valentin Schneider,
	kernel-team

On 6/2/21 6:47 PM, Will Deacon wrote:
> In preparation for restricting the affinity of a task during execve()
> on arm64, introduce a new task_cpus_dl_admissible() helper function to
> give an indication as to whether the restricted mask is admissible for
> a deadline task.
> 
> Signed-off-by: Will Deacon <will@kernel.org>
> ---
>  include/linux/sched.h |  6 ++++++
>  kernel/sched/core.c   | 44 +++++++++++++++++++++++++++----------------
>  2 files changed, 34 insertions(+), 16 deletions(-)
> 
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index 91a6cfeae242..9b17d8cfa6ef 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -1691,6 +1691,7 @@ extern void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new
>  extern int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask);
>  extern int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src, int node);
>  extern void release_user_cpus_ptr(struct task_struct *p);
> +extern bool task_cpus_dl_admissible(struct task_struct *p, const struct cpumask *mask);
>  extern void force_compatible_cpus_allowed_ptr(struct task_struct *p);
>  extern void relax_compatible_cpus_allowed_ptr(struct task_struct *p);
>  #else
> @@ -1713,6 +1714,11 @@ static inline void release_user_cpus_ptr(struct task_struct *p)
>  {
>  	WARN_ON(p->user_cpus_ptr);
>  }
> +
> +static inline bool task_cpus_dl_admissible(struct task_struct *p, const struct cpumask *mask)
> +{
> +	return true;
> +}
>  #endif
>  
>  extern int yield_to(struct task_struct *p, bool preempt);
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index 58e2cf7520c0..b4f8dc18ae11 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -6933,6 +6933,31 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr,
>  	return retval;
>  }
>  
> +#ifdef CONFIG_SMP
> +bool task_cpus_dl_admissible(struct task_struct *p, const struct cpumask *mask)

Would you mind renaming it to dl_task_check_affinity(), in the case of a v9? It
will look coherent with dl_task_can_attach()...

Reviewed-by: Daniel Bristot de Oliveira <bristot@redhat.com>

Thanks!
-- Daniel


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 14/19] arm64: exec: Adjust affinity for compat tasks with mismatched 32-bit EL0
  2021-06-02 16:47 ` [PATCH v8 14/19] arm64: exec: Adjust affinity for compat tasks with mismatched 32-bit EL0 Will Deacon
@ 2021-06-03  9:45   ` Daniel Bristot de Oliveira
  0 siblings, 0 replies; 48+ messages in thread
From: Daniel Bristot de Oliveira @ 2021-06-03  9:45 UTC (permalink / raw)
  To: Will Deacon, linux-arm-kernel
  Cc: linux-arch, linux-kernel, Catalin Marinas, Marc Zyngier,
	Greg Kroah-Hartman, Peter Zijlstra, Morten Rasmussen,
	Qais Yousef, Suren Baghdasaryan, Quentin Perret, Tejun Heo,
	Johannes Weiner, Ingo Molnar, Juri Lelli, Vincent Guittot,
	Rafael J. Wysocki, Dietmar Eggemann, Valentin Schneider,
	kernel-team

On 6/2/21 6:47 PM, Will Deacon wrote:
> When 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.
> 
> Similarly, when exec'ing a 64-bit task on such a system, try to restore
> the old affinity mask if it was previously restricted.
> 
> Reviewed-by: Quentin Perret <qperret@google.com>
> Signed-off-by: Will Deacon <will@kernel.org>
> ---

[...]

>  
> +#ifdef CONFIG_COMPAT
> +int compat_elf_check_arch(const struct elf32_hdr *hdr)
> +{
> +	if (!system_supports_32bit_el0())
> +		return false;
> +
> +	if ((hdr)->e_machine != EM_ARM)
> +		return false;
> +
> +	if (!((hdr)->e_flags & EF_ARM_EABI_MASK))
> +		return false;
> +
> +	/*
> +	 * Prevent execve() of a 32-bit program from a deadline task
> +	 * if the restricted affinity mask would be inadmissible on an
> +	 * asymmetric system.
> +	 */
> +	return !static_branch_unlikely(&arm64_mismatched_32bit_el0) ||
> +	       task_cpus_dl_admissible(current, system_32bit_el0_cpumask());
> +}
> +#endif

From the DL perspective:

Reviewed-by: Daniel Bristot de Oliveira <bristot@redhat.com>

Thanks!
-- Daniel


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 12/19] sched: Introduce task_cpus_dl_admissible() to check proposed affinity
  2021-06-03  9:43   ` Daniel Bristot de Oliveira
@ 2021-06-03  9:52     ` Will Deacon
  0 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-03  9:52 UTC (permalink / raw)
  To: Daniel Bristot de Oliveira
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Valentin Schneider, kernel-team

On Thu, Jun 03, 2021 at 11:43:08AM +0200, Daniel Bristot de Oliveira wrote:
> On 6/2/21 6:47 PM, Will Deacon wrote:
> > In preparation for restricting the affinity of a task during execve()
> > on arm64, introduce a new task_cpus_dl_admissible() helper function to
> > give an indication as to whether the restricted mask is admissible for
> > a deadline task.
> > 
> > Signed-off-by: Will Deacon <will@kernel.org>
> > ---
> >  include/linux/sched.h |  6 ++++++
> >  kernel/sched/core.c   | 44 +++++++++++++++++++++++++++----------------
> >  2 files changed, 34 insertions(+), 16 deletions(-)
> > 
> > diff --git a/include/linux/sched.h b/include/linux/sched.h
> > index 91a6cfeae242..9b17d8cfa6ef 100644
> > --- a/include/linux/sched.h
> > +++ b/include/linux/sched.h
> > @@ -1691,6 +1691,7 @@ extern void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new
> >  extern int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask);
> >  extern int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src, int node);
> >  extern void release_user_cpus_ptr(struct task_struct *p);
> > +extern bool task_cpus_dl_admissible(struct task_struct *p, const struct cpumask *mask);
> >  extern void force_compatible_cpus_allowed_ptr(struct task_struct *p);
> >  extern void relax_compatible_cpus_allowed_ptr(struct task_struct *p);
> >  #else
> > @@ -1713,6 +1714,11 @@ static inline void release_user_cpus_ptr(struct task_struct *p)
> >  {
> >  	WARN_ON(p->user_cpus_ptr);
> >  }
> > +
> > +static inline bool task_cpus_dl_admissible(struct task_struct *p, const struct cpumask *mask)
> > +{
> > +	return true;
> > +}
> >  #endif
> >  
> >  extern int yield_to(struct task_struct *p, bool preempt);
> > diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> > index 58e2cf7520c0..b4f8dc18ae11 100644
> > --- a/kernel/sched/core.c
> > +++ b/kernel/sched/core.c
> > @@ -6933,6 +6933,31 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr,
> >  	return retval;
> >  }
> >  
> > +#ifdef CONFIG_SMP
> > +bool task_cpus_dl_admissible(struct task_struct *p, const struct cpumask *mask)
> 
> Would you mind renaming it to dl_task_check_affinity(), in the case of a v9? It
> will look coherent with dl_task_can_attach()...

Of course! I struggled with the naming myself, and your suggestion is much
better.

> Reviewed-by: Daniel Bristot de Oliveira <bristot@redhat.com>

Cheers!

Will

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support
  2021-06-02 16:47 ` [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support Will Deacon
@ 2021-06-03 12:37   ` Mark Rutland
  2021-06-03 17:44     ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Mark Rutland @ 2021-06-03 12:37 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Wed, Jun 02, 2021 at 05:47:02PM +0100, Will Deacon wrote:
> When 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.
> 
> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
> Signed-off-by: Will Deacon <will@kernel.org>
> ---
>  arch/arm64/include/asm/cpufeature.h |   8 +-
>  arch/arm64/kernel/cpufeature.c      | 114 ++++++++++++++++++++++++----
>  arch/arm64/tools/cpucaps            |   3 +-
>  3 files changed, 110 insertions(+), 15 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index 338840c00e8e..603bf4160cd6 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -630,9 +630,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);
>  }

Note that read_sanitised_ftr_reg() has to do a bsearch() to find the
arm64_ftr_reg, so this will make system_32bit_el0_cpumask() a fair
amount more expensive than it needs to be.

Can we follow the pattern we have for arm64_ftr_reg_ctrel0, and have a
arm64_ftr_reg_id_aa64pfr0_el1 that we can address directly here?

That said. I reckon this could be much cleaner if we maintained separate
caps:

ARM64_ALL_CPUS_HAVE_32BIT_EL0
ARM64_SOME_CPUS_HAVE_32BIT_EL0

... and allow arm64_mismatched_32bit_el0 to be set dependent on
ARM64_SOME_CPUS_HAVE_32BIT_EL0. With that, this can be simplified to:

static inline bool system_supports_32bit_el0(void)
{
	return (cpus_have_const_cap(ARM64_ALL_CPUS_HAVE_32BIT_EL0)) ||
		static_branch_unlikely(&arm64_mismatched_32bit_el0))
}

>  static inline bool system_supports_4kb_granule(void)
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index a4db25cd7122..4194a47de62d 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -107,6 +107,24 @@ DECLARE_BITMAP(boot_capabilities, ARM64_NPATCHABLE);
>  bool arm64_use_ng_mappings = false;
>  EXPORT_SYMBOL(arm64_use_ng_mappings);
>  
> +/*
> + * Permit PER_LINUX32 and execve() of 32-bit binaries even if not all CPUs
> + * support it?
> + */
> +static bool __read_mostly allow_mismatched_32bit_el0;
> +
> +/*
> + * Static branch enabled only if allow_mismatched_32bit_el0 is set and we have
> + * seen at least one CPU capable of 32-bit EL0.
> + */
> +DEFINE_STATIC_KEY_FALSE(arm64_mismatched_32bit_el0);
> +
> +/*
> + * Mask of CPUs supporting 32-bit EL0.
> + * Only valid if arm64_mismatched_32bit_el0 is enabled.
> + */
> +static cpumask_var_t cpu_32bit_el0_mask __cpumask_var_read_mostly;
> +
>  /*
>   * Flag to indicate if we have computed the system wide
>   * capabilities based on the boot time active CPUs. This
> @@ -767,7 +785,7 @@ static void __init sort_ftr_regs(void)
>   * Any bits that are not covered by an arm64_ftr_bits entry are considered
>   * RES0 for the system-wide value, and must strictly match.
>   */
> -static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
> +static void init_cpu_ftr_reg(u32 sys_reg, u64 new)
>  {
>  	u64 val = 0;
>  	u64 strict_mask = ~0x0ULL;
> @@ -863,7 +881,7 @@ static void __init init_cpu_hwcaps_indirect_list(void)
>  
>  static void __init setup_boot_cpu_capabilities(void);
>  
> -static void __init init_32bit_cpu_features(struct cpuinfo_32bit *info)
> +static void init_32bit_cpu_features(struct cpuinfo_32bit *info)
>  {
>  	init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
>  	init_cpu_ftr_reg(SYS_ID_DFR1_EL1, info->reg_id_dfr1);
> @@ -979,6 +997,22 @@ static void relax_cpu_ftr_reg(u32 sys_id, int field)
>  	WARN_ON(!ftrp->width);
>  }
>  
> +static void update_mismatched_32bit_el0_cpu_features(struct cpuinfo_arm64 *info,
> +						     struct cpuinfo_arm64 *boot)

Could we s/update/lazy_init/ here?

IIUC this caters for the case where CPU0 doesn't have AArch32 but a
secondary does. That, and the naming looks odd in update_cpu_features()
when we have:

	update_mismatched_32bit_el0_cpu_features(...)
	update_32bit_cpu_features(...);

> +{
> +	static bool boot_cpu_32bit_regs_overridden = false;
> +
> +	if (!allow_mismatched_32bit_el0 || boot_cpu_32bit_regs_overridden)
> +		return;
> +
> +	if (id_aa64pfr0_32bit_el0(boot->reg_id_aa64pfr0))
> +		return;
> +
> +	boot->aarch32 = info->aarch32;
> +	init_32bit_cpu_features(&boot->aarch32);
> +	boot_cpu_32bit_regs_overridden = true;
> +}

Can't we share this with the boot CPU path if we do:

/*
 * Initialize the common AArch32 features on the first CPU with AArch32.
 */
static void lazy_init_32bit_el0_cpu_features(struct cpuinfo_arm64 *info,
					     struct cpuinfo_arm64 *boot)
{
	static bool initialised = false;
	if (initialised || !id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
		return;
	
	boot->aarch32 = info->aarch32;
	init_32bit_cpu_features(&boot->aarch32);
	initiaised = true;
}

... or is the allow_mismatched_32bit_el0 check necessary for late
hotplug?


> +
>  static int update_32bit_cpu_features(int cpu, struct cpuinfo_32bit *info,
>  				     struct cpuinfo_32bit *boot)
>  {
> @@ -1139,6 +1173,7 @@ void update_cpu_features(int cpu,
>  	 * (e.g. SYS_ID_AA64PFR0_EL1), so we call it last.
>  	 */
>  	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
> +		update_mismatched_32bit_el0_cpu_features(info, boot);
>  		taint |= update_32bit_cpu_features(cpu, &info->aarch32,
>  						   &boot->aarch32);
>  	}
> @@ -1251,6 +1286,28 @@ has_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope)
>  	return feature_matches(val, entry);
>  }
>  
> +const struct cpumask *system_32bit_el0_cpumask(void)
> +{
> +	if (!system_supports_32bit_el0())
> +		return cpu_none_mask;
> +
> +	if (static_branch_unlikely(&arm64_mismatched_32bit_el0))
> +		return cpu_32bit_el0_mask;
> +
> +	return cpu_possible_mask;
> +}
> +
> +static bool has_32bit_el0(const struct arm64_cpu_capabilities *entry, int scope)
> +{
> +	if (!has_cpuid_feature(entry, scope))
> +		return allow_mismatched_32bit_el0;
> +
> +	if (scope == SCOPE_SYSTEM)
> +		pr_info("detected: 32-bit EL0 Support\n");
> +
> +	return true;
> +}
> +
>  static bool has_useable_gicv3_cpuif(const struct arm64_cpu_capabilities *entry, int scope)
>  {
>  	bool has_sre;
> @@ -1869,10 +1926,9 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
>  		.cpu_enable = cpu_copy_el2regs,
>  	},
>  	{
> -		.desc = "32-bit EL0 Support",
> -		.capability = ARM64_HAS_32BIT_EL0,
> +		.capability = ARM64_HAS_32BIT_EL0_DO_NOT_USE,
>  		.type = ARM64_CPUCAP_SYSTEM_FEATURE,
> -		.matches = has_cpuid_feature,
> +		.matches = has_32bit_el0,
>  		.sys_reg = SYS_ID_AA64PFR0_EL1,
>  		.sign = FTR_UNSIGNED,
>  		.field_pos = ID_AA64PFR0_EL0_SHIFT,
> @@ -2381,7 +2437,7 @@ static const struct arm64_cpu_capabilities compat_elf_hwcaps[] = {
>  	{},
>  };
>  
> -static void __init cap_set_elf_hwcap(const struct arm64_cpu_capabilities *cap)
> +static void cap_set_elf_hwcap(const struct arm64_cpu_capabilities *cap)
>  {
>  	switch (cap->hwcap_type) {
>  	case CAP_HWCAP:
> @@ -2426,7 +2482,7 @@ static bool cpus_have_elf_hwcap(const struct arm64_cpu_capabilities *cap)
>  	return rc;
>  }
>  
> -static void __init setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps)
> +static void setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps)
>  {
>  	/* We support emulation of accesses to CPU ID feature registers */
>  	cpu_set_named_feature(CPUID);
> @@ -2601,7 +2657,7 @@ static void check_early_cpu_features(void)
>  }
>  
>  static void
> -verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps)
> +__verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps)
>  {
>  
>  	for (; caps->matches; caps++)
> @@ -2612,6 +2668,14 @@ verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps)
>  		}
>  }
>  
> +static void verify_local_elf_hwcaps(void)
> +{
> +	__verify_local_elf_hwcaps(arm64_elf_hwcaps);
> +
> +	if (id_aa64pfr0_32bit_el0(read_cpuid(ID_AA64PFR0_EL1)))
> +		__verify_local_elf_hwcaps(compat_elf_hwcaps);
> +}
> +
>  static void verify_sve_features(void)
>  {
>  	u64 safe_zcr = read_sanitised_ftr_reg(SYS_ZCR_EL1);
> @@ -2676,11 +2740,7 @@ static void verify_local_cpu_capabilities(void)
>  	 * on all secondary CPUs.
>  	 */
>  	verify_local_cpu_caps(SCOPE_ALL & ~SCOPE_BOOT_CPU);
> -
> -	verify_local_elf_hwcaps(arm64_elf_hwcaps);
> -
> -	if (system_supports_32bit_el0())
> -		verify_local_elf_hwcaps(compat_elf_hwcaps);
> +	verify_local_elf_hwcaps();
>  
>  	if (system_supports_sve())
>  		verify_sve_features();
> @@ -2815,6 +2875,34 @@ void __init setup_cpu_features(void)
>  			ARCH_DMA_MINALIGN);
>  }
>  
> +static int enable_mismatched_32bit_el0(unsigned int cpu)
> +{
> +	struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
> +	bool cpu_32bit = id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0);
> +
> +	if (cpu_32bit) {
> +		cpumask_set_cpu(cpu, cpu_32bit_el0_mask);
> +		static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0);
> +		setup_elf_hwcaps(compat_elf_hwcaps);
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init init_32bit_el0_mask(void)
> +{
> +	if (!allow_mismatched_32bit_el0)
> +		return 0;
> +
> +	if (!zalloc_cpumask_var(&cpu_32bit_el0_mask, GFP_KERNEL))
> +		return -ENOMEM;
> +
> +	return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> +				 "arm64/mismatched_32bit_el0:online",
> +				 enable_mismatched_32bit_el0, NULL);
> +}

Shouldn't we clear this on a hot-unplug?

Thanks,
Mark.

> +subsys_initcall_sync(init_32bit_el0_mask);
> +
>  static void __maybe_unused cpu_enable_cnp(struct arm64_cpu_capabilities const *cap)
>  {
>  	cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
> diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
> index 21fbdda7086e..49305c2e6dfd 100644
> --- a/arch/arm64/tools/cpucaps
> +++ b/arch/arm64/tools/cpucaps
> @@ -3,7 +3,8 @@
>  # Internal CPU capabilities constants, keep this list sorted
>  
>  BTI
> -HAS_32BIT_EL0
> +# Unreliable: use system_supports_32bit_el0() instead.
> +HAS_32BIT_EL0_DO_NOT_USE
>  HAS_32BIT_EL1
>  HAS_ADDRESS_AUTH
>  HAS_ADDRESS_AUTH_ARCH
> -- 
> 2.32.0.rc0.204.g9fa02ecfa5-goog
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 01/19] arm64: cpuinfo: Split AArch32 registers out into a separate struct
  2021-06-02 16:47 ` [PATCH v8 01/19] arm64: cpuinfo: Split AArch32 registers out into a separate struct Will Deacon
@ 2021-06-03 12:38   ` Mark Rutland
  2021-06-03 17:24     ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Mark Rutland @ 2021-06-03 12:38 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Wed, Jun 02, 2021 at 05:47:01PM +0100, Will Deacon wrote:
> 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.
> 
> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
> Signed-off-by: Will Deacon <will@kernel.org>

Makes sense to me; if it's not too painful to change, I'd suggest
`aarch32` rather than `32bit` in the name, but either way:

Acked-by: Mark Rutland <mark.rutland@arm.com>

Mark.

> ---
>  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(-)
> 
> diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h
> index 7faae6ff3ab4..f4e01aa0f442 100644
> --- a/arch/arm64/include/asm/cpu.h
> +++ b/arch/arm64/include/asm/cpu.h
> @@ -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;
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index efed2830d141..a4db25cd7122 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -863,6 +863,31 @@ static void __init init_cpu_hwcaps_indirect_list(void)
>  
>  static void __init setup_boot_cpu_capabilities(void);
>  
> +static void __init init_32bit_cpu_features(struct cpuinfo_32bit *info)
> +{
> +	init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
> +	init_cpu_ftr_reg(SYS_ID_DFR1_EL1, info->reg_id_dfr1);
> +	init_cpu_ftr_reg(SYS_ID_ISAR0_EL1, info->reg_id_isar0);
> +	init_cpu_ftr_reg(SYS_ID_ISAR1_EL1, info->reg_id_isar1);
> +	init_cpu_ftr_reg(SYS_ID_ISAR2_EL1, info->reg_id_isar2);
> +	init_cpu_ftr_reg(SYS_ID_ISAR3_EL1, info->reg_id_isar3);
> +	init_cpu_ftr_reg(SYS_ID_ISAR4_EL1, info->reg_id_isar4);
> +	init_cpu_ftr_reg(SYS_ID_ISAR5_EL1, info->reg_id_isar5);
> +	init_cpu_ftr_reg(SYS_ID_ISAR6_EL1, info->reg_id_isar6);
> +	init_cpu_ftr_reg(SYS_ID_MMFR0_EL1, info->reg_id_mmfr0);
> +	init_cpu_ftr_reg(SYS_ID_MMFR1_EL1, info->reg_id_mmfr1);
> +	init_cpu_ftr_reg(SYS_ID_MMFR2_EL1, info->reg_id_mmfr2);
> +	init_cpu_ftr_reg(SYS_ID_MMFR3_EL1, info->reg_id_mmfr3);
> +	init_cpu_ftr_reg(SYS_ID_MMFR4_EL1, info->reg_id_mmfr4);
> +	init_cpu_ftr_reg(SYS_ID_MMFR5_EL1, info->reg_id_mmfr5);
> +	init_cpu_ftr_reg(SYS_ID_PFR0_EL1, info->reg_id_pfr0);
> +	init_cpu_ftr_reg(SYS_ID_PFR1_EL1, info->reg_id_pfr1);
> +	init_cpu_ftr_reg(SYS_ID_PFR2_EL1, info->reg_id_pfr2);
> +	init_cpu_ftr_reg(SYS_MVFR0_EL1, info->reg_mvfr0);
> +	init_cpu_ftr_reg(SYS_MVFR1_EL1, info->reg_mvfr1);
> +	init_cpu_ftr_reg(SYS_MVFR2_EL1, info->reg_mvfr2);
> +}
> +
>  void __init init_cpu_features(struct cpuinfo_arm64 *info)
>  {
>  	/* Before we start using the tables, make sure it is sorted */
> @@ -882,29 +907,8 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)
>  	init_cpu_ftr_reg(SYS_ID_AA64PFR1_EL1, info->reg_id_aa64pfr1);
>  	init_cpu_ftr_reg(SYS_ID_AA64ZFR0_EL1, info->reg_id_aa64zfr0);
>  
> -	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
> -		init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
> -		init_cpu_ftr_reg(SYS_ID_DFR1_EL1, info->reg_id_dfr1);
> -		init_cpu_ftr_reg(SYS_ID_ISAR0_EL1, info->reg_id_isar0);
> -		init_cpu_ftr_reg(SYS_ID_ISAR1_EL1, info->reg_id_isar1);
> -		init_cpu_ftr_reg(SYS_ID_ISAR2_EL1, info->reg_id_isar2);
> -		init_cpu_ftr_reg(SYS_ID_ISAR3_EL1, info->reg_id_isar3);
> -		init_cpu_ftr_reg(SYS_ID_ISAR4_EL1, info->reg_id_isar4);
> -		init_cpu_ftr_reg(SYS_ID_ISAR5_EL1, info->reg_id_isar5);
> -		init_cpu_ftr_reg(SYS_ID_ISAR6_EL1, info->reg_id_isar6);
> -		init_cpu_ftr_reg(SYS_ID_MMFR0_EL1, info->reg_id_mmfr0);
> -		init_cpu_ftr_reg(SYS_ID_MMFR1_EL1, info->reg_id_mmfr1);
> -		init_cpu_ftr_reg(SYS_ID_MMFR2_EL1, info->reg_id_mmfr2);
> -		init_cpu_ftr_reg(SYS_ID_MMFR3_EL1, info->reg_id_mmfr3);
> -		init_cpu_ftr_reg(SYS_ID_MMFR4_EL1, info->reg_id_mmfr4);
> -		init_cpu_ftr_reg(SYS_ID_MMFR5_EL1, info->reg_id_mmfr5);
> -		init_cpu_ftr_reg(SYS_ID_PFR0_EL1, info->reg_id_pfr0);
> -		init_cpu_ftr_reg(SYS_ID_PFR1_EL1, info->reg_id_pfr1);
> -		init_cpu_ftr_reg(SYS_ID_PFR2_EL1, info->reg_id_pfr2);
> -		init_cpu_ftr_reg(SYS_MVFR0_EL1, info->reg_mvfr0);
> -		init_cpu_ftr_reg(SYS_MVFR1_EL1, info->reg_mvfr1);
> -		init_cpu_ftr_reg(SYS_MVFR2_EL1, info->reg_mvfr2);
> -	}
> +	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
> +		init_32bit_cpu_features(&info->aarch32);
>  
>  	if (id_aa64pfr0_sve(info->reg_id_aa64pfr0)) {
>  		init_cpu_ftr_reg(SYS_ZCR_EL1, info->reg_zcr);
> @@ -975,20 +979,12 @@ static void relax_cpu_ftr_reg(u32 sys_id, int field)
>  	WARN_ON(!ftrp->width);
>  }
>  
> -static int update_32bit_cpu_features(int cpu, struct cpuinfo_arm64 *info,
> -				     struct cpuinfo_arm64 *boot)
> +static int update_32bit_cpu_features(int cpu, struct cpuinfo_32bit *info,
> +				     struct cpuinfo_32bit *boot)
>  {
>  	int taint = 0;
>  	u64 pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
>  
> -	/*
> -	 * If we don't have AArch32 at all then skip the checks entirely
> -	 * as the register values may be UNKNOWN and we're not going to be
> -	 * using them for anything.
> -	 */
> -	if (!id_aa64pfr0_32bit_el0(pfr0))
> -		return taint;
> -
>  	/*
>  	 * If we don't have AArch32 at EL1, then relax the strictness of
>  	 * EL1-dependent register fields to avoid spurious sanity check fails.
> @@ -1135,10 +1131,17 @@ void update_cpu_features(int cpu,
>  	}
>  
>  	/*
> +	 * If we don't have AArch32 at all then skip the checks entirely
> +	 * as the register values may be UNKNOWN and we're not going to be
> +	 * using them for anything.
> +	 *
>  	 * This relies on a sanitised view of the AArch64 ID registers
>  	 * (e.g. SYS_ID_AA64PFR0_EL1), so we call it last.
>  	 */
> -	taint |= update_32bit_cpu_features(cpu, info, boot);
> +	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
> +		taint |= update_32bit_cpu_features(cpu, &info->aarch32,
> +						   &boot->aarch32);
> +	}
>  
>  	/*
>  	 * Mismatched CPU features are a recipe for disaster. Don't even
> diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
> index 51fcf99d5351..264c119a6cae 100644
> --- a/arch/arm64/kernel/cpuinfo.c
> +++ b/arch/arm64/kernel/cpuinfo.c
> @@ -344,6 +344,32 @@ static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
>  	pr_info("Detected %s I-cache on CPU%d\n", icache_policy_str[l1ip], cpu);
>  }
>  
> +static void __cpuinfo_store_cpu_32bit(struct cpuinfo_32bit *info)
> +{
> +	info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
> +	info->reg_id_dfr1 = read_cpuid(ID_DFR1_EL1);
> +	info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
> +	info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
> +	info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
> +	info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
> +	info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
> +	info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
> +	info->reg_id_isar6 = read_cpuid(ID_ISAR6_EL1);
> +	info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
> +	info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
> +	info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
> +	info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
> +	info->reg_id_mmfr4 = read_cpuid(ID_MMFR4_EL1);
> +	info->reg_id_mmfr5 = read_cpuid(ID_MMFR5_EL1);
> +	info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
> +	info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
> +	info->reg_id_pfr2 = read_cpuid(ID_PFR2_EL1);
> +
> +	info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
> +	info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
> +	info->reg_mvfr2 = read_cpuid(MVFR2_EL1);
> +}
> +
>  static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
>  {
>  	info->reg_cntfrq = arch_timer_get_cntfrq();
> @@ -371,31 +397,8 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
>  	info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);
>  	info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1);
>  
> -	/* Update the 32bit ID registers only if AArch32 is implemented */
> -	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
> -		info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
> -		info->reg_id_dfr1 = read_cpuid(ID_DFR1_EL1);
> -		info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
> -		info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
> -		info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
> -		info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
> -		info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
> -		info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
> -		info->reg_id_isar6 = read_cpuid(ID_ISAR6_EL1);
> -		info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
> -		info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
> -		info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
> -		info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
> -		info->reg_id_mmfr4 = read_cpuid(ID_MMFR4_EL1);
> -		info->reg_id_mmfr5 = read_cpuid(ID_MMFR5_EL1);
> -		info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
> -		info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
> -		info->reg_id_pfr2 = read_cpuid(ID_PFR2_EL1);
> -
> -		info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
> -		info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
> -		info->reg_mvfr2 = read_cpuid(MVFR2_EL1);
> -	}
> +	if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
> +		__cpuinfo_store_cpu_32bit(&info->aarch32);
>  
>  	if (IS_ENABLED(CONFIG_ARM64_SVE) &&
>  	    id_aa64pfr0_sve(info->reg_id_aa64pfr0))
> -- 
> 2.32.0.rc0.204.g9fa02ecfa5-goog
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 15/19] arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system
  2021-06-02 16:47 ` [PATCH v8 15/19] arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system Will Deacon
@ 2021-06-03 12:58   ` Mark Rutland
  2021-06-03 17:40     ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Mark Rutland @ 2021-06-03 12:58 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Wed, Jun 02, 2021 at 05:47:15PM +0100, Will Deacon wrote:
> If 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.
> 
> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
> Signed-off-by: Will Deacon <will@kernel.org>
> ---
>  arch/arm64/kernel/cpufeature.c | 20 +++++++++++++++++++-
>  1 file changed, 19 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 4194a47de62d..b31d7a1eaed6 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -2877,15 +2877,33 @@ void __init setup_cpu_features(void)
>  
>  static int enable_mismatched_32bit_el0(unsigned int cpu)
>  {
> +	static int lucky_winner = -1;

This is cute, but could we please give it a meaningful name, e.g.
`pinned_cpu` ?

> +
>  	struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
>  	bool cpu_32bit = id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0);
>  
>  	if (cpu_32bit) {
>  		cpumask_set_cpu(cpu, cpu_32bit_el0_mask);
>  		static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0);
> -		setup_elf_hwcaps(compat_elf_hwcaps);
>  	}
>  
> +	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;
> +	setup_elf_hwcaps(compat_elf_hwcaps);
> +	pr_info("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n",
> +		cpu, lucky_winner);
>  	return 0;
>  }

I guess this is going to play havoc with kexec and hibernate. :/

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 01/19] arm64: cpuinfo: Split AArch32 registers out into a separate struct
  2021-06-03 12:38   ` Mark Rutland
@ 2021-06-03 17:24     ` Will Deacon
  0 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-03 17:24 UTC (permalink / raw)
  To: Mark Rutland
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Thu, Jun 03, 2021 at 01:38:52PM +0100, Mark Rutland wrote:
> On Wed, Jun 02, 2021 at 05:47:01PM +0100, Will Deacon wrote:
> > 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.
> > 
> > Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
> > Signed-off-by: Will Deacon <will@kernel.org>
> 
> Makes sense to me; if it's not too painful to change, I'd suggest
> `aarch32` rather than `32bit` in the name, but either way:
> 
> Acked-by: Mark Rutland <mark.rutland@arm.com>

Thanks. "32bit" is already pervasive in cpufeature.c and we're using arm64
instead of aarch64 in cpuinfo_arm64, so I'll leave this as-is and offer
somebody else the refactoring opportunity ;)

Will

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 15/19] arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system
  2021-06-03 12:58   ` Mark Rutland
@ 2021-06-03 17:40     ` Will Deacon
  2021-06-04  9:49       ` Mark Rutland
  0 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-03 17:40 UTC (permalink / raw)
  To: Mark Rutland
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Thu, Jun 03, 2021 at 01:58:56PM +0100, Mark Rutland wrote:
> On Wed, Jun 02, 2021 at 05:47:15PM +0100, Will Deacon wrote:
> > If 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.
> > 
> > Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
> > Signed-off-by: Will Deacon <will@kernel.org>
> > ---
> >  arch/arm64/kernel/cpufeature.c | 20 +++++++++++++++++++-
> >  1 file changed, 19 insertions(+), 1 deletion(-)
> > 
> > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > index 4194a47de62d..b31d7a1eaed6 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -2877,15 +2877,33 @@ void __init setup_cpu_features(void)
> >  
> >  static int enable_mismatched_32bit_el0(unsigned int cpu)
> >  {
> > +	static int lucky_winner = -1;
> 
> This is cute, but could we please give it a meaningful name, e.g.
> `pinned_cpu` ?

I really don't see the problem, nor why it's "cute".

Tell you what, I'll add a comment instead:

	/*
	 * The first 32-bit-capable CPU we detected and so can no longer
	 * be offlined by userspace. -1 indicates we haven't yet onlined
	 * a 32-bit-capable CPU.
	 */

> >  	struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
> >  	bool cpu_32bit = id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0);
> >  
> >  	if (cpu_32bit) {
> >  		cpumask_set_cpu(cpu, cpu_32bit_el0_mask);
> >  		static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0);
> > -		setup_elf_hwcaps(compat_elf_hwcaps);
> >  	}
> >  
> > +	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;
> > +	setup_elf_hwcaps(compat_elf_hwcaps);
> > +	pr_info("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n",
> > +		cpu, lucky_winner);
> >  	return 0;
> >  }
> 
> I guess this is going to play havoc with kexec and hibernate. :/

The kernel can still offline the CPUs (see the whole freezer mess that I
linked to in the cover letter). What specific havoc are you thinking of?

Will

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support
  2021-06-03 12:37   ` Mark Rutland
@ 2021-06-03 17:44     ` Will Deacon
  2021-06-04  9:38       ` Mark Rutland
  0 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-03 17:44 UTC (permalink / raw)
  To: Mark Rutland
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Thu, Jun 03, 2021 at 01:37:15PM +0100, Mark Rutland wrote:
> On Wed, Jun 02, 2021 at 05:47:02PM +0100, Will Deacon wrote:
> > When 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.
> > 
> > Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
> > Signed-off-by: Will Deacon <will@kernel.org>
> > ---
> >  arch/arm64/include/asm/cpufeature.h |   8 +-
> >  arch/arm64/kernel/cpufeature.c      | 114 ++++++++++++++++++++++++----
> >  arch/arm64/tools/cpucaps            |   3 +-
> >  3 files changed, 110 insertions(+), 15 deletions(-)
> > 
> > diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> > index 338840c00e8e..603bf4160cd6 100644
> > --- a/arch/arm64/include/asm/cpufeature.h
> > +++ b/arch/arm64/include/asm/cpufeature.h
> > @@ -630,9 +630,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);
> >  }
> 
> Note that read_sanitised_ftr_reg() has to do a bsearch() to find the
> arm64_ftr_reg, so this will make system_32bit_el0_cpumask() a fair
> amount more expensive than it needs to be.

I seriously doubt that it matters, but it did come up before and I proposed
a potential solution if it's actually a concern:

https://lore.kernel.org/r/20201202172727.GC29813@willie-the-truck

so if you can show that it's a problem, we can resurrect something like
that.

> Can we follow the pattern we have for arm64_ftr_reg_ctrel0, and have a
> arm64_ftr_reg_id_aa64pfr0_el1 that we can address directly here?

I mean, clearly its possible, but based on what data?

> That said. I reckon this could be much cleaner if we maintained separate
> caps:
> 
> ARM64_ALL_CPUS_HAVE_32BIT_EL0
> ARM64_SOME_CPUS_HAVE_32BIT_EL0
> 
> ... and allow arm64_mismatched_32bit_el0 to be set dependent on
> ARM64_SOME_CPUS_HAVE_32BIT_EL0. With that, this can be simplified to:
> 
> static inline bool system_supports_32bit_el0(void)
> {
> 	return (cpus_have_const_cap(ARM64_ALL_CPUS_HAVE_32BIT_EL0)) ||
> 		static_branch_unlikely(&arm64_mismatched_32bit_el0))

Something similar was discussed in November last year but this falls
apart with late onlining because its not generally possible to tell whether
you've seen all the CPUs or not.

I'm mostly reluctant to make significant changes based on cosmetic
preferences because testing and debugging this with all the system
combinations is really difficult. Do you see a functional issue with what
I have?

> >  static inline bool system_supports_4kb_granule(void)
> > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > index a4db25cd7122..4194a47de62d 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -107,6 +107,24 @@ DECLARE_BITMAP(boot_capabilities, ARM64_NPATCHABLE);
> >  bool arm64_use_ng_mappings = false;
> >  EXPORT_SYMBOL(arm64_use_ng_mappings);
> >  
> > +/*
> > + * Permit PER_LINUX32 and execve() of 32-bit binaries even if not all CPUs
> > + * support it?
> > + */
> > +static bool __read_mostly allow_mismatched_32bit_el0;
> > +
> > +/*
> > + * Static branch enabled only if allow_mismatched_32bit_el0 is set and we have
> > + * seen at least one CPU capable of 32-bit EL0.
> > + */
> > +DEFINE_STATIC_KEY_FALSE(arm64_mismatched_32bit_el0);
> > +
> > +/*
> > + * Mask of CPUs supporting 32-bit EL0.
> > + * Only valid if arm64_mismatched_32bit_el0 is enabled.
> > + */
> > +static cpumask_var_t cpu_32bit_el0_mask __cpumask_var_read_mostly;
> > +
> >  /*
> >   * Flag to indicate if we have computed the system wide
> >   * capabilities based on the boot time active CPUs. This
> > @@ -767,7 +785,7 @@ static void __init sort_ftr_regs(void)
> >   * Any bits that are not covered by an arm64_ftr_bits entry are considered
> >   * RES0 for the system-wide value, and must strictly match.
> >   */
> > -static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
> > +static void init_cpu_ftr_reg(u32 sys_reg, u64 new)
> >  {
> >  	u64 val = 0;
> >  	u64 strict_mask = ~0x0ULL;
> > @@ -863,7 +881,7 @@ static void __init init_cpu_hwcaps_indirect_list(void)
> >  
> >  static void __init setup_boot_cpu_capabilities(void);
> >  
> > -static void __init init_32bit_cpu_features(struct cpuinfo_32bit *info)
> > +static void init_32bit_cpu_features(struct cpuinfo_32bit *info)
> >  {
> >  	init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
> >  	init_cpu_ftr_reg(SYS_ID_DFR1_EL1, info->reg_id_dfr1);
> > @@ -979,6 +997,22 @@ static void relax_cpu_ftr_reg(u32 sys_id, int field)
> >  	WARN_ON(!ftrp->width);
> >  }
> >  
> > +static void update_mismatched_32bit_el0_cpu_features(struct cpuinfo_arm64 *info,
> > +						     struct cpuinfo_arm64 *boot)
> 
> Could we s/update/lazy_init/ here?
> 
> IIUC this caters for the case where CPU0 doesn't have AArch32 but a
> secondary does. That, and the naming looks odd in update_cpu_features()
> when we have:
> 
> 	update_mismatched_32bit_el0_cpu_features(...)
> 	update_32bit_cpu_features(...);

Sure thing, I'll rename this.

> > +{
> > +	static bool boot_cpu_32bit_regs_overridden = false;
> > +
> > +	if (!allow_mismatched_32bit_el0 || boot_cpu_32bit_regs_overridden)
> > +		return;
> > +
> > +	if (id_aa64pfr0_32bit_el0(boot->reg_id_aa64pfr0))
> > +		return;
> > +
> > +	boot->aarch32 = info->aarch32;
> > +	init_32bit_cpu_features(&boot->aarch32);
> > +	boot_cpu_32bit_regs_overridden = true;
> > +}
> 
> Can't we share this with the boot CPU path if we do:
> 
> /*
>  * Initialize the common AArch32 features on the first CPU with AArch32.
>  */
> static void lazy_init_32bit_el0_cpu_features(struct cpuinfo_arm64 *info,
> 					     struct cpuinfo_arm64 *boot)
> {
> 	static bool initialised = false;
> 	if (initialised || !id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
> 		return;
> 	
> 	boot->aarch32 = info->aarch32;
> 	init_32bit_cpu_features(&boot->aarch32);
> 	initiaised = true;
> }
> 
> ... or is the allow_mismatched_32bit_el0 check necessary for late
> hotplug?

Interesting. I think this works, but I'm wary that it results in the
32-bit features of a 64-bit-only boot CPU being populated using the first
32-bit-capable CPU even if we're not running with allow_mismatched_32bit_el0
enabled. That feels like setting ourselves up for future bugs. For example,
compat_has_neon() would unexpectedly return true even though 32-bit execve()
would be forbidden.

> > +static int __init init_32bit_el0_mask(void)
> > +{
> > +	if (!allow_mismatched_32bit_el0)
> > +		return 0;
> > +
> > +	if (!zalloc_cpumask_var(&cpu_32bit_el0_mask, GFP_KERNEL))
> > +		return -ENOMEM;
> > +
> > +	return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> > +				 "arm64/mismatched_32bit_el0:online",
> > +				 enable_mismatched_32bit_el0, NULL);
> > +}
> 
> Shouldn't we clear this on a hot-unplug?

No, the mask is intended to show all of the 32-bit CPUs we've seen, so that
userspace can use it to construct 32-bit-capable affinity masks. Having a
race with hot-unplug doesn't help with that and sched_setaffinity() is quite
happy with offline CPUs in the provided mask.

Additionally, the underlying mask is used to implement task_cpu_possible(),
so if we wanted to remove CPUs then we'd need a separate mask just for
sysfs (which _already_ exposes the online mask separately).

Will

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support
  2021-06-03 17:44     ` Will Deacon
@ 2021-06-04  9:38       ` Mark Rutland
  2021-06-04 11:05         ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Mark Rutland @ 2021-06-04  9:38 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Thu, Jun 03, 2021 at 06:44:14PM +0100, Will Deacon wrote:
> On Thu, Jun 03, 2021 at 01:37:15PM +0100, Mark Rutland wrote:
> > On Wed, Jun 02, 2021 at 05:47:02PM +0100, Will Deacon wrote:
> > > When 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.
> > > 
> > > Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
> > > Signed-off-by: Will Deacon <will@kernel.org>
> > > ---
> > >  arch/arm64/include/asm/cpufeature.h |   8 +-
> > >  arch/arm64/kernel/cpufeature.c      | 114 ++++++++++++++++++++++++----
> > >  arch/arm64/tools/cpucaps            |   3 +-
> > >  3 files changed, 110 insertions(+), 15 deletions(-)
> > > 
> > > diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> > > index 338840c00e8e..603bf4160cd6 100644
> > > --- a/arch/arm64/include/asm/cpufeature.h
> > > +++ b/arch/arm64/include/asm/cpufeature.h
> > > @@ -630,9 +630,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);
> > >  }
> > 
> > Note that read_sanitised_ftr_reg() has to do a bsearch() to find the
> > arm64_ftr_reg, so this will make system_32bit_el0_cpumask() a fair
> > amount more expensive than it needs to be.
> 
> I seriously doubt that it matters, but it did come up before and I proposed
> a potential solution if it's actually a concern:
> 
> https://lore.kernel.org/r/20201202172727.GC29813@willie-the-truck
> 
> so if you can show that it's a problem, we can resurrect something like
> that.

I'm happy to leave that for future. I raised this because elsewhere this
is an issue when we need to avoid instrumentation; if that's not a
concern here on any path then I am not aware of a functional issue.

> > Can we follow the pattern we have for arm64_ftr_reg_ctrel0, and have a
> > arm64_ftr_reg_id_aa64pfr0_el1 that we can address directly here?
> 
> I mean, clearly its possible, but based on what data?
> 
> > That said. I reckon this could be much cleaner if we maintained separate
> > caps:
> > 
> > ARM64_ALL_CPUS_HAVE_32BIT_EL0
> > ARM64_SOME_CPUS_HAVE_32BIT_EL0
> > 
> > ... and allow arm64_mismatched_32bit_el0 to be set dependent on
> > ARM64_SOME_CPUS_HAVE_32BIT_EL0. With that, this can be simplified to:
> > 
> > static inline bool system_supports_32bit_el0(void)
> > {
> > 	return (cpus_have_const_cap(ARM64_ALL_CPUS_HAVE_32BIT_EL0)) ||
> > 		static_branch_unlikely(&arm64_mismatched_32bit_el0))
> 
> Something similar was discussed in November last year but this falls
> apart with late onlining because its not generally possible to tell whether
> you've seen all the CPUs or not.

Ah; is that for when your boot CPU set is all AArch32-capable, but a
late-onlined CPU is not?

I assume that we require at least one of the set of boot CPUs to be
AArch32 cpable, and don't settle the compat hwcaps after userspace has
started.

> I'm mostly reluctant to make significant changes based on cosmetic
> preferences because testing and debugging this with all the system
> combinations is really difficult.

Fair enough.

> > > +{
> > > +	static bool boot_cpu_32bit_regs_overridden = false;
> > > +
> > > +	if (!allow_mismatched_32bit_el0 || boot_cpu_32bit_regs_overridden)
> > > +		return;
> > > +
> > > +	if (id_aa64pfr0_32bit_el0(boot->reg_id_aa64pfr0))
> > > +		return;
> > > +
> > > +	boot->aarch32 = info->aarch32;
> > > +	init_32bit_cpu_features(&boot->aarch32);
> > > +	boot_cpu_32bit_regs_overridden = true;
> > > +}
> > 
> > Can't we share this with the boot CPU path if we do:
> > 
> > /*
> >  * Initialize the common AArch32 features on the first CPU with AArch32.
> >  */
> > static void lazy_init_32bit_el0_cpu_features(struct cpuinfo_arm64 *info,
> > 					     struct cpuinfo_arm64 *boot)
> > {
> > 	static bool initialised = false;
> > 	if (initialised || !id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
> > 		return;
> > 	
> > 	boot->aarch32 = info->aarch32;
> > 	init_32bit_cpu_features(&boot->aarch32);
> > 	initiaised = true;
> > }
> > 
> > ... or is the allow_mismatched_32bit_el0 check necessary for late
> > hotplug?
> 
> Interesting. I think this works, but I'm wary that it results in the
> 32-bit features of a 64-bit-only boot CPU being populated using the first
> 32-bit-capable CPU even if we're not running with allow_mismatched_32bit_el0
> enabled. That feels like setting ourselves up for future bugs. For example,
> compat_has_neon() would unexpectedly return true even though 32-bit execve()
> would be forbidden.

Sure; I had not considered that case.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 15/19] arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system
  2021-06-03 17:40     ` Will Deacon
@ 2021-06-04  9:49       ` Mark Rutland
  2021-06-04 12:14         ` Qais Yousef
  0 siblings, 1 reply; 48+ messages in thread
From: Mark Rutland @ 2021-06-04  9:49 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Thu, Jun 03, 2021 at 06:40:57PM +0100, Will Deacon wrote:
> On Thu, Jun 03, 2021 at 01:58:56PM +0100, Mark Rutland wrote:
> > On Wed, Jun 02, 2021 at 05:47:15PM +0100, Will Deacon wrote:
> > > If 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.
> > > 
> > > Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
> > > Signed-off-by: Will Deacon <will@kernel.org>
> > > ---
> > >  arch/arm64/kernel/cpufeature.c | 20 +++++++++++++++++++-
> > >  1 file changed, 19 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > > index 4194a47de62d..b31d7a1eaed6 100644
> > > --- a/arch/arm64/kernel/cpufeature.c
> > > +++ b/arch/arm64/kernel/cpufeature.c
> > > @@ -2877,15 +2877,33 @@ void __init setup_cpu_features(void)
> > >  
> > >  static int enable_mismatched_32bit_el0(unsigned int cpu)
> > >  {
> > > +	static int lucky_winner = -1;
> > 
> > This is cute, but could we please give it a meaningful name, e.g.
> > `pinned_cpu` ?
> 
> I really don't see the problem, nor why it's "cute".
> 
> Tell you what, I'll add a comment instead:
> 
> 	/*
> 	 * The first 32-bit-capable CPU we detected and so can no longer
> 	 * be offlined by userspace. -1 indicates we haven't yet onlined
> 	 * a 32-bit-capable CPU.
> 	 */

Thanks for the comment; that's helpful.

However, my concern here is that when we inevitably have to discuss this
with others in future, "lucky winner" is jarring (and also unclear to
those where English is not their native language). For clarity, it would
be really nice to use a term like "cpu", "chosen_cpu", "pinned_cpu",
etc.

However, you're the maintainer; choose what you think is appropriate.

> > >  	struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
> > >  	bool cpu_32bit = id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0);
> > >  
> > >  	if (cpu_32bit) {
> > >  		cpumask_set_cpu(cpu, cpu_32bit_el0_mask);
> > >  		static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0);
> > > -		setup_elf_hwcaps(compat_elf_hwcaps);
> > >  	}
> > >  
> > > +	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;
> > > +	setup_elf_hwcaps(compat_elf_hwcaps);
> > > +	pr_info("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n",
> > > +		cpu, lucky_winner);
> > >  	return 0;
> > >  }
> > 
> > I guess this is going to play havoc with kexec and hibernate. :/
> 
> The kernel can still offline the CPUs (see the whole freezer mess that I
> linked to in the cover letter). What specific havoc are you thinking of?

Ah. If this is just inhibiting userspace-driven offlining, that sounds
fine.

For kexec, I was concerned that either this would inhibit kexec, or
smp_shutdown_nonboot_cpus() would fail to offline the pinned CPU, and
that'd trigger a BUG(), which would be unfortunate.

For hibernate, the equivalent is freeze_secondary_cpus(), which I guess
is dealt with by the freezer bits you mention.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support
  2021-06-04  9:38       ` Mark Rutland
@ 2021-06-04 11:05         ` Will Deacon
  2021-06-04 12:04           ` Mark Rutland
  0 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-04 11:05 UTC (permalink / raw)
  To: Mark Rutland
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Fri, Jun 04, 2021 at 10:38:08AM +0100, Mark Rutland wrote:
> On Thu, Jun 03, 2021 at 06:44:14PM +0100, Will Deacon wrote:
> > On Thu, Jun 03, 2021 at 01:37:15PM +0100, Mark Rutland wrote:
> > > On Wed, Jun 02, 2021 at 05:47:02PM +0100, Will Deacon wrote:
> > > > diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> > > > index 338840c00e8e..603bf4160cd6 100644
> > > > --- a/arch/arm64/include/asm/cpufeature.h
> > > > +++ b/arch/arm64/include/asm/cpufeature.h
> > > > @@ -630,9 +630,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);
> > > >  }
> > > 
> > > Note that read_sanitised_ftr_reg() has to do a bsearch() to find the
> > > arm64_ftr_reg, so this will make system_32bit_el0_cpumask() a fair
> > > amount more expensive than it needs to be.
> > 
> > I seriously doubt that it matters, but it did come up before and I proposed
> > a potential solution if it's actually a concern:
> > 
> > https://lore.kernel.org/r/20201202172727.GC29813@willie-the-truck
> > 
> > so if you can show that it's a problem, we can resurrect something like
> > that.
> 
> I'm happy to leave that for future. I raised this because elsewhere this
> is an issue when we need to avoid instrumentation; if that's not a
> concern here on any path then I am not aware of a functional issue.

I can't think of a reason why instrumentation would be an issue for any of
the current callers, but that's a good point to bear in mind.

> > > That said. I reckon this could be much cleaner if we maintained separate
> > > caps:
> > > 
> > > ARM64_ALL_CPUS_HAVE_32BIT_EL0
> > > ARM64_SOME_CPUS_HAVE_32BIT_EL0
> > > 
> > > ... and allow arm64_mismatched_32bit_el0 to be set dependent on
> > > ARM64_SOME_CPUS_HAVE_32BIT_EL0. With that, this can be simplified to:
> > > 
> > > static inline bool system_supports_32bit_el0(void)
> > > {
> > > 	return (cpus_have_const_cap(ARM64_ALL_CPUS_HAVE_32BIT_EL0)) ||
> > > 		static_branch_unlikely(&arm64_mismatched_32bit_el0))
> > 
> > Something similar was discussed in November last year but this falls
> > apart with late onlining because its not generally possible to tell whether
> > you've seen all the CPUs or not.
> 
> Ah; is that for when your boot CPU set is all AArch32-capable, but a
> late-onlined CPU is not?
> 
> I assume that we require at least one of the set of boot CPUs to be
> AArch32 cpable, and don't settle the compat hwcaps after userspace has
> started.

Heh, you assume wrong :)

When we allow the mismatch, then we do actually defer initialisation of
the compat hwcaps until we see a 32-bit CPU. That's fine, as they won't
be visible to userspace until then anyway (PER_LINUX32 is unavailable).

Will

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support
  2021-06-04 11:05         ` Will Deacon
@ 2021-06-04 12:04           ` Mark Rutland
  2021-06-04 13:50             ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Mark Rutland @ 2021-06-04 12:04 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Fri, Jun 04, 2021 at 12:05:27PM +0100, Will Deacon wrote:
> On Fri, Jun 04, 2021 at 10:38:08AM +0100, Mark Rutland wrote:
> > On Thu, Jun 03, 2021 at 06:44:14PM +0100, Will Deacon wrote:
> > > On Thu, Jun 03, 2021 at 01:37:15PM +0100, Mark Rutland wrote:
> > > > On Wed, Jun 02, 2021 at 05:47:02PM +0100, Will Deacon wrote:
> > > > > diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h

> > > > That said. I reckon this could be much cleaner if we maintained separate
> > > > caps:
> > > > 
> > > > ARM64_ALL_CPUS_HAVE_32BIT_EL0
> > > > ARM64_SOME_CPUS_HAVE_32BIT_EL0
> > > > 
> > > > ... and allow arm64_mismatched_32bit_el0 to be set dependent on
> > > > ARM64_SOME_CPUS_HAVE_32BIT_EL0. With that, this can be simplified to:
> > > > 
> > > > static inline bool system_supports_32bit_el0(void)
> > > > {
> > > > 	return (cpus_have_const_cap(ARM64_ALL_CPUS_HAVE_32BIT_EL0)) ||
> > > > 		static_branch_unlikely(&arm64_mismatched_32bit_el0))
> > > 
> > > Something similar was discussed in November last year but this falls
> > > apart with late onlining because its not generally possible to tell whether
> > > you've seen all the CPUs or not.
> > 
> > Ah; is that for when your boot CPU set is all AArch32-capable, but a
> > late-onlined CPU is not?
> > 
> > I assume that we require at least one of the set of boot CPUs to be
> > AArch32 cpable, and don't settle the compat hwcaps after userspace has
> > started.
> 
> Heh, you assume wrong :)
> 
> When we allow the mismatch, then we do actually defer initialisation of
> the compat hwcaps until we see a 32-bit CPU. That's fine, as they won't
> be visible to userspace until then anyway (PER_LINUX32 is unavailable).

That sounds quite scary, to me, though I don't have a concrete problem
to hand. :/

Do we really need to support initializing that so late? For all other
caps we've settled things when the boot CPUs come up, and it's
unfortunate to have to treat this differently.

I'll go see if there's anything that's liable to break today.

Thanks,
Mark

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 15/19] arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system
  2021-06-04  9:49       ` Mark Rutland
@ 2021-06-04 12:14         ` Qais Yousef
  0 siblings, 0 replies; 48+ messages in thread
From: Qais Yousef @ 2021-06-04 12:14 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Will Deacon, linux-arm-kernel, linux-arch, linux-kernel,
	Catalin Marinas, Marc Zyngier, Greg Kroah-Hartman,
	Peter Zijlstra, Morten Rasmussen, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On 06/04/21 10:49, Mark Rutland wrote:
> On Thu, Jun 03, 2021 at 06:40:57PM +0100, Will Deacon wrote:
> > On Thu, Jun 03, 2021 at 01:58:56PM +0100, Mark Rutland wrote:
> > > On Wed, Jun 02, 2021 at 05:47:15PM +0100, Will Deacon wrote:
> > > > If 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.
> > > > 
> > > > Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
> > > > Signed-off-by: Will Deacon <will@kernel.org>
> > > > ---
> > > >  arch/arm64/kernel/cpufeature.c | 20 +++++++++++++++++++-
> > > >  1 file changed, 19 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > > > index 4194a47de62d..b31d7a1eaed6 100644
> > > > --- a/arch/arm64/kernel/cpufeature.c
> > > > +++ b/arch/arm64/kernel/cpufeature.c
> > > > @@ -2877,15 +2877,33 @@ void __init setup_cpu_features(void)
> > > >  
> > > >  static int enable_mismatched_32bit_el0(unsigned int cpu)
> > > >  {
> > > > +	static int lucky_winner = -1;
> > > 
> > > This is cute, but could we please give it a meaningful name, e.g.
> > > `pinned_cpu` ?
> > 
> > I really don't see the problem, nor why it's "cute".
> > 
> > Tell you what, I'll add a comment instead:
> > 
> > 	/*
> > 	 * The first 32-bit-capable CPU we detected and so can no longer
> > 	 * be offlined by userspace. -1 indicates we haven't yet onlined
> > 	 * a 32-bit-capable CPU.
> > 	 */
> 
> Thanks for the comment; that's helpful.
> 
> However, my concern here is that when we inevitably have to discuss this
> with others in future, "lucky winner" is jarring (and also unclear to
> those where English is not their native language). For clarity, it would
> be really nice to use a term like "cpu", "chosen_cpu", "pinned_cpu",
> etc.
> 
> However, you're the maintainer; choose what you think is appropriate.
> 
> > > >  	struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
> > > >  	bool cpu_32bit = id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0);
> > > >  
> > > >  	if (cpu_32bit) {
> > > >  		cpumask_set_cpu(cpu, cpu_32bit_el0_mask);
> > > >  		static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0);
> > > > -		setup_elf_hwcaps(compat_elf_hwcaps);
> > > >  	}
> > > >  
> > > > +	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;
> > > > +	setup_elf_hwcaps(compat_elf_hwcaps);
> > > > +	pr_info("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n",
> > > > +		cpu, lucky_winner);
> > > >  	return 0;
> > > >  }
> > > 
> > > I guess this is going to play havoc with kexec and hibernate. :/
> > 
> > The kernel can still offline the CPUs (see the whole freezer mess that I
> > linked to in the cover letter). What specific havoc are you thinking of?
> 
> Ah. If this is just inhibiting userspace-driven offlining, that sounds
> fine.
> 
> For kexec, I was concerned that either this would inhibit kexec, or
> smp_shutdown_nonboot_cpus() would fail to offline the pinned CPU, and
> that'd trigger a BUG(), which would be unfortunate.
> 
> For hibernate, the equivalent is freeze_secondary_cpus(), which I guess
> is dealt with by the freezer bits you mention.

()->offline_disabled will only block offline requests performed by
device_offline(). kexec, hibernate, suspend/resume use cpu_online/offline()
directly so won't be impacted by that. I have sent patches that make
cpu_online/offline() 'private' and not used or exported outside of cpu
subsystem and the odd support function for arch code. All other users use
device_offline() or add/remove_cpu() now.

I have made sure to test kexec, suspend to disk and ram in my older similar
implementation in the past. So we should be good.

Cheers

--
Qais Yousef

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support
  2021-06-04 12:04           ` Mark Rutland
@ 2021-06-04 13:50             ` Will Deacon
  0 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-04 13:50 UTC (permalink / raw)
  To: Mark Rutland
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, Valentin Schneider, kernel-team

On Fri, Jun 04, 2021 at 01:04:27PM +0100, Mark Rutland wrote:
> On Fri, Jun 04, 2021 at 12:05:27PM +0100, Will Deacon wrote:
> > On Fri, Jun 04, 2021 at 10:38:08AM +0100, Mark Rutland wrote:
> > > On Thu, Jun 03, 2021 at 06:44:14PM +0100, Will Deacon wrote:
> > > > On Thu, Jun 03, 2021 at 01:37:15PM +0100, Mark Rutland wrote:
> > > > > On Wed, Jun 02, 2021 at 05:47:02PM +0100, Will Deacon wrote:
> > > > > > diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> 
> > > > > That said. I reckon this could be much cleaner if we maintained separate
> > > > > caps:
> > > > > 
> > > > > ARM64_ALL_CPUS_HAVE_32BIT_EL0
> > > > > ARM64_SOME_CPUS_HAVE_32BIT_EL0
> > > > > 
> > > > > ... and allow arm64_mismatched_32bit_el0 to be set dependent on
> > > > > ARM64_SOME_CPUS_HAVE_32BIT_EL0. With that, this can be simplified to:
> > > > > 
> > > > > static inline bool system_supports_32bit_el0(void)
> > > > > {
> > > > > 	return (cpus_have_const_cap(ARM64_ALL_CPUS_HAVE_32BIT_EL0)) ||
> > > > > 		static_branch_unlikely(&arm64_mismatched_32bit_el0))
> > > > 
> > > > Something similar was discussed in November last year but this falls
> > > > apart with late onlining because its not generally possible to tell whether
> > > > you've seen all the CPUs or not.
> > > 
> > > Ah; is that for when your boot CPU set is all AArch32-capable, but a
> > > late-onlined CPU is not?
> > > 
> > > I assume that we require at least one of the set of boot CPUs to be
> > > AArch32 cpable, and don't settle the compat hwcaps after userspace has
> > > started.
> > 
> > Heh, you assume wrong :)
> > 
> > When we allow the mismatch, then we do actually defer initialisation of
> > the compat hwcaps until we see a 32-bit CPU. That's fine, as they won't
> > be visible to userspace until then anyway (PER_LINUX32 is unavailable).
> 
> That sounds quite scary, to me, though I don't have a concrete problem
> to hand. :/
> 
> Do we really need to support initializing that so late? For all other
> caps we've settled things when the boot CPUs come up, and it's
> unfortunate to have to treat this differently.

I think it's the nature of the beast, unfortunately. Since we're talking
about multiple generations of SoCs rather than just one oddball design,
then placing artificial restrictions on the boot CPUs doesn't feel like
it will last very long.

> I'll go see if there's anything that's liable to break today.

Please let me know if you find anything.

Will

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 05/19] sched: Introduce task_cpu_possible_mask() to limit fallback rq selection
  2021-06-02 16:47 ` [PATCH v8 05/19] sched: Introduce task_cpu_possible_mask() to limit fallback rq selection Will Deacon
@ 2021-06-04 17:10   ` Valentin Schneider
  2021-06-07 17:04     ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Valentin Schneider @ 2021-06-04 17:10 UTC (permalink / raw)
  To: Will Deacon, linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team

On 02/06/21 17:47, Will Deacon wrote:
> 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.
>
> 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.
>

Nit: Some uses of this mask (cpu_is_allowed(), __set_cpus_allowed_ptr())
don't apply to kthreads. This makes sense for the 32-bit@EL0 faff, but it
wouldn't hurt to point this out somewhere IMO.

Also, that's an odd place for the definitions, but IIRC there isn't a much
better choice.

Reviewed-by: Valentin Schneider <Valentin.Schneider@arm.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 06/19] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
  2021-06-02 16:47 ` [PATCH v8 06/19] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1 Will Deacon
@ 2021-06-04 17:11   ` Valentin Schneider
  2021-06-07 17:20     ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Valentin Schneider @ 2021-06-04 17:11 UTC (permalink / raw)
  To: Will Deacon, linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team, Li Zefan

On 02/06/21 17:47, Will Deacon wrote:
> @@ -3322,9 +3322,13 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
>
>  void cpuset_cpus_allowed_fallback(struct task_struct *tsk)
>  {
> +	const struct cpumask *cs_mask;
> +	const struct cpumask *possible_mask = task_cpu_possible_mask(tsk);
> +
>       rcu_read_lock();
> -	do_set_cpus_allowed(tsk, is_in_v2_mode() ?
> -		task_cs(tsk)->cpus_allowed : cpu_possible_mask);
> +	cs_mask = task_cs(tsk)->cpus_allowed;
> +	if (is_in_v2_mode() && cpumask_subset(cs_mask, possible_mask))
> +		do_set_cpus_allowed(tsk, cs_mask);

Since the task will still go through the is_cpu_allowed() loop in
select_fallback_rq() after this, is the subset check actually required
here?

It would have more merit if cpuset_cpus_allowed_fallback() returned whether
it actually changed the allowed mask or not, in which case we could branch
either to the is_cpu_allowed() loop (as we do unconditionally now), or to
the 'state == possible' switch case.

>       rcu_read_unlock();
>
>       /*
> --
> 2.32.0.rc0.204.g9fa02ecfa5-goog

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 07/19] cpuset: Honour task_cpu_possible_mask() in guarantee_online_cpus()
  2021-06-02 16:47 ` [PATCH v8 07/19] cpuset: Honour task_cpu_possible_mask() in guarantee_online_cpus() Will Deacon
@ 2021-06-04 17:11   ` Valentin Schneider
  0 siblings, 0 replies; 48+ messages in thread
From: Valentin Schneider @ 2021-06-04 17:11 UTC (permalink / raw)
  To: Will Deacon, linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team, Li Zefan

On 02/06/21 17:47, Will Deacon wrote:
> 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 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.
>
> Cc: Li Zefan <lizefan@huawei.com>
> Cc: Tejun Heo <tj@kernel.org>
> Cc: Johannes Weiner <hannes@cmpxchg.org>
> Signed-off-by: Will Deacon <will@kernel.org>

Reviewed-by: Valentin Schneider <Valentin.Schneider@arm.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 08/19] sched: Reject CPU affinity changes based on task_cpu_possible_mask()
  2021-06-02 16:47 ` [PATCH v8 08/19] sched: Reject CPU affinity changes based on task_cpu_possible_mask() Will Deacon
@ 2021-06-04 17:11   ` Valentin Schneider
  2021-06-07 22:43     ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Valentin Schneider @ 2021-06-04 17:11 UTC (permalink / raw)
  To: Will Deacon, linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team

On 02/06/21 17:47, Will Deacon wrote:
> Reject 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.
>
> Reviewed-by: Quentin Perret <qperret@google.com>
> Signed-off-by: Will Deacon <will@kernel.org>

One comment/observation below, but regardless:

Reviewed-by: Valentin Schneider <Valentin.Schneider@arm.com>

> ---
>  kernel/sched/core.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index 0c1b6f1a6c91..b23c7f0ab31a 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -2347,15 +2347,17 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
>                                 u32 flags)
>  {
>       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;
> +	bool kthread = p->flags & PF_KTHREAD;
>
>       rq = task_rq_lock(p, &rf);
>       update_rq_clock(rq);
>
> -	if (p->flags & PF_KTHREAD || is_migration_disabled(p)) {
> +	if (kthread || is_migration_disabled(p)) {
>               /*
>                * Kernel threads are allowed on online && !active CPUs,
>                * however, during cpu-hot-unplug, even these might get pushed
> @@ -2369,6 +2371,11 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
>               cpu_valid_mask = cpu_online_mask;
>       }
>
> +	if (!kthread && !cpumask_subset(new_mask, cpu_allowed_mask)) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +

IIUC this wouldn't be required if guarantee_online_cpus() couldn't build a
mask that extends beyond task_cpu_possible_mask(p): if the new mask doesn't
intersect with that possible mask, it means we're carrying an empty cpumask
and the cpumask_any_and_distribute() below would return nr_cpu_ids, so we'd
bail with -EINVAL.

I don't really see a way around it though due to the expectations behind
guarantee_online_cpus() :/

>       /*
>        * Must re-check here, to close a race against __kthread_bind(),
>        * sched_setaffinity() is not guaranteed to observe the flag.
> --
> 2.32.0.rc0.204.g9fa02ecfa5-goog

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 09/19] sched: Introduce task_struct::user_cpus_ptr to track requested affinity
  2021-06-02 16:47 ` [PATCH v8 09/19] sched: Introduce task_struct::user_cpus_ptr to track requested affinity Will Deacon
@ 2021-06-04 17:12   ` Valentin Schneider
  0 siblings, 0 replies; 48+ messages in thread
From: Valentin Schneider @ 2021-06-04 17:12 UTC (permalink / raw)
  To: Will Deacon, linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team

On 02/06/21 17:47, Will Deacon wrote:
> In preparation for saving and restoring the user-requested CPU affinity
> mask of a task, add a new cpumask_t pointer to 'struct task_struct'.
>
> If the pointer is non-NULL, then the mask is copied across fork() and
> freed on task exit.
>
> Signed-off-by: Will Deacon <will@kernel.org>

Reviewed-by: Valentin Schneider <Valentin.Schneider@arm.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 10/19] sched: Split the guts of sched_setaffinity() into a helper function
  2021-06-02 16:47 ` [PATCH v8 10/19] sched: Split the guts of sched_setaffinity() into a helper function Will Deacon
@ 2021-06-04 17:12   ` Valentin Schneider
  0 siblings, 0 replies; 48+ messages in thread
From: Valentin Schneider @ 2021-06-04 17:12 UTC (permalink / raw)
  To: Will Deacon, linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team

On 02/06/21 17:47, Will Deacon wrote:
> In preparation for replaying user affinity requests using a saved mask,
> split sched_setaffinity() up so that the initial task lookup and
> security checks are only performed when the request is coming directly
> from userspace.
>
> Signed-off-by: Will Deacon <will@kernel.org>

Reviewed-by: Valentin Schneider <Valentin.Schneider@arm.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 11/19] sched: Allow task CPU affinity to be restricted on asymmetric systems
  2021-06-02 16:47 ` [PATCH v8 11/19] sched: Allow task CPU affinity to be restricted on asymmetric systems Will Deacon
@ 2021-06-04 17:12   ` Valentin Schneider
  2021-06-07 22:52     ` Will Deacon
  0 siblings, 1 reply; 48+ messages in thread
From: Valentin Schneider @ 2021-06-04 17:12 UTC (permalink / raw)
  To: Will Deacon, linux-arm-kernel
  Cc: linux-arch, linux-kernel, Will Deacon, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team

On 02/06/21 17:47, Will Deacon wrote:
> +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;
> +	int err;
> +	struct cpumask *user_mask = NULL;
> +
> +	if (!p->user_cpus_ptr) {
> +		user_mask = kmalloc(cpumask_size(), GFP_KERNEL);
> +
> +		if (!user_mask)
> +			return -ENOMEM;
> +	}
> +
> +	rq = task_rq_lock(p, &rf);
> +
> +	/*
> +	 * Forcefully restricting the affinity of a deadline task is
> +	 * likely to cause problems, so fail and noisily override the
> +	 * mask entirely.
> +	 */
> +	if (task_has_dl_policy(p) && dl_bandwidth_enabled()) {
> +		err = -EPERM;
> +		goto err_unlock;
> +	}
> +
> +	if (!cpumask_and(new_mask, &p->cpus_mask, subset_mask)) {
> +		err = -EINVAL;
> +		goto err_unlock;
> +	}
> +
> +	/*
> +	 * We're about to butcher the task affinity, so keep track of what
> +	 * the user asked for in case we're able to restore it later on.
> +	 */
> +	if (user_mask) {
> +		cpumask_copy(user_mask, p->cpus_ptr);
> +		p->user_cpus_ptr = user_mask;
> +	}
> +

Shouldn't that be done before any of the bailouts above, so we can
potentially restore the mask even if we end up forcefully expanding the
affinity?

> +	return __set_cpus_allowed_ptr_locked(p, new_mask, 0, rq, &rf);
> +
> +err_unlock:
> +	task_rq_unlock(rq, p, &rf);
> +	kfree(user_mask);
> +	return err;
> +}
> +
> +/*
> + * Restrict the CPU affinity of task @p so that it is a subset of
> + * task_cpu_possible_mask() and point @p->user_cpu_ptr to a copy of the
> + * old affinity mask. If the resulting mask is empty, we warn and walk
> + * up the cpuset hierarchy until we find a suitable mask.
> + */
> +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);
> +
> +	alloc_cpumask_var(&new_mask, GFP_KERNEL);
> +
> +	/*
> +	 * __migrate_task() can fail silently in the face of concurrent
> +	 * offlining of the chosen destination CPU, so take the hotplug
> +	 * lock to ensure that the migration succeeds.
> +	 */
> +	cpus_read_lock();

I'm thinking this might not be required with:

  http://lore.kernel.org/r/20210526205751.842360-3-valentin.schneider@arm.com

but then again this isn't merged yet :-)

> +	if (!cpumask_available(new_mask))
> +		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));
> +	}
> +
> +	WARN_ON(set_cpus_allowed_ptr(p, override_mask));
> +out_free_mask:
> +	cpus_read_unlock();
> +	free_cpumask_var(new_mask);
> +}
> +
> +static int
> +__sched_setaffinity(struct task_struct *p, const struct cpumask *mask);
> +
> +/*
> + * Restore the affinity of a task @p which was previously restricted by a
> + * call to force_compatible_cpus_allowed_ptr(). This will clear (and free)
> + * @p->user_cpus_ptr.
> + */
> +void relax_compatible_cpus_allowed_ptr(struct task_struct *p)
> +{
> +	unsigned long flags;
> +	struct cpumask *mask = p->user_cpus_ptr;
> +
> +	/*
> +	 * Try to restore the old affinity mask. If this fails, then
> +	 * we free the mask explicitly to avoid it being inherited across
> +	 * a subsequent fork().
> +	 */
> +	if (!mask || !__sched_setaffinity(p, mask))
> +		return;
> +
> +	raw_spin_lock_irqsave(&p->pi_lock, flags);
> +	release_user_cpus_ptr(p);
> +	raw_spin_unlock_irqrestore(&p->pi_lock, flags);

AFAICT an affinity change can happen between __sched_setaffinity() and
reacquiring the ->pi_lock. Right now this can't be another
force_compatible_cpus_allowed_ptr() because this is only driven by
arch_setup_new_exec() against current, so we should be fine, but here be
dragons.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 05/19] sched: Introduce task_cpu_possible_mask() to limit fallback rq selection
  2021-06-04 17:10   ` Valentin Schneider
@ 2021-06-07 17:04     ` Will Deacon
  0 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-07 17:04 UTC (permalink / raw)
  To: Valentin Schneider
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team

On Fri, Jun 04, 2021 at 06:10:46PM +0100, Valentin Schneider wrote:
> On 02/06/21 17:47, Will Deacon wrote:
> > 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.
> >
> > 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.
> >
> 
> Nit: Some uses of this mask (cpu_is_allowed(), __set_cpus_allowed_ptr())
> don't apply to kthreads. This makes sense for the 32-bit@EL0 faff, but it
> wouldn't hurt to point this out somewhere IMO.

That's a good point: even after these patches, we still assume the kernel
(and therefore kthreads) can run on all CPUs. I'll expand the comment.

> Also, that's an odd place for the definitions, but IIRC there isn't a much
> better choice.

Short of adding a new header just for this, I couldn't find anything, no.

> Reviewed-by: Valentin Schneider <Valentin.Schneider@arm.com>

Thanks!

Will

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 06/19] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
  2021-06-04 17:11   ` Valentin Schneider
@ 2021-06-07 17:20     ` Will Deacon
  2021-06-10 10:20       ` Valentin Schneider
  0 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-07 17:20 UTC (permalink / raw)
  To: Valentin Schneider
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team, Li Zefan

On Fri, Jun 04, 2021 at 06:11:03PM +0100, Valentin Schneider wrote:
> On 02/06/21 17:47, Will Deacon wrote:
> > @@ -3322,9 +3322,13 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
> >
> >  void cpuset_cpus_allowed_fallback(struct task_struct *tsk)
> >  {
> > +	const struct cpumask *cs_mask;
> > +	const struct cpumask *possible_mask = task_cpu_possible_mask(tsk);
> > +
> >       rcu_read_lock();
> > -	do_set_cpus_allowed(tsk, is_in_v2_mode() ?
> > -		task_cs(tsk)->cpus_allowed : cpu_possible_mask);
> > +	cs_mask = task_cs(tsk)->cpus_allowed;
> > +	if (is_in_v2_mode() && cpumask_subset(cs_mask, possible_mask))
> > +		do_set_cpus_allowed(tsk, cs_mask);
> 
> Since the task will still go through the is_cpu_allowed() loop in
> select_fallback_rq() after this, is the subset check actually required
> here?

Yes, I think it's needed. do_set_cpus_allowed() doesn't do any checking
against the task_cpu_possible_mask, so if we returned to
select_fallback_rq() with a mask containing a mixture of 32-bit-capable and
64-bit-only CPUs then we'd end up setting an affinity mask for a 32-bit
task which contains 64-bit-only cores.

> It would have more merit if cpuset_cpus_allowed_fallback() returned whether
> it actually changed the allowed mask or not, in which case we could branch
> either to the is_cpu_allowed() loop (as we do unconditionally now), or to
> the 'state == possible' switch case.

I think this is a cleanup, so I can include it as a separate patch (see
below).

Will

--->8

diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index 414a8e694413..d2b9c41c8edf 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -59,7 +59,7 @@ extern void cpuset_wait_for_hotplug(void);
 extern void cpuset_read_lock(void);
 extern void cpuset_read_unlock(void);
 extern void cpuset_cpus_allowed(struct task_struct *p, struct cpumask *mask);
-extern void cpuset_cpus_allowed_fallback(struct task_struct *p);
+extern bool cpuset_cpus_allowed_fallback(struct task_struct *p);
 extern nodemask_t cpuset_mems_allowed(struct task_struct *p);
 #define cpuset_current_mems_allowed (current->mems_allowed)
 void cpuset_init_current_mems_allowed(void);
@@ -188,8 +188,9 @@ static inline void cpuset_cpus_allowed(struct task_struct *p,
        cpumask_copy(mask, task_cpu_possible_mask(p));
 }
 
-static inline void cpuset_cpus_allowed_fallback(struct task_struct *p)
+static inline bool cpuset_cpus_allowed_fallback(struct task_struct *p)
 {
+       return false;
 }
 
 static inline nodemask_t cpuset_mems_allowed(struct task_struct *p)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 4e7c271e3800..a6bab2259f98 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -3327,17 +3327,22 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
  * which will not contain a sane cpumask during cases such as cpu hotplugging.
  * This is the absolute last resort for the scheduler and it is only used if
  * _every_ other avenue has been traveled.
+ *
+ * Returns true if the affinity of @tsk was changed, false otherwise.
  **/
 
-void cpuset_cpus_allowed_fallback(struct task_struct *tsk)
+bool cpuset_cpus_allowed_fallback(struct task_struct *tsk)
 {
        const struct cpumask *cs_mask;
+       bool changed = false;
        const struct cpumask *possible_mask = task_cpu_possible_mask(tsk);
 
        rcu_read_lock();
        cs_mask = task_cs(tsk)->cpus_allowed;
-       if (is_in_v2_mode() && cpumask_subset(cs_mask, possible_mask))
+       if (is_in_v2_mode() && cpumask_subset(cs_mask, possible_mask)) {
                do_set_cpus_allowed(tsk, cs_mask);
+               changed = true;
+       }
        rcu_read_unlock();
 
        /*
@@ -3357,6 +3362,7 @@ void cpuset_cpus_allowed_fallback(struct task_struct *tsk)
         * select_fallback_rq() will fix things ups and set cpu_possible_mask
         * if required.
         */
+       return changed;
 }
 
 void __init cpuset_init_current_mems_allowed(void)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index fc7de4f955cf..9d7a74a07632 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2951,8 +2951,7 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
                /* No more Mr. Nice Guy. */
                switch (state) {
                case cpuset:
-                       if (IS_ENABLED(CONFIG_CPUSETS)) {
-                               cpuset_cpus_allowed_fallback(p);
+                       if (cpuset_cpus_allowed_fallback(p)) {
                                state = possible;
                                break;
                        }


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 08/19] sched: Reject CPU affinity changes based on task_cpu_possible_mask()
  2021-06-04 17:11   ` Valentin Schneider
@ 2021-06-07 22:43     ` Will Deacon
  0 siblings, 0 replies; 48+ messages in thread
From: Will Deacon @ 2021-06-07 22:43 UTC (permalink / raw)
  To: Valentin Schneider
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team

On Fri, Jun 04, 2021 at 06:11:52PM +0100, Valentin Schneider wrote:
> On 02/06/21 17:47, Will Deacon wrote:
> > Reject 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.
> >
> > Reviewed-by: Quentin Perret <qperret@google.com>
> > Signed-off-by: Will Deacon <will@kernel.org>
> 
> One comment/observation below, but regardless:
> 
> Reviewed-by: Valentin Schneider <Valentin.Schneider@arm.com>

Thanks!

> > diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> > index 0c1b6f1a6c91..b23c7f0ab31a 100644
> > --- a/kernel/sched/core.c
> > +++ b/kernel/sched/core.c
> > @@ -2347,15 +2347,17 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
> >                                 u32 flags)
> >  {
> >       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;
> > +	bool kthread = p->flags & PF_KTHREAD;
> >
> >       rq = task_rq_lock(p, &rf);
> >       update_rq_clock(rq);
> >
> > -	if (p->flags & PF_KTHREAD || is_migration_disabled(p)) {
> > +	if (kthread || is_migration_disabled(p)) {
> >               /*
> >                * Kernel threads are allowed on online && !active CPUs,
> >                * however, during cpu-hot-unplug, even these might get pushed
> > @@ -2369,6 +2371,11 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
> >               cpu_valid_mask = cpu_online_mask;
> >       }
> >
> > +	if (!kthread && !cpumask_subset(new_mask, cpu_allowed_mask)) {
> > +		ret = -EINVAL;
> > +		goto out;
> > +	}
> > +
> 
> IIUC this wouldn't be required if guarantee_online_cpus() couldn't build a
> mask that extends beyond task_cpu_possible_mask(p): if the new mask doesn't
> intersect with that possible mask, it means we're carrying an empty cpumask
> and the cpumask_any_and_distribute() below would return nr_cpu_ids, so we'd
> bail with -EINVAL.
> 
> I don't really see a way around it though due to the expectations behind
> guarantee_online_cpus() :/

Mostly agreed. I started out hacking SCA and only then started knocking the
callers on the head. However, given how many callers there are for this
thing, I'm much more comfortable having the mask check there to ensure that
we return an error if the requested mask contains CPUs on which we're unable
to run, and yes, guarantee_online_cpus() is one such caller.

Will

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 11/19] sched: Allow task CPU affinity to be restricted on asymmetric systems
  2021-06-04 17:12   ` Valentin Schneider
@ 2021-06-07 22:52     ` Will Deacon
  2021-06-10 10:20       ` Valentin Schneider
  0 siblings, 1 reply; 48+ messages in thread
From: Will Deacon @ 2021-06-07 22:52 UTC (permalink / raw)
  To: Valentin Schneider
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team

On Fri, Jun 04, 2021 at 06:12:32PM +0100, Valentin Schneider wrote:
> On 02/06/21 17:47, Will Deacon wrote:
> > +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;
> > +	int err;
> > +	struct cpumask *user_mask = NULL;
> > +
> > +	if (!p->user_cpus_ptr) {
> > +		user_mask = kmalloc(cpumask_size(), GFP_KERNEL);
> > +
> > +		if (!user_mask)
> > +			return -ENOMEM;
> > +	}
> > +
> > +	rq = task_rq_lock(p, &rf);
> > +
> > +	/*
> > +	 * Forcefully restricting the affinity of a deadline task is
> > +	 * likely to cause problems, so fail and noisily override the
> > +	 * mask entirely.
> > +	 */
> > +	if (task_has_dl_policy(p) && dl_bandwidth_enabled()) {
> > +		err = -EPERM;
> > +		goto err_unlock;
> > +	}
> > +
> > +	if (!cpumask_and(new_mask, &p->cpus_mask, subset_mask)) {
> > +		err = -EINVAL;
> > +		goto err_unlock;
> > +	}
> > +
> > +	/*
> > +	 * We're about to butcher the task affinity, so keep track of what
> > +	 * the user asked for in case we're able to restore it later on.
> > +	 */
> > +	if (user_mask) {
> > +		cpumask_copy(user_mask, p->cpus_ptr);
> > +		p->user_cpus_ptr = user_mask;
> > +	}
> > +
> 
> Shouldn't that be done before any of the bailouts above, so we can
> potentially restore the mask even if we end up forcefully expanding the
> affinity?

I don't think so. I deliberately only track the old mask if we've managed
to take a subset for the 32-bit task. If we end up having to override the
mask entirely, then I treat it the same way as an explicit affinity change
(only with a warning printed) and don't then try to restore the old mask --
it feels like we'd be overriding the affinity twice if we tried to do that.

> > +	return __set_cpus_allowed_ptr_locked(p, new_mask, 0, rq, &rf);
> > +
> > +err_unlock:
> > +	task_rq_unlock(rq, p, &rf);
> > +	kfree(user_mask);
> > +	return err;
> > +}
> > +
> > +/*
> > + * Restrict the CPU affinity of task @p so that it is a subset of
> > + * task_cpu_possible_mask() and point @p->user_cpu_ptr to a copy of the
> > + * old affinity mask. If the resulting mask is empty, we warn and walk
> > + * up the cpuset hierarchy until we find a suitable mask.
> > + */
> > +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);
> > +
> > +	alloc_cpumask_var(&new_mask, GFP_KERNEL);
> > +
> > +	/*
> > +	 * __migrate_task() can fail silently in the face of concurrent
> > +	 * offlining of the chosen destination CPU, so take the hotplug
> > +	 * lock to ensure that the migration succeeds.
> > +	 */
> > +	cpus_read_lock();
> 
> I'm thinking this might not be required with:
> 
>   http://lore.kernel.org/r/20210526205751.842360-3-valentin.schneider@arm.com
> 
> but then again this isn't merged yet :-)

Agreed, if that patch does what it says on the tin ;)

I need to digest your reply to me, as this is mind-bending stuff.

> > +static int
> > +__sched_setaffinity(struct task_struct *p, const struct cpumask *mask);
> > +
> > +/*
> > + * Restore the affinity of a task @p which was previously restricted by a
> > + * call to force_compatible_cpus_allowed_ptr(). This will clear (and free)
> > + * @p->user_cpus_ptr.
> > + */
> > +void relax_compatible_cpus_allowed_ptr(struct task_struct *p)
> > +{
> > +	unsigned long flags;
> > +	struct cpumask *mask = p->user_cpus_ptr;
> > +
> > +	/*
> > +	 * Try to restore the old affinity mask. If this fails, then
> > +	 * we free the mask explicitly to avoid it being inherited across
> > +	 * a subsequent fork().
> > +	 */
> > +	if (!mask || !__sched_setaffinity(p, mask))
> > +		return;
> > +
> > +	raw_spin_lock_irqsave(&p->pi_lock, flags);
> > +	release_user_cpus_ptr(p);
> > +	raw_spin_unlock_irqrestore(&p->pi_lock, flags);
> 
> AFAICT an affinity change can happen between __sched_setaffinity() and
> reacquiring the ->pi_lock. Right now this can't be another
> force_compatible_cpus_allowed_ptr() because this is only driven by
> arch_setup_new_exec() against current, so we should be fine, but here be
> dragons.

That's a good point. I'll add a comment for now, since I'm not sure who
else might end up using this in future. Generally it's pretty agnostic to
how it's being used, but we're certainly relying on the serialisation of
restrict/relax calls.

Will

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 06/19] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1
  2021-06-07 17:20     ` Will Deacon
@ 2021-06-10 10:20       ` Valentin Schneider
  0 siblings, 0 replies; 48+ messages in thread
From: Valentin Schneider @ 2021-06-10 10:20 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team, Li Zefan

On 07/06/21 18:20, Will Deacon wrote:
> On Fri, Jun 04, 2021 at 06:11:03PM +0100, Valentin Schneider wrote:
>> On 02/06/21 17:47, Will Deacon wrote:
>> > @@ -3322,9 +3322,13 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
>> >
>> >  void cpuset_cpus_allowed_fallback(struct task_struct *tsk)
>> >  {
>> > +	const struct cpumask *cs_mask;
>> > +	const struct cpumask *possible_mask = task_cpu_possible_mask(tsk);
>> > +
>> >       rcu_read_lock();
>> > -	do_set_cpus_allowed(tsk, is_in_v2_mode() ?
>> > -		task_cs(tsk)->cpus_allowed : cpu_possible_mask);
>> > +	cs_mask = task_cs(tsk)->cpus_allowed;
>> > +	if (is_in_v2_mode() && cpumask_subset(cs_mask, possible_mask))
>> > +		do_set_cpus_allowed(tsk, cs_mask);
>>
>> Since the task will still go through the is_cpu_allowed() loop in
>> select_fallback_rq() after this, is the subset check actually required
>> here?
>
> Yes, I think it's needed. do_set_cpus_allowed() doesn't do any checking
> against the task_cpu_possible_mask, so if we returned to
> select_fallback_rq() with a mask containing a mixture of 32-bit-capable and
> 64-bit-only CPUs then we'd end up setting an affinity mask for a 32-bit
> task which contains 64-bit-only cores.
>

Once again, you're right :-)

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v8 11/19] sched: Allow task CPU affinity to be restricted on asymmetric systems
  2021-06-07 22:52     ` Will Deacon
@ 2021-06-10 10:20       ` Valentin Schneider
  0 siblings, 0 replies; 48+ messages in thread
From: Valentin Schneider @ 2021-06-10 10:20 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, linux-arch, linux-kernel, Catalin Marinas,
	Marc Zyngier, Greg Kroah-Hartman, Peter Zijlstra,
	Morten Rasmussen, Qais Yousef, Suren Baghdasaryan,
	Quentin Perret, Tejun Heo, Johannes Weiner, Ingo Molnar,
	Juri Lelli, Vincent Guittot, Rafael J. Wysocki, Dietmar Eggemann,
	Daniel Bristot de Oliveira, kernel-team

On 07/06/21 23:52, Will Deacon wrote:
> On Fri, Jun 04, 2021 at 06:12:32PM +0100, Valentin Schneider wrote:
>> On 02/06/21 17:47, Will Deacon wrote:
>> > +	/*
>> > +	 * Forcefully restricting the affinity of a deadline task is
>> > +	 * likely to cause problems, so fail and noisily override the
>> > +	 * mask entirely.
>> > +	 */
>> > +	if (task_has_dl_policy(p) && dl_bandwidth_enabled()) {
>> > +		err = -EPERM;
>> > +		goto err_unlock;
>> > +	}
>> > +
>> > +	if (!cpumask_and(new_mask, &p->cpus_mask, subset_mask)) {
>> > +		err = -EINVAL;
>> > +		goto err_unlock;
>> > +	}
>> > +
>> > +	/*
>> > +	 * We're about to butcher the task affinity, so keep track of what
>> > +	 * the user asked for in case we're able to restore it later on.
>> > +	 */
>> > +	if (user_mask) {
>> > +		cpumask_copy(user_mask, p->cpus_ptr);
>> > +		p->user_cpus_ptr = user_mask;
>> > +	}
>> > +
>>
>> Shouldn't that be done before any of the bailouts above, so we can
>> potentially restore the mask even if we end up forcefully expanding the
>> affinity?
>
> I don't think so. I deliberately only track the old mask if we've managed
> to take a subset for the 32-bit task. If we end up having to override the
> mask entirely, then I treat it the same way as an explicit affinity change
> (only with a warning printed) and don't then try to restore the old mask --
> it feels like we'd be overriding the affinity twice if we tried to do that.
>

Put in this way, it does make sense to me. Thanks!

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2021-06-10 10:20 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-02 16:47 [PATCH v8 00/19] Add support for 32-bit tasks on asymmetric AArch32 systems Will Deacon
2021-06-02 16:47 ` [PATCH v8 01/19] arm64: cpuinfo: Split AArch32 registers out into a separate struct Will Deacon
2021-06-03 12:38   ` Mark Rutland
2021-06-03 17:24     ` Will Deacon
2021-06-02 16:47 ` [PATCH v8 02/19] arm64: Allow mismatched 32-bit EL0 support Will Deacon
2021-06-03 12:37   ` Mark Rutland
2021-06-03 17:44     ` Will Deacon
2021-06-04  9:38       ` Mark Rutland
2021-06-04 11:05         ` Will Deacon
2021-06-04 12:04           ` Mark Rutland
2021-06-04 13:50             ` Will Deacon
2021-06-02 16:47 ` [PATCH v8 03/19] KVM: arm64: Kill 32-bit vCPUs on systems with mismatched " Will Deacon
2021-06-02 16:47 ` [PATCH v8 04/19] arm64: Kill 32-bit applications scheduled on 64-bit-only CPUs Will Deacon
2021-06-02 16:47 ` [PATCH v8 05/19] sched: Introduce task_cpu_possible_mask() to limit fallback rq selection Will Deacon
2021-06-04 17:10   ` Valentin Schneider
2021-06-07 17:04     ` Will Deacon
2021-06-02 16:47 ` [PATCH v8 06/19] cpuset: Don't use the cpu_possible_mask as a last resort for cgroup v1 Will Deacon
2021-06-04 17:11   ` Valentin Schneider
2021-06-07 17:20     ` Will Deacon
2021-06-10 10:20       ` Valentin Schneider
2021-06-02 16:47 ` [PATCH v8 07/19] cpuset: Honour task_cpu_possible_mask() in guarantee_online_cpus() Will Deacon
2021-06-04 17:11   ` Valentin Schneider
2021-06-02 16:47 ` [PATCH v8 08/19] sched: Reject CPU affinity changes based on task_cpu_possible_mask() Will Deacon
2021-06-04 17:11   ` Valentin Schneider
2021-06-07 22:43     ` Will Deacon
2021-06-02 16:47 ` [PATCH v8 09/19] sched: Introduce task_struct::user_cpus_ptr to track requested affinity Will Deacon
2021-06-04 17:12   ` Valentin Schneider
2021-06-02 16:47 ` [PATCH v8 10/19] sched: Split the guts of sched_setaffinity() into a helper function Will Deacon
2021-06-04 17:12   ` Valentin Schneider
2021-06-02 16:47 ` [PATCH v8 11/19] sched: Allow task CPU affinity to be restricted on asymmetric systems Will Deacon
2021-06-04 17:12   ` Valentin Schneider
2021-06-07 22:52     ` Will Deacon
2021-06-10 10:20       ` Valentin Schneider
2021-06-02 16:47 ` [PATCH v8 12/19] sched: Introduce task_cpus_dl_admissible() to check proposed affinity Will Deacon
2021-06-03  9:43   ` Daniel Bristot de Oliveira
2021-06-03  9:52     ` Will Deacon
2021-06-02 16:47 ` [PATCH v8 13/19] arm64: Implement task_cpu_possible_mask() Will Deacon
2021-06-02 16:47 ` [PATCH v8 14/19] arm64: exec: Adjust affinity for compat tasks with mismatched 32-bit EL0 Will Deacon
2021-06-03  9:45   ` Daniel Bristot de Oliveira
2021-06-02 16:47 ` [PATCH v8 15/19] arm64: Prevent offlining first CPU with 32-bit EL0 on mismatched system Will Deacon
2021-06-03 12:58   ` Mark Rutland
2021-06-03 17:40     ` Will Deacon
2021-06-04  9:49       ` Mark Rutland
2021-06-04 12:14         ` Qais Yousef
2021-06-02 16:47 ` [PATCH v8 16/19] arm64: Advertise CPUs capable of running 32-bit applications in sysfs Will Deacon
2021-06-02 16:47 ` [PATCH v8 17/19] arm64: Hook up cmdline parameter to allow mismatched 32-bit EL0 Will Deacon
2021-06-02 16:47 ` [PATCH v8 18/19] arm64: Remove logic to kill 32-bit tasks on 64-bit-only cores Will Deacon
2021-06-02 16:47 ` [PATCH v8 19/19] Documentation: arm64: describe asymmetric 32-bit support Will Deacon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).