Re: [RFC][PATCH 2/8] perf, arch: Use early_initcall() for all archpmu implementations

From: Peter Zijlstra
Date: Thu Nov 25 2010 - 09:48:36 EST


On Thu, 2010-11-25 at 11:25 +0100, Peter Zijlstra wrote:
>
> Right, so hw perf init happens from (after this patch):
>
> arch_initcall: powerpc, arm, sh, mips
> early_initcall: x86, sparc, alpha
>
>
> Now the problem is that the generic watchdog code (kernel/watchdog.c)
> tries to create hw perf events, and that too runs from early_initcall.
>
> So my question is, how do we go about curing this, because powerpc, arm,
> sh and mips are too late and the rest depends on link order to work, not
> really a nice situation.
>
> There's two categories of solutions:
> - move the watchdog later, and
> - move the hw perf init earlier.
>
> The former is undesired because we want the watchdog as early as
> possible, the later needs new infrastructure (also, I don't know if the
> arch implementations can actually run this early).
>
> So do I create a perf_initcall() or is there another solution that
> avoids things like calling the watchdog code from all arch init code?

How about something like these? After this we could even look at making
the watchdog code an explicit init in main.c right after
do_perf_initcalls() (or later if it requires more to be up and running).



Hrm,. except that it all builds but doesn't seem to actually work, I
guess I need more magics to make the perf_initcall() thing work.

/me goes investigate


Subject: perf, core: Introduce perf_initcall()
From: Peter Zijlstra <a.p.zijlstra@xxxxxxxxx>
Date: Thu Nov 25 15:27:49 CET 2010

In order to initialize the built-in hardware PMU drivers before
early_initcall() -- which is used by the watchdog code and expects the
hardware PMUs to be present.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@xxxxxxxxx>
LKML-Reference: <new-submission>
---
include/asm-generic/vmlinux.lds.h | 2 +
include/linux/init.h | 7 +++++
init/main.c | 53 ++++++++++++++++++++++----------------
3 files changed, 40 insertions(+), 22 deletions(-)

Index: linux-2.6/include/asm-generic/vmlinux.lds.h
===================================================================
--- linux-2.6.orig/include/asm-generic/vmlinux.lds.h
+++ linux-2.6/include/asm-generic/vmlinux.lds.h
@@ -600,6 +600,8 @@
VMLINUX_SYMBOL(__setup_end) = .;

#define INITCALLS \
+ *(.initcallperf.init) \
+ VMLINUX_SYMBOL(__perf_initcall_end) = .; \
*(.initcallearly.init) \
VMLINUX_SYMBOL(__early_initcall_end) = .; \
*(.initcall0.init) \
Index: linux-2.6/include/linux/init.h
===================================================================
--- linux-2.6.orig/include/linux/init.h
+++ linux-2.6/include/linux/init.h
@@ -179,6 +179,13 @@ extern int initcall_debug;
__attribute__((__section__(".initcall" level ".init"))) = fn

/*
+ * Very early initcalls, run after the perf core is up.
+ *
+ * Only for build-in code, not modules.
+ */
+#define perf_initcall(fn) __define_initcall("perf",fn,perf)
+
+/*
* Early initcalls run before initializing SMP.
*
* Only for built-in code, not modules.
Index: linux-2.6/init/main.c
===================================================================
--- linux-2.6.orig/init/main.c
+++ linux-2.6/init/main.c
@@ -515,6 +515,36 @@ void __init __weak thread_info_cache_ini
{
}

+extern initcall_t __initcall_start[], __initcall_end[],
+ __perf_initcall_end[], __early_initcall_end[];
+
+static void __init do_perf_initcalls(void)
+{
+ initcall_t *fn;
+
+ for (fn = __initcall_start; fn < __perf_initcall_end; fn++)
+ do_one_initcall(*fn);
+}
+
+static void __init do_pre_smp_initcalls(void)
+{
+ initcall_t *fn;
+
+ for (fn = __perf_initcall_end; fn < __early_initcall_end; fn++)
+ do_one_initcall(*fn);
+}
+
+static void __init do_initcalls(void)
+{
+ initcall_t *fn;
+
+ for (fn = __early_initcall_end; fn < __initcall_end; fn++)
+ do_one_initcall(*fn);
+
+ /* Make sure there is no pending stuff from the initcall sequence */
+ flush_scheduled_work();
+}
+
/*
* Set up kernel memory allocators
*/
@@ -605,6 +635,7 @@ asmlinkage void __init start_kernel(void
local_irq_disable();
}
perf_event_init();
+ do_perf_initcalls();
rcu_init();
radix_tree_init();
/* init some links before init_ISA_irqs() */
@@ -768,20 +799,6 @@ int __init_or_module do_one_initcall(ini
return ret;
}

