Linux Trace Kernel
 help / color / mirror / Atom feed
* [PATCH v3 2/2] spi: qcom-geni: add GENI SE registers trace event on error paths
From: Praveen Talari @ 2026-07-15  5:20 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Steven Rostedt, Masami Hiramatsu,
	Mathieu Desnoyers, Mark Brown
  Cc: Praveen Talari, linux-kernel, linux-arm-msm, linux-trace-kernel,
	linux-spi, Mukesh Kumar Savaliya, Konrad Dybcio
In-Reply-To: <20260715-add-tracepoints-for-se-reg-dump-v3-0-0f787f93badd@oss.qualcomm.com>

The GENI SPI driver reports various transfer failures such as command
timeouts, DMA reset timeouts, DMA transaction errors, and unexpected
interrupt conditions. However, diagnosing the root cause of these
failures is difficult as the hardware state is not captured when the
error occurs.

Add trace_geni_se_regs() calls at critical SPI error handling paths to
automatically capture GENI serial engine debug registers when failures
are detected. This includes:

- M_CMD abort/cancel timeout
- DMA TX/RX FSM reset timeout
- DMA transaction failures and pending residue conditions
- Unexpected interrupt error status
- Premature transfer completion with pending TX/RX data

Dumping the SE debug registers at the time of failure provides
additional hardware context and significantly improves post-mortem
analysis of SPI transfer issues without affecting normal operation.

Acked-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
 drivers/spi/spi-geni-qcom.c | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 2914d781dbf5..8528f9b80f03 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 // Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
 
+#include <trace/events/qcom_geni_se.h>
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/qcom_geni_spi.h>
 
@@ -192,6 +194,7 @@ static void handle_se_timeout(struct spi_controller *spi)
 	time_left = wait_for_completion_timeout(&mas->abort_done, HZ);
 	if (!time_left) {
 		dev_err(mas->dev, "Failed to cancel/abort m_cmd\n");
+		trace_geni_se_regs(se);
 
 		/*
 		 * No need for a lock since SPI core has a lock and we never
@@ -209,8 +212,10 @@ static void handle_se_timeout(struct spi_controller *spi)
 				writel(1, se->base + SE_DMA_TX_FSM_RST);
 				spin_unlock_irq(&mas->lock);
 				time_left = wait_for_completion_timeout(&mas->tx_reset_done, HZ);
-				if (!time_left)
+				if (!time_left) {
 					dev_err(mas->dev, "DMA TX RESET failed\n");
+					trace_geni_se_regs(se);
+				}
 			}
 			if (xfer->rx_buf) {
 				spin_lock_irq(&mas->lock);
@@ -218,8 +223,10 @@ static void handle_se_timeout(struct spi_controller *spi)
 				writel(1, se->base + SE_DMA_RX_FSM_RST);
 				spin_unlock_irq(&mas->lock);
 				time_left = wait_for_completion_timeout(&mas->rx_reset_done, HZ);
-				if (!time_left)
+				if (!time_left) {
 					dev_err(mas->dev, "DMA RX RESET failed\n");
+					trace_geni_se_regs(se);
+				}
 			}
 		} else {
 			/*
@@ -391,10 +398,12 @@ static void
 spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
 {
 	struct spi_controller *spi = cb;
+	struct spi_geni_master *mas = spi_controller_get_devdata(spi);
 
 	spi->cur_msg->status = -EIO;
 	if (result->result != DMA_TRANS_NOERROR) {
 		dev_err(&spi->dev, "DMA txn failed: %d\n", result->result);
+		trace_geni_se_regs(&mas->se);
 		spi_finalize_current_transfer(spi);
 		return;
 	}
@@ -404,6 +413,7 @@ spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
 		dev_dbg(&spi->dev, "DMA txn completed\n");
 	} else {
 		dev_err(&spi->dev, "DMA xfer has pending: %d\n", result->residue);
+		trace_geni_se_regs(&mas->se);
 	}
 
 	spi_finalize_current_transfer(spi);
@@ -953,8 +963,10 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
 
 	if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |
 		     M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN |
-		     M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN))
+		     M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN)) {
 		dev_warn(mas->dev, "Unexpected IRQ err status %#010x\n", m_irq);
+		trace_geni_se_regs(se);
+	}
 
 	spin_lock(&mas->lock);
 
@@ -983,13 +995,16 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
 				 * weren't written correctly.
 				 */
 				if (mas->tx_rem_bytes) {
+					trace_geni_se_regs(se);
 					writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
 					dev_err(mas->dev, "Premature done. tx_rem = %d bpw%d\n",
 						mas->tx_rem_bytes, mas->cur_bits_per_word);
 				}
-				if (mas->rx_rem_bytes)
+				if (mas->rx_rem_bytes) {
 					dev_err(mas->dev, "Premature done. rx_rem = %d bpw%d\n",
 						mas->rx_rem_bytes, mas->cur_bits_per_word);
+					trace_geni_se_regs(se);
+				}
 			} else {
 				complete(&mas->cs_done);
 			}

-- 
2.34.1


^ permalink raw reply related

* [PATCH 1/2] rv/nomiss: fix task storage lifetime
From: Li Qiang @ 2026-07-15  6:00 UTC (permalink / raw)
  To: linux-trace-kernel
  Cc: linux-kernel, rostedt, gmonaco, mhiramat, mathieu.desnoyers,
	namcao, juri.lelli, wen.yang, Li Qiang

The nomiss monitor stores a pointer to a task's embedded deadline entity
in per-PID object storage. The exit handler only removed that storage when
the task was still SCHED_DEADLINE. A task that changed to another policy
before exiting left its entry behind. Once the PID was reused, the new task
could reuse the old entry and dereference the freed deadline entity.

Remove storage unconditionally at sched_process_exit. Serialize creation
and destruction with locks sharded by PID, and do not create storage once
PF_EXITING is set. Each shard tracks both stored entries and in-flight
creators, allowing exits for shards without storage to avoid locking and
hash lookups while preserving the create/exit ordering.

Fixes: b133207deb72 ("rv: Add nomiss deadline monitor")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
 kernel/trace/rv/monitors/deadline/deadline.h | 86 +++++++++++++++++++-
 kernel/trace/rv/monitors/nomiss/nomiss.c     |  2 +-
 2 files changed, 84 insertions(+), 4 deletions(-)

diff --git a/kernel/trace/rv/monitors/deadline/deadline.h b/kernel/trace/rv/monitors/deadline/deadline.h
index 78fca873d61e..b92480e79807 100644
--- a/kernel/trace/rv/monitors/deadline/deadline.h
+++ b/kernel/trace/rv/monitors/deadline/deadline.h
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 
 #include <linux/kernel.h>
+#include <linux/hash.h>
+#include <linux/spinlock.h>
 #include <linux/uaccess.h>
 #include <linux/sched/deadline.h>
 #include <asm/syscall.h>
@@ -148,6 +150,37 @@ static inline struct sched_dl_entity *get_server(struct task_struct *tsk, u8 typ
 	return NULL;
 }
 
+#define DEADLINE_STORAGE_LOCK_BITS	6
+#define DEADLINE_STORAGE_LOCKS		BIT(DEADLINE_STORAGE_LOCK_BITS)
+
+/*
+ * A shard tracks both entries and creators which have not yet checked
+ * PF_EXITING. This lets unrelated exits avoid taking a storage lock while
+ * preserving the create/exit ordering.
+ */
+struct deadline_storage_shard {
+	raw_spinlock_t lock;
+	atomic_t users;
+} ____cacheline_aligned_in_smp;
+
+static struct deadline_storage_shard
+	deadline_storage_shards[DEADLINE_STORAGE_LOCKS];
+
+static inline struct deadline_storage_shard *deadline_storage_shard(int pid)
+{
+	return &deadline_storage_shards[hash_32(pid, DEADLINE_STORAGE_LOCK_BITS)];
+}
+
+static void deadline_storage_init(void)
+{
+	int i;
+
+	for (i = 0; i < DEADLINE_STORAGE_LOCKS; i++) {
+		raw_spin_lock_init(&deadline_storage_shards[i].lock);
+		atomic_set(&deadline_storage_shards[i].users, 0);
+	}
+}
+
 /*
  * Initialise monitors for all tasks and pre-allocate the storage for servers.
  * This is necessary since we don't have access to the servers here and
@@ -159,6 +192,8 @@ static inline int init_storage(bool skip_tasks)
 	struct task_struct *g, *p;
 	int cpu;
 
+	deadline_storage_init();
+
 	for_each_possible_cpu(cpu) {
 		if (!da_create_empty_storage(fair_server_id(cpu)))
 			goto fail;
@@ -177,6 +212,7 @@ static inline int init_storage(bool skip_tasks)
 				read_unlock(&tasklist_lock);
 				goto fail;
 			}
+			atomic_inc(&deadline_storage_shard(p->pid)->users);
 		}
 	}
 	read_unlock(&tasklist_lock);
@@ -187,17 +223,61 @@ static inline int init_storage(bool skip_tasks)
 	return -ENOMEM;
 }
 
+static void deadline_create_task_storage(struct task_struct *task)
+{
+	struct deadline_storage_shard *shard = deadline_storage_shard(task->pid);
+	bool created = false;
+
+	/*
+	 * The temporary reference makes an in-flight creator visible to exit.
+	 * If storage is created, it becomes the reference owned by that entry.
+	 */
+	atomic_inc(&shard->users);
+	/* Pair with exit before it tests users without taking the shard lock. */
+	smp_mb__after_atomic();
+
+	raw_spin_lock(&shard->lock);
+	if (!(READ_ONCE(task->flags) & PF_EXITING)) {
+		guard(rcu)();
+		if (!da_get_monitor(EXPAND_ID_TASK(task)) &&
+		    da_create_storage(EXPAND_ID_TASK(task), NULL))
+			created = true;
+	}
+	raw_spin_unlock(&shard->lock);
+
+	if (!created)
+		atomic_dec(&shard->users);
+}
+
+static void deadline_destroy_task_storage(struct task_struct *task)
+{
+	struct deadline_storage_shard *shard = deadline_storage_shard(task->pid);
+
+	/* Pair with the creator barrier before taking the no-storage fast path. */
+	smp_mb();
+	if (!atomic_read(&shard->users))
+		return;
+
+	raw_spin_lock(&shard->lock);
+	guard(rcu)();
+	if (da_get_monitor(task->pid, NULL)) {
+		/* A task may leave SCHED_DEADLINE before exiting. */
+		da_destroy_storage(task->pid);
+		atomic_dec(&shard->users);
+	}
+	raw_spin_unlock(&shard->lock);
+}
+
 static void __maybe_unused handle_newtask(void *data, struct task_struct *task, u64 flags)
 {
 	/* Might be superfluous as tasks are not started with this policy.. */
 	if (task->policy == SCHED_DEADLINE)
-		da_create_storage(EXPAND_ID_TASK(task), NULL);
+		deadline_create_task_storage(task);
 }
 
 static void __maybe_unused handle_exit(void *data, struct task_struct *p, bool group_dead)
 {
-	if (p->policy == SCHED_DEADLINE)
-		da_destroy_storage(get_entity_id(&p->dl, DL_TASK, DL_TASK));
+	deadline_destroy_task_storage(p);
 }
 
 #endif
diff --git a/kernel/trace/rv/monitors/nomiss/nomiss.c b/kernel/trace/rv/monitors/nomiss/nomiss.c
index 8ead8783c29f..2e5ee681505b 100644
--- a/kernel/trace/rv/monitors/nomiss/nomiss.c
+++ b/kernel/trace/rv/monitors/nomiss/nomiss.c
@@ -214,7 +214,7 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
 	if (p->policy == SCHED_DEADLINE)
 		da_reset(EXPAND_ID_TASK(p));
 	else if (new_policy == SCHED_DEADLINE)
-		da_create_or_get(EXPAND_ID_TASK(p));
+		deadline_create_task_storage(p);
 }
 
 static void handle_sched_wakeup(void *data, struct task_struct *tsk)
