diff options
| author | Ingo Molnar <mingo@kernel.org> | 2026-07-31 11:41:07 +0200 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2026-07-31 11:41:07 +0200 |
| commit | f70494ac4c07bcd3c5d2d8af96591032c69d9d44 (patch) | |
| tree | 053cc4730ed3e999c1bea23dc23df168b5d856bc /kernel/trace | |
| parent | edda9051e267b7390c7ce24b1b71434414ad156e (diff) | |
| parent | cc679d7a6303e84d769f2afcde1fc51c51f127cd (diff) | |
Merge branch 'perf/urgent' into perf/core, to pick up fixes
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/trace')
| -rw-r--r-- | kernel/trace/fprobe.c | 10 | ||||
| -rw-r--r-- | kernel/trace/ftrace.c | 13 | ||||
| -rw-r--r-- | kernel/trace/ring_buffer.c | 10 | ||||
| -rw-r--r-- | kernel/trace/trace.c | 5 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 1 | ||||
| -rw-r--r-- | kernel/trace/trace_eprobe.c | 5 | ||||
| -rw-r--r-- | kernel/trace/trace_events.c | 4 | ||||
| -rw-r--r-- | kernel/trace/trace_events_filter.c | 6 | ||||
| -rw-r--r-- | kernel/trace/trace_events_hist.c | 2 | ||||
| -rw-r--r-- | kernel/trace/trace_events_synth.c | 10 | ||||
| -rw-r--r-- | kernel/trace/trace_events_trigger.c | 22 | ||||
| -rw-r--r-- | kernel/trace/trace_events_user.c | 39 | ||||
| -rw-r--r-- | kernel/trace/trace_functions.c | 8 | ||||
| -rw-r--r-- | kernel/trace/trace_mmiotrace.c | 4 | ||||
| -rw-r--r-- | kernel/trace/trace_osnoise.c | 4 | ||||
| -rw-r--r-- | kernel/trace/trace_preemptirq.c | 2 | ||||
| -rw-r--r-- | kernel/trace/trace_probe.c | 28 | ||||
| -rw-r--r-- | kernel/trace/trace_probe.h | 2 | ||||
| -rw-r--r-- | kernel/trace/trace_remote.c | 34 | ||||
| -rw-r--r-- | kernel/trace/trace_syscalls.c | 5 |
20 files changed, 148 insertions, 66 deletions
diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index f378613ad120..f215990b9061 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -613,6 +613,16 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops continue; data_size = fp->entry_data_size; + /* + * The list may have grown since it was sized, so this node + * may not fit. Skip it as missed rather than overrun the + * reservation. + */ + if (fp->exit_handler && + used + FPROBE_HEADER_SIZE_IN_LONG + SIZE_IN_LONG(data_size) > reserved_words) { + fp->nmissed++; + continue; + } if (data_size && fp->exit_handler) data = fgraph_data + used + FPROBE_HEADER_SIZE_IN_LONG; else diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f93e34dd2328..6c47a94f5924 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1098,6 +1098,12 @@ struct ftrace_ops global_ops = { }; /* + * parser_lock - Protects trace_parser state against concurrent operations. + * Held across trace_get_user() and subsequent buffer parsing to prevent races. + */ +static DEFINE_MUTEX(parser_lock); + +/* * Used by the stack unwinder to know about dynamic ftrace trampolines. */ struct ftrace_ops *ftrace_ops_trampoline(unsigned long addr) @@ -5842,6 +5848,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 +6992,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 +7331,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 +7449,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/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..01a5e87af299 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) { @@ -6188,7 +6187,7 @@ char *trace_user_fault_read(struct trace_user_buf_info *tinfo, { int cpu = smp_processor_id(); char *buffer = per_cpu_ptr(tinfo->tbuf, cpu)->buf; - unsigned int cnt; + unsigned long long cnt; int trys = 0; int ret; diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 80fe152af1dd..bf77331f56a4 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1941,6 +1941,7 @@ struct event_trigger_data { struct list_head named_list; struct event_trigger_data *named_data; struct llist_node llist; + void (*private_data_free)(struct event_trigger_data *data); }; /* Avoid typos */ diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index b66d6196338d..bcd97cb24ac9 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -172,7 +172,8 @@ static bool eprobe_dyn_event_match(const char *system, const char *event, if (!slash) return false; - if (strncmp(ep->event_system, argv[0], slash - argv[0])) + if (strncmp(ep->event_system, argv[0], slash - argv[0]) || + ep->event_system[slash - argv[0]] != '\0') return false; if (strcmp(ep->event_name, slash + 1)) return false; @@ -315,7 +316,7 @@ get_event_field(struct fetch_insn *code, void *rec) val = (unsigned long)addr; break; case FILTER_PTR_STRING: - val = (unsigned long)(*(char *)addr); + val = *(unsigned long *)addr; break; default: WARN_ON_ONCE(1); diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index c46e623e7e0d..956692856fa8 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1350,7 +1350,9 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match, call = file->event_call; /* If a module is specified, skip events that are not that module */ - if (module && (!call->module || strcmp(module_name(call->module), module))) + if (module && + ((call->flags & TRACE_EVENT_FL_DYNAMIC) || + !call->module || strcmp(module_name(call->module), module))) continue; name = trace_event_name(call); 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_hist.c b/kernel/trace/trace_events_hist.c index 82ce492ab268..58d28cd1afa3 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -6349,6 +6349,7 @@ static void event_hist_trigger_free(struct event_trigger_data *data) trigger_data_free(data); + tracepoint_synchronize_unregister(); remove_hist_vars(hist_data); unregister_field_var_hists(hist_data); @@ -6388,6 +6389,7 @@ static void event_hist_trigger_named_free(struct event_trigger_data *data) del_named_trigger(data); trigger_data_free(data); + tracepoint_synchronize_unregister(); kfree(cmd_ops); } } 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_trigger.c b/kernel/trace/trace_events_trigger.c index 655db2e82513..ad83419cb420 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -38,6 +38,13 @@ static void trigger_create_kthread_locked(void) } } +static void trigger_data_free_one(struct event_trigger_data *data) +{ + if (data->private_data_free) + data->private_data_free(data); + kfree(data); +} + static void trigger_data_free_queued_locked(void) { struct event_trigger_data *data, *tmp; @@ -52,7 +59,7 @@ static void trigger_data_free_queued_locked(void) tracepoint_synchronize_unregister(); llist_for_each_entry_safe(data, tmp, llnodes, llist) - kfree(data); + trigger_data_free_one(data); } /* Bulk garbage collection of event_trigger_data elements */ @@ -75,7 +82,7 @@ static int trigger_kthread_fn(void *ignore) tracepoint_synchronize_unregister(); llist_for_each_entry_safe(data, tmp, llnodes, llist) - kfree(data); + trigger_data_free_one(data); } return 0; @@ -1717,6 +1724,14 @@ int event_enable_trigger_print(struct seq_file *m, return 0; } +static void enable_trigger_private_data_free(struct event_trigger_data *data) +{ + struct enable_trigger_data *enable_data = data->private_data; + + trace_event_put_ref(enable_data->file->event_call); + kfree(enable_data); +} + void event_enable_trigger_free(struct event_trigger_data *data) { struct enable_trigger_data *enable_data = data->private_data; @@ -1728,9 +1743,8 @@ void event_enable_trigger_free(struct event_trigger_data *data) if (!data->ref) { /* Remove the SOFT_MODE flag */ trace_event_enable_disable(enable_data->file, 0, 1); - trace_event_put_ref(enable_data->file->event_call); + data->private_data_free = enable_trigger_private_data_free; trigger_data_free(data); - kfree(enable_data); } } 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 @@ error: 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_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index 226cf66e0d68..b88b8d9923ad 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -109,7 +109,6 @@ static void mmio_pipe_open(struct trace_iterator *iter) iter->private = hiter; } -/* XXX: This is not called when the pipe is closed! */ static void mmio_close(struct trace_iterator *iter) { struct header_iter *hiter = iter->private; @@ -146,7 +145,7 @@ static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp, goto print_out; } - if (!hiter) + if (!hiter || !hiter->dev) return 0; mmio_print_pcidev(s, hiter->dev); @@ -279,6 +278,7 @@ static struct tracer mmio_tracer __read_mostly = .start = mmio_trace_start, .pipe_open = mmio_pipe_open, .close = mmio_close, + .pipe_close = mmio_close, .read = mmio_read, .print_line = mmio_print_line, .noboot = true, 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_probe.c b/kernel/trace/trace_probe.c index fd1caa1f9723..506e6037e163 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -188,7 +188,7 @@ void __trace_probe_log_err(int offset, int err_type) lockdep_assert_held(&dyn_event_ops_mutex); - if (!trace_probe_log.argv) + if (!trace_probe_log.argv || !trace_probe_log.argc) return; /* Recalculate the length and allocate buffer */ @@ -342,10 +342,6 @@ static int parse_trace_event(char *arg, struct fetch_insn *code, ret = parse_trace_event_arg(arg, code, ctx); if (!ret) return 0; - if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { - code->op = FETCH_OP_COMM; - return 0; - } return -EINVAL; } @@ -678,7 +674,7 @@ static int parse_btf_arg(char *varname, int i, is_ptr, ret; u32 tid; - if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT))) + if (!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT)) return -EINVAL; is_ptr = split_next_field(varname, &field, ctx); @@ -1068,8 +1064,14 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, int len; if (ctx->flags & TPARG_FL_TEVENT) { - if (parse_trace_event(arg, code, ctx) < 0) + if (parse_trace_event(arg, code, ctx) < 0) { + /* 'comm' should be checked after field parsing. */ + if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { + code->op = FETCH_OP_COMM; + return 0; + } goto inval; + } return 0; } @@ -1241,6 +1243,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->op = FETCH_OP_FOFFS; code->immediate = (unsigned long)offset; // imm64? + offset = 0; } else { /* uprobes don't support symbols */ if (!(ctx->flags & TPARG_FL_KERNEL)) { @@ -2010,7 +2013,7 @@ int traceprobe_update_arg(struct probe_arg *arg) } /* When len=0, we just calculate the needed length */ -#define LEN_OR_ZERO (len ? len - pos : 0) +#define LEN_OR_ZERO (len > pos ? len - pos : 0) static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, enum probe_print_type ptype) { @@ -2335,16 +2338,17 @@ int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b) bool trace_probe_match_command_args(struct trace_probe *tp, int argc, const char **argv) { - char buf[MAX_ARGSTR_LEN + 1]; int i; if (tp->nr_args < argc) return false; for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "%s=%s", - tp->args[i].name, tp->args[i].comm); - if (strcmp(buf, argv[i])) + int len = strlen(tp->args[i].name); + + if (strncmp(argv[i], tp->args[i].name, len) || + argv[i][len] != '=' || + strcmp(argv[i] + len + 1, tp->args[i].comm)) return false; } return true; diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 15758cc11fc6..0f09f7aaf93f 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -511,7 +511,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, C(NO_RETVAL, "This function returns 'void' type"), \ C(BAD_STACK_NUM, "Invalid stack number"), \ C(BAD_ARG_NUM, "Invalid argument number"), \ - C(BAD_VAR, "Invalid $-valiable specified"), \ + C(BAD_VAR, "Invalid $-variable specified"), \ C(BAD_REG_NAME, "Invalid register name"), \ C(BAD_MEM_ADDR, "Invalid memory address"), \ C(BAD_IMM, "Invalid immediate value"), \ diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index 2a6cc000ec98..e6724f947170 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -979,43 +979,36 @@ 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->nr_page_va++; rb_desc->page_va[id] = (unsigned long)__get_free_page(GFP_KERNEL); if (!rb_desc->page_va[id]) goto err; - - 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); } @@ -1156,10 +1149,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; } diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index e98ee7e1e66f..8a4f3c75e39f 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -1451,6 +1451,11 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) if (syscall_get_data(sys_data, args, &user_ptr, &size, user_sizes, &uargs, buf_size) < 0) return; + + /* The above may have caused a migration */ + head = this_cpu_ptr(sys_data->enter_event->perf_events); + if (hlist_empty(head)) + return; } /* get the size after alignment with the u32 buffer size field */ |
