diff options
author | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2017-09-22 10:59:01 +0200 |
---|---|---|
committer | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2017-09-22 10:59:01 +0200 |
commit | c5c5f525611e97a1aed5670c0d499070ee98163a (patch) | |
tree | 46a8e81830047df9bbe17ec3300f089e08b7225c | |
parent | 6820d5710a5845455e65c035d99b43bd223c3f9e (diff) | |
download | linux-rt-4.11.12-rt14-patches.tar.gz |
[ANNOUNCE] v4.11.12-rt14v4.11.12-rt14-patches
Dear RT folks!
I'm pleased to announce the v4.11.12-rt14 patch set.
Changes since v4.11.12-rt13:
- Update the "Inter-event (e.g. latency) support" patch to v2 as
posted by Tom Zanussi on 2017-09-05. Patch 40 from this series has
been replaced with Steven Rostedt's version of it (as posted as
reply to the original patch) since the original broke some things.
- The bluetooth code could deadlock itself on RT. Reported by Mart van
de Wege.
- The AMD-iommu driver produced "sleeping while atomic" warnings.
Reported by Vinod Adhikary.
- The local_lock locking around icmp_sk_lock needs to be a try-lock
because it may be recursive. With this fixed, the deadlocks as
reported by Jacek Konieczny are gone.
- Since the futex work, the RT-only fixup needed adaption and was
overseen which led to strange errors. Reported by Gusenleitner
Klaus.
Known issues
none
The delta patch against v4.11.12-rt13 is appended below and can be found here:
https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.11/incr/patch-4.11.12-rt13-rt14.patch.xz
You can get this release via the git tree at:
git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git v4.11.12-rt14
The RT patch against v4.11.12 can be found here:
https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.11/older/patch-4.11.12-rt14.patch.xz
The split quilt queue is available at:
https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.11/older/patches-4.11.12-rt14.tar.xz
Sebastian
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
63 files changed, 3919 insertions, 2933 deletions
diff --git a/patches/0001-tracing-Exclude-generic-fields-from-histograms.patch b/patches/0001-tracing-Exclude-generic-fields-from-histograms.patch new file mode 100644 index 000000000000..b0e806b3df2e --- /dev/null +++ b/patches/0001-tracing-Exclude-generic-fields-from-histograms.patch @@ -0,0 +1,37 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Tue, 5 Sep 2017 16:57:13 -0500 +Subject: [PATCH 01/40] tracing: Exclude 'generic fields' from histograms + +There are a small number of 'generic fields' (comm/COMM/cpu/CPU) that +are found by trace_find_event_field() but are only meant for +filtering. Specifically, they unlike normal fields, they have a size +of 0 and thus wreak havoc when used as a histogram key. + +Exclude these (return -EINVAL) when used as histogram keys. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace_events_hist.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -450,7 +450,7 @@ static int create_val_field(struct hist_ + } + + field = trace_find_event_field(file->event_call, field_name); +- if (!field) { ++ if (!field || !field->size) { + ret = -EINVAL; + goto out; + } +@@ -548,7 +548,7 @@ static int create_key_field(struct hist_ + } + + field = trace_find_event_field(file->event_call, field_name); +- if (!field) { ++ if (!field || !field->size) { + ret = -EINVAL; + goto out; + } diff --git a/patches/0002-tracing-Add-support-to-detect-and-avoid-duplicates.patch b/patches/0002-tracing-Add-support-to-detect-and-avoid-duplicates.patch new file mode 100644 index 000000000000..17e926674fef --- /dev/null +++ b/patches/0002-tracing-Add-support-to-detect-and-avoid-duplicates.patch @@ -0,0 +1,108 @@ +From: Vedang Patel <vedang.patel@intel.com> +Date: Tue, 5 Sep 2017 16:57:14 -0500 +Subject: [PATCH 02/40] tracing: Add support to detect and avoid duplicates + +A duplicate in the tracing_map hash table is when 2 different entries +have the same key and, as a result, the key_hash. This is possible due +to a race condition in the algorithm. This race condition is inherent to +the algorithm and not a bug. This was fine because, until now, we were +only interested in the sum of all the values related to a particular +key (the duplicates are dealt with in tracing_map_sort_entries()). But, +with the inclusion of variables[1], we are interested in individual +values. So, it will not be clear what value to choose when +there are duplicates. So, the duplicates need to be removed. + +The duplicates can occur in the code in the following scenarios: + +- A thread is in the process of adding a new element. It has +successfully executed cmpxchg() and inserted the key. But, it is still +not done acquiring the trace_map_elt struct, populating it and storing +the pointer to the struct in the value field of tracing_map hash table. +If another thread comes in at this time and wants to add an element with +the same key, it will not see the current element and add a new one. + +- There are multiple threads trying to execute cmpxchg at the same time, +one of the threads will succeed and the others will fail. The ones which +fail will go ahead increment 'idx' and add a new element there creating +a duplicate. + +This patch detects and avoids the first condition by asking the thread +which detects the duplicate to loop one more time. There is also a +possibility of infinite loop if the thread which is trying to insert +goes to sleep indefinitely and the one which is trying to insert a new +element detects a duplicate. Which is why, the thread loops for +map_size iterations before returning NULL. + +The second scenario is avoided by preventing the threads which failed +cmpxchg() from incrementing idx. This way, they will loop +around and check if the thread which succeeded in executing cmpxchg() +had the same key. + +[1] - https://lkml.org/lkml/2017/6/26/751 + +Signed-off-by: Vedang Patel <vedang.patel@intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/tracing_map.c | 37 +++++++++++++++++++++++++++++++++---- + 1 file changed, 33 insertions(+), 4 deletions(-) + +--- a/kernel/trace/tracing_map.c ++++ b/kernel/trace/tracing_map.c +@@ -411,6 +411,7 @@ static inline struct tracing_map_elt * + __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; + + key_hash = jhash(key, map->key_size, 0); +@@ -423,10 +424,31 @@ static inline struct tracing_map_elt * + entry = TRACING_MAP_ENTRY(map->map, idx); + test_key = entry->key; + +- if (test_key && test_key == key_hash && entry->val && +- keys_match(key, entry->val->key, map->key_size)) { +- atomic64_inc(&map->hits); +- return entry->val; ++ if (test_key && test_key == key_hash) { ++ if (entry->val && ++ keys_match(key, entry->val->key, map->key_size)) { ++ atomic64_inc(&map->hits); ++ return entry->val; ++ } else if (unlikely(!entry->val)) { ++ /* ++ * The key is present. But, val (pointer to elt ++ * struct) is still NULL. which means some other ++ * thread is in the process of inserting an ++ * element. ++ * ++ * On top of that, it's key_hash is same as the ++ * one being inserted right now. So, it's ++ * possible that the element has the same ++ * key as well. ++ */ ++ ++ dup_try++; ++ if (dup_try > map->map_size) { ++ atomic64_inc(&map->drops); ++ break; ++ } ++ continue; ++ } + } + + if (!test_key) { +@@ -448,6 +470,13 @@ static inline struct tracing_map_elt * + atomic64_inc(&map->hits); + + return entry->val; ++ } else { ++ /* ++ * cmpxchg() failed. Loop around once ++ * more to check what key was inserted. ++ */ ++ dup_try++; ++ continue; + } + } + diff --git a/patches/0003-tracing-Remove-code-which-merges-duplicates.patch b/patches/0003-tracing-Remove-code-which-merges-duplicates.patch new file mode 100644 index 000000000000..9c0b254ac837 --- /dev/null +++ b/patches/0003-tracing-Remove-code-which-merges-duplicates.patch @@ -0,0 +1,187 @@ +From: Vedang Patel <vedang.patel@intel.com> +Date: Tue, 5 Sep 2017 16:57:15 -0500 +Subject: [PATCH 03/40] tracing: Remove code which merges duplicates + +We now have the logic to detect and remove duplicates in the +tracing_map hash table. The code which merges duplicates in the +histogram is redundant now. So, modify this code just to detect +duplicates. The duplication detection code is still kept to ensure +that any rare race condition which might cause duplicates does not go +unnoticed. + +Signed-off-by: Vedang Patel <vedang.patel@intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace_events_hist.c | 11 ----- + kernel/trace/tracing_map.c | 83 ++------------------------------------- + kernel/trace/tracing_map.h | 7 --- + 3 files changed, 6 insertions(+), 95 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -315,16 +315,6 @@ static int hist_trigger_elt_comm_alloc(s + return 0; + } + +-static void hist_trigger_elt_comm_copy(struct tracing_map_elt *to, +- struct tracing_map_elt *from) +-{ +- char *comm_from = from->private_data; +- char *comm_to = to->private_data; +- +- if (comm_from) +- memcpy(comm_to, comm_from, TASK_COMM_LEN + 1); +-} +- + static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt) + { + char *comm = elt->private_data; +@@ -335,7 +325,6 @@ static void hist_trigger_elt_comm_init(s + + static const struct tracing_map_ops hist_trigger_elt_comm_ops = { + .elt_alloc = hist_trigger_elt_comm_alloc, +- .elt_copy = hist_trigger_elt_comm_copy, + .elt_free = hist_trigger_elt_comm_free, + .elt_init = hist_trigger_elt_comm_init, + }; +--- a/kernel/trace/tracing_map.c ++++ b/kernel/trace/tracing_map.c +@@ -841,67 +841,15 @@ create_sort_entry(void *key, struct trac + return sort_entry; + } + +-static struct tracing_map_elt *copy_elt(struct tracing_map_elt *elt) +-{ +- struct tracing_map_elt *dup_elt; +- unsigned int i; +- +- dup_elt = tracing_map_elt_alloc(elt->map); +- if (IS_ERR(dup_elt)) +- return NULL; +- +- if (elt->map->ops && elt->map->ops->elt_copy) +- elt->map->ops->elt_copy(dup_elt, elt); +- +- dup_elt->private_data = elt->private_data; +- memcpy(dup_elt->key, elt->key, elt->map->key_size); +- +- for (i = 0; i < elt->map->n_fields; i++) { +- atomic64_set(&dup_elt->fields[i].sum, +- atomic64_read(&elt->fields[i].sum)); +- dup_elt->fields[i].cmp_fn = elt->fields[i].cmp_fn; +- } +- +- return dup_elt; +-} +- +-static int merge_dup(struct tracing_map_sort_entry **sort_entries, +- unsigned int target, unsigned int dup) +-{ +- struct tracing_map_elt *target_elt, *elt; +- bool first_dup = (target - dup) == 1; +- int i; +- +- if (first_dup) { +- elt = sort_entries[target]->elt; +- target_elt = copy_elt(elt); +- if (!target_elt) +- return -ENOMEM; +- sort_entries[target]->elt = target_elt; +- sort_entries[target]->elt_copied = true; +- } else +- target_elt = sort_entries[target]->elt; +- +- elt = sort_entries[dup]->elt; +- +- for (i = 0; i < elt->map->n_fields; i++) +- atomic64_add(atomic64_read(&elt->fields[i].sum), +- &target_elt->fields[i].sum); +- +- sort_entries[dup]->dup = true; +- +- return 0; +-} +- +-static int merge_dups(struct tracing_map_sort_entry **sort_entries, ++static void detect_dups(struct tracing_map_sort_entry **sort_entries, + int n_entries, unsigned int key_size) + { + unsigned int dups = 0, total_dups = 0; +- int err, i, j; ++ int i; + void *key; + + if (n_entries < 2) +- return total_dups; ++ return; + + sort(sort_entries, n_entries, sizeof(struct tracing_map_sort_entry *), + (int (*)(const void *, const void *))cmp_entries_dup, NULL); +@@ -910,30 +858,14 @@ static int merge_dups(struct tracing_map + for (i = 1; i < n_entries; i++) { + if (!memcmp(sort_entries[i]->key, key, key_size)) { + dups++; total_dups++; +- err = merge_dup(sort_entries, i - dups, i); +- if (err) +- return err; + continue; + } + key = sort_entries[i]->key; + dups = 0; + } + +- if (!total_dups) +- return total_dups; +- +- for (i = 0, j = 0; i < n_entries; i++) { +- if (!sort_entries[i]->dup) { +- sort_entries[j] = sort_entries[i]; +- if (j++ != i) +- sort_entries[i] = NULL; +- } else { +- destroy_sort_entry(sort_entries[i]); +- sort_entries[i] = NULL; +- } +- } +- +- return total_dups; ++ WARN_ONCE(total_dups > 0, ++ "Duplicates detected: %d\n", total_dups); + } + + static bool is_key(struct tracing_map *map, unsigned int field_idx) +@@ -1059,10 +991,7 @@ int tracing_map_sort_entries(struct trac + return 1; + } + +- ret = merge_dups(entries, n_entries, map->key_size); +- if (ret < 0) +- goto free; +- n_entries -= ret; ++ detect_dups(entries, n_entries, map->key_size); + + if (is_key(map, sort_keys[0].field_idx)) + cmp_entries_fn = cmp_entries_key; +--- a/kernel/trace/tracing_map.h ++++ b/kernel/trace/tracing_map.h +@@ -214,11 +214,6 @@ struct tracing_map { + * Element allocation occurs before tracing begins, when the + * tracing_map_init() call is made by client code. + * +- * @elt_copy: At certain points in the lifetime of an element, it may +- * need to be copied. The copy should include a copy of the +- * client-allocated data, which can be copied into the 'to' +- * element from the 'from' element. +- * + * @elt_free: When a tracing_map_elt is freed, this function is called + * and allows client-allocated per-element data to be freed. + * +@@ -232,8 +227,6 @@ struct tracing_map { + */ + struct tracing_map_ops { + int (*elt_alloc)(struct tracing_map_elt *elt); +- void (*elt_copy)(struct tracing_map_elt *to, +- struct tracing_map_elt *from); + void (*elt_free)(struct tracing_map_elt *elt); + void (*elt_clear)(struct tracing_map_elt *elt); + void (*elt_init)(struct tracing_map_elt *elt); diff --git a/patches/0001-tracing-Add-hist_field_name-accessor.patch b/patches/0004-tracing-Add-hist_field_name-accessor.patch index f934609d44c0..95d05b5d1e03 100644 --- a/patches/0001-tracing-Add-hist_field_name-accessor.patch +++ b/patches/0004-tracing-Add-hist_field_name-accessor.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:02 -0500 -Subject: [PATCH 01/32] tracing: Add hist_field_name() accessor +Date: Tue, 5 Sep 2017 16:57:16 -0500 +Subject: [PATCH 04/40] tracing: Add hist_field_name() accessor In preparation for hist_fields that won't be strictly based on trace_event_fields, add a new hist_field_name() accessor to allow that @@ -9,8 +9,8 @@ flexibility and update associated users. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - kernel/trace/trace_events_hist.c | 68 ++++++++++++++++++++++++++------------- - 1 file changed, 46 insertions(+), 22 deletions(-) + kernel/trace/trace_events_hist.c | 67 ++++++++++++++++++++++++++------------- + 1 file changed, 45 insertions(+), 22 deletions(-) --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -38,7 +38,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static hist_field_fn_t select_value_fn(int field_size, int field_is_signed) { hist_field_fn_t fn = NULL; -@@ -653,7 +670,6 @@ static int is_descending(const char *str +@@ -642,7 +659,6 @@ static int is_descending(const char *str static int create_sort_keys(struct hist_trigger_data *hist_data) { char *fields_str = hist_data->attrs->sort_key_str; @@ -46,7 +46,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> struct tracing_map_sort_key *sort_key; int descending, ret = 0; unsigned int i, j; -@@ -670,7 +686,9 @@ static int create_sort_keys(struct hist_ +@@ -659,7 +675,9 @@ static int create_sort_keys(struct hist_ } for (i = 0; i < TRACING_MAP_SORT_KEYS_MAX; i++) { @@ -56,7 +56,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> sort_key = &hist_data->sort_keys[i]; -@@ -703,8 +721,11 @@ static int create_sort_keys(struct hist_ +@@ -692,8 +710,10 @@ static int create_sort_keys(struct hist_ } for (j = 1; j < hist_data->n_fields; j++) { @@ -64,13 +64,12 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> - if (field && (strcmp(field_name, field->name) == 0)) { + hist_field = hist_data->fields[j]; + test_name = hist_field_name(hist_field, 0); -+ if (test_name == NULL) -+ continue; ++ + if (strcmp(field_name, test_name) == 0) { sort_key->field_idx = j; descending = is_descending(field_str); if (descending < 0) { -@@ -952,6 +973,7 @@ hist_trigger_entry_print(struct seq_file +@@ -941,6 +961,7 @@ hist_trigger_entry_print(struct seq_file struct hist_field *key_field; char str[KSYM_SYMBOL_LEN]; bool multiline = false; @@ -78,7 +77,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> unsigned int i; u64 uval; -@@ -963,26 +985,27 @@ hist_trigger_entry_print(struct seq_file +@@ -952,26 +973,27 @@ hist_trigger_entry_print(struct seq_file if (i > hist_data->n_vals) seq_puts(m, ", "); @@ -114,7 +113,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } else if (key_field->flags & HIST_FIELD_FL_SYSCALL) { const char *syscall_name; -@@ -991,8 +1014,8 @@ hist_trigger_entry_print(struct seq_file +@@ -980,8 +1002,8 @@ hist_trigger_entry_print(struct seq_file if (!syscall_name) syscall_name = "unknown_syscall"; @@ -125,7 +124,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } else if (key_field->flags & HIST_FIELD_FL_STACKTRACE) { seq_puts(m, "stacktrace:\n"); hist_trigger_stacktrace_print(m, -@@ -1000,15 +1023,14 @@ hist_trigger_entry_print(struct seq_file +@@ -989,15 +1011,14 @@ hist_trigger_entry_print(struct seq_file HIST_STACKTRACE_DEPTH); multiline = true; } else if (key_field->flags & HIST_FIELD_FL_LOG2) { @@ -144,7 +143,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } } -@@ -1021,13 +1043,13 @@ hist_trigger_entry_print(struct seq_file +@@ -1010,13 +1031,13 @@ hist_trigger_entry_print(struct seq_file tracing_map_read_sum(elt, HITCOUNT_IDX)); for (i = 1; i < hist_data->n_vals; i++) { @@ -162,7 +161,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> tracing_map_read_sum(elt, i)); } } -@@ -1142,7 +1164,9 @@ static const char *get_hist_field_flags( +@@ -1131,7 +1152,9 @@ static const char *get_hist_field_flags( static void hist_field_print(struct seq_file *m, struct hist_field *hist_field) { diff --git a/patches/0002-tracing-Reimplement-log2.patch b/patches/0005-tracing-Reimplement-log2.patch index 0d674beb6c9f..a0030142b794 100644 --- a/patches/0002-tracing-Reimplement-log2.patch +++ b/patches/0005-tracing-Reimplement-log2.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:03 -0500 -Subject: [PATCH 02/32] tracing: Reimplement log2 +Date: Tue, 5 Sep 2017 16:57:17 -0500 +Subject: [PATCH 05/40] tracing: Reimplement log2 log2 as currently implemented applies only to u64 trace_event_field derived fields, and assumes that anything it's applied to is a u64 @@ -61,7 +61,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (field_name == NULL) field_name = ""; -@@ -357,8 +365,20 @@ static const struct tracing_map_ops hist +@@ -346,8 +354,20 @@ static const struct tracing_map_ops hist .elt_init = hist_trigger_elt_comm_init, }; @@ -78,12 +78,12 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return; + + for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) -+ destroy_hist_field(hist_field->operands[i], ++level); ++ destroy_hist_field(hist_field->operands[i], level + 1); + kfree(hist_field); } -@@ -385,7 +405,10 @@ static struct hist_field *create_hist_fi +@@ -374,7 +394,10 @@ static struct hist_field *create_hist_fi } if (flags & HIST_FIELD_FL_LOG2) { @@ -94,7 +94,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> goto out; } -@@ -405,7 +428,7 @@ static struct hist_field *create_hist_fi +@@ -394,7 +417,7 @@ static struct hist_field *create_hist_fi hist_field->fn = select_value_fn(field->size, field->is_signed); if (!hist_field->fn) { @@ -103,7 +103,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return NULL; } } -@@ -422,7 +445,7 @@ static void destroy_hist_fields(struct h +@@ -411,7 +434,7 @@ static void destroy_hist_fields(struct h for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) { if (hist_data->fields[i]) { diff --git a/patches/0003-ring-buffer-Add-interface-for-setting-absolute-time-.patch b/patches/0006-ring-buffer-Add-interface-for-setting-absolute-time-.patch index edc0f2f13e65..263313eb3e83 100644 --- a/patches/0003-ring-buffer-Add-interface-for-setting-absolute-time-.patch +++ b/patches/0006-ring-buffer-Add-interface-for-setting-absolute-time-.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:04 -0500 -Subject: [PATCH 03/32] ring-buffer: Add interface for setting absolute time +Date: Tue, 5 Sep 2017 16:57:18 -0500 +Subject: [PATCH 06/40] ring-buffer: Add interface for setting absolute time stamps Define a new function, tracing_set_time_stamp_abs(), which can be used @@ -18,9 +18,9 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- include/linux/ring_buffer.h | 2 ++ kernel/trace/ring_buffer.c | 11 +++++++++++ - kernel/trace/trace.c | 25 ++++++++++++++++++++++++- - kernel/trace/trace.h | 2 ++ - 4 files changed, 39 insertions(+), 1 deletion(-) + kernel/trace/trace.c | 40 +++++++++++++++++++++++++++++++++++++++- + kernel/trace/trace.h | 3 +++ + 4 files changed, 55 insertions(+), 1 deletion(-) --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -71,14 +71,30 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> (EVENT_FILE_FL_SOFT_DISABLED | EVENT_FILE_FL_FILTERED)) && (entry = this_cpu_read(trace_buffered_event))) { /* Try to use the per cpu buffer first */ -@@ -5959,6 +5959,29 @@ static int tracing_clock_open(struct ino +@@ -5958,6 +5958,44 @@ static int tracing_clock_open(struct ino + return ret; } - ++ +int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs) +{ ++ int ret = 0; ++ + mutex_lock(&trace_types_lock); + ++ if (abs && tr->time_stamp_abs_ref++) ++ goto out; ++ ++ if (!abs) { ++ if (WARN_ON_ONCE(!tr->time_stamp_abs_ref)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (--tr->time_stamp_abs_ref) ++ goto out; ++ } ++ + ring_buffer_set_time_stamp_abs(tr->trace_buffer.buffer, abs); + + /* @@ -92,18 +108,25 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + ring_buffer_set_time_stamp_abs(tr->max_buffer.buffer, abs); + tracing_reset_online_cpus(&tr->max_buffer); +#endif -+ ++ out: + mutex_unlock(&trace_types_lock); + -+ return 0; ++ return ret; +} -+ + struct ftrace_buffer_info { struct trace_iterator iter; - void *spare; --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h -@@ -278,6 +278,8 @@ extern struct mutex trace_types_lock; +@@ -265,6 +265,7 @@ struct trace_array { + /* function tracing enabled */ + int function_enabled; + #endif ++ int time_stamp_abs_ref; + }; + + enum { +@@ -278,6 +279,8 @@ extern struct mutex trace_types_lock; extern int trace_array_get(struct trace_array *tr); extern void trace_array_put(struct trace_array *tr); diff --git a/patches/0007-tracing-Apply-absolute-timestamps-to-instance-max-bu.patch b/patches/0007-tracing-Apply-absolute-timestamps-to-instance-max-bu.patch new file mode 100644 index 000000000000..cca73e93a23d --- /dev/null +++ b/patches/0007-tracing-Apply-absolute-timestamps-to-instance-max-bu.patch @@ -0,0 +1,41 @@ +From: Baohong Liu <baohong.liu@intel.com> +Date: Tue, 5 Sep 2017 16:57:19 -0500 +Subject: [PATCH 07/40] tracing: Apply absolute timestamps to instance max + buffer + +Currently absolute timestamps are applied to both regular and max +buffers only for global trace. For instance trace, absolute +timestamps are applied only to regular buffer. But, regular and max +buffers can be swapped, for example, following a snapshot. So, for +instance trace, bad timestamps can be seen following a snapshot. +Let's apply absolute timestamps to instance max buffer as well. + +Similarly, buffer clock change is applied to instance max buffer +as well. + +Signed-off-by: Baohong Liu <baohong.liu@intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -5903,7 +5903,7 @@ static int tracing_set_clock(struct trac + 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_clock(tr->max_buffer.buffer, trace_clocks[i].func); + tracing_reset_online_cpus(&tr->max_buffer); + #endif +@@ -5987,7 +5987,7 @@ int tracing_set_time_stamp_abs(struct tr + 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 diff --git a/patches/0004-ring-buffer-Redefine-the-unimplemented-RINGBUF_TIME_.patch b/patches/0008-ring-buffer-Redefine-the-unimplemented-RINGBUF_TIME_.patch index d7ad02d26f16..be733e52ae2c 100644 --- a/patches/0004-ring-buffer-Redefine-the-unimplemented-RINGBUF_TIME_.patch +++ b/patches/0008-ring-buffer-Redefine-the-unimplemented-RINGBUF_TIME_.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:05 -0500 -Subject: [PATCH 04/32] ring-buffer: Redefine the unimplemented +Date: Tue, 5 Sep 2017 16:57:20 -0500 +Subject: [PATCH 08/40] ring-buffer: Redefine the unimplemented RINGBUF_TIME_TIME_STAMP RINGBUF_TYPE_TIME_STAMP is defined but not used, and from what I can @@ -23,9 +23,9 @@ previous interface patch. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - include/linux/ring_buffer.h | 12 ++-- - kernel/trace/ring_buffer.c | 107 +++++++++++++++++++++++++++++++------------- - 2 files changed, 83 insertions(+), 36 deletions(-) + include/linux/ring_buffer.h | 12 ++--- + kernel/trace/ring_buffer.c | 105 +++++++++++++++++++++++++++++++------------- + 2 files changed, 83 insertions(+), 34 deletions(-) --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -71,7 +71,14 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> trace_seq_printf(s, "\tdata max type_len == %d\n", RINGBUF_TYPE_DATA_TYPE_LEN_MAX); -@@ -147,6 +149,9 @@ enum { +@@ -141,12 +143,15 @@ int ring_buffer_print_entry_header(struc + + enum { + RB_LEN_TIME_EXTEND = 8, +- RB_LEN_TIME_STAMP = 16, ++ RB_LEN_TIME_STAMP = 8, + }; + #define skip_time_extend(event) \ ((struct ring_buffer_event *)((char *)event + RB_LEN_TIME_EXTEND)) @@ -81,19 +88,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static inline int rb_null_event(struct ring_buffer_event *event) { return event->type_len == RINGBUF_TYPE_PADDING && !event->time_delta; -@@ -187,10 +192,8 @@ rb_event_length(struct ring_buffer_event - return event->array[0] + RB_EVNT_HDR_SIZE; - - case RINGBUF_TYPE_TIME_EXTEND: -- return RB_LEN_TIME_EXTEND; -- - case RINGBUF_TYPE_TIME_STAMP: -- return RB_LEN_TIME_STAMP; -+ return RB_LEN_TIME_EXTEND; - - case RINGBUF_TYPE_DATA: - return rb_event_data_length(event); -@@ -210,7 +213,7 @@ rb_event_ts_length(struct ring_buffer_ev +@@ -210,7 +215,7 @@ rb_event_ts_length(struct ring_buffer_ev { unsigned len = 0; @@ -102,7 +97,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> /* time extends include the data event after it */ len = RB_LEN_TIME_EXTEND; event = skip_time_extend(event); -@@ -232,7 +235,7 @@ unsigned ring_buffer_event_length(struct +@@ -232,7 +237,7 @@ unsigned ring_buffer_event_length(struct { unsigned length; @@ -111,7 +106,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> event = skip_time_extend(event); length = rb_event_length(event); -@@ -249,7 +252,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_leng +@@ -249,7 +254,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_leng static __always_inline void * rb_event_data(struct ring_buffer_event *event) { @@ -120,7 +115,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> event = skip_time_extend(event); BUG_ON(event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX); /* If length is in len field, then array[0] has the data */ -@@ -276,6 +279,27 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data +@@ -276,6 +281,27 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data #define TS_MASK ((1ULL << TS_SHIFT) - 1) #define TS_DELTA_TEST (~TS_MASK) @@ -148,7 +143,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> /* Flag when events were overwritten */ #define RB_MISSED_EVENTS (1 << 31) /* Missed count stored at end */ -@@ -2219,13 +2243,16 @@ rb_move_tail(struct ring_buffer_per_cpu +@@ -2219,13 +2245,16 @@ rb_move_tail(struct ring_buffer_per_cpu } /* Slow path, do not inline */ @@ -170,7 +165,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> event->time_delta = delta & TS_MASK; event->array[0] = delta >> TS_SHIFT; } else { -@@ -2268,7 +2295,9 @@ rb_update_event(struct ring_buffer_per_c +@@ -2268,7 +2297,9 @@ rb_update_event(struct ring_buffer_per_c * add it to the start of the resevered space. */ if (unlikely(info->add_timestamp)) { @@ -181,7 +176,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> length -= RB_LEN_TIME_EXTEND; delta = 0; } -@@ -2456,7 +2485,7 @@ static __always_inline void rb_end_commi +@@ -2456,7 +2487,7 @@ static __always_inline void rb_end_commi static inline void rb_event_discard(struct ring_buffer_event *event) { @@ -190,7 +185,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> event = skip_time_extend(event); /* array[0] holds the actual length for the discarded event */ -@@ -2487,6 +2516,10 @@ rb_update_write_stamp(struct ring_buffer +@@ -2487,6 +2518,10 @@ rb_update_write_stamp(struct ring_buffer { u64 delta; @@ -201,7 +196,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> /* * The event first in the commit queue updates the * time stamp. -@@ -2500,9 +2533,7 @@ rb_update_write_stamp(struct ring_buffer +@@ -2500,9 +2535,7 @@ rb_update_write_stamp(struct ring_buffer cpu_buffer->write_stamp = cpu_buffer->commit_page->page->time_stamp; else if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) { @@ -212,7 +207,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> cpu_buffer->write_stamp += delta; } else cpu_buffer->write_stamp += event->time_delta; -@@ -2686,7 +2717,7 @@ static struct ring_buffer_event * +@@ -2686,7 +2719,7 @@ static struct ring_buffer_event * * If this is the first commit on the page, then it has the same * timestamp as the page itself. */ @@ -221,7 +216,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> info->delta = 0; /* See if we shot pass the end of this buffer page */ -@@ -2764,8 +2795,11 @@ rb_reserve_next_event(struct ring_buffer +@@ -2764,8 +2797,11 @@ rb_reserve_next_event(struct ring_buffer /* make sure this diff is calculated here */ barrier(); @@ -235,7 +230,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> info.delta = diff; if (unlikely(test_time_stamp(info.delta))) rb_handle_timestamp(cpu_buffer, &info); -@@ -3447,14 +3481,12 @@ rb_update_read_stamp(struct ring_buffer_ +@@ -3447,14 +3483,12 @@ rb_update_read_stamp(struct ring_buffer_ return; case RINGBUF_TYPE_TIME_EXTEND: @@ -252,7 +247,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return; case RINGBUF_TYPE_DATA: -@@ -3478,14 +3510,12 @@ rb_update_iter_read_stamp(struct ring_bu +@@ -3478,14 +3512,12 @@ rb_update_iter_read_stamp(struct ring_bu return; case RINGBUF_TYPE_TIME_EXTEND: @@ -269,7 +264,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return; case RINGBUF_TYPE_DATA: -@@ -3709,6 +3739,8 @@ rb_buffer_peek(struct ring_buffer_per_cp +@@ -3709,6 +3741,8 @@ rb_buffer_peek(struct ring_buffer_per_cp struct buffer_page *reader; int nr_loops = 0; @@ -278,7 +273,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> again: /* * We repeat when a time extend is encountered. -@@ -3745,12 +3777,17 @@ rb_buffer_peek(struct ring_buffer_per_cp +@@ -3745,12 +3779,17 @@ rb_buffer_peek(struct ring_buffer_per_cp goto again; case RINGBUF_TYPE_TIME_STAMP: @@ -298,7 +293,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> *ts = cpu_buffer->read_stamp + event->time_delta; ring_buffer_normalize_time_stamp(cpu_buffer->buffer, cpu_buffer->cpu, ts); -@@ -3775,6 +3812,9 @@ rb_iter_peek(struct ring_buffer_iter *it +@@ -3775,6 +3814,9 @@ rb_iter_peek(struct ring_buffer_iter *it struct ring_buffer_event *event; int nr_loops = 0; @@ -308,7 +303,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> cpu_buffer = iter->cpu_buffer; buffer = cpu_buffer->buffer; -@@ -3827,12 +3867,17 @@ rb_iter_peek(struct ring_buffer_iter *it +@@ -3827,12 +3869,17 @@ rb_iter_peek(struct ring_buffer_iter *it goto again; case RINGBUF_TYPE_TIME_STAMP: diff --git a/patches/0005-tracing-Give-event-triggers-access-to-ring_buffer_ev.patch b/patches/0009-tracing-Give-event-triggers-access-to-ring_buffer_ev.patch index 5f55d10387c0..840e4441dd87 100644 --- a/patches/0005-tracing-Give-event-triggers-access-to-ring_buffer_ev.patch +++ b/patches/0009-tracing-Give-event-triggers-access-to-ring_buffer_ev.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:06 -0500 -Subject: [PATCH 05/32] tracing: Give event triggers access to +Date: Tue, 5 Sep 2017 16:57:21 -0500 +Subject: [PATCH 09/40] tracing: Give event triggers access to ring_buffer_event The ring_buffer event can provide a timestamp that may be useful to @@ -47,7 +47,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (eflags & EVENT_FILE_FL_PID_FILTER) --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h -@@ -1189,7 +1189,7 @@ static inline bool +@@ -1190,7 +1190,7 @@ static inline bool unsigned long eflags = file->flags; if (eflags & EVENT_FILE_FL_TRIGGER_COND) @@ -56,7 +56,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags) || (unlikely(file->flags & EVENT_FILE_FL_FILTERED) && -@@ -1226,7 +1226,7 @@ event_trigger_unlock_commit(struct trace +@@ -1227,7 +1227,7 @@ event_trigger_unlock_commit(struct trace trace_buffer_unlock_commit(file->tr, buffer, event, irq_flags, pc); if (tt) @@ -65,7 +65,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } /** -@@ -1259,7 +1259,7 @@ event_trigger_unlock_commit_regs(struct +@@ -1260,7 +1260,7 @@ event_trigger_unlock_commit_regs(struct irq_flags, pc, regs); if (tt) @@ -74,7 +74,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } #define FILTER_PRED_INVALID ((unsigned short)-1) -@@ -1482,7 +1482,8 @@ extern int register_trigger_hist_enable_ +@@ -1483,7 +1483,8 @@ extern int register_trigger_hist_enable_ */ struct event_trigger_ops { void (*func)(struct event_trigger_data *data, @@ -86,7 +86,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> void (*free)(struct event_trigger_ops *ops, --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -921,7 +921,8 @@ static inline void add_to_key(char *comp +@@ -909,7 +909,8 @@ static inline void add_to_key(char *comp memcpy(compound_key + key_field->offset, key, size); } @@ -96,7 +96,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> { struct hist_trigger_data *hist_data = data->private_data; bool use_compound_key = (hist_data->n_keys > 1); -@@ -1672,7 +1673,8 @@ static struct event_command trigger_hist +@@ -1660,7 +1661,8 @@ static struct event_command trigger_hist } static void @@ -106,7 +106,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> { struct enable_trigger_data *enable_data = data->private_data; struct event_trigger_data *test; -@@ -1688,7 +1690,8 @@ hist_enable_trigger(struct event_trigger +@@ -1676,7 +1678,8 @@ hist_enable_trigger(struct event_trigger } static void @@ -116,7 +116,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> { if (!data->count) return; -@@ -1696,7 +1699,7 @@ hist_enable_count_trigger(struct event_t +@@ -1684,7 +1687,7 @@ hist_enable_count_trigger(struct event_t if (data->count != -1) (data->count)--; diff --git a/patches/0010-tracing-Add-NO_DISCARD-event-file-flag.patch b/patches/0010-tracing-Add-NO_DISCARD-event-file-flag.patch deleted file mode 100644 index c738f5638d1d..000000000000 --- a/patches/0010-tracing-Add-NO_DISCARD-event-file-flag.patch +++ /dev/null @@ -1,105 +0,0 @@ -From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:11 -0500 -Subject: [PATCH 10/32] tracing: Add NO_DISCARD event file flag - -Whenever an event_command has a post-trigger that needs access to the -event record, the event record can't be discarded, or the post-trigger -will eventually see bogus data. - -In order to allow the discard check to treat this case separately, add -an EVENT_FILE_FL_NO_DISCARD flag to the event file flags, along with -code in the discard check that checks the flag and avoids the discard -when the flag is set. - -Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> -Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ---- - include/linux/trace_events.h | 3 +++ - kernel/trace/trace.h | 13 ++++++++++--- - kernel/trace/trace_events_trigger.c | 16 +++++++++++++--- - 3 files changed, 26 insertions(+), 6 deletions(-) - ---- a/include/linux/trace_events.h -+++ b/include/linux/trace_events.h -@@ -306,6 +306,7 @@ enum { - EVENT_FILE_FL_TRIGGER_MODE_BIT, - EVENT_FILE_FL_TRIGGER_COND_BIT, - EVENT_FILE_FL_PID_FILTER_BIT, -+ EVENT_FILE_FL_NO_DISCARD_BIT, - }; - - /* -@@ -320,6 +321,7 @@ enum { - * TRIGGER_MODE - When set, invoke the triggers associated with the event - * TRIGGER_COND - When set, one or more triggers has an associated filter - * PID_FILTER - When set, the event is filtered based on pid -+ * NO_DISCARD - When set, do not discard events, something needs them later - */ - enum { - EVENT_FILE_FL_ENABLED = (1 << EVENT_FILE_FL_ENABLED_BIT), -@@ -331,6 +333,7 @@ enum { - EVENT_FILE_FL_TRIGGER_MODE = (1 << EVENT_FILE_FL_TRIGGER_MODE_BIT), - EVENT_FILE_FL_TRIGGER_COND = (1 << EVENT_FILE_FL_TRIGGER_COND_BIT), - EVENT_FILE_FL_PID_FILTER = (1 << EVENT_FILE_FL_PID_FILTER_BIT), -+ EVENT_FILE_FL_NO_DISCARD = (1 << EVENT_FILE_FL_NO_DISCARD_BIT), - }; - - struct trace_event_file { ---- a/kernel/trace/trace.h -+++ b/kernel/trace/trace.h -@@ -1191,9 +1191,16 @@ static inline bool - if (eflags & EVENT_FILE_FL_TRIGGER_COND) - *tt = event_triggers_call(file, entry, event); - -- if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags) || -- (unlikely(file->flags & EVENT_FILE_FL_FILTERED) && -- !filter_match_preds(file->filter, entry))) { -+ if (unlikely(file->flags & EVENT_FILE_FL_FILTERED) && -+ !filter_match_preds(file->filter, entry)) { -+ __trace_event_discard_commit(buffer, event); -+ return true; -+ } -+ -+ if (test_bit(EVENT_FILE_FL_NO_DISCARD_BIT, &file->flags)) -+ return false; -+ -+ if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags)) { - __trace_event_discard_commit(buffer, event); - return true; - } ---- a/kernel/trace/trace_events_trigger.c -+++ b/kernel/trace/trace_events_trigger.c -@@ -505,20 +505,30 @@ clear_event_triggers(struct trace_array - void update_cond_flag(struct trace_event_file *file) - { - struct event_trigger_data *data; -- bool set_cond = false; -+ bool set_cond = false, set_no_discard = false; - - list_for_each_entry_rcu(data, &file->triggers, list) { - if (data->filter || event_command_post_trigger(data->cmd_ops) || -- event_command_needs_rec(data->cmd_ops)) { -+ event_command_needs_rec(data->cmd_ops)) - set_cond = true; -+ -+ if (event_command_post_trigger(data->cmd_ops) && -+ event_command_needs_rec(data->cmd_ops)) -+ set_no_discard = true; -+ -+ if (set_cond && set_no_discard) - break; -- } - } - - if (set_cond) - set_bit(EVENT_FILE_FL_TRIGGER_COND_BIT, &file->flags); - else - clear_bit(EVENT_FILE_FL_TRIGGER_COND_BIT, &file->flags); -+ -+ if (set_no_discard) -+ set_bit(EVENT_FILE_FL_NO_DISCARD_BIT, &file->flags); -+ else -+ clear_bit(EVENT_FILE_FL_NO_DISCARD_BIT, &file->flags); - } - - /** diff --git a/patches/0006-tracing-Add-ring-buffer-event-param-to-hist-field-fu.patch b/patches/0010-tracing-Add-ring-buffer-event-param-to-hist-field-fu.patch index 9d2ac71a1928..84a46a8b7074 100644 --- a/patches/0006-tracing-Add-ring-buffer-event-param-to-hist-field-fu.patch +++ b/patches/0010-tracing-Add-ring-buffer-event-param-to-hist-field-fu.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:07 -0500 -Subject: [PATCH 06/32] tracing: Add ring buffer event param to hist field +Date: Tue, 5 Sep 2017 16:57:22 -0500 +Subject: [PATCH 10/40] tracing: Add ring buffer event param to hist field functions Some events such as timestamps require access to a ring_buffer_event @@ -90,7 +90,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> { \ type *addr = (type *)(event + hist_field->field->offset); \ \ -@@ -883,8 +892,8 @@ create_hist_data(unsigned int map_bits, +@@ -871,8 +880,8 @@ create_hist_data(unsigned int map_bits, } static void hist_trigger_elt_update(struct hist_trigger_data *hist_data, @@ -101,7 +101,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> { struct hist_field *hist_field; unsigned int i; -@@ -892,7 +901,7 @@ static void hist_trigger_elt_update(stru +@@ -880,7 +889,7 @@ static void hist_trigger_elt_update(stru for_each_hist_val_field(i, hist_data) { hist_field = hist_data->fields[i]; @@ -110,7 +110,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> tracing_map_update_sum(elt, i, hist_val); } } -@@ -922,7 +931,7 @@ static inline void add_to_key(char *comp +@@ -910,7 +919,7 @@ static inline void add_to_key(char *comp } static void event_hist_trigger(struct event_trigger_data *data, void *rec, @@ -119,7 +119,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> { struct hist_trigger_data *hist_data = data->private_data; bool use_compound_key = (hist_data->n_keys > 1); -@@ -951,7 +960,7 @@ static void event_hist_trigger(struct ev +@@ -939,7 +948,7 @@ static void event_hist_trigger(struct ev key = entries; } else { @@ -128,7 +128,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (key_field->flags & HIST_FIELD_FL_STRING) { key = (void *)(unsigned long)field_contents; use_compound_key = true; -@@ -968,7 +977,7 @@ static void event_hist_trigger(struct ev +@@ -956,7 +965,7 @@ static void event_hist_trigger(struct ev elt = tracing_map_insert(hist_data->map, key); if (elt) diff --git a/patches/0011-tracing-Add-post-trigger-flag-to-hist-trigger-comman.patch b/patches/0011-tracing-Add-post-trigger-flag-to-hist-trigger-comman.patch deleted file mode 100644 index bb63da5961c2..000000000000 --- a/patches/0011-tracing-Add-post-trigger-flag-to-hist-trigger-comman.patch +++ /dev/null @@ -1,28 +0,0 @@ -From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:12 -0500 -Subject: [PATCH 11/32] tracing: Add post-trigger flag to hist trigger command - -Add EVENT_CMD_FL_POST_TRIGGER to the hist trigger cmd - it doesn't -affect the hist trigger results, and allows further events such as -synthetic events to be generated from a hist trigger. - -Without this change, generating an event from a hist trigger will -cause the generated event to fail a ring buffer trace_recursive_lock() -check and return without actually logging the event. - -Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ---- - kernel/trace/trace_events_hist.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/kernel/trace/trace_events_hist.c -+++ b/kernel/trace/trace_events_hist.c -@@ -1676,7 +1676,7 @@ static int event_hist_trigger_func(struc - static struct event_command trigger_hist_cmd = { - .name = "hist", - .trigger_type = ETT_EVENT_HIST, -- .flags = EVENT_CMD_FL_NEEDS_REC, -+ .flags = EVENT_CMD_FL_NEEDS_REC | EVENT_CMD_FL_POST_TRIGGER, - .func = event_hist_trigger_func, - .reg = hist_register_trigger, - .unreg = hist_unregister_trigger, diff --git a/patches/0007-tracing-Increase-tracing-map-KEYS_MAX-size.patch b/patches/0011-tracing-Increase-tracing-map-KEYS_MAX-size.patch index f889dff5f484..670eef00a2e4 100644 --- a/patches/0007-tracing-Increase-tracing-map-KEYS_MAX-size.patch +++ b/patches/0011-tracing-Increase-tracing-map-KEYS_MAX-size.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:08 -0500 -Subject: [PATCH 07/32] tracing: Increase tracing map KEYS_MAX size +Date: Tue, 5 Sep 2017 16:57:23 -0500 +Subject: [PATCH 11/40] tracing: Increase tracing map KEYS_MAX size The current default for the number of subkeys in a compound key is 2, which is too restrictive. Increase it to a more realistic value of 3. diff --git a/patches/0008-tracing-Break-out-hist-trigger-assignment-parsing.patch b/patches/0012-tracing-Break-out-hist-trigger-assignment-parsing.patch index 07d771d47db6..d49318f8575c 100644 --- a/patches/0008-tracing-Break-out-hist-trigger-assignment-parsing.patch +++ b/patches/0012-tracing-Break-out-hist-trigger-assignment-parsing.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:09 -0500 -Subject: [PATCH 08/32] tracing: Break out hist trigger assignment parsing +Date: Tue, 5 Sep 2017 16:57:24 -0500 +Subject: [PATCH 12/40] tracing: Break out hist trigger assignment parsing This will make it easier to add variables, and makes the parsing code cleaner regardless. diff --git a/patches/0009-tracing-Make-traceprobe-parsing-code-reusable.patch b/patches/0013-tracing-Make-traceprobe-parsing-code-reusable.patch index 3f91dc611b9d..05a7bbd6da53 100644 --- a/patches/0009-tracing-Make-traceprobe-parsing-code-reusable.patch +++ b/patches/0013-tracing-Make-traceprobe-parsing-code-reusable.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:10 -0500 -Subject: [PATCH 09/32] tracing: Make traceprobe parsing code reusable +Date: Tue, 5 Sep 2017 16:57:25 -0500 +Subject: [PATCH 13/40] tracing: Make traceprobe parsing code reusable traceprobe_probes_write() and traceprobe_command() actually contain nothing that ties them to kprobes - the code is generically useful for @@ -24,7 +24,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c -@@ -7907,6 +7907,92 @@ void ftrace_dump(enum ftrace_dump_mode o +@@ -7922,6 +7922,92 @@ void ftrace_dump(enum ftrace_dump_mode o } EXPORT_SYMBOL_GPL(ftrace_dump); @@ -119,7 +119,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> int ring_buf_size; --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h -@@ -1650,6 +1650,13 @@ void trace_printk_start_comm(void); +@@ -1651,6 +1651,13 @@ void trace_printk_start_comm(void); int trace_keep_overwrite(struct tracer *tracer, u32 mask, int set); int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled); diff --git a/patches/0012-tracing-Add-hist-trigger-timestamp-support.patch b/patches/0014-tracing-Add-hist-trigger-timestamp-support.patch index 3092c4117e9d..c70686bbd64c 100644 --- a/patches/0012-tracing-Add-hist-trigger-timestamp-support.patch +++ b/patches/0014-tracing-Add-hist-trigger-timestamp-support.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:13 -0500 -Subject: [PATCH 12/32] tracing: Add hist trigger timestamp support +Date: Tue, 5 Sep 2017 16:57:26 -0500 +Subject: [PATCH 14/40] tracing: Add hist trigger timestamp support Add support for a timestamp event field. This is actually a 'pseudo-' event field in that it behaves like it's part of the event record, but @@ -23,8 +23,8 @@ a histogram makes use of the "$common_timestamp" field. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - kernel/trace/trace_events_hist.c | 90 ++++++++++++++++++++++++++++----------- - 1 file changed, 66 insertions(+), 24 deletions(-) + kernel/trace/trace_events_hist.c | 88 ++++++++++++++++++++++++++++----------- + 1 file changed, 65 insertions(+), 23 deletions(-) --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -66,7 +66,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (field_name == NULL) field_name = ""; -@@ -435,6 +445,12 @@ static struct hist_field *create_hist_fi +@@ -424,6 +434,12 @@ static struct hist_field *create_hist_fi goto out; } @@ -79,12 +79,12 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (WARN_ON_ONCE(!field)) goto out; -@@ -512,10 +528,15 @@ static int create_val_field(struct hist_ +@@ -501,10 +517,15 @@ static int create_val_field(struct hist_ } } - field = trace_find_event_field(file->event_call, field_name); -- if (!field) { +- if (!field || !field->size) { - ret = -EINVAL; - goto out; + if (strcmp(field_name, "$common_timestamp") == 0) { @@ -92,19 +92,19 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + hist_data->enable_timestamps = true; + } else { + field = trace_find_event_field(file->event_call, field_name); -+ if (!field) { ++ if (!field || !field->size) { + ret = -EINVAL; + goto out; + } } hist_data->fields[val_idx] = create_hist_field(field, flags); -@@ -610,16 +631,22 @@ static int create_key_field(struct hist_ +@@ -599,16 +620,22 @@ static int create_key_field(struct hist_ } } - field = trace_find_event_field(file->event_call, field_name); -- if (!field) { +- if (!field || !field->size) { - ret = -EINVAL; - goto out; - } @@ -114,7 +114,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + key_size = sizeof(u64); + } else { + field = trace_find_event_field(file->event_call, field_name); -+ if (!field) { ++ if (!field || !field->size) { + ret = -EINVAL; + goto out; + } @@ -131,16 +131,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } hist_data->fields[key_idx] = create_hist_field(field, flags); -@@ -756,7 +783,7 @@ static int create_sort_keys(struct hist_ - break; - } - -- if (strcmp(field_name, "hitcount") == 0) { -+ if ((strcmp(field_name, "hitcount") == 0)) { - descending = is_descending(field_str); - if (descending < 0) { - ret = descending; -@@ -816,6 +843,9 @@ static int create_tracing_map_fields(str +@@ -804,6 +831,9 @@ static int create_tracing_map_fields(str if (hist_field->flags & HIST_FIELD_FL_STACKTRACE) cmp_fn = tracing_map_cmp_none; @@ -150,7 +141,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> else if (is_string_field(field)) cmp_fn = tracing_map_cmp_string; else -@@ -1213,7 +1243,11 @@ static void hist_field_print(struct seq_ +@@ -1201,7 +1231,11 @@ static void hist_field_print(struct seq_ { const char *field_name = hist_field_name(hist_field, 0); @@ -163,7 +154,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (hist_field->flags) { const char *flags_str = get_hist_field_flags(hist_field); -@@ -1264,27 +1298,25 @@ static int event_hist_trigger_print(stru +@@ -1252,27 +1286,25 @@ static int event_hist_trigger_print(stru for (i = 0; i < hist_data->n_sort_keys; i++) { struct tracing_map_sort_key *sort_key; @@ -198,7 +189,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits)); if (data->filter_str) -@@ -1452,6 +1484,10 @@ static bool hist_trigger_match(struct ev +@@ -1440,6 +1472,10 @@ static bool hist_trigger_match(struct ev return false; if (key_field->offset != key_field_test->offset) return false; @@ -209,7 +200,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } for (i = 0; i < hist_data->n_sort_keys; i++) { -@@ -1534,6 +1570,9 @@ static int hist_register_trigger(char *g +@@ -1522,6 +1558,9 @@ static int hist_register_trigger(char *g update_cond_flag(file); @@ -219,7 +210,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (trace_event_trigger_enable_disable(file, 1) < 0) { list_del_rcu(&data->list); update_cond_flag(file); -@@ -1568,6 +1607,9 @@ static void hist_unregister_trigger(char +@@ -1556,6 +1595,9 @@ static void hist_unregister_trigger(char if (unregistered && test->ops->free) test->ops->free(test->ops, test); diff --git a/patches/0013-tracing-Add-per-element-variable-support-to-tracing_.patch b/patches/0015-tracing-Add-per-element-variable-support-to-tracing_.patch index 4d05c895072d..6ecb342492fe 100644 --- a/patches/0013-tracing-Add-per-element-variable-support-to-tracing_.patch +++ b/patches/0015-tracing-Add-per-element-variable-support-to-tracing_.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:14 -0500 -Subject: [PATCH 13/32] tracing: Add per-element variable support to +Date: Tue, 5 Sep 2017 16:57:27 -0500 +Subject: [PATCH 15/40] tracing: Add per-element variable support to tracing_map In order to allow information to be passed between trace events, add @@ -20,9 +20,9 @@ important for event-matching uses. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - kernel/trace/tracing_map.c | 113 +++++++++++++++++++++++++++++++++++++++++++++ + kernel/trace/tracing_map.c | 108 +++++++++++++++++++++++++++++++++++++++++++++ kernel/trace/tracing_map.h | 11 ++++ - 2 files changed, 124 insertions(+) + 2 files changed, 119 insertions(+) --- a/kernel/trace/tracing_map.c +++ b/kernel/trace/tracing_map.c @@ -150,10 +150,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> kfree(elt->key); kfree(elt); } -@@ -330,6 +426,18 @@ static struct tracing_map_elt *tracing_m +@@ -329,6 +425,18 @@ static struct tracing_map_elt *tracing_m + err = -ENOMEM; goto free; } - ++ + elt->vars = kcalloc(map->n_vars, sizeof(*elt->vars), GFP_KERNEL); + if (!elt->vars) { + err = -ENOMEM; @@ -165,21 +166,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + err = -ENOMEM; + goto free; + } -+ - tracing_map_elt_init_fields(elt); - - if (map->ops && map->ops->elt_alloc) { -@@ -833,6 +941,11 @@ static struct tracing_map_elt *copy_elt( - dup_elt->fields[i].cmp_fn = elt->fields[i].cmp_fn; - } -+ for (i = 0; i < elt->map->n_vars; i++) { -+ atomic64_set(&dup_elt->vars[i], atomic64_read(&elt->vars[i])); -+ dup_elt->var_set[i] = elt->var_set[i]; -+ } -+ - return dup_elt; - } + tracing_map_elt_init_fields(elt); --- a/kernel/trace/tracing_map.h +++ b/kernel/trace/tracing_map.h @@ -208,7 +196,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> atomic64_t hits; atomic64_t drops; }; -@@ -247,6 +251,7 @@ tracing_map_create(unsigned int map_bits +@@ -240,6 +244,7 @@ tracing_map_create(unsigned int map_bits extern int tracing_map_init(struct tracing_map *map); extern int tracing_map_add_sum_field(struct tracing_map *map); @@ -216,7 +204,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> extern int tracing_map_add_key_field(struct tracing_map *map, unsigned int offset, tracing_map_cmp_fn_t cmp_fn); -@@ -266,7 +271,13 @@ extern int tracing_map_cmp_none(void *va +@@ -259,7 +264,13 @@ extern int tracing_map_cmp_none(void *va extern void tracing_map_update_sum(struct tracing_map_elt *elt, unsigned int i, u64 n); diff --git a/patches/0014-tracing-Add-hist_data-member-to-hist_field.patch b/patches/0016-tracing-Add-hist_data-member-to-hist_field.patch index ae3d689550b3..fc725401801b 100644 --- a/patches/0014-tracing-Add-hist_data-member-to-hist_field.patch +++ b/patches/0016-tracing-Add-hist_data-member-to-hist_field.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:15 -0500 -Subject: [PATCH 14/32] tracing: Add hist_data member to hist_field +Date: Tue, 5 Sep 2017 16:57:28 -0500 +Subject: [PATCH 16/40] tracing: Add hist_data member to hist_field Allow hist_data access via hist_field. Some users of hist_fields require or will require more access to the associated hist_data. @@ -21,7 +21,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> }; static u64 hist_field_none(struct hist_field *field, void *event, -@@ -415,7 +416,8 @@ static void destroy_hist_field(struct hi +@@ -404,7 +405,8 @@ static void destroy_hist_field(struct hi kfree(hist_field); } @@ -31,7 +31,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> unsigned long flags) { struct hist_field *hist_field; -@@ -427,6 +429,8 @@ static struct hist_field *create_hist_fi +@@ -416,6 +418,8 @@ static struct hist_field *create_hist_fi if (!hist_field) return NULL; @@ -40,7 +40,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (flags & HIST_FIELD_FL_HITCOUNT) { hist_field->fn = hist_field_counter; goto out; -@@ -440,7 +444,7 @@ static struct hist_field *create_hist_fi +@@ -429,7 +433,7 @@ static struct hist_field *create_hist_fi if (flags & HIST_FIELD_FL_LOG2) { unsigned long fl = flags & ~HIST_FIELD_FL_LOG2; hist_field->fn = hist_field_log2; @@ -49,7 +49,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> hist_field->size = hist_field->operands[0]->size; goto out; } -@@ -493,7 +497,7 @@ static void destroy_hist_fields(struct h +@@ -482,7 +486,7 @@ static void destroy_hist_fields(struct h static int create_hitcount_val(struct hist_trigger_data *hist_data) { hist_data->fields[HITCOUNT_IDX] = @@ -58,7 +58,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (!hist_data->fields[HITCOUNT_IDX]) return -ENOMEM; -@@ -539,7 +543,7 @@ static int create_val_field(struct hist_ +@@ -528,7 +532,7 @@ static int create_val_field(struct hist_ } } @@ -67,7 +67,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (!hist_data->fields[val_idx]) { ret = -ENOMEM; goto out; -@@ -649,7 +653,7 @@ static int create_key_field(struct hist_ +@@ -638,7 +642,7 @@ static int create_key_field(struct hist_ } } diff --git a/patches/0015-tracing-Add-usecs-modifier-for-hist-trigger-timestam.patch b/patches/0017-tracing-Add-usecs-modifier-for-hist-trigger-timestam.patch index b90640f23182..e5232007c2ca 100644 --- a/patches/0015-tracing-Add-usecs-modifier-for-hist-trigger-timestam.patch +++ b/patches/0017-tracing-Add-usecs-modifier-for-hist-trigger-timestam.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:16 -0500 -Subject: [PATCH 15/32] tracing: Add usecs modifier for hist trigger timestamps +Date: Tue, 5 Sep 2017 16:57:29 -0500 +Subject: [PATCH 17/40] tracing: Add usecs modifier for hist trigger timestamps Appending .usecs onto a common_timestamp field will cause the timestamp value to be in microseconds instead of the default @@ -40,7 +40,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> */ --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h -@@ -280,6 +280,8 @@ extern void trace_array_put(struct trace +@@ -281,6 +281,8 @@ extern void trace_array_put(struct trace extern int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs); @@ -101,7 +101,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static const char *hist_field_name(struct hist_field *field, unsigned int level) { -@@ -629,6 +639,8 @@ static int create_key_field(struct hist_ +@@ -618,6 +628,8 @@ static int create_key_field(struct hist_ flags |= HIST_FIELD_FL_SYSCALL; else if (strcmp(field_str, "log2") == 0) flags |= HIST_FIELD_FL_LOG2; @@ -110,7 +110,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> else { ret = -EINVAL; goto out; -@@ -638,6 +650,8 @@ static int create_key_field(struct hist_ +@@ -627,6 +639,8 @@ static int create_key_field(struct hist_ if (strcmp(field_name, "$common_timestamp") == 0) { flags |= HIST_FIELD_FL_TIMESTAMP; hist_data->enable_timestamps = true; @@ -119,7 +119,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> key_size = sizeof(u64); } else { field = trace_find_event_field(file->event_call, field_name); -@@ -1239,6 +1253,8 @@ static const char *get_hist_field_flags( +@@ -1227,6 +1241,8 @@ static const char *get_hist_field_flags( flags_str = "syscall"; else if (hist_field->flags & HIST_FIELD_FL_LOG2) flags_str = "log2"; diff --git a/patches/0016-tracing-Add-variable-support-to-hist-triggers.patch b/patches/0018-tracing-Add-variable-support-to-hist-triggers.patch index 9d046d4a1dfe..bbc4e015e8bb 100644 --- a/patches/0016-tracing-Add-variable-support-to-hist-triggers.patch +++ b/patches/0018-tracing-Add-variable-support-to-hist-triggers.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:17 -0500 -Subject: [PATCH 16/32] tracing: Add variable support to hist triggers +Date: Tue, 5 Sep 2017 16:57:30 -0500 +Subject: [PATCH 18/40] tracing: Add variable support to hist triggers Add support for saving the value of a current event's event field by assigning it to a variable that can be read by a subsequent event. @@ -11,8 +11,8 @@ to any event field. Both keys and values can be saved and retrieved in this way: - # echo 'hist:keys=next_pid:vals=ts0=common_timestamp ... - # echo 'hist:key=timer_pid=common_pid ...' + # echo 'hist:keys=next_pid:vals=$ts0:ts0=common_timestamp ... + # echo 'hist:timer_pid=common_pid:key=$timer_pid ...' If a variable isn't a key variable or prefixed with 'vals=', the associated event field will be saved in a variable but won't be summed @@ -22,21 +22,22 @@ as a value: Multiple variables can be assigned at the same time: - # echo 'hist:keys=pid:vals=ts0=common_timestamp,b=field1,field2 ... + # echo 'hist:keys=pid:vals=$ts0,$b,field2:ts0=common_timestamp,b=field1 ... Multiple (or single) variables can also be assigned at the same time using separate assignments: - # echo 'hist:keys=pid:vals=ts0=common_timestamp:b=field1:c=field2 ... + # echo 'hist:keys=pid:vals=$ts0:ts0=common_timestamp:b=field1:c=field2 ... Variables set as above can be used by being referenced from another event, as described in a subsequent patch. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Baohong Liu <baohong.liu@intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - kernel/trace/trace_events_hist.c | 299 ++++++++++++++++++++++++++++++++++----- - 1 file changed, 264 insertions(+), 35 deletions(-) + kernel/trace/trace_events_hist.c | 372 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 333 insertions(+), 39 deletions(-) --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -62,22 +63,30 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> }; static u64 hist_field_none(struct hist_field *field, void *event, -@@ -138,6 +146,8 @@ enum hist_field_flags { +@@ -138,6 +146,14 @@ enum hist_field_flags { 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, ++}; ++ ++struct var_defs { ++ unsigned int n_vars; ++ char *name[TRACING_MAP_VARS_MAX]; ++ char *expr[TRACING_MAP_VARS_MAX]; }; struct hist_trigger_attrs { -@@ -150,13 +160,18 @@ struct hist_trigger_attrs { +@@ -150,13 +166,20 @@ struct hist_trigger_attrs { bool clear; bool ts_in_usecs; unsigned int map_bits; + + char *assignment_str[TRACING_MAP_VARS_MAX]; + unsigned int n_assignments; ++ ++ struct var_defs var_defs; }; struct hist_trigger_data { @@ -91,7 +100,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> unsigned int key_size; struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX]; unsigned int n_sort_keys; -@@ -164,6 +179,7 @@ struct hist_trigger_data { +@@ -164,6 +187,7 @@ struct hist_trigger_data { struct hist_trigger_attrs *attrs; struct tracing_map *map; bool enable_timestamps; @@ -99,7 +108,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> }; static u64 hist_field_timestamp(struct hist_field *hist_field, void *event, -@@ -262,9 +278,14 @@ static int parse_map_size(char *str) +@@ -262,9 +286,14 @@ static int parse_map_size(char *str) static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs) { @@ -114,7 +123,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> kfree(attrs->name); kfree(attrs->sort_key_str); kfree(attrs->keys_str); -@@ -295,8 +316,22 @@ static int parse_assignment(char *str, s +@@ -295,8 +324,22 @@ static int parse_assignment(char *str, s goto out; } attrs->map_bits = map_bits; @@ -139,9 +148,9 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> out: return ret; } -@@ -423,12 +458,15 @@ static void destroy_hist_field(struct hi +@@ -412,12 +455,15 @@ static void destroy_hist_field(struct hi for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) - destroy_hist_field(hist_field->operands[i], ++level); + destroy_hist_field(hist_field->operands[i], level + 1); + kfree(hist_field->var.name); + @@ -156,7 +165,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> { struct hist_field *hist_field; -@@ -454,7 +492,7 @@ static struct hist_field *create_hist_fi +@@ -443,7 +489,7 @@ static struct hist_field *create_hist_fi if (flags & HIST_FIELD_FL_LOG2) { unsigned long fl = flags & ~HIST_FIELD_FL_LOG2; hist_field->fn = hist_field_log2; @@ -165,7 +174,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> hist_field->size = hist_field->operands[0]->size; goto out; } -@@ -489,14 +527,23 @@ static struct hist_field *create_hist_fi +@@ -478,14 +524,23 @@ static struct hist_field *create_hist_fi hist_field->field = field; hist_field->flags = flags; @@ -190,7 +199,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (hist_data->fields[i]) { destroy_hist_field(hist_data->fields[i], 0); hist_data->fields[i] = NULL; -@@ -507,11 +554,12 @@ static void destroy_hist_fields(struct h +@@ -496,11 +551,12 @@ static void destroy_hist_fields(struct h static int create_hitcount_val(struct hist_trigger_data *hist_data) { hist_data->fields[HITCOUNT_IDX] = @@ -204,10 +213,14 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX)) return -EINVAL; -@@ -519,19 +567,81 @@ static int create_hitcount_val(struct hi +@@ -508,19 +564,53 @@ static int create_hitcount_val(struct hi return 0; } +-static int create_val_field(struct hist_trigger_data *hist_data, +- unsigned int val_idx, +- struct trace_event_file *file, +- char *field_str) +static struct hist_field *find_var_field(struct hist_trigger_data *hist_data, + const char *var_name) +{ @@ -245,51 +258,24 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return NULL; +} + - static int create_val_field(struct hist_trigger_data *hist_data, - unsigned int val_idx, - struct trace_event_file *file, -- char *field_str) -+ char *field_str, bool var_only) ++static int __create_val_field(struct hist_trigger_data *hist_data, ++ unsigned int val_idx, ++ struct trace_event_file *file, ++ char *var_name, char *field_str, ++ unsigned long flags) { struct ftrace_event_field *field = NULL; -+ char *field_name, *var_name; - unsigned long flags = 0; -- char *field_name; +- unsigned long flags = 0; + char *field_name; int ret = 0; - if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX)) -+ if (WARN_ON(!var_only && val_idx >= TRACING_MAP_VALS_MAX)) - return -EINVAL; - -+ var_name = strsep(&field_str, "="); -+ if (field_str && var_name) { -+ if (find_var(file, var_name) && -+ !hist_data->remove) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ flags |= HIST_FIELD_FL_VAR; -+ hist_data->n_vars++; -+ if (hist_data->n_vars > TRACING_MAP_VARS_MAX) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ if (var_only) -+ flags |= HIST_FIELD_FL_VAR_ONLY; -+ } else if (!var_only && var_name != NULL && field_str == NULL) { -+ field_str = var_name; -+ var_name = NULL; -+ } else { -+ ret = -EINVAL; -+ goto out; -+ } -+ +- return -EINVAL; +- field_name = strsep(&field_str, "."); if (field_str) { if (strcmp(field_str, "hex") == 0) -@@ -553,15 +663,19 @@ static int create_val_field(struct hist_ +@@ -542,25 +632,65 @@ static int create_val_field(struct hist_ } } @@ -311,7 +297,45 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ret = -EINVAL; out: return ret; -@@ -571,7 +685,7 @@ static int create_val_fields(struct hist + } + ++static int create_val_field(struct hist_trigger_data *hist_data, ++ unsigned int val_idx, ++ struct trace_event_file *file, ++ char *field_str) ++{ ++ if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX)) ++ return -EINVAL; ++ ++ return __create_val_field(hist_data, val_idx, file, NULL, field_str, 0); ++} ++ ++static int create_var_field(struct hist_trigger_data *hist_data, ++ unsigned int val_idx, ++ struct trace_event_file *file, ++ char *var_name, char *expr_str) ++{ ++ unsigned long flags = 0; ++ ++ if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX)) ++ return -EINVAL; ++ ++ if (find_var(file, var_name) && !hist_data->remove) { ++ return -EINVAL; ++ } ++ ++ flags |= HIST_FIELD_FL_VAR; ++ hist_data->n_vars++; ++ if (hist_data->n_vars > TRACING_MAP_VARS_MAX) { ++ return -EINVAL; ++ } ++ ++ flags |= HIST_FIELD_FL_VAR_ONLY; ++ ++ return __create_val_field(hist_data, val_idx, file, var_name, expr_str, flags); ++} ++ + static int create_val_fields(struct hist_trigger_data *hist_data, struct trace_event_file *file) { char *fields_str, *field_str; @@ -320,16 +344,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> int ret; ret = create_hitcount_val(hist_data); -@@ -591,12 +705,15 @@ static int create_val_fields(struct hist +@@ -580,12 +710,15 @@ static int create_val_fields(struct hist field_str = strsep(&fields_str, ","); if (!field_str) break; + if (strcmp(field_str, "hitcount") == 0) continue; -- ret = create_val_field(hist_data, j++, file, field_str); + -+ ret = create_val_field(hist_data, j++, file, field_str, false); + ret = create_val_field(hist_data, j++, file, field_str); if (ret) goto out; } @@ -337,14 +360,13 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (fields_str && (strcmp(fields_str, "hitcount") != 0)) ret = -EINVAL; out: -@@ -610,18 +727,32 @@ static int create_key_field(struct hist_ +@@ -599,11 +732,12 @@ static int create_key_field(struct hist_ char *field_str) { struct ftrace_event_field *field = NULL; + struct hist_field *hist_field = NULL; unsigned long flags = 0; unsigned int key_size; -+ char *var_name; int ret = 0; - if (WARN_ON(key_idx >= TRACING_MAP_FIELDS_MAX)) @@ -352,35 +374,24 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return -EINVAL; flags |= HIST_FIELD_FL_KEY; - -+ var_name = strsep(&field_str, "="); -+ if (field_str) { -+ if (find_var(file, var_name) && -+ !hist_data->remove) -+ return -EINVAL; -+ flags |= HIST_FIELD_FL_VAR; -+ } else { -+ field_str = var_name; -+ var_name = NULL; -+ } -+ +@@ -611,6 +745,7 @@ static int create_key_field(struct hist_ if (strcmp(field_str, "stacktrace") == 0) { flags |= HIST_FIELD_FL_STACKTRACE; key_size = sizeof(unsigned long) * HIST_STACKTRACE_DEPTH; -+ hist_field = create_hist_field(hist_data, NULL, flags, var_name); ++ hist_field = create_hist_field(hist_data, NULL, flags, NULL); } else { char *field_name = strsep(&field_str, "."); -@@ -667,7 +798,7 @@ static int create_key_field(struct hist_ +@@ -656,7 +791,7 @@ static int create_key_field(struct hist_ } } - hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags); -+ hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags, var_name); ++ hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags, NULL); if (!hist_data->fields[key_idx]) { ret = -ENOMEM; goto out; -@@ -683,6 +814,7 @@ static int create_key_field(struct hist_ +@@ -672,6 +807,7 @@ static int create_key_field(struct hist_ } hist_data->n_keys++; @@ -388,37 +399,99 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (WARN_ON(hist_data->n_keys > TRACING_MAP_KEYS_MAX)) return -EINVAL; -@@ -726,6 +858,29 @@ static int create_key_fields(struct hist +@@ -715,21 +851,108 @@ static int create_key_fields(struct hist return ret; } +static int create_var_fields(struct hist_trigger_data *hist_data, + struct trace_event_file *file) +{ -+ unsigned int i, j, k = hist_data->n_vals; -+ char *str, *field_str; ++ unsigned int i, j = hist_data->n_vals; ++ int ret = 0; ++ ++ unsigned int n_vars = hist_data->attrs->var_defs.n_vars; ++ ++ for (i = 0; i < n_vars; i++) { ++ char *var_name = hist_data->attrs->var_defs.name[i]; ++ char *expr = hist_data->attrs->var_defs.expr[i]; ++ ++ ret = create_var_field(hist_data, j++, file, var_name, expr); ++ if (ret) ++ goto out; ++ } ++ out: ++ return ret; ++} ++ ++static void free_var_defs(struct hist_trigger_data *hist_data) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < hist_data->attrs->var_defs.n_vars; i++) { ++ kfree(hist_data->attrs->var_defs.name[i]); ++ kfree(hist_data->attrs->var_defs.expr[i]); ++ } ++ ++ hist_data->attrs->var_defs.n_vars = 0; ++} ++ ++static int parse_var_defs(struct hist_trigger_data *hist_data) ++{ ++ char *s, *str, *var_name, *field_str; ++ unsigned int i, j, n_vars = 0; + int ret = 0; + + for (i = 0; i < hist_data->attrs->n_assignments; i++) { + str = hist_data->attrs->assignment_str[i]; -+ + for (j = 0; j < TRACING_MAP_VARS_MAX; j++) { + field_str = strsep(&str, ","); + if (!field_str) + break; -+ ret = create_val_field(hist_data, k++, file, field_str, true); -+ if (ret) -+ goto out; ++ ++ var_name = strsep(&field_str, "="); ++ if (!var_name || !field_str) { ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ s = kstrdup(var_name, GFP_KERNEL); ++ if (!s) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ hist_data->attrs->var_defs.name[n_vars] = s; ++ ++ s = kstrdup(field_str, GFP_KERNEL); ++ if (!s) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ hist_data->attrs->var_defs.expr[n_vars++] = s; ++ ++ hist_data->attrs->var_defs.n_vars = n_vars; ++ ++ if (n_vars == TRACING_MAP_VARS_MAX) ++ goto free; + } + } -+ out: ++ ++ return ret; ++ free: ++ free_var_defs(hist_data); ++ + return ret; +} + static int create_hist_fields(struct hist_trigger_data *hist_data, struct trace_event_file *file) { -@@ -735,11 +890,13 @@ static int create_hist_fields(struct his + int ret; + ++ ret = parse_var_defs(hist_data); ++ if (ret) ++ goto out; ++ + ret = create_val_fields(hist_data, file); if (ret) goto out; @@ -432,9 +505,12 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + if (ret) + goto out; out: ++ free_var_defs(hist_data); ++ return ret; } -@@ -763,7 +920,7 @@ static int create_sort_keys(struct hist_ + +@@ -752,7 +975,7 @@ static int create_sort_keys(struct hist_ char *fields_str = hist_data->attrs->sort_key_str; struct tracing_map_sort_key *sort_key; int descending, ret = 0; @@ -443,13 +519,13 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> hist_data->n_sort_keys = 1; /* we always have at least one, hitcount */ -@@ -811,13 +968,21 @@ static int create_sort_keys(struct hist_ +@@ -800,12 +1023,19 @@ static int create_sort_keys(struct hist_ continue; } - for (j = 1; j < hist_data->n_fields; j++) { + for (j = 1, k = 1; j < hist_data->n_fields; j++) { -+ unsigned idx; ++ unsigned int idx; + hist_field = hist_data->fields[j]; + if (hist_field->flags & HIST_FIELD_FL_VAR_ONLY) @@ -458,16 +534,14 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + idx = k++; + test_name = hist_field_name(hist_field, 0); -+ - if (test_name == NULL) - continue; + if (strcmp(field_name, test_name) == 0) { - sort_key->field_idx = j; + sort_key->field_idx = idx; descending = is_descending(field_str); if (descending < 0) { ret = descending; -@@ -832,6 +997,7 @@ static int create_sort_keys(struct hist_ +@@ -820,6 +1050,7 @@ static int create_sort_keys(struct hist_ break; } } @@ -475,7 +549,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> hist_data->n_sort_keys = i; out: return ret; -@@ -872,12 +1038,19 @@ static int create_tracing_map_fields(str +@@ -860,12 +1091,19 @@ static int create_tracing_map_fields(str idx = tracing_map_add_key_field(map, hist_field->offset, cmp_fn); @@ -497,7 +571,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } return 0; -@@ -901,7 +1074,8 @@ static bool need_tracing_map_ops(struct +@@ -889,7 +1127,8 @@ static bool need_tracing_map_ops(struct static struct hist_trigger_data * create_hist_data(unsigned int map_bits, struct hist_trigger_attrs *attrs, @@ -507,7 +581,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> { const struct tracing_map_ops *map_ops = NULL; struct hist_trigger_data *hist_data; -@@ -912,6 +1086,7 @@ create_hist_data(unsigned int map_bits, +@@ -900,6 +1139,7 @@ create_hist_data(unsigned int map_bits, return ERR_PTR(-ENOMEM); hist_data->attrs = attrs; @@ -515,7 +589,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ret = create_hist_fields(hist_data, file); if (ret) -@@ -958,14 +1133,29 @@ static void hist_trigger_elt_update(stru +@@ -946,14 +1186,29 @@ static void hist_trigger_elt_update(stru struct ring_buffer_event *rbe) { struct hist_field *hist_field; @@ -547,7 +621,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } static inline void add_to_key(char *compound_key, void *key, -@@ -1140,6 +1330,9 @@ hist_trigger_entry_print(struct seq_file +@@ -1128,6 +1383,9 @@ hist_trigger_entry_print(struct seq_file for (i = 1; i < hist_data->n_vals; i++) { field_name = hist_field_name(hist_data->fields[i], 0); @@ -557,7 +631,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) { seq_printf(m, " %s: %10llx", field_name, tracing_map_read_sum(elt, i)); -@@ -1263,6 +1456,9 @@ static void hist_field_print(struct seq_ +@@ -1251,6 +1509,9 @@ static void hist_field_print(struct seq_ { const char *field_name = hist_field_name(hist_field, 0); @@ -567,7 +641,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP) seq_puts(m, "$common_timestamp"); else if (field_name) -@@ -1281,7 +1477,8 @@ static int event_hist_trigger_print(stru +@@ -1269,7 +1530,8 @@ static int event_hist_trigger_print(stru struct event_trigger_data *data) { struct hist_trigger_data *hist_data = data->private_data; @@ -577,7 +651,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> unsigned int i; seq_puts(m, "hist:"); -@@ -1292,25 +1489,47 @@ static int event_hist_trigger_print(stru +@@ -1280,25 +1542,47 @@ static int event_hist_trigger_print(stru seq_puts(m, "keys="); for_each_hist_key_field(i, hist_data) { @@ -629,7 +703,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } } -@@ -1318,7 +1537,10 @@ static int event_hist_trigger_print(stru +@@ -1306,7 +1590,10 @@ static int event_hist_trigger_print(stru for (i = 0; i < hist_data->n_sort_keys; i++) { struct tracing_map_sort_key *sort_key; @@ -641,7 +715,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> sort_key = &hist_data->sort_keys[i]; idx = sort_key->field_idx; -@@ -1331,8 +1553,11 @@ static int event_hist_trigger_print(stru +@@ -1319,8 +1606,11 @@ static int event_hist_trigger_print(stru if (idx == HITCOUNT_IDX) seq_puts(m, "hitcount"); @@ -654,7 +728,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (sort_key->descending) seq_puts(m, ".descending"); -@@ -1656,12 +1881,16 @@ static int event_hist_trigger_func(struc +@@ -1644,12 +1934,16 @@ static int event_hist_trigger_func(struc struct hist_trigger_attrs *attrs; struct event_trigger_ops *trigger_ops; struct hist_trigger_data *hist_data; @@ -671,7 +745,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> /* separate the trigger from the filter (k:v [if filter]) */ trigger = strsep(¶m, " \t"); if (!trigger) -@@ -1674,7 +1903,7 @@ static int event_hist_trigger_func(struc +@@ -1662,7 +1956,7 @@ static int event_hist_trigger_func(struc if (attrs->map_bits) hist_trigger_bits = attrs->map_bits; @@ -680,7 +754,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (IS_ERR(hist_data)) { destroy_hist_trigger_attrs(attrs); return PTR_ERR(hist_data); -@@ -1703,7 +1932,7 @@ static int event_hist_trigger_func(struc +@@ -1691,7 +1985,7 @@ static int event_hist_trigger_func(struc goto out_free; } diff --git a/patches/0017-tracing-Account-for-variables-in-named-trigger-compa.patch b/patches/0019-tracing-Account-for-variables-in-named-trigger-compa.patch index b55236edd88c..8e717ed18ce7 100644 --- a/patches/0017-tracing-Account-for-variables-in-named-trigger-compa.patch +++ b/patches/0019-tracing-Account-for-variables-in-named-trigger-compa.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:18 -0500 -Subject: [PATCH 17/32] tracing: Account for variables in named trigger +Date: Tue, 5 Sep 2017 16:57:31 -0500 +Subject: [PATCH 19/40] tracing: Account for variables in named trigger compatibility Named triggers must also have the same set of variables in order to be @@ -18,7 +18,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -1545,7 +1545,7 @@ static int event_hist_trigger_print(stru +@@ -1598,7 +1598,7 @@ static int event_hist_trigger_print(stru sort_key = &hist_data->sort_keys[i]; idx = sort_key->field_idx; @@ -27,7 +27,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return -EINVAL; if (i > 0) -@@ -1733,6 +1733,12 @@ static bool hist_trigger_match(struct ev +@@ -1786,6 +1786,12 @@ static bool hist_trigger_match(struct ev return false; if (key_field->is_signed != key_field_test->is_signed) return false; diff --git a/patches/0018-tracing-Add-simple-expression-support-to-hist-trigge.patch b/patches/0020-tracing-Add-simple-expression-support-to-hist-trigge.patch index dd2ae233312b..56b2385fe37f 100644 --- a/patches/0018-tracing-Add-simple-expression-support-to-hist-trigge.patch +++ b/patches/0020-tracing-Add-simple-expression-support-to-hist-trigge.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:19 -0500 -Subject: [PATCH 18/32] tracing: Add simple expression support to hist triggers +Date: Tue, 5 Sep 2017 16:57:32 -0500 +Subject: [PATCH 20/40] tracing: Add simple expression support to hist triggers Add support for simple addition, subtraction, and unary expressions (-(expr) and expr, where expr = b-a, a+b, a+b+c) to hist triggers, in @@ -16,8 +16,8 @@ parsing. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - kernel/trace/trace_events_hist.c | 457 +++++++++++++++++++++++++++++++++------ - 1 file changed, 390 insertions(+), 67 deletions(-) + kernel/trace/trace_events_hist.c | 556 ++++++++++++++++++++++++++++++++------- + 1 file changed, 460 insertions(+), 96 deletions(-) --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -93,8 +93,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + HIST_FIELD_FL_EXPR = 16384, }; - struct hist_trigger_attrs { -@@ -210,6 +255,8 @@ static const char *hist_field_name(struc + struct var_defs { +@@ -218,6 +263,8 @@ static const char *hist_field_name(struc field_name = hist_field_name(field->operands[0], ++level); else if (field->flags & HIST_FIELD_FL_TIMESTAMP) field_name = "$common_timestamp"; @@ -103,15 +103,41 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (field_name == NULL) field_name = ""; -@@ -444,6 +491,73 @@ static const struct tracing_map_ops hist +@@ -441,6 +488,115 @@ static const struct tracing_map_ops hist .elt_init = hist_trigger_elt_comm_init, }; ++static const char *get_hist_field_flags(struct hist_field *hist_field) ++{ ++ const char *flags_str = NULL; ++ ++ if (hist_field->flags & HIST_FIELD_FL_HEX) ++ flags_str = "hex"; ++ else if (hist_field->flags & HIST_FIELD_FL_SYM) ++ flags_str = "sym"; ++ else if (hist_field->flags & HIST_FIELD_FL_SYM_OFFSET) ++ flags_str = "sym-offset"; ++ else if (hist_field->flags & HIST_FIELD_FL_EXECNAME) ++ flags_str = "execname"; ++ else if (hist_field->flags & HIST_FIELD_FL_SYSCALL) ++ flags_str = "syscall"; ++ else if (hist_field->flags & HIST_FIELD_FL_LOG2) ++ flags_str = "log2"; ++ else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP_USECS) ++ flags_str = "usecs"; ++ ++ return flags_str; ++} ++ +static char *expr_str(struct hist_field *field, unsigned int level) +{ -+ char *expr = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); ++ char *expr; + -+ if (!expr || level > 1) ++ if (level > 1) ++ return NULL; ++ ++ expr = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); ++ if (!expr) + return NULL; + + if (field->operator == FIELD_OP_UNARY_MINUS) { @@ -130,6 +156,14 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + } + + strcat(expr, hist_field_name(field->operands[0], 0)); ++ if (field->operands[0]->flags) { ++ const char *flags_str = get_hist_field_flags(field->operands[0]); ++ ++ if (flags_str) { ++ strcat(expr, "."); ++ strcat(expr, flags_str); ++ } ++ } + + switch (field->operator) { + case FIELD_OP_MINUS: @@ -144,6 +178,14 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + } + + strcat(expr, hist_field_name(field->operands[1], 0)); ++ if (field->operands[1]->flags) { ++ const char *flags_str = get_hist_field_flags(field->operands[1]); ++ ++ if (flags_str) { ++ strcat(expr, "."); ++ strcat(expr, flags_str); ++ } ++ } + + return expr; +} @@ -177,15 +219,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static void destroy_hist_field(struct hist_field *hist_field, unsigned int level) { -@@ -459,6 +573,7 @@ static void destroy_hist_field(struct hi - destroy_hist_field(hist_field->operands[i], ++level); +@@ -456,6 +612,7 @@ static void destroy_hist_field(struct hi + destroy_hist_field(hist_field->operands[i], level + 1); kfree(hist_field->var.name); + kfree(hist_field->name); kfree(hist_field); } -@@ -479,6 +594,9 @@ static struct hist_field *create_hist_fi +@@ -476,6 +633,9 @@ static struct hist_field *create_hist_fi hist_field->hist_data = hist_data; @@ -195,36 +237,70 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (flags & HIST_FIELD_FL_HITCOUNT) { hist_field->fn = hist_field_counter; goto out; -@@ -551,6 +669,247 @@ static void destroy_hist_fields(struct h +@@ -548,6 +708,287 @@ static void destroy_hist_fields(struct h } } ++static char *field_name_from_var(struct hist_trigger_data *hist_data, ++ char *var_name) ++{ ++ char *name, *field; ++ unsigned int i; ++ ++ for (i = 0; i < hist_data->attrs->var_defs.n_vars; i++) { ++ name = hist_data->attrs->var_defs.name[i]; ++ ++ if (strcmp(var_name, name) == 0) { ++ field = hist_data->attrs->var_defs.expr[i]; ++ if (contains_operator(field)) ++ continue; ++ return field; ++ } ++ } ++ ++ return NULL; ++} ++ ++static char *local_field_var_ref(struct hist_trigger_data *hist_data, ++ char *var_name) ++{ ++ var_name++; ++ ++ return field_name_from_var(hist_data, var_name); ++} ++ +static struct ftrace_event_field * +parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, + char *field_str, unsigned long *flags) +{ + struct ftrace_event_field *field = NULL; -+ char *field_name; ++ char *field_name, *modifier, *str; + -+ field_name = strsep(&field_str, "."); -+ if (field_str) { -+ if (strcmp(field_str, "hex") == 0) ++ modifier = str = kstrdup(field_str, GFP_KERNEL); ++ if (!modifier) ++ return ERR_PTR(-ENOMEM); ++ ++ field_name = strsep(&modifier, "."); ++ if (modifier) { ++ if (strcmp(modifier, "hex") == 0) + *flags |= HIST_FIELD_FL_HEX; -+ else if (strcmp(field_str, "sym") == 0) ++ else if (strcmp(modifier, "sym") == 0) + *flags |= HIST_FIELD_FL_SYM; -+ else if (strcmp(field_str, "sym-offset") == 0) ++ else if (strcmp(modifier, "sym-offset") == 0) + *flags |= HIST_FIELD_FL_SYM_OFFSET; -+ else if ((strcmp(field_str, "execname") == 0) && ++ else if ((strcmp(modifier, "execname") == 0) && + (strcmp(field_name, "common_pid") == 0)) + *flags |= HIST_FIELD_FL_EXECNAME; -+ else if (strcmp(field_str, "syscall") == 0) ++ else if (strcmp(modifier, "syscall") == 0) + *flags |= HIST_FIELD_FL_SYSCALL; -+ else if (strcmp(field_str, "log2") == 0) ++ else if (strcmp(modifier, "log2") == 0) + *flags |= HIST_FIELD_FL_LOG2; -+ else if (strcmp(field_str, "usecs") == 0) ++ else if (strcmp(modifier, "usecs") == 0) + *flags |= HIST_FIELD_FL_TIMESTAMP_USECS; -+ else -+ return ERR_PTR(-EINVAL); ++ else { ++ field = ERR_PTR(-EINVAL); ++ goto out; ++ } + } + + if (strcmp(field_name, "$common_timestamp") == 0) { @@ -234,9 +310,13 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + hist_data->attrs->ts_in_usecs = true; + } else { + field = trace_find_event_field(file->event_call, field_name); -+ if (!field) -+ return ERR_PTR(-EINVAL); ++ if (!field || !field->size) { ++ field = ERR_PTR(-EINVAL); ++ goto out; ++ } + } ++ out: ++ kfree(str); + + return field; +} @@ -245,10 +325,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + struct trace_event_file *file, char *str, + unsigned long *flags, char *var_name) +{ ++ char *s; + struct ftrace_event_field *field = NULL; + struct hist_field *hist_field = NULL; + int ret = 0; + ++ s = local_field_var_ref(hist_data, str); ++ if (s) ++ str = s; ++ + field = parse_field(hist_data, file, str, flags); + if (IS_ERR(field)) { + ret = PTR_ERR(field); @@ -278,7 +363,6 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +{ + struct hist_field *operand1, *expr = NULL; + unsigned long operand_flags; -+ char *operand1_str; + int ret = 0; + char *s; + @@ -307,8 +391,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + goto free; + } + -+ operand1_str = strsep(&str, "("); -+ if (!operand1_str) ++ strsep(&str, "("); ++ if (!str) + goto free; + + flags |= HIST_FIELD_FL_EXPR; @@ -325,16 +409,6 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + goto free; + } + -+ if (operand1 == NULL) { -+ operand_flags = 0; -+ operand1 = parse_atom(hist_data, file, operand1_str, -+ &operand_flags, NULL); -+ if (IS_ERR(operand1)) { -+ ret = PTR_ERR(operand1); -+ goto free; -+ } -+ } -+ + expr->fn = hist_field_unary_minus; + expr->operands[0] = operand1; + expr->operator = FIELD_OP_UNARY_MINUS; @@ -345,6 +419,19 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return ERR_PTR(ret); +} + ++static int check_expr_operands(struct hist_field *operand1, ++ struct hist_field *operand2) ++{ ++ unsigned long operand1_flags = operand1->flags; ++ unsigned long operand2_flags = operand2->flags; ++ ++ if ((operand1_flags & HIST_FIELD_FL_TIMESTAMP_USECS) != ++ (operand2_flags & HIST_FIELD_FL_TIMESTAMP_USECS)) ++ return -EINVAL; ++ ++ return 0; ++} ++ +static struct hist_field *parse_expr(struct hist_trigger_data *hist_data, + struct trace_event_file *file, + char *str, unsigned long flags, @@ -356,11 +443,12 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + char *sep, *operand1_str; + + if (level > 2) -+ return NULL; ++ return ERR_PTR(-EINVAL); + + field_op = contains_operator(str); ++ + if (field_op == FIELD_OP_NONE) -+ return NULL; ++ return parse_atom(hist_data, file, str, &flags, var_name); + + if (field_op == FIELD_OP_UNARY_MINUS) + return parse_unary(hist_data, file, str, flags, var_name, ++level); @@ -397,16 +485,10 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + operand2 = NULL; + goto free; + } -+ if (!operand2) { -+ operand_flags = 0; -+ operand2 = parse_atom(hist_data, file, str, -+ &operand_flags, NULL); -+ if (IS_ERR(operand2)) { -+ ret = PTR_ERR(operand2); -+ operand2 = NULL; -+ goto free; -+ } -+ } ++ ++ ret = check_expr_operands(operand1, operand2); ++ if (ret) ++ goto free; + + flags |= HIST_FIELD_FL_EXPR; + expr = create_hist_field(hist_data, NULL, flags, var_name); @@ -443,22 +525,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static int create_hitcount_val(struct hist_trigger_data *hist_data) { hist_data->fields[HITCOUNT_IDX] = -@@ -609,9 +968,9 @@ static int create_val_field(struct hist_ - struct trace_event_file *file, - char *field_str, bool var_only) +@@ -607,41 +1048,21 @@ static int __create_val_field(struct his + char *var_name, char *field_str, + unsigned long flags) { - struct ftrace_event_field *field = NULL; -- char *field_name, *var_name; +- char *field_name; + struct hist_field *hist_field; - unsigned long flags = 0; -+ char *var_name; int ret = 0; - if (WARN_ON(!var_only && val_idx >= TRACING_MAP_VALS_MAX)) -@@ -642,37 +1001,27 @@ static int create_val_field(struct hist_ - goto out; - } - - field_name = strsep(&field_str, "."); - if (field_str) { - if (strcmp(field_str, "hex") == 0) @@ -467,35 +542,30 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> - ret = -EINVAL; - goto out; - } -+ hist_field = parse_expr(hist_data, file, field_str, flags, var_name, 0); -+ if (IS_ERR(hist_field)) { -+ ret = PTR_ERR(hist_field); -+ goto out; - } - +- } +- - if (strcmp(field_name, "$common_timestamp") == 0) { - flags |= HIST_FIELD_FL_TIMESTAMP; - hist_data->enable_timestamps = true; - } else { - field = trace_find_event_field(file->event_call, field_name); -- if (!field) { +- if (!field || !field->size) { - ret = -EINVAL; -+ if (!hist_field) { -+ hist_field = parse_atom(hist_data, file, field_str, -+ &flags, var_name); -+ if (IS_ERR(hist_field)) { -+ ret = PTR_ERR(hist_field); - goto out; - } - } - +- goto out; +- } +- } +- - hist_data->fields[val_idx] = create_hist_field(hist_data, field, flags, var_name); - if (!hist_data->fields[val_idx]) { - ret = -ENOMEM; -- goto out; -- } -+ hist_data->fields[val_idx] = hist_field; ++ hist_field = parse_expr(hist_data, file, field_str, flags, var_name, 0); ++ if (IS_ERR(hist_field)) { ++ ret = PTR_ERR(hist_field); + goto out; + } ++ hist_data->fields[val_idx] = hist_field; ++ ++hist_data->n_vals; ++hist_data->n_fields; @@ -504,7 +574,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> hist_data->n_var_only++; if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX)) -@@ -726,8 +1075,8 @@ static int create_key_field(struct hist_ +@@ -731,8 +1152,8 @@ static int create_key_field(struct hist_ struct trace_event_file *file, char *field_str) { @@ -513,10 +583,10 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + unsigned long flags = 0; unsigned int key_size; - char *var_name; -@@ -754,60 +1103,33 @@ static int create_key_field(struct hist_ + int ret = 0; +@@ -747,60 +1168,24 @@ static int create_key_field(struct hist_ key_size = sizeof(unsigned long) * HIST_STACKTRACE_DEPTH; - hist_field = create_hist_field(hist_data, NULL, flags, var_name); + hist_field = create_hist_field(hist_data, NULL, flags, NULL); } else { - char *field_name = strsep(&field_str, "."); - @@ -541,7 +611,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> - goto out; - } + hist_field = parse_expr(hist_data, file, field_str, flags, -+ var_name, 0); ++ NULL, 0); + if (IS_ERR(hist_field)) { + ret = PTR_ERR(hist_field); + goto out; @@ -555,32 +625,26 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> - key_size = sizeof(u64); - } else { - field = trace_find_event_field(file->event_call, field_name); -- if (!field) { +- if (!field || !field->size) { - ret = -EINVAL; -+ if (!hist_field) { -+ hist_field = parse_atom(hist_data, file, field_str, -+ &flags, var_name); -+ if (IS_ERR(hist_field)) { -+ ret = PTR_ERR(hist_field); - goto out; - } +- goto out; +- } - - if (is_string_field(field)) - key_size = MAX_FILTER_STR_VAL; - else - key_size = field->size; - } -- } +- } ++ key_size = hist_field->size; + } -- hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags, var_name); +- hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags, NULL); - if (!hist_data->fields[key_idx]) { - ret = -ENOMEM; - goto out; -+ key_size = hist_field->size; - } - +- } + hist_data->fields[key_idx] = hist_field; -+ + key_size = ALIGN(key_size, sizeof(u64)); hist_data->fields[key_idx]->size = key_size; hist_data->fields[key_idx]->offset = key_offset; @@ -590,7 +654,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (hist_data->key_size > HIST_KEY_SIZE_MAX) { ret = -EINVAL; goto out; -@@ -1330,7 +1652,8 @@ hist_trigger_entry_print(struct seq_file +@@ -1383,7 +1768,8 @@ hist_trigger_entry_print(struct seq_file for (i = 1; i < hist_data->n_vals; i++) { field_name = hist_field_name(hist_data->fields[i], 0); @@ -600,3 +664,32 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> continue; if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) { +@@ -1483,28 +1869,6 @@ const struct file_operations event_hist_ + .release = single_release, + }; + +-static const char *get_hist_field_flags(struct hist_field *hist_field) +-{ +- const char *flags_str = NULL; +- +- if (hist_field->flags & HIST_FIELD_FL_HEX) +- flags_str = "hex"; +- else if (hist_field->flags & HIST_FIELD_FL_SYM) +- flags_str = "sym"; +- else if (hist_field->flags & HIST_FIELD_FL_SYM_OFFSET) +- flags_str = "sym-offset"; +- else if (hist_field->flags & HIST_FIELD_FL_EXECNAME) +- flags_str = "execname"; +- else if (hist_field->flags & HIST_FIELD_FL_SYSCALL) +- flags_str = "syscall"; +- else if (hist_field->flags & HIST_FIELD_FL_LOG2) +- flags_str = "log2"; +- else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP_USECS) +- flags_str = "usecs"; +- +- return flags_str; +-} +- + static void hist_field_print(struct seq_file *m, struct hist_field *hist_field) + { + const char *field_name = hist_field_name(hist_field, 0); diff --git a/patches/0020-tracing-Add-support-for-dynamic-tracepoints.patch b/patches/0020-tracing-Add-support-for-dynamic-tracepoints.patch deleted file mode 100644 index d7bc190643da..000000000000 --- a/patches/0020-tracing-Add-support-for-dynamic-tracepoints.patch +++ /dev/null @@ -1,195 +0,0 @@ -From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:21 -0500 -Subject: [PATCH 20/32] tracing: Add support for dynamic tracepoints - -The tracepoint infrastructure assumes statically-defined tracepoints -and uses static_keys for tracepoint enablement. In order to define -tracepoints on the fly, we need to have a dynamic counterpart. - -Add a dynamic_tracepoint_probe_register() and a dynamic param onto -tracepoint_probe_unregister() for this purpose. - -Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> -Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ---- - include/linux/tracepoint.h | 11 +++++++---- - kernel/trace/trace_events.c | 4 ++-- - kernel/tracepoint.c | 42 ++++++++++++++++++++++++++++++------------ - 3 files changed, 39 insertions(+), 18 deletions(-) - ---- a/include/linux/tracepoint.h -+++ b/include/linux/tracepoint.h -@@ -37,9 +37,12 @@ extern int - tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data); - extern int - tracepoint_probe_register_prio(struct tracepoint *tp, void *probe, void *data, -- int prio); -+ int prio, bool dynamic); -+extern int dynamic_tracepoint_probe_register(struct tracepoint *tp, -+ void *probe, void *data); - extern int --tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data); -+tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data, -+ bool dynamic); - extern void - for_each_kernel_tracepoint(void (*fct)(struct tracepoint *tp, void *priv), - void *priv); -@@ -206,13 +209,13 @@ extern void syscall_unregfunc(void); - int prio) \ - { \ - return tracepoint_probe_register_prio(&__tracepoint_##name, \ -- (void *)probe, data, prio); \ -+ (void *)probe, data, prio, false); \ - } \ - static inline int \ - unregister_trace_##name(void (*probe)(data_proto), void *data) \ - { \ - return tracepoint_probe_unregister(&__tracepoint_##name,\ -- (void *)probe, data); \ -+ (void *)probe, data, false); \ - } \ - static inline void \ - check_trace_callback_type_##name(void (*cb)(data_proto)) \ ---- a/kernel/trace/trace_events.c -+++ b/kernel/trace/trace_events.c -@@ -297,7 +297,7 @@ int trace_event_reg(struct trace_event_c - case TRACE_REG_UNREGISTER: - tracepoint_probe_unregister(call->tp, - call->class->probe, -- file); -+ file, false); - return 0; - - #ifdef CONFIG_PERF_EVENTS -@@ -308,7 +308,7 @@ int trace_event_reg(struct trace_event_c - case TRACE_REG_PERF_UNREGISTER: - tracepoint_probe_unregister(call->tp, - call->class->perf_probe, -- call); -+ call, false); - return 0; - case TRACE_REG_PERF_OPEN: - case TRACE_REG_PERF_CLOSE: ---- a/kernel/tracepoint.c -+++ b/kernel/tracepoint.c -@@ -192,12 +192,15 @@ static void *func_remove(struct tracepoi - * Add the probe function to a tracepoint. - */ - static int tracepoint_add_func(struct tracepoint *tp, -- struct tracepoint_func *func, int prio) -+ struct tracepoint_func *func, int prio, -+ bool dynamic) - { - struct tracepoint_func *old, *tp_funcs; - int ret; - -- if (tp->regfunc && !static_key_enabled(&tp->key)) { -+ if (tp->regfunc && -+ ((dynamic && !(atomic_read(&tp->key.enabled) > 0)) || -+ !static_key_enabled(&tp->key))) { - ret = tp->regfunc(); - if (ret < 0) - return ret; -@@ -219,7 +222,9 @@ static int tracepoint_add_func(struct tr - * is used. - */ - rcu_assign_pointer(tp->funcs, tp_funcs); -- if (!static_key_enabled(&tp->key)) -+ if (dynamic && !(atomic_read(&tp->key.enabled) > 0)) -+ atomic_inc(&tp->key.enabled); -+ else if (!dynamic && !static_key_enabled(&tp->key)) - static_key_slow_inc(&tp->key); - release_probes(old); - return 0; -@@ -232,7 +237,7 @@ static int tracepoint_add_func(struct tr - * by preempt_disable around the call site. - */ - static int tracepoint_remove_func(struct tracepoint *tp, -- struct tracepoint_func *func) -+ struct tracepoint_func *func, bool dynamic) - { - struct tracepoint_func *old, *tp_funcs; - -@@ -246,10 +251,14 @@ static int tracepoint_remove_func(struct - - if (!tp_funcs) { - /* Removed last function */ -- if (tp->unregfunc && static_key_enabled(&tp->key)) -+ if (tp->unregfunc && -+ ((dynamic && (atomic_read(&tp->key.enabled) > 0)) || -+ static_key_enabled(&tp->key))) - tp->unregfunc(); - -- if (static_key_enabled(&tp->key)) -+ if (dynamic && (atomic_read(&tp->key.enabled) > 0)) -+ atomic_dec(&tp->key.enabled); -+ else if (!dynamic && static_key_enabled(&tp->key)) - static_key_slow_dec(&tp->key); - } - rcu_assign_pointer(tp->funcs, tp_funcs); -@@ -258,7 +267,7 @@ static int tracepoint_remove_func(struct - } - - /** -- * tracepoint_probe_register - Connect a probe to a tracepoint -+ * tracepoint_probe_register_prio - Connect a probe to a tracepoint - * @tp: tracepoint - * @probe: probe handler - * @data: tracepoint data -@@ -271,7 +280,7 @@ static int tracepoint_remove_func(struct - * within module exit functions. - */ - int tracepoint_probe_register_prio(struct tracepoint *tp, void *probe, -- void *data, int prio) -+ void *data, int prio, bool dynamic) - { - struct tracepoint_func tp_func; - int ret; -@@ -280,7 +289,7 @@ int tracepoint_probe_register_prio(struc - tp_func.func = probe; - tp_func.data = data; - tp_func.prio = prio; -- ret = tracepoint_add_func(tp, &tp_func, prio); -+ ret = tracepoint_add_func(tp, &tp_func, prio, dynamic); - mutex_unlock(&tracepoints_mutex); - return ret; - } -@@ -301,10 +310,18 @@ EXPORT_SYMBOL_GPL(tracepoint_probe_regis - */ - int tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data) - { -- return tracepoint_probe_register_prio(tp, probe, data, TRACEPOINT_DEFAULT_PRIO); -+ return tracepoint_probe_register_prio(tp, probe, data, TRACEPOINT_DEFAULT_PRIO, false); - } - EXPORT_SYMBOL_GPL(tracepoint_probe_register); - -+int dynamic_tracepoint_probe_register(struct tracepoint *tp, void *probe, -+ void *data) -+{ -+ return tracepoint_probe_register_prio(tp, probe, data, -+ TRACEPOINT_DEFAULT_PRIO, true); -+} -+EXPORT_SYMBOL_GPL(dynamic_tracepoint_probe_register); -+ - /** - * tracepoint_probe_unregister - Disconnect a probe from a tracepoint - * @tp: tracepoint -@@ -313,7 +330,8 @@ EXPORT_SYMBOL_GPL(tracepoint_probe_regis - * - * Returns 0 if ok, error value on error. - */ --int tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data) -+int tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data, -+ bool dynamic) - { - struct tracepoint_func tp_func; - int ret; -@@ -321,7 +339,7 @@ int tracepoint_probe_unregister(struct t - mutex_lock(&tracepoints_mutex); - tp_func.func = probe; - tp_func.data = data; -- ret = tracepoint_remove_func(tp, &tp_func); -+ ret = tracepoint_remove_func(tp, &tp_func, dynamic); - mutex_unlock(&tracepoints_mutex); - return ret; - } diff --git a/patches/0021-tracing-Generalize-per-element-hist-trigger-data.patch b/patches/0021-tracing-Generalize-per-element-hist-trigger-data.patch new file mode 100644 index 000000000000..1ab8f84aefed --- /dev/null +++ b/patches/0021-tracing-Generalize-per-element-hist-trigger-data.patch @@ -0,0 +1,143 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Tue, 5 Sep 2017 16:57:33 -0500 +Subject: [PATCH 21/40] tracing: Generalize per-element hist trigger data + +Up until now, hist triggers only needed per-element support for saving +'comm' data, which was saved directly as a private data pointer. + +In anticipation of the need to save other data besides 'comm', add a +new hist_elt_data struct for the purpose, and switch the current +'comm'-related code over to that. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace_events_hist.c | 65 +++++++++++++++++++-------------------- + 1 file changed, 32 insertions(+), 33 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -249,6 +249,10 @@ static u64 hist_field_timestamp(struct h + return ts; + } + ++struct hist_elt_data { ++ char *comm; ++}; ++ + static const char *hist_field_name(struct hist_field *field, + unsigned int level) + { +@@ -447,26 +451,36 @@ static inline void save_comm(char *comm, + memcpy(comm, task->comm, TASK_COMM_LEN); + } + +-static void hist_trigger_elt_comm_free(struct tracing_map_elt *elt) ++static void hist_trigger_elt_data_free(struct tracing_map_elt *elt) + { +- kfree((char *)elt->private_data); ++ struct hist_elt_data *private_data = elt->private_data; ++ ++ kfree(private_data->comm); ++ kfree(private_data); + } + +-static int hist_trigger_elt_comm_alloc(struct tracing_map_elt *elt) ++static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt) + { + struct hist_trigger_data *hist_data = elt->map->private_data; ++ unsigned int size = TASK_COMM_LEN + 1; ++ struct hist_elt_data *elt_data; + struct hist_field *key_field; + unsigned int i; + ++ elt->private_data = elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL); ++ if (!elt_data) ++ return -ENOMEM; ++ + for_each_hist_key_field(i, hist_data) { + key_field = hist_data->fields[i]; + + if (key_field->flags & HIST_FIELD_FL_EXECNAME) { +- unsigned int size = TASK_COMM_LEN + 1; +- +- elt->private_data = kzalloc(size, GFP_KERNEL); +- if (!elt->private_data) ++ elt_data->comm = kzalloc(size, GFP_KERNEL); ++ if (!elt_data->comm) { ++ kfree(elt_data); ++ elt->private_data = NULL; + return -ENOMEM; ++ } + break; + } + } +@@ -474,18 +488,18 @@ static int hist_trigger_elt_comm_alloc(s + return 0; + } + +-static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt) ++static void hist_trigger_elt_data_init(struct tracing_map_elt *elt) + { +- char *comm = elt->private_data; ++ struct hist_elt_data *private_data = elt->private_data; + +- if (comm) +- save_comm(comm, current); ++ if (private_data->comm) ++ save_comm(private_data->comm, current); + } + +-static const struct tracing_map_ops hist_trigger_elt_comm_ops = { +- .elt_alloc = hist_trigger_elt_comm_alloc, +- .elt_free = hist_trigger_elt_comm_free, +- .elt_init = hist_trigger_elt_comm_init, ++static const struct tracing_map_ops hist_trigger_elt_data_ops = { ++ .elt_alloc = hist_trigger_elt_data_alloc, ++ .elt_free = hist_trigger_elt_data_free, ++ .elt_init = hist_trigger_elt_data_init, + }; + + static const char *get_hist_field_flags(struct hist_field *hist_field) +@@ -1494,21 +1508,6 @@ static int create_tracing_map_fields(str + return 0; + } + +-static bool need_tracing_map_ops(struct hist_trigger_data *hist_data) +-{ +- struct hist_field *key_field; +- unsigned int i; +- +- for_each_hist_key_field(i, hist_data) { +- key_field = hist_data->fields[i]; +- +- if (key_field->flags & HIST_FIELD_FL_EXECNAME) +- return true; +- } +- +- return false; +-} +- + static struct hist_trigger_data * + create_hist_data(unsigned int map_bits, + struct hist_trigger_attrs *attrs, +@@ -1534,8 +1533,7 @@ create_hist_data(unsigned int map_bits, + if (ret) + goto free; + +- if (need_tracing_map_ops(hist_data)) +- map_ops = &hist_trigger_elt_comm_ops; ++ map_ops = &hist_trigger_elt_data_ops; + + hist_data->map = tracing_map_create(map_bits, hist_data->key_size, + map_ops, hist_data); +@@ -1724,7 +1722,8 @@ hist_trigger_entry_print(struct seq_file + seq_printf(m, "%s: [%llx] %-55s", field_name, + uval, str); + } else if (key_field->flags & HIST_FIELD_FL_EXECNAME) { +- char *comm = elt->private_data; ++ struct hist_elt_data *elt_data = elt->private_data; ++ char *comm = elt_data->comm; + + uval = *(u64 *)(key + key_field->offset); + seq_printf(m, "%s: %-16s[%10llu]", field_name, diff --git a/patches/0022-tracing-Pass-tracing_map_elt-to-hist_field-accessor-.patch b/patches/0022-tracing-Pass-tracing_map_elt-to-hist_field-accessor-.patch new file mode 100644 index 000000000000..faa6dd2a0da7 --- /dev/null +++ b/patches/0022-tracing-Pass-tracing_map_elt-to-hist_field-accessor-.patch @@ -0,0 +1,221 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Tue, 5 Sep 2017 16:57:34 -0500 +Subject: [PATCH 22/40] tracing: Pass tracing_map_elt to hist_field accessor + functions + +Some accessor functions, such as for variable references, require +access to a corrsponding tracing_map_elt. + +Add a tracing_map_elt param to the function signature and update the +accessor functions accordingly. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace_events_hist.c | 91 ++++++++++++++++++++++++--------------- + 1 file changed, 57 insertions(+), 34 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -26,8 +26,10 @@ + + struct hist_field; + +-typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event, +- struct ring_buffer_event *rbe); ++typedef u64 (*hist_field_fn_t) (struct hist_field *field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event); + + #define HIST_FIELD_OPERANDS_MAX 2 + #define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX) +@@ -59,28 +61,36 @@ struct hist_field { + char *name; + }; + +-static u64 hist_field_none(struct hist_field *field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_none(struct hist_field *field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + return 0; + } + +-static u64 hist_field_counter(struct hist_field *field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_counter(struct hist_field *field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + return 1; + } + +-static u64 hist_field_string(struct hist_field *hist_field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_string(struct hist_field *hist_field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + char *addr = (char *)(event + hist_field->field->offset); + + return (u64)(unsigned long)addr; + } + +-static u64 hist_field_dynstring(struct hist_field *hist_field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_dynstring(struct hist_field *hist_field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + u32 str_item = *(u32 *)(event + hist_field->field->offset); + int str_loc = str_item & 0xffff; +@@ -89,54 +99,64 @@ static u64 hist_field_dynstring(struct h + return (u64)(unsigned long)addr; + } + +-static u64 hist_field_pstring(struct hist_field *hist_field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_pstring(struct hist_field *hist_field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + char **addr = (char **)(event + hist_field->field->offset); + + return (u64)(unsigned long)*addr; + } + +-static u64 hist_field_log2(struct hist_field *hist_field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_log2(struct hist_field *hist_field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + struct hist_field *operand = hist_field->operands[0]; + +- u64 val = operand->fn(operand, event, rbe); ++ u64 val = operand->fn(operand, elt, rbe, event); + + return (u64) ilog2(roundup_pow_of_two(val)); + } + +-static u64 hist_field_plus(struct hist_field *hist_field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_plus(struct hist_field *hist_field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + struct hist_field *operand1 = hist_field->operands[0]; + struct hist_field *operand2 = hist_field->operands[1]; + +- u64 val1 = operand1->fn(operand1, event, rbe); +- u64 val2 = operand2->fn(operand2, event, rbe); ++ u64 val1 = operand1->fn(operand1, elt, rbe, event); ++ u64 val2 = operand2->fn(operand2, elt, rbe, event); + + return val1 + val2; + } + +-static u64 hist_field_minus(struct hist_field *hist_field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_minus(struct hist_field *hist_field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + struct hist_field *operand1 = hist_field->operands[0]; + struct hist_field *operand2 = hist_field->operands[1]; + +- u64 val1 = operand1->fn(operand1, event, rbe); +- u64 val2 = operand2->fn(operand2, event, rbe); ++ u64 val1 = operand1->fn(operand1, elt, rbe, event); ++ u64 val2 = operand2->fn(operand2, elt, rbe, event); + + return val1 - val2; + } + +-static u64 hist_field_unary_minus(struct hist_field *hist_field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_unary_minus(struct hist_field *hist_field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + struct hist_field *operand = hist_field->operands[0]; + +- s64 sval = (s64)operand->fn(operand, event, rbe); ++ s64 sval = (s64)operand->fn(operand, elt, rbe, event); + u64 val = (u64)-sval; + + return val; +@@ -144,8 +164,9 @@ static u64 hist_field_unary_minus(struct + + #define DEFINE_HIST_FIELD_FN(type) \ + static u64 hist_field_##type(struct hist_field *hist_field, \ +- void *event, \ +- struct ring_buffer_event *rbe) \ ++ struct tracing_map_elt *elt, \ ++ struct ring_buffer_event *rbe, \ ++ void *event) \ + { \ + type *addr = (type *)(event + hist_field->field->offset); \ + \ +@@ -235,8 +256,10 @@ struct hist_trigger_data { + bool remove; + }; + +-static u64 hist_field_timestamp(struct hist_field *hist_field, void *event, +- struct ring_buffer_event *rbe) ++static u64 hist_field_timestamp(struct hist_field *hist_field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) + { + struct hist_trigger_data *hist_data = hist_field->hist_data; + struct trace_array *tr = hist_data->event_file->tr; +@@ -1574,7 +1597,7 @@ static void hist_trigger_elt_update(stru + + for_each_hist_val_field(i, hist_data) { + hist_field = hist_data->fields[i]; +- hist_val = hist_field->fn(hist_field, rbe, rec); ++ hist_val = hist_field->fn(hist_field, elt, rbe, rec); + if (hist_field->flags & HIST_FIELD_FL_VAR) { + var_idx = hist_field->var.idx; + tracing_map_set_var(elt, var_idx, hist_val); +@@ -1587,7 +1610,7 @@ static void hist_trigger_elt_update(stru + for_each_hist_key_field(i, hist_data) { + hist_field = hist_data->fields[i]; + if (hist_field->flags & HIST_FIELD_FL_VAR) { +- hist_val = hist_field->fn(hist_field, rbe, rec); ++ hist_val = hist_field->fn(hist_field, elt, rbe, rec); + var_idx = hist_field->var.idx; + tracing_map_set_var(elt, var_idx, hist_val); + } +@@ -1625,9 +1648,9 @@ static void event_hist_trigger(struct ev + bool use_compound_key = (hist_data->n_keys > 1); + unsigned long entries[HIST_STACKTRACE_DEPTH]; + char compound_key[HIST_KEY_SIZE_MAX]; ++ struct tracing_map_elt *elt = NULL; + struct stack_trace stacktrace; + struct hist_field *key_field; +- struct tracing_map_elt *elt; + u64 field_contents; + void *key = NULL; + unsigned int i; +@@ -1648,7 +1671,7 @@ static void event_hist_trigger(struct ev + + key = entries; + } else { +- field_contents = key_field->fn(key_field, rec, rbe); ++ field_contents = key_field->fn(key_field, elt, rbe, rec); + if (key_field->flags & HIST_FIELD_FL_STRING) { + key = (void *)(unsigned long)field_contents; + use_compound_key = true; diff --git a/patches/0023-tracing-Add-hist_field-type-field.patch b/patches/0023-tracing-Add-hist_field-type-field.patch new file mode 100644 index 000000000000..63bd58c3f92a --- /dev/null +++ b/patches/0023-tracing-Add-hist_field-type-field.patch @@ -0,0 +1,113 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Tue, 5 Sep 2017 16:57:35 -0500 +Subject: [PATCH 23/40] tracing: Add hist_field 'type' field + +Future support for synthetic events requires hist_field 'type' +information, so add a field for that. + +Also, make other hist_field attribute usage consistent (size, +is_signed, etc). + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace_events_hist.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -54,6 +54,7 @@ struct hist_field { + unsigned int size; + unsigned int offset; + unsigned int is_signed; ++ const char *type; + struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; + struct hist_trigger_data *hist_data; + struct hist_var var; +@@ -650,6 +651,7 @@ static void destroy_hist_field(struct hi + + kfree(hist_field->var.name); + kfree(hist_field->name); ++ kfree(hist_field->type); + + kfree(hist_field); + } +@@ -675,6 +677,10 @@ static struct hist_field *create_hist_fi + + if (flags & HIST_FIELD_FL_HITCOUNT) { + hist_field->fn = hist_field_counter; ++ hist_field->size = sizeof(u64); ++ hist_field->type = kstrdup("u64", GFP_KERNEL); ++ if (!hist_field->type) ++ goto free; + goto out; + } + +@@ -688,12 +694,18 @@ static struct hist_field *create_hist_fi + hist_field->fn = hist_field_log2; + hist_field->operands[0] = create_hist_field(hist_data, field, fl, NULL); + hist_field->size = hist_field->operands[0]->size; ++ hist_field->type = kstrdup(hist_field->operands[0]->type, GFP_KERNEL); ++ if (!hist_field->type) ++ goto free; + goto out; + } + + if (flags & HIST_FIELD_FL_TIMESTAMP) { + hist_field->fn = hist_field_timestamp; + hist_field->size = sizeof(u64); ++ hist_field->type = kstrdup("u64", GFP_KERNEL); ++ if (!hist_field->type) ++ goto free; + goto out; + } + +@@ -703,6 +715,11 @@ static struct hist_field *create_hist_fi + if (is_string_field(field)) { + flags |= HIST_FIELD_FL_STRING; + ++ hist_field->size = MAX_FILTER_STR_VAL; ++ hist_field->type = kstrdup(field->type, GFP_KERNEL); ++ if (!hist_field->type) ++ goto free; ++ + if (field->filter_type == FILTER_STATIC_STRING) + hist_field->fn = hist_field_string; + else if (field->filter_type == FILTER_DYN_STRING) +@@ -710,6 +727,12 @@ static struct hist_field *create_hist_fi + else + hist_field->fn = hist_field_pstring; + } else { ++ hist_field->size = field->size; ++ hist_field->is_signed = field->is_signed; ++ hist_field->type = kstrdup(field->type, GFP_KERNEL); ++ if (!hist_field->type) ++ goto free; ++ + hist_field->fn = select_value_fn(field->size, + field->is_signed); + if (!hist_field->fn) { +@@ -917,6 +940,11 @@ static struct hist_field *parse_unary(st + expr->operands[0] = operand1; + expr->operator = FIELD_OP_UNARY_MINUS; + expr->name = expr_str(expr, 0); ++ expr->type = kstrdup(operand1->type, GFP_KERNEL); ++ if (!expr->type) { ++ ret = -ENOMEM; ++ goto free; ++ } + + return expr; + free: +@@ -1005,6 +1033,11 @@ static struct hist_field *parse_expr(str + expr->operands[1] = operand2; + expr->operator = field_op; + expr->name = expr_str(expr, 0); ++ expr->type = kstrdup(operand1->type, GFP_KERNEL); ++ if (!expr->type) { ++ ret = -ENOMEM; ++ goto free; ++ } + + switch (field_op) { + case FIELD_OP_MINUS: diff --git a/patches/0023-tracing-Add-onmatch-hist-trigger-action-support.patch b/patches/0023-tracing-Add-onmatch-hist-trigger-action-support.patch deleted file mode 100644 index 8aa665e08a25..000000000000 --- a/patches/0023-tracing-Add-onmatch-hist-trigger-action-support.patch +++ /dev/null @@ -1,1268 +0,0 @@ -From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:24 -0500 -Subject: [PATCH 23/32] tracing: Add 'onmatch' hist trigger action support - -Add an 'onmatch(matching.event).<synthetic_event_name>(param list)' -hist trigger action which is invoked with the set of variables or -event fields named in the 'param list'. The result is the generation -of a synthetic event that consists of the values contained in those -variables and/or fields at the time the invoking event was hit. - -As an example the below defines a simple synthetic event using a -variable defined on the sched_wakeup_new event, and shows the event -definition with unresolved fields, since the sched_wakeup_new event -with the testpid variable hasn't been defined yet: - - # echo 'wakeup_new_test pid_t pid; int prio' >> \ - /sys/kernel/debug/tracing/synthetic_events - - # cat /sys/kernel/debug/tracing/synthetic_events - wakeup_new_test pid_t pid; int prio - -The following hist trigger both defines a testpid variable and -specifies an onmatch() trace action that uses that variable along with -a non-variable field to generate a wakeup_new_test synthetic event -whenever a sched_wakeup_new event occurs, which because of the 'if -comm == "cyclictest"' filter only happens when the executable is -cyclictest: - - # echo 'hist:keys=testpid=pid:\ - onmatch(sched.sched_wakeup_new).wakeup_new_test($testpid, prio) \ - if comm=="cyclictest"' >> \ - /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/trigger - -Creating and displaying a histogram based on those events is now just -a matter of using the fields and new synthetic event in the -tracing/events/synthetic directory, as usual: - - # echo 'hist:keys=pid,prio:sort=pid,prio' >> \ - /sys/kernel/debug/tracing/events/synthetic/wakeup_new_test/trigger - -Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> -Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ---- - kernel/trace/trace_events_hist.c | 955 ++++++++++++++++++++++++++++++++++++++- - 1 file changed, 940 insertions(+), 15 deletions(-) - ---- a/kernel/trace/trace_events_hist.c -+++ b/kernel/trace/trace_events_hist.c -@@ -59,6 +59,7 @@ struct hist_field { - unsigned int size; - unsigned int offset; - unsigned int is_signed; -+ const char *type; - struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; - struct hist_trigger_data *hist_data; - struct hist_var var; -@@ -243,6 +244,16 @@ struct hist_trigger_attrs { - unsigned int n_actions; - }; - -+struct field_var { -+ struct hist_field *var; -+ struct hist_field *val; -+}; -+ -+struct field_var_hist { -+ struct hist_trigger_data *hist_data; -+ char *cmd; -+}; -+ - struct hist_trigger_data { - struct hist_field *fields[HIST_FIELDS_MAX]; - unsigned int n_vals; -@@ -263,6 +274,14 @@ struct hist_trigger_data { - - struct action_data *actions[HIST_ACTIONS_MAX]; - unsigned int n_actions; -+ -+ struct hist_field *synth_var_refs[SYNTH_FIELDS_MAX]; -+ unsigned int n_synth_var_refs; -+ struct field_var *field_vars[SYNTH_FIELDS_MAX]; -+ unsigned int n_field_vars; -+ unsigned int n_field_var_str; -+ struct field_var_hist *field_var_hists[SYNTH_FIELDS_MAX]; -+ unsigned int n_field_var_hists; - }; - - struct synth_field { -@@ -291,7 +310,14 @@ typedef void (*action_fn_t) (struct hist - - struct action_data { - action_fn_t fn; -+ unsigned int n_params; -+ char *params[SYNTH_FIELDS_MAX]; -+ - unsigned int var_ref_idx; -+ char *match_event; -+ char *match_event_system; -+ char *synth_event_name; -+ struct synth_event *synth_event; - }; - - static LIST_HEAD(synth_event_list); -@@ -802,6 +828,50 @@ static struct synth_event *alloc_synth_e - return event; - } - -+static void action_trace(struct hist_trigger_data *hist_data, -+ struct tracing_map_elt *elt, void *rec, -+ struct ring_buffer_event *rbe, -+ struct action_data *data, u64 *var_ref_vals) -+{ -+ struct synth_event *event = data->synth_event; -+ -+ trace_synth(event, var_ref_vals, data->var_ref_idx); -+} -+ -+static bool check_hist_action_refs(struct hist_trigger_data *hist_data, -+ struct synth_event *event) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < hist_data->n_actions; i++) { -+ struct action_data *data = hist_data->actions[i]; -+ -+ if (data->fn == action_trace && data->synth_event == event) -+ return true; -+ } -+ -+ return false; -+} -+ -+static LIST_HEAD(hist_action_list); -+static LIST_HEAD(hist_var_list); -+ -+struct hist_var_data { -+ struct list_head list; -+ struct hist_trigger_data *hist_data; -+}; -+ -+static bool check_synth_action_refs(struct synth_event *event) -+{ -+ struct hist_var_data *var_data; -+ -+ list_for_each_entry(var_data, &hist_action_list, list) -+ if (check_hist_action_refs(var_data->hist_data, event)) -+ return true; -+ -+ return false; -+} -+ - static int create_synth_event(int argc, char **argv) - { - struct synth_field *fields[SYNTH_FIELDS_MAX]; -@@ -832,15 +902,17 @@ static int create_synth_event(int argc, - event = find_synth_event(name); - if (event) { - if (delete_event) { -+ if (check_synth_action_refs(event)) { -+ ret = -EBUSY; -+ goto out; -+ } - remove_synth_event(event); - goto err; - } else - ret = -EEXIST; - goto out; -- } else if (delete_event) { -- ret = -EINVAL; -+ } else if (delete_event) - goto out; -- } - - if (argc < 2) { - ret = -EINVAL; -@@ -891,11 +963,18 @@ static int release_all_synth_events(void - - mutex_lock(&synth_event_mutex); - -+ list_for_each_entry(event, &synth_event_list, list) { -+ if (check_synth_action_refs(event)) { -+ ret = -EBUSY; -+ goto out; -+ } -+ } -+ - list_for_each_entry_safe(event, e, &synth_event_list, list) { - remove_synth_event(event); - free_synth_event(event); - } -- -+ out: - mutex_unlock(&synth_event_mutex); - - return ret; -@@ -992,13 +1071,6 @@ static u64 hist_field_timestamp(struct h - return ts; - } - --static LIST_HEAD(hist_var_list); -- --struct hist_var_data { -- struct list_head list; -- struct hist_trigger_data *hist_data; --}; -- - static struct hist_field *check_var_ref(struct hist_field *hist_field, - struct hist_trigger_data *var_data, - unsigned int var_idx) -@@ -1248,6 +1320,7 @@ static struct hist_field *find_event_var - struct hist_elt_data { - char *comm; - u64 *var_ref_vals; -+ char *field_var_str[SYNTH_FIELDS_MAX]; - }; - - static u64 hist_field_var_ref(struct hist_field *hist_field, -@@ -1415,11 +1488,21 @@ static void destroy_hist_trigger_attrs(s - - static int parse_action(char *str, struct hist_trigger_attrs *attrs) - { -- int ret = 0; -+ int ret = -EINVAL; - - if (attrs->n_actions >= HIST_ACTIONS_MAX) - return ret; - -+ if ((strncmp(str, "onmatch(", strlen("onmatch(")) == 0)) { -+ attrs->action_str[attrs->n_actions] = kstrdup(str, GFP_KERNEL); -+ if (!attrs->action_str[attrs->n_actions]) { -+ ret = -ENOMEM; -+ return ret; -+ } -+ attrs->n_actions++; -+ ret = 0; -+ } -+ - return ret; - } - -@@ -1525,7 +1608,14 @@ static inline void save_comm(char *comm, - - 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; -+ -+ n_str = hist_data->n_field_var_str; -+ -+ for (i = 0; i < n_str; i++) -+ kfree(private_data->field_var_str[i]); - - kfree(private_data->comm); - kfree(private_data); -@@ -1537,7 +1627,7 @@ static int hist_trigger_elt_data_alloc(s - unsigned int size = TASK_COMM_LEN + 1; - struct hist_elt_data *elt_data; - struct hist_field *key_field; -- unsigned int i; -+ unsigned int i, n_str; - - elt->private_data = elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL); - if (!elt_data) -@@ -1557,6 +1647,16 @@ static int hist_trigger_elt_data_alloc(s - } - } - -+ n_str = hist_data->n_field_var_str; -+ -+ 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); -+ return -ENOMEM; -+ } -+ } -+ - return 0; - } - -@@ -1674,6 +1774,7 @@ static void destroy_hist_field(struct hi - - kfree(hist_field->var.name); - kfree(hist_field->name); -+ kfree(hist_field->type); - - kfree(hist_field); - } -@@ -1704,6 +1805,10 @@ static struct hist_field *create_hist_fi - - if (flags & HIST_FIELD_FL_HITCOUNT) { - hist_field->fn = hist_field_counter; -+ hist_field->size = sizeof(u64); -+ hist_field->type = kstrdup("u64", GFP_KERNEL); -+ if (!hist_field->type) -+ goto free; - goto out; - } - -@@ -1717,12 +1822,18 @@ static struct hist_field *create_hist_fi - hist_field->fn = hist_field_log2; - hist_field->operands[0] = create_hist_field(hist_data, field, fl, NULL); - hist_field->size = hist_field->operands[0]->size; -+ hist_field->type = kstrdup(hist_field->operands[0]->type, GFP_KERNEL); -+ if (!hist_field->type) -+ goto free; - goto out; - } - - if (flags & HIST_FIELD_FL_TIMESTAMP) { - hist_field->fn = hist_field_timestamp; - hist_field->size = sizeof(u64); -+ hist_field->type = kstrdup("u64", GFP_KERNEL); -+ if (!hist_field->type) -+ goto free; - goto out; - } - -@@ -1731,6 +1842,10 @@ static struct hist_field *create_hist_fi - - if (is_string_field(field)) { - flags |= HIST_FIELD_FL_STRING; -+ hist_field->size = MAX_FILTER_STR_VAL; -+ hist_field->type = kstrdup(field->type, GFP_KERNEL); -+ if (!hist_field->type) -+ goto free; - - if (field->filter_type == FILTER_STATIC_STRING) - hist_field->fn = hist_field_string; -@@ -1739,6 +1854,12 @@ static struct hist_field *create_hist_fi - else - hist_field->fn = hist_field_pstring; - } else { -+ hist_field->size = field->size; -+ hist_field->is_signed = field->is_signed; -+ hist_field->type = kstrdup(field->type, GFP_KERNEL); -+ if (!hist_field->type) -+ goto free; -+ - hist_field->fn = select_value_fn(field->size, - field->is_signed); - if (!hist_field->fn) { -@@ -1786,7 +1907,10 @@ static struct hist_field *create_var_ref - ref_field->size = var_field->size; - ref_field->is_signed = var_field->is_signed; - ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL); -- if (!ref_field->name) { -+ ref_field->type = kstrdup(var_field->type, GFP_KERNEL); -+ if (!ref_field->name || !ref_field->type) { -+ kfree(ref_field->name); -+ kfree(ref_field->type); - destroy_hist_field(ref_field, 0); - return NULL; - } -@@ -1970,6 +2094,11 @@ static struct hist_field *parse_unary(st - expr->operands[0] = operand1; - expr->operator = FIELD_OP_UNARY_MINUS; - expr->name = expr_str(expr, 0); -+ expr->type = kstrdup(operand1->type, GFP_KERNEL); -+ if (!expr->type) { -+ ret = -ENOMEM; -+ goto free; -+ } - - return expr; - free: -@@ -2053,6 +2182,11 @@ static struct hist_field *parse_expr(str - expr->operands[1] = operand2; - expr->operator = field_op; - expr->name = expr_str(expr, 0); -+ expr->type = kstrdup(operand1->type, GFP_KERNEL); -+ if (!expr->type) { -+ ret = -ENOMEM; -+ goto free; -+ } - - switch (field_op) { - case FIELD_OP_MINUS: -@@ -2074,6 +2208,718 @@ static struct hist_field *parse_expr(str - return ERR_PTR(ret); - } - -+static struct hist_var_data *find_actions(struct hist_trigger_data *hist_data) -+{ -+ struct hist_var_data *var_data, *found = NULL; -+ -+ list_for_each_entry(var_data, &hist_action_list, list) { -+ if (var_data->hist_data == hist_data) { -+ found = var_data; -+ break; -+ } -+ } -+ -+ return found; -+} -+ -+static int save_hist_actions(struct hist_trigger_data *hist_data) -+{ -+ struct hist_var_data *var_data; -+ -+ var_data = find_actions(hist_data); -+ if (var_data) -+ return 0; -+ -+ var_data = kzalloc(sizeof(*var_data), GFP_KERNEL); -+ if (!var_data) -+ return -ENOMEM; -+ -+ var_data->hist_data = hist_data; -+ list_add(&var_data->list, &hist_action_list); -+ -+ return 0; -+} -+ -+static void remove_hist_actions(struct hist_trigger_data *hist_data) -+{ -+ struct hist_var_data *var_data; -+ -+ var_data = find_actions(hist_data); -+ if (!var_data) -+ return; -+ -+ list_del(&var_data->list); -+ -+ kfree(var_data); -+} -+ -+static char *find_trigger_filter(struct hist_trigger_data *hist_data, -+ struct trace_event_file *file) -+{ -+ struct event_trigger_data *test; -+ -+ list_for_each_entry_rcu(test, &file->triggers, list) { -+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { -+ if (test->private_data == hist_data) -+ return test->filter_str; -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct event_command trigger_hist_cmd; -+static int event_hist_trigger_func(struct event_command *cmd_ops, -+ struct trace_event_file *file, -+ char *glob, char *cmd, char *param); -+ -+static bool compatible_keys(struct hist_trigger_data *target_hist_data, -+ struct hist_trigger_data *hist_data, -+ unsigned int n_keys) -+{ -+ struct hist_field *target_hist_field, *hist_field; -+ unsigned int n, i, j; -+ -+ if (hist_data->n_fields - hist_data->n_vals != n_keys) -+ return false; -+ -+ i = hist_data->n_vals; -+ j = target_hist_data->n_vals; -+ -+ for (n = 0; n < n_keys; n++) { -+ hist_field = hist_data->fields[i + n]; -+ target_hist_field = hist_data->fields[j + n]; -+ -+ if (strcmp(hist_field->type, target_hist_field->type) != 0) -+ return false; -+ if (hist_field->size != target_hist_field->size) -+ return false; -+ if (hist_field->is_signed != target_hist_field->is_signed) -+ return false; -+ } -+ -+ return true; -+} -+ -+static struct hist_trigger_data * -+find_compatible_hist(struct hist_trigger_data *target_hist_data, -+ struct trace_event_file *file) -+{ -+ struct hist_trigger_data *hist_data; -+ struct event_trigger_data *test; -+ unsigned int n_keys; -+ -+ n_keys = target_hist_data->n_fields - target_hist_data->n_vals; -+ -+ list_for_each_entry_rcu(test, &file->triggers, list) { -+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { -+ hist_data = test->private_data; -+ -+ if (compatible_keys(target_hist_data, hist_data, n_keys)) -+ return hist_data; -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct trace_event_file *event_file(char *system, char *event_name) -+{ -+ struct trace_event_file *file; -+ struct trace_array *tr; -+ -+ tr = top_trace_array(); -+ if (!tr) -+ return ERR_PTR(-ENODEV); -+ -+ file = find_event_file(tr, system, event_name); -+ if (!file) -+ return ERR_PTR(-EINVAL); -+ -+ return file; -+} -+ -+static struct hist_field * -+create_field_var_hist(struct hist_trigger_data *target_hist_data, -+ char *system, char *event_name, char *field_name) -+{ -+ struct hist_field *event_var = ERR_PTR(-EINVAL); -+ struct hist_trigger_data *hist_data; -+ unsigned int i, n, first = true; -+ struct field_var_hist *var_hist; -+ struct trace_event_file *file; -+ struct hist_field *key_field; -+ struct trace_array *tr; -+ char *saved_filter; -+ char *cmd; -+ int ret; -+ -+ if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX) -+ return ERR_PTR(-EINVAL); -+ -+ tr = top_trace_array(); -+ if (!tr) -+ return ERR_PTR(-ENODEV); -+ -+ file = event_file(system, event_name); -+ if (IS_ERR(file)) { -+ ret = PTR_ERR(file); -+ return ERR_PTR(ret); -+ } -+ -+ hist_data = find_compatible_hist(target_hist_data, file); -+ if (!hist_data) -+ return ERR_PTR(-EINVAL); -+ -+ var_hist = kzalloc(sizeof(*var_hist), GFP_KERNEL); -+ if (!var_hist) -+ return ERR_PTR(-ENOMEM); -+ -+ cmd = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); -+ if (!cmd) { -+ kfree(var_hist); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ strcat(cmd, "keys="); -+ -+ for_each_hist_key_field(i, hist_data) { -+ key_field = hist_data->fields[i]; -+ if (!first) -+ strcat(cmd, ","); -+ strcat(cmd, key_field->field->name); -+ first = false; -+ } -+ -+ strcat(cmd, ":synthetic_"); -+ strcat(cmd, field_name); -+ strcat(cmd, "="); -+ strcat(cmd, field_name); -+ -+ saved_filter = find_trigger_filter(hist_data, file); -+ if (saved_filter) { -+ strcat(cmd, " if "); -+ strcat(cmd, saved_filter); -+ } -+ -+ var_hist->cmd = kstrdup(cmd, GFP_KERNEL); -+ if (!var_hist->cmd) { -+ kfree(cmd); -+ kfree(var_hist); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ var_hist->hist_data = hist_data; -+ -+ ret = event_hist_trigger_func(&trigger_hist_cmd, file, -+ "", "hist", cmd); -+ if (ret) { -+ kfree(cmd); -+ kfree(var_hist->cmd); -+ kfree(var_hist); -+ return ERR_PTR(ret); -+ } -+ -+ strcpy(cmd, "synthetic_"); -+ strcat(cmd, field_name); -+ -+ event_var = find_event_var(system, event_name, cmd); -+ if (!event_var) { -+ kfree(cmd); -+ kfree(var_hist->cmd); -+ kfree(var_hist); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ n = target_hist_data->n_field_var_hists; -+ target_hist_data->field_var_hists[n] = var_hist; -+ target_hist_data->n_field_var_hists++; -+ -+ return event_var; -+} -+ -+static struct hist_field * -+find_target_event_var(struct hist_trigger_data *hist_data, -+ char *system, char *event_name, char *var_name) -+{ -+ struct trace_event_file *file = hist_data->event_file; -+ struct hist_field *hist_field = NULL; -+ -+ if (system) { -+ struct trace_event_call *call; -+ -+ if (!event_name) -+ return NULL; -+ -+ call = file->event_call; -+ -+ if (strcmp(system, call->class->system) != 0) -+ return NULL; -+ -+ if (strcmp(event_name, trace_event_name(call)) != 0) -+ return NULL; -+ } -+ -+ hist_field = find_var_field(hist_data, var_name); -+ -+ return hist_field; -+} -+ -+static inline void __update_field_vars(struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *rec, -+ struct field_var **field_vars, -+ unsigned int n_field_vars, -+ unsigned int field_var_str_start) -+{ -+ struct hist_elt_data *elt_data = elt->private_data; -+ unsigned int i, j, var_idx; -+ u64 var_val; -+ -+ for (i = 0, j = field_var_str_start; i < n_field_vars; i++) { -+ struct field_var *field_var = field_vars[i]; -+ struct hist_field *var = field_var->var; -+ struct hist_field *val = field_var->val; -+ -+ var_val = val->fn(val, elt, rbe, rec); -+ var_idx = var->var.idx; -+ -+ if (val->flags & HIST_FIELD_FL_STRING) { -+ char *str = elt_data->field_var_str[j++]; -+ -+ memcpy(str, (char *)(uintptr_t)var_val, -+ TASK_COMM_LEN + 1); -+ var_val = (u64)(uintptr_t)str; -+ } -+ tracing_map_set_var(elt, var_idx, var_val); -+ } -+} -+ -+static void update_field_vars(struct hist_trigger_data *hist_data, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *rec) -+{ -+ __update_field_vars(elt, rbe, rec, hist_data->field_vars, -+ hist_data->n_field_vars, 0); -+} -+ -+static struct hist_field *create_var(struct hist_trigger_data *hist_data, -+ struct trace_event_file *file, -+ char *name, int size, const char *type) -+{ -+ struct hist_field *var; -+ int idx; -+ -+ if (find_var(file, name) && !hist_data->remove) { -+ var = ERR_PTR(-EINVAL); -+ goto out; -+ } -+ -+ var = kzalloc(sizeof(struct hist_field), GFP_KERNEL); -+ if (!var) { -+ var = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ idx = tracing_map_add_var(hist_data->map); -+ if (idx < 0) { -+ kfree(var); -+ var = ERR_PTR(-EINVAL); -+ goto out; -+ } -+ -+ var->flags = HIST_FIELD_FL_VAR; -+ var->var.idx = idx; -+ var->var.hist_data = var->hist_data = hist_data; -+ var->size = size; -+ var->var.name = kstrdup(name, GFP_KERNEL); -+ var->type = kstrdup(type, GFP_KERNEL); -+ if (!var->var.name || !var->type) { -+ kfree(var->var.name); -+ kfree(var->type); -+ kfree(var); -+ var = ERR_PTR(-ENOMEM); -+ } -+ out: -+ return var; -+} -+ -+static struct field_var *create_field_var(struct hist_trigger_data *hist_data, -+ struct trace_event_file *file, -+ char *field_name) -+{ -+ struct hist_field *val = NULL, *var = NULL; -+ unsigned long flags = HIST_FIELD_FL_VAR; -+ struct field_var *field_var; -+ int ret = 0; -+ -+ if (hist_data->n_field_vars >= SYNTH_FIELDS_MAX) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ val = parse_atom(hist_data, file, field_name, &flags, NULL); -+ if (IS_ERR(val)) { -+ ret = PTR_ERR(val); -+ goto err; -+ } -+ -+ var = create_var(hist_data, file, field_name, val->size, val->type); -+ if (IS_ERR(var)) { -+ kfree(val); -+ ret = PTR_ERR(var); -+ goto err; -+ } -+ -+ field_var = kzalloc(sizeof(struct field_var), GFP_KERNEL); -+ if (!field_var) { -+ kfree(val); -+ kfree(var); -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ field_var->var = var; -+ field_var->val = val; -+ out: -+ return field_var; -+ err: -+ field_var = ERR_PTR(ret); -+ goto out; -+} -+ -+static struct field_var * -+create_target_field_var(struct hist_trigger_data *hist_data, -+ char *system, char *event_name, char *var_name) -+{ -+ struct trace_event_file *file = hist_data->event_file; -+ -+ if (system) { -+ struct trace_event_call *call; -+ -+ if (!event_name) -+ return NULL; -+ -+ call = file->event_call; -+ -+ if (strcmp(system, call->class->system) != 0) -+ return NULL; -+ -+ if (strcmp(event_name, trace_event_name(call)) != 0) -+ return NULL; -+ } -+ -+ return create_field_var(hist_data, file, var_name); -+} -+ -+static void onmatch_destroy(struct action_data *data) -+{ -+ unsigned int i; -+ -+ kfree(data->match_event); -+ kfree(data->match_event_system); -+ kfree(data->synth_event_name); -+ -+ for (i = 0; i < data->n_params; i++) -+ kfree(data->params[i]); -+ -+ kfree(data); -+} -+ -+static void destroy_field_var(struct field_var *field_var) -+{ -+ if (!field_var) -+ return; -+ -+ destroy_hist_field(field_var->var, 0); -+ destroy_hist_field(field_var->val, 0); -+ -+ kfree(field_var); -+} -+ -+static void destroy_field_vars(struct hist_trigger_data *hist_data) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < hist_data->n_field_vars; i++) -+ destroy_field_var(hist_data->field_vars[i]); -+} -+ -+static void save_field_var(struct hist_trigger_data *hist_data, -+ struct field_var *field_var) -+{ -+ hist_data->field_vars[hist_data->n_field_vars++] = field_var; -+ -+ if (field_var->val->flags & HIST_FIELD_FL_STRING) -+ hist_data->n_field_var_str++; -+} -+ -+static void destroy_synth_var_refs(struct hist_trigger_data *hist_data) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < hist_data->n_synth_var_refs; i++) -+ destroy_hist_field(hist_data->synth_var_refs[i], 0); -+} -+ -+static void save_synth_var_ref(struct hist_trigger_data *hist_data, -+ struct hist_field *var_ref) -+{ -+ hist_data->synth_var_refs[hist_data->n_synth_var_refs++] = var_ref; -+ -+ hist_data->var_refs[hist_data->n_var_refs] = var_ref; -+ var_ref->var_ref_idx = hist_data->n_var_refs++; -+} -+ -+static int check_synth_field(struct synth_event *event, -+ struct hist_field *hist_field, -+ unsigned int field_pos) -+{ -+ struct synth_field *field; -+ -+ if (field_pos >= event->n_fields) -+ return -EINVAL; -+ -+ field = event->fields[field_pos]; -+ -+ if (strcmp(field->type, hist_field->type) != 0) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static int parse_action_params(char *params, struct action_data *data) -+{ -+ char *param, *saved_param; -+ int ret = 0; -+ -+ while (params) { -+ if (data->n_params >= SYNTH_FIELDS_MAX) -+ goto out; -+ -+ param = strsep(¶ms, ","); -+ if (!param) -+ goto out; -+ -+ param = strstrip(param); -+ if (strlen(param) < 2) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ saved_param = kstrdup(param, GFP_KERNEL); -+ if (!saved_param) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ data->params[data->n_params++] = saved_param; -+ } -+ out: -+ return ret; -+} -+ -+static struct hist_field * -+onmatch_find_var(struct hist_trigger_data *hist_data, struct action_data *data, -+ char *system, char *event, char *var) -+{ -+ struct hist_field *hist_field; -+ -+ var++; /* skip '$' */ -+ -+ hist_field = find_target_event_var(hist_data, system, event, var); -+ if (!hist_field) { -+ if (!system) { -+ system = data->match_event_system; -+ event = data->match_event; -+ } -+ -+ hist_field = find_event_var(system, event, var); -+ } -+ -+ return hist_field; -+} -+ -+static struct hist_field * -+onmatch_create_field_var(struct hist_trigger_data *hist_data, -+ struct action_data *data, char *system, -+ char *event, char *var) -+{ -+ struct hist_field *hist_field = NULL; -+ struct field_var *field_var; -+ -+ field_var = create_target_field_var(hist_data, system, event, var); -+ if (IS_ERR(field_var)) -+ goto out; -+ -+ if (field_var) { -+ save_field_var(hist_data, field_var); -+ hist_field = field_var->var; -+ } else { -+ if (!system) { -+ system = data->match_event_system; -+ event = data->match_event; -+ } -+ -+ hist_field = create_field_var_hist(hist_data, system, event, var); -+ if (IS_ERR(hist_field)) -+ goto free; -+ } -+ out: -+ return hist_field; -+ free: -+ destroy_field_var(field_var); -+ hist_field = NULL; -+ goto out; -+} -+ -+static int onmatch_create(struct hist_trigger_data *hist_data, -+ struct trace_event_file *file, -+ struct action_data *data) -+{ -+ char *event_name, *param, *system = NULL; -+ struct hist_field *hist_field, *var_ref; -+ unsigned int i, var_ref_idx; -+ unsigned int field_pos = 0; -+ struct synth_event *event; -+ int ret = 0; -+ -+ mutex_lock(&synth_event_mutex); -+ -+ event = find_synth_event(data->synth_event_name); -+ if (!event) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ var_ref_idx = hist_data->n_var_refs; -+ -+ for (i = 0; i < data->n_params; i++) { -+ char *p; -+ -+ p = param = kstrdup(data->params[i], GFP_KERNEL); -+ if (!param) -+ goto out; -+ -+ system = strsep(¶m, "."); -+ if (!param) { -+ param = (char *)system; -+ system = event_name = NULL; -+ } else { -+ event_name = strsep(¶m, "."); -+ if (!param) { -+ kfree(p); -+ ret = -EINVAL; -+ goto out; -+ } -+ } -+ -+ if (param[0] == '$') -+ hist_field = onmatch_find_var(hist_data, data, system, -+ event_name, param); -+ else -+ hist_field = onmatch_create_field_var(hist_data, data, -+ system, -+ event_name, -+ param); -+ -+ if (!hist_field) { -+ kfree(p); -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ if (check_synth_field(event, hist_field, field_pos) == 0) { -+ var_ref = create_var_ref(hist_field); -+ if (!var_ref) { -+ kfree(p); -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ save_synth_var_ref(hist_data, var_ref); -+ field_pos++; -+ kfree(p); -+ continue; -+ } -+ -+ kfree(p); -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ if (field_pos != event->n_fields) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ data->fn = action_trace; -+ data->synth_event = event; -+ data->var_ref_idx = var_ref_idx; -+ hist_data->actions[hist_data->n_actions++] = data; -+ save_hist_actions(hist_data); -+ out: -+ mutex_unlock(&synth_event_mutex); -+ -+ return ret; -+} -+ -+static struct action_data *onmatch_parse(char *str) -+{ -+ char *match_event, *match_event_system; -+ char *synth_event_name, *params; -+ struct action_data *data; -+ int ret = -EINVAL; -+ -+ data = kzalloc(sizeof(*data), GFP_KERNEL); -+ if (!data) -+ return ERR_PTR(-ENOMEM); -+ -+ match_event = strsep(&str, ")"); -+ if (!match_event || !str) -+ goto free; -+ -+ match_event_system = strsep(&match_event, "."); -+ if (!match_event) -+ goto free; -+ -+ if (IS_ERR(event_file(match_event_system, match_event))) -+ goto free; -+ -+ data->match_event = kstrdup(match_event, GFP_KERNEL); -+ data->match_event_system = kstrdup(match_event_system, GFP_KERNEL); -+ -+ strsep(&str, "."); -+ if (!str) -+ goto free; -+ -+ synth_event_name = strsep(&str, "("); -+ if (!synth_event_name || !str) -+ goto free; -+ data->synth_event_name = kstrdup(synth_event_name, GFP_KERNEL); -+ -+ params = strsep(&str, ")"); -+ if (!params || !str || (str && strlen(str))) -+ goto free; -+ -+ ret = parse_action_params(params, data); -+ if (ret) -+ goto free; -+ -+ if (!data->match_event_system || !data->match_event || -+ !data->synth_event_name) { -+ ret = -ENOMEM; -+ goto free; -+ } -+ out: -+ return data; -+ free: -+ onmatch_destroy(data); -+ data = ERR_PTR(ret); -+ goto out; -+} -+ - static int create_hitcount_val(struct hist_trigger_data *hist_data) - { - hist_data->fields[HITCOUNT_IDX] = -@@ -2465,19 +3311,37 @@ static void destroy_actions(struct hist_ - for (i = 0; i < hist_data->n_actions; i++) { - struct action_data *data = hist_data->actions[i]; - -- kfree(data); -+ if (data->fn == action_trace) -+ onmatch_destroy(data); -+ else -+ kfree(data); - } - } - - static int create_actions(struct hist_trigger_data *hist_data, - struct trace_event_file *file) - { -+ struct action_data *data; - unsigned int i; - int ret = 0; - char *str; - - for (i = 0; i < hist_data->attrs->n_actions; i++) { - str = hist_data->attrs->action_str[i]; -+ -+ if (strncmp(str, "onmatch(", strlen("onmatch(")) == 0) { -+ char *action_str = str + strlen("onmatch("); -+ -+ data = onmatch_parse(action_str); -+ if (IS_ERR(data)) -+ return PTR_ERR(data); -+ -+ ret = onmatch_create(hist_data, file, data); -+ if (ret) { -+ onmatch_destroy(data); -+ return ret; -+ } -+ } - } - - return ret; -@@ -2494,6 +3358,26 @@ static void print_actions(struct seq_fil - } - } - -+static void print_onmatch_spec(struct seq_file *m, -+ struct hist_trigger_data *hist_data, -+ struct action_data *data) -+{ -+ unsigned int i; -+ -+ seq_printf(m, ":onmatch(%s.%s).", data->match_event_system, -+ data->match_event); -+ -+ seq_printf(m, "%s(", data->synth_event->name); -+ -+ for (i = 0; i < data->n_params; i++) { -+ if (i) -+ seq_puts(m, ","); -+ seq_printf(m, "%s", data->params[i]); -+ } -+ -+ seq_puts(m, ")"); -+} -+ - static void print_actions_spec(struct seq_file *m, - struct hist_trigger_data *hist_data) - { -@@ -2501,6 +3385,19 @@ static void print_actions_spec(struct se - - for (i = 0; i < hist_data->n_actions; i++) { - struct action_data *data = hist_data->actions[i]; -+ -+ if (data->fn == action_trace) -+ print_onmatch_spec(m, hist_data, data); -+ } -+} -+ -+static void destroy_field_var_hists(struct hist_trigger_data *hist_data) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < hist_data->n_field_var_hists; i++) { -+ kfree(hist_data->field_var_hists[i]->cmd); -+ kfree(hist_data->field_var_hists[i]); - } - } - -@@ -2514,6 +3411,9 @@ static void destroy_hist_data(struct his - tracing_map_destroy(hist_data->map); - - destroy_actions(hist_data); -+ destroy_field_vars(hist_data); -+ destroy_field_var_hists(hist_data); -+ destroy_synth_var_refs(hist_data); - - kfree(hist_data); - } -@@ -2648,6 +3548,8 @@ static void hist_trigger_elt_update(stru - tracing_map_set_var(elt, var_idx, hist_val); - } - } -+ -+ update_field_vars(hist_data, elt, rbe, rec); - } - - static inline void add_to_key(char *compound_key, void *key, -@@ -2861,6 +3763,8 @@ hist_trigger_entry_print(struct seq_file - } - } - -+ print_actions(m, hist_data, elt); -+ - seq_puts(m, "\n"); - } - -@@ -3128,6 +4032,8 @@ static void event_hist_trigger_free(stru - - remove_hist_vars(hist_data); - -+ remove_hist_actions(hist_data); -+ - destroy_hist_data(hist_data); - } - } -@@ -3390,6 +4296,21 @@ static bool hist_trigger_check_refs(stru - return false; - } - -+static void unregister_field_var_hists(struct hist_trigger_data *hist_data) -+{ -+ struct trace_event_file *file; -+ unsigned int i; -+ char *cmd; -+ int ret; -+ -+ for (i = 0; i < hist_data->n_field_var_hists; i++) { -+ file = hist_data->field_var_hists[i]->hist_data->event_file; -+ cmd = hist_data->field_var_hists[i]->cmd; -+ ret = event_hist_trigger_func(&trigger_hist_cmd, file, -+ "!hist", "hist", cmd); -+ } -+} -+ - static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, - struct event_trigger_data *data, - struct trace_event_file *file) -@@ -3405,6 +4326,7 @@ static void hist_unregister_trigger(char - if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { - if (!hist_trigger_match(data, test, named_data, false)) - continue; -+ unregister_field_var_hists(test->private_data); - unregistered = true; - list_del_rcu(&test->list); - trace_event_trigger_enable_disable(file, 0); -@@ -3448,6 +4370,7 @@ static void hist_unreg_all(struct trace_ - - list_for_each_entry_safe(test, n, &file->triggers, list) { - if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { -+ unregister_field_var_hists(test->private_data); - list_del_rcu(&test->list); - trace_event_trigger_enable_disable(file, 0); - update_cond_flag(file); -@@ -3571,6 +4494,8 @@ static int event_hist_trigger_func(struc - - remove_hist_vars(hist_data); - -+ remove_hist_actions(hist_data); -+ - kfree(trigger_data); - destroy_hist_data(hist_data); - diff --git a/patches/0019-tracing-Add-variable-reference-handling-to-hist-trig.patch b/patches/0024-tracing-Add-variable-reference-handling-to-hist-trig.patch index 558d2c2ffd0b..6576aeb1de12 100644 --- a/patches/0019-tracing-Add-variable-reference-handling-to-hist-trig.patch +++ b/patches/0024-tracing-Add-variable-reference-handling-to-hist-trig.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:20 -0500 -Subject: [PATCH 19/32] tracing: Add variable reference handling to hist +Date: Tue, 5 Sep 2017 16:57:36 -0500 +Subject: [PATCH 24/40] tracing: Add variable reference handling to hist triggers Add the necessary infrastructure to allow the variables defined on one @@ -24,14 +24,41 @@ be displayed in a latency histogram. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - kernel/trace/trace.h | 2 - kernel/trace/trace_events_hist.c | 719 +++++++++++++++++++++++++++++------- + kernel/trace/trace.c | 2 + kernel/trace/trace.h | 3 + kernel/trace/trace_events_hist.c | 606 ++++++++++++++++++++++++++++++++---- kernel/trace/trace_events_trigger.c | 6 - 3 files changed, 604 insertions(+), 123 deletions(-) + 4 files changed, 561 insertions(+), 56 deletions(-) +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -7380,6 +7380,7 @@ static int instance_mkdir(const char *na + + INIT_LIST_HEAD(&tr->systems); + INIT_LIST_HEAD(&tr->events); ++ INIT_LIST_HEAD(&tr->hist_vars); + + if (allocate_trace_buffers(tr, trace_buf_size) < 0) + goto out_free_tr; +@@ -8112,6 +8113,7 @@ ssize_t trace_parse_run_command(struct f + + INIT_LIST_HEAD(&global_trace.systems); + INIT_LIST_HEAD(&global_trace.events); ++ INIT_LIST_HEAD(&global_trace.hist_vars); + list_add(&global_trace.list, &ftrace_trace_arrays); + + apply_trace_boot_options(); --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h -@@ -1448,6 +1448,8 @@ extern void pause_named_trigger(struct e +@@ -266,6 +266,7 @@ struct trace_array { + int function_enabled; + #endif + int time_stamp_abs_ref; ++ struct list_head hist_vars; + }; + + enum { +@@ -1442,6 +1443,8 @@ extern void pause_named_trigger(struct e extern void unpause_named_trigger(struct event_trigger_data *data); extern void set_named_trigger_data(struct event_trigger_data *data, struct event_trigger_data *named_data); @@ -42,20 +69,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> extern int register_trigger_hist_enable_disable_cmds(void); --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -26,8 +26,10 @@ - - struct hist_field; - --typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event, -- struct ring_buffer_event *rbe); -+typedef u64 (*hist_field_fn_t) (struct hist_field *field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event); - - #define HIST_FIELD_OPERANDS_MAX 2 - #define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX) -@@ -57,30 +59,41 @@ struct hist_field { +@@ -60,6 +60,9 @@ struct hist_field { struct hist_var var; enum field_op_id operator; char *name; @@ -64,149 +78,16 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + bool read_once; }; --static u64 hist_field_none(struct hist_field *field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_none(struct hist_field *field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - return 0; - } - --static u64 hist_field_counter(struct hist_field *field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_counter(struct hist_field *field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - return 1; - } - --static u64 hist_field_string(struct hist_field *hist_field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_string(struct hist_field *hist_field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - char *addr = (char *)(event + hist_field->field->offset); - - return (u64)(unsigned long)addr; - } - --static u64 hist_field_dynstring(struct hist_field *hist_field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_dynstring(struct hist_field *hist_field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - u32 str_item = *(u32 *)(event + hist_field->field->offset); - int str_loc = str_item & 0xffff; -@@ -89,54 +102,64 @@ static u64 hist_field_dynstring(struct h - return (u64)(unsigned long)addr; - } - --static u64 hist_field_pstring(struct hist_field *hist_field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_pstring(struct hist_field *hist_field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - char **addr = (char **)(event + hist_field->field->offset); - - return (u64)(unsigned long)*addr; - } - --static u64 hist_field_log2(struct hist_field *hist_field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_log2(struct hist_field *hist_field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - struct hist_field *operand = hist_field->operands[0]; - -- u64 val = operand->fn(operand, event, rbe); -+ u64 val = operand->fn(operand, elt, rbe, event); - - return (u64) ilog2(roundup_pow_of_two(val)); - } - --static u64 hist_field_plus(struct hist_field *hist_field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_plus(struct hist_field *hist_field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - struct hist_field *operand1 = hist_field->operands[0]; - struct hist_field *operand2 = hist_field->operands[1]; - -- u64 val1 = operand1->fn(operand1, event, rbe); -- u64 val2 = operand2->fn(operand2, event, rbe); -+ u64 val1 = operand1->fn(operand1, elt, rbe, event); -+ u64 val2 = operand2->fn(operand2, elt, rbe, event); - - return val1 + val2; - } - --static u64 hist_field_minus(struct hist_field *hist_field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_minus(struct hist_field *hist_field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - struct hist_field *operand1 = hist_field->operands[0]; - struct hist_field *operand2 = hist_field->operands[1]; - -- u64 val1 = operand1->fn(operand1, event, rbe); -- u64 val2 = operand2->fn(operand2, event, rbe); -+ u64 val1 = operand1->fn(operand1, elt, rbe, event); -+ u64 val2 = operand2->fn(operand2, elt, rbe, event); - - return val1 - val2; - } - --static u64 hist_field_unary_minus(struct hist_field *hist_field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_unary_minus(struct hist_field *hist_field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - struct hist_field *operand = hist_field->operands[0]; - -- s64 sval = (s64)operand->fn(operand, event, rbe); -+ s64 sval = (s64)operand->fn(operand, elt, rbe, event); - u64 val = (u64)-sval; - - return val; -@@ -144,8 +167,9 @@ static u64 hist_field_unary_minus(struct - - #define DEFINE_HIST_FIELD_FN(type) \ - static u64 hist_field_##type(struct hist_field *hist_field, \ -- void *event, \ -- struct ring_buffer_event *rbe) \ -+ struct tracing_map_elt *elt, \ -+ struct ring_buffer_event *rbe, \ -+ void *event) \ - { \ - type *addr = (type *)(event + hist_field->field->offset); \ - \ -@@ -193,6 +217,7 @@ enum hist_field_flags { + static u64 hist_field_none(struct hist_field *field, +@@ -215,6 +218,7 @@ enum hist_field_flags { HIST_FIELD_FL_VAR = 4096, HIST_FIELD_FL_VAR_ONLY = 8192, HIST_FIELD_FL_EXPR = 16384, + HIST_FIELD_FL_VAR_REF = 32768, }; - struct hist_trigger_attrs { -@@ -225,10 +250,14 @@ struct hist_trigger_data { + struct var_defs { +@@ -255,6 +259,8 @@ struct hist_trigger_data { struct tracing_map *map; bool enable_timestamps; bool remove; @@ -214,21 +95,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + unsigned int n_var_refs; }; --static u64 hist_field_timestamp(struct hist_field *hist_field, void *event, -- struct ring_buffer_event *rbe) -+static u64 hist_field_timestamp(struct hist_field *hist_field, -+ struct tracing_map_elt *elt, -+ struct ring_buffer_event *rbe, -+ void *event) - { - struct hist_trigger_data *hist_data = hist_field->hist_data; - struct trace_array *tr = hist_data->event_file->tr; -@@ -241,6 +270,324 @@ static u64 hist_field_timestamp(struct h + static u64 hist_field_timestamp(struct hist_field *hist_field, +@@ -273,10 +279,344 @@ static u64 hist_field_timestamp(struct h return ts; } -+static LIST_HEAD(hist_var_list); -+ +struct hist_var_data { + struct list_head list; + struct hist_trigger_data *hist_data; @@ -279,10 +150,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data, + unsigned int var_idx) +{ ++ struct trace_array *tr = hist_data->event_file->tr; + struct hist_field *found = NULL; + struct hist_var_data *var_data; + -+ list_for_each_entry(var_data, &hist_var_list, list) { ++ list_for_each_entry(var_data, &tr->hist_vars, list) { + found = find_var_ref(var_data->hist_data, hist_data, var_idx); + if (found) + break; @@ -312,9 +184,10 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + +static struct hist_var_data *find_hist_vars(struct hist_trigger_data *hist_data) +{ ++ struct trace_array *tr = hist_data->event_file->tr; + struct hist_var_data *var_data, *found = NULL; + -+ list_for_each_entry(var_data, &hist_var_list, list) { ++ list_for_each_entry(var_data, &tr->hist_vars, list) { + if (var_data->hist_data == hist_data) { + found = var_data; + break; @@ -327,40 +200,56 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +static bool has_hist_vars(struct hist_trigger_data *hist_data) +{ + struct hist_field *hist_field; -+ bool found = false; -+ int i; ++ int i, j; + + for_each_hist_field(i, hist_data) { + hist_field = hist_data->fields[i]; -+ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR) { -+ found = true; -+ break; ++ if (hist_field && ++ (hist_field->flags & HIST_FIELD_FL_VAR || ++ hist_field->flags & HIST_FIELD_FL_VAR_REF)) ++ return true; ++ ++ for (j = 0; j < HIST_FIELD_OPERANDS_MAX; j++) { ++ struct hist_field *operand; ++ ++ operand = hist_field->operands[j]; ++ if (operand && ++ (operand->flags & HIST_FIELD_FL_VAR || ++ operand->flags & HIST_FIELD_FL_VAR_REF)) ++ return true; + } + } + -+ return found; ++ return false; +} + +static int save_hist_vars(struct hist_trigger_data *hist_data) +{ ++ struct trace_array *tr = hist_data->event_file->tr; + struct hist_var_data *var_data; + + var_data = find_hist_vars(hist_data); + if (var_data) + return 0; + ++ if (trace_array_get(tr) < 0) ++ return -ENODEV; ++ + var_data = kzalloc(sizeof(*var_data), GFP_KERNEL); -+ if (!var_data) ++ if (!var_data) { ++ trace_array_put(tr); + return -ENOMEM; ++ } + + var_data->hist_data = hist_data; -+ list_add(&var_data->list, &hist_var_list); ++ list_add(&var_data->list, &tr->hist_vars); + + return 0; +} + +static void remove_hist_vars(struct hist_trigger_data *hist_data) +{ ++ struct trace_array *tr = hist_data->event_file->tr; + struct hist_var_data *var_data; + + var_data = find_hist_vars(hist_data); @@ -373,6 +262,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + list_del(&var_data->list); + + kfree(var_data); ++ ++ trace_array_put(tr); +} + +static struct hist_field *find_var_field(struct hist_trigger_data *hist_data, @@ -412,7 +303,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return NULL; +} + -+static struct trace_event_file *find_var_file(const char *system, ++static struct trace_event_file *find_var_file(struct trace_array *tr, ++ const char *system, + const char *event_name, + const char *var_name) +{ @@ -422,7 +314,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + struct trace_event_file *file; + const char *name; + -+ list_for_each_entry(var_data, &hist_var_list, list) { ++ list_for_each_entry(var_data, &tr->hist_vars, list) { + var_hist_data = var_data->hist_data; + file = var_hist_data->event_file; + call = file->event_call; @@ -464,14 +356,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return NULL; +} + -+static struct hist_field *find_event_var(const char *system, ++static struct hist_field *find_event_var(struct trace_array *tr, ++ const char *system, + const char *event_name, + const char *var_name) +{ + struct hist_field *hist_field = NULL; + struct trace_event_file *file; + -+ file = find_var_file(system, event_name, var_name); ++ file = find_var_file(tr, system, event_name, var_name); + if (!file) + return NULL; + @@ -480,11 +373,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return hist_field; +} + -+struct hist_elt_data { -+ char *comm; + struct hist_elt_data { + char *comm; + u64 *var_ref_vals; -+}; -+ + }; + +static u64 hist_field_var_ref(struct hist_field *hist_field, + struct tracing_map_elt *elt, + struct ring_buffer_event *rbe, @@ -548,7 +441,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static const char *hist_field_name(struct hist_field *field, unsigned int level) { -@@ -255,7 +602,8 @@ static const char *hist_field_name(struc +@@ -291,7 +631,8 @@ static const char *hist_field_name(struc field_name = hist_field_name(field->operands[0], ++level); else if (field->flags & HIST_FIELD_FL_TIMESTAMP) field_name = "$common_timestamp"; @@ -558,115 +451,25 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> field_name = field->name; if (field_name == NULL) -@@ -439,26 +787,36 @@ static inline void save_comm(char *comm, - memcpy(comm, task->comm, TASK_COMM_LEN); - } - --static void hist_trigger_elt_comm_free(struct tracing_map_elt *elt) -+static void hist_trigger_elt_data_free(struct tracing_map_elt *elt) - { -- kfree((char *)elt->private_data); -+ struct hist_elt_data *private_data = elt->private_data; -+ -+ kfree(private_data->comm); -+ kfree(private_data); - } - --static int hist_trigger_elt_comm_alloc(struct tracing_map_elt *elt) -+static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt) - { - struct hist_trigger_data *hist_data = elt->map->private_data; -+ unsigned int size = TASK_COMM_LEN + 1; -+ struct hist_elt_data *elt_data; - struct hist_field *key_field; - unsigned int i; - -+ elt->private_data = elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL); -+ if (!elt_data) -+ return -ENOMEM; -+ - for_each_hist_key_field(i, hist_data) { - key_field = hist_data->fields[i]; - - if (key_field->flags & HIST_FIELD_FL_EXECNAME) { -- unsigned int size = TASK_COMM_LEN + 1; -- -- elt->private_data = kzalloc(size, GFP_KERNEL); -- if (!elt->private_data) -+ elt_data->comm = kzalloc(size, GFP_KERNEL); -+ if (!elt_data->comm) { -+ kfree(elt_data); -+ elt->private_data = NULL; - return -ENOMEM; -+ } - break; - } - } -@@ -466,29 +824,31 @@ static int hist_trigger_elt_comm_alloc(s - return 0; - } - --static void hist_trigger_elt_comm_copy(struct tracing_map_elt *to, -+static void hist_trigger_elt_data_copy(struct tracing_map_elt *to, - struct tracing_map_elt *from) - { -- char *comm_from = from->private_data; -- char *comm_to = to->private_data; -+ struct hist_elt_data *from_data = from->private_data; -+ struct hist_elt_data *to_data = to->private_data; -+ -+ memcpy(to_data, from_data, sizeof(*to)); - -- if (comm_from) -- memcpy(comm_to, comm_from, TASK_COMM_LEN + 1); -+ if (from_data->comm) -+ memcpy(to_data->comm, from_data->comm, TASK_COMM_LEN + 1); - } - --static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt) -+static void hist_trigger_elt_data_init(struct tracing_map_elt *elt) - { -- char *comm = elt->private_data; -+ struct hist_elt_data *private_data = elt->private_data; - -- if (comm) -- save_comm(comm, current); -+ if (private_data->comm) -+ save_comm(private_data->comm, current); - } - --static const struct tracing_map_ops hist_trigger_elt_comm_ops = { -- .elt_alloc = hist_trigger_elt_comm_alloc, -- .elt_copy = hist_trigger_elt_comm_copy, -- .elt_free = hist_trigger_elt_comm_free, -- .elt_init = hist_trigger_elt_comm_init, -+static const struct tracing_map_ops hist_trigger_elt_data_ops = { -+ .elt_alloc = hist_trigger_elt_data_alloc, -+ .elt_copy = hist_trigger_elt_data_copy, -+ .elt_free = hist_trigger_elt_data_free, -+ .elt_init = hist_trigger_elt_data_init, - }; - - static char *expr_str(struct hist_field *field, unsigned int level) -@@ -513,6 +873,8 @@ static char *expr_str(struct hist_field +@@ -574,6 +915,8 @@ static char *expr_str(struct hist_field return expr; } + if (field->operands[0]->flags & HIST_FIELD_FL_VAR_REF) + strcat(expr, "$"); strcat(expr, hist_field_name(field->operands[0], 0)); - - switch (field->operator) { -@@ -527,6 +889,8 @@ static char *expr_str(struct hist_field + if (field->operands[0]->flags) { + const char *flags_str = get_hist_field_flags(field->operands[0]); +@@ -596,6 +939,8 @@ static char *expr_str(struct hist_field return NULL; } + if (field->operands[1]->flags & HIST_FIELD_FL_VAR_REF) + strcat(expr, "$"); strcat(expr, hist_field_name(field->operands[1], 0)); - - return expr; -@@ -597,6 +961,11 @@ static struct hist_field *create_hist_fi + if (field->operands[1]->flags) { + const char *flags_str = get_hist_field_flags(field->operands[1]); +@@ -675,6 +1020,11 @@ static struct hist_field *create_hist_fi if (flags & HIST_FIELD_FL_EXPR) goto out; /* caller will populate */ @@ -677,11 +480,32 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + if (flags & HIST_FIELD_FL_HITCOUNT) { hist_field->fn = hist_field_counter; - goto out; -@@ -669,6 +1038,44 @@ static void destroy_hist_fields(struct h + hist_field->size = sizeof(u64); +@@ -768,6 +1118,51 @@ static void destroy_hist_fields(struct h } } ++static int init_var_ref(struct hist_field *ref_field, ++ struct hist_field *var_field) ++{ ++ ref_field->var.idx = var_field->var.idx; ++ ref_field->var.hist_data = var_field->hist_data; ++ ref_field->size = var_field->size; ++ ref_field->is_signed = var_field->is_signed; ++ ++ ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL); ++ if (!ref_field->name) ++ return -ENOMEM; ++ ++ ref_field->type = kstrdup(var_field->type, GFP_KERNEL); ++ if (!ref_field->type) { ++ kfree(ref_field->name); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ +static struct hist_field *create_var_ref(struct hist_field *var_field) +{ + unsigned long flags = HIST_FIELD_FL_VAR_REF; @@ -689,12 +513,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + + ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL); + if (ref_field) { -+ ref_field->var.idx = var_field->var.idx; -+ ref_field->var.hist_data = var_field->hist_data; -+ ref_field->size = var_field->size; -+ ref_field->is_signed = var_field->is_signed; -+ ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL); -+ if (!ref_field->name) { ++ if (init_var_ref(ref_field, var_field)) { + destroy_hist_field(ref_field, 0); + return NULL; + } @@ -703,17 +522,50 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return ref_field; +} + -+static struct hist_field *parse_var_ref(char *system, char *event_name, ++static bool is_var_ref(char *var_name) ++{ ++ if (!var_name || strlen(var_name) < 2 || var_name[0] != '$') ++ return false; ++ ++ return true; ++} ++ + static char *field_name_from_var(struct hist_trigger_data *hist_data, + char *var_name) + { +@@ -779,7 +1174,7 @@ static char *field_name_from_var(struct + + if (strcmp(var_name, name) == 0) { + field = hist_data->attrs->var_defs.expr[i]; +- if (contains_operator(field)) ++ if (contains_operator(field) || is_var_ref(field)) + continue; + return field; + } +@@ -791,11 +1186,32 @@ static char *field_name_from_var(struct + static char *local_field_var_ref(struct hist_trigger_data *hist_data, + char *var_name) + { ++ if (!is_var_ref(var_name)) ++ return NULL; ++ + var_name++; + + return field_name_from_var(hist_data, var_name); + } + ++static struct hist_field *parse_var_ref(struct trace_array *tr, ++ char *system, char *event_name, + char *var_name) +{ + struct hist_field *var_field = NULL, *ref_field = NULL; + -+ if (!var_name || strlen(var_name) < 2 || var_name[0] != '$') ++ if (!is_var_ref(var_name)) + return NULL; + + var_name++; + -+ var_field = find_event_var(system, event_name, var_name); ++ var_field = find_event_var(tr, system, event_name, var_name); + if (var_field) + ref_field = create_var_ref(var_field); + @@ -723,15 +575,19 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static struct ftrace_event_field * parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, char *field_str, unsigned long *flags) -@@ -715,10 +1122,28 @@ struct hist_field *parse_atom(struct his +@@ -852,13 +1268,31 @@ struct hist_field *parse_atom(struct his struct trace_event_file *file, char *str, unsigned long *flags, char *var_name) { +- char *s; + char *s, *ref_system = NULL, *ref_event = NULL, *ref_var = str; ++ struct trace_array *tr = hist_data->event_file->tr; struct ftrace_event_field *field = NULL; struct hist_field *hist_field = NULL; int ret = 0; +- s = local_field_var_ref(hist_data, str); +- if (s) + s = strchr(str, '.'); + if (s) { + s = strchr(++s, '.'); @@ -742,17 +598,19 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + } + } + -+ hist_field = parse_var_ref(ref_system, ref_event, ref_var); -+ if (hist_field) { -+ hist_data->var_refs[hist_data->n_var_refs] = hist_field; -+ hist_field->var_ref_idx = hist_data->n_var_refs++; -+ return hist_field; -+ } -+ ++ s = local_field_var_ref(hist_data, ref_var); ++ if (!s) { ++ hist_field = parse_var_ref(tr, ref_system, ref_event, ref_var); ++ if (hist_field) { ++ hist_data->var_refs[hist_data->n_var_refs] = hist_field; ++ hist_field->var_ref_idx = hist_data->n_var_refs++; ++ return hist_field; ++ } ++ } else + str = s; + field = parse_field(hist_data, file, str, flags); - if (IS_ERR(field)) { - ret = PTR_ERR(field); -@@ -885,6 +1310,9 @@ static struct hist_field *parse_expr(str +@@ -1029,6 +1463,9 @@ static struct hist_field *parse_expr(str goto free; } @@ -762,7 +620,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> expr->operands[0] = operand1; expr->operands[1] = operand2; expr->operator = field_op; -@@ -926,43 +1354,6 @@ static int create_hitcount_val(struct hi +@@ -1075,43 +1512,6 @@ static int create_hitcount_val(struct hi return 0; } @@ -803,11 +661,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> - return NULL; -} - - static int create_val_field(struct hist_trigger_data *hist_data, - unsigned int val_idx, - struct trace_event_file *file, -@@ -1119,6 +1510,12 @@ static int create_key_field(struct hist_ - } + static int __create_val_field(struct hist_trigger_data *hist_data, + unsigned int val_idx, + struct trace_event_file *file, +@@ -1245,6 +1645,12 @@ static int create_key_field(struct hist_ + goto out; } + if (hist_field->flags & HIST_FIELD_FL_VAR_REF) { @@ -819,50 +677,28 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> key_size = hist_field->size; } -@@ -1378,21 +1775,6 @@ static int create_tracing_map_fields(str - return 0; - } - --static bool need_tracing_map_ops(struct hist_trigger_data *hist_data) --{ -- struct hist_field *key_field; -- unsigned int i; -- -- for_each_hist_key_field(i, hist_data) { -- key_field = hist_data->fields[i]; -- -- if (key_field->flags & HIST_FIELD_FL_EXECNAME) -- return true; -- } -- -- return false; --} -- - static struct hist_trigger_data * - create_hist_data(unsigned int map_bits, - struct hist_trigger_attrs *attrs, -@@ -1418,8 +1800,7 @@ create_hist_data(unsigned int map_bits, - if (ret) - goto free; +@@ -1580,6 +1986,7 @@ create_hist_data(unsigned int map_bits, -- if (need_tracing_map_ops(hist_data)) -- map_ops = &hist_trigger_elt_comm_ops; -+ map_ops = &hist_trigger_elt_data_ops; + hist_data->attrs = attrs; + hist_data->remove = remove; ++ hist_data->event_file = file; - hist_data->map = tracing_map_create(map_bits, hist_data->key_size, - map_ops, hist_data); -@@ -1433,10 +1814,6 @@ create_hist_data(unsigned int map_bits, + ret = create_hist_fields(hist_data, file); + if (ret) +@@ -1602,12 +2009,6 @@ create_hist_data(unsigned int map_bits, + ret = create_tracing_map_fields(hist_data); if (ret) goto free; - +- - ret = tracing_map_init(hist_data->map); - if (ret) - goto free; - - hist_data->event_file = file; +- hist_data->event_file = file; out: return hist_data; -@@ -1452,15 +1829,20 @@ create_hist_data(unsigned int map_bits, + free: +@@ -1622,12 +2023,17 @@ create_hist_data(unsigned int map_bits, static void hist_trigger_elt_update(struct hist_trigger_data *hist_data, struct tracing_map_elt *elt, void *rec, @@ -880,43 +716,16 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + for_each_hist_val_field(i, hist_data) { hist_field = hist_data->fields[i]; -- hist_val = hist_field->fn(hist_field, rbe, rec); -+ hist_val = hist_field->fn(hist_field, elt, rbe, rec); - if (hist_field->flags & HIST_FIELD_FL_VAR) { - var_idx = hist_field->var.idx; - tracing_map_set_var(elt, var_idx, hist_val); -@@ -1473,7 +1855,7 @@ static void hist_trigger_elt_update(stru - for_each_hist_key_field(i, hist_data) { - hist_field = hist_data->fields[i]; - if (hist_field->flags & HIST_FIELD_FL_VAR) { -- hist_val = hist_field->fn(hist_field, rbe, rec); -+ hist_val = hist_field->fn(hist_field, elt, rbe, rec); - var_idx = hist_field->var.idx; - tracing_map_set_var(elt, var_idx, hist_val); - } -@@ -1510,10 +1892,11 @@ static void event_hist_trigger(struct ev + hist_val = hist_field->fn(hist_field, elt, rbe, rec); +@@ -1680,6 +2086,7 @@ static void event_hist_trigger(struct ev struct hist_trigger_data *hist_data = data->private_data; bool use_compound_key = (hist_data->n_keys > 1); unsigned long entries[HIST_STACKTRACE_DEPTH]; + u64 var_ref_vals[TRACING_MAP_VARS_MAX]; char compound_key[HIST_KEY_SIZE_MAX]; -+ struct tracing_map_elt *elt = NULL; + struct tracing_map_elt *elt = NULL; struct stack_trace stacktrace; - struct hist_field *key_field; -- struct tracing_map_elt *elt; - u64 field_contents; - void *key = NULL; - unsigned int i; -@@ -1534,7 +1917,7 @@ static void event_hist_trigger(struct ev - - key = entries; - } else { -- field_contents = key_field->fn(key_field, rec, rbe); -+ field_contents = key_field->fn(key_field, elt, rbe, rec); - if (key_field->flags & HIST_FIELD_FL_STRING) { - key = (void *)(unsigned long)field_contents; - use_compound_key = true; -@@ -1549,9 +1932,15 @@ static void event_hist_trigger(struct ev +@@ -1719,9 +2126,15 @@ static void event_hist_trigger(struct ev if (use_compound_key) key = compound_key; @@ -934,17 +743,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } static void hist_trigger_stacktrace_print(struct seq_file *m, -@@ -1608,7 +1997,8 @@ hist_trigger_entry_print(struct seq_file - seq_printf(m, "%s: [%llx] %-55s", field_name, - uval, str); - } else if (key_field->flags & HIST_FIELD_FL_EXECNAME) { -- char *comm = elt->private_data; -+ struct hist_elt_data *elt_data = elt->private_data; -+ char *comm = elt_data->comm; - - uval = *(u64 *)(key + key_field->offset); - seq_printf(m, "%s: %-16s[%10llu]", field_name, -@@ -1653,7 +2043,8 @@ hist_trigger_entry_print(struct seq_file +@@ -1824,7 +2237,8 @@ hist_trigger_entry_print(struct seq_file field_name = hist_field_name(hist_data->fields[i], 0); if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR || @@ -954,7 +753,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> continue; if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) { -@@ -1925,7 +2316,11 @@ static void event_hist_trigger_free(stru +@@ -2074,7 +2488,11 @@ static void event_hist_trigger_free(stru if (!data->ref) { if (data->name) del_named_trigger(data); @@ -966,7 +765,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> destroy_hist_data(hist_data); } } -@@ -2139,23 +2534,55 @@ static int hist_register_trigger(char *g +@@ -2288,23 +2706,55 @@ static int hist_register_trigger(char *g goto out; } @@ -1026,7 +825,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, struct event_trigger_data *data, struct trace_event_file *file) -@@ -2186,10 +2613,32 @@ static void hist_unregister_trigger(char +@@ -2335,10 +2785,30 @@ static void hist_unregister_trigger(char tracing_set_time_stamp_abs(file->tr, false); } @@ -1035,8 +834,6 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + struct hist_trigger_data *hist_data; + struct event_trigger_data *test; + -+ printk("func: %s\n", __func__); -+ + list_for_each_entry_rcu(test, &file->triggers, list) { + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { + hist_data = test->private_data; @@ -1054,12 +851,12 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> struct event_trigger_data *test, *n; + if (hist_file_check_refs(file)) -+ return; ++ return; + list_for_each_entry_safe(test, n, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { list_del_rcu(&test->list); -@@ -2262,6 +2711,11 @@ static int event_hist_trigger_func(struc +@@ -2411,6 +2881,11 @@ static int event_hist_trigger_func(struc } if (remove) { @@ -1071,7 +868,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); ret = 0; goto out_free; -@@ -2279,14 +2733,33 @@ static int event_hist_trigger_func(struc +@@ -2428,14 +2903,33 @@ static int event_hist_trigger_func(struc goto out_free; } else if (ret < 0) goto out_free; @@ -1107,7 +904,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> destroy_hist_data(hist_data); --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c -@@ -919,6 +919,12 @@ void set_named_trigger_data(struct event +@@ -909,6 +909,12 @@ void set_named_trigger_data(struct event data->named_data = named_data; } diff --git a/patches/0025-tracing-Add-support-for-dynamic-tracepoints.patch b/patches/0025-tracing-Add-support-for-dynamic-tracepoints.patch new file mode 100644 index 000000000000..153daef07243 --- /dev/null +++ b/patches/0025-tracing-Add-support-for-dynamic-tracepoints.patch @@ -0,0 +1,78 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Tue, 5 Sep 2017 16:57:37 -0500 +Subject: [PATCH 25/40] tracing: Add support for dynamic tracepoints + +The tracepoint infrastructure assumes statically-defined tracepoints +and uses static_keys for tracepoint enablement. In order to define +tracepoints on the fly, we need to have a dynamic counterpart. + +Add a 'dynamic' flag to struct tracepoint along with accompanying +logic for this purpose. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + include/linux/tracepoint-defs.h | 1 + + kernel/tracepoint.c | 18 +++++++++++++----- + 2 files changed, 14 insertions(+), 5 deletions(-) + +--- a/include/linux/tracepoint-defs.h ++++ b/include/linux/tracepoint-defs.h +@@ -32,6 +32,7 @@ struct tracepoint { + int (*regfunc)(void); + void (*unregfunc)(void); + struct tracepoint_func __rcu *funcs; ++ bool dynamic; + }; + + #endif +--- a/kernel/tracepoint.c ++++ b/kernel/tracepoint.c +@@ -197,7 +197,9 @@ static int tracepoint_add_func(struct tr + struct tracepoint_func *old, *tp_funcs; + int ret; + +- if (tp->regfunc && !static_key_enabled(&tp->key)) { ++ if (tp->regfunc && ++ ((tp->dynamic && !(atomic_read(&tp->key.enabled) > 0)) || ++ !static_key_enabled(&tp->key))) { + ret = tp->regfunc(); + if (ret < 0) + return ret; +@@ -219,7 +221,9 @@ static int tracepoint_add_func(struct tr + * is used. + */ + rcu_assign_pointer(tp->funcs, tp_funcs); +- if (!static_key_enabled(&tp->key)) ++ if (tp->dynamic && !(atomic_read(&tp->key.enabled) > 0)) ++ atomic_inc(&tp->key.enabled); ++ else if (!tp->dynamic && !static_key_enabled(&tp->key)) + static_key_slow_inc(&tp->key); + release_probes(old); + return 0; +@@ -246,10 +250,14 @@ static int tracepoint_remove_func(struct + + if (!tp_funcs) { + /* Removed last function */ +- if (tp->unregfunc && static_key_enabled(&tp->key)) ++ if (tp->unregfunc && ++ ((tp->dynamic && (atomic_read(&tp->key.enabled) > 0)) || ++ static_key_enabled(&tp->key))) + tp->unregfunc(); + +- if (static_key_enabled(&tp->key)) ++ if (tp->dynamic && (atomic_read(&tp->key.enabled) > 0)) ++ atomic_dec(&tp->key.enabled); ++ else if (!tp->dynamic && static_key_enabled(&tp->key)) + static_key_slow_dec(&tp->key); + } + rcu_assign_pointer(tp->funcs, tp_funcs); +@@ -258,7 +266,7 @@ static int tracepoint_remove_func(struct + } + + /** +- * tracepoint_probe_register - Connect a probe to a tracepoint ++ * tracepoint_probe_register_prio - Connect a probe to a tracepoint + * @tp: tracepoint + * @probe: probe handler + * @data: tracepoint data diff --git a/patches/0021-tracing-Add-hist-trigger-action-hook.patch b/patches/0026-tracing-Add-hist-trigger-action-hook.patch index e0913ac9216d..5b693de18759 100644 --- a/patches/0021-tracing-Add-hist-trigger-action-hook.patch +++ b/patches/0026-tracing-Add-hist-trigger-action-hook.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:22 -0500 -Subject: [PATCH 21/32] tracing: Add hist trigger action hook +Date: Tue, 5 Sep 2017 16:57:38 -0500 +Subject: [PATCH 26/40] tracing: Add hist trigger action hook Add a hook for executing extra actions whenever a histogram entry is added or updated. @@ -29,17 +29,17 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> enum field_op_id { FIELD_OP_NONE, -@@ -233,6 +234,9 @@ struct hist_trigger_attrs { - +@@ -241,6 +242,9 @@ struct hist_trigger_attrs { char *assignment_str[TRACING_MAP_VARS_MAX]; unsigned int n_assignments; -+ + + char *action_str[HIST_ACTIONS_MAX]; + unsigned int n_actions; ++ + struct var_defs var_defs; }; - struct hist_trigger_data { -@@ -252,6 +256,21 @@ struct hist_trigger_data { +@@ -261,6 +265,21 @@ struct hist_trigger_data { bool remove; struct hist_field *var_refs[TRACING_MAP_VARS_MAX]; unsigned int n_var_refs; @@ -61,7 +61,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> }; static u64 hist_field_timestamp(struct hist_field *hist_field, -@@ -681,6 +700,9 @@ static void destroy_hist_trigger_attrs(s +@@ -710,6 +729,9 @@ static void destroy_hist_trigger_attrs(s for (i = 0; i < attrs->n_assignments; i++) kfree(attrs->assignment_str[i]); @@ -71,7 +71,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> kfree(attrs->name); kfree(attrs->sort_key_str); kfree(attrs->keys_str); -@@ -688,6 +710,16 @@ static void destroy_hist_trigger_attrs(s +@@ -717,6 +739,16 @@ static void destroy_hist_trigger_attrs(s kfree(attrs); } @@ -88,7 +88,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static int parse_assignment(char *str, struct hist_trigger_attrs *attrs) { int ret = 0; -@@ -755,8 +787,9 @@ static struct hist_trigger_attrs *parse_ +@@ -784,8 +816,9 @@ static struct hist_trigger_attrs *parse_ else if (strcmp(str, "clear") == 0) attrs->clear = true; else { @@ -100,7 +100,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } } -@@ -1722,11 +1755,63 @@ static int create_sort_keys(struct hist_ +@@ -1917,11 +1950,63 @@ static int create_sort_keys(struct hist_ return ret; } @@ -164,7 +164,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> kfree(hist_data); } -@@ -1886,6 +1971,20 @@ static inline void add_to_key(char *comp +@@ -2080,6 +2165,20 @@ static inline void add_to_key(char *comp memcpy(compound_key + key_field->offset, key, size); } @@ -185,7 +185,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static void event_hist_trigger(struct event_trigger_data *data, void *rec, struct ring_buffer_event *rbe) { -@@ -1941,6 +2040,9 @@ static void event_hist_trigger(struct ev +@@ -2135,6 +2234,9 @@ static void event_hist_trigger(struct ev return; hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals); @@ -195,7 +195,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } static void hist_trigger_stacktrace_print(struct seq_file *m, -@@ -2278,6 +2380,8 @@ static int event_hist_trigger_print(stru +@@ -2450,6 +2552,8 @@ static int event_hist_trigger_print(stru } seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits)); @@ -204,7 +204,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (data->filter_str) seq_printf(m, " if %s", data->filter_str); -@@ -2740,6 +2844,10 @@ static int event_hist_trigger_func(struc +@@ -2910,6 +3014,10 @@ static int event_hist_trigger_func(struc if (has_hist_vars(hist_data)) save_hist_vars(hist_data); @@ -215,7 +215,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ret = tracing_map_init(hist_data->map); if (ret) goto out_unreg; -@@ -2761,8 +2869,8 @@ static int event_hist_trigger_func(struc +@@ -2931,8 +3039,8 @@ static int event_hist_trigger_func(struc remove_hist_vars(hist_data); kfree(trigger_data); diff --git a/patches/0026-tracing-Make-duplicate-count-from-tracing_map-availa.patch b/patches/0026-tracing-Make-duplicate-count-from-tracing_map-availa.patch deleted file mode 100644 index aeaca9d7c9b2..000000000000 --- a/patches/0026-tracing-Make-duplicate-count-from-tracing_map-availa.patch +++ /dev/null @@ -1,124 +0,0 @@ -From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:27 -0500 -Subject: [PATCH 26/32] tracing: Make duplicate count from tracing_map - available - -Though extremely rare, there can be duplicate entries in the tracing -map. This isn't normally a problem, as the sorting code makes this -transparent by merging them during the sort. - -It's useful to know however, as a check on that assumption - if a -non-zero duplicate count is seen more than rarely, it might indicate -an unexpected change to the algorithm, or a pathological data set. - -Add an extra param to tracing_map_sort_entries() and use it to display -the value in the hist trigger output. - -Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> -Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ---- - kernel/trace/trace_events_hist.c | 14 ++++++++------ - kernel/trace/tracing_map.c | 12 +++++++++--- - kernel/trace/tracing_map.h | 3 ++- - 3 files changed, 19 insertions(+), 10 deletions(-) - ---- a/kernel/trace/trace_events_hist.c -+++ b/kernel/trace/trace_events_hist.c -@@ -4011,7 +4011,8 @@ hist_trigger_entry_print(struct seq_file - } - - static int print_entries(struct seq_file *m, -- struct hist_trigger_data *hist_data) -+ struct hist_trigger_data *hist_data, -+ unsigned int *n_dups) - { - struct tracing_map_sort_entry **sort_entries = NULL; - struct tracing_map *map = hist_data->map; -@@ -4019,7 +4020,7 @@ static int print_entries(struct seq_file - - n_entries = tracing_map_sort_entries(map, hist_data->sort_keys, - hist_data->n_sort_keys, -- &sort_entries); -+ &sort_entries, n_dups); - if (n_entries < 0) - return n_entries; - -@@ -4038,6 +4039,7 @@ static void hist_trigger_show(struct seq - { - struct hist_trigger_data *hist_data; - int n_entries, ret = 0; -+ unsigned int n_dups; - - if (n > 0) - seq_puts(m, "\n\n"); -@@ -4047,15 +4049,15 @@ static void hist_trigger_show(struct seq - seq_puts(m, "#\n\n"); - - hist_data = data->private_data; -- n_entries = print_entries(m, hist_data); -+ n_entries = print_entries(m, hist_data, &n_dups); - if (n_entries < 0) { - ret = n_entries; - n_entries = 0; - } - -- seq_printf(m, "\nTotals:\n Hits: %llu\n Entries: %u\n Dropped: %llu\n", -- (u64)atomic64_read(&hist_data->map->hits), -- n_entries, (u64)atomic64_read(&hist_data->map->drops)); -+ seq_printf(m, "\nTotals:\n Hits: %llu\n Entries: %u\n Dropped: %llu\n Duplicates: %u\n", -+ (u64)atomic64_read(&hist_data->map->hits), n_entries, -+ (u64)atomic64_read(&hist_data->map->drops), n_dups); - } - - static int hist_show(struct seq_file *m, void *v) ---- a/kernel/trace/tracing_map.c -+++ b/kernel/trace/tracing_map.c -@@ -1084,6 +1084,7 @@ static void sort_secondary(struct tracin - * @map: The tracing_map - * @sort_key: The sort key to use for sorting - * @sort_entries: outval: pointer to allocated and sorted array of entries -+ * @n_dups: outval: pointer to variable receiving a count of duplicates found - * - * tracing_map_sort_entries() sorts the current set of entries in the - * map and returns the list of tracing_map_sort_entries containing -@@ -1100,13 +1101,16 @@ static void sort_secondary(struct tracin - * The client should not hold on to the returned array but should use - * it and call tracing_map_destroy_sort_entries() when done. - * -- * Return: the number of sort_entries in the struct tracing_map_sort_entry -- * array, negative on error -+ * Return: the number of sort_entries in the struct -+ * tracing_map_sort_entry array, negative on error. If n_dups is -+ * non-NULL, it will receive the number of duplicate entries found -+ * (and merged) during the sort. - */ - int tracing_map_sort_entries(struct tracing_map *map, - struct tracing_map_sort_key *sort_keys, - unsigned int n_sort_keys, -- struct tracing_map_sort_entry ***sort_entries) -+ struct tracing_map_sort_entry ***sort_entries, -+ unsigned int *n_dups) - { - int (*cmp_entries_fn)(const struct tracing_map_sort_entry **, - const struct tracing_map_sort_entry **); -@@ -1147,6 +1151,8 @@ int tracing_map_sort_entries(struct trac - if (ret < 0) - goto free; - n_entries -= ret; -+ if (n_dups) -+ *n_dups = ret; - - if (is_key(map, sort_keys[0].field_idx)) - cmp_entries_fn = cmp_entries_key; ---- a/kernel/trace/tracing_map.h -+++ b/kernel/trace/tracing_map.h -@@ -286,7 +286,8 @@ extern int - tracing_map_sort_entries(struct tracing_map *map, - struct tracing_map_sort_key *sort_keys, - unsigned int n_sort_keys, -- struct tracing_map_sort_entry ***sort_entries); -+ struct tracing_map_sort_entry ***sort_entries, -+ unsigned int *n_dups); - - extern void - tracing_map_destroy_sort_entries(struct tracing_map_sort_entry **entries, diff --git a/patches/0022-tracing-Add-support-for-synthetic-events.patch b/patches/0027-tracing-Add-support-for-synthetic-events.patch index 24cf13a45be3..945a4d9c6e19 100644 --- a/patches/0022-tracing-Add-support-for-synthetic-events.patch +++ b/patches/0027-tracing-Add-support-for-synthetic-events.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:23 -0500 -Subject: [PATCH 22/32] tracing: Add support for 'synthetic' events +Date: Tue, 5 Sep 2017 16:57:39 -0500 +Subject: [PATCH 27/40] tracing: Add support for 'synthetic' events Synthetic events are user-defined events generated from hist trigger variables saved from one or more other events. @@ -47,12 +47,12 @@ discussed in a subsequent patch. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - kernel/trace/trace_events_hist.c | 738 +++++++++++++++++++++++++++++++++++++++ - 1 file changed, 738 insertions(+) + kernel/trace/trace_events_hist.c | 863 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 863 insertions(+) --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -20,10 +20,14 @@ +@@ -20,10 +20,16 @@ #include <linux/slab.h> #include <linux/stacktrace.h> #include <linux/rculist.h> @@ -64,25 +64,30 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +#define SYNTH_SYSTEM "synthetic" +#define SYNTH_FIELDS_MAX 16 + ++#define STR_VAR_LEN_MAX 32 /* must be multiple of sizeof(u64) */ ++ struct hist_field; typedef u64 (*hist_field_fn_t) (struct hist_field *field, -@@ -261,6 +265,23 @@ struct hist_trigger_data { +@@ -270,6 +276,26 @@ struct hist_trigger_data { unsigned int n_actions; }; +struct synth_field { + char *type; + char *name; -+ unsigned int size; ++ size_t size; + bool is_signed; ++ bool is_string; +}; + +struct synth_event { + struct list_head list; ++ int ref; + char *name; + struct synth_field **fields; + unsigned int n_fields; ++ unsigned int n_u64; + struct trace_event_class class; + struct trace_event_call call; + struct tracepoint *tp; @@ -91,7 +96,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> struct action_data; typedef void (*action_fn_t) (struct hist_trigger_data *hist_data, -@@ -273,6 +294,688 @@ struct action_data { +@@ -282,6 +308,798 @@ struct action_data { unsigned int var_ref_idx; }; @@ -100,7 +105,6 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + +struct synth_trace_event { + struct trace_entry ent; -+ int n_fields; + u64 fields[]; +}; + @@ -109,24 +113,163 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + struct synth_trace_event trace; + int offset = offsetof(typeof(trace), fields); + struct synth_event *event = call->data; -+ unsigned int i, size; ++ unsigned int i, size, n_u64; + char *name, *type; + bool is_signed; + int ret = 0; + -+ for (i = 0; i < event->n_fields; i++) { ++ for (i = 0, n_u64 = 0; i < event->n_fields; i++) { + size = event->fields[i]->size; + is_signed = event->fields[i]->is_signed; + type = event->fields[i]->type; + name = event->fields[i]->name; + ret = trace_define_field(call, type, name, offset, size, + is_signed, FILTER_OTHER); -+ offset += sizeof(u64); ++ if (ret) ++ break; ++ ++ if (event->fields[i]->is_string) { ++ offset += STR_VAR_LEN_MAX; ++ n_u64 += STR_VAR_LEN_MAX / sizeof(u64); ++ } else { ++ offset += sizeof(u64); ++ n_u64++; ++ } + } + ++ event->n_u64 = n_u64; ++ + return ret; +} + ++static bool synth_field_signed(char *type) ++{ ++ if (strncmp(type, "u", 1) == 0) ++ return false; ++ ++ return true; ++} ++ ++static int synth_field_is_string(char *type) ++{ ++ if (strstr(type, "char[") != NULL) ++ return true; ++ ++ return false; ++} ++ ++static int synth_field_string_size(char *type) ++{ ++ char buf[4], *end, *start; ++ unsigned int len; ++ int size, err; ++ ++ start = strstr(type, "char["); ++ if (start == NULL) ++ return -EINVAL; ++ start += strlen("char["); ++ ++ end = strchr(type, ']'); ++ if (!end || end < start) ++ return -EINVAL; ++ ++ len = end - start; ++ if (len > 2) ++ return -EINVAL; ++ ++ strncpy(buf, start, len); ++ buf[len] = '\0'; ++ ++ err = kstrtouint(buf, 0, &size); ++ if (err) ++ return err; ++ ++ if (size > STR_VAR_LEN_MAX) ++ return -EINVAL; ++ ++ return size; ++} ++ ++static int synth_field_size(char *type) ++{ ++ int size = 0; ++ ++ if (strcmp(type, "s64") == 0) ++ size = sizeof(s64); ++ else if (strcmp(type, "u64") == 0) ++ size = sizeof(u64); ++ else if (strcmp(type, "s32") == 0) ++ size = sizeof(s32); ++ else if (strcmp(type, "u32") == 0) ++ size = sizeof(u32); ++ else if (strcmp(type, "s16") == 0) ++ size = sizeof(s16); ++ else if (strcmp(type, "u16") == 0) ++ size = sizeof(u16); ++ else if (strcmp(type, "s8") == 0) ++ size = sizeof(s8); ++ else if (strcmp(type, "u8") == 0) ++ size = sizeof(u8); ++ else if (strcmp(type, "char") == 0) ++ size = sizeof(char); ++ else if (strcmp(type, "unsigned char") == 0) ++ size = sizeof(unsigned char); ++ else if (strcmp(type, "int") == 0) ++ size = sizeof(int); ++ else if (strcmp(type, "unsigned int") == 0) ++ size = sizeof(unsigned int); ++ else if (strcmp(type, "long") == 0) ++ size = sizeof(long); ++ else if (strcmp(type, "unsigned long") == 0) ++ size = sizeof(unsigned long); ++ else if (strcmp(type, "pid_t") == 0) ++ size = sizeof(pid_t); ++ else if (synth_field_is_string(type)) ++ size = synth_field_string_size(type); ++ ++ return size; ++} ++ ++static const char *synth_field_fmt(char *type) ++{ ++ const char *fmt = "%llu"; ++ ++ if (strcmp(type, "s64") == 0) ++ fmt = "%lld"; ++ else if (strcmp(type, "u64") == 0) ++ fmt = "%llu"; ++ else if (strcmp(type, "s32") == 0) ++ fmt = "%d"; ++ else if (strcmp(type, "u32") == 0) ++ fmt = "%u"; ++ else if (strcmp(type, "s16") == 0) ++ fmt = "%d"; ++ else if (strcmp(type, "u16") == 0) ++ fmt = "%u"; ++ else if (strcmp(type, "s8") == 0) ++ fmt = "%d"; ++ else if (strcmp(type, "u8") == 0) ++ fmt = "%u"; ++ else if (strcmp(type, "char") == 0) ++ fmt = "%d"; ++ else if (strcmp(type, "unsigned char") == 0) ++ fmt = "%u"; ++ else if (strcmp(type, "int") == 0) ++ fmt = "%d"; ++ else if (strcmp(type, "unsigned int") == 0) ++ fmt = "%u"; ++ else if (strcmp(type, "long") == 0) ++ fmt = "%ld"; ++ else if (strcmp(type, "unsigned long") == 0) ++ fmt = "%lu"; ++ else if (strcmp(type, "pid_t") == 0) ++ fmt = "%d"; ++ else if (strstr(type, "[") == 0) ++ fmt = "%s"; ++ ++ return fmt; ++} ++ +static enum print_line_t print_synth_event(struct trace_iterator *iter, + int flags, + struct trace_event *event) @@ -135,25 +278,39 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + struct trace_seq *s = &iter->seq; + struct synth_trace_event *entry; + struct synth_event *se; -+ unsigned int i; ++ unsigned int i, n_u64; ++ char print_fmt[32]; ++ const char *fmt; + + entry = (struct synth_trace_event *)iter->ent; + se = container_of(event, struct synth_event, call.event); + + trace_seq_printf(s, "%s: ", se->name); + -+ for (i = 0; i < entry->n_fields; i++) { ++ for (i = 0, n_u64 = 0; i < se->n_fields; i++) { + if (trace_seq_has_overflowed(s)) + goto end; + ++ fmt = synth_field_fmt(se->fields[i]->type); ++ + /* parameter types */ + if (tr->trace_flags & TRACE_ITER_VERBOSE) -+ trace_seq_printf(s, "%s ", "u64"); ++ trace_seq_printf(s, "%s ", fmt); ++ ++ sprintf(print_fmt, "%%s=%s%%s", fmt); + + /* parameter values */ -+ trace_seq_printf(s, "%s=%llu%s", se->fields[i]->name, -+ entry->fields[i], -+ i == entry->n_fields - 1 ? "" : ", "); ++ if (se->fields[i]->is_string) { ++ trace_seq_printf(s, print_fmt, se->fields[i]->name, ++ (char *)entry->fields[n_u64], ++ i == se->n_fields - 1 ? "" : " "); ++ n_u64 += STR_VAR_LEN_MAX / sizeof(u64); ++ } else { ++ trace_seq_printf(s, print_fmt, se->fields[i]->name, ++ entry->fields[n_u64], ++ i == se->n_fields - 1 ? "" : " "); ++ n_u64++; ++ } + } +end: + trace_seq_putc(s, '\n'); @@ -172,27 +329,34 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + struct trace_event_file *trace_file = __data; + struct synth_trace_event *entry; + struct trace_event_buffer fbuffer; -+ int fields_size; -+ unsigned int i; -+ + struct synth_event *event; ++ unsigned int i, n_u64; ++ int fields_size = 0; + + event = trace_file->event_call->data; + + if (trace_trigger_soft_disabled(trace_file)) + return; + -+ fields_size = event->n_fields * sizeof(u64); ++ fields_size = event->n_u64 * sizeof(u64); + + entry = trace_event_buffer_reserve(&fbuffer, trace_file, + sizeof(*entry) + fields_size); + if (!entry) + return; + -+ entry->n_fields = event->n_fields; ++ 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_field = (char *)&entry->fields[n_u64]; + -+ for (i = 0; i < event->n_fields; i++) -+ entry->fields[i] = var_ref_vals[var_ref_idx + i]; ++ strncpy(str_field, str_val, STR_VAR_LEN_MAX); ++ n_u64 += STR_VAR_LEN_MAX / sizeof(u64); ++ } else { ++ entry->fields[i] = var_ref_vals[var_ref_idx + i]; ++ n_u64++; ++ } ++ } + + trace_event_buffer_commit(&fbuffer); +} @@ -206,6 +370,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +static int __set_synth_event_print_fmt(struct synth_event *event, + char *buf, int len) +{ ++ const char *fmt; + int pos = 0; + int i; + @@ -214,15 +379,16 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + + pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); + for (i = 0; i < event->n_fields; i++) { -+ pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx%s", -+ event->fields[i]->name, sizeof(u64), ++ fmt = synth_field_fmt(event->fields[i]->type); ++ pos += snprintf(buf + pos, LEN_OR_ZERO, "%s=%s%s", ++ event->fields[i]->name, fmt, + i == event->n_fields - 1 ? "" : ", "); + } + pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); + + for (i = 0; i < event->n_fields; i++) { + pos += snprintf(buf + pos, LEN_OR_ZERO, -+ ", ((u64)(REC->%s))", event->fields[i]->name); ++ ", REC->%s", event->fields[i]->name); + } + +#undef LEN_OR_ZERO @@ -251,43 +417,6 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return 0; +} + -+int dynamic_trace_event_reg(struct trace_event_call *call, -+ enum trace_reg type, void *data) -+{ -+ struct trace_event_file *file = data; -+ -+ WARN_ON(!(call->flags & TRACE_EVENT_FL_TRACEPOINT)); -+ switch (type) { -+ case TRACE_REG_REGISTER: -+ return dynamic_tracepoint_probe_register(call->tp, -+ call->class->probe, -+ file); -+ case TRACE_REG_UNREGISTER: -+ tracepoint_probe_unregister(call->tp, -+ call->class->probe, -+ file, true); -+ return 0; -+ -+#ifdef CONFIG_PERF_EVENTS -+ case TRACE_REG_PERF_REGISTER: -+ return dynamic_tracepoint_probe_register(call->tp, -+ call->class->perf_probe, -+ call); -+ case TRACE_REG_PERF_UNREGISTER: -+ tracepoint_probe_unregister(call->tp, -+ call->class->perf_probe, -+ call, true); -+ return 0; -+ case TRACE_REG_PERF_OPEN: -+ case TRACE_REG_PERF_CLOSE: -+ case TRACE_REG_PERF_ADD: -+ case TRACE_REG_PERF_DEL: -+ return 0; -+#endif -+ } -+ return 0; -+} -+ +static void free_synth_field(struct synth_field *field) +{ + kfree(field->type); @@ -295,54 +424,6 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + kfree(field); +} + -+static bool synth_field_signed(char *type) -+{ -+ if (strncmp(type, "u", 1) == 0) -+ return false; -+ -+ return true; -+} -+ -+static unsigned int synth_field_size(char *type) -+{ -+ unsigned int size = 0; -+ -+ if (strcmp(type, "s64") == 0) -+ size = sizeof(s64); -+ else if (strcmp(type, "u64") == 0) -+ size = sizeof(u64); -+ else if (strcmp(type, "s32") == 0) -+ size = sizeof(s32); -+ else if (strcmp(type, "u32") == 0) -+ size = sizeof(u32); -+ else if (strcmp(type, "s16") == 0) -+ size = sizeof(s16); -+ else if (strcmp(type, "u16") == 0) -+ size = sizeof(u16); -+ else if (strcmp(type, "s8") == 0) -+ size = sizeof(s8); -+ else if (strcmp(type, "u8") == 0) -+ size = sizeof(u8); -+ else if (strcmp(type, "char") == 0) -+ size = sizeof(char); -+ else if (strcmp(type, "unsigned char") == 0) -+ size = sizeof(unsigned char); -+ else if (strcmp(type, "int") == 0) -+ size = sizeof(int); -+ else if (strcmp(type, "unsigned int") == 0) -+ size = sizeof(unsigned int); -+ else if (strcmp(type, "long") == 0) -+ size = sizeof(long); -+ else if (strcmp(type, "unsigned long") == 0) -+ size = sizeof(unsigned long); -+ else if (strcmp(type, "pid_t") == 0) -+ size = sizeof(pid_t); -+ else if (strstr(type, "[") == 0) -+ size = sizeof(u64); -+ -+ return size; -+} -+ +static struct synth_field *parse_synth_field(char *field_type, + char *field_name) +{ @@ -371,8 +452,10 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + goto free; + } + strcat(field->type, field_type); -+ if (array) ++ if (array) { + strcat(field->type, array); ++ *array = '\0'; ++ } + + field->size = synth_field_size(field->type); + if (!field->size) { @@ -380,6 +463,9 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + goto free; + } + ++ if (synth_field_is_string(field->type)) ++ field->is_string = true; ++ + field->is_signed = synth_field_signed(field->type); + + field->name = kstrdup(field_name, GFP_KERNEL); @@ -421,6 +507,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + goto free; + } + ++ tp->dynamic = true; ++ + return tp; + free: + free_synth_tracepoint(tp); @@ -428,26 +516,29 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return ERR_PTR(ret); +} + ++typedef void (*synth_probe_func_t) (void *__data, u64 *var_ref_vals, ++ unsigned int var_ref_idx); ++ +static inline void trace_synth(struct synth_event *event, u64 *var_ref_vals, + unsigned int var_ref_idx) +{ + struct tracepoint *tp = event->tp; + + if (unlikely(atomic_read(&tp->key.enabled) > 0)) { -+ struct tracepoint_func *it_func_ptr; -+ void *it_func; ++ struct tracepoint_func *probe_func_ptr; ++ synth_probe_func_t probe_func; + void *__data; + + if (!(cpu_online(raw_smp_processor_id()))) + return; + -+ it_func_ptr = rcu_dereference_sched((tp)->funcs); -+ if (it_func_ptr) { ++ probe_func_ptr = rcu_dereference_sched((tp)->funcs); ++ if (probe_func_ptr) { + do { -+ it_func = (it_func_ptr)->func; -+ __data = (it_func_ptr)->data; -+ ((void(*)(void *__data, u64 *var_ref_vals, unsigned int var_ref_idx))(it_func))(__data, var_ref_vals, var_ref_idx); -+ } while ((++it_func_ptr)->func); ++ 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); + } + } +} @@ -493,11 +584,14 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + goto out; + } + call->flags = TRACE_EVENT_FL_TRACEPOINT; -+ call->class->reg = dynamic_trace_event_reg; ++ call->class->reg = trace_event_reg; + call->class->probe = trace_event_raw_event_synth; + call->data = event; + call->tp = event->tp; ++ ++ mutex_unlock(&synth_event_mutex); + ret = trace_add_event_call(call); ++ mutex_lock(&synth_event_mutex); + if (ret) { + pr_warn("Failed to register synthetic event: %s\n", + trace_event_name(call)); @@ -506,7 +600,9 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + + ret = set_synth_event_print_fmt(call); + if (ret < 0) { ++ mutex_unlock(&synth_event_mutex); + trace_remove_event_call(call); ++ mutex_lock(&synth_event_mutex); + goto err; + } + out: @@ -521,7 +617,9 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + struct trace_event_call *call = &event->call; + int ret; + ++ 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)); @@ -605,7 +703,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + +static int create_synth_event(int argc, char **argv) +{ -+ struct synth_field *fields[SYNTH_FIELDS_MAX]; ++ struct synth_field *field, *fields[SYNTH_FIELDS_MAX]; + struct synth_event *event = NULL; + bool delete_event = false; + int i, n_fields = 0, ret = 0; @@ -621,7 +719,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + */ + if (argc < 1) { + ret = -EINVAL; -+ goto err; ++ goto out; + } + + name = argv[0]; @@ -633,10 +731,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + event = find_synth_event(name); + if (event) { + if (delete_event) { ++ if (event->ref) { ++ ret = -EBUSY; ++ goto out; ++ } + remove_synth_event(event); -+ goto err; -+ } else -+ ret = -EEXIST; ++ free_synth_event(event); ++ goto out; ++ } ++ ret = -EEXIST; + goto out; + } else if (delete_event) { + ret = -EINVAL; @@ -645,7 +748,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + + if (argc < 2) { + ret = -EINVAL; -+ goto err; ++ goto out; + } + + for (i = 1; i < argc - 1; i++) { @@ -653,16 +756,21 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + continue; + if (n_fields == SYNTH_FIELDS_MAX) { + ret = -EINVAL; -+ goto out; ++ goto err; + } -+ fields[n_fields] = parse_synth_field(argv[i], argv[i + 1]); -+ if (!fields[n_fields]) ++ ++ field = parse_synth_field(argv[i], argv[i + 1]); ++ if (IS_ERR(field)) { ++ ret = PTR_ERR(field); + goto err; ++ } ++ fields[n_fields] = field; + i++; n_fields++; + } ++ + if (i < argc) { + ret = -EINVAL; -+ goto out; ++ goto err; + } + + event = alloc_synth_event(name, n_fields, fields); @@ -692,11 +800,18 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + + mutex_lock(&synth_event_mutex); + ++ list_for_each_entry(event, &synth_event_list, list) { ++ if (event->ref) { ++ ret = -EBUSY; ++ goto out; ++ } ++ } ++ + list_for_each_entry_safe(event, e, &synth_event_list, list) { + remove_synth_event(event); + free_synth_event(event); + } -+ ++ out: + mutex_unlock(&synth_event_mutex); + + return ret; @@ -780,7 +895,45 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static u64 hist_field_timestamp(struct hist_field *hist_field, struct tracing_map_elt *elt, struct ring_buffer_event *rbe, -@@ -3028,3 +3731,38 @@ static __init void unregister_trigger_hi +@@ -2933,6 +3751,8 @@ static int event_hist_trigger_func(struc + struct hist_trigger_attrs *attrs; + struct event_trigger_ops *trigger_ops; + struct hist_trigger_data *hist_data; ++ struct synth_event *se; ++ const char *se_name; + bool remove = false; + char *trigger; + int ret = 0; +@@ -2991,6 +3811,14 @@ static int event_hist_trigger_func(struc + } + + cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); ++ ++ mutex_lock(&synth_event_mutex); ++ se_name = trace_event_name(file->event_call); ++ se = find_synth_event(se_name); ++ if (se) ++ se->ref--; ++ mutex_unlock(&synth_event_mutex); ++ + ret = 0; + goto out_free; + } +@@ -3008,6 +3836,13 @@ static int event_hist_trigger_func(struc + } else if (ret < 0) + goto out_free; + ++ mutex_lock(&synth_event_mutex); ++ se_name = trace_event_name(file->event_call); ++ se = find_synth_event(se_name); ++ if (se) ++ se->ref++; ++ mutex_unlock(&synth_event_mutex); ++ + if (get_named_trigger_data(trigger_data)) + goto enable; + +@@ -3198,3 +4033,31 @@ static __init void unregister_trigger_hi return ret; } @@ -788,16 +941,9 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +static __init int trace_events_hist_init(void) +{ + struct dentry *entry = NULL; -+ struct trace_array *tr; + struct dentry *d_tracer; + int err = 0; + -+ tr = top_trace_array(); -+ if (!tr) { -+ err = -ENODEV; -+ goto err; -+ } -+ + d_tracer = tracing_init_dentry(); + if (IS_ERR(d_tracer)) { + err = PTR_ERR(d_tracer); @@ -805,7 +951,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + } + + entry = tracefs_create_file("synthetic_events", 0644, d_tracer, -+ tr, &synth_events_fops); ++ NULL, &synth_events_fops); + if (!entry) { + err = -ENODEV; + goto err; diff --git a/patches/0028-tracing-Add-support-for-field-variables.patch b/patches/0028-tracing-Add-support-for-field-variables.patch new file mode 100644 index 000000000000..ecadd73b2aaf --- /dev/null +++ b/patches/0028-tracing-Add-support-for-field-variables.patch @@ -0,0 +1,567 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Tue, 5 Sep 2017 16:57:40 -0500 +Subject: [PATCH 28/40] tracing: Add support for 'field variables' + +Users should be able to directly specify event fields in hist trigger +'actions' rather than being forced to explicitly create a variable for +that purpose. + +Add support allowing fields to be used directly in actions, which +essentially does just that - creates 'invisible' variables for each +bare field specified in an action. If a bare field refers to a field +on another (matching) event, it even creates a special histogram for +the purpose (since variables can't be defined on an existing histogram +after histogram creation). + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace_events_hist.c | 452 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 451 insertions(+), 1 deletion(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -254,6 +254,16 @@ struct hist_trigger_attrs { + struct var_defs var_defs; + }; + ++struct field_var { ++ struct hist_field *var; ++ struct hist_field *val; ++}; ++ ++struct field_var_hist { ++ struct hist_trigger_data *hist_data; ++ char *cmd; ++}; ++ + struct hist_trigger_data { + struct hist_field *fields[HIST_FIELDS_MAX]; + unsigned int n_vals; +@@ -274,6 +284,14 @@ struct hist_trigger_data { + + struct action_data *actions[HIST_ACTIONS_MAX]; + unsigned int n_actions; ++ ++ struct hist_field *synth_var_refs[SYNTH_FIELDS_MAX]; ++ unsigned int n_synth_var_refs; ++ struct field_var *field_vars[SYNTH_FIELDS_MAX]; ++ unsigned int n_field_vars; ++ unsigned int n_field_var_str; ++ struct field_var_hist *field_var_hists[SYNTH_FIELDS_MAX]; ++ unsigned int n_field_var_hists; + }; + + struct synth_field { +@@ -1392,6 +1410,7 @@ static struct hist_field *find_event_var + struct hist_elt_data { + char *comm; + u64 *var_ref_vals; ++ char *field_var_str[SYNTH_FIELDS_MAX]; + }; + + static u64 hist_field_var_ref(struct hist_field *hist_field, +@@ -1669,7 +1688,14 @@ static inline void save_comm(char *comm, + + 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; ++ ++ n_str = hist_data->n_field_var_str; ++ ++ for (i = 0; i < n_str; i++) ++ kfree(private_data->field_var_str[i]); + + kfree(private_data->comm); + kfree(private_data); +@@ -1681,7 +1707,7 @@ static int hist_trigger_elt_data_alloc(s + unsigned int size = TASK_COMM_LEN + 1; + struct hist_elt_data *elt_data; + struct hist_field *key_field; +- unsigned int i; ++ unsigned int i, n_str; + + elt->private_data = elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL); + if (!elt_data) +@@ -1701,6 +1727,18 @@ static int hist_trigger_elt_data_alloc(s + } + } + ++ n_str = hist_data->n_field_var_str; ++ ++ size = STR_VAR_LEN_MAX; ++ ++ 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); ++ return -ENOMEM; ++ } ++ } ++ + return 0; + } + +@@ -2347,6 +2385,387 @@ static struct hist_field *parse_expr(str + return ERR_PTR(ret); + } + ++static char *find_trigger_filter(struct hist_trigger_data *hist_data, ++ struct trace_event_file *file) ++{ ++ struct event_trigger_data *test; ++ ++ list_for_each_entry_rcu(test, &file->triggers, list) { ++ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { ++ if (test->private_data == hist_data) ++ return test->filter_str; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct event_command trigger_hist_cmd; ++static int event_hist_trigger_func(struct event_command *cmd_ops, ++ struct trace_event_file *file, ++ char *glob, char *cmd, char *param); ++ ++static bool compatible_keys(struct hist_trigger_data *target_hist_data, ++ struct hist_trigger_data *hist_data, ++ unsigned int n_keys) ++{ ++ struct hist_field *target_hist_field, *hist_field; ++ unsigned int n, i, j; ++ ++ if (hist_data->n_fields - hist_data->n_vals != n_keys) ++ return false; ++ ++ i = hist_data->n_vals; ++ j = target_hist_data->n_vals; ++ ++ for (n = 0; n < n_keys; n++) { ++ hist_field = hist_data->fields[i + n]; ++ target_hist_field = hist_data->fields[j + n]; ++ ++ if (strcmp(hist_field->type, target_hist_field->type) != 0) ++ return false; ++ if (hist_field->size != target_hist_field->size) ++ return false; ++ if (hist_field->is_signed != target_hist_field->is_signed) ++ return false; ++ } ++ ++ return true; ++} ++ ++static struct hist_trigger_data * ++find_compatible_hist(struct hist_trigger_data *target_hist_data, ++ struct trace_event_file *file) ++{ ++ struct hist_trigger_data *hist_data; ++ struct event_trigger_data *test; ++ unsigned int n_keys; ++ ++ n_keys = target_hist_data->n_fields - target_hist_data->n_vals; ++ ++ list_for_each_entry_rcu(test, &file->triggers, list) { ++ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { ++ hist_data = test->private_data; ++ ++ if (compatible_keys(target_hist_data, hist_data, n_keys)) ++ return hist_data; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct trace_event_file *event_file(struct trace_array *tr, ++ char *system, char *event_name) ++{ ++ struct trace_event_file *file; ++ ++ file = find_event_file(tr, system, event_name); ++ if (!file) ++ return ERR_PTR(-EINVAL); ++ ++ return file; ++} ++ ++static struct hist_field * ++create_field_var_hist(struct hist_trigger_data *target_hist_data, ++ char *system, char *event_name, char *field_name) ++{ ++ struct trace_array *tr = target_hist_data->event_file->tr; ++ struct hist_field *event_var = ERR_PTR(-EINVAL); ++ struct hist_trigger_data *hist_data; ++ unsigned int i, n, first = true; ++ struct field_var_hist *var_hist; ++ struct trace_event_file *file; ++ struct hist_field *key_field; ++ char *saved_filter; ++ char *cmd; ++ int ret; ++ ++ if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX) ++ return ERR_PTR(-EINVAL); ++ ++ file = event_file(tr, system, event_name); ++ ++ if (IS_ERR(file)) { ++ ret = PTR_ERR(file); ++ return ERR_PTR(ret); ++ } ++ ++ hist_data = find_compatible_hist(target_hist_data, file); ++ if (!hist_data) ++ return ERR_PTR(-EINVAL); ++ ++ var_hist = kzalloc(sizeof(*var_hist), GFP_KERNEL); ++ if (!var_hist) ++ return ERR_PTR(-ENOMEM); ++ ++ cmd = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); ++ if (!cmd) { ++ kfree(var_hist); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ strcat(cmd, "keys="); ++ ++ for_each_hist_key_field(i, hist_data) { ++ key_field = hist_data->fields[i]; ++ if (!first) ++ strcat(cmd, ","); ++ strcat(cmd, key_field->field->name); ++ first = false; ++ } ++ ++ strcat(cmd, ":synthetic_"); ++ strcat(cmd, field_name); ++ strcat(cmd, "="); ++ strcat(cmd, field_name); ++ ++ saved_filter = find_trigger_filter(hist_data, file); ++ if (saved_filter) { ++ strcat(cmd, " if "); ++ strcat(cmd, saved_filter); ++ } ++ ++ var_hist->cmd = kstrdup(cmd, GFP_KERNEL); ++ if (!var_hist->cmd) { ++ kfree(cmd); ++ kfree(var_hist); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ var_hist->hist_data = hist_data; ++ ++ ret = event_hist_trigger_func(&trigger_hist_cmd, file, ++ "", "hist", cmd); ++ if (ret) { ++ kfree(cmd); ++ kfree(var_hist->cmd); ++ kfree(var_hist); ++ return ERR_PTR(ret); ++ } ++ ++ strcpy(cmd, "synthetic_"); ++ strcat(cmd, field_name); ++ ++ event_var = find_event_var(tr, system, event_name, cmd); ++ if (!event_var) { ++ kfree(cmd); ++ kfree(var_hist->cmd); ++ kfree(var_hist); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ n = target_hist_data->n_field_var_hists; ++ target_hist_data->field_var_hists[n] = var_hist; ++ target_hist_data->n_field_var_hists++; ++ ++ return event_var; ++} ++ ++static struct hist_field * ++find_target_event_var(struct hist_trigger_data *hist_data, ++ char *system, char *event_name, char *var_name) ++{ ++ struct trace_event_file *file = hist_data->event_file; ++ struct hist_field *hist_field = NULL; ++ ++ if (system) { ++ struct trace_event_call *call; ++ ++ if (!event_name) ++ return NULL; ++ ++ call = file->event_call; ++ ++ if (strcmp(system, call->class->system) != 0) ++ return NULL; ++ ++ if (strcmp(event_name, trace_event_name(call)) != 0) ++ return NULL; ++ } ++ ++ hist_field = find_var_field(hist_data, var_name); ++ ++ return hist_field; ++} ++ ++static inline void __update_field_vars(struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *rec, ++ struct field_var **field_vars, ++ unsigned int n_field_vars, ++ unsigned int field_var_str_start) ++{ ++ struct hist_elt_data *elt_data = elt->private_data; ++ unsigned int i, j, var_idx; ++ u64 var_val; ++ ++ for (i = 0, j = field_var_str_start; i < n_field_vars; i++) { ++ struct field_var *field_var = field_vars[i]; ++ struct hist_field *var = field_var->var; ++ struct hist_field *val = field_var->val; ++ ++ var_val = val->fn(val, elt, rbe, rec); ++ var_idx = var->var.idx; ++ ++ if (val->flags & HIST_FIELD_FL_STRING) { ++ char *str = elt_data->field_var_str[j++]; ++ char *val_str = (char *)(uintptr_t)var_val; ++ ++ strncpy(str, val_str, STR_VAR_LEN_MAX); ++ var_val = (u64)(uintptr_t)str; ++ } ++ tracing_map_set_var(elt, var_idx, var_val); ++ } ++} ++ ++static void update_field_vars(struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *rec) ++{ ++ __update_field_vars(elt, rbe, rec, hist_data->field_vars, ++ hist_data->n_field_vars, 0); ++} ++ ++static struct hist_field *create_var(struct hist_trigger_data *hist_data, ++ struct trace_event_file *file, ++ char *name, int size, const char *type) ++{ ++ struct hist_field *var; ++ int idx; ++ ++ if (find_var(file, name) && !hist_data->remove) { ++ var = ERR_PTR(-EINVAL); ++ goto out; ++ } ++ ++ var = kzalloc(sizeof(struct hist_field), GFP_KERNEL); ++ if (!var) { ++ var = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ idx = tracing_map_add_var(hist_data->map); ++ if (idx < 0) { ++ kfree(var); ++ var = ERR_PTR(-EINVAL); ++ goto out; ++ } ++ ++ var->flags = HIST_FIELD_FL_VAR; ++ var->var.idx = idx; ++ var->var.hist_data = var->hist_data = hist_data; ++ var->size = size; ++ var->var.name = kstrdup(name, GFP_KERNEL); ++ var->type = kstrdup(type, GFP_KERNEL); ++ if (!var->var.name || !var->type) { ++ kfree(var->var.name); ++ kfree(var->type); ++ kfree(var); ++ var = ERR_PTR(-ENOMEM); ++ } ++ out: ++ return var; ++} ++ ++static struct field_var *create_field_var(struct hist_trigger_data *hist_data, ++ struct trace_event_file *file, ++ char *field_name) ++{ ++ struct hist_field *val = NULL, *var = NULL; ++ unsigned long flags = HIST_FIELD_FL_VAR; ++ struct field_var *field_var; ++ int ret = 0; ++ ++ if (hist_data->n_field_vars >= SYNTH_FIELDS_MAX) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ val = parse_atom(hist_data, file, field_name, &flags, NULL); ++ if (IS_ERR(val)) { ++ ret = PTR_ERR(val); ++ goto err; ++ } ++ ++ var = create_var(hist_data, file, field_name, val->size, val->type); ++ if (IS_ERR(var)) { ++ kfree(val); ++ ret = PTR_ERR(var); ++ goto err; ++ } ++ ++ field_var = kzalloc(sizeof(struct field_var), GFP_KERNEL); ++ if (!field_var) { ++ kfree(val); ++ kfree(var); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ field_var->var = var; ++ field_var->val = val; ++ out: ++ return field_var; ++ err: ++ field_var = ERR_PTR(ret); ++ goto out; ++} ++ ++static struct field_var * ++create_target_field_var(struct hist_trigger_data *hist_data, ++ char *system, char *event_name, char *var_name) ++{ ++ struct trace_event_file *file = hist_data->event_file; ++ ++ if (system) { ++ struct trace_event_call *call; ++ ++ if (!event_name) ++ return NULL; ++ ++ call = file->event_call; ++ ++ if (strcmp(system, call->class->system) != 0) ++ return NULL; ++ ++ if (strcmp(event_name, trace_event_name(call)) != 0) ++ return NULL; ++ } ++ ++ return create_field_var(hist_data, file, var_name); ++} ++ ++static void destroy_field_var(struct field_var *field_var) ++{ ++ if (!field_var) ++ return; ++ ++ destroy_hist_field(field_var->var, 0); ++ destroy_hist_field(field_var->val, 0); ++ ++ kfree(field_var); ++} ++ ++static void destroy_field_vars(struct hist_trigger_data *hist_data) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < hist_data->n_field_vars; i++) ++ destroy_field_var(hist_data->field_vars[i]); ++} ++ ++static void save_field_var(struct hist_trigger_data *hist_data, ++ struct field_var *field_var) ++{ ++ hist_data->field_vars[hist_data->n_field_vars++] = field_var; ++ ++ if (field_var->val->flags & HIST_FIELD_FL_STRING) ++ hist_data->n_field_var_str++; ++} ++ + static int create_hitcount_val(struct hist_trigger_data *hist_data) + { + hist_data->fields[HITCOUNT_IDX] = +@@ -2814,6 +3233,16 @@ static void print_actions_spec(struct se + } + } + ++static void destroy_field_var_hists(struct hist_trigger_data *hist_data) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < hist_data->n_field_var_hists; i++) { ++ kfree(hist_data->field_var_hists[i]->cmd); ++ kfree(hist_data->field_var_hists[i]); ++ } ++} ++ + static void destroy_hist_data(struct hist_trigger_data *hist_data) + { + if (!hist_data) +@@ -2824,6 +3253,8 @@ static void destroy_hist_data(struct his + tracing_map_destroy(hist_data->map); + + destroy_actions(hist_data); ++ destroy_field_vars(hist_data); ++ destroy_field_var_hists(hist_data); + + kfree(hist_data); + } +@@ -2957,6 +3388,8 @@ static void hist_trigger_elt_update(stru + tracing_map_set_var(elt, var_idx, hist_val); + } + } ++ ++ update_field_vars(hist_data, elt, rbe, rec); + } + + static inline void add_to_key(char *compound_key, void *key, +@@ -3677,6 +4110,21 @@ static bool hist_trigger_check_refs(stru + return false; + } + ++static void unregister_field_var_hists(struct hist_trigger_data *hist_data) ++{ ++ struct trace_event_file *file; ++ unsigned int i; ++ char *cmd; ++ int ret; ++ ++ for (i = 0; i < hist_data->n_field_var_hists; i++) { ++ file = hist_data->field_var_hists[i]->hist_data->event_file; ++ cmd = hist_data->field_var_hists[i]->cmd; ++ ret = event_hist_trigger_func(&trigger_hist_cmd, file, ++ "!hist", "hist", cmd); ++ } ++} ++ + static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, + struct event_trigger_data *data, + struct trace_event_file *file) +@@ -3692,6 +4140,7 @@ static void hist_unregister_trigger(char + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { + if (!hist_trigger_match(data, test, named_data, false)) + continue; ++ unregister_field_var_hists(test->private_data); + unregistered = true; + list_del_rcu(&test->list); + trace_event_trigger_enable_disable(file, 0); +@@ -3733,6 +4182,7 @@ static void hist_unreg_all(struct trace_ + + list_for_each_entry_safe(test, n, &file->triggers, list) { + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { ++ unregister_field_var_hists(test->private_data); + list_del_rcu(&test->list); + trace_event_trigger_enable_disable(file, 0); + update_cond_flag(file); diff --git a/patches/0029-tracing-Add-onmatch-hist-trigger-action-support.patch b/patches/0029-tracing-Add-onmatch-hist-trigger-action-support.patch new file mode 100644 index 000000000000..85b59bde2318 --- /dev/null +++ b/patches/0029-tracing-Add-onmatch-hist-trigger-action-support.patch @@ -0,0 +1,550 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Tue, 5 Sep 2017 16:57:41 -0500 +Subject: [PATCH 29/40] tracing: Add 'onmatch' hist trigger action support + +Add an 'onmatch(matching.event).<synthetic_event_name>(param list)' +hist trigger action which is invoked with the set of variables or +event fields named in the 'param list'. The result is the generation +of a synthetic event that consists of the values contained in those +variables and/or fields at the time the invoking event was hit. + +As an example the below defines a simple synthetic event using a +variable defined on the sched_wakeup_new event, and shows the event +definition with unresolved fields, since the sched_wakeup_new event +with the testpid variable hasn't been defined yet: + + # echo 'wakeup_new_test pid_t pid; int prio' >> \ + /sys/kernel/debug/tracing/synthetic_events + + # cat /sys/kernel/debug/tracing/synthetic_events + wakeup_new_test pid_t pid; int prio + +The following hist trigger both defines a testpid variable and +specifies an onmatch() trace action that uses that variable along with +a non-variable field to generate a wakeup_new_test synthetic event +whenever a sched_wakeup_new event occurs, which because of the 'if +comm == "cyclictest"' filter only happens when the executable is +cyclictest: + + # echo 'hist:testpid=pid:keys=$testpid:\ + onmatch(sched.sched_wakeup_new).wakeup_new_test($testpid, prio) \ + if comm=="cyclictest"' >> \ + /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/trigger + +Creating and displaying a histogram based on those events is now just +a matter of using the fields and new synthetic event in the +tracing/events/synthetic directory, as usual: + + # echo 'hist:keys=pid,prio:sort=pid,prio' >> \ + /sys/kernel/debug/tracing/events/synthetic/wakeup_new_test/trigger + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace_events_hist.c | 394 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 382 insertions(+), 12 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -323,7 +323,18 @@ typedef void (*action_fn_t) (struct hist + + struct action_data { + action_fn_t fn; +- unsigned int var_ref_idx; ++ unsigned int n_params; ++ char *params[SYNTH_FIELDS_MAX]; ++ ++ union { ++ struct { ++ unsigned int var_ref_idx; ++ char *match_event; ++ char *match_event_system; ++ char *synth_event_name; ++ struct synth_event *synth_event; ++ } onmatch; ++ }; + }; + + static LIST_HEAD(synth_event_list); +@@ -927,6 +938,21 @@ static struct synth_event *alloc_synth_e + return event; + } + ++static void action_trace(struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, void *rec, ++ struct ring_buffer_event *rbe, ++ struct action_data *data, u64 *var_ref_vals) ++{ ++ struct synth_event *event = data->onmatch.synth_event; ++ ++ trace_synth(event, var_ref_vals, data->onmatch.var_ref_idx); ++} ++ ++struct hist_var_data { ++ struct list_head list; ++ struct hist_trigger_data *hist_data; ++}; ++ + static int create_synth_event(int argc, char **argv) + { + struct synth_field *field, *fields[SYNTH_FIELDS_MAX]; +@@ -967,10 +993,8 @@ static int create_synth_event(int argc, + } + ret = -EEXIST; + goto out; +- } else if (delete_event) { +- ret = -EINVAL; ++ } else if (delete_event) + goto out; +- } + + if (argc < 2) { + ret = -EINVAL; +@@ -1134,11 +1158,6 @@ static u64 hist_field_timestamp(struct h + return ts; + } + +-struct hist_var_data { +- struct list_head list; +- struct hist_trigger_data *hist_data; +-}; +- + static struct hist_field *check_var_ref(struct hist_field *hist_field, + struct hist_trigger_data *var_data, + unsigned int var_idx) +@@ -1578,11 +1597,21 @@ static void destroy_hist_trigger_attrs(s + + static int parse_action(char *str, struct hist_trigger_attrs *attrs) + { +- int ret = 0; ++ int ret = -EINVAL; + + if (attrs->n_actions >= HIST_ACTIONS_MAX) + return ret; + ++ if ((strncmp(str, "onmatch(", strlen("onmatch(")) == 0)) { ++ attrs->action_str[attrs->n_actions] = kstrdup(str, GFP_KERNEL); ++ if (!attrs->action_str[attrs->n_actions]) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ attrs->n_actions++; ++ ret = 0; ++ } ++ + return ret; + } + +@@ -2420,7 +2449,7 @@ static bool compatible_keys(struct hist_ + + for (n = 0; n < n_keys; n++) { + hist_field = hist_data->fields[i + n]; +- target_hist_field = hist_data->fields[j + n]; ++ target_hist_field = target_hist_data->fields[j + n]; + + if (strcmp(hist_field->type, target_hist_field->type) != 0) + return false; +@@ -2738,6 +2767,27 @@ create_target_field_var(struct hist_trig + return create_field_var(hist_data, file, var_name); + } + ++static void onmatch_destroy(struct action_data *data) ++{ ++ unsigned int i; ++ ++ mutex_lock(&synth_event_mutex); ++ ++ kfree(data->onmatch.match_event); ++ kfree(data->onmatch.match_event_system); ++ kfree(data->onmatch.synth_event_name); ++ ++ for (i = 0; i < data->n_params; i++) ++ kfree(data->params[i]); ++ ++ kfree(data); ++ ++ if (data->onmatch.synth_event) ++ data->onmatch.synth_event->ref--; ++ ++ mutex_unlock(&synth_event_mutex); ++} ++ + static void destroy_field_var(struct field_var *field_var) + { + if (!field_var) +@@ -2766,6 +2816,281 @@ static void save_field_var(struct hist_t + hist_data->n_field_var_str++; + } + ++ ++static void destroy_synth_var_refs(struct hist_trigger_data *hist_data) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < hist_data->n_synth_var_refs; i++) ++ destroy_hist_field(hist_data->synth_var_refs[i], 0); ++} ++ ++static void save_synth_var_ref(struct hist_trigger_data *hist_data, ++ struct hist_field *var_ref) ++{ ++ hist_data->synth_var_refs[hist_data->n_synth_var_refs++] = var_ref; ++ ++ hist_data->var_refs[hist_data->n_var_refs] = var_ref; ++ var_ref->var_ref_idx = hist_data->n_var_refs++; ++} ++ ++static int check_synth_field(struct synth_event *event, ++ struct hist_field *hist_field, ++ unsigned int field_pos) ++{ ++ struct synth_field *field; ++ ++ if (field_pos >= event->n_fields) ++ return -EINVAL; ++ ++ field = event->fields[field_pos]; ++ ++ if (strcmp(field->type, hist_field->type) != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int parse_action_params(char *params, struct action_data *data) ++{ ++ char *param, *saved_param; ++ int ret = 0; ++ ++ while (params) { ++ if (data->n_params >= SYNTH_FIELDS_MAX) ++ goto out; ++ ++ param = strsep(¶ms, ","); ++ if (!param) ++ goto out; ++ ++ param = strstrip(param); ++ if (strlen(param) < 2) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ saved_param = kstrdup(param, GFP_KERNEL); ++ if (!saved_param) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ data->params[data->n_params++] = saved_param; ++ } ++ out: ++ return ret; ++} ++ ++static struct hist_field * ++onmatch_find_var(struct hist_trigger_data *hist_data, struct action_data *data, ++ char *system, char *event, char *var) ++{ ++ struct trace_array *tr = hist_data->event_file->tr; ++ struct hist_field *hist_field; ++ ++ var++; /* skip '$' */ ++ ++ hist_field = find_target_event_var(hist_data, system, event, var); ++ if (!hist_field) { ++ if (!system) { ++ system = data->onmatch.match_event_system; ++ event = data->onmatch.match_event; ++ } ++ ++ hist_field = find_event_var(tr, system, event, var); ++ } ++ ++ return hist_field; ++} ++ ++static struct hist_field * ++onmatch_create_field_var(struct hist_trigger_data *hist_data, ++ struct action_data *data, char *system, ++ char *event, char *var) ++{ ++ struct hist_field *hist_field = NULL; ++ struct field_var *field_var; ++ ++ field_var = create_target_field_var(hist_data, system, event, var); ++ if (IS_ERR(field_var)) ++ goto out; ++ ++ if (field_var) { ++ save_field_var(hist_data, field_var); ++ hist_field = field_var->var; ++ } else { ++ if (!system) { ++ system = data->onmatch.match_event_system; ++ event = data->onmatch.match_event; ++ } ++ ++ hist_field = create_field_var_hist(hist_data, system, event, var); ++ if (IS_ERR(hist_field)) ++ goto free; ++ } ++ out: ++ return hist_field; ++ free: ++ destroy_field_var(field_var); ++ hist_field = NULL; ++ goto out; ++} ++ ++static int onmatch_create(struct hist_trigger_data *hist_data, ++ struct trace_event_file *file, ++ struct action_data *data) ++{ ++ char *event_name, *param, *system = NULL; ++ struct hist_field *hist_field, *var_ref; ++ unsigned int i, var_ref_idx; ++ unsigned int field_pos = 0; ++ struct synth_event *event; ++ int ret = 0; ++ ++ mutex_lock(&synth_event_mutex); ++ ++ event = find_synth_event(data->onmatch.synth_event_name); ++ if (!event) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ var_ref_idx = hist_data->n_var_refs; ++ ++ for (i = 0; i < data->n_params; i++) { ++ char *p; ++ ++ p = param = kstrdup(data->params[i], GFP_KERNEL); ++ if (!param) ++ goto out; ++ ++ system = strsep(¶m, "."); ++ if (!param) { ++ param = (char *)system; ++ system = event_name = NULL; ++ } else { ++ event_name = strsep(¶m, "."); ++ if (!param) { ++ kfree(p); ++ ret = -EINVAL; ++ goto out; ++ } ++ } ++ ++ if (param[0] == '$') ++ hist_field = onmatch_find_var(hist_data, data, system, ++ event_name, param); ++ else ++ hist_field = onmatch_create_field_var(hist_data, data, ++ system, ++ event_name, ++ param); ++ ++ if (!hist_field) { ++ kfree(p); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (check_synth_field(event, hist_field, field_pos) == 0) { ++ var_ref = create_var_ref(hist_field); ++ if (!var_ref) { ++ kfree(p); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ save_synth_var_ref(hist_data, var_ref); ++ field_pos++; ++ kfree(p); ++ continue; ++ } ++ ++ kfree(p); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (field_pos != event->n_fields) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ data->fn = action_trace; ++ data->onmatch.synth_event = event; ++ data->onmatch.var_ref_idx = var_ref_idx; ++ hist_data->actions[hist_data->n_actions++] = data; ++ event->ref++; ++ out: ++ mutex_unlock(&synth_event_mutex); ++ ++ return ret; ++} ++ ++static struct action_data *onmatch_parse(struct trace_array *tr, char *str) ++{ ++ char *match_event, *match_event_system; ++ char *synth_event_name, *params; ++ struct action_data *data; ++ int ret = -EINVAL; ++ ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return ERR_PTR(-ENOMEM); ++ ++ match_event = strsep(&str, ")"); ++ if (!match_event || !str) ++ goto free; ++ ++ match_event_system = strsep(&match_event, "."); ++ if (!match_event) ++ goto free; ++ ++ if (IS_ERR(event_file(tr, match_event_system, match_event))) ++ goto free; ++ ++ data->onmatch.match_event = kstrdup(match_event, GFP_KERNEL); ++ if (!data->onmatch.match_event) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ ++ data->onmatch.match_event_system = kstrdup(match_event_system, GFP_KERNEL); ++ if (!data->onmatch.match_event_system) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ ++ strsep(&str, "."); ++ if (!str) ++ goto free; ++ ++ synth_event_name = strsep(&str, "("); ++ if (!synth_event_name || !str) ++ goto free; ++ ++ data->onmatch.synth_event_name = kstrdup(synth_event_name, GFP_KERNEL); ++ if (!data->onmatch.synth_event_name) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ ++ params = strsep(&str, ")"); ++ if (!params || !str || (str && strlen(str))) ++ goto free; ++ ++ ret = parse_action_params(params, data); ++ if (ret) ++ goto free; ++ out: ++ return data; ++ free: ++ onmatch_destroy(data); ++ data = ERR_PTR(ret); ++ goto out; ++} ++ + static int create_hitcount_val(struct hist_trigger_data *hist_data) + { + hist_data->fields[HITCOUNT_IDX] = +@@ -3194,19 +3519,38 @@ static void destroy_actions(struct hist_ + for (i = 0; i < hist_data->n_actions; i++) { + struct action_data *data = hist_data->actions[i]; + +- kfree(data); ++ if (data->fn == action_trace) ++ onmatch_destroy(data); ++ else ++ kfree(data); + } + } + + static int create_actions(struct hist_trigger_data *hist_data, + struct trace_event_file *file) + { ++ struct trace_array *tr = hist_data->event_file->tr; ++ struct action_data *data; + unsigned int i; + int ret = 0; + char *str; + + for (i = 0; i < hist_data->attrs->n_actions; i++) { + str = hist_data->attrs->action_str[i]; ++ ++ if (strncmp(str, "onmatch(", strlen("onmatch(")) == 0) { ++ char *action_str = str + strlen("onmatch("); ++ ++ data = onmatch_parse(tr, action_str); ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ ret = onmatch_create(hist_data, file, data); ++ if (ret) { ++ onmatch_destroy(data); ++ return ret; ++ } ++ } + } + + return ret; +@@ -3223,6 +3567,26 @@ static void print_actions(struct seq_fil + } + } + ++static void print_onmatch_spec(struct seq_file *m, ++ struct hist_trigger_data *hist_data, ++ struct action_data *data) ++{ ++ unsigned int i; ++ ++ seq_printf(m, ":onmatch(%s.%s).", data->onmatch.match_event_system, ++ data->onmatch.match_event); ++ ++ seq_printf(m, "%s(", data->onmatch.synth_event->name); ++ ++ for (i = 0; i < data->n_params; i++) { ++ if (i) ++ seq_puts(m, ","); ++ seq_printf(m, "%s", data->params[i]); ++ } ++ ++ seq_puts(m, ")"); ++} ++ + static void print_actions_spec(struct seq_file *m, + struct hist_trigger_data *hist_data) + { +@@ -3230,6 +3594,9 @@ static void print_actions_spec(struct se + + for (i = 0; i < hist_data->n_actions; i++) { + struct action_data *data = hist_data->actions[i]; ++ ++ if (data->fn == action_trace) ++ print_onmatch_spec(m, hist_data, data); + } + } + +@@ -3255,6 +3622,7 @@ static void destroy_hist_data(struct his + destroy_actions(hist_data); + destroy_field_vars(hist_data); + destroy_field_var_hists(hist_data); ++ destroy_synth_var_refs(hist_data); + + kfree(hist_data); + } +@@ -3603,6 +3971,8 @@ hist_trigger_entry_print(struct seq_file + } + } + ++ print_actions(m, hist_data, elt); ++ + seq_puts(m, "\n"); + } + diff --git a/patches/0024-tracing-Add-onmax-hist-trigger-action-support.patch b/patches/0030-tracing-Add-onmax-hist-trigger-action-support.patch index 3fe92f608abc..61c4e76e3a7b 100644 --- a/patches/0024-tracing-Add-onmax-hist-trigger-action-support.patch +++ b/patches/0030-tracing-Add-onmax-hist-trigger-action-support.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:25 -0500 -Subject: [PATCH 24/32] tracing: Add 'onmax' hist trigger action support +Date: Tue, 5 Sep 2017 16:57:42 -0500 +Subject: [PATCH 30/40] tracing: Add 'onmax' hist trigger action support Add an 'onmax(var).save(field,...)' hist trigger action which is invoked whenever an event exceeds the current maximum. @@ -20,12 +20,12 @@ the timestamp difference is calculated. If the resulting latency exceeds the current maximum latency, the specified save() values are saved: - # echo 'hist:keys=pid:ts0=common_timestamp.usecs \ + # echo 'hist:keys=pid:ts0=$common_timestamp.usecs \ if comm=="cyclictest"' >> \ /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger # echo 'hist:keys=next_pid:\ - wakeup_lat=common_timestamp.usecs-$ts0:\ + wakeup_lat=$common_timestamp.usecs-$ts0:\ onmax($wakeup_lat).save(next_comm,prev_pid,prev_prio,prev_comm) \ if next_comm=="cyclictest"' >> \ /sys/kernel/debug/tracing/events/sched/sched_switch/trigger @@ -35,28 +35,31 @@ corresponding to the max are displayed following the rest of the fields: # cat /sys/kernel/debug/tracing/events/sched/sched_switch/hist - { next_pid: 2255 } hitcount: 239 \ - common_timestamp-$ts0: 0 - max: 27 next_comm: cyclictest \ - prev_pid: 0 prev_prio: 120 prev_comm: swapper/1 \ - { next_pid: 2256 } hitcount: 2355 common_timestamp-$ts0: 0 \ - max: 49 next_comm: cyclictest \ - prev_pid: 0 prev_prio: 120 prev_comm: swapper/0 + + { next_pid: 3728 } hitcount: 199 \ + max: 123 next_comm: cyclictest prev_pid: 0 \ + prev_prio: 120 prev_comm: swapper/3 + { next_pid: 3730 } hitcount: 1321 \ + max: 15 next_comm: cyclictest prev_pid: 0 \ + prev_prio: 120 prev_comm: swapper/1 + { next_pid: 3729 } hitcount: 1973\ + max: 25 next_comm: cyclictest prev_pid: 0 \ + prev_prio: 120 prev_comm: swapper/0 Totals: - Hits: 12970 - Entries: 2 - Dropped: 0 + Hits: 3493 + Entries: 3 + Dropped: 0 Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - kernel/trace/trace_events_hist.c | 310 ++++++++++++++++++++++++++++++++++----- - 1 file changed, 276 insertions(+), 34 deletions(-) + kernel/trace/trace_events_hist.c | 313 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 279 insertions(+), 34 deletions(-) --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -282,6 +282,10 @@ struct hist_trigger_data { +@@ -292,6 +292,10 @@ struct hist_trigger_data { unsigned int n_field_var_str; struct field_var_hist *field_var_hists[SYNTH_FIELDS_MAX]; unsigned int n_field_var_hists; @@ -67,20 +70,22 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> }; struct synth_field { -@@ -318,6 +322,12 @@ struct action_data { - char *match_event_system; - char *synth_event_name; - struct synth_event *synth_event; -+ -+ char *onmax_var_str; -+ char *onmax_fn_name; -+ unsigned int max_var_ref_idx; -+ struct hist_field *max_var; -+ struct hist_field *onmax_var; +@@ -334,6 +338,14 @@ struct action_data { + char *synth_event_name; + struct synth_event *synth_event; + } onmatch; ++ ++ struct { ++ char *var_str; ++ char *fn_name; ++ unsigned int max_var_ref_idx; ++ struct hist_field *max_var; ++ struct hist_field *var; ++ } onmax; + }; }; - static LIST_HEAD(synth_event_list); -@@ -1493,7 +1503,8 @@ static int parse_action(char *str, struc +@@ -1602,7 +1614,8 @@ static int parse_action(char *str, struc if (attrs->n_actions >= HIST_ACTIONS_MAX) return ret; @@ -90,7 +95,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> attrs->action_str[attrs->n_actions] = kstrdup(str, GFP_KERNEL); if (!attrs->action_str[attrs->n_actions]) { ret = -ENOMEM; -@@ -1612,7 +1623,7 @@ static void hist_trigger_elt_data_free(s +@@ -1721,7 +1734,7 @@ static void hist_trigger_elt_data_free(s struct hist_elt_data *private_data = elt->private_data; unsigned int i, n_str; @@ -99,16 +104,16 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> for (i = 0; i < n_str; i++) kfree(private_data->field_var_str[i]); -@@ -1647,7 +1658,7 @@ static int hist_trigger_elt_data_alloc(s +@@ -1756,7 +1769,7 @@ static int hist_trigger_elt_data_alloc(s } } - n_str = hist_data->n_field_var_str; + n_str = hist_data->n_field_var_str + hist_data->n_max_var_str; - for (i = 0; i < n_str; i++) { - elt_data->field_var_str[i] = kzalloc(size, GFP_KERNEL); -@@ -2504,6 +2515,15 @@ static void update_field_vars(struct his + size = STR_VAR_LEN_MAX; + +@@ -2658,6 +2671,15 @@ static void update_field_vars(struct his hist_data->n_field_vars, 0); } @@ -124,7 +129,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static struct hist_field *create_var(struct hist_trigger_data *hist_data, struct trace_event_file *file, char *name, int size, const char *type) -@@ -2613,6 +2633,222 @@ create_target_field_var(struct hist_trig +@@ -2767,6 +2789,223 @@ create_target_field_var(struct hist_trig return create_field_var(hist_data, file, var_name); } @@ -133,7 +138,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + struct tracing_map_elt *elt, + struct action_data *data) +{ -+ unsigned int i, save_var_idx, max_idx = data->max_var->var.idx; ++ unsigned int i, save_var_idx, max_idx = data->onmax.max_var->var.idx; + + seq_printf(m, "\n\tmax: %10llu", tracing_map_read_var(elt, max_idx)); + @@ -147,7 +152,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + val = tracing_map_read_var(elt, save_var_idx); + + if (save_val->flags & HIST_FIELD_FL_STRING) { -+ seq_printf(m, " %s: %-50s", save_var->var.name, ++ seq_printf(m, " %s: %-32s", save_var->var.name, + (char *)(uintptr_t)(val)); + } else + seq_printf(m, " %s: %10llu", save_var->var.name, val); @@ -159,8 +164,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + struct ring_buffer_event *rbe, + struct action_data *data, u64 *var_ref_vals) +{ -+ unsigned int max_idx = data->max_var->var.idx; -+ unsigned int max_var_ref_idx = data->max_var_ref_idx; ++ unsigned int max_idx = data->onmax.max_var->var.idx; ++ unsigned int max_var_ref_idx = data->onmax.max_var_ref_idx; + + u64 var_val, max_val; + @@ -179,11 +184,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +{ + unsigned int i; + -+ destroy_hist_field(data->max_var, 0); -+ destroy_hist_field(data->onmax_var, 0); ++ destroy_hist_field(data->onmax.max_var, 0); ++ destroy_hist_field(data->onmax.var, 0); + -+ kfree(data->onmax_var_str); -+ kfree(data->onmax_fn_name); ++ kfree(data->onmax.var_str); ++ kfree(data->onmax.fn_name); + + for (i = 0; i < data->n_params; i++) + kfree(data->params[i]); @@ -205,7 +210,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + unsigned int i; + int ret = 0; + -+ onmax_var_str = data->onmax_var_str; ++ onmax_var_str = data->onmax.var_str; + if (onmax_var_str[0] != '$') + return -EINVAL; + onmax_var_str++; @@ -220,27 +225,23 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + if (!ref_field) + return -ENOMEM; + -+ ref_field->var.idx = var_field->var.idx; -+ ref_field->var.hist_data = hist_data; -+ ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL); -+ ref_field->type = kstrdup(var_field->type, GFP_KERNEL); -+ if (!ref_field->name || !ref_field->type) { ++ if (init_var_ref(ref_field, var_field)) { + destroy_hist_field(ref_field, 0); + ret = -ENOMEM; + goto out; + } + hist_data->var_refs[hist_data->n_var_refs] = ref_field; + ref_field->var_ref_idx = hist_data->n_var_refs++; -+ data->onmax_var = ref_field; ++ data->onmax.var = ref_field; + + data->fn = onmax_save; -+ data->max_var_ref_idx = var_ref_idx; ++ data->onmax.max_var_ref_idx = var_ref_idx; + max_var = create_var(hist_data, file, "max", sizeof(u64), "u64"); + if (IS_ERR(max_var)) { + ret = PTR_ERR(max_var); + goto out; + } -+ data->max_var = max_var; ++ data->onmax.max_var = max_var; + + for (i = 0; i < data->n_params; i++) { + param = kstrdup(data->params[i], GFP_KERNEL); @@ -310,7 +311,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + onmax_var_str = strsep(&str, ")"); + if (!onmax_var_str || !str) + return ERR_PTR(-EINVAL); -+ data->onmax_var_str = kstrdup(onmax_var_str, GFP_KERNEL); ++ data->onmax.var_str = kstrdup(onmax_var_str, GFP_KERNEL); ++ if (!data->onmax.var_str) { ++ ret = -ENOMEM; ++ goto free; ++ } + + strsep(&str, "."); + if (!str) @@ -329,10 +334,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + ret = parse_action_params(params, data); + if (ret) + goto free; -+ } -+ data->onmax_fn_name = kstrdup(onmax_fn_name, GFP_KERNEL); ++ } else ++ goto free; + -+ if (!data->onmax_var_str || !data->onmax_fn_name) { ++ data->onmax.fn_name = kstrdup(onmax_fn_name, GFP_KERNEL); ++ if (!data->onmax.fn_name) { + ret = -ENOMEM; + goto free; + } @@ -347,7 +353,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static void onmatch_destroy(struct action_data *data) { unsigned int i; -@@ -2689,37 +2925,6 @@ static int check_synth_field(struct synt +@@ -2851,37 +3090,6 @@ static int check_synth_field(struct synt return 0; } @@ -385,7 +391,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static struct hist_field * onmatch_find_var(struct hist_trigger_data *hist_data, struct action_data *data, char *system, char *event, char *var) -@@ -3313,6 +3518,8 @@ static void destroy_actions(struct hist_ +@@ -3521,6 +3729,8 @@ static void destroy_actions(struct hist_ if (data->fn == action_trace) onmatch_destroy(data); @@ -394,7 +400,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> else kfree(data); } -@@ -3341,6 +3548,18 @@ static int create_actions(struct hist_tr +@@ -3550,6 +3760,18 @@ static int create_actions(struct hist_tr onmatch_destroy(data); return ret; } @@ -413,7 +419,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } } -@@ -3355,9 +3574,30 @@ static void print_actions(struct seq_fil +@@ -3564,9 +3786,30 @@ static void print_actions(struct seq_fil for (i = 0; i < hist_data->n_actions; i++) { struct action_data *data = hist_data->actions[i]; @@ -430,8 +436,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + unsigned int i; + + seq_puts(m, ":onmax("); -+ seq_printf(m, "%s", data->onmax_var_str); -+ seq_printf(m, ").%s(", data->onmax_fn_name); ++ seq_printf(m, "%s", data->onmax.var_str); ++ seq_printf(m, ").%s(", data->onmax.fn_name); + + for (i = 0; i < hist_data->n_max_vars; i++) { + seq_printf(m, "%s", hist_data->max_vars[i]->var->var.name); @@ -444,7 +450,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static void print_onmatch_spec(struct seq_file *m, struct hist_trigger_data *hist_data, struct action_data *data) -@@ -3388,6 +3628,8 @@ static void print_actions_spec(struct se +@@ -3597,6 +3840,8 @@ static void print_actions_spec(struct se if (data->fn == action_trace) print_onmatch_spec(m, hist_data, data); diff --git a/patches/0025-tracing-Allow-whitespace-to-surround-hist-trigger-fi.patch b/patches/0031-tracing-Allow-whitespace-to-surround-hist-trigger-fi.patch index 9c88c398bcdf..1694bb042120 100644 --- a/patches/0025-tracing-Allow-whitespace-to-surround-hist-trigger-fi.patch +++ b/patches/0031-tracing-Allow-whitespace-to-surround-hist-trigger-fi.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:26 -0500 -Subject: [PATCH 25/32] tracing: Allow whitespace to surround hist trigger +Date: Tue, 5 Sep 2017 16:57:43 -0500 +Subject: [PATCH 31/40] tracing: Allow whitespace to surround hist trigger filter The existing code only allows for one space before and after the 'if' @@ -15,16 +15,16 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -4632,7 +4632,7 @@ static int event_hist_trigger_func(struc - struct event_trigger_ops *trigger_ops; - struct hist_trigger_data *hist_data; +@@ -4819,7 +4819,7 @@ static int event_hist_trigger_func(struc + struct synth_event *se; + const char *se_name; bool remove = false; - char *trigger; + char *trigger, *p; int ret = 0; if (!param) -@@ -4642,9 +4642,19 @@ static int event_hist_trigger_func(struc +@@ -4829,9 +4829,19 @@ static int event_hist_trigger_func(struc remove = true; /* separate the trigger from the filter (k:v [if filter]) */ @@ -47,7 +47,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> attrs = parse_hist_trigger_attrs(trigger); if (IS_ERR(attrs)) -@@ -4694,6 +4704,7 @@ static int event_hist_trigger_func(struc +@@ -4889,6 +4899,7 @@ static int event_hist_trigger_func(struc } ret = cmd_ops->reg(glob, trigger_ops, trigger_data, file); diff --git a/patches/0027-tracing-Add-cpu-field-for-hist-triggers.patch b/patches/0032-tracing-Add-cpu-field-for-hist-triggers.patch index 3312a4c53402..099409f5133f 100644 --- a/patches/0027-tracing-Add-cpu-field-for-hist-triggers.patch +++ b/patches/0032-tracing-Add-cpu-field-for-hist-triggers.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:28 -0500 -Subject: [PATCH 27/32] tracing: Add cpu field for hist triggers +Date: Tue, 5 Sep 2017 16:57:44 -0500 +Subject: [PATCH 32/40] tracing: Add cpu field for hist triggers A common key to use in a histogram is the cpuid - add a new cpu 'synthetic' field for that purpose. This field is named cpu rather @@ -44,15 +44,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --------------------------- --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -224,6 +224,7 @@ enum hist_field_flags { +@@ -226,6 +226,7 @@ enum hist_field_flags { HIST_FIELD_FL_VAR_ONLY = 8192, HIST_FIELD_FL_EXPR = 16384, HIST_FIELD_FL_VAR_REF = 32768, + HIST_FIELD_FL_CPU = 65536, }; - struct hist_trigger_attrs { -@@ -1081,6 +1082,16 @@ static u64 hist_field_timestamp(struct h + struct var_defs { +@@ -1170,6 +1171,16 @@ static u64 hist_field_timestamp(struct h return ts; } @@ -69,7 +69,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static struct hist_field *check_var_ref(struct hist_field *hist_field, struct hist_trigger_data *var_data, unsigned int var_idx) -@@ -1407,6 +1418,8 @@ static const char *hist_field_name(struc +@@ -1518,6 +1529,8 @@ static const char *hist_field_name(struc field_name = hist_field_name(field->operands[0], ++level); else if (field->flags & HIST_FIELD_FL_TIMESTAMP) field_name = "$common_timestamp"; @@ -78,7 +78,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> else if (field->flags & HIST_FIELD_FL_EXPR || field->flags & HIST_FIELD_FL_VAR_REF) field_name = field->name; -@@ -1848,6 +1861,15 @@ static struct hist_field *create_hist_fi +@@ -1990,6 +2003,15 @@ static struct hist_field *create_hist_fi goto out; } @@ -94,7 +94,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (WARN_ON_ONCE(!field)) goto out; -@@ -1980,7 +2002,9 @@ parse_field(struct hist_trigger_data *hi +@@ -2182,7 +2204,9 @@ parse_field(struct hist_trigger_data *hi hist_data->enable_timestamps = true; if (*flags & HIST_FIELD_FL_TIMESTAMP_USECS) hist_data->attrs->ts_in_usecs = true; @@ -103,9 +103,9 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + *flags |= HIST_FIELD_FL_CPU; + else { field = trace_find_event_field(file->event_call, field_name); - if (!field) - return ERR_PTR(-EINVAL); -@@ -3019,7 +3043,6 @@ static int onmatch_create(struct hist_tr + if (!field || !field->size) { + field = ERR_PTR(-EINVAL); +@@ -3185,7 +3209,6 @@ static int onmatch_create(struct hist_tr goto out; } } @@ -113,7 +113,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (param[0] == '$') hist_field = onmatch_find_var(hist_data, data, system, event_name, param); -@@ -3034,7 +3057,6 @@ static int onmatch_create(struct hist_tr +@@ -3200,7 +3223,6 @@ static int onmatch_create(struct hist_tr ret = -EINVAL; goto out; } @@ -121,7 +121,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (check_synth_field(event, hist_field, field_pos) == 0) { var_ref = create_var_ref(hist_field); if (!var_ref) { -@@ -4128,6 +4150,8 @@ static void hist_field_print(struct seq_ +@@ -4315,6 +4337,8 @@ static void hist_field_print(struct seq_ if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP) seq_puts(m, "$common_timestamp"); diff --git a/patches/0028-tracing-Add-hist-trigger-support-for-variable-refere.patch b/patches/0033-tracing-Add-hist-trigger-support-for-variable-refere.patch index 6993202a8e00..e6ca275f7f7d 100644 --- a/patches/0028-tracing-Add-hist-trigger-support-for-variable-refere.patch +++ b/patches/0033-tracing-Add-hist-trigger-support-for-variable-refere.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:29 -0500 -Subject: [PATCH 28/32] tracing: Add hist trigger support for variable +Date: Tue, 5 Sep 2017 16:57:45 -0500 +Subject: [PATCH 33/40] tracing: Add hist trigger support for variable reference aliases Add support for alias=$somevar where alias can be used as @@ -9,20 +9,20 @@ onmatch($alias). Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - kernel/trace/trace_events_hist.c | 46 ++++++++++++++++++++++++++++++++++++--- - 1 file changed, 43 insertions(+), 3 deletions(-) + kernel/trace/trace_events_hist.c | 61 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 58 insertions(+), 3 deletions(-) --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -225,6 +225,7 @@ enum hist_field_flags { +@@ -227,6 +227,7 @@ enum hist_field_flags { HIST_FIELD_FL_EXPR = 16384, HIST_FIELD_FL_VAR_REF = 32768, HIST_FIELD_FL_CPU = 65536, + HIST_FIELD_FL_ALIAS = 131072, }; - struct hist_trigger_attrs { -@@ -1414,7 +1415,8 @@ static const char *hist_field_name(struc + struct var_defs { +@@ -1525,7 +1526,8 @@ static const char *hist_field_name(struc if (field->field) field_name = field->field->name; @@ -32,7 +32,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> field_name = hist_field_name(field->operands[0], ++level); else if (field->flags & HIST_FIELD_FL_TIMESTAMP) field_name = "$common_timestamp"; -@@ -1819,7 +1821,7 @@ static struct hist_field *create_hist_fi +@@ -1961,7 +1963,7 @@ static struct hist_field *create_hist_fi hist_field->hist_data = hist_data; @@ -41,7 +41,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> goto out; /* caller will populate */ if (flags & HIST_FIELD_FL_VAR_REF) { -@@ -2013,6 +2015,34 @@ parse_field(struct hist_trigger_data *hi +@@ -2219,6 +2221,29 @@ parse_field(struct hist_trigger_data *hi return field; } @@ -59,13 +59,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + + alias->fn = var_ref->fn; + alias->operands[0] = var_ref; -+ alias->var.idx = var_ref->var.idx; -+ alias->var.hist_data = var_ref->hist_data; -+ alias->size = var_ref->size; -+ alias->is_signed = var_ref->is_signed; -+ alias->type = kstrdup(var_ref->type, GFP_KERNEL); -+ if (!alias->type) { -+ kfree(alias->type); ++ ++ if (init_var_ref(alias, var_ref)) { + destroy_hist_field(alias, 0); + return NULL; + } @@ -76,21 +71,48 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> struct hist_field *parse_atom(struct hist_trigger_data *hist_data, struct trace_event_file *file, char *str, unsigned long *flags, char *var_name) -@@ -2036,6 +2066,13 @@ struct hist_field *parse_atom(struct his - if (hist_field) { - hist_data->var_refs[hist_data->n_var_refs] = hist_field; - hist_field->var_ref_idx = hist_data->n_var_refs++; -+ if (var_name) { -+ hist_field = create_alias(hist_data, hist_field, var_name); -+ if (!hist_field) { -+ ret = -ENOMEM; -+ goto out; +@@ -2245,6 +2270,13 @@ struct hist_field *parse_atom(struct his + if (hist_field) { + hist_data->var_refs[hist_data->n_var_refs] = hist_field; + hist_field->var_ref_idx = hist_data->n_var_refs++; ++ if (var_name) { ++ hist_field = create_alias(hist_data, hist_field, var_name); ++ if (!hist_field) { ++ ret = -ENOMEM; ++ goto out; ++ } + } -+ } - return hist_field; - } + return hist_field; + } + } else +@@ -2346,6 +2378,26 @@ static int check_expr_operands(struct hi + unsigned long operand1_flags = operand1->flags; + unsigned long operand2_flags = operand2->flags; -@@ -4152,8 +4189,11 @@ static void hist_field_print(struct seq_ ++ if ((operand1_flags & HIST_FIELD_FL_VAR_REF) || ++ (operand1_flags & HIST_FIELD_FL_ALIAS)) { ++ struct hist_field *var; ++ ++ var = find_var_field(operand1->var.hist_data, operand1->name); ++ if (!var) ++ return -EINVAL; ++ operand1_flags = var->flags; ++ } ++ ++ if ((operand2_flags & HIST_FIELD_FL_VAR_REF) || ++ (operand2_flags & HIST_FIELD_FL_ALIAS)) { ++ struct hist_field *var; ++ ++ var = find_var_field(operand2->var.hist_data, operand2->name); ++ if (!var) ++ return -EINVAL; ++ operand2_flags = var->flags; ++ } ++ + if ((operand1_flags & HIST_FIELD_FL_TIMESTAMP_USECS) != + (operand2_flags & HIST_FIELD_FL_TIMESTAMP_USECS)) + return -EINVAL; +@@ -4339,8 +4391,11 @@ static void hist_field_print(struct seq_ seq_puts(m, "$common_timestamp"); else if (hist_field->flags & HIST_FIELD_FL_CPU) seq_puts(m, "cpu"); diff --git a/patches/0029-tracing-Add-last-error-error-facility-for-hist-trigg.patch b/patches/0034-tracing-Add-last-error-error-facility-for-hist-trigg.patch index 325bfb804421..84c5378223de 100644 --- a/patches/0029-tracing-Add-last-error-error-facility-for-hist-trigg.patch +++ b/patches/0034-tracing-Add-last-error-error-facility-for-hist-trigg.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:30 -0500 -Subject: [PATCH 29/32] tracing: Add 'last error' error facility for hist +Date: Tue, 5 Sep 2017 16:57:46 -0500 +Subject: [PATCH 34/40] tracing: Add 'last error' error facility for hist triggers With the addition of variables and actions, it's become necessary to @@ -25,9 +25,9 @@ Also add specific error messages for variable and action errors. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - Documentation/trace/events.txt | 19 ++++ - kernel/trace/trace_events_hist.c | 181 ++++++++++++++++++++++++++++++++++++--- - 2 files changed, 188 insertions(+), 12 deletions(-) + Documentation/trace/events.txt | 19 +++ + kernel/trace/trace_events_hist.c | 188 ++++++++++++++++++++++++++++++++++++--- + 2 files changed, 194 insertions(+), 13 deletions(-) --- a/Documentation/trace/events.txt +++ b/Documentation/trace/events.txt @@ -59,16 +59,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --------------------------- --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -288,6 +288,7 @@ struct hist_trigger_data { - struct field_var *max_vars[SYNTH_FIELDS_MAX]; - unsigned int n_max_vars; - unsigned int n_max_var_str; -+ char *last_err; - }; - - struct synth_field { -@@ -332,6 +333,83 @@ struct action_data { - struct hist_field *onmax_var; +@@ -351,6 +351,88 @@ struct action_data { + }; }; + @@ -80,9 +72,14 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + 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 (!last_hist_cmd || !hist_err_str) ++ if (!hist_err_str) { ++ kfree(last_hist_cmd); + ret = -ENOMEM; ++ } + + return ret; +} @@ -92,7 +89,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + if (!last_hist_cmd || !str) + return; + -+ if (strlen(last_hist_cmd) > MAX_FILTER_STR_VAL - 1) ++ if (strlen(str) > MAX_FILTER_STR_VAL - 1) + return; + + strcpy(last_hist_cmd, str); @@ -102,10 +99,10 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +{ + int maxlen = MAX_FILTER_STR_VAL - 1; + -+ if (strlen(hist_err_str)) ++ if (!hist_err_str || !str) + return; + -+ if (!hist_err_str || !str) ++ if (strlen(hist_err_str)) + return; + + if (!var) @@ -151,7 +148,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static LIST_HEAD(synth_event_list); static DEFINE_MUTEX(synth_event_mutex); -@@ -1954,12 +2032,21 @@ static struct hist_field *create_var_ref +@@ -2110,9 +2192,18 @@ static struct hist_field *create_var_ref return ref_field; } @@ -163,18 +160,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + return false; +} + - static struct hist_field *parse_var_ref(char *system, char *event_name, - char *var_name) + static bool is_var_ref(char *var_name) { - struct hist_field *var_field = NULL, *ref_field = NULL; - - if (!var_name || strlen(var_name) < 2 || var_name[0] != '$') + if (!var_name || strlen(var_name) < 2 || var_name[0] != '$' || + is_common_field(var_name)) - return NULL; + return false; - var_name++; -@@ -1968,6 +2055,10 @@ static struct hist_field *parse_var_ref( + return true; +@@ -2164,6 +2255,10 @@ static struct hist_field *parse_var_ref( if (var_field) ref_field = create_var_ref(var_field); @@ -185,7 +179,19 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return ref_field; } -@@ -2426,8 +2517,11 @@ create_field_var_hist(struct hist_trigge +@@ -2399,8 +2494,10 @@ static int check_expr_operands(struct hi + } + + if ((operand1_flags & HIST_FIELD_FL_TIMESTAMP_USECS) != +- (operand2_flags & HIST_FIELD_FL_TIMESTAMP_USECS)) ++ (operand2_flags & HIST_FIELD_FL_TIMESTAMP_USECS)) { ++ hist_err("Timestamp units in expression don't match", NULL); + return -EINVAL; ++ } + + return 0; + } +@@ -2600,19 +2697,27 @@ create_field_var_hist(struct hist_trigge char *cmd; int ret; @@ -196,11 +202,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return ERR_PTR(-EINVAL); + } - tr = top_trace_array(); - if (!tr) -@@ -2435,13 +2529,18 @@ create_field_var_hist(struct hist_trigge + file = event_file(tr, system, event_name); - file = event_file(system, event_name); if (IS_ERR(file)) { + hist_err_event("onmatch: Event file not found: ", + system, event_name, field_name); @@ -218,7 +221,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> var_hist = kzalloc(sizeof(*var_hist), GFP_KERNEL); if (!var_hist) -@@ -2489,6 +2588,8 @@ create_field_var_hist(struct hist_trigge +@@ -2660,6 +2765,8 @@ create_field_var_hist(struct hist_trigge kfree(cmd); kfree(var_hist->cmd); kfree(var_hist); @@ -227,7 +230,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return ERR_PTR(ret); } -@@ -2500,6 +2601,8 @@ create_field_var_hist(struct hist_trigge +@@ -2671,6 +2778,8 @@ create_field_var_hist(struct hist_trigge kfree(cmd); kfree(var_hist->cmd); kfree(var_hist); @@ -236,7 +239,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return ERR_PTR(-EINVAL); } -@@ -2636,18 +2739,21 @@ static struct field_var *create_field_va +@@ -2807,18 +2916,21 @@ static struct field_var *create_field_va int ret = 0; if (hist_data->n_field_vars >= SYNTH_FIELDS_MAX) { @@ -258,10 +261,10 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> kfree(val); ret = PTR_ERR(var); goto err; -@@ -2772,14 +2878,18 @@ static int onmax_create(struct hist_trig +@@ -2943,14 +3055,18 @@ static int onmax_create(struct hist_trig int ret = 0; - onmax_var_str = data->onmax_var_str; + onmax_var_str = data->onmax.var_str; - if (onmax_var_str[0] != '$') + if (onmax_var_str[0] != '$') { + hist_err("onmax: For onmax(x), x must be a variable: ", onmax_var_str); @@ -279,15 +282,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> flags = HIST_FIELD_FL_VAR_REF; ref_field = create_hist_field(hist_data, NULL, flags, NULL); -@@ -2803,6 +2913,7 @@ static int onmax_create(struct hist_trig - data->max_var_ref_idx = var_ref_idx; +@@ -2970,6 +3086,7 @@ static int onmax_create(struct hist_trig + data->onmax.max_var_ref_idx = var_ref_idx; max_var = create_var(hist_data, file, "max", sizeof(u64), "u64"); if (IS_ERR(max_var)) { + hist_err("onmax: Couldn't create onmax variable: ", "max"); ret = PTR_ERR(max_var); goto out; } -@@ -2815,6 +2926,7 @@ static int onmax_create(struct hist_trig +@@ -2982,6 +3099,7 @@ static int onmax_create(struct hist_trig field_var = create_target_field_var(hist_data, NULL, NULL, param); if (IS_ERR(field_var)) { @@ -295,7 +298,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ret = PTR_ERR(field_var); kfree(param); goto out; -@@ -2847,6 +2959,7 @@ static int parse_action_params(char *par +@@ -3014,6 +3132,7 @@ static int parse_action_params(char *par param = strstrip(param); if (strlen(param) < 2) { @@ -303,8 +306,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ret = -EINVAL; goto out; } -@@ -3004,6 +3117,9 @@ onmatch_find_var(struct hist_trigger_dat - hist_field = find_event_var(system, event, var); +@@ -3185,6 +3304,9 @@ onmatch_find_var(struct hist_trigger_dat + hist_field = find_event_var(tr, system, event, var); } + if (!hist_field) @@ -313,15 +316,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return hist_field; } -@@ -3055,6 +3171,7 @@ static int onmatch_create(struct hist_tr +@@ -3236,6 +3358,7 @@ static int onmatch_create(struct hist_tr - event = find_synth_event(data->synth_event_name); + event = find_synth_event(data->onmatch.synth_event_name); if (!event) { -+ hist_err("onmatch: Couldn't find synthetic event: ", data->synth_event_name); ++ hist_err("onmatch: Couldn't find synthetic event: ", data->onmatch.synth_event_name); ret = -EINVAL; goto out; } -@@ -3094,6 +3211,7 @@ static int onmatch_create(struct hist_tr +@@ -3275,6 +3398,7 @@ static int onmatch_create(struct hist_tr ret = -EINVAL; goto out; } @@ -329,7 +332,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (check_synth_field(event, hist_field, field_pos) == 0) { var_ref = create_var_ref(hist_field); if (!var_ref) { -@@ -3108,12 +3226,15 @@ static int onmatch_create(struct hist_tr +@@ -3289,12 +3413,15 @@ static int onmatch_create(struct hist_tr continue; } @@ -345,7 +348,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ret = -EINVAL; goto out; } -@@ -3141,31 +3262,44 @@ static struct action_data *onmatch_parse +@@ -3322,15 +3449,22 @@ static struct action_data *onmatch_parse return ERR_PTR(-ENOMEM); match_event = strsep(&str, ")"); @@ -362,15 +365,17 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> goto free; + } -- if (IS_ERR(event_file(match_event_system, match_event))) -+ if (IS_ERR(event_file(match_event_system, match_event))) { +- if (IS_ERR(event_file(tr, match_event_system, match_event))) ++ if (IS_ERR(event_file(tr, match_event_system, match_event))) { + hist_err_event("onmatch: Invalid subsystem or event name: ", + match_event_system, match_event, NULL); goto free; + } - data->match_event = kstrdup(match_event, GFP_KERNEL); - data->match_event_system = kstrdup(match_event_system, GFP_KERNEL); + data->onmatch.match_event = kstrdup(match_event, GFP_KERNEL); + if (!data->onmatch.match_event) { +@@ -3345,12 +3479,16 @@ static struct action_data *onmatch_parse + } strsep(&str, "."); - if (!str) @@ -385,7 +390,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + hist_err("onmatch: Missing opening paramlist paren: ", synth_event_name); goto free; + } - data->synth_event_name = kstrdup(synth_event_name, GFP_KERNEL); + + data->onmatch.synth_event_name = kstrdup(synth_event_name, GFP_KERNEL); + if (!data->onmatch.synth_event_name) { +@@ -3359,8 +3497,10 @@ static struct action_data *onmatch_parse + } params = strsep(&str, ")"); - if (!params || !str || (str && strlen(str))) @@ -396,39 +405,30 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ret = parse_action_params(params, data); if (ret) -@@ -3217,6 +3351,7 @@ static int create_val_field(struct hist_ - if (field_str && var_name) { - if (find_var(file, var_name) && - !hist_data->remove) { -+ hist_err("Variable already defined: ", var_name); - ret = -EINVAL; - goto out; - } -@@ -3224,6 +3359,7 @@ static int create_val_field(struct hist_ - flags |= HIST_FIELD_FL_VAR; - hist_data->n_vars++; - if (hist_data->n_vars > TRACING_MAP_VARS_MAX) { -+ hist_err("Too many variables defined: ", var_name); - ret = -EINVAL; - goto out; - } -@@ -3234,6 +3370,7 @@ static int create_val_field(struct hist_ - field_str = var_name; - var_name = NULL; - } else { -+ hist_err("Malformed assignment: ", var_name); - ret = -EINVAL; - goto out; +@@ -3440,12 +3580,14 @@ static int create_var_field(struct hist_ + return -EINVAL; + + if (find_var(file, var_name) && !hist_data->remove) { ++ hist_err("Variable already defined: ", var_name); + return -EINVAL; } -@@ -3248,6 +3385,7 @@ static int create_val_field(struct hist_ - hist_field = parse_atom(hist_data, file, field_str, - &flags, var_name); - if (IS_ERR(hist_field)) { -+ hist_err("Unable to parse atom: ", field_str); - ret = PTR_ERR(hist_field); - goto out; - } -@@ -4138,6 +4276,11 @@ static int hist_show(struct seq_file *m, + + flags |= HIST_FIELD_FL_VAR; + hist_data->n_vars++; + if (hist_data->n_vars > TRACING_MAP_VARS_MAX) { ++ hist_err("Too many variables defined: ", var_name); + return -EINVAL; + } + +@@ -3636,6 +3778,7 @@ static int parse_var_defs(struct hist_tr + + var_name = strsep(&field_str, "="); + if (!var_name || !field_str) { ++ hist_err("Malformed assignment: ", var_name); + ret = -EINVAL; + goto free; + } +@@ -4362,6 +4505,11 @@ static int hist_show(struct seq_file *m, hist_trigger_show(m, data, n++); } @@ -440,7 +440,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> out_unlock: mutex_unlock(&event_mutex); -@@ -4509,6 +4652,7 @@ static int hist_register_trigger(char *g +@@ -4709,6 +4857,7 @@ static int hist_register_trigger(char *g if (named_data) { if (!hist_trigger_match(data, named_data, named_data, true)) { @@ -448,7 +448,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ret = -EINVAL; goto out; } -@@ -4528,13 +4672,16 @@ static int hist_register_trigger(char *g +@@ -4728,13 +4877,16 @@ static int hist_register_trigger(char *g test->paused = false; else if (hist_data->attrs->clear) hist_clear(test); @@ -466,7 +466,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ret = -ENOENT; goto out; } -@@ -4701,6 +4848,11 @@ static int event_hist_trigger_func(struc +@@ -4901,6 +5053,11 @@ static int event_hist_trigger_func(struc char *trigger, *p; int ret = 0; @@ -478,7 +478,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> if (!param) return -EINVAL; -@@ -4804,6 +4956,9 @@ static int event_hist_trigger_func(struc +@@ -5019,6 +5176,9 @@ static int event_hist_trigger_func(struc /* Just return zero, not the number of registered triggers */ ret = 0; out: @@ -488,7 +488,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return ret; out_unreg: cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); -@@ -5002,6 +5157,8 @@ static __init int trace_events_hist_init +@@ -5208,6 +5368,8 @@ static __init int trace_events_hist_init goto err; } diff --git a/patches/0035-tracing-Reverse-the-order-event_mutex-trace_types_lo.patch b/patches/0035-tracing-Reverse-the-order-event_mutex-trace_types_lo.patch new file mode 100644 index 000000000000..400cac857dc7 --- /dev/null +++ b/patches/0035-tracing-Reverse-the-order-event_mutex-trace_types_lo.patch @@ -0,0 +1,95 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Tue, 5 Sep 2017 16:57:47 -0500 +Subject: [PATCH 35/40] tracing: Reverse the order event_mutex/trace_types_lock + are taken + +Change the order event_mutex and trace_types_lock are taken, to avoid +circular dependencies and lockdep spew. + +Changing the order shouldn't matter to any current code, but does to +anything that takes the event_mutex first and then trace_types_lock. +This is the case when calling tracing_set_clock from inside an event +command, which already holds the event_mutex. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace_events.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -1366,8 +1366,8 @@ static int subsystem_open(struct inode * + return -ENODEV; + + /* Make sure the system still exists */ +- mutex_lock(&trace_types_lock); + mutex_lock(&event_mutex); ++ mutex_lock(&trace_types_lock); + list_for_each_entry(tr, &ftrace_trace_arrays, list) { + list_for_each_entry(dir, &tr->systems, list) { + if (dir == inode->i_private) { +@@ -1381,8 +1381,8 @@ static int subsystem_open(struct inode * + } + } + exit_loop: +- mutex_unlock(&event_mutex); + mutex_unlock(&trace_types_lock); ++ mutex_unlock(&event_mutex); + + if (!system) + return -ENODEV; +@@ -2250,15 +2250,15 @@ static void __add_event_to_tracers(struc + int trace_add_event_call(struct trace_event_call *call) + { + int ret; +- mutex_lock(&trace_types_lock); + mutex_lock(&event_mutex); ++ mutex_lock(&trace_types_lock); + + ret = __register_event(call, NULL); + if (ret >= 0) + __add_event_to_tracers(call); + +- mutex_unlock(&event_mutex); + mutex_unlock(&trace_types_lock); ++ mutex_unlock(&event_mutex); + return ret; + } + +@@ -2312,13 +2312,13 @@ int trace_remove_event_call(struct trace + { + int ret; + +- mutex_lock(&trace_types_lock); + mutex_lock(&event_mutex); ++ mutex_lock(&trace_types_lock); + down_write(&trace_event_sem); + ret = probe_remove_event_call(call); + up_write(&trace_event_sem); +- mutex_unlock(&event_mutex); + mutex_unlock(&trace_types_lock); ++ mutex_unlock(&event_mutex); + + return ret; + } +@@ -2385,8 +2385,8 @@ static int trace_module_notify(struct no + { + struct module *mod = data; + +- mutex_lock(&trace_types_lock); + mutex_lock(&event_mutex); ++ mutex_lock(&trace_types_lock); + switch (val) { + case MODULE_STATE_COMING: + trace_module_add_events(mod); +@@ -2395,8 +2395,8 @@ static int trace_module_notify(struct no + trace_module_remove_events(mod); + break; + } +- mutex_unlock(&event_mutex); + mutex_unlock(&trace_types_lock); ++ mutex_unlock(&event_mutex); + + return 0; + } diff --git a/patches/0036-tracing-Remove-lookups-from-tracing_map-hitcount.patch b/patches/0036-tracing-Remove-lookups-from-tracing_map-hitcount.patch new file mode 100644 index 000000000000..39722e6579c3 --- /dev/null +++ b/patches/0036-tracing-Remove-lookups-from-tracing_map-hitcount.patch @@ -0,0 +1,26 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Tue, 5 Sep 2017 16:57:48 -0500 +Subject: [PATCH 36/40] tracing: Remove lookups from tracing_map hitcount + +Lookups inflate the hitcount, making it essentially useless. Only +inserts and updates should really affect the hitcount anyway, so +explicitly filter lookups out. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/tracing_map.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/kernel/trace/tracing_map.c ++++ b/kernel/trace/tracing_map.c +@@ -535,7 +535,8 @@ static inline struct tracing_map_elt * + if (test_key && test_key == key_hash) { + if (entry->val && + keys_match(key, entry->val->key, map->key_size)) { +- atomic64_inc(&map->hits); ++ if (!lookup_only) ++ atomic64_inc(&map->hits); + return entry->val; + } else if (unlikely(!entry->val)) { + /* diff --git a/patches/0030-tracing-Add-inter-event-hist-trigger-Documentation.patch b/patches/0037-tracing-Add-inter-event-hist-trigger-Documentation.patch index 18c4d2093b18..08fc6d64a9d9 100644 --- a/patches/0030-tracing-Add-inter-event-hist-trigger-Documentation.patch +++ b/patches/0037-tracing-Add-inter-event-hist-trigger-Documentation.patch @@ -1,15 +1,16 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:31 -0500 -Subject: [PATCH 30/32] tracing: Add inter-event hist trigger Documentation +Date: Tue, 5 Sep 2017 16:57:49 -0500 +Subject: [PATCH 37/40] tracing: Add inter-event hist trigger Documentation Add background and details on inter-event hist triggers, including hist variables, synthetic events, and actions. Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Baohong Liu <baohong.liu@intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- - Documentation/trace/events.txt | 376 +++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 376 insertions(+) + Documentation/trace/events.txt | 385 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 385 insertions(+) --- a/Documentation/trace/events.txt +++ b/Documentation/trace/events.txt @@ -21,7 +22,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Note that in general the semantics of a given field aren't interpreted when applying a modifier to it, but there are some -@@ -2101,3 +2102,378 @@ triggers (you have to use '!' for each o +@@ -2101,3 +2102,387 @@ triggers (you have to use '!' for each o Hits: 489 Entries: 7 Dropped: 0 @@ -124,14 +125,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +creates a variable named 'ts0' for a histogram entry with the key +'next_pid': + -+ # echo 'hist:keys=next_pid:vals=ts0=$common_timestamp ... >> event/trigger ++ # echo 'hist:keys=next_pid:vals=$ts0:ts0=$common_timestamp ... >> \ ++ event/trigger + +The ts0 variable can be accessed by any subsequent event having the +same pid as 'next_pid'. + +Variable references are formed by prepending the variable name with +the '$' sign. Thus for example, the ts0 variable above would be -+referenced as '$ts0' in subsequent expressions. ++referenced as '$ts0' in expressions. + +Because 'vals=' is used, the $common_timestamp variable value above +will also be summed as a normal histogram value would (though for a @@ -139,7 +141,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + +The below shows that a key value can also be saved in the same way: + -+ # echo 'hist:key=timer_pid=common_pid ...' >> event/trigger ++ # echo 'hist:timer_pid=common_pid:key=timer_pid ...' >> event/trigger + +If a variable isn't a key variable or prefixed with 'vals=', the +associated event field will be saved in a variable but won't be summed @@ -151,7 +153,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +result in both ts0 and b being created as variables, with both +common_timestamp and field1 additionally being summed as values: + -+ # echo 'hist:keys=pid:vals=ts0=$common_timestamp,b=field1 ... >> event/trigger ++ # echo 'hist:keys=pid:vals=$ts0,$b:ts0=$common_timestamp,b=field1 ... >> \ ++ event/trigger ++ ++Note that variable assignments can appear either preceding or ++following their use. The command below behaves identically to the ++command above: ++ ++ # echo 'hist:keys=pid:ts0=$common_timestamp,b=field1:vals=$ts0,$b ... >> \ ++ event/trigger + +Any number of variables not bound to a 'vals=' prefix can also be +assigned by simply separating them with colons. Below is the same @@ -293,7 +303,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + occurs, which because of the 'if comm == "cyclictest"' filter only + happens when the executable is cyclictest: + -+ # echo 'hist:keys=testpid=pid:onmatch(sched.sched_wakeup_new).\ ++ # echo 'hist:keys=$testpid:testpid=pid:onmatch(sched.sched_wakeup_new).\ + wakeup_new_test($testpid) if comm=="cyclictest"' >> \ + /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/trigger + @@ -319,12 +329,12 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + # echo 'wakeup_latency u64 lat; pid_t pid; int prio' >> \ + /sys/kernel/debug/tracing/synthetic_events + -+ Next, we specify that whenever we see a sched_wakeup event for a ++ Next, we specify that whenever we see a sched_waking event for a + cyclictest thread, save the timestamp in a 'ts0' variable: + -+ # echo 'hist:keys=saved_pid=pid:ts0=$common_timestamp.usecs \ ++ # echo 'hist:keys=$saved_pid:saved_pid=pid:ts0=$common_timestamp.usecs \ + if comm=="cyclictest"' >> \ -+ /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger ++ /sys/kernel/debug/tracing/events/sched/sched_waking/trigger + + Then, when the corresponding thread is actually scheduled onto the + CPU by a sched_switch event, calculate the latency and use that @@ -332,7 +342,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + wakeup_latency synthetic event: + + # echo 'hist:keys=next_pid:wakeup_lat=$common_timestamp.usecs-$ts0:\ -+ onmatch(sched.sched_wakeup).wakeup_latency($wakeup_lat,\ ++ onmatch(sched.sched_waking).wakeup_latency($wakeup_lat,\ + $saved_pid,next_prio) if next_comm=="cyclictest"' >> \ + /sys/kernel/debug/tracing/events/sched/sched_switch/trigger + @@ -348,7 +358,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + + # cat /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/hist + -+ - onmax(var).save(field,...) ++ - onmax(var).save(field,.. .) + + The 'onmax(var).save(field,...)' hist trigger action is invoked + whenever the value of 'var' associated with a histogram entry @@ -362,8 +372,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + displaying the saved values will be printed. + + As an example the below defines a couple of hist triggers, one for -+ sched_wakeup and another for sched_switch, keyed on pid. Whenever -+ a sched_wakeup occurs, the timestamp is saved in the entry ++ sched_waking and another for sched_switch, keyed on pid. Whenever ++ a sched_waking occurs, the timestamp is saved in the entry + corresponding to the current pid, and when the scheduler switches + back to that pid, the timestamp difference is calculated. If the + resulting latency, stored in wakeup_lat, exceeds the current @@ -372,7 +382,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + + # echo 'hist:keys=pid:ts0=$common_timestamp.usecs \ + if comm=="cyclictest"' >> \ -+ /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger ++ /sys/kernel/debug/tracing/events/sched/sched_waking/trigger + + # echo 'hist:keys=next_pid:\ + wakeup_lat=$common_timestamp.usecs-$ts0:\ diff --git a/patches/0031-tracing-Make-tracing_set_clock-non-static.patch b/patches/0038-tracing-Make-tracing_set_clock-non-static.patch index 88e35cf67957..a72269254654 100644 --- a/patches/0031-tracing-Make-tracing_set_clock-non-static.patch +++ b/patches/0038-tracing-Make-tracing_set_clock-non-static.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:32 -0500 -Subject: [PATCH 31/32] tracing: Make tracing_set_clock() non-static +Date: Tue, 5 Sep 2017 16:57:50 -0500 +Subject: [PATCH 38/40] tracing: Make tracing_set_clock() non-static Allow tracing code outside of trace.c to access tracing_set_clock(). @@ -29,7 +29,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h -@@ -279,6 +279,7 @@ extern int trace_array_get(struct trace_ +@@ -281,6 +281,7 @@ extern int trace_array_get(struct trace_ extern void trace_array_put(struct trace_array *tr); extern int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs); diff --git a/patches/0032-tracing-Add-a-clock-attribute-for-hist-triggers.patch b/patches/0039-tracing-Add-a-clock-attribute-for-hist-triggers.patch index 71dd2da4d25f..1e23eb9bf4e3 100644 --- a/patches/0032-tracing-Add-a-clock-attribute-for-hist-triggers.patch +++ b/patches/0039-tracing-Add-a-clock-attribute-for-hist-triggers.patch @@ -1,6 +1,6 @@ From: Tom Zanussi <tom.zanussi@linux.intel.com> -Date: Mon, 26 Jun 2017 17:49:33 -0500 -Subject: [PATCH 32/32] tracing: Add a clock attribute for hist triggers +Date: Tue, 5 Sep 2017 16:57:51 -0500 +Subject: [PATCH 39/40] tracing: Add a clock attribute for hist triggers The default clock if timestamps are used in a histogram is "global". If timestamps aren't used, the clock is irrelevant. @@ -25,7 +25,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +histogram, the trace buffer is automatically switched over to using +absolute timestamps and the "global" trace clock, in order to avoid +bogus timestamp differences with other clocks that aren't coherent -+across CPUs. This can be overriden by specifying one of the other ++across CPUs. This can be overridden by specifying one of the other +trace clocks instead, using the "clock=XXX" hist trigger attribute, +where XXX is any of the clocks listed in the tracing/trace_clock +pseudo-file. @@ -35,7 +35,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> 6.3.1 Histogram Variables --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c -@@ -233,6 +233,7 @@ struct hist_trigger_attrs { +@@ -241,6 +241,7 @@ struct hist_trigger_attrs { char *vals_str; char *sort_key_str; char *name; @@ -43,7 +43,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> bool pause; bool cont; bool clear; -@@ -1586,6 +1587,7 @@ static void destroy_hist_trigger_attrs(s +@@ -1701,6 +1702,7 @@ static void destroy_hist_trigger_attrs(s kfree(attrs->sort_key_str); kfree(attrs->keys_str); kfree(attrs->vals_str); @@ -51,7 +51,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> kfree(attrs); } -@@ -1625,7 +1627,16 @@ static int parse_assignment(char *str, s +@@ -1740,7 +1742,16 @@ static int parse_assignment(char *str, s attrs->sort_key_str = kstrdup(str, GFP_KERNEL); else if (strncmp(str, "name=", strlen("name=")) == 0) attrs->name = kstrdup(str, GFP_KERNEL); @@ -69,7 +69,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> int map_bits = parse_map_size(str); if (map_bits < 0) { -@@ -1688,6 +1699,12 @@ static struct hist_trigger_attrs *parse_ +@@ -1803,6 +1814,12 @@ static struct hist_trigger_attrs *parse_ goto free; } @@ -82,7 +82,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> return attrs; free: destroy_hist_trigger_attrs(attrs); -@@ -4437,6 +4454,8 @@ static int event_hist_trigger_print(stru +@@ -4644,6 +4661,8 @@ static int event_hist_trigger_print(stru seq_puts(m, ".descending"); } seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits)); @@ -91,7 +91,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> print_actions_spec(m, hist_data); -@@ -4702,10 +4721,19 @@ static int hist_register_trigger(char *g +@@ -4907,10 +4926,19 @@ static int hist_register_trigger(char *g goto out; } diff --git a/patches/0040-tracing-Add-trace_event_buffer_reserve-variant-that-.patch b/patches/0040-tracing-Add-trace_event_buffer_reserve-variant-that-.patch new file mode 100644 index 000000000000..18369c5b01cf --- /dev/null +++ b/patches/0040-tracing-Add-trace_event_buffer_reserve-variant-that-.patch @@ -0,0 +1,132 @@ +From: Steven Rostedt <rostedt@goodmis.org> +Date: Fri, 8 Sep 2017 16:27:56 -0400 +Subject: [PATCH] tracing: Add trace_event_buffer_reserve() variant that allows + recursion + +On Tue, 5 Sep 2017 16:57:52 -0500 +Tom Zanussi <tom.zanussi@linux.intel.com> wrote: + +> Synthetic event generation requires the reservation of a second event +> while the reservation of a previous event is still in progress. The +> trace_recursive_lock() check in ring_buffer_lock_reserve() prevents +> this however. +> +> This sets up a special reserve pathway for this particular case, +> leaving existing pathways untouched, other than an additional check in +> ring_buffer_lock_reserve() and trace_event_buffer_reserve(). These +> checks could be gotten rid of as well, with copies of those functions, +> but for now try to avoid that unless necessary. +> +> Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> + +I've been planing on changing that lock, which may help you here +without having to mess around with parameters. That is to simply add a +counter. Would this patch help you. You can add a patch to increment +the count to 5 with an explanation of handling synthetic events, but +even getting to 4 is extremely unlikely. + +I'll make this into an official patch if this works for you, and then +you can include it in your series. + +-- Steve + +Link: https://lkml.kernel.org/r/20170908162756.74c6be8a@gandalf.local.home +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/ring_buffer.c | 64 ++++++++++++---------------------------------- + 1 file changed, 17 insertions(+), 47 deletions(-) + +diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c +index ac34ea2b2a10..29430b81c674 100644 +--- a/kernel/trace/ring_buffer.c ++++ b/kernel/trace/ring_buffer.c +@@ -2581,61 +2581,29 @@ rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer) + * The lock and unlock are done within a preempt disable section. + * The current_context per_cpu variable can only be modified + * by the current task between lock and unlock. But it can +- * be modified more than once via an interrupt. To pass this +- * information from the lock to the unlock without having to +- * access the 'in_interrupt()' functions again (which do show +- * a bit of overhead in something as critical as function tracing, +- * we use a bitmask trick. ++ * be modified more than once via an interrupt. There are four ++ * different contexts that we need to consider. + * +- * bit 0 = NMI context +- * bit 1 = IRQ context +- * bit 2 = SoftIRQ context +- * bit 3 = normal context. ++ * Normal context. ++ * SoftIRQ context ++ * IRQ context ++ * NMI context + * +- * This works because this is the order of contexts that can +- * preempt other contexts. A SoftIRQ never preempts an IRQ +- * context. +- * +- * When the context is determined, the corresponding bit is +- * checked and set (if it was set, then a recursion of that context +- * happened). +- * +- * On unlock, we need to clear this bit. To do so, just subtract +- * 1 from the current_context and AND it to itself. +- * +- * (binary) +- * 101 - 1 = 100 +- * 101 & 100 = 100 (clearing bit zero) +- * +- * 1010 - 1 = 1001 +- * 1010 & 1001 = 1000 (clearing bit 1) +- * +- * The least significant bit can be cleared this way, and it +- * just so happens that it is the same bit corresponding to +- * the current 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. + */ + + static __always_inline int + trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer) + { +- unsigned int val = cpu_buffer->current_context; +- int bit; +- +- if (in_interrupt()) { +- if (in_nmi()) +- bit = RB_CTX_NMI; +- else if (in_irq()) +- bit = RB_CTX_IRQ; +- else +- bit = RB_CTX_SOFTIRQ; +- } else +- bit = RB_CTX_NORMAL; +- +- if (unlikely(val & (1 << bit))) ++ if (cpu_buffer->current_context >= 4) + return 1; + +- val |= (1 << bit); +- cpu_buffer->current_context = val; ++ cpu_buffer->current_context++; ++ /* Interrupts must see this update */ ++ barrier(); + + return 0; + } +@@ -2643,7 +2611,9 @@ trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer) + static __always_inline void + trace_recursive_unlock(struct ring_buffer_per_cpu *cpu_buffer) + { +- cpu_buffer->current_context &= cpu_buffer->current_context - 1; ++ /* Don't let the dec leak out */ ++ barrier(); ++ cpu_buffer->current_context--; + } + + /** +-- +2.14.1 + diff --git a/patches/Bluetooth-avoid-recursive-locking-in-hci_send_to_cha.patch b/patches/Bluetooth-avoid-recursive-locking-in-hci_send_to_cha.patch new file mode 100644 index 000000000000..07429dcf638a --- /dev/null +++ b/patches/Bluetooth-avoid-recursive-locking-in-hci_send_to_cha.patch @@ -0,0 +1,70 @@ +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +Date: Thu, 21 Sep 2017 15:35:57 +0200 +Subject: Bluetooth: avoid recursive locking in + hci_send_to_channel() + +Mart reported a deadlock in -RT in the call path: + hci_send_monitor_ctrl_event() -> hci_send_to_channel() + +because both functions acquire the same read lock hci_sk_list.lock. This +is also a mainline issue because the qrwlock implementation is writer +fair (the traditional rwlock implementation is reader biased). + +To avoid the deadlock there is now __hci_send_to_channel() which expects +the readlock to be held. + +Cc: Marcel Holtmann <marcel@holtmann.org> +Cc: Johan Hedberg <johan.hedberg@intel.com> +Cc: rt-stable@vger.kernel.org +Fixes: 38ceaa00d02d ("Bluetooth: Add support for sending MGMT commands and events to monitor") +Reported-by: Mart van de Wege <mvdwege@gmail.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + net/bluetooth/hci_sock.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +--- a/net/bluetooth/hci_sock.c ++++ b/net/bluetooth/hci_sock.c +@@ -251,15 +251,13 @@ void hci_send_to_sock(struct hci_dev *hd + } + + /* Send frame to sockets with specific channel */ +-void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, +- int flag, struct sock *skip_sk) ++static void __hci_send_to_channel(unsigned short channel, struct sk_buff *skb, ++ int flag, struct sock *skip_sk) + { + struct sock *sk; + + BT_DBG("channel %u len %d", channel, skb->len); + +- read_lock(&hci_sk_list.lock); +- + sk_for_each(sk, &hci_sk_list.head) { + struct sk_buff *nskb; + +@@ -285,6 +283,13 @@ void hci_send_to_channel(unsigned short + kfree_skb(nskb); + } + ++} ++ ++void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, ++ int flag, struct sock *skip_sk) ++{ ++ read_lock(&hci_sk_list.lock); ++ __hci_send_to_channel(channel, skb, flag, skip_sk); + read_unlock(&hci_sk_list.lock); + } + +@@ -388,8 +393,8 @@ void hci_send_monitor_ctrl_event(struct + hdr->index = index; + hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE); + +- hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, +- HCI_SOCK_TRUSTED, NULL); ++ __hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, ++ HCI_SOCK_TRUSTED, NULL); + kfree_skb(skb); + } + diff --git a/patches/drivers-zram-fix-zcomp_stream_get-smp_processor_id-u.patch b/patches/drivers-zram-fix-zcomp_stream_get-smp_processor_id-u.patch index 40ece686ab02..8eabb445eaef 100644 --- a/patches/drivers-zram-fix-zcomp_stream_get-smp_processor_id-u.patch +++ b/patches/drivers-zram-fix-zcomp_stream_get-smp_processor_id-u.patch @@ -1,4 +1,3 @@ -From 8b91f086c2492145698727d3d9c1918d97a0e41c Mon Sep 17 00:00:00 2001 From: Mike Galbraith <efault@gmx.de> Date: Wed, 23 Aug 2017 11:57:29 +0200 Subject: [PATCH] drivers/zram: fix zcomp_stream_get() smp_processor_id() use diff --git a/patches/ftrace-Fix-trace-header-alignment.patch b/patches/ftrace-Fix-trace-header-alignment.patch index ba263bd43182..c209ce46a496 100644 --- a/patches/ftrace-Fix-trace-header-alignment.patch +++ b/patches/ftrace-Fix-trace-header-alignment.patch @@ -14,7 +14,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c -@@ -3121,17 +3121,17 @@ get_total_entries(struct trace_buffer *b +@@ -3144,17 +3144,17 @@ get_total_entries(struct trace_buffer *b static void print_lat_help_header(struct seq_file *m) { @@ -43,7 +43,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> } static void print_event_info(struct trace_buffer *buf, struct seq_file *m) -@@ -3160,11 +3160,11 @@ static void print_func_help_header_irq(s +@@ -3183,11 +3183,11 @@ static void print_func_help_header_irq(s "# |/ _-----=> need-resched_lazy\n" "# || / _---=> hardirq/softirq\n" "# ||| / _--=> preempt-depth\n" diff --git a/patches/ftrace-migrate-disable-tracing.patch b/patches/ftrace-migrate-disable-tracing.patch index dd2ebfb8e4bc..c59aa3e75d62 100644 --- a/patches/ftrace-migrate-disable-tracing.patch +++ b/patches/ftrace-migrate-disable-tracing.patch @@ -23,7 +23,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> #define TRACE_EVENT_TYPE_MAX \ --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c -@@ -1954,6 +1954,8 @@ tracing_generic_entry_update(struct trac +@@ -1955,6 +1955,8 @@ tracing_generic_entry_update(struct trac ((pc & SOFTIRQ_OFFSET) ? TRACE_FLAG_SOFTIRQ : 0) | (tif_need_resched() ? TRACE_FLAG_NEED_RESCHED : 0) | (test_preempt_need_resched() ? TRACE_FLAG_PREEMPT_RESCHED : 0); @@ -32,7 +32,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> } EXPORT_SYMBOL_GPL(tracing_generic_entry_update); -@@ -3122,9 +3124,10 @@ static void print_lat_help_header(struct +@@ -3145,9 +3147,10 @@ static void print_lat_help_header(struct "# | / _----=> need-resched \n" "# || / _---=> hardirq/softirq \n" "# ||| / _--=> preempt-depth \n" diff --git a/patches/futex-rtmutex-Cure-RT-double-blocking-issue.patch b/patches/futex-rtmutex-Cure-RT-double-blocking-issue.patch index 551f4789d2e9..831ec3d398f5 100644 --- a/patches/futex-rtmutex-Cure-RT-double-blocking-issue.patch +++ b/patches/futex-rtmutex-Cure-RT-double-blocking-issue.patch @@ -25,7 +25,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c -@@ -2407,6 +2407,7 @@ int rt_mutex_wait_proxy_lock(struct rt_m +@@ -2406,6 +2406,7 @@ int rt_mutex_wait_proxy_lock(struct rt_m struct hrtimer_sleeper *to, struct rt_mutex_waiter *waiter) { @@ -33,7 +33,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> int ret; raw_spin_lock_irq(&lock->wait_lock); -@@ -2418,6 +2419,24 @@ int rt_mutex_wait_proxy_lock(struct rt_m +@@ -2417,6 +2418,24 @@ int rt_mutex_wait_proxy_lock(struct rt_m * have to fix that up. */ fixup_rt_mutex_waiters(lock); diff --git a/patches/hrtimer-consolidate-hrtimer_init-hrtimer_init_sleepe.patch b/patches/hrtimer-consolidate-hrtimer_init-hrtimer_init_sleepe.patch index 3474b406ce5b..4ba25565b78e 100644 --- a/patches/hrtimer-consolidate-hrtimer_init-hrtimer_init_sleepe.patch +++ b/patches/hrtimer-consolidate-hrtimer_init-hrtimer_init_sleepe.patch @@ -1,4 +1,3 @@ -From 3c3702c557d49df718523d04ae1393ce05769ef6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Mon, 4 Sep 2017 18:31:50 +0200 Subject: [PATCH] hrtimer: consolidate hrtimer_init() + hrtimer_init_sleeper() diff --git a/patches/iommu-amd-Use-raw_cpu_ptr-instead-of-get_cpu_ptr-for.patch b/patches/iommu-amd-Use-raw_cpu_ptr-instead-of-get_cpu_ptr-for.patch new file mode 100644 index 000000000000..228c9adf2755 --- /dev/null +++ b/patches/iommu-amd-Use-raw_cpu_ptr-instead-of-get_cpu_ptr-for.patch @@ -0,0 +1,43 @@ +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +Date: Tue, 5 Sep 2017 14:11:41 +0200 +Subject: iommu/amd: Use raw_cpu_ptr() instead of get_cpu_ptr() for + ->flush_queue + +get_cpu_ptr() disabled preemption and returns the ->flush_queue object +of the current CPU. raw_cpu_ptr() does the same except that it not +disable preemption which means the scheduler can move it to another CPU +after it obtained the per-CPU object. +In this case this is not bad because the data structure itself is +protected with a spin_lock. This change shouldn't matter however on RT +it does because the sleeping lock can't be accessed with disabled +preemption. + +Cc: rt-stable@vger.kernel.org +Cc: Joerg Roedel <joro@8bytes.org> +Cc: iommu@lists.linux-foundation.org +Reported-by: Vinod Adhikary <vinadhy@gmail.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + drivers/iommu/amd_iommu.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/iommu/amd_iommu.c ++++ b/drivers/iommu/amd_iommu.c +@@ -2289,7 +2289,7 @@ static void queue_add(struct dma_ops_dom + pages = __roundup_pow_of_two(pages); + address >>= PAGE_SHIFT; + +- queue = get_cpu_ptr(&flush_queue); ++ queue = raw_cpu_ptr(&flush_queue); + spin_lock_irqsave(&queue->lock, flags); + + if (queue->next == FLUSH_QUEUE_SIZE) +@@ -2306,8 +2306,6 @@ static void queue_add(struct dma_ops_dom + + if (atomic_cmpxchg(&queue_timer_on, 0, 1) == 0) + mod_timer(&queue_timer, jiffies + msecs_to_jiffies(10)); +- +- put_cpu_ptr(&flush_queue); + } + + diff --git a/patches/locallock-add-local_lock_on.patch b/patches/locallock-add-local_lock_on.patch index 0c714f2192f5..bb3846f205d9 100644 --- a/patches/locallock-add-local_lock_on.patch +++ b/patches/locallock-add-local_lock_on.patch @@ -19,7 +19,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static inline int __local_trylock(struct local_irq_lock *lv) { if (lv->owner != current && spin_trylock_local(&lv->lock)) { -@@ -98,6 +101,9 @@ static inline void __local_unlock(struct +@@ -101,6 +104,9 @@ static inline void __local_unlock(struct put_local_var(lvar); \ } while (0) diff --git a/patches/localversion.patch b/patches/localversion.patch index 25e5fadbaae8..e1f3b8d87864 100644 --- a/patches/localversion.patch +++ b/patches/localversion.patch @@ -10,4 +10,4 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- /dev/null +++ b/localversion-rt @@ -0,0 +1 @@ -+-rt13 ++-rt14 diff --git a/patches/locking-rtmutex-don-t-drop-the-wait_lock-twice.patch b/patches/locking-rtmutex-don-t-drop-the-wait_lock-twice.patch new file mode 100644 index 000000000000..e5a2346657e8 --- /dev/null +++ b/patches/locking-rtmutex-don-t-drop-the-wait_lock-twice.patch @@ -0,0 +1,29 @@ +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +Date: Thu, 7 Sep 2017 12:38:47 +0200 +Subject: locking/rtmutex: don't drop the wait_lock twice + +Since the futex rework, __rt_mutex_start_proxy_lock() does no longer +acquire the wait_lock so it must not drop it. Otherwise the lock is not +only unlocked twice but also the preemption counter is underflown. + +It is okay to remove that line because this function does not disable +interrupts nor does it acquire the ->wait_lock. The caller does this so it is +wrong do it here (after the futex rework). + +Cc: rt-stable@vger.kernel.org #v4.9.18-rt14+ +Reported-by: Gusenleitner Klaus <gus@keba.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/locking/rtmutex.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/kernel/locking/rtmutex.c ++++ b/kernel/locking/rtmutex.c +@@ -1745,7 +1745,6 @@ int __rt_mutex_start_proxy_lock(struct r + raw_spin_lock(&task->pi_lock); + if (task->pi_blocked_on) { + raw_spin_unlock(&task->pi_lock); +- raw_spin_unlock_irq(&lock->wait_lock); + return -EAGAIN; + } + task->pi_blocked_on = PI_REQUEUE_INPROGRESS; diff --git a/patches/net-use-trylock-in-icmp_sk.patch b/patches/net-use-trylock-in-icmp_sk.patch new file mode 100644 index 000000000000..3e706f3c8ede --- /dev/null +++ b/patches/net-use-trylock-in-icmp_sk.patch @@ -0,0 +1,73 @@ +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +Date: Thu, 21 Sep 2017 14:42:04 +0200 +Subject: net: use trylock in icmp_sk + +The locking path can be recursive (same as for sk->sk_lock.slock) and +therefore we need a trylock version for the locallock, too. + +Cc: rt-stable@vger.kernel.org +Reported-by: Jacek Konieczny <jajcus@jajcus.net> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + net/ipv4/icmp.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/net/ipv4/icmp.c ++++ b/net/ipv4/icmp.c +@@ -217,12 +217,16 @@ static inline struct sock *icmp_xmit_loc + { + struct sock *sk; + ++ if (!local_trylock(icmp_sk_lock)) ++ return NULL; ++ + sk = icmp_sk(net); + + if (unlikely(!spin_trylock(&sk->sk_lock.slock))) { + /* This can happen if the output path signals a + * dst_link_failure() for an outgoing ICMP packet. + */ ++ local_unlock(icmp_sk_lock); + return NULL; + } + return sk; +@@ -231,6 +235,7 @@ static inline struct sock *icmp_xmit_loc + static inline void icmp_xmit_unlock(struct sock *sk) + { + spin_unlock(&sk->sk_lock.slock); ++ local_unlock(icmp_sk_lock); + } + + int sysctl_icmp_msgs_per_sec __read_mostly = 1000; +@@ -420,7 +425,6 @@ static void icmp_reply(struct icmp_bxm * + + /* Needed by both icmp_global_allow and icmp_xmit_lock */ + local_bh_disable(); +- local_lock(icmp_sk_lock); + + /* global icmp_msgs_per_sec */ + if (!icmpv4_global_allow(net, type, code)) +@@ -465,7 +469,6 @@ static void icmp_reply(struct icmp_bxm * + out_unlock: + icmp_xmit_unlock(sk); + out_bh_enable: +- local_unlock(icmp_sk_lock); + local_bh_enable(); + } + +@@ -678,7 +681,6 @@ void icmp_send(struct sk_buff *skb_in, i + + /* Needed by both icmp_global_allow and icmp_xmit_lock */ + local_bh_disable(); +- local_lock(icmp_sk_lock); + + /* Check global sysctl_icmp_msgs_per_sec ratelimit, unless + * incoming dev is loopback. If outgoing dev change to not be +@@ -767,7 +769,6 @@ void icmp_send(struct sk_buff *skb_in, i + out_unlock: + icmp_xmit_unlock(sk); + out_bh_enable: +- local_unlock(icmp_sk_lock); + local_bh_enable(); + out:; + } diff --git a/patches/ping-sysrq.patch b/patches/ping-sysrq.patch index f14a905f6d0b..38eaa1ae2caf 100644 --- a/patches/ping-sysrq.patch +++ b/patches/ping-sysrq.patch @@ -59,7 +59,7 @@ Signed-off-by: Carsten Emde <C.Emde@osadl.org> #include <linux/socket.h> #include <linux/in.h> #include <linux/inet.h> -@@ -931,6 +932,30 @@ static bool icmp_redirect(struct sk_buff +@@ -932,6 +933,30 @@ static bool icmp_redirect(struct sk_buff } /* @@ -90,7 +90,7 @@ Signed-off-by: Carsten Emde <C.Emde@osadl.org> * Handle ICMP_ECHO ("ping") requests. * * RFC 1122: 3.2.2.6 MUST have an echo server that answers ICMP echo -@@ -957,6 +982,11 @@ static bool icmp_echo(struct sk_buff *sk +@@ -958,6 +983,11 @@ static bool icmp_echo(struct sk_buff *sk icmp_param.data_len = skb->len; icmp_param.head_len = sizeof(struct icmphdr); icmp_reply(&icmp_param, skb); diff --git a/patches/preempt-lazy-support.patch b/patches/preempt-lazy-support.patch index e98d84bc008d..89124728466b 100644 --- a/patches/preempt-lazy-support.patch +++ b/patches/preempt-lazy-support.patch @@ -492,7 +492,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c -@@ -1942,6 +1942,7 @@ tracing_generic_entry_update(struct trac +@@ -1943,6 +1943,7 @@ tracing_generic_entry_update(struct trac struct task_struct *tsk = current; entry->preempt_count = pc & 0xff; @@ -500,7 +500,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> entry->pid = (tsk) ? tsk->pid : 0; entry->flags = #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT -@@ -1952,7 +1953,8 @@ tracing_generic_entry_update(struct trac +@@ -1953,7 +1954,8 @@ tracing_generic_entry_update(struct trac ((pc & NMI_MASK ) ? TRACE_FLAG_NMI : 0) | ((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) | ((pc & SOFTIRQ_OFFSET) ? TRACE_FLAG_SOFTIRQ : 0) | @@ -510,7 +510,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> (test_preempt_need_resched() ? TRACE_FLAG_PREEMPT_RESCHED : 0); entry->migrate_disable = (tsk) ? __migrate_disabled(tsk) & 0xFF : 0; -@@ -3119,15 +3121,17 @@ get_total_entries(struct trace_buffer *b +@@ -3142,15 +3144,17 @@ get_total_entries(struct trace_buffer *b static void print_lat_help_header(struct seq_file *m) { @@ -537,7 +537,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> } static void print_event_info(struct trace_buffer *buf, struct seq_file *m) -@@ -3153,11 +3157,14 @@ static void print_func_help_header_irq(s +@@ -3176,11 +3180,14 @@ static void print_func_help_header_irq(s print_event_info(buf, m); seq_puts(m, "# _-----=> irqs-off\n" "# / _----=> need-resched\n" diff --git a/patches/rt-add-rt-locks.patch b/patches/rt-add-rt-locks.patch index e6d1fa49adb3..89e706c8cc53 100644 --- a/patches/rt-add-rt-locks.patch +++ b/patches/rt-add-rt-locks.patch @@ -2244,7 +2244,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> debug_rt_mutex_proxy_lock(lock, proxy_owner); rt_mutex_set_owner(lock, proxy_owner); } -@@ -1926,3 +2314,25 @@ bool rt_mutex_cleanup_proxy_lock(struct +@@ -1925,3 +2313,25 @@ bool rt_mutex_cleanup_proxy_lock(struct return cleanup; } diff --git a/patches/rt-locking--Consolidate-lock-functions.patch b/patches/rt-locking--Consolidate-lock-functions.patch index 20dd8f7123b6..8340f8ec99a0 100644 --- a/patches/rt-locking--Consolidate-lock-functions.patch +++ b/patches/rt-locking--Consolidate-lock-functions.patch @@ -49,7 +49,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> LL_WARN(lv->owner); LL_WARN(lv->nestcnt); lv->owner = current; -@@ -98,7 +82,7 @@ static inline void __local_unlock(struct +@@ -101,7 +85,7 @@ static inline void __local_unlock(struct return; lv->owner = NULL; diff --git a/patches/rt-locking-allow-recursive-local_trylock.patch b/patches/rt-locking-allow-recursive-local_trylock.patch new file mode 100644 index 000000000000..82521dd7e4c6 --- /dev/null +++ b/patches/rt-locking-allow-recursive-local_trylock.patch @@ -0,0 +1,38 @@ +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +Date: Thu, 21 Sep 2017 14:39:56 +0200 +Subject: rt/locking: allow recursive local_trylock() + +required for following networking patch which does recursive try-lock. +While at it, add the !RT version of it because it did not yet exist. + +Cc: rt-stable@vger.kernel.org +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + include/linux/locallock.h | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/include/linux/locallock.h ++++ b/include/linux/locallock.h +@@ -68,6 +68,9 @@ static inline int __local_trylock(struct + lv->owner = current; + lv->nestcnt = 1; + return 1; ++ } else if (lv->owner == current) { ++ lv->nestcnt++; ++ return 1; + } + return 0; + } +@@ -238,6 +241,12 @@ static inline int __local_unlock_irqrest + + static inline void local_irq_lock_init(int lvar) { } + ++#define local_trylock(lvar) \ ++ ({ \ ++ preempt_disable(); \ ++ 1; \ ++ }) ++ + #define local_lock(lvar) preempt_disable() + #define local_unlock(lvar) preempt_enable() + #define local_lock_irq(lvar) local_irq_disable() diff --git a/patches/rtmutex-add-a-first-shot-of-ww_mutex.patch b/patches/rtmutex-add-a-first-shot-of-ww_mutex.patch index ea217e46dcef..eb991c15e0ef 100644 --- a/patches/rtmutex-add-a-first-shot-of-ww_mutex.patch +++ b/patches/rtmutex-add-a-first-shot-of-ww_mutex.patch @@ -323,7 +323,7 @@ Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc> rt_mutex_slowlock); } EXPORT_SYMBOL_GPL(rt_mutex_timed_lock); -@@ -2247,7 +2394,7 @@ int rt_mutex_wait_proxy_lock(struct rt_m +@@ -2246,7 +2393,7 @@ int rt_mutex_wait_proxy_lock(struct rt_m raw_spin_lock_irq(&lock->wait_lock); /* sleep on the mutex */ set_current_state(TASK_INTERRUPTIBLE); @@ -332,7 +332,7 @@ Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc> /* * try_to_take_rt_mutex() sets the waiter bit unconditionally. We might * have to fix that up. -@@ -2314,24 +2461,98 @@ bool rt_mutex_cleanup_proxy_lock(struct +@@ -2313,24 +2460,98 @@ bool rt_mutex_cleanup_proxy_lock(struct return cleanup; } diff --git a/patches/series b/patches/series index fb207e236f2e..32bfb07ec7d9 100644 --- a/patches/series +++ b/patches/series @@ -172,6 +172,8 @@ NFSv4-replace-seqcount_t-with-a-seqlock_t.patch ############################################################ # Submitted on LKML ############################################################ +Bluetooth-avoid-recursive-locking-in-hci_send_to_cha.patch +iommu-amd-Use-raw_cpu_ptr-instead-of-get_cpu_ptr-for.patch # SPARC part of erly printk consolidation sparc64-use-generic-rwsem-spinlocks-rt.patch @@ -210,39 +212,47 @@ CPUFREQ-Loongson2-drop-set_cpus_allowed_ptr.patch kernel-sched-Provide-a-pointer-to-the-valid-CPU-mask.patch add_migrate_disable.patch -# tracing: Inter-event (e.g. latency) support | 2017-06-27 -0001-tracing-Add-hist_field_name-accessor.patch -0002-tracing-Reimplement-log2.patch -0003-ring-buffer-Add-interface-for-setting-absolute-time-.patch -0004-ring-buffer-Redefine-the-unimplemented-RINGBUF_TIME_.patch -0005-tracing-Give-event-triggers-access-to-ring_buffer_ev.patch -0006-tracing-Add-ring-buffer-event-param-to-hist-field-fu.patch -0007-tracing-Increase-tracing-map-KEYS_MAX-size.patch -0008-tracing-Break-out-hist-trigger-assignment-parsing.patch -0009-tracing-Make-traceprobe-parsing-code-reusable.patch -0010-tracing-Add-NO_DISCARD-event-file-flag.patch -0011-tracing-Add-post-trigger-flag-to-hist-trigger-comman.patch -0012-tracing-Add-hist-trigger-timestamp-support.patch -0013-tracing-Add-per-element-variable-support-to-tracing_.patch -0014-tracing-Add-hist_data-member-to-hist_field.patch -0015-tracing-Add-usecs-modifier-for-hist-trigger-timestam.patch -0016-tracing-Add-variable-support-to-hist-triggers.patch -0017-tracing-Account-for-variables-in-named-trigger-compa.patch -0018-tracing-Add-simple-expression-support-to-hist-trigge.patch -0019-tracing-Add-variable-reference-handling-to-hist-trig.patch -0020-tracing-Add-support-for-dynamic-tracepoints.patch -0021-tracing-Add-hist-trigger-action-hook.patch -0022-tracing-Add-support-for-synthetic-events.patch -0023-tracing-Add-onmatch-hist-trigger-action-support.patch -0024-tracing-Add-onmax-hist-trigger-action-support.patch -0025-tracing-Allow-whitespace-to-surround-hist-trigger-fi.patch -0026-tracing-Make-duplicate-count-from-tracing_map-availa.patch -0027-tracing-Add-cpu-field-for-hist-triggers.patch -0028-tracing-Add-hist-trigger-support-for-variable-refere.patch -0029-tracing-Add-last-error-error-facility-for-hist-trigg.patch -0030-tracing-Add-inter-event-hist-trigger-Documentation.patch -0031-tracing-Make-tracing_set_clock-non-static.patch -0032-tracing-Add-a-clock-attribute-for-hist-triggers.patch +# tracing: Inter-event (e.g. latency) support | 2017-09-05 +0001-tracing-Exclude-generic-fields-from-histograms.patch +0002-tracing-Add-support-to-detect-and-avoid-duplicates.patch +0003-tracing-Remove-code-which-merges-duplicates.patch +0004-tracing-Add-hist_field_name-accessor.patch +0005-tracing-Reimplement-log2.patch +0006-ring-buffer-Add-interface-for-setting-absolute-time-.patch +0007-tracing-Apply-absolute-timestamps-to-instance-max-bu.patch +0008-ring-buffer-Redefine-the-unimplemented-RINGBUF_TIME_.patch +0009-tracing-Give-event-triggers-access-to-ring_buffer_ev.patch +0010-tracing-Add-ring-buffer-event-param-to-hist-field-fu.patch +0011-tracing-Increase-tracing-map-KEYS_MAX-size.patch +0012-tracing-Break-out-hist-trigger-assignment-parsing.patch +0013-tracing-Make-traceprobe-parsing-code-reusable.patch +0014-tracing-Add-hist-trigger-timestamp-support.patch +0015-tracing-Add-per-element-variable-support-to-tracing_.patch +0016-tracing-Add-hist_data-member-to-hist_field.patch +0017-tracing-Add-usecs-modifier-for-hist-trigger-timestam.patch +0018-tracing-Add-variable-support-to-hist-triggers.patch +0019-tracing-Account-for-variables-in-named-trigger-compa.patch +0020-tracing-Add-simple-expression-support-to-hist-trigge.patch +0021-tracing-Generalize-per-element-hist-trigger-data.patch +0022-tracing-Pass-tracing_map_elt-to-hist_field-accessor-.patch +0023-tracing-Add-hist_field-type-field.patch +0024-tracing-Add-variable-reference-handling-to-hist-trig.patch +0025-tracing-Add-support-for-dynamic-tracepoints.patch +0026-tracing-Add-hist-trigger-action-hook.patch +0027-tracing-Add-support-for-synthetic-events.patch +0028-tracing-Add-support-for-field-variables.patch +0029-tracing-Add-onmatch-hist-trigger-action-support.patch +0030-tracing-Add-onmax-hist-trigger-action-support.patch +0031-tracing-Allow-whitespace-to-surround-hist-trigger-fi.patch +0032-tracing-Add-cpu-field-for-hist-triggers.patch +0033-tracing-Add-hist-trigger-support-for-variable-refere.patch +0034-tracing-Add-last-error-error-facility-for-hist-trigg.patch +0035-tracing-Reverse-the-order-event_mutex-trace_types_lo.patch +0036-tracing-Remove-lookups-from-tracing_map-hitcount.patch +0037-tracing-Add-inter-event-hist-trigger-Documentation.patch +0038-tracing-Make-tracing_set_clock-non-static.patch +0039-tracing-Add-a-clock-attribute-for-hist-triggers.patch +0040-tracing-Add-trace_event_buffer_reserve-variant-that-.patch # SCHED BLOCK/WQ block-shorten-interrupt-disabled-regions.patch @@ -335,6 +345,7 @@ preempt-nort-rt-variants.patch # local locks & migrate disable futex-workaround-migrate_disable-enable-in-different.patch rt-local-irq-lock.patch +rt-locking-allow-recursive-local_trylock.patch locallock-add-local_lock_on.patch # ANNOTATE local_irq_disable sites @@ -474,6 +485,7 @@ fs-nfs-turn-rmdir_sem-into-a-semaphore.patch # FUTEX/RTMUTEX rtmutex-futex-prepare-rt.patch futex-requeue-pi-fix.patch +locking-rtmutex-don-t-drop-the-wait_lock-twice.patch futex-Ensure-lock-unlock-symetry-versus-pi_lock-and-.patch # RTMUTEX @@ -594,6 +606,7 @@ net-Qdisc-use-a-seqlock-instead-seqcount.patch net-add-back-the-missing-serialization-in-ip_send_un.patch net-take-the-tcp_sk_lock-lock-with-BH-disabled.patch net-add-a-lock-around-icmp_sk.patch +net-use-trylock-in-icmp_sk.patch net-Have-__napi_schedule_irqoff-disable-interrupts-o.patch # NETWORK DEBUGGING AID |