tracing: various fixes
Steven Rostedt
maintainerIngo,
I rebased the last two patches but only changed the change logs. The first patch I rephrased the subject, the second patch I rewrote the change log to something that explains things much better.
I also added a third patch that fixes a regression that was reported to me earlier today.
Please pull the latest tip/perf/urgent-2 tree, which can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace.git
tip/perf/urgent-2
Head SHA1: 43dd61c9a09bd413e837df829e6bfb42159be52a
Steven Rostedt (3):
tracing: Fix bug when reading system filters on module removal
tracing: Have "enable" file use refcounts like the "filter" file
ftrace: Fix regression of :mod:module function enabling
----
include/linux/ftrace.h | 3 +- kernel/trace/ftrace.c | 12 +--- kernel/trace/trace.h | 1 + kernel/trace/trace_events.c | 113 ++++++++++++++++++++++++++++++------ kernel/trace/trace_events_filter.c | 6 ++ kernel/trace/trace_functions.c | 3 +- 6 files changed, 109 insertions(+), 29 deletions(-)
Steven Rostedt
maintainerPATCH v2 1/3 tracing: Fix bug when reading system filters on module removal
From: Steven Rostedt <[email protected]>
The event system is freed when its nr_events is set to zero. This happens when a module created an event system and then later the module is removed. Modules may share systems, so the system is allocated when it is created and freed when the modules are unloaded and all the events under the system are removed (nr_events set to zero).
The problem arises when a task opened the "filter" file for the system. If the module is unloaded and it removed the last event for that system, the system structure is freed. If the task that opened the filter file accesses the "filter" file after the system has been freed, the system will access an invalid pointer.
By adding a ref_count, and using it to keep track of what is using the event system, we can free it after all users are finished with the event system.
--- kernel/trace/trace.h | 1 + kernel/trace/trace_events.c | 86 +++++++++++++++++++++++++++++++----- kernel/trace/trace_events_filter.c | 6 +++ 3 files changed, 82 insertions(+), 11 deletions(-)
kernel/trace/trace.h +1 -0
@@ -677,6 +677,7 @@ struct event_subsystem {struct dentry *entry;struct event_filter *filter;int nr_events; +int ref_count; };#define FILTER_PRED_INVALID ((unsigned short)-1)
kernel/trace/trace_events.c +75 -11
133 lines not on this page · raw message
kernel/trace/trace_events_filter.c +6 -0
@@ -1886,6 +1886,12 @@ int apply_subsystem_event_filter(struct event_subsystem *system,mutex_lock (&event_mutex); +/* Make sure the system still has events */ +if (!system->nr_events) { + err = -ENODEV ; + goto out_unlock; + } +if (!strcmp (strstrip (filter_string),"0" )) {filter_free_subsystem_preds (system);remove_filter_string (system->filter);
Signature · 1 line
1.7.5.4
Steven Rostedt
maintainerPATCH v2 2/3 tracing: Have "enable" file use refcounts like the "filter" file
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
The "enable" file for the event system can be removed when a module is unloaded and the event system only has events from that module. As the event system nr_events count goes to zero, it may be freed if its ref_count is also set to zero.
Like the "filter" file, the "enable" file may be opened by a task and referenced later, after a module has been unloaded and the events for that event system have been removed.
Although the "filter" file referenced the event system structure, the "enable" file only references a pointer to the event system name. Since the name is freed when the event system is removed, it is possible that an access to the "enable" file may reference a freed pointer.
Update the "enable" file to use the subsystem_open() routine that the "filter" file uses, to keep a reference to the event system structure while the "enable" file is opened.
--- kernel/trace/trace_events.c | 31 ++++++++++++++++++++++--------- 1 files changed, 22 insertions(+), 9 deletions(-)
kernel/trace/trace_events.c +22 -9
@@ -557,7 +557,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) {const char set_to_char[4 ] = {'?' ,'0' ,'1' ,'X' }; -const char *system = filp->private_data; +struct event_subsystem *system = filp->private_data;struct ftrace_event_call *call;char buf[2 ];int set =0 ;
@@ -568,7 +568,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt,if (!call->name || !call->class || !call->class->reg)continue ; -if (system &&strcmp (call->class->system, system) !=0 ) +if (system &&strcmp (call->class->system, system->name) !=0 )continue ;/*
@@ -598,7 +598,8 @@ static ssize_t system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - const char *system = filp->private_data; + struct event_subsystem *system = filp->private_data; +const char *name =NULL ;unsigned long val;char buf[64 ];ssize_t ret;
@@ -622,7 +623,14 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,if (val !=0 && val !=1 )return -EINVAL ; - ret =__ftrace_set_clr_event (NULL , system,NULL , val); +/* +* Opening of "enable" adds a ref count to system, +* so the name is safe to use. +*/ +if (system) + name = system->name; + + ret =__ftrace_set_clr_event (NULL , name,NULL , val);if (ret) goto out;
@@ -862,6 +870,9 @@ static int subsystem_open(struct inode *inode, struct file *filp)struct event_subsystem *system =NULL ;int ret; +if (!inode->i_private) + goto skip_search; +/* Make sure the system still exists */ mutex_lock (&event_mutex);list_for_each_entry (system, &event_subsystems, list) {
@@ -880,8 +891,9 @@ static int subsystem_open(struct inode *inode, struct file *filp)if (system != inode->i_private)return -ENODEV ; + skip_search: ret =tracing_open_generic (inode, filp); -if (ret <0 ) +if (ret <0 && system)put_system (system);return ret;
@@ -891,7 +903,8 @@ static int subsystem_release(struct inode *inode, struct file *file) {struct event_subsystem *system = inode->i_private; -put_system (system); +if (system) +put_system (system);return 0 ; }
@@ -1041,10 +1054,11 @@ static const struct file_operations ftrace_subsystem_filter_fops = { };static const struct file_operations ftrace_system_enable_fops = { - .open = tracing_open_generic, + .open = subsystem_open, .read = system_enable_read, .write = system_enable_write, .llseek = default_llseek, + .release = subsystem_release, };static const struct file_operations ftrace_show_header_fops = {
@@ -1133,8 +1147,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) "'%s/filter' entry\n", name); } -trace_create_file ("enable" ,0644 , system->entry, - (void *)system->name, +trace_create_file ("enable" ,0644 , system->entry, system, &ftrace_system_enable_fops);return system->entry;
Signature · 1 line
1.7.5.4
Steven Rostedt
maintainerPATCH v2 3/3 ftrace: Fix regression of :mod:module function enabling
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
The new code that allows different utilities to pick and choose what functions they trace broke the :mod: hook that allows users to trace only functions of a particular module.
The reason is that the :mod: hook bypasses the hash that is setup to allow individual users to trace their own functions and uses the global hash directly. But if the global hash has not been set up, it will cause a bug:
echo '*:mod:radeon' > /sys/kernel/debug/set_ftrace_filter
produces:
[drm:drm_mode_getfb] *ERROR* invalid framebuffer id [drm:radeon_crtc_page_flip] *ERROR* failed to reserve new rbo buffer before flip
BUG: unable to handle kernel paging request at ffffffff8160ec90 IP: [<ffffffff810d9136>] add_hash_entry+0x66/0xd0 PGD 1a05067 PUD 1a09063 PMD 80000000016001e1 Oops: 0003 [#1] SMP Jul 7 04:02:28 phyllis kernel: [55303.858604] CPU 1 Modules linked in: cryptd aes_x86_64 aes_generic binfmt_misc rfcomm bnep ip6table_filter hid radeon r8169 ahci libahci mii ttm drm_kms_helper drm video i2c_algo_bit intel_agp intel_gtt
Pid: 10344, comm: bash Tainted: G WC 3.0.0-rc5 #1 Dell Inc. Inspiron N5010/0YXXJJ
RIP: 0010:[<ffffffff810d9136>] [<ffffffff810d9136>] add_hash_entry+0x66/0xd0 RSP: 0018:ffff88003a96bda8 EFLAGS: 00010246 RAX: ffff8801301735c0 RBX: ffffffff8160ec80 RCX: 0000000000306ee0 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff880137c92940 RBP: ffff88003a96bdb8 R08: ffff880137c95680 R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000000 R12: ffffffff81c9df78 R13: ffff8801153d1000 R14: 0000000000000000 R15: 0000000000000000 FS: 00007f329c18a700(0000) GS:ffff880137c80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffffff8160ec90 CR3: 000000003002b000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process bash (pid: 10344, threadinfo ffff88003a96a000, task ffff88012fcfc470) Stack: 0000000000000fd0 00000000000000fc ffff88003a96be38 ffffffff810d92f5 ffff88011c4c4e00 ffff880000000000 000000000b69f4d0 ffffffff8160ec80 ffff8800300e6f06 0000000081130295 0000000000000282 ffff8800300e6f00 Call Trace: [<ffffffff810d92f5>] match_records+0x155/0x1b0 [<ffffffff810d940c>] ftrace_mod_callback+0xbc/0x100 [<ffffffff810dafdf>] ftrace_regex_write+0x16f/0x210 [<ffffffff810db09f>] ftrace_filter_write+0xf/0x20 [<ffffffff81166e48>] vfs_write+0xc8/0x190 [<ffffffff81167001>] sys_write+0x51/0x90 [<ffffffff815c7e02>] system_call_fastpath+0x16/0x1b Code: 48 8b 33 31 d2 48 85 f6 75 33 49 89 d4 4c 03 63 08 49 8b 14 24 48 85 d2 48 89 10 74 04 48 89 42 08 49 89 04 24 4c 89 60 08 31 d2 RIP [<ffffffff810d9136>] add_hash_entry+0x66/0xd0 RSP <ffff88003a96bda8> CR2: ffffffff8160ec90 ---[ end trace a5d031828efdd88e ]---
--- include/linux/ftrace.h | 3 ++- kernel/trace/ftrace.c | 12 +++--------- kernel/trace/trace_functions.c | 3 ++- 3 files changed, 7 insertions(+), 11 deletions(-)
include/linux/ftrace.h +2 -1
@@ -123,7 +123,8 @@ stack_trace_sysctl(struct ctl_table *table, int write,struct ftrace_func_command {struct list_head list;char *name; -int (*func)(char *func,char *cmd, +int (*func)(struct ftrace_hash *hash, +char *func,char *cmd,char *params,int enable); };
kernel/trace/ftrace.c +3 -9
@@ -2407,10 +2407,9 @@ ftrace_match_module_records(struct ftrace_hash *hash, char *buff, char *mod)*/ static int -ftrace_mod_callback (char *func,char *cmd,char *param,int enable) +ftrace_mod_callback (struct ftrace_hash *hash, +char *func,char *cmd,char *param,int enable) { -struct ftrace_ops *ops = &global_ops; -struct ftrace_hash *hash;char *mod;int ret = -EINVAL ;
@@ -2430,11 +2429,6 @@ ftrace_mod_callback(char *func, char *cmd, char *param, int enable)if (!strlen (mod))return ret; -if (enable) - hash = ops->filter_hash; -else - hash = ops->notrace_hash; - ret =ftrace_match_module_records (hash, func, mod);if (!ret) ret = -EINVAL ;
@@ -2760,7 +2754,7 @@ static int ftrace_process_regex(struct ftrace_hash *hash,mutex_lock (&ftrace_cmd_mutex);list_for_each_entry (p, &ftrace_commands, list) {if (strcmp (p->name, command) ==0 ) { - ret = p->func (func, command, next, enable); + ret = p->func (hash, func, command, next, enable); goto out_unlock; } }
kernel/trace/trace_functions.c +2 -1
@@ -324,7 +324,8 @@ ftrace_trace_onoff_unreg(char *glob, char *cmd, char *param) }static int -ftrace_trace_onoff_callback (char *glob,char *cmd,char *param,int enable) +ftrace_trace_onoff_callback (struct ftrace_hash *hash, +char *glob,char *cmd,char *param,int enable) {struct ftrace_probe_ops *ops;void *count = (void *)-1 ;
Signature · 1 line
1.7.5.4