Skip to content

Commit 4f9e7fa

Browse files
committed
Merge tag 'trace-v6.5-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull tracing fixes from Steven Rostedt: - Fix ring buffer being permanently disabled due to missed record_disabled() Changing the trace cpu mask will disable the ring buffers for the CPUs no longer in the mask. But it fails to update the snapshot buffer. If a snapshot takes place, the accounting for the ring buffer being disabled is corrupted and this can lead to the ring buffer being permanently disabled. - Add test case for snapshot and cpu mask working together - Fix memleak by the function graph tracer not getting closed properly. The iterator is used to read the ring buffer. When it opens, it calls the open function of a tracer, and when it is closed, it calls the close iteration. While a trace is being read, it is still possible to change the tracer. If this happens between the function graph tracer and the wakeup tracer (which uses function graph tracing), the tracers are not closed properly during when the iterator sees the switch, and the wakeup function did not initialize its private pointer to NULL, which is used to know if the function graph tracer was the last tracer. It could be fooled in thinking it is, but then on exit it does not call the close function of the function graph tracer to clean up its data. - Fix synthetic events on big endian machines, by introducing a union that does the conversions properly. - Fix synthetic events from printing out the number of elements in the stacktrace when it shouldn't. - Fix synthetic events stacktrace to not print a bogus value at the end. - Introduce a pipe_cpumask that prevents the trace_pipe files from being opened by more than one task (file descriptor). There was a race found where if splice is called, the iter->ent could become stale and events could be missed. There's no point reading a producer/consumer file by more than one task as they will corrupt each other anyway. Add a cpumask that keeps track of the per_cpu trace_pipe files as well as the global trace_pipe file that prevents more than one open of a trace_pipe file that represents the same ring buffer. This prevents the race from happening. - Fix ftrace samples for arm64 to work with older compilers. * tag 'trace-v6.5-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: samples: ftrace: Replace bti assembly with hint for older compiler tracing: Introduce pipe_cpumask to avoid race on trace_pipes tracing: Fix memleak due to race between current_tracer and trace tracing/synthetic: Allocate one additional element for size tracing/synthetic: Skip first entry for stack traces tracing/synthetic: Use union instead of casts selftests/ftrace: Add a basic testcase for snapshot tracing: Fix cpu buffers unavailable due to 'record_disabled' missed
2 parents 14ddccc + e332938 commit 4f9e7fa

File tree

12 files changed

+166
-78
lines changed

12 files changed

+166
-78
lines changed

include/linux/trace_events.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ int trace_raw_output_prep(struct trace_iterator *iter,
5959
extern __printf(2, 3)
6060
void trace_event_printf(struct trace_iterator *iter, const char *fmt, ...);
6161

62+
/* Used to find the offset and length of dynamic fields in trace events */
63+
struct trace_dynamic_info {
64+
#ifdef CONFIG_CPU_BIG_ENDIAN
65+
u16 offset;
66+
u16 len;
67+
#else
68+
u16 len;
69+
u16 offset;
70+
#endif
71+
};
72+
6273
/*
6374
* The trace entry - the most basic unit of tracing. This is what
6475
* is printed in the end as a single line in the trace output, such as:

kernel/trace/trace.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4213,8 +4213,15 @@ static void *s_start(struct seq_file *m, loff_t *pos)
42134213
* will point to the same string as current_trace->name.
42144214
*/
42154215
mutex_lock(&trace_types_lock);
4216-
if (unlikely(tr->current_trace && iter->trace->name != tr->current_trace->name))
4216+
if (unlikely(tr->current_trace && iter->trace->name != tr->current_trace->name)) {
4217+
/* Close iter->trace before switching to the new current tracer */
4218+
if (iter->trace->close)
4219+
iter->trace->close(iter);
42174220
*iter->trace = *tr->current_trace;
4221+
/* Reopen the new current tracer */
4222+
if (iter->trace->open)
4223+
iter->trace->open(iter);
4224+
}
42184225
mutex_unlock(&trace_types_lock);
42194226

42204227
#ifdef CONFIG_TRACER_MAX_TRACE
@@ -5277,11 +5284,17 @@ int tracing_set_cpumask(struct trace_array *tr,
52775284
!cpumask_test_cpu(cpu, tracing_cpumask_new)) {
52785285
atomic_inc(&per_cpu_ptr(tr->array_buffer.data, cpu)->disabled);
52795286
ring_buffer_record_disable_cpu(tr->array_buffer.buffer, cpu);
5287+
#ifdef CONFIG_TRACER_MAX_TRACE
5288+
ring_buffer_record_disable_cpu(tr->max_buffer.buffer, cpu);
5289+
#endif
52805290
}
52815291
if (!cpumask_test_cpu(cpu, tr->tracing_cpumask) &&
52825292
cpumask_test_cpu(cpu, tracing_cpumask_new)) {
52835293
atomic_dec(&per_cpu_ptr(tr->array_buffer.data, cpu)->disabled);
52845294
ring_buffer_record_enable_cpu(tr->array_buffer.buffer, cpu);
5295+
#ifdef CONFIG_TRACER_MAX_TRACE
5296+
ring_buffer_record_enable_cpu(tr->max_buffer.buffer, cpu);
5297+
#endif
52855298
}
52865299
}
52875300
arch_spin_unlock(&tr->max_lock);
@@ -6705,24 +6718,53 @@ tracing_max_lat_write(struct file *filp, const char __user *ubuf,
67056718

67066719
#endif
67076720

6721+
static int open_pipe_on_cpu(struct trace_array *tr, int cpu)
6722+
{
6723+
if (cpu == RING_BUFFER_ALL_CPUS) {
6724+
if (cpumask_empty(tr->pipe_cpumask)) {
6725+
cpumask_setall(tr->pipe_cpumask);
6726+
return 0;
6727+
}
6728+
} else if (!cpumask_test_cpu(cpu, tr->pipe_cpumask)) {
6729+
cpumask_set_cpu(cpu, tr->pipe_cpumask);
6730+
return 0;
6731+
}
6732+
return -EBUSY;
6733+
}
6734+
6735+
static void close_pipe_on_cpu(struct trace_array *tr, int cpu)
6736+
{
6737+
if (cpu == RING_BUFFER_ALL_CPUS) {
6738+
WARN_ON(!cpumask_full(tr->pipe_cpumask));
6739+
cpumask_clear(tr->pipe_cpumask);
6740+
} else {
6741+
WARN_ON(!cpumask_test_cpu(cpu, tr->pipe_cpumask));
6742+
cpumask_clear_cpu(cpu, tr->pipe_cpumask);
6743+
}
6744+
}
6745+
67086746
static int tracing_open_pipe(struct inode *inode, struct file *filp)
67096747
{
67106748
struct trace_array *tr = inode->i_private;
67116749
struct trace_iterator *iter;
6750+
int cpu;
67126751
int ret;
67136752

67146753
ret = tracing_check_open_get_tr(tr);
67156754
if (ret)
67166755
return ret;
67176756

67186757
mutex_lock(&trace_types_lock);
6758+
cpu = tracing_get_cpu(inode);
6759+
ret = open_pipe_on_cpu(tr, cpu);
6760+
if (ret)
6761+
goto fail_pipe_on_cpu;
67196762

67206763
/* create a buffer to store the information to pass to userspace */
67216764
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
67226765
if (!iter) {
67236766
ret = -ENOMEM;
6724-
__trace_array_put(tr);
6725-
goto out;
6767+
goto fail_alloc_iter;
67266768
}
67276769

67286770
trace_seq_init(&iter->seq);
@@ -6745,7 +6787,7 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
67456787

67466788
iter->tr = tr;
67476789
iter->array_buffer = &tr->array_buffer;
6748-
iter->cpu_file = tracing_get_cpu(inode);
6790+
iter->cpu_file = cpu;
67496791
mutex_init(&iter->mutex);
67506792
filp->private_data = iter;
67516793

@@ -6755,12 +6797,15 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
67556797
nonseekable_open(inode, filp);
67566798

67576799
tr->trace_ref++;
6758-
out:
6800+
67596801
mutex_unlock(&trace_types_lock);
67606802
return ret;
67616803

67626804
fail:
67636805
kfree(iter);
6806+
fail_alloc_iter:
6807+
close_pipe_on_cpu(tr, cpu);
6808+
fail_pipe_on_cpu:
67646809
__trace_array_put(tr);
67656810
mutex_unlock(&trace_types_lock);
67666811
return ret;
@@ -6777,7 +6822,7 @@ static int tracing_release_pipe(struct inode *inode, struct file *file)
67776822

67786823
if (iter->trace->pipe_close)
67796824
iter->trace->pipe_close(iter);
6780-
6825+
close_pipe_on_cpu(tr, iter->cpu_file);
67816826
mutex_unlock(&trace_types_lock);
67826827

67836828
free_cpumask_var(iter->started);
@@ -9441,6 +9486,9 @@ static struct trace_array *trace_array_create(const char *name)
94419486
if (!alloc_cpumask_var(&tr->tracing_cpumask, GFP_KERNEL))
94429487
goto out_free_tr;
94439488

9489+
if (!alloc_cpumask_var(&tr->pipe_cpumask, GFP_KERNEL))
9490+
goto out_free_tr;
9491+
94449492
tr->trace_flags = global_trace.trace_flags & ~ZEROED_TRACE_FLAGS;
94459493

94469494
cpumask_copy(tr->tracing_cpumask, cpu_all_mask);
@@ -9482,6 +9530,7 @@ static struct trace_array *trace_array_create(const char *name)
94829530
out_free_tr:
94839531
ftrace_free_ftrace_ops(tr);
94849532
free_trace_buffers(tr);
9533+
free_cpumask_var(tr->pipe_cpumask);
94859534
free_cpumask_var(tr->tracing_cpumask);
94869535
kfree(tr->name);
94879536
kfree(tr);
@@ -9584,6 +9633,7 @@ static int __remove_instance(struct trace_array *tr)
95849633
}
95859634
kfree(tr->topts);
95869635

9636+
free_cpumask_var(tr->pipe_cpumask);
95879637
free_cpumask_var(tr->tracing_cpumask);
95889638
kfree(tr->name);
95899639
kfree(tr);
@@ -10381,12 +10431,14 @@ __init static int tracer_alloc_buffers(void)
1038110431
if (trace_create_savedcmd() < 0)
1038210432
goto out_free_temp_buffer;
1038310433

10434+
if (!alloc_cpumask_var(&global_trace.pipe_cpumask, GFP_KERNEL))
10435+
goto out_free_savedcmd;
10436+
1038410437
/* TODO: make the number of buffers hot pluggable with CPUS */
1038510438
if (allocate_trace_buffers(&global_trace, ring_buf_size) < 0) {
1038610439
MEM_FAIL(1, "tracer: failed to allocate ring buffer!\n");
10387-
goto out_free_savedcmd;
10440+
goto out_free_pipe_cpumask;
1038810441
}
10389-
1039010442
if (global_trace.buffer_disabled)
1039110443
tracing_off();
1039210444

@@ -10439,6 +10491,8 @@ __init static int tracer_alloc_buffers(void)
1043910491

1044010492
return 0;
1044110493

10494+
out_free_pipe_cpumask:
10495+
free_cpumask_var(global_trace.pipe_cpumask);
1044210496
out_free_savedcmd:
1044310497
free_saved_cmdlines_buffer(savedcmd);
1044410498
out_free_temp_buffer:

kernel/trace/trace.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ struct trace_array {
377377
struct list_head events;
378378
struct trace_event_file *trace_marker_file;
379379
cpumask_var_t tracing_cpumask; /* only trace on set CPUs */
380+
/* one per_cpu trace_pipe can be opened by only one user */
381+
cpumask_var_t pipe_cpumask;
380382
int ref;
381383
int trace_ref;
382384
#ifdef CONFIG_FUNCTION_TRACER
@@ -1295,6 +1297,14 @@ static inline void trace_branch_disable(void)
12951297
/* set ring buffers to default size if not already done so */
12961298
int tracing_update_buffers(void);
12971299

1300+
union trace_synth_field {
1301+
u8 as_u8;
1302+
u16 as_u16;
1303+
u32 as_u32;
1304+
u64 as_u64;
1305+
struct trace_dynamic_info as_dynamic;
1306+
};
1307+
12981308
struct ftrace_event_field {
12991309
struct list_head link;
13001310
const char *name;

0 commit comments

Comments
 (0)