* [GIT PULL] tracing: Fixes for 7.2
@ 2026-07-12 16:29 Steven Rostedt
2026-07-12 19:13 ` pr-tracker-bot
0 siblings, 1 reply; 2+ messages in thread
From: Steven Rostedt @ 2026-07-12 16:29 UTC (permalink / raw)
To: Linus Torvalds
Cc: LKML, Masami Hiramatsu, Mathieu Desnoyers, Ben Dooks,
Crystal Wood, David Carlier, Hui Wang, Huihui Huang,
Michael Bommarito, Vincent Donnefort, Wayen.Yan, Yu Peng,
Yuanhe Shu, Yudistira Putra
Linus,
tracing fixes for 7.2:
- Free field in error path of synthetic event parse
In __create_synth_event() the field was allocated but was not freed in the
error path.
- Fix ring_buffer_event_length() on 8 byte aligned architectures
On architectures with CONFIG_HAVE_64BIT_ALIGNED_ACCESS set to y,
the ring_buffer_event_length() may return the wrong size. This
is because archs with that config set will always use the "big
event meta header" as that is 8 bytes keeping the payload 8 bytes
aligned, even when a 4 byte header could hold the size of the
event.
But ring_buffer_event_length() doesn't take this into account and only
subtracts 4 bytes for the meta header in the length when it should have
subtracted 8 bytes.
- Have osnoise wait for a full rcu synchronization on unregister
osnoise_unregister_instance() used to call synchronize_rcu() before
freeing its copy of the instance but was switched to kfree_rcu().
The osniose tracer has code that traverses the instances that it
uses, and inst is just a pointer to that instance. By using kfree_rcu()
instead of synchronize_rcu(), the instance that the inst pointer is
pointing to can be freed while the osnoise code is still referencing it.
That is, a rmdir on an instance first unregisters the tracer. When the
unregister finishes, the rmdir expects that the tracer is finished with
the instance that it is using. By putting back the synchronize_rcu()
in osnoise_unregister_instance() the unregistering of osnoise will now
return when all the users of the instance have finished.
- Remove an unused setting of "ret" in tracing_set_tracer()
- Fix ring_buffer_read_page() copying events
The commit that changed ring_buffer_read_page() to show dropped events
from the buffer itself, split the "commit" variable between the commit
value (with flags) and "size" that holds the size of the sub-buffer.
A cut and paste error changed the test of the reading from checking the
size of the buffer to the size of the event causing reads to only read one
event at a time.
- Make tracepoint_printk a static variable
When the tracing sysctl knobs were move from sysctl.c to trace.c, the
variable tracepoint_printk no longer needed to be global. Make it static.
- Fix some typos
- Fix NULL pointer dereference in func_set_flag()
The flags update of the function tracer first checks if the value of the
flag is the same and exits if they are, and then it checks if the current
tracer is the function tracer and exits if it isn't. The problem is that
these checks need to be in a reversed order, as if the tracer isn't the
function tracer, then the flag being checked may not exist. Reverse the
order of these checks.
- Fix ufs core trace events to not dereference a pointer in TP_printk()
The TP_printk() part of the TRACE_EVENT() macro is called when the user
reads the "trace" file. This can be seconds, minutes, hours, days, weeks,
and even months after the data was recorded into the ring buffer. Thus,
saving a pointer to an object into the ring buffer and then dereferencing
it from TP_printk() can cause harm as the object the pointer is pointing
to may no longer exist.
Fix all the trace events in ufs core to save the device name in the ring
buffer instead of dereferencing the device descriptor from TP_printk().
- Prevent out-of-bound reads in glob matching of trace events
The filter logic of events allows simple glob logic to add wild cards to
filter on strings. But some events have fields that may not have a
terminating 'nul' character. This may cause the glob matching to go beyond
the string. Change the logic to always pass in the length of the field
that is being matched.
- Add no-rcu-check version of trace_##event##_enabled()
The trace_##event##_enabled() usually wraps trace events to do extra work
that is only needed when the trace event is enabled. But this can hide
events that are placed in locations where RCU is not watching, and can
make lockdep not see these bugs when the event is not enabled.
The trace_##event##_enabled() was updated to always test to make sure RCU
is watching to catch locations that may call events without RCU being
active.
This caused a false positive for the irq_disabled() and related events. As
that use trace_irq_disabled_enabled() to force RCU to be watching when the
event is enabled via the ct_irq_enter() function, calls the event, and
then calls ct_irq_exit() to put RCU back to its original state.
The trace_irq_disabled_enabled() should not trigger a warning when RCU is
not watching because the code within its block handles the case properly.
Make a __trace_##event##_enabled() version for this event to use that
doesn't check RCU is watching as it handles the case when it isn't.
- Fix use-after-free in user_event_mm_dup()
When the enabler is removed from the link list, it is freed immediately.
But it is protected via RCU and needs to be freed after an RCU
grace period. Use queue_rcu_work() so that the event_mutex can also
be taken as user_event_put() takes the mutex on the last reference
is released.
- Free type string in error path of parse_synth_field()
There's an error path in parse_synth_field() where the allocated type
string is not freed.
- Add selftest that tests deferred event teardown
- Fix leak in error path of trace_remote_alloc_buffer()
If page allocation fails, the desc->nr_cpus is not incremented for the
current CPU and the allocations done for it are not freed.
- Fix allocation length in trace_remote_alloc_buffer()
The logic to calculate the struct_len was doing a double count and setting
the value too large. Calculate the size upfront to fix the error and
simplify the logic.
- Fix sparse CPU masks in ring_buffer_desc()
If there are sparse CPUs (gaps in the numbering), the ring_buffer_desc()
will fail as it tests the CPU number against the number of CPUs that are
used.
Please pull the latest trace-v7.2-rc2 tree, which can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git
trace-v7.2-rc2
Tag SHA1: 9f254ce911b4a2c845c496725fcc86fb4c1a5b34
Head SHA1: 601ddaceb861be7eb557278109966320a6f3478c
Ben Dooks (1):
tracing: Make tracepoint_printk static as not exported
Crystal Wood (1):
tracing/osnoise: Call synchronize_rcu() when unregistering
David Carlier (1):
ring-buffer: Fix ring_buffer_read_page() copying only one event per page
Hui Wang (1):
ring-buffer: Fix event length with forced 8-byte alignment
Huihui Huang (1):
tracing: Prevent out-of-bounds read in glob matching
Michael Bommarito (2):
tracing/user_events: Fix use-after-free in user_event_mm_dup()
selftests/user_events: Wait for deferred event teardown after unregister
Steven Rostedt (2):
ufs: core: tracing: Do not dereference pointers in TP_printk()
tracing: Add a no-rcu-check version of trace_##event##_enabled()
Vincent Donnefort (3):
tracing/remotes: Fix leak in trace_remote_alloc_buffer() error path
tracing/remotes: Fix struct_len in trace_remote_alloc_buffer()
ring-buffer: Allow sparse CPU masks in ring_buffer_desc()
Wayen.Yan (1):
tracing: Remove unused ret assignment in tracing_set_tracer()
Yu Peng (2):
tracing/synthetic: Free pending field on error path
tracing/synthetic: Free type string on error path
Yuanhe Shu (1):
tracing: Fix NULL pointer dereference in func_set_flag()
Yudistira Putra (1):
samples: ftrace: Fix typos in benchmark comment
----
drivers/ufs/core/ufs_trace.h | 36 +++++++++++++++++------
include/linux/glob.h | 1 +
include/linux/tracepoint.h | 12 +++++++-
kernel/trace/ring_buffer.c | 10 +++----
kernel/trace/trace.c | 3 +-
kernel/trace/trace_events_filter.c | 6 ++--
kernel/trace/trace_events_synth.c | 10 +++++--
kernel/trace/trace_events_user.c | 39 ++++++++++++++++++++-----
kernel/trace/trace_functions.c | 8 ++---
kernel/trace/trace_osnoise.c | 4 ++-
kernel/trace/trace_preemptirq.c | 2 +-
kernel/trace/trace_remote.c | 18 ++++--------
lib/glob.c | 31 ++++++++++++++++++--
samples/ftrace/ftrace-ops.c | 4 +--
tools/testing/selftests/user_events/abi_test.c | 29 +++++++++++++++++-
tools/testing/selftests/user_events/perf_test.c | 26 +++++++++++++++--
16 files changed, 181 insertions(+), 58 deletions(-)
---------------------------
diff --git a/drivers/ufs/core/ufs_trace.h b/drivers/ufs/core/ufs_trace.h
index 309ae51b4906..377a3c54b9f5 100644
--- a/drivers/ufs/core/ufs_trace.h
+++ b/drivers/ufs/core/ufs_trace.h
@@ -89,16 +89,18 @@ TRACE_EVENT(ufshcd_clk_gating,
TP_STRUCT__entry(
__field(struct ufs_hba *, hba)
+ __string(dev_name, dev_name(hba->dev))
__field(int, state)
),
TP_fast_assign(
+ __assign_str(dev_name);
__entry->hba = hba;
__entry->state = state;
),
TP_printk("%s: gating state changed to %s",
- dev_name(__entry->hba->dev),
+ __get_str(dev_name),
__print_symbolic(__entry->state, UFSCHD_CLK_GATING_STATES))
);
@@ -111,6 +113,7 @@ TRACE_EVENT(ufshcd_clk_scaling,
TP_STRUCT__entry(
__field(struct ufs_hba *, hba)
+ __string(dev_name, dev_name(hba->dev))
__string(state, state)
__string(clk, clk)
__field(u32, prev_state)
@@ -119,6 +122,7 @@ TRACE_EVENT(ufshcd_clk_scaling,
TP_fast_assign(
__entry->hba = hba;
+ __assign_str(dev_name);
__assign_str(state);
__assign_str(clk);
__entry->prev_state = prev_state;
@@ -126,7 +130,7 @@ TRACE_EVENT(ufshcd_clk_scaling,
),
TP_printk("%s: %s %s from %u to %u Hz",
- dev_name(__entry->hba->dev), __get_str(state), __get_str(clk),
+ __get_str(dev_name), __get_str(state), __get_str(clk),
__entry->prev_state, __entry->curr_state)
);
@@ -138,16 +142,18 @@ TRACE_EVENT(ufshcd_auto_bkops_state,
TP_STRUCT__entry(
__field(struct ufs_hba *, hba)
+ __string(dev_name, dev_name(hba->dev))
__string(state, state)
),
TP_fast_assign(
__entry->hba = hba;
+ __assign_str(dev_name);
__assign_str(state);
),
TP_printk("%s: auto bkops - %s",
- dev_name(__entry->hba->dev), __get_str(state))
+ __get_str(dev_name), __get_str(state))
);
DECLARE_EVENT_CLASS(ufshcd_profiling_template,
@@ -158,6 +164,7 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template,
TP_STRUCT__entry(
__field(struct ufs_hba *, hba)
+ __string(dev_name, dev_name(hba->dev))
__string(profile_info, profile_info)
__field(s64, time_us)
__field(int, err)
@@ -165,13 +172,14 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template,
TP_fast_assign(
__entry->hba = hba;
+ __assign_str(dev_name);
__assign_str(profile_info);
__entry->time_us = time_us;
__entry->err = err;
),
TP_printk("%s: %s: took %lld usecs, err %d",
- dev_name(__entry->hba->dev), __get_str(profile_info),
+ __get_str(dev_name), __get_str(profile_info),
__entry->time_us, __entry->err)
);
@@ -200,6 +208,7 @@ DECLARE_EVENT_CLASS(ufshcd_template,
__field(s64, usecs)
__field(int, err)
__field(struct ufs_hba *, hba)
+ __string(dev_name, dev_name(hba->dev))
__field(int, dev_state)
__field(int, link_state)
),
@@ -208,13 +217,14 @@ DECLARE_EVENT_CLASS(ufshcd_template,
__entry->usecs = usecs;
__entry->err = err;
__entry->hba = hba;
+ __assign_str(dev_name);
__entry->dev_state = dev_state;
__entry->link_state = link_state;
),
TP_printk(
"%s: took %lld usecs, dev_state: %s, link_state: %s, err %d",
- dev_name(__entry->hba->dev),
+ __get_str(dev_name),
__entry->usecs,
__print_symbolic(__entry->dev_state, UFS_PWR_MODES),
__print_symbolic(__entry->link_state, UFS_LINK_STATES),
@@ -279,6 +289,7 @@ TRACE_EVENT(ufshcd_command,
TP_STRUCT__entry(
__field(struct scsi_device *, sdev)
__field(struct ufs_hba *, hba)
+ __string(dev_name, dev_name(&sdev->sdev_dev))
__field(enum ufs_trace_str_t, str_t)
__field(unsigned int, tag)
__field(u32, doorbell)
@@ -291,6 +302,7 @@ TRACE_EVENT(ufshcd_command,
),
TP_fast_assign(
+ __assign_str(dev_name);
__entry->sdev = sdev;
__entry->hba = hba;
__entry->str_t = str_t;
@@ -307,7 +319,7 @@ TRACE_EVENT(ufshcd_command,
TP_printk(
"%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x (%s), group_id: 0x%x, hwq_id: %d",
show_ufs_cmd_trace_str(__entry->str_t),
- dev_name(&__entry->sdev->sdev_dev), __entry->tag,
+ __get_str(dev_name), __entry->tag,
__entry->doorbell, __entry->transfer_len, __entry->intr,
__entry->lba, (u32)__entry->opcode, str_opcode(__entry->opcode),
(u32)__entry->group_id, __entry->hwq_id
@@ -322,6 +334,7 @@ TRACE_EVENT(ufshcd_uic_command,
TP_STRUCT__entry(
__field(struct ufs_hba *, hba)
+ __string(dev_name, dev_name(hba->dev))
__field(enum ufs_trace_str_t, str_t)
__field(u32, cmd)
__field(u32, arg1)
@@ -331,6 +344,7 @@ TRACE_EVENT(ufshcd_uic_command,
TP_fast_assign(
__entry->hba = hba;
+ __assign_str(dev_name);
__entry->str_t = str_t;
__entry->cmd = cmd;
__entry->arg1 = arg1;
@@ -340,7 +354,7 @@ TRACE_EVENT(ufshcd_uic_command,
TP_printk(
"%s: %s: cmd: 0x%x, arg1: 0x%x, arg2: 0x%x, arg3: 0x%x",
- show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev),
+ show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name),
__entry->cmd, __entry->arg1, __entry->arg2, __entry->arg3
)
);
@@ -353,6 +367,7 @@ TRACE_EVENT(ufshcd_upiu,
TP_STRUCT__entry(
__field(struct ufs_hba *, hba)
+ __string(dev_name, dev_name(hba->dev))
__field(enum ufs_trace_str_t, str_t)
__array(unsigned char, hdr, 12)
__array(unsigned char, tsf, 16)
@@ -361,6 +376,7 @@ TRACE_EVENT(ufshcd_upiu,
TP_fast_assign(
__entry->hba = hba;
+ __assign_str(dev_name);
__entry->str_t = str_t;
memcpy(__entry->hdr, hdr, sizeof(__entry->hdr));
memcpy(__entry->tsf, tsf, sizeof(__entry->tsf));
@@ -369,7 +385,7 @@ TRACE_EVENT(ufshcd_upiu,
TP_printk(
"%s: %s: HDR:%s, %s:%s",
- show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev),
+ show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name),
__print_hex(__entry->hdr, sizeof(__entry->hdr)),
show_ufs_cmd_trace_tsf(__entry->tsf_t),
__print_hex(__entry->tsf, sizeof(__entry->tsf))
@@ -384,16 +400,18 @@ TRACE_EVENT(ufshcd_exception_event,
TP_STRUCT__entry(
__field(struct ufs_hba *, hba)
+ __string(dev_name, dev_name(hba->dev))
__field(u16, status)
),
TP_fast_assign(
__entry->hba = hba;
+ __assign_str(dev_name);
__entry->status = status;
),
TP_printk("%s: status 0x%x",
- dev_name(__entry->hba->dev), __entry->status
+ __get_str(dev_name), __entry->status
)
);
diff --git a/include/linux/glob.h b/include/linux/glob.h
index 861327b33e41..91595e750936 100644
--- a/include/linux/glob.h
+++ b/include/linux/glob.h
@@ -6,5 +6,6 @@
#include <linux/compiler.h> /* For __pure */
bool __pure glob_match(char const *pat, char const *str);
+bool __pure glob_match_len(char const *pat, char const *str, size_t len);
#endif /* _LINUX_GLOB_H */
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index 4a0c36f40fe2..e0d838c9ce93 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -292,13 +292,18 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
{ \
} \
static inline bool \
+ __trace_##name##_enabled(void) \
+ { \
+ return static_branch_unlikely(&__tracepoint_##name.key);\
+ } \
+ static inline bool \
trace_##name##_enabled(void) \
{ \
if (IS_ENABLED(CONFIG_LOCKDEP)) { \
WARN_ONCE(!rcu_is_watching(), \
"RCU not watching for tracepoint"); \
} \
- return static_branch_unlikely(&__tracepoint_##name.key);\
+ return __trace_##name##_enabled(); \
}
#define __DECLARE_TRACE(name, proto, args, cond, data_proto) \
@@ -457,6 +462,11 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
{ \
} \
static inline bool \
+ __trace_##name##_enabled(void) \
+ { \
+ return false; \
+ } \
+ static inline bool \
trace_##name##_enabled(void) \
{ \
return false; \
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 56a328e94395..804ccae694d2 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -270,7 +270,8 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event)
if (event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX)
return length;
length -= RB_EVNT_HDR_SIZE;
- if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]))
+ if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]) ||
+ RB_FORCE_8BYTE_ALIGNMENT)
length -= sizeof(event->array[0]);
return length;
}
@@ -2329,10 +2330,7 @@ static struct ring_buffer_desc *ring_buffer_desc(struct trace_buffer_desc *trace
size_t len;
int i;
- if (!trace_desc)
- return NULL;
-
- if (cpu >= trace_desc->nr_cpus)
+ if (!trace_desc || !trace_desc->nr_cpus)
return NULL;
end = (struct ring_buffer_desc *)((void *)trace_desc + trace_desc->struct_len);
@@ -7174,7 +7172,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
rpos = reader->read;
pos += event_size;
- if (rpos >= event_size)
+ if (rpos >= size)
break;
event = rb_reader_event(cpu_buffer);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 1146b83b711a..18710c190c92 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -87,7 +87,7 @@ void __init disable_tracing_selftest(const char *reason)
/* Pipe tracepoints to printk */
static struct trace_iterator *tracepoint_print_iter;
-int tracepoint_printk;
+static int tracepoint_printk;
static bool tracepoint_printk_stop_on_boot __initdata;
static bool traceoff_after_boot __initdata;
static DEFINE_STATIC_KEY_FALSE(tracepoint_printk_key);
@@ -5015,7 +5015,6 @@ int tracing_set_tracer(struct trace_array *tr, const char *buf)
RING_BUFFER_ALL_CPUS);
if (ret < 0)
return ret;
- ret = 0;
}
list_for_each_entry(t, &tr->tracers, list) {
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 609325f57942..6385cd662d8d 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -1056,11 +1056,9 @@ static int regex_match_end(char *str, struct regex *r, int len)
return 0;
}
-static int regex_match_glob(char *str, struct regex *r, int len __maybe_unused)
+static int regex_match_glob(char *str, struct regex *r, int len)
{
- if (glob_match(r->pattern, str))
- return 1;
- return 0;
+ return glob_match_len(r->pattern, str, len) ? 1 : 0;
}
/**
diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c
index e6871230bde9..dc15658a887c 100644
--- a/kernel/trace/trace_events_synth.c
+++ b/kernel/trace/trace_events_synth.c
@@ -839,8 +839,10 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
seq_buf_puts(&s, "__data_loc ");
seq_buf_puts(&s, field->type);
- if (WARN_ON_ONCE(!seq_buf_buffer_left(&s)))
+ if (WARN_ON_ONCE(!seq_buf_buffer_left(&s))) {
+ kfree(type);
goto free;
+ }
s.buffer[s.len] = '\0';
kfree(field->type);
@@ -1446,13 +1448,13 @@ static int __create_synth_event(const char *name, const char *raw_fields)
if (cmd_version > 1 && n_fields_this_loop >= 1) {
synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str));
ret = -EINVAL;
- goto err_free_arg;
+ goto err_free_field;
}
if (n_fields == SYNTH_FIELDS_MAX) {
synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0);
ret = -EINVAL;
- goto err_free_arg;
+ goto err_free_field;
}
fields[n_fields++] = field;
@@ -1491,6 +1493,8 @@ static int __create_synth_event(const char *name, const char *raw_fields)
kfree(saved_fields);
return ret;
+ err_free_field:
+ free_synth_field(field);
err_free_arg:
argv_free(argv);
err:
diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index c4ba484f7b38..8c82ecb735f4 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -109,6 +109,9 @@ struct user_event_enabler {
/* Track enable bit, flags, etc. Aligned for bitops. */
unsigned long values;
+
+ /* Defer the event put and enabler free past an RCU grace period. */
+ struct rcu_work put_rwork;
};
/* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */
@@ -396,17 +399,39 @@ static struct user_event_group *user_event_group_create(void)
return NULL;
};
-static void user_event_enabler_destroy(struct user_event_enabler *enabler,
- bool locked)
+static void delayed_user_event_enabler_put(struct work_struct *work)
{
- list_del_rcu(&enabler->mm_enablers_link);
+ struct user_event_enabler *enabler = container_of(to_rcu_work(work),
+ struct user_event_enabler, put_rwork);
/* No longer tracking the event via the enabler */
- user_event_put(enabler->event, locked);
+ user_event_put(enabler->event, false);
+ /* Run from queue_rcu_work(), the RCU grace period has elapsed */
kfree(enabler);
}
+static void user_event_enabler_destroy(struct user_event_enabler *enabler)
+{
+ list_del_rcu(&enabler->mm_enablers_link);
+
+ /*
+ * The enabler is removed from an RCU-traversed list
+ * (user_event_mm_dup() walks mm->enablers under rcu_read_lock() only),
+ * and readers there dereference enabler->event and take a new ref on
+ * it. Both the put of that event reference and the free of the enabler
+ * therefore have to wait for a grace period so no reader can be looking
+ * at the enabler or racing the last put of its event.
+ *
+ * The put itself must not run in RCU context: when it drops the last
+ * reference user_event_put() takes event_mutex, which cannot be taken
+ * from a softirq/RCU callback. Defer both to a work item scheduled
+ * after a grace period via queue_rcu_work().
+ */
+ INIT_RCU_WORK(&enabler->put_rwork, delayed_user_event_enabler_put);
+ queue_rcu_work(system_percpu_wq, &enabler->put_rwork);
+}
+
static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr,
int attempt)
{
@@ -464,7 +489,7 @@ static void user_event_enabler_fault_fixup(struct work_struct *work)
/* User asked for enabler to be removed during fault */
if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) {
- user_event_enabler_destroy(enabler, true);
+ user_event_enabler_destroy(enabler);
goto out;
}
@@ -764,7 +789,7 @@ static void user_event_mm_destroy(struct user_event_mm *mm)
struct user_event_enabler *enabler, *next;
list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link)
- user_event_enabler_destroy(enabler, false);
+ user_event_enabler_destroy(enabler);
mmdrop(mm->mm);
kfree(mm);
@@ -2645,7 +2670,7 @@ static long user_events_ioctl_unreg(unsigned long uarg)
flags |= enabler->values & ENABLE_VAL_COMPAT_MASK;
if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler)))
- user_event_enabler_destroy(enabler, true);
+ user_event_enabler_destroy(enabler);
/* Removed at least one */
ret = 0;
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index f283391a4dc8..cd37f2013758 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -458,12 +458,12 @@ func_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
ftrace_func_t func;
u32 new_flags;
- /* Do nothing if already set. */
- if (!!set == !!(tr->current_trace_flags->val & bit))
+ /* We can change this flag only when current tracer is function. */
+ if (tr->current_trace != &function_trace)
return 0;
- /* We can change this flag only when not running. */
- if (tr->current_trace != &function_trace)
+ /* Do nothing if already set. */
+ if (!!set == !!(tr->current_trace_flags->val & bit))
return 0;
new_flags = (tr->current_trace_flags->val & ~bit) | (set ? bit : 0);
diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c
index 5e83c4f6f2b4..0e1265acd1cc 100644
--- a/kernel/trace/trace_osnoise.c
+++ b/kernel/trace/trace_osnoise.c
@@ -179,7 +179,9 @@ static void osnoise_unregister_instance(struct trace_array *tr)
if (!found)
return;
- kvfree_rcu_mightsleep(inst);
+ /* Do a full sync to ensure that tr remains valid, not just inst */
+ synchronize_rcu();
+ kvfree(inst);
}
/*
diff --git a/kernel/trace/trace_preemptirq.c b/kernel/trace/trace_preemptirq.c
index 0c42b15c3800..b63e3558948f 100644
--- a/kernel/trace/trace_preemptirq.c
+++ b/kernel/trace/trace_preemptirq.c
@@ -30,7 +30,7 @@
#else
#define trace(point, args) \
do { \
- if (trace_##point##_enabled()) { \
+ if (__trace_##point##_enabled()) { \
bool exit_rcu = false; \
if (in_nmi()) \
break; \
diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c
index 2a6cc000ec98..0f6ef5c36d84 100644
--- a/kernel/trace/trace_remote.c
+++ b/kernel/trace/trace_remote.c
@@ -979,33 +979,30 @@ EXPORT_SYMBOL_GPL(trace_remote_free_buffer);
int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, size_t buffer_size,
const struct cpumask *cpumask)
{
+ size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask));
unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1;
- void *desc_end = desc + desc_size;
struct ring_buffer_desc *rb_desc;
int cpu, ret = -ENOMEM;
- if (desc_size < struct_size(desc, __data, 0))
+ if (desc_size < min_desc_size)
return -EINVAL;
desc->nr_cpus = 0;
- desc->struct_len = struct_size(desc, __data, 0);
+ desc->struct_len = min_desc_size;
- rb_desc = (struct ring_buffer_desc *)&desc->__data[0];
+ rb_desc = __first_ring_buffer_desc(desc);
for_each_cpu(cpu, cpumask) {
unsigned int id;
- if ((void *)rb_desc + struct_size(rb_desc, page_va, nr_pages) > desc_end) {
- ret = -EINVAL;
- goto err;
- }
-
rb_desc->cpu = cpu;
rb_desc->nr_page_va = 0;
rb_desc->meta_va = (unsigned long)__get_free_page(GFP_KERNEL);
if (!rb_desc->meta_va)
goto err;
+ desc->nr_cpus++;
+
for (id = 0; id < nr_pages; id++) {
rb_desc->page_va[id] = (unsigned long)__get_free_page(GFP_KERNEL);
if (!rb_desc->page_va[id])
@@ -1013,9 +1010,6 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size,
rb_desc->nr_page_va++;
}
- desc->nr_cpus++;
- desc->struct_len += offsetof(struct ring_buffer_desc, page_va);
- desc->struct_len += struct_size(rb_desc, page_va, rb_desc->nr_page_va);
rb_desc = __next_ring_buffer_desc(rb_desc);
}
diff --git a/lib/glob.c b/lib/glob.c
index 7aca76c25bcb..c80d9dd736b4 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -11,6 +11,9 @@
MODULE_DESCRIPTION("glob(7) matching");
MODULE_LICENSE("Dual MIT/GPL");
+static bool __pure glob_match_str(char const *pat, char const *str,
+ char const *str_end);
+
/**
* glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
* @pat: Shell-style pattern to match, e.g. "*.[ch]".
@@ -40,6 +43,29 @@ MODULE_LICENSE("Dual MIT/GPL");
* An opening bracket without a matching close is matched literally.
*/
bool __pure glob_match(char const *pat, char const *str)
+{
+ return glob_match_str(pat, str, NULL);
+}
+EXPORT_SYMBOL(glob_match);
+
+/**
+ * glob_match_len - glob match against a length-bounded string
+ * @pat: Shell-style pattern to match.
+ * @str: String to match. Need not be NUL-terminated.
+ * @len: Number of bytes of @str that may be read.
+ *
+ * Like glob_match(), but @str is only read up to @len bytes, so it can be
+ * used on buffers that are not NUL-terminated (e.g. trace event fields).
+ * A NUL byte within @len still terminates the string.
+ */
+bool __pure glob_match_len(char const *pat, char const *str, size_t len)
+{
+ return glob_match_str(pat, str, str + len);
+}
+EXPORT_SYMBOL(glob_match_len);
+
+static bool __pure glob_match_str(char const *pat, char const *str,
+ char const *str_end)
{
/*
* Backtrack to previous * on mismatch and retry starting one
@@ -55,9 +81,11 @@ bool __pure glob_match(char const *pat, char const *str)
* on mismatch, or true after matching the trailing nul bytes.
*/
for (;;) {
- unsigned char c = *str++;
+ unsigned char c = (str_end && str >= str_end) ? '\0' : *str;
unsigned char d = *pat++;
+ str++;
+
switch (d) {
case '?': /* Wildcard: anything but nul */
if (c == '\0')
@@ -125,4 +153,3 @@ bool __pure glob_match(char const *pat, char const *str)
}
}
}
-EXPORT_SYMBOL(glob_match);
diff --git a/samples/ftrace/ftrace-ops.c b/samples/ftrace/ftrace-ops.c
index 68d6685c80bd..152ffc1a30b6 100644
--- a/samples/ftrace/ftrace-ops.c
+++ b/samples/ftrace/ftrace-ops.c
@@ -232,8 +232,8 @@ static int __init ftrace_ops_sample_init(void)
ops_destroy(ops_irrelevant, nr_ops_irrelevant);
/*
- * The benchmark completed sucessfully, but there's no reason to keep
- * the module around. Return an error do the user doesn't have to
+ * The benchmark completed successfully, but there's no reason to keep
+ * the module around. Return an error so the user doesn't have to
* manually unload the module.
*/
return -EINVAL;
diff --git a/tools/testing/selftests/user_events/abi_test.c b/tools/testing/selftests/user_events/abi_test.c
index 85892b3b719c..b71813eaf5c0 100644
--- a/tools/testing/selftests/user_events/abi_test.c
+++ b/tools/testing/selftests/user_events/abi_test.c
@@ -132,6 +132,33 @@ static int event_delete(void)
return ret;
}
+/*
+ * Deleting an event drops its last reference, but an unregister may defer
+ * that put (and the freeing of the associated enabler) past an RCU grace
+ * period. The delete can therefore transiently fail with -EBUSY while the
+ * previous reference is still being dropped. Retry only on that transient
+ * failure; treat an already-deleted event (-ENOENT) as success and return
+ * any other error immediately rather than spinning for the full timeout.
+ */
+static int wait_for_event_delete(void)
+{
+ int i, ret;
+
+ for (i = 0; i < 10000; ++i) {
+ ret = event_delete();
+
+ if (ret == 0 || errno == ENOENT)
+ return 0;
+
+ if (errno != EBUSY)
+ return ret;
+
+ usleep(1000);
+ }
+
+ return ret;
+}
+
static int reg_enable_multi(void *enable, int size, int bit, int flags,
char *args)
{
@@ -262,7 +289,7 @@ TEST_F(user, flags) {
ASSERT_TRUE(event_exists());
/* Ensure we can delete it */
- ASSERT_EQ(0, event_delete());
+ ASSERT_EQ(0, wait_for_event_delete());
/* USER_EVENT_REG_MAX or above is not allowed */
ASSERT_EQ(-1, reg_enable_flags(&self->check, sizeof(int), 0,
diff --git a/tools/testing/selftests/user_events/perf_test.c b/tools/testing/selftests/user_events/perf_test.c
index cafec0e52eb3..5727cb5b914c 100644
--- a/tools/testing/selftests/user_events/perf_test.c
+++ b/tools/testing/selftests/user_events/perf_test.c
@@ -85,6 +85,7 @@ static int get_offset(void)
static int clear(int *check)
{
struct user_unreg unreg = {0};
+ int i, ret;
unreg.size = sizeof(unreg);
unreg.disable_bit = 31;
@@ -99,13 +100,32 @@ static int clear(int *check)
if (errno != ENOENT)
return -1;
- if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1)
- if (errno != ENOENT)
+ /*
+ * Deleting the event drops its last reference, but the unregister
+ * above defers that put (and the freeing of the enabler) past an RCU
+ * grace period. The delete can therefore transiently fail with -EBUSY
+ * until that reference is dropped. Retry for up to ~10 seconds so the
+ * event is actually gone before the next test registers the same name.
+ */
+ for (i = 0; i < 10000; ++i) {
+ ret = ioctl(fd, DIAG_IOCSDEL, "__test_event");
+
+ if (ret == 0 || errno == ENOENT) {
+ ret = 0;
+ break;
+ }
+
+ if (errno != EBUSY) {
+ close(fd);
return -1;
+ }
+
+ usleep(1000);
+ }
close(fd);
- return 0;
+ return ret;
}
FIXTURE(user) {
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [GIT PULL] tracing: Fixes for 7.2
2026-07-12 16:29 [GIT PULL] tracing: Fixes for 7.2 Steven Rostedt
@ 2026-07-12 19:13 ` pr-tracker-bot
0 siblings, 0 replies; 2+ messages in thread
From: pr-tracker-bot @ 2026-07-12 19:13 UTC (permalink / raw)
To: Steven Rostedt
Cc: Linus Torvalds, LKML, Masami Hiramatsu, Mathieu Desnoyers,
Ben Dooks, Crystal Wood, David Carlier, Hui Wang, Huihui Huang,
Michael Bommarito, Vincent Donnefort, Wayen.Yan, Yu Peng,
Yuanhe Shu, Yudistira Putra
The pull request you sent on Sun, 12 Jul 2026 12:29:20 -0400:
> git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git trace-v7.2-rc2
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/6205562c5904ee23786239298299043876b1a977
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-12 19:13 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-12 16:29 [GIT PULL] tracing: Fixes for 7.2 Steven Rostedt
2026-07-12 19:13 ` pr-tracker-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox