From 04c4927359b1f09310bfee92e7187c9022be3e00 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Mon, 19 Feb 2018 12:09:54 +0530 Subject: [PATCH 01/17] arm64: Fix compilation error while accessing MPIDR_HWID_BITMASK from .S files Since commit e1a50de37860 (arm64: cputype: Silence Sparse warnings), compilation of arm64 architecture is broken with the following error messages: AR arch/arm64/kernel/built-in.o arch/arm64/kernel/head.S: Assembler messages: arch/arm64/kernel/head.S:677: Error: found 'L', expected: ')' arch/arm64/kernel/head.S:677: Error: found 'L', expected: ')' arch/arm64/kernel/head.S:677: Error: found 'L', expected: ')' arch/arm64/kernel/head.S:677: Error: junk at end of line, first unrecognized character is `L' arch/arm64/kernel/head.S:677: Error: unexpected characters following instruction at operand 2 -- `movz x1,:abs_g1_s:0xff00ffffffUL' arch/arm64/kernel/head.S:677: Error: unexpected characters following instruction at operand 2 -- `movk x1,:abs_g0_nc:0xff00ffffffUL' This patch fixes the same by using the UL() macro correctly for assigning the MPIDR_HWID_BITMASK macro value. Fixes: e1a50de37860 ("arm64: cputype: Silence Sparse warnings") Acked-by: Arnd Bergmann Acked-by: Robin Murphy Signed-off-by: Bhupesh Sharma Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/cputype.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index eda8c5f629fc8..350c76a1d15ba 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -20,7 +20,7 @@ #define MPIDR_UP_BITMASK (0x1 << 30) #define MPIDR_MT_BITMASK (0x1 << 24) -#define MPIDR_HWID_BITMASK 0xff00ffffffUL +#define MPIDR_HWID_BITMASK UL(0xff00ffffff) #define MPIDR_LEVEL_BITS_SHIFT 3 #define MPIDR_LEVEL_BITS (1 << MPIDR_LEVEL_BITS_SHIFT) From 9085b34d0e8361595a7d19034c550d5d15044556 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 19 Feb 2018 13:38:00 +0000 Subject: [PATCH 02/17] arm64: uaccess: Formalise types for access_ok() In converting __range_ok() into a static inline, I inadvertently made it more type-safe, but without considering the ordering of the relevant conversions. This leads to quite a lot of Sparse noise about the fact that we use __chk_user_ptr() after addr has already been converted from a user pointer to an unsigned long. Rather than just adding another cast for the sake of shutting Sparse up, it seems reasonable to rework the types to make logical sense (although the resulting codegen for __range_ok() remains identical). The only callers this affects directly are our compat traps where the inferred "user-pointer-ness" of a register value now warrants explicit casting. Signed-off-by: Robin Murphy Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/uaccess.h | 12 ++++++------ arch/arm64/kernel/armv8_deprecated.c | 4 +++- arch/arm64/kernel/sys_compat.c | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 543e11f0f657e..e66b0fca99c2f 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -72,15 +72,15 @@ static inline void set_fs(mm_segment_t fs) * This is equivalent to the following test: * (u65)addr + (u65)size <= (u65)current->addr_limit + 1 */ -static inline unsigned long __range_ok(unsigned long addr, unsigned long size) +static inline unsigned long __range_ok(const void __user *addr, unsigned long size) { - unsigned long limit = current_thread_info()->addr_limit; + unsigned long ret, limit = current_thread_info()->addr_limit; __chk_user_ptr(addr); asm volatile( // A + B <= C + 1 for all A,B,C, in four easy steps: // 1: X = A + B; X' = X % 2^64 - " adds %0, %0, %2\n" + " adds %0, %3, %2\n" // 2: Set C = 0 if X > 2^64, to guarantee X' > C in step 4 " csel %1, xzr, %1, hi\n" // 3: Set X' = ~0 if X >= 2^64. For X == 2^64, this decrements X' @@ -92,9 +92,9 @@ static inline unsigned long __range_ok(unsigned long addr, unsigned long size) // testing X' - C == 0, subject to the previous adjustments. " sbcs xzr, %0, %1\n" " cset %0, ls\n" - : "+r" (addr), "+r" (limit) : "Ir" (size) : "cc"); + : "=&r" (ret), "+r" (limit) : "Ir" (size), "0" (addr) : "cc"); - return addr; + return ret; } /* @@ -104,7 +104,7 @@ static inline unsigned long __range_ok(unsigned long addr, unsigned long size) */ #define untagged_addr(addr) sign_extend64(addr, 55) -#define access_ok(type, addr, size) __range_ok((unsigned long)(addr), size) +#define access_ok(type, addr, size) __range_ok(addr, size) #define user_addr_max get_fs #define _ASM_EXTABLE(from, to) \ diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c index c33b5e4010ab7..68450e954d47d 100644 --- a/arch/arm64/kernel/armv8_deprecated.c +++ b/arch/arm64/kernel/armv8_deprecated.c @@ -370,6 +370,7 @@ static unsigned int __kprobes aarch32_check_condition(u32 opcode, u32 psr) static int swp_handler(struct pt_regs *regs, u32 instr) { u32 destreg, data, type, address = 0; + const void __user *user_ptr; int rn, rt2, res = 0; perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); @@ -401,7 +402,8 @@ static int swp_handler(struct pt_regs *regs, u32 instr) aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET), data); /* Check access in reasonable access range for both SWP and SWPB */ - if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) { + user_ptr = (const void __user *)(unsigned long)(address & ~3); + if (!access_ok(VERIFY_WRITE, user_ptr, 4)) { pr_debug("SWP{B} emulation: access to 0x%08x not allowed!\n", address); goto fault; diff --git a/arch/arm64/kernel/sys_compat.c b/arch/arm64/kernel/sys_compat.c index 8b8bbd3eaa52c..a382b2a1b84e3 100644 --- a/arch/arm64/kernel/sys_compat.c +++ b/arch/arm64/kernel/sys_compat.c @@ -57,7 +57,7 @@ do_compat_cache_op(unsigned long start, unsigned long end, int flags) if (end < start || flags) return -EINVAL; - if (!access_ok(VERIFY_READ, start, end - start)) + if (!access_ok(VERIFY_READ, (const void __user *)start, end - start)) return -EFAULT; return __do_compat_cache_op(start, end); From be68a8aaf925aaf35574260bf820bb09d2f9e07f Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 19 Feb 2018 14:41:44 +0000 Subject: [PATCH 03/17] arm64: cpufeature: Fix CTR_EL0 field definitions Our field definitions for CTR_EL0 suffer from a number of problems: - The IDC and DIC fields are missing, which causes us to enable CTR trapping on CPUs with either of these returning non-zero values. - The ERG is FTR_LOWER_SAFE, whereas it should be treated like CWG as FTR_HIGHER_SAFE so that applications can use it to avoid false sharing. - [nit] A RES1 field is described as "RAO" This patch updates the CTR_EL0 field definitions to fix these issues. Cc: Cc: Shanker Donthineni Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/kernel/cpufeature.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 29b1f873e337f..2985a067fc131 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -199,9 +199,11 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = { }; static const struct arm64_ftr_bits ftr_ctr[] = { - ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, 31, 1, 1), /* RAO */ + ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, 31, 1, 1), /* RES1 */ + ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 29, 1, 1), /* DIC */ + ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 28, 1, 1), /* IDC */ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_HIGHER_SAFE, 24, 4, 0), /* CWG */ - ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0), /* ERG */ + ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_HIGHER_SAFE, 20, 4, 0), /* ERG */ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 1), /* DminLine */ /* * Linux can handle differing I-cache policies. Userspace JITs will From 5ee39a71fd89ab7240c5339d04161c44a8e03269 Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Thu, 1 Feb 2018 23:13:38 +0100 Subject: [PATCH 04/17] arm64: Disable unhandled signal log messages by default aarch64 unhandled signal kernel messages are very verbose, suggesting them to be more of a debugging aid: sigsegv[33]: unhandled level 2 translation fault (11) at 0x00000000, esr 0x92000046, in sigsegv[400000+71000] CPU: 1 PID: 33 Comm: sigsegv Tainted: G W 4.15.0-rc3+ #3 Hardware name: linux,dummy-virt (DT) pstate: 60000000 (nZCv daif -PAN -UAO) pc : 0x4003f4 lr : 0x4006bc sp : 0000fffffe94a060 x29: 0000fffffe94a070 x28: 0000000000000000 x27: 0000000000000000 x26: 0000000000000000 x25: 0000000000000000 x24: 00000000004001b0 x23: 0000000000486ac8 x22: 00000000004001c8 x21: 0000000000000000 x20: 0000000000400be8 x19: 0000000000400b30 x18: 0000000000484728 x17: 000000000865ffc8 x16: 000000000000270f x15: 00000000000000b0 x14: 0000000000000002 x13: 0000000000000001 x12: 0000000000000000 x11: 0000000000000000 x10: 0008000020008008 x9 : 000000000000000f x8 : ffffffffffffffff x7 : 0004000000000000 x6 : ffffffffffffffff x5 : 0000000000000000 x4 : 0000000000000000 x3 : 00000000004003e4 x2 : 0000fffffe94a1e8 x1 : 000000000000000a x0 : 0000000000000000 Disable them by default, so they can be enabled using /proc/sys/debug/exception-trace. Cc: Signed-off-by: Michael Weiser Signed-off-by: Will Deacon --- arch/arm64/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index bbb0fde2780ef..c8639f95e59a3 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -57,7 +57,7 @@ static const char *handler[]= { "Error" }; -int show_unhandled_signals = 1; +int show_unhandled_signals = 0; static void dump_backtrace_entry(unsigned long where) { From 1962682d2b2fbe6cfa995a85c53c069fadda473e Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Thu, 1 Feb 2018 23:13:36 +0100 Subject: [PATCH 05/17] arm64: Remove unimplemented syscall log message Stop printing a (ratelimited) kernel message for each instance of an unimplemented syscall being called. Userland making an unimplemented syscall is not necessarily misbehaviour and to be expected with a current userland running on an older kernel. Also, the current message looks scary to users but does not actually indicate a real problem nor help them narrow down the cause. Just rely on sys_ni_syscall() to return -ENOSYS. Cc: Acked-by: Will Deacon Signed-off-by: Michael Weiser Signed-off-by: Will Deacon --- arch/arm64/kernel/traps.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index c8639f95e59a3..eb2d15147e8d3 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -526,14 +526,6 @@ asmlinkage long do_ni_syscall(struct pt_regs *regs) } #endif - if (show_unhandled_signals_ratelimited()) { - pr_info("%s[%d]: syscall %d\n", current->comm, - task_pid_nr(current), regs->syscallno); - dump_instr("", regs); - if (user_mode(regs)) - __show_regs(regs); - } - return sys_ni_syscall(); } From a06f818a70de21b4b3b4186816094208fc7accf9 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 19 Feb 2018 16:46:57 +0000 Subject: [PATCH 06/17] arm64: __show_regs: Only resolve kernel symbols when running at EL1 __show_regs pretty prints PC and LR by attempting to map them to kernel function names to improve the utility of crash reports. Unfortunately, this mapping is applied even when the pt_regs corresponds to user mode, resulting in a KASLR oracle. Avoid this issue by only looking up the function symbols when the register state indicates that we're actually running at EL1. Cc: Reported-by: NCSC Security Signed-off-by: Will Deacon --- arch/arm64/kernel/process.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index ad8aeb098b31e..c0da6efe54655 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -220,8 +220,15 @@ void __show_regs(struct pt_regs *regs) show_regs_print_info(KERN_DEFAULT); print_pstate(regs); - printk("pc : %pS\n", (void *)regs->pc); - printk("lr : %pS\n", (void *)lr); + + if (!user_mode(regs)) { + printk("pc : %pS\n", (void *)regs->pc); + printk("lr : %pS\n", (void *)lr); + } else { + printk("pc : %016llx\n", regs->pc); + printk("lr : %016llx\n", lr); + } + printk("sp : %016llx\n", sp); i = top_reg; From 2b05f6ae1ee5a3c625478acd10b0966b66a3a017 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 5 Feb 2018 16:41:55 +0000 Subject: [PATCH 07/17] ARM: ux500: remove PMU IRQ bouncer The ux500 PMU IRQ bouncer is getting in the way of some fundametnal changes to the ARM PMU driver, and it's the only special case that exists today. Let's remove it. Reviewed-by: Linus Walleij Signed-off-by: Mark Rutland Signed-off-by: Will Deacon --- arch/arm/mach-ux500/cpu-db8500.c | 35 -------------------------------- 1 file changed, 35 deletions(-) diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index 57058ac46f497..7e5d7a0837070 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -112,37 +111,6 @@ static void ux500_restart(enum reboot_mode mode, const char *cmd) prcmu_system_reset(0); } -/* - * The PMU IRQ lines of two cores are wired together into a single interrupt. - * Bounce the interrupt to the other core if it's not ours. - */ -static irqreturn_t db8500_pmu_handler(int irq, void *dev, irq_handler_t handler) -{ - irqreturn_t ret = handler(irq, dev); - int other = !smp_processor_id(); - - if (ret == IRQ_NONE && cpu_online(other)) - irq_set_affinity(irq, cpumask_of(other)); - - /* - * We should be able to get away with the amount of IRQ_NONEs we give, - * while still having the spurious IRQ detection code kick in if the - * interrupt really starts hitting spuriously. - */ - return ret; -} - -static struct arm_pmu_platdata db8500_pmu_platdata = { - .handle_irq = db8500_pmu_handler, - .irq_flags = IRQF_NOBALANCING | IRQF_NO_THREAD, -}; - -static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = { - /* Requires call-back bindings. */ - OF_DEV_AUXDATA("arm,cortex-a9-pmu", 0, "arm-pmu", &db8500_pmu_platdata), - {}, -}; - static struct of_dev_auxdata u8540_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("stericsson,db8500-prcmu", 0x80157000, "db8500-prcmu", NULL), {}, @@ -165,9 +133,6 @@ static void __init u8500_init_machine(void) if (of_machine_is_compatible("st-ericsson,u8540")) of_platform_populate(NULL, u8500_local_bus_nodes, u8540_auxdata_lookup, NULL); - else - of_platform_populate(NULL, u8500_local_bus_nodes, - u8500_auxdata_lookup, NULL); } static const char * stericsson_dt_platform_compat[] = { From c0248c96631f38f02d58762fc018e316843acac8 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 5 Feb 2018 16:41:56 +0000 Subject: [PATCH 08/17] arm_pmu: kill arm_pmu_platdata Now that we have no platforms passing platform data to the arm_pmu code, we can get rid of the platdata and associated hooks, paving the way for rework of our IRQ handling. Signed-off-by: Mark Rutland Cc: Will Deacon Signed-off-by: Will Deacon --- drivers/perf/arm_pmu.c | 27 ++++----------------------- include/linux/perf/arm_pmu.h | 17 ----------------- 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 7bc5eee96b310..82b09d1cb42c2 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -320,17 +319,9 @@ validate_group(struct perf_event *event) return 0; } -static struct arm_pmu_platdata *armpmu_get_platdata(struct arm_pmu *armpmu) -{ - struct platform_device *pdev = armpmu->plat_device; - - return pdev ? dev_get_platdata(&pdev->dev) : NULL; -} - static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) { struct arm_pmu *armpmu; - struct arm_pmu_platdata *plat; int ret; u64 start_clock, finish_clock; @@ -342,13 +333,8 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) */ armpmu = *(void **)dev; - plat = armpmu_get_platdata(armpmu); - start_clock = sched_clock(); - if (plat && plat->handle_irq) - ret = plat->handle_irq(irq, armpmu, armpmu->handle_irq); - else - ret = armpmu->handle_irq(irq, armpmu); + ret = armpmu->handle_irq(irq, armpmu); finish_clock = sched_clock(); perf_sample_event_took(finish_clock - start_clock); @@ -578,7 +564,6 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) goto err_out; } } else { - struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu); unsigned long irq_flags; err = irq_force_affinity(irq, cpumask_of(cpu)); @@ -589,13 +574,9 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) goto err_out; } - if (platdata && platdata->irq_flags) { - irq_flags = platdata->irq_flags; - } else { - irq_flags = IRQF_PERCPU | - IRQF_NOBALANCING | - IRQF_NO_THREAD; - } + irq_flags = IRQF_PERCPU | + IRQF_NOBALANCING | + IRQF_NO_THREAD; err = request_irq(irq, handler, irq_flags, "arm-pmu", per_cpu_ptr(&hw_events->percpu_pmu, cpu)); diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index af0f44effd44a..712764b35c6a1 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -17,23 +17,6 @@ #include #include -/* - * struct arm_pmu_platdata - ARM PMU platform data - * - * @handle_irq: an optional handler which will be called from the - * interrupt and passed the address of the low level handler, - * and can be used to implement any platform specific handling - * before or after calling it. - * - * @irq_flags: if non-zero, these flags will be passed to request_irq - * when requesting interrupts for this PMU device. - */ -struct arm_pmu_platdata { - irqreturn_t (*handle_irq)(int irq, void *dev, - irq_handler_t pmu_handler); - unsigned long irq_flags; -}; - #ifdef CONFIG_ARM_PMU /* From d3d5aac206b4e9e569a22fe1811c909dde17587c Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 5 Feb 2018 16:41:57 +0000 Subject: [PATCH 09/17] arm_pmu: fold platform helpers into platform code The armpmu_{request,free}_irqs() helpers are only used by arm_pmu_platform.c, so let's fold them in and make them static. Signed-off-by: Mark Rutland Cc: Will Deacon Signed-off-by: Will Deacon --- drivers/perf/arm_pmu.c | 21 --------------------- drivers/perf/arm_pmu_platform.c | 21 +++++++++++++++++++++ include/linux/perf/arm_pmu.h | 2 -- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 82b09d1cb42c2..373dfd7d8a1da 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -534,14 +534,6 @@ void armpmu_free_irq(struct arm_pmu *armpmu, int cpu) free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu)); } -void armpmu_free_irqs(struct arm_pmu *armpmu) -{ - int cpu; - - for_each_cpu(cpu, &armpmu->supported_cpus) - armpmu_free_irq(armpmu, cpu); -} - int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) { int err = 0; @@ -593,19 +585,6 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) return err; } -int armpmu_request_irqs(struct arm_pmu *armpmu) -{ - int cpu, err; - - for_each_cpu(cpu, &armpmu->supported_cpus) { - err = armpmu_request_irq(armpmu, cpu); - if (err) - break; - } - - return err; -} - static int armpmu_get_cpu_irq(struct arm_pmu *pmu, int cpu) { struct pmu_hw_events __percpu *hw_events = pmu->hw_events; diff --git a/drivers/perf/arm_pmu_platform.c b/drivers/perf/arm_pmu_platform.c index 46501cc79fd7c..244558cfdbce7 100644 --- a/drivers/perf/arm_pmu_platform.c +++ b/drivers/perf/arm_pmu_platform.c @@ -164,6 +164,27 @@ static int pmu_parse_irqs(struct arm_pmu *pmu) return 0; } +static int armpmu_request_irqs(struct arm_pmu *armpmu) +{ + int cpu, err; + + for_each_cpu(cpu, &armpmu->supported_cpus) { + err = armpmu_request_irq(armpmu, cpu); + if (err) + break; + } + + return err; +} + +static void armpmu_free_irqs(struct arm_pmu *armpmu) +{ + int cpu; + + for_each_cpu(cpu, &armpmu->supported_cpus) + armpmu_free_irq(armpmu, cpu); +} + int arm_pmu_device_probe(struct platform_device *pdev, const struct of_device_id *of_table, const struct pmu_probe_info *probe_table) diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index 712764b35c6a1..899bc7ef08812 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -159,8 +159,6 @@ static inline int arm_pmu_acpi_probe(armpmu_init_fn init_fn) { return 0; } struct arm_pmu *armpmu_alloc(void); void armpmu_free(struct arm_pmu *pmu); int armpmu_register(struct arm_pmu *pmu); -int armpmu_request_irqs(struct arm_pmu *armpmu); -void armpmu_free_irqs(struct arm_pmu *armpmu); int armpmu_request_irq(struct arm_pmu *armpmu, int cpu); void armpmu_free_irq(struct arm_pmu *armpmu, int cpu); From 0dc1a1851af1d593eee248b94c1277c7c7ccbbce Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 5 Feb 2018 16:41:58 +0000 Subject: [PATCH 10/17] arm_pmu: add armpmu_alloc_atomic() In ACPI systems, we don't know the makeup of CPUs until we hotplug them on, and thus have to allocate the PMU datastructures at hotplug time. Thus, we must use GFP_ATOMIC allocations. Let's add an armpmu_alloc_atomic() that we can use in this case. Signed-off-by: Mark Rutland Cc: Will Deacon Signed-off-by: Will Deacon --- drivers/perf/arm_pmu.c | 17 ++++++++++++++--- drivers/perf/arm_pmu_acpi.c | 2 +- include/linux/perf/arm_pmu.h | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 373dfd7d8a1da..4f73c5e8d6239 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -760,18 +760,18 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu) &cpu_pmu->node); } -struct arm_pmu *armpmu_alloc(void) +static struct arm_pmu *__armpmu_alloc(gfp_t flags) { struct arm_pmu *pmu; int cpu; - pmu = kzalloc(sizeof(*pmu), GFP_KERNEL); + pmu = kzalloc(sizeof(*pmu), flags); if (!pmu) { pr_info("failed to allocate PMU device!\n"); goto out; } - pmu->hw_events = alloc_percpu(struct pmu_hw_events); + pmu->hw_events = alloc_percpu_gfp(struct pmu_hw_events, flags); if (!pmu->hw_events) { pr_info("failed to allocate per-cpu PMU data.\n"); goto out_free_pmu; @@ -817,6 +817,17 @@ struct arm_pmu *armpmu_alloc(void) return NULL; } +struct arm_pmu *armpmu_alloc(void) +{ + return __armpmu_alloc(GFP_KERNEL); +} + +struct arm_pmu *armpmu_alloc_atomic(void) +{ + return __armpmu_alloc(GFP_ATOMIC); +} + + void armpmu_free(struct arm_pmu *pmu) { free_percpu(pmu->hw_events); diff --git a/drivers/perf/arm_pmu_acpi.c b/drivers/perf/arm_pmu_acpi.c index 705f1a390e312..30c5f2bbce59a 100644 --- a/drivers/perf/arm_pmu_acpi.c +++ b/drivers/perf/arm_pmu_acpi.c @@ -127,7 +127,7 @@ static struct arm_pmu *arm_pmu_acpi_find_alloc_pmu(void) return pmu; } - pmu = armpmu_alloc(); + pmu = armpmu_alloc_atomic(); if (!pmu) { pr_warn("Unable to allocate PMU for CPU%d\n", smp_processor_id()); diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index 899bc7ef08812..1f8bb83ef42fc 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -157,6 +157,7 @@ static inline int arm_pmu_acpi_probe(armpmu_init_fn init_fn) { return 0; } /* Internal functions only for core arm_pmu code */ struct arm_pmu *armpmu_alloc(void); +struct arm_pmu *armpmu_alloc_atomic(void); void armpmu_free(struct arm_pmu *pmu); int armpmu_register(struct arm_pmu *pmu); int armpmu_request_irq(struct arm_pmu *armpmu, int cpu); From 43fc9a2febbd96dd39588d67ace456b7bbc73d9f Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 5 Feb 2018 16:41:59 +0000 Subject: [PATCH 11/17] arm_pmu: acpi: check for mismatched PPIs The arm_pmu platform code explicitly checks for mismatched PPIs at probe time, while the ACPI code leaves this to the core code. Future refactoring will make this difficult for the core code to check, so let's have the ACPI code check this explicitly. As before, upon a failure we'll continue on without an interrupt. Ho hum. Signed-off-by: Mark Rutland Cc: Will Deacon Signed-off-by: Will Deacon --- drivers/perf/arm_pmu.c | 17 ++++--------- drivers/perf/arm_pmu_acpi.c | 42 +++++++++++++++++++++++++++++---- drivers/perf/arm_pmu_platform.c | 7 ------ 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 4f73c5e8d6239..ddcabd6a5d52f 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -543,19 +543,7 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) if (!irq) return 0; - if (irq_is_percpu_devid(irq) && cpumask_empty(&armpmu->active_irqs)) { - err = request_percpu_irq(irq, handler, "arm-pmu", - &hw_events->percpu_pmu); - } else if (irq_is_percpu_devid(irq)) { - int other_cpu = cpumask_first(&armpmu->active_irqs); - int other_irq = per_cpu(hw_events->irq, other_cpu); - - if (irq != other_irq) { - pr_warn("mismatched PPIs detected.\n"); - err = -EINVAL; - goto err_out; - } - } else { + if (!irq_is_percpu_devid(irq)) { unsigned long irq_flags; err = irq_force_affinity(irq, cpumask_of(cpu)); @@ -572,6 +560,9 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) err = request_irq(irq, handler, irq_flags, "arm-pmu", per_cpu_ptr(&hw_events->percpu_pmu, cpu)); + } else if (cpumask_empty(&armpmu->active_irqs)) { + err = request_percpu_irq(irq, handler, "arm-pmu", + &hw_events->percpu_pmu); } if (err) diff --git a/drivers/perf/arm_pmu_acpi.c b/drivers/perf/arm_pmu_acpi.c index 30c5f2bbce59a..09a1a36cff579 100644 --- a/drivers/perf/arm_pmu_acpi.c +++ b/drivers/perf/arm_pmu_acpi.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -139,6 +141,35 @@ static struct arm_pmu *arm_pmu_acpi_find_alloc_pmu(void) return pmu; } +/* + * Check whether the new IRQ is compatible with those already associated with + * the PMU (e.g. we don't have mismatched PPIs). + */ +static bool pmu_irq_matches(struct arm_pmu *pmu, int irq) +{ + struct pmu_hw_events __percpu *hw_events = pmu->hw_events; + int cpu; + + if (!irq) + return true; + + for_each_cpu(cpu, &pmu->supported_cpus) { + int other_irq = per_cpu(hw_events->irq, cpu); + if (!other_irq) + continue; + + if (irq == other_irq) + continue; + if (!irq_is_percpu_devid(irq) && !irq_is_percpu_devid(other_irq)) + continue; + + pr_warn("mismatched PPIs detected\n"); + return false; + } + + return true; +} + /* * This must run before the common arm_pmu hotplug logic, so that we can * associate a CPU and its interrupt before the common code tries to manage the @@ -164,18 +195,21 @@ static int arm_pmu_acpi_cpu_starting(unsigned int cpu) if (!pmu) return -ENOMEM; - cpumask_set_cpu(cpu, &pmu->supported_cpus); - per_cpu(probed_pmus, cpu) = pmu; + if (pmu_irq_matches(pmu, irq)) { + hw_events = pmu->hw_events; + per_cpu(hw_events->irq, cpu) = irq; + } + + cpumask_set_cpu(cpu, &pmu->supported_cpus); + /* * Log and request the IRQ so the core arm_pmu code can manage it. In * some situations (e.g. mismatched PPIs), we may fail to request the * IRQ. However, it may be too late for us to do anything about it. * The common ARM PMU code will log a warning in this case. */ - hw_events = pmu->hw_events; - per_cpu(hw_events->irq, cpu) = irq; armpmu_request_irq(pmu, cpu); /* diff --git a/drivers/perf/arm_pmu_platform.c b/drivers/perf/arm_pmu_platform.c index 244558cfdbce7..1dc3c1f574e0a 100644 --- a/drivers/perf/arm_pmu_platform.c +++ b/drivers/perf/arm_pmu_platform.c @@ -127,13 +127,6 @@ static int pmu_parse_irqs(struct arm_pmu *pmu) pdev->dev.of_node); } - /* - * Some platforms have all PMU IRQs OR'd into a single IRQ, with a - * special platdata function that attempts to demux them. - */ - if (dev_get_platdata(&pdev->dev)) - cpumask_setall(&pmu->supported_cpus); - for (i = 0; i < num_irqs; i++) { int cpu, irq; From 6de3f79112cc26bf24edbb240248d21e1dd85dde Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 5 Feb 2018 16:42:00 +0000 Subject: [PATCH 12/17] arm_pmu: explicitly enable/disable SPIs at hotplug To support ACPI systems, we need to request IRQs before CPUs are hotplugged, and thus we need to request IRQs before we know their associated PMU. This is problematic if a PMU IRQ is pending out of reset, as it may be taken before we know the PMU, and thus the IRQ handler won't be able to handle it, leaving it screaming. To avoid such problems, lets request all IRQs in a disabled state, and explicitly enable/disable them at hotplug time, when we're sure the PMU has been probed. Signed-off-by: Mark Rutland Cc: Will Deacon Signed-off-by: Will Deacon --- drivers/perf/arm_pmu.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index ddcabd6a5d52f..72118e6f9122b 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -558,6 +558,7 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) IRQF_NOBALANCING | IRQF_NO_THREAD; + irq_set_status_flags(irq, IRQ_NOAUTOEN); err = request_irq(irq, handler, irq_flags, "arm-pmu", per_cpu_ptr(&hw_events->percpu_pmu, cpu)); } else if (cpumask_empty(&armpmu->active_irqs)) { @@ -600,10 +601,10 @@ static int arm_perf_starting_cpu(unsigned int cpu, struct hlist_node *node) irq = armpmu_get_cpu_irq(pmu, cpu); if (irq) { - if (irq_is_percpu_devid(irq)) { + if (irq_is_percpu_devid(irq)) enable_percpu_irq(irq, IRQ_TYPE_NONE); - return 0; - } + else + enable_irq(irq); } return 0; @@ -618,8 +619,12 @@ static int arm_perf_teardown_cpu(unsigned int cpu, struct hlist_node *node) return 0; irq = armpmu_get_cpu_irq(pmu, cpu); - if (irq && irq_is_percpu_devid(irq)) - disable_percpu_irq(irq); + if (irq) { + if (irq_is_percpu_devid(irq)) + disable_percpu_irq(irq); + else + disable_irq(irq); + } return 0; } From 84b4be57ae17f8c0b3c1d8629e10f23910838fd7 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 12 Dec 2017 16:56:06 +0000 Subject: [PATCH 13/17] arm_pmu: note IRQs and PMUs per-cpu To support ACPI systems, we need to request IRQs before we know the associated PMU, and thus we need some percpu variable that the IRQ handler can find the PMU from. As we're going to request IRQs without the PMU, we can't rely on the arm_pmu::active_irqs mask, and similarly need to track requested IRQs with a percpu variable. Signed-off-by: Mark Rutland [will: made armpmu_count_irq_users static] Signed-off-by: Will Deacon --- drivers/perf/arm_pmu.c | 69 +++++++++++++++++++++++++++--------- include/linux/perf/arm_pmu.h | 1 - 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 72118e6f9122b..2b2af35db1b6e 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -25,6 +25,9 @@ #include +static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu); +static DEFINE_PER_CPU(int, cpu_irq); + static int armpmu_map_cache_event(const unsigned (*cache_map) [PERF_COUNT_HW_CACHE_MAX] @@ -332,6 +335,8 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) * dereference. */ armpmu = *(void **)dev; + if (WARN_ON_ONCE(!armpmu)) + return IRQ_NONE; start_clock = sched_clock(); ret = armpmu->handle_irq(irq, armpmu); @@ -517,29 +522,45 @@ int perf_num_counters(void) } EXPORT_SYMBOL_GPL(perf_num_counters); -void armpmu_free_irq(struct arm_pmu *armpmu, int cpu) +static int armpmu_count_irq_users(const int irq) { - struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; - int irq = per_cpu(hw_events->irq, cpu); + int cpu, count = 0; - if (!cpumask_test_and_clear_cpu(cpu, &armpmu->active_irqs)) - return; + for_each_possible_cpu(cpu) { + if (per_cpu(cpu_irq, cpu) == irq) + count++; + } + + return count; +} - if (irq_is_percpu_devid(irq)) { - free_percpu_irq(irq, &hw_events->percpu_pmu); - cpumask_clear(&armpmu->active_irqs); +void armpmu_free_cpu_irq(int irq, int cpu) +{ + if (per_cpu(cpu_irq, cpu) == 0) return; - } + if (WARN_ON(irq != per_cpu(cpu_irq, cpu))) + return; + + if (!irq_is_percpu_devid(irq)) + free_irq(irq, per_cpu_ptr(&cpu_armpmu, cpu)); + else if (armpmu_count_irq_users(irq) == 1) + free_percpu_irq(irq, &cpu_armpmu); - free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu)); + per_cpu(cpu_irq, cpu) = 0; } -int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) +void armpmu_free_irq(struct arm_pmu *armpmu, int cpu) { - int err = 0; struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; - const irq_handler_t handler = armpmu_dispatch_irq; int irq = per_cpu(hw_events->irq, cpu); + + armpmu_free_cpu_irq(irq, cpu); +} + +int armpmu_request_cpu_irq(int irq, int cpu) +{ + int err = 0; + const irq_handler_t handler = armpmu_dispatch_irq; if (!irq) return 0; @@ -560,16 +581,16 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) irq_set_status_flags(irq, IRQ_NOAUTOEN); err = request_irq(irq, handler, irq_flags, "arm-pmu", - per_cpu_ptr(&hw_events->percpu_pmu, cpu)); - } else if (cpumask_empty(&armpmu->active_irqs)) { + per_cpu_ptr(&cpu_armpmu, cpu)); + } else if (armpmu_count_irq_users(irq) == 0) { err = request_percpu_irq(irq, handler, "arm-pmu", - &hw_events->percpu_pmu); + &cpu_armpmu); } if (err) goto err_out; - cpumask_set_cpu(cpu, &armpmu->active_irqs); + per_cpu(cpu_irq, cpu) = irq; return 0; err_out: @@ -577,6 +598,16 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) return err; } +int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) +{ + struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; + int irq = per_cpu(hw_events->irq, cpu); + if (!irq) + return 0; + + return armpmu_request_cpu_irq(irq, cpu); +} + static int armpmu_get_cpu_irq(struct arm_pmu *pmu, int cpu) { struct pmu_hw_events __percpu *hw_events = pmu->hw_events; @@ -599,6 +630,8 @@ static int arm_perf_starting_cpu(unsigned int cpu, struct hlist_node *node) if (pmu->reset) pmu->reset(pmu); + per_cpu(cpu_armpmu, cpu) = pmu; + irq = armpmu_get_cpu_irq(pmu, cpu); if (irq) { if (irq_is_percpu_devid(irq)) @@ -626,6 +659,8 @@ static int arm_perf_teardown_cpu(unsigned int cpu, struct hlist_node *node) disable_irq(irq); } + per_cpu(cpu_armpmu, cpu) = NULL; + return 0; } diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index 1f8bb83ef42fc..feec9e7e85db8 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -75,7 +75,6 @@ enum armpmu_attr_groups { struct arm_pmu { struct pmu pmu; - cpumask_t active_irqs; cpumask_t supported_cpus; char *name; irqreturn_t (*handle_irq)(int irq_num, void *dev); From 167e61438da0664cab87c825a6c0cb83510d578e Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 9 Oct 2017 17:09:05 +0100 Subject: [PATCH 14/17] arm_pmu: acpi: request IRQs up-front We can't request IRQs in atomic context, so for ACPI systems we'll have to request them up-front, and later associate them with CPUs. This patch reorganises the arm_pmu code to do so. As we no longer have the arm_pmu structure at probe time, a number of prototypes need to be adjusted, requiring changes to the common arm_pmu code and arm_pmu platform code. Signed-off-by: Mark Rutland Cc: Will Deacon Signed-off-by: Will Deacon --- drivers/perf/arm_pmu.c | 22 ++-------------------- drivers/perf/arm_pmu_acpi.c | 19 ++++++------------- drivers/perf/arm_pmu_platform.c | 15 ++++++++++++--- include/linux/perf/arm_pmu.h | 5 +++-- 4 files changed, 23 insertions(+), 38 deletions(-) diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 2b2af35db1b6e..0c2ed11c06030 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -534,7 +534,7 @@ static int armpmu_count_irq_users(const int irq) return count; } -void armpmu_free_cpu_irq(int irq, int cpu) +void armpmu_free_irq(int irq, int cpu) { if (per_cpu(cpu_irq, cpu) == 0) return; @@ -549,15 +549,7 @@ void armpmu_free_cpu_irq(int irq, int cpu) per_cpu(cpu_irq, cpu) = 0; } -void armpmu_free_irq(struct arm_pmu *armpmu, int cpu) -{ - struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; - int irq = per_cpu(hw_events->irq, cpu); - - armpmu_free_cpu_irq(irq, cpu); -} - -int armpmu_request_cpu_irq(int irq, int cpu) +int armpmu_request_irq(int irq, int cpu) { int err = 0; const irq_handler_t handler = armpmu_dispatch_irq; @@ -598,16 +590,6 @@ int armpmu_request_cpu_irq(int irq, int cpu) return err; } -int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) -{ - struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; - int irq = per_cpu(hw_events->irq, cpu); - if (!irq) - return 0; - - return armpmu_request_cpu_irq(irq, cpu); -} - static int armpmu_get_cpu_irq(struct arm_pmu *pmu, int cpu) { struct pmu_hw_events __percpu *hw_events = pmu->hw_events; diff --git a/drivers/perf/arm_pmu_acpi.c b/drivers/perf/arm_pmu_acpi.c index 09a1a36cff579..0f197516d7089 100644 --- a/drivers/perf/arm_pmu_acpi.c +++ b/drivers/perf/arm_pmu_acpi.c @@ -89,7 +89,13 @@ static int arm_pmu_acpi_parse_irqs(void) pr_warn("No ACPI PMU IRQ for CPU%d\n", cpu); } + /* + * Log and request the IRQ so the core arm_pmu code can manage + * it. We'll have to sanity-check IRQs later when we associate + * them with their PMUs. + */ per_cpu(pmu_irqs, cpu) = irq; + armpmu_request_irq(irq, cpu); } return 0; @@ -204,14 +210,6 @@ static int arm_pmu_acpi_cpu_starting(unsigned int cpu) cpumask_set_cpu(cpu, &pmu->supported_cpus); - /* - * Log and request the IRQ so the core arm_pmu code can manage it. In - * some situations (e.g. mismatched PPIs), we may fail to request the - * IRQ. However, it may be too late for us to do anything about it. - * The common ARM PMU code will log a warning in this case. - */ - armpmu_request_irq(pmu, cpu); - /* * Ideally, we'd probe the PMU here when we find the first matching * CPU. We can't do that for several reasons; see the comment in @@ -281,11 +279,6 @@ static int arm_pmu_acpi_init(void) if (acpi_disabled) return 0; - /* - * We can't request IRQs yet, since we don't know the cookie value - * until we know which CPUs share the same logical PMU. We'll handle - * that in arm_pmu_acpi_cpu_starting(). - */ ret = arm_pmu_acpi_parse_irqs(); if (ret) return ret; diff --git a/drivers/perf/arm_pmu_platform.c b/drivers/perf/arm_pmu_platform.c index 1dc3c1f574e0a..7729eda5909df 100644 --- a/drivers/perf/arm_pmu_platform.c +++ b/drivers/perf/arm_pmu_platform.c @@ -159,10 +159,15 @@ static int pmu_parse_irqs(struct arm_pmu *pmu) static int armpmu_request_irqs(struct arm_pmu *armpmu) { + struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; int cpu, err; for_each_cpu(cpu, &armpmu->supported_cpus) { - err = armpmu_request_irq(armpmu, cpu); + int irq = per_cpu(hw_events->irq, cpu); + if (!irq) + continue; + + err = armpmu_request_irq(irq, cpu); if (err) break; } @@ -173,9 +178,13 @@ static int armpmu_request_irqs(struct arm_pmu *armpmu) static void armpmu_free_irqs(struct arm_pmu *armpmu) { int cpu; + struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; - for_each_cpu(cpu, &armpmu->supported_cpus) - armpmu_free_irq(armpmu, cpu); + for_each_cpu(cpu, &armpmu->supported_cpus) { + int irq = per_cpu(hw_events->irq, cpu); + + armpmu_free_irq(irq, cpu); + } } int arm_pmu_device_probe(struct platform_device *pdev, diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index feec9e7e85db8..40036a57d072f 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -159,8 +160,8 @@ struct arm_pmu *armpmu_alloc(void); struct arm_pmu *armpmu_alloc_atomic(void); void armpmu_free(struct arm_pmu *pmu); int armpmu_register(struct arm_pmu *pmu); -int armpmu_request_irq(struct arm_pmu *armpmu, int cpu); -void armpmu_free_irq(struct arm_pmu *armpmu, int cpu); +int armpmu_request_irq(int irq, int cpu); +void armpmu_free_irq(int irq, int cpu); #define ARMV8_PMU_PDEV_NAME "armv8-pmu" From 0331365edb1d6ccd6ae68b1038111da85d4c68d1 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 14 Feb 2018 17:21:57 +0000 Subject: [PATCH 15/17] arm64: perf: correct PMUVer probing The ID_AA64DFR0_EL1.PMUVer field doesn't follow the usual ID registers scheme. While value 0xf indicates a non-architected PMU is implemented, values 0x1 to 0xe indicate an increasingly featureful architected PMU, as if the field were unsigned. For more details, see ARM DDI 0487C.a, D10.1.4, "Alternative ID scheme used for the Performance Monitors Extension version". Currently, we treat the field as signed, and erroneously bail out for values 0x8 to 0xe. Let's correct that. Signed-off-by: Mark Rutland Reviewed-by: Robin Murphy Cc: Will Deacon Signed-off-by: Will Deacon --- arch/arm64/kernel/perf_event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index 75b220ba73a32..85a251b6dfa84 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -908,9 +908,9 @@ static void __armv8pmu_probe_pmu(void *info) int pmuver; dfr0 = read_sysreg(id_aa64dfr0_el1); - pmuver = cpuid_feature_extract_signed_field(dfr0, + pmuver = cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_PMUVER_SHIFT); - if (pmuver < 1) + if (pmuver == 0xf || pmuver == 0) return; probe->present = true; From 15122ee2c515a253b0c66a3e618bc7ebe35105eb Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 21 Feb 2018 12:59:27 +0000 Subject: [PATCH 16/17] arm64: Enforce BBM for huge IO/VMAP mappings ioremap_page_range doesn't honour break-before-make and attempts to put down huge mappings (using p*d_set_huge) over the top of pre-existing table entries. This leads to us leaking page table memory and also gives rise to TLB conflicts and spurious aborts, which have been seen in practice on Cortex-A75. Until this has been resolved, refuse to put block mappings when the existing entry is found to be present. Fixes: 324420bf91f60 ("arm64: add support for ioremap() block mappings") Reported-by: Hanjun Guo Reported-by: Lei Li Acked-by: Ard Biesheuvel Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/mm/mmu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 3161b853f29e1..84a019f550229 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -933,6 +933,11 @@ int pud_set_huge(pud_t *pudp, phys_addr_t phys, pgprot_t prot) { pgprot_t sect_prot = __pgprot(PUD_TYPE_SECT | pgprot_val(mk_sect_prot(prot))); + + /* ioremap_page_range doesn't honour BBM */ + if (pud_present(READ_ONCE(*pudp))) + return 0; + BUG_ON(phys & ~PUD_MASK); set_pud(pudp, pfn_pud(__phys_to_pfn(phys), sect_prot)); return 1; @@ -942,6 +947,11 @@ int pmd_set_huge(pmd_t *pmdp, phys_addr_t phys, pgprot_t prot) { pgprot_t sect_prot = __pgprot(PMD_TYPE_SECT | pgprot_val(mk_sect_prot(prot))); + + /* ioremap_page_range doesn't honour BBM */ + if (pmd_present(READ_ONCE(*pmdp))) + return 0; + BUG_ON(phys & ~PMD_MASK); set_pmd(pmdp, pfn_pmd(__phys_to_pfn(phys), sect_prot)); return 1; From 9f416319f40cd857d2bb517630e5855a905ef3fb Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 5 Feb 2018 14:28:01 +0100 Subject: [PATCH 17/17] arm64: fix unwind_frame() for filtered out fn for function graph tracing do_task_stat() calls get_wchan(), which further does unwind_frame(). unwind_frame() restores frame->pc to original value in case function graph tracer has modified a return address (LR) in a stack frame to hook a function return. However, if function graph tracer has hit a filtered function, then we can't unwind it as ftrace_push_return_trace() has biased the index(frame->graph) with a 'huge negative' offset(-FTRACE_NOTRACE_DEPTH). Moreover, arm64 stack walker defines index(frame->graph) as unsigned int, which can not compare a -ve number. Similar problem we can have with calling of walk_stackframe() from save_stack_trace_tsk() or dump_backtrace(). This patch fixes unwind_frame() to test the index for -ve value and restore index accordingly before we can restore frame->pc. Reproducer: cd /sys/kernel/debug/tracing/ echo schedule > set_graph_notrace echo 1 > options/display-graph echo wakeup > current_tracer ps -ef | grep -i agent Above commands result in: Unable to handle kernel paging request at virtual address ffff801bd3d1e000 pgd = ffff8003cbe97c00 [ffff801bd3d1e000] *pgd=0000000000000000, *pud=0000000000000000 Internal error: Oops: 96000006 [#1] SMP [...] CPU: 5 PID: 11696 Comm: ps Not tainted 4.11.0+ #33 [...] task: ffff8003c21ba000 task.stack: ffff8003cc6c0000 PC is at unwind_frame+0x12c/0x180 LR is at get_wchan+0xd4/0x134 pc : [] lr : [] pstate: 60000145 sp : ffff8003cc6c3ab0 x29: ffff8003cc6c3ab0 x28: 0000000000000001 x27: 0000000000000026 x26: 0000000000000026 x25: 00000000000012d8 x24: 0000000000000000 x23: ffff8003c1c04000 x22: ffff000008c83000 x21: ffff8003c1c00000 x20: 000000000000000f x19: ffff8003c1bc0000 x18: 0000fffffc593690 x17: 0000000000000000 x16: 0000000000000001 x15: 0000b855670e2b60 x14: 0003e97f22cf1d0f x13: 0000000000000001 x12: 0000000000000000 x11: 00000000e8f4883e x10: 0000000154f47ec8 x9 : 0000000070f367c0 x8 : 0000000000000000 x7 : 00008003f7290000 x6 : 0000000000000018 x5 : 0000000000000000 x4 : ffff8003c1c03cb0 x3 : ffff8003c1c03ca0 x2 : 00000017ffe80000 x1 : ffff8003cc6c3af8 x0 : ffff8003d3e9e000 Process ps (pid: 11696, stack limit = 0xffff8003cc6c0000) Stack: (0xffff8003cc6c3ab0 to 0xffff8003cc6c4000) [...] [] unwind_frame+0x12c/0x180 [] do_task_stat+0x864/0x870 [] proc_tgid_stat+0x3c/0x48 [] proc_single_show+0x5c/0xb8 [] seq_read+0x160/0x414 [] __vfs_read+0x58/0x164 [] vfs_read+0x88/0x144 [] SyS_read+0x60/0xc0 [] __sys_trace_return+0x0/0x4 Fixes: 20380bb390a4 (arm64: ftrace: fix a stack tracer's output under function graph tracer) Signed-off-by: Pratyush Anand Signed-off-by: Jerome Marchand [catalin.marinas@arm.com: replace WARN_ON with WARN_ON_ONCE] Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/stacktrace.h | 2 +- arch/arm64/kernel/stacktrace.c | 5 +++++ arch/arm64/kernel/time.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h index 472ef944e9326..902f9edacbea9 100644 --- a/arch/arm64/include/asm/stacktrace.h +++ b/arch/arm64/include/asm/stacktrace.h @@ -28,7 +28,7 @@ struct stackframe { unsigned long fp; unsigned long pc; #ifdef CONFIG_FUNCTION_GRAPH_TRACER - unsigned int graph; + int graph; #endif }; diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index 76809ccd309cc..d5718a060672e 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -59,6 +59,11 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) #ifdef CONFIG_FUNCTION_GRAPH_TRACER if (tsk->ret_stack && (frame->pc == (unsigned long)return_to_handler)) { + if (WARN_ON_ONCE(frame->graph == -1)) + return -EINVAL; + if (frame->graph < -1) + frame->graph += FTRACE_NOTRACE_DEPTH; + /* * This is a case where function graph tracer has * modified a return address (LR) in a stack frame diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index a4391280fba96..f258636273c95 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -52,7 +52,7 @@ unsigned long profile_pc(struct pt_regs *regs) frame.fp = regs->regs[29]; frame.pc = regs->pc; #ifdef CONFIG_FUNCTION_GRAPH_TRACER - frame.graph = -1; /* no task info */ + frame.graph = current->curr_ret_stack; #endif do { int ret = unwind_frame(NULL, &frame);