-
-extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
-
-static void __init do_initcalls(void)
-{
- initcall_t *fn;
-
- for (fn = __early_initcall_end; fn < __initcall_end; fn++)
- do_one_initcall(*fn);
-
- /* Make sure there is no pending stuff from the initcall sequence */
- flush_scheduled_work();
-}
-
/*
* Ok, the machine is now initialized. None of the devices
* have been touched yet, but the CPU subsystem is up and
@@ -800,14 +817,6 @@ static void __init do_basic_setup(void)
do_initcalls();
}

-static void __init do_pre_smp_initcalls(void)
-{
- initcall_t *fn;
-
- for (fn = __initcall_start; fn < __early_initcall_end; fn++)
- do_one_initcall(*fn);
-}
-
static void run_init_process(const char *init_filename)
{
argv_init[0] = init_filename;
Subject: perf, arch: Use perf_initcall() for all arch pmu implementations
From: Peter Zijlstra <a.p.zijlstra@xxxxxxxxx>
Date: Tue Nov 16 22:08:28 CET 2010

Currently architectures use various random locations to init the PMU
driver, for some this happens before the perf core code is
initialized.

In order to avoid calling perf_pmu_register() before the core code is
up and running and able to deal with it, move all arch pmu init calls
to perf_initcall().

Cc: Michael Cree <mcree@xxxxxxxxxxxx>
Cc: David Miller <davem@xxxxxxxxxxxxx>
Cc: Paul Mundt <lethal@xxxxxxxxxxxx>
Cc: Will Deacon <will.deacon@xxxxxxx>
Cc: paulus <paulus@xxxxxxxxx>
Cc: Deng-Cheng Zhu <dengcheng.zhu@xxxxxxxxx>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@xxxxxxxxx>
LKML-Reference: <20101117222056.040309789@xxxxxxxxx>
---
arch/alpha/include/asm/perf_event.h | 6 ------
arch/alpha/kernel/irq_alpha.c | 2 --
arch/alpha/kernel/perf_event.c | 9 ++++++---
arch/arm/kernel/perf_event.c | 2 +-
arch/mips/kernel/perf_event_mipsxx.c | 2 +-
arch/powerpc/kernel/e500-pmu.c | 2 +-
arch/powerpc/kernel/mpc7450-pmu.c | 2 +-
arch/powerpc/kernel/power4-pmu.c | 2 +-
arch/powerpc/kernel/power5+-pmu.c | 2 +-
arch/powerpc/kernel/power5-pmu.c | 2 +-
arch/powerpc/kernel/power6-pmu.c | 2 +-
arch/powerpc/kernel/power7-pmu.c | 2 +-
arch/powerpc/kernel/ppc970-pmu.c | 2 +-
arch/sh/kernel/cpu/sh4/perf_event.c | 2 +-
arch/sh/kernel/cpu/sh4a/perf_event.c | 2 +-
arch/sparc/include/asm/perf_event.h | 4 ----
arch/sparc/kernel/nmi.c | 2 --
arch/sparc/kernel/perf_event.c | 7 +++++--
arch/x86/include/asm/perf_event.h | 2 --
arch/x86/kernel/cpu/common.c | 1 -
arch/x86/kernel/cpu/perf_event.c | 11 +++++++----
21 files changed, 30 insertions(+), 38 deletions(-)

Index: linux-2.6/arch/alpha/include/asm/perf_event.h
===================================================================
--- linux-2.6.orig/arch/alpha/include/asm/perf_event.h
+++ linux-2.6/arch/alpha/include/asm/perf_event.h
@@ -1,10 +1,4 @@
#ifndef __ASM_ALPHA_PERF_EVENT_H
#define __ASM_ALPHA_PERF_EVENT_H

-#ifdef CONFIG_PERF_EVENTS
-extern void init_hw_perf_events(void);
-#else
-static inline void init_hw_perf_events(void) { }
-#endif
-
#endif /* __ASM_ALPHA_PERF_EVENT_H */
Index: linux-2.6/arch/alpha/kernel/irq_alpha.c
===================================================================
--- linux-2.6.orig/arch/alpha/kernel/irq_alpha.c
+++ linux-2.6/arch/alpha/kernel/irq_alpha.c
@@ -112,8 +112,6 @@ init_IRQ(void)
wrent(entInt, 0);

