Commit 99ddf225 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'trace-v6.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull tracing fixes from Steven Rostedt:

 - Fix timerlat notification, as it was not triggering the notify to
   users when a new max latency was hit.

 - Do not trigger max latency if the tracing is off.

   When tracing is off, the ring buffer is not updated, it does not make
   sense to notify when there's a new max latency detected by the
   tracer, as why that latency happened is not available. The tracing
   logic still runs when the ring buffer is disabled, but it should not
   be triggering notifications.

 - Fix race on freeing the synthetic event "last_cmd" variable by adding
   a mutex around it.

 - Fix race between reader and writer of the ring buffer by adding
   memory barriers. When the writer is still on the reader page it must
   have its content visible on the buffer before it moves the commit
   index that the reader uses to know how much content is on the page.

 - Make get_lock_parent_ip() always inlined, as it uses _THIS_IP_ and
   _RET_IP_, which gets broken if it is not inlined.

 - Make __field(int, arr[5]) in a TRACE_EVENT() macro fail to build.

   The field formats of trace events are calculated by using
   sizeof(type) and other means by what is passed into the structure
   macros like __field(). The __field() macro is only meant for atom
   types like int, long, short, pointer, etc. It is not meant for
   arrays.

   The code will currently compile with arrays, but then the format
   produced will be inaccurate, and user space parsing tools will break.

   Two bugs have already been fixed, now add code that will make the
   kernel fail to build if another trace event includes this buggy field
   format.

 - Fix boot up snapshot code:

   Boot snapshots were triggering when not even asked for on the kernel
   command line. This was caused by two bugs:

    1) It would trigger a snapshot on any instance if one was created
       from the kernel command line.

    2) The error handling would only affect the top level instance.
       So the fact that a snapshot was done on a instance that didn't
       allocate a buffer triggered a warning written into the top level
       buffer, and worse yet, disabled the top level buffer.

 - Fix memory leak that was caused when an error was logged in a trace
   buffer instance, and then the buffer instance was removed.

   The allocated error log messages still needed to be freed.

* tag 'trace-v6.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  tracing: Free error logs of tracing instances
  tracing: Fix ftrace_boot_snapshot command line logic
  tracing: Have tracing_snapshot_instance_cond() write errors to the appropriate instance
  tracing: Error if a trace event has an array for a __field()
  tracing/osnoise: Fix notify new tracing_max_latency
  tracing/timerlat: Notify new max thread latency
  ftrace: Mark get_lock_parent_ip() __always_inline
  ring-buffer: Fix race while reader and writer are on the same page
  tracing/synthetic: Fix races on freeing last_cmd
