tracing: A few updates for 4.15
Subsystems (3)
Steven Rostedt
maintainercover tracing: A few updates for 4.15
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
for-next
Head SHA1: 8715b108cd75523c9b2e833cdcd7aeb363767f95
Joel Fernandes (3):
tracing: Prepare to add preempt and irq trace events
tracing: Add support for preempt and irq enable/disable events
ftrace: Clear hashes of stale ips of init memory
----
include/linux/ftrace.h | 3 +- include/trace/events/preemptirq.h | 70 ++++++++++++++++++++ kernel/trace/Kconfig | 11 ++++ kernel/trace/Makefile | 1 + kernel/trace/ftrace.c | 69 ++++++++++++++++++++ kernel/trace/trace_irqsoff.c | 133 ++++++++++++++++++++++++++++++-------- 6 files changed, 260 insertions(+), 27 deletions(-) create mode 100644 include/trace/events/preemptirq.h
Steven Rostedt
maintainerPATCH 1/3 tracing: Prepare to add preempt and irq trace events
From: Joel Fernandes <[email protected]>
In preparation of adding irqsoff and preemptsoff enable and disable trace events, move required functions and code to make it easier to add these events in a later patch. This patch is just code movement and no functional change.
--- kernel/trace/trace_irqsoff.c | 100 ++++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 26 deletions(-)
kernel/trace/trace_irqsoff.c +74 -26
@@ -16,6 +16,7 @@#include "trace.h" +#if defined(CONFIG_IRQSOFF_TRACER ) || defined(CONFIG_PREEMPT_TRACER )static struct trace_array *irqsoff_trace__read_mostly ;static int tracer_enabled__read_mostly ;
@@ -463,63 +464,43 @@ void time_hardirqs_off(unsigned long a0, unsigned long a1)#else /* !CONFIG_PROVE_LOCKING */ /* -* Stubs: -*/ - -void trace_softirqs_on (unsigned long ip) -{ -} - -void trace_softirqs_off (unsigned long ip) -{ -} - -inline void print_irqtrace_events (struct task_struct *curr) -{ -} - -/* * We are only interested in hardirq on/off events: */ -void trace_hardirqs_on (void ) +static inline void tracer_hardirqs_on (void ) {if (!preempt_trace () &&irq_trace ())stop_critical_timing (CALLER_ADDR0 ,CALLER_ADDR1 ); } -EXPORT_SYMBOL (trace_hardirqs_on); -void trace_hardirqs_off (void ) +static inline void tracer_hardirqs_off (void ) {if (!preempt_trace () &&irq_trace ())start_critical_timing (CALLER_ADDR0 ,CALLER_ADDR1 ); } -EXPORT_SYMBOL (trace_hardirqs_off); -__visible void trace_hardirqs_on_caller(unsigned long caller_addr) +static inline void tracer_hardirqs_on_caller (unsigned long caller_addr) {if (!preempt_trace () &&irq_trace ())stop_critical_timing (CALLER_ADDR0 , caller_addr); } -EXPORT_SYMBOL (trace_hardirqs_on_caller); -__visible void trace_hardirqs_off_caller(unsigned long caller_addr) +static inline void tracer_hardirqs_off_caller (unsigned long caller_addr) {if (!preempt_trace () &&irq_trace ())start_critical_timing (CALLER_ADDR0 , caller_addr); } -EXPORT_SYMBOL (trace_hardirqs_off_caller);#endif /* CONFIG_PROVE_LOCKING */ #endif /* CONFIG_IRQSOFF_TRACER */ #ifdef CONFIG_PREEMPT_TRACER -void trace_preempt_on (unsigned long a0,unsigned long a1) +static inline void tracer_preempt_on (unsigned long a0,unsigned long a1) {if (preempt_trace () && !irq_trace ())stop_critical_timing (a0, a1); } -void trace_preempt_off (unsigned long a0,unsigned long a1) +static inline void tracer_preempt_off (unsigned long a0,unsigned long a1) {if (preempt_trace () && !irq_trace ())start_critical_timing (a0, a1);
@@ -781,3 +762,70 @@ __init static int init_irqsoff_tracer(void)return 0 ; }core_initcall (init_irqsoff_tracer); +#endif /* IRQSOFF_TRACER || PREEMPTOFF_TRACER */ + +#ifndef CONFIG_IRQSOFF_TRACER +static inline void tracer_hardirqs_on (void ) { } +static inline void tracer_hardirqs_off (void ) { } +static inline void tracer_hardirqs_on_caller (unsigned long caller_addr) { } +static inline void tracer_hardirqs_off_caller (unsigned long caller_addr) { } +#endif + +#ifndef CONFIG_PREEMPT_TRACER +static inline void tracer_preempt_on (unsigned long a0,unsigned long a1) { } +static inline void tracer_preempt_off (unsigned long a0,unsigned long a1) { } +#endif + +#if defined(CONFIG_TRACE_IRQFLAGS ) && !defined(CONFIG_PROVE_LOCKING ) +void trace_hardirqs_on (void ) +{ +tracer_hardirqs_on (); +} +EXPORT_SYMBOL (trace_hardirqs_on); + +void trace_hardirqs_off (void ) +{ +tracer_hardirqs_off (); +} +EXPORT_SYMBOL (trace_hardirqs_off); + +__visible void trace_hardirqs_on_caller(unsigned long caller_addr) +{ +tracer_hardirqs_on_caller (caller_addr); +} +EXPORT_SYMBOL (trace_hardirqs_on_caller); + +__visible void trace_hardirqs_off_caller(unsigned long caller_addr) +{ +tracer_hardirqs_off_caller (caller_addr); +} +EXPORT_SYMBOL (trace_hardirqs_off_caller); + +/* +* Stubs: +*/ + +void trace_softirqs_on (unsigned long ip) +{ +} + +void trace_softirqs_off (unsigned long ip) +{ +} + +inline void print_irqtrace_events (struct task_struct *curr) +{ +} +#endif + +#ifdef CONFIG_PREEMPT_TRACER +void trace_preempt_on (unsigned long a0,unsigned long a1) +{ +tracer_preempt_on (a0, a1); +} + +void trace_preempt_off (unsigned long a0,unsigned long a1) +{ +tracer_preempt_off (a0, a1); +} +#endif
Signature · 1 line
2.13.2
Steven Rostedt
maintainerPATCH 2/3 tracing: Add support for preempt and irq enable/disable events
In reply to Steven RostedtFrom: Joel Fernandes <[email protected]>
Preempt and irq trace events can be used for tracing the start and end of an atomic section which can be used by a trace viewer like systrace to graphically view the start and end of an atomic section and correlate them with latencies and scheduling issues.
This also serves as a prelude to using synthetic events or probes to rewrite the preempt and irqsoff tracers, along with numerous benefits of using trace events features for these events.
--- include/linux/ftrace.h | 3 +- include/trace/events/preemptirq.h | 70 +++++++++++++++++++++++++++++++++++++++ kernel/trace/Kconfig | 11 ++++++ kernel/trace/Makefile | 1 + kernel/trace/trace_irqsoff.c | 35 +++++++++++++++++++- 5 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 include/trace/events/preemptirq.h
include/linux/ftrace.h +2 -1
@@ -769,7 +769,8 @@ static inline unsigned long get_lock_parent_ip(void)static inline void time_hardirqs_off (unsigned long a0,unsigned long a1) { }#endif -#ifdef CONFIG_PREEMPT_TRACER +#if defined(CONFIG_PREEMPT_TRACER ) || \ + (defined(CONFIG_DEBUG_PREEMPT ) && defined(CONFIG_PREEMPTIRQ_EVENTS ))extern void trace_preempt_on (unsigned long a0,unsigned long a1);extern void trace_preempt_off (unsigned long a0,unsigned long a1);#else
include/trace/events/preemptirq.h new file +70 -0
70 lines not on this page · raw message
kernel/trace/Kconfig +11 -0
@@ -160,6 +160,17 @@ config FUNCTION_GRAPH_TRACER address on the current task structure into a stack of calls. +config PREEMPTIRQ_EVENTS + bool "Enable trace events for preempt and irq disable/enable" + select TRACE_IRQFLAGS + depends on DEBUG_PREEMPT || !PROVE_LOCKING + default n + help + Enable tracing of disable and enable events for preemption and irqs. + For tracing preempt disable/enable events, DEBUG_PREEMPT must be + enabled. For tracing irq disable/enable events, PROVE_LOCKING must + be disabled. + config IRQSOFF_TRACER bool "Interrupts-off Latency Tracer" default n
kernel/trace/Makefile +1 -0
@@ -34,6 +34,7 @@ obj-$(CONFIG_TRACING) += trace_printk.o obj-$(CONFIG_TRACING_MAP) += tracing_map.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o +obj-$(CONFIG_PREEMPTIRQ_EVENTS) += trace_irqsoff.o obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o
kernel/trace/trace_irqsoff.c +34 -1
@@ -16,6 +16,9 @@#include "trace.h" +#define CREATE_TRACE_POINTS +#include <trace/events/preemptirq.h> +#if defined(CONFIG_IRQSOFF_TRACER ) || defined(CONFIG_PREEMPT_TRACER )static struct trace_array *irqsoff_trace__read_mostly ;static int tracer_enabled__read_mostly ;
@@ -777,26 +780,53 @@ static inline void tracer_preempt_off(unsigned long a0, unsigned long a1) { }#endif #if defined(CONFIG_TRACE_IRQFLAGS ) && !defined(CONFIG_PROVE_LOCKING ) +/* Per-cpu variable to prevent redundant calls when IRQs already off */ +static DEFINE_PER_CPU(int, tracing_irq_cpu); +void trace_hardirqs_on (void ) { +if (!this_cpu_read (tracing_irq_cpu)) +return ; + +trace_irq_enable_rcuidle (CALLER_ADDR0 ,CALLER_ADDR1 );tracer_hardirqs_on (); + +this_cpu_write (tracing_irq_cpu,0 ); }EXPORT_SYMBOL (trace_hardirqs_on);void trace_hardirqs_off (void ) { +if (this_cpu_read (tracing_irq_cpu)) +return ; + +this_cpu_write (tracing_irq_cpu,1 ); + +trace_irq_disable_rcuidle (CALLER_ADDR0 ,CALLER_ADDR1 );tracer_hardirqs_off (); }EXPORT_SYMBOL (trace_hardirqs_off); __visible void trace_hardirqs_on_caller(unsigned long caller_addr) { +if (!this_cpu_read (tracing_irq_cpu)) +return ; + +trace_irq_enable_rcuidle (CALLER_ADDR0 , caller_addr);tracer_hardirqs_on_caller (caller_addr); + +this_cpu_write (tracing_irq_cpu,0 ); }EXPORT_SYMBOL (trace_hardirqs_on_caller); __visible void trace_hardirqs_off_caller(unsigned long caller_addr) { +if (this_cpu_read (tracing_irq_cpu)) +return ; + +this_cpu_write (tracing_irq_cpu,1 ); + +trace_irq_disable_rcuidle (CALLER_ADDR0 , caller_addr);tracer_hardirqs_off_caller (caller_addr); }EXPORT_SYMBOL (trace_hardirqs_off_caller);
@@ -818,14 +848,17 @@ inline void print_irqtrace_events(struct task_struct *curr) }#endif -#ifdef CONFIG_PREEMPT_TRACER +#if defined(CONFIG_PREEMPT_TRACER ) || \ + (defined(CONFIG_DEBUG_PREEMPT ) && defined(CONFIG_PREEMPTIRQ_EVENTS ))void trace_preempt_on (unsigned long a0,unsigned long a1) { +trace_preempt_enable_rcuidle (a0, a1);tracer_preempt_on (a0, a1); }void trace_preempt_off (unsigned long a0,unsigned long a1) { +trace_preempt_disable_rcuidle (a0, a1);tracer_preempt_off (a0, a1); }#endif
Signature · 1 line
2.13.2
Steven Rostedt
maintainerPATCH 3/3 ftrace: Clear hashes of stale ips of init memory
In reply to Steven RostedtFrom: Joel Fernandes <[email protected]>
Filters should be cleared of init functions during freeing of init memory when the ftrace dyn records are released. However in current code, the filters are left as is. This patch clears the hashes of the saved init functions when the init memory is freed. This fixes the following issue reproducible with the following sequence of commands for a test module:
================================================
void bar(void)
{
printk(KERN_INFO "bar!\n");
}void foo(void)
{
printk(KERN_INFO "foo!\n");
bar();
}static int __init hello_init(void)
{
printk(KERN_INFO "Hello world!\n");
foo();
return 0;
}static void __exit hello_cleanup(void)
{
printk(KERN_INFO "Cleaning up module.\n");
}module_init(hello_init);module_exit(hello_cleanup);
================================================
Commands:
echo '*:mod:test' > /d/tracing/set_ftrace_filter
echo function > /d/tracing/current_tracer
modprobe test
rmmod test
sleep 1
modprobe test
cat /d/tracing/set_ftrace_filter
Behavior without patch: Init function is still in the filter Expected behavior: Shouldn't have any of the filters set
--- kernel/trace/ftrace.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+)
kernel/trace/ftrace.c +69 -0
@@ -6067,6 +6067,63 @@ allocate_ftrace_mod_map(struct module *mod, }#endif /* CONFIG_MODULES */ +struct ftrace_init_func { +struct list_head list; +unsigned long ip; +}; + +/* Clear any init ips from hashes */ +static void +clear_func_from_hash (struct ftrace_init_func *func,struct ftrace_hash *hash) +{ +struct ftrace_func_entry *entry; + +if (ftrace_hash_empty (hash)) +return ; + + entry =__ftrace_lookup_ip (hash, func->ip); + +/* +* Do not allow this rec to match again. +* Yeah, it may waste some memory, but will be removed +* if/when the hash is modified again. +*/ +if (entry) + entry->ip =0 ; +} + +static void +clear_func_from_hashes (struct ftrace_init_func *func) +{ +struct trace_array *tr; + +mutex_lock (&trace_types_lock); +list_for_each_entry (tr, &ftrace_trace_arrays, list) { +if (!tr->ops || !tr->ops->func_hash) +continue ; +mutex_lock (&tr->ops->func_hash->regex_lock); +clear_func_from_hash (func, tr->ops->func_hash->filter_hash); +clear_func_from_hash (func, tr->ops->func_hash->notrace_hash); +mutex_unlock (&tr->ops->func_hash->regex_lock); + } +mutex_unlock (&trace_types_lock); +} + +static void add_to_clear_hash_list (struct list_head *clear_list, +struct dyn_ftrace *rec) +{ +struct ftrace_init_func *func; + + func =kmalloc (sizeof (*func),GFP_KERNEL ); +if (!func) { +WARN_ONCE (1 ,"alloc failure, ftrace filter could be stale\n" ); +return ; + } + + func->ip = rec->ip; +list_add (&func->list, clear_list); +} +void ftrace_free_mem (struct module *mod,void *start_ptr,void *end_ptr) {unsigned long start = (unsigned long )(start_ptr);
@@ -6076,8 +6133,12 @@ void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr)struct dyn_ftrace *rec;struct dyn_ftrace key;struct ftrace_mod_map *mod_map =NULL ; +struct ftrace_init_func *func, *func_next; +struct list_head clear_hash;int order; +INIT_LIST_HEAD (&clear_hash); + key.ip = start; key.flags = end;/* overload flags, as it is unsigned long */
@@ -6102,6 +6163,9 @@ void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr)if (!rec)continue ; +/* rec will be cleared from hashes after ftrace_lock unlock */ +add_to_clear_hash_list (&clear_hash, rec); +if (mod_map)save_ftrace_mod_rec (mod_map, rec);
@@ -6123,6 +6187,11 @@ void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr) goto again; }mutex_unlock (&ftrace_lock); + +list_for_each_entry_safe (func, func_next, &clear_hash, list) { +clear_func_from_hashes (func); +kfree (func); + } }void __init ftrace_free_init_mem (void )
Signature · 1 line
2.13.2