alpha_mv.init_irq();
-
- init_hw_perf_events();
}

/*
Index: linux-2.6/arch/alpha/kernel/perf_event.c
===================================================================
--- linux-2.6.orig/arch/alpha/kernel/perf_event.c
+++ linux-2.6/arch/alpha/kernel/perf_event.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/kdebug.h>
#include <linux/mutex.h>
+#include <linux/init.h>

#include <asm/hwrpb.h>
#include <asm/atomic.h>
@@ -863,13 +864,13 @@ static void alpha_perf_event_irq_handler
/*
* Init call to initialise performance events at kernel startup.
*/
-void __init init_hw_perf_events(void)
+int __init init_hw_perf_events(void)
{
pr_info("Performance events: ");

if (!supported_cpu()) {
pr_cont("No support for your CPU.\n");
- return;
+ return 0;
}

pr_cont("Supported CPU type!\n");
@@ -882,5 +883,7 @@ void __init init_hw_perf_events(void)
alpha_pmu = &ev67_pmu;

perf_pmu_register(&pmu);
-}

+ return 0;
+}
+perf_initcall(init_hw_perf_events);
Index: linux-2.6/arch/sparc/include/asm/perf_event.h
===================================================================
--- linux-2.6.orig/arch/sparc/include/asm/perf_event.h
+++ linux-2.6/arch/sparc/include/asm/perf_event.h
@@ -4,8 +4,6 @@
#ifdef CONFIG_PERF_EVENTS
#include <asm/ptrace.h>

-extern void init_hw_perf_events(void);
-
#define perf_arch_fetch_caller_regs(regs, ip) \
do { \
unsigned long _pstate, _asi, _pil, _i7, _fp; \
@@ -26,8 +24,6 @@ do { \
(regs)->u_regs[UREG_I6] = _fp; \
(regs)->u_regs[UREG_I7] = _i7; \
} while (0)
-#else
-static inline void init_hw_perf_events(void) { }
#endif

#endif
Index: linux-2.6/arch/sparc/kernel/nmi.c
===================================================================
--- linux-2.6.orig/arch/sparc/kernel/nmi.c
+++ linux-2.6/arch/sparc/kernel/nmi.c
@@ -270,8 +270,6 @@ int __init nmi_init(void)
atomic_set(&nmi_active, -1);
}
}
- if (!err)
- init_hw_perf_events();

return err;
}
Index: linux-2.6/arch/sparc/kernel/perf_event.c
===================================================================
--- linux-2.6.orig/arch/sparc/kernel/perf_event.c
+++ linux-2.6/arch/sparc/kernel/perf_event.c
@@ -1307,20 +1307,23 @@ static bool __init supported_pmu(void)
return false;
}

-void __init init_hw_perf_events(void)
+int __init init_hw_perf_events(void)
{
pr_info("Performance events: ");

if (!supported_pmu()) {
pr_cont("No support for PMU type '%s'\n", sparc_pmu_type);
- return;
+ return 0;
}

pr_cont("Supported PMU type is '%s'\n", sparc_pmu_type);

perf_pmu_register(&pmu);
register_die_notifier(&perf_event_nmi_notifier);
+
+ return 0;
}
+perf_initcall(init_hw_perf_event);