parents 76f598ba 3357c6e4
...@@ -980,7 +980,7 @@ static inline void __ftrace_enabled_restore(int enabled) ...@@ -980,7 +980,7 @@ static inline void __ftrace_enabled_restore(int enabled)
#define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5)) #define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5))
#define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6)) #define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6))
static inline unsigned long get_lock_parent_ip(void) static __always_inline unsigned long get_lock_parent_ip(void)
{ {
unsigned long addr = CALLER_ADDR0; unsigned long addr = CALLER_ADDR0;
......
...@@ -9,17 +9,30 @@ ...@@ -9,17 +9,30 @@
#undef __entry #undef __entry
#define __entry entry #define __entry entry
/*
* Fields should never declare an array: i.e. __field(int, arr[5])
* If they do, it will cause issues in parsing and possibly corrupt the
* events. To prevent that from happening, test the sizeof() a fictitious
* type called "struct _test_no_array_##item" which will fail if "item"
* contains array elements (like "arr[5]").
*
* If you hit this, use __array(int, arr, 5) instead.
*/
#undef __field #undef __field
#define __field(type, item) #define __field(type, item) \
{ (void)sizeof(struct _test_no_array_##item *); }
#undef __field_ext #undef __field_ext
#define __field_ext(type, item, filter_type) #define __field_ext(type, item, filter_type) \
{ (void)sizeof(struct _test_no_array_##item *); }
#undef __field_struct #undef __field_struct
#define __field_struct(type, item) #define __field_struct(type, item) \
{ (void)sizeof(struct _test_no_array_##item *); }
#undef __field_struct_ext #undef __field_struct_ext
#define __field_struct_ext(type, item, filter_type) #define __field_struct_ext(type, item, filter_type) \
{ (void)sizeof(struct _test_no_array_##item *); }
#undef __array #undef __array
#define __array(type, item, len) #define __array(type, item, len)
......
...@@ -3098,6 +3098,10 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer) ...@@ -3098,6 +3098,10 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
if (RB_WARN_ON(cpu_buffer, if (RB_WARN_ON(cpu_buffer,
rb_is_reader_page(cpu_buffer->tail_page))) rb_is_reader_page(cpu_buffer->tail_page)))
return; return;
/*
* No need for a memory barrier here, as the update
* of the tail_page did it for this page.
*/
local_set(&cpu_buffer->commit_page->page->commit, local_set(&cpu_buffer->commit_page->page->commit,
rb_page_write(cpu_buffer->commit_page)); rb_page_write(cpu_buffer->commit_page));
rb_inc_page(&cpu_buffer->commit_page); rb_inc_page(&cpu_buffer->commit_page);
...@@ -3107,6 +3111,8 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer) ...@@ -3107,6 +3111,8 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
while (rb_commit_index(cpu_buffer) != while (rb_commit_index(cpu_buffer) !=
rb_page_write(cpu_buffer->commit_page)) { rb_page_write(cpu_buffer->commit_page)) {
/* Make sure the readers see the content of what is committed. */
smp_wmb();
local_set(&cpu_buffer->commit_page->page->commit, local_set(&cpu_buffer->commit_page->page->commit,
rb_page_write(cpu_buffer->commit_page)); rb_page_write(cpu_buffer->commit_page));
RB_WARN_ON(cpu_buffer, RB_WARN_ON(cpu_buffer,
...@@ -4684,7 +4690,12 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) ...@@ -4684,7 +4690,12 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
/* /*
* Make sure we see any padding after the write update * Make sure we see any padding after the write update
* (see rb_reset_tail()) * (see rb_reset_tail()).
*
* In addition, a writer may be writing on the reader page
* if the page has not been fully filled, so the read barrier
* is also needed to make sure we see the content of what is
* committed by the writer (see rb_set_commit_to_write()).
*/ */
smp_rmb(); smp_rmb();
......
...@@ -1149,22 +1149,22 @@ static void tracing_snapshot_instance_cond(struct trace_array *tr, ...@@ -1149,22 +1149,22 @@ static void tracing_snapshot_instance_cond(struct trace_array *tr,
unsigned long flags; unsigned long flags;
if (in_nmi()) { if (in_nmi()) {
internal_trace_puts("*** SNAPSHOT CALLED FROM NMI CONTEXT ***\n"); trace_array_puts(tr, "*** SNAPSHOT CALLED FROM NMI CONTEXT ***\n");
internal_trace_puts("*** snapshot is being ignored ***\n"); trace_array_puts(tr, "*** snapshot is being ignored ***\n");
return; return;
} }
if (!tr->allocated_snapshot) { if (!tr->allocated_snapshot) {
internal_trace_puts("*** SNAPSHOT NOT ALLOCATED ***\n"); trace_array_puts(tr, "*** SNAPSHOT NOT ALLOCATED ***\n");
internal_trace_puts("*** stopping trace here! ***\n"); trace_array_puts(tr, "*** stopping trace here! ***\n");
tracing_off(); tracer_tracing_off(tr);
return; return;
} }
/* Note, snapshot can not be used when the tracer uses it */ /* Note, snapshot can not be used when the tracer uses it */
if (tracer->use_max_tr) { if (tracer->use_max_tr) {
internal_trace_puts("*** LATENCY TRACER ACTIVE ***\n"); trace_array_puts(tr, "*** LATENCY TRACER ACTIVE ***\n");
internal_trace_puts("*** Can not use snapshot (sorry) ***\n"); trace_array_puts(tr, "*** Can not use snapshot (sorry) ***\n");
return; return;
} }
...@@ -9516,6 +9516,7 @@ static int __remove_instance(struct trace_array *tr) ...@@ -9516,6 +9516,7 @@ static int __remove_instance(struct trace_array *tr)
tracefs_remove(tr->dir); tracefs_remove(tr->dir);
free_percpu(tr->last_func_repeats); free_percpu(tr->last_func_repeats);
free_trace_buffers(tr); free_trace_buffers(tr);
clear_tracing_err_log(tr);
for (i = 0; i < tr->nr_topts; i++) { for (i = 0; i < tr->nr_topts; i++) {
kfree(tr->topts[i].topts); kfree(tr->topts[i].topts);
...@@ -10393,19 +10394,20 @@ __init static int tracer_alloc_buffers(void) ...@@ -10393,19 +10394,20 @@ __init static int tracer_alloc_buffers(void)
void __init ftrace_boot_snapshot(void) void __init ftrace_boot_snapshot(void)
{ {
#ifdef CONFIG_TRACER_MAX_TRACE
struct trace_array *tr; struct trace_array *tr;
if (snapshot_at_boot) { if (!snapshot_at_boot)
tracing_snapshot(); return;
internal_trace_puts("** Boot snapshot taken **\n");
}
list_for_each_entry(tr, &ftrace_trace_arrays, list) { list_for_each_entry(tr, &ftrace_trace_arrays, list) {
if (tr == &global_trace) if (!tr->allocated_snapshot)
continue; continue;
trace_array_puts(tr, "** Boot snapshot taken **\n");
tracing_snapshot_instance(tr); tracing_snapshot_instance(tr);
trace_array_puts(tr, "** Boot snapshot taken **\n");
} }
#endif
} }
void __init early_trace_init(void) void __init early_trace_init(void)
......
...@@ -44,14 +44,21 @@ enum { ERRORS }; ...@@ -44,14 +44,21 @@ enum { ERRORS };
static const char *err_text[] = { ERRORS }; static const char *err_text[] = { ERRORS };
DEFINE_MUTEX(lastcmd_mutex);
static char *last_cmd; static char *last_cmd;
static int errpos(const char *str) static int errpos(const char *str)
{ {
int ret = 0;
mutex_lock(&lastcmd_mutex);
if (!str || !last_cmd) if (!str || !last_cmd)
return 0; goto out;
return err_pos(last_cmd, str); ret = err_pos(last_cmd, str);
out:
mutex_unlock(&lastcmd_mutex);
return ret;
} }
static void last_cmd_set(const char *str) static void last_cmd_set(const char *str)
...@@ -59,18 +66,22 @@ static void last_cmd_set(const char *str) ...@@ -59,18 +66,22 @@ static void last_cmd_set(const char *str)
if (!str) if (!str)
return; return;
mutex_lock(&lastcmd_mutex);
kfree(last_cmd); kfree(last_cmd);
last_cmd = kstrdup(str, GFP_KERNEL); last_cmd = kstrdup(str, GFP_KERNEL);
mutex_unlock(&lastcmd_mutex);
} }
static void synth_err(u8 err_type, u16 err_pos) static void synth_err(u8 err_type, u16 err_pos)
{ {
mutex_lock(&lastcmd_mutex);
if (!last_cmd) if (!last_cmd)
return; goto out;
tracing_log_err(NULL, "synthetic_events", last_cmd, err_text, tracing_log_err(NULL, "synthetic_events", last_cmd, err_text,
err_type, err_pos); err_type, err_pos);
out:
mutex_unlock(&lastcmd_mutex);
} }
static int create_synth_event(const char *raw_command); static int create_synth_event(const char *raw_command);
......
...@@ -1296,7 +1296,7 @@ static void notify_new_max_latency(u64 latency) ...@@ -1296,7 +1296,7 @@ static void notify_new_max_latency(u64 latency)
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(inst, &osnoise_instances, list) { list_for_each_entry_rcu(inst, &osnoise_instances, list) {
tr = inst->tr; tr = inst->tr;
if (tr->max_latency < latency) { if (tracer_tracing_is_on(tr) && tr->max_latency < latency) {
tr->max_latency = latency; tr->max_latency = latency;
latency_fsnotify(tr); latency_fsnotify(tr);
} }
...@@ -1738,6 +1738,8 @@ static int timerlat_main(void *data) ...@@ -1738,6 +1738,8 @@ static int timerlat_main(void *data)
trace_timerlat_sample(&s); trace_timerlat_sample(&s);
notify_new_max_latency(diff);
timerlat_dump_stack(time_to_us(diff)); timerlat_dump_stack(time_to_us(diff));
tlat->tracing_thread = false; tlat->tracing_thread = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment