Commit 68114e5e authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'trace-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace

Pull tracing updates from Steven Rostedt:
 "Most of the changes were largely clean ups, and some documentation.
  But there were a few features that were added:

  Uprobes now work with event triggers and multi buffers and have
  support under ftrace and perf.

  The big feature is that the function tracer can now be used within the
  multi buffer instances.  That is, you can now trace some functions in
  one buffer, others in another buffer, all functions in a third buffer
  and so on.  They are basically agnostic from each other.  This only
  works for the function tracer and not for the function graph trace,
  although you can have the function graph tracer running in the top
  level buffer (or any tracer for that matter) and have different
  function tracing going on in the sub buffers"

* tag 'trace-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (45 commits)
  tracing: Add BUG_ON when stack end location is over written
  tracepoint: Remove unused API functions
  Revert "tracing: Move event storage for array from macro to standalone function"
  ftrace: Constify ftrace_text_reserved
  tracepoints: API doc update to tracepoint_probe_register() return value
  tracepoints: API doc update to data argument
  ftrace: Fix compilation warning about control_ops_free
  ftrace/x86: BUG when ftrace recovery fails
  ftrace: Warn on error when modifying ftrace function
  ftrace: Remove freelist from struct dyn_ftrace
  ftrace: Do not pass data to ftrace_dyn_arch_init
  ftrace: Pass retval through return in ftrace_dyn_arch_init()
  ftrace: Inline the code from ftrace_dyn_table_alloc()
  ftrace: Cleanup of global variables ftrace_new_pgs and ftrace_update_cnt
  tracing: Evaluate len expression only once in __dynamic_array macro
  tracing: Correctly expand len expressions from __dynamic_array macro
  tracing/module: Replace include of tracepoint.h with jump_label.h in module.h
  tracing: Fix event header migrate.h to include tracepoint.h
  tracing: Fix event header writeback.h to include tracepoint.h
  tracing: Warn if a tracepoint is not set via debugfs
  ...
parents 59ecc260 38628078
...@@ -358,11 +358,8 @@ Every arch has an init callback function. If you need to do something early on ...@@ -358,11 +358,8 @@ Every arch has an init callback function. If you need to do something early on
to initialize some state, this is the time to do that. Otherwise, this simple to initialize some state, this is the time to do that. Otherwise, this simple
function below should be sufficient for most people: function below should be sufficient for most people:
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
/* return value is done indirectly via data */
*(unsigned long *)data = 0;
return 0; return 0;
} }
......
...@@ -156,10 +156,8 @@ int ftrace_make_nop(struct module *mod, ...@@ -156,10 +156,8 @@ int ftrace_make_nop(struct module *mod,
return ret; return ret;
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
*(unsigned long *)data = 0;
return 0; return 0;
} }
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
......
...@@ -65,11 +65,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func) ...@@ -65,11 +65,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return ftrace_modify_code(ip, call, sizeof(call)); return ftrace_modify_code(ip, call, sizeof(call));
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
/* return value is done indirectly via data */
*(unsigned long *)data = 0;
return 0; return 0;
} }
......
...@@ -198,9 +198,7 @@ int ftrace_update_ftrace_func(ftrace_func_t func) ...@@ -198,9 +198,7 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
} }
/* run from kstop_machine */ /* run from kstop_machine */
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
*(unsigned long *)data = 0;
return 0; return 0;
} }
...@@ -117,10 +117,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) ...@@ -117,10 +117,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
} }
/* run from kstop_machine */ /* run from kstop_machine */
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
/* The return code is returned via data */
writel(0, data);
return 0; return 0;
} }
...@@ -171,11 +171,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) ...@@ -171,11 +171,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
return ret; return ret;
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
/* The return code is retured via data */
*(unsigned long *)data = 0;
return 0; return 0;
} }
......
...@@ -201,7 +201,7 @@ int ftrace_update_ftrace_func(ftrace_func_t func) ...@@ -201,7 +201,7 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return ftrace_modify_code(FTRACE_CALL_IP, new); return ftrace_modify_code(FTRACE_CALL_IP, new);
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
/* Encode the instructions when booting */ /* Encode the instructions when booting */
ftrace_dyn_arch_init_insns(); ftrace_dyn_arch_init_insns();
...@@ -209,9 +209,6 @@ int __init ftrace_dyn_arch_init(void *data) ...@@ -209,9 +209,6 @@ int __init ftrace_dyn_arch_init(void *data)
/* Remove "b ftrace_stub" to ensure ftrace_caller() is executed */ /* Remove "b ftrace_stub" to ensure ftrace_caller() is executed */
ftrace_modify_code(MCOUNT_ADDR, INSN_NOP); ftrace_modify_code(MCOUNT_ADDR, INSN_NOP);
/* The return code is retured via data */
*(unsigned long *)data = 0;
return 0; return 0;
} }
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
......
...@@ -532,13 +532,8 @@ void arch_ftrace_update_code(int command) ...@@ -532,13 +532,8 @@ void arch_ftrace_update_code(int command)
ftrace_disable_ftrace_graph_caller(); ftrace_disable_ftrace_graph_caller();
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
/* caller expects data to be zero */
unsigned long *p = data;
*p = 0;
return 0; return 0;
} }
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
......
...@@ -130,9 +130,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func) ...@@ -130,9 +130,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return 0; return 0;
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
*(unsigned long *) data = 0;
return 0; return 0;
} }
......
...@@ -272,11 +272,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) ...@@ -272,11 +272,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
return ftrace_modify_code(rec->ip, old, new); return ftrace_modify_code(rec->ip, old, new);
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
/* The return code is retured via data */
__raw_writel(0, (unsigned long)data);
return 0; return 0;
} }
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
......
...@@ -82,12 +82,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func) ...@@ -82,12 +82,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return ftrace_modify_code(ip, old, new); return ftrace_modify_code(ip, old, new);
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
unsigned long *p = data;
*p = 0;
return 0; return 0;
} }
#endif #endif
......
...@@ -167,10 +167,8 @@ int ftrace_make_nop(struct module *mod, ...@@ -167,10 +167,8 @@ int ftrace_make_nop(struct module *mod,
return ret; return ret;
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
*(unsigned long *)data = 0;
return 0; return 0;
} }
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
......
...@@ -308,7 +308,10 @@ static int ftrace_write(unsigned long ip, const char *val, int size) ...@@ -308,7 +308,10 @@ static int ftrace_write(unsigned long ip, const char *val, int size)
if (within(ip, (unsigned long)_text, (unsigned long)_etext)) if (within(ip, (unsigned long)_text, (unsigned long)_etext))
ip = (unsigned long)__va(__pa_symbol(ip)); ip = (unsigned long)__va(__pa_symbol(ip));
return probe_kernel_write((void *)ip, val, size); if (probe_kernel_write((void *)ip, val, size))
return -EPERM;
return 0;
} }
static int add_break(unsigned long ip, const char *old) static int add_break(unsigned long ip, const char *old)
...@@ -323,10 +326,7 @@ static int add_break(unsigned long ip, const char *old) ...@@ -323,10 +326,7 @@ static int add_break(unsigned long ip, const char *old)
if (memcmp(replaced, old, MCOUNT_INSN_SIZE) != 0) if (memcmp(replaced, old, MCOUNT_INSN_SIZE) != 0)
return -EINVAL; return -EINVAL;
if (ftrace_write(ip, &brk, 1)) return ftrace_write(ip, &brk, 1);
return -EPERM;
return 0;
} }
static int add_brk_on_call(struct dyn_ftrace *rec, unsigned long addr) static int add_brk_on_call(struct dyn_ftrace *rec, unsigned long addr)
...@@ -425,7 +425,7 @@ static int remove_breakpoint(struct dyn_ftrace *rec) ...@@ -425,7 +425,7 @@ static int remove_breakpoint(struct dyn_ftrace *rec)
/* If this does not have a breakpoint, we are done */ /* If this does not have a breakpoint, we are done */
if (ins[0] != brk) if (ins[0] != brk)
return -1; return 0;
nop = ftrace_nop_replace(); nop = ftrace_nop_replace();
...@@ -455,7 +455,7 @@ static int remove_breakpoint(struct dyn_ftrace *rec) ...@@ -455,7 +455,7 @@ static int remove_breakpoint(struct dyn_ftrace *rec)
} }
update: update:
return probe_kernel_write((void *)ip, &nop[0], 1); return ftrace_write(ip, nop, 1);
} }
static int add_update_code(unsigned long ip, unsigned const char *new) static int add_update_code(unsigned long ip, unsigned const char *new)
...@@ -463,9 +463,7 @@ static int add_update_code(unsigned long ip, unsigned const char *new) ...@@ -463,9 +463,7 @@ static int add_update_code(unsigned long ip, unsigned const char *new)
/* skip breakpoint */ /* skip breakpoint */
ip++; ip++;
new++; new++;
if (ftrace_write(ip, new, MCOUNT_INSN_SIZE - 1)) return ftrace_write(ip, new, MCOUNT_INSN_SIZE - 1);
return -EPERM;
return 0;
} }
static int add_update_call(struct dyn_ftrace *rec, unsigned long addr) static int add_update_call(struct dyn_ftrace *rec, unsigned long addr)
...@@ -520,10 +518,7 @@ static int finish_update_call(struct dyn_ftrace *rec, unsigned long addr) ...@@ -520,10 +518,7 @@ static int finish_update_call(struct dyn_ftrace *rec, unsigned long addr)
new = ftrace_call_replace(ip, addr); new = ftrace_call_replace(ip, addr);
if (ftrace_write(ip, new, 1)) return ftrace_write(ip, new, 1);
return -EPERM;
return 0;
} }
static int finish_update_nop(struct dyn_ftrace *rec) static int finish_update_nop(struct dyn_ftrace *rec)
...@@ -533,9 +528,7 @@ static int finish_update_nop(struct dyn_ftrace *rec) ...@@ -533,9 +528,7 @@ static int finish_update_nop(struct dyn_ftrace *rec)
new = ftrace_nop_replace(); new = ftrace_nop_replace();
if (ftrace_write(ip, new, 1)) return ftrace_write(ip, new, 1);
return -EPERM;
return 0;
} }
static int finish_update(struct dyn_ftrace *rec, int enable) static int finish_update(struct dyn_ftrace *rec, int enable)
...@@ -632,8 +625,14 @@ void ftrace_replace_code(int enable) ...@@ -632,8 +625,14 @@ void ftrace_replace_code(int enable)
printk(KERN_WARNING "Failed on %s (%d):\n", report, count); printk(KERN_WARNING "Failed on %s (%d):\n", report, count);
for_ftrace_rec_iter(iter) { for_ftrace_rec_iter(iter) {
rec = ftrace_rec_iter_record(iter); rec = ftrace_rec_iter_record(iter);
remove_breakpoint(rec); /*
* Breakpoints are handled only when this function is in
* progress. The system could not work with them.
*/
if (remove_breakpoint(rec))
BUG();
} }
run_sync();
} }
static int static int
...@@ -655,16 +654,19 @@ ftrace_modify_code(unsigned long ip, unsigned const char *old_code, ...@@ -655,16 +654,19 @@ ftrace_modify_code(unsigned long ip, unsigned const char *old_code,
run_sync(); run_sync();
ret = ftrace_write(ip, new_code, 1); ret = ftrace_write(ip, new_code, 1);
if (ret) { /*
ret = -EPERM; * The breakpoint is handled only when this function is in progress.
goto out; * The system could not work if we could not remove it.
} */
run_sync(); BUG_ON(ret);
out: out:
run_sync();
return ret; return ret;
fail_update: fail_update:
probe_kernel_write((void *)ip, &old_code[0], 1); /* Also here the system could not work with the breakpoint */
if (ftrace_write(ip, old_code, 1))
BUG();
goto out; goto out;
} }
...@@ -678,11 +680,8 @@ void arch_ftrace_update_code(int command) ...@@ -678,11 +680,8 @@ void arch_ftrace_update_code(int command)
atomic_dec(&modifying_ftrace_code); atomic_dec(&modifying_ftrace_code);
} }
int __init ftrace_dyn_arch_init(void *data) int __init ftrace_dyn_arch_init(void)
{ {
/* The return code is retured via data */
*(unsigned long *)data = 0;
return 0; return 0;
} }
#endif #endif
......
...@@ -92,6 +92,7 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip, ...@@ -92,6 +92,7 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
* STUB - The ftrace_ops is just a place holder. * STUB - The ftrace_ops is just a place holder.
* INITIALIZED - The ftrace_ops has already been initialized (first use time * INITIALIZED - The ftrace_ops has already been initialized (first use time
* register_ftrace_function() is called, it will initialized the ops) * register_ftrace_function() is called, it will initialized the ops)
* DELETED - The ops are being deleted, do not let them be registered again.
*/ */
enum { enum {
FTRACE_OPS_FL_ENABLED = 1 << 0, FTRACE_OPS_FL_ENABLED = 1 << 0,
...@@ -103,13 +104,26 @@ enum { ...@@ -103,13 +104,26 @@ enum {
FTRACE_OPS_FL_RECURSION_SAFE = 1 << 6, FTRACE_OPS_FL_RECURSION_SAFE = 1 << 6,
FTRACE_OPS_FL_STUB = 1 << 7, FTRACE_OPS_FL_STUB = 1 << 7,
FTRACE_OPS_FL_INITIALIZED = 1 << 8, FTRACE_OPS_FL_INITIALIZED = 1 << 8,
FTRACE_OPS_FL_DELETED = 1 << 9,
}; };
/*
* Note, ftrace_ops can be referenced outside of RCU protection.
* (Although, for perf, the control ops prevent that). If ftrace_ops is
* allocated and not part of kernel core data, the unregistering of it will
* perform a scheduling on all CPUs to make sure that there are no more users.
* Depending on the load of the system that may take a bit of time.
*
* Any private data added must also take care not to be freed and if private
* data is added to a ftrace_ops that is in core code, the user of the
* ftrace_ops must perform a schedule_on_each_cpu() before freeing it.
*/
struct ftrace_ops { struct ftrace_ops {
ftrace_func_t func; ftrace_func_t func;
struct ftrace_ops *next; struct ftrace_ops *next;
unsigned long flags; unsigned long flags;
int __percpu *disabled; int __percpu *disabled;
void *private;
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
struct ftrace_hash *notrace_hash; struct ftrace_hash *notrace_hash;
struct ftrace_hash *filter_hash; struct ftrace_hash *filter_hash;
...@@ -285,7 +299,7 @@ extern void ...@@ -285,7 +299,7 @@ extern void
unregister_ftrace_function_probe_func(char *glob, struct ftrace_probe_ops *ops); unregister_ftrace_function_probe_func(char *glob, struct ftrace_probe_ops *ops);
extern void unregister_ftrace_function_probe_all(char *glob); extern void unregister_ftrace_function_probe_all(char *glob);
extern int ftrace_text_reserved(void *start, void *end); extern int ftrace_text_reserved(const void *start, const void *end);
extern int ftrace_nr_registered_ops(void); extern int ftrace_nr_registered_ops(void);
...@@ -316,12 +330,9 @@ enum { ...@@ -316,12 +330,9 @@ enum {
#define FTRACE_REF_MAX ((1UL << 29) - 1) #define FTRACE_REF_MAX ((1UL << 29) - 1)
struct dyn_ftrace { struct dyn_ftrace {
union { unsigned long ip; /* address of mcount call-site */
unsigned long ip; /* address of mcount call-site */
struct dyn_ftrace *freelist;
};
unsigned long flags; unsigned long flags;
struct dyn_arch_ftrace arch; struct dyn_arch_ftrace arch;
}; };
int ftrace_force_update(void); int ftrace_force_update(void);
...@@ -409,7 +420,7 @@ ftrace_set_early_filter(struct ftrace_ops *ops, char *buf, int enable); ...@@ -409,7 +420,7 @@ ftrace_set_early_filter(struct ftrace_ops *ops, char *buf, int enable);
/* defined in arch */ /* defined in arch */
extern int ftrace_ip_converted(unsigned long ip); extern int ftrace_ip_converted(unsigned long ip);
extern int ftrace_dyn_arch_init(void *data); extern int ftrace_dyn_arch_init(void);
extern void ftrace_replace_code(int enable); extern void ftrace_replace_code(int enable);
extern int ftrace_update_ftrace_func(ftrace_func_t func); extern int ftrace_update_ftrace_func(ftrace_func_t func);
extern void ftrace_caller(void); extern void ftrace_caller(void);
...@@ -541,7 +552,7 @@ static inline __init int unregister_ftrace_command(char *cmd_name) ...@@ -541,7 +552,7 @@ static inline __init int unregister_ftrace_command(char *cmd_name)
{ {
return -EINVAL; return -EINVAL;
} }
static inline int ftrace_text_reserved(void *start, void *end) static inline int ftrace_text_reserved(const void *start, const void *end)
{ {
return 0; return 0;
} }
......
...@@ -163,6 +163,8 @@ void trace_current_buffer_discard_commit(struct ring_buffer *buffer, ...@@ -163,6 +163,8 @@ void trace_current_buffer_discard_commit(struct ring_buffer *buffer,
void tracing_record_cmdline(struct task_struct *tsk); void tracing_record_cmdline(struct task_struct *tsk);
int ftrace_output_call(struct trace_iterator *iter, char *name, char *fmt, ...);
struct event_filter; struct event_filter;
enum trace_reg { enum trace_reg {
...@@ -197,6 +199,32 @@ struct ftrace_event_class { ...@@ -197,6 +199,32 @@ struct ftrace_event_class {
extern int ftrace_event_reg(struct ftrace_event_call *event, extern int ftrace_event_reg(struct ftrace_event_call *event,
enum trace_reg type, void *data); enum trace_reg type, void *data);
int ftrace_output_event(struct trace_iterator *iter, struct ftrace_event_call *event,
char *fmt, ...);
int ftrace_event_define_field(struct ftrace_event_call *call,
char *type, int len, char *item, int offset,
int field_size, int sign, int filter);
struct ftrace_event_buffer {
struct ring_buffer *buffer;
struct ring_buffer_event *event;
struct ftrace_event_file *ftrace_file;
void *entry;
unsigned long flags;
int pc;
};
void *ftrace_event_buffer_reserve(struct ftrace_event_buffer *fbuffer,
struct ftrace_event_file *ftrace_file,
unsigned long len);
void ftrace_event_buffer_commit(struct ftrace_event_buffer *fbuffer);
int ftrace_event_define_field(struct ftrace_event_call *call,
char *type, int len, char *item, int offset,
int field_size, int sign, int filter);
enum { enum {
TRACE_EVENT_FL_FILTERED_BIT, TRACE_EVENT_FL_FILTERED_BIT,
TRACE_EVENT_FL_CAP_ANY_BIT, TRACE_EVENT_FL_CAP_ANY_BIT,
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include <linux/stringify.h> #include <linux/stringify.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/tracepoint.h> #include <linux/jump_label.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/percpu.h> #include <linux/percpu.h>
......
...@@ -48,12 +48,6 @@ extern int tracepoint_probe_register(const char *name, void *probe, void *data); ...@@ -48,12 +48,6 @@ extern int tracepoint_probe_register(const char *name, void *probe, void *data);
extern int extern int
tracepoint_probe_unregister(const char *name, void *probe, void *data); tracepoint_probe_unregister(const char *name, void *probe, void *data);
extern int tracepoint_probe_register_noupdate(const char *name, void *probe,
void *data);
extern int tracepoint_probe_unregister_noupdate(const char *name, void *probe,
void *data);
extern void tracepoint_probe_update_all(void);
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
struct tp_module { struct tp_module {
struct list_head list; struct list_head list;
...@@ -68,18 +62,6 @@ static inline bool trace_module_has_bad_taint(struct module *mod) ...@@ -68,18 +62,6 @@ static inline bool trace_module_has_bad_taint(struct module *mod)
} }
#endif /* CONFIG_MODULES */ #endif /* CONFIG_MODULES */
struct tracepoint_iter {
#ifdef CONFIG_MODULES
struct tp_module *module;
#endif /* CONFIG_MODULES */
struct tracepoint * const *tracepoint;
};
extern void tracepoint_iter_start(struct tracepoint_iter *iter);
extern void tracepoint_iter_next(struct tracepoint_iter *iter);
extern void tracepoint_iter_stop(struct tracepoint_iter *iter);
extern void tracepoint_iter_reset(struct tracepoint_iter *iter);
/* /*
* tracepoint_synchronize_unregister must be called between the last tracepoint * tracepoint_synchronize_unregister must be called between the last tracepoint
* probe unregistration and the end of module exit to make sure there is no * probe unregistration and the end of module exit to make sure there is no
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#if !defined(_TRACE_MIGRATE_H) || defined(TRACE_HEADER_MULTI_READ) #if !defined(_TRACE_MIGRATE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_MIGRATE_H #define _TRACE_MIGRATE_H
#include <linux/tracepoint.h>
#define MIGRATE_MODE \ #define MIGRATE_MODE \
{MIGRATE_ASYNC, "MIGRATE_ASYNC"}, \ {MIGRATE_ASYNC, "MIGRATE_ASYNC"}, \
{MIGRATE_SYNC_LIGHT, "MIGRATE_SYNC_LIGHT"}, \ {MIGRATE_SYNC_LIGHT, "MIGRATE_SYNC_LIGHT"}, \
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#if !defined(_TRACE_WRITEBACK_H) || defined(TRACE_HEADER_MULTI_READ) #if !defined(_TRACE_WRITEBACK_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_WRITEBACK_H #define _TRACE_WRITEBACK_H
#include <linux/tracepoint.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/writeback.h> #include <linux/writeback.h>
......
...@@ -265,11 +265,9 @@ static notrace enum print_line_t \ ...@@ -265,11 +265,9 @@ static notrace enum print_line_t \
ftrace_raw_output_##call(struct trace_iterator *iter, int flags, \ ftrace_raw_output_##call(struct trace_iterator *iter, int flags, \
struct trace_event *event) \ struct trace_event *event) \
{ \ { \
struct trace_seq *s = &iter->seq; \
struct ftrace_raw_##template *field; \ struct ftrace_raw_##template *field; \
struct trace_entry *entry; \ struct trace_entry *entry; \
struct trace_seq *p = &iter->tmp_seq; \ struct trace_seq *p = &iter->tmp_seq; \
int ret; \
\ \
entry = iter->ent; \ entry = iter->ent; \
\ \
...@@ -281,13 +279,7 @@ ftrace_raw_output_##call(struct trace_iterator *iter, int flags, \ ...@@ -281,13 +279,7 @@ ftrace_raw_output_##call(struct trace_iterator *iter, int flags, \
field = (typeof(field))entry; \ field = (typeof(field))entry; \
\ \
trace_seq_init(p); \ trace_seq_init(p); \
ret = trace_seq_printf(s, "%s: ", #call); \ return ftrace_output_call(iter, #call, print); \
if (ret) \
ret = trace_seq_printf(s, print); \
if (!ret) \
return TRACE_TYPE_PARTIAL_LINE; \
\
return TRACE_TYPE_HANDLED; \
} \ } \
static struct trace_event_functions ftrace_event_type_funcs_##call = { \ static struct trace_event_functions ftrace_event_type_funcs_##call = { \
.trace = ftrace_raw_output_##call, \ .trace = ftrace_raw_output_##call, \
...@@ -370,10 +362,11 @@ ftrace_define_fields_##call(struct ftrace_event_call *event_call) \ ...@@ -370,10 +362,11 @@ ftrace_define_fields_##call(struct ftrace_event_call *event_call) \
#undef __dynamic_array #undef __dynamic_array
#define __dynamic_array(type, item, len) \ #define __dynamic_array(type, item, len) \
__item_length = (len) * sizeof(type); \
__data_offsets->item = __data_size + \ __data_offsets->item = __data_size + \
offsetof(typeof(*entry), __data); \ offsetof(typeof(*entry), __data); \
__data_offsets->item |= (len * sizeof(type)) << 16; \ __data_offsets->item |= __item_length << 16; \
__data_size += (len) * sizeof(type); __data_size += __item_length;
#undef __string #undef __string
#define __string(item, src) __dynamic_array(char, item, \ #define __string(item, src) __dynamic_array(char, item, \
...@@ -385,6 +378,7 @@ static inline notrace int ftrace_get_offsets_##call( \ ...@@ -385,6 +378,7 @@ static inline notrace int ftrace_get_offsets_##call( \
struct ftrace_data_offsets_##call *__data_offsets, proto) \ struct ftrace_data_offsets_##call *__data_offsets, proto) \
{ \ { \
int __data_size = 0; \ int __data_size = 0; \
int __maybe_unused __item_length; \
struct ftrace_raw_##call __maybe_unused *entry; \ struct ftrace_raw_##call __maybe_unused *entry; \
\ \
tstruct; \ tstruct; \
...@@ -541,37 +535,27 @@ static notrace void \ ...@@ -541,37 +535,27 @@ static notrace void \
ftrace_raw_event_##call(void *__data, proto) \ ftrace_raw_event_##call(void *__data, proto) \
{ \ { \
struct ftrace_event_file *ftrace_file = __data; \ struct ftrace_event_file *ftrace_file = __data; \
struct ftrace_event_call *event_call = ftrace_file->event_call; \
struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\ struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\
struct ring_buffer_event *event; \ struct ftrace_event_buffer fbuffer; \
struct ftrace_raw_##call *entry; \ struct ftrace_raw_##call *entry; \
struct ring_buffer *buffer; \
unsigned long irq_flags; \
int __data_size; \ int __data_size; \
int pc; \
\ \
if (ftrace_trigger_soft_disabled(ftrace_file)) \ if (ftrace_trigger_soft_disabled(ftrace_file)) \
return; \ return; \
\ \
local_save_flags(irq_flags); \
pc = preempt_count(); \
\
__data_size = ftrace_get_offsets_##call(&__data_offsets, args); \ __data_size = ftrace_get_offsets_##call(&__data_offsets, args); \
\ \
event = trace_event_buffer_lock_reserve(&buffer, ftrace_file, \ entry = ftrace_event_buffer_reserve(&fbuffer, ftrace_file, \
event_call->event.type, \ sizeof(*entry) + __data_size); \
sizeof(*entry) + __data_size, \ \
irq_flags, pc); \ if (!entry) \
if (!event) \
return; \ return; \
entry = ring_buffer_event_data(event); \
\ \
tstruct \ tstruct \
\ \
{ assign; } \ { assign; } \
\ \
event_trigger_unlock_commit(ftrace_file, buffer, event, entry, \ ftrace_event_buffer_commit(&fbuffer); \
irq_flags, pc); \
} }
/* /*
* The ftrace_test_probe is compiled out, it is only here as a build time check * The ftrace_test_probe is compiled out, it is only here as a build time check
......
...@@ -1429,7 +1429,8 @@ static enum print_line_t blk_tracer_print_line(struct trace_iterator *iter) ...@@ -1429,7 +1429,8 @@ static enum print_line_t blk_tracer_print_line(struct trace_iterator *iter)
return print_one_line(iter, true); return print_one_line(iter, true);
} }
static int blk_tracer_set_flag(u32 old_flags, u32 bit, int set) static int
blk_tracer_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{ {
/* don't output context-info for blk_classic output */ /* don't output context-info for blk_classic output */
if (bit == TRACE_BLK_OPT_CLASSIC) { if (bit == TRACE_BLK_OPT_CLASSIC) {
......
...@@ -237,14 +237,13 @@ static int control_ops_alloc(struct ftrace_ops *ops) ...@@ -237,14 +237,13 @@ static int control_ops_alloc(struct ftrace_ops *ops)
return 0; return 0;
} }
static void control_ops_free(struct ftrace_ops *ops)
{
free_percpu(ops->disabled);
}
static void update_global_ops(void) static void update_global_ops(void)
{ {
ftrace_func_t func; ftrace_func_t func = ftrace_global_list_func;
void *private = NULL;
/* The list has its own recursion protection. */
global_ops.flags |= FTRACE_OPS_FL_RECURSION_SAFE;
/* /*
* If there's only one function registered, then call that * If there's only one function registered, then call that
...@@ -254,23 +253,17 @@ static void update_global_ops(void) ...@@ -254,23 +253,17 @@ static void update_global_ops(void)
if (ftrace_global_list == &ftrace_list_end || if (ftrace_global_list == &ftrace_list_end ||
ftrace_global_list->next == &ftrace_list_end) { ftrace_global_list->next == &ftrace_list_end) {
func = ftrace_global_list->func; func = ftrace_global_list->func;
private = ftrace_global_list->private;
/* /*
* As we are calling the function directly. * As we are calling the function directly.
* If it does not have recursion protection, * If it does not have recursion protection,
* the function_trace_op needs to be updated * the function_trace_op needs to be updated
* accordingly. * accordingly.
*/ */
if (ftrace_global_list->flags & FTRACE_OPS_FL_RECURSION_SAFE) if (!(ftrace_global_list->flags & FTRACE_OPS_FL_RECURSION_SAFE))
global_ops.flags |= FTRACE_OPS_FL_RECURSION_SAFE;
else
global_ops.flags &= ~FTRACE_OPS_FL_RECURSION_SAFE; global_ops.flags &= ~FTRACE_OPS_FL_RECURSION_SAFE;
} else {
func = ftrace_global_list_func;
/* The list has its own recursion protection. */
global_ops.flags |= FTRACE_OPS_FL_RECURSION_SAFE;
} }
/* If we filter on pids, update to use the pid function */ /* If we filter on pids, update to use the pid function */
if (!list_empty(&ftrace_pids)) { if (!list_empty(&ftrace_pids)) {
set_ftrace_pid_function(func); set_ftrace_pid_function(func);
...@@ -278,6 +271,7 @@ static void update_global_ops(void) ...@@ -278,6 +271,7 @@ static void update_global_ops(void)
} }
global_ops.func = func; global_ops.func = func;
global_ops.private = private;
} }
static void ftrace_sync(struct work_struct *work) static void ftrace_sync(struct work_struct *work)
...@@ -437,6 +431,9 @@ static int remove_ftrace_list_ops(struct ftrace_ops **list, ...@@ -437,6 +431,9 @@ static int remove_ftrace_list_ops(struct ftrace_ops **list,
static int __register_ftrace_function(struct ftrace_ops *ops) static int __register_ftrace_function(struct ftrace_ops *ops)
{ {
if (ops->flags & FTRACE_OPS_FL_DELETED)
return -EINVAL;
if (FTRACE_WARN_ON(ops == &global_ops)) if (FTRACE_WARN_ON(ops == &global_ops))
return -EINVAL; return -EINVAL;
...@@ -1172,8 +1169,6 @@ struct ftrace_page { ...@@ -1172,8 +1169,6 @@ struct ftrace_page {
int size; int size;
}; };
static struct ftrace_page *ftrace_new_pgs;
#define ENTRY_SIZE sizeof(struct dyn_ftrace) #define ENTRY_SIZE sizeof(struct dyn_ftrace)
#define ENTRIES_PER_PAGE (PAGE_SIZE / ENTRY_SIZE) #define ENTRIES_PER_PAGE (PAGE_SIZE / ENTRY_SIZE)
...@@ -1560,7 +1555,7 @@ unsigned long ftrace_location(unsigned long ip) ...@@ -1560,7 +1555,7 @@ unsigned long ftrace_location(unsigned long ip)
* the function tracer. It checks the ftrace internal tables to * the function tracer. It checks the ftrace internal tables to
* determine if the address belongs or not. * determine if the address belongs or not.
*/ */
int ftrace_text_reserved(void *start, void *end) int ftrace_text_reserved(const void *start, const void *end)
{ {
unsigned long ret; unsigned long ret;
...@@ -1994,6 +1989,7 @@ int __weak ftrace_arch_code_modify_post_process(void) ...@@ -1994,6 +1989,7 @@ int __weak ftrace_arch_code_modify_post_process(void)
void ftrace_modify_all_code(int command) void ftrace_modify_all_code(int command)
{ {
int update = command & FTRACE_UPDATE_TRACE_FUNC; int update = command & FTRACE_UPDATE_TRACE_FUNC;
int err = 0;
/* /*
* If the ftrace_caller calls a ftrace_ops func directly, * If the ftrace_caller calls a ftrace_ops func directly,
...@@ -2005,8 +2001,11 @@ void ftrace_modify_all_code(int command) ...@@ -2005,8 +2001,11 @@ void ftrace_modify_all_code(int command)
* to make sure the ops are having the right functions * to make sure the ops are having the right functions
* traced. * traced.
*/ */
if (update) if (update) {
ftrace_update_ftrace_func(ftrace_ops_list_func); err = ftrace_update_ftrace_func(ftrace_ops_list_func);
if (FTRACE_WARN_ON(err))
return;
}
if (command & FTRACE_UPDATE_CALLS) if (command & FTRACE_UPDATE_CALLS)
ftrace_replace_code(1); ftrace_replace_code(1);
...@@ -2019,13 +2018,16 @@ void ftrace_modify_all_code(int command) ...@@ -2019,13 +2018,16 @@ void ftrace_modify_all_code(int command)
/* If irqs are disabled, we are in stop machine */ /* If irqs are disabled, we are in stop machine */
if (!irqs_disabled()) if (!irqs_disabled())
smp_call_function(ftrace_sync_ipi, NULL, 1); smp_call_function(ftrace_sync_ipi, NULL, 1);
ftrace_update_ftrace_func(ftrace_trace_function); err = ftrace_update_ftrace_func(ftrace_trace_function);
if (FTRACE_WARN_ON(err))
return;
} }
if (command & FTRACE_START_FUNC_RET) if (command & FTRACE_START_FUNC_RET)
ftrace_enable_ftrace_graph_caller(); err = ftrace_enable_ftrace_graph_caller();
else if (command & FTRACE_STOP_FUNC_RET) else if (command & FTRACE_STOP_FUNC_RET)
ftrace_disable_ftrace_graph_caller(); err = ftrace_disable_ftrace_graph_caller();
FTRACE_WARN_ON(err);
} }
static int __ftrace_modify_code(void *data) static int __ftrace_modify_code(void *data)
...@@ -2093,6 +2095,11 @@ static ftrace_func_t saved_ftrace_func; ...@@ -2093,6 +2095,11 @@ static ftrace_func_t saved_ftrace_func;
static int ftrace_start_up; static int ftrace_start_up;
static int global_start_up; static int global_start_up;
static void control_ops_free(struct ftrace_ops *ops)
{
free_percpu(ops->disabled);
}
static void ftrace_startup_enable(int command) static void ftrace_startup_enable(int command)
{ {
if (saved_ftrace_func != ftrace_trace_function) { if (saved_ftrace_func != ftrace_trace_function) {
...@@ -2244,7 +2251,6 @@ static void ftrace_shutdown_sysctl(void) ...@@ -2244,7 +2251,6 @@ static void ftrace_shutdown_sysctl(void)
} }
static cycle_t ftrace_update_time; static cycle_t ftrace_update_time;
static unsigned long ftrace_update_cnt;
unsigned long ftrace_update_tot_cnt; unsigned long ftrace_update_tot_cnt;
static inline int ops_traces_mod(struct ftrace_ops *ops) static inline int ops_traces_mod(struct ftrace_ops *ops)
...@@ -2300,11 +2306,12 @@ static int referenced_filters(struct dyn_ftrace *rec) ...@@ -2300,11 +2306,12 @@ static int referenced_filters(struct dyn_ftrace *rec)
return cnt; return cnt;
} }
static int ftrace_update_code(struct module *mod) static int ftrace_update_code(struct module *mod, struct ftrace_page *new_pgs)
{ {
struct ftrace_page *pg; struct ftrace_page *pg;
struct dyn_ftrace *p; struct dyn_ftrace *p;
cycle_t start, stop; cycle_t start, stop;
unsigned long update_cnt = 0;
unsigned long ref = 0; unsigned long ref = 0;
bool test = false; bool test = false;
int i; int i;
...@@ -2330,9 +2337,8 @@ static int ftrace_update_code(struct module *mod) ...@@ -2330,9 +2337,8 @@ static int ftrace_update_code(struct module *mod)
} }
start = ftrace_now(raw_smp_processor_id()); start = ftrace_now(raw_smp_processor_id());
ftrace_update_cnt = 0;
for (pg = ftrace_new_pgs; pg; pg = pg->next) { for (pg = new_pgs; pg; pg = pg->next) {
for (i = 0; i < pg->index; i++) { for (i = 0; i < pg->index; i++) {
int cnt = ref; int cnt = ref;
...@@ -2353,7 +2359,7 @@ static int ftrace_update_code(struct module *mod) ...@@ -2353,7 +2359,7 @@ static int ftrace_update_code(struct module *mod)
if (!ftrace_code_disable(mod, p)) if (!ftrace_code_disable(mod, p))
break; break;
ftrace_update_cnt++; update_cnt++;
/* /*
* If the tracing is enabled, go ahead and enable the record. * If the tracing is enabled, go ahead and enable the record.
...@@ -2372,11 +2378,9 @@ static int ftrace_update_code(struct module *mod) ...@@ -2372,11 +2378,9 @@ static int ftrace_update_code(struct module *mod)
} }
} }
ftrace_new_pgs = NULL;
stop = ftrace_now(raw_smp_processor_id()); stop = ftrace_now(raw_smp_processor_id());
ftrace_update_time = stop - start; ftrace_update_time = stop - start;
ftrace_update_tot_cnt += ftrace_update_cnt; ftrace_update_tot_cnt += update_cnt;
return 0; return 0;
} }
...@@ -2468,22 +2472,6 @@ ftrace_allocate_pages(unsigned long num_to_init) ...@@ -2468,22 +2472,6 @@ ftrace_allocate_pages(unsigned long num_to_init)
return NULL; return NULL;
} }
static int __init ftrace_dyn_table_alloc(unsigned long num_to_init)
{
int cnt;
if (!num_to_init) {
pr_info("ftrace: No functions to be traced?\n");
return -1;
}
cnt = num_to_init / ENTRIES_PER_PAGE;
pr_info("ftrace: allocating %ld entries in %d pages\n",
num_to_init, cnt + 1);
return 0;
}
#define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */
struct ftrace_iterator { struct ftrace_iterator {
...@@ -2871,7 +2859,9 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, ...@@ -2871,7 +2859,9 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag,
static int static int
ftrace_filter_open(struct inode *inode, struct file *file) ftrace_filter_open(struct inode *inode, struct file *file)
{ {
return ftrace_regex_open(&global_ops, struct ftrace_ops *ops = inode->i_private;
return ftrace_regex_open(ops,
FTRACE_ITER_FILTER | FTRACE_ITER_DO_HASH, FTRACE_ITER_FILTER | FTRACE_ITER_DO_HASH,
inode, file); inode, file);
} }
...@@ -2879,7 +2869,9 @@ ftrace_filter_open(struct inode *inode, struct file *file) ...@@ -2879,7 +2869,9 @@ ftrace_filter_open(struct inode *inode, struct file *file)
static int static int
ftrace_notrace_open(struct inode *inode, struct file *file) ftrace_notrace_open(struct inode *inode, struct file *file)
{ {
return ftrace_regex_open(&global_ops, FTRACE_ITER_NOTRACE, struct ftrace_ops *ops = inode->i_private;
return ftrace_regex_open(ops, FTRACE_ITER_NOTRACE,
inode, file); inode, file);
} }
...@@ -4109,6 +4101,36 @@ static const struct file_operations ftrace_graph_notrace_fops = { ...@@ -4109,6 +4101,36 @@ static const struct file_operations ftrace_graph_notrace_fops = {
}; };
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
void ftrace_create_filter_files(struct ftrace_ops *ops,
struct dentry *parent)
{
trace_create_file("set_ftrace_filter", 0644, parent,
ops, &ftrace_filter_fops);
trace_create_file("set_ftrace_notrace", 0644, parent,
ops, &ftrace_notrace_fops);
}
/*
* The name "destroy_filter_files" is really a misnomer. Although
* in the future, it may actualy delete the files, but this is
* really intended to make sure the ops passed in are disabled
* and that when this function returns, the caller is free to
* free the ops.
*
* The "destroy" name is only to match the "create" name that this
* should be paired with.
*/
void ftrace_destroy_filter_files(struct ftrace_ops *ops)
{
mutex_lock(&ftrace_lock);
if (ops->flags & FTRACE_OPS_FL_ENABLED)
ftrace_shutdown(ops, 0);
ops->flags |= FTRACE_OPS_FL_DELETED;
mutex_unlock(&ftrace_lock);
}
static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer) static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
{ {
...@@ -4118,11 +4140,7 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer) ...@@ -4118,11 +4140,7 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
trace_create_file("enabled_functions", 0444, trace_create_file("enabled_functions", 0444,
d_tracer, NULL, &ftrace_enabled_fops); d_tracer, NULL, &ftrace_enabled_fops);
trace_create_file("set_ftrace_filter", 0644, d_tracer, ftrace_create_filter_files(&global_ops, d_tracer);
NULL, &ftrace_filter_fops);
trace_create_file("set_ftrace_notrace", 0644, d_tracer,
NULL, &ftrace_notrace_fops);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
trace_create_file("set_graph_function", 0444, d_tracer, trace_create_file("set_graph_function", 0444, d_tracer,
...@@ -4238,9 +4256,6 @@ static int ftrace_process_locs(struct module *mod, ...@@ -4238,9 +4256,6 @@ static int ftrace_process_locs(struct module *mod,
/* Assign the last page to ftrace_pages */ /* Assign the last page to ftrace_pages */
ftrace_pages = pg; ftrace_pages = pg;
/* These new locations need to be initialized */
ftrace_new_pgs = start_pg;
/* /*
* We only need to disable interrupts on start up * We only need to disable interrupts on start up
* because we are modifying code that an interrupt * because we are modifying code that an interrupt
...@@ -4251,7 +4266,7 @@ static int ftrace_process_locs(struct module *mod, ...@@ -4251,7 +4266,7 @@ static int ftrace_process_locs(struct module *mod,
*/ */
if (!mod) if (!mod)
local_irq_save(flags); local_irq_save(flags);
ftrace_update_code(mod); ftrace_update_code(mod, start_pg);
if (!mod) if (!mod)
local_irq_restore(flags); local_irq_restore(flags);
ret = 0; ret = 0;
...@@ -4360,30 +4375,27 @@ struct notifier_block ftrace_module_exit_nb = { ...@@ -4360,30 +4375,27 @@ struct notifier_block ftrace_module_exit_nb = {
.priority = INT_MIN, /* Run after anything that can remove kprobes */ .priority = INT_MIN, /* Run after anything that can remove kprobes */
}; };
extern unsigned long __start_mcount_loc[];
extern unsigned long __stop_mcount_loc[];
void __init ftrace_init(void) void __init ftrace_init(void)
{ {
unsigned long count, addr, flags; extern unsigned long __start_mcount_loc[];
extern unsigned long __stop_mcount_loc[];
unsigned long count, flags;
int ret; int ret;
/* Keep the ftrace pointer to the stub */
addr = (unsigned long)ftrace_stub;
local_irq_save(flags); local_irq_save(flags);
ftrace_dyn_arch_init(&addr); ret = ftrace_dyn_arch_init();
local_irq_restore(flags); local_irq_restore(flags);
if (ret)
/* ftrace_dyn_arch_init places the return code in addr */
if (addr)
goto failed; goto failed;
count = __stop_mcount_loc - __start_mcount_loc; count = __stop_mcount_loc - __start_mcount_loc;
if (!count) {
ret = ftrace_dyn_table_alloc(count); pr_info("ftrace: No functions to be traced?\n");
if (ret)
goto failed; goto failed;
}
pr_info("ftrace: allocating %ld entries in %ld pages\n",
count, count / ENTRIES_PER_PAGE + 1);
last_ftrace_enabled = ftrace_enabled = 1; last_ftrace_enabled = ftrace_enabled = 1;
...@@ -4431,7 +4443,13 @@ static inline void ftrace_startup_enable(int command) { } ...@@ -4431,7 +4443,13 @@ static inline void ftrace_startup_enable(int command) { }
(ops)->flags |= FTRACE_OPS_FL_ENABLED; \ (ops)->flags |= FTRACE_OPS_FL_ENABLED; \
___ret; \ ___ret; \
}) })
# define ftrace_shutdown(ops, command) __unregister_ftrace_function(ops) # define ftrace_shutdown(ops, command) \
({ \
int ___ret = __unregister_ftrace_function(ops); \
if (!___ret) \
(ops)->flags &= ~FTRACE_OPS_FL_ENABLED; \
___ret; \
})
# define ftrace_startup_sysctl() do { } while (0) # define ftrace_startup_sysctl() do { } while (0)
# define ftrace_shutdown_sysctl() do { } while (0) # define ftrace_shutdown_sysctl() do { } while (0)
......
...@@ -73,7 +73,8 @@ static struct tracer_flags dummy_tracer_flags = { ...@@ -73,7 +73,8 @@ static struct tracer_flags dummy_tracer_flags = {
.opts = dummy_tracer_opt .opts = dummy_tracer_opt
}; };
static int dummy_set_flag(u32 old_flags, u32 bit, int set) static int
dummy_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{ {
return 0; return 0;
} }
...@@ -118,7 +119,7 @@ enum ftrace_dump_mode ftrace_dump_on_oops; ...@@ -118,7 +119,7 @@ enum ftrace_dump_mode ftrace_dump_on_oops;
/* When set, tracing will stop when a WARN*() is hit */ /* When set, tracing will stop when a WARN*() is hit */
int __disable_trace_on_warning; int __disable_trace_on_warning;
static int tracing_set_tracer(const char *buf); static int tracing_set_tracer(struct trace_array *tr, const char *buf);
#define MAX_TRACER_SIZE 100 #define MAX_TRACER_SIZE 100
static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata; static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata;
...@@ -180,6 +181,17 @@ static int __init set_trace_boot_options(char *str) ...@@ -180,6 +181,17 @@ static int __init set_trace_boot_options(char *str)
} }
__setup("trace_options=", set_trace_boot_options); __setup("trace_options=", set_trace_boot_options);
static char trace_boot_clock_buf[MAX_TRACER_SIZE] __initdata;
static char *trace_boot_clock __initdata;
static int __init set_trace_boot_clock(char *str)
{
strlcpy(trace_boot_clock_buf, str, MAX_TRACER_SIZE);
trace_boot_clock = trace_boot_clock_buf;
return 0;
}
__setup("trace_clock=", set_trace_boot_clock);
unsigned long long ns2usecs(cycle_t nsec) unsigned long long ns2usecs(cycle_t nsec)
{ {
...@@ -1230,7 +1242,7 @@ int register_tracer(struct tracer *type) ...@@ -1230,7 +1242,7 @@ int register_tracer(struct tracer *type)
printk(KERN_INFO "Starting tracer '%s'\n", type->name); printk(KERN_INFO "Starting tracer '%s'\n", type->name);
/* Do we want this tracer to start on bootup? */ /* Do we want this tracer to start on bootup? */
tracing_set_tracer(type->name); tracing_set_tracer(&global_trace, type->name);
default_bootup_tracer = NULL; default_bootup_tracer = NULL;
/* disable other selftests, since this will break it. */ /* disable other selftests, since this will break it. */
tracing_selftest_disabled = true; tracing_selftest_disabled = true;
...@@ -3137,27 +3149,52 @@ static int tracing_open(struct inode *inode, struct file *file) ...@@ -3137,27 +3149,52 @@ static int tracing_open(struct inode *inode, struct file *file)
return ret; return ret;
} }
/*
* Some tracers are not suitable for instance buffers.
* A tracer is always available for the global array (toplevel)
* or if it explicitly states that it is.
*/
static bool
trace_ok_for_array(struct tracer *t, struct trace_array *tr)
{
return (tr->flags & TRACE_ARRAY_FL_GLOBAL) || t->allow_instances;
}
/* Find the next tracer that this trace array may use */
static struct tracer *
get_tracer_for_array(struct trace_array *tr, struct tracer *t)
{
while (t && !trace_ok_for_array(t, tr))
t = t->next;
return t;
}
static void * static void *
t_next(struct seq_file *m, void *v, loff_t *pos) t_next(struct seq_file *m, void *v, loff_t *pos)
{ {
struct trace_array *tr = m->private;
struct tracer *t = v; struct tracer *t = v;
(*pos)++; (*pos)++;
if (t) if (t)
t = t->next; t = get_tracer_for_array(tr, t->next);
return t; return t;
} }
static void *t_start(struct seq_file *m, loff_t *pos) static void *t_start(struct seq_file *m, loff_t *pos)
{ {
struct trace_array *tr = m->private;
struct tracer *t; struct tracer *t;
loff_t l = 0; loff_t l = 0;
mutex_lock(&trace_types_lock); mutex_lock(&trace_types_lock);
for (t = trace_types; t && l < *pos; t = t_next(m, t, &l))
; t = get_tracer_for_array(tr, trace_types);
for (; t && l < *pos; t = t_next(m, t, &l))
;
return t; return t;
} }
...@@ -3192,10 +3229,21 @@ static const struct seq_operations show_traces_seq_ops = { ...@@ -3192,10 +3229,21 @@ static const struct seq_operations show_traces_seq_ops = {
static int show_traces_open(struct inode *inode, struct file *file) static int show_traces_open(struct inode *inode, struct file *file)
{ {
struct trace_array *tr = inode->i_private;
struct seq_file *m;
int ret;
if (tracing_disabled) if (tracing_disabled)
return -ENODEV; return -ENODEV;
return seq_open(file, &show_traces_seq_ops); ret = seq_open(file, &show_traces_seq_ops);
if (ret)
return ret;
m = file->private_data;
m->private = tr;
return 0;
} }
static ssize_t static ssize_t
...@@ -3355,13 +3403,14 @@ static int tracing_trace_options_show(struct seq_file *m, void *v) ...@@ -3355,13 +3403,14 @@ static int tracing_trace_options_show(struct seq_file *m, void *v)
return 0; return 0;
} }
static int __set_tracer_option(struct tracer *trace, static int __set_tracer_option(struct trace_array *tr,
struct tracer_flags *tracer_flags, struct tracer_flags *tracer_flags,
struct tracer_opt *opts, int neg) struct tracer_opt *opts, int neg)
{ {
struct tracer *trace = tr->current_trace;
int ret; int ret;
ret = trace->set_flag(tracer_flags->val, opts->bit, !neg); ret = trace->set_flag(tr, tracer_flags->val, opts->bit, !neg);
if (ret) if (ret)
return ret; return ret;
...@@ -3373,8 +3422,9 @@ static int __set_tracer_option(struct tracer *trace, ...@@ -3373,8 +3422,9 @@ static int __set_tracer_option(struct tracer *trace,
} }
/* Try to assign a tracer specific option */ /* Try to assign a tracer specific option */
static int set_tracer_option(struct tracer *trace, char *cmp, int neg) static int set_tracer_option(struct trace_array *tr, char *cmp, int neg)
{ {
struct tracer *trace = tr->current_trace;
struct tracer_flags *tracer_flags = trace->flags; struct tracer_flags *tracer_flags = trace->flags;
struct tracer_opt *opts = NULL; struct tracer_opt *opts = NULL;
int i; int i;
...@@ -3383,8 +3433,7 @@ static int set_tracer_option(struct tracer *trace, char *cmp, int neg) ...@@ -3383,8 +3433,7 @@ static int set_tracer_option(struct tracer *trace, char *cmp, int neg)
opts = &tracer_flags->opts[i]; opts = &tracer_flags->opts[i];
if (strcmp(cmp, opts->name) == 0) if (strcmp(cmp, opts->name) == 0)
return __set_tracer_option(trace, trace->flags, return __set_tracer_option(tr, trace->flags, opts, neg);
opts, neg);
} }
return -EINVAL; return -EINVAL;
...@@ -3407,7 +3456,7 @@ int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled) ...@@ -3407,7 +3456,7 @@ int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled)
/* Give the tracer a chance to approve the change */ /* Give the tracer a chance to approve the change */
if (tr->current_trace->flag_changed) if (tr->current_trace->flag_changed)
if (tr->current_trace->flag_changed(tr->current_trace, mask, !!enabled)) if (tr->current_trace->flag_changed(tr, mask, !!enabled))
return -EINVAL; return -EINVAL;
if (enabled) if (enabled)
...@@ -3456,7 +3505,7 @@ static int trace_set_options(struct trace_array *tr, char *option) ...@@ -3456,7 +3505,7 @@ static int trace_set_options(struct trace_array *tr, char *option)
/* If no option could be set, test the specific tracer options */ /* If no option could be set, test the specific tracer options */
if (!trace_options[i]) if (!trace_options[i])
ret = set_tracer_option(tr->current_trace, cmp, neg); ret = set_tracer_option(tr, cmp, neg);
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
...@@ -3885,10 +3934,26 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer); ...@@ -3885,10 +3934,26 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer);
static void static void
destroy_trace_option_files(struct trace_option_dentry *topts); destroy_trace_option_files(struct trace_option_dentry *topts);
static int tracing_set_tracer(const char *buf) /*
* Used to clear out the tracer before deletion of an instance.
* Must have trace_types_lock held.
*/
static void tracing_set_nop(struct trace_array *tr)
{
if (tr->current_trace == &nop_trace)
return;
tr->current_trace->enabled--;
if (tr->current_trace->reset)
tr->current_trace->reset(tr);
tr->current_trace = &nop_trace;
}
static int tracing_set_tracer(struct trace_array *tr, const char *buf)
{ {
static struct trace_option_dentry *topts; static struct trace_option_dentry *topts;
struct trace_array *tr = &global_trace;
struct tracer *t; struct tracer *t;
#ifdef CONFIG_TRACER_MAX_TRACE #ifdef CONFIG_TRACER_MAX_TRACE
bool had_max_tr; bool had_max_tr;
...@@ -3916,9 +3981,15 @@ static int tracing_set_tracer(const char *buf) ...@@ -3916,9 +3981,15 @@ static int tracing_set_tracer(const char *buf)
if (t == tr->current_trace) if (t == tr->current_trace)
goto out; goto out;
/* Some tracers are only allowed for the top level buffer */
if (!trace_ok_for_array(t, tr)) {
ret = -EINVAL;
goto out;
}
trace_branch_disable(); trace_branch_disable();
tr->current_trace->enabled = false; tr->current_trace->enabled--;
if (tr->current_trace->reset) if (tr->current_trace->reset)
tr->current_trace->reset(tr); tr->current_trace->reset(tr);
...@@ -3941,9 +4012,11 @@ static int tracing_set_tracer(const char *buf) ...@@ -3941,9 +4012,11 @@ static int tracing_set_tracer(const char *buf)
free_snapshot(tr); free_snapshot(tr);
} }
#endif #endif
destroy_trace_option_files(topts); /* Currently, only the top instance has options */
if (tr->flags & TRACE_ARRAY_FL_GLOBAL) {
topts = create_trace_option_files(tr, t); destroy_trace_option_files(topts);
topts = create_trace_option_files(tr, t);
}
#ifdef CONFIG_TRACER_MAX_TRACE #ifdef CONFIG_TRACER_MAX_TRACE
if (t->use_max_tr && !had_max_tr) { if (t->use_max_tr && !had_max_tr) {
...@@ -3960,7 +4033,7 @@ static int tracing_set_tracer(const char *buf) ...@@ -3960,7 +4033,7 @@ static int tracing_set_tracer(const char *buf)
} }
tr->current_trace = t; tr->current_trace = t;
tr->current_trace->enabled = true; tr->current_trace->enabled++;
trace_branch_enable(tr); trace_branch_enable(tr);
out: out:
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
...@@ -3972,6 +4045,7 @@ static ssize_t ...@@ -3972,6 +4045,7 @@ static ssize_t
tracing_set_trace_write(struct file *filp, const char __user *ubuf, tracing_set_trace_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos) size_t cnt, loff_t *ppos)
{ {
struct trace_array *tr = filp->private_data;
char buf[MAX_TRACER_SIZE+1]; char buf[MAX_TRACER_SIZE+1];
int i; int i;
size_t ret; size_t ret;
...@@ -3991,7 +4065,7 @@ tracing_set_trace_write(struct file *filp, const char __user *ubuf, ...@@ -3991,7 +4065,7 @@ tracing_set_trace_write(struct file *filp, const char __user *ubuf,
for (i = cnt - 1; i > 0 && isspace(buf[i]); i--) for (i = cnt - 1; i > 0 && isspace(buf[i]); i--)
buf[i] = 0; buf[i] = 0;
err = tracing_set_tracer(buf); err = tracing_set_tracer(tr, buf);
if (err) if (err)
return err; return err;
...@@ -4699,25 +4773,10 @@ static int tracing_clock_show(struct seq_file *m, void *v) ...@@ -4699,25 +4773,10 @@ static int tracing_clock_show(struct seq_file *m, void *v)
return 0; return 0;
} }
static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf, static int tracing_set_clock(struct trace_array *tr, const char *clockstr)
size_t cnt, loff_t *fpos)
{ {
struct seq_file *m = filp->private_data;
struct trace_array *tr = m->private;
char buf[64];
const char *clockstr;
int i; int i;
if (cnt >= sizeof(buf))
return -EINVAL;
if (copy_from_user(&buf, ubuf, cnt))
return -EFAULT;
buf[cnt] = 0;
clockstr = strstrip(buf);
for (i = 0; i < ARRAY_SIZE(trace_clocks); i++) { for (i = 0; i < ARRAY_SIZE(trace_clocks); i++) {
if (strcmp(trace_clocks[i].name, clockstr) == 0) if (strcmp(trace_clocks[i].name, clockstr) == 0)
break; break;
...@@ -4745,6 +4804,32 @@ static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf, ...@@ -4745,6 +4804,32 @@ static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf,
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
return 0;
}
static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *fpos)
{
struct seq_file *m = filp->private_data;
struct trace_array *tr = m->private;
char buf[64];
const char *clockstr;
int ret;
if (cnt >= sizeof(buf))
return -EINVAL;
if (copy_from_user(&buf, ubuf, cnt))
return -EFAULT;
buf[cnt] = 0;
clockstr = strstrip(buf);
ret = tracing_set_clock(tr, clockstr);
if (ret)
return ret;
*fpos += cnt; *fpos += cnt;
return cnt; return cnt;
...@@ -5705,7 +5790,7 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt, ...@@ -5705,7 +5790,7 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt,
if (!!(topt->flags->val & topt->opt->bit) != val) { if (!!(topt->flags->val & topt->opt->bit) != val) {
mutex_lock(&trace_types_lock); mutex_lock(&trace_types_lock);
ret = __set_tracer_option(topt->tr->current_trace, topt->flags, ret = __set_tracer_option(topt->tr, topt->flags,
topt->opt, !val); topt->opt, !val);
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
if (ret) if (ret)
...@@ -6112,7 +6197,9 @@ static int instance_delete(const char *name) ...@@ -6112,7 +6197,9 @@ static int instance_delete(const char *name)
list_del(&tr->list); list_del(&tr->list);
tracing_set_nop(tr);
event_trace_del_tracer(tr); event_trace_del_tracer(tr);
ftrace_destroy_function_files(tr);
debugfs_remove_recursive(tr->dir); debugfs_remove_recursive(tr->dir);
free_percpu(tr->trace_buffer.data); free_percpu(tr->trace_buffer.data);
ring_buffer_free(tr->trace_buffer.buffer); ring_buffer_free(tr->trace_buffer.buffer);
...@@ -6207,6 +6294,12 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer) ...@@ -6207,6 +6294,12 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer)
{ {
int cpu; int cpu;
trace_create_file("available_tracers", 0444, d_tracer,
tr, &show_traces_fops);
trace_create_file("current_tracer", 0644, d_tracer,
tr, &set_tracer_fops);
trace_create_file("tracing_cpumask", 0644, d_tracer, trace_create_file("tracing_cpumask", 0644, d_tracer,
tr, &tracing_cpumask_fops); tr, &tracing_cpumask_fops);
...@@ -6237,6 +6330,9 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer) ...@@ -6237,6 +6330,9 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer)
trace_create_file("tracing_on", 0644, d_tracer, trace_create_file("tracing_on", 0644, d_tracer,
tr, &rb_simple_fops); tr, &rb_simple_fops);
if (ftrace_create_function_files(tr, d_tracer))
WARN(1, "Could not allocate function filter files");
#ifdef CONFIG_TRACER_SNAPSHOT #ifdef CONFIG_TRACER_SNAPSHOT
trace_create_file("snapshot", 0644, d_tracer, trace_create_file("snapshot", 0644, d_tracer,
tr, &snapshot_fops); tr, &snapshot_fops);
...@@ -6259,12 +6355,6 @@ static __init int tracer_init_debugfs(void) ...@@ -6259,12 +6355,6 @@ static __init int tracer_init_debugfs(void)
init_tracer_debugfs(&global_trace, d_tracer); init_tracer_debugfs(&global_trace, d_tracer);
trace_create_file("available_tracers", 0444, d_tracer,
&global_trace, &show_traces_fops);
trace_create_file("current_tracer", 0644, d_tracer,
&global_trace, &set_tracer_fops);
#ifdef CONFIG_TRACER_MAX_TRACE #ifdef CONFIG_TRACER_MAX_TRACE
trace_create_file("tracing_max_latency", 0644, d_tracer, trace_create_file("tracing_max_latency", 0644, d_tracer,
&tracing_max_latency, &tracing_max_lat_fops); &tracing_max_latency, &tracing_max_lat_fops);
...@@ -6527,6 +6617,13 @@ __init static int tracer_alloc_buffers(void) ...@@ -6527,6 +6617,13 @@ __init static int tracer_alloc_buffers(void)
trace_init_cmdlines(); trace_init_cmdlines();
if (trace_boot_clock) {
ret = tracing_set_clock(&global_trace, trace_boot_clock);
if (ret < 0)
pr_warning("Trace clock %s not defined, going back to default\n",
trace_boot_clock);
}
/* /*
* register_tracer() might reference current_trace, so it * register_tracer() might reference current_trace, so it
* needs to be set before we register anything. This is * needs to be set before we register anything. This is
......
...@@ -210,6 +210,11 @@ struct trace_array { ...@@ -210,6 +210,11 @@ struct trace_array {
struct list_head events; struct list_head events;
cpumask_var_t tracing_cpumask; /* only trace on set CPUs */ cpumask_var_t tracing_cpumask; /* only trace on set CPUs */
int ref; int ref;
#ifdef CONFIG_FUNCTION_TRACER
struct ftrace_ops *ops;
/* function tracing enabled */
int function_enabled;
#endif
}; };
enum { enum {
...@@ -355,14 +360,16 @@ struct tracer { ...@@ -355,14 +360,16 @@ struct tracer {
void (*print_header)(struct seq_file *m); void (*print_header)(struct seq_file *m);
enum print_line_t (*print_line)(struct trace_iterator *iter); enum print_line_t (*print_line)(struct trace_iterator *iter);
/* If you handled the flag setting, return 0 */ /* If you handled the flag setting, return 0 */
int (*set_flag)(u32 old_flags, u32 bit, int set); int (*set_flag)(struct trace_array *tr,
u32 old_flags, u32 bit, int set);
/* Return 0 if OK with change, else return non-zero */ /* Return 0 if OK with change, else return non-zero */
int (*flag_changed)(struct tracer *tracer, int (*flag_changed)(struct trace_array *tr,
u32 mask, int set); u32 mask, int set);
struct tracer *next; struct tracer *next;
struct tracer_flags *flags; struct tracer_flags *flags;
int enabled;
bool print_max; bool print_max;
bool enabled; bool allow_instances;
#ifdef CONFIG_TRACER_MAX_TRACE #ifdef CONFIG_TRACER_MAX_TRACE
bool use_max_tr; bool use_max_tr;
#endif #endif
...@@ -812,13 +819,36 @@ static inline int ftrace_trace_task(struct task_struct *task) ...@@ -812,13 +819,36 @@ static inline int ftrace_trace_task(struct task_struct *task)
return test_tsk_trace_trace(task); return test_tsk_trace_trace(task);
} }
extern int ftrace_is_dead(void); extern int ftrace_is_dead(void);
int ftrace_create_function_files(struct trace_array *tr,
struct dentry *parent);
void ftrace_destroy_function_files(struct trace_array *tr);
#else #else
static inline int ftrace_trace_task(struct task_struct *task) static inline int ftrace_trace_task(struct task_struct *task)
{ {
return 1; return 1;
} }
static inline int ftrace_is_dead(void) { return 0; } static inline int ftrace_is_dead(void) { return 0; }
#endif static inline int
ftrace_create_function_files(struct trace_array *tr,
struct dentry *parent)
{
return 0;
}
static inline void ftrace_destroy_function_files(struct trace_array *tr) { }
#endif /* CONFIG_FUNCTION_TRACER */
#if defined(CONFIG_FUNCTION_TRACER) && defined(CONFIG_DYNAMIC_FTRACE)
void ftrace_create_filter_files(struct ftrace_ops *ops,
struct dentry *parent);
void ftrace_destroy_filter_files(struct ftrace_ops *ops);
#else
/*
* The ops parameter passed in is usually undefined.
* This must be a macro.
*/
#define ftrace_create_filter_files(ops, parent) do { } while (0)
#define ftrace_destroy_filter_files(ops) do { } while (0)
#endif /* CONFIG_FUNCTION_TRACER && CONFIG_DYNAMIC_FTRACE */
int ftrace_event_is_function(struct ftrace_event_call *call); int ftrace_event_is_function(struct ftrace_event_call *call);
......
...@@ -188,6 +188,36 @@ int trace_event_raw_init(struct ftrace_event_call *call) ...@@ -188,6 +188,36 @@ int trace_event_raw_init(struct ftrace_event_call *call)
} }
EXPORT_SYMBOL_GPL(trace_event_raw_init); EXPORT_SYMBOL_GPL(trace_event_raw_init);
void *ftrace_event_buffer_reserve(struct ftrace_event_buffer *fbuffer,
struct ftrace_event_file *ftrace_file,
unsigned long len)
{
struct ftrace_event_call *event_call = ftrace_file->event_call;
local_save_flags(fbuffer->flags);
fbuffer->pc = preempt_count();
fbuffer->ftrace_file = ftrace_file;
fbuffer->event =
trace_event_buffer_lock_reserve(&fbuffer->buffer, ftrace_file,
event_call->event.type, len,
fbuffer->flags, fbuffer->pc);
if (!fbuffer->event)
return NULL;
fbuffer->entry = ring_buffer_event_data(fbuffer->event);
return fbuffer->entry;
}
EXPORT_SYMBOL_GPL(ftrace_event_buffer_reserve);
void ftrace_event_buffer_commit(struct ftrace_event_buffer *fbuffer)
{
event_trigger_unlock_commit(fbuffer->ftrace_file, fbuffer->buffer,
fbuffer->event, fbuffer->entry,
fbuffer->flags, fbuffer->pc);
}
EXPORT_SYMBOL_GPL(ftrace_event_buffer_commit);
int ftrace_event_reg(struct ftrace_event_call *call, int ftrace_event_reg(struct ftrace_event_call *call,
enum trace_reg type, void *data) enum trace_reg type, void *data)
{ {
......
...@@ -13,32 +13,106 @@ ...@@ -13,32 +13,106 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <linux/slab.h>
#include <linux/fs.h> #include <linux/fs.h>
#include "trace.h" #include "trace.h"
/* function tracing enabled */ static void tracing_start_function_trace(struct trace_array *tr);
static int ftrace_function_enabled; static void tracing_stop_function_trace(struct trace_array *tr);
static void
function_trace_call(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *pt_regs);
static void
function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *pt_regs);
static struct ftrace_ops trace_ops;
static struct ftrace_ops trace_stack_ops;
static struct tracer_flags func_flags;
/* Our option */
enum {
TRACE_FUNC_OPT_STACK = 0x1,
};
static int allocate_ftrace_ops(struct trace_array *tr)
{
struct ftrace_ops *ops;
ops = kzalloc(sizeof(*ops), GFP_KERNEL);
if (!ops)
return -ENOMEM;
static struct trace_array *func_trace; /* Currently only the non stack verision is supported */
ops->func = function_trace_call;
ops->flags = FTRACE_OPS_FL_RECURSION_SAFE;
tr->ops = ops;
ops->private = tr;
return 0;
}
int ftrace_create_function_files(struct trace_array *tr,
struct dentry *parent)
{
int ret;
/* The top level array uses the "global_ops". */
if (!(tr->flags & TRACE_ARRAY_FL_GLOBAL)) {
ret = allocate_ftrace_ops(tr);
if (ret)
return ret;
}
ftrace_create_filter_files(tr->ops, parent);
return 0;
}
static void tracing_start_function_trace(void); void ftrace_destroy_function_files(struct trace_array *tr)
static void tracing_stop_function_trace(void); {
ftrace_destroy_filter_files(tr->ops);
kfree(tr->ops);
tr->ops = NULL;
}
static int function_trace_init(struct trace_array *tr) static int function_trace_init(struct trace_array *tr)
{ {
func_trace = tr; struct ftrace_ops *ops;
if (tr->flags & TRACE_ARRAY_FL_GLOBAL) {
/* There's only one global tr */
if (!trace_ops.private) {
trace_ops.private = tr;
trace_stack_ops.private = tr;
}
if (func_flags.val & TRACE_FUNC_OPT_STACK)
ops = &trace_stack_ops;
else
ops = &trace_ops;
tr->ops = ops;
} else if (!tr->ops) {
/*
* Instance trace_arrays get their ops allocated
* at instance creation. Unless it failed
* the allocation.
*/
return -ENOMEM;
}
tr->trace_buffer.cpu = get_cpu(); tr->trace_buffer.cpu = get_cpu();
put_cpu(); put_cpu();
tracing_start_cmdline_record(); tracing_start_cmdline_record();
tracing_start_function_trace(); tracing_start_function_trace(tr);
return 0; return 0;
} }
static void function_trace_reset(struct trace_array *tr) static void function_trace_reset(struct trace_array *tr)
{ {
tracing_stop_function_trace(); tracing_stop_function_trace(tr);
tracing_stop_cmdline_record(); tracing_stop_cmdline_record();
} }
...@@ -47,25 +121,18 @@ static void function_trace_start(struct trace_array *tr) ...@@ -47,25 +121,18 @@ static void function_trace_start(struct trace_array *tr)
tracing_reset_online_cpus(&tr->trace_buffer); tracing_reset_online_cpus(&tr->trace_buffer);
} }
/* Our option */
enum {
TRACE_FUNC_OPT_STACK = 0x1,
};
static struct tracer_flags func_flags;
static void static void
function_trace_call(unsigned long ip, unsigned long parent_ip, function_trace_call(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *pt_regs) struct ftrace_ops *op, struct pt_regs *pt_regs)
{ {
struct trace_array *tr = func_trace; struct trace_array *tr = op->private;
struct trace_array_cpu *data; struct trace_array_cpu *data;
unsigned long flags; unsigned long flags;
int bit; int bit;
int cpu; int cpu;
int pc; int pc;
if (unlikely(!ftrace_function_enabled)) if (unlikely(!tr->function_enabled))
return; return;
pc = preempt_count(); pc = preempt_count();
...@@ -91,14 +158,14 @@ static void ...@@ -91,14 +158,14 @@ static void
function_stack_trace_call(unsigned long ip, unsigned long parent_ip, function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *pt_regs) struct ftrace_ops *op, struct pt_regs *pt_regs)
{ {
struct trace_array *tr = func_trace; struct trace_array *tr = op->private;
struct trace_array_cpu *data; struct trace_array_cpu *data;
unsigned long flags; unsigned long flags;
long disabled; long disabled;
int cpu; int cpu;
int pc; int pc;
if (unlikely(!ftrace_function_enabled)) if (unlikely(!tr->function_enabled))
return; return;
/* /*
...@@ -128,7 +195,6 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip, ...@@ -128,7 +195,6 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
local_irq_restore(flags); local_irq_restore(flags);
} }
static struct ftrace_ops trace_ops __read_mostly = static struct ftrace_ops trace_ops __read_mostly =
{ {
.func = function_trace_call, .func = function_trace_call,
...@@ -153,29 +219,21 @@ static struct tracer_flags func_flags = { ...@@ -153,29 +219,21 @@ static struct tracer_flags func_flags = {
.opts = func_opts .opts = func_opts
}; };
static void tracing_start_function_trace(void) static void tracing_start_function_trace(struct trace_array *tr)
{ {
ftrace_function_enabled = 0; tr->function_enabled = 0;
register_ftrace_function(tr->ops);
if (func_flags.val & TRACE_FUNC_OPT_STACK) tr->function_enabled = 1;
register_ftrace_function(&trace_stack_ops);
else
register_ftrace_function(&trace_ops);
ftrace_function_enabled = 1;
} }
static void tracing_stop_function_trace(void) static void tracing_stop_function_trace(struct trace_array *tr)
{ {
ftrace_function_enabled = 0; tr->function_enabled = 0;
unregister_ftrace_function(tr->ops);
if (func_flags.val & TRACE_FUNC_OPT_STACK)
unregister_ftrace_function(&trace_stack_ops);
else
unregister_ftrace_function(&trace_ops);
} }
static int func_set_flag(u32 old_flags, u32 bit, int set) static int
func_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{ {
switch (bit) { switch (bit) {
case TRACE_FUNC_OPT_STACK: case TRACE_FUNC_OPT_STACK:
...@@ -183,12 +241,14 @@ static int func_set_flag(u32 old_flags, u32 bit, int set) ...@@ -183,12 +241,14 @@ static int func_set_flag(u32 old_flags, u32 bit, int set)
if (!!set == !!(func_flags.val & TRACE_FUNC_OPT_STACK)) if (!!set == !!(func_flags.val & TRACE_FUNC_OPT_STACK))
break; break;
unregister_ftrace_function(tr->ops);
if (set) { if (set) {
unregister_ftrace_function(&trace_ops); tr->ops = &trace_stack_ops;
register_ftrace_function(&trace_stack_ops); register_ftrace_function(tr->ops);
} else { } else {
unregister_ftrace_function(&trace_stack_ops); tr->ops = &trace_ops;
register_ftrace_function(&trace_ops); register_ftrace_function(tr->ops);
} }
break; break;
...@@ -208,6 +268,7 @@ static struct tracer function_trace __tracer_data = ...@@ -208,6 +268,7 @@ static struct tracer function_trace __tracer_data =
.wait_pipe = poll_wait_pipe, .wait_pipe = poll_wait_pipe,
.flags = &func_flags, .flags = &func_flags,
.set_flag = func_set_flag, .set_flag = func_set_flag,
.allow_instances = true,
#ifdef CONFIG_FTRACE_SELFTEST #ifdef CONFIG_FTRACE_SELFTEST
.selftest = trace_selftest_startup_function, .selftest = trace_selftest_startup_function,
#endif #endif
......
...@@ -1476,7 +1476,8 @@ void graph_trace_close(struct trace_iterator *iter) ...@@ -1476,7 +1476,8 @@ void graph_trace_close(struct trace_iterator *iter)
} }
} }
static int func_graph_set_flag(u32 old_flags, u32 bit, int set) static int
func_graph_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{ {
if (bit == TRACE_GRAPH_PRINT_IRQS) if (bit == TRACE_GRAPH_PRINT_IRQS)
ftrace_graph_skip_irqs = !set; ftrace_graph_skip_irqs = !set;
......
...@@ -160,7 +160,8 @@ static struct ftrace_ops trace_ops __read_mostly = ...@@ -160,7 +160,8 @@ static struct ftrace_ops trace_ops __read_mostly =
#endif /* CONFIG_FUNCTION_TRACER */ #endif /* CONFIG_FUNCTION_TRACER */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
static int irqsoff_set_flag(u32 old_flags, u32 bit, int set) static int
irqsoff_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{ {
int cpu; int cpu;
...@@ -266,7 +267,8 @@ __trace_function(struct trace_array *tr, ...@@ -266,7 +267,8 @@ __trace_function(struct trace_array *tr,
#else #else
#define __trace_function trace_function #define __trace_function trace_function
static int irqsoff_set_flag(u32 old_flags, u32 bit, int set) static int
irqsoff_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{ {
return -EINVAL; return -EINVAL;
} }
...@@ -570,8 +572,10 @@ static void irqsoff_function_set(int set) ...@@ -570,8 +572,10 @@ static void irqsoff_function_set(int set)
unregister_irqsoff_function(is_graph()); unregister_irqsoff_function(is_graph());
} }
static int irqsoff_flag_changed(struct tracer *tracer, u32 mask, int set) static int irqsoff_flag_changed(struct trace_array *tr, u32 mask, int set)
{ {
struct tracer *tracer = tr->current_trace;
if (mask & TRACE_ITER_FUNCTION) if (mask & TRACE_ITER_FUNCTION)
irqsoff_function_set(set); irqsoff_function_set(set);
......
...@@ -35,11 +35,6 @@ struct trace_kprobe { ...@@ -35,11 +35,6 @@ struct trace_kprobe {
struct trace_probe tp; struct trace_probe tp;
}; };
struct event_file_link {
struct ftrace_event_file *file;
struct list_head list;
};
#define SIZEOF_TRACE_KPROBE(n) \ #define SIZEOF_TRACE_KPROBE(n) \
(offsetof(struct trace_kprobe, tp.args) + \ (offsetof(struct trace_kprobe, tp.args) + \
(sizeof(struct probe_arg) * (n))) (sizeof(struct probe_arg) * (n)))
...@@ -387,18 +382,6 @@ enable_trace_kprobe(struct trace_kprobe *tk, struct ftrace_event_file *file) ...@@ -387,18 +382,6 @@ enable_trace_kprobe(struct trace_kprobe *tk, struct ftrace_event_file *file)
return ret; return ret;
} }
static struct event_file_link *
find_event_file_link(struct trace_probe *tp, struct ftrace_event_file *file)
{
struct event_file_link *link;
list_for_each_entry(link, &tp->files, list)
if (link->file == file)
return link;
return NULL;
}
/* /*
* Disable trace_probe * Disable trace_probe
* if the file is NULL, disable "perf" handler, or disable "trace" handler. * if the file is NULL, disable "perf" handler, or disable "trace" handler.
......
...@@ -62,7 +62,7 @@ static void nop_trace_reset(struct trace_array *tr) ...@@ -62,7 +62,7 @@ static void nop_trace_reset(struct trace_array *tr)
* If you don't implement it, then the flag setting will be * If you don't implement it, then the flag setting will be
* automatically accepted. * automatically accepted.
*/ */
static int nop_set_flag(u32 old_flags, u32 bit, int set) static int nop_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{ {
/* /*
* Note that you don't need to update nop_flags.val yourself. * Note that you don't need to update nop_flags.val yourself.
...@@ -96,6 +96,7 @@ struct tracer nop_trace __read_mostly = ...@@ -96,6 +96,7 @@ struct tracer nop_trace __read_mostly =
.selftest = trace_selftest_startup_nop, .selftest = trace_selftest_startup_nop,
#endif #endif
.flags = &nop_flags, .flags = &nop_flags,
.set_flag = nop_set_flag .set_flag = nop_set_flag,
.allow_instances = true,
}; };
...@@ -439,6 +439,37 @@ int ftrace_raw_output_prep(struct trace_iterator *iter, ...@@ -439,6 +439,37 @@ int ftrace_raw_output_prep(struct trace_iterator *iter,
} }
EXPORT_SYMBOL(ftrace_raw_output_prep); EXPORT_SYMBOL(ftrace_raw_output_prep);
static int ftrace_output_raw(struct trace_iterator *iter, char *name,
char *fmt, va_list ap)
{
struct trace_seq *s = &iter->seq;
int ret;
ret = trace_seq_printf(s, "%s: ", name);
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
ret = trace_seq_vprintf(s, fmt, ap);
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
return TRACE_TYPE_HANDLED;
}
int ftrace_output_call(struct trace_iterator *iter, char *name, char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = ftrace_output_raw(iter, name, fmt, ap);
va_end(ap);
return ret;
}
EXPORT_SYMBOL_GPL(ftrace_output_call);
#ifdef CONFIG_KRETPROBES #ifdef CONFIG_KRETPROBES
static inline const char *kretprobed(const char *name) static inline const char *kretprobed(const char *name)
{ {
......
...@@ -288,6 +288,11 @@ struct trace_probe { ...@@ -288,6 +288,11 @@ struct trace_probe {
struct probe_arg args[]; struct probe_arg args[];
}; };
struct event_file_link {
struct ftrace_event_file *file;
struct list_head list;
};
static inline bool trace_probe_is_enabled(struct trace_probe *tp) static inline bool trace_probe_is_enabled(struct trace_probe *tp)
{ {
return !!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE)); return !!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE));
...@@ -316,6 +321,18 @@ static inline int is_good_name(const char *name) ...@@ -316,6 +321,18 @@ static inline int is_good_name(const char *name)
return 1; return 1;
} }
static inline struct event_file_link *
find_event_file_link(struct trace_probe *tp, struct ftrace_event_file *file)
{
struct event_file_link *link;
list_for_each_entry(link, &tp->files, list)
if (link->file == file)
return link;
return NULL;
}
extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size, extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
struct probe_arg *parg, bool is_return, bool is_kprobe); struct probe_arg *parg, bool is_return, bool is_kprobe);
......
...@@ -179,8 +179,10 @@ static void wakeup_function_set(int set) ...@@ -179,8 +179,10 @@ static void wakeup_function_set(int set)
unregister_wakeup_function(is_graph()); unregister_wakeup_function(is_graph());
} }
static int wakeup_flag_changed(struct tracer *tracer, u32 mask, int set) static int wakeup_flag_changed(struct trace_array *tr, u32 mask, int set)
{ {
struct tracer *tracer = tr->current_trace;
if (mask & TRACE_ITER_FUNCTION) if (mask & TRACE_ITER_FUNCTION)
wakeup_function_set(set); wakeup_function_set(set);
...@@ -209,7 +211,8 @@ static void stop_func_tracer(int graph) ...@@ -209,7 +211,8 @@ static void stop_func_tracer(int graph)
} }
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
static int wakeup_set_flag(u32 old_flags, u32 bit, int set) static int
wakeup_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{ {
if (!(bit & TRACE_DISPLAY_GRAPH)) if (!(bit & TRACE_DISPLAY_GRAPH))
...@@ -311,7 +314,8 @@ __trace_function(struct trace_array *tr, ...@@ -311,7 +314,8 @@ __trace_function(struct trace_array *tr,
#else #else
#define __trace_function trace_function #define __trace_function trace_function
static int wakeup_set_flag(u32 old_flags, u32 bit, int set) static int
wakeup_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{ {
return -EINVAL; return -EINVAL;
} }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/magic.h>
#include <asm/setup.h> #include <asm/setup.h>
...@@ -144,6 +145,8 @@ check_stack(unsigned long ip, unsigned long *stack) ...@@ -144,6 +145,8 @@ check_stack(unsigned long ip, unsigned long *stack)
i++; i++;
} }
BUG_ON(current != &init_task &&
*(end_of_stack(current)) != STACK_END_MAGIC);
out: out:
arch_spin_unlock(&max_stack_lock); arch_spin_unlock(&max_stack_lock);
local_irq_restore(flags); local_irq_restore(flags);
......
...@@ -260,6 +260,7 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret) ...@@ -260,6 +260,7 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret)
goto error; goto error;
INIT_LIST_HEAD(&tu->list); INIT_LIST_HEAD(&tu->list);
INIT_LIST_HEAD(&tu->tp.files);
tu->consumer.handler = uprobe_dispatcher; tu->consumer.handler = uprobe_dispatcher;
if (is_ret) if (is_ret)
tu->consumer.ret_handler = uretprobe_dispatcher; tu->consumer.ret_handler = uretprobe_dispatcher;
...@@ -758,31 +759,32 @@ static void uprobe_buffer_put(struct uprobe_cpu_buffer *ucb) ...@@ -758,31 +759,32 @@ static void uprobe_buffer_put(struct uprobe_cpu_buffer *ucb)
mutex_unlock(&ucb->mutex); mutex_unlock(&ucb->mutex);
} }
static void uprobe_trace_print(struct trace_uprobe *tu, static void __uprobe_trace_func(struct trace_uprobe *tu,
unsigned long func, struct pt_regs *regs) unsigned long func, struct pt_regs *regs,
struct uprobe_cpu_buffer *ucb, int dsize,
struct ftrace_event_file *ftrace_file)
{ {
struct uprobe_trace_entry_head *entry; struct uprobe_trace_entry_head *entry;
struct ring_buffer_event *event; struct ring_buffer_event *event;
struct ring_buffer *buffer; struct ring_buffer *buffer;
struct uprobe_cpu_buffer *ucb;
void *data; void *data;
int size, dsize, esize; int size, esize;
struct ftrace_event_call *call = &tu->tp.call; struct ftrace_event_call *call = &tu->tp.call;
dsize = __get_data_size(&tu->tp, regs); WARN_ON(call != ftrace_file->event_call);
esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
if (WARN_ON_ONCE(!uprobe_cpu_buffer || tu->tp.size + dsize > PAGE_SIZE)) if (WARN_ON_ONCE(tu->tp.size + dsize > PAGE_SIZE))
return; return;
ucb = uprobe_buffer_get(); if (ftrace_trigger_soft_disabled(ftrace_file))
store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize); return;
esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
size = esize + tu->tp.size + dsize; size = esize + tu->tp.size + dsize;
event = trace_current_buffer_lock_reserve(&buffer, call->event.type, event = trace_event_buffer_lock_reserve(&buffer, ftrace_file,
size, 0, 0); call->event.type, size, 0, 0);
if (!event) if (!event)
goto out; return;
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
if (is_ret_probe(tu)) { if (is_ret_probe(tu)) {
...@@ -796,25 +798,36 @@ static void uprobe_trace_print(struct trace_uprobe *tu, ...@@ -796,25 +798,36 @@ static void uprobe_trace_print(struct trace_uprobe *tu,
memcpy(data, ucb->buf, tu->tp.size + dsize); memcpy(data, ucb->buf, tu->tp.size + dsize);
if (!call_filter_check_discard(call, entry, buffer, event)) event_trigger_unlock_commit(ftrace_file, buffer, event, entry, 0, 0);
trace_buffer_unlock_commit(buffer, event, 0, 0);
out:
uprobe_buffer_put(ucb);
} }
/* uprobe handler */ /* uprobe handler */
static int uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs) static int uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs,
struct uprobe_cpu_buffer *ucb, int dsize)
{ {
if (!is_ret_probe(tu)) struct event_file_link *link;
uprobe_trace_print(tu, 0, regs);
if (is_ret_probe(tu))
return 0;
rcu_read_lock();
list_for_each_entry_rcu(link, &tu->tp.files, list)
__uprobe_trace_func(tu, 0, regs, ucb, dsize, link->file);
rcu_read_unlock();
return 0; return 0;
} }
static void uretprobe_trace_func(struct trace_uprobe *tu, unsigned long func, static void uretprobe_trace_func(struct trace_uprobe *tu, unsigned long func,
struct pt_regs *regs) struct pt_regs *regs,
struct uprobe_cpu_buffer *ucb, int dsize)
{ {
uprobe_trace_print(tu, func, regs); struct event_file_link *link;
rcu_read_lock();
list_for_each_entry_rcu(link, &tu->tp.files, list)
__uprobe_trace_func(tu, func, regs, ucb, dsize, link->file);
rcu_read_unlock();
} }
/* Event entry printers */ /* Event entry printers */
...@@ -861,12 +874,24 @@ typedef bool (*filter_func_t)(struct uprobe_consumer *self, ...@@ -861,12 +874,24 @@ typedef bool (*filter_func_t)(struct uprobe_consumer *self,
struct mm_struct *mm); struct mm_struct *mm);
static int static int
probe_event_enable(struct trace_uprobe *tu, int flag, filter_func_t filter) probe_event_enable(struct trace_uprobe *tu, struct ftrace_event_file *file,
filter_func_t filter)
{ {
int ret = 0; bool enabled = trace_probe_is_enabled(&tu->tp);
struct event_file_link *link = NULL;
int ret;
if (file) {
link = kmalloc(sizeof(*link), GFP_KERNEL);
if (!link)
return -ENOMEM;
if (trace_probe_is_enabled(&tu->tp)) link->file = file;
return -EINTR; list_add_tail_rcu(&link->list, &tu->tp.files);
tu->tp.flags |= TP_FLAG_TRACE;
} else
tu->tp.flags |= TP_FLAG_PROFILE;
ret = uprobe_buffer_enable(); ret = uprobe_buffer_enable();
if (ret < 0) if (ret < 0)
...@@ -874,24 +899,49 @@ probe_event_enable(struct trace_uprobe *tu, int flag, filter_func_t filter) ...@@ -874,24 +899,49 @@ probe_event_enable(struct trace_uprobe *tu, int flag, filter_func_t filter)
WARN_ON(!uprobe_filter_is_empty(&tu->filter)); WARN_ON(!uprobe_filter_is_empty(&tu->filter));
tu->tp.flags |= flag; if (enabled)
return 0;
tu->consumer.filter = filter; tu->consumer.filter = filter;
ret = uprobe_register(tu->inode, tu->offset, &tu->consumer); ret = uprobe_register(tu->inode, tu->offset, &tu->consumer);
if (ret) if (ret) {
tu->tp.flags &= ~flag; if (file) {
list_del(&link->list);
kfree(link);
tu->tp.flags &= ~TP_FLAG_TRACE;
} else
tu->tp.flags &= ~TP_FLAG_PROFILE;
}
return ret; return ret;
} }
static void probe_event_disable(struct trace_uprobe *tu, int flag) static void
probe_event_disable(struct trace_uprobe *tu, struct ftrace_event_file *file)
{ {
if (!trace_probe_is_enabled(&tu->tp)) if (!trace_probe_is_enabled(&tu->tp))
return; return;
if (file) {
struct event_file_link *link;
link = find_event_file_link(&tu->tp, file);
if (!link)
return;
list_del_rcu(&link->list);
/* synchronize with u{,ret}probe_trace_func */
synchronize_sched();
kfree(link);
if (!list_empty(&tu->tp.files))
return;
}
WARN_ON(!uprobe_filter_is_empty(&tu->filter)); WARN_ON(!uprobe_filter_is_empty(&tu->filter));
uprobe_unregister(tu->inode, tu->offset, &tu->consumer); uprobe_unregister(tu->inode, tu->offset, &tu->consumer);
tu->tp.flags &= ~flag; tu->tp.flags &= file ? ~TP_FLAG_TRACE : ~TP_FLAG_PROFILE;
uprobe_buffer_disable(); uprobe_buffer_disable();
} }
...@@ -1014,31 +1064,24 @@ static bool uprobe_perf_filter(struct uprobe_consumer *uc, ...@@ -1014,31 +1064,24 @@ static bool uprobe_perf_filter(struct uprobe_consumer *uc,
return ret; return ret;
} }
static void uprobe_perf_print(struct trace_uprobe *tu, static void __uprobe_perf_func(struct trace_uprobe *tu,
unsigned long func, struct pt_regs *regs) unsigned long func, struct pt_regs *regs,
struct uprobe_cpu_buffer *ucb, int dsize)
{ {
struct ftrace_event_call *call = &tu->tp.call; struct ftrace_event_call *call = &tu->tp.call;
struct uprobe_trace_entry_head *entry; struct uprobe_trace_entry_head *entry;
struct hlist_head *head; struct hlist_head *head;
struct uprobe_cpu_buffer *ucb;
void *data; void *data;
int size, dsize, esize; int size, esize;
int rctx; int rctx;
dsize = __get_data_size(&tu->tp, regs);
esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu)); esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
if (WARN_ON_ONCE(!uprobe_cpu_buffer))
return;
size = esize + tu->tp.size + dsize; size = esize + tu->tp.size + dsize;
size = ALIGN(size + sizeof(u32), sizeof(u64)) - sizeof(u32); size = ALIGN(size + sizeof(u32), sizeof(u64)) - sizeof(u32);
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough")) if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough"))
return; return;
ucb = uprobe_buffer_get();
store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
preempt_disable(); preempt_disable();
head = this_cpu_ptr(call->perf_events); head = this_cpu_ptr(call->perf_events);
if (hlist_empty(head)) if (hlist_empty(head))
...@@ -1068,46 +1111,49 @@ static void uprobe_perf_print(struct trace_uprobe *tu, ...@@ -1068,46 +1111,49 @@ static void uprobe_perf_print(struct trace_uprobe *tu,
perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL); perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);
out: out:
preempt_enable(); preempt_enable();
uprobe_buffer_put(ucb);
} }
/* uprobe profile handler */ /* uprobe profile handler */
static int uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) static int uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs,
struct uprobe_cpu_buffer *ucb, int dsize)
{ {
if (!uprobe_perf_filter(&tu->consumer, 0, current->mm)) if (!uprobe_perf_filter(&tu->consumer, 0, current->mm))
return UPROBE_HANDLER_REMOVE; return UPROBE_HANDLER_REMOVE;
if (!is_ret_probe(tu)) if (!is_ret_probe(tu))
uprobe_perf_print(tu, 0, regs); __uprobe_perf_func(tu, 0, regs, ucb, dsize);
return 0; return 0;
} }
static void uretprobe_perf_func(struct trace_uprobe *tu, unsigned long func, static void uretprobe_perf_func(struct trace_uprobe *tu, unsigned long func,
struct pt_regs *regs) struct pt_regs *regs,
struct uprobe_cpu_buffer *ucb, int dsize)
{ {
uprobe_perf_print(tu, func, regs); __uprobe_perf_func(tu, func, regs, ucb, dsize);
} }
#endif /* CONFIG_PERF_EVENTS */ #endif /* CONFIG_PERF_EVENTS */
static static int
int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, void *data) trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type,
void *data)
{ {
struct trace_uprobe *tu = event->data; struct trace_uprobe *tu = event->data;
struct ftrace_event_file *file = data;
switch (type) { switch (type) {
case TRACE_REG_REGISTER: case TRACE_REG_REGISTER:
return probe_event_enable(tu, TP_FLAG_TRACE, NULL); return probe_event_enable(tu, file, NULL);
case TRACE_REG_UNREGISTER: case TRACE_REG_UNREGISTER:
probe_event_disable(tu, TP_FLAG_TRACE); probe_event_disable(tu, file);
return 0; return 0;
#ifdef CONFIG_PERF_EVENTS #ifdef CONFIG_PERF_EVENTS
case TRACE_REG_PERF_REGISTER: case TRACE_REG_PERF_REGISTER:
return probe_event_enable(tu, TP_FLAG_PROFILE, uprobe_perf_filter); return probe_event_enable(tu, NULL, uprobe_perf_filter);
case TRACE_REG_PERF_UNREGISTER: case TRACE_REG_PERF_UNREGISTER:
probe_event_disable(tu, TP_FLAG_PROFILE); probe_event_disable(tu, NULL);
return 0; return 0;
case TRACE_REG_PERF_OPEN: case TRACE_REG_PERF_OPEN:
...@@ -1127,8 +1173,11 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) ...@@ -1127,8 +1173,11 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs)
{ {
struct trace_uprobe *tu; struct trace_uprobe *tu;
struct uprobe_dispatch_data udd; struct uprobe_dispatch_data udd;
struct uprobe_cpu_buffer *ucb;
int dsize, esize;
int ret = 0; int ret = 0;
tu = container_of(con, struct trace_uprobe, consumer); tu = container_of(con, struct trace_uprobe, consumer);
tu->nhit++; tu->nhit++;
...@@ -1137,13 +1186,29 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) ...@@ -1137,13 +1186,29 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs)
current->utask->vaddr = (unsigned long) &udd; current->utask->vaddr = (unsigned long) &udd;
#ifdef CONFIG_PERF_EVENTS
if ((tu->tp.flags & TP_FLAG_TRACE) == 0 &&
!uprobe_perf_filter(&tu->consumer, 0, current->mm))
return UPROBE_HANDLER_REMOVE;
#endif
if (WARN_ON_ONCE(!uprobe_cpu_buffer))
return 0;
dsize = __get_data_size(&tu->tp, regs);
esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
ucb = uprobe_buffer_get();
store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
if (tu->tp.flags & TP_FLAG_TRACE) if (tu->tp.flags & TP_FLAG_TRACE)
ret |= uprobe_trace_func(tu, regs); ret |= uprobe_trace_func(tu, regs, ucb, dsize);
#ifdef CONFIG_PERF_EVENTS #ifdef CONFIG_PERF_EVENTS
if (tu->tp.flags & TP_FLAG_PROFILE) if (tu->tp.flags & TP_FLAG_PROFILE)
ret |= uprobe_perf_func(tu, regs); ret |= uprobe_perf_func(tu, regs, ucb, dsize);
#endif #endif
uprobe_buffer_put(ucb);
return ret; return ret;
} }
...@@ -1152,6 +1217,8 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con, ...@@ -1152,6 +1217,8 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
{ {
struct trace_uprobe *tu; struct trace_uprobe *tu;
struct uprobe_dispatch_data udd; struct uprobe_dispatch_data udd;
struct uprobe_cpu_buffer *ucb;
int dsize, esize;
tu = container_of(con, struct trace_uprobe, consumer); tu = container_of(con, struct trace_uprobe, consumer);
...@@ -1160,13 +1227,23 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con, ...@@ -1160,13 +1227,23 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
current->utask->vaddr = (unsigned long) &udd; current->utask->vaddr = (unsigned long) &udd;
if (WARN_ON_ONCE(!uprobe_cpu_buffer))
return 0;
dsize = __get_data_size(&tu->tp, regs);
esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
ucb = uprobe_buffer_get();
store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
if (tu->tp.flags & TP_FLAG_TRACE) if (tu->tp.flags & TP_FLAG_TRACE)
uretprobe_trace_func(tu, func, regs); uretprobe_trace_func(tu, func, regs, ucb, dsize);
#ifdef CONFIG_PERF_EVENTS #ifdef CONFIG_PERF_EVENTS
if (tu->tp.flags & TP_FLAG_PROFILE) if (tu->tp.flags & TP_FLAG_PROFILE)
uretprobe_perf_func(tu, func, regs); uretprobe_perf_func(tu, func, regs, ucb, dsize);
#endif #endif
uprobe_buffer_put(ucb);
return 0; return 0;
} }
......
...@@ -62,14 +62,12 @@ struct tracepoint_entry { ...@@ -62,14 +62,12 @@ struct tracepoint_entry {
struct hlist_node hlist; struct hlist_node hlist;
struct tracepoint_func *funcs; struct tracepoint_func *funcs;
int refcount; /* Number of times armed. 0 if disarmed. */ int refcount; /* Number of times armed. 0 if disarmed. */
int enabled; /* Tracepoint enabled */
char name[0]; char name[0];
}; };
struct tp_probes { struct tp_probes {
union { struct rcu_head rcu;
struct rcu_head rcu;
struct list_head list;
} u;
struct tracepoint_func probes[0]; struct tracepoint_func probes[0];
}; };
...@@ -82,7 +80,7 @@ static inline void *allocate_probes(int count) ...@@ -82,7 +80,7 @@ static inline void *allocate_probes(int count)
static void rcu_free_old_probes(struct rcu_head *head) static void rcu_free_old_probes(struct rcu_head *head)
{ {
kfree(container_of(head, struct tp_probes, u.rcu)); kfree(container_of(head, struct tp_probes, rcu));
} }
static inline void release_probes(struct tracepoint_func *old) static inline void release_probes(struct tracepoint_func *old)
...@@ -90,7 +88,7 @@ static inline void release_probes(struct tracepoint_func *old) ...@@ -90,7 +88,7 @@ static inline void release_probes(struct tracepoint_func *old)
if (old) { if (old) {
struct tp_probes *tp_probes = container_of(old, struct tp_probes *tp_probes = container_of(old,
struct tp_probes, probes[0]); struct tp_probes, probes[0]);
call_rcu_sched(&tp_probes->u.rcu, rcu_free_old_probes); call_rcu_sched(&tp_probes->rcu, rcu_free_old_probes);
} }
} }
...@@ -237,6 +235,7 @@ static struct tracepoint_entry *add_tracepoint(const char *name) ...@@ -237,6 +235,7 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
memcpy(&e->name[0], name, name_len); memcpy(&e->name[0], name, name_len);
e->funcs = NULL; e->funcs = NULL;
e->refcount = 0; e->refcount = 0;
e->enabled = 0;
hlist_add_head(&e->hlist, head); hlist_add_head(&e->hlist, head);
return e; return e;
} }
...@@ -316,6 +315,7 @@ static void tracepoint_update_probe_range(struct tracepoint * const *begin, ...@@ -316,6 +315,7 @@ static void tracepoint_update_probe_range(struct tracepoint * const *begin,
if (mark_entry) { if (mark_entry) {
set_tracepoint(&mark_entry, *iter, set_tracepoint(&mark_entry, *iter,
!!mark_entry->refcount); !!mark_entry->refcount);
mark_entry->enabled = !!mark_entry->refcount;
} else { } else {
disable_tracepoint(*iter); disable_tracepoint(*iter);
} }
...@@ -373,13 +373,26 @@ tracepoint_add_probe(const char *name, void *probe, void *data) ...@@ -373,13 +373,26 @@ tracepoint_add_probe(const char *name, void *probe, void *data)
* tracepoint_probe_register - Connect a probe to a tracepoint * tracepoint_probe_register - Connect a probe to a tracepoint
* @name: tracepoint name * @name: tracepoint name
* @probe: probe handler * @probe: probe handler
* @data: probe private data
*
* Returns:
* - 0 if the probe was successfully registered, and tracepoint
* callsites are currently loaded for that probe,
* - -ENODEV if the probe was successfully registered, but no tracepoint
* callsite is currently loaded for that probe,
* - other negative error value on error.
*
* When tracepoint_probe_register() returns either 0 or -ENODEV,
* parameters @name, @probe, and @data may be used by the tracepoint
* infrastructure until the probe is unregistered.
* *
* Returns 0 if ok, error value on error.
* The probe address must at least be aligned on the architecture pointer size. * The probe address must at least be aligned on the architecture pointer size.
*/ */
int tracepoint_probe_register(const char *name, void *probe, void *data) int tracepoint_probe_register(const char *name, void *probe, void *data)
{ {
struct tracepoint_func *old; struct tracepoint_func *old;
struct tracepoint_entry *entry;
int ret = 0;
mutex_lock(&tracepoints_mutex); mutex_lock(&tracepoints_mutex);
old = tracepoint_add_probe(name, probe, data); old = tracepoint_add_probe(name, probe, data);
...@@ -388,9 +401,13 @@ int tracepoint_probe_register(const char *name, void *probe, void *data) ...@@ -388,9 +401,13 @@ int tracepoint_probe_register(const char *name, void *probe, void *data)
return PTR_ERR(old); return PTR_ERR(old);
} }
tracepoint_update_probes(); /* may update entry */ tracepoint_update_probes(); /* may update entry */
entry = get_tracepoint(name);
/* Make sure the entry was enabled */
if (!entry || !entry->enabled)
ret = -ENODEV;
mutex_unlock(&tracepoints_mutex); mutex_unlock(&tracepoints_mutex);
release_probes(old); release_probes(old);
return 0; return ret;
} }
EXPORT_SYMBOL_GPL(tracepoint_probe_register); EXPORT_SYMBOL_GPL(tracepoint_probe_register);
...@@ -415,6 +432,7 @@ tracepoint_remove_probe(const char *name, void *probe, void *data) ...@@ -415,6 +432,7 @@ tracepoint_remove_probe(const char *name, void *probe, void *data)
* tracepoint_probe_unregister - Disconnect a probe from a tracepoint * tracepoint_probe_unregister - Disconnect a probe from a tracepoint
* @name: tracepoint name * @name: tracepoint name
* @probe: probe function pointer * @probe: probe function pointer
* @data: probe private data
* *
* We do not need to call a synchronize_sched to make sure the probes have * We do not need to call a synchronize_sched to make sure the probes have
* finished running before doing a module unload, because the module unload * finished running before doing a module unload, because the module unload
...@@ -438,197 +456,6 @@ int tracepoint_probe_unregister(const char *name, void *probe, void *data) ...@@ -438,197 +456,6 @@ int tracepoint_probe_unregister(const char *name, void *probe, void *data)
} }
EXPORT_SYMBOL_GPL(tracepoint_probe_unregister); EXPORT_SYMBOL_GPL(tracepoint_probe_unregister);
static LIST_HEAD(old_probes);
static int need_update;
static void tracepoint_add_old_probes(void *old)
{
need_update = 1;
if (old) {
struct tp_probes *tp_probes = container_of(old,
struct tp_probes, probes[0]);
list_add(&tp_probes->u.list, &old_probes);
}
}
/**
* tracepoint_probe_register_noupdate - register a probe but not connect
* @name: tracepoint name
* @probe: probe handler
*
* caller must call tracepoint_probe_update_all()
*/
int tracepoint_probe_register_noupdate(const char *name, void *probe,
void *data)
{
struct tracepoint_func *old;
mutex_lock(&tracepoints_mutex);
old = tracepoint_add_probe(name, probe, data);
if (IS_ERR(old)) {
mutex_unlock(&tracepoints_mutex);
return PTR_ERR(old);
}
tracepoint_add_old_probes(old);
mutex_unlock(&tracepoints_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(tracepoint_probe_register_noupdate);
/**
* tracepoint_probe_unregister_noupdate - remove a probe but not disconnect
* @name: tracepoint name
* @probe: probe function pointer
*
* caller must call tracepoint_probe_update_all()
*/
int tracepoint_probe_unregister_noupdate(const char *name, void *probe,
void *data)
{
struct tracepoint_func *old;
mutex_lock(&tracepoints_mutex);
old = tracepoint_remove_probe(name, probe, data);
if (IS_ERR(old)) {
mutex_unlock(&tracepoints_mutex);
return PTR_ERR(old);
}
tracepoint_add_old_probes(old);
mutex_unlock(&tracepoints_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(tracepoint_probe_unregister_noupdate);
/**
* tracepoint_probe_update_all - update tracepoints
*/
void tracepoint_probe_update_all(void)
{
LIST_HEAD(release_probes);
struct tp_probes *pos, *next;
mutex_lock(&tracepoints_mutex);
if (!need_update) {
mutex_unlock(&tracepoints_mutex);
return;
}
if (!list_empty(&old_probes))
list_replace_init(&old_probes, &release_probes);
need_update = 0;
tracepoint_update_probes();
mutex_unlock(&tracepoints_mutex);
list_for_each_entry_safe(pos, next, &release_probes, u.list) {
list_del(&pos->u.list);
call_rcu_sched(&pos->u.rcu, rcu_free_old_probes);
}
}
EXPORT_SYMBOL_GPL(tracepoint_probe_update_all);
/**
* tracepoint_get_iter_range - Get a next tracepoint iterator given a range.
* @tracepoint: current tracepoints (in), next tracepoint (out)
* @begin: beginning of the range
* @end: end of the range
*
* Returns whether a next tracepoint has been found (1) or not (0).
* Will return the first tracepoint in the range if the input tracepoint is
* NULL.
*/
static int tracepoint_get_iter_range(struct tracepoint * const **tracepoint,
struct tracepoint * const *begin, struct tracepoint * const *end)
{
if (!*tracepoint && begin != end) {
*tracepoint = begin;
return 1;
}
if (*tracepoint >= begin && *tracepoint < end)
return 1;
return 0;
}
#ifdef CONFIG_MODULES
static void tracepoint_get_iter(struct tracepoint_iter *iter)
{
int found = 0;
struct tp_module *iter_mod;
/* Core kernel tracepoints */
if (!iter->module) {
found = tracepoint_get_iter_range(&iter->tracepoint,
__start___tracepoints_ptrs,
__stop___tracepoints_ptrs);
if (found)
goto end;
}
/* Tracepoints in modules */
mutex_lock(&tracepoints_mutex);
list_for_each_entry(iter_mod, &tracepoint_module_list, list) {
/*
* Sorted module list
*/
if (iter_mod < iter->module)
continue;
else if (iter_mod > iter->module)
iter->tracepoint = NULL;
found = tracepoint_get_iter_range(&iter->tracepoint,
iter_mod->tracepoints_ptrs,
iter_mod->tracepoints_ptrs
+ iter_mod->num_tracepoints);
if (found) {
iter->module = iter_mod;
break;
}
}
mutex_unlock(&tracepoints_mutex);
end:
if (!found)
tracepoint_iter_reset(iter);
}
#else /* CONFIG_MODULES */
static void tracepoint_get_iter(struct tracepoint_iter *iter)
{
int found = 0;
/* Core kernel tracepoints */
found = tracepoint_get_iter_range(&iter->tracepoint,
__start___tracepoints_ptrs,
__stop___tracepoints_ptrs);
if (!found)
tracepoint_iter_reset(iter);
}
#endif /* CONFIG_MODULES */
void tracepoint_iter_start(struct tracepoint_iter *iter)
{
tracepoint_get_iter(iter);
}
EXPORT_SYMBOL_GPL(tracepoint_iter_start);
void tracepoint_iter_next(struct tracepoint_iter *iter)
{
iter->tracepoint++;
/*
* iter->tracepoint may be invalid because we blindly incremented it.
* Make sure it is valid by marshalling on the tracepoints, getting the
* tracepoints from following modules if necessary.
*/
tracepoint_get_iter(iter);
}
EXPORT_SYMBOL_GPL(tracepoint_iter_next);
void tracepoint_iter_stop(struct tracepoint_iter *iter)
{
}
EXPORT_SYMBOL_GPL(tracepoint_iter_stop);
void tracepoint_iter_reset(struct tracepoint_iter *iter)
{
#ifdef CONFIG_MODULES
iter->module = NULL;
#endif /* CONFIG_MODULES */
iter->tracepoint = NULL;
}
EXPORT_SYMBOL_GPL(tracepoint_iter_reset);
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
bool trace_module_has_bad_taint(struct module *mod) bool trace_module_has_bad_taint(struct module *mod)
...@@ -638,9 +465,12 @@ bool trace_module_has_bad_taint(struct module *mod) ...@@ -638,9 +465,12 @@ bool trace_module_has_bad_taint(struct module *mod)
static int tracepoint_module_coming(struct module *mod) static int tracepoint_module_coming(struct module *mod)
{ {
struct tp_module *tp_mod, *iter; struct tp_module *tp_mod;
int ret = 0; int ret = 0;
if (!mod->num_tracepoints)
return 0;
/* /*
* We skip modules that taint the kernel, especially those with different * We skip modules that taint the kernel, especially those with different
* module headers (for forced load), to make sure we don't cause a crash. * module headers (for forced load), to make sure we don't cause a crash.
...@@ -656,23 +486,7 @@ static int tracepoint_module_coming(struct module *mod) ...@@ -656,23 +486,7 @@ static int tracepoint_module_coming(struct module *mod)
} }
tp_mod->num_tracepoints = mod->num_tracepoints; tp_mod->num_tracepoints = mod->num_tracepoints;
tp_mod->tracepoints_ptrs = mod->tracepoints_ptrs; tp_mod->tracepoints_ptrs = mod->tracepoints_ptrs;
list_add_tail(&tp_mod->list, &tracepoint_module_list);
/*
* tracepoint_module_list is kept sorted by struct module pointer
* address for iteration on tracepoints from a seq_file that can release
* the mutex between calls.
*/
list_for_each_entry_reverse(iter, &tracepoint_module_list, list) {
BUG_ON(iter == tp_mod); /* Should never be in the list twice */
if (iter < tp_mod) {
/* We belong to the location right after iter. */
list_add(&tp_mod->list, &iter->list);
goto module_added;
}
}
/* We belong to the beginning of the list */
list_add(&tp_mod->list, &tracepoint_module_list);
module_added:
tracepoint_update_probe_range(mod->tracepoints_ptrs, tracepoint_update_probe_range(mod->tracepoints_ptrs,
mod->tracepoints_ptrs + mod->num_tracepoints); mod->tracepoints_ptrs + mod->num_tracepoints);
end: end:
...@@ -684,6 +498,9 @@ static int tracepoint_module_going(struct module *mod) ...@@ -684,6 +498,9 @@ static int tracepoint_module_going(struct module *mod)
{ {
struct tp_module *pos; struct tp_module *pos;
if (!mod->num_tracepoints)
return 0;
mutex_lock(&tracepoints_mutex); mutex_lock(&tracepoints_mutex);
tracepoint_update_probe_range(mod->tracepoints_ptrs, tracepoint_update_probe_range(mod->tracepoints_ptrs,
mod->tracepoints_ptrs + mod->num_tracepoints); mod->tracepoints_ptrs + mod->num_tracepoints);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment