ftrace: Add more checks to ignore disabled functions
Subsystems (3)
Steven Rostedt
maintainerLinus,
Alexei discovered a race condition in modules failing to load that can cause a ftrace check to trigger and disable ftrace. This is because of the way modules are registered to ftrace. Their functions are loaded in the ftrace function tables but set to "disabled" since they are still in the process of being loaded by the module. After the module is finished, it calls back into the ftrace infrastructure to enable it. Looking deeper into the locations that access all the functions in the table, I found more locations that should ignore the disabled ones.
Please pull the latest trace-v4.9-rc5 tree, which can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
trace-v4.9-rc5
Tag SHA1: 0f6a062b1a2a55e2c0f801514e9f8792e4339aef Head SHA1: 546fece4eae871f033925ccf0ff2b740725ae915
Alexei Starovoitov (1):
ftrace: Ignore FTRACE_FL_DISABLED while walking dyn_ftrace records
Steven Rostedt (Red Hat) (1):
ftrace: Add more checks for FTRACE_FL_DISABLED in processing ip records
----
kernel/trace/ftrace.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-)
Steven Rostedt
maintainerPATCH 1/2 ftrace: Ignore FTRACE_FL_DISABLED while walking dyn_ftrace records
From: Alexei Starovoitov <[email protected]>
ftrace_shutdown() checks for sanity of ftrace records
and if dyn_ftrace->flags is not zero, it will warn.
It can happen that 'flags' are set to FTRACE_FL_DISABLED at this point, since some module was loaded, but before ftrace_module_enable() cleared the flags for this module.
In other words the module.c is doing:ftrace_module_init(mod); // calls ftrace_update_code() that sets flags=FTRACE_FL_DISABLED ... // here ftrace_shutdown() is called that warns, since
err = prepare_coming_module(mod); // didn't have a chance to clear FTRACE_FL_DISABLED
Fix it by ignoring disabled records.
It's similar to what __ftrace_hash_rec_update() is already doing.
--- kernel/trace/ftrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ftrace.c +1 -1
@@ -2763,7 +2763,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)struct dyn_ftrace *rec;do_for_each_ftrace_rec (pg, rec) { -if (FTRACE_WARN_ON_ONCE (rec->flags)) +if (FTRACE_WARN_ON_ONCE (rec->flags & ~FTRACE_FL_DISABLED ))pr_warn (" %pS flags:%lx\n" , (void *)rec->ip, rec->flags); }while_for_each_ftrace_rec ();
Signature · 1 line
2.10.2
Steven Rostedt
maintainerPATCH 2/2 ftrace: Add more checks for FTRACE_FL_DISABLED in processing ip records
In reply to Steven RostedtFrom: "Steven Rostedt (Red Hat)" <[email protected]>
When a module is first loaded and its function ip records are added to the ftrace list of functions to modify, they are set to DISABLED, as their text is still in a read only state. When the module is fully loaded, and can be updated, the flag is cleared, and if their's any functions that should be tracing them, it is updated at that moment.
But there's several locations that do record accounting and should ignore records that are marked as disabled, or they can cause issues.
Alexei already fixed one location, but others need to be addressed.
--- kernel/trace/ftrace.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
kernel/trace/ftrace.c +22 -0
@@ -1862,6 +1862,10 @@ static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,/* Update rec->flags */ do_for_each_ftrace_rec (pg, rec) { + +if (rec->flags &FTRACE_FL_DISABLED ) +continue ; +/* We need to update only differences of filter_hash */ in_old = !!ftrace_lookup_ip (old_hash, rec->ip); in_new = !!ftrace_lookup_ip (new_hash, rec->ip);
@@ -1884,6 +1888,10 @@ rollback:/* Roll back what we did above */ do_for_each_ftrace_rec (pg, rec) { + +if (rec->flags &FTRACE_FL_DISABLED ) +continue ; +if (rec == end) goto err_out;
@@ -2397,6 +2405,10 @@ void __weak ftrace_replace_code(int enable)return ;do_for_each_ftrace_rec (pg, rec) { + +if (rec->flags &FTRACE_FL_DISABLED ) +continue ; + failed =__ftrace_replace_code (rec, enable);if (failed) {ftrace_bug (failed, rec);
@@ -3598,6 +3610,10 @@ match_records(struct ftrace_hash *hash, char *func, int len, char *mod) goto out_unlock;do_for_each_ftrace_rec (pg, rec) { + +if (rec->flags &FTRACE_FL_DISABLED ) +continue ; +if (ftrace_match_record (rec, &func_g, mod_match, exclude_mod)) { ret =enter_record (hash, rec, clear_filter);if (ret <0 ) {
@@ -3793,6 +3809,9 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,do_for_each_ftrace_rec (pg, rec) { +if (rec->flags &FTRACE_FL_DISABLED ) +continue ; +if (!ftrace_match_record (rec, &func_g,NULL ,0 ))continue ;
@@ -4685,6 +4704,9 @@ ftrace_set_func(unsigned long *array, int *idx, int size, char *buffer)do_for_each_ftrace_rec (pg, rec) { +if (rec->flags &FTRACE_FL_DISABLED ) +continue ; +if (ftrace_match_record (rec, &func_g,NULL ,0 )) {/* if it is in the array */ exists = false;
Signature · 1 line
2.10.2