void perf_callchain_kernel(struct perf_callchain_entry *entry,
struct pt_regs *regs)
Index: linux-2.6/arch/x86/include/asm/perf_event.h
===================================================================
--- linux-2.6.orig/arch/x86/include/asm/perf_event.h
+++ linux-2.6/arch/x86/include/asm/perf_event.h
@@ -125,7 +125,6 @@ union cpuid10_edx {
#define IBS_OP_MAX_CNT_EXT 0x007FFFFFULL /* not a register bit mask */

#ifdef CONFIG_PERF_EVENTS
-extern void init_hw_perf_events(void);
extern void perf_events_lapic_init(void);

#define PERF_EVENT_INDEX_OFFSET 0
@@ -156,7 +155,6 @@ extern unsigned long perf_misc_flags(str
}

#else
-static inline void init_hw_perf_events(void) { }
static inline void perf_events_lapic_init(void) { }
#endif

Index: linux-2.6/arch/x86/kernel/cpu/common.c
===================================================================
--- linux-2.6.orig/arch/x86/kernel/cpu/common.c
+++ linux-2.6/arch/x86/kernel/cpu/common.c
@@ -894,7 +894,6 @@ void __init identify_boot_cpu(void)
#else
vgetcpu_set_mode();
#endif
- init_hw_perf_events();
}

void __cpuinit identify_secondary_cpu(struct cpuinfo_x86 *c)
Index: linux-2.6/arch/x86/kernel/cpu/perf_event.c
===================================================================
--- linux-2.6.orig/arch/x86/kernel/cpu/perf_event.c
+++ linux-2.6/arch/x86/kernel/cpu/perf_event.c
@@ -1423,7 +1423,7 @@ static void __init pmu_check_apic(void)
pr_info("no hardware sampling interrupt available.\n");
}

-void __init init_hw_perf_events(void)
+int __init init_hw_perf_events(void)
{
struct event_constraint *c;
int err;
@@ -1438,11 +1438,11 @@ void __init init_hw_perf_events(void)
err = amd_pmu_init();
break;
default:
- return;
+ return 0;
}
if (err != 0) {
pr_cont("no PMU driver, software events only.\n");
- return;
+ return 0;
}

pmu_check_apic();
@@ -1450,7 +1450,7 @@ void __init init_hw_perf_events(void)
/* sanity check that the hardware exists or is emulated */
if (!check_hw_exists()) {
pr_cont("Broken PMU hardware detected, software events only.\n");
- return;
+ return 0;
}

pr_cont("%s PMU driver.\n", x86_pmu.name);
@@ -1501,7 +1501,10 @@ void __init init_hw_perf_events(void)

perf_pmu_register(&pmu);
perf_cpu_notifier(x86_pmu_notifier);
+
+ return 0;
}
+perf_initcall(init_hw_perf_events);

static inline void x86_pmu_read(struct perf_event *event)
{
Index: linux-2.6/arch/arm/kernel/perf_event.c
===================================================================
--- linux-2.6.orig/arch/arm/kernel/perf_event.c
+++ linux-2.6/arch/arm/kernel/perf_event.c
@@ -3038,7 +3038,7 @@ init_hw_perf_events(void)

return 0;
}
-arch_initcall(init_hw_perf_events);
+perf_initcall(init_hw_perf_events);

/*
* Callchain handling code.
Index: linux-2.6/arch/mips/kernel/perf_event_mipsxx.c
===================================================================
--- linux-2.6.orig/arch/mips/kernel/perf_event_mipsxx.c
+++ linux-2.6/arch/mips/kernel/perf_event_mipsxx.c
@@ -1047,6 +1047,6 @@ init_hw_perf_events(void)

return 0;
}
-arch_initcall(init_hw_perf_events);
+perf_initcall(init_hw_perf_events);

#endif /* defined(CONFIG_CPU_MIPS32)... */
Index: linux-2.6/arch/powerpc/kernel/e500-pmu.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/e500-pmu.c
+++ linux-2.6/arch/powerpc/kernel/e500-pmu.c
@@ -126,4 +126,4 @@ static int init_e500_pmu(void)
return register_fsl_emb_pmu(&e500_pmu);
}

