On Mon, Sep 21, 2026 at 07:30:46PM +0800, Xiang Gao wrote:
> Provide a per-CPU view of the tracing ring-buffer memory usage under
> trace_stats/per_cpu/cpuN/memory_usage_kb, using the same format as the
> aggregate file. Each file reports only the buffers belonging to that CPU
> across the global trace array and all tracing instances.
>
> Signed-off-by: Xiang Gao <[email protected]>
> ---
> Documentation/trace/ftrace.rst | 3 ++
> kernel/trace/trace.c | 67 +++++++++++++++++++++++++++++++++-
> 2 files changed, 69 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst
> index 99ddfe26b7cd..e53cdd8d6702 100644
> --- a/Documentation/trace/ftrace.rst
> +++ b/Documentation/trace/ftrace.rst
> @@ -230,6 +230,9 @@ of ftrace. Here is a list of some of the key files:
> buffers: ...
> snapshot_buffers: ...
>
> + The same file is provided under trace_stats/per_cpu/cpuN/, reporting
> + only the buffers belonging to that CPU.
> +
> buffer_subbuf_size_kb:
Could you split the documentation part into a different commit?
>
> This sets or displays the sub buffer size. The ring buffer is broken up
> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index d4a913ff8a69..4010f28f3ebf 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -5844,6 +5844,52 @@ static const struct file_operations trace_mem_fops = {
> .release = single_release,
> };
>
> +static struct trace_mem_stats trace_buffers_memory_cpu(int cpu)
> +{
> + struct trace_mem_stats stats = {};
> + struct trace_array *tr;
> +
> + guard(mutex)(&trace_types_lock);
> +
> + list_for_each_entry(tr, &ftrace_trace_arrays, list)
> + trace_array_buffer_memory(tr, cpu, &stats.buffers,
> + &stats.snapshot);
> +
> + /* Account for the bootstrapping temp_buffer as well. */
> + if (temp_buffer)
> + stats.buffers += ring_buffer_memory_size(temp_buffer, cpu);
> +
> + return stats;
> +}
> +
> +static int trace_mem_per_cpu_show(struct seq_file *m, void *v)
> +{
> + struct trace_mem_stats stats =
> trace_buffers_memory_cpu((long)m->private);
> +
> + seq_printf(m, "buffers: %lu\n", stats.buffers >> 10);
> + seq_printf(m, "snapshot_buffers: %lu\n", stats.snapshot >> 10);
> +
> + return 0;
> +}
> +
> +static int trace_mem_per_cpu_open(struct inode *inode, struct file *file)
> +{
> + int ret;
> +
> + ret = tracing_check_open_get_tr(NULL);
> + if (ret)
> + return ret;
> +
> + return single_open(file, trace_mem_per_cpu_show, inode->i_private);
> +}
> +
> +static const struct file_operations trace_mem_per_cpu_fops = {
> + .open = trace_mem_per_cpu_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> #define LAST_BOOT_HEADER ((void *)1)
>
> static void *l_next(struct seq_file *m, void *v, loff_t *pos)
> @@ -9360,7 +9406,9 @@ static struct notifier_block trace_module_nb = {
>
> static __init void init_trace_stats_tracefs(void)
> {
> - struct dentry *stats_dir;
> + struct dentry *stats_dir, *per_cpu_dir, *cpu_dir;
> + char cpu_dir_name[30];
> + int cpu;
>
> /*
> * tracer_alloc_buffers() frees tracing_buffer_mask and temp_buffer
> @@ -9376,6 +9424,23 @@ static __init void init_trace_stats_tracefs(void)
>
> trace_create_file("memory_usage_kb", TRACE_MODE_READ, stats_dir,
> NULL, &trace_mem_fops);
> +
> + per_cpu_dir = tracefs_create_dir("per_cpu", stats_dir);
> + if (!per_cpu_dir)
> + return;
> +
> + for_each_tracing_cpu(cpu) {
> + snprintf(cpu_dir_name, 30, "cpu%d", cpu);
> + cpu_dir = tracefs_create_dir(cpu_dir_name, per_cpu_dir);
> + if (!cpu_dir) {
> + pr_warn("Could not create tracefs '%s' entry\n",
> + cpu_dir_name);
> + continue;
> + }
> +
> + trace_create_file("memory_usage_kb", TRACE_MODE_READ, cpu_dir,
> + (void *)(long)cpu, &trace_mem_per_cpu_fops);
> + }
> }
>
> static __init void tracer_init_tracefs_work_func(struct work_struct *work)
> --
> 2.34.1
>
--
Vincent