tracing: Make the backup instance non-reusable

Since there is no reason to reuse the backup instance, make it readonly
(but erasable).  Note that only backup instances are readonly, because
other trace instances will be empty unless it is writable.  Only backup
instances have copy entries from the original.

With this change, most of the trace control files are removed from the
backup instance, including eventfs enable/filter etc.

 # find /sys/kernel/tracing/instances/backup/events/ | wc -l
 4093
 # find /sys/kernel/tracing/instances/boot_map/events/ | wc -l
 9573

Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Link: https://patch.msgid.link/177502546939.1311542.1826814401724828930.stgit@mhiramat.tok.corp.google.com
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
This commit is contained in:
Masami Hiramatsu (Google) 2026-04-01 15:37:49 +09:00 committed by Steven Rostedt (Google)
commit 2c79da099a
4 changed files with 104 additions and 65 deletions

View file

@ -4022,6 +4022,11 @@ int tracing_open_generic_tr(struct inode *inode, struct file *filp)
if (ret)
return ret;
if ((filp->f_mode & FMODE_WRITE) && trace_array_is_readonly(tr)) {
trace_array_put(tr);
return -EACCES;
}
filp->private_data = inode->i_private;
return 0;
@ -7097,6 +7102,11 @@ static int tracing_clock_open(struct inode *inode, struct file *file)
if (ret)
return ret;
if ((file->f_mode & FMODE_WRITE) && trace_array_is_readonly(tr)) {
trace_array_put(tr);
return -EACCES;
}
ret = single_open(file, tracing_clock_show, inode->i_private);
if (ret < 0)
trace_array_put(tr);
@ -9866,17 +9876,22 @@ static __init void create_trace_instances(struct dentry *d_tracer)
static void
init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
{
umode_t writable_mode = TRACE_MODE_WRITE;
int cpu;
if (trace_array_is_readonly(tr))
writable_mode = TRACE_MODE_READ;
trace_create_file("available_tracers", TRACE_MODE_READ, d_tracer,
tr, &show_traces_fops);
tr, &show_traces_fops);
trace_create_file("current_tracer", TRACE_MODE_WRITE, d_tracer,
tr, &set_tracer_fops);
trace_create_file("current_tracer", writable_mode, d_tracer,
tr, &set_tracer_fops);
trace_create_file("tracing_cpumask", TRACE_MODE_WRITE, d_tracer,
trace_create_file("tracing_cpumask", writable_mode, d_tracer,
tr, &tracing_cpumask_fops);
/* Options are used for changing print-format even for readonly instance. */
trace_create_file("trace_options", TRACE_MODE_WRITE, d_tracer,
tr, &tracing_iter_fops);
@ -9886,12 +9901,36 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
trace_create_file("trace_pipe", TRACE_MODE_READ, d_tracer,
tr, &tracing_pipe_fops);
trace_create_file("buffer_size_kb", TRACE_MODE_WRITE, d_tracer,
trace_create_file("buffer_size_kb", writable_mode, d_tracer,
tr, &tracing_entries_fops);
trace_create_file("buffer_total_size_kb", TRACE_MODE_READ, d_tracer,
tr, &tracing_total_entries_fops);
trace_create_file("trace_clock", writable_mode, d_tracer, tr,
&trace_clock_fops);
trace_create_file("timestamp_mode", TRACE_MODE_READ, d_tracer, tr,
&trace_time_stamp_mode_fops);
tr->buffer_percent = 50;
trace_create_file("buffer_subbuf_size_kb", writable_mode, d_tracer,
tr, &buffer_subbuf_size_fops);
create_trace_options_dir(tr);
if (tr->range_addr_start)
trace_create_file("last_boot_info", TRACE_MODE_READ, d_tracer,
tr, &last_boot_fops);
for_each_tracing_cpu(cpu)
tracing_init_tracefs_percpu(tr, cpu);
/* Read-only instance has above files only. */
if (trace_array_is_readonly(tr))
return;
trace_create_file("free_buffer", 0200, d_tracer,
tr, &tracing_free_buffer_fops);
@ -9903,49 +9942,29 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
trace_create_file("trace_marker_raw", 0220, d_tracer,
tr, &tracing_mark_raw_fops);
trace_create_file("trace_clock", TRACE_MODE_WRITE, d_tracer, tr,
&trace_clock_fops);
trace_create_file("buffer_percent", TRACE_MODE_WRITE, d_tracer,
tr, &buffer_percent_fops);
trace_create_file("syscall_user_buf_size", TRACE_MODE_WRITE, d_tracer,
tr, &tracing_syscall_buf_fops);
trace_create_file("tracing_on", TRACE_MODE_WRITE, d_tracer,
tr, &rb_simple_fops);
trace_create_file("timestamp_mode", TRACE_MODE_READ, d_tracer, tr,
&trace_time_stamp_mode_fops);
tr->buffer_percent = 50;
trace_create_file("buffer_percent", TRACE_MODE_WRITE, d_tracer,
tr, &buffer_percent_fops);
trace_create_file("buffer_subbuf_size_kb", TRACE_MODE_WRITE, d_tracer,
tr, &buffer_subbuf_size_fops);
trace_create_file("syscall_user_buf_size", TRACE_MODE_WRITE, d_tracer,
tr, &tracing_syscall_buf_fops);
create_trace_options_dir(tr);
trace_create_maxlat_file(tr, d_tracer);
if (ftrace_create_function_files(tr, d_tracer))
MEM_FAIL(1, "Could not allocate function filter files");
if (tr->range_addr_start) {
trace_create_file("last_boot_info", TRACE_MODE_READ, d_tracer,
tr, &last_boot_fops);
#ifdef CONFIG_TRACER_SNAPSHOT
} else {
if (!tr->range_addr_start)
trace_create_file("snapshot", TRACE_MODE_WRITE, d_tracer,
tr, &snapshot_fops);
#endif
}
trace_create_file("error_log", TRACE_MODE_WRITE, d_tracer,
tr, &tracing_err_log_fops);
for_each_tracing_cpu(cpu)
tracing_init_tracefs_percpu(tr, cpu);
ftrace_init_tracefs(tr, d_tracer);
}
@ -10772,7 +10791,7 @@ __init static void enable_instances(void)
* Backup buffers can be freed but need vfree().
*/
if (backup)
tr->flags |= TRACE_ARRAY_FL_VMALLOC;
tr->flags |= TRACE_ARRAY_FL_VMALLOC | TRACE_ARRAY_FL_RDONLY;
if (start || backup) {
tr->flags |= TRACE_ARRAY_FL_BOOT | TRACE_ARRAY_FL_LAST_BOOT;

View file

@ -462,6 +462,7 @@ enum {
TRACE_ARRAY_FL_MOD_INIT = BIT(3),
TRACE_ARRAY_FL_MEMMAP = BIT(4),
TRACE_ARRAY_FL_VMALLOC = BIT(5),
TRACE_ARRAY_FL_RDONLY = BIT(6),
};
#ifdef CONFIG_MODULES
@ -491,6 +492,12 @@ extern unsigned long trace_adjust_address(struct trace_array *tr, unsigned long
extern struct trace_array *printk_trace;
static inline bool trace_array_is_readonly(struct trace_array *tr)
{
/* backup instance is read only. */
return tr->flags & TRACE_ARRAY_FL_RDONLY;
}
/*
* The global tracer (top) should be the first trace array added,
* but we check the flag anyway.

View file

@ -61,7 +61,8 @@ trace_boot_set_instance_options(struct trace_array *tr, struct xbc_node *node)
v = memparse(p, NULL);
if (v < PAGE_SIZE)
pr_err("Buffer size is too small: %s\n", p);
if (tracing_resize_ring_buffer(tr, v, RING_BUFFER_ALL_CPUS) < 0)
if (trace_array_is_readonly(tr) ||
tracing_resize_ring_buffer(tr, v, RING_BUFFER_ALL_CPUS) < 0)
pr_err("Failed to resize trace buffer to %s\n", p);
}
@ -597,7 +598,7 @@ trace_boot_enable_tracer(struct trace_array *tr, struct xbc_node *node)
p = xbc_node_find_value(node, "tracer", NULL);
if (p && *p != '\0') {
if (tracing_set_tracer(tr, p) < 0)
if (trace_array_is_readonly(tr) || tracing_set_tracer(tr, p) < 0)
pr_err("Failed to set given tracer: %s\n", p);
}

View file

@ -1401,6 +1401,9 @@ static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
{
int ret;
if (trace_array_is_readonly(tr))
return -EACCES;
mutex_lock(&event_mutex);
ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set, mod);
mutex_unlock(&event_mutex);
@ -2973,8 +2976,8 @@ event_subsystem_dir(struct trace_array *tr, const char *name,
} else
__get_system(system);
/* ftrace only has directories no files */
if (strcmp(name, "ftrace") == 0)
/* ftrace only has directories no files, readonly instance too. */
if (strcmp(name, "ftrace") == 0 || trace_array_is_readonly(tr))
nr_entries = 0;
else
nr_entries = ARRAY_SIZE(system_entries);
@ -3138,6 +3141,18 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
int nr_entries;
int ret;
static struct eventfs_entry event_entries[] = {
{
.name = "format",
.callback = event_callback,
},
#ifdef CONFIG_PERF_EVENTS
{
.name = "id",
.callback = event_callback,
},
#endif
#define NR_RO_EVENT_ENTRIES (1 + IS_ENABLED(CONFIG_PERF_EVENTS))
/* Readonly files must be above this line and counted by NR_RO_EVENT_ENTRIES. */
{
.name = "enable",
.callback = event_callback,
@ -3151,16 +3166,6 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
.name = "trigger",
.callback = event_callback,
},
{
.name = "format",
.callback = event_callback,
},
#ifdef CONFIG_PERF_EVENTS
{
.name = "id",
.callback = event_callback,
},
#endif
#ifdef CONFIG_HIST_TRIGGERS
{
.name = "hist",
@ -3193,7 +3198,10 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
if (!e_events)
return -ENOMEM;
nr_entries = ARRAY_SIZE(event_entries);
if (trace_array_is_readonly(tr))
nr_entries = NR_RO_EVENT_ENTRIES;
else
nr_entries = ARRAY_SIZE(event_entries);
name = trace_event_name(call);
ei = eventfs_create_dir(name, e_events, event_entries, nr_entries, file);
@ -4535,10 +4543,6 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
struct dentry *entry;
int nr_entries;
static struct eventfs_entry events_entries[] = {
{
.name = "enable",
.callback = events_callback,
},
{
.name = "header_page",
.callback = events_callback,
@ -4547,20 +4551,37 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
.name = "header_event",
.callback = events_callback,
},
#define NR_RO_TOP_ENTRIES 2
/* Readonly files must be above this line and counted by NR_RO_TOP_ENTRIES. */
{
.name = "enable",
.callback = events_callback,
},
};
entry = trace_create_file("set_event", TRACE_MODE_WRITE, parent,
tr, &ftrace_set_event_fops);
if (!entry)
return -ENOMEM;
if (!trace_array_is_readonly(tr)) {
entry = trace_create_file("set_event", TRACE_MODE_WRITE, parent,
tr, &ftrace_set_event_fops);
if (!entry)
return -ENOMEM;
trace_create_file("show_event_filters", TRACE_MODE_READ, parent, tr,
&ftrace_show_event_filters_fops);
/* There are not as crucial, just warn if they are not created */
trace_create_file("show_event_filters", TRACE_MODE_READ, parent, tr,
&ftrace_show_event_filters_fops);
trace_create_file("show_event_triggers", TRACE_MODE_READ, parent, tr,
&ftrace_show_event_triggers_fops);
trace_create_file("show_event_triggers", TRACE_MODE_READ, parent, tr,
&ftrace_show_event_triggers_fops);
nr_entries = ARRAY_SIZE(events_entries);
trace_create_file("set_event_pid", TRACE_MODE_WRITE, parent,
tr, &ftrace_set_event_pid_fops);
trace_create_file("set_event_notrace_pid",
TRACE_MODE_WRITE, parent, tr,
&ftrace_set_event_notrace_pid_fops);
nr_entries = ARRAY_SIZE(events_entries);
} else {
nr_entries = NR_RO_TOP_ENTRIES;
}
e_events = eventfs_create_events_dir("events", parent, events_entries,
nr_entries, tr);
@ -4569,15 +4590,6 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
return -ENOMEM;
}
/* There are not as crucial, just warn if they are not created */
trace_create_file("set_event_pid", TRACE_MODE_WRITE, parent,
tr, &ftrace_set_event_pid_fops);
trace_create_file("set_event_notrace_pid",
TRACE_MODE_WRITE, parent, tr,
&ftrace_set_event_notrace_pid_fops);
tr->event_dir = e_events;
return 0;