-- 
2.50.1


^ permalink raw reply related

* [PATCH 2/2] rv/nomiss: close enable-time task exit race
From: Li Qiang @ 2026-07-15  6:00 UTC (permalink / raw)
  To: linux-trace-kernel
  Cc: linux-kernel, rostedt, gmonaco, mhiramat, mathieu.desnoyers,
	namcao, juri.lelli, wen.yang, Li Qiang
In-Reply-To: <20260715060023.1502983-1-liqiang01@kylinos.cn>

The initial task scan runs before the first patch registers the task exit
tracepoint. A task can begin exiting after it was selected by the scan but
before the exit handler is attached. sched_process_exit runs before
exit_notify() removes the task from the task list, so the scan could then
store a pointer to the exiting task's deadline entity after its cleanup
event had already been missed.

Initialize the shard state and attach the new-task, exit, and syscall
tracepoints before the initial scan. Make the scan use the same
PF_EXITING-aware creation helper as dynamic policy changes. On failure,
detach those tracepoints before destroying the monitor storage.

Fixes: b133207deb72 ("rv: Add nomiss deadline monitor")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
 kernel/trace/rv/monitors/deadline/deadline.h | 27 ++++++++++-------
 kernel/trace/rv/monitors/nomiss/nomiss.c     | 18 +++++++----
 2 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/kernel/trace/rv/monitors/deadline/deadline.h b/kernel/trace/rv/monitors/deadline/deadline.h
index b92480e79807..4626d6e2b218 100644
--- a/kernel/trace/rv/monitors/deadline/deadline.h
+++ b/kernel/trace/rv/monitors/deadline/deadline.h
@@ -181,6 +181,8 @@ static void deadline_storage_init(void)
 	}
 }
 
+static int deadline_create_task_storage(struct task_struct *task);
+
 /*
  * Initialise monitors for all tasks and pre-allocate the storage for servers.
  * This is necessary since we don't have access to the servers here and
@@ -190,9 +192,7 @@ static void deadline_storage_init(void)
 static inline int init_storage(bool skip_tasks)
 {
 	struct task_struct *g, *p;
-	int cpu;
-
-	deadline_storage_init();
+	int cpu, ret;
 
 	for_each_possible_cpu(cpu) {
 		if (!da_create_empty_storage(fair_server_id(cpu)))
@@ -208,25 +208,25 @@ static inline int init_storage(bool skip_tasks)
 	read_lock(&tasklist_lock);
 	for_each_process_thread(g, p) {
 		if (p->policy == SCHED_DEADLINE) {
-			if (!da_create_storage(EXPAND_ID_TASK(p), NULL)) {
+			ret = deadline_create_task_storage(p);
+			if (ret && ret != -ESRCH) {
 				read_unlock(&tasklist_lock);
-				goto fail;
+				return ret;
 			}
-			atomic_inc(&deadline_storage_shard(p->pid)->users);
 		}
 	}
 	read_unlock(&tasklist_lock);
 	return 0;
 
 fail:
-	da_monitor_destroy();
 	return -ENOMEM;
 }
 
-static void deadline_create_task_storage(struct task_struct *task)
+static int deadline_create_task_storage(struct task_struct *task)
 {
 	struct deadline_storage_shard *shard = deadline_storage_shard(task->pid);
 	bool created = false;
+	int ret = -ESRCH;
 
 	/*
 	 * The temporary reference makes an in-flight creator visible to exit.
@@ -239,14 +239,21 @@ static void deadline_create_task_storage(struct task_struct *task)
 	raw_spin_lock(&shard->lock);
 	if (!(READ_ONCE(task->flags) & PF_EXITING)) {
 		guard(rcu)();
-		if (!da_get_monitor(EXPAND_ID_TASK(task)) &&
-		    da_create_storage(EXPAND_ID_TASK(task), NULL))
+		if (da_get_monitor(EXPAND_ID_TASK(task))) {
+			ret = 0;
+		} else if (da_create_storage(EXPAND_ID_TASK(task), NULL)) {
 			created = true;
+			ret = 0;
+		} else {
+			ret = -ENOMEM;
+		}
 	}
 	raw_spin_unlock(&shard->lock);
 
 	if (!created)
 		atomic_dec(&shard->users);
+
+	return ret;
 }
 
 static void deadline_destroy_task_storage(struct task_struct *task)
diff --git a/kernel/trace/rv/monitors/nomiss/nomiss.c b/kernel/trace/rv/monitors/nomiss/nomiss.c
index 2e5ee681505b..843262779a41 100644
--- a/kernel/trace/rv/monitors/nomiss/nomiss.c
+++ b/kernel/trace/rv/monitors/nomiss/nomiss.c
@@ -231,18 +231,26 @@ static int enable_nomiss(void)
 	if (retval)
 		return retval;
 
+	deadline_storage_init();
+	rv_attach_trace_probe("nomiss", task_newtask, handle_newtask);
+	rv_attach_trace_probe("nomiss", sched_process_exit, handle_exit);
+	if (!should_skip_syscall_handle())
+		rv_attach_trace_probe("nomiss", sys_enter, handle_sys_enter);
+
 	retval = init_storage(false);
-	if (retval)
+	if (retval) {
+		rv_detach_trace_probe("nomiss", task_newtask, handle_newtask);
+		rv_detach_trace_probe("nomiss", sched_process_exit, handle_exit);
+		if (!should_skip_syscall_handle())
+			rv_detach_trace_probe("nomiss", sys_enter, handle_sys_enter);
+		ha_monitor_destroy();
 		return retval;
+	}
 	rv_attach_trace_probe("nomiss", sched_dl_replenish_tp, handle_dl_replenish);
 	rv_attach_trace_probe("nomiss", sched_dl_throttle_tp, handle_dl_throttle);
 	rv_attach_trace_probe("nomiss", sched_dl_server_stop_tp, handle_dl_server_stop);
 	rv_attach_trace_probe("nomiss", sched_switch, handle_sched_switch);
 	rv_attach_trace_probe("nomiss", sched_wakeup, handle_sched_wakeup);
-	if (!should_skip_syscall_handle())
-		rv_attach_trace_probe("nomiss", sys_enter, handle_sys_enter);
-	rv_attach_trace_probe("nomiss", task_newtask, handle_newtask);
-	rv_attach_trace_probe("nomiss", sched_process_exit, handle_exit);
 
 	return 0;
 }
-- 
2.50.1


^ permalink raw reply related

* [PATCH] tracing: Propagate errors from remote event bulk updates
From: Jackie Liu @ 2026-07-15  6:04 UTC (permalink / raw)
  To: rostedt, mhiramat; +Cc: mathieu.desnoyers, linux-trace-kernel

From: Jackie Liu <liuyun01@kylinos.cn>

remote_events_dir_enable_write() ignores the return value from
trace_remote_enable_event(). If a remote rejects an event state change,
the write therefore reports success even though the affected event remains
in its previous state.

Keep trying all events, but retain and return the first error. This matches
__ftrace_set_clr_event_nolock(), which permits partial updates while
notifying userspace when an operation fails.

Fixes: 775cb093bc50 ("tracing: Add events/ root files to trace remotes")
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Jackie Liu <liuyun01@kylinos.cn>
---
 kernel/trace/trace_remote.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c
index 0f6ef5c36d84..5212e685f0a3 100644
--- a/kernel/trace/trace_remote.c
+++ b/kernel/trace/trace_remote.c
@@ -1149,11 +1149,22 @@ static ssize_t remote_events_dir_enable_write(struct file *filp, const char __us
 	guard(mutex)(&remote->lock);
 
 	for (i = 0; i < remote->nr_events; i++) {
+		int eret;
 		struct remote_event *evt = &remote->events[i];
 
-		trace_remote_enable_event(remote, evt, enable);
+		eret = trace_remote_enable_event(remote, evt, enable);
+		/*
+		 * Save the first error and return that. Some events
+		 * may still have been enabled, but let the user
+		 * know that something went wrong.
+		 */
+		if (!ret && eret)
+			ret = eret;
 	}
 
+	if (ret)
+		return ret;
+
 	return count;
 }
 
-- 
2.54.0


^ permalink raw reply related

* Re: [RFC PATCH 08/13] mm/kwatch: add hardware breakpoint backend
From: Jinchao Wang @ 2026-07-15  6:09 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Andrew Morton, Peter Zijlstra, Thomas Gleixner, Masami Hiramatsu,
	Ingo Molnar, Borislav Petkov, Dave Hansen, H . Peter Anvin, x86,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Mathieu Desnoyers, David Hildenbrand, Jonathan Corbet,
	Matthew Wilcox, linux-kernel, linux-mm, linux-trace-kernel,
	linux-perf-users, linux-doc
In-Reply-To: <20260714171438.226faf7b@gandalf.local.home>

