Loading include/linux/ftrace.h +70 −3 Original line number Diff line number Diff line Loading @@ -31,16 +31,33 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip); /* * FTRACE_OPS_FL_* bits denote the state of ftrace_ops struct and are * set in the flags member. * * ENABLED - set/unset when ftrace_ops is registered/unregistered * GLOBAL - set manualy by ftrace_ops user to denote the ftrace_ops * is part of the global tracers sharing the same filter * via set_ftrace_* debugfs files. * DYNAMIC - set when ftrace_ops is registered to denote dynamically * allocated ftrace_ops which need special care * CONTROL - set manualy by ftrace_ops user to denote the ftrace_ops * could be controled by following calls: * ftrace_function_local_enable * ftrace_function_local_disable */ enum { FTRACE_OPS_FL_ENABLED = 1 << 0, FTRACE_OPS_FL_GLOBAL = 1 << 1, FTRACE_OPS_FL_DYNAMIC = 1 << 2, FTRACE_OPS_FL_CONTROL = 1 << 3, }; struct ftrace_ops { ftrace_func_t func; struct ftrace_ops *next; unsigned long flags; int __percpu *disabled; #ifdef CONFIG_DYNAMIC_FTRACE struct ftrace_hash *notrace_hash; struct ftrace_hash *filter_hash; Loading Loading @@ -97,6 +114,55 @@ int register_ftrace_function(struct ftrace_ops *ops); int unregister_ftrace_function(struct ftrace_ops *ops); void clear_ftrace_function(void); /** * ftrace_function_local_enable - enable controlled ftrace_ops on current cpu * * This function enables tracing on current cpu by decreasing * the per cpu control variable. * It must be called with preemption disabled and only on ftrace_ops * registered with FTRACE_OPS_FL_CONTROL. If called without preemption * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. */ static inline void ftrace_function_local_enable(struct ftrace_ops *ops) { if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL))) return; (*this_cpu_ptr(ops->disabled))--; } /** * ftrace_function_local_disable - enable controlled ftrace_ops on current cpu * * This function enables tracing on current cpu by decreasing * the per cpu control variable. * It must be called with preemption disabled and only on ftrace_ops * registered with FTRACE_OPS_FL_CONTROL. If called without preemption * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. */ static inline void ftrace_function_local_disable(struct ftrace_ops *ops) { if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL))) return; (*this_cpu_ptr(ops->disabled))++; } /** * ftrace_function_local_disabled - returns ftrace_ops disabled value * on current cpu * * This function returns value of ftrace_ops::disabled on current cpu. * It must be called with preemption disabled and only on ftrace_ops * registered with FTRACE_OPS_FL_CONTROL. If called without preemption * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. */ static inline int ftrace_function_local_disabled(struct ftrace_ops *ops) { WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL)); return *this_cpu_ptr(ops->disabled); } extern void ftrace_stub(unsigned long a0, unsigned long a1); #else /* !CONFIG_FUNCTION_TRACER */ Loading Loading @@ -184,6 +250,7 @@ int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, int len, int reset); void ftrace_set_global_filter(unsigned char *buf, int len, int reset); void ftrace_set_global_notrace(unsigned char *buf, int len, int reset); void ftrace_free_filter(struct ftrace_ops *ops); int register_ftrace_command(struct ftrace_func_command *cmd); int unregister_ftrace_command(struct ftrace_func_command *cmd); Loading Loading @@ -314,9 +381,6 @@ extern void ftrace_enable_daemon(void); #else static inline int skip_trace(unsigned long ip) { return 0; } static inline int ftrace_force_update(void) { return 0; } static inline void ftrace_set_filter(unsigned char *buf, int len, int reset) { } static inline void ftrace_disable_daemon(void) { } static inline void ftrace_enable_daemon(void) { } static inline void ftrace_release_mod(struct module *mod) {} Loading @@ -340,6 +404,9 @@ static inline int ftrace_text_reserved(void *start, void *end) */ #define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; }) #define ftrace_set_early_filter(ops, buf, enable) do { } while (0) #define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_free_filter(ops) do { } while (0) static inline ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { return -ENODEV; } Loading include/linux/ftrace_event.h +7 −2 Original line number Diff line number Diff line Loading @@ -146,6 +146,10 @@ enum trace_reg { TRACE_REG_UNREGISTER, TRACE_REG_PERF_REGISTER, TRACE_REG_PERF_UNREGISTER, TRACE_REG_PERF_OPEN, TRACE_REG_PERF_CLOSE, TRACE_REG_PERF_ADD, TRACE_REG_PERF_DEL, }; struct ftrace_event_call; Loading @@ -157,7 +161,7 @@ struct ftrace_event_class { void *perf_probe; #endif int (*reg)(struct ftrace_event_call *event, enum trace_reg type); enum trace_reg type, void *data); int (*define_fields)(struct ftrace_event_call *); struct list_head *(*get_fields)(struct ftrace_event_call *); struct list_head fields; Loading @@ -165,7 +169,7 @@ struct ftrace_event_class { }; extern int ftrace_event_reg(struct ftrace_event_call *event, enum trace_reg type); enum trace_reg type, void *data); enum { TRACE_EVENT_FL_ENABLED_BIT, Loading Loading @@ -241,6 +245,7 @@ enum { FILTER_STATIC_STRING, FILTER_DYN_STRING, FILTER_PTR_STRING, FILTER_TRACE_FN, }; #define EVENT_STORAGE_SIZE 128 Loading include/linux/perf_event.h +3 −0 Original line number Diff line number Diff line Loading @@ -859,6 +859,9 @@ struct perf_event { #ifdef CONFIG_EVENT_TRACING struct ftrace_event_call *tp_event; struct event_filter *filter; #ifdef CONFIG_FUNCTION_TRACER struct ftrace_ops ftrace_ops; #endif #endif #ifdef CONFIG_CGROUP_PERF Loading kernel/trace/ftrace.c +110 −7 Original line number Diff line number Diff line Loading @@ -62,6 +62,8 @@ #define FTRACE_HASH_DEFAULT_BITS 10 #define FTRACE_HASH_MAX_BITS 12 #define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_CONTROL) /* ftrace_enabled is a method to turn ftrace on or off */ int ftrace_enabled __read_mostly; static int last_ftrace_enabled; Loading Loading @@ -89,12 +91,14 @@ static struct ftrace_ops ftrace_list_end __read_mostly = { }; static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_control_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end; ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub; ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; static struct ftrace_ops global_ops; static struct ftrace_ops control_ops; static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip); Loading Loading @@ -168,6 +172,32 @@ static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip) } #endif static void control_ops_disable_all(struct ftrace_ops *ops) { int cpu; for_each_possible_cpu(cpu) *per_cpu_ptr(ops->disabled, cpu) = 1; } static int control_ops_alloc(struct ftrace_ops *ops) { int __percpu *disabled; disabled = alloc_percpu(int); if (!disabled) return -ENOMEM; ops->disabled = disabled; control_ops_disable_all(ops); return 0; } static void control_ops_free(struct ftrace_ops *ops) { free_percpu(ops->disabled); } static void update_global_ops(void) { ftrace_func_t func; Loading Loading @@ -259,6 +289,26 @@ static int remove_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops) return 0; } static void add_ftrace_list_ops(struct ftrace_ops **list, struct ftrace_ops *main_ops, struct ftrace_ops *ops) { int first = *list == &ftrace_list_end; add_ftrace_ops(list, ops); if (first) add_ftrace_ops(&ftrace_ops_list, main_ops); } static int remove_ftrace_list_ops(struct ftrace_ops **list, struct ftrace_ops *main_ops, struct ftrace_ops *ops) { int ret = remove_ftrace_ops(list, ops); if (!ret && *list == &ftrace_list_end) ret = remove_ftrace_ops(&ftrace_ops_list, main_ops); return ret; } static int __register_ftrace_function(struct ftrace_ops *ops) { if (ftrace_disabled) Loading @@ -270,15 +320,20 @@ static int __register_ftrace_function(struct ftrace_ops *ops) if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED)) return -EBUSY; /* We don't support both control and global flags set. */ if ((ops->flags & FL_GLOBAL_CONTROL_MASK) == FL_GLOBAL_CONTROL_MASK) return -EINVAL; if (!core_kernel_data((unsigned long)ops)) ops->flags |= FTRACE_OPS_FL_DYNAMIC; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { int first = ftrace_global_list == &ftrace_list_end; add_ftrace_ops(&ftrace_global_list, ops); add_ftrace_list_ops(&ftrace_global_list, &global_ops, ops); ops->flags |= FTRACE_OPS_FL_ENABLED; if (first) add_ftrace_ops(&ftrace_ops_list, &global_ops); } else if (ops->flags & FTRACE_OPS_FL_CONTROL) { if (control_ops_alloc(ops)) return -ENOMEM; add_ftrace_list_ops(&ftrace_control_list, &control_ops, ops); } else add_ftrace_ops(&ftrace_ops_list, ops); Loading @@ -302,11 +357,23 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) return -EINVAL; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { ret = remove_ftrace_ops(&ftrace_global_list, ops); if (!ret && ftrace_global_list == &ftrace_list_end) ret = remove_ftrace_ops(&ftrace_ops_list, &global_ops); ret = remove_ftrace_list_ops(&ftrace_global_list, &global_ops, ops); if (!ret) ops->flags &= ~FTRACE_OPS_FL_ENABLED; } else if (ops->flags & FTRACE_OPS_FL_CONTROL) { ret = remove_ftrace_list_ops(&ftrace_control_list, &control_ops, ops); if (!ret) { /* * The ftrace_ops is now removed from the list, * so there'll be no new users. We must ensure * all current users are done before we free * the control data. */ synchronize_sched(); control_ops_free(ops); } } else ret = remove_ftrace_ops(&ftrace_ops_list, ops); Loading Loading @@ -1119,6 +1186,12 @@ static void free_ftrace_hash_rcu(struct ftrace_hash *hash) call_rcu_sched(&hash->rcu, __free_ftrace_hash_rcu); } void ftrace_free_filter(struct ftrace_ops *ops) { free_ftrace_hash(ops->filter_hash); free_ftrace_hash(ops->notrace_hash); } static struct ftrace_hash *alloc_ftrace_hash(int size_bits) { struct ftrace_hash *hash; Loading Loading @@ -3873,6 +3946,36 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) #endif /* CONFIG_DYNAMIC_FTRACE */ static void ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip) { struct ftrace_ops *op; if (unlikely(trace_recursion_test(TRACE_CONTROL_BIT))) return; /* * Some of the ops may be dynamically allocated, * they must be freed after a synchronize_sched(). */ preempt_disable_notrace(); trace_recursion_set(TRACE_CONTROL_BIT); op = rcu_dereference_raw(ftrace_control_list); while (op != &ftrace_list_end) { if (!ftrace_function_local_disabled(op) && ftrace_ops_test(op, ip)) op->func(ip, parent_ip); op = rcu_dereference_raw(op->next); }; trace_recursion_clear(TRACE_CONTROL_BIT); preempt_enable_notrace(); } static struct ftrace_ops control_ops = { .func = ftrace_ops_control_func, }; static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip) { Loading kernel/trace/trace.h +28 −10 Original line number Diff line number Diff line Loading @@ -56,7 +56,7 @@ enum trace_type { #define F_STRUCT(args...) args #undef FTRACE_ENTRY #define FTRACE_ENTRY(name, struct_name, id, tstruct, print) \ #define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter) \ struct struct_name { \ struct trace_entry ent; \ tstruct \ Loading @@ -66,7 +66,13 @@ enum trace_type { #define TP_ARGS(args...) args #undef FTRACE_ENTRY_DUP #define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk) #define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk, filter) #undef FTRACE_ENTRY_REG #define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, \ filter, regfn) \ FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \ filter) #include "trace_entries.h" Loading Loading @@ -288,6 +294,8 @@ struct tracer { /* for function tracing recursion */ #define TRACE_INTERNAL_BIT (1<<11) #define TRACE_GLOBAL_BIT (1<<12) #define TRACE_CONTROL_BIT (1<<13) /* * Abuse of the trace_recursion. * As we need a way to maintain state if we are tracing the function Loading Loading @@ -589,6 +597,8 @@ static inline int ftrace_trace_task(struct task_struct *task) static inline int ftrace_is_dead(void) { return 0; } #endif int ftrace_event_is_function(struct ftrace_event_call *call); /* * struct trace_parser - servers for reading the user input separated by spaces * @cont: set if the input is not complete - no final space char was found Loading Loading @@ -766,9 +776,7 @@ struct filter_pred { u64 val; struct regex regex; unsigned short *ops; #ifdef CONFIG_FTRACE_STARTUP_TEST struct ftrace_event_field *field; #endif int offset; int not; int op; Loading Loading @@ -818,12 +826,22 @@ extern const char *__start___trace_bprintk_fmt[]; extern const char *__stop___trace_bprintk_fmt[]; #undef FTRACE_ENTRY #define FTRACE_ENTRY(call, struct_name, id, tstruct, print) \ #define FTRACE_ENTRY(call, struct_name, id, tstruct, print, filter) \ extern struct ftrace_event_call \ __attribute__((__aligned__(4))) event_##call; #undef FTRACE_ENTRY_DUP #define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print) \ FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print)) #define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print, filter) \ FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print), \ filter) #include "trace_entries.h" #ifdef CONFIG_PERF_EVENTS #ifdef CONFIG_FUNCTION_TRACER int perf_ftrace_event_register(struct ftrace_event_call *call, enum trace_reg type, void *data); #else #define perf_ftrace_event_register NULL #endif /* CONFIG_FUNCTION_TRACER */ #endif /* CONFIG_PERF_EVENTS */ #endif /* _LINUX_KERNEL_TRACE_H */ Loading
include/linux/ftrace.h +70 −3 Original line number Diff line number Diff line Loading @@ -31,16 +31,33 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip); /* * FTRACE_OPS_FL_* bits denote the state of ftrace_ops struct and are * set in the flags member. * * ENABLED - set/unset when ftrace_ops is registered/unregistered * GLOBAL - set manualy by ftrace_ops user to denote the ftrace_ops * is part of the global tracers sharing the same filter * via set_ftrace_* debugfs files. * DYNAMIC - set when ftrace_ops is registered to denote dynamically * allocated ftrace_ops which need special care * CONTROL - set manualy by ftrace_ops user to denote the ftrace_ops * could be controled by following calls: * ftrace_function_local_enable * ftrace_function_local_disable */ enum { FTRACE_OPS_FL_ENABLED = 1 << 0, FTRACE_OPS_FL_GLOBAL = 1 << 1, FTRACE_OPS_FL_DYNAMIC = 1 << 2, FTRACE_OPS_FL_CONTROL = 1 << 3, }; struct ftrace_ops { ftrace_func_t func; struct ftrace_ops *next; unsigned long flags; int __percpu *disabled; #ifdef CONFIG_DYNAMIC_FTRACE struct ftrace_hash *notrace_hash; struct ftrace_hash *filter_hash; Loading Loading @@ -97,6 +114,55 @@ int register_ftrace_function(struct ftrace_ops *ops); int unregister_ftrace_function(struct ftrace_ops *ops); void clear_ftrace_function(void); /** * ftrace_function_local_enable - enable controlled ftrace_ops on current cpu * * This function enables tracing on current cpu by decreasing * the per cpu control variable. * It must be called with preemption disabled and only on ftrace_ops * registered with FTRACE_OPS_FL_CONTROL. If called without preemption * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. */ static inline void ftrace_function_local_enable(struct ftrace_ops *ops) { if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL))) return; (*this_cpu_ptr(ops->disabled))--; } /** * ftrace_function_local_disable - enable controlled ftrace_ops on current cpu * * This function enables tracing on current cpu by decreasing * the per cpu control variable. * It must be called with preemption disabled and only on ftrace_ops * registered with FTRACE_OPS_FL_CONTROL. If called without preemption * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. */ static inline void ftrace_function_local_disable(struct ftrace_ops *ops) { if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL))) return; (*this_cpu_ptr(ops->disabled))++; } /** * ftrace_function_local_disabled - returns ftrace_ops disabled value * on current cpu * * This function returns value of ftrace_ops::disabled on current cpu. * It must be called with preemption disabled and only on ftrace_ops * registered with FTRACE_OPS_FL_CONTROL. If called without preemption * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. */ static inline int ftrace_function_local_disabled(struct ftrace_ops *ops) { WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL)); return *this_cpu_ptr(ops->disabled); } extern void ftrace_stub(unsigned long a0, unsigned long a1); #else /* !CONFIG_FUNCTION_TRACER */ Loading Loading @@ -184,6 +250,7 @@ int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, int len, int reset); void ftrace_set_global_filter(unsigned char *buf, int len, int reset); void ftrace_set_global_notrace(unsigned char *buf, int len, int reset); void ftrace_free_filter(struct ftrace_ops *ops); int register_ftrace_command(struct ftrace_func_command *cmd); int unregister_ftrace_command(struct ftrace_func_command *cmd); Loading Loading @@ -314,9 +381,6 @@ extern void ftrace_enable_daemon(void); #else static inline int skip_trace(unsigned long ip) { return 0; } static inline int ftrace_force_update(void) { return 0; } static inline void ftrace_set_filter(unsigned char *buf, int len, int reset) { } static inline void ftrace_disable_daemon(void) { } static inline void ftrace_enable_daemon(void) { } static inline void ftrace_release_mod(struct module *mod) {} Loading @@ -340,6 +404,9 @@ static inline int ftrace_text_reserved(void *start, void *end) */ #define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; }) #define ftrace_set_early_filter(ops, buf, enable) do { } while (0) #define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_free_filter(ops) do { } while (0) static inline ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { return -ENODEV; } Loading
include/linux/ftrace_event.h +7 −2 Original line number Diff line number Diff line Loading @@ -146,6 +146,10 @@ enum trace_reg { TRACE_REG_UNREGISTER, TRACE_REG_PERF_REGISTER, TRACE_REG_PERF_UNREGISTER, TRACE_REG_PERF_OPEN, TRACE_REG_PERF_CLOSE, TRACE_REG_PERF_ADD, TRACE_REG_PERF_DEL, }; struct ftrace_event_call; Loading @@ -157,7 +161,7 @@ struct ftrace_event_class { void *perf_probe; #endif int (*reg)(struct ftrace_event_call *event, enum trace_reg type); enum trace_reg type, void *data); int (*define_fields)(struct ftrace_event_call *); struct list_head *(*get_fields)(struct ftrace_event_call *); struct list_head fields; Loading @@ -165,7 +169,7 @@ struct ftrace_event_class { }; extern int ftrace_event_reg(struct ftrace_event_call *event, enum trace_reg type); enum trace_reg type, void *data); enum { TRACE_EVENT_FL_ENABLED_BIT, Loading Loading @@ -241,6 +245,7 @@ enum { FILTER_STATIC_STRING, FILTER_DYN_STRING, FILTER_PTR_STRING, FILTER_TRACE_FN, }; #define EVENT_STORAGE_SIZE 128 Loading
include/linux/perf_event.h +3 −0 Original line number Diff line number Diff line Loading @@ -859,6 +859,9 @@ struct perf_event { #ifdef CONFIG_EVENT_TRACING struct ftrace_event_call *tp_event; struct event_filter *filter; #ifdef CONFIG_FUNCTION_TRACER struct ftrace_ops ftrace_ops; #endif #endif #ifdef CONFIG_CGROUP_PERF Loading
kernel/trace/ftrace.c +110 −7 Original line number Diff line number Diff line Loading @@ -62,6 +62,8 @@ #define FTRACE_HASH_DEFAULT_BITS 10 #define FTRACE_HASH_MAX_BITS 12 #define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_CONTROL) /* ftrace_enabled is a method to turn ftrace on or off */ int ftrace_enabled __read_mostly; static int last_ftrace_enabled; Loading Loading @@ -89,12 +91,14 @@ static struct ftrace_ops ftrace_list_end __read_mostly = { }; static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_control_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end; ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub; ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; static struct ftrace_ops global_ops; static struct ftrace_ops control_ops; static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip); Loading Loading @@ -168,6 +172,32 @@ static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip) } #endif static void control_ops_disable_all(struct ftrace_ops *ops) { int cpu; for_each_possible_cpu(cpu) *per_cpu_ptr(ops->disabled, cpu) = 1; } static int control_ops_alloc(struct ftrace_ops *ops) { int __percpu *disabled; disabled = alloc_percpu(int); if (!disabled) return -ENOMEM; ops->disabled = disabled; control_ops_disable_all(ops); return 0; } static void control_ops_free(struct ftrace_ops *ops) { free_percpu(ops->disabled); } static void update_global_ops(void) { ftrace_func_t func; Loading Loading @@ -259,6 +289,26 @@ static int remove_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops) return 0; } static void add_ftrace_list_ops(struct ftrace_ops **list, struct ftrace_ops *main_ops, struct ftrace_ops *ops) { int first = *list == &ftrace_list_end; add_ftrace_ops(list, ops); if (first) add_ftrace_ops(&ftrace_ops_list, main_ops); } static int remove_ftrace_list_ops(struct ftrace_ops **list, struct ftrace_ops *main_ops, struct ftrace_ops *ops) { int ret = remove_ftrace_ops(list, ops); if (!ret && *list == &ftrace_list_end) ret = remove_ftrace_ops(&ftrace_ops_list, main_ops); return ret; } static int __register_ftrace_function(struct ftrace_ops *ops) { if (ftrace_disabled) Loading @@ -270,15 +320,20 @@ static int __register_ftrace_function(struct ftrace_ops *ops) if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED)) return -EBUSY; /* We don't support both control and global flags set. */ if ((ops->flags & FL_GLOBAL_CONTROL_MASK) == FL_GLOBAL_CONTROL_MASK) return -EINVAL; if (!core_kernel_data((unsigned long)ops)) ops->flags |= FTRACE_OPS_FL_DYNAMIC; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { int first = ftrace_global_list == &ftrace_list_end; add_ftrace_ops(&ftrace_global_list, ops); add_ftrace_list_ops(&ftrace_global_list, &global_ops, ops); ops->flags |= FTRACE_OPS_FL_ENABLED; if (first) add_ftrace_ops(&ftrace_ops_list, &global_ops); } else if (ops->flags & FTRACE_OPS_FL_CONTROL) { if (control_ops_alloc(ops)) return -ENOMEM; add_ftrace_list_ops(&ftrace_control_list, &control_ops, ops); } else add_ftrace_ops(&ftrace_ops_list, ops); Loading @@ -302,11 +357,23 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) return -EINVAL; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { ret = remove_ftrace_ops(&ftrace_global_list, ops); if (!ret && ftrace_global_list == &ftrace_list_end) ret = remove_ftrace_ops(&ftrace_ops_list, &global_ops); ret = remove_ftrace_list_ops(&ftrace_global_list, &global_ops, ops); if (!ret) ops->flags &= ~FTRACE_OPS_FL_ENABLED; } else if (ops->flags & FTRACE_OPS_FL_CONTROL) { ret = remove_ftrace_list_ops(&ftrace_control_list, &control_ops, ops); if (!ret) { /* * The ftrace_ops is now removed from the list, * so there'll be no new users. We must ensure * all current users are done before we free * the control data. */ synchronize_sched(); control_ops_free(ops); } } else ret = remove_ftrace_ops(&ftrace_ops_list, ops); Loading Loading @@ -1119,6 +1186,12 @@ static void free_ftrace_hash_rcu(struct ftrace_hash *hash) call_rcu_sched(&hash->rcu, __free_ftrace_hash_rcu); } void ftrace_free_filter(struct ftrace_ops *ops) { free_ftrace_hash(ops->filter_hash); free_ftrace_hash(ops->notrace_hash); } static struct ftrace_hash *alloc_ftrace_hash(int size_bits) { struct ftrace_hash *hash; Loading Loading @@ -3873,6 +3946,36 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) #endif /* CONFIG_DYNAMIC_FTRACE */ static void ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip) { struct ftrace_ops *op; if (unlikely(trace_recursion_test(TRACE_CONTROL_BIT))) return; /* * Some of the ops may be dynamically allocated, * they must be freed after a synchronize_sched(). */ preempt_disable_notrace(); trace_recursion_set(TRACE_CONTROL_BIT); op = rcu_dereference_raw(ftrace_control_list); while (op != &ftrace_list_end) { if (!ftrace_function_local_disabled(op) && ftrace_ops_test(op, ip)) op->func(ip, parent_ip); op = rcu_dereference_raw(op->next); }; trace_recursion_clear(TRACE_CONTROL_BIT); preempt_enable_notrace(); } static struct ftrace_ops control_ops = { .func = ftrace_ops_control_func, }; static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip) { Loading
kernel/trace/trace.h +28 −10 Original line number Diff line number Diff line Loading @@ -56,7 +56,7 @@ enum trace_type { #define F_STRUCT(args...) args #undef FTRACE_ENTRY #define FTRACE_ENTRY(name, struct_name, id, tstruct, print) \ #define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter) \ struct struct_name { \ struct trace_entry ent; \ tstruct \ Loading @@ -66,7 +66,13 @@ enum trace_type { #define TP_ARGS(args...) args #undef FTRACE_ENTRY_DUP #define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk) #define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk, filter) #undef FTRACE_ENTRY_REG #define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, \ filter, regfn) \ FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \ filter) #include "trace_entries.h" Loading Loading @@ -288,6 +294,8 @@ struct tracer { /* for function tracing recursion */ #define TRACE_INTERNAL_BIT (1<<11) #define TRACE_GLOBAL_BIT (1<<12) #define TRACE_CONTROL_BIT (1<<13) /* * Abuse of the trace_recursion. * As we need a way to maintain state if we are tracing the function Loading Loading @@ -589,6 +597,8 @@ static inline int ftrace_trace_task(struct task_struct *task) static inline int ftrace_is_dead(void) { return 0; } #endif int ftrace_event_is_function(struct ftrace_event_call *call); /* * struct trace_parser - servers for reading the user input separated by spaces * @cont: set if the input is not complete - no final space char was found Loading Loading @@ -766,9 +776,7 @@ struct filter_pred { u64 val; struct regex regex; unsigned short *ops; #ifdef CONFIG_FTRACE_STARTUP_TEST struct ftrace_event_field *field; #endif int offset; int not; int op; Loading Loading @@ -818,12 +826,22 @@ extern const char *__start___trace_bprintk_fmt[]; extern const char *__stop___trace_bprintk_fmt[]; #undef FTRACE_ENTRY #define FTRACE_ENTRY(call, struct_name, id, tstruct, print) \ #define FTRACE_ENTRY(call, struct_name, id, tstruct, print, filter) \ extern struct ftrace_event_call \ __attribute__((__aligned__(4))) event_##call; #undef FTRACE_ENTRY_DUP #define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print) \ FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print)) #define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print, filter) \ FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print), \ filter) #include "trace_entries.h" #ifdef CONFIG_PERF_EVENTS #ifdef CONFIG_FUNCTION_TRACER int perf_ftrace_event_register(struct ftrace_event_call *call, enum trace_reg type, void *data); #else #define perf_ftrace_event_register NULL #endif /* CONFIG_FUNCTION_TRACER */ #endif /* CONFIG_PERF_EVENTS */ #endif /* _LINUX_KERNEL_TRACE_H */