ftrace: handle NMIs safely
Subsystems (7)
Steven Rostedt
maintainercover ftrace: handle NMIs safely
The robustness of ftrace has been the focus of the code modification in 2.6.28. There is one remaining issue that needed to be addressed. This was the case of NMIs.
To state the problem: if a process on one CPU is modifying code that is being executed on another CPU, that CPU will have undefined results, and possibly issue a GPF. To cover this with dynamic ftrace, it calls kstop_machine to prevent the other CPUs from issuing interrupts or executing any other process. The problem we have is that this does not protect us against NMIs.
Discussing this with Arjan and Ingo, we came up with this RCU like solution. When the patcher wants to modify code, it must do the following steps:
1) load the instruction pointer to modify into an IP buffer and the contents that will be modified into a "code" buffer.
2) set a write_bit flag.
3) wait for any running NMIs to finish.
4) modify the code.
5) clear the write bit flag.
6) wait for any running NMIs to finish
7) return the status of the write.
In the NMI handler, the first thing it will do is to check if the write_bit is set, and if so, the NMI performs the write. We do not worry about multiple writers writing to the code at the same time, they are all writing the same thing.
The idea is that executing code will not have a problem if another CPU has a process writing to that code with the same value as what is in the code. In-other-words, the code does not change.
To help verify this, I wrote this module and let it run for a while:
-----------------------------------------------
#include <linux/module.h>
#include <linux/kthread.h>
#define WRITE_SIZE 50
static int write_thread(void *arg)
{
unsigned char *ptr = (unsigned char *)schedule;
static unsigned char write_buf[WRITE_SIZE];
memcpy(write_buf, ptr, WRITE_SIZE);
while (!kthread_should_stop()) {
memcpy(ptr, write_buf, WRITE_SIZE);
msleep(1);
}
return 0;
}static struct task_struct *writer;
static int __init writer_torture_init(void)
{
int ret;
writer = kthread_run(write_thread, NULL, "write_tortured");
ret = PTR_ERR(writer);
if (IS_ERR(writer)) {
writer = NULL;
return ret;
}
return 0;
}static void writer_torture_exit(void)
{
if (writer)
kthread_stop(writer);
}module_init(writer_torture_init);module_exit(writer_torture_exit);
MODULE_AUTHOR("Steven Rostedt");MODULE_DESCRIPTION("Write code torture");MODULE_LICENSE("GPL");
-----------------------------------------------
Do not run the dynamic ftrace while running this module ;-)
The second patch adds stats to the dyn_ftrace_total_info that adds
NR-times-nmi-detect NR-times-NMI-wrote
-- Steve
Steven Rostedt
maintainerPATCH 1/2 ftrace: nmi safe code modification
Modifying code is something that needs special care. On SMP boxes, if code that is being modified is also being executed on another CPU, that CPU will have undefined results.
The dynamic ftrace uses kstop_machine to make the system act like a uniprocessor system. But this does not address NMIs, that can still run on other CPUs.
One approach to handle this is to make all code that are used by NMIs not be traced. But NMIs can call notifiers that spread throughout the kernel and this will be very hard to maintain, and the chance of missing a function is very high.
The approach that this patch takes is to have the NMIs modify the code if the modification is taking place. The way this works is that just writing to code executing on another CPU is not harmful if what is written is the same as what exists.
Two buffers are used: an IP buffer and a "code" buffer.
The steps that the patcher takes are:
1) Put in the instruction pointer into the IP buffer and the new code into the "code" buffer.
2) Set a flag that says we are modifying code
3) Wait for any running NMIs to finish.
4) Write the code
5) clear the flag.
6) Wait for any running NMIs to finish.
If an NMI is executed, it will also write the pending code. Multiple writes are OK, because what is being written is the same. Then the patcher must wait for all running NMIs to finish before going to the next line that must be patched.
This is basically the RCU approach to code modification.
Thanks to Ingo Molnar for suggesting the idea, and to Arjan van de Ven for his guidence on what is safe and what is not.
--- arch/arm/include/asm/ftrace.h | 5 + arch/powerpc/include/asm/ftrace.h | 5 + arch/sh/include/asm/ftrace.h | 5 + arch/sparc/include/asm/ftrace.h | 5 + arch/x86/include/asm/ftrace.h | 15 +++++ arch/x86/kernel/ftrace.c | 107 +++++++++++++++++++++++++++++++++++++- include/linux/hardirq.h | 15 ++++- 7 files changed, 154 insertions(+), 3 deletions(-)
arch/arm/include/asm/ftrace.h +5 -0
@@ -1,6 +1,11 @@#ifndef _ASM_ARM_FTRACE#define _ASM_ARM_FTRACE +#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif +#ifdef CONFIG_FUNCTION_TRACER #define MCOUNT_ADDR ((long)(mcount))#define MCOUNT_INSN_SIZE 4/* sizeof mcount call */
arch/powerpc/include/asm/ftrace.h +5 -0
@@ -1,6 +1,11 @@#ifndef _ASM_POWERPC_FTRACE#define _ASM_POWERPC_FTRACE +#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif +#ifdef CONFIG_FUNCTION_TRACER #define MCOUNT_ADDR ((long)(_mcount))#define MCOUNT_INSN_SIZE 4/* sizeof mcount call */
arch/sh/include/asm/ftrace.h +5 -0
@@ -2,6 +2,11 @@#define __ASM_SH_FTRACE_H#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif + +#ifndef __ASSEMBLY__extern void mcount (void );#endif
arch/sparc/include/asm/ftrace.h +5 -0
@@ -1,6 +1,11 @@#ifndef _ASM_SPARC64_FTRACE#define _ASM_SPARC64_FTRACE +#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif +#ifdef CONFIG_MCOUNT #define MCOUNT_ADDR ((long)(_mcount))#define MCOUNT_INSN_SIZE 4/* sizeof mcount call */
arch/x86/include/asm/ftrace.h +15 -0
@@ -17,6 +17,21 @@ static inline unsigned long ftrace_call_*/ return addr -1 ; } + +#ifdef CONFIG_DYNAMIC_FTRACE +extern void ftrace_nmi_enter (void ); +extern void ftrace_nmi_exit (void ); +#else +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif +#endif + +#else /* CONFIG_FUNCTION_TRACER */ + +#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0)#endif #endif /* CONFIG_FUNCTION_TRACER */
arch/x86/kernel/ftrace.c +106 -1
119 lines not on this page · raw message
include/linux/hardirq.h +13 -2
@@ -5,6 +5,7 @@#include <linux/smp_lock.h> #include <linux/lockdep.h> #include <asm/hardirq.h> +#include <asm/ftrace.h> #include <asm/system.h> /*
@@ -161,7 +162,17 @@ extern void irq_enter(void);*/ extern void irq_exit (void ); -#define nmi_enter () do { lockdep_off(); __irq_enter(); } while (0) -#define nmi_exit () do { __irq_exit(); lockdep_on(); } while (0) +#define nmi_enter () \ + do { \ + ftrace_nmi_enter(); \ + lockdep_off(); \ + __irq_enter(); \ + } while (0) +#define nmi_exit () \ + do { \ + __irq_exit(); \ + lockdep_on(); \ + ftrace_nmi_exit(); \ + } while (0)#endif /* LINUX_HARDIRQ_H */
Re: [PATCH 1/2] ftrace: nmi safe code modification
Quoted 13 lines · On Thu, 30 Oct 2008 16:08:32 -0400 Steven Rostedt <[email protected]> wrote:
Modifying code is something that needs special care. On SMP boxes, if code that is being modified is also being executed on another CPU, that CPU will have undefined results.
The dynamic ftrace uses
kstop_machineto make the system act like a uniprocessor system. But this does not address NMIs, that can still run on other CPUs.One approach to handle this is to make all code that are used by NMIs not be traced. But NMIs can call notifiers that spread throughout the kernel and this will be very hard to maintain, and the chance of missing a function is very high.
Quoted 4 lines
The approach that this patch takes is to have the NMIs modify the code if the modification is taking place. The way this works is that just writing to code executing on another CPU is not harmful if what is written is the same as what exists.
Seems like it'll work.
arch/x86/include/asm/ftrace.h static inline unsigned long ftrace_call_
Quoted 9 lines
arch/x86/include/asm/ftrace.h +8
-0+#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif +#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif
These could all be written in C. If there's a reson to write them in cpp then the `#ifndef __ASSEMBLY__' isn't really needed.
Quoted 10 lines
arch/sparc/include/asm/ftrace.h +2
-0+#endif +#ifdef CONFIG_MCOUNT #define MCOUNT_ADDR ((long)(_mcount))#define MCOUNT_INSN_SIZE 4/* sizeof mcount call */ Index:
linux-tip.git/arch/x86/include/asm/ftrace.h
===================================================================
arch/x86/include/asm/ftrace.h static inline unsigned long ftrace_call_ · line 26
Quoted 10 lines
arch/x86/include/asm/ftrace.h +7
-0@@ -17,6 +17,21 @@ static inline unsigned long ftrace_call_*/ return addr -1 ; } + +#ifdef CONFIG_DYNAMIC_FTRACE +extern void ftrace_nmi_enter (void ); +extern void ftrace_nmi_exit (void ); +#else +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0)
Either this one misses the `#ifndef __ASSEMBLY__' ...
arch/x86/include/asm/ftrace.h static inline unsigned long ftrace_call_ · line 35
Quoted 9 lines
arch/x86/include/asm/ftrace.h +8
-0+#endif +#endif + +#else /* CONFIG_FUNCTION_TRACER */ + +#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0)#endif
or this one didn't need it.
Quoted 12 lines
arch/x86/include/asm/ftrace.h +0
-0#endif /* CONFIG_FUNCTION_TRACER */ Index:
linux-tip.git/arch/x86/kernel/ftrace.c
===================================================================arch/x86/kernel/ftrace.c +2
-0@@ -56,6 +56,111 @@ unsigned char *ftrace_call_replace(unsigreturn calc.code; } +/* +* Modifying code must take extra care. On an SMP machine, if
arch/x86/kernel/ftrace.c unsigned char *ftrace_call_replace(unsig · line 70
Quoted 10 lines
arch/x86/kernel/ftrace.c +10
-0+* the code being modified is also being executed on another CPU +* that CPU will have undefined results and possibly take a GPF. +* We use kstop_machine to stop other CPUS from exectuing code. +* But this does not stop NMIs from happening. We still need +* to protect against that. We separate out the modification of +* the code to take care of this. +* +* Two buffers are added: An IP buffer and a "code" buffer. +* +* 1) Put in the instruction pointer into the IP buffer
s/in //
Quoted 8 lines
arch/x86/kernel/ftrace.c +8
-0+* and the new code into the "code" buffer. +* 2) Set a flag that says we are modifying code +* 3) Wait for any running NMIs to finish. +* 4) Write the code +* 5) clear the flag. +* 6) Wait for any running NMIs to finish. +* +* If an NMI is executed, the first thing it does is to call
arch/x86/kernel/ftrace.c unsigned char *ftrace_call_replace(unsig · line 88
Quoted 10 lines
arch/x86/kernel/ftrace.c +10
-0+* "ftrace_nmi_enter". This will check if the flag is set to write +* and if it is, it will write what is in the IP and "code" buffers. +* +* The trick is, it does not matter if everyone is writing the same +* content to the code location. Also, if a CPU is executing code +* it is OK to write to that code location if the contents being written +* are the same as what exists. +*/ + +static atomic_t in_nmi;
Should formally have an ATONIC_INIT. If we're going to formalise the "all zeroes is OK for atomic_t's" rule then we can leave this as-is.
arch/x86/kernel/ftrace.c unsigned char *ftrace_call_replace(unsig · line 92
Quoted 4 lines
arch/x86/kernel/ftrace.c +4
-0+static int mod_code_status; +static int mod_code_write; +static void *mod_code_ip; +static void *mod_code_newcode;
Some comments decribing the global state would be nice.
Quoted 58 lines
arch/x86/kernel/ftrace.c +58
-0+static void ftrace_mod_code (void ) +{ +/* +* Yes, more than one CPU process can be writing to mod_code_status. +* (and the code itself) +* But if one were to fail, then they all should, and if one were +* to succeed, then they all should. +*/ + mod_code_status =probe_kernel_write (mod_code_ip, mod_code_newcode, +MCOUNT_INSN_SIZE ); + +} + +void ftrace_nmi_enter (void ) +{ +atomic_inc (&in_nmi); +/* Must have in_nmi seen before reading write flag */ +smp_mb (); +if (mod_code_write) +ftrace_mod_code (); +} + +void ftrace_nmi_exit (void ) +{ +/* Finish all executions before clearing in_nmi */ +smp_wmb (); +atomic_dec (&in_nmi); +} + +static void wait_for_nmi (void ) +{ +while (atomic_read (&in_nmi)) +cpu_relax (); +} + +static int +do_ftrace_mod_code (unsigned long ip,void *new_code) +{ + mod_code_ip = (void *)ip; + mod_code_newcode = new_code; + +/* The buffers need to be visible before we let NMIs write them */ +smp_wmb (); + + mod_code_write =1 ; + +/* Make sure write bit is visible before we wait on NMIs */ +smp_mb (); + +wait_for_nmi (); + +/* Make sure all running NMIs have finished before we write the code */ +smp_mb (); + +ftrace_mod_code (); + +/* Make sure the write happens before clearing the bit */ +smp_wmb ();
arch/x86/kernel/ftrace.c unsigned char *ftrace_call_replace(unsig · line 161
Quoted 10 lines
arch/x86/kernel/ftrace.c +10
-0+ + mod_code_write =0 ; + +/* make sure NMIs see the cleared bit */ +smp_mb (); + +wait_for_nmi (); + +return mod_code_status; +}
I guess the weakness here is that the code will only allow a single contiguous hunk of text to be modified. One could envisage situations where two or more separate areas of memory need to be modified atomically/together.
I guess we can cross that bridge when we fall off it.
Quoted 19 lines
include/linux/hardirq.h +4
-2@@ -5,6 +5,7 @@#include <linux/smp_lock.h> #include <linux/lockdep.h> #include <asm/hardirq.h> +#include <asm/ftrace.h> #include <asm/system.h> /* @@ -161,7 +162,17 @@ extern void irq_enter(void);*/ extern void irq_exit (void ); -#define nmi_enter () do { lockdep_off(); __irq_enter(); } while (0) -#define nmi_exit () do { __irq_exit(); lockdep_on(); } while (0) +#define nmi_enter () \ + do { \ + ftrace_nmi_enter(); \
include/linux/hardirq.h extern void irq_enter(void); · line 177
Quoted 10 lines
include/linux/hardirq.h +9
-0+lockdep_off (); \ +__irq_enter (); \ + } while (0) +#define nmi_exit () \ + do { \ + __irq_exit(); \ + lockdep_on(); \ + ftrace_nmi_exit(); \ + } while (0)
<wonders if these needed to be written in cpp>
Re: [PATCH 1/2] ftrace: nmi safe code modification
Quoted 13 lines · Andrew Morton
arch/x86/kernel/ftrace.c unsigned char *ftrace_call_replace(unsig · line 161
Quoted 7 lines
arch/x86/kernel/ftrace.c +7
-0+/* make sure NMIs see the cleared bit */ +smp_mb (); + +wait_for_nmi (); + +return mod_code_status; +}I guess the weakness here is that the code will only allow a single contiguous hunk of text to be modified. One could envisage situations where two or more separate areas of memory need to be modified atomically/together.
Quoted 1 line
I guess we can cross that bridge when we fall off it.
yeah - the whole 'transaction' concept can be extended easily, in just one place.
Not that i think that it would really be useful to go beyond the current (target,len) abstraction - the less complex code patching is done, the better.
Ingo
Steven Rostedt
maintainerRe: [PATCH 1/2] ftrace: nmi safe code modification
In reply to Andrew MortonQuoted 13 lines · Andrew Morton
arch/x86/include/asm/ftrace.h static inline unsigned long ftrace_call_
Quoted 10 lines
arch/x86/include/asm/ftrace.h +8
-0+#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif +#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif These could all be written in C. If there's a reson to write them in cpp then the `#ifndef
__ASSEMBLY__' isn't really needed.
I could do the C macro, and you are right, I did not need the __ASSEMBLY__ part. I guess that was me just being over-protective :-/
Which would you prefer? Changing to C or removing the __ASSEMBLY__?
Quoted 24 lines
Quoted 11 lines
arch/sparc/include/asm/ftrace.h +2
-0+#endif +#ifdef CONFIG_MCOUNT #define MCOUNT_ADDR ((long)(_mcount))#define MCOUNT_INSN_SIZE 4/* sizeof mcount call */ Index:
linux-tip.git/arch/x86/include/asm/ftrace.h
===================================================================arch/x86/include/asm/ftrace.h static inline unsigned long ftrace_call_ · line 26
Quoted 10 lines
arch/x86/include/asm/ftrace.h +7
-0@@ -17,6 +17,21 @@ static inline unsigned long ftrace_call_*/ return addr -1 ; } + +#ifdef CONFIG_DYNAMIC_FTRACE +extern void ftrace_nmi_enter (void ); +extern void ftrace_nmi_exit (void ); +#else +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0)Either this one misses the `#ifndef
__ASSEMBLY__' ...
Quoted 11 lines
arch/x86/include/asm/ftrace.h static inline unsigned long ftrace_call_ · line 35
Quoted 9 lines
arch/x86/include/asm/ftrace.h +8
-0+#endif +#endif + +#else /* CONFIG_FUNCTION_TRACER */ + +#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0)#endif or this one didn't need it.
Wrong in both cases (If we ignore the fact that MACROS do not need the __ASSEMBLY__) ;-)
Notice the two #endif in a row? Here's the full code:
#ifdef CONFIG_FUNCTION_TRACER
#define MCOUNT_ADDR ((long)(mcount))
#define MCOUNT_INSN_SIZE 5 /* sizeof mcount call */
#ifndef __ASSEMBLY__
extern void mcount(void);
static inline unsigned long ftrace_call_adjust(unsigned long addr) {
/*
* call mcount is "e8 <4 byte offset>"
* The addr points to the 4 byte offset and the caller of this
* function wants the pointer to e8. Simply subtract one. */
return addr - 1;
}
#ifdef CONFIG_DYNAMIC_FTRACE
extern void ftrace_nmi_enter(void);
extern void ftrace_nmi_exit(void);
#else
#define ftrace_nmi_enter() do { } while (0)
#define ftrace_nmi_exit() do { } while (0)
#endif
#endif
#else /* CONFIG_FUNCTION_TRACER */
#ifndef __ASSEMBLY__
#define ftrace_nmi_enter() do { } while (0)
#define ftrace_nmi_exit() do { } while (0)
#endif
#endif /* CONFIG_FUNCTION_TRACER */
I guess I could add a comment /* __ASSEMBLY__ */ on that second endif.
Quoted 11 lines
Quoted 11 lines
arch/x86/include/asm/ftrace.h +0
-0#endif /* CONFIG_FUNCTION_TRACER */ Index:
linux-tip.git/arch/x86/kernel/ftrace.c
===================================================================arch/x86/kernel/ftrace.c +0
-0@@ -56,6 +56,111 @@ unsigned char *ftrace_call_replace(unsigreturn calc.code; }
Quoted 14 lines
arch/x86/kernel/ftrace.c unsigned char *ftrace_call_replace(unsig · line 70
Quoted 12 lines
arch/x86/kernel/ftrace.c +12
-0+/* +* Modifying code must take extra care. On an SMP machine, if +* the code being modified is also being executed on another CPU +* that CPU will have undefined results and possibly take a GPF. +* We use kstop_machine to stop other CPUS from exectuing code. +* But this does not stop NMIs from happening. We still need +* to protect against that. We separate out the modification of +* the code to take care of this. +* +* Two buffers are added: An IP buffer and a "code" buffer. +* +* 1) Put in the instruction pointer into the IP buffer s/in //
OK.
Quoted 14 lines
Quoted 14 lines
arch/x86/kernel/ftrace.c +13
-0+* and the new code into the "code" buffer. +* 2) Set a flag that says we are modifying code +* 3) Wait for any running NMIs to finish. +* 4) Write the code +* 5) clear the flag. +* 6) Wait for any running NMIs to finish. +* +* If an NMI is executed, the first thing it does is to call +* "ftrace_nmi_enter". This will check if the flag is set to write +* and if it is, it will write what is in the IP and "code" buffers. +* +* The trick is, it does not matter if everyone is writing the same +* content to the code location. Also, if a CPU is executing code
Quoted 8 lines
arch/x86/kernel/ftrace.c unsigned char *ftrace_call_replace(unsig · line 88
Quoted 5 lines
arch/x86/kernel/ftrace.c +5
-0+* it is OK to write to that code location if the contents being written +* are the same as what exists. +*/ + +static atomic_t in_nmi;Should formally have an
ATONIC_INIT. If we're going to formalise the "all zeroes is OK for atomic_t's" rule then we can leave this as-is.
This is a bad habit of mine :-/ I would like to formalize the "all zeroes is OK for atomic_t's" rule, but I'm not in that position to do so.
Quoted 7 lines
arch/x86/kernel/ftrace.c unsigned char *ftrace_call_replace(unsig · line 92
Quoted 5 lines
arch/x86/kernel/ftrace.c +4
-0+static int mod_code_status; +static int mod_code_write; +static void *mod_code_ip; +static void *mod_code_newcode;Some comments decribing the global state would be nice.
Hmm, I thought I commented enough about this in the above comments. But I guess it would not hurt to add more.
Quoted 75 lines
Quoted 59 lines
arch/x86/kernel/ftrace.c +58
-0+static void ftrace_mod_code (void ) +{ +/* +* Yes, more than one CPU process can be writing to mod_code_status. +* (and the code itself) +* But if one were to fail, then they all should, and if one were +* to succeed, then they all should. +*/ + mod_code_status =probe_kernel_write (mod_code_ip, mod_code_newcode, +MCOUNT_INSN_SIZE ); + +} + +void ftrace_nmi_enter (void ) +{ +atomic_inc (&in_nmi); +/* Must have in_nmi seen before reading write flag */ +smp_mb (); +if (mod_code_write) +ftrace_mod_code (); +} + +void ftrace_nmi_exit (void ) +{ +/* Finish all executions before clearing in_nmi */ +smp_wmb (); +atomic_dec (&in_nmi); +} + +static void wait_for_nmi (void ) +{ +while (atomic_read (&in_nmi)) +cpu_relax (); +} + +static int +do_ftrace_mod_code (unsigned long ip,void *new_code) +{ + mod_code_ip = (void *)ip; + mod_code_newcode = new_code; + +/* The buffers need to be visible before we let NMIs write them */ +smp_wmb (); + + mod_code_write =1 ; + +/* Make sure write bit is visible before we wait on NMIs */ +smp_mb (); + +wait_for_nmi (); + +/* Make sure all running NMIs have finished before we write the code */ +smp_mb (); + +ftrace_mod_code (); + +/* Make sure the write happens before clearing the bit */ +smp_wmb ();arch/x86/kernel/ftrace.c unsigned char *ftrace_call_replace(unsig · line 161
Quoted 10 lines
arch/x86/kernel/ftrace.c +10
-0+ + mod_code_write =0 ; + +/* make sure NMIs see the cleared bit */ +smp_mb (); + +wait_for_nmi (); + +return mod_code_status; +}I guess the weakness here is that the code will only allow a single contiguous hunk of text to be modified. One could envisage situations where two or more separate areas of memory need to be modified atomically/together.
Quoted 1 line
I guess we can cross that bridge when we fall off it.
Well the current code (luckily) does not need that. As Ingo has stated, the simpler this code is the better. There's just too much that can go wrong if this code is defective.
Quoted 24 lines
Quoted 24 lines
include/linux/hardirq.h +8
-2@@ -5,6 +5,7 @@#include <linux/smp_lock.h> #include <linux/lockdep.h> #include <asm/hardirq.h> +#include <asm/ftrace.h> #include <asm/system.h> /* @@ -161,7 +162,17 @@ extern void irq_enter(void);*/ extern void irq_exit (void ); -#define nmi_enter () do { lockdep_off(); __irq_enter(); } while (0) -#define nmi_exit () do { __irq_exit(); lockdep_on(); } while (0) +#define nmi_enter () \ + do { \ + ftrace_nmi_enter(); \ + lockdep_off(); \ + __irq_enter(); \ + } while (0) +#define nmi_exit () \
Quoted 8 lines
include/linux/hardirq.h extern void irq_enter(void); · line 177
Quoted 6 lines
include/linux/hardirq.h +5
-0+do { \ +__irq_exit (); \ +lockdep_on (); \ +ftrace_nmi_exit (); \ + }while (0 )<wonders if these needed to be written in cpp>
A clean up patch can follow this. I would not do it here, because it follows the style of the irq_enter and irq_exit macros just above this code.
I do not see why this could not be converted to a static inline, but that should be a separate patch that udpates both this and the irq_enter/exit macros together.
-- Steve
Re: [PATCH 1/2] ftrace: nmi safe code modification
Quoted 15 lines · On Thu, 30 Oct 2008 16:58:55 -0400 (EDT) Steven Rostedt <[email protected]>…
Quoted 13 lines · Andrew Morton
arch/x86/include/asm/ftrace.h static inline unsigned long ftrace_call_
Quoted 10 lines
arch/x86/include/asm/ftrace.h +8
-0+#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif +#ifndef __ASSEMBLY__ +#define ftrace_nmi_enter () do { } while (0) +#define ftrace_nmi_exit () do { } while (0) +#endif These could all be written in C. If there's a reson to write them in cpp then the `#ifndef
__ASSEMBLY__' isn't really needed.
Quoted 6 lines
I could do the C macro, and you are right, I did not need the
__ASSEMBLY__part. I guess that was me just being over-protective :-/Which would you prefer? Changing to C or removing the
__ASSEMBLY__?From a general perspective, C is better. Has typechecking, adds a ref
to the arguments which can prevent unused-var warnings, easier to read and maintain, more likely to be commented, known about by debug info, doesn't all get clumped into a single line in debug info, easier/safer to uninline, blah, blah.
Also it seems a bit weird to do
#ifdef SOMETHING
#define foo(...) ...
#else
extern void foo(...);
#endif
Doing it in C has the downside that more things need to be visible at the definition site, so more includes might be needed. Often fixable by uninlining.
I dunno. People seem to instinctively reach for a macro without thinking, because that's how grandpa did it or something.
Steven Rostedt
maintainerPATCH 1/1 ftrace: nmi safe code clean ups
This patch cleans up the NMI safe code for dynamic ftrace as suggested by Andrew Morton.
--- arch/arm/include/asm/ftrace.h | 4 ++-- arch/powerpc/include/asm/ftrace.h | 4 ++-- arch/sh/include/asm/ftrace.h | 4 ++-- arch/sparc/include/asm/ftrace.h | 4 ++-- arch/x86/include/asm/ftrace.h | 10 +++++----- arch/x86/kernel/ftrace.c | 16 ++++++++-------- include/linux/ftrace.h | 3 +++ kernel/trace/trace.c | 9 ++++----- 8 files changed, 28 insertions(+), 26 deletions(-)
arch/arm/include/asm/ftrace.h +2 -2
@@ -2,8 +2,8 @@#define _ASM_ARM_FTRACE#ifndef __ASSEMBLY__ -#define ftrace_nmi_enter () do { } while (0) -#define ftrace_nmi_exit () do { } while (0) +static inline void ftrace_nmi_enter (void ) { } +static inline void ftrace_nmi_exit (void ) { }#endif #ifdef CONFIG_FUNCTION_TRACER
arch/powerpc/include/asm/ftrace.h +2 -2
@@ -2,8 +2,8 @@#define _ASM_POWERPC_FTRACE#ifndef __ASSEMBLY__ -#define ftrace_nmi_enter () do { } while (0) -#define ftrace_nmi_exit () do { } while (0) +static inline void ftrace_nmi_enter (void ) { } +static inline void ftrace_nmi_exit (void ) { }#endif #ifdef CONFIG_FUNCTION_TRACER
arch/sh/include/asm/ftrace.h +2 -2
@@ -2,8 +2,8 @@#define __ASM_SH_FTRACE_H#ifndef __ASSEMBLY__ -#define ftrace_nmi_enter () do { } while (0) -#define ftrace_nmi_exit () do { } while (0) +static inline void ftrace_nmi_enter (void ) { } +static inline void ftrace_nmi_exit (void ) { }#endif #ifndef __ASSEMBLY__
arch/sparc/include/asm/ftrace.h +2 -2
@@ -2,8 +2,8 @@#define _ASM_SPARC64_FTRACE#ifndef __ASSEMBLY__ -#define ftrace_nmi_enter () do { } while (0) -#define ftrace_nmi_exit () do { } while (0) +static inline void ftrace_nmi_enter (void ) { } +static inline void ftrace_nmi_exit (void ) { }#endif #ifdef CONFIG_MCOUNT
arch/x86/include/asm/ftrace.h +5 -5
@@ -22,16 +22,16 @@ static inline unsigned long ftrace_call_extern void ftrace_nmi_enter (void );extern void ftrace_nmi_exit (void );#else -#define ftrace_nmi_enter () do { } while (0) -#define ftrace_nmi_exit () do { } while (0) -#endif +static inline void ftrace_nmi_enter (void ) { } +static inline void ftrace_nmi_exit (void ) { }#endif +#endif /* __ASSEMBLY__ */ #else /* CONFIG_FUNCTION_TRACER */ #ifndef __ASSEMBLY__ -#define ftrace_nmi_enter () do { } while (0) -#define ftrace_nmi_exit () do { } while (0) +static inline void ftrace_nmi_enter (void ) { } +static inline void ftrace_nmi_exit (void ) { }#endif #endif /* CONFIG_FUNCTION_TRACER */
arch/x86/kernel/ftrace.c +8 -8
@@ -67,7 +67,7 @@ unsigned char *ftrace_call_replace(unsig* * Two buffers are added: An IP buffer and a "code" buffer. * -* 1) Put in the instruction pointer into the IP buffer +* 1) Put the instruction pointer into the IP buffer * and the new code into the "code" buffer. * 2) Set a flag that says we are modifying code * 3) Wait for any running NMIs to finish.
@@ -85,14 +85,14 @@ unsigned char *ftrace_call_replace(unsig* are the same as what exists. */ -static atomic_t in_nmi; -static int mod_code_status; -static int mod_code_write; -static void *mod_code_ip; -static void *mod_code_newcode; +static atomic_t in_nmi =ATOMIC_INIT (0 ); +static int mod_code_status;/* holds return value of text write */ +static int mod_code_write;/* set when NMI should do the write */ +static void *mod_code_ip;/* holds the IP to write to */ +static void *mod_code_newcode;/* holds the text to write to the IP */ -static int nmi_wait_count; -static atomic_t nmi_update_count; +static unsigned nmi_wait_count; +static atomic_t nmi_update_count =ATOMIC_INIT (0 );int ftrace_arch_read_dyn_info (char *buf,int size) {
include/linux/ftrace.h +3 -0
@@ -74,6 +74,9 @@ extern void ftrace_caller(void);extern void ftrace_call (void );extern void mcount_call (void ); +/* May be defined in arch */ +extern int ftrace_arch_read_dyn_info (char *buf,int size); +/** * ftrace_modify_code - modify code segment * @ip: the address of the code segment
kernel/trace/trace.c +4 -5
@@ -2837,10 +2837,6 @@ static struct file_operations tracing_ma#ifdef CONFIG_DYNAMIC_FTRACE -#define DYN_INFO_BUF_SIZE 1023 -static char ftrace_dyn_info_buffer[DYN_INFO_BUF_SIZE +1 ]; -static DEFINE_MUTEX (dyn_info_mutex); -int __weak ftrace_arch_read_dyn_info (char *buf,int size) {return 0 ;
@@ -2850,14 +2846,17 @@ static ssize_t tracing_read_dyn_info(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { + static char ftrace_dyn_info_buffer[1024]; +static DEFINE_MUTEX (dyn_info_mutex);unsigned long *p = filp->private_data;char *buf = ftrace_dyn_info_buffer; +int size =ARRAY_SIZE (ftrace_dyn_info_buffer);int r;mutex_lock (&dyn_info_mutex); r =sprintf (buf,"%ld " , *p); - r +=ftrace_arch_read_dyn_info (buf+r,DYN_INFO_BUF_SIZE -r); + r +=ftrace_arch_read_dyn_info (buf+r, (size-1 )-r); buf[r++] ='\n' ; r =simple_read_from_buffer (ubuf, cnt, ppos, buf, r);
Steven Rostedt
maintainerPATCH 1/1 hardirq.h clean up
This patch converts the CPP macros of __irq_enter, __irq_exit, nmi_enter, and nmi_exit into static inlines.
--- include/linux/hardirq.h | 53 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 26 deletions(-)
include/linux/hardirq.h +27 -26
@@ -133,13 +133,13 @@ extern void rcu_irq_exit(void);* always balanced, so the interrupted value of ->hardirq_context * will always be restored. */ -#define __irq_enter () \ - do { \ - rcu_irq_enter(); \ - account_system_vtime(current); \ - add_preempt_count(HARDIRQ_OFFSET); \ - trace_hardirq_enter(); \ - } while (0) +static inline void __irq_enter (void ) +{ +rcu_irq_enter (); +account_system_vtime (current); +add_preempt_count (HARDIRQ_OFFSET ); +trace_hardirq_enter (); +}/* * Enter irq context (on NO_HZ, update jiffies):
@@ -149,30 +149,31 @@ extern void irq_enter(void);/* * Exit irq context without processing softirqs: */ -#define __irq_exit () \ - do { \ - trace_hardirq_exit(); \ - account_system_vtime(current); \ - sub_preempt_count(HARDIRQ_OFFSET); \ - rcu_irq_exit(); \ - } while (0) +static inline void __irq_exit (void ) +{ +trace_hardirq_exit (); +account_system_vtime (current); +sub_preempt_count (HARDIRQ_OFFSET ); +rcu_irq_exit (); +}/* * Exit irq context and process softirqs if needed: */ extern void irq_exit (void ); -#define nmi_enter () \ - do { \ - ftrace_nmi_enter(); \ - lockdep_off(); \ - __irq_enter(); \ - } while (0) -#define nmi_exit () \ - do { \ - __irq_exit(); \ - lockdep_on(); \ - ftrace_nmi_exit(); \ - } while (0) +static inline void nmi_enter (void ) +{ +ftrace_nmi_enter (); +lockdep_off (); +__irq_enter (); +} + +static inline void nmi_exit (void ) +{ +__irq_exit (); +lockdep_on (); +ftrace_nmi_exit (); +}#endif /* LINUX_HARDIRQ_H */
Re: [PATCH] hardirq.h clean up
Quoted 8 lines · Steven Rostedt
This patch converts the CPP macros of
__irq_enter,__irq_exit,nmi_enter, andnmi_exitinto static inlines.Signed-off-by Steven Rostedt <[email protected]>--- include/linux/hardirq.h | 53 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 26 deletions(-)
makes sense, but have you done build-testing (and cross-build-testing) of this? I remember that this area was include-file-dependencies-hell in the past.
perhaps with your simulate-old-arch patch on x86 we could tickle some of the issues that trigger here.
Ingo
Steven Rostedt
maintainerPATCH 1/1 ftrace: fix hardirq header for non ftrace archs
Not all archs implement ftrace, and therefore do not have an asm/ftrace.h. This patch corrects the problem.
The ftrace_nmi_enter/exit now must be defined for all archs that implement dynamic ftrace. Currently, only x86 does.
--- arch/arm/include/asm/ftrace.h | 5 ----- arch/powerpc/include/asm/ftrace.h | 5 ----- arch/sh/include/asm/ftrace.h | 5 ----- arch/sparc/include/asm/ftrace.h | 5 ----- arch/x86/include/asm/ftrace.h | 16 ---------------- include/linux/ftrace.h | 5 ++++- include/linux/hardirq.h | 2 +- 7 files changed, 5 insertions(+), 38 deletions(-)
arch/arm/include/asm/ftrace.h +0 -5
@@ -1,11 +1,6 @@#ifndef _ASM_ARM_FTRACE#define _ASM_ARM_FTRACE -#ifndef __ASSEMBLY__ -static inline void ftrace_nmi_enter (void ) { } -static inline void ftrace_nmi_exit (void ) { } -#endif -#ifdef CONFIG_FUNCTION_TRACER #define MCOUNT_ADDR ((long)(mcount))#define MCOUNT_INSN_SIZE 4/* sizeof mcount call */
arch/powerpc/include/asm/ftrace.h +0 -5
@@ -1,11 +1,6 @@#ifndef _ASM_POWERPC_FTRACE#define _ASM_POWERPC_FTRACE -#ifndef __ASSEMBLY__ -static inline void ftrace_nmi_enter (void ) { } -static inline void ftrace_nmi_exit (void ) { } -#endif -#ifdef CONFIG_FUNCTION_TRACER #define MCOUNT_ADDR ((long)(_mcount))#define MCOUNT_INSN_SIZE 4/* sizeof mcount call */
arch/sh/include/asm/ftrace.h +0 -5
@@ -2,11 +2,6 @@#define __ASM_SH_FTRACE_H#ifndef __ASSEMBLY__ -static inline void ftrace_nmi_enter (void ) { } -static inline void ftrace_nmi_exit (void ) { } -#endif - -#ifndef __ASSEMBLY__extern void mcount (void );#endif
arch/sparc/include/asm/ftrace.h +0 -5
@@ -1,11 +1,6 @@#ifndef _ASM_SPARC64_FTRACE#define _ASM_SPARC64_FTRACE -#ifndef __ASSEMBLY__ -static inline void ftrace_nmi_enter (void ) { } -static inline void ftrace_nmi_exit (void ) { } -#endif -#ifdef CONFIG_MCOUNT #define MCOUNT_ADDR ((long)(_mcount))#define MCOUNT_INSN_SIZE 4/* sizeof mcount call */
arch/x86/include/asm/ftrace.h +0 -16
@@ -17,23 +17,7 @@ static inline unsigned long ftrace_call_*/ return addr -1 ; } - -#ifdef CONFIG_DYNAMIC_FTRACE -extern void ftrace_nmi_enter (void ); -extern void ftrace_nmi_exit (void ); -#else -static inline void ftrace_nmi_enter (void ) { } -static inline void ftrace_nmi_exit (void ) { } -#endif #endif /* __ASSEMBLY__ */ - -#else /* CONFIG_FUNCTION_TRACER */ - -#ifndef __ASSEMBLY__ -static inline void ftrace_nmi_enter (void ) { } -static inline void ftrace_nmi_exit (void ) { } -#endif -#endif /* CONFIG_FUNCTION_TRACER */ #endif /* _ASM_X86_FTRACE_H */
include/linux/ftrace.h +4 -1
@@ -44,7 +44,6 @@ static inline void ftrace_kill(void) { }#endif /* CONFIG_FUNCTION_TRACER */ #ifdef CONFIG_DYNAMIC_FTRACE -enum {FTRACE_FL_FREE = (1 <<0 ), FTRACE_FL_FAILED = (1 << 1),
@@ -105,6 +104,8 @@ extern void ftrace_release(void *start,extern void ftrace_disable_daemon (void );extern void ftrace_enable_daemon (void ); +extern void ftrace_nmi_enter (void ); +extern void ftrace_nmi_exit (void );#else # define skip_trace (ip) ({ 0; })
@@ -113,6 +114,8 @@ extern void ftrace_enable_daemon(void);# define ftrace_disable_daemon () do { } while (0)# define ftrace_enable_daemon () do { } while (0)static inline void ftrace_release (void *start,unsigned long size) { } +static inline void ftrace_nmi_enter (void ) { } +static inline void ftrace_nmi_exit (void ) { }#endif /* CONFIG_DYNAMIC_FTRACE */ /* totally disable ftrace - can not re-enable after this */
include/linux/hardirq.h +1 -1
@@ -4,8 +4,8 @@#include <linux/preempt.h> #include <linux/smp_lock.h> #include <linux/lockdep.h> +#include <linux/ftrace.h> #include <asm/hardirq.h> -#include <asm/ftrace.h> #include <asm/system.h> /*
Ingo Molnar
AppliedRe: [PATCH] ftrace: fix hardirq header for non ftrace archs
Quoted 8 lines · Steven Rostedt
Not all archs implement ftrace, and therefore do not have an
asm/ftrace.h. This patch corrects the problem.The ftrace_nmi_enter/exit now must be defined for all archs that implement dynamic ftrace. Currently, only x86 does.
Signed-off-by Steven Rostedt <[email protected]>
Quoted 9 lines
--- arch/arm/include/asm/ftrace.h | 5 ----- arch/powerpc/include/asm/ftrace.h | 5 ----- arch/sh/include/asm/ftrace.h | 5 ----- arch/sparc/include/asm/ftrace.h | 5 ----- arch/x86/include/asm/ftrace.h | 16 ---------------- include/linux/ftrace.h | 5 ++++- include/linux/hardirq.h | 2 +- 7 files changed, 5 insertions(+), 38 deletions(-)
applied to tip/tracing/nmisafe, thanks Steve!
Ingo
Ingo Molnar
AppliedRe: [PATCH] ftrace: nmi safe code clean ups
In reply to Steven RostedtQuoted 5 lines · Steven Rostedt
This patch cleans up the NMI safe code for dynamic ftrace as suggested by Andrew Morton.
Signed-off-by Steven Rostedt <[email protected]>
Quoted 10 lines
--- arch/arm/include/asm/ftrace.h | 4 ++-- arch/powerpc/include/asm/ftrace.h | 4 ++-- arch/sh/include/asm/ftrace.h | 4 ++-- arch/sparc/include/asm/ftrace.h | 4 ++-- arch/x86/include/asm/ftrace.h | 10 +++++----- arch/x86/kernel/ftrace.c | 16 ++++++++-------- include/linux/ftrace.h | 3 +++ kernel/trace/trace.c | 9 ++++----- 8 files changed, 28 insertions(+), 26 deletions(-)
applied to tip/tracing/nmisafe, thanks Steve!
Ingo
Steven Rostedt
maintainerPATCH 2/2 ftrace: nmi update statistics
In reply to Steven RostedtThis patch adds dynamic ftrace NMI update statistics to the /debugfs/tracing/dyn_ftrace_total_info stat file.
--- arch/x86/kernel/ftrace.c | 26 ++++++++++++++++++++++++-- kernel/trace/trace.c | 31 ++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 9 deletions(-)
arch/x86/kernel/ftrace.c +24 -2
@@ -91,6 +91,19 @@ static int mod_code_write;static void *mod_code_ip;static void *mod_code_newcode; +static int nmi_wait_count; +static atomic_t nmi_update_count; + +int ftrace_arch_read_dyn_info (char *buf,int size) +{ +int r; + + r =snprintf (buf, size,"%u %u" , + nmi_wait_count, +atomic_read (&nmi_update_count)); +return r; +} +static void ftrace_mod_code (void ) {/*
@@ -109,8 +122,10 @@ void ftrace_nmi_enter(void)atomic_inc (&in_nmi);/* Must have in_nmi seen before reading write flag */ smp_mb (); -if (mod_code_write) +if (mod_code_write) {ftrace_mod_code (); +atomic_inc (&nmi_update_count); + } }void ftrace_nmi_exit (void )
@@ -122,8 +137,15 @@ void ftrace_nmi_exit(void)static void wait_for_nmi (void ) { -while (atomic_read (&in_nmi)) +int waited =0 ; + +while (atomic_read (&in_nmi)) { + waited =1 ;cpu_relax (); + } + +if (waited) + nmi_wait_count++; } static int
kernel/trace/trace.c +24 -7
@@ -2837,22 +2837,39 @@ static struct file_operations tracing_ma#ifdef CONFIG_DYNAMIC_FTRACE +#define DYN_INFO_BUF_SIZE 1023 +static char ftrace_dyn_info_buffer[DYN_INFO_BUF_SIZE +1 ]; +static DEFINE_MUTEX (dyn_info_mutex); + +int __weak ftrace_arch_read_dyn_info (char *buf,int size) +{ +return 0 ; +} +static ssize_t -tracing_read_long (struct file *filp,char __user *ubuf, +tracing_read_dyn_info (struct file *filp,char __user *ubuf,size_t cnt,loff_t *ppos) {unsigned long *p = filp->private_data; -char buf[64 ]; +char *buf = ftrace_dyn_info_buffer;int r; - r =sprintf (buf,"%ld\n" , *p); +mutex_lock (&dyn_info_mutex); + r =sprintf (buf,"%ld " , *p); + + r +=ftrace_arch_read_dyn_info (buf+r,DYN_INFO_BUF_SIZE -r); + buf[r++] ='\n' ; + + r =simple_read_from_buffer (ubuf, cnt, ppos, buf, r); + +mutex_unlock (&dyn_info_mutex); -return simple_read_from_buffer (ubuf, cnt, ppos, buf, r); +return r; } -static struct file_operations tracing_read_long_fops = { +static struct file_operations tracing_dyn_info_fops = { .open = tracing_open_generic, - .read = tracing_read_long, + .read = tracing_read_dyn_info, };#endif
@@ -2961,7 +2978,7 @@ static __init int tracer_init_debugfs(vo#ifdef CONFIG_DYNAMIC_FTRACE entry =debugfs_create_file ("dyn_ftrace_total_info" ,0444 , d_tracer, &ftrace_update_tot_cnt, - &tracing_read_long_fops); + &tracing_dyn_info_fops);if (!entry)pr_warning ("Could not create debugfs " "'dyn_ftrace_total_info' entry\n" );
Re: [PATCH 2/2] ftrace: nmi update statistics
Quoted 7 lines · On Thu, 30 Oct 2008 16:08:33 -0400 Steven Rostedt <[email protected]> wrote:
This patch adds dynamic ftrace NMI update statistics to the /debugfs/tracing/dyn_ftrace_total_info stat file.
Signed-off-by Steven Rostedt <[email protected]>--- arch/x86/kernel/ftrace.c | 26 ++++++++++++++++++++++++-- kernel/trace/trace.c | 31 ++++++++++++++++++++++++-------
No header files were modified, but ftrace_arch_read_dyn_info() should have been declared so that the above two compilation units see the declaration.
arch/x86/kernel/ftrace.c static int mod_code_write; · line 95
Quoted 11 lines
Index:
linux-tip.git/arch/x86/kernel/ftrace.c
===================================================================arch/x86/kernel/ftrace.c +2
-0@@ -91,6 +91,19 @@ static int mod_code_write;static void *mod_code_ip;static void *mod_code_newcode; +static int nmi_wait_count; +static atomic_t nmi_update_count;
<the ATOMIC_INIT thing>
arch/x86/kernel/ftrace.c static int mod_code_write; · line 105
Quoted 9 lines
arch/x86/kernel/ftrace.c +9
-0+int ftrace_arch_read_dyn_info (char *buf,int size) +{ +int r; + + r =snprintf (buf, size,"%u %u" , + nmi_wait_count, +atomic_read (&nmi_update_count)); +return r; +}
Yes, nmi_wait_count is an unsigned quantity. Might as well define it thusly?
Quoted 37 lines
arch/x86/kernel/ftrace.c +11
-2static void ftrace_mod_code (void ) {/* @@ -109,8 +122,10 @@ void ftrace_nmi_enter(void)atomic_inc (&in_nmi);/* Must have in_nmi seen before reading write flag */ smp_mb (); -if (mod_code_write) +if (mod_code_write) {ftrace_mod_code (); +atomic_inc (&nmi_update_count); + } }void ftrace_nmi_exit (void )@@ -122,8 +137,15 @@ void ftrace_nmi_exit(void)static void wait_for_nmi (void ) { -while (atomic_read (&in_nmi)) +int waited =0 ; + +while (atomic_read (&in_nmi)) { + waited =1 ;cpu_relax (); + } + +if (waited) + nmi_wait_count++; } static intIndex:
linux-tip.git/kernel/trace/trace.c
===================================================================
kernel/trace/trace.c static struct file_operations tracing_ma · line 2841
Quoted 5 lines
kernel/trace/trace.c +2
-0@@ -2837,22 +2837,39 @@ static struct file_operations tracing_ma#ifdef CONFIG_DYNAMIC_FTRACE +#define DYN_INFO_BUF_SIZE 1023 +static char ftrace_dyn_info_buffer[DYN_INFO_BUF_SIZE +1 ];
Could be made local to tracing_read_dyn_info().
Could just be
static char ftrace_dyn_info_buffer[1024];
then use sizeof/ARRAY_SIZE elsewhere. I think this is a bit safer and more readable - the reader doesn't have to run around the code checking that the correct #define was used in both places.
Quoted 50 lines
kernel/trace/trace.c +22
-7+static DEFINE_MUTEX (dyn_info_mutex); + +int __weak ftrace_arch_read_dyn_info (char *buf,int size) +{ +return 0 ; +} +static ssize_t -tracing_read_long (struct file *filp,char __user *ubuf, +tracing_read_dyn_info (struct file *filp,char __user *ubuf,size_t cnt,loff_t *ppos) {unsigned long *p = filp->private_data; -char buf[64 ]; +char *buf = ftrace_dyn_info_buffer;int r; - r =sprintf (buf,"%ld\n" , *p); +mutex_lock (&dyn_info_mutex); + r =sprintf (buf,"%ld " , *p); + + r +=ftrace_arch_read_dyn_info (buf+r,DYN_INFO_BUF_SIZE -r); + buf[r++] ='\n' ; + + r =simple_read_from_buffer (ubuf, cnt, ppos, buf, r); + +mutex_unlock (&dyn_info_mutex); -return simple_read_from_buffer (ubuf, cnt, ppos, buf, r); +return r; } -static struct file_operations tracing_read_long_fops = { +static struct file_operations tracing_dyn_info_fops = { .open = tracing_open_generic, - .read = tracing_read_long, + .read = tracing_read_dyn_info, };#endif @@ -2961,7 +2978,7 @@ static __init int tracer_init_debugfs(vo#ifdef CONFIG_DYNAMIC_FTRACE entry =debugfs_create_file ("dyn_ftrace_total_info" ,0444 , d_tracer, &ftrace_update_tot_cnt, - &tracing_read_long_fops); + &tracing_dyn_info_fops);if (!entry)pr_warning ("Could not create debugfs " "'dyn_ftrace_total_info' entry\n" );
Steven Rostedt
maintainerRe: [PATCH 2/2] ftrace: nmi update statistics
Quoted 6 lines · Andrew Morton
Quoted 3 lines · On Thu, 30 Oct 2008 16:08:33 -0400 Steven Rostedt <[email protected]> wrote:
This patch adds dynamic ftrace NMI update statistics to the /debugfs/tracing/dyn_ftrace_total_info stat file.
Quoted 8 lines
Quoted 4 lines
Signed-off-by Steven Rostedt <[email protected]>--- arch/x86/kernel/ftrace.c | 26 ++++++++++++++++++++++++-- kernel/trace/trace.c | 31 ++++++++++++++++++++++++-------No header files were modified, but
ftrace_arch_read_dyn_info()should have been declared so that the above two compilation units see the declaration.
Ah, you caught me being lazy ;-) Since both also define the function, I figured I'd be OK. But you are right, this would prevent bugs where the two functions somehow got different parameters.
Will fix.
Quoted 10 lines
Quoted 10 lines
Index:
linux-tip.git/arch/x86/kernel/ftrace.c
===================================================================arch/x86/kernel/ftrace.c +0
-0@@ -91,6 +91,19 @@ static int mod_code_write;static void *mod_code_ip;static void *mod_code_newcode;
Quoted 4 lines
arch/x86/kernel/ftrace.c static int mod_code_write; · line 95
Quoted 2 lines
arch/x86/kernel/ftrace.c +2
-0+static int nmi_wait_count; +static atomic_t nmi_update_count;<the
ATOMIC_INITthing>
Hmm, this being arch specific code, is there such a thing as "atomic declared as zero on x86 is OK" rule?
Quoted 13 lines
arch/x86/kernel/ftrace.c static int mod_code_write; · line 105
Quoted 10 lines
arch/x86/kernel/ftrace.c +9
-0+int ftrace_arch_read_dyn_info (char *buf,int size) +{ +int r; + + r =snprintf (buf, size,"%u %u" , + nmi_wait_count, +atomic_read (&nmi_update_count)); +return r; +}Yes,
nmi_wait_countis an unsigned quantity. Might as well define it thusly?
OK.
Quoted 47 lines
Quoted 38 lines
arch/x86/kernel/ftrace.c +11
-2static void ftrace_mod_code (void ) {/* @@ -109,8 +122,10 @@ void ftrace_nmi_enter(void)atomic_inc (&in_nmi);/* Must have in_nmi seen before reading write flag */ smp_mb (); -if (mod_code_write) +if (mod_code_write) {ftrace_mod_code (); +atomic_inc (&nmi_update_count); + } }void ftrace_nmi_exit (void )@@ -122,8 +137,15 @@ void ftrace_nmi_exit(void)static void wait_for_nmi (void ) { -while (atomic_read (&in_nmi)) +int waited =0 ; + +while (atomic_read (&in_nmi)) { + waited =1 ;cpu_relax (); + } + +if (waited) + nmi_wait_count++; } static intIndex:
linux-tip.git/kernel/trace/trace.c
===================================================================kernel/trace/trace.c static struct file_operations tracing_ma · line 2841
Quoted 5 lines
kernel/trace/trace.c +2
-0@@ -2837,22 +2837,39 @@ static struct file_operations tracing_ma#ifdef CONFIG_DYNAMIC_FTRACE +#define DYN_INFO_BUF_SIZE 1023 +static char ftrace_dyn_info_buffer[DYN_INFO_BUF_SIZE +1 ];Could be made local to
tracing_read_dyn_info().
Quoted 5 lines
Could just be
static char ftrace_dyn_info_buffer[1024];then use sizeof/ARRAY_SIZE elsewhere. I think this is a bit safer and
I think there's a defined macro for that... (goes look)
Yep! it's called ARRAY_SIZE(arr)
Quoted 2 lines
more readable - the reader doesn't have to run around the code checking that the correct #define was used in both places.
Will fix.
Thanks,
-- Steve
Ingo Molnar
AppliedQuoted 3 lines · Steven Rostedt
The robustness of ftrace has been the focus of the code modification in 2.6.28. There is one remaining issue that needed to be addressed. This was the case of NMIs.
applied to tip/tracing/nmisafe, thanks Steve!
this looks a lot nicer approach than either putting some sort of lock into NMI context (yuck) or the disabling of NMIs (not really possible in a generic way architecturally).
the impact is quite non-trivial, so i dont think this is v2.6.28 material.
Ingo
Steven Rostedt
maintainerQuoted 7 lines · Ingo Molnar
Quoted 3 lines · Steven Rostedt
The robustness of ftrace has been the focus of the code modification in 2.6.28. There is one remaining issue that needed to be addressed. This was the case of NMIs.
Quoted 8 lines
applied to tip/tracing/nmisafe, thanks Steve!
this looks a lot nicer approach than either putting some sort of lock into NMI context (yuck) or the disabling of NMIs (not really possible in a generic way architecturally).
the impact is quite non-trivial, so i dont think this is v2.6.28 material.
Ingo,
Do you want me to send patches on top of these to address Andrew's comements? Or do you want me to resend these with the updates.
I prefer to send patches on top, that way Andrew can make sure I did his changes correctly ;-)
-- Steve
Quoted 19 lines · Steven Rostedt
Quoted 7 lines · Ingo Molnar
Quoted 3 lines · Steven Rostedt
The robustness of ftrace has been the focus of the code modification in 2.6.28. There is one remaining issue that needed to be addressed. This was the case of NMIs.
Quoted 8 lines
applied to tip/tracing/nmisafe, thanks Steve!
this looks a lot nicer approach than either putting some sort of lock into NMI context (yuck) or the disabling of NMIs (not really possible in a generic way architecturally).
the impact is quite non-trivial, so i dont think this is v2.6.28 material.
Quoted 7 lines
Ingo,
Do you want me to send patches on top of these to address Andrew's comements? Or do you want me to resend these with the updates.
I prefer to send patches on top, that way Andrew can make sure I did his changes correctly ;-)
yeah, please do it that way. It's a separate topic so we can fold them back if they are also bugfixes. (and they are all cleanliness related and i wanted to see the stability impact ASAP)
Ingo