-arch_initcall(init_e500_pmu);
+perf_initcall(init_e500_pmu);
Index: linux-2.6/arch/powerpc/kernel/mpc7450-pmu.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/mpc7450-pmu.c
+++ linux-2.6/arch/powerpc/kernel/mpc7450-pmu.c
@@ -414,4 +414,4 @@ static int init_mpc7450_pmu(void)
return register_power_pmu(&mpc7450_pmu);
}

-arch_initcall(init_mpc7450_pmu);
+perf_initcall(init_mpc7450_pmu);
Index: linux-2.6/arch/powerpc/kernel/power4-pmu.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/power4-pmu.c
+++ linux-2.6/arch/powerpc/kernel/power4-pmu.c
@@ -613,4 +613,4 @@ static int init_power4_pmu(void)
return register_power_pmu(&power4_pmu);
}

-arch_initcall(init_power4_pmu);
+perf_initcall(init_power4_pmu);
Index: linux-2.6/arch/powerpc/kernel/power5+-pmu.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/power5+-pmu.c
+++ linux-2.6/arch/powerpc/kernel/power5+-pmu.c
@@ -682,4 +682,4 @@ static int init_power5p_pmu(void)
return register_power_pmu(&power5p_pmu);
}

-arch_initcall(init_power5p_pmu);
+perf_initcall(init_power5p_pmu);
Index: linux-2.6/arch/powerpc/kernel/power5-pmu.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/power5-pmu.c
+++ linux-2.6/arch/powerpc/kernel/power5-pmu.c
@@ -621,4 +621,4 @@ static int init_power5_pmu(void)
return register_power_pmu(&power5_pmu);
}

-arch_initcall(init_power5_pmu);
+perf_initcall(init_power5_pmu);
Index: linux-2.6/arch/powerpc/kernel/power6-pmu.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/power6-pmu.c
+++ linux-2.6/arch/powerpc/kernel/power6-pmu.c
@@ -544,4 +544,4 @@ static int init_power6_pmu(void)
return register_power_pmu(&power6_pmu);
}

-arch_initcall(init_power6_pmu);
+perf_initcall(init_power6_pmu);
Index: linux-2.6/arch/powerpc/kernel/power7-pmu.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/power7-pmu.c
+++ linux-2.6/arch/powerpc/kernel/power7-pmu.c
@@ -369,4 +369,4 @@ static int init_power7_pmu(void)
return register_power_pmu(&power7_pmu);
}

-arch_initcall(init_power7_pmu);
+perf_initcall(init_power7_pmu);
Index: linux-2.6/arch/powerpc/kernel/ppc970-pmu.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/ppc970-pmu.c
+++ linux-2.6/arch/powerpc/kernel/ppc970-pmu.c
@@ -494,4 +494,4 @@ static int init_ppc970_pmu(void)
return register_power_pmu(&ppc970_pmu);
}

-arch_initcall(init_ppc970_pmu);
+perf_initcall(init_ppc970_pmu);
Index: linux-2.6/arch/sh/kernel/cpu/sh4/perf_event.c
===================================================================
--- linux-2.6.orig/arch/sh/kernel/cpu/sh4/perf_event.c
+++ linux-2.6/arch/sh/kernel/cpu/sh4/perf_event.c
@@ -250,4 +250,4 @@ static int __init sh7750_pmu_init(void)

return register_sh_pmu(&sh7750_pmu);
}
-arch_initcall(sh7750_pmu_init);
+perf_initcall(sh7750_pmu_init);
Index: linux-2.6/arch/sh/kernel/cpu/sh4a/perf_event.c
===================================================================
--- linux-2.6.orig/arch/sh/kernel/cpu/sh4a/perf_event.c
+++ linux-2.6/arch/sh/kernel/cpu/sh4a/perf_event.c
@@ -284,4 +284,4 @@ static int __init sh4a_pmu_init(void)

return register_sh_pmu(&sh4a_pmu);
}
-arch_initcall(sh4a_pmu_init);
+perf_initcall(sh4a_pmu_init);