tracing: Fixes for 7.0
Subsystems (4)
cover tracing: Fixes for 7.0
tracing fixes for 7.0:
- Revert "tracing: Remove pid in task_rename tracing output"
A change was made to remove the pid field from the task_rename event because it was thought that it was always done for the current task and recording the pid would be redundant. This turned out to be incorrect and there are a few corner casse where this is not true and caused some regressions in tooling.
- Fix the reading from user space for migration
The reading of user space uses a seq lock type of logic where it uses a per-cpu temporary buffer and disables migration, then enables preemption, does the copy from user space, disables preemption, enables migration and checks if there was any schedule switches while preemption was enabled. If there was a context switch, then it is considered that the per-cpu buffer could be corrupted and it tries again. There's a protection check that tests if it takes a hundred tries it issues a warning and exits out to prevent a live lock.
This was triggered because the task was selected by the load balancer to be migrated to another CPU, every time preemption is enabled the migration task would schedule in try to migrate the task but can't because migration is enabled and let it run again. This caused the scheduler to schedule out the task every time it enabled preemption and made the loop never exit (until the 100 iteration test triggered).
Fix this by enabling and disabling preemption and keeping migration enabled on a failure. This will let the migration thread to migrate the task and the copy from user space will likely pass on the next iteration.
- Fix trace_marker copy option freeing
The "copy_trace_marker" option allows an tracing instance to get a copy of a write to the trace_marker file of the top level instance. This is managed by a link list protected by RCU. When an instance is removed, a check is made if the option is set, and if so synchronized_rcu() is called. The problem is that an iteration is made to reset all the flags to what they were when the instance was created (to perform clean ups) was done before the check of the copy_trace_marker option and that option was cleared, so the synchronize_rcu() was never called.
Move the clearing of all the flags after the check of copy_trace_marker to do synchronize_rcu() so that the option is still set if it was before and the synchronization is performed.
- Fix entries setting when validating the persistent ring buffer
When validating the persistent ring buffer on boot up, the number of events per sub-buffer is added to the sub-buffer meta page. The validator was updating cpu_buffer->head_page (the first sub-buffer of the per-cpu buffer) and not the "head_page" variable that was iterating the sub-buffers. This was causing the first sub-buffer to be assigned the entries for each sub-buffer and not the sub-buffer that was supposed to be updated.
- Use "hash" value to update the direct callers
When updating the ftrace direct direct callers it assigned a temporary callback to all the callback functions of the ftrace ops and not just the functions represented by the passed in hash. This causes an unnecessary slow down of the functions of the ftrace_ops that is not being modified. Only update the functions that are going to be modified to call the ftrace loop function so that the update can be made on those functions.
git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git
trace/fixes
Head SHA1: 50b35c9e50a865600344ab1d8f9a8b3384d7e63d
Jiri Olsa (1):
ftrace: Use hash argument for tmp_ops in update_ftrace_direct_mod
Masami Hiramatsu (Google) (1):
ring-buffer: Fix to update per-subbuf entries of persistent ring buffer
Steven Rostedt (2):
tracing: Fix failure to read user space from system call trace events
tracing: Fix trace_marker copy link list updates
Xuewen Yan (1):
tracing: Revert "tracing: Remove pid in task_rename tracing output"
----
include/trace/events/task.h | 7 +++++-- kernel/trace/ftrace.c | 4 ++-- kernel/trace/ring_buffer.c | 2 +- kernel/trace/trace.c | 36 +++++++++++++++++++++++++++--------- 4 files changed, 35 insertions(+), 14 deletions(-)
PATCH 1/5 tracing: Revert "tracing: Remove pid in task_rename tracing output"
From: Xuewen Yan <[email protected]>
This reverts commit e3f6a42272e028c46695acc83fc7d7c42f2750ad.
The commit says that the tracepoint only deals with the current task, however the following case is not current task:
comm_write() {
p = get_proc_task(inode);
if (!p)
return -ESRCH;
if (same_thread_group(current, p))
set_task_comm(p, buffer);
}where set_task_comm() calls __set_task_comm() which records the update of p and not current.
So revert the patch to show pid.
--- include/trace/events/task.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
include/trace/events/task.h +5 -2
@@ -38,19 +38,22 @@ TRACE_EVENT(task_rename, TP_ARGS(task, comm),TP_STRUCT__entry ( +__field (pid_t ,pid )__array ( char, oldcomm,TASK_COMM_LEN )__array ( char, newcomm,TASK_COMM_LEN )__field ( short, oom_score_adj) ),TP_fast_assign ( + __entry->pid = task->pid;memcpy (entry->oldcomm, task->comm,TASK_COMM_LEN );strscpy (entry->newcomm, comm,TASK_COMM_LEN ); __entry->oom_score_adj = task->signal->oom_score_adj; ), -TP_printk ("oldcomm=%s newcomm=%s oom_score_adj=%hd" , - __entry->oldcomm, __entry->newcomm, __entry->oom_score_adj) +TP_printk ("pid=%d oldcomm=%s newcomm=%s oom_score_adj=%hd" , + __entry->pid, __entry->oldcomm, + __entry->newcomm, __entry->oom_score_adj) );/**
Signature · 1 line
2.51.0
PATCH 2/5 tracing: Fix failure to read user space from system call trace events
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
The system call trace events call trace_user_fault_read() to read the user space part of some system calls. This is done by grabbing a per-cpu buffer, disabling migration, enabling preemption, calling copy_from_user(), disabling preemption, enabling migration and checking if the task was preempted while preemption was enabled. If it was, the buffer is considered corrupted and it tries again.
There's a safety mechanism that will fail out of this loop if it fails 100 times (with a warning). That warning message was triggered in some pi_futex stress tests. Enabling the sched_switch trace event and traceoff_on_warning, showed the problem:
pi_mutex_hammer-1375 [006] d..21 138.981648: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981651: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981656: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981659: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981664: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981667: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981671: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981675: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981679: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981682: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981687: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981690: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981695: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981698: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981703: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981706: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981711: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981714: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981719: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981722: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981727: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981730: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95
pi_mutex_hammer-1375 [006] d..21 138.981735: sched_switch: prev_comm=pi_mutex_hammer prev_pid=1375 prev_prio=95 prev_state=R+ ==> next_comm=migration/6 next_pid=47 next_prio=0
migration/6-47 [006] d..2. 138.981738: sched_switch: prev_comm=migration/6 prev_pid=47 prev_prio=0 prev_state=S ==> next_comm=pi_mutex_hammer next_pid=1375 next_prio=95What happened was the task 1375 was flagged to be migrated. When preemption was enabled, the migration thread woke up to migrate that task, but failed because migration for that task was disabled. This caused the loop to fail to exit because the task scheduled out while trying to read user space.
Every time the task enabled preemption the migration thread would schedule in, try to migrate the task, fail and let the task continue. But because the loop would only enable preemption with migration disabled, it would always fail because each time it enabled preemption to read user space, the migration thread would try to migrate it.
To solve this, when the loop fails to read user space without being scheduled out, enabled and disable preemption with migration enabled. This will allow the migration task to successfully migrate the task and the next loop should succeed to read user space without being scheduled out.
--- kernel/trace/trace.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
kernel/trace/trace.c +17 -0
@@ -6783,6 +6783,23 @@ char *trace_user_fault_read(struct trace_user_buf_info *tinfo,*/ do { + /* + * It is possible that something is trying to migrate this + * task. What happens then, is when preemption is enabled, + * the migration thread will preempt this task, try to + * migrate it, fail, then let it run again. That will + * cause this to loop again and never succeed. + * On failures, enabled and disable preemption with + * migration enabled, to allow the migration thread to + * migrate this task. + */ + if (trys) { + preempt_enable_notrace(); + preempt_disable_notrace(); + cpu = smp_processor_id(); + buffer = per_cpu_ptr(tinfo->tbuf, cpu)->buf; + } + /* * If for some reason, copy_from_user() always causes a context * switch, this would then cause an infinite loop.
Signature · 1 line
2.51.0
PATCH 3/5 tracing: Fix trace_marker copy link list updates
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
When the "copy_trace_marker" option is enabled for an instance, anything written into /sys/kernel/tracing/trace_marker is also copied into that instances buffer. When the option is set, that instance's trace_array descriptor is added to the marker_copies link list. This list is protected by RCU, as all iterations uses an RCU protected list traversal.
When the instance is deleted, all the flags that were enabled are cleared. This also clears the copy_trace_marker flag and removes the trace_array descriptor from the list.
The issue is after the flags are called, a direct call to update_marker_trace() is performed to clear the flag. This function returns true if the state of the flag changed and false otherwise. If it returns true here, synchronize_rcu() is called to make sure all readers see that its removed from the list.
But since the flag was already cleared, the state does not change and the synchronization is never called, leaving a possible UAF bug.
Move the clearing of all flags below the updating of the copy_trace_marker option which then makes sure the synchronization is performed.
Also use the flag for checking the state in update_marker_trace() instead of looking at if the list is empty.
--- kernel/trace/trace.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-)
kernel/trace/trace.c +10 -9
@@ -555,7 +555,7 @@ static bool update_marker_trace(struct trace_array *tr, int enabled)lockdep_assert_held (&event_mutex);if (enabled) { -if (!list_empty (&tr->marker_list)) +if (tr->trace_flags &TRACE_ITER (COPY_MARKER ))return false;list_add_rcu (&tr->marker_list, &marker_copies);
@@ -563,10 +563,10 @@ static bool update_marker_trace(struct trace_array *tr, int enabled)return true; } -if (list_empty (&tr->marker_list)) +if (!(tr->trace_flags &TRACE_ITER (COPY_MARKER )))return false; -list_del_init (&tr->marker_list); +list_del_rcu (&tr->marker_list); tr->trace_flags &= ~TRACE_ITER (COPY_MARKER );return true; }
@@ -9761,18 +9761,19 @@ static int __remove_instance(struct trace_array *tr)list_del (&tr->list); -/* Disable all the flags that were enabled coming in */ -for (i =0 ; i <TRACE_FLAGS_MAX_SIZE ; i++) { -if ((1ULL << i) &ZEROED_TRACE_FLAGS ) -set_tracer_flag (tr,1ULL << i,0 ); - } -if (printk_trace == tr)update_printk_trace (&global_trace); +/* Must be done before disabling all the flags */ if (update_marker_trace (tr,0 ))synchronize_rcu (); +/* Disable all the flags that were enabled coming in */ +for (i =0 ; i <TRACE_FLAGS_MAX_SIZE ; i++) { +if ((1ULL << i) &ZEROED_TRACE_FLAGS ) +set_tracer_flag (tr,1ULL << i,0 ); + } +tracing_set_nop (tr);clear_ftrace_function_probes (tr);event_trace_del_tracer (tr);
Signature · 1 line
2.51.0
PATCH 4/5 ring-buffer: Fix to update per-subbuf entries of persistent ring buffer
In reply to Steven RostedtFrom: "Masami Hiramatsu (Google)" <[email protected]>
Since the validation loop in rb_meta_validate_events() updates the same cpu_buffer->head_page->entries, the other subbuf entries are not updated. Fix to use head_page to update the entries field, since it is the cursor in this loop.
--- kernel/trace/ring_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ring_buffer.c +1 -1
@@ -2053,7 +2053,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) entries += ret; entry_bytes +=local_read (&head_page->page->commit); -local_set (&cpu_buffer->head_page->entries, ret); +local_set (&head_page->entries, ret);if (head_page == cpu_buffer->commit_page)break ;
Signature · 1 line
2.51.0
PATCH 5/5 ftrace: Use hash argument for tmp_ops in update_ftrace_direct_mod
In reply to Steven RostedtFrom: Jiri Olsa <[email protected]>
The modify logic registers temporary ftrace_ops object (tmp_ops) to trigger the slow path for all direct callers to be able to safely modify attached addresses.
At the moment we use ops->func_hash for tmp_ops filter, which represents all the systems attachments. It's faster to use just the passed hash filter, which contains only the modified sites and is always a subset of the ops->func_hash.
--- kernel/trace/ftrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/trace/ftrace.c +2 -2
@@ -6606,9 +6606,9 @@ int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, bif (!orig_hash) goto unlock; -/* Enable the tmp_ops to have the same functions as the direct ops */ +/* Enable the tmp_ops to have the same functions as the hash object. */ ftrace_ops_init (&tmp_ops); - tmp_ops.func_hash = ops->func_hash; + tmp_ops.func_hash->filter_hash = hash; err =register_ftrace_function_nolock (&tmp_ops);if (err)
Signature · 1 line
2.51.0