On 7/14/2026 5:14 PM, Steven Rostedt wrote:
> On Wed, 15 Jul 2026 02:32:06 +0800
> Jinchao Wang <wangjinchao600@gmail.com> wrote:
> 
>> --- /dev/null
>> +++ b/include/trace/events/kwatch.h
>> @@ -0,0 +1,57 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +#undef TRACE_SYSTEM
>> +#define TRACE_SYSTEM kwatch
>> +
>> +#if !defined(_TRACE_KWATCH_H) || defined(TRACE_HEADER_MULTI_READ)
>> +#define _TRACE_KWATCH_H
>> +
>> +#include <linux/tracepoint.h>
>> +#include <linux/ptrace.h>
>> +
>> +#define KWATCH_STACK_DEPTH 8
>> +
>> +struct trace_seq;
>> +const char *kwatch_trace_print_stack(struct trace_seq *p,
>> +				     const unsigned long *stack,
>> +				     unsigned int nr);
>> +
>> +TRACE_EVENT(kwatch_hit,
>> +	TP_PROTO(unsigned long ip, unsigned long sp, unsigned long addr,
>> +		 u64 time_ns,
>> +		 unsigned long *stack_entries, unsigned int stack_nr),
>> +	TP_ARGS(ip, sp, addr, time_ns, stack_entries, stack_nr),
>> +
>> +	TP_STRUCT__entry(
>> +		__field(unsigned long, ip)
>> +		__field(unsigned long, sp)
>> +		__field(unsigned long, addr)
>> +		__field(u64, time_ns)
> 
> Move the time_ns to the first field, as unsigned long on 32 bit
> architectures is 4 bytes, and this will make 4 byte "hole" in the event.
Will fix in v2.> 
> 
>> +		__field(unsigned int, stack_nr)
> 
> Make stack_nr the last element for the same reason.

Will fix in v2.

> 
>> +		__array(unsigned long, stack, KWATCH_STACK_DEPTH)
> 
> Make the above a dynamic array based on stack entries.
> 
> 		__dynamic_array(unsigned long, stack, min_t(unsigned int, stack_nr,
> 			  KWATCH_STACK_DEPTH);

Much better than always paying for the full depth - will convert to
__dynamic_array (and use __get_dynamic_array() in TP_fast_assign and
TP_printk as you showed) in v2.

Thank you for the review!

Thanks,
Jinchao

> 
> 
>> +	),
>> +
>> +	TP_fast_assign(
>> +		unsigned int i;
> 		unsigned long *stack = __get_dynamic_array(stack);
>> +
>> +		__entry->ip = ip;
>> +		__entry->sp = sp;
>> +		__entry->addr = addr;
>> +		__entry->time_ns = time_ns;
>> +		__entry->stack_nr = min_t(unsigned int, stack_nr,
>> +					  KWATCH_STACK_DEPTH);
>> +		for (i = 0; i < __entry->stack_nr; i++)
>> +			__entry->stack[i] = stack_entries[i];
> 
> 			stack[i] = stack_entries[i];
> 
>> +	),
>> +
>> +	TP_printk("KWatch HIT: time=%llu.%06lu ip=%pS addr=0x%lx%s",
>> +		  __entry->time_ns / 1000000000ULL,
>> +		  (unsigned long)((__entry->time_ns / 1000ULL) % 1000000ULL),
>> +		  (void *)__entry->ip, __entry->addr,
>> +		  kwatch_trace_print_stack(p, __entry->stack,
> 
> 		  kwatch_trace_print_stack(p, __get_dynamic_array(stack),
> 
>> +					   __entry->stack_nr))
>> +);
>> +
> 
> -- Steve


^ permalink raw reply

* Re: [PATCH] tracing: Propagate errors from remote event bulk updates
From: Vincent Donnefort @ 2026-07-15  6:44 UTC (permalink / raw)
  To: Jackie Liu; +Cc: rostedt, mhiramat, mathieu.desnoyers, linux-trace-kernel
In-Reply-To: <20260715060448.17674-1-liu.yun@linux.dev>

On Wed, Jul 15, 2026 at 02:04:48PM +0800, Jackie Liu wrote:
> From: Jackie Liu <liuyun01@kylinos.cn>
> 
> remote_events_dir_enable_write() ignores the return value from
> trace_remote_enable_event(). If a remote rejects an event state change,
> the write therefore reports success even though the affected event remains
> in its previous state.
> 
> Keep trying all events, but retain and return the first error. This matches
> __ftrace_set_clr_event_nolock(), which permits partial updates while
> notifying userspace when an operation fails.
> 
> Fixes: 775cb093bc50 ("tracing: Add events/ root files to trace remotes")
> Assisted-by: Codex:gpt-5.6-sol
> Signed-off-by: Jackie Liu <liuyun01@kylinos.cn>

I have sent a related improvement for that here [1]

[1] https://lore.kernel.org/all/20260605163825.1762953-3-vdonnefort@google.com/


> ---
>  kernel/trace/trace_remote.c | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c
> index 0f6ef5c36d84..5212e685f0a3 100644
> --- a/kernel/trace/trace_remote.c
> +++ b/kernel/trace/trace_remote.c
> @@ -1149,11 +1149,22 @@ static ssize_t remote_events_dir_enable_write(struct file *filp, const char __us
>  	guard(mutex)(&remote->lock);
>  
>  	for (i = 0; i < remote->nr_events; i++) {
> +		int eret;
>  		struct remote_event *evt = &remote->events[i];
>  
> -		trace_remote_enable_event(remote, evt, enable);
> +		eret = trace_remote_enable_event(remote, evt, enable);
> +		/*
> +		 * Save the first error and return that. Some events
> +		 * may still have been enabled, but let the user
> +		 * know that something went wrong.
> +		 */
> +		if (!ret && eret)
> +			ret = eret;
>  	}
>  
> +	if (ret)
> +		return ret;
> +
>  	return count;
>  }
>  
> -- 
> 2.54.0
> 
> 

^ permalink raw reply

* Re: [PATCH v3 4/5] sched: Add task enqueue/dequeue trace points
From: Gabriele Monaco @ 2026-07-15  6:51 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Ingo Molnar, Peter Zijlstra, Nam Cao, linux-trace-kernel,
	linux-kernel, Juri Lelli, Vincent Guittot, Masami Hiramatsu,
	Mathieu Desnoyers, Dietmar Eggemann, Ben Segall, Mel Gorman,
	Valentin Schneider, K Prateek Nayak
In-Reply-To: <20260714121145.1e73c05e@gandalf.local.home>

On Tue, 2026-07-14 at 12:11 -0400, Steven Rostedt wrote:
> On Wed, 13 Aug 2025 08:50:39 +0200
> Gabriele Monaco <gmonaco@redhat.com> wrote:
> 
> > On Mon, 2025-08-11 at 10:40 +0200, Nam Cao wrote:
> > > Add trace points into enqueue_task() and dequeue_task(). They are
> > > useful to implement RV monitor which validates RT scheduling.
> > > 
> > > Signed-off-by: Nam Cao <namcao@linutronix.de>
> > > ---  
> > 
> > Peter, Ingo, this patch adds new tracepoints in the scheduler do agree
> > with the change, can we get an Ack?
> 
> Are we still waiting on an Ack for this?

Yes, but since I'm planning to rebase the patches requiring it on other changes
I held this back. I will likely send everything for the next development cycle
after merging those new dependencies.

An Ack is surely welcome also now but not urgent.

Thanks,
Gabriele

> 
> I see it in the archive of Patchwork:
> 
>  
> https://patchwork.kernel.org/project/linux-trace-kernel/list/?series=989907&state=%2A&archive=both
> 
> -- Steve


^ permalink raw reply

* Re: [PATCH] tracing: Propagate errors from remote event bulk updates
From: Jackie Liu @ 2026-07-15  7:02 UTC (permalink / raw)
  To: Vincent Donnefort
  Cc: rostedt, mhiramat, mathieu.desnoyers, linux-trace-kernel
In-Reply-To: <alcsNLVLhWOH3KbJ@google.com>

2026年7月15日 14:44, "Vincent Donnefort" <vdonnefort@google.com mailto:vdonnefort@google.com?to=%22Vincent%20Donnefort%22%20%3Cvdonnefort%40google.com%3E > 写到:


> 
> On Wed, Jul 15, 2026 at 02:04:48PM +0800, Jackie Liu wrote:
> 
> > 
> > From: Jackie Liu <liuyun01@kylinos.cn>
> >  
> >  remote_events_dir_enable_write() ignores the return value from
> >  trace_remote_enable_event(). If a remote rejects an event state change,
> >  the write therefore reports success even though the affected event remains
> >  in its previous state.
> >  
> >  Keep trying all events, but retain and return the first error. This matches
> >  __ftrace_set_clr_event_nolock(), which permits partial updates while
> >  notifying userspace when an operation fails.
> >  
> >  Fixes: 775cb093bc50 ("tracing: Add events/ root files to trace remotes")
> >  Assisted-by: Codex:gpt-5.6-sol
> >  Signed-off-by: Jackie Liu <liuyun01@kylinos.cn>
> > 
> I have sent a related improvement for that here [1]
> 
> [1] https://lore.kernel.org/all/20260605163825.1762953-3-vdonnefort@google.com/

Thanks for the pointer.

I checked the v2 series. Patch 02/18 handles registration failure cleanup,
and patch 03/18 changes the boolean parsing in
remote_events_dir_enable_write(), but the loop still ignores errors from
trace_remote_enable_event().

Would you prefer this fix to be folded into your series, or should I resend
a v2 rebased on top of it?

-- 
Jackie Liu

> 
> > 
> > ---
> >  kernel/trace/trace_remote.c | 13 ++++++++++++-
> >  1 file changed, 12 insertions(+), 1 deletion(-)
> >  
> >  diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c
> >  index 0f6ef5c36d84..5212e685f0a3 100644
> >  --- a/kernel/trace/trace_remote.c
> >  +++ b/kernel/trace/trace_remote.c
> >  @@ -1149,11 +1149,22 @@ static ssize_t remote_events_dir_enable_write(struct file *filp, const char __us
> >  guard(mutex)(&remote->lock);
> >  
> >  for (i = 0; i < remote->nr_events; i++) {
> >  + int eret;
> >  struct remote_event *evt = &remote->events[i];
> >  
> >  - trace_remote_enable_event(remote, evt, enable);
> >  + eret = trace_remote_enable_event(remote, evt, enable);
> >  + /*
> >  + * Save the first error and return that. Some events
> >  + * may still have been enabled, but let the user
> >  + * know that something went wrong.
> >  + */
> >  + if (!ret && eret)
> >  + ret = eret;
> >  }
> >  
> >  + if (ret)
> >  + return ret;
> >  +
> >  return count;
> >  }
> >  
> >  -- 
> >  2.54.0
> >
>

^ permalink raw reply

* Re: [PATCH] tracing: Propagate errors from remote event bulk updates
From: Vincent Donnefort @ 2026-07-15  7:25 UTC (permalink / raw)
  To: Jackie Liu; +Cc: rostedt, mhiramat, mathieu.desnoyers, linux-trace-kernel
In-Reply-To: <f9050a5ba183da3c9071c5be5172c4e814972836@linux.dev>

On Wed, Jul 15, 2026 at 07:02:01AM +0000, Jackie Liu wrote:
> 2026年7月15日 14:44, "Vincent Donnefort" <vdonnefort@google.com mailto:vdonnefort@google.com?to=%22Vincent%20Donnefort%22%20%3Cvdonnefort%40google.com%3E > 写到:
> 
> 
> > 
> > On Wed, Jul 15, 2026 at 02:04:48PM +0800, Jackie Liu wrote:
> > 
> > > 
> > > From: Jackie Liu <liuyun01@kylinos.cn>
> > >  
> > >  remote_events_dir_enable_write() ignores the return value from
> > >  trace_remote_enable_event(). If a remote rejects an event state change,
> > >  the write therefore reports success even though the affected event remains
> > >  in its previous state.
> > >  
> > >  Keep trying all events, but retain and return the first error. This matches
> > >  __ftrace_set_clr_event_nolock(), which permits partial updates while
> > >  notifying userspace when an operation fails.
> > >  
> > >  Fixes: 775cb093bc50 ("tracing: Add events/ root files to trace remotes")
> > >  Assisted-by: Codex:gpt-5.6-sol
> > >  Signed-off-by: Jackie Liu <liuyun01@kylinos.cn>
> > > 
> > I have sent a related improvement for that here [1]
> > 
> > [1] https://lore.kernel.org/all/20260605163825.1762953-3-vdonnefort@google.com/
> 
> Thanks for the pointer.
> 
> I checked the v2 series. Patch 02/18 handles registration failure cleanup,
> and patch 03/18 changes the boolean parsing in
> remote_events_dir_enable_write(), but the loop still ignores errors from
> trace_remote_enable_event().
> 
> Would you prefer this fix to be folded into your series, or should I resend
> a v2 rebased on top of it?

Ha yes my bad. Then no, no need to send it separately. 

> 
> -- 
> Jackie Liu
> 
> > 
> > > 
> > > ---
> > >  kernel/trace/trace_remote.c | 13 ++++++++++++-
> > >  1 file changed, 12 insertions(+), 1 deletion(-)
> > >  
> > >  diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c
> > >  index 0f6ef5c36d84..5212e685f0a3 100644
> > >  --- a/kernel/trace/trace_remote.c
> > >  +++ b/kernel/trace/trace_remote.c
> > >  @@ -1149,11 +1149,22 @@ static ssize_t remote_events_dir_enable_write(struct file *filp, const char __us
> > >  guard(mutex)(&remote->lock);
> > >  
> > >  for (i = 0; i < remote->nr_events; i++) {
> > >  + int eret;
> > >  struct remote_event *evt = &remote->events[i];

Could you declare ert after? I have tried to use the reverse christmas tree
everywhere in the trace remote code.

> > >  
> > >  - trace_remote_enable_event(remote, evt, enable);
> > >  + eret = trace_remote_enable_event(remote, evt, enable);
> > >  + /*
> > >  + * Save the first error and return that. Some events
> > >  + * may still have been enabled, but let the user
> > >  + * know that something went wrong.
> > >  + */
> > >  + if (!ret && eret)
> > >  + ret = eret;
> > >  }
> > >  
> > >  + if (ret)
> > >  + return ret;
> > >  +
> > >  return count;
> > >  }
> > >  
> > >  -- 
> > >  2.54.0
> > >
> >

^ permalink raw reply

* Re: [PATCH] tracing: ring-buffer: allowlist clang-generated symbols
From: Arnd Bergmann @ 2026-07-15  7:38 UTC (permalink / raw)
  To: Steven Rostedt, Vincent Donnefort
  Cc: Arnd Bergmann, Masami Hiramatsu, Nathan Chancellor,
	Mathieu Desnoyers, Nick Desaulniers, Bill Wendling, Justin Stitt,
	Marc Zyngier, Thomas Weißschuh, Paolo Bonzini, linux-kernel,
	linux-trace-kernel, llvm
In-Reply-To: <20260714175617.319a6ec7@gandalf.local.home>

On Tue, Jul 14, 2026, at 23:56, Steven Rostedt wrote:
> On Wed, 17 Jun 2026 14:26:36 +0100 Vincent Donnefort <vdonnefort@google.com> wrote:
>
>> On Tue, Jun 16, 2026 at 06:42:03PM +0200, Arnd Bergmann wrote:
>> > From: Arnd Bergmann <arnd@arndb.de>
>> > 
>> > In randconfig build testing using clang-22, I came across two
>> > sets of extra symbols in the ring buffer code that may get
>> > inserted by the compiler:
>> > 
>> > Unexpected symbols in kernel/trace/simple_ring_buffer.o:
>> >          U memset
>> > 
>> > Unexpected symbols in kernel/trace/simple_ring_buffer.o:
>> >                  U llvm_gcda_emit_arcs
>> >                  U llvm_gcda_emit_function
>> >                  U llvm_gcda_end_file
>> >                  U llvm_gcda_start_file
>> >                  U llvm_gcda_summary_info
>> >                  U llvm_gcov_init
>> > 
>> > Add all of these to the allowlist.
>> > 
>> > Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>> > ---
>> >  kernel/trace/Makefile | 1 +
>> >  1 file changed, 1 insertion(+)
>> > 
>> > diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
>> > index f934ff586bd4..aa8564fb8ff4 100644
>> > --- a/kernel/trace/Makefile
>> > +++ b/kernel/trace/Makefile
>> > @@ -146,6 +146,7 @@ KASAN_SANITIZE_undefsyms_base.o := y  
>> 
>> Would "GCOV_PROFILE_undefsyms_base.o := y" work?
>
> Arnd?

Yes, turning off gcov for this file should avoid the llvm_gcda and
llvm_gcov symbols, but not the memset() symbol.

All the other features that leave annotations are handled by
listing the symbol names (__asan __gcov __kasan __kcsan __hwasan
__sancov __sanitizer __tsan __ubsan __msan), so I think for
consistency it makes sense to treat llvm gcov the same way
we handle gcc gcov and the rest.

      Arnd

^ permalink raw reply

* [PATCH v2] tracing: Propagate errors from remote event bulk updates
From: Jackie Liu @ 2026-07-15  7:44 UTC (permalink / raw)
  To: vdonnefort, rostedt, mhiramat; +Cc: mathieu.desnoyers, linux-trace-kernel

From: Jackie Liu <liuyun01@kylinos.cn>

remote_events_dir_enable_write() ignores the return value from
trace_remote_enable_event(). If a remote rejects an event state change,
the write therefore reports success even though the affected event remains
in its previous state.

Keep trying all events, but retain and return the first error. This matches
__ftrace_set_clr_event_nolock(), which permits partial updates while
notifying userspace when an operation fails.

Fixes: 775cb093bc50 ("tracing: Add events/ root files to trace remotes")
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Jackie Liu <liuyun01@kylinos.cn>
---
 Changes in v2:
- Move the eret declaration after evt to follow reverse Christmas tree
  ordering.

 kernel/trace/trace_remote.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c
index 0f6ef5c36d84..daeaf1d3eb1c 100644
--- a/kernel/trace/trace_remote.c
+++ b/kernel/trace/trace_remote.c
@@ -1150,10 +1150,21 @@ static ssize_t remote_events_dir_enable_write(struct file *filp, const char __us
 
 	for (i = 0; i < remote->nr_events; i++) {
 		struct remote_event *evt = &remote->events[i];
+		int eret;
 
-		trace_remote_enable_event(remote, evt, enable);
+		eret = trace_remote_enable_event(remote, evt, enable);
+		/*
+		 * Save the first error and return that. Some events
+		 * may still have been enabled, but let the user
+		 * know that something went wrong.
+		 */
+		if (!ret && eret)
+			ret = eret;
 	}
 
+	if (ret)
+		return ret;
+
 	return count;
 }
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH v2] tracing: Add mutex to trace_parser to fix concurrent write races
From: Tengda Wu @ 2026-07-15  8:19 UTC (permalink / raw)
  To: Steven Rostedt, Masami Hiramatsu
  Cc: Mark Rutland, Mathieu Desnoyers, linux-trace-kernel, linux-kernel,
	Tengda Wu

The trace_parser structure is allocated and initialized when a trace
file is opened, and is subsequently used in the write handler to parse
user input. If userspace opens a trace file descriptor and shares it
across multiple threads, concurrent write calls will race on the
parser's internal state, specifically the idx, cont, and buffer fields,
leading to corrupted input or undefined behavior.

Fix this by embedding a mutex directly in struct trace_parser. The mutex
is initialized in trace_parser_get_init() and destroyed in
trace_parser_put(). All write-side users that access parser state
(trace_get_user() followed by checking trace_parser_loaded() /
trace_parser_cont() against the buffer) now hold the mutex across the
full critical section, avoiding any TOCTOU gap between the parse and the
subsequent consumption of parser->buffer.

Fixes: e704eff3ff51 ("ftrace: Have set_graph_function handle multiple functions in one write")
Fixes: 689fd8b65d66 ("tracing: trace parser support for function and graph")
Cc: stable@vger.kernel.org
Signed-off-by: Tengda Wu <wutengda@huaweicloud.com>
---
v2: Add proper lockdep assertions to enforce that the parser lock is
    held by all callers (Steven).
v1: https://lore.kernel.org/all/20260713134640.708323-1-wutengda@huaweicloud.com/

 kernel/trace/ftrace.c       | 7 +++++++
 kernel/trace/trace.c        | 4 ++++
 kernel/trace/trace.h        | 5 +++++
 kernel/trace/trace_events.c | 2 ++
 kernel/trace/trace_pid.c    | 2 ++
 5 files changed, 20 insertions(+)

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f93e34dd2328..ef47e5659283 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5842,6 +5842,8 @@ ftrace_regex_write(struct file *file, const char __user *ubuf,
 	/* iter->hash is a local copy, so we don't need regex_lock */
 
 	parser = &iter->parser;
+
+	guard(mutex)(&parser->lock);
 	read = trace_get_user(parser, ubuf, cnt, ppos);
 
 	if (read >= 0 && trace_parser_loaded(parser) &&
@@ -6984,12 +6986,14 @@ int ftrace_regex_release(struct inode *inode, struct file *file)
 		iter = file->private_data;
 
 	parser = &iter->parser;
+	mutex_lock(&parser->lock);
 	if (trace_parser_loaded(parser)) {
 		int enable = !(iter->flags & FTRACE_ITER_NOTRACE);
 
 		ftrace_process_regex(iter, parser->buffer,
 				     parser->idx, enable);
 	}
+	mutex_unlock(&parser->lock);
 
 	trace_parser_put(parser);
 
@@ -7321,10 +7325,12 @@ ftrace_graph_release(struct inode *inode, struct file *file)
 
 		parser = &fgd->parser;
 
+		mutex_lock(&parser->lock);
 		if (trace_parser_loaded((parser))) {
 			ret = ftrace_graph_set_hash(fgd->new_hash,
 						    parser->buffer);
 		}
+		mutex_unlock(&parser->lock);
 
 		trace_parser_put(parser);
 
@@ -7437,6 +7443,7 @@ ftrace_graph_write(struct file *file, const char __user *ubuf,
 
 	parser = &fgd->parser;
 
+	guard(mutex)(&parser->lock);
 	read = trace_get_user(parser, ubuf, cnt, ppos);
 
 	if (read >= 0 && trace_parser_loaded(parser) &&
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 1146b83b711a..255432879847 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1100,6 +1100,7 @@ int trace_parser_get_init(struct trace_parser *parser, int size)
 		return 1;
 
 	parser->size = size;
+	mutex_init(&parser->lock);
 	return 0;
 }
 
@@ -1108,6 +1109,7 @@ int trace_parser_get_init(struct trace_parser *parser, int size)
  */
 void trace_parser_put(struct trace_parser *parser)
 {
+	mutex_destroy(&parser->lock);
 	kfree(parser->buffer);
 	parser->buffer = NULL;
 }
@@ -1130,6 +1132,8 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
 	size_t read = 0;
 	ssize_t ret;
 
+	lockdep_assert_held(&parser->lock);
+
 	if (!*ppos)
 		trace_parser_clear(parser);
 
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 2537c33ddd49..b87baf249eb7 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1387,26 +1387,31 @@ struct trace_parser {
 	char		*buffer;
 	unsigned	idx;
 	unsigned	size;
+	struct mutex	lock;
 };
 
 static inline bool trace_parser_loaded(struct trace_parser *parser)
 {
+	lockdep_assert_held(&parser->lock);
 	return !parser->fail && parser->idx != 0;
 }
 
 static inline bool trace_parser_cont(struct trace_parser *parser)
 {
+	lockdep_assert_held(&parser->lock);
 	return parser->cont;
 }
 
 static inline void trace_parser_clear(struct trace_parser *parser)
 {
+	lockdep_assert_held(&parser->lock);
 	parser->cont = false;
 	parser->idx = 0;
 }
 
 static inline void trace_parser_fail(struct trace_parser *parser)
 {
+	lockdep_assert_held(&parser->lock);
 	parser->fail = true;
 }
 
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index c46e623e7e0d..644e8aad43d4 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1535,6 +1535,7 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
 	if (trace_parser_get_init(&parser, EVENT_BUF_SIZE + 1))
 		return -ENOMEM;
 
+	mutex_lock(&parser.lock);
 	read = trace_get_user(&parser, ubuf, cnt, ppos);
 
 	if (read >= 0 && trace_parser_loaded((&parser))) {
@@ -1551,6 +1552,7 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
 	ret = read;
 
  out_put:
+	mutex_unlock(&parser.lock);
 	trace_parser_put(&parser);
 
 	return ret;
diff --git a/kernel/trace/trace_pid.c b/kernel/trace/trace_pid.c
index 7127c8de4174..f438291ee3b0 100644
--- a/kernel/trace/trace_pid.c
+++ b/kernel/trace/trace_pid.c
@@ -195,6 +195,7 @@ int trace_pid_write(struct trace_pid_list *filtered_pids,
 	}
 
 	ret = 0;
+	mutex_lock(&parser.lock);
 	while (cnt > 0) {
 
 		pos = 0;
@@ -225,6 +226,7 @@ int trace_pid_write(struct trace_pid_list *filtered_pids,
 		trace_parser_clear(&parser);
 		ret = 0;
 	}
+	mutex_unlock(&parser.lock);
  out:
 	trace_parser_put(&parser);
 
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH v3 14/17] verification/rvgen: Add selftests for rvgen kunit
From: Gabriele Monaco @ 2026-07-15  9:25 UTC (permalink / raw)
  To: Wen Yang, linux-trace-kernel, linux-kernel, Nam Cao
  Cc: Steven Rostedt, Thomas Weissschuh, Tomas Glozar, John Kacur
In-Reply-To: <a20ddbab-aebe-4f2c-9d07-ec8dee588eab@linux.dev>

On Wed, 2026-07-15 at 01:49 +0800, Wen Yang wrote:
> 
> Note that the same typo (handle_sample_event) appears in the other two
> LTL golden files added in patch 05/17 as well: ltl_pertask.c and
> test_ltl.c. The root is likely in the rvgen LTL template itself, so
> the fix there would regenerate all three consistently.

Yeah that's trivial to fix.

> Understood on the intent. One small concern:  MODULE_AUTHOR(/* TODO */)
> is subtly different from an XXX comment — a C preprocessor strips the
> comment before macro expansion, leaving  MODULE_AUTHOR() with an empty
> argument. When the user does go to compile, they get a cryptic error
> rather than a clear "fill this in" hint.

Well, I believe a compiler failure was indeed the original intent (that's kinda
what we do in rv_attach_trace_probe), to avoid unauthored monitors.

Anyway an unauthored monitor isn't as bad as a missing tracepoint, so probably I
could make the LTL template consistent with the others.

Unless Nam has concerns with that.

Thanks,
Gabriele


^ permalink raw reply

* Re: [PATCH v2] tracing: Propagate errors from remote event bulk updates
From: Vincent Donnefort @ 2026-07-15  9:33 UTC (permalink / raw)
  To: Jackie Liu; +Cc: rostedt, mhiramat, mathieu.desnoyers, linux-trace-kernel
In-Reply-To: <20260715074455.3897-1-liu.yun@linux.dev>

On Wed, Jul 15, 2026 at 03:44:55PM +0800, Jackie Liu wrote:
> From: Jackie Liu <liuyun01@kylinos.cn>
> 
> remote_events_dir_enable_write() ignores the return value from
> trace_remote_enable_event(). If a remote rejects an event state change,
> the write therefore reports success even though the affected event remains
> in its previous state.
> 
> Keep trying all events, but retain and return the first error. This matches
> __ftrace_set_clr_event_nolock(), which permits partial updates while
> notifying userspace when an operation fails.
> 
> Fixes: 775cb093bc50 ("tracing: Add events/ root files to trace remotes")
> Assisted-by: Codex:gpt-5.6-sol
> Signed-off-by: Jackie Liu <liuyun01@kylinos.cn>

Thanks!

Reviewed-by: Vincent Donnefort <vdonnefort@google.com>

> ---
>  Changes in v2:
> - Move the eret declaration after evt to follow reverse Christmas tree
>   ordering.
> 
>  kernel/trace/trace_remote.c | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c
> index 0f6ef5c36d84..daeaf1d3eb1c 100644
> --- a/kernel/trace/trace_remote.c
> +++ b/kernel/trace/trace_remote.c
> @@ -1150,10 +1150,21 @@ static ssize_t remote_events_dir_enable_write(struct file *filp, const char __us
>  
>  	for (i = 0; i < remote->nr_events; i++) {
>  		struct remote_event *evt = &remote->events[i];
> +		int eret;
>  
> -		trace_remote_enable_event(remote, evt, enable);
> +		eret = trace_remote_enable_event(remote, evt, enable);
> +		/*
> +		 * Save the first error and return that. Some events
> +		 * may still have been enabled, but let the user
> +		 * know that something went wrong.
> +		 */
> +		if (!ret && eret)
> +			ret = eret;
>  	}
>  
> +	if (ret)
> +		return ret;
> +
>  	return count;
>  }
>  
> -- 
> 2.54.0
> 

^ permalink raw reply

* Re: [PATCH v3 14/17] verification/rvgen: Add selftests for rvgen kunit
From: Nam Cao @ 2026-07-15 11:23 UTC (permalink / raw)
  To: Gabriele Monaco, Wen Yang, linux-trace-kernel, linux-kernel
  Cc: Steven Rostedt, Thomas Weissschuh, Tomas Glozar, John Kacur
In-Reply-To: <4885b2132ca1e8ed0fd6010ef9506d6e99e58fdb.camel@redhat.com>

Gabriele Monaco <gmonaco@redhat.com> writes:
> Well, I believe a compiler failure was indeed the original intent (that's kinda
> what we do in rv_attach_trace_probe), to avoid unauthored monitors.
>
> Anyway an unauthored monitor isn't as bad as a missing tracepoint, so probably I
> could make the LTL template consistent with the others.
>
> Unless Nam has concerns with that.

No. Go for it.

Nam

^ permalink raw reply

* Re: [PATCH v3 04/17] tools/rv: Add selftests
From: Gabriele Monaco @ 2026-07-15 11:24 UTC (permalink / raw)
  To: Wen Yang, linux-trace-kernel, linux-kernel
  Cc: Nam Cao, Steven Rostedt, Thomas Weissschuh, Tomas Glozar,
	John Kacur
In-Reply-To: <6b232d66-703b-4fe6-bf0c-4f0eeff4b4a7@linux.dev>

On Wed, 2026-07-15 at 02:01 +0800, Wen Yang wrote:
> > Any better idea? We cannot really rely on the shell's $! because command
> > is using a combination of eval+timer and we'd get the wrong pid.
> > 
> 
> - Since $bgpid is the timeout process, its direct child is exactly the
>     command we want. Using pgrep -P $bgpid avoids the fragile pattern
>     matching of pgrep -f and won't accidentally match unrelated
>     processes with a similar command string, eg:
> 
>       for i in $(seq 10); do
>           pid=$(pgrep -P "$bgpid" | head -1)
>           [ -n "$pid" ] && break
>           sleep 0.5
>       done

That would be neat, but apparently eval cmd & spawns a new shell (I'd assume to
keep control of the background task), so in practice we get something like:

  bash(88148)───timeout(88150)───rv mon(88151)

while getting the pid of bash in $!, we could probably do:

  pgrep -P "$(pgrep -P "$bgpid")"

but I'm not really sure how portable this is.

Now I tried all sorts of bash hacks but couldn't reliably avoid this subshell
(technically eval "cmd &" with quoted ampersand skips it, but breaks output
redirection or whatever else).

Unless you have a reliable way to get the pid that doesn't rely on some shell-
specific dark magic, I'd keep pgrep -f .

>     Note: a bounded retry loop may be necessary; without an upper limit 
> the loop hangs indefinitely if the command fails to exec.
> 
> - For the verbose test specifically ("my pid is $pid"), the pid already
>     appears in rv's own output. An alternative is to match it with a
>     numeric pattern instead:
> 
>       "my pid is [0-9]\+"
> 
>     This sidesteps the race entirely for that test case.

Yeah an accurate pid isn't as important in that test, but since it seems to work
fine I'd keep it.

Thanks,
Gabriele


^ permalink raw reply

* Re: [PATCH v3 4/5] sched: Add task enqueue/dequeue trace points
From: Nam Cao @ 2026-07-15 11:31 UTC (permalink / raw)
  To: Gabriele Monaco, Steven Rostedt
  Cc: Ingo Molnar, Peter Zijlstra, linux-trace-kernel, linux-kernel,
	Juri Lelli, Vincent Guittot, Masami Hiramatsu, Mathieu Desnoyers,
	Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
	K Prateek Nayak
In-Reply-To: <cac78b0340121da7691ba8ffab8b7ed8350977a9.camel@redhat.com>

Gabriele Monaco <gmonaco@redhat.com> writes:
> Yes, but since I'm planning to rebase the patches requiring it on other changes
> I held this back. I will likely send everything for the next development cycle
> after merging those new dependencies.
>
> An Ack is surely welcome also now but not urgent.

I forgot that this exists.

Searching my mail box again, Peter had some comments, but in the v2
thread and not this one.

So let me revise and send v4.

Nam

^ permalink raw reply

* Re: [PATCH v3 4/5] sched: Add task enqueue/dequeue trace points
From: Gabriele Monaco @ 2026-07-15 11:38 UTC (permalink / raw)
  To: Nam Cao, Steven Rostedt
  Cc: Ingo Molnar, Peter Zijlstra, linux-trace-kernel, linux-kernel,
	Juri Lelli, Vincent Guittot, Masami Hiramatsu, Mathieu Desnoyers,
	Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
	K Prateek Nayak
In-Reply-To: <87qzl4lc9g.fsf@yellow.woof>

On Wed, 2026-07-15 at 13:31 +0200, Nam Cao wrote:
> Gabriele Monaco <gmonaco@redhat.com> writes:
> > Yes, but since I'm planning to rebase the patches requiring it on other
> > changes
> > I held this back. I will likely send everything for the next development
> > cycle
> > after merging those new dependencies.
> > 
> > An Ack is surely welcome also now but not urgent.
> 
> I forgot that this exists.
> 
> Searching my mail box again, Peter had some comments, but in the v2
> thread and not this one.
> 
> So let me revise and send v4.

Uh I just realised this was your version. I had one along with the dl monitors.
I applied Peter's comments and also Prateek chipped in, if you're sending the
per-CPU LTL series again, please use [1].

Thanks,
Gabriele

[1] - https://lore.kernel.org/lkml/20260310105627.332044-9-gmonaco@redhat.com/


^ permalink raw reply

* [PATCH v5 0/5] Enable perf tracing for unprivileged users
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
  To: rostedt, acme, peterz
  Cc: linux-kernel, linux-trace-kernel, linux-perf-users,
	Anubhav Shelat

Enable users to use perf-trace to trace their own processes, like strace
but without the overhead of ptrace(). Ensure that users cannot access
other users' or systemwide tracing data.

Changes in v5:
- Move event_define_fields() before directory creation. If
  event_define_fields() fails then we don't need to cleanup whatever
  dirs were created. 
- New read-only eventfs file system with the same structure as
  /sys/kernel/tracing/events/ to handle files read by unprivileged
  users.
- Allow unprivileged users to fall back to /sys/kernel/events/ when they
  cannot access /sys/kernel/tracing/events/.
- Factor out reused code into helper function that checks if a
  tracepoint should be restricted in commit 5.

Changes in v4:
- Preserve security_perf_event_open(PERF_SECURITY_KERNEL) LSM hook in
  the tp_bypass path.
- Lift the PERF_SAMPLE_IP check out of the tp_bypass path above the
  PERF_SAMPLE_RAW branch so it applies to counting and sampling. This
  also allows us to ensure PERF_SAMPLE_IP is set for uprobes.
- Block counting path for TRACE_EVENT_FL_CAP_ANY for unprivileged users
  with sysctl_perf_event_paranoid > 1.

Changes in v3:
- Don't set PERF_SAMPLE_IP for unprivileged tracepoints. This allows us
  to exclude PERF_SAMPLE_IP from kaddr_leak without weakening KASLR.
- Mount tracefs as world-traversable so users can access eventfs
  directories.

Anubhav Shelat (5):
  eventfs: define event fields before directory creation
  tracefs: add read-only eventfs filesystem at /sys/kernel/events
  perf tools: fall back to eventfs for unprivileged event discovery
  perf evsel: don't set PERF_SAMPLE_IP for unprivileged tracepoints
  perf: enable unprivileged syscall tracing with perf trace

 fs/tracefs/event_inode.c           |  61 ++++++++++++++++++
 fs/tracefs/inode.c                 |  95 ++++++++++++++++++++++++++-
 fs/tracefs/internal.h              |   3 +
 include/linux/trace_events.h       |   1 +
 include/linux/tracefs.h            |   4 ++
 include/uapi/linux/magic.h         |   1 +
 kernel/events/core.c               |  28 +++++++-
 kernel/trace/trace.h               |   2 +
 kernel/trace/trace_event_perf.c    |  28 +++++++-
 kernel/trace/trace_events.c        | 100 +++++++++++++++++++++++++++--
 tools/lib/api/fs/fs.c              |  10 +++
 tools/lib/api/fs/fs.h              |   1 +
 tools/lib/api/fs/tracing_path.c    |  52 +++++++++++++--
 tools/lib/api/fs/tracing_path.h    |   1 +
 tools/perf/util/evsel.c            |  14 +++-
 tools/perf/util/tp_pmu.c           |   5 +-
 tools/perf/util/trace-event-info.c |  19 +++---
 17 files changed, 395 insertions(+), 30 deletions(-)

-- 
2.54.0


^ permalink raw reply

* [PATCH v5 1/5] eventfs: define event fields before directory creation
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
  To: rostedt, acme, peterz, Masami Hiramatsu, Mathieu Desnoyers,
	linux-kernel, linux-trace-kernel
  Cc: linux-perf-users, Anubhav Shelat
In-Reply-To: <20260715135231.338535-2-ashelat@redhat.com>

Move the event_define_fields() call in event_create_dir() before the
eventfs directory creation. Previously, a failure after directory
creation wouldn't clean up eventfs_inode because the error path didn't
call eventfs_remove_dir(). This eliminates the need to clean up the
eventfs directories if event_define_fields() fails.

Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
 kernel/trace/trace_events.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index c46e623e7e0d..ddb6932a3ee7 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -3190,6 +3190,13 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
 	if (WARN_ON_ONCE(strcmp(call->class->system, TRACE_SYSTEM) == 0))
 		return -ENODEV;
 
+	ret = event_define_fields(call);
+	if (ret < 0) {
+		pr_warn("Could not initialize trace point events/%s\n",
+			trace_event_name(call));
+		return ret;
+	}
+
 	e_events = event_subsystem_dir(tr, call->class->system, file, parent);
 	if (!e_events)
 		return -ENOMEM;
@@ -3208,12 +3215,6 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
 
 	file->ei = ei;
 
-	ret = event_define_fields(call);
-	if (ret < 0) {
-		pr_warn("Could not initialize trace point events/%s\n", name);
-		return ret;
-	}
-
 	/* Gets decremented on freeing of the "enable" file */
 	event_file_get(file);
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH v5 2/5] tracefs: add read-only eventfs filesystem at /sys/kernel/events
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
  To: rostedt, acme, peterz, Masami Hiramatsu, Mathieu Desnoyers,
	Shivank Garg, Ackerley Tng, Fuad Tabba, Christian Brauner,
	Sean Christopherson, Anubhav Shelat, linux-kernel,
	linux-trace-kernel
  Cc: linux-perf-users
In-Reply-To: <20260715135231.338535-2-ashelat@redhat.com>

Introduce a new read-only pseudo-filesystem "eventfs" mounted at
/sys/kernel/events that exposes trace event format and id files
(mode 0444) to unprivileged users. This allows tools like perf to
discover event formats without requiring access to the full
tracefs/debugfs mount.

The eventfs filesystem reuses the existing eventfs_inode lazy-lookup
infrastructure. A new set of super_operations
(eventfs_ro_super_operations) shares the tracefs inode allocator so
that eventfs_get_inode() and get_tracefs() work on the RO superblock.
The superblock is manually initialized to ensure the root inode is
allocated with tracefs_alloc_inode, allowing the root to serve
directly as the events directory without another events subdirectory.

Each qualifying event gets a subsystem directory containing format
and id files. The top-level events directory also exposes header_page
and header_event. Similar to tracefs, a change will need to be made in
systemd to mount this filesystem automatically.

Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
 fs/tracefs/event_inode.c     | 61 +++++++++++++++++++++++
 fs/tracefs/inode.c           | 95 +++++++++++++++++++++++++++++++++++-
 fs/tracefs/internal.h        |  3 ++
 include/linux/trace_events.h |  1 +
 include/linux/tracefs.h      |  4 ++
 include/uapi/linux/magic.h   |  1 +
 kernel/trace/trace.h         |  2 +
 kernel/trace/trace_events.c  | 87 +++++++++++++++++++++++++++++++++
 8 files changed, 252 insertions(+), 2 deletions(-)

diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index 39c7a34531e8..fd6f63ec3ce0 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -812,6 +812,67 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
 	return ERR_PTR(-ENOMEM);
 }
 
+/**
+ * eventfs_create_events_dir_ro - create a read-only events directory
+ * @name: The name of the top level directory to create.
+ * @entries: A list of entries that represent the files under this directory
+ * @size: The number of @entries
+ * @data: The default data to pass to the files (an entry may override it).
+ *
+ * This function configures the eventfs filesystem root as a read-only
+ * trace event directory using the existing eventfs_inode lazy-lookup
+ * infrastructure.
+ *
+ * See eventfs_create_dir() for use of @entries.
+ */
+struct eventfs_inode *eventfs_create_events_dir_ro(const char *name,
+						   const struct eventfs_entry *entries,
+						   int size, void *data)
+{
+	struct dentry *dentry;
+	struct eventfs_root_inode *rei;
+	struct eventfs_inode *ei;
+	struct tracefs_inode *ti;
+	struct inode *inode;
+
+	dentry = eventfs_ro_get_root();
+	if (IS_ERR(dentry))
+		return ERR_CAST(dentry);
+
+	inode = d_inode(dentry);
+
+	ei = alloc_root_ei(name);
+	if (!ei)
+		goto fail;
+
+	rei = get_root_inode(ei);
+	rei->events_dir = dentry;
+
+	ei->entries = entries;
+	ei->nr_entries = size;
+	ei->data = data;
+
+	INIT_LIST_HEAD(&ei->children);
+	INIT_LIST_HEAD(&ei->list);
+
+	ti = get_tracefs(inode);
+	ti->flags |= TRACEFS_EVENT_INODE;
+	ti->private = ei;
+
+	inode->i_op = &eventfs_dir_inode_operations;
+	inode->i_fop = &eventfs_file_operations;
+
+	dentry->d_fsdata = get_ei(ei);
+
+	return ei;
+
+ fail:
+	cleanup_ei(ei);
+	dput(dentry);
+	eventfs_ro_put_root();
+	return ERR_PTR(-ENOMEM);
+}
+
 /**
  * eventfs_remove_rec - remove eventfs dir or file from list
  * @ei: eventfs_inode to be removed.
diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
index f3d6188a3b7b..fd064d79d940 100644
--- a/fs/tracefs/inode.c
+++ b/fs/tracefs/inode.c
@@ -30,6 +30,9 @@ static struct vfsmount *tracefs_mount;
 static int tracefs_mount_count;
 static bool tracefs_registered;
 
+static struct vfsmount *eventfs_ro_mount;
+static int eventfs_ro_mount_count;
+
 /*
  * Keep track of all tracefs_inodes in order to update their
  * flags if necessary on a remount.
@@ -423,6 +426,14 @@ static const struct super_operations tracefs_super_operations = {
 	.show_options	= tracefs_show_options,
 };
 
+static const struct super_operations eventfs_ro_super_operations = {
+	.alloc_inode    = tracefs_alloc_inode,
+	.free_inode     = tracefs_free_inode,
+	.destroy_inode  = tracefs_destroy_inode,
+	.drop_inode     = tracefs_drop_inode,
+	.statfs		= simple_statfs,
+};
+
 /*
  * It would be cleaner if eventfs had its own dentry ops.
  *
@@ -523,6 +534,79 @@ static struct file_system_type trace_fs_type = {
 };
 MODULE_ALIAS_FS("tracefs");
 
+static int eventfs_ro_fill_super(struct super_block *sb, struct fs_context *fc)
+{
+	struct inode *inode;
+	struct dentry *root;
+
+	sb->s_blocksize = PAGE_SIZE;
+	sb->s_blocksize_bits = PAGE_SHIFT;
+	sb->s_magic = EVENTFS_SUPER_MAGIC;
+	sb->s_op = &eventfs_ro_super_operations;
+	sb->s_time_gran = 1;
+	sb->s_flags |= SB_RDONLY;
+
+	inode = new_inode(sb);
+	if (!inode)
+		return -ENOMEM;
+
+	inode->i_ino = 1;
+	inode->i_mode = S_IFDIR | 0555;
+	simple_inode_init_ts(inode);
+	inode->i_op = &simple_dir_inode_operations;
+	inode->i_fop = &simple_dir_operations;
+	set_nlink(inode, 2);
+
+	set_default_d_op(sb, &tracefs_dentry_operations);
+
+	root = d_make_root(inode);
+	if (!root)
+		return -ENOMEM;
+
+	sb->s_root = root;
+
+	return 0;
+}
+
+static int eventfs_ro_get_tree(struct fs_context *fc)
+{
+	return get_tree_single(fc, eventfs_ro_fill_super);
+}
+
+static const struct fs_context_operations eventfs_ro_context_ops = {
+	.get_tree	= eventfs_ro_get_tree,
+};
+
+static int eventfs_ro_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &eventfs_ro_context_ops;
+	return 0;
+}
+
+static struct file_system_type eventfs_ro_fs_type = {
+	.owner =	THIS_MODULE,
+	.name =		"eventfs",
+	.init_fs_context = eventfs_ro_init_fs_context,
+	.kill_sb =	kill_anon_super,
+};
+
+struct dentry *eventfs_ro_get_root(void)
+{
+	int error;
+
+	error = simple_pin_fs(&eventfs_ro_fs_type, &eventfs_ro_mount,
+			      &eventfs_ro_mount_count);
+	if (error)
+		return ERR_PTR(error);
+
+	return dget(eventfs_ro_mount->mnt_root);
+}
+
+void eventfs_ro_put_root(void)
+{
+	simple_release_fs(&eventfs_ro_mount, &eventfs_ro_mount_count);
+}
+
 struct dentry *tracefs_start_creating(const char *name, struct dentry *parent)
 {
 	struct dentry *dentry;
@@ -801,8 +885,15 @@ static int __init tracefs_init(void)
 		return -EINVAL;
 
 	retval = register_filesystem(&trace_fs_type);
-	if (!retval)
-		tracefs_registered = true;
+	if (retval)
+		return retval;
+	tracefs_registered = true;
+
+	retval = sysfs_create_mount_point(kernel_kobj, "events");
+	if (retval)
+		return retval;
+
+	retval = register_filesystem(&eventfs_ro_fs_type);
 
 	return retval;
 }
diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
index a4a7f8431aff..0440413f959b 100644
--- a/fs/tracefs/internal.h
+++ b/fs/tracefs/internal.h
@@ -73,6 +73,9 @@ struct dentry *tracefs_end_creating(struct dentry *dentry);
 struct dentry *tracefs_failed_creating(struct dentry *dentry);
 struct inode *tracefs_get_inode(struct super_block *sb);
 
+struct dentry *eventfs_ro_get_root(void);
+void eventfs_ro_put_root(void);
+
 void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
 void eventfs_d_release(struct dentry *dentry);
 
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 308c76b57d13..957695fbb015 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -648,6 +648,7 @@ struct trace_event_file {
 	struct trace_event_call		*event_call;
 	struct event_filter __rcu	*filter;
 	struct eventfs_inode		*ei;
+	struct eventfs_inode		*ei_ro;
 	struct trace_array		*tr;
 	struct trace_subsystem_dir	*system;
 	struct list_head		triggers;
diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h
index bc354d340046..c175efc51d20 100644
--- a/include/linux/tracefs.h
+++ b/include/linux/tracefs.h
@@ -87,6 +87,10 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode
 					 const struct eventfs_entry *entries,
 					 int size, void *data);
 
+struct eventfs_inode *eventfs_create_events_dir_ro(const char *name,
+						   const struct eventfs_entry *entries,
+						   int size, void *data);
+
 void eventfs_remove_events_dir(struct eventfs_inode *ei);
 void eventfs_remove_dir(struct eventfs_inode *ei);
 
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index 4f2da935a76c..7cf8f1a1ae38 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -75,6 +75,7 @@
 #define STACK_END_MAGIC		0x57AC6E9D
 
 #define TRACEFS_MAGIC          0x74726163
+#define EVENTFS_SUPER_MAGIC    0x65766673
 
 #define V9FS_MAGIC		0x01021997
 
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 80fe152af1dd..00c35aaa5069 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -416,6 +416,7 @@ struct trace_array {
 	struct dentry		*options;
 	struct dentry		*percpu_dir;
 	struct eventfs_inode	*event_dir;
+	struct eventfs_inode	*event_dir_ro;
 	struct trace_options	*topts;
 	struct list_head	systems;
 	struct list_head	events;
@@ -1604,6 +1605,7 @@ struct trace_subsystem_dir {
 	struct event_subsystem		*subsystem;
 	struct trace_array		*tr;
 	struct eventfs_inode		*ei;
+	struct eventfs_inode		*ei_ro;
 	int				ref_count;
 	int				nr_events;
 };
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index ddb6932a3ee7..9662cb24a92c 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1279,6 +1279,7 @@ static void remove_subsystem(struct trace_subsystem_dir *dir)
 
 	if (!--dir->nr_events) {
 		eventfs_remove_dir(dir->ei);
+		eventfs_remove_dir(dir->ei_ro);
 		list_del(&dir->list);
 		__put_system_dir(dir);
 	}
@@ -1308,6 +1309,7 @@ void event_file_put(struct trace_event_file *file)
 static void remove_event_file_dir(struct trace_event_file *file)
 {
 	eventfs_remove_dir(file->ei);
+	eventfs_remove_dir(file->ei_ro);
 	list_del(&file->list);
 	remove_subsystem(file->system);
 	free_event_filter(file->filter);
@@ -2986,6 +2988,7 @@ event_subsystem_dir(struct trace_array *tr, const char *name,
 	}
 
 	dir->ei = ei;
+	dir->ei_ro = NULL;
 	dir->tr = tr;
 	dir->ref_count = 1;
 	dir->nr_events = 1;
@@ -3126,6 +3129,33 @@ static void event_release(const char *name, void *data)
 	event_file_put(file);
 }
 
+static int event_callback_ro(const char *name, umode_t *mode, void **data,
+			     const struct file_operations **fops)
+{
+	int ret = event_callback(name, mode, data, fops);
+
+	/* Skip writable entries in the read-only tree */
+	if (ret && (*mode & 0222))
+		return 0;
+	if (ret)
+		*mode = 0444;
+	return ret;
+}
+
+static struct eventfs_entry event_entries_ro[] = {
+	{
+		.name		= "format",
+		.callback	= event_callback_ro,
+		.release	= event_release,
+	},
+#ifdef CONFIG_PERF_EVENTS
+	{
+		.name		= "id",
+		.callback	= event_callback_ro,
+	},
+#endif
+};
+
 static int
 event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
 {
@@ -3218,6 +3248,28 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
 	/* Gets decremented on freeing of the "enable" file */
 	event_file_get(file);
 
+	/* Create read only eventfs directory */
+	if (!(call->flags & TRACE_EVENT_FL_DYNAMIC) &&
+	    !IS_ERR_OR_NULL(tr->event_dir_ro)) {
+		struct trace_subsystem_dir *sdir = file->system;
+
+		if (!sdir->ei_ro) {
+			sdir->ei_ro = eventfs_create_dir(call->class->system,
+					tr->event_dir_ro, NULL, 0, sdir);
+			if (IS_ERR(sdir->ei_ro))
+				sdir->ei_ro = NULL;
+		}
+		if (sdir->ei_ro) {
+			file->ei_ro = eventfs_create_dir(name, sdir->ei_ro,
+					event_entries_ro,
+					ARRAY_SIZE(event_entries_ro), file);
+			if (IS_ERR(file->ei_ro))
+				file->ei_ro = NULL;
+			else
+				event_file_get(file);
+		}
+	}
+
 	return 0;
 }
 
@@ -4539,10 +4591,35 @@ static int events_callback(const char *name, umode_t *mode, void **data,
 	return 1;
 }
 
+static int events_callback_ro(const char *name, umode_t *mode, void **data,
+			      const struct file_operations **fops)
+{
+	int ret = events_callback(name, mode, data, fops);
+
+	/* Skip writable entries in the read-only tree */
+	if (ret && (*mode & 0222))
+		return 0;
+	if (ret)
+		*mode = 0444;
+	return ret;
+}
+
+static struct eventfs_entry events_entries_ro[] = {
+	{
+		.name		= "header_page",
+		.callback	= events_callback_ro,
+	},
+	{
+		.name		= "header_event",
+		.callback	= events_callback_ro,
+	},
+};
+
 /* Expects to have event_mutex held when called */
 static int
 create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
 {
+	static bool event_dir_ro_created;
 	struct eventfs_inode *e_events;
 	struct dentry *entry;
 	int nr_entries;
@@ -4596,6 +4673,16 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
 
 	tr->event_dir = e_events;
 
+	if (!event_dir_ro_created && (tr->flags & TRACE_ARRAY_FL_GLOBAL)) {
+		tr->event_dir_ro = eventfs_create_events_dir_ro(
+				"events", events_entries_ro,
+				ARRAY_SIZE(events_entries_ro), tr);
+		if (IS_ERR(tr->event_dir_ro))
+			tr->event_dir_ro = NULL;
+		else
+			event_dir_ro_created = true;
+	}
+
 	return 0;
 }
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH v5 3/5] perf tools: fall back to eventfs for unprivileged event discovery
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
  To: rostedt, acme, peterz, Ingo Molnar, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Anubhav Shelat, linux-kernel, linux-perf-users
  Cc: linux-trace-kernel
In-Reply-To: <20260715135231.338535-2-ashelat@redhat.com>

When tracefs events are not readable by unprivileged users, fall back
to /sys/kernel/events (the read-only eventfs mount) for tracepoint
format and id file discovery. This allows perf trace to work for
unprivileged users on kernels that expose the eventfs filesystem.

The fallback is transparent: get_events_file() tries the tracefs path
first and only switches to eventfs when access() fails. On kernels
without eventfs, the existing error path is preserved.

Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
 tools/lib/api/fs/fs.c              | 10 ++++++
 tools/lib/api/fs/fs.h              |  1 +
 tools/lib/api/fs/tracing_path.c    | 52 ++++++++++++++++++++++++++----
 tools/lib/api/fs/tracing_path.h    |  1 +
 tools/perf/util/tp_pmu.c           |  5 +--
 tools/perf/util/trace-event-info.c | 19 ++++++-----
 6 files changed, 71 insertions(+), 17 deletions(-)

diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c
index cbd8eab0d1df..abc3581a9703 100644
--- a/tools/lib/api/fs/fs.c
+++ b/tools/lib/api/fs/fs.c
@@ -46,6 +46,10 @@
 #define BPF_FS_MAGIC           0xcafe4a11
 #endif
 
+#ifndef EVENTFS_SUPER_MAGIC
+#define EVENTFS_SUPER_MAGIC    0x65766673
+#endif
+
 static const char * const sysfs__known_mountpoints[] = {
 	"/sys",
 	0,
@@ -88,6 +92,11 @@ static const char * const bpf_fs__known_mountpoints[] = {
 	0,
 };
 
+static const char * const eventfs__known_mountpoints[] = {
+	"/sys/kernel/events",
+	0,
+};
+
 struct fs {
 	const char *		 const name;
 	const char * const *	 const mounts;
@@ -150,6 +159,7 @@ FS(debugfs, debugfs, DEBUGFS);
 FS(tracefs, tracefs, TRACEFS);
 FS(hugetlbfs, hugetlbfs, HUGETLBFS);
 FS(bpf_fs, bpf, BPF_FS);
+FS(eventfs, eventfs, EVENTFS_SUPER);
 
 static bool fs__read_mounts(struct fs *fs)
 {
diff --git a/tools/lib/api/fs/fs.h b/tools/lib/api/fs/fs.h
index aa222ca30311..57790c93f80e 100644
--- a/tools/lib/api/fs/fs.h
+++ b/tools/lib/api/fs/fs.h
@@ -36,6 +36,7 @@ FS(debugfs)
 FS(tracefs)
 FS(hugetlbfs)
 FS(bpf_fs)
+FS(eventfs)
 
 #undef FS
 
diff --git a/tools/lib/api/fs/tracing_path.c b/tools/lib/api/fs/tracing_path.c
index 834fd64c7130..de4ed7c61415 100644
--- a/tools/lib/api/fs/tracing_path.c
+++ b/tools/lib/api/fs/tracing_path.c
@@ -3,6 +3,8 @@
 # define _GNU_SOURCE
 #endif
 
+#include <dirent.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -80,13 +82,46 @@ void put_tracing_file(char *file)
 	free(file);
 }
 
+char *get_events_dir(void)
+{
+	const char *eventfs;
+	char *path;
+	int saved_errno;
+
+	path = get_tracing_file("events");
+	if (path && faccessat(AT_FDCWD, path, R_OK, AT_EACCESS) == 0)
+		return path;
+
+	saved_errno = errno;
+	put_tracing_file(path);
+
+	eventfs = eventfs__mount();
+	if (eventfs && faccessat(AT_FDCWD, eventfs, R_OK, AT_EACCESS) == 0 &&
+	    eventfs__configured())
+		return strdup(eventfs);
+
+	/*
+	 * Prefer EACCES over other errors: it tells the user that events
+	 * exist but are not accessible, which is more actionable than
+	 * ENOENT from a missing filesystem.
+	 */
+	if (errno != EACCES)
+		errno = saved_errno;
+	return NULL;
+}
+
 char *get_events_file(const char *name)
 {
-	char *file;
+	char *dir, *file;
 
-	if (asprintf(&file, "%s/events/%s", tracing_path_mount(), name) < 0)
+	dir = get_events_dir();
+	if (!dir)
 		return NULL;
 
+	if (asprintf(&file, "%s/%s", dir, name) < 0)
+		file = NULL;
+
+	free(dir);
 	return file;
 }
 
@@ -97,8 +132,8 @@ void put_events_file(char *file)
 
 DIR *tracing_events__opendir(void)
 {
+	char *path = get_events_dir();
 	DIR *dir = NULL;
-	char *path = get_tracing_file("events");
 
 	if (path) {
 		dir = opendir(path);
@@ -110,7 +145,7 @@ DIR *tracing_events__opendir(void)
 
 int tracing_events__scandir_alphasort(struct dirent ***namelist)
 {
-	char *path = get_tracing_file("events");
+	char *path = get_events_dir();
 	int ret;
 
 	if (!path) {
@@ -121,6 +156,9 @@ int tracing_events__scandir_alphasort(struct dirent ***namelist)
 	ret = scandir(path, namelist, NULL, alphasort);
 	put_events_file(path);
 
+	if (ret < 0)
+		*namelist = NULL;
+
 	return ret;
 }
 
@@ -162,12 +200,12 @@ int tracing_path__strerror_open_tp(int err, char *buf, size_t size,
 			 "Hint:\tIs the debugfs/tracefs filesystem mounted?\n"
 			 "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
 		break;
-	case EACCES: {
+	case EACCES:
 		snprintf(buf, size,
 			 "Error:\tNo permissions to read %s/events/%s\n"
-			 "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
+			 "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n"
+			 "Hint:\tOr check if /sys/kernel/events is available\n",
 			 tracing_path, filename, tracing_path_mount());
-	}
 		break;
 	default:
 		snprintf(buf, size, "%s", str_error_r(err, sbuf, sizeof(sbuf)));
diff --git a/tools/lib/api/fs/tracing_path.h b/tools/lib/api/fs/tracing_path.h
index fc6347c11deb..a93befb38f80 100644
--- a/tools/lib/api/fs/tracing_path.h
+++ b/tools/lib/api/fs/tracing_path.h
@@ -14,6 +14,7 @@ const char *tracing_path_mount(void);
 char *get_tracing_file(const char *name);
 void put_tracing_file(char *file);
 
+char *get_events_dir(void);
 char *get_events_file(const char *name);
 void put_events_file(char *file);
 
diff --git a/tools/perf/util/tp_pmu.c b/tools/perf/util/tp_pmu.c
index c2be8c9f9084..4f45bd816945 100644
--- a/tools/perf/util/tp_pmu.c
+++ b/tools/perf/util/tp_pmu.c
@@ -6,6 +6,7 @@
 #include <api/io_dir.h>
 #include <linux/kernel.h>
 #include <errno.h>
+#include <stdlib.h>
 #include <string.h>
 
 int tp_pmu__id(const char *sys, const char *name)
@@ -15,7 +16,7 @@ int tp_pmu__id(const char *sys, const char *name)
 	int id, err;
 
 	if (!tp_dir)
-		return -1;
+		return -errno;
 
 	scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name);
 	put_events_file(tp_dir);
@@ -66,7 +67,7 @@ int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb)
 	struct io_dirent64 *events_ent;
 	struct io_dir events_dir;
 	int ret = 0;
-	char *events_dir_path = get_tracing_file("events");
+	char *events_dir_path = get_events_dir();
 
 	if (!events_dir_path)
 		return -errno;
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index 45774722f249..643451d2c08d 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -92,8 +92,9 @@ static int record_header_files(void)
 	int err = -EIO;
 
 	if (!path) {
+		err = -errno;
 		pr_debug("can't get tracing/events/header_page");
-		return -ENOMEM;
+		return err;
 	}
 
 	if (stat(path, &st) < 0) {
@@ -115,8 +116,8 @@ static int record_header_files(void)
 
 	path = get_events_file("header_event");
 	if (!path) {
+		err = -errno;
 		pr_debug("can't get tracing/events/header_event");
-		err = -ENOMEM;
 		goto out;
 	}
 
@@ -228,13 +229,14 @@ static int record_ftrace_files(struct tracepoint_path *tps)
 
 	path = get_events_file("ftrace");
 	if (!path) {
+		ret = -errno;
 		pr_debug("can't get tracing/events/ftrace");
-		return -ENOMEM;
+		return ret;
 	}
 
 	ret = copy_event_system(path, tps);
 
-	put_tracing_file(path);
+	put_events_file(path);
 
 	return ret;
 }
@@ -256,15 +258,16 @@ static int record_event_files(struct tracepoint_path *tps)
 	struct stat st;
 	char *path;
 	char *sys;
-	DIR *dir;
+	DIR *dir = NULL;
 	int count = 0;
 	int ret;
 	int err;
 
-	path = get_tracing_file("events");
+	path = get_events_dir();
 	if (!path) {
+		err = -errno;
 		pr_debug("can't get tracing/events");
-		return -ENOMEM;
+		goto out;
 	}
 
 	dir = opendir(path);
@@ -315,7 +318,7 @@ static int record_event_files(struct tracepoint_path *tps)
 out:
 	if (dir)
 		closedir(dir);
-	put_tracing_file(path);
+	put_events_file(path);
 
 	return err;
 }
-- 
2.54.0


^ permalink raw reply related

* [PATCH v5 4/5] perf evsel: don't set PERF_SAMPLE_IP for unprivileged tracepoints
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
  To: rostedt, acme, peterz, Ingo Molnar, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Thomas Falcon, linux-perf-users, linux-kernel
  Cc: linux-trace-kernel, Anubhav Shelat
In-Reply-To: <20260715135231.338535-2-ashelat@redhat.com>

For tracepoint events the IP is a static kernel address.
It doesn't vary by sample and provides no useful information for
unprivileged users. Skipping setting PERF_SAMPLE_IP for unprivileged
tracepoints avoids exposing a kernel address that reveals the KASLR base
offset.

Make an exception for uprobes, which are registered as
PERF_TYPE_TRACEPOINT, because the IP is important for their
functionality and is a safe userspace address. Detect them with
__probe_ip (entry) and __probe_ret_ip (return) using evsel__field().

Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
 tools/perf/util/evsel.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index ea9fa04429f0..1d85e5503cf3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1571,7 +1571,19 @@ void evsel__config(struct evsel *evsel, const struct record_opts *opts,
 	attr->write_backward = opts->overwrite ? 1 : 0;
 	attr->read_format   = PERF_FORMAT_LOST;
 
-	evsel__set_sample_bit(evsel, IP);
+	/*
+	 * Don't set PERF_SAMPLE_IP for unprivileged kernel tracepoints to
+	 * avoid exposing kernel addresses. Uprobes expose only userspace
+	 * addresses so they're safe. Detect both entry and return uprobes.
+	 */
+	if (attr->type != PERF_TYPE_TRACEPOINT || perf_event_paranoid_check(1)
+#ifdef HAVE_LIBTRACEEVENT
+	    || evsel__field(evsel, "__probe_ip")
+	    || evsel__field(evsel, "__probe_ret_ip")
+#endif
+	    )
+		evsel__set_sample_bit(evsel, IP);
+
 	evsel__set_sample_bit(evsel, TID);
 
 	if (evsel->sample_read) {
-- 
2.54.0


^ permalink raw reply related

* [PATCH v5 5/5] perf: enable unprivileged syscall tracing with perf trace
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
  To: rostedt, acme, peterz, Ingo Molnar, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Masami Hiramatsu, Mathieu Desnoyers,
	linux-perf-users, linux-kernel, linux-trace-kernel
  Cc: Anubhav Shelat
In-Reply-To: <20260715135231.338535-2-ashelat@redhat.com>

Allow unprivileged users to trace their own processes' syscalls using
perf trace, similar to strace without the overhead of ptrace().

Currently, perf trace requires CAP_PERFMON or paranoid level ≤ 1 even
though the kernel has existing infrastructure (TRACE_EVENT_FL_CAP_ANY)
designed to mark syscall tracepoints as safe for unprivileged access.
To fix this:

1. Loosen the condition in perf_event_open() which requires privileges
   for all events with exclude_kernel=0. This allows perf_event_open() to
   bypass the paranoid check for task-attached tracepoint events. Ensure
   that sample types which can expose kernel addresses to unprivileged
   users are blocked. Ensure the PERF_SECURITY_KERNEL LSM hook is
   preserved.

2. Add a check to perf_trace_event_perm() to block PERF_SAMPLE_IP on
   kernel tracepoints for unprivileged users to prevent KASLR bypass. We do
   this here rather than in kaddr_leak because perf_trace_event_perm() can
   distinguish between kernel tracepoints and uprobe tracepoints, where the
   IP is a safe user space address and is necessary for uprobe
   functionality.

3. Restrict pure counting events (no PERF_SAMPLE_RAW) to
   TRACE_EVENT_FL_CAP_ANY tracepoints preventing unprivileged users from
   counting internal kernel tracepoints while preserving current
   behavior for exclude_kernel=1 events.

Example usage after this change:
  $ perf trace ls          # works as unprivileged user
  $ perf trace             # system-wide, still requires privileges
  $ perf trace -p 1234     # requires ptrace permission on pid 1234

Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
 kernel/events/core.c            | 28 +++++++++++++++++++++++++---
 kernel/trace/trace_event_perf.c | 28 +++++++++++++++++++++++++++-
 2 files changed, 52 insertions(+), 4 deletions(-)

diff --git a/kernel/events/core.c b/kernel/events/core.c
index 954c36e28101..48bfff07ae02 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -13910,9 +13910,31 @@ SYSCALL_DEFINE5(perf_event_open,
 		return err;
 
 	if (!attr.exclude_kernel) {
-		err = perf_allow_kernel();
-		if (err)
-			return err;
+		bool tp_bypass = false;
+
+		/* Check unprivileged tracepoints */
+		if (attr.type == PERF_TYPE_TRACEPOINT && pid != -1) {
+			/*
+			 * Block sample types that expose kernel addresses to
+			 * prevent KASLR bypass
+			 */
+			u64 kaddr_leak = PERF_SAMPLE_CALLCHAIN |
+					 PERF_SAMPLE_BRANCH_STACK |
+					 PERF_SAMPLE_ADDR |
+					 PERF_SAMPLE_REGS_INTR;
+
+			tp_bypass = !(attr.sample_type & kaddr_leak);
+		}
+
+		if (!tp_bypass) {
+			err = perf_allow_kernel();
+			if (err)
+				return err;
+		} else {
+			err = security_perf_event_open(PERF_SECURITY_KERNEL);
+			if (err)
+				return err;
+		}
 	}
 
 	if (attr.namespaces) {
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index 5b272856e5ab..a264154b460e 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -24,6 +24,16 @@ typedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)])
 /* Count the events in use (per event id, not per instance) */
 static int	total_ref_count;
 
+/* Check if perf tracepoint is restricted for unprivileged users */
+static bool perf_tp_is_restricted(struct perf_event *p_event)
+{
+	if (p_event->attr.exclude_kernel)
+		return false;
+	if (sysctl_perf_event_paranoid <= 1 || perfmon_capable())
+		return false;
+	return true;
+}
+
 static int perf_trace_event_perm(struct trace_event_call *tp_event,
 				 struct perf_event *p_event)
 {
@@ -72,9 +82,25 @@ static int perf_trace_event_perm(struct trace_event_call *tp_event,
 			return -EINVAL;
 	}
 
+	/*
+	 * PERF_SAMPLE_IP on kernel tracepoints exposes a kernel text
+	 * address, weakening KASLR. Block for unprivileged users unless
+	 * the tracepoint is a uprobe (userspace IP, safe to expose).
+	 */
+	if ((p_event->attr.sample_type & PERF_SAMPLE_IP) &&
+	    !(tp_event->flags & TRACE_EVENT_FL_UPROBE) &&
+	    perf_tp_is_restricted(p_event))
+		return -EACCES;
+
 	/* No tracing, just counting, so no obvious leak */
-	if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW))
+	if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) {
+		/* Prevent unprivileged users from counting kernel tracepoints */
+		if (perf_tp_is_restricted(p_event) &&
+		    !(p_event->attach_state == PERF_ATTACH_TASK &&
+		      (tp_event->flags & TRACE_EVENT_FL_CAP_ANY)))
+			return -EACCES;
 		return 0;
+	}
 
 	/* Some events are ok to be traced by non-root users... */
 	if (p_event->attach_state == PERF_ATTACH_TASK) {
-- 
2.54.0


^ permalink raw reply related

* Re: [RFC v3 2/2] arm64: kprobes: Allow reentering kprobes while single-stepping
From: Pu Hu @ 2026-07-15 13:56 UTC (permalink / raw)
  To: Masami Hiramatsu (Google), will@kernel.org,
	catalin.marinas@arm.com
  Cc: ada.coupriediaz@arm.com, davem@davemloft.net, Hongyan Xia,
	Jiazi Li, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
	naveen@kernel.org, yang@os.amperecomputing.com
In-Reply-To: <20260710183857.176dc60026ea9fcbe4984b1d@kernel.org>

On 7/10/2026 5:38 PM, Masami Hiramatsu wrote:
> On Fri, 10 Jul 2026 06:32:55 +0000
> Pu Hu <hupu@transsion.com> wrote:
> 
>> From: Pu Hu <hupu@transsion.com>
>>
>> A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This
>> can happen when tracing or perf code runs from the debug exception path
>> while the first kprobe is preparing or executing its out-of-line
>> single-step instruction.
>>
>> Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable,
>> the same as a hit in KPROBE_REENTER. This is too strict. A hit in
>> KPROBE_HIT_SS is still a one-level reentry and can be handled by saving
>> the current kprobe state and setting up single-step for the new probe,
>> just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
>>
>> The truly unrecoverable case is hitting another kprobe while already in
>> KPROBE_REENTER, because the reentry save area has already been consumed.
>>
>> Move KPROBE_HIT_SS to the recoverable reentry cases and leave
>> KPROBE_REENTER as the unrecoverable nested reentry case.
>>
>> This change also requires saving saved_irqflag in struct prev_kprobe.
>> When a nested kprobe calls kprobes_save_local_irqflag(), it overwrites
>> kcb->saved_irqflag with the currently masked DAIF value, losing the
>> outer kprobe's original DAIF state. Without this fix, when the outer
>> kprobe's single-step finishes, kprobes_restore_local_irqflag() applies
>> the wrong DAIF mask and leaves interrupts permanently disabled.
>>
>> Extend struct prev_kprobe with a saved_irqflag field and save/restore it
>> alongside kp and status. This ensures the outer kprobe's original
>> interrupt state is preserved across reentry.
>>
>> This mirrors the x86 fix in commit 6a5022a56ac3
>> ("kprobes/x86: Allow to handle reentered kprobe on single-stepping").
>>
> 
> OK, this looks good to me.
> 
> Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> for this series.
> 
> Will, Catalin, can you pick this series?
> 
> Thanks!
> 

Hi Will, Catalin,

Just a gentle ping on this series.

Masami has kindly reviewed it and provided his Reviewed-by tag.
I was wondering if you had a chance to take a look, or if there is
anything else I should address before it can be picked up.

No rush, and apologies for the reminder if this is already on your radar.

Thanks a lot for your time!

Thanks,
Pu Hu


^ 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