tracing: Some more fixes for 4.17
Subsystems (4)
Steven Rostedt
maintainerLinus,
Various fixes in tracing:
- Tracepoints should not give warning on OOM failures
- Use special field for function pointer in trace event
- Fix igrab issues in uprobes
- Fixes to the new histogram triggers
Please pull the latest trace-v4.17-rc1-2 tree, which can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
trace-v4.17-rc1-2
Tag SHA1: a21207bca13342cd75fdb77962f4bc0ab96d9371 Head SHA1: d66a270be3310d7aa132fec0cea77d3d32a0ff75
Mathieu Desnoyers (1):
tracepoint: Do not warn on ENOMEM
Rishabh Bhatnagar (1):
tracing: initcall: Ordered comparison of function pointers
Song Liu (2):
tracing: Fix bad use of igrab in trace_uprobe.c
tracing: Remove igrab() iput() call from uprobes.c
Tom Zanussi (3):
tracing: Restore proper field flag printing when displaying triggers
tracing: Add field parsing hist error for hist triggers
tracing: Add field modifier parsing hist error for hist triggers
----
include/trace/events/initcall.h | 14 +++++++++++--- kernel/events/uprobes.c | 7 +++---- kernel/trace/trace_events_hist.c | 12 ++++++++++++ kernel/trace/trace_uprobe.c | 35 ++++++++++++++--------------------- kernel/tracepoint.c | 4 ++-- 5 files changed, 42 insertions(+), 30 deletions(-)
Steven Rostedt
maintainerPATCH 1/7 tracing: Fix bad use of igrab in trace_uprobe.c
From: Song Liu <[email protected]>
As Miklos reported and suggested:
This pattern repeats two times in trace_uprobe.c and in kernel/events/core.c as well:
ret = kern_path(filename, LOOKUP_FOLLOW, &path);
if (ret)
goto fail_address_parse;
inode = igrab(d_inode(path.dentry));
path_put(&path); And it's wrong. You can only hold a reference to the inode if you have an active ref to the superblock as well (which is normally through path.mnt) or holding s_umount.
This way unmounting the containing filesystem while the tracepoint is active will give you the "VFS: Busy inodes after unmount..." message and a crash when the inode is finally put.
Solution: store path instead of inode.
This patch fixes two instances in trace_uprobe.c. struct path is added to struct trace_uprobe to keep the inode and containing mount point referenced.
--- kernel/trace/trace_uprobe.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-)
kernel/trace/trace_uprobe.c +14 -21
@@ -55,6 +55,7 @@ struct trace_uprobe {struct list_head list;struct trace_uprobe_filter filter;struct uprobe_consumer consumer; +struct path path;struct inode *inode;char *filename;unsigned long offset;
@@ -289,7 +290,7 @@ static void free_trace_uprobe(struct trace_uprobe *tu)for (i =0 ; i < tu->tp.nr_args; i++)traceprobe_free_probe_arg (&tu->tp.args[i]); -iput (tu->inode); +path_put (&tu->path);kfree (tu->tp.call.class->system);kfree (tu->tp.call.name);kfree (tu->filename);
@@ -363,7 +364,6 @@ static int register_trace_uprobe(struct trace_uprobe *tu)static int create_trace_uprobe (int argc,char **argv) {struct trace_uprobe *tu; -struct inode *inode;char *arg, *event, *group, *filename;char buf[MAX_EVENT_NAME_LEN ];struct path path;
@@ -371,7 +371,6 @@ static int create_trace_uprobe(int argc, char **argv)bool is_delete, is_return;int i, ret; - inode =NULL ; ret =0 ; is_delete = false; is_return = false;
@@ -437,21 +436,16 @@ static int create_trace_uprobe(int argc, char **argv) }/* Find the last occurrence, in case the path contains ':' too. */ arg =strrchr (argv[1 ],':' ); -if (!arg) { - ret = -EINVAL ; - goto fail_address_parse; - } +if (!arg) +return -EINVAL ; *arg++ ='\0' ; filename = argv[1 ]; ret =kern_path (filename,LOOKUP_FOLLOW , &path);if (ret) - goto fail_address_parse; - - inode =igrab (d_real_inode (path.dentry)); -path_put (&path); +return ret; -if (!inode || !S_ISREG (inode->i_mode)) { +if (!d_is_reg (path.dentry)) { ret = -EINVAL ; goto fail_address_parse; }
@@ -490,7 +484,7 @@ static int create_trace_uprobe(int argc, char **argv) goto fail_address_parse; } tu->offset = offset; - tu->inode = inode; + tu->path = path; tu->filename =kstrdup (filename,GFP_KERNEL );if (!tu->filename) {
@@ -558,7 +552,7 @@ static int create_trace_uprobe(int argc, char **argv)return ret; fail_address_parse: -iput (inode); +path_put (&path);pr_info ("Failed to parse address or file.\n" );
@@ -922,6 +916,7 @@ probe_event_enable(struct trace_uprobe *tu, struct trace_event_file *file, goto err_flags; tu->consumer.filter = filter; + tu->inode =d_real_inode (tu->path.dentry); ret =uprobe_register (tu->inode, tu->offset, &tu->consumer);if (ret) goto err_buffer;
@@ -967,6 +962,7 @@ probe_event_disable(struct trace_uprobe *tu, struct trace_event_file *file)WARN_ON (!uprobe_filter_is_empty (&tu->filter));uprobe_unregister (tu->inode, tu->offset, &tu->consumer); + tu->inode =NULL ; tu->tp.flags &= file ? ~TP_FLAG_TRACE : ~TP_FLAG_PROFILE ;uprobe_buffer_disable ();
@@ -1337,7 +1333,6 @@ struct trace_event_call * create_local_trace_uprobe(char *name, unsigned long offs, bool is_return) {struct trace_uprobe *tu; -struct inode *inode;struct path path;int ret;
@@ -1345,11 +1340,8 @@ create_local_trace_uprobe(char *name, unsigned long offs, bool is_return)if (ret)return ERR_PTR (ret); - inode =igrab (d_inode (path.dentry)); -path_put (&path); - -if (!inode || !S_ISREG (inode->i_mode)) { -iput (inode); +if (!d_is_reg (path.dentry)) { +path_put (&path);return ERR_PTR (-EINVAL ); }
@@ -1364,11 +1356,12 @@ create_local_trace_uprobe(char *name, unsigned long offs, bool is_return)if (IS_ERR (tu)) {pr_info ("Failed to allocate trace_uprobe.(%d)\n" , (int )PTR_ERR (tu)); +path_put (&path);return ERR_CAST (tu); } tu->offset = offs; - tu->inode = inode; + tu->path = path; tu->filename =kstrdup (name,GFP_KERNEL );init_trace_event_call (tu, &tu->tp.call);
Signature · 1 line
2.17.0
Steven Rostedt
maintainerPATCH 2/7 tracing: Remove igrab() iput() call from uprobes.c
In reply to Steven RostedtFrom: Song Liu <[email protected]>
Caller of uprobe_register is required to keep the inode and containing mount point referenced.
There was misuse of igrab() in uprobes.c and trace_uprobe.c. This is because igrab() will not prevent umount of the containing mount point. To fix this, we added path to struct trace_uprobe, which keeps the inode and containing mount reference.
For uprobes.c, it is not necessary to call igrab() in uprobe_register(), as the caller is required to keep the inode reference. The igrab() is removed and comments on this requirement is added to uprobe_register().
--- kernel/events/uprobes.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
kernel/events/uprobes.c +3 -4
@@ -491,7 +491,7 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset)if (!uprobe)return NULL ; - uprobe->inode =igrab (inode); + uprobe->inode = inode; uprobe->offset = offset;init_rwsem (&uprobe->register_rwsem);init_rwsem (&uprobe->consumer_rwsem);
@@ -502,7 +502,6 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset)if (cur_uprobe) {kfree (uprobe); uprobe = cur_uprobe; -iput (inode); }return uprobe;
@@ -701,7 +700,6 @@ static void delete_uprobe(struct uprobe *uprobe)rb_erase (&uprobe->rb_node, &uprobes_tree);spin_unlock (&uprobes_treelock);RB_CLEAR_NODE (&uprobe->rb_node);/* for uprobe_is_active() */ -iput (uprobe->inode);put_uprobe (uprobe); }
@@ -873,7 +871,8 @@ static void __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *u* tuple). Creation refcount stops uprobe_unregister from freeing the * @uprobe even before the register operation is complete. Creation * refcount is released when the last @uc for the @uprobe -* unregisters. +* unregisters. Caller of uprobe_register() is required to keep @inode +* (and the containing mount) referenced. * * Return errno if it cannot successully install probes * else return 0 (success)
Signature · 1 line
2.17.0
Steven Rostedt
maintainerPATCH 3/7 tracing: initcall: Ordered comparison of function pointers
In reply to Steven RostedtFrom: Rishabh Bhatnagar <[email protected]>
Using initcall_t in the __field macro generates the following warning with clang version 6.0:
include/trace/events/initcall.h:34:3: warning: ordered comparison of function pointers ('initcall_t' (aka 'int (*)(void)') and 'initcall_t')
__field macro expands to __field_ext macro which does is_signed_type check on the type argument. Since initcall_t is defined as a function pointer, using it as the type in the __field macro, leads to an ordered comparison of function pointer warning, inside the check. Using __field_struct macro avoids the issue.
--- include/trace/events/initcall.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
include/trace/events/initcall.h +11 -3
@@ -31,7 +31,11 @@ TRACE_EVENT(initcall_start,TP_ARGS (func),TP_STRUCT__entry ( -__field (initcall_t, func) +/* +* Use field_struct to avoid is_signed_type() +* comparison of a function pointer +*/ +__field_struct (initcall_t, func) ), TP_fast_assign(
@@ -48,8 +52,12 @@ TRACE_EVENT(initcall_finish,TP_ARGS (func, ret),TP_STRUCT__entry ( - __field(initcall_t, func) -__field (int, ret) +/* +* Use field_struct to avoid is_signed_type() +* comparison of a function pointer +*/ + __field_struct(initcall_t, func) +__field (int, ret) ), TP_fast_assign(
Signature · 1 line
2.17.0
Steven Rostedt
maintainerPATCH 4/7 tracing: Restore proper field flag printing when displaying triggers
In reply to Steven RostedtFrom: Tom Zanussi <[email protected]>
The flag-printing code used when displaying hist triggers somehow got dropped during refactoring of the inter-event patchset. This restores it.
Below are a couple examples - in the first case, .usecs wasn't being displayed properly for common_timestamps and the second illustrates the same for other flags such as .execname.
Before:
# echo 'hist:key=common_pid.execname:val=count:sort=count' > /sys/kernel/debug/tracing/events/syscalls/sys_enter_read/trigger # cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_read/trigger hist:keys=common_pid:vals=hitcount,count:sort=count:size=2048 [active]
# echo 'hist:keys=pid:ts0=common_timestamp.usecs if comm=="cyclictest"' >> /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger # cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger hist:keys=pid:vals=hitcount:ts0=common_timestamp:sort=hitcount:size=2048:clock=global if comm=="cyclictest" [active]
After:
# echo 'hist:key=common_pid.execname:val=count:sort=count' > /sys/kernel/debug/tracing/events/syscalls/sys_enter_read/trigger # cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_read/trigger hist:keys=common_pid.execname:vals=hitcount,count:sort=count:size=2048 [active]
# echo 'hist:keys=pid:ts0=common_timestamp.usecs if comm=="cyclictest"' >> /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger # cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger hist:keys=pid:vals=hitcount:ts0=common_timestamp.usecs:sort=hitcount:size=2048:clock=global if comm=="cyclictest" [active]
--- kernel/trace/trace_events_hist.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
kernel/trace/trace_events_hist.c +10 -0
@@ -4913,6 +4913,16 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)seq_printf (m,"%s" , field_name); }else if (hist_field->flags &HIST_FIELD_FL_TIMESTAMP )seq_puts (m,"common_timestamp" ); + +if (hist_field->flags) { +if (!(hist_field->flags &HIST_FIELD_FL_VAR_REF ) && + !(hist_field->flags &HIST_FIELD_FL_EXPR )) { +const char *flags =get_hist_field_flags (hist_field); + +if (flags) +seq_printf (m,".%s" , flags); + } + } } static int event_hist_trigger_print(struct seq_file *m,
Signature · 1 line
2.17.0
Steven Rostedt
maintainerPATCH 5/7 tracing: Add field parsing hist error for hist triggers
In reply to Steven RostedtFrom: Tom Zanussi <[email protected]>
If the user specifies a nonexistent field for a hist trigger, the current code correctly flags that as an error, but doesn't tell the user what happened.
Fix this by invoking hist_err() with an appropriate message when nonexistent fields are specified.
Before:
# echo 'hist:keys=pid:ts0=common_timestamp.usecs' >> /sys/kernel/debug/tracing/events/sched/sched_switch/trigger -su: echo: write error: Invalid argument # cat /sys/kernel/debug/tracing/events/sched/sched_switch/hist
After:
# echo 'hist:keys=pid:ts0=common_timestamp.usecs' >> /sys/kernel/debug/tracing/events/sched/sched_switch/trigger
-su: echo: write error: Invalid argument
# cat /sys/kernel/debug/tracing/events/sched/sched_switch/hist
ERROR: Couldn't find field: pid
Last command: keys=pid:ts0=common_timestamp.usecs--- kernel/trace/trace_events_hist.c | 1 + 1 file changed, 1 insertion(+)
kernel/trace/trace_events_hist.c +1 -0
@@ -2481,6 +2481,7 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,else { field =trace_find_event_field (file->event_call, field_name);if (!field || !field->size) { +hist_err ("Couldn't find field: " , field_name); field =ERR_PTR (-EINVAL ); goto out; }
Signature · 1 line
2.17.0
Steven Rostedt
maintainerPATCH 6/7 tracing: Add field modifier parsing hist error for hist triggers
In reply to Steven RostedtFrom: Tom Zanussi <[email protected]>
If the user specifies an invalid field modifier for a hist trigger, the current code correctly flags that as an error, but doesn't tell the user what happened.
Fix this by invoking hist_err() with an appropriate message when invalid modifiers are specified.
Before:
# echo 'hist:keys=pid:ts0=common_timestamp.junkusecs' >> /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger -su: echo: write error: Invalid argument # cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/hist
After:
# echo 'hist:keys=pid:ts0=common_timestamp.junkusecs' >> /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
-su: echo: write error: Invalid argument
# cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/hist
ERROR: Invalid field modifier: junkusecs
Last command: keys=pid:ts0=common_timestamp.junkusecs--- kernel/trace/trace_events_hist.c | 1 + 1 file changed, 1 insertion(+)
kernel/trace/trace_events_hist.c +1 -0
@@ -2466,6 +2466,7 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,else if (strcmp (modifier,"usecs" ) ==0 ) *flags |=HIST_FIELD_FL_TIMESTAMP_USECS ;else { +hist_err ("Invalid field modifier: " , modifier); field =ERR_PTR (-EINVAL ); goto out; }
Signature · 1 line
2.17.0
Steven Rostedt
maintainerPATCH 7/7 tracepoint: Do not warn on ENOMEM
In reply to Steven RostedtFrom: Mathieu Desnoyers <[email protected]>
Tracepoint should only warn when a kernel API user does not respect the required preconditions (e.g. same tracepoint enabled twice, or called to remove a tracepoint that does not exist).
Silence warning in out-of-memory conditions, given that the error is returned to the caller.
This ensures that out-of-memory error-injection testing does not trigger warnings in tracepoint.c, which were seen by syzbot.
--- kernel/tracepoint.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/tracepoint.c +2 -2
@@ -207,7 +207,7 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old =func_add (&tp_funcs, func, prio);if (IS_ERR (old)) { -WARN_ON_ONCE (1 ); +WARN_ON_ONCE (PTR_ERR (old) != -ENOMEM );return PTR_ERR (old); }
@@ -239,7 +239,7 @@ static int tracepoint_remove_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old =func_remove (&tp_funcs, func);if (IS_ERR (old)) { -WARN_ON_ONCE (1 ); +WARN_ON_ONCE (PTR_ERR (old) != -ENOMEM );return PTR_ERR (old); }
Signature · 1 line
2.17.0