mirror of
https://github.com/torvalds/linux.git
synced 2024-12-26 21:02:19 +00:00
4ac3dbec80
The kernel build with CONFIG_OPROFILE and CPU_HOTPLUG enabled. The oprofile is initialised using system timer in absence of hardware counters supports. Oprofile isn't started from userland. In this setup while doing a CPU offline the kernel hangs in infinite for loop inside lock_hrtimer_base() function This happens because as part of oprofile_cpu_notify(, it tries to stop an hrtimer which was never started. These per-cpu hrtimers are started when the oprfile is started. echo 1 > /dev/oprofile/enable This problem also existwhen the cpu is booted with maxcpus parameter set. When bringing the remaining cpus online the timers are started even if oprofile is not yet enabled. This patch fix this issue by adding a state variable so that these hrtimer start/stop is only attempted when oprofile is started For stable kernels v2.6.35.y and v2.6.36.y. Reported-by: Jan Sebastien <s-jan@ti.com> Tested-by: sricharan <r.sricharan@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Cc: stable@kernel.org Signed-off-by: Robert Richter <robert.richter@amd.com>
120 lines
2.4 KiB
C
120 lines
2.4 KiB
C
/**
|
|
* @file timer_int.c
|
|
*
|
|
* @remark Copyright 2002 OProfile authors
|
|
* @remark Read the file COPYING
|
|
*
|
|
* @author John Levon <levon@movementarian.org>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/oprofile.h>
|
|
#include <linux/profile.h>
|
|
#include <linux/init.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/hrtimer.h>
|
|
#include <asm/irq_regs.h>
|
|
#include <asm/ptrace.h>
|
|
|
|
#include "oprof.h"
|
|
|
|
static DEFINE_PER_CPU(struct hrtimer, oprofile_hrtimer);
|
|
static int ctr_running;
|
|
|
|
static enum hrtimer_restart oprofile_hrtimer_notify(struct hrtimer *hrtimer)
|
|
{
|
|
oprofile_add_sample(get_irq_regs(), 0);
|
|
hrtimer_forward_now(hrtimer, ns_to_ktime(TICK_NSEC));
|
|
return HRTIMER_RESTART;
|
|
}
|
|
|
|
static void __oprofile_hrtimer_start(void *unused)
|
|
{
|
|
struct hrtimer *hrtimer = &__get_cpu_var(oprofile_hrtimer);
|
|
|
|
if (!ctr_running)
|
|
return;
|
|
|
|
hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
hrtimer->function = oprofile_hrtimer_notify;
|
|
|
|
hrtimer_start(hrtimer, ns_to_ktime(TICK_NSEC),
|
|
HRTIMER_MODE_REL_PINNED);
|
|
}
|
|
|
|
static int oprofile_hrtimer_start(void)
|
|
{
|
|
get_online_cpus();
|
|
ctr_running = 1;
|
|
on_each_cpu(__oprofile_hrtimer_start, NULL, 1);
|
|
put_online_cpus();
|
|
return 0;
|
|
}
|
|
|
|
static void __oprofile_hrtimer_stop(int cpu)
|
|
{
|
|
struct hrtimer *hrtimer = &per_cpu(oprofile_hrtimer, cpu);
|
|
|
|
if (!ctr_running)
|
|
return;
|
|
|
|
hrtimer_cancel(hrtimer);
|
|
}
|
|
|
|
static void oprofile_hrtimer_stop(void)
|
|
{
|
|
int cpu;
|
|
|
|
get_online_cpus();
|
|
for_each_online_cpu(cpu)
|
|
__oprofile_hrtimer_stop(cpu);
|
|
ctr_running = 0;
|
|
put_online_cpus();
|
|
}
|
|
|
|
static int __cpuinit oprofile_cpu_notify(struct notifier_block *self,
|
|
unsigned long action, void *hcpu)
|
|
{
|
|
long cpu = (long) hcpu;
|
|
|
|
switch (action) {
|
|
case CPU_ONLINE:
|
|
case CPU_ONLINE_FROZEN:
|
|
smp_call_function_single(cpu, __oprofile_hrtimer_start,
|
|
NULL, 1);
|
|
break;
|
|
case CPU_DEAD:
|
|
case CPU_DEAD_FROZEN:
|
|
__oprofile_hrtimer_stop(cpu);
|
|
break;
|
|
}
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block __refdata oprofile_cpu_notifier = {
|
|
.notifier_call = oprofile_cpu_notify,
|
|
};
|
|
|
|
int __init oprofile_timer_init(struct oprofile_operations *ops)
|
|
{
|
|
int rc;
|
|
|
|
rc = register_hotcpu_notifier(&oprofile_cpu_notifier);
|
|
if (rc)
|
|
return rc;
|
|
ops->create_files = NULL;
|
|
ops->setup = NULL;
|
|
ops->shutdown = NULL;
|
|
ops->start = oprofile_hrtimer_start;
|
|
ops->stop = oprofile_hrtimer_stop;
|
|
ops->cpu_type = "timer";
|
|
return 0;
|
|
}
|
|
|
|
void __exit oprofile_timer_exit(void)
|
|
{
|
|
unregister_hotcpu_notifier(&oprofile_cpu_notifier);
|
|
}
|