diff --git a/Documentation/trace/events.txt b/Documentation/trace/events.txt index 9bd4b7f966b5..820bbf3b892a 100644 --- a/Documentation/trace/events.txt +++ b/Documentation/trace/events.txt @@ -669,15 +669,15 @@ triggers (you have to use '!' for each one added.) The examples below provide a more concrete illustration of the concepts and typical usage patterns discussed above. - 'synthetic' event fields + 'special' event fields ------------------------ - There are a number of 'synthetic fields' available for use as keys - or values in a hist trigger. These look like and behave as if they - were event fields, but aren't actually part of the event's field - definition or format file. They are however available for any + There are a number of 'special event fields' available for use as + keys or values in a hist trigger. These look like and behave as if + they were actual event fields, but aren't really part of the event's + field definition or format file. They are however available for any event, and can be used anywhere an actual event field could be. - 'Synthetic' field names are always prefixed with a '$' character to + 'Special' field names are always prefixed with a '$' character to indicate that they're not normal fields (with the exception of 'cpu', for compatibility with existing filter usage): diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index df6fd9df698e..15e7ff4ea178 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -530,7 +530,7 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize) return false; } - zram_meta_init_table_locks(zram, disksize); + zram_meta_init_table_locks(zram, num_pages); return true; } diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 7e55aa9ce680..85750ec39645 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -52,6 +52,31 @@ static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *da return container_of(data, struct tpm_tis_tcg_phy, priv); } +#ifdef CONFIG_PREEMPT_RT_FULL +/* + * Flushes previous write operations to chip so that a subsequent + * ioread*()s won't stall a cpu. + */ +static inline void tpm_tis_flush(void __iomem *iobase) +{ + ioread8(iobase + TPM_ACCESS(0)); +} +#else +#define tpm_tis_flush(iobase) do { } while (0) +#endif + +static inline void tpm_tis_iowrite8(u8 b, void __iomem *iobase, u32 addr) +{ + iowrite8(b, iobase + addr); + tpm_tis_flush(iobase); +} + +static inline void tpm_tis_iowrite32(u32 b, void __iomem *iobase, u32 addr) +{ + iowrite32(b, iobase + addr); + tpm_tis_flush(iobase); +} + static bool interrupts = true; module_param(interrupts, bool, 0444); MODULE_PARM_DESC(interrupts, "Enable interrupts"); @@ -230,7 +255,7 @@ static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len, tpm_platform_begin_xfer(); while (len--) - iowrite8(*value++, phy->iobase + addr); + tpm_tis_iowrite8(*value++, phy->iobase, addr); tpm_platform_end_xfer(); @@ -269,7 +294,7 @@ static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value) tpm_platform_begin_xfer(); - iowrite32(value, phy->iobase + addr); + tpm_tis_iowrite32(value, phy->iobase, addr); tpm_platform_end_xfer(); diff --git a/fs/dcache.c b/fs/dcache.c index 1ec40bb6eb3b..6842e2220924 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2417,9 +2417,10 @@ EXPORT_SYMBOL(d_rehash); static inline unsigned start_dir_add(struct inode *dir) { + preempt_disable_rt(); for (;;) { - unsigned n = dir->i_dir_seq; - if (!(n & 1) && cmpxchg(&dir->i_dir_seq, n, n + 1) == n) + unsigned n = dir->__i_dir_seq; + if (!(n & 1) && cmpxchg(&dir->__i_dir_seq, n, n + 1) == n) return n; cpu_relax(); } @@ -2427,7 +2428,8 @@ static inline unsigned start_dir_add(struct inode *dir) static inline void end_dir_add(struct inode *dir, unsigned n) { - smp_store_release(&dir->i_dir_seq, n + 2); + smp_store_release(&dir->__i_dir_seq, n + 2); + preempt_enable_rt(); } static void d_wait_lookup(struct dentry *dentry) @@ -2463,7 +2465,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent, retry: rcu_read_lock(); - seq = smp_load_acquire(&parent->d_inode->i_dir_seq) & ~1; + seq = smp_load_acquire(&parent->d_inode->__i_dir_seq) & ~1; r_seq = read_seqbegin(&rename_lock); dentry = __d_lookup_rcu(parent, name, &d_seq); if (unlikely(dentry)) { @@ -2485,7 +2487,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent, goto retry; } hlist_bl_lock(b); - if (unlikely(parent->d_inode->i_dir_seq != seq)) { + if (unlikely(parent->d_inode->__i_dir_seq != seq)) { hlist_bl_unlock(b); rcu_read_unlock(); goto retry; diff --git a/fs/inode.c b/fs/inode.c index 6a1626e0edaf..601b666e76a9 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -154,7 +154,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode) inode->i_bdev = NULL; inode->i_cdev = NULL; inode->i_link = NULL; - inode->i_dir_seq = 0; + inode->__i_dir_seq = 0; inode->i_rdev = 0; inode->dirtied_when = 0; diff --git a/fs/libfs.c b/fs/libfs.c index 3aabe553fc45..b5d63bf1ad8e 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -90,7 +90,7 @@ static struct dentry *next_positive(struct dentry *parent, struct list_head *from, int count) { - unsigned *seq = &parent->d_inode->i_dir_seq, n; + unsigned *seq = &parent->d_inode->__i_dir_seq, n; struct dentry *res; struct list_head *p; bool skipped; @@ -123,8 +123,9 @@ static struct dentry *next_positive(struct dentry *parent, static void move_cursor(struct dentry *cursor, struct list_head *after) { struct dentry *parent = cursor->d_parent; - unsigned n, *seq = &parent->d_inode->i_dir_seq; + unsigned n, *seq = &parent->d_inode->__i_dir_seq; spin_lock(&parent->d_lock); + preempt_disable_rt(); for (;;) { n = *seq; if (!(n & 1) && cmpxchg(seq, n, n + 1) == n) @@ -137,6 +138,7 @@ static void move_cursor(struct dentry *cursor, struct list_head *after) else list_add_tail(&cursor->d_child, &parent->d_subdirs); smp_store_release(seq, n + 2); + preempt_enable_rt(); spin_unlock(&parent->d_lock); } diff --git a/include/linux/fs.h b/include/linux/fs.h index d0c0ca8ea8c1..30b9561cf142 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -651,7 +651,7 @@ struct inode { struct block_device *i_bdev; struct cdev *i_cdev; char *i_link; - unsigned i_dir_seq; + unsigned __i_dir_seq; }; __u32 i_generation; diff --git a/include/linux/notifier.h b/include/linux/notifier.h index e9515066250e..49bef3e96eb2 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -119,12 +119,20 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); struct raw_notifier_head name = \ RAW_NOTIFIER_INIT(name) +#ifdef CONFIG_TREE_SRCU #define _SRCU_NOTIFIER_HEAD(name, mod) \ static DEFINE_PER_CPU(struct srcu_data, \ name##_head_srcu_data); \ mod struct srcu_notifier_head name = \ SRCU_NOTIFIER_INIT(name, name##_head_srcu_data) +#else +#define _SRCU_NOTIFIER_HEAD(name, mod) \ + mod struct srcu_notifier_head name = \ + SRCU_NOTIFIER_INIT(name, name) + +#endif + #define SRCU_NOTIFIER_HEAD(name) \ _SRCU_NOTIFIER_HEAD(name, ) diff --git a/include/linux/srcutiny.h b/include/linux/srcutiny.h index cfbfc540cafc..1bdcbdd3317c 100644 --- a/include/linux/srcutiny.h +++ b/include/linux/srcutiny.h @@ -43,7 +43,7 @@ struct srcu_struct { void srcu_drive_gp(struct work_struct *wp); -#define __SRCU_STRUCT_INIT(name) \ +#define __SRCU_STRUCT_INIT(name, __ignored) \ { \ .srcu_wq = __SWAIT_QUEUE_HEAD_INITIALIZER(name.srcu_wq), \ .srcu_cb_tail = &name.srcu_cb_head, \ @@ -56,9 +56,9 @@ void srcu_drive_gp(struct work_struct *wp); * Tree SRCU, which needs some per-CPU data. */ #define DEFINE_SRCU(name) \ - struct srcu_struct name = __SRCU_STRUCT_INIT(name) + struct srcu_struct name = __SRCU_STRUCT_INIT(name, name) #define DEFINE_STATIC_SRCU(name) \ - static struct srcu_struct name = __SRCU_STRUCT_INIT(name) + static struct srcu_struct name = __SRCU_STRUCT_INIT(name, name) void synchronize_srcu(struct srcu_struct *sp); diff --git a/include/linux/tracepoint-defs.h b/include/linux/tracepoint-defs.h index bc22d54adc52..a03192052066 100644 --- a/include/linux/tracepoint-defs.h +++ b/include/linux/tracepoint-defs.h @@ -32,7 +32,6 @@ struct tracepoint { int (*regfunc)(void); void (*unregfunc)(void); struct tracepoint_func __rcu *funcs; - bool dynamic; }; #endif diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 434c840e2d82..ad55ddecc1e9 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -585,6 +585,9 @@ config HIST_TRIGGERS event activity as an initial guide for further investigation using more advanced tools. + Inter-event tracing of quantities such as latencies is also + supported using hist triggers under this option. + See Documentation/trace/events.txt. If in doubt, say N. diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 9dbb4590b836..f78a1ad89f82 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2590,16 +2590,16 @@ rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer) * IRQ context * NMI context * - * If for some reason the ring buffer starts to recurse, we - * only allow that to happen at most 4 times (one for each - * context). If it happens 5 times, then we consider this a - * recusive loop and do not let it go further. + * If for some reason the ring buffer starts to recurse, we only allow + * that to happen at most 6 times (one for each context, plus possibly + * two levels of synthetic event generation). If it happens 7 times, + * then we consider this a recusive loop and do not let it go further. */ static __always_inline int trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer) { - if (cpu_buffer->current_context >= 4) + if (cpu_buffer->current_context >= 6) return 1; cpu_buffer->current_context++; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 7e69a14177cf..fced553fda6b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6333,7 +6333,7 @@ int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs) tracing_reset_online_cpus(&tr->trace_buffer); #ifdef CONFIG_TRACER_MAX_TRACE - if (tr->flags & TRACE_ARRAY_FL_GLOBAL && tr->max_buffer.buffer) + if (tr->max_buffer.buffer) ring_buffer_set_time_stamp_abs(tr->max_buffer.buffer, abs); tracing_reset_online_cpus(&tr->max_buffer); #endif @@ -7732,6 +7732,7 @@ static int instance_mkdir(const char *name) struct trace_array *tr; int ret; + mutex_lock(&event_mutex); mutex_lock(&trace_types_lock); ret = -EEXIST; @@ -7788,6 +7789,7 @@ static int instance_mkdir(const char *name) list_add(&tr->list, &ftrace_trace_arrays); mutex_unlock(&trace_types_lock); + mutex_unlock(&event_mutex); return 0; @@ -7799,6 +7801,7 @@ static int instance_mkdir(const char *name) out_unlock: mutex_unlock(&trace_types_lock); + mutex_unlock(&event_mutex); return ret; @@ -7811,6 +7814,7 @@ static int instance_rmdir(const char *name) int ret; int i; + mutex_lock(&event_mutex); mutex_lock(&trace_types_lock); ret = -ENODEV; @@ -7856,6 +7860,7 @@ static int instance_rmdir(const char *name) out_unlock: mutex_unlock(&trace_types_lock); + mutex_unlock(&event_mutex); return ret; } diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 1d5095e2dfe5..08ee331b554f 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -2953,24 +2953,24 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr) * creates the event hierachry in the @parent/events directory. * * Returns 0 on success. + * + * Must be called with event_mutex held. */ int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr) { int ret; - mutex_lock(&event_mutex); + lockdep_assert_held(&event_mutex); ret = create_event_toplevel_files(parent, tr); if (ret) - goto out_unlock; + goto out; down_write(&trace_event_sem); __trace_add_event_dirs(tr); up_write(&trace_event_sem); - out_unlock: - mutex_unlock(&event_mutex); - + out: return ret; } @@ -2999,9 +2999,10 @@ early_event_add_tracer(struct dentry *parent, struct trace_array *tr) return ret; } +/* Must be called with event_mutex held */ int event_trace_del_tracer(struct trace_array *tr) { - mutex_lock(&event_mutex); + lockdep_assert_held(&event_mutex); /* Disable any event triggers and associated soft-disabled events */ clear_event_triggers(tr); @@ -3022,8 +3023,6 @@ int event_trace_del_tracer(struct trace_array *tr) tr->event_dir = NULL; - mutex_unlock(&event_mutex); - return 0; } diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index eb77eee93611..e884d5d78551 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -210,24 +210,24 @@ DEFINE_HIST_FIELD_FN(u8); #define HIST_KEY_SIZE_MAX (MAX_FILTER_STR_VAL + HIST_STACKTRACE_SIZE) enum hist_field_flags { - HIST_FIELD_FL_HITCOUNT = 1, - HIST_FIELD_FL_KEY = 2, - HIST_FIELD_FL_STRING = 4, - HIST_FIELD_FL_HEX = 8, - HIST_FIELD_FL_SYM = 16, - HIST_FIELD_FL_SYM_OFFSET = 32, - HIST_FIELD_FL_EXECNAME = 64, - HIST_FIELD_FL_SYSCALL = 128, - HIST_FIELD_FL_STACKTRACE = 256, - HIST_FIELD_FL_LOG2 = 512, - HIST_FIELD_FL_TIMESTAMP = 1024, - HIST_FIELD_FL_TIMESTAMP_USECS = 2048, - HIST_FIELD_FL_VAR = 4096, - HIST_FIELD_FL_VAR_ONLY = 8192, - HIST_FIELD_FL_EXPR = 16384, - HIST_FIELD_FL_VAR_REF = 32768, - HIST_FIELD_FL_CPU = 65536, - HIST_FIELD_FL_ALIAS = 131072, + HIST_FIELD_FL_HITCOUNT = 1 << 0, + HIST_FIELD_FL_KEY = 1 << 1, + HIST_FIELD_FL_STRING = 1 << 2, + HIST_FIELD_FL_HEX = 1 << 3, + HIST_FIELD_FL_SYM = 1 << 4, + HIST_FIELD_FL_SYM_OFFSET = 1 << 5, + HIST_FIELD_FL_EXECNAME = 1 << 6, + HIST_FIELD_FL_SYSCALL = 1 << 7, + HIST_FIELD_FL_STACKTRACE = 1 << 8, + HIST_FIELD_FL_LOG2 = 1 << 9, + HIST_FIELD_FL_TIMESTAMP = 1 << 10, + HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11, + HIST_FIELD_FL_VAR = 1 << 12, + HIST_FIELD_FL_VAR_ONLY = 1 << 13, + HIST_FIELD_FL_EXPR = 1 << 14, + HIST_FIELD_FL_VAR_REF = 1 << 15, + HIST_FIELD_FL_CPU = 1 << 16, + HIST_FIELD_FL_ALIAS = 1 << 17, }; struct var_defs { @@ -353,42 +353,22 @@ struct action_data { }; -static char *hist_err_str; -static char *last_hist_cmd; - -static int hist_err_alloc(void) -{ - int ret = 0; - - last_hist_cmd = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); - if (!last_hist_cmd) - return -ENOMEM; - - hist_err_str = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); - if (!hist_err_str) { - kfree(last_hist_cmd); - ret = -ENOMEM; - } - - return ret; -} +static char last_hist_cmd[MAX_FILTER_STR_VAL]; +static char hist_err_str[MAX_FILTER_STR_VAL]; static void last_cmd_set(char *str) { - if (!last_hist_cmd || !str) + if (!str) return; - if (strlen(str) > MAX_FILTER_STR_VAL - 1) - return; - - strcpy(last_hist_cmd, str); + strncpy(last_hist_cmd, str, MAX_FILTER_STR_VAL - 1); } static void hist_err(char *str, char *var) { int maxlen = MAX_FILTER_STR_VAL - 1; - if (!hist_err_str || !str) + if (!str) return; if (strlen(hist_err_str)) @@ -409,26 +389,23 @@ static void hist_err_event(char *str, char *system, char *event, char *var) char err[MAX_FILTER_STR_VAL]; if (system && var) - sprintf(err, "%s.%s.%s", system, event, var); + snprintf(err, MAX_FILTER_STR_VAL, "%s.%s.%s", system, event, var); else if (system) - sprintf(err, "%s.%s", system, event); + snprintf(err, MAX_FILTER_STR_VAL, "%s.%s", system, event); else - strcpy(err, var); + strncpy(err, var, MAX_FILTER_STR_VAL); hist_err(str, err); } static void hist_err_clear(void) { - if (!hist_err_str) - return; - hist_err_str[0] = '\0'; } static bool have_hist_err(void) { - if (hist_err_str && strlen(hist_err_str)) + if (strlen(hist_err_str)) return true; return false; @@ -508,7 +485,7 @@ static int synth_field_string_size(char *type) return -EINVAL; len = end - start; - if (len > 2) + if (len > 3) return -EINVAL; strncpy(buf, start, len); @@ -631,12 +608,12 @@ static enum print_line_t print_synth_event(struct trace_iterator *iter, if (tr->trace_flags & TRACE_ITER_VERBOSE) trace_seq_printf(s, "%s ", fmt); - sprintf(print_fmt, "%%s=%s%%s", fmt); + snprintf(print_fmt, sizeof(print_fmt), "%%s=%s%%s", fmt); /* parameter values */ if (se->fields[i]->is_string) { trace_seq_printf(s, print_fmt, se->fields[i]->name, - (char *)entry->fields[n_u64], + (char *)(long)entry->fields[n_u64], i == se->n_fields - 1 ? "" : " "); n_u64 += STR_VAR_LEN_MAX / sizeof(u64); } else { @@ -681,7 +658,7 @@ static notrace void trace_event_raw_event_synth(void *__data, for (i = 0, n_u64 = 0; i < event->n_fields; i++) { if (event->fields[i]->is_string) { - char *str_val = (char *)var_ref_vals[var_ref_idx + i]; + char *str_val = (char *)(long)var_ref_vals[var_ref_idx + i]; char *str_field = (char *)&entry->fields[n_u64]; strncpy(str_field, str_val, STR_VAR_LEN_MAX); @@ -697,8 +674,10 @@ static notrace void trace_event_raw_event_synth(void *__data, static void free_synth_event_print_fmt(struct trace_event_call *call) { - if (call) + if (call) { kfree(call->print_fmt); + call->print_fmt = NULL; + } } static int __set_synth_event_print_fmt(struct synth_event *event, @@ -827,27 +806,18 @@ static void free_synth_tracepoint(struct tracepoint *tp) static struct tracepoint *alloc_synth_tracepoint(char *name) { struct tracepoint *tp; - int ret = 0; tp = kzalloc(sizeof(*tp), GFP_KERNEL); - if (!tp) { - ret = -ENOMEM; - goto free; - } + if (!tp) + return ERR_PTR(-ENOMEM); tp->name = kstrdup(name, GFP_KERNEL); if (!tp->name) { - ret = -ENOMEM; - goto free; + kfree(tp); + return ERR_PTR(-ENOMEM); } - tp->dynamic = true; - return tp; - free: - free_synth_tracepoint(tp); - - return ERR_PTR(ret); } typedef void (*synth_probe_func_t) (void *__data, u64 *var_ref_vals, @@ -869,8 +839,8 @@ static inline void trace_synth(struct synth_event *event, u64 *var_ref_vals, probe_func_ptr = rcu_dereference_sched((tp)->funcs); if (probe_func_ptr) { do { - probe_func = (probe_func_ptr)->func; - __data = (probe_func_ptr)->data; + probe_func = probe_func_ptr->func; + __data = probe_func_ptr->data; probe_func(__data, var_ref_vals, var_ref_idx); } while ((++probe_func_ptr)->func); } @@ -889,6 +859,7 @@ static struct synth_event *find_synth_event(const char *name) return NULL; } +/* This function releases synth_event_mutex */ static int register_synth_event(struct synth_event *event) { struct trace_event_call *call = &event->call; @@ -923,6 +894,11 @@ static int register_synth_event(struct synth_event *event) call->data = event; call->tp = event->tp; + /* + * trace_add_event_call() grabs event_mutex, but that can + * deadlock with a hist trigger cmd already holding it that + * can grab synth_event_mutex + */ mutex_unlock(&synth_event_mutex); ret = trace_add_event_call(call); mutex_lock(&synth_event_mutex); @@ -946,6 +922,7 @@ static int register_synth_event(struct synth_event *event) goto out; } +/* This function releases synth_event_mutex */ static int unregister_synth_event(struct synth_event *event) { struct trace_event_call *call = &event->call; @@ -954,20 +931,18 @@ static int unregister_synth_event(struct synth_event *event) mutex_unlock(&synth_event_mutex); ret = trace_remove_event_call(call); mutex_lock(&synth_event_mutex); - if (ret) { - pr_warn("Failed to remove synthetic event: %s\n", - trace_event_name(call)); - free_synth_event_print_fmt(call); - unregister_trace_event(&call->event); - } return ret; } -static void remove_synth_event(struct synth_event *event) +static int remove_synth_event(struct synth_event *event) { - unregister_synth_event(event); - list_del(&event->list); + int ret = unregister_synth_event(event); + + if (!ret) + list_del(&event->list); + + return ret; } static int add_synth_event(struct synth_event *event) @@ -1050,6 +1025,7 @@ struct hist_var_data { struct hist_trigger_data *hist_data; }; +/* This function releases synth_event_mutex */ static int create_synth_event(int argc, char **argv) { struct synth_field *field, *fields[SYNTH_FIELDS_MAX]; @@ -1084,8 +1060,9 @@ static int create_synth_event(int argc, char **argv) ret = -EBUSY; goto out; } - remove_synth_event(event); - free_synth_event(event); + ret = remove_synth_event(event); + if (!ret) + free_synth_event(event); goto out; } ret = -EEXIST; @@ -1140,6 +1117,7 @@ static int create_synth_event(int argc, char **argv) goto out; } +/* This function releases synth_event_mutex */ static int release_all_synth_events(void) { struct synth_event *event, *e; @@ -1155,8 +1133,9 @@ static int release_all_synth_events(void) } list_for_each_entry_safe(event, e, &synth_event_list, list) { - remove_synth_event(event); - free_synth_event(event); + ret = remove_synth_event(event); + if (!ret) + free_synth_event(event); } out: mutex_unlock(&synth_event_mutex); @@ -1260,7 +1239,7 @@ static u64 hist_field_cpu(struct hist_field *hist_field, struct ring_buffer_event *rbe, void *event) { - int cpu = raw_smp_processor_id(); + int cpu = smp_processor_id(); return cpu; } @@ -1732,17 +1711,33 @@ static int parse_assignment(char *str, struct hist_trigger_attrs *attrs) int ret = 0; if ((strncmp(str, "key=", strlen("key=")) == 0) || - (strncmp(str, "keys=", strlen("keys=")) == 0)) + (strncmp(str, "keys=", strlen("keys=")) == 0)) { attrs->keys_str = kstrdup(str, GFP_KERNEL); - else if ((strncmp(str, "val=", strlen("val=")) == 0) || + if (!attrs->keys_str) { + ret = -ENOMEM; + goto out; + } + } else if ((strncmp(str, "val=", strlen("val=")) == 0) || (strncmp(str, "vals=", strlen("vals=")) == 0) || - (strncmp(str, "values=", strlen("values=")) == 0)) + (strncmp(str, "values=", strlen("values=")) == 0)) { attrs->vals_str = kstrdup(str, GFP_KERNEL); - else if (strncmp(str, "sort=", strlen("sort=")) == 0) + if (!attrs->vals_str) { + ret = -ENOMEM; + goto out; + } + } else if (strncmp(str, "sort=", strlen("sort=")) == 0) { attrs->sort_key_str = kstrdup(str, GFP_KERNEL); - else if (strncmp(str, "name=", strlen("name=")) == 0) + if (!attrs->sort_key_str) { + ret = -ENOMEM; + goto out; + } + } else if (strncmp(str, "name=", strlen("name=")) == 0) { attrs->name = kstrdup(str, GFP_KERNEL); - else if (strncmp(str, "clock=", strlen("clock=")) == 0) { + if (!attrs->name) { + ret = -ENOMEM; + goto out; + } + } else if (strncmp(str, "clock=", strlen("clock=")) == 0) { strsep(&str, "="); if (!str) { ret = -EINVAL; @@ -1751,6 +1746,10 @@ static int parse_assignment(char *str, struct hist_trigger_attrs *attrs) str = strstrip(str); attrs->clock = kstrdup(str, GFP_KERNEL); + if (!attrs->clock) { + ret = -ENOMEM; + goto out; + } } else if (strncmp(str, "size=", strlen("size=")) == 0) { int map_bits = parse_map_size(str); @@ -1816,8 +1815,10 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str) if (!attrs->clock) { attrs->clock = kstrdup("global", GFP_KERNEL); - if (!attrs->clock) + if (!attrs->clock) { + ret = -ENOMEM; goto free; + } } return attrs; @@ -1842,19 +1843,22 @@ static inline void save_comm(char *comm, struct task_struct *task) memcpy(comm, task->comm, TASK_COMM_LEN); } +static void hist_elt_data_free(struct hist_elt_data *elt_data) +{ + unsigned int i; + + for (i = 0; i < SYNTH_FIELDS_MAX; i++) + kfree(elt_data->field_var_str[i]); + + kfree(elt_data->comm); + kfree(elt_data); +} + static void hist_trigger_elt_data_free(struct tracing_map_elt *elt) { - struct hist_trigger_data *hist_data = elt->map->private_data; - struct hist_elt_data *private_data = elt->private_data; - unsigned int i, n_str; + struct hist_elt_data *elt_data = elt->private_data; - n_str = hist_data->n_field_var_str + hist_data->n_max_var_str; - - for (i = 0; i < n_str; i++) - kfree(private_data->field_var_str[i]); - - kfree(private_data->comm); - kfree(private_data); + hist_elt_data_free(elt_data); } static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt) @@ -1865,7 +1869,7 @@ static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt) struct hist_field *key_field; unsigned int i, n_str; - elt->private_data = elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL); + elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL); if (!elt_data) return -ENOMEM; @@ -1876,7 +1880,6 @@ static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt) elt_data->comm = kzalloc(size, GFP_KERNEL); if (!elt_data->comm) { kfree(elt_data); - elt->private_data = NULL; return -ENOMEM; } break; @@ -1890,20 +1893,22 @@ static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt) for (i = 0; i < n_str; i++) { elt_data->field_var_str[i] = kzalloc(size, GFP_KERNEL); if (!elt_data->field_var_str[i]) { - hist_trigger_elt_data_free(elt); + hist_elt_data_free(elt_data); return -ENOMEM; } } + elt->private_data = elt_data; + return 0; } static void hist_trigger_elt_data_init(struct tracing_map_elt *elt) { - struct hist_elt_data *private_data = elt->private_data; + struct hist_elt_data *elt_data = elt->private_data; - if (private_data->comm) - save_comm(private_data->comm, current); + if (elt_data->comm) + save_comm(elt_data->comm, current); } static const struct tracing_map_ops hist_trigger_elt_data_ops = { @@ -2371,7 +2376,15 @@ struct hist_field *parse_atom(struct hist_trigger_data *hist_data, s = strchr(++s, '.'); if (s) { ref_system = strsep(&str, "."); + if (!str) { + ret = -EINVAL; + goto out; + } ref_event = strsep(&str, "."); + if (!str) { + ret = -EINVAL; + goto out; + } ref_var = str; } } @@ -2452,8 +2465,10 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data, } strsep(&str, "("); - if (!str) + if (!str) { + ret = -EINVAL; goto free; + } flags |= HIST_FIELD_FL_EXPR; expr = create_hist_field(hist_data, NULL, flags, var_name); @@ -3111,8 +3126,10 @@ static int onmax_create(struct hist_trigger_data *hist_data, for (i = 0; i < data->n_params; i++) { param = kstrdup(data->params[i], GFP_KERNEL); - if (!param) + if (!param) { + ret = -ENOMEM; goto out; + } field_var = create_target_field_var(hist_data, NULL, NULL, param); if (IS_ERR(field_var)) { @@ -3144,8 +3161,10 @@ static int parse_action_params(char *params, struct action_data *data) goto out; param = strsep(¶ms, ","); - if (!param) + if (!param) { + ret = -EINVAL; goto out; + } param = strstrip(param); if (strlen(param) < 2) { @@ -3196,8 +3215,10 @@ static struct action_data *onmax_parse(char *str) if (strncmp(onmax_fn_name, "save", strlen("save")) == 0) { char *params = strsep(&str, ")"); - if (!params) + if (!params) { + ret = -EINVAL; goto free; + } ret = parse_action_params(params, data); if (ret) @@ -3231,11 +3252,11 @@ static void onmatch_destroy(struct action_data *data) for (i = 0; i < data->n_params; i++) kfree(data->params[i]); - kfree(data); - if (data->onmatch.synth_event) data->onmatch.synth_event->ref--; + kfree(data); + mutex_unlock(&synth_event_mutex); } @@ -3372,13 +3393,13 @@ static int onmatch_create(struct hist_trigger_data *hist_data, int ret = 0; mutex_lock(&synth_event_mutex); - event = find_synth_event(data->onmatch.synth_event_name); if (!event) { hist_err("onmatch: Couldn't find synthetic event: ", data->onmatch.synth_event_name); - ret = -EINVAL; - goto out; + mutex_unlock(&synth_event_mutex); + return -EINVAL; } + mutex_unlock(&synth_event_mutex); var_ref_idx = hist_data->n_var_refs; @@ -3386,8 +3407,10 @@ static int onmatch_create(struct hist_trigger_data *hist_data, char *p; p = param = kstrdup(data->params[i], GFP_KERNEL); - if (!param) + if (!param) { + ret = -ENOMEM; goto out; + } system = strsep(¶m, "."); if (!param) { @@ -3401,6 +3424,7 @@ static int onmatch_create(struct hist_trigger_data *hist_data, goto out; } } + if (param[0] == '$') hist_field = onmatch_find_var(hist_data, data, system, event_name, param); @@ -3449,8 +3473,6 @@ static int onmatch_create(struct hist_trigger_data *hist_data, hist_data->actions[hist_data->n_actions++] = data; event->ref++; out: - mutex_unlock(&synth_event_mutex); - return ret; } @@ -4839,11 +4861,10 @@ static bool hist_trigger_match(struct event_trigger_data *data, return false; if (key_field->is_signed != key_field_test->is_signed) return false; - if ((key_field->var.name && !key_field_test->var.name) || - (!key_field->var.name && key_field_test->var.name)) + if (!!key_field->var.name != !!key_field_test->var.name) return false; - if ((key_field->var.name && key_field_test->var.name) && - strcmp(key_field->var.name, key_field_test->var.name) != 0) + if (key_field->var.name && + strcmp(key_field->var.name, key_field_test->var.name) != 0) return false; } @@ -5026,8 +5047,10 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, if (unregistered && test->ops->free) test->ops->free(test->ops, test); - if (hist_data->enable_timestamps) - tracing_set_time_stamp_abs(file->tr, false); + if (hist_data->enable_timestamps) { + if (!hist_data->remove || unregistered) + tracing_set_time_stamp_abs(file->tr, false); + } } static bool hist_file_check_refs(struct trace_event_file *file) @@ -5040,7 +5063,6 @@ static bool hist_file_check_refs(struct trace_event_file *file) hist_data = test->private_data; if (check_var_refs(hist_data)) return true; - break; } } @@ -5092,7 +5114,10 @@ static int event_hist_trigger_func(struct event_command *cmd_ops, if (glob[0] == '!') remove = true; - /* separate the trigger from the filter (k:v [if filter]) */ + /* + * separate the trigger from the filter (k:v [if filter]) + * allowing for whitespace in the trigger + */ trigger = param; p = strstr(param, " if"); if (!p) @@ -5396,8 +5421,6 @@ static __init int trace_events_hist_init(void) goto err; } - hist_err_alloc(); - return err; err: pr_warn("Could not create tracefs 'synthetic_events' entry\n"); diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c index f8e2338dec6e..5cadb1b8b5fe 100644 --- a/kernel/trace/tracing_map.c +++ b/kernel/trace/tracing_map.c @@ -524,6 +524,7 @@ __tracing_map_insert(struct tracing_map *map, void *key, bool lookup_only) u32 idx, key_hash, test_key; int dup_try = 0; struct tracing_map_entry *entry; + struct tracing_map_elt *val; key_hash = jhash(key, map->key_size, 0); if (key_hash == 0) @@ -536,12 +537,13 @@ __tracing_map_insert(struct tracing_map *map, void *key, bool lookup_only) test_key = entry->key; if (test_key && test_key == key_hash) { - if (entry->val && - keys_match(key, entry->val->key, map->key_size)) { + val = READ_ONCE(entry->val); + if (val && + keys_match(key, val->key, map->key_size)) { if (!lookup_only) atomic64_inc(&map->hits); - return entry->val; - } else if (unlikely(!entry->val)) { + return val; + } else if (unlikely(!val)) { /* * The key is present. But, val (pointer to elt * struct) is still NULL. which means some other diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 1c5957f23b9a..685c50ae6300 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -197,9 +197,7 @@ static int tracepoint_add_func(struct tracepoint *tp, struct tracepoint_func *old, *tp_funcs; int ret; - if (tp->regfunc && - ((tp->dynamic && !(atomic_read(&tp->key.enabled) > 0)) || - !static_key_enabled(&tp->key))) { + if (tp->regfunc && !static_key_enabled(&tp->key)) { ret = tp->regfunc(); if (ret < 0) return ret; @@ -221,9 +219,7 @@ static int tracepoint_add_func(struct tracepoint *tp, * is used. */ rcu_assign_pointer(tp->funcs, tp_funcs); - if (tp->dynamic && !(atomic_read(&tp->key.enabled) > 0)) - atomic_inc(&tp->key.enabled); - else if (!tp->dynamic && !static_key_enabled(&tp->key)) + if (!static_key_enabled(&tp->key)) static_key_slow_inc(&tp->key); release_probes(old); return 0; @@ -250,14 +246,10 @@ static int tracepoint_remove_func(struct tracepoint *tp, if (!tp_funcs) { /* Removed last function */ - if (tp->unregfunc && - ((tp->dynamic && (atomic_read(&tp->key.enabled) > 0)) || - static_key_enabled(&tp->key))) + if (tp->unregfunc && static_key_enabled(&tp->key)) tp->unregfunc(); - if (tp->dynamic && (atomic_read(&tp->key.enabled) > 0)) - atomic_dec(&tp->key.enabled); - else if (!tp->dynamic && static_key_enabled(&tp->key)) + if (static_key_enabled(&tp->key)) static_key_slow_dec(&tp->key); } rcu_assign_pointer(tp->funcs, tp_funcs); @@ -266,7 +258,7 @@ static int tracepoint_remove_func(struct tracepoint *tp, } /** - * tracepoint_probe_register_prio - Connect a probe to a tracepoint + * tracepoint_probe_register - Connect a probe to a tracepoint * @tp: tracepoint * @probe: probe handler * @data: tracepoint data diff --git a/localversion-rt b/localversion-rt index c3054d08a112..1445cd65885c 100644 --- a/localversion-rt +++ b/localversion-rt @@ -1 +1 @@ --rt2 +-rt3