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.
 -----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCgAyFiEEXtmkj8VMCiLR0IBM68Js21pW3nMFAmpTv/cUHHJvc3RlZHRA
 Z29vZG1pcy5vcmcACgkQ68Js21pW3nN80w/9HEbliUUrJVNw8QCY8BmeBEzfCRhZ
 2CeDzTg+UrLMb2BtqUhA9EjQnRPawe8mPxAfpZ750SdFnCkRhbfNwYmKar3M/8iW
 y2Bhsvd3Hz6nRiHvKhG/1RkflgKOWLYf/TQSByKlakYLV8t4uffa0fe0N0zcnikK
 XPGrhrTeRDJ7s94vb1u7p7mSSp2iy4ZhfXbagk966X3I4zvseIYNn5IIzyM3IIPT
 M7mE60SxvgSRz0QqTJqihmqjXYSeNOQ2iU18wnvp4QfcT0IVuLKm027JnK/YpjWS
 ZasMNGzO1kp5uo6hkXLrMlT3LjaNEMFTGogs1m9o9auhbMf443LK8GtZ1m1rwnTG
 cXn1PocBevQIaXWPbnxMrSwIuN7YMUqjgX6SCxbXpDxSSuE5i/x1VmE08CjxnSm6
 TdAdom9bE44FvMgaAz9+KhIUgm013c2rFDNiFroYtfkma5FqpfnfglA4/TkJvexY
 e2L75cR3lrDEDiXXMQDndmvxNMhppwNlAH3mOZLiaCmGyFQFHhgAaG250K2Y6a5K
 HtIfJkT6RQ3rWifV0E0+zlyRiOrvVCCA8WwNArWb1dTWrVU86Hr7q58jAq9p0Yrz
 FnhpjdE3eWoUHZ5f28AMR2k5lMG3o6mOGT55JyUVCLw+rPSG5358UIg2xZiEo22k
 RFlT26FFHbMW50Y=
 =+//w
 -----END PGP SIGNATURE-----

Merge tag 'trace-v7.2-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull tracing fixes from Steven Rostedt:

 - 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

* tag 'trace-v7.2-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  ring-buffer: Allow sparse CPU masks in ring_buffer_desc()
  tracing/remotes: Fix struct_len in trace_remote_alloc_buffer()
  tracing/remotes: Fix leak in trace_remote_alloc_buffer() error path
  selftests/user_events: Wait for deferred event teardown after unregister
  tracing/synthetic: Free type string on error path
  tracing/user_events: Fix use-after-free in user_event_mm_dup()
  tracing: Add a no-rcu-check version of trace_##event##_enabled()
  tracing: Prevent out-of-bounds read in glob matching
  ufs: core: tracing: Do not dereference pointers in TP_printk()
  tracing: Fix NULL pointer dereference in func_set_flag()
  samples: ftrace: Fix typos in benchmark comment
  tracing: Make tracepoint_printk static as not exported
  ring-buffer: Fix ring_buffer_read_page() copying only one event per page
  tracing: Remove unused ret assignment in tracing_set_tracer()
  tracing/osnoise: Call synchronize_rcu() when unregistering
  ring-buffer: Fix event length with forced 8-byte alignment
  tracing/synthetic: Free pending field on error path
This commit is contained in:
Linus Torvalds 2026-07-12 09:46:37 -07:00
commit 6205562c59
16 changed files with 183 additions and 60 deletions

View File

@ -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
)
);

View File

@ -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 */

View File

@ -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; \

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}
/**

View File

@ -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:

View File

@ -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,15 +399,37 @@ 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)
{
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, 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);
/* No longer tracking the event via the enabler */
user_event_put(enabler->event, locked);
kfree(enabler);
/*
* 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,
@ -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;

View File

@ -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);

View File

@ -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);
}
/*

View File

@ -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; \

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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) {