linux/arch/x86/kernel/tracepoint.c
Steven Rostedt (Red Hat) 2b4bc78956 trace,x86: Do not call local_irq_save() in load_current_idt()
As load_current_idt() is now what is used to update the IDT for the
switches needed for NMI, lockdep debug, and for tracing, it must not
call local_irq_save(). This is because one of the users of this is
lockdep, which does tracing of local_irq_save() and when the debug
trap is hit, we need to update the IDT before tracing interrupts
being disabled. As load_current_idt() is used to do this, calling
local_irq_save() which lockdep traces, defeats the point of calling
load_current_idt().

As interrupts are already disabled when used by lockdep and NMI, the
only other user is tracing that can disable interrupts itself. Simply
have the tracing update disable interrupts before calling load_current_idt()
instead of breaking the other users.

Here's the dump that happened:

------------[ cut here ]------------
WARNING: at /work/autotest/nobackup/linux-test.git/kernel/fork.c:1196 copy_process+0x2c3/0x1398()
DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled)
Modules linked in:
CPU: 1 PID: 4570 Comm: gdm-simple-gree Not tainted 3.10.0-rc3-test+ #5
Hardware name:                  /DG965MQ, BIOS MQ96510J.86A.0372.2006.0605.1717 06/05/2006
 ffffffff81d2a7a5 ffff88006ed13d50 ffffffff8192822b ffff88006ed13d90
 ffffffff81035f25 ffff8800721c6000 ffff88006ed13da0 0000000001200011
 0000000000000000 ffff88006ed5e000 ffff8800721c6000 ffff88006ed13df0
Call Trace:
 [<ffffffff8192822b>] dump_stack+0x19/0x1b
 [<ffffffff81035f25>] warn_slowpath_common+0x67/0x80
 [<ffffffff81035fe1>] warn_slowpath_fmt+0x46/0x48
 [<ffffffff812bfc5d>] ? __raw_spin_lock_init+0x31/0x52
 [<ffffffff810341f7>] copy_process+0x2c3/0x1398
 [<ffffffff8103539d>] do_fork+0xa8/0x260
 [<ffffffff810ca7b1>] ? trace_preempt_on+0x2a/0x2f
 [<ffffffff812afb3e>] ? trace_hardirqs_on_thunk+0x3a/0x3f
 [<ffffffff81937fe7>] ? sysret_check+0x1b/0x56
 [<ffffffff81937fe7>] ? sysret_check+0x1b/0x56
 [<ffffffff810355cf>] SyS_clone+0x16/0x18
 [<ffffffff81938369>] stub_clone+0x69/0x90
 [<ffffffff81937fc2>] ? system_call_fastpath+0x16/0x1b
---[ end trace 8b157a9d20ca1aa2 ]---

in fork.c:

 #ifdef CONFIG_PROVE_LOCKING
	DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); <-- bug here
	DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
 #endif

Cc: Seiji Aguchi <seiji.aguchi@hds.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
2013-06-22 13:16:19 -04:00

62 lines
1.3 KiB
C

/*
* Code for supporting irq vector tracepoints.
*
* Copyright (C) 2013 Seiji Aguchi <seiji.aguchi@hds.com>
*
*/
#include <asm/hw_irq.h>
#include <asm/desc.h>
#include <linux/atomic.h>
atomic_t trace_idt_ctr = ATOMIC_INIT(0);
struct desc_ptr trace_idt_descr = { NR_VECTORS * 16 - 1,
(unsigned long) trace_idt_table };
#ifndef CONFIG_X86_64
gate_desc trace_idt_table[NR_VECTORS] __page_aligned_data
= { { { { 0, 0 } } }, };
#endif
static int trace_irq_vector_refcount;
static DEFINE_MUTEX(irq_vector_mutex);
static void set_trace_idt_ctr(int val)
{
atomic_set(&trace_idt_ctr, val);
/* Ensure the trace_idt_ctr is set before sending IPI */
wmb();
}
static void switch_idt(void *arg)
{
unsigned long flags;
local_irq_save(flags);
load_current_idt();
local_irq_restore(flags);
}
void trace_irq_vector_regfunc(void)
{
mutex_lock(&irq_vector_mutex);
if (!trace_irq_vector_refcount) {
set_trace_idt_ctr(1);
smp_call_function(switch_idt, NULL, 0);
switch_idt(NULL);
}
trace_irq_vector_refcount++;
mutex_unlock(&irq_vector_mutex);
}
void trace_irq_vector_unregfunc(void)
{
mutex_lock(&irq_vector_mutex);
trace_irq_vector_refcount--;
if (!trace_irq_vector_refcount) {
set_trace_idt_ctr(0);
smp_call_function(switch_idt, NULL, 0);
switch_idt(NULL);
}
mutex_unlock(&irq_vector_mutex);
}