Linux Trace Kernel
 help / color / mirror / Atom feed
* [PATCH v2 0/2] tracing: Remove backup instance after read all
From: Masami Hiramatsu (Google) @ 2026-01-08 14:22 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
	linux-trace-kernel

Hi,

Here is the 2nd version of the series to improve backup instances of
the persistent ring buffer. The previous version is here:

https://lore.kernel.org/all/176779714767.4193242.1978666866487010024.stgit@mhiramat.tok.corp.google.com/

In this version, I updated [1/2] to use dedicated file operations
for read only instance instead of checking on each write function.
Also use dedicated entries files for eventfs to remove writable
control files from it. So it has only 'format' and 'id' files in
readonly backup instance.

Since backup instances are a kind of snapshot of the persistent
ring buffer, it should be readonly. And if it is readonly
there is no reason to keep it after reading all data via trace_pipe
because the data has been consumed.
Thus, [1/2] makes backup instances readonly (not able to write any
events, cleanup trace, change buffer size). Also, [2/2] removes the
backup instance after consuming all data via trace_pipe.
With this improvements, even if we makes a backup instance (using
the same amount of memory of the persistent ring buffer), it will
be removed after reading the data automatically.

---

Masami Hiramatsu (Google) (2):
      tracing: Make the backup instance readonly
      tracing: Add autoremove feature to the backup instance


 kernel/trace/trace.c        |  227 +++++++++++++++++++++++++++++++++++--------
 kernel/trace/trace.h        |   20 ++++
 kernel/trace/trace_boot.c   |    5 +
 kernel/trace/trace_events.c |   75 ++++++++++----
 4 files changed, 261 insertions(+), 66 deletions(-)

--
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply

* [PATCH v2 1/2] tracing: Make the backup instance readonly
From: Masami Hiramatsu (Google) @ 2026-01-08 14:23 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
	linux-trace-kernel
In-Reply-To: <176788217131.1398317.11144318616426272901.stgit@mhiramat.tok.corp.google.com>

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Since there is no reason to reuse the backup instance, make it
readonly. 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

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v2:
  - Use readonly file_operations to prohibit writing instead of
    checking flags in write() callbacks.
  - Remove writable files from eventfs.
---
 kernel/trace/trace.c        |  163 ++++++++++++++++++++++++++++++++-----------
 kernel/trace/trace.h        |   14 +++-
 kernel/trace/trace_boot.c   |    5 +
 kernel/trace/trace_events.c |   75 ++++++++++++++------
 4 files changed, 192 insertions(+), 65 deletions(-)

diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 38f7a7a55c23..1b87595413fe 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4888,6 +4888,9 @@ static int tracing_open(struct inode *inode, struct file *file)
 		int cpu = tracing_get_cpu(inode);
 		struct array_buffer *trace_buf = &tr->array_buffer;
 
+		if (trace_array_is_readonly(tr))
+			return -EPERM;
+
 #ifdef CONFIG_TRACER_MAX_TRACE
 		if (tr->current_trace->print_max)
 			trace_buf = &tr->max_buffer;
@@ -5055,6 +5058,15 @@ static const struct file_operations tracing_fops = {
 	.release	= tracing_release,
 };
 
