Skip to content

Commit b5df5b8

Browse files
mrutland-armwilldeacon
authored andcommitted
arm64: idle: don't instrument idle code with KCOV
The low-level idle code in arch_cpu_idle() and its callees runs at a time where where portions of the kernel environment aren't available. For example, RCU may not be watching, and lockdep state may be out-of-sync with the hardware. Due to this, it is not sound to instrument this code. We generally avoid instrumentation by marking the entry functions as `noinstr`, but currently this doesn't inhibit KCOV instrumentation. Prevent this by factoring these functions into a new idle.c so that we can disable KCOV for the entire compilation unit, as is done for the core idle code in kernel/sched/idle.c. We'd like to keep instrumentation of the rest of process.c, and for the existing code in cpuidle.c, so a new compilation unit is preferable. The arch_cpu_idle_dead() function in process.c is a cpu hotplug function that is safe to instrument, so it is left as-is in process.c. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Marc Zyngier <maz@kernel.org> Cc: James Morse <james.morse@arm.com> Cc: Will Deacon <will@kernel.org> Link: https://lore.kernel.org/r/20210607094624.34689-21-mark.rutland@arm.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent bf6fa2c commit b5df5b8

3 files changed

Lines changed: 71 additions & 58 deletions

File tree

arch/arm64/kernel/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ CFLAGS_syscall.o += -fno-stack-protector
1818
# available or are out-of-sync with HW state. Since `noinstr` doesn't always
1919
# inhibit KCOV instrumentation, disable it for the entire compilation unit.
2020
KCOV_INSTRUMENT_entry.o := n
21+
KCOV_INSTRUMENT_idle.o := n
2122

2223
# Object file lists.
2324
obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
@@ -27,7 +28,7 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
2728
return_address.o cpuinfo.o cpu_errata.o \
2829
cpufeature.o alternative.o cacheinfo.o \
2930
smp.o smp_spin_table.o topology.o smccc-call.o \
30-
syscall.o proton-pack.o idreg-override.o
31+
syscall.o proton-pack.o idreg-override.o idle.o
3132

3233
targets += efi-entry.o
3334

arch/arm64/kernel/idle.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Low-level idle sequences
4+
*/
5+
6+
#include <linux/cpu.h>
7+
#include <linux/irqflags.h>
8+
9+
#include <asm/arch_gicv3.h>
10+
#include <asm/barrier.h>
11+
#include <asm/cpufeature.h>
12+
#include <asm/sysreg.h>
13+
14+
static void noinstr __cpu_do_idle(void)
15+
{
16+
dsb(sy);
17+
wfi();
18+
}
19+
20+
static void noinstr __cpu_do_idle_irqprio(void)
21+
{
22+
unsigned long pmr;
23+
unsigned long daif_bits;
24+
25+
daif_bits = read_sysreg(daif);
26+
write_sysreg(daif_bits | PSR_I_BIT | PSR_F_BIT, daif);
27+
28+
/*
29+
* Unmask PMR before going idle to make sure interrupts can
30+
* be raised.
31+
*/
32+
pmr = gic_read_pmr();
33+
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
34+
35+
__cpu_do_idle();
36+
37+
gic_write_pmr(pmr);
38+
write_sysreg(daif_bits, daif);
39+
}
40+
41+
/*
42+
* cpu_do_idle()
43+
*
44+
* Idle the processor (wait for interrupt).
45+
*
46+
* If the CPU supports priority masking we must do additional work to
47+
* ensure that interrupts are not masked at the PMR (because the core will
48+
* not wake up if we block the wake up signal in the interrupt controller).
49+
*/
50+
void noinstr cpu_do_idle(void)
51+
{
52+
if (system_uses_irq_prio_masking())
53+
__cpu_do_idle_irqprio();
54+
else
55+
__cpu_do_idle();
56+
}
57+
58+
/*
59+
* This is our default idle handler.
60+
*/
61+
void noinstr arch_cpu_idle(void)
62+
{
63+
/*
64+
* This should do all the clock switching and wait for interrupt
65+
* tricks
66+
*/
67+
cpu_do_idle();
68+
raw_local_irq_enable();
69+
}

arch/arm64/kernel/process.c

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -73,63 +73,6 @@ EXPORT_SYMBOL_GPL(pm_power_off);
7373

7474
void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
7575

76-
static void noinstr __cpu_do_idle(void)
77-
{
78-
dsb(sy);
79-
wfi();
80-
}
81-
82-
static void noinstr __cpu_do_idle_irqprio(void)
83-
{
84-
unsigned long pmr;
85-
unsigned long daif_bits;
86-
87-
daif_bits = read_sysreg(daif);
88-
write_sysreg(daif_bits | PSR_I_BIT | PSR_F_BIT, daif);
89-
90-
/*
91-
* Unmask PMR before going idle to make sure interrupts can
92-
* be raised.
93-
*/
94-
pmr = gic_read_pmr();
95-
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
96-
97-
__cpu_do_idle();
98-
99-
gic_write_pmr(pmr);
100-
write_sysreg(daif_bits, daif);
101-
}
102-
103-
/*
104-
* cpu_do_idle()
105-
*
106-
* Idle the processor (wait for interrupt).
107-
*
108-
* If the CPU supports priority masking we must do additional work to
109-
* ensure that interrupts are not masked at the PMR (because the core will
110-
* not wake up if we block the wake up signal in the interrupt controller).
111-
*/
112-
void noinstr cpu_do_idle(void)
113-
{
114-
if (system_uses_irq_prio_masking())
115-
__cpu_do_idle_irqprio();
116-
else
117-
__cpu_do_idle();
118-
}
119-
120-
/*
121-
* This is our default idle handler.
122-
*/
123-
void noinstr arch_cpu_idle(void)
124-
{
125-
/*
126-
* This should do all the clock switching and wait for interrupt
127-
* tricks
128-
*/
129-
cpu_do_idle();
130-
raw_local_irq_enable();
131-
}
132-
13376
#ifdef CONFIG_HOTPLUG_CPU
13477
void arch_cpu_idle_dead(void)
13578
{

0 commit comments

Comments
 (0)