+static const struct file_operations tracing_ro_fops = {
+	.open		= tracing_open,
+	.read		= seq_read,
+	.read_iter	= seq_read_iter,
+	.splice_read	= copy_splice_read,
+	.llseek		= tracing_lseek,
+	.release	= tracing_release,
+};
+
 static const struct file_operations show_traces_fops = {
 	.open		= show_traces_open,
 	.read		= seq_read,
@@ -5162,6 +5174,13 @@ static const struct file_operations tracing_cpumask_fops = {
 	.llseek		= generic_file_llseek,
 };
 
+static const struct file_operations tracing_cpumask_ro_fops = {
+	.open		= tracing_open_generic_tr,
+	.read		= tracing_cpumask_read,
+	.release	= tracing_release_generic_tr,
+	.llseek		= generic_file_llseek,
+};
+
 static int tracing_trace_options_show(struct seq_file *m, void *v)
 {
 	struct tracer_opt *trace_opts;
@@ -8106,6 +8125,13 @@ static const struct file_operations set_tracer_fops = {
 	.release	= tracing_release_generic_tr,
 };
 
+static const struct file_operations set_tracer_ro_fops = {
+	.open		= tracing_open_generic_tr,
+	.read		= tracing_set_trace_read,
+	.llseek		= generic_file_llseek,
+	.release	= tracing_release_generic_tr,
+};
+
 static const struct file_operations tracing_pipe_fops = {
 	.open		= tracing_open_pipe,
 	.poll		= tracing_poll_pipe,
@@ -8122,6 +8148,13 @@ static const struct file_operations tracing_entries_fops = {
 	.release	= tracing_release_generic_tr,
 };
 
+static const struct file_operations tracing_entries_ro_fops = {
+	.open		= tracing_open_generic_tr,
+	.read		= tracing_entries_read,
+	.llseek		= generic_file_llseek,
+	.release	= tracing_release_generic_tr,
+};
+
 static const struct file_operations tracing_syscall_buf_fops = {
 	.open		= tracing_open_generic_tr,
 	.read		= tracing_syscall_buf_read,
@@ -8170,6 +8203,13 @@ static const struct file_operations trace_clock_fops = {
 	.write		= tracing_clock_write,
 };
 
+static const struct file_operations trace_clock_ro_fops = {
+	.open		= tracing_clock_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= tracing_single_release_tr,
+};
+
 static const struct file_operations trace_time_stamp_mode_fops = {
 	.open		= tracing_time_stamp_mode_open,
 	.read		= seq_read,
@@ -9353,12 +9393,16 @@ static void
 tracing_init_tracefs_percpu(struct trace_array *tr, long cpu)
 {
 	struct dentry *d_percpu = tracing_dentry_percpu(tr, cpu);
+	umode_t writable_mode = TRACE_MODE_WRITE;
 	struct dentry *d_cpu;
 	char cpu_dir[30]; /* 30 characters should be more than enough */
 
 	if (!d_percpu)
 		return;
 
+	if (trace_array_is_readonly(tr))
+		writable_mode = TRACE_MODE_READ;
+
 	snprintf(cpu_dir, 30, "cpu%ld", cpu);
 	d_cpu = tracefs_create_dir(cpu_dir, d_percpu);
 	if (!d_cpu) {
@@ -9371,7 +9415,7 @@ tracing_init_tracefs_percpu(struct trace_array *tr, long cpu)
 				tr, cpu, &tracing_pipe_fops);
 
 	/* per cpu trace */
-	trace_create_cpu_file("trace", TRACE_MODE_WRITE, d_cpu,
+	trace_create_cpu_file("trace", writable_mode, d_cpu,
 				tr, cpu, &tracing_fops);
 
 	trace_create_cpu_file("trace_pipe_raw", TRACE_MODE_READ, d_cpu,
@@ -9566,21 +9610,31 @@ static const struct file_operations trace_options_core_fops = {
 	.llseek = generic_file_llseek,
 };
 
-struct dentry *trace_create_file(const char *name,
-				 umode_t mode,
-				 struct dentry *parent,
-				 void *data,
-				 const struct file_operations *fops)
+struct dentry *__trace_create_file(const char *name,
+				   umode_t mode,
+				   struct dentry *parent,
+				   void *data,
+				   const struct file_operations *fops,
+				   const struct file_operations *ro_fops)
 {
+	bool readonly = !!(mode & TRACE_MODE_WRITE_MASK);
 	struct dentry *ret;
 
-	ret = tracefs_create_file(name, mode, parent, data, fops);
+	ret = tracefs_create_file(name, mode, parent, data, readonly ? ro_fops : fops);
 	if (!ret)
 		pr_warn("Could not create tracefs '%s' entry\n", name);
 
 	return ret;
 }
 
+struct dentry *trace_create_file(const char *name,
+				 umode_t mode,
+				 struct dentry *parent,
+				 void *data,
+				 const struct file_operations *fops)
+{
+	return __trace_create_file(name, mode, parent, data, fops, fops);
+}
 
 static struct dentry *trace_options_init_dentry(struct trace_array *tr)
 {
@@ -9811,6 +9865,9 @@ rb_simple_write(struct file *filp, const char __user *ubuf,
 	unsigned long val;
 	int ret;
 
+	if (trace_array_is_readonly(tr))
+		return -EPERM;
+
 	ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
 	if (ret)
 		return ret;
@@ -9845,6 +9902,13 @@ static const struct file_operations rb_simple_fops = {
 	.llseek		= default_llseek,
 };
 
+static const struct file_operations rb_simple_ro_fops = {
+	.open		= tracing_open_generic_tr,
+	.read		= rb_simple_read,
+	.release	= tracing_release_generic_tr,
+	.llseek		= default_llseek,
+};
+
 static ssize_t
 buffer_percent_read(struct file *filp, char __user *ubuf,
 		    size_t cnt, loff_t *ppos)
@@ -9986,6 +10050,13 @@ static const struct file_operations buffer_subbuf_size_fops = {
 	.llseek		= default_llseek,
 };
 
+static const struct file_operations buffer_subbuf_size_ro_fops = {
+	.open		= tracing_open_generic_tr,
+	.read		= buffer_subbuf_size_read,
+	.release	= tracing_release_generic_tr,
+	.llseek		= default_llseek,
+};
+
 static struct dentry *trace_instance_dir;
 
 static void
@@ -10597,89 +10668,101 @@ 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;
+	bool readonly = trace_array_is_readonly(tr);
 	int cpu;
 
+	if (readonly)
+		writable_mode = TRACE_MODE_READ;
+
 	trace_create_file("available_tracers", TRACE_MODE_READ, d_tracer,
 			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, &set_tracer_ro_fops);
 
-	trace_create_file("tracing_cpumask", TRACE_MODE_WRITE, d_tracer,
-			  tr, &tracing_cpumask_fops);
+	__trace_create_file("tracing_cpumask", writable_mode, d_tracer,
+			    tr, &tracing_cpumask_fops, &tracing_cpumask_ro_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);
 
-	trace_create_file("trace", TRACE_MODE_WRITE, d_tracer,
-			  tr, &tracing_fops);
+	__trace_create_file("trace", writable_mode, d_tracer,
+			    tr, &tracing_fops, &tracing_ro_fops);
 
 	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,
-			  tr, &tracing_entries_fops);
+	__trace_create_file("buffer_size_kb", writable_mode, d_tracer,
+			    tr, &tracing_entries_fops, &tracing_entries_ro_fops);
 
 	trace_create_file("buffer_total_size_kb", TRACE_MODE_READ, d_tracer,
 			  tr, &tracing_total_entries_fops);
 
-	trace_create_file("free_buffer", 0200, d_tracer,
-			  tr, &tracing_free_buffer_fops);
+	if (!readonly) {
+		trace_create_file("free_buffer", 0200, d_tracer,
+				tr, &tracing_free_buffer_fops);
+
+		trace_create_file("trace_marker", 0220, d_tracer,
+				tr, &tracing_mark_fops);
+
+		tr->trace_marker_file = __find_event_file(tr, "ftrace", "print");
 
-	trace_create_file("trace_marker", 0220, d_tracer,
-			  tr, &tracing_mark_fops);
+		trace_create_file("trace_marker_raw", 0220, d_tracer,
+				tr, &tracing_mark_raw_fops);
 
-	tr->trace_marker_file = __find_event_file(tr, "ftrace", "print");
+		trace_create_file("buffer_percent", TRACE_MODE_WRITE, d_tracer,
+				tr, &buffer_percent_fops);
 
-	trace_create_file("trace_marker_raw", 0220, d_tracer,
-			  tr, &tracing_mark_raw_fops);
+		trace_create_file("syscall_user_buf_size", TRACE_MODE_WRITE, d_tracer,
+				tr, &tracing_syscall_buf_fops);
+	}
 
-	trace_create_file("trace_clock", TRACE_MODE_WRITE, d_tracer, tr,
-			  &trace_clock_fops);
+	__trace_create_file("trace_clock", writable_mode, d_tracer, tr,
+			    &trace_clock_fops, &trace_clock_ro_fops);
 
-	trace_create_file("tracing_on", TRACE_MODE_WRITE, d_tracer,
-			  tr, &rb_simple_fops);
+	__trace_create_file("tracing_on", writable_mode, d_tracer,
+			    tr, &rb_simple_fops, &rb_simple_ro_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);
+	__trace_create_file("buffer_subbuf_size_kb", writable_mode, d_tracer,
+			    tr, &buffer_subbuf_size_fops,
+			    &buffer_subbuf_size_ro_fops);
 
 	create_trace_options_dir(tr);
 
 #ifdef CONFIG_TRACER_MAX_TRACE
-	trace_create_maxlat_file(tr, d_tracer);
+	if (!readonly)
+		trace_create_maxlat_file(tr, d_tracer);
 #endif
 
-	if (ftrace_create_function_files(tr, d_tracer))
+	if (!readonly && 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 {
+	} else if (!readonly) {
 		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);
+	if (!readonly)
+		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);
+	if (!readonly)
+		ftrace_init_tracefs(tr, d_tracer);
 }
 
 #ifdef CONFIG_TRACEFS_AUTOMOUNT_DEPRECATED
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index b6d42fe06115..4fae5cf1182c 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -33,6 +33,7 @@
 
 #define TRACE_MODE_WRITE	0640
 #define TRACE_MODE_READ		0440
+#define TRACE_MODE_WRITE_MASK	(TRACE_MODE_WRITE & ~TRACE_MODE_READ)
 
 enum trace_type {
 	__TRACE_FIRST_TYPE = 0,
@@ -483,6 +484,12 @@ extern bool trace_clock_in_ns(struct trace_array *tr);
 
 extern unsigned long trace_adjust_address(struct trace_array *tr, unsigned long addr);
 
+static inline bool trace_array_is_readonly(struct trace_array *tr)
+{
+	/* backup instance is read only. */
+	return tr->flags & TRACE_ARRAY_FL_VMALLOC;
+}
+
 /*
  * The global tracer (top) should be the first trace array added,
  * but we check the flag anyway.
@@ -680,7 +687,12 @@ struct dentry *trace_create_file(const char *name,
 				 struct dentry *parent,
 				 void *data,
 				 const struct file_operations *fops);
-
+struct dentry *__trace_create_file(const char *name,
+				   umode_t mode,
+				   struct dentry *parent,
+				   void *data,
+				   const struct file_operations *fops,
+				   const struct file_operations *ro_fops);
 
 /**
  * tracer_tracing_is_on_cpu - show real state of ring buffer enabled on for a cpu
diff --git a/kernel/trace/trace_boot.c b/kernel/trace/trace_boot.c
index dbe29b4c6a7a..2ca2541c8a58 100644
--- a/kernel/trace/trace_boot.c
+++ b/kernel/trace/trace_boot.c
@@ -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);
 	}
 
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 9b07ad9eb284..741b16b54d90 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1379,6 +1379,9 @@ static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
 {
 	int ret;
 
+	if (trace_array_is_readonly(tr))
+		return -EPERM;
+
 	mutex_lock(&event_mutex);
 	ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set, mod);
 	mutex_unlock(&event_mutex);
@@ -2817,8 +2820,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);
@@ -2979,7 +2982,6 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
 	struct eventfs_inode *e_events;
 	struct eventfs_inode *ei;
 	const char *name;
-	int nr_entries;
 	int ret;
 	static struct eventfs_entry event_entries[] = {
 		{
@@ -3024,6 +3026,18 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
 		},
 #endif
 	};
+	static struct eventfs_entry event_ro_entries[] = {
+		{
+			.name		= "format",
+			.callback	= event_callback,
+		},
+#ifdef CONFIG_PERF_EVENTS
+		{
+			.name		= "id",
+			.callback	= event_callback,
+		},
+#endif
+	};
 
 	/*
 	 * If the trace point header did not define TRACE_SYSTEM
@@ -3037,10 +3051,14 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
 	if (!e_events)
 		return -ENOMEM;
 
-	nr_entries = ARRAY_SIZE(event_entries);
-
 	name = trace_event_name(call);
-	ei = eventfs_create_dir(name, e_events, event_entries, nr_entries, file);
+
+	if (trace_array_is_readonly(tr))
+		ei = eventfs_create_dir(name, e_events, event_ro_entries,
+					ARRAY_SIZE(event_ro_entries), file);
+	else
+		ei = eventfs_create_dir(name, e_events, event_entries,
+					ARRAY_SIZE(event_entries), file);
 	if (IS_ERR(ei)) {
 		pr_warn("Could not create tracefs '%s' directory\n", name);
 		return -1;
@@ -4378,7 +4396,6 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
 {
 	struct eventfs_inode *e_events;
 	struct dentry *entry;
-	int nr_entries;
 	static struct eventfs_entry events_entries[] = {
 		{
 			.name		= "enable",
@@ -4393,30 +4410,44 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
 			.callback	= events_callback,
 		},
 	};
+	static struct eventfs_entry events_ro_entries[] = {
+		{
+			.name		= "header_page",
+			.callback	= events_callback,
+		},
+		{
+			.name		= "header_event",
+			.callback	= events_callback,
+		},
+	};
 
-	entry = trace_create_file("set_event", TRACE_MODE_WRITE, parent,
-				  tr, &ftrace_set_event_fops);
-	if (!entry)
-		return -ENOMEM;
-
-	nr_entries = ARRAY_SIZE(events_entries);
-
-	e_events = eventfs_create_events_dir("events", parent, events_entries,
-					     nr_entries, tr);
+	if (trace_array_is_readonly(tr))
+		e_events = eventfs_create_events_dir("events", parent, events_ro_entries,
+						ARRAY_SIZE(events_ro_entries), tr);
+	else
+		e_events = eventfs_create_events_dir("events", parent, events_entries,
+						ARRAY_SIZE(events_entries), tr);
 	if (IS_ERR(e_events)) {
 		pr_warn("Could not create tracefs 'events' directory\n");
 		return -ENOMEM;
 	}
 
-	/* There are not as crucial, just warn if they are not created */
+	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("set_event_pid", TRACE_MODE_WRITE, parent,
-			  tr, &ftrace_set_event_pid_fops);
+		/* There are not as crucial, just warn if they are not created */
 
-	trace_create_file("set_event_notrace_pid",
-			  TRACE_MODE_WRITE, parent, tr,
-			  &ftrace_set_event_notrace_pid_fops);
+		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;


^ permalink raw reply related

* [PATCH v2 2/2] tracing: Add autoremove feature to the backup instance
From: Masami Hiramatsu (Google) @ 2026-01-08 14:23 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
	linux-trace-kernel
In-Reply-To: <176788217131.1398317.11144318616426272901.stgit@mhiramat.tok.corp.google.com>

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Since the backup instance is readonly, after reading all data
via pipe, no data is left on the instance. Thus it can be
removed safely after closing all files.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 kernel/trace/trace.c |   64 +++++++++++++++++++++++++++++++++++++++++++++++++-
 kernel/trace/trace.h |    6 +++++
 2 files changed, 69 insertions(+), 1 deletion(-)

diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 1b87595413fe..c0b05a560203 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -590,6 +590,55 @@ void trace_set_ring_buffer_expanded(struct trace_array *tr)
 	tr->ring_buffer_expanded = true;
 }
 
+static int __remove_instance(struct trace_array *tr);
+
+static void trace_array_autoremove(struct work_struct *work)
+{
+	struct trace_array *tr = container_of(work, struct trace_array, autoremove_work);
+
+	guard(mutex)(&event_mutex);
+	guard(mutex)(&trace_types_lock);
+
+	/*
+	 * This can be fail if someone gets @tr before starting this
+	 * function, but in that case, this will be kicked again when
+	 * putting it. So we don't care the result.
+	 */
+	__remove_instance(tr);
+}
+
+static struct workqueue_struct *autoremove_wq;
+
+static void trace_array_init_autoremove(struct trace_array *tr)
+{
+	INIT_WORK(&tr->autoremove_work, trace_array_autoremove);
+}
+
+static void trace_array_kick_autoremove(struct trace_array *tr)
+{
+	if (!work_pending(&tr->autoremove_work) && autoremove_wq)
+		queue_work(autoremove_wq, &tr->autoremove_work);
+}
+
+static void trace_array_cancel_autoremove(struct trace_array *tr)
+{
+	if (work_pending(&tr->autoremove_work))
+		cancel_work(&tr->autoremove_work);
+}
+
+__init static int trace_array_init_autoremove_wq(void)
+{
+	autoremove_wq = alloc_workqueue("tr_autoremove_wq",
+					WQ_UNBOUND | WQ_HIGHPRI, 0);
+	if (!autoremove_wq) {
+		pr_err("Unable to allocate tr_autoremove_wq\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+late_initcall_sync(trace_array_init_autoremove_wq);
+
 LIST_HEAD(ftrace_trace_arrays);
 
 int trace_array_get(struct trace_array *this_tr)
@@ -598,7 +647,7 @@ int trace_array_get(struct trace_array *this_tr)
 
 	guard(mutex)(&trace_types_lock);
 	list_for_each_entry(tr, &ftrace_trace_arrays, list) {
-		if (tr == this_tr) {
+		if (tr == this_tr && !tr->free_on_close) {
 			tr->ref++;
 			return 0;
 		}
@@ -611,6 +660,12 @@ static void __trace_array_put(struct trace_array *this_tr)
 {
 	WARN_ON(!this_tr->ref);
 	this_tr->ref--;
+	/*
+	 * When free_on_close is set, prepare removing the array
+	 * when the last reference is released.
+	 */
+	if (this_tr->ref == 1 && this_tr->free_on_close)
+		trace_array_kick_autoremove(this_tr);
 }
 
 /**
@@ -6225,6 +6280,10 @@ static void update_last_data(struct trace_array *tr)
 	/* Only if the buffer has previous boot data clear and update it. */
 	tr->flags &= ~TRACE_ARRAY_FL_LAST_BOOT;
 
+	/* If this is a backup instance, mark it for autoremove. */
+	if (tr->flags & TRACE_ARRAY_FL_VMALLOC)
+		tr->free_on_close = true;
+
 	/* Reset the module list and reload them */
 	if (tr->scratch) {
 		struct trace_scratch *tscratch = tr->scratch;
@@ -10428,6 +10487,8 @@ trace_array_create_systems(const char *name, const char *systems,
 	if (ftrace_allocate_ftrace_ops(tr) < 0)
 		goto out_free_tr;
 
+	trace_array_init_autoremove(tr);
+
 	ftrace_init_trace_array(tr);
 
 	init_trace_flags_index(tr);
@@ -10576,6 +10637,7 @@ static int __remove_instance(struct trace_array *tr)
 	if (update_marker_trace(tr, 0))
 		synchronize_rcu();
 
+	trace_array_cancel_autoremove(tr);
 	tracing_set_nop(tr);
 	clear_ftrace_function_probes(tr);
 	event_trace_del_tracer(tr);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 4fae5cf1182c..b6b56e790b13 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -447,6 +447,12 @@ struct trace_array {
 	 * we do not waste memory on systems that are not using tracing.
 	 */
 	bool ring_buffer_expanded;
+	/*
+	 * If the ring buffer is a read only backup instance, it will be
+	 * removed after dumping all data via pipe, because no readable data.
+	 */
+	bool free_on_close;
+	struct work_struct	autoremove_work;
 };
 
 enum {


^ permalink raw reply related

* Re: [PATCH bpf-next 1/2] arm64/ftrace,bpf: Fix partial regs after bpf_prog_run
From: Jiri Olsa @ 2026-01-08 14:32 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Masami Hiramatsu, Will Deacon, Mahe Tardy, Peter Zijlstra, bpf,
	linux-trace-kernel, linux-arm-kernel, x86, Yonghong Song,
	Song Liu, Andrii Nakryiko, Mark Rutland
In-Reply-To: <20260107110352.3fd7ddda@gandalf.local.home>

On Wed, Jan 07, 2026 at 11:03:52AM -0500, Steven Rostedt wrote:
> On Wed,  7 Jan 2026 10:32:55 +0100
> Jiri Olsa <jolsa@kernel.org> wrote:
> 
> > diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> > index 1621c84f44b3..177c7bbf3b84 100644
> > --- a/arch/arm64/include/asm/ftrace.h
> > +++ b/arch/arm64/include/asm/ftrace.h
> > @@ -157,6 +157,30 @@ ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
> >  	return regs;
> >  }
> >  
> 
> See my reply in the other email thread:
> 
>   https://lore.kernel.org/all/20260107105316.2b70a308@gandalf.local.home/

ah ok, thanks

jirka

> 
> -- Steve
> 
> 
> > +/*
> > + * ftrace_partial_regs_update - update the original ftrace_regs from regs
> > + * @fregs: The ftrace_regs to update from @regs
> > + * @regs: The partial regs from ftrace_partial_regs() that was updated
> > + *
> > + * Some architectures have the partial regs living in the ftrace_regs
> > + * structure, whereas other architectures need to make a different copy
> > + * of the @regs. If a partial @regs is retrieved by ftrace_partial_regs() and
> > + * if the code using @regs updates a field (like the instruction pointer or
> > + * stack pointer) it may need to propagate that change to the original @fregs
> > + * it retrieved the partial @regs from. Use this function to guarantee that
> > + * update happens.
> > + */
> > +static __always_inline void
> > +ftrace_partial_regs_update(const struct ftrace_regs *fregs, struct pt_regs *regs)
> > +{
> > +	struct __arch_ftrace_regs *afregs = arch_ftrace_regs(fregs);
> > +
> > +	if (afregs->pc != regs->pc) {
> > +		afregs->pc = regs->pc;
> > +		afregs->regs[0] = regs->regs[0];
> > +	}
> > +}

^ permalink raw reply

* Re: [PATCH] tracing: Check the return value of tracing_update_buffers()
From: Masami Hiramatsu @ 2026-01-08 14:44 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: LKML, Linux Trace Kernel, Masami Hiramatsu, Mathieu Desnoyers,
	Li Zhong
In-Reply-To: <20260107161510.4dc98b15@gandalf.local.home>

On Wed, 7 Jan 2026 16:15:10 -0500
Steven Rostedt <rostedt@goodmis.org> wrote:

> From: Steven Rostedt <rostedt@goodmis.org>
> 
> In the very unlikely event that tracing_update_buffers() fails in
> trace_printk_init_buffers(), report the failure so that it is known.
> 
> Link: https://lore.kernel.org/all/20220917020353.3836285-1-floridsleeves@gmail.com/

Good catch! Looks good to me.

Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Thanks,

> 
> Suggested-by: Li Zhong <floridsleeves@gmail.com>
> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
> ---
>  kernel/trace/trace.c | 7 ++++---
>  1 file changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index 2d387d56dcd4..1722e01e2238 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -3418,9 +3418,10 @@ void trace_printk_init_buffers(void)
>  	pr_warn("**********************************************************\n");
>  
>  	/* Expand the buffers to set size */
> -	tracing_update_buffers(&global_trace);
> -
> -	buffers_allocated = 1;
> +	if (tracing_update_buffers(&global_trace) < 0)
> +		pr_err("Failed to expand tracing buffers for trace_printk() calls\n");
> +	else
> +		buffers_allocated = 1;
>  
>  	/*
>  	 * trace_printk_init_buffers() can be called by modules.
> -- 
> 2.51.0
> 
> 


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply

* Re: [PATCH 0/2] PCI tracepoint documentation fixes
From: Steven Rostedt @ 2026-01-08 16:06 UTC (permalink / raw)
  To: Bagas Sanjaya
  Cc: Linux Kernel Mailing List, Linux Documentation,
	Linux Kernel Tracing, Masami Hiramatsu, Mathieu Desnoyers,
	Jonathan Corbet, Shuai Xue, Bjorn Helgaas, Ilpo Järvinen,
	Swaraj Gaikwad
In-Reply-To: <20260108013956.14351-1-bagasdotme@gmail.com>

On Thu,  8 Jan 2026 08:39:54 +0700
Bagas Sanjaya <bagasdotme@gmail.com> wrote:

> Hi,
> 
> Here are two fixes for htmldocs warnings on PCI tracepoint docs as reported in
> linux-next [1]. The shortlog below should be self-explanatory.
> 
> Enjoy!
> 
> [1]: https://lore.kernel.org/linux-next/20260105160000.0368ec8d@canb.auug.org.au/
> 
> Bagas Sanjaya (2):
>   Documentation: tracing: Add toctree entry for PCI tracepoint
>   Documentation: tracing: Wrap PCI tracepoint examples
> 
>  Documentation/trace/events-pci.rst | 4 ++--
>  Documentation/trace/index.rst      | 1 +
>  2 files changed, 3 insertions(+), 2 deletions(-)
> 
> 
> base-commit: 29a77b4897f1a0f40209bee929129d4c3f9c7a4b

Fine with me. Jon feel free to take these patches.

Acked-by: Steven Rostedt (Google) <rostedt@goodmis.org>

-- Steve

^ permalink raw reply

* Re: [PATCH v13 mm-new 05/16] khugepaged: introduce is_mthp_order helper
From: Lorenzo Stoakes @ 2026-01-08 16:01 UTC (permalink / raw)
  To: Nico Pache
  Cc: linux-kernel, linux-trace-kernel, linux-mm, linux-doc, david, ziy,
	baolin.wang, Liam.Howlett, ryan.roberts, dev.jain, corbet,
	rostedt, mhiramat, mathieu.desnoyers, akpm, baohua, willy, peterx,
	wangkefeng.wang, usamaarif642, sunnanyong, vishal.moola,
	thomas.hellstrom, yang, kas, aarcange, raquini, anshuman.khandual,
	catalin.marinas, tiwai, will, dave.hansen, jack, cl, jglisse,
	surenb, zokeefe, hannes, rientjes, mhocko, rdunlap, hughd,
	richard.weiyang, lance.yang, vbabka, rppt, jannh, pfalcato
In-Reply-To: <20251201174627.23295-6-npache@redhat.com>

On Mon, Dec 01, 2025 at 10:46:16AM -0700, Nico Pache wrote:
> In order to add mTHP support, we will often be checking if a given order
> is a mTHP or PMD order. Lets create a simple helper function to keep the
> code clean and readable.
>
> Suggested-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> Signed-off-by: Nico Pache <npache@redhat.com>

Notwithstanding what Zi said re: using elsewhere (please do :), LGTM so:

Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>

> ---
>  mm/khugepaged.c | 9 +++++++--
>  1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index 8599c7fa112e..9c041141b2e3 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c
> @@ -338,6 +338,11 @@ static bool pte_none_or_zero(pte_t pte)
>  	return pte_present(pte) && is_zero_pfn(pte_pfn(pte));
>  }
>
> +static bool is_mthp_order(unsigned int order)
> +{
> +	return order != HPAGE_PMD_ORDER;
> +}
> +
>  int hugepage_madvise(struct vm_area_struct *vma,
>  		     vm_flags_t *vm_flags, int advice)
>  {
> @@ -1071,13 +1076,13 @@ static int alloc_charge_folio(struct folio **foliop, struct mm_struct *mm,
>  	folio = __folio_alloc(gfp, order, node, &cc->alloc_nmask);
>  	if (!folio) {
>  		*foliop = NULL;
> -		if (order == HPAGE_PMD_ORDER)
> +		if (!is_mthp_order(order))
>  			count_vm_event(THP_COLLAPSE_ALLOC_FAILED);
>  		count_mthp_stat(order, MTHP_STAT_COLLAPSE_ALLOC_FAILED);
>  		return SCAN_ALLOC_HUGE_PAGE_FAIL;
>  	}
>
> -	if (order == HPAGE_PMD_ORDER)
> +	if (!is_mthp_order(order))
>  		count_vm_event(THP_COLLAPSE_ALLOC);
>  	count_mthp_stat(order, MTHP_STAT_COLLAPSE_ALLOC);
>
> --
> 2.51.1
>

^ permalink raw reply

* Re: [PATCH 0/2] PCI tracepoint documentation fixes
From: Bjorn Helgaas @ 2026-01-08 16:09 UTC (permalink / raw)
  To: Bagas Sanjaya
  Cc: Linux Kernel Mailing List, Linux Documentation,
	Linux Kernel Tracing, Steven Rostedt, Masami Hiramatsu,
	Mathieu Desnoyers, Jonathan Corbet, Shuai Xue, Bjorn Helgaas,
	Ilpo Järvinen, Swaraj Gaikwad
In-Reply-To: <20260108013956.14351-1-bagasdotme@gmail.com>

On Thu, Jan 08, 2026 at 08:39:54AM +0700, Bagas Sanjaya wrote:
> Hi,
> 
> Here are two fixes for htmldocs warnings on PCI tracepoint docs as reported in
> linux-next [1]. The shortlog below should be self-explanatory.
> 
> Enjoy!
> 
> [1]: https://lore.kernel.org/linux-next/20260105160000.0368ec8d@canb.auug.org.au/
> 
> Bagas Sanjaya (2):
>   Documentation: tracing: Add toctree entry for PCI tracepoint
>   Documentation: tracing: Wrap PCI tracepoint examples
> 
>  Documentation/trace/events-pci.rst | 4 ++--
>  Documentation/trace/index.rst      | 1 +
>  2 files changed, 3 insertions(+), 2 deletions(-)

These are for the pci/trace branch, which has not been merged into
mainline yet, so I squashed these fixes into the "Documentation:
tracing: Add PCI tracepoint documentation" patch on that branch.

Thanks!

Bjorn

^ permalink raw reply

* Re: [PATCH v13 mm-new 00/16] khugepaged: mTHP support
From: David Hildenbrand (Red Hat) @ 2026-01-08 16:09 UTC (permalink / raw)
  To: Nico Pache, linux-kernel, linux-trace-kernel, linux-mm, linux-doc
  Cc: ziy, baolin.wang, lorenzo.stoakes, Liam.Howlett, ryan.roberts,
	dev.jain, corbet, rostedt, mhiramat, mathieu.desnoyers, akpm,
	baohua, willy, peterx, wangkefeng.wang, usamaarif642, sunnanyong,
	vishal.moola, thomas.hellstrom, yang, kas, aarcange, raquini,
	anshuman.khandual, catalin.marinas, tiwai, will, dave.hansen,
	jack, cl, jglisse, surenb, zokeefe, hannes, rientjes, mhocko,
	rdunlap, hughd, richard.weiyang, lance.yang, vbabka, rppt, jannh,
	pfalcato
In-Reply-To: <20251201174627.23295-1-npache@redhat.com>

On 12/1/25 18:46, Nico Pache wrote:
> The following series provides khugepaged with the capability to collapse
> anonymous memory regions to mTHPs.
> 
> To achieve this we generalize the khugepaged functions to no longer depend
> on PMD_ORDER. Then during the PMD scan, we use a bitmap to track individual
> pages that are occupied (!none/zero). After the PMD scan is done, we use
> the bitmap to find the optimal mTHP sizes for the PMD range. The
> restriction on max_ptes_none is removed during the scan, to make sure we
> account for the whole PMD range in the bitmap. When no mTHP size is
> enabled, the legacy behavior of khugepaged is maintained.
> 
> We currently only support max_ptes_none values of 0 or HPAGE_PMD_NR - 1
> (ie 511). If any other value is specified, the kernel will emit a warning
> and no mTHP collapse will be attempted. If a mTHP collapse is attempted,
> but contains swapped out, or shared pages, we don't perform the collapse.
> It is now also possible to collapse to mTHPs without requiring the PMD THP
> size to be enabled. These limitiations are to prevent collapse "creep"
> behavior. This prevents constantly promoting mTHPs to the next available
> size, which would occur because a collapse introduces more non-zero pages
> that would satisfy the promotion condition on subsequent scans.
> 
> Patch 1:     Refactor/rename hpage_collapse
> Patch 2:     Refactoring to combine madvise_collapse and khugepaged

These two patches could get queued already separately, right?

-- 
Cheers

David

^ permalink raw reply

* Re: [PATCH v13 mm-new 00/16] khugepaged: mTHP support
From: Lorenzo Stoakes @ 2026-01-08 16:12 UTC (permalink / raw)
  To: Nico Pache
  Cc: linux-kernel, linux-trace-kernel, linux-mm, linux-doc, david, ziy,
	baolin.wang, Liam.Howlett, ryan.roberts, dev.jain, corbet,
	rostedt, mhiramat, mathieu.desnoyers, akpm, baohua, willy, peterx,
	wangkefeng.wang, usamaarif642, sunnanyong, vishal.moola,
	thomas.hellstrom, yang, kas, aarcange, raquini, anshuman.khandual,
	catalin.marinas, tiwai, will, dave.hansen, jack, cl, jglisse,
	surenb, zokeefe, hannes, rientjes, mhocko, rdunlap, hughd,
	richard.weiyang, lance.yang, vbabka, rppt, jannh, pfalcato
In-Reply-To: <20251201174627.23295-1-npache@redhat.com>

One nit on this series - you sent it on the 1st day of the merge window :)

We unfortunately have a suboptimal approach to the merge window in mm in my
view. Unfortunately it seems unlikely to change any time soon, so I would
simply politely ask that in future esp. with big series whether you could
send a respin within a cycle? :)

Sorry for the longer delay here btw, I've been on leave after that!
Unfortunate timing.

Note that I am scaling down my review somewhat at the moment as I am simply
disinclined to deal with the merge-by-default insanity mm engages in any
longer, but since I promised to review this I will do so.

Cheers, Lorenzo

On Mon, Dec 01, 2025 at 10:46:11AM -0700, Nico Pache wrote:
> The following series provides khugepaged with the capability to collapse
> anonymous memory regions to mTHPs.
>
> To achieve this we generalize the khugepaged functions to no longer depend
> on PMD_ORDER. Then during the PMD scan, we use a bitmap to track individual
> pages that are occupied (!none/zero). After the PMD scan is done, we use
> the bitmap to find the optimal mTHP sizes for the PMD range. The
> restriction on max_ptes_none is removed during the scan, to make sure we
> account for the whole PMD range in the bitmap. When no mTHP size is
> enabled, the legacy behavior of khugepaged is maintained.
>
> We currently only support max_ptes_none values of 0 or HPAGE_PMD_NR - 1
> (ie 511). If any other value is specified, the kernel will emit a warning
> and no mTHP collapse will be attempted. If a mTHP collapse is attempted,
> but contains swapped out, or shared pages, we don't perform the collapse.
> It is now also possible to collapse to mTHPs without requiring the PMD THP
> size to be enabled. These limitiations are to prevent collapse "creep"
> behavior. This prevents constantly promoting mTHPs to the next available
> size, which would occur because a collapse introduces more non-zero pages
> that would satisfy the promotion condition on subsequent scans.
>
> Patch 1:     Refactor/rename hpage_collapse
> Patch 2:     Refactoring to combine madvise_collapse and khugepaged
> Patch 3-8:   Generalize khugepaged functions for arbitrary orders and
> 	     introduce some helper functions
> Patch 9:     skip collapsing mTHP to smaller orders
> Patch 10-11: Add per-order mTHP statistics and tracepoints
> Patch 12:    Introduce collapse_allowable_orders
> Patch 13-15: Introduce bitmap and mTHP collapse support, fully enabled
> Patch 16:    Documentation
>
> ---------
>  Testing
> ---------
> - Built for x86_64, aarch64, ppc64le, and s390x
> - selftests mm
> - I created a test script that I used to push khugepaged to its limits
>    while monitoring a number of stats and tracepoints. The code is
>    available here[1] (Run in legacy mode for these changes and set mthp
>    sizes to inherit)
>    The summary from my testings was that there was no significant
>    regression noticed through this test. In some cases my changes had
>    better collapse latencies, and was able to scan more pages in the same
>    amount of time/work, but for the most part the results were consistent.
> - redis testing. I tested these changes along with my defer changes
>   (see followup [2] post for more details). We've decided to get the mTHP
>   changes merged first before attempting the defer series.
> - some basic testing on 64k page size.
> - lots of general use.
>
> V13 Changes:
> - Lots of minor nits, cleanups, comments, and renames
> - Bitmap function simplification and more helpers (Wei, Lorenzo)
> - Max_ptes_none (0 or 511) restriction
> - commit description expansion
> - list all reachable enum values in mthp_collapse()
> - Fix ppc64 compile error due to using HPAGE_PMD_ORDER (replace with
>   ilog2(MAX_PTRS_PER_PTE))
>
> V12: https://lore.kernel.org/lkml/20251022183717.70829-1-npache@redhat.com/
> V11: https://lore.kernel.org/lkml/20250912032810.197475-1-npache@redhat.com/
> V10: https://lore.kernel.org/lkml/20250819134205.622806-1-npache@redhat.com/
> V9 : https://lore.kernel.org/lkml/20250714003207.113275-1-npache@redhat.com/
> V8 : https://lore.kernel.org/lkml/20250702055742.102808-1-npache@redhat.com/
> V7 : https://lore.kernel.org/lkml/20250515032226.128900-1-npache@redhat.com/
> V6 : https://lore.kernel.org/lkml/20250515030312.125567-1-npache@redhat.com/
> V5 : https://lore.kernel.org/lkml/20250428181218.85925-1-npache@redhat.com/
> V4 : https://lore.kernel.org/lkml/20250417000238.74567-1-npache@redhat.com/
> V3 : https://lore.kernel.org/lkml/20250414220557.35388-1-npache@redhat.com/
> V2 : https://lore.kernel.org/lkml/20250211003028.213461-1-npache@redhat.com/
> V1 : https://lore.kernel.org/lkml/20250108233128.14484-1-npache@redhat.com/
>
> A big thanks to everyone that has reviewed, tested, and participated in
> the development process. Its been a great experience working with all of
> you on this endeavour.
>
> [1] - https://gitlab.com/npache/khugepaged_mthp_test
> [2] - https://lore.kernel.org/lkml/20250515033857.132535-1-npache@redhat.com/
>
> Baolin Wang (1):
>   khugepaged: run khugepaged for all orders
>
> Dev Jain (1):
>   khugepaged: generalize alloc_charge_folio()
>
> Nico Pache (14):
>   khugepaged: rename hpage_collapse_* to collapse_*
>   introduce collapse_single_pmd to unify khugepaged and madvise_collapse
>   khugepaged: generalize hugepage_vma_revalidate for mTHP support
>   khugepaged: introduce is_mthp_order helper
>   khugepaged: generalize __collapse_huge_page_* for mTHP support
>   khugepaged: introduce collapse_max_ptes_none helper function
>   khugepaged: generalize collapse_huge_page for mTHP collapse
>   khugepaged: skip collapsing mTHP to smaller orders
>   khugepaged: add per-order mTHP collapse failure statistics
>   khugepaged: improve tracepoints for mTHP orders
>   khugepaged: introduce collapse_allowable_orders helper function
>   khugepaged: Introduce mTHP collapse support
>   khugepaged: avoid unnecessary mTHP collapse attempts
>   Documentation: mm: update the admin guide for mTHP collapse
>
>  Documentation/admin-guide/mm/transhuge.rst |  80 ++-
>  include/linux/huge_mm.h                    |   5 +
>  include/trace/events/huge_memory.h         |  34 +-
>  mm/huge_memory.c                           |  11 +
>  mm/khugepaged.c                            | 696 +++++++++++++++------
>  mm/mremap.c                                |   2 +-
>  6 files changed, 618 insertions(+), 210 deletions(-)
>
> --
> 2.51.1
>

^ permalink raw reply

* Re: [PATCH] ring-buffer: Use a housekeeping CPU to wake up waiters
From: Steven Rostedt @ 2026-01-08 16:58 UTC (permalink / raw)
  To: Petr Tesarik
  Cc: Masami Hiramatsu, Mathieu Desnoyers, Sebastian Andrzej Siewior,
	Clark Williams, linux-kernel, linux-trace-kernel, linux-rt-devel
In-Reply-To: <20260108093932.252f6bc7@mordecai>

On Thu, 8 Jan 2026 09:39:32 +0100
Petr Tesarik <ptesarik@suse.com> wrote:

> > > Or we simply change it to:
> > > 
> > > static inline void    
> > 
> > Actually, the above should be noinline, as it's in a slower path, and
> > should not be adding logic into the cache of the fast path.  
> 
> However, to be honest, I'm surprized this is considered slow path. My
> use case is to record a few selected trace events with "trace-cmd
> record", which spends most time polling trace_pipe_raw. Consequently,
> there is almost always a pending waiter that requires a wakeup.
> 
> In short, irq_work_queue() is the hot path for me.
> 
> OTOH I don't mind making it noinline, because on recent Intel and AMD
> systems, a function call (noinline) is often cheaper than an increase
> in L1 cache footprint (caused by inlining). But I'm confused. I have
> always thought most people use tracing same way as I do.

The call to rb_wakeups() is a fast path, but the wakeup itself is a slow
path. This is the case even when you have user space in a loop that is just
waiting on data.

User space tool:

  ring_buffer_wait() {
    wake_event_interruptible(.., rb_wait_cond(..));
  }

Writer:

  rb_wakeups() {
    if (!full_hit())
      return;
  }

The full_hit() is the watermark check. If you look at the tracefs
directory, you'll see a "buffer_percent" file, which is default set to 50.
That means that full_hit() will not return true until the ring buffer is
around 50 percent full. This function is called thousands of times before
the first wakeup happens.

Let's look at even a waiter that isn't using the buffer percent. This means
it will be woken up on any event in the buffer.

  rb_wakeups() {
	if (buffer->irq_work.waiters_pending) {
		buffer->irq_work.waiters_pending = false;
		/* irq_work_queue() supplies it's own memory barriers */
		irq_work_queue(&buffer->irq_work.work);


So it clears the waiters_pending flag and wakes up the waiter. Now the
waiter wakes up and starts reading the ring buffer. While the ring buffer
has content, it will continue to read and doesn't block again until the
ring buffer is empty. This means that thousands of events are being
recorded with no waiters to wake up.

See why this is a slow path?

-- Steve

^ permalink raw reply

* Re: [PATCH v13 mm-new 02/16] introduce collapse_single_pmd to unify khugepaged and madvise_collapse
From: Lorenzo Stoakes @ 2026-01-08 17:02 UTC (permalink / raw)
  To: Nico Pache
  Cc: linux-kernel, linux-trace-kernel, linux-mm, linux-doc, david, ziy,
	baolin.wang, Liam.Howlett, ryan.roberts, dev.jain, corbet,
	rostedt, mhiramat, mathieu.desnoyers, akpm, baohua, willy, peterx,
	wangkefeng.wang, usamaarif642, sunnanyong, vishal.moola,
	thomas.hellstrom, yang, kas, aarcange, raquini, anshuman.khandual,
	catalin.marinas, tiwai, will, dave.hansen, jack, cl, jglisse,
	surenb, zokeefe, hannes, rientjes, mhocko, rdunlap, hughd,
	richard.weiyang, lance.yang, vbabka, rppt, jannh, pfalcato
In-Reply-To: <20251201174627.23295-3-npache@redhat.com>

Sorry I realise it's due to delay in review but, unsurprisingly got a knock-on
merge conflict here:

Looks like it's commit 823953d831d8 ("mm/khugepaged: use enum scan_result for
result variables and return types").


<<<<<<< HEAD

static unsigned int collapse_scan_mm_slot(unsigned int pages, enum scan_result *result,
		struct collapse_control *cc)
=======
/*
 * Try to collapse a single PMD starting at a PMD aligned addr, and return
 * the results.
 */
static int collapse_single_pmd(unsigned long addr,
		struct vm_area_struct *vma, bool *mmap_locked,
		struct collapse_control *cc)
{
	struct mm_struct *mm = vma->vm_mm;
	int result;
	struct file *file;
	pgoff_t pgoff;

	if (vma_is_anonymous(vma)) {
		result = collapse_scan_pmd(mm, vma, addr, mmap_locked, cc);
		goto end;
	}

	file = get_file(vma->vm_file);
	pgoff = linear_page_index(vma, addr);

	mmap_read_unlock(mm);
	*mmap_locked = false;
	result = collapse_scan_file(mm, addr, file, pgoff, cc);
	fput(file);
	if (result != SCAN_PTE_MAPPED_HUGEPAGE)
		goto end;

	mmap_read_lock(mm);
	*mmap_locked = true;
	if (collapse_test_exit_or_disable(mm)) {
		mmap_read_unlock(mm);
		*mmap_locked = false;
		return SCAN_ANY_PROCESS;
	}
	result = collapse_pte_mapped_thp(mm, addr, !cc->is_khugepaged);
	if (result == SCAN_PMD_MAPPED)
		result = SCAN_SUCCEED;
	mmap_read_unlock(mm);
	*mmap_locked = false;

end:
	if (cc->is_khugepaged && result == SCAN_SUCCEED)
		++khugepaged_pages_collapsed;
	return result;
}

static unsigned int collapse_scan_mm_slot(unsigned int pages, int *result,
					    struct collapse_control *cc)
>>>>>>> introduce collapse_single_pmd to unify khugepaged and madvise_collapse


On Mon, Dec 01, 2025 at 10:46:13AM -0700, Nico Pache wrote:
> The khugepaged daemon and madvise_collapse have two different
> implementations that do almost the same thing.
>
> Create collapse_single_pmd to increase code reuse and create an entry
> point to these two users.
>
> Refactor madvise_collapse and collapse_scan_mm_slot to use the new
> collapse_single_pmd function. This introduces a minor behavioral change
> that is most likely an undiscovered bug. The current implementation of
> khugepaged tests collapse_test_exit_or_disable before calling
> collapse_pte_mapped_thp, but we weren't doing it in the madvise_collapse
> case. By unifying these two callers madvise_collapse now also performs
> this check. We also modify the return value to be SCAN_ANY_PROCESS which
> properly indicates that this process is no longer valid to operate on.
>
> We also guard the khugepaged_pages_collapsed variable to ensure its only
> incremented for khugepaged.
>
> Reviewed-by: Wei Yang <richard.weiyang@gmail.com>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
> Acked-by: David Hildenbrand <david@redhat.com>
> Signed-off-by: Nico Pache <npache@redhat.com>
> ---
>  mm/khugepaged.c | 97 ++++++++++++++++++++++++++-----------------------
>  1 file changed, 52 insertions(+), 45 deletions(-)
>
> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index 959be77f2e65..433ea7283488 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c
> @@ -2392,6 +2392,53 @@ static int collapse_scan_file(struct mm_struct *mm, unsigned long addr,
>  	return result;
>  }
>
> +/*
> + * Try to collapse a single PMD starting at a PMD aligned addr, and return
> + * the results.
> + */
> +static int collapse_single_pmd(unsigned long addr,
> +		struct vm_area_struct *vma, bool *mmap_locked,
> +		struct collapse_control *cc)
> +{
> +	struct mm_struct *mm = vma->vm_mm;
> +	int result;
> +	struct file *file;
> +	pgoff_t pgoff;
> +
> +	if (vma_is_anonymous(vma)) {
> +		result = collapse_scan_pmd(mm, vma, addr, mmap_locked, cc);
> +		goto end;
> +	}
> +
> +	file = get_file(vma->vm_file);
> +	pgoff = linear_page_index(vma, addr);
> +
> +	mmap_read_unlock(mm);
> +	*mmap_locked = false;
> +	result = collapse_scan_file(mm, addr, file, pgoff, cc);
> +	fput(file);
> +	if (result != SCAN_PTE_MAPPED_HUGEPAGE)
> +		goto end;
> +
> +	mmap_read_lock(mm);
> +	*mmap_locked = true;
> +	if (collapse_test_exit_or_disable(mm)) {
> +		mmap_read_unlock(mm);
> +		*mmap_locked = false;
> +		return SCAN_ANY_PROCESS;
> +	}
> +	result = collapse_pte_mapped_thp(mm, addr, !cc->is_khugepaged);
> +	if (result == SCAN_PMD_MAPPED)
> +		result = SCAN_SUCCEED;
> +	mmap_read_unlock(mm);
> +	*mmap_locked = false;
> +
> +end:
> +	if (cc->is_khugepaged && result == SCAN_SUCCEED)
> +		++khugepaged_pages_collapsed;
> +	return result;
> +}
> +
>  static unsigned int collapse_scan_mm_slot(unsigned int pages, int *result,
>  					    struct collapse_control *cc)
>  	__releases(&khugepaged_mm_lock)
> @@ -2462,34 +2509,9 @@ static unsigned int collapse_scan_mm_slot(unsigned int pages, int *result,
>  			VM_BUG_ON(khugepaged_scan.address < hstart ||
>  				  khugepaged_scan.address + HPAGE_PMD_SIZE >
>  				  hend);
> -			if (!vma_is_anonymous(vma)) {
> -				struct file *file = get_file(vma->vm_file);
> -				pgoff_t pgoff = linear_page_index(vma,
> -						khugepaged_scan.address);
> -
> -				mmap_read_unlock(mm);
> -				mmap_locked = false;
> -				*result = collapse_scan_file(mm,
> -					khugepaged_scan.address, file, pgoff, cc);
> -				fput(file);
> -				if (*result == SCAN_PTE_MAPPED_HUGEPAGE) {
> -					mmap_read_lock(mm);
> -					if (collapse_test_exit_or_disable(mm))
> -						goto breakouterloop;
> -					*result = collapse_pte_mapped_thp(mm,
> -						khugepaged_scan.address, false);
> -					if (*result == SCAN_PMD_MAPPED)
> -						*result = SCAN_SUCCEED;
> -					mmap_read_unlock(mm);
> -				}
> -			} else {
> -				*result = collapse_scan_pmd(mm, vma,
> -					khugepaged_scan.address, &mmap_locked, cc);
> -			}
> -
> -			if (*result == SCAN_SUCCEED)
> -				++khugepaged_pages_collapsed;
>
> +			*result = collapse_single_pmd(khugepaged_scan.address,
> +						      vma, &mmap_locked, cc);
>  			/* move to next address */
>  			khugepaged_scan.address += HPAGE_PMD_SIZE;
>  			progress += HPAGE_PMD_NR;
> @@ -2801,35 +2823,20 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start,
>  			hend = min(hend, vma->vm_end & HPAGE_PMD_MASK);
>  		}
>  		mmap_assert_locked(mm);
> -		if (!vma_is_anonymous(vma)) {
> -			struct file *file = get_file(vma->vm_file);
> -			pgoff_t pgoff = linear_page_index(vma, addr);
>
> -			mmap_read_unlock(mm);
> -			mmap_locked = false;
> -			result = collapse_scan_file(mm, addr, file, pgoff, cc);
> -			fput(file);
> -		} else {
> -			result = collapse_scan_pmd(mm, vma, addr,
> -						   &mmap_locked, cc);
> -		}
> +		result = collapse_single_pmd(addr, vma, &mmap_locked, cc);
> +
>  		if (!mmap_locked)
>  			*lock_dropped = true;
>
> -handle_result:
>  		switch (result) {
>  		case SCAN_SUCCEED:
>  		case SCAN_PMD_MAPPED:
>  			++thps;
>  			break;
> -		case SCAN_PTE_MAPPED_HUGEPAGE:
> -			BUG_ON(mmap_locked);
> -			mmap_read_lock(mm);
> -			result = collapse_pte_mapped_thp(mm, addr, true);
> -			mmap_read_unlock(mm);
> -			goto handle_result;
>  		/* Whitelisted set of results where continuing OK */
>  		case SCAN_NO_PTE_TABLE:
> +		case SCAN_PTE_MAPPED_HUGEPAGE:
>  		case SCAN_PTE_NON_PRESENT:
>  		case SCAN_PTE_UFFD_WP:
>  		case SCAN_LACK_REFERENCED_PAGE:
> --
> 2.51.1
>

^ permalink raw reply

* Re: [PATCH v13 mm-new 01/16] khugepaged: rename hpage_collapse_* to collapse_*
From: Lorenzo Stoakes @ 2026-01-08 16:54 UTC (permalink / raw)
  To: Nico Pache
  Cc: linux-kernel, linux-trace-kernel, linux-mm, linux-doc, david, ziy,
	baolin.wang, Liam.Howlett, ryan.roberts, dev.jain, corbet,
	rostedt, mhiramat, mathieu.desnoyers, akpm, baohua, willy, peterx,
	wangkefeng.wang, usamaarif642, sunnanyong, vishal.moola,
	thomas.hellstrom, yang, kas, aarcange, raquini, anshuman.khandual,
	catalin.marinas, tiwai, will, dave.hansen, jack, cl, jglisse,
	surenb, zokeefe, hannes, rientjes, mhocko, rdunlap, hughd,
	richard.weiyang, lance.yang, vbabka, rppt, jannh, pfalcato
In-Reply-To: <20251201174627.23295-2-npache@redhat.com>

FYI there's a merge conflict vs. mm-unstable atm (mm-new is unworkably broken,
so prefer to base on mm-unstable if possible thanks).

In mm/khugepaged.c:

<<<<<<< HEAD
static enum scan_result hpage_collapse_scan_pmd(struct mm_struct *mm,
						struct vm_area_struct *vma,
						unsigned long start_addr, bool *mmap_locked,
						struct collapse_control *cc)
=======
static int collapse_scan_pmd(struct mm_struct *mm,
			     struct vm_area_struct *vma,
			     unsigned long start_addr, bool *mmap_locked,
			     struct collapse_control *cc)
>>>>>>> khugepaged: rename hpage_collapse_* to collapse_*

<<<<<<< HEAD
static enum scan_result hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
						 struct file *file, pgoff_t start,
						 struct collapse_control *cc)
=======
static int collapse_scan_file(struct mm_struct *mm, unsigned long addr,
			      struct file *file, pgoff_t start,
			      struct collapse_control *cc)
>>>>>>> khugepaged: rename hpage_collapse_* to collapse_*

<<<<<<< HEAD
static unsigned int khugepaged_scan_mm_slot(unsigned int pages, enum scan_result *result,
=======
static unsigned int collapse_scan_mm_slot(unsigned int pages, int *result,
>>>>>>> khugepaged: rename hpage_collapse_* to collapse_*

<<<<<<< HEAD
static enum scan_result hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
						 struct file *file, pgoff_t start,
						 struct collapse_control *cc)
=======
static int collapse_scan_file(struct mm_struct *mm, unsigned long addr,
			      struct file *file, pgoff_t start,
			      struct collapse_control *cc)
>>>>>>> khugepaged: rename hpage_collapse_* to collapse_*

Maybe worth just double-tab indenting the args on subsequent lines to make
dealing with this kind of thing a bit easier in general? Tab/space alignment to
function name, while neat, can be a pain when return type changes.

On Mon, Dec 01, 2025 at 10:46:12AM -0700, Nico Pache wrote:
> The hpage_collapse functions describe functions used by madvise_collapse
> and khugepaged. remove the unnecessary hpage prefix to shorten the
> function name.
>
> Reviewed-by: Wei Yang <richard.weiyang@gmail.com>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>
> Reviewed-by: Zi Yan <ziy@nvidia.com>
> Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
> Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> Acked-by: David Hildenbrand <david@redhat.com>
> Signed-off-by: Nico Pache <npache@redhat.com>
> ---
>  mm/khugepaged.c | 73 ++++++++++++++++++++++++-------------------------
>  mm/mremap.c     |  2 +-
>  2 files changed, 37 insertions(+), 38 deletions(-)
>
> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index 97d1b2824386..959be77f2e65 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c
> @@ -394,14 +394,14 @@ void __init khugepaged_destroy(void)
>  	kmem_cache_destroy(mm_slot_cache);
>  }
>
> -static inline int hpage_collapse_test_exit(struct mm_struct *mm)
> +static inline int collapse_test_exit(struct mm_struct *mm)
>  {
>  	return atomic_read(&mm->mm_users) == 0;
>  }
>
> -static inline int hpage_collapse_test_exit_or_disable(struct mm_struct *mm)
> +static inline int collapse_test_exit_or_disable(struct mm_struct *mm)
>  {
> -	return hpage_collapse_test_exit(mm) ||
> +	return collapse_test_exit(mm) ||
>  		mm_flags_test(MMF_DISABLE_THP_COMPLETELY, mm);
>  }
>
> @@ -435,7 +435,7 @@ void __khugepaged_enter(struct mm_struct *mm)
>  	int wakeup;
>
>  	/* __khugepaged_exit() must not run from under us */
> -	VM_BUG_ON_MM(hpage_collapse_test_exit(mm), mm);
> +	VM_BUG_ON_MM(collapse_test_exit(mm), mm);
>  	if (unlikely(mm_flags_test_and_set(MMF_VM_HUGEPAGE, mm)))
>  		return;
>
> @@ -489,7 +489,7 @@ void __khugepaged_exit(struct mm_struct *mm)
>  	} else if (slot) {
>  		/*
>  		 * This is required to serialize against
> -		 * hpage_collapse_test_exit() (which is guaranteed to run
> +		 * collapse_test_exit() (which is guaranteed to run
>  		 * under mmap sem read mode). Stop here (after we return all
>  		 * pagetables will be destroyed) until khugepaged has finished
>  		 * working on the pagetables under the mmap_lock.
> @@ -579,7 +579,7 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
>  		folio = page_folio(page);
>  		VM_BUG_ON_FOLIO(!folio_test_anon(folio), folio);
>
> -		/* See hpage_collapse_scan_pmd(). */
> +		/* See collapse_scan_pmd(). */
>  		if (folio_maybe_mapped_shared(folio)) {
>  			++shared;
>  			if (cc->is_khugepaged &&
> @@ -830,7 +830,7 @@ struct collapse_control khugepaged_collapse_control = {
>  	.is_khugepaged = true,
>  };
>
> -static bool hpage_collapse_scan_abort(int nid, struct collapse_control *cc)
> +static bool collapse_scan_abort(int nid, struct collapse_control *cc)
>  {
>  	int i;
>
> @@ -865,7 +865,7 @@ static inline gfp_t alloc_hugepage_khugepaged_gfpmask(void)
>  }
>
>  #ifdef CONFIG_NUMA
> -static int hpage_collapse_find_target_node(struct collapse_control *cc)
> +static int collapse_find_target_node(struct collapse_control *cc)
>  {
>  	int nid, target_node = 0, max_value = 0;
>
> @@ -884,7 +884,7 @@ static int hpage_collapse_find_target_node(struct collapse_control *cc)
>  	return target_node;
>  }
>  #else
> -static int hpage_collapse_find_target_node(struct collapse_control *cc)
> +static int collapse_find_target_node(struct collapse_control *cc)
>  {
>  	return 0;
>  }
> @@ -905,7 +905,7 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
>  	enum tva_type type = cc->is_khugepaged ? TVA_KHUGEPAGED :
>  				 TVA_FORCED_COLLAPSE;
>
> -	if (unlikely(hpage_collapse_test_exit_or_disable(mm)))
> +	if (unlikely(collapse_test_exit_or_disable(mm)))
>  		return SCAN_ANY_PROCESS;
>
>  	*vmap = vma = find_vma(mm, address);
> @@ -978,7 +978,7 @@ static int check_pmd_still_valid(struct mm_struct *mm,
>
>  /*
>   * Bring missing pages in from swap, to complete THP collapse.
> - * Only done if hpage_collapse_scan_pmd believes it is worthwhile.
> + * Only done if khugepaged_scan_pmd believes it is worthwhile.
>   *
>   * Called and returns without pte mapped or spinlocks held.
>   * Returns result: if not SCAN_SUCCEED, mmap_lock has been released.
> @@ -1065,7 +1065,7 @@ static int alloc_charge_folio(struct folio **foliop, struct mm_struct *mm,
>  {
>  	gfp_t gfp = (cc->is_khugepaged ? alloc_hugepage_khugepaged_gfpmask() :
>  		     GFP_TRANSHUGE);
> -	int node = hpage_collapse_find_target_node(cc);
> +	int node = collapse_find_target_node(cc);
>  	struct folio *folio;
>
>  	folio = __folio_alloc(gfp, HPAGE_PMD_ORDER, node, &cc->alloc_nmask);
> @@ -1244,10 +1244,10 @@ static int collapse_huge_page(struct mm_struct *mm, unsigned long address,
>  	return result;
>  }
>
> -static int hpage_collapse_scan_pmd(struct mm_struct *mm,
> -				   struct vm_area_struct *vma,
> -				   unsigned long start_addr, bool *mmap_locked,
> -				   struct collapse_control *cc)
> +static int collapse_scan_pmd(struct mm_struct *mm,
> +			     struct vm_area_struct *vma,
> +			     unsigned long start_addr, bool *mmap_locked,
> +			     struct collapse_control *cc)
>  {
>  	pmd_t *pmd;
>  	pte_t *pte, *_pte;
> @@ -1355,7 +1355,7 @@ static int hpage_collapse_scan_pmd(struct mm_struct *mm,
>  		 * hit record.
>  		 */
>  		node = folio_nid(folio);
> -		if (hpage_collapse_scan_abort(node, cc)) {
> +		if (collapse_scan_abort(node, cc)) {
>  			result = SCAN_SCAN_ABORT;
>  			goto out_unmap;
>  		}
> @@ -1421,7 +1421,7 @@ static void collect_mm_slot(struct mm_slot *slot)
>
>  	lockdep_assert_held(&khugepaged_mm_lock);
>
> -	if (hpage_collapse_test_exit(mm)) {
> +	if (collapse_test_exit(mm)) {
>  		/* free mm_slot */
>  		hash_del(&slot->hash);
>  		list_del(&slot->mm_node);
> @@ -1769,7 +1769,7 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff)
>  		if (find_pmd_or_thp_or_none(mm, addr, &pmd) != SCAN_SUCCEED)
>  			continue;
>
> -		if (hpage_collapse_test_exit(mm))
> +		if (collapse_test_exit(mm))
>  			continue;
>
>  		if (!file_backed_vma_is_retractable(vma))
> @@ -2285,9 +2285,9 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
>  	return result;
>  }
>
> -static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
> -				    struct file *file, pgoff_t start,
> -				    struct collapse_control *cc)
> +static int collapse_scan_file(struct mm_struct *mm, unsigned long addr,
> +			      struct file *file, pgoff_t start,
> +			      struct collapse_control *cc)
>  {
>  	struct folio *folio = NULL;
>  	struct address_space *mapping = file->f_mapping;
> @@ -2342,7 +2342,7 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
>  		}
>
>  		node = folio_nid(folio);
> -		if (hpage_collapse_scan_abort(node, cc)) {
> +		if (collapse_scan_abort(node, cc)) {
>  			result = SCAN_SCAN_ABORT;
>  			folio_put(folio);
>  			break;
> @@ -2392,7 +2392,7 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
>  	return result;
>  }
>
> -static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int *result,
> +static unsigned int collapse_scan_mm_slot(unsigned int pages, int *result,
>  					    struct collapse_control *cc)
>  	__releases(&khugepaged_mm_lock)
>  	__acquires(&khugepaged_mm_lock)
> @@ -2427,7 +2427,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int *result,
>  		goto breakouterloop_mmap_lock;
>
>  	progress++;
> -	if (unlikely(hpage_collapse_test_exit_or_disable(mm)))
> +	if (unlikely(collapse_test_exit_or_disable(mm)))
>  		goto breakouterloop;
>
>  	vma_iter_init(&vmi, mm, khugepaged_scan.address);
> @@ -2435,7 +2435,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int *result,
>  		unsigned long hstart, hend;
>
>  		cond_resched();
> -		if (unlikely(hpage_collapse_test_exit_or_disable(mm))) {
> +		if (unlikely(collapse_test_exit_or_disable(mm))) {
>  			progress++;
>  			break;
>  		}
> @@ -2456,7 +2456,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int *result,
>  			bool mmap_locked = true;
>
>  			cond_resched();
> -			if (unlikely(hpage_collapse_test_exit_or_disable(mm)))
> +			if (unlikely(collapse_test_exit_or_disable(mm)))
>  				goto breakouterloop;
>
>  			VM_BUG_ON(khugepaged_scan.address < hstart ||
> @@ -2469,12 +2469,12 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int *result,
>
>  				mmap_read_unlock(mm);
>  				mmap_locked = false;
> -				*result = hpage_collapse_scan_file(mm,
> +				*result = collapse_scan_file(mm,
>  					khugepaged_scan.address, file, pgoff, cc);
>  				fput(file);
>  				if (*result == SCAN_PTE_MAPPED_HUGEPAGE) {
>  					mmap_read_lock(mm);
> -					if (hpage_collapse_test_exit_or_disable(mm))
> +					if (collapse_test_exit_or_disable(mm))
>  						goto breakouterloop;
>  					*result = collapse_pte_mapped_thp(mm,
>  						khugepaged_scan.address, false);
> @@ -2483,7 +2483,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int *result,
>  					mmap_read_unlock(mm);
>  				}
>  			} else {
> -				*result = hpage_collapse_scan_pmd(mm, vma,
> +				*result = collapse_scan_pmd(mm, vma,
>  					khugepaged_scan.address, &mmap_locked, cc);
>  			}
>
> @@ -2516,7 +2516,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int *result,
>  	 * Release the current mm_slot if this mm is about to die, or
>  	 * if we scanned all vmas of this mm.
>  	 */
> -	if (hpage_collapse_test_exit(mm) || !vma) {
> +	if (collapse_test_exit(mm) || !vma) {
>  		/*
>  		 * Make sure that if mm_users is reaching zero while
>  		 * khugepaged runs here, khugepaged_exit will find
> @@ -2567,8 +2567,8 @@ static void khugepaged_do_scan(struct collapse_control *cc)
>  			pass_through_head++;
>  		if (khugepaged_has_work() &&
>  		    pass_through_head < 2)
> -			progress += khugepaged_scan_mm_slot(pages - progress,
> -							    &result, cc);
> +			progress += collapse_scan_mm_slot(pages - progress,
> +							  &result, cc);
>  		else
>  			progress = pages;
>  		spin_unlock(&khugepaged_mm_lock);
> @@ -2807,12 +2807,11 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start,
>
>  			mmap_read_unlock(mm);
>  			mmap_locked = false;
> -			result = hpage_collapse_scan_file(mm, addr, file, pgoff,
> -							  cc);
> +			result = collapse_scan_file(mm, addr, file, pgoff, cc);
>  			fput(file);
>  		} else {
> -			result = hpage_collapse_scan_pmd(mm, vma, addr,
> -							 &mmap_locked, cc);
> +			result = collapse_scan_pmd(mm, vma, addr,
> +						   &mmap_locked, cc);
>  		}
>  		if (!mmap_locked)
>  			*lock_dropped = true;
> diff --git a/mm/mremap.c b/mm/mremap.c
> index 672264807db6..db31d0231e05 100644
> --- a/mm/mremap.c
> +++ b/mm/mremap.c
> @@ -244,7 +244,7 @@ static int move_ptes(struct pagetable_move_control *pmc,
>  		goto out;
>  	}
>  	/*
> -	 * Now new_pte is none, so hpage_collapse_scan_file() path can not find
> +	 * Now new_pte is none, so collapse_scan_file() path can not find
>  	 * this by traversing file->f_mapping, so there is no concurrency with
>  	 * retract_page_tables(). In addition, we already hold the exclusive
>  	 * mmap_lock, so this new_pte page is stable, so there is no need to get
> --
> 2.51.1
>

^ permalink raw reply

* Re: [PATCH v13 mm-new 03/16] khugepaged: generalize hugepage_vma_revalidate for mTHP support
From: Lorenzo Stoakes @ 2026-01-08 17:05 UTC (permalink / raw)
  To: Nico Pache
  Cc: linux-kernel, linux-trace-kernel, linux-mm, linux-doc, david, ziy,
	baolin.wang, Liam.Howlett, ryan.roberts, dev.jain, corbet,
	rostedt, mhiramat, mathieu.desnoyers, akpm, baohua, willy, peterx,
	wangkefeng.wang, usamaarif642, sunnanyong, vishal.moola,
	thomas.hellstrom, yang, kas, aarcange, raquini, anshuman.khandual,
	catalin.marinas, tiwai, will, dave.hansen, jack, cl, jglisse,
	surenb, zokeefe, hannes, rientjes, mhocko, rdunlap, hughd,
	richard.weiyang, lance.yang, vbabka, rppt, jannh, pfalcato
In-Reply-To: <20251201174627.23295-4-npache@redhat.com>

OK I'll stop reporting the merge conflicts from the that series but yeah here
too I guess it kills a whole bunch :)

On Mon, Dec 01, 2025 at 10:46:14AM -0700, Nico Pache wrote:
> For khugepaged to support different mTHP orders, we must generalize this
> to check if the PMD is not shared by another VMA and that the order is
> enabled.
>
> No functional change in this patch. Also correct a comment about the
> functionality of the revalidation.
>
> Reviewed-by: Wei Yang <richard.weiyang@gmail.com>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
> Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> Acked-by: David Hildenbrand <david@redhat.com>
> Co-developed-by: Dev Jain <dev.jain@arm.com>
> Signed-off-by: Dev Jain <dev.jain@arm.com>
> Signed-off-by: Nico Pache <npache@redhat.com>
> ---
>  mm/khugepaged.c | 20 +++++++++++---------
>  1 file changed, 11 insertions(+), 9 deletions(-)
>
> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index 433ea7283488..69fc6b41f010 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c
> @@ -892,14 +892,13 @@ static int collapse_find_target_node(struct collapse_control *cc)
>
>  /*
>   * If mmap_lock temporarily dropped, revalidate vma
> - * before taking mmap_lock.
> + * after taking the mmap_lock again.
>   * Returns enum scan_result value.
>   */
>
>  static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
> -				   bool expect_anon,
> -				   struct vm_area_struct **vmap,
> -				   struct collapse_control *cc)
> +		bool expect_anon, struct vm_area_struct **vmap,
> +		struct collapse_control *cc, unsigned int order)
>  {
>  	struct vm_area_struct *vma;
>  	enum tva_type type = cc->is_khugepaged ? TVA_KHUGEPAGED :
> @@ -912,15 +911,16 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
>  	if (!vma)
>  		return SCAN_VMA_NULL;
>
> +	/* Always check the PMD order to ensure its not shared by another VMA */
>  	if (!thp_vma_suitable_order(vma, address, PMD_ORDER))
>  		return SCAN_ADDRESS_RANGE;
> -	if (!thp_vma_allowable_order(vma, vma->vm_flags, type, PMD_ORDER))
> +	if (!thp_vma_allowable_orders(vma, vma->vm_flags, type, BIT(order)))
>  		return SCAN_VMA_CHECK;
>  	/*
>  	 * Anon VMA expected, the address may be unmapped then
>  	 * remapped to file after khugepaged reaquired the mmap_lock.
>  	 *
> -	 * thp_vma_allowable_order may return true for qualified file
> +	 * thp_vma_allowable_orders may return true for qualified file
>  	 * vmas.
>  	 */
>  	if (expect_anon && (!(*vmap)->anon_vma || !vma_is_anonymous(*vmap)))
> @@ -1117,7 +1117,8 @@ static int collapse_huge_page(struct mm_struct *mm, unsigned long address,
>  		goto out_nolock;
>
>  	mmap_read_lock(mm);
> -	result = hugepage_vma_revalidate(mm, address, true, &vma, cc);
> +	result = hugepage_vma_revalidate(mm, address, true, &vma, cc,
> +					 HPAGE_PMD_ORDER);
>  	if (result != SCAN_SUCCEED) {
>  		mmap_read_unlock(mm);
>  		goto out_nolock;
> @@ -1151,7 +1152,8 @@ static int collapse_huge_page(struct mm_struct *mm, unsigned long address,
>  	 * mmap_lock.
>  	 */
>  	mmap_write_lock(mm);
> -	result = hugepage_vma_revalidate(mm, address, true, &vma, cc);
> +	result = hugepage_vma_revalidate(mm, address, true, &vma, cc,
> +					 HPAGE_PMD_ORDER);
>  	if (result != SCAN_SUCCEED)
>  		goto out_up_write;
>  	/* check if the pmd is still valid */
> @@ -2814,7 +2816,7 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start,
>  			mmap_read_lock(mm);
>  			mmap_locked = true;
>  			result = hugepage_vma_revalidate(mm, addr, false, &vma,
> -							 cc);
> +							 cc, HPAGE_PMD_ORDER);
>  			if (result  != SCAN_SUCCEED) {
>  				last_fail = result;
>  				goto out_nolock;
> --
> 2.51.1
>

^ permalink raw reply

* Re: [PATCH v13 mm-new 12/16] khugepaged: introduce collapse_allowable_orders helper function
From: Lorenzo Stoakes @ 2026-01-08 17:13 UTC (permalink / raw)
  To: Nico Pache
  Cc: linux-kernel, linux-trace-kernel, linux-mm, linux-doc, david, ziy,
	baolin.wang, Liam.Howlett, ryan.roberts, dev.jain, corbet,
	rostedt, mhiramat, mathieu.desnoyers, akpm, baohua, willy, peterx,
	wangkefeng.wang, usamaarif642, sunnanyong, vishal.moola,
	thomas.hellstrom, yang, kas, aarcange, raquini, anshuman.khandual,
	catalin.marinas, tiwai, will, dave.hansen, jack, cl, jglisse,
	surenb, zokeefe, hannes, rientjes, mhocko, rdunlap, hughd,
	richard.weiyang, lance.yang, vbabka, rppt, jannh, pfalcato
In-Reply-To: <20251201174627.23295-13-npache@redhat.com>

This one seems to also conflict with (current mm tree) commit 85c89728af35
("mm/khugepaged: remove unnecessary goto 'skip' label").

On Mon, Dec 01, 2025 at 10:46:23AM -0700, Nico Pache wrote:
> Add collapse_allowable_orders() to generalize THP order eligibility. The
> function determines which THP orders are permitted based on collapse
> context (khugepaged vs madv_collapse).
>
> This consolidates collapse configuration logic and provides a clean
> interface for future mTHP collapse support where the orders may be
> different.
>
> Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
> Signed-off-by: Nico Pache <npache@redhat.com>
> ---
>  mm/khugepaged.c | 16 +++++++++++++---
>  1 file changed, 13 insertions(+), 3 deletions(-)
>
> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index 049da0305440..33b70ca070b4 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c
> @@ -501,12 +501,22 @@ static unsigned int collapse_max_ptes_none(unsigned int order, bool full_scan)
>  	return -EINVAL;
>  }
>
> +/* Check what orders are allowed based on the vma and collapse type */
> +static unsigned long collapse_allowable_orders(struct vm_area_struct *vma,
> +			vm_flags_t vm_flags, bool is_khugepaged)
> +{
> +	enum tva_type tva_flags = is_khugepaged ? TVA_KHUGEPAGED : TVA_FORCED_COLLAPSE;
> +	unsigned long orders = BIT(HPAGE_PMD_ORDER);
> +
> +	return thp_vma_allowable_orders(vma, vm_flags, tva_flags, orders);
> +}
> +
>  void khugepaged_enter_vma(struct vm_area_struct *vma,
>  			  vm_flags_t vm_flags)
>  {
>  	if (!mm_flags_test(MMF_VM_HUGEPAGE, vma->vm_mm) &&
>  	    hugepage_pmd_enabled()) {
> -		if (thp_vma_allowable_order(vma, vm_flags, TVA_KHUGEPAGED, PMD_ORDER))
> +		if (collapse_allowable_orders(vma, vm_flags, /*is_khugepaged=*/true))
>  			__khugepaged_enter(vma->vm_mm);
>  	}
>  }
> @@ -2606,7 +2616,7 @@ static unsigned int collapse_scan_mm_slot(unsigned int pages, int *result,
>  			progress++;
>  			break;
>  		}
> -		if (!thp_vma_allowable_order(vma, vma->vm_flags, TVA_KHUGEPAGED, PMD_ORDER)) {
> +		if (!collapse_allowable_orders(vma, vma->vm_flags, /*is_khugepaged=*/true)) {
>  skip:
>  			progress++;
>  			continue;
> @@ -2912,7 +2922,7 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start,
>  	BUG_ON(vma->vm_start > start);
>  	BUG_ON(vma->vm_end < end);
>
> -	if (!thp_vma_allowable_order(vma, vma->vm_flags, TVA_FORCED_COLLAPSE, PMD_ORDER))
> +	if (!collapse_allowable_orders(vma, vma->vm_flags, /*is_khugepaged=*/false))
>  		return -EINVAL;
>
>  	cc = kmalloc(sizeof(*cc), GFP_KERNEL);
> --
> 2.51.1
>

^ permalink raw reply

* Re: [PATCH v13 mm-new 00/16] khugepaged: mTHP support
From: Lorenzo Stoakes @ 2026-01-08 17:19 UTC (permalink / raw)
  To: Nico Pache
  Cc: linux-kernel, linux-trace-kernel, linux-mm, linux-doc, david, ziy,
	baolin.wang, Liam.Howlett, ryan.roberts, dev.jain, corbet,
	rostedt, mhiramat, mathieu.desnoyers, akpm, baohua, willy, peterx,
	wangkefeng.wang, usamaarif642, sunnanyong, vishal.moola,
	thomas.hellstrom, yang, kas, aarcange, raquini, anshuman.khandual,
	catalin.marinas, tiwai, will, dave.hansen, jack, cl, jglisse,
	surenb, zokeefe, hannes, rientjes, mhocko, rdunlap, hughd,
	richard.weiyang, lance.yang, vbabka, rppt, jannh, pfalcato
In-Reply-To: <20251201174627.23295-1-npache@redhat.com>

(Sorry for multiple mails replying to same, lei/lore are broken again so my
setup isn't working properly).

I tried to fixup the conflicts here to run tests locally but there's too many
and I messed it up.

Could you please resend this series rebased on mm-unstable please?

Thanks, Lorenzo

On Mon, Dec 01, 2025 at 10:46:11AM -0700, Nico Pache wrote:
> The following series provides khugepaged with the capability to collapse
> anonymous memory regions to mTHPs.
>
> To achieve this we generalize the khugepaged functions to no longer depend
> on PMD_ORDER. Then during the PMD scan, we use a bitmap to track individual
> pages that are occupied (!none/zero). After the PMD scan is done, we use
> the bitmap to find the optimal mTHP sizes for the PMD range. The
> restriction on max_ptes_none is removed during the scan, to make sure we
> account for the whole PMD range in the bitmap. When no mTHP size is
> enabled, the legacy behavior of khugepaged is maintained.
>
> We currently only support max_ptes_none values of 0 or HPAGE_PMD_NR - 1
> (ie 511). If any other value is specified, the kernel will emit a warning
> and no mTHP collapse will be attempted. If a mTHP collapse is attempted,
> but contains swapped out, or shared pages, we don't perform the collapse.
> It is now also possible to collapse to mTHPs without requiring the PMD THP
> size to be enabled. These limitiations are to prevent collapse "creep"
> behavior. This prevents constantly promoting mTHPs to the next available
> size, which would occur because a collapse introduces more non-zero pages
> that would satisfy the promotion condition on subsequent scans.
>
> Patch 1:     Refactor/rename hpage_collapse
> Patch 2:     Refactoring to combine madvise_collapse and khugepaged
> Patch 3-8:   Generalize khugepaged functions for arbitrary orders and
> 	     introduce some helper functions
> Patch 9:     skip collapsing mTHP to smaller orders
> Patch 10-11: Add per-order mTHP statistics and tracepoints
> Patch 12:    Introduce collapse_allowable_orders
> Patch 13-15: Introduce bitmap and mTHP collapse support, fully enabled
> Patch 16:    Documentation
>
> ---------
>  Testing
> ---------
> - Built for x86_64, aarch64, ppc64le, and s390x
> - selftests mm
> - I created a test script that I used to push khugepaged to its limits
>    while monitoring a number of stats and tracepoints. The code is
>    available here[1] (Run in legacy mode for these changes and set mthp
>    sizes to inherit)
>    The summary from my testings was that there was no significant
>    regression noticed through this test. In some cases my changes had
>    better collapse latencies, and was able to scan more pages in the same
>    amount of time/work, but for the most part the results were consistent.
> - redis testing. I tested these changes along with my defer changes
>   (see followup [2] post for more details). We've decided to get the mTHP
>   changes merged first before attempting the defer series.
> - some basic testing on 64k page size.
> - lots of general use.
>
> V13 Changes:
> - Lots of minor nits, cleanups, comments, and renames
> - Bitmap function simplification and more helpers (Wei, Lorenzo)
> - Max_ptes_none (0 or 511) restriction
> - commit description expansion
> - list all reachable enum values in mthp_collapse()
> - Fix ppc64 compile error due to using HPAGE_PMD_ORDER (replace with
>   ilog2(MAX_PTRS_PER_PTE))
>
> V12: https://lore.kernel.org/lkml/20251022183717.70829-1-npache@redhat.com/
> V11: https://lore.kernel.org/lkml/20250912032810.197475-1-npache@redhat.com/
> V10: https://lore.kernel.org/lkml/20250819134205.622806-1-npache@redhat.com/
> V9 : https://lore.kernel.org/lkml/20250714003207.113275-1-npache@redhat.com/
> V8 : https://lore.kernel.org/lkml/20250702055742.102808-1-npache@redhat.com/
> V7 : https://lore.kernel.org/lkml/20250515032226.128900-1-npache@redhat.com/
> V6 : https://lore.kernel.org/lkml/20250515030312.125567-1-npache@redhat.com/
> V5 : https://lore.kernel.org/lkml/20250428181218.85925-1-npache@redhat.com/
> V4 : https://lore.kernel.org/lkml/20250417000238.74567-1-npache@redhat.com/
> V3 : https://lore.kernel.org/lkml/20250414220557.35388-1-npache@redhat.com/
> V2 : https://lore.kernel.org/lkml/20250211003028.213461-1-npache@redhat.com/
> V1 : https://lore.kernel.org/lkml/20250108233128.14484-1-npache@redhat.com/
>
> A big thanks to everyone that has reviewed, tested, and participated in
> the development process. Its been a great experience working with all of
> you on this endeavour.
>
> [1] - https://gitlab.com/npache/khugepaged_mthp_test
> [2] - https://lore.kernel.org/lkml/20250515033857.132535-1-npache@redhat.com/
>
> Baolin Wang (1):
>   khugepaged: run khugepaged for all orders
>
> Dev Jain (1):
>   khugepaged: generalize alloc_charge_folio()
>
> Nico Pache (14):
>   khugepaged: rename hpage_collapse_* to collapse_*
>   introduce collapse_single_pmd to unify khugepaged and madvise_collapse
>   khugepaged: generalize hugepage_vma_revalidate for mTHP support
>   khugepaged: introduce is_mthp_order helper
>   khugepaged: generalize __collapse_huge_page_* for mTHP support
>   khugepaged: introduce collapse_max_ptes_none helper function
>   khugepaged: generalize collapse_huge_page for mTHP collapse
>   khugepaged: skip collapsing mTHP to smaller orders
>   khugepaged: add per-order mTHP collapse failure statistics
>   khugepaged: improve tracepoints for mTHP orders
>   khugepaged: introduce collapse_allowable_orders helper function
>   khugepaged: Introduce mTHP collapse support
>   khugepaged: avoid unnecessary mTHP collapse attempts
>   Documentation: mm: update the admin guide for mTHP collapse
>
>  Documentation/admin-guide/mm/transhuge.rst |  80 ++-
>  include/linux/huge_mm.h                    |   5 +
>  include/trace/events/huge_memory.h         |  34 +-
>  mm/huge_memory.c                           |  11 +
>  mm/khugepaged.c                            | 696 +++++++++++++++------
>  mm/mremap.c                                |   2 +-
>  6 files changed, 618 insertions(+), 210 deletions(-)
>
> --
> 2.51.1
>

^ permalink raw reply

* Re: [PATCH v2] selftests/tracing: Fix test_multiple_writes stall
From: Shuah @ 2026-01-08 18:27 UTC (permalink / raw)
  To: Steven Rostedt, Fushuai Wang
  Cc: linux-kernel, linux-kselftest, linux-trace-kernel,
	mathieu.desnoyers, mhiramat, shuah, wangfushuai, Shuah Khan
In-Reply-To: <20260107101846.61c182e0@gandalf.local.home>

On 1/7/26 08:18, Steven Rostedt wrote:
> On Wed,  7 Jan 2026 11:49:14 +0800
> Fushuai Wang <fushuai.wang@linux.dev> wrote:
> 
>>> When /sys/kernel/tracing/buffer_size_kb is less than 12KB,
>>> the test_multiple_writes test will stall and wait for more
>>> input due to insufficient buffer space.
>>>
>>> Check current buffer_size_kb value before the test. If it is
>>> less than 12KB, it temporarily increase the buffer to 12KB,
>>> and restore the original value after the tests are completed.
>>>
>>> Fixes: 37f46601383a ("selftests/tracing: Add basic test for trace_marker_raw file")
>>> Suggested-by: Steven Rostedt <rostedt@goodmis.org>
>>> Signed-off-by: Fushuai Wang <wangfushuai@baidu.com>
>>
>> Gentle ping.
>>
> 
> Acked-by: Steven Rostedt (Google) <rostedt@goodmis.org>
> 
> Shuah,
> 
> Care to take this through your tree? It does fix a bug so it likely should
> go into the rc releases and not wait for the next merge window.
> 

Yes I can take this through and send this for rc5 or rc6.
But first the following warning needs fixing before I can
apply the patch

WARNING: From:/Signed-off-by: email address mismatch: 'From: Fushuai Wang <fushuai.wang@linux.dev>' != 'Signed-off-by: Fushuai Wang <wangfushuai@baidu.com>'

Fushuai, please send me v3

thanks,
-- Shuah

^ permalink raw reply

* Re: [PATCH v2] selftests/tracing: Fix test_multiple_writes stall
From: Steven Rostedt @ 2026-01-08 19:48 UTC (permalink / raw)
  To: Shuah
  Cc: Fushuai Wang, linux-kernel, linux-kselftest, linux-trace-kernel,
	mathieu.desnoyers, mhiramat, wangfushuai, Shuah Khan
In-Reply-To: <4513bb41-e493-48bb-bd58-f1cc3ea44e5f@kernel.org>

On Thu, 8 Jan 2026 11:27:26 -0700
Shuah <shuah@kernel.org> wrote:

> Yes I can take this through and send this for rc5 or rc6.
> But first the following warning needs fixing before I can
> apply the patch
> 
> WARNING: From:/Signed-off-by: email address mismatch: 'From: Fushuai Wang <fushuai.wang@linux.dev>' != 'Signed-off-by: Fushuai Wang <wangfushuai@baidu.com>'

Hmm, do emails have to match? There's times I used a different SoB than the
From. The From is different than the SoB. The important part I found was to
make sure the name is the same. But I didn't think the email address was
important. And this will become more of an issue, as gmail blocks my
goodmis.org account, so I send with my kernel.org account when my goodmis
is the only one I sign off with. Thus this restriction will fail for me on
my patches.

-- Steve

^ permalink raw reply

* Re: [PATCH v2] selftests/tracing: Fix test_multiple_writes stall
From: Shuah Khan @ 2026-01-08 21:03 UTC (permalink / raw)
  To: Steven Rostedt, Shuah
  Cc: Fushuai Wang, linux-kernel, linux-kselftest, linux-trace-kernel,
	mathieu.desnoyers, mhiramat, wangfushuai, Shuah Khan
In-Reply-To: <20260108144842.0663008c@gandalf.local.home>

On 1/8/26 12:48, Steven Rostedt wrote:
> On Thu, 8 Jan 2026 11:27:26 -0700
> Shuah <shuah@kernel.org> wrote:
> 
>> Yes I can take this through and send this for rc5 or rc6.
>> But first the following warning needs fixing before I can
>> apply the patch
>>
>> WARNING: From:/Signed-off-by: email address mismatch: 'From: Fushuai Wang <fushuai.wang@linux.dev>' != 'Signed-off-by: Fushuai Wang <wangfushuai@baidu.com>'
> 
> Hmm, do emails have to match? There's times I used a different SoB than the
> From. The From is different than the SoB. The important part I found was to
> make sure the name is the same. But I didn't think the email address was
> important. And this will become more of an issue, as gmail blocks my
> goodmis.org account, so I send with my kernel.org account when my goodmis
> is the only one I sign off with. Thus this restriction will fail for me on
> my patches.

I go by the warning - I don't take patches unless From address
matches the SoB.

thanks,
-- Shuah

^ permalink raw reply

* Re: [PATCH v2] selftests/tracing: Fix test_multiple_writes stall
From: Steven Rostedt @ 2026-01-08 21:52 UTC (permalink / raw)
  To: Shuah Khan
  Cc: Shuah, Fushuai Wang, linux-kernel, linux-kselftest,
	linux-trace-kernel, mathieu.desnoyers, mhiramat, wangfushuai
In-Reply-To: <3d31087c-ce86-4ce2-9b70-f955720a7311@linuxfoundation.org>

On Thu, 8 Jan 2026 14:03:31 -0700
Shuah Khan <skhan@linuxfoundation.org> wrote:

> On 1/8/26 12:48, Steven Rostedt wrote:
> > On Thu, 8 Jan 2026 11:27:26 -0700
> > Shuah <shuah@kernel.org> wrote:
> >   
> >> Yes I can take this through and send this for rc5 or rc6.
> >> But first the following warning needs fixing before I can
> >> apply the patch
> >>
> >> WARNING: From:/Signed-off-by: email address mismatch: 'From: Fushuai Wang <fushuai.wang@linux.dev>' != 'Signed-off-by: Fushuai Wang <wangfushuai@baidu.com>'  
> > 
> > Hmm, do emails have to match? There's times I used a different SoB than the
> > From. The From is different than the SoB. The important part I found was to
> > make sure the name is the same. But I didn't think the email address was
> > important. And this will become more of an issue, as gmail blocks my
> > goodmis.org account, so I send with my kernel.org account when my goodmis
> > is the only one I sign off with. Thus this restriction will fail for me on
> > my patches.  
> 
> I go by the warning - I don't take patches unless From address
> matches the SoB.

I guess my patches would then fail your requirements ;-)

Anyway, Fushuai, you can add to the top of your commit:

From: Fushuai Wang <wangfushuai@baidu.com>

and that will make the From and SoB match without having to change your
mail client. I usually have that. Because Linus doesn't like my patches
having my company name in it for the "author" but I have it in the SoB to
give credit to the one paying me to do the work.

-- Steve



^ permalink raw reply

* Re: [PATCH v2] arm64: Disable branch profiling for all arm64 code
From: Will Deacon @ 2026-01-08 22:29 UTC (permalink / raw)
  To: Catalin Marinas, Mark Rutland, Laura Abbott, Breno Leitao
  Cc: kernel-team, Will Deacon, linux-arm-kernel, linux-kernel,
	linux-trace-kernel, Steven Rostedt, Masami Hiramatsu, kernel-team,
	puranjay, stable
In-Reply-To: <20260106-annotated-v2-1-fb7600ebd47f@debian.org>

On Tue, 06 Jan 2026 02:16:35 -0800, Breno Leitao wrote:
> The arm64 kernel doesn't boot with annotated branches
> (PROFILE_ANNOTATED_BRANCHES) enabled and CONFIG_DEBUG_VIRTUAL together.
> 
> Bisecting it, I found that disabling branch profiling in arch/arm64/mm
> solved the problem. Narrowing down a bit further, I found that
> physaddr.c is the file that needs to have branch profiling disabled to
> get the machine to boot.
> 
> [...]

Applied to arm64 (for-next/misc), thanks!

[1/1] arm64: Disable branch profiling for all arm64 code
      https://git.kernel.org/arm64/c/f22c81bebf8b

Cheers,
-- 
Will

https://fixes.arm64.dev
https://next.arm64.dev
https://will.arm64.dev

^ permalink raw reply

* [PATCH] tracing: Remove redundant call to event_trigger_reset_filter() in event_hist_trigger_parse()
From: Steven Rostedt @ 2026-01-08 22:44 UTC (permalink / raw)
  To: LKML, Linux Trace Kernel
  Cc: Masami Hiramatsu, Mathieu Desnoyers, Miaoqian Lin

From: Steven Rostedt <rostedt@goodmis.org>

With the change to replace kfree() with trigger_data_free(), which starts
out doing the exact same thing as event_trigger_reset_filter(), there's no
reason to call event_trigger_reset_filter() before calling
trigger_data_free(). Remove the call to it.

Link: https://lore.kernel.org/linux-trace-kernel/20251211204520.0f3ba6d1@fedora/

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
 kernel/trace/trace_events_hist.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index f9886fff7123..4a7f945e368d 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -6898,8 +6898,6 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops,
  out_unreg:
 	event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
  out_free:
-	event_trigger_reset_filter(cmd_ops, trigger_data);
-
 	remove_hist_vars(hist_data);
 
 	trigger_data_free(trigger_data);
-- 
2.51.0


^ permalink raw reply related

* Re: [PATCH] tracing: Remove redundant call to event_trigger_reset_filter() in event_hist_trigger_parse()
From: Masami Hiramatsu @ 2026-01-08 23:53 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: LKML, Linux Trace Kernel, Masami Hiramatsu, Mathieu Desnoyers,
	Miaoqian Lin
In-Reply-To: <20260108174429.2d9ca51f@gandalf.local.home>

On Thu, 8 Jan 2026 17:44:29 -0500
Steven Rostedt <rostedt@kernel.org> wrote:

> From: Steven Rostedt <rostedt@goodmis.org>
> 
> With the change to replace kfree() with trigger_data_free(), which starts
> out doing the exact same thing as event_trigger_reset_filter(), there's no
> reason to call event_trigger_reset_filter() before calling
> trigger_data_free(). Remove the call to it.
> 
> Link: https://lore.kernel.org/linux-trace-kernel/20251211204520.0f3ba6d1@fedora/
> 

Looks good to me.

Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Thanks,

> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
> ---
>  kernel/trace/trace_events_hist.c | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
> index f9886fff7123..4a7f945e368d 100644
> --- a/kernel/trace/trace_events_hist.c
> +++ b/kernel/trace/trace_events_hist.c
> @@ -6898,8 +6898,6 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops,
>   out_unreg:
>  	event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
>   out_free:
> -	event_trigger_reset_filter(cmd_ops, trigger_data);
> -
>  	remove_hist_vars(hist_data);
>  
>  	trigger_data_free(trigger_data);
> -- 
> 2.51.0
> 


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply

* [PATCH v5] tracing: Guard __DECLARE_TRACE() use of __DO_TRACE_CALL() with SRCU-fast
From: Steven Rostedt @ 2026-01-09  3:05 UTC (permalink / raw)
  To: LKML, Linux trace kernel, bpf
  Cc: Masami Hiramatsu, Mathieu Desnoyers, Paul E. McKenney,
	Sebastian Andrzej Siewior

From: "Paul E. McKenney" <paulmck@kernel.org>

The current use of guard(preempt_notrace)() within __DECLARE_TRACE()
to protect invocation of __DO_TRACE_CALL() means that BPF programs
attached to tracepoints are non-preemptible.  This is unhelpful in
real-time systems, whose users apparently wish to use BPF while also
achieving low latencies.  (Who knew?)

One option would be to use preemptible RCU, but this introduces
many opportunities for infinite recursion, which many consider to
be counterproductive, especially given the relatively small stacks
provided by the Linux kernel.  These opportunities could be shut down
by sufficiently energetic duplication of code, but this sort of thing
is considered impolite in some circles.

Therefore, use the shiny new SRCU-fast API, which provides somewhat faster
readers than those of preemptible RCU, at least on Paul E. McKenney's
laptop, where task_struct access is more expensive than access to per-CPU
variables.  And SRCU-fast provides way faster readers than does SRCU,
courtesy of being able to avoid the read-side use of smp_mb().  Also,
it is quite straightforward to create srcu_read_{,un}lock_fast_notrace()
functions.

While in the area, SRCU now supports early boot call_srcu().  Therefore,
remove the checks that used to avoid such use from rcu_free_old_probes()
before this commit was applied:

e53244e2c893 ("tracepoint: Remove SRCU protection")

The current commit can be thought of as an approximate revert of that
commit, with some compensating additions of preemption disabling.
This preemption disabling uses guard(preempt_notrace)().

However, Yonghong Song points out that BPF assumes that non-sleepable
BPF programs will remain on the same CPU, which means that migration
must be disabled whenever preemption remains enabled.  In addition,
non-RT kernels have performance expectations that would be violated by
allowing the BPF programs to be preempted.

Therefore, continue to disable preemption in non-RT kernels, and protect
the BPF program with both SRCU and migration disabling for RT kernels,
and even then only if preemption is not already disabled.

Link: https://lore.kernel.org/all/20250613152218.1924093-1-bigeasy@linutronix.de/

Co-developed-by: Steve Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Changes since v4: https://lore.kernel.org/all/20251216120819.3499e00e@gandalf.local.home/

- Added kerneldoc to trace_event_buffer_reserve{_syscall}

- Restructured the tracepoint.c code to not have #ifdef within functions

 include/linux/trace_events.h  | 24 ++++++++++++++
 include/linux/tracepoint.h    | 25 ++++++++++++--
 include/trace/perf.h          |  4 +--
 include/trace/trace_events.h  | 21 ++++++++++--
 kernel/trace/trace_events.c   | 61 +++++++++++++++++++++++++++++------
 kernel/trace/trace_syscalls.c |  4 +--
 kernel/tracepoint.c           | 44 +++++++++++++++++++++++++
 7 files changed, 164 insertions(+), 19 deletions(-)

diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 3690221ba3d8..a2704c35eda8 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -222,6 +222,26 @@ static inline unsigned int tracing_gen_ctx_dec(void)
 	return trace_ctx;
 }
 
+/*
+ * When PREEMPT_RT is enabled, trace events are called with disabled
+ * migration. The trace events need to know if the tracepoint disabled
+ * migration or not so that what is recorded to the ring buffer shows
+ * the state of when the trace event triggered, and not the state caused
+ * by the trace event.
+ */
+#ifdef CONFIG_PREEMPT_RT
+static inline unsigned int tracing_gen_ctx_dec_cond(void)
+{
+	unsigned int trace_ctx;
+
+	trace_ctx = tracing_gen_ctx_dec();
+	/* The migration counter starts at bit 4 */
+	return trace_ctx - (1 << 4);
+}
+#else
+# define tracing_gen_ctx_dec_cond() tracing_gen_ctx_dec()
+#endif
+
 struct trace_event_file;
 
 struct ring_buffer_event *
@@ -313,6 +333,10 @@ void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,
 				  struct trace_event_file *trace_file,
 				  unsigned long len);
 
+void *trace_event_buffer_reserve_syscall(struct trace_event_buffer *fbuffer,
+					 struct trace_event_file *trace_file,
+					 unsigned long len);
+
 void trace_event_buffer_commit(struct trace_event_buffer *fbuffer);
 
 enum {
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index 8a56f3278b1b..0563c7d9fcb2 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -100,6 +100,25 @@ void for_each_tracepoint_in_module(struct module *mod,
 }
 #endif /* CONFIG_MODULES */
 
+/*
+ * BPF programs can attach to the tracepoint callbacks. But if the
+ * callbacks are called with preemption disabled, the BPF programs
+ * can cause quite a bit of latency. When PREEMPT_RT is enabled,
+ * instead of disabling preemption, use srcu_fast_notrace() for
+ * synchronization. As BPF programs that are attached to tracepoints
+ * expect to stay on the same CPU, also disable migration.
+ */
+#ifdef CONFIG_PREEMPT_RT
+extern struct srcu_struct tracepoint_srcu;
+# define tracepoint_sync() synchronize_srcu(&tracepoint_srcu);
+# define tracepoint_guard()				\
+	guard(srcu_fast_notrace)(&tracepoint_srcu);	\
+	guard(migrate)()
+#else
+# define tracepoint_sync() synchronize_rcu();
+# define tracepoint_guard() guard(preempt_notrace)()
+#endif
+
 /*
  * tracepoint_synchronize_unregister must be called between the last tracepoint
  * probe unregistration and the end of module exit to make sure there is no
@@ -115,7 +134,7 @@ void for_each_tracepoint_in_module(struct module *mod,
 static inline void tracepoint_synchronize_unregister(void)
 {
 	synchronize_rcu_tasks_trace();
-	synchronize_rcu();
+	tracepoint_sync();
 }
 static inline bool tracepoint_is_faultable(struct tracepoint *tp)
 {
@@ -275,13 +294,13 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
 		return static_branch_unlikely(&__tracepoint_##name.key);\
 	}
 
-#define __DECLARE_TRACE(name, proto, args, cond, data_proto)		\
+#define __DECLARE_TRACE(name, proto, args, cond, data_proto)			\
 	__DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) \
 	static inline void __do_trace_##name(proto)			\
 	{								\
 		TRACEPOINT_CHECK(name)					\
 		if (cond) {						\
-			guard(preempt_notrace)();			\
+			tracepoint_guard();				\
 			__DO_TRACE_CALL(name, TP_ARGS(args));		\
 		}							\
 	}								\
diff --git a/include/trace/perf.h b/include/trace/perf.h
index a1754b73a8f5..348ad1d9b556 100644
--- a/include/trace/perf.h
+++ b/include/trace/perf.h
@@ -71,6 +71,7 @@ perf_trace_##call(void *__data, proto)					\
 	u64 __count __attribute__((unused));				\
 	struct task_struct *__task __attribute__((unused));		\
 									\
+	guard(preempt_notrace)();					\
 	do_perf_trace_##call(__data, args);				\
 }
 
@@ -85,9 +86,8 @@ perf_trace_##call(void *__data, proto)					\
 	struct task_struct *__task __attribute__((unused));		\
 									\
 	might_fault();							\
-	preempt_disable_notrace();					\
+	guard(preempt_notrace)();					\
 	do_perf_trace_##call(__data, args);				\
-	preempt_enable_notrace();					\
 }
 
 /*
diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h
index 4f22136fd465..6fb58387e9f1 100644
--- a/include/trace/trace_events.h
+++ b/include/trace/trace_events.h
@@ -429,6 +429,22 @@ do_trace_event_raw_event_##call(void *__data, proto)			\
 	trace_event_buffer_commit(&fbuffer);				\
 }
 
+/*
+ * When PREEMPT_RT is enabled, the tracepoint does not disable preemption
+ * but instead disables migration. The callbacks for the trace events
+ * need to have a consistent state so that it can reflect the proper
+ * preempt_disabled counter.
+ */
+#ifdef CONFIG_PREEMPT_RT
+/* disable preemption for RT so that the counters still match */
+# define trace_event_guard() guard(preempt_notrace)()
+/* Have syscalls up the migrate disable counter to emulate non-syscalls */
+# define trace_syscall_event_guard() guard(migrate)()
+#else
+# define trace_event_guard()
+# define trace_syscall_event_guard()
+#endif
+
 #undef DECLARE_EVENT_CLASS
 #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print)	\
 __DECLARE_EVENT_CLASS(call, PARAMS(proto), PARAMS(args), PARAMS(tstruct), \
@@ -436,6 +452,7 @@ __DECLARE_EVENT_CLASS(call, PARAMS(proto), PARAMS(args), PARAMS(tstruct), \
 static notrace void							\
 trace_event_raw_event_##call(void *__data, proto)			\
 {									\
+	trace_event_guard();						\
 	do_trace_event_raw_event_##call(__data, args);			\
 }
 
@@ -447,9 +464,9 @@ static notrace void							\
 trace_event_raw_event_##call(void *__data, proto)			\
 {									\
 	might_fault();							\
-	preempt_disable_notrace();					\
+	trace_syscall_event_guard();					\
+	guard(preempt_notrace)();					\
 	do_trace_event_raw_event_##call(__data, args);			\
-	preempt_enable_notrace();					\
 }
 
 /*
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 137b4d9bb116..758c8a4ec7c2 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -649,9 +649,9 @@ bool trace_event_ignore_this_pid(struct trace_event_file *trace_file)
 }
 EXPORT_SYMBOL_GPL(trace_event_ignore_this_pid);
 
-void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,
-				 struct trace_event_file *trace_file,
-				 unsigned long len)
+static __always_inline void *buffer_reserve(struct trace_event_buffer *fbuffer,
+					    struct trace_event_file *trace_file,
+					    unsigned long len)
 {
 	struct trace_event_call *event_call = trace_file->event_call;
 
@@ -659,13 +659,6 @@ void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,
 	    trace_event_ignore_this_pid(trace_file))
 		return NULL;
 
-	/*
-	 * If CONFIG_PREEMPTION is enabled, then the tracepoint itself disables
-	 * preemption (adding one to the preempt_count). Since we are
-	 * interested in the preempt_count at the time the tracepoint was
-	 * hit, we need to subtract one to offset the increment.
-	 */
-	fbuffer->trace_ctx = tracing_gen_ctx_dec();
 	fbuffer->trace_file = trace_file;
 
 	fbuffer->event =
@@ -679,8 +672,56 @@ void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,
 	fbuffer->entry = ring_buffer_event_data(fbuffer->event);
 	return fbuffer->entry;
 }
+
+/**
+ * trace_event_buffer_reserve - reserve space on the ring buffer for an event
+ * @fbuffer: information about how to save the event
+ * @trace_file: the instance file descriptor for the event
+ * @len: The length of the event
+ *
+ * The @fbuffer has information about the ring buffer and data will
+ * be added to it to be used by the call to trace_event_buffer_commit().
+ * The @trace_file is the desrciptor with information about the status
+ * of the given event for a specific trace_array instance.
+ * The @len is the length of data to save for the event.
+ *
+ * Returns a pointer to the data on the ring buffer or NULL if the
+ *   event was not reserved (event was filtered, too big, or the buffer
+ *   simply was disabled for write).
+ */
+void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,
+				 struct trace_event_file *trace_file,
+				 unsigned long len)
+{
+	fbuffer->trace_ctx = tracing_gen_ctx_dec_cond();
+	return buffer_reserve(fbuffer, trace_file, len);
+}
 EXPORT_SYMBOL_GPL(trace_event_buffer_reserve);
 
+/**
+ * trace_event_buffer_reserve_syscall - reserve space for a syscall event
+ * @fbuffer: information about how to save the event
+ * @trace_file: the instance file descriptor for the event
+ * @len: The length of the event
+ *
+ * This behaves exactly the same as trace_event_buffer_reserve(), but
+ * needs to act slightly different for system call events when PREEMPT_RT
+ * is enabled. The way the preempt count and migration disabled are
+ * set is different than trace_event_buffer_reserve(), as normal events
+ * have migration disabled instead of preemption in PREEMPT_RT, system
+ * call events do not disable migrating but still disable preemption.
+ *
+ * Returns the same as trace_event_buffer_reserve().
+ */
+void *trace_event_buffer_reserve_syscall(struct trace_event_buffer *fbuffer,
+					 struct trace_event_file *trace_file,
+					 unsigned long len)
+{
+	fbuffer->trace_ctx = tracing_gen_ctx_dec();
+	return buffer_reserve(fbuffer, trace_file, len);
+}
+
+
 int trace_event_reg(struct trace_event_call *call,
 		    enum trace_reg type, void *data)
 {
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c
index e96d0063cbcf..f330fd22ea78 100644
--- a/kernel/trace/trace_syscalls.c
+++ b/kernel/trace/trace_syscalls.c
@@ -909,7 +909,7 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)
 
 	size += sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
 
-	entry = trace_event_buffer_reserve(&fbuffer, trace_file, size);
+	entry = trace_event_buffer_reserve_syscall(&fbuffer, trace_file, size);
 	if (!entry)
 		return;
 
@@ -955,7 +955,7 @@ static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret)
 	if (!sys_data)
 		return;
 
-	entry = trace_event_buffer_reserve(&fbuffer, trace_file, sizeof(*entry));
+	entry = trace_event_buffer_reserve_syscall(&fbuffer, trace_file, sizeof(*entry));
 	if (!entry)
 		return;
 
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c
index 62719d2941c9..17e11d91d029 100644
--- a/kernel/tracepoint.c
+++ b/kernel/tracepoint.c
@@ -34,9 +34,32 @@ enum tp_transition_sync {
 
 struct tp_transition_snapshot {
 	unsigned long rcu;
+	unsigned long srcu_gp;
 	bool ongoing;
 };
 
+/* In PREEMPT_RT, SRCU is used to protect the tracepoint callbacks */
+#ifdef CONFIG_PREEMPT_RT
+DEFINE_SRCU_FAST(tracepoint_srcu);
+EXPORT_SYMBOL_GPL(tracepoint_srcu);
+static inline void start_rt_synchronize(struct tp_transition_snapshot *snapshot)
+{
+	snapshot->srcu_gp = start_poll_synchronize_srcu(&tracepoint_srcu);
+}
+static inline void poll_rt_synchronize(struct tp_transition_snapshot *snapshot)
+{
+	if (!poll_state_synchronize_srcu(&tracepoint_srcu, snapshot->srcu_gp))
+		synchronize_srcu(&tracepoint_srcu);
+}
+#else
+static inline void start_rt_synchronize(struct tp_transition_snapshot *snapshot)
+{
+}
+static inline void poll_rt_synchronize(struct tp_transition_snapshot *snapshot)
+{
+}
+#endif
+
 /* Protected by tracepoints_mutex */
 static struct tp_transition_snapshot tp_transition_snapshot[_NR_TP_TRANSITION_SYNC];
 
@@ -46,6 +69,7 @@ static void tp_rcu_get_state(enum tp_transition_sync sync)
 
 	/* Keep the latest get_state snapshot. */
 	snapshot->rcu = get_state_synchronize_rcu();
+	start_rt_synchronize(snapshot);
 	snapshot->ongoing = true;
 }
 
@@ -56,6 +80,7 @@ static void tp_rcu_cond_sync(enum tp_transition_sync sync)
 	if (!snapshot->ongoing)
 		return;
 	cond_synchronize_rcu(snapshot->rcu);
+	poll_rt_synchronize(snapshot);
 	snapshot->ongoing = false;
 }
 
@@ -101,10 +126,22 @@ static inline void *allocate_probes(int count)
 	return p == NULL ? NULL : p->probes;
 }
 
+#ifdef CONFIG_PREEMPT_RT
+static void srcu_free_old_probes(struct rcu_head *head)
+{
+	kfree(container_of(head, struct tp_probes, rcu));
+}
+
+static void rcu_free_old_probes(struct rcu_head *head)
+{
+	call_srcu(&tracepoint_srcu, head, srcu_free_old_probes);
+}
+#else
 static void rcu_free_old_probes(struct rcu_head *head)
 {
 	kfree(container_of(head, struct tp_probes, rcu));
 }
+#endif
 
 static inline void release_probes(struct tracepoint *tp, struct tracepoint_func *old)
 {
@@ -112,6 +149,13 @@ static inline void release_probes(struct tracepoint *tp, struct tracepoint_func
 		struct tp_probes *tp_probes = container_of(old,
 			struct tp_probes, probes[0]);
 
+		/*
+		 * Tracepoint probes are protected by either RCU or
+		 * Tasks Trace RCU and also by SRCU.  By calling the SRCU
+		 * callback in the [Tasks Trace] RCU callback we cover
+		 * both cases. So let us chain the SRCU and [Tasks Trace]
+		 * RCU callbacks to wait for both grace periods.
+		 */
 		if (tracepoint_is_faultable(tp))
 			call_rcu_tasks_trace(&tp_probes->rcu, rcu_free_old_probes);
 		else
-- 
2.51.0


^ permalink raw reply related

* Re: [PATCH v2] selftests/tracing: Fix test_multiple_writes stall
From: Fushuai Wang @ 2026-01-09  3:27 UTC (permalink / raw)
  To: rostedt
  Cc: fushuai.wang, linux-kernel, linux-kselftest, linux-trace-kernel,
	mathieu.desnoyers, mhiramat, shuah, skhan, wangfushuai
In-Reply-To: <20260108165247.3ae2f21a@gandalf.local.home>

> Anyway, Fushuai, you can add to the top of your commit:
> 
> From: Fushuai Wang <wangfushuai@baidu.com>
> 
> and that will make the From and SoB match without having to change your
> mail client. I usually have that. Because Linus doesn't like my patches
> having my company name in it for the "author" but I have it in the SoB to
> give credit to the one paying me to do the work.

Thanks!
I will send a v3 shortly.

---
Regards,